summaryrefslogtreecommitdiffstats
path: root/chromium/chrome/browser
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-05-03 13:42:47 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-05-15 10:27:51 +0000
commit8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (patch)
treed29d987c4d7b173cf853279b79a51598f104b403 /chromium/chrome/browser
parent830c9e163d31a9180fadca926b3e1d7dfffb5021 (diff)
BASELINE: Update Chromium to 66.0.3359.156
Change-Id: I0c9831ad39911a086b6377b16f995ad75a51e441 Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/chrome/browser')
-rw-r--r--chromium/chrome/browser/BUILD.gn226
-rw-r--r--chromium/chrome/browser/android/vr/BUILD.gn (renamed from chromium/chrome/browser/android/vr_shell/BUILD.gn)12
-rw-r--r--chromium/chrome/browser/browser_resources.grd22
-rw-r--r--chromium/chrome/browser/chrome_content_browser_manifest_overlay.json7
-rw-r--r--chromium/chrome/browser/chrome_content_gpu_manifest_overlay.json1
-rw-r--r--chromium/chrome/browser/chrome_content_plugin_manifest_overlay.json1
-rw-r--r--chromium/chrome/browser/chrome_content_renderer_manifest_overlay.json8
-rw-r--r--chromium/chrome/browser/chrome_content_utility_manifest_overlay.json1
-rw-r--r--chromium/chrome/browser/chrome_notification_types.h5
-rw-r--r--chromium/chrome/browser/chromeos/BUILD.gn114
-rw-r--r--chromium/chrome/browser/custom_handlers/OWNERS1
-rw-r--r--chromium/chrome/browser/custom_handlers/protocol_handler_registry.cc7
-rw-r--r--chromium/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc74
-rw-r--r--chromium/chrome/browser/devtools/OWNERS1
-rw-r--r--chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc13
-rw-r--r--chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h2
-rw-r--r--chromium/chrome/browser/devtools/chrome_devtools_session.cc3
-rw-r--r--chromium/chrome/browser/devtools/device/adb/mock_adb_server.cc9
-rw-r--r--chromium/chrome/browser/devtools/device/android_device_manager.cc8
-rw-r--r--chromium/chrome/browser/devtools/device/android_web_socket.cc4
-rw-r--r--chromium/chrome/browser/devtools/device/cast_device_provider.cc33
-rw-r--r--chromium/chrome/browser/devtools/device/cast_device_provider.h6
-rw-r--r--chromium/chrome/browser/devtools/device/cast_device_provider_unittest.cc7
-rw-r--r--chromium/chrome/browser/devtools/device/devtools_android_bridge.cc6
-rw-r--r--chromium/chrome/browser/devtools/device/devtools_device_discovery.cc6
-rw-r--r--chromium/chrome/browser/devtools/device/port_forwarding_controller.cc6
-rw-r--r--chromium/chrome/browser/devtools/device/usb/android_usb_browsertest.cc4
-rw-r--r--chromium/chrome/browser/devtools/device/usb/android_usb_device.cc24
-rw-r--r--chromium/chrome/browser/devtools/devtools_eye_dropper.cc15
-rw-r--r--chromium/chrome/browser/devtools/devtools_eye_dropper.h3
-rw-r--r--chromium/chrome/browser/devtools/devtools_file_helper.cc2
-rw-r--r--chromium/chrome/browser/devtools/devtools_file_system_indexer_unittest.cc5
-rw-r--r--chromium/chrome/browser/devtools/devtools_protocol.cc6
-rw-r--r--chromium/chrome/browser/devtools/devtools_sanity_browsertest.cc94
-rw-r--r--chromium/chrome/browser/devtools/devtools_targets_ui.cc10
-rw-r--r--chromium/chrome/browser/devtools/devtools_ui_bindings.cc4
-rw-r--r--chromium/chrome/browser/devtools/devtools_window.cc163
-rw-r--r--chromium/chrome/browser/devtools/devtools_window.h14
-rw-r--r--chromium/chrome/browser/devtools/global_confirm_info_bar_browsertest.cc4
-rw-r--r--chromium/chrome/browser/devtools/protocol_string.cc2
-rw-r--r--chromium/chrome/browser/devtools/remote_debugging_server.cc34
-rw-r--r--chromium/chrome/browser/devtools/remote_debugging_server.h4
-rw-r--r--chromium/chrome/browser/devtools/serialize_host_descriptions.cc2
-rw-r--r--chromium/chrome/browser/engagement/BUILD.gn2
-rw-r--r--chromium/chrome/browser/engagement/site_engagement_details.mojom2
-rw-r--r--chromium/chrome/browser/extensions/BUILD.gn41
-rw-r--r--chromium/chrome/browser/extensions/api/DEPS2
-rw-r--r--chromium/chrome/browser/extensions/api/automation/automation_apitest.cc25
-rw-r--r--chromium/chrome/browser/extensions/api/automation_internal/automation_event_router.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc59
-rw-r--r--chromium/chrome/browser/extensions/api/automation_internal/automation_internal_api.h5
-rw-r--r--chromium/chrome/browser/extensions/api/autotest_private/DEPS3
-rw-r--r--chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc6
-rw-r--r--chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.h2
-rw-r--r--chromium/chrome/browser/extensions/api/bookmarks/bookmark_apitest.cc8
-rw-r--r--chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc18
-rw-r--r--chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.h23
-rw-r--r--chromium/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc7
-rw-r--r--chromium/chrome/browser/extensions/api/browsing_data/browsing_data_test.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/cast_streaming/performance_test.cc13
-rw-r--r--chromium/chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.cc1
-rw-r--r--chromium/chrome/browser/extensions/api/commands/command_service.cc38
-rw-r--r--chromium/chrome/browser/extensions/api/commands/command_service.h12
-rw-r--r--chromium/chrome/browser/extensions/api/cookies/cookies_api.cc5
-rw-r--r--chromium/chrome/browser/extensions/api/cookies/cookies_api.h2
-rw-r--r--chromium/chrome/browser/extensions/api/cookies/cookies_helpers.h2
-rw-r--r--chromium/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc57
-rw-r--r--chromium/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api_unittest.cc8
-rw-r--r--chromium/chrome/browser/extensions/api/dashboard_private/dashboard_private_api.cc3
-rw-r--r--chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc72
-rw-r--r--chromium/chrome/browser/extensions/api/debugger/extension_dev_tools_infobar.cc6
-rw-r--r--chromium/chrome/browser/extensions/api/declarative/declarative_apitest.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc58
-rw-r--r--chromium/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/declarative_content/request_content_script_apitest.cc2
-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.cc201
-rw-r--r--chromium/chrome/browser/extensions/api/declarative_net_request/rule_indexing_unittest.cc7
-rw-r--r--chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc44
-rw-r--r--chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_matcher_unittest.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc103
-rw-r--r--chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc20
-rw-r--r--chromium/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc5
-rw-r--r--chromium/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc6
-rw-r--r--chromium/chrome/browser/extensions/api/device_permissions_manager_unittest.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/dial/dial_api.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/dial/dial_apitest.cc9
-rw-r--r--chromium/chrome/browser/extensions/api/downloads/downloads_api.cc138
-rw-r--r--chromium/chrome/browser/extensions/api/downloads/downloads_api.h25
-rw-r--r--chromium/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc234
-rw-r--r--chromium/chrome/browser/extensions/api/downloads_internal/downloads_internal_api.cc16
-rw-r--r--chromium/chrome/browser/extensions/api/downloads_internal/downloads_internal_api.h6
-rw-r--r--chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc12
-rw-r--r--chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc31
-rw-r--r--chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.cc77
-rw-r--r--chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.h47
-rw-r--r--chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_apitest.cc53
-rw-r--r--chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc22
-rw-r--r--chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc14
-rw-r--r--chromium/chrome/browser/extensions/api/experience_sampling_private/OWNERS1
-rw-r--r--chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling.cc113
-rw-r--r--chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling.h79
-rw-r--r--chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling_private_api.cc26
-rw-r--r--chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling_private_api.h27
-rw-r--r--chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling_private_api_unittest.cc43
-rw-r--r--chromium/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc39
-rw-r--r--chromium/chrome/browser/extensions/api/extension_action/extension_action_api.cc45
-rw-r--r--chromium/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/file_system/request_file_system_notification.cc8
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_apitest.cc12
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_get_accounts_function.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_get_accounts_function.h2
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.h2
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_get_profile_user_info_function.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/identity/identity_get_profile_user_info_function.h2
-rw-r--r--chromium/chrome/browser/extensions/api/image_writer_private/image_writer_private_apitest.cc1
-rw-r--r--chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h2
-rw-r--r--chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/image_writer_private/operation.cc11
-rw-r--r--chromium/chrome/browser/extensions/api/image_writer_private/operation_unittest.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/input_ime/input_ime_api.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/input_ime/input_ime_api.h5
-rw-r--r--chromium/chrome/browser/extensions/api/input_ime/input_ime_api_nonchromeos.cc29
-rw-r--r--chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc24
-rw-r--r--chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/management/management_api_browsertest.cc6
-rw-r--r--chromium/chrome/browser/extensions/api/management/management_api_unittest.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/media_galleries/media_galleries_apitest.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.cc11
-rw-r--r--chromium/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc8
-rw-r--r--chromium/chrome/browser/extensions/api/messaging/incognito_connectability_infobar_delegate.cc5
-rw-r--r--chromium/chrome/browser/extensions/api/messaging/incognito_connectability_infobar_delegate.h1
-rw-r--r--chromium/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc1
-rw-r--r--chromium/chrome/browser/extensions/api/messaging/native_process_launcher.cc9
-rw-r--r--chromium/chrome/browser/extensions/api/networking_config_chromeos_apitest_chromeos.cc7
-rw-r--r--chromium/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_browsertest.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_win.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/notifications/extension_notification_handler.cc12
-rw-r--r--chromium/chrome/browser/extensions/api/notifications/extension_notification_handler.h1
-rw-r--r--chromium/chrome/browser/extensions/api/notifications/extension_notification_handler_unittest.cc6
-rw-r--r--chromium/chrome/browser/extensions/api/notifications/notifications_api.cc22
-rw-r--r--chromium/chrome/browser/extensions/api/notifications/notifications_api.h2
-rw-r--r--chromium/chrome/browser/extensions/api/notifications/notifications_apitest.cc10
-rw-r--r--chromium/chrome/browser/extensions/api/page_capture/page_capture_api.cc6
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.cc44
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.h36
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc34
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h15
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc82
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h17
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc37
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.cc35
-rw-r--r--chromium/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h12
-rw-r--r--chromium/chrome/browser/extensions/api/permissions/permissions_api_unittest.cc3
-rw-r--r--chromium/chrome/browser/extensions/api/preference/preference_api_prefs_unittest.cc20
-rw-r--r--chromium/chrome/browser/extensions/api/processes/processes_api.cc5
-rw-r--r--chromium/chrome/browser/extensions/api/processes/processes_apitest.cc43
-rw-r--r--chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.cc8
-rw-r--r--chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.h2
-rw-r--r--chromium/chrome/browser/extensions/api/runtime/runtime_apitest.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/sessions/sessions_api.cc14
-rw-r--r--chromium/chrome/browser/extensions/api/sessions/sessions_apitest.cc10
-rw-r--r--chromium/chrome/browser/extensions/api/settings_private/prefs_util.cc22
-rw-r--r--chromium/chrome/browser/extensions/api/socket/udp_socket_unittest.cc152
-rw-r--r--chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.cc7
-rw-r--r--chromium/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc9
-rw-r--r--chromium/chrome/browser/extensions/api/sync_file_system/sync_file_system_api.h2
-rw-r--r--chromium/chrome/browser/extensions/api/sync_file_system/sync_file_system_browsertest.cc1
-rw-r--r--chromium/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc5
-rw-r--r--chromium/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc39
-rw-r--r--chromium/chrome/browser/extensions/api/tabs/app_window_controller.cc64
-rw-r--r--chromium/chrome/browser/extensions/api/tabs/app_window_controller.h11
-rw-r--r--chromium/chrome/browser/extensions/api/tabs/tabs_api.cc276
-rw-r--r--chromium/chrome/browser/extensions/api/tabs/tabs_api.h7
-rw-r--r--chromium/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc17
-rw-r--r--chromium/chrome/browser/extensions/api/tabs/tabs_event_router.cc7
-rw-r--r--chromium/chrome/browser/extensions/api/tabs/tabs_test.cc154
-rw-r--r--chromium/chrome/browser/extensions/api/tabs/windows_event_router.cc42
-rw-r--r--chromium/chrome/browser/extensions/api/tabs/windows_util.cc48
-rw-r--r--chromium/chrome/browser/extensions/api/tabs/windows_util.h14
-rw-r--r--chromium/chrome/browser/extensions/api/terminal/terminal_private_api.cc48
-rw-r--r--chromium/chrome/browser/extensions/api/terminal/terminal_private_api.h7
-rw-r--r--chromium/chrome/browser/extensions/api/virtual_keyboard_private/DEPS1
-rw-r--r--chromium/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc35
-rw-r--r--chromium/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.h5
-rw-r--r--chromium/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc21
-rw-r--r--chromium/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc61
-rw-r--r--chromium/chrome/browser/extensions/api/web_request/web_request_apitest.cc105
-rw-r--r--chromium/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc67
-rw-r--r--chromium/chrome/browser/extensions/api/webrtc_audio_private/DEPS1
-rw-r--r--chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.cc85
-rw-r--r--chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h27
-rw-r--r--chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc13
-rw-r--r--chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc2
-rw-r--r--chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_browsertest.cc4
-rw-r--r--chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc3
-rw-r--r--chromium/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc2
-rw-r--r--chromium/chrome/browser/media/BUILD.gn2
-rw-r--r--chromium/chrome/browser/media/media_engagement_score_details.mojom5
-rw-r--r--chromium/chrome/browser/media/router/BUILD.gn15
-rw-r--r--chromium/chrome/browser/net/chrome_mojo_proxy_resolver_factory.h4
-rw-r--r--chromium/chrome/browser/printing/background_printing_manager.cc2
-rw-r--r--chromium/chrome/browser/printing/cloud_print/cloud_print_proxy_service_unittest.cc34
-rw-r--r--chromium/chrome/browser/printing/cloud_print/gcd_api_flow_unittest.cc4
-rw-r--r--chromium/chrome/browser/printing/cloud_print/privet_device_lister_impl.cc17
-rw-r--r--chromium/chrome/browser/printing/cloud_print/privet_device_lister_impl.h9
-rw-r--r--chromium/chrome/browser/printing/cloud_print/privet_device_lister_unittest.cc4
-rw-r--r--chromium/chrome/browser/printing/cloud_print/privet_http_impl.cc4
-rw-r--r--chromium/chrome/browser/printing/cloud_print/privet_notifications.cc4
-rw-r--r--chromium/chrome/browser/printing/cloud_print/privet_notifications.h2
-rw-r--r--chromium/chrome/browser/printing/cloud_print/privet_notifications_unittest.cc47
-rw-r--r--chromium/chrome/browser/printing/pdf_to_emf_converter.cc4
-rw-r--r--chromium/chrome/browser/printing/pdf_to_emf_converter_browsertest.cc3
-rw-r--r--chromium/chrome/browser/printing/print_browsertest.cc483
-rw-r--r--chromium/chrome/browser/printing/print_job.cc93
-rw-r--r--chromium/chrome/browser/printing/print_job.h13
-rw-r--r--chromium/chrome/browser/printing/print_job_manager.cc3
-rw-r--r--chromium/chrome/browser/printing/print_job_unittest.cc2
-rw-r--r--chromium/chrome/browser/printing/print_job_worker.cc82
-rw-r--r--chromium/chrome/browser/printing/print_job_worker.h16
-rw-r--r--chromium/chrome/browser/printing/print_job_worker_owner.cc4
-rw-r--r--chromium/chrome/browser/printing/print_job_worker_owner.h2
-rw-r--r--chromium/chrome/browser/printing/print_preview_data_service.cc2
-rw-r--r--chromium/chrome/browser/printing/print_preview_dialog_controller.cc2
-rw-r--r--chromium/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc4
-rw-r--r--chromium/chrome/browser/printing/print_preview_message_handler.cc34
-rw-r--r--chromium/chrome/browser/printing/print_preview_message_handler.h4
-rw-r--r--chromium/chrome/browser/printing/print_preview_pdf_generated_browsertest.cc12
-rw-r--r--chromium/chrome/browser/printing/print_view_manager.cc4
-rw-r--r--chromium/chrome/browser/printing/print_view_manager_base.cc48
-rw-r--r--chromium/chrome/browser/printing/print_view_manager_base.h3
-rw-r--r--chromium/chrome/browser/printing/print_view_manager_common.cc11
-rw-r--r--chromium/chrome/browser/printing/print_view_manager_common.h7
-rw-r--r--chromium/chrome/browser/printing/printer_manager_dialog_linux.cc30
-rw-r--r--chromium/chrome/browser/printing/printer_query.cc27
-rw-r--r--chromium/chrome/browser/printing/printer_query.h6
-rw-r--r--chromium/chrome/browser/printing/printing_init.cc2
-rw-r--r--chromium/chrome/browser/printing/printing_message_filter.cc2
-rw-r--r--chromium/chrome/browser/printing/pwg_raster_converter.cc60
-rw-r--r--chromium/chrome/browser/printing/pwg_raster_converter_browsertest.cc10
-rw-r--r--chromium/chrome/browser/profiling_host/BUILD.gn2
-rw-r--r--chromium/chrome/browser/renderer_host/pepper/DEPS2
-rw-r--r--chromium/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.cc12
-rw-r--r--chromium/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.h2
-rw-r--r--chromium/chrome/browser/resource_coordinator/BUILD.gn (renamed from chromium/chrome/browser/ui/tabs/BUILD.gn)0
-rw-r--r--chromium/chrome/browser/resources/BUILD.gn12
-rw-r--r--chromium/chrome/browser/resources/PRESUBMIT.py2
-rw-r--r--chromium/chrome/browser/resources/about_voicesearch.html38
-rw-r--r--chromium/chrome/browser/resources/about_voicesearch.js37
-rw-r--r--chromium/chrome/browser/resources/app_list/OWNERS2
-rw-r--r--chromium/chrome/browser/resources/app_list/start_page.css32
-rw-r--r--chromium/chrome/browser/resources/app_list/start_page.html23
-rw-r--r--chromium/chrome/browser/resources/app_list/start_page.js120
-rw-r--r--chromium/chrome/browser/resources/bluetooth_internals/adapter_broker.js2
-rw-r--r--chromium/chrome/browser/resources/chromeos/chromevox/BUILD.gn1
-rw-r--r--chromium/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd21
-rw-r--r--chromium/chrome/browser/resources/chromeos/genius_app/manifest.json1
-rw-r--r--chromium/chrome/browser/resources/chromeos/login/BUILD.gn30
-rw-r--r--chromium/chrome/browser/resources/chromeos/login/compiled_resources2.gyp5
-rw-r--r--chromium/chrome/browser/resources/chromeos/quick_unlock/compiled_resources2.gyp25
-rw-r--r--chromium/chrome/browser/resources/chromeos/select_to_speak/BUILD.gn6
-rw-r--r--chromium/chrome/browser/resources/chromeos/select_to_speak/compiled_resources2.gyp83
-rw-r--r--chromium/chrome/browser/resources/chromeos/wallpaper_manager/manifest.json1
-rw-r--r--chromium/chrome/browser/resources/components.js36
-rw-r--r--chromium/chrome/browser/resources/cryptotoken/enroller.js57
-rw-r--r--chromium/chrome/browser/resources/cryptotoken/gnubby-u2f.js4
-rw-r--r--chromium/chrome/browser/resources/cryptotoken/manifest.json2
-rw-r--r--chromium/chrome/browser/resources/download_internals/download_internals.html6
-rw-r--r--chromium/chrome/browser/resources/download_internals/download_internals.js4
-rw-r--r--chromium/chrome/browser/resources/download_internals/download_internals_browser_proxy.js11
-rw-r--r--chromium/chrome/browser/resources/extensions/extensions.js5
-rw-r--r--chromium/chrome/browser/resources/feedback/js/feedback.js7
-rw-r--r--chromium/chrome/browser/resources/feedback/js/take_screenshot.js3
-rw-r--r--chromium/chrome/browser/resources/gaia_auth_host/saml_handler.js2
-rw-r--r--chromium/chrome/browser/resources/identity_internals.html6
-rw-r--r--chromium/chrome/browser/resources/input_ime/ime_window_close.pngbin270 -> 154 bytes
-rw-r--r--chromium/chrome/browser/resources/input_ime/ime_window_close_click.pngbin299 -> 156 bytes
-rw-r--r--chromium/chrome/browser/resources/input_ime/ime_window_close_hover.pngbin299 -> 156 bytes
-rw-r--r--chromium/chrome/browser/resources/inspect/inspect.css8
-rw-r--r--chromium/chrome/browser/resources/inspect/inspect.js37
-rw-r--r--chromium/chrome/browser/resources/local_discovery/local_discovery.html7
-rw-r--r--chromium/chrome/browser/resources/local_discovery/local_discovery.js10
-rw-r--r--chromium/chrome/browser/resources/local_ntp/local_ntp.css3
-rw-r--r--chromium/chrome/browser/resources/local_ntp/most_visited_single.js45
-rw-r--r--chromium/chrome/browser/resources/md_bookmarks/app.html1
-rw-r--r--chromium/chrome/browser/resources/md_bookmarks/bookmarks.html5
-rw-r--r--chromium/chrome/browser/resources/md_bookmarks/command_manager.html9
-rw-r--r--chromium/chrome/browser/resources/md_bookmarks/command_manager.js49
-rw-r--r--chromium/chrome/browser/resources/md_bookmarks/edit_dialog.html7
-rw-r--r--chromium/chrome/browser/resources/md_bookmarks/folder_node.html6
-rw-r--r--chromium/chrome/browser/resources/md_bookmarks/folder_node.js2
-rw-r--r--chromium/chrome/browser/resources/md_bookmarks/list.html2
-rw-r--r--chromium/chrome/browser/resources/md_bookmarks/list.js8
-rw-r--r--chromium/chrome/browser/resources/md_bookmarks/toast_manager.html7
-rw-r--r--chromium/chrome/browser/resources/md_bookmarks/toolbar.html2
-rw-r--r--chromium/chrome/browser/resources/md_downloads/1x/incognito_marker.pngbin334 -> 174 bytes
-rw-r--r--chromium/chrome/browser/resources/md_downloads/1x/no_downloads.pngbin5921 -> 2914 bytes
-rw-r--r--chromium/chrome/browser/resources/md_downloads/2x/incognito_marker.pngbin828 -> 370 bytes
-rw-r--r--chromium/chrome/browser/resources/md_downloads/2x/no_downloads.pngbin12405 -> 5440 bytes
-rw-r--r--chromium/chrome/browser/resources/md_downloads/compiled_resources2.gyp1
-rw-r--r--chromium/chrome/browser/resources/md_downloads/downloads.html3
-rw-r--r--chromium/chrome/browser/resources/md_downloads/item.html23
-rw-r--r--chromium/chrome/browser/resources/md_downloads/manager.html3
-rw-r--r--chromium/chrome/browser/resources/md_downloads/toolbar.html10
-rw-r--r--chromium/chrome/browser/resources/md_extensions/code_section.html25
-rw-r--r--chromium/chrome/browser/resources/md_extensions/code_section.js70
-rw-r--r--chromium/chrome/browser/resources/md_extensions/compiled_resources2.gyp2
-rw-r--r--chromium/chrome/browser/resources/md_extensions/detail_view.html58
-rw-r--r--chromium/chrome/browser/resources/md_extensions/detail_view.js10
-rw-r--r--chromium/chrome/browser/resources/md_extensions/error_page.html22
-rw-r--r--chromium/chrome/browser/resources/md_extensions/extensions.html3
-rw-r--r--chromium/chrome/browser/resources/md_extensions/icons.html2
-rw-r--r--chromium/chrome/browser/resources/md_extensions/install_warnings_dialog.html2
-rw-r--r--chromium/chrome/browser/resources/md_extensions/item.html50
-rw-r--r--chromium/chrome/browser/resources/md_extensions/item.js7
-rw-r--r--chromium/chrome/browser/resources/md_extensions/item_list.html4
-rw-r--r--chromium/chrome/browser/resources/md_extensions/item_util.js1
-rw-r--r--chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.html8
-rw-r--r--chromium/chrome/browser/resources/md_extensions/kiosk_dialog.html14
-rw-r--r--chromium/chrome/browser/resources/md_extensions/load_error.html4
-rw-r--r--chromium/chrome/browser/resources/md_extensions/options_dialog.html3
-rw-r--r--chromium/chrome/browser/resources/md_extensions/options_dialog.js45
-rw-r--r--chromium/chrome/browser/resources/md_extensions/pack_dialog.html8
-rw-r--r--chromium/chrome/browser/resources/md_extensions/pack_dialog_alert.html4
-rw-r--r--chromium/chrome/browser/resources/md_extensions/shortcut_input.html2
-rw-r--r--chromium/chrome/browser/resources/md_extensions/sidebar.html6
-rw-r--r--chromium/chrome/browser/resources/md_extensions/toolbar.html33
-rw-r--r--chromium/chrome/browser/resources/md_extensions/toolbar.js30
-rw-r--r--chromium/chrome/browser/resources/md_history/app.html2
-rw-r--r--chromium/chrome/browser/resources/md_history/app.js12
-rw-r--r--chromium/chrome/browser/resources/md_history/history.html11
-rw-r--r--chromium/chrome/browser/resources/md_history/history_item.html2
-rw-r--r--chromium/chrome/browser/resources/md_history/history_item.js6
-rw-r--r--chromium/chrome/browser/resources/md_history/history_list.html17
-rw-r--r--chromium/chrome/browser/resources/md_history/history_list.js23
-rw-r--r--chromium/chrome/browser/resources/md_history/side_bar.html2
-rw-r--r--chromium/chrome/browser/resources/md_history/synced_device_card.html4
-rw-r--r--chromium/chrome/browser/resources/md_history/synced_device_card.js3
-rw-r--r--chromium/chrome/browser/resources/md_history/synced_device_manager.html13
-rw-r--r--chromium/chrome/browser/resources/md_user_manager/shared_styles.html2
-rw-r--r--chromium/chrome/browser/resources/md_user_manager/user_manager.html2
-rw-r--r--chromium/chrome/browser/resources/md_user_manager/user_manager_pages.html1
-rw-r--r--chromium/chrome/browser/resources/media/media_engagement.html21
-rw-r--r--chromium/chrome/browser/resources/media/media_engagement.js36
-rw-r--r--chromium/chrome/browser/resources/media/mei_preload/preloaded_data.pbbin14 -> 7857 bytes
-rw-r--r--chromium/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.css2
-rw-r--r--chromium/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.html15
-rw-r--r--chromium/chrome/browser/resources/media_router/elements/route_details/route_details.css4
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/BUILD.gn78
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/assemble_extension.py41
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/concat_js_modules.py87
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/manifest.yaml53
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/prelude.js50
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/background.js9
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/config.js26
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/event_listener.js188
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/event_listener_test.js147
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/extension_selector.js47
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/external_message_listener.js80
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/external_message_listener_test.js67
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/externs.js917
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/files.gni145
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/init.js157
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/init_helper.js61
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/init_test.js82
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/interface_data/issue.js150
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/interface_data/media_route_controller.js166
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/interface_data/media_route_controller_test.js70
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/interface_data/mojo.js415
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/interface_data/route.js178
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/interface_data/route_message.js48
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/interface_data/route_request_error.js89
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/interface_data/sink.js79
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/interface_data/sink_list.js34
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/interface_data/sink_search_criteria.js30
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/internal_message.js62
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/internal_message_listener.js61
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/internal_message_listener_test.js52
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/log_manager.js241
-rwxr-xr-xchromium/chrome/browser/resources/media_router/extension/src/manager/cancellable_promise.js255
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/manager/mr_event_senders/route_message_sender.js344
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/manager/mr_event_senders/route_message_sender_test.js173
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/manager/mr_event_senders/throttling_sender.js89
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/manager/mr_event_senders/throttling_sender_test.js69
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/manager/presentation_enums.js35
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/manager/provider.js258
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/manager/provider_events.js46
-rwxr-xr-xchromium/chrome/browser/resources/media_router/extension/src/manager/provider_manager.js1280
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/manager/provider_manager_callbacks.js223
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/manager/provider_manager_test.js1076
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/manager/route_id.js114
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/manager/route_id_test.js41
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/manager/route_message_port.js74
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/manager/route_message_port_impl.js107
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/manager/sink_availability.js21
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_activity.js150
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_activity_test.js93
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_analytics.js74
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_analytics_test.js111
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_config.js31
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_service.js522
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_service_loader.js47
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_service_name.js21
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_session.js179
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_session_test.js118
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_settings.js400
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_settings_test.js123
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/mirror_services/stream_capture/capture_parameters.js208
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/mirror_services/stream_capture/mirror_media_stream.js340
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/mirror_services/stream_capture/mirror_media_stream_test.js407
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/module.js247
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/module_test.js90
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/mojo_externs.js522
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/persistent_data.js463
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/persistent_data_test.js361
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/presentation.js191
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/presentation_services/cloud_webrtc/webrtc_presentation_session.js192
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/presentation_services/presentation_session.js31
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/common/id_generator.js80
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/common/id_generator_test.js36
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/common/net_utils.js98
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/common/net_utils_test.js61
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/common/retry.js198
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/common/runtime_error_utils.js38
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/common/sink_utils.js187
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/common/xhr_utils.js72
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_activity.js22
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_activity_records.js156
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_activity_records_test.js100
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_analytics.js119
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_analytics_test.js88
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_app_discovery_service.js480
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_app_discovery_service_test.js362
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_client.js325
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_client_test.js284
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_provider.js474
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_provider_callbacks.js65
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_provider_test.js256
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_sink.js309
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_sink_discovery_service.js334
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_sink_discovery_service_test.js163
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_sink_test.js127
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/presentation_url.js146
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/presentation_url_test.js75
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/providers/dial/sink_app_status.js23
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/analytics.js305
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/analytics_test.js246
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/assertions.js95
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/base64.js46
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/base64_test.js72
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/device_counts.js18
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/device_counts_provider.js23
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/event_analytics.js57
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/event_analytics_test.js27
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/event_target.js69
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/fixed_size_queue.js126
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/fixed_size_queue_test.js74
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/logger.js261
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/logger_test.js166
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/media_source_utils.js127
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/media_source_utils_test.js66
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/mock_clock.js391
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/mock_promise.js602
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/mock_promise_test.js850
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/mojo_utils.js63
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/object_utils.js31
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/object_utils_test.js33
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/platform_utils.js62
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/promise_resolver.js54
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/promise_utils.js36
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/sha1.js239
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/sha1_test.js60
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/string_utils.js24
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/tab_utils.js28
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/throttle.js93
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/throttle_test.js100
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/unit_test_utils.js307
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/xhr_manager.js191
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/utils/xhr_manager_test.js185
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/webrtc/messages.js200
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/webrtc/peer_connection.js570
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/webrtc/peer_connection_analytics.js58
-rw-r--r--chromium/chrome/browser/resources/media_router/extension/src/webrtc/peer_connection_test.js215
-rw-r--r--chromium/chrome/browser/resources/media_router/media_router_ui_interface.js15
-rw-r--r--chromium/chrome/browser/resources/net_internals/http_cache_view.html4
-rw-r--r--chromium/chrome/browser/resources/ntp4/new_tab.js3
-rw-r--r--chromium/chrome/browser/resources/offline_pages/offline_internals.html8
-rw-r--r--chromium/chrome/browser/resources/offline_pages/offline_internals.js25
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.html4
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html17
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html8
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html2
-rw-r--r--chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.html2
-rw-r--r--chromium/chrome/browser/resources/pdf/open_pdf_params_parser.js100
-rw-r--r--chromium/chrome/browser/resources/pdf/pdf.js58
-rw-r--r--chromium/chrome/browser/resources/plugin_metadata/plugins_linux.json6
-rw-r--r--chromium/chrome/browser/resources/plugin_metadata/plugins_mac.json6
-rw-r--r--chromium/chrome/browser/resources/plugin_metadata/plugins_win.json6
-rw-r--r--chromium/chrome/browser/resources/policy.css5
-rw-r--r--chromium/chrome/browser/resources/policy.html4
-rw-r--r--chromium/chrome/browser/resources/predictors/resource_prefetch_predictor.html49
-rw-r--r--chromium/chrome/browser/resources/predictors/resource_prefetch_predictor.js49
-rw-r--r--chromium/chrome/browser/resources/print_preview/OWNERS4
-rw-r--r--chromium/chrome/browser/resources/print_preview/cloud_print_interface.html9
-rw-r--r--chromium/chrome/browser/resources/print_preview/data/cloud_parsers.html5
-rw-r--r--chromium/chrome/browser/resources/print_preview/data/destination.js43
-rw-r--r--chromium/chrome/browser/resources/print_preview/data/destination_store.html3
-rw-r--r--chromium/chrome/browser/resources/print_preview/data/invitation.html4
-rw-r--r--chromium/chrome/browser/resources/print_preview/images/2x/printer.pngbin1935 -> 706 bytes
-rw-r--r--chromium/chrome/browser/resources/print_preview/images/2x/printer_shared.pngbin2603 -> 1529 bytes
-rw-r--r--chromium/chrome/browser/resources/print_preview/images/third_party.pngbin284 -> 0 bytes
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/advanced_options_settings.html13
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/advanced_options_settings.js19
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/advanced_settings_dialog.html47
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/advanced_settings_dialog.js82
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/advanced_settings_item.html68
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/advanced_settings_item.js154
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/app.html40
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/app.js255
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/button_css.html4
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/checkbox_radio_css.html4
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/color_settings.html3
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/color_settings.js4
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/compiled_resources2.gyp80
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/copies_settings.html7
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/copies_settings.js12
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/destination_dialog.html132
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/destination_dialog.js205
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/destination_list.html102
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/destination_list.js138
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/destination_list_item.html136
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/destination_list_item.js58
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/destination_settings.html24
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/destination_settings.js69
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/dpi_settings.html2
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/dpi_settings.js2
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/header.html15
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/header.js134
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/highlight_utils.html3
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/highlight_utils.js60
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/layout_settings.html3
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/layout_settings.js4
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/margins_settings.html3
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/margins_settings.js4
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/media_size_settings.html2
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/media_size_settings.js2
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/model.html1
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/model.js289
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/number_settings_section.html11
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/number_settings_section.js53
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/other_options_settings.html6
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/other_options_settings.js4
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/pages_settings.html6
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/pages_settings.js87
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/preview_area.html45
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/preview_area.js180
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/print_preview_search_box.html41
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/print_preview_search_box.js42
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/scaling_settings.html6
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/scaling_settings.js32
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/search_dialog_css.html48
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/select_css.html4
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/settings_behavior.js2
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/settings_select.html5
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/settings_select.js22
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/state.html5
-rw-r--r--chromium/chrome/browser/resources/print_preview/new/state.js76
-rw-r--r--chromium/chrome/browser/resources/print_preview/preview_generator.js10
-rw-r--r--chromium/chrome/browser/resources/print_preview/previewarea/preview_area.css1
-rw-r--r--chromium/chrome/browser/resources/print_preview/previewarea/preview_area.js42
-rw-r--r--chromium/chrome/browser/resources/print_preview/print_preview.js5
-rw-r--r--chromium/chrome/browser/resources/print_preview/print_preview_new.html11
-rw-r--r--chromium/chrome/browser/resources/print_preview/print_preview_resources.grd72
-rw-r--r--chromium/chrome/browser/resources/print_preview/print_preview_utils.js2
-rw-r--r--chromium/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs11
-rw-r--r--chromium/chrome/browser/resources/print_preview/search/destination_list_item.js22
-rw-r--r--chromium/chrome/browser/resources/print_preview/search/destination_search.css4
-rw-r--r--chromium/chrome/browser/resources/print_preview/search/destination_search.html3
-rw-r--r--chromium/chrome/browser/resources/print_preview/search/destination_search.js2
-rw-r--r--chromium/chrome/browser/resources/print_preview/settings/advanced_settings/advanced_settings_item.js18
-rw-r--r--chromium/chrome/browser/resources/print_preview/settings/destination_settings.js16
-rw-r--r--chromium/chrome/browser/resources/safe_browsing/download_file_types.asciipb4
-rw-r--r--chromium/chrome/browser/resources/settings/PRESUBMIT.py24
-rw-r--r--chromium/chrome/browser/resources/settings/a11y_page/a11y_page.html2
-rw-r--r--chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html80
-rw-r--r--chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.js33
-rw-r--r--chromium/chrome/browser/resources/settings/about_page/about_page.html25
-rw-r--r--chromium/chrome/browser/resources/settings/about_page/channel_switcher_dialog.html6
-rw-r--r--chromium/chrome/browser/resources/settings/about_page/detailed_build_info.html2
-rw-r--r--chromium/chrome/browser/resources/settings/about_page/update_warning_dialog.html4
-rw-r--r--chromium/chrome/browser/resources/settings/android_apps_page/android_apps_page.html4
-rw-r--r--chromium/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.html6
-rw-r--r--chromium/chrome/browser/resources/settings/android_apps_page/android_settings_element.html2
-rw-r--r--chromium/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html2
-rw-r--r--chromium/chrome/browser/resources/settings/appearance_page/appearance_page.html12
-rw-r--r--chromium/chrome/browser/resources/settings/basic_page/basic_page.html4
-rw-r--r--chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_device_list_item.html7
-rw-r--r--chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html6
-rw-r--r--chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html8
-rw-r--r--chromium/chrome/browser/resources/settings/change_password_page/change_password_page.html2
-rw-r--r--chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html8
-rw-r--r--chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.js4
-rw-r--r--chromium/chrome/browser/resources/settings/chrome_cleanup_page/items_to_remove_list.html22
-rw-r--r--chromium/chrome/browser/resources/settings/chrome_cleanup_page/items_to_remove_list.js44
-rw-r--r--chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html334
-rw-r--r--chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.js156
-rw-r--r--chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.html311
-rw-r--r--chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.js367
-rw-r--r--chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/compiled_resources2.gyp1
-rw-r--r--chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/history_deletion_dialog.html2
-rw-r--r--chromium/chrome/browser/resources/settings/compiled_resources2.gyp2
-rw-r--r--chromium/chrome/browser/resources/settings/controls/controlled_button.html2
-rw-r--r--chromium/chrome/browser/resources/settings/controls/controlled_button.js2
-rw-r--r--chromium/chrome/browser/resources/settings/controls/controlled_radio_button.html23
-rw-r--r--chromium/chrome/browser/resources/settings/controls/controlled_radio_button.js9
-rw-r--r--chromium/chrome/browser/resources/settings/controls/extension_controlled_indicator.html4
-rw-r--r--chromium/chrome/browser/resources/settings/controls/important_site_checkbox.html4
-rw-r--r--chromium/chrome/browser/resources/settings/controls/settings_checkbox.html4
-rw-r--r--chromium/chrome/browser/resources/settings/controls/settings_checkbox.js2
-rw-r--r--chromium/chrome/browser/resources/settings/controls/settings_toggle_button.html5
-rw-r--r--chromium/chrome/browser/resources/settings/date_time_page/date_time_page.html4
-rw-r--r--chromium/chrome/browser/resources/settings/default_browser_page/default_browser_page.html2
-rw-r--r--chromium/chrome/browser/resources/settings/device_page/device_page.html12
-rw-r--r--chromium/chrome/browser/resources/settings/device_page/device_page_browser_proxy.js2
-rw-r--r--chromium/chrome/browser/resources/settings/device_page/display.html6
-rw-r--r--chromium/chrome/browser/resources/settings/device_page/display.js31
-rw-r--r--chromium/chrome/browser/resources/settings/device_page/display_layout.html12
-rw-r--r--chromium/chrome/browser/resources/settings/device_page/display_layout.js11
-rw-r--r--chromium/chrome/browser/resources/settings/device_page/display_overscan_dialog.html4
-rw-r--r--chromium/chrome/browser/resources/settings/device_page/drive_cache_dialog.html4
-rw-r--r--chromium/chrome/browser/resources/settings/device_page/keyboard.html4
-rw-r--r--chromium/chrome/browser/resources/settings/device_page/pointers.html2
-rw-r--r--chromium/chrome/browser/resources/settings/device_page/storage.html10
-rw-r--r--chromium/chrome/browser/resources/settings/device_page/stylus.html6
-rw-r--r--chromium/chrome/browser/resources/settings/downloads_page/downloads_page.html4
-rw-r--r--chromium/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html2
-rw-r--r--chromium/chrome/browser/resources/settings/icons.html5
-rw-r--r--chromium/chrome/browser/resources/settings/images/sync_banner.svg10
-rw-r--r--chromium/chrome/browser/resources/settings/incompatible_applications_page/compiled_resources2.gyp33
-rw-r--r--chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_application_item.html25
-rw-r--r--chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_application_item.js103
-rw-r--r--chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_applications_browser_proxy.html2
-rw-r--r--chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_applications_browser_proxy.js122
-rw-r--r--chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_applications_page.html59
-rw-r--r--chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_applications_page.js138
-rw-r--r--chromium/chrome/browser/resources/settings/internet_page/internet_config.html12
-rw-r--r--chromium/chrome/browser/resources/settings/internet_page/internet_config.js34
-rw-r--r--chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.html18
-rw-r--r--chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.js1
-rw-r--r--chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html20
-rw-r--r--chromium/chrome/browser/resources/settings/internet_page/internet_page.html10
-rw-r--r--chromium/chrome/browser/resources/settings/internet_page/internet_page.js25
-rw-r--r--chromium/chrome/browser/resources/settings/internet_page/internet_subpage.html19
-rw-r--r--chromium/chrome/browser/resources/settings/internet_page/internet_subpage.js2
-rw-r--r--chromium/chrome/browser/resources/settings/internet_page/network_proxy_section.html4
-rw-r--r--chromium/chrome/browser/resources/settings/internet_page/network_summary_item.html8
-rw-r--r--chromium/chrome/browser/resources/settings/internet_page/network_summary_item.js20
-rw-r--r--chromium/chrome/browser/resources/settings/internet_page/tether_connection_dialog.html4
-rw-r--r--chromium/chrome/browser/resources/settings/languages_page/add_languages_dialog.html4
-rw-r--r--chromium/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html4
-rw-r--r--chromium/chrome/browser/resources/settings/languages_page/languages_page.html47
-rw-r--r--chromium/chrome/browser/resources/settings/languages_page/languages_page.js4
-rw-r--r--chromium/chrome/browser/resources/settings/on_startup_page/on_startup_page.html6
-rw-r--r--chromium/chrome/browser/resources/settings/on_startup_page/on_startup_page.js24
-rw-r--r--chromium/chrome/browser/resources/settings/on_startup_page/startup_url_dialog.html4
-rw-r--r--chromium/chrome/browser/resources/settings/on_startup_page/startup_url_entry.html7
-rw-r--r--chromium/chrome/browser/resources/settings/on_startup_page/startup_url_entry.js2
-rw-r--r--chromium/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html8
-rw-r--r--chromium/chrome/browser/resources/settings/passwords_and_forms_page/address_edit_dialog.html6
-rw-r--r--chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.html36
-rw-r--r--chromium/chrome/browser/resources/settings/passwords_and_forms_page/credit_card_edit_dialog.html4
-rw-r--r--chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_edit_dialog.html8
-rw-r--r--chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_list_item.html6
-rw-r--r--chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.html4
-rw-r--r--chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.html51
-rw-r--r--chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.js199
-rw-r--r--chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html40
-rw-r--r--chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js77
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/change_picture.html2
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/change_picture.js8
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/change_picture_browser_proxy.js5
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/compiled_resources2.gyp14
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/easy_unlock_turn_off_dialog.html5
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/fingerprint_list.html6
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/import_data_dialog.html6
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/lock_screen.html10
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/password_prompt_dialog.html4
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/people_page.html153
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/people_page.js68
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/pin_keyboard.html1
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/pin_keyboard.js5
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/profile_info_browser_proxy.js11
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.html4
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.js2
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/setup_pin_dialog.html13
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/sync_account_control.html200
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/sync_account_control.js222
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/sync_browser_proxy.js63
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/sync_page.html8
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/user_list.html2
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/users_add_user_dialog.html4
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/users_page.html9
-rw-r--r--chromium/chrome/browser/resources/settings/people_page/users_page.js9
-rw-r--r--chromium/chrome/browser/resources/settings/prefs/pref_util.js2
-rw-r--r--chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html40
-rw-r--r--chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js7
-rw-r--r--chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html27
-rw-r--r--chromium/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html8
-rw-r--r--chromium/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html2
-rw-r--r--chromium/chrome/browser/resources/settings/printing_page/cups_printers.html15
-rw-r--r--chromium/chrome/browser/resources/settings/printing_page/cups_printers.js14
-rw-r--r--chromium/chrome/browser/resources/settings/printing_page/cups_printers_list.html6
-rw-r--r--chromium/chrome/browser/resources/settings/printing_page/cups_set_manufacturer_model_behavior.js16
-rw-r--r--chromium/chrome/browser/resources/settings/printing_page/printing_page.html6
-rw-r--r--chromium/chrome/browser/resources/settings/privacy_page/privacy_page.html43
-rw-r--r--chromium/chrome/browser/resources/settings/privacy_page/privacy_page.js32
-rw-r--r--chromium/chrome/browser/resources/settings/privacy_page/privacy_page_browser_proxy.js5
-rw-r--r--chromium/chrome/browser/resources/settings/reset_page/powerwash_dialog.html4
-rw-r--r--chromium/chrome/browser/resources/settings/reset_page/reset_page.html39
-rw-r--r--chromium/chrome/browser/resources/settings/reset_page/reset_page.js14
-rw-r--r--chromium/chrome/browser/resources/settings/reset_page/reset_profile_banner.html4
-rw-r--r--chromium/chrome/browser/resources/settings/reset_page/reset_profile_dialog.html4
-rw-r--r--chromium/chrome/browser/resources/settings/reset_page/reset_profile_dialog.js2
-rw-r--r--chromium/chrome/browser/resources/settings/route.js7
-rw-r--r--chromium/chrome/browser/resources/settings/search_engines_page/omnibox_extension_entry.html8
-rw-r--r--chromium/chrome/browser/resources/settings/search_engines_page/search_engine_dialog.html4
-rw-r--r--chromium/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html8
-rw-r--r--chromium/chrome/browser/resources/settings/search_engines_page/search_engines_list.html2
-rw-r--r--chromium/chrome/browser/resources/settings/search_engines_page/search_engines_page.html4
-rw-r--r--chromium/chrome/browser/resources/settings/search_page/search_page.html6
-rw-r--r--chromium/chrome/browser/resources/settings/search_settings.js117
-rw-r--r--chromium/chrome/browser/resources/settings/settings.html3
-rw-r--r--chromium/chrome/browser/resources/settings/settings_main/settings_main.html1
-rw-r--r--chromium/chrome/browser/resources/settings/settings_page/settings_animated_pages.html1
-rw-r--r--chromium/chrome/browser/resources/settings/settings_page/settings_section.html6
-rw-r--r--chromium/chrome/browser/resources/settings/settings_page/settings_subpage.html8
-rw-r--r--chromium/chrome/browser/resources/settings/settings_page/settings_subpage_search.html8
-rw-r--r--chromium/chrome/browser/resources/settings/settings_resources.grd43
-rw-r--r--chromium/chrome/browser/resources/settings/settings_shared_css.html56
-rw-r--r--chromium/chrome/browser/resources/settings/settings_ui/settings_ui.html4
-rw-r--r--chromium/chrome/browser/resources/settings/settings_ui/settings_ui.js2
-rw-r--r--chromium/chrome/browser/resources/settings/settings_vars_css.html6
-rw-r--r--chromium/chrome/browser/resources/settings/site_settings/add_site_dialog.html4
-rw-r--r--chromium/chrome/browser/resources/settings/site_settings/all_sites.html2
-rw-r--r--chromium/chrome/browser/resources/settings/site_settings/category_default_setting.js1
-rw-r--r--chromium/chrome/browser/resources/settings/site_settings/constants.js3
-rw-r--r--chromium/chrome/browser/resources/settings/site_settings/edit_exception_dialog.html4
-rw-r--r--chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.html35
-rw-r--r--chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.js78
-rw-r--r--chromium/chrome/browser/resources/settings/site_settings/site_data.html12
-rw-r--r--chromium/chrome/browser/resources/settings/site_settings/site_data_details_subpage.html2
-rw-r--r--chromium/chrome/browser/resources/settings/site_settings/site_details.html27
-rw-r--r--chromium/chrome/browser/resources/settings/site_settings/site_details.js11
-rw-r--r--chromium/chrome/browser/resources/settings/site_settings/site_list.html30
-rw-r--r--chromium/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js16
-rw-r--r--chromium/chrome/browser/resources/settings/site_settings/usb_devices.html5
-rw-r--r--chromium/chrome/browser/resources/settings/site_settings/zoom_levels.html2
-rw-r--r--chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.html103
-rw-r--r--chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.js10
-rw-r--r--chromium/chrome/browser/resources/settings/system_page/system_page.html2
-rw-r--r--chromium/chrome/browser/resources/signin/dice_sync_confirmation/images/ic_google.pngbin617 -> 481 bytes
-rw-r--r--chromium/chrome/browser/resources/signin/dice_sync_confirmation/images/ic_google_2x.pngbin1221 -> 857 bytes
-rw-r--r--chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.html31
-rw-r--r--chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.js37
-rw-r--r--chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_browser_proxy.js30
-rw-r--r--chromium/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html33
-rw-r--r--chromium/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.js33
-rw-r--r--chromium/chrome/browser/resources/snippets_internals.html48
-rw-r--r--chromium/chrome/browser/resources/vr/assets/PRESUBMIT.py30
-rw-r--r--chromium/chrome/browser/resources/vr/assets/VERSION2
-rw-r--r--chromium/chrome/browser/resources/vr/assets/chromium/background.pngbin0 -> 90 bytes
-rw-r--r--chromium/chrome/browser/resources/vr/assets/chromium/fullscreen_gradient.pngbin0 -> 127 bytes
-rw-r--r--chromium/chrome/browser/resources/vr/assets/chromium/incognito_gradient.pngbin0 -> 127 bytes
-rw-r--r--chromium/chrome/browser/resources/vr/assets/chromium/normal_gradient.pngbin0 -> 90 bytes
-rw-r--r--chromium/chrome/browser/resources/vr/assets/fullscreen_gradient.png.sha11
-rw-r--r--chromium/chrome/browser/resources/vr/assets/google_chrome/background.png.sha1 (renamed from chromium/chrome/browser/resources/vr/assets/background.png.sha1)0
-rw-r--r--chromium/chrome/browser/resources/vr/assets/google_chrome/fullscreen_gradient.png.sha11
-rw-r--r--chromium/chrome/browser/resources/vr/assets/google_chrome/incognito_gradient.png.sha11
-rw-r--r--chromium/chrome/browser/resources/vr/assets/google_chrome/normal_gradient.png.sha11
-rw-r--r--chromium/chrome/browser/resources/vr/assets/incognito_gradient.png.sha11
-rw-r--r--chromium/chrome/browser/resources/vr/assets/normal_gradient.png.sha11
-rwxr-xr-xchromium/chrome/browser/resources/vr/assets/push_assets_component.py8
-rw-r--r--chromium/chrome/browser/resources/vr/assets/vr_assets_component_files.json8
-rw-r--r--chromium/chrome/browser/resources/vr_shell/OWNERS3
-rw-r--r--chromium/chrome/browser/resources/vr_shell/ddcontroller.glbbin10900 -> 0 bytes
-rw-r--r--chromium/chrome/browser/resources/vr_shell/tex/ddcontroller_app.pngbin2420 -> 0 bytes
-rw-r--r--chromium/chrome/browser/resources/vr_shell/tex/ddcontroller_idle.pngbin14780 -> 0 bytes
-rw-r--r--chromium/chrome/browser/resources/vr_shell/tex/ddcontroller_system.pngbin1990 -> 0 bytes
-rw-r--r--chromium/chrome/browser/resources/vr_shell/tex/ddcontroller_touchpad.pngbin8941 -> 0 bytes
-rw-r--r--chromium/chrome/browser/resources/vr_shell_resources.grd19
-rw-r--r--chromium/chrome/browser/safe_browsing/BUILD.gn12
-rw-r--r--chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl.cc12
-rw-r--r--chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl.h42
-rw-r--r--chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl_mac.cc (renamed from chromium/chrome/browser/spellchecker/spellcheck_message_filter_platform_mac.cc)215
-rw-r--r--chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl_mac_browsertest.cc67
-rw-r--r--chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl_mac_unittest.cc46
-rw-r--r--chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl_unittest.cc2
-rw-r--r--chromium/chrome/browser/spellchecker/spell_check_panel_host_impl.cc2
-rw-r--r--chromium/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc15
-rw-r--r--chromium/chrome/browser/spellchecker/spellcheck_custom_dictionary_unittest.cc116
-rw-r--r--chromium/chrome/browser/spellchecker/spellcheck_factory.cc4
-rw-r--r--chromium/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc35
-rw-r--r--chromium/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h11
-rw-r--r--chromium/chrome/browser/spellchecker/spellcheck_language_policy_handler.cc15
-rw-r--r--chromium/chrome/browser/spellchecker/spellcheck_message_filter_platform_mac_browsertest.cc72
-rw-r--r--chromium/chrome/browser/spellchecker/spellcheck_message_filter_platform_mac_unittest.cc68
-rw-r--r--chromium/chrome/browser/spellchecker/spellcheck_service.cc20
-rw-r--r--chromium/chrome/browser/spellchecker/spellcheck_service_browsertest.cc14
-rw-r--r--chromium/chrome/browser/ui/BUILD.gn1345
-rw-r--r--chromium/chrome/browser/ui/libgtkui/BUILD.gn2
-rw-r--r--chromium/chrome/browser/ui/webui/OWNERS2
-rw-r--r--chromium/chrome/browser/ui/webui/about_ui.cc22
-rw-r--r--chromium/chrome/browser/ui/webui/app_list/OWNERS2
-rw-r--r--chromium/chrome/browser/ui/webui/app_list/start_page_browsertest.js161
-rw-r--r--chromium/chrome/browser/ui/webui/app_list/start_page_handler.cc112
-rw-r--r--chromium/chrome/browser/ui/webui/app_list/start_page_handler.h42
-rw-r--r--chromium/chrome/browser/ui/webui/app_list/start_page_ui.cc48
-rw-r--r--chromium/chrome/browser/ui/webui/app_list/start_page_ui.h28
-rw-r--r--chromium/chrome/browser/ui/webui/bluetooth_internals/BUILD.gn2
-rw-r--r--chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals.mojom2
-rw-r--r--chromium/chrome/browser/ui/webui/browsing_history_handler.cc13
-rw-r--r--chromium/chrome/browser/ui/webui/browsing_history_handler.h9
-rw-r--r--chromium/chrome/browser/ui/webui/browsing_history_handler_unittest.cc15
-rw-r--r--chromium/chrome/browser/ui/webui/certificate_manager_localized_strings_provider.cc1
-rw-r--r--chromium/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc31
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/DEPS2
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/image_source.cc1
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc17
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/active_directory_password_change_screen_handler.cc15
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/active_directory_password_change_screen_handler.h10
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc68
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h12
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc70
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h1
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc16
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.h8
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler_unittest.cc23
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc43
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h2
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc123
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h22
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.cc15
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/network_state_informer.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_unittest.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc41
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.h1
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc44
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h31
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/signin_userlist_unittest.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/sync_consent_screen_handler.cc39
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/sync_consent_screen_handler.h6
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/components_ui.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/devtools_ui.cc143
-rw-r--r--chromium/chrome/browser/ui/webui/discards/discards_ui.cc11
-rw-r--r--chromium/chrome/browser/ui/webui/discards/discards_ui.h2
-rw-r--r--chromium/chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.cc61
-rw-r--r--chromium/chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.h5
-rw-r--r--chromium/chrome/browser/ui/webui/engagement/site_engagement_ui.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_browsertest.js4
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js4
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extension_settings_handler.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extensions_ui.cc8
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/install_extension_handler.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/flags_ui.cc21
-rw-r--r--chromium/chrome/browser/ui/webui/foreign_session_handler.cc28
-rw-r--r--chromium/chrome/browser/ui/webui/help/version_updater_chromeos.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/help/version_updater_mac.mm2
-rw-r--r--chromium/chrome/browser/ui/webui/inspect_ui.cc24
-rw-r--r--chromium/chrome/browser/ui/webui/inspect_ui.h2
-rw-r--r--chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/interventions_internals/BUILD.gn2
-rw-r--r--chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals.mojom2
-rw-r--r--chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler.cc26
-rw-r--r--chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler_unittest.cc36
-rw-r--r--chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals_ui.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui.cc7
-rw-r--r--chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc12
-rw-r--r--chromium/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker.cc58
-rw-r--r--chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker.h26
-rw-r--r--chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker_unittest.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.cc43
-rw-r--r--chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.h15
-rw-r--r--chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler_unittest.cc16
-rw-r--r--chromium/chrome/browser/ui/webui/md_downloads/md_downloads_ui.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/media/media_engagement_ui.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/media/webrtc_logs_ui.cc9
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_file_dialog.cc1
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_ui.cc92
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_ui.h36
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_ui_service_factory_unittest.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc137
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_web_ui_test.cc16
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h4
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/web_contents_display_observer.h38
-rw-r--r--chromium/chrome/browser/ui/webui/memory_internals_ui.cc18
-rw-r--r--chromium/chrome/browser/ui/webui/nacl_ui.cc1
-rw-r--r--chromium/chrome/browser/ui/webui/net_internals/net_internals_ui.cc20
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.cc12
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc90
-rw-r--r--chromium/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc1
-rw-r--r--chromium/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc27
-rw-r--r--chromium/chrome/browser/ui/webui/policy_ui.cc1
-rw-r--r--chromium/chrome/browser/ui/webui/predictors/predictors_handler.cc69
-rw-r--r--chromium/chrome/browser/ui/webui/predictors/predictors_handler.h3
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc12
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc33
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler.h6
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler_win_unittest.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_handler.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_handler.h2
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc22
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_ui.cc8
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/printer_capabilities.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/printer_handler.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/printer_handler.h2
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/privet_printer_handler.cc20
-rw-r--r--chromium/chrome/browser/ui/webui/profile_helper.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/profile_helper.h5
-rw-r--r--chromium/chrome/browser/ui/webui/profile_helper_browsertest.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.h2
-rw-r--r--chromium/chrome/browser/ui/webui/quota_internals/quota_internals_types.h2
-rw-r--r--chromium/chrome/browser/ui/webui/sandbox_internals_ui.cc13
-rw-r--r--chromium/chrome/browser/ui/webui/settings/about_handler.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/settings/appearance_handler.cc55
-rw-r--r--chromium/chrome/browser/ui/webui/settings/appearance_handler.h8
-rw-r--r--chromium/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/change_password_handler.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc149
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h7
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc15
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h4
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.h2
-rw-r--r--chromium/chrome/browser/ui/webui/settings/downloads_handler_unittest.cc21
-rw-r--r--chromium/chrome/browser/ui/webui/settings/incompatible_applications_handler_win.cc168
-rw-r--r--chromium/chrome/browser/ui/webui/settings/incompatible_applications_handler_win.h58
-rw-r--r--chromium/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc167
-rw-r--r--chromium/chrome/browser/ui/webui/settings/md_settings_ui.cc23
-rw-r--r--chromium/chrome/browser/ui/webui/settings/metrics_reporting_handler.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/settings/people_handler.cc108
-rw-r--r--chromium/chrome/browser/ui/webui/settings/people_handler.h26
-rw-r--r--chromium/chrome/browser/ui/webui/settings/people_handler_unittest.cc10
-rw-r--r--chromium/chrome/browser/ui/webui/settings/profile_info_handler.cc41
-rw-r--r--chromium/chrome/browser/ui/webui/settings/profile_info_handler.h15
-rw-r--r--chromium/chrome/browser/ui/webui/settings/profile_info_handler_unittest.cc49
-rw-r--r--chromium/chrome/browser/ui/webui/settings/protocol_handlers_handler.cc52
-rw-r--r--chromium/chrome/browser/ui/webui/settings/protocol_handlers_handler.h12
-rw-r--r--chromium/chrome/browser/ui/webui/settings/safe_browsing_handler.cc13
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc82
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h9
-rw-r--r--chromium/chrome/browser/ui/webui/settings/site_settings_handler.cc45
-rw-r--r--chromium/chrome/browser/ui/webui/settings/site_settings_handler.h15
-rw-r--r--chromium/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc94
-rw-r--r--chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.cc241
-rw-r--r--chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h108
-rw-r--r--chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_delegate_impl.cc183
-rw-r--r--chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_delegate_impl.h81
-rw-r--r--chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_unittest.cc715
-rw-r--r--chromium/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/signin/inline_login_ui_browsertest.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/signin/login_ui_service.cc5
-rw-r--r--chromium/chrome/browser/ui/webui/signin/md_user_manager_ui.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/signin/md_user_manager_ui.h2
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler.h2
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc10
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_dice_internals_handler.cc85
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_dice_internals_handler.h40
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_error_ui.cc24
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_supervised_user_import_handler_unittest.cc9
-rw-r--r--chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc42
-rw-r--r--chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.h22
-rw-r--r--chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc132
-rw-r--r--chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc108
-rw-r--r--chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.h19
-rw-r--r--chromium/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc49
-rw-r--r--chromium/chrome/browser/ui/webui/signin_internals_ui.cc9
-rw-r--r--chromium/chrome/browser/ui/webui/site_settings_helper.cc6
-rw-r--r--chromium/chrome/browser/ui/webui/snippets_internals_message_handler.cc22
-rw-r--r--chromium/chrome/browser/ui/webui/theme_source.cc1
-rw-r--r--chromium/chrome/browser/ui/webui/usb_internals/BUILD.gn4
-rw-r--r--chromium/chrome/browser/ui/webui/usb_internals/usb_internals.mojom4
-rw-r--r--chromium/chrome/browser/ui/webui/usb_internals/usb_internals_ui.cc4
-rw-r--r--chromium/chrome/browser/ui/webui/version_handler.cc21
-rw-r--r--chromium/chrome/browser/ui/webui/version_ui.cc3
-rw-r--r--chromium/chrome/browser/ui/webui/web_ui_test_handler.cc2
-rw-r--r--chromium/chrome/browser/ui/webui/webui_browsertest.cc22
-rw-r--r--chromium/chrome/browser/ui/webui/welcome_handler.cc10
-rw-r--r--chromium/chrome/browser/vr/BUILD.gn69
-rw-r--r--chromium/chrome/browser/vr/testapp/BUILD.gn2
-rw-r--r--chromium/chrome/browser/vr/testapp/vr_testapp_resources.grd20
-rw-r--r--chromium/chrome/browser/vr/vector_icons/BUILD.gn5
1009 files changed, 39128 insertions, 8648 deletions
diff --git a/chromium/chrome/browser/BUILD.gn b/chromium/chrome/browser/BUILD.gn
index c61e8b60b4c..957ba49abb8 100644
--- a/chromium/chrome/browser/BUILD.gn
+++ b/chromium/chrome/browser/BUILD.gn
@@ -5,12 +5,13 @@
import("//build/config/chrome_build.gni")
import("//build/config/crypto.gni")
import("//build/config/features.gni")
+import("//build/config/jumbo.gni")
import("//build/config/ui.gni")
import("//build/split_static_library.gni")
import("//chrome/common/features.gni")
import("//components/feature_engagement/features.gni")
import("//components/nacl/features.gni")
-import("//components/offline_pages/features/features.gni")
+import("//components/offline_pages/buildflags/features.gni")
import("//components/os_crypt/features.gni")
import("//components/signin/features.gni")
import("//components/spellcheck/spellcheck_build_features.gni")
@@ -24,7 +25,6 @@ import("//rlz/features/features.gni")
import("//sandbox/features.gni")
import("//third_party/protobuf/proto_library.gni")
import("//ui/base/ui_features.gni")
-import("//build/buildflag_header.gni")
# //build/config/android/rules.gni imports //tools/grit/grit_rule.gni, which
# produces a conflict for the "grit" template so we have to only include one.
@@ -34,10 +34,6 @@ if (is_android) {
import("//tools/grit/grit_rule.gni")
}
-if (enable_vr) {
- import("//chrome/browser/vr/features.gni")
-}
-
additional_modules_list_file =
"$root_gen_dir/chrome/browser/internal/additional_modules_list.txt"
@@ -52,7 +48,6 @@ if (is_win) {
"netapi32.lib",
"ndfapi.lib", # Used by browser/net/net_error_diagnostics_dialog_win.h
"pdh.lib", # Used by browser/private_working_set_snapshot.h
- "msi.lib", # Used by browser/conflicts/msi_util_win.h
]
ldflags = [
"/DELAYLOAD:ndfapi.dll",
@@ -61,16 +56,9 @@ if (is_win) {
}
}
-if (enable_vr) {
- buildflag_header("vr_build_features") {
- header = "vr_features.h"
- flags = [ "USE_VR_ASSETS_COMPONENT=$use_vr_assets_component" ]
- }
-}
-
# 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.
-split_static_library("browser") {
+jumbo_split_static_library("browser") {
# Split into multiple static libraries on Windows builds. We have hit size
# limits on Windows official builds and on goma builds when symbol_level = 2
# is selected. Always splitting on Windows builds is simpler than trying to
@@ -222,6 +210,8 @@ split_static_library("browser") {
"browsing_data/counters/site_data_counting_helper.h",
"browsing_data/local_data_container.cc",
"browsing_data/local_data_container.h",
+ "browsing_data/navigation_entry_remover.cc",
+ "browsing_data/navigation_entry_remover.h",
"browsing_data/site_data_size_collector.cc",
"browsing_data/site_data_size_collector.h",
"budget_service/budget_database.cc",
@@ -306,14 +296,10 @@ split_static_library("browser") {
"component_updater/supervised_user_whitelist_installer.h",
"component_updater/sw_reporter_installer_win.cc",
"component_updater/sw_reporter_installer_win.h",
- "component_updater/third_party_module_list_component_installer_win.cc",
- "component_updater/third_party_module_list_component_installer_win.h",
"conflicts/enumerate_input_method_editors_win.cc",
"conflicts/enumerate_input_method_editors_win.h",
"conflicts/enumerate_shell_extensions_win.cc",
"conflicts/enumerate_shell_extensions_win.h",
- "conflicts/installed_programs_win.cc",
- "conflicts/installed_programs_win.h",
"conflicts/module_database_observer_win.h",
"conflicts/module_database_win.cc",
"conflicts/module_database_win.h",
@@ -325,10 +311,6 @@ split_static_library("browser") {
"conflicts/module_info_win.h",
"conflicts/module_inspector_win.cc",
"conflicts/module_inspector_win.h",
- "conflicts/module_list_manager_win.cc",
- "conflicts/module_list_manager_win.h",
- "conflicts/msi_util_win.cc",
- "conflicts/msi_util_win.h",
"conflicts/third_party_metrics_recorder_win.cc",
"conflicts/third_party_metrics_recorder_win.h",
"consent_auditor/consent_auditor_factory.cc",
@@ -357,6 +339,8 @@ split_static_library("browser") {
"custom_handlers/protocol_handler_registry.h",
"custom_handlers/protocol_handler_registry_factory.cc",
"custom_handlers/protocol_handler_registry_factory.h",
+ "data_reduction_proxy_util.cc",
+ "data_reduction_proxy_util.h",
"data_usage/tab_id_annotator.cc",
"data_usage/tab_id_annotator.h",
"data_usage/tab_id_provider.cc",
@@ -410,6 +394,7 @@ split_static_library("browser") {
"download/download_permission_request.h",
"download/download_prefs.cc",
"download/download_prefs.h",
+ "download/download_prompt_status.h",
"download/download_query.cc",
"download/download_query.h",
"download/download_request_limiter.cc",
@@ -518,12 +503,8 @@ split_static_library("browser") {
"google/google_brand_chromeos.cc",
"google/google_brand_chromeos.h",
"google/google_update_settings_posix.cc",
- "google/google_update_win.cc",
- "google/google_update_win.h",
"google/google_url_tracker_factory.cc",
"google/google_url_tracker_factory.h",
- "gpu/gpu_driver_info_manager_android.cc",
- "gpu/gpu_driver_info_manager_android.h",
"gpu/gpu_mode_manager.cc",
"gpu/gpu_mode_manager.h",
"gpu/three_d_api_observer.cc",
@@ -571,6 +552,7 @@ split_static_library("browser") {
"install_verification/win/module_list.h",
"install_verification/win/module_verification_common.cc",
"install_verification/win/module_verification_common.h",
+ "installable/installable_ambient_badge_infobar_delegate.h",
"installable/installable_data.cc",
"installable/installable_data.h",
"installable/installable_logging.cc",
@@ -661,9 +643,12 @@ split_static_library("browser") {
"media/platform_verification_impl.h",
"media/router/media_router_feature.cc",
"media/router/media_router_feature.h",
+ "media/single_client_video_capture_host.cc",
+ "media/single_client_video_capture_host.h",
"media/webrtc/desktop_media_list.h",
"media/webrtc/desktop_media_list_base.cc",
"media/webrtc/desktop_media_list_base.h",
+ "media/webrtc/desktop_media_picker.cc",
"media/webrtc/desktop_media_picker.h",
"media/webrtc/desktop_streams_registry.cc",
"media/webrtc/desktop_streams_registry.h",
@@ -681,10 +666,6 @@ split_static_library("browser") {
"media/webrtc/native_desktop_media_list.h",
"media/webrtc/permission_bubble_media_access_handler.cc",
"media/webrtc/permission_bubble_media_access_handler.h",
-
- # TODO(brettw) should this go with the webrtc sources?
- "media/webrtc/webrtc_log_list.cc",
- "media/webrtc/webrtc_log_list.h",
"media/webrtc/window_icon_util.h",
"media/webrtc/window_icon_util_chromeos.cc",
"media/webrtc/window_icon_util_mac.mm",
@@ -740,6 +721,8 @@ split_static_library("browser") {
"metrics/sampling_metrics_provider.h",
"metrics/subprocess_metrics_provider.cc",
"metrics/subprocess_metrics_provider.h",
+ "metrics/testing/metrics_reporting_pref_helper.cc",
+ "metrics/testing/metrics_reporting_pref_helper.h",
"metrics/thread_watcher.cc",
"metrics/thread_watcher.h",
"metrics/thread_watcher_android.cc",
@@ -805,6 +788,8 @@ split_static_library("browser") {
"net/quota_policy_channel_id_store.h",
"net/referrer.cc",
"net/referrer.h",
+ "net/reporting_permissions_checker.cc",
+ "net/reporting_permissions_checker.h",
"net/safe_search_util.cc",
"net/safe_search_util.h",
"net/service_providers_win.cc",
@@ -860,6 +845,8 @@ split_static_library("browser") {
"notifications/persistent_notification_handler.h",
"notifications/platform_notification_service_impl.cc",
"notifications/platform_notification_service_impl.h",
+ "notifications/system_notification_helper.cc",
+ "notifications/system_notification_helper.h",
"ntp_snippets/bookmark_last_visit_updater.cc",
"ntp_snippets/bookmark_last_visit_updater.h",
"ntp_snippets/content_suggestions_notifier_service_factory.cc",
@@ -940,6 +927,8 @@ split_static_library("browser") {
"page_load_metrics/observers/previews_ukm_observer.h",
"page_load_metrics/observers/protocol_page_load_metrics_observer.cc",
"page_load_metrics/observers/protocol_page_load_metrics_observer.h",
+ "page_load_metrics/observers/security_state_page_load_metrics_observer.cc",
+ "page_load_metrics/observers/security_state_page_load_metrics_observer.h",
"page_load_metrics/observers/service_worker_page_load_metrics_observer.cc",
"page_load_metrics/observers/service_worker_page_load_metrics_observer.h",
"page_load_metrics/observers/subresource_filter_metrics_observer.cc",
@@ -967,6 +956,8 @@ split_static_library("browser") {
"page_load_metrics/user_input_tracker.h",
"password_manager/chrome_password_manager_client.cc",
"password_manager/chrome_password_manager_client.h",
+ "password_manager/password_manager_util_linux.cc",
+ "password_manager/password_manager_util_linux.h",
"password_manager/password_manager_util_mac.h",
"password_manager/password_manager_util_mac.mm",
"password_manager/password_manager_util_win.cc",
@@ -977,6 +968,9 @@ split_static_library("browser") {
"password_manager/password_store_mac.h",
"password_manager/password_store_win.cc",
"password_manager/password_store_win.h",
+ "password_manager/reauth_purpose.h",
+ "payments/payment_handler_permission_context.cc",
+ "payments/payment_handler_permission_context.h",
"payments/ssl_validity_checker.cc",
"performance_monitor/performance_monitor.cc",
"performance_monitor/performance_monitor.h",
@@ -1091,8 +1085,6 @@ split_static_library("browser") {
"predictors/resource_prefetch_predictor_tab_helper.h",
"predictors/resource_prefetch_predictor_tables.cc",
"predictors/resource_prefetch_predictor_tables.h",
- "predictors/resource_prefetcher.cc",
- "predictors/resource_prefetcher.h",
"prefs/browser_prefs.cc",
"prefs/browser_prefs.h",
"prefs/chrome_command_line_pref_store.cc",
@@ -1351,6 +1343,8 @@ split_static_library("browser") {
"signin/signin_tracker_factory.h",
"signin/signin_util.cc",
"signin/signin_util.h",
+ "signin/unified_consent_helper.cc",
+ "signin/unified_consent_helper.h",
"site_details.cc",
"site_details.h",
"speech/chrome_speech_recognition_manager_delegate.cc",
@@ -1388,6 +1382,8 @@ split_static_library("browser") {
"ssl/chrome_ssl_host_state_delegate_factory.h",
"ssl/common_name_mismatch_handler.cc",
"ssl/common_name_mismatch_handler.h",
+ "ssl/connection_help_tab_helper.cc",
+ "ssl/connection_help_tab_helper.h",
"ssl/insecure_sensitive_input_driver.cc",
"ssl/insecure_sensitive_input_driver.h",
"ssl/insecure_sensitive_input_driver_factory.cc",
@@ -1523,6 +1519,8 @@ split_static_library("browser") {
"webshare/webshare_target.h",
"win/app_icon.cc",
"win/app_icon.h",
+ "win/automation_controller.cc",
+ "win/automation_controller.h",
"win/browser_util.cc",
"win/browser_util.h",
"win/chrome_elf_init.cc",
@@ -1547,15 +1545,10 @@ split_static_library("browser") {
"win/taskbar_icon_finder.h",
"win/titlebar_config.cc",
"win/titlebar_config.h",
+ "win/ui_automation_util.cc",
+ "win/ui_automation_util.h",
]
- if (enable_downloadable_strings) {
- sources += [
- "component_updater/downloadable_strings_component_installer.cc",
- "component_updater/downloadable_strings_component_installer.h",
- ]
- }
-
configs += [
"//build/config/compiler:wexit_time_destructors",
"//build/config:precompiled_headers",
@@ -1573,7 +1566,7 @@ split_static_library("browser") {
"//base",
"//chrome/common",
"//components/autofill/core/browser",
- "//components/nacl/common:features",
+ "//components/nacl/common:buildflags",
"//components/payments/core",
"//components/sync",
"//content/public/browser",
@@ -1584,7 +1577,7 @@ split_static_library("browser") {
":active_use_util",
":resource_prefetch_predictor_proto",
"//base:i18n",
- "//base/allocator:features",
+ "//base/allocator:buildflags",
"//cc",
"//chrome:extra_resources",
"//chrome:resources",
@@ -1639,12 +1632,13 @@ split_static_library("browser") {
"//components/domain_reliability",
"//components/download/content/factory",
"//components/download/downloader/in_progress",
- "//components/download/public",
+ "//components/download/public/background_service:public",
"//components/error_page/common",
"//components/favicon/content",
"//components/favicon/core",
"//components/favicon_base",
"//components/feature_engagement",
+ "//components/filename_generation",
"//components/flags_ui",
"//components/gcm_driver",
"//components/google/core/browser",
@@ -1672,7 +1666,7 @@ split_static_library("browser") {
"//components/ntp_snippets",
"//components/ntp_tiles",
"//components/offline_items_collection/core",
- "//components/offline_pages/features:features",
+ "//components/offline_pages/buildflags",
"//components/omnibox/browser",
"//components/optimization_guide",
"//components/os_crypt",
@@ -1722,15 +1716,15 @@ split_static_library("browser") {
"//components/sync_bookmarks",
"//components/sync_preferences",
"//components/sync_sessions",
- "//components/task_scheduler_util/browser",
"//components/task_scheduler_util/common",
"//components/tracing:startup_tracing",
"//components/translate/content/browser",
"//components/translate/core/browser",
"//components/translate/core/common",
"//components/ukm:observers",
- "//components/ukm/debug_page",
+ "//components/ukm/content/debug_page",
"//components/undo",
+ "//components/unzip_service/public/interfaces",
"//components/update_client",
"//components/upload_list",
"//components/url_formatter",
@@ -1752,20 +1746,20 @@ split_static_library("browser") {
"//content/public/common:feature_h264_with_openh264_ffmpeg",
"//content/public/common:features",
"//content/public/common:service_names",
- "//content/public/network",
"//courgette:courgette_lib",
"//crypto",
"//crypto:platform",
"//device/base",
"//device/geolocation",
"//device/usb/mojo",
- "//device/usb/public/interfaces",
+ "//device/usb/public/mojom",
"//device/vr/features",
"//extensions/features",
"//google_apis",
"//gpu/config",
"//media",
"//media:media_features",
+ "//media/capture",
"//media/cast:net",
"//media/midi",
"//media/mojo:features",
@@ -1783,28 +1777,30 @@ split_static_library("browser") {
"//rlz/features",
"//services/data_decoder/public/cpp",
"//services/device/public/cpp:device_features",
- "//services/device/public/interfaces",
+ "//services/device/public/mojom",
"//services/identity:lib",
"//services/identity/public/cpp",
"//services/metrics/public/cpp:ukm_builders",
- "//services/network/public/interfaces",
+ "//services/network:network_service",
+ "//services/network/public/cpp",
+ "//services/network/public/mojom",
"//services/preferences/public/cpp",
"//services/preferences/public/cpp:service_main",
"//services/preferences/public/cpp/tracked",
- "//services/preferences/public/interfaces",
+ "//services/preferences/public/mojom",
"//services/preferences/tracked",
- "//services/proxy_resolver/public/interfaces",
+ "//services/proxy_resolver/public/mojom",
"//services/resource_coordinator/public/cpp:resource_coordinator_cpp",
"//services/service_manager/public/cpp",
- "//services/shape_detection/public/interfaces",
+ "//services/shape_detection/public/mojom",
"//skia",
"//sql",
"//storage/browser",
"//storage/common",
- "//third_party/WebKit/common:blink_common",
"//third_party/WebKit/public:features",
"//third_party/WebKit/public:resources",
"//third_party/WebKit/public:scaled_resources",
+ "//third_party/WebKit/public/common",
"//third_party/cacheinvalidation",
"//third_party/icu",
"//third_party/leveldatabase",
@@ -1864,6 +1860,8 @@ split_static_library("browser") {
"android/browsing_data/browsing_data_counter_bridge.h",
"android/browsing_data/url_filter_bridge.cc",
"android/browsing_data/url_filter_bridge.h",
+ "android/byte_array_int_callback.cc",
+ "android/byte_array_int_callback.h",
"android/chrome_backup_agent.cc",
"android/chrome_backup_agent.h",
"android/chrome_backup_watcher.cc",
@@ -1913,6 +1911,7 @@ split_static_library("browser") {
"android/compositor/scene_layer/toolbar_scene_layer.h",
"android/compositor/tab_content_manager.cc",
"android/compositor/tab_content_manager.h",
+ "android/consent_auditor/consent_auditor_bridge.cc",
"android/content/content_utils.cc",
"android/contextualsearch/contextual_search_context.cc",
"android/contextualsearch/contextual_search_context.h",
@@ -1934,6 +1933,8 @@ split_static_library("browser") {
"android/cookies/cookies_fetcher.h",
"android/crash/pure_java_exception_handler.cc",
"android/crash/pure_java_exception_handler.h",
+ "android/customtabs/detached_resource_request.cc",
+ "android/customtabs/detached_resource_request.h",
"android/customtabs/origin_verifier.cc",
"android/customtabs/origin_verifier.h",
"android/data_usage/data_use_matcher.cc",
@@ -1973,6 +1974,8 @@ split_static_library("browser") {
"android/download/download_controller.h",
"android/download/download_controller_base.cc",
"android/download/download_controller_base.h",
+ "android/download/download_location_dialog_bridge.cc",
+ "android/download/download_location_dialog_bridge.h",
"android/download/download_manager_service.cc",
"android/download/download_manager_service.h",
"android/download/duplicate_download_infobar_delegate.cc",
@@ -1991,6 +1994,7 @@ split_static_library("browser") {
"android/feature_utilities.cc",
"android/feature_utilities.h",
"android/feedback/connectivity_checker.cc",
+ "android/feedback/process_id_feedback_source.cc",
"android/feedback/screenshot_task.cc",
"android/feedback/system_info_feedback_source.cc",
"android/find_in_page/find_in_page_bridge.cc",
@@ -2048,14 +2052,14 @@ split_static_library("browser") {
"android/metrics/variations_session.cc",
"android/mojo/chrome_interface_registrar_android.cc",
"android/mojo/chrome_interface_registrar_android.h",
- "android/net/external_estimate_provider_android.cc",
- "android/net/external_estimate_provider_android.h",
"android/ntp/android_content_suggestions_notifier.cc",
"android/ntp/android_content_suggestions_notifier.h",
"android/ntp/content_suggestions_notifier.cc",
"android/ntp/content_suggestions_notifier.h",
"android/ntp/content_suggestions_notifier_service.cc",
"android/ntp/content_suggestions_notifier_service.h",
+ "android/ntp/get_remote_suggestions_scheduler.cc",
+ "android/ntp/get_remote_suggestions_scheduler.h",
"android/ntp/most_visited_sites_bridge.cc",
"android/ntp/most_visited_sites_bridge.h",
"android/ntp/new_tab_page_url_handler.cc",
@@ -2144,6 +2148,7 @@ split_static_library("browser") {
"android/signin/signin_manager_android.h",
"android/signin/signin_promo_util_android.cc",
"android/signin/signin_promo_util_android.h",
+ "android/subresource_filter/test_subresource_filter_publisher.cc",
"android/tab_android.cc",
"android/tab_android.h",
"android/tab_state.cc",
@@ -2158,8 +2163,6 @@ split_static_library("browser") {
"android/url_utilities.cc",
"android/usb/web_usb_chooser_service_android.cc",
"android/usb/web_usb_chooser_service_android.h",
- "android/voice_search_tab_helper.cc",
- "android/voice_search_tab_helper.h",
"android/warmup_manager.cc",
"android/web_contents_factory.cc",
"android/webapk/chrome_webapk_host.cc",
@@ -2199,6 +2202,8 @@ split_static_library("browser") {
"banners/app_banner_infobar_delegate_android.h",
"banners/app_banner_manager_android.cc",
"banners/app_banner_manager_android.h",
+ "banners/app_banner_ui_delegate_android.cc",
+ "banners/app_banner_ui_delegate_android.h",
"chrome_browser_field_trials_mobile.cc",
"chrome_browser_field_trials_mobile.h",
"dom_distiller/dom_distiller_service_factory_android.cc",
@@ -2214,6 +2219,7 @@ split_static_library("browser") {
"history/android/bookmark_model_sql_handler.h",
"history/android/sqlite_cursor.cc",
"history/android/sqlite_cursor.h",
+ "installable/installable_ambient_badge_infobar_delegate.cc",
"invalidation/invalidation_service_factory_android.cc",
"invalidation/invalidation_service_factory_android.h",
"lifetime/application_lifetime_android.cc",
@@ -2299,7 +2305,6 @@ split_static_library("browser") {
"sync/glue/synced_window_delegates_getter_android.h",
"sync/profile_sync_service_android.cc",
"sync/profile_sync_service_android.h",
- "sync/sessions/sync_sessions_metrics_android.cc",
]
deps += [
":client_discourse_context_proto",
@@ -2536,8 +2541,6 @@ split_static_library("browser") {
"net/firefox_proxy_settings.h",
"notifications/message_center_notification_manager.cc",
"notifications/message_center_notification_manager.h",
- "notifications/message_center_stats_collector.cc",
- "notifications/message_center_stats_collector.h",
"notifications/notification_system_observer.cc",
"notifications/notification_system_observer.h",
"notifications/notification_ui_manager_desktop.cc",
@@ -2551,6 +2554,8 @@ split_static_library("browser") {
"page_load_metrics/observers/session_restore_page_load_metrics_observer.h",
"pdf/pdf_extension_util.cc",
"pdf/pdf_extension_util.h",
+ "permissions/attestation_permission_request.cc",
+ "permissions/attestation_permission_request.h",
"picture_in_picture/picture_in_picture_window_controller.cc",
"picture_in_picture/picture_in_picture_window_controller.h",
"policy/local_sync_policy_handler.cc",
@@ -2628,6 +2633,8 @@ split_static_library("browser") {
"resource_coordinator/tab_manager_stats_collector.h",
"resource_coordinator/tab_manager_web_contents_data.cc",
"resource_coordinator/tab_manager_web_contents_data.h",
+ "resource_coordinator/tab_metrics_logger.cc",
+ "resource_coordinator/tab_metrics_logger.h",
"resource_coordinator/tab_stats.cc",
"resource_coordinator/tab_stats.h",
"resource_coordinator/time.cc",
@@ -2774,6 +2781,7 @@ split_static_library("browser") {
"//chrome/app/vector_icons",
"//chrome/browser/policy:path_parser",
"//chrome/browser/profile_resetter:profile_reset_report_proto",
+ "//chrome/browser/resource_coordinator:tab_metrics_event_proto",
"//chrome/browser/resources:component_extension_resources",
"//chrome/browser/search:generated",
"//chrome/common/importer:interfaces",
@@ -2851,11 +2859,11 @@ split_static_library("browser") {
]
deps += [
"//ash",
+ "//ash/components/quick_launch/public/mojom:constants",
"//ash/public/cpp",
"//chrome/browser/chromeos",
"//components/font_service:lib",
"//components/font_service/public/interfaces",
- "//mash/quick_launch/public/interfaces:constants",
"//services/ui/public/cpp/input_devices",
"//services/ui/public/cpp/input_devices:input_device_controller",
"//services/ui/public/interfaces",
@@ -2892,8 +2900,6 @@ split_static_library("browser") {
"downgrade/user_data_downgrade.cc",
"downgrade/user_data_downgrade.h",
"first_run/upgrade_util.cc",
- "notifications/mock_itoastnotification.cc",
- "notifications/mock_itoastnotification.h",
"notifications/notification_image_retainer.cc",
"notifications/notification_image_retainer.h",
"notifications/notification_template_builder.cc",
@@ -2910,14 +2916,13 @@ split_static_library("browser") {
"//chrome/common:metrics_constants_util_win",
"//chrome/common:version_header",
"//chrome/install_static:install_static_util",
- "//chrome/services/util_win/public/interfaces",
+ "//chrome/services/util_win/public/mojom",
"//chrome_elf:blacklist",
"//chrome_elf:constants",
"//chrome_elf:dll_hash",
"//components/browser_watcher",
"//components/browser_watcher:browser_watcher_client",
"//components/browser_watcher:stability_client",
- "//google_update",
"//third_party/crashpad/crashpad/client:client",
"//third_party/iaccessible2",
"//third_party/isimpledom",
@@ -2929,14 +2934,38 @@ split_static_library("browser") {
if (enable_native_notifications) {
sources += [
+ "notifications/notification_launch_id.cc",
+ "notifications/notification_launch_id.h",
"notifications/notification_platform_bridge_win.cc",
"notifications/notification_platform_bridge_win.h",
]
}
- if (!is_chrome_branded) {
- deps -= [ "//google_update" ]
- sources -= [
+ if (is_chrome_branded) {
+ deps += [
+ ":conflicts_module_list_proto",
+ "//google_update",
+ ]
+ libs += [ "msi.lib" ]
+ sources += [
+ "component_updater/third_party_module_list_component_installer_win.cc",
+ "component_updater/third_party_module_list_component_installer_win.h",
+ "conflicts/installed_programs_win.cc",
+ "conflicts/installed_programs_win.h",
+ "conflicts/module_list_filter_win.cc",
+ "conflicts/module_list_filter_win.h",
+ "conflicts/msi_util_win.cc",
+ "conflicts/msi_util_win.h",
+ "conflicts/problematic_programs_updater_win.cc",
+ "conflicts/problematic_programs_updater_win.h",
+ "conflicts/registry_key_watcher_win.cc",
+ "conflicts/registry_key_watcher_win.h",
+ "conflicts/third_party_conflicts_manager_win.cc",
+ "conflicts/third_party_conflicts_manager_win.h",
+ "conflicts/token_util_win.cc",
+ "conflicts/token_util_win.h",
+ "conflicts/uninstall_application_win.cc",
+ "conflicts/uninstall_application_win.h",
"google/google_update_win.cc",
"google/google_update_win.h",
]
@@ -3298,7 +3327,7 @@ split_static_library("browser") {
]
}
if (is_win || enable_print_preview) {
- deps += [ "//chrome/services/printing/public/interfaces" ]
+ deps += [ "//chrome/services/printing/public/mojom" ]
}
if (enable_print_preview) {
# Full printing on top of the above.
@@ -3610,7 +3639,7 @@ split_static_library("browser") {
"//components/guest_view/browser",
"//extensions/components/javascript_dialog_extensions_client",
"//media/cast:net",
- "//services/device/public/interfaces",
+ "//services/device/public/mojom",
]
}
@@ -3859,7 +3888,7 @@ split_static_library("browser") {
"//media:media_features",
"//ppapi/features",
"//ppapi/proxy:ipc",
- "//services/device/public/interfaces",
+ "//services/device/public/mojom",
"//third_party/adobe/flash:flapper_version_h",
]
}
@@ -3945,6 +3974,7 @@ split_static_library("browser") {
sources += [
"spellchecker/spell_check_host_chrome_impl.cc",
"spellchecker/spell_check_host_chrome_impl.h",
+ "spellchecker/spell_check_host_chrome_impl_mac.cc",
"spellchecker/spellcheck_custom_dictionary.cc",
"spellchecker/spellcheck_custom_dictionary.h",
"spellchecker/spellcheck_factory.cc",
@@ -3953,7 +3983,6 @@ split_static_library("browser") {
"spellchecker/spellcheck_hunspell_dictionary.h",
"spellchecker/spellcheck_language_policy_handler.cc",
"spellchecker/spellcheck_language_policy_handler.h",
- "spellchecker/spellcheck_message_filter_platform_mac.cc",
"spellchecker/spellcheck_service.cc",
"spellchecker/spellcheck_service.h",
]
@@ -4076,20 +4105,16 @@ split_static_library("browser") {
if (enable_vr) {
if (enable_gvr_services) {
- deps += [ "android/vr_shell:vr_android" ]
+ deps += [ "android/vr:vr_android" ]
configs += [ "//third_party/gvr-android-sdk:libgvr_config" ]
- allow_circular_includes_from += [ "android/vr_shell:vr_android" ]
+ allow_circular_includes_from += [ "android/vr:vr_android" ]
}
sources += [
"component_updater/vr_assets_component_installer.cc",
"component_updater/vr_assets_component_installer.h",
- "vr_features.h",
- ]
- deps += [
- ":vr_build_features",
- "//chrome/browser/vr:vr_common",
]
+ deps += [ "//chrome/browser/vr:vr_common" ]
}
if (enable_wayland_server) {
@@ -4107,8 +4132,6 @@ split_static_library("browser") {
sources += [
"media/webrtc/audio_debug_recordings_handler.cc",
"media/webrtc/audio_debug_recordings_handler.h",
-
- # TODO(brettw) should webrtc_log_list.cc go here?
"media/webrtc/webrtc_log_uploader.cc",
"media/webrtc/webrtc_log_uploader.h",
"media/webrtc/webrtc_log_util.cc",
@@ -4123,7 +4146,9 @@ split_static_library("browser") {
"media/webrtc/webrtc_text_log_handler.h",
]
deps += [
- "//third_party/webrtc/modules/desktop_capture",
+ "//components/webrtc_logging/browser",
+ "//components/webrtc_logging/common",
+ "//services/audio/public/cpp",
"//third_party/webrtc_overrides",
"//third_party/webrtc_overrides:init_webrtc",
]
@@ -4185,7 +4210,6 @@ if (is_android) {
"../android/java/src/org/chromium/chrome/browser/IntentHelper.java",
"../android/java/src/org/chromium/chrome/browser/JavascriptAppModalDialog.java",
"../android/java/src/org/chromium/chrome/browser/NearOomMonitor.java",
- "../android/java/src/org/chromium/chrome/browser/PasswordUIView.java",
"../android/java/src/org/chromium/chrome/browser/SSLClientCertificateRequest.java",
"../android/java/src/org/chromium/chrome/browser/SearchGeolocationDisclosureTabHelper.java",
"../android/java/src/org/chromium/chrome/browser/ServiceTabLauncher.java",
@@ -4206,6 +4230,7 @@ if (is_android) {
"../android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java",
"../android/java/src/org/chromium/chrome/browser/autofill/PhoneNumberUtil.java",
"../android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java",
+ "../android/java/src/org/chromium/chrome/browser/banners/AppBannerUiDelegateAndroid.java",
"../android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java",
"../android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java",
"../android/java/src/org/chromium/chrome/browser/browsing_data/UrlFilterBridge.java",
@@ -4222,6 +4247,7 @@ if (is_android) {
"../android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java",
"../android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java",
"../android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java",
+ "../android/java/src/org/chromium/chrome/browser/consent_auditor/ConsentAuditorBridge.java",
"../android/java/src/org/chromium/chrome/browser/content/ContentUtils.java",
"../android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java",
"../android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuParams.java",
@@ -4245,6 +4271,7 @@ if (is_android) {
"../android/java/src/org/chromium/chrome/browser/download/DownloadController.java",
"../android/java/src/org/chromium/chrome/browser/download/DownloadInfo.java",
"../android/java/src/org/chromium/chrome/browser/download/DownloadItem.java",
+ "../android/java/src/org/chromium/chrome/browser/download/DownloadLocationDialogBridge.java",
"../android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java",
"../android/java/src/org/chromium/chrome/browser/download/items/OfflineContentAggregatorFactory.java",
"../android/java/src/org/chromium/chrome/browser/download/service/DownloadBackgroundTask.java",
@@ -4254,6 +4281,7 @@ if (is_android) {
"../android/java/src/org/chromium/chrome/browser/favicon/LargeIconBridge.java",
"../android/java/src/org/chromium/chrome/browser/feature_engagement/TrackerFactory.java",
"../android/java/src/org/chromium/chrome/browser/feedback/ConnectivityChecker.java",
+ "../android/java/src/org/chromium/chrome/browser/feedback/ProcessIdFeedbackSource.java",
"../android/java/src/org/chromium/chrome/browser/feedback/ScreenshotTask.java",
"../android/java/src/org/chromium/chrome/browser/feedback/SystemInfoFeedbackSource.java",
"../android/java/src/org/chromium/chrome/browser/findinpage/FindInPageBridge.java",
@@ -4271,6 +4299,7 @@ if (is_android) {
"../android/java/src/org/chromium/chrome/browser/infobar/GeneratedPasswordSavedInfoBarDelegate.java",
"../android/java/src/org/chromium/chrome/browser/infobar/InfoBar.java",
"../android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainer.java",
+ "../android/java/src/org/chromium/chrome/browser/infobar/InstallableAmbientBadgeInfoBar.java",
"../android/java/src/org/chromium/chrome/browser/infobar/InstantAppsInfoBar.java",
"../android/java/src/org/chromium/chrome/browser/infobar/InstantAppsInfoBarDelegate.java",
"../android/java/src/org/chromium/chrome/browser/infobar/NearOomInfoBar.java",
@@ -4299,7 +4328,6 @@ if (is_android) {
"../android/java/src/org/chromium/chrome/browser/metrics/UmaUtils.java",
"../android/java/src/org/chromium/chrome/browser/metrics/VariationsSession.java",
"../android/java/src/org/chromium/chrome/browser/mojo/ChromeInterfaceRegistrar.java",
- "../android/java/src/org/chromium/chrome/browser/net/qualityprovider/ExternalEstimateProviderAndroid.java",
"../android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java",
"../android/java/src/org/chromium/chrome/browser/notifications/ActionInfo.java",
"../android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java",
@@ -4354,6 +4382,8 @@ if (is_android) {
"../android/java/src/org/chromium/chrome/browser/preferences/PrefServiceBridge.java",
"../android/java/src/org/chromium/chrome/browser/preferences/PreferencesLauncher.java",
"../android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfileBridge.java",
+ "../android/java/src/org/chromium/chrome/browser/preferences/password/ByteArrayIntCallback.java",
+ "../android/java/src/org/chromium/chrome/browser/preferences/password/PasswordUIView.java",
"../android/java/src/org/chromium/chrome/browser/preferences/privacy/BrowsingDataBridge.java",
"../android/java/src/org/chromium/chrome/browser/preferences/privacy/BrowsingDataCounterBridge.java",
"../android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferenceBridge.java",
@@ -4377,12 +4407,12 @@ if (is_android) {
"../android/java/src/org/chromium/chrome/browser/snackbar/smartlockautosignin/AutoSigninSnackbarController.java",
"../android/java/src/org/chromium/chrome/browser/ssl/CaptivePortalHelper.java",
"../android/java/src/org/chromium/chrome/browser/ssl/SecurityStateModel.java",
+ "../android/java/src/org/chromium/chrome/browser/subresource_filter/TestSubresourceFilterPublisher.java",
"../android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSites.java",
"../android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java",
"../android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsEventReporterBridge.java",
"../android/java/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProvider.java",
"../android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java",
- "../android/java/src/org/chromium/chrome/browser/sync/SyncSessionsMetrics.java",
"../android/java/src/org/chromium/chrome/browser/tab/Tab.java",
"../android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java",
"../android/java/src/org/chromium/chrome/browser/tabmodel/SingleTabModel.java",
@@ -4505,10 +4535,10 @@ grit("resources") {
"//chrome/browser/ui/webui/interventions_internals:mojo_bindings_js",
"//chrome/browser/ui/webui/omnibox:mojo_bindings_js",
"//chrome/browser/ui/webui/usb_internals:mojo_bindings_js",
- "//device/bluetooth/public/interfaces:deprecated_experimental_interfaces_js",
- "//device/bluetooth/public/interfaces:interfaces_js",
- "//url/mojo:url_mojom_gurl_js",
- "//url/mojo:url_mojom_origin_js",
+ "//device/bluetooth/public/mojom:deprecated_experimental_interfaces_js",
+ "//device/bluetooth/public/mojom:mojom_js",
+ "//url/mojom:url_mojom_gurl_js",
+ "//url/mojom:url_mojom_origin_js",
]
if (is_win || is_mac || is_desktop_linux || is_chromeos) {
@@ -4693,6 +4723,10 @@ static_library("test_support") {
"sessions/session_restore_test_helper.h",
"sessions/session_service_test_helper.cc",
"sessions/session_service_test_helper.h",
+ "ui/tabs/tab_activity_simulator.cc",
+ "ui/tabs/tab_activity_simulator.h",
+ "ui/tabs/tab_ukm_test_helper.cc",
+ "ui/tabs/tab_ukm_test_helper.h",
]
}
@@ -4718,6 +4752,8 @@ static_library("test_support") {
"chromeos/login/test/js_checker.h",
"chromeos/login/test/oobe_screen_waiter.cc",
"chromeos/login/test/oobe_screen_waiter.h",
+ "chromeos/login/ui/fake_login_display_host.cc",
+ "chromeos/login/ui/fake_login_display_host.h",
"chromeos/login/ui/mock_login_display.cc",
"chromeos/login/ui/mock_login_display.h",
"chromeos/login/ui/mock_login_display_host.cc",
@@ -4756,6 +4792,13 @@ static_library("test_support") {
]
}
+ if (is_win) {
+ sources += [
+ "notifications/mock_itoastnotification.cc",
+ "notifications/mock_itoastnotification.h",
+ ]
+ }
+
if (enable_app_list) {
sources += [
"ui/app_list/test/chrome_app_list_test_support.cc",
@@ -4781,8 +4824,6 @@ static_library("test_support") {
"extensions/test_blacklist.h",
"extensions/test_blacklist_state_fetcher.cc",
"extensions/test_blacklist_state_fetcher.h",
- "extensions/test_extension_dir.cc",
- "extensions/test_extension_dir.h",
"extensions/test_extension_environment.cc",
"extensions/test_extension_environment.h",
"extensions/test_extension_prefs.cc",
@@ -4798,6 +4839,7 @@ static_library("test_support") {
"//components/drive:test_support",
"//components/storage_monitor:test_support",
"//extensions:test_support",
+ "//services/data_decoder/public/cpp:test_support",
]
}
diff --git a/chromium/chrome/browser/android/vr_shell/BUILD.gn b/chromium/chrome/browser/android/vr/BUILD.gn
index 0150f161693..9e8263952a3 100644
--- a/chromium/chrome/browser/android/vr_shell/BUILD.gn
+++ b/chromium/chrome/browser/android/vr/BUILD.gn
@@ -27,14 +27,16 @@ static_library("vr_android") {
"gvr_util.h",
"mailbox_to_surface_bridge.cc",
"mailbox_to_surface_bridge.h",
- "vr_compositor.cc",
- "vr_compositor.h",
"vr_controller.cc",
"vr_controller.h",
"vr_core_info.cc",
"vr_core_info.h",
+ "vr_dialog.cc",
+ "vr_dialog.h",
"vr_gl_thread.cc",
"vr_gl_thread.h",
+ "vr_input_connection.cc",
+ "vr_input_connection.h",
"vr_metrics_util.cc",
"vr_metrics_util.h",
"vr_shell.cc",
@@ -60,7 +62,7 @@ static_library("vr_android") {
"//content/public/common",
"//device/gamepad",
"//device/vr",
- "//services/device/public/interfaces",
+ "//services/device/public/mojom",
"//services/metrics/public/cpp:ukm_builders",
"//services/ui/public/cpp/gpu",
"//ui/android",
@@ -71,7 +73,7 @@ static_library("vr_android") {
]
public_deps = [
- "//device/vr:mojo_bindings",
+ "//device/vr/public/mojom",
]
libs = [
@@ -90,9 +92,11 @@ generate_jni("vr_shell_jni_headers") {
"//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/AndroidUiGestureTarget.java",
"//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/AndroidVSyncHelper.java",
"//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrCoreInfo.java",
+ "//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrInputConnection.java",
"//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java",
"//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java",
"//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/keyboard/GvrKeyboardLoaderClient.java",
+ "//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/keyboard/TextEditAction.java",
]
jni_package = "vr_shell"
}
diff --git a/chromium/chrome/browser/browser_resources.grd b/chromium/chrome/browser/browser_resources.grd
index c25812b62a3..5c8745bc214 100644
--- a/chromium/chrome/browser/browser_resources.grd
+++ b/chromium/chrome/browser/browser_resources.grd
@@ -8,11 +8,6 @@
</outputs>
<release seq="1">
<structures>
- <if expr="enable_app_list">
- <structure name="IDR_APP_LIST_START_PAGE_CSS" file="resources\app_list\start_page.css" flattenhtml="true" type="chrome_html" />
- <structure name="IDR_APP_LIST_START_PAGE_HTML" file="resources\app_list\start_page.html" flattenhtml="true" type="chrome_html" />
- <structure name="IDR_APP_LIST_START_PAGE_JS" file="resources\app_list\start_page.js" flattenhtml="true" type="chrome_html" />
- </if>
<if expr="enable_extensions">
<structure name="IDR_EXTENSIONS_HTML" file="resources\extensions\extensions.html" flattenhtml="true" type="chrome_html" />
</if>
@@ -53,10 +48,6 @@
<structure name="IDR_CUSTOM_ELEMENTS_USER_POD_HTML" file="resources\chromeos\login\custom_elements_user_pod.html" flattenhtml="true" type="chrome_html" />
<structure name="IDR_CUSTOM_ELEMENTS_LOGIN_HTML" file="resources\chromeos\login\custom_elements_login.html" flattenhtml="true" type="chrome_html" />
<structure name="IDR_CUSTOM_ELEMENTS_LOGIN_JS" file="resources\chromeos\login\custom_elements_login.js" flattenhtml="true" type="chrome_html" />
- <structure name="IDR_CUSTOM_ELEMENTS_PIN_KEYBOARD_HTML" file="resources\chromeos\quick_unlock\pin_keyboard.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
- <structure name="IDR_CUSTOM_ELEMENTS_PIN_KEYBOARD_JS" file="resources\chromeos\quick_unlock\pin_keyboard.js" type="chrome_html" />
- <structure name="IDR_MD_CUSTOM_ELEMENTS_PIN_KEYBOARD_HTML" file="resources\chromeos\quick_unlock\md_pin_keyboard.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
- <structure name="IDR_MD_CUSTOM_ELEMENTS_PIN_KEYBOARD_JS" file="resources\chromeos\quick_unlock\md_pin_keyboard.js" type="chrome_html" />
</if>
<structure name="IDR_SIGNIN_SHARED_CSS_HTML" file="resources\signin\signin_shared_css.html" preprocess="true" allowexternalscript="true" type="chrome_html" />
<if expr="not is_android and not chromeos">
@@ -100,9 +91,9 @@
<include name="IDR_ABOUT_SYS_HTML" file="resources\about_sys\about_sys.html" flattenhtml="true" type="BINDATA" />
</if>
<include name="IDR_AD_NETWORK_HASHES" file="resources\ad_networks.dat" type="BINDATA" />
- <include name="IDR_BLUETOOTH_ADAPTER_MOJO_JS" file="${root_gen_dir}\device\bluetooth\public\interfaces\adapter.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
- <include name="IDR_BLUETOOTH_DEVICE_MOJO_JS" file="${root_gen_dir}\device\bluetooth\public\interfaces\device.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
- <include name="IDR_BLUETOOTH_UUID_MOJO_JS" file="${root_gen_dir}\device\bluetooth\public\interfaces\uuid.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
+ <include name="IDR_BLUETOOTH_ADAPTER_MOJO_JS" file="${root_gen_dir}\device\bluetooth\public\mojom\adapter.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
+ <include name="IDR_BLUETOOTH_DEVICE_MOJO_JS" file="${root_gen_dir}\device\bluetooth\public\mojom\device.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
+ <include name="IDR_BLUETOOTH_UUID_MOJO_JS" file="${root_gen_dir}\device\bluetooth\public\mojom\uuid.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
<include name="IDR_BLUETOOTH_INTERNALS_CSS" file="resources\bluetooth_internals\bluetooth_internals.css" type="BINDATA" compress="gzip" />
<include name="IDR_BLUETOOTH_INTERNALS_ADAPTER_BROKER_JS" file="resources\bluetooth_internals\adapter_broker.js" type="BINDATA" compress="gzip" />
<include name="IDR_BLUETOOTH_INTERNALS_ADAPTER_PAGE_JS" file="resources\bluetooth_internals\adapter_page.js" type="BINDATA" compress="gzip" />
@@ -135,6 +126,7 @@
<include name="IDR_CLOUDPRINT_MANIFEST" file="resources\cloud_print_app\manifest.json" type="BINDATA" />
<include name="IDR_PDF_COMPOSITOR_MANIFEST" file="..\..\components\printing\service\pdf_compositor_manifest.json" type="BINDATA" />
</if>
+ <include name="IDR_CHROME_RENDERER_SERVICE_MANIFEST" file="..\app\chrome_renderer_manifest.json" type="BINDATA" />
<include name="IDR_DEVTOOLS_DISCOVERY_PAGE_HTML" file="devtools\frontend\devtools_discovery_page.html" type="BINDATA"/>
<include name="IDR_DOMAIN_RELIABILITY_INTERNALS_HTML" file="resources\domain_reliability_internals.html" compress="gzip" type="BINDATA" />
<include name="IDR_DOMAIN_RELIABILITY_INTERNALS_CSS" file="resources\domain_reliability_internals.css" compress="gzip" type="BINDATA" />
@@ -353,7 +345,7 @@
<include name="IDR_OMNIBOX_CSS" file="resources\omnibox\omnibox.css" type="BINDATA" compress="gzip" />
<include name="IDR_OMNIBOX_JS" file="resources\omnibox\omnibox.js" type="BINDATA" compress="gzip" />
<include name="IDR_OMNIBOX_MOJO_JS" file="${root_gen_dir}\chrome\browser\ui\webui\omnibox\omnibox.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
- <include name="IDR_ORIGIN_MOJO_JS" file="${root_gen_dir}\url\mojo\origin.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip"/>
+ <include name="IDR_ORIGIN_MOJO_JS" file="${root_gen_dir}\url\mojom\origin.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip"/>
<include name="IDR_COMPONENTS_HTML" file="resources\components.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
<include name="IDR_COMPONENTS_JS" file="resources\components.js" type="BINDATA" compress="gzip" />
<include name="IDR_MEMORY_INTERNALS_HTML" file="resources\memory_internals.html" flattenhtml="true" type="BINDATA" compress="gzip" />
@@ -408,8 +400,6 @@
file="resources\print_preview\images\google_doc.png" type="BINDATA" />
<include name="IDR_PRINT_PREVIEW_IMAGES_PDF"
file="resources\print_preview\images\pdf.png" type="BINDATA" />
- <include name="IDR_PRINT_PREVIEW_IMAGES_THIRD_PARTY"
- file="resources\print_preview\images\third_party.png" type="BINDATA" />
<include name="IDR_PRINT_PREVIEW_IMAGES_MOBILE"
file="resources\print_preview\images\mobile.png" type="BINDATA" />
<include name="IDR_PRINT_PREVIEW_IMAGES_MOBILE_SHARED"
@@ -418,7 +408,7 @@
<include name="IDR_SITE_ENGAGEMENT_HTML" file="resources\engagement\site_engagement.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
<include name="IDR_SITE_ENGAGEMENT_JS" file="resources\engagement\site_engagement.js" flattenhtml="true" type="BINDATA" compress="gzip" />
<include name="IDR_SITE_ENGAGEMENT_MOJO_JS" file="${root_gen_dir}\chrome\browser\engagement\site_engagement_details.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
- <include name="IDR_URL_MOJO_JS" file="${root_gen_dir}\url\mojo\url.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
+ <include name="IDR_URL_MOJO_JS" file="${root_gen_dir}\url\mojom\url.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
<include name="IDR_SYNC_CONFIRMATION_CSS" file="resources\signin\sync_confirmation\sync_confirmation.css" type="BINDATA" />
<include name="IDR_SYNC_CONFIRMATION_HTML" file="resources\signin\sync_confirmation\sync_confirmation.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
<include name="IDR_SYNC_CONFIRMATION_JS" file="resources\signin\sync_confirmation\sync_confirmation.js" type="BINDATA" />
diff --git a/chromium/chrome/browser/chrome_content_browser_manifest_overlay.json b/chromium/chrome/browser/chrome_content_browser_manifest_overlay.json
index 19cb2b02d4c..b016c0546d9 100644
--- a/chromium/chrome/browser/chrome_content_browser_manifest_overlay.json
+++ b/chromium/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -10,6 +10,7 @@
"chrome::mojom::CacheStatsRecorder",
"chrome::mojom::NetBenchmarking",
"extensions::StashService",
+ "metrics::mojom::CallStackProfileCollector",
"metrics::mojom::LeakDetector",
"mojom::ModuleEventSink",
"rappor::mojom::RapporRecorder",
@@ -25,7 +26,7 @@
]
},
"requires": {
- "accessibility_autoclick": [ "ash:autoclick" ],
+ "autoclick_app": [ "chromeos:autoclick" ],
"ash": [ "system_ui", "test", "display" ],
// Only used in classic ash case.
"ash_pref_connector": [ "pref_connector" ],
@@ -49,7 +50,7 @@
"patch": [ "patch_file" ],
"pdf_compositor": [ "compositor" ],
"profile_import": [ "import" ],
- "profiling": [ "profiling" ],
+ "profiling": [ "profiling", "heap_profiler" ],
"proxy_resolver": [ "factory" ],
"preferences": [ "pref_client", "pref_control" ],
"removable_storage_writer": [ "removable_storage_writer" ],
@@ -59,6 +60,7 @@
"input_device_controller",
"window_manager"
],
+ "unzip": [ "unzip_file" ],
"util_win" : [ "shell_util_win" ],
"wifi_util_win": [ "wifi_credentials" ]
}
@@ -84,6 +86,7 @@
"media_router::mojom::MediaRouter",
"page_load_metrics::mojom::PageLoadMetrics",
"password_manager::mojom::CredentialManager",
+ "safe_browsing::mojom::PhishingDetectorClient",
"translate::mojom::ContentTranslateDriver",
// TODO(beng): These should be moved to a separate capability.
diff --git a/chromium/chrome/browser/chrome_content_gpu_manifest_overlay.json b/chromium/chrome/browser/chrome_content_gpu_manifest_overlay.json
index 0d7c7c15121..9f8e9f0355b 100644
--- a/chromium/chrome/browser/chrome_content_gpu_manifest_overlay.json
+++ b/chromium/chrome/browser/chrome_content_gpu_manifest_overlay.json
@@ -9,7 +9,6 @@
"arc::mojom::VideoDecodeClient",
"arc::mojom::VideoEncodeAccelerator",
"arc::mojom::VideoEncodeClient",
- "chrome::mojom::ResourceUsageReporter",
"profiling::mojom::ProfilingClient"
]
}
diff --git a/chromium/chrome/browser/chrome_content_plugin_manifest_overlay.json b/chromium/chrome/browser/chrome_content_plugin_manifest_overlay.json
index 6d04b5f4a8e..c47eb05bb7e 100644
--- a/chromium/chrome/browser/chrome_content_plugin_manifest_overlay.json
+++ b/chromium/chrome/browser/chrome_content_plugin_manifest_overlay.json
@@ -4,7 +4,6 @@
"service_manager:connector": {
"provides": {
"browser": [
- "chrome::mojom::ResourceUsageReporter"
]
}
}
diff --git a/chromium/chrome/browser/chrome_content_renderer_manifest_overlay.json b/chromium/chrome/browser/chrome_content_renderer_manifest_overlay.json
index d5b3e090519..6e9f7efb631 100644
--- a/chromium/chrome/browser/chrome_content_renderer_manifest_overlay.json
+++ b/chromium/chrome/browser/chrome_content_renderer_manifest_overlay.json
@@ -5,14 +5,9 @@
"service_manager:connector": {
"provides": {
"browser": [
- "chrome::mojom::ResourceUsageReporter",
"chrome::mojom::SearchBouncer",
- "spellcheck::mojom::SpellChecker",
"profiling::mojom::ProfilingClient"
]
- },
- "requires": {
- "chrome": [ "renderer" ]
}
},
"navigation:frame": {
@@ -25,8 +20,11 @@
"chrome::mojom::ChromeRenderFrame",
"chrome::mojom::ContentSettingsRenderer",
"chrome::mojom::PrerenderDispatcher",
+ "chrome::mojom::SandboxStatusExtension",
"dom_distiller::mojom::DistillerPageNotifierService",
"extensions::mojom::AppWindow",
+ "safe_browsing::mojom::ThreatReporter",
+ "safe_browsing::mojom::PhishingDetector",
"spellcheck::mojom::SpellCheckPanel"
]
}
diff --git a/chromium/chrome/browser/chrome_content_utility_manifest_overlay.json b/chromium/chrome/browser/chrome_content_utility_manifest_overlay.json
index b0eec0e6e40..5bd06f880e3 100644
--- a/chromium/chrome/browser/chrome_content_utility_manifest_overlay.json
+++ b/chromium/chrome/browser/chrome_content_utility_manifest_overlay.json
@@ -6,7 +6,6 @@
"browser": [
"chrome::mojom::DialDeviceDescriptionParser",
"chrome::mojom::ProfileImport",
- "chrome::mojom::ResourceUsageReporter",
"chrome::mojom::ShellHandler",
"extensions::mojom::ExtensionUnpacker",
"extensions::mojom::ManifestParser",
diff --git a/chromium/chrome/browser/chrome_notification_types.h b/chromium/chrome/browser/chrome_notification_types.h
index 6ca55171b3b..baaa3be6440 100644
--- a/chromium/chrome/browser/chrome_notification_types.h
+++ b/chromium/chrome/browser/chrome_notification_types.h
@@ -318,8 +318,6 @@ enum NotificationType {
// NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE
// 4. Boot into retail mode
// NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE
- // 5. Boot into kiosk mode
- // NOTIFICATION_KIOSK_APP_LAUNCHED
NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
// Send when kiosk auto-launch warning screen is visible.
@@ -340,9 +338,6 @@ enum NotificationType {
// Sent when kiosk app list is loaded in UI.
NOTIFICATION_KIOSK_APPS_LOADED,
- // Sent when a kiosk app is launched.
- NOTIFICATION_KIOSK_APP_LAUNCHED,
-
// Sent when the user list has changed.
NOTIFICATION_USER_LIST_CHANGED,
diff --git a/chromium/chrome/browser/chromeos/BUILD.gn b/chromium/chrome/browser/chromeos/BUILD.gn
index c484e216a46..bc72201a1ca 100644
--- a/chromium/chrome/browser/chromeos/BUILD.gn
+++ b/chromium/chrome/browser/chromeos/BUILD.gn
@@ -4,6 +4,7 @@
import("//build/config/features.gni")
import("//build/config/ui.gni")
+import("//chromeos/assistant/assistant.gni")
import("//extensions/features/features.gni")
import("//media/media_options.gni")
import("//printing/features/features.gni")
@@ -40,12 +41,12 @@ source_set("chromeos") {
deps = [
# TODO(tbarzic): Cleanup this list.
":attestation_proto",
+ ":backdrop_wallpaper_proto",
":device_policy_remover_generated",
":user_activity_event_proto",
"//apps",
"//ash",
"//ash:ash_with_content",
- "//ash/autoclick/mus/public/interfaces",
"//ash/public/cpp",
"//cc/paint",
"//chrome/app:command_ids",
@@ -53,6 +54,7 @@ source_set("chromeos") {
"//chrome/browser:rlz",
"//chrome/browser/devtools",
"//chrome/browser/extensions",
+ "//chrome/browser/resource_coordinator:tab_metrics_event_proto",
"//chrome/browser/safe_browsing:chunk_proto",
"//chrome/common",
"//chrome/common/extensions/api",
@@ -66,12 +68,14 @@ source_set("chromeos") {
"//chromeos:biod_proto",
"//chromeos:cryptohome_proto",
"//chromeos:cryptohome_signkey_proto",
+ "//chromeos/assistant:buildflags",
"//chromeos/components/tether",
"//components/arc",
"//components/browser_sync",
"//components/captive_portal",
"//components/certificate_reporting:cert_logger_proto",
"//components/component_updater:crl_set_remover",
+ "//components/consent_auditor:consent_auditor",
"//components/constrained_window",
"//components/content_settings/core/browser",
"//components/crash/content/app",
@@ -92,6 +96,7 @@ source_set("chromeos") {
"//components/keep_alive_registry",
"//components/keyed_service/content",
"//components/keyed_service/core",
+ "//components/language/core/common",
"//components/login",
"//components/metrics:serialization",
"//components/metrics/leak_detector",
@@ -122,7 +127,10 @@ source_set("chromeos") {
"//components/tracing:startup_tracing",
"//components/ukm/content",
"//components/user_manager",
+ "//components/viz/host",
+ "//services/identity/public/cpp",
"//services/metrics/public/cpp:ukm_builders",
+ "//services/resource_coordinator/public/cpp:resource_coordinator_cpp",
"//third_party/fontconfig",
"//third_party/metrics_proto",
@@ -143,21 +151,21 @@ source_set("chromeos") {
"//device/bluetooth",
"//device/media_transfer_protocol",
"//device/usb/public/cpp",
- "//device/usb/public/interfaces",
+ "//device/usb/public/mojom",
"//extensions/browser",
"//extensions/browser/kiosk",
"//gpu",
"//gpu/ipc/host",
"//gpu/ipc/service",
- "//mash/public/interfaces",
+ "//mash/public/mojom",
"//media",
"//media/mojo/interfaces",
"//mojo/common",
"//net",
"//ppapi/proxy:ipc", # For PpapiMsg_LoadPlugin
"//services/data_decoder/public/cpp",
- "//services/device/public/interfaces",
- "//services/preferences/public/interfaces",
+ "//services/device/public/mojom",
+ "//services/preferences/public/mojom",
"//services/service_manager/public/cpp",
"//services/service_manager/runner/common",
"//services/ui/public/interfaces/display",
@@ -170,7 +178,7 @@ source_set("chromeos") {
"//skia",
"//storage/browser",
"//storage/common",
- "//third_party/WebKit/common:blink_common",
+ "//third_party/WebKit/public/common",
"//third_party/adobe/flash:flapper_version_h",
"//third_party/cacheinvalidation",
"//third_party/icu",
@@ -213,10 +221,6 @@ source_set("chromeos") {
"//url",
]
- data_deps = [
- "//ash/autoclick/mus:accessibility_autoclick",
- ]
-
allow_circular_includes_from = [ "//chrome/browser/extensions" ]
sources = [
@@ -230,8 +234,6 @@ source_set("chromeos") {
"../supervised_user/chromeos/supervised_user_password_service_factory.h",
"accessibility/accessibility_extension_loader.cc",
"accessibility/accessibility_extension_loader.h",
- "accessibility/accessibility_highlight_manager.cc",
- "accessibility/accessibility_highlight_manager.h",
"accessibility/accessibility_manager.cc",
"accessibility/accessibility_manager.h",
"accessibility/chromevox_panel.cc",
@@ -303,8 +305,6 @@ source_set("chromeos") {
"arc/accessibility/arc_accessibility_helper_bridge.h",
"arc/accessibility/ax_tree_source_arc.cc",
"arc/accessibility/ax_tree_source_arc.h",
- "arc/arc_auth_notification.cc",
- "arc/arc_auth_notification.h",
"arc/arc_migration_constants.h",
"arc/arc_migration_guide_notification.cc",
"arc/arc_migration_guide_notification.h",
@@ -425,6 +425,10 @@ source_set("chromeos") {
"arc/process/arc_process.h",
"arc/process/arc_process_service.cc",
"arc/process/arc_process_service.h",
+ "arc/screen_capture/arc_screen_capture_bridge.cc",
+ "arc/screen_capture/arc_screen_capture_bridge.h",
+ "arc/screen_capture/arc_screen_capture_session.cc",
+ "arc/screen_capture/arc_screen_capture_session.h",
"arc/tracing/arc_tracing_bridge.cc",
"arc/tracing/arc_tracing_bridge.h",
"arc/tts/arc_tts_service.cc",
@@ -457,8 +461,6 @@ source_set("chromeos") {
"attestation/platform_verification_flow.h",
"authpolicy/auth_policy_credentials_manager.cc",
"authpolicy/auth_policy_credentials_manager.h",
- "background/ash_wallpaper_delegate.cc",
- "background/ash_wallpaper_delegate.h",
"base/file_flusher.cc",
"base/file_flusher.h",
"base/locale_util.cc",
@@ -504,6 +506,8 @@ source_set("chromeos") {
"dbus/chrome_proxy_resolution_service_provider_delegate.h",
"dbus/chrome_virtual_file_request_service_provider_delegate.cc",
"dbus/chrome_virtual_file_request_service_provider_delegate.h",
+ "dbus/finch_features_service_provider_delegate.cc",
+ "dbus/finch_features_service_provider_delegate.h",
"dbus/kiosk_info_service_provider.cc",
"dbus/kiosk_info_service_provider.h",
"dbus/screen_lock_service_provider.cc",
@@ -520,6 +524,8 @@ source_set("chromeos") {
"display/output_protection_delegate.h",
"display/quirks_manager_delegate_impl.cc",
"display/quirks_manager_delegate_impl.h",
+ "docked_magnifier/docked_magnifier_client.cc",
+ "docked_magnifier/docked_magnifier_client.h",
"drive/debug_info_collector.cc",
"drive/debug_info_collector.h",
"drive/download_handler.cc",
@@ -1025,8 +1031,6 @@ source_set("chromeos") {
"login/screens/wrong_hwid_screen.cc",
"login/screens/wrong_hwid_screen.h",
"login/screens/wrong_hwid_screen_view.h",
- "login/session/app_terminating_stack_dumper.cc",
- "login/session/app_terminating_stack_dumper.h",
"login/session/chrome_session_manager.cc",
"login/session/chrome_session_manager.h",
"login/session/user_session_manager.cc",
@@ -1068,8 +1072,6 @@ source_set("chromeos") {
"login/startup_utils.h",
"login/supervised/supervised_user_authentication.cc",
"login/supervised/supervised_user_authentication.h",
- "login/supervised/supervised_user_authenticator.cc",
- "login/supervised/supervised_user_authenticator.h",
"login/supervised/supervised_user_constants.cc",
"login/supervised/supervised_user_constants.h",
"login/supervised/supervised_user_creation_controller.cc",
@@ -1086,12 +1088,16 @@ source_set("chromeos") {
"login/ui/captive_portal_view.h",
"login/ui/captive_portal_window_proxy.cc",
"login/ui/captive_portal_window_proxy.h",
+ "login/ui/gaia_dialog_delegate.cc",
+ "login/ui/gaia_dialog_delegate.h",
"login/ui/input_events_blocker.cc",
"login/ui/input_events_blocker.h",
"login/ui/login_display.cc",
"login/ui/login_display.h",
"login/ui/login_display_host.cc",
"login/ui/login_display_host.h",
+ "login/ui/login_display_host_common.cc",
+ "login/ui/login_display_host_common.h",
"login/ui/login_display_host_views.cc",
"login/ui/login_display_host_views.h",
"login/ui/login_display_host_webui.cc",
@@ -1150,8 +1156,6 @@ source_set("chromeos") {
"login/users/supervised_user_manager_impl.cc",
"login/users/supervised_user_manager_impl.h",
"login/users/user_manager_interface.h",
- "login/users/wallpaper/wallpaper_manager.cc",
- "login/users/wallpaper/wallpaper_manager.h",
"login/version_info_updater.cc",
"login/version_info_updater.h",
"login/wizard_controller.cc",
@@ -1232,6 +1236,18 @@ source_set("chromeos") {
"policy/affiliated_invalidation_service_provider_impl.h",
"policy/android_management_client.cc",
"policy/android_management_client.h",
+ "policy/app_install_event_log.cc",
+ "policy/app_install_event_log.h",
+ "policy/app_install_event_log_collector.cc",
+ "policy/app_install_event_log_collector.h",
+ "policy/app_install_event_log_manager.cc",
+ "policy/app_install_event_log_manager.h",
+ "policy/app_install_event_log_manager_wrapper.cc",
+ "policy/app_install_event_log_manager_wrapper.h",
+ "policy/app_install_event_log_uploader.cc",
+ "policy/app_install_event_log_uploader.h",
+ "policy/app_install_event_logger.cc",
+ "policy/app_install_event_logger.h",
"policy/auto_enrollment_client.cc",
"policy/auto_enrollment_client.h",
"policy/bluetooth_policy_handler.cc",
@@ -1342,14 +1358,16 @@ source_set("chromeos") {
"policy/server_backed_device_state.h",
"policy/server_backed_state_keys_broker.cc",
"policy/server_backed_state_keys_broker.h",
+ "policy/single_app_install_event_log.cc",
+ "policy/single_app_install_event_log.h",
"policy/status_uploader.cc",
"policy/status_uploader.h",
"policy/system_log_uploader.cc",
"policy/system_log_uploader.h",
+ "policy/temp_certs_cache_nss.cc",
+ "policy/temp_certs_cache_nss.h",
"policy/ticl_device_settings_provider.cc",
"policy/ticl_device_settings_provider.h",
- "policy/untrusted_authority_certs_cache.cc",
- "policy/untrusted_authority_certs_cache.h",
"policy/upload_job.h",
"policy/upload_job_impl.cc",
"policy/upload_job_impl.h",
@@ -1381,8 +1399,13 @@ source_set("chromeos") {
"power/idle_action_warning_dialog_view.h",
"power/idle_action_warning_observer.cc",
"power/idle_action_warning_observer.h",
+ "power/ml/boot_clock.h",
"power/ml/idle_event_notifier.cc",
"power/ml/idle_event_notifier.h",
+ "power/ml/real_boot_clock.cc",
+ "power/ml/real_boot_clock.h",
+ "power/ml/recent_events_counter.cc",
+ "power/ml/recent_events_counter.h",
"power/ml/user_activity_logger.cc",
"power/ml/user_activity_logger.h",
"power/ml/user_activity_logger_delegate.h",
@@ -1504,6 +1527,8 @@ source_set("chromeos") {
"smb_client/smb_service.h",
"smb_client/smb_service_factory.cc",
"smb_client/smb_service_factory.h",
+ "smb_client/temp_file_manager.cc",
+ "smb_client/temp_file_manager.h",
"status/network_menu.cc",
"status/network_menu.h",
"system/automatic_reboot_manager.cc",
@@ -1544,8 +1569,8 @@ source_set("chromeos") {
"system_logs/single_debug_daemon_log_source.h",
"system_logs/single_log_file_log_source.cc",
"system_logs/single_log_file_log_source.h",
+ "system_logs/touch_log_source.cc",
"system_logs/touch_log_source.h",
- "system_logs/touch_log_source_ozone.cc",
"tether/fake_tether_service.cc",
"tether/fake_tether_service.h",
"tether/tether_service.cc",
@@ -1571,8 +1596,12 @@ source_set("chromeos") {
"ui/screen_capture_notification_ui_chromeos.h",
"upgrade_detector_chromeos.cc",
"upgrade_detector_chromeos.h",
+ "virtual_machines/virtual_machines_util.cc",
+ "virtual_machines/virtual_machines_util.h",
# Extension API implementations.
+ "extensions/backdrop_wallpaper_handlers/backdrop_wallpaper_handlers.cc",
+ "extensions/backdrop_wallpaper_handlers/backdrop_wallpaper_handlers.h",
"extensions/echo_private_api.cc",
"extensions/echo_private_api.h",
"extensions/file_manager/device_event_router.cc",
@@ -1628,12 +1657,17 @@ source_set("chromeos") {
"extensions/wallpaper_api.h",
"extensions/wallpaper_function_base.cc",
"extensions/wallpaper_function_base.h",
- "extensions/wallpaper_manager_util.cc",
- "extensions/wallpaper_manager_util.h",
"extensions/wallpaper_private_api.cc",
"extensions/wallpaper_private_api.h",
]
+ if (enable_cros_assistant) {
+ deps += [
+ "//chromeos/services/assistant:lib",
+ "//chromeos/services/assistant/public/mojom",
+ ]
+ }
+
if (use_cups) {
sources += [
"printing/cups_print_job_manager_impl.cc",
@@ -1731,6 +1765,7 @@ source_set("unit_tests") {
"accessibility/spoken_feedback_event_rewriter_unittest.cc",
"app_mode/startup_app_launcher_unittest.cc",
"arc/accessibility/arc_accessibility_helper_bridge_unittest.cc",
+ "arc/accessibility/ax_tree_source_arc_unittest.cc",
"arc/arc_play_store_enabled_preference_handler_unittest.cc",
"arc/arc_session_manager_unittest.cc",
"arc/arc_support_host_unittest.cc",
@@ -1885,8 +1920,6 @@ source_set("unit_tests") {
"login/users/affiliation_unittest.cc",
"login/users/multi_profile_user_controller_unittest.cc",
"login/users/user_manager_unittest.cc",
- "login/users/wallpaper/wallpaper_manager_test_utils.cc",
- "login/users/wallpaper/wallpaper_manager_test_utils.h",
"mobile/mobile_activator_unittest.cc",
"mobile_config_unittest.cc",
"net/cert_verify_proc_chromeos_unittest.cc",
@@ -1906,6 +1939,12 @@ source_set("unit_tests") {
"policy/affiliated_cloud_policy_invalidator_unittest.cc",
"policy/affiliated_invalidation_service_provider_impl_unittest.cc",
"policy/android_management_client_unittest.cc",
+ "policy/app_install_event_log_collector_unittest.cc",
+ "policy/app_install_event_log_manager_unittest.cc",
+ "policy/app_install_event_log_manager_wrapper_unittest.cc",
+ "policy/app_install_event_log_unittest.cc",
+ "policy/app_install_event_log_uploader_unittest.cc",
+ "policy/app_install_event_logger_unittest.cc",
"policy/auto_enrollment_client_unittest.cc",
"policy/bluetooth_policy_handler_unittest.cc",
"policy/cached_policy_key_loader_chromeos_unittest.cc",
@@ -1935,15 +1974,20 @@ source_set("unit_tests") {
"policy/remote_commands/device_command_set_volume_job_unittest.cc",
"policy/secondary_google_account_signin_policy_handler_unittest.cc",
"policy/server_backed_state_keys_broker_unittest.cc",
+ "policy/single_app_install_event_log_unittest.cc",
"policy/status_uploader_unittest.cc",
"policy/system_log_uploader_unittest.cc",
- "policy/untrusted_authority_certs_cache_unittest.cc",
+ "policy/temp_certs_cache_nss_unittest.cc",
"policy/upload_job_unittest.cc",
"policy/user_cloud_policy_manager_chromeos_unittest.cc",
"policy/user_cloud_policy_store_chromeos_unittest.cc",
"power/cpu_data_collector_unittest.cc",
"power/extension_event_observer_unittest.cc",
+ "power/ml/fake_boot_clock.cc",
+ "power/ml/fake_boot_clock.h",
"power/ml/idle_event_notifier_unittest.cc",
+ "power/ml/real_boot_clock_unittest.cc",
+ "power/ml/recent_events_counter_unittest.cc",
"power/ml/user_activity_logger_delegate_ukm_unittest.cc",
"power/ml/user_activity_logger_unittest.cc",
"power/power_data_collector_unittest.cc",
@@ -1958,7 +2002,6 @@ source_set("unit_tests") {
"printing/printers_sync_bridge_unittest.cc",
"printing/specifics_translation_unittest.cc",
"printing/synced_printers_manager_unittest.cc",
- "printing/usb_printer_detector_unittest.cc",
"printing/zeroconf_printer_detector_unittest.cc",
"profiles/profile_list_chromeos_unittest.cc",
"proxy_config_service_impl_unittest.cc",
@@ -1974,11 +2017,13 @@ source_set("unit_tests") {
"settings/shutdown_policy_handler_unittest.cc",
"settings/stub_cros_settings_provider_unittest.cc",
"smb_client/smb_service_unittest.cc",
+ "smb_client/temp_file_manager_unittest.cc",
"system/automatic_reboot_manager_unittest.cc",
"system/device_disabling_manager_unittest.cc",
"system_logs/single_debug_daemon_log_source_unittest.cc",
"system_logs/single_log_file_log_source_unittest.cc",
"tether/tether_service_unittest.cc",
+ "tpm_firmware_update_unittest.cc",
"ui/idle_app_name_notification_view_unittest.cc",
"ui/low_disk_notification_unittest.cc",
@@ -2095,6 +2140,13 @@ proto_library("user_activity_event_proto") {
]
}
+proto_library("backdrop_wallpaper_proto") {
+ sources = [
+ "extensions/backdrop_wallpaper_handlers/backdrop_wallpaper.proto",
+ ]
+ generate_python = false
+}
+
service_manifest("ash_pref_connector_manifest") {
name = "ash_pref_connector"
source = "prefs/ash_pref_connector_manifest.json"
diff --git a/chromium/chrome/browser/custom_handlers/OWNERS b/chromium/chrome/browser/custom_handlers/OWNERS
index 9fd573abbf2..f4a612fe88c 100644
--- a/chromium/chrome/browser/custom_handlers/OWNERS
+++ b/chromium/chrome/browser/custom_handlers/OWNERS
@@ -1 +1,2 @@
benwells@chromium.org
+mgiuca@chromium.org
diff --git a/chromium/chrome/browser/custom_handlers/protocol_handler_registry.cc b/chromium/chrome/browser/custom_handlers/protocol_handler_registry.cc
index 608da379d81..d3de61c47ba 100644
--- a/chromium/chrome/browser/custom_handlers/protocol_handler_registry.cc
+++ b/chromium/chrome/browser/custom_handlers/protocol_handler_registry.cc
@@ -567,6 +567,11 @@ bool ProtocolHandlerRegistry::IsHandledProtocol(
void ProtocolHandlerRegistry::RemoveHandler(
const ProtocolHandler& handler) {
+ if (IsIgnored(handler)) {
+ RemoveIgnoredHandler(handler);
+ return;
+ }
+
DCHECK_CURRENTLY_ON(BrowserThread::UI);
ProtocolHandlerList& handlers = protocol_handlers_[handler.protocol()];
bool erase_success = false;
@@ -751,7 +756,7 @@ base::Value* ProtocolHandlerRegistry::EncodeRegisteredHandlers() {
j != i->second.end(); ++j) {
std::unique_ptr<base::DictionaryValue> encoded = j->Encode();
if (IsDefault(*j)) {
- encoded->Set("default", base::MakeUnique<base::Value>(true));
+ encoded->Set("default", std::make_unique<base::Value>(true));
}
protocol_handlers->Append(std::move(encoded));
}
diff --git a/chromium/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc b/chromium/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
index 5b47bd09cd8..10a4f8545bf 100644
--- a/chromium/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
+++ b/chromium/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
@@ -115,7 +115,7 @@ void AssertWillHandle(
std::unique_ptr<base::DictionaryValue> GetProtocolHandlerValue(
const std::string& protocol,
const std::string& url) {
- auto value = base::MakeUnique<base::DictionaryValue>();
+ auto value = std::make_unique<base::DictionaryValue>();
value->SetString("protocol", protocol);
value->SetString("url", url);
return value;
@@ -492,6 +492,14 @@ TEST_F(ProtocolHandlerRegistryTest, TestRemoveHandler) {
registry()->RemoveHandler(ph1);
ASSERT_FALSE(registry()->IsRegistered(ph1));
ASSERT_FALSE(registry()->IsHandledProtocol("test"));
+
+ registry()->OnIgnoreRegisterProtocolHandler(ph1);
+ ASSERT_FALSE(registry()->IsRegistered(ph1));
+ ASSERT_TRUE(registry()->IsIgnored(ph1));
+
+ registry()->RemoveHandler(ph1);
+ ASSERT_FALSE(registry()->IsRegistered(ph1));
+ ASSERT_FALSE(registry()->IsIgnored(ph1));
}
TEST_F(ProtocolHandlerRegistryTest, TestIsRegistered) {
@@ -992,6 +1000,70 @@ TEST_F(ProtocolHandlerRegistryTest, TestPrefPolicyOverlapIgnore) {
ASSERT_EQ(InMemoryIgnoredHandlerCount(), 4);
}
+TEST_F(ProtocolHandlerRegistryTest, TestURIPercentEncoding) {
+ ProtocolHandler ph =
+ CreateProtocolHandler("web+custom", GURL("https://test.com/url=%s"));
+ registry()->OnAcceptRegisterProtocolHandler(ph);
+
+ // Normal case.
+ GURL translated_url = ph.TranslateUrl(GURL("web+custom://custom/handler"));
+ ASSERT_EQ(translated_url,
+ GURL("https://test.com/url=web%2Bcustom%3A%2F%2Fcustom%2Fhandler"));
+
+ // Percent-encoding.
+ translated_url = ph.TranslateUrl(GURL("web+custom://custom/%20handler"));
+ ASSERT_EQ(
+ translated_url,
+ GURL("https://test.com/url=web%2Bcustom%3A%2F%2Fcustom%2F%2520handler"));
+
+ // Space character.
+ translated_url = ph.TranslateUrl(GURL("web+custom://custom handler"));
+ // TODO(mgiuca): Check whether this(' ') should be encoded as '%20'.
+ ASSERT_EQ(translated_url,
+ GURL("https://test.com/url=web%2Bcustom%3A%2F%2Fcustom+handler"));
+
+ // Query parameters.
+ translated_url = ph.TranslateUrl(GURL("web+custom://custom?foo=bar&bar=baz"));
+ ASSERT_EQ(translated_url,
+ GURL("https://test.com/"
+ "url=web%2Bcustom%3A%2F%2Fcustom%3Ffoo%3Dbar%26bar%3Dbaz"));
+
+ // Non-ASCII characters.
+ translated_url = ph.TranslateUrl(GURL("web+custom://custom/<>`{}#?\"'😂"));
+ ASSERT_EQ(translated_url, GURL("https://test.com/"
+ "url=web%2Bcustom%3A%2F%2Fcustom%2F%3C%3E%60%"
+ "7B%7D%23%3F%22'%25F0%259F%2598%2582"));
+
+ // C0 characters. GURL constructor encodes U+001F as "%1F" first, because
+ // U+001F is an illegal char. Then the protocol handler translator encodes it
+ // to "%251F" again. That's why the expected result has double-encoded URL.
+ translated_url = ph.TranslateUrl(GURL("web+custom://custom/\x1fhandler"));
+ ASSERT_EQ(
+ translated_url,
+ GURL("https://test.com/url=web%2Bcustom%3A%2F%2Fcustom%2F%251Fhandler"));
+
+ // Control characters.
+ // TODO(crbug.com/809852): Check why non-special URLs don't encode any
+ // characters above U+001F.
+ translated_url = ph.TranslateUrl(GURL("web+custom://custom/\x7Fhandler"));
+ ASSERT_EQ(
+ translated_url,
+ GURL("https://test.com/url=web%2Bcustom%3A%2F%2Fcustom%2F%7Fhandler"));
+
+ // Path percent-encode set.
+ translated_url =
+ ph.TranslateUrl(GURL("web+custom://custom/handler=#download"));
+ ASSERT_EQ(translated_url,
+ GURL("https://test.com/"
+ "url=web%2Bcustom%3A%2F%2Fcustom%2Fhandler%3D%23download"));
+
+ // Userinfo percent-encode set.
+ translated_url = ph.TranslateUrl(GURL("web+custom://custom/handler:@id="));
+ ASSERT_EQ(translated_url,
+ GURL("https://test.com/"
+ "url=web%2Bcustom%3A%2F%2Fcustom%2Fhandler%3A%40id%3D"));
+}
+
TEST_F(ProtocolHandlerRegistryTest, TestMultiplePlaceholders) {
ProtocolHandler ph =
CreateProtocolHandler("test", GURL("http://example.com/%s/url=%s"));
diff --git a/chromium/chrome/browser/devtools/OWNERS b/chromium/chrome/browser/devtools/OWNERS
index 8d426acaa95..1329473478e 100644
--- a/chromium/chrome/browser/devtools/OWNERS
+++ b/chromium/chrome/browser/devtools/OWNERS
@@ -1,5 +1,6 @@
dgozman@chromium.org
pfeldman@chromium.org
+caseq@chromium.org
per-file devtools_embedder_message_dispatcher.*=set noparent
per-file devtools_embedder_message_dispatcher.*=file://ipc/SECURITY_OWNERS
diff --git a/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc b/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
index 22a76735980..35ee33fd445 100644
--- a/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
+++ b/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
@@ -132,10 +132,10 @@ bool ChromeDevToolsManagerDelegate::HandleCommand(
std::string ChromeDevToolsManagerDelegate::GetTargetType(
content::WebContents* web_contents) {
- for (TabContentsIterator it; !it.done(); it.Next()) {
- if (*it == web_contents)
- return DevToolsAgentHost::kTypePage;
- }
+ auto& all_tabs = AllTabContentses();
+ auto it = std::find(all_tabs.begin(), all_tabs.end(), web_contents);
+ if (it != all_tabs.end())
+ return DevToolsAgentHost::kTypePage;
std::string extension_name;
std::string extension_type;
@@ -184,9 +184,8 @@ std::string ChromeDevToolsManagerDelegate::GetDiscoveryPageHTML() {
.as_string();
}
-std::string ChromeDevToolsManagerDelegate::GetFrontendResource(
- const std::string& path) {
- return content::DevToolsFrontendHost::GetFrontendResource(path).as_string();
+bool ChromeDevToolsManagerDelegate::HasBundledFrontendResources() {
+ return true;
}
void ChromeDevToolsManagerDelegate::DevToolsAgentHostAttached(
diff --git a/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h b/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h
index 9e837b163f2..6b81036cbe9 100644
--- a/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h
+++ b/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h
@@ -50,7 +50,7 @@ class ChromeDevToolsManagerDelegate :
scoped_refptr<content::DevToolsAgentHost> CreateNewTarget(
const GURL& url) override;
std::string GetDiscoveryPageHTML() override;
- std::string GetFrontendResource(const std::string& path) override;
+ bool HasBundledFrontendResources() override;
// content::DevToolsAgentHostObserver overrides.
void DevToolsAgentHostAttached(
diff --git a/chromium/chrome/browser/devtools/chrome_devtools_session.cc b/chromium/chrome/browser/devtools/chrome_devtools_session.cc
index 6dd16ded0f9..2ee5b2a5d63 100644
--- a/chromium/chrome/browser/devtools/chrome_devtools_session.cc
+++ b/chromium/chrome/browser/devtools/chrome_devtools_session.cc
@@ -20,7 +20,8 @@ ChromeDevToolsSession::ChromeDevToolsSession(
client_(client),
dispatcher_(std::make_unique<protocol::UberDispatcher>(this)) {
dispatcher_->setFallThroughForNotFound(true);
- if (agent_host->GetWebContents()) {
+ if (agent_host->GetWebContents() &&
+ agent_host->GetType() == content::DevToolsAgentHost::kTypePage) {
page_handler_ = std::make_unique<PageHandler>(agent_host->GetWebContents(),
dispatcher_.get());
}
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 2b91e7dc1cf..f91488647fc 100644
--- a/chromium/chrome/browser/devtools/device/adb/mock_adb_server.cc
+++ b/chromium/chrome/browser/devtools/device/adb/mock_adb_server.cc
@@ -29,6 +29,7 @@
#include "net/log/net_log_source.h"
#include "net/socket/stream_socket.h"
#include "net/socket/tcp_server_socket.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
using content::BrowserThread;
@@ -343,9 +344,9 @@ void SimpleHttpServer::Connection::WriteData() {
output_buffer_->offset() + bytes_to_write_) << "Overflow";
int write_result = socket_->Write(
- output_buffer_.get(),
- bytes_to_write_,
- base::Bind(&Connection::OnDataWritten, base::Unretained(this)));
+ output_buffer_.get(), bytes_to_write_,
+ base::Bind(&Connection::OnDataWritten, base::Unretained(this)),
+ TRAFFIC_ANNOTATION_FOR_TESTS);
if (write_result != net::ERR_IO_PENDING)
OnDataWritten(write_result);
@@ -444,7 +445,7 @@ class AdbParser : public SimpleHttpServer::Parser,
Send("FAIL", "device offline (x)");
} else {
mock_connection_ =
- base::MakeUnique<MockAndroidConnection>(this, serial_, command);
+ std::make_unique<MockAndroidConnection>(this, serial_, command);
}
}
diff --git a/chromium/chrome/browser/devtools/device/android_device_manager.cc b/chromium/chrome/browser/devtools/device/android_device_manager.cc
index 18327ea762f..6f1d6059e0d 100644
--- a/chromium/chrome/browser/devtools/device/android_device_manager.cc
+++ b/chromium/chrome/browser/devtools/device/android_device_manager.cc
@@ -35,7 +35,7 @@ static const char kModelOffline[] = "Offline";
static const char kRequestLineFormat[] = "GET %s HTTP/1.1";
-net::NetworkTrafficAnnotationTag kTrafficAnnotation =
+net::NetworkTrafficAnnotationTag kAndroidDeviceManagerTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("android_device_manager_socket", R"(
semantics {
sender: "Android Device Manager"
@@ -85,7 +85,7 @@ static void PostHttpUpgradeCallback(
std::unique_ptr<net::StreamSocket> socket) {
response_task_runner->PostTask(
FROM_HERE, base::BindOnce(callback, result, extensions, body_head,
- base::Passed(&socket)));
+ std::move(socket)));
}
class HttpRequest {
@@ -167,7 +167,7 @@ class HttpRequest {
result = socket_->Write(
request_.get(), request_->BytesRemaining(),
base::Bind(&HttpRequest::DoSendRequest, base::Unretained(this)),
- kTrafficAnnotation);
+ kAndroidDeviceManagerTrafficAnnotation);
}
}
@@ -357,7 +357,7 @@ class DevicesRequest : public base::RefCountedThreadSafe<DevicesRequest> {
friend class base::RefCountedThreadSafe<DevicesRequest>;
~DevicesRequest() {
response_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(callback_, base::Passed(&descriptors_)));
+ FROM_HERE, base::BindOnce(callback_, std::move(descriptors_)));
}
typedef std::vector<std::string> Serials;
diff --git a/chromium/chrome/browser/devtools/device/android_web_socket.cc b/chromium/chrome/browser/devtools/device/android_web_socket.cc
index 3130eb55da4..bc307ab2f8e 100644
--- a/chromium/chrome/browser/devtools/device/android_web_socket.cc
+++ b/chromium/chrome/browser/devtools/device/android_web_socket.cc
@@ -26,7 +26,7 @@ namespace {
const int kBufferSize = 16 * 1024;
const char kCloseResponse[] = "\x88\x80\x2D\x0E\x1E\xFA";
-net::NetworkTrafficAnnotationTag kTrafficAnnotation =
+net::NetworkTrafficAnnotationTag kAndroidWebSocketTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("android_web_socket", R"(
semantics {
sender: "Android Web Socket"
@@ -160,7 +160,7 @@ class AndroidDeviceManager::AndroidWebSocket::WebSocketImpl {
result = socket_->Write(buffer.get(), buffer->size(),
base::Bind(&WebSocketImpl::SendPendingRequests,
weak_factory_.GetWeakPtr()),
- kTrafficAnnotation);
+ kAndroidWebSocketTrafficAnnotation);
if (result != net::ERR_IO_PENDING)
SendPendingRequests(result);
}
diff --git a/chromium/chrome/browser/devtools/device/cast_device_provider.cc b/chromium/chrome/browser/devtools/device/cast_device_provider.cc
index 53d8e6d02dd..5a15374c740 100644
--- a/chromium/chrome/browser/devtools/device/cast_device_provider.cc
+++ b/chromium/chrome/browser/devtools/device/cast_device_provider.cc
@@ -102,30 +102,33 @@ class CastDeviceProvider::DeviceListerDelegate
if (device_lister_)
return;
service_discovery_client_ = ServiceDiscoverySharedClient::GetInstance();
- device_lister_.reset(new ServiceDiscoveryDeviceLister(
- this, service_discovery_client_.get(), kCastServiceType));
+ device_lister_ = ServiceDiscoveryDeviceLister::Create(
+ this, service_discovery_client_.get(), kCastServiceType);
device_lister_->Start();
device_lister_->DiscoverNewDevices();
}
// ServiceDiscoveryDeviceLister::Delegate implementation:
- void OnDeviceChanged(bool added,
+ void OnDeviceChanged(const std::string& service_type,
+ bool added,
const ServiceDescription& service_description) override {
- runner_->PostTask(FROM_HERE,
- base::BindOnce(&CastDeviceProvider::OnDeviceChanged,
- provider_, added, service_description));
+ runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&CastDeviceProvider::OnDeviceChanged, provider_,
+ service_type, added, service_description));
}
- void OnDeviceRemoved(const std::string& service_name) override {
+ void OnDeviceRemoved(const std::string& service_type,
+ const std::string& service_name) override {
runner_->PostTask(FROM_HERE,
base::BindOnce(&CastDeviceProvider::OnDeviceRemoved,
- provider_, service_name));
+ provider_, service_type, service_name));
}
- void OnDeviceCacheFlushed() override {
- runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&CastDeviceProvider::OnDeviceCacheFlushed, provider_));
+ void OnDeviceCacheFlushed(const std::string& service_type) override {
+ runner_->PostTask(FROM_HERE,
+ base::BindOnce(&CastDeviceProvider::OnDeviceCacheFlushed,
+ provider_, service_type));
}
private:
@@ -173,6 +176,7 @@ void CastDeviceProvider::OpenSocket(const std::string& serial,
}
void CastDeviceProvider::OnDeviceChanged(
+ const std::string& service_type,
bool added,
const ServiceDescription& service_description) {
VLOG(1) << "Device " << (added ? "added: " : "changed: ")
@@ -190,7 +194,8 @@ void CastDeviceProvider::OnDeviceChanged(
device_info_map_[host] = ServiceDescriptionToDeviceInfo(service_description);
}
-void CastDeviceProvider::OnDeviceRemoved(const std::string& service_name) {
+void CastDeviceProvider::OnDeviceRemoved(const std::string& service_type,
+ const std::string& service_name) {
VLOG(1) << "Device removed: " << service_name;
auto it = service_hostname_map_.find(service_name);
if (it == service_hostname_map_.end())
@@ -200,7 +205,7 @@ void CastDeviceProvider::OnDeviceRemoved(const std::string& service_name) {
service_hostname_map_.erase(it);
}
-void CastDeviceProvider::OnDeviceCacheFlushed() {
+void CastDeviceProvider::OnDeviceCacheFlushed(const std::string& service_type) {
VLOG(1) << "Device cache flushed";
service_hostname_map_.clear();
device_info_map_.clear();
diff --git a/chromium/chrome/browser/devtools/device/cast_device_provider.h b/chromium/chrome/browser/devtools/device/cast_device_provider.h
index f0462cabaa8..f2d81461403 100644
--- a/chromium/chrome/browser/devtools/device/cast_device_provider.h
+++ b/chromium/chrome/browser/devtools/device/cast_device_provider.h
@@ -35,10 +35,12 @@ class CastDeviceProvider
// ServiceDiscoveryDeviceLister::Delegate implementation:
void OnDeviceChanged(
+ const std::string& service_type,
bool added,
const local_discovery::ServiceDescription& service_description) override;
- void OnDeviceRemoved(const std::string& service_name) override;
- void OnDeviceCacheFlushed() override;
+ void OnDeviceRemoved(const std::string& service_type,
+ const std::string& service_name) override;
+ void OnDeviceCacheFlushed(const std::string& service_type) override;
private:
class DeviceListerDelegate;
diff --git a/chromium/chrome/browser/devtools/device/cast_device_provider_unittest.cc b/chromium/chrome/browser/devtools/device/cast_device_provider_unittest.cc
index 936d7e56099..26ce4d4b019 100644
--- a/chromium/chrome/browser/devtools/device/cast_device_provider_unittest.cc
+++ b/chromium/chrome/browser/devtools/device/cast_device_provider_unittest.cc
@@ -52,7 +52,7 @@ TEST(CastDeviceProviderTest, ServiceDiscovery) {
cast_service.metadata.push_back("md=" + cast_service_model);
ASSERT_TRUE(cast_service.ip_address.AssignFromIPLiteral("192.168.1.101"));
- device_provider_->OnDeviceChanged(true, cast_service);
+ device_provider_->OnDeviceChanged(cast_service_type, true, cast_service);
BrowserInfo exp_browser_info;
exp_browser_info.socket_name = "9222";
@@ -91,7 +91,7 @@ TEST(CastDeviceProviderTest, ServiceDiscovery) {
base::Bind(&DummyCallback, &was_run));
ASSERT_FALSE(was_run);
- device_provider_->OnDeviceChanged(true, other_service);
+ device_provider_->OnDeviceChanged(cast_service_type, true, other_service);
// Callback should not be run, since non-cast services are not discovered by
// this device provider.
@@ -100,7 +100,8 @@ TEST(CastDeviceProviderTest, ServiceDiscovery) {
ASSERT_FALSE(was_run);
// Remove the cast service.
- device_provider_->OnDeviceRemoved(cast_service.service_name);
+ device_provider_->OnDeviceRemoved(cast_service_type,
+ cast_service.service_name);
// Callback should not be run, since the cast service has been removed.
device_provider_->QueryDeviceInfo(cast_service.address.host(),
diff --git a/chromium/chrome/browser/devtools/device/devtools_android_bridge.cc b/chromium/chrome/browser/devtools/device/devtools_android_bridge.cc
index 36e084ec663..8148747b721 100644
--- a/chromium/chrome/browser/devtools/device/devtools_android_bridge.cc
+++ b/chromium/chrome/browser/devtools/device/devtools_android_bridge.cc
@@ -36,8 +36,8 @@
#include "chrome/browser/devtools/devtools_window.h"
#include "chrome/browser/devtools/remote_debugging_server.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_switches.h"
-#include "chrome/common/features.h"
#include "chrome/common/pref_names.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/prefs/pref_service.h"
@@ -72,8 +72,6 @@ bool BrowserIdFromString(const std::string& browser_id_str,
return true;
}
-static void NoOp(int, const std::string&) {}
-
} // namespace
// static
@@ -146,7 +144,7 @@ void DevToolsAndroidBridge::OpenRemotePage(scoped_refptr<RemoteBrowser> browser,
std::string query = net::EscapeQueryParamValue(url, false /* use_plus */);
std::string request =
base::StringPrintf(kNewPageRequestWithURL, query.c_str());
- SendJsonRequest(browser->GetId(), request, base::Bind(&NoOp));
+ SendJsonRequest(browser->GetId(), request, base::DoNothing());
}
DevToolsAndroidBridge::DevToolsAndroidBridge(
diff --git a/chromium/chrome/browser/devtools/device/devtools_device_discovery.cc b/chromium/chrome/browser/devtools/device/devtools_device_discovery.cc
index d0cf7a70db3..178a6467b3b 100644
--- a/chromium/chrome/browser/devtools/device/devtools_device_discovery.cc
+++ b/chromium/chrome/browser/devtools/device/devtools_device_discovery.cc
@@ -42,8 +42,6 @@ const char kPageReloadCommand[] = "Page.reload";
const char kWebViewSocketPrefix[] = "webview_devtools_remote";
-static void NoOp(int, const std::string&) {}
-
static void ScheduleTaskDefault(const base::Closure& task) {
BrowserThread::PostDelayedTask(
BrowserThread::UI,
@@ -340,7 +338,7 @@ std::string AgentHostDelegate::GetFrontendURL() {
bool AgentHostDelegate::Activate() {
std::string request = base::StringPrintf(kActivatePageRequest,
remote_id_.c_str());
- device_->SendJsonRequest(browser_id_, request, base::Bind(&NoOp));
+ device_->SendJsonRequest(browser_id_, request, base::DoNothing());
return true;
}
@@ -352,7 +350,7 @@ void AgentHostDelegate::Reload() {
bool AgentHostDelegate::Close() {
std::string request = base::StringPrintf(kClosePageRequest,
remote_id_.c_str());
- device_->SendJsonRequest(browser_id_, request, base::Bind(&NoOp));
+ device_->SendJsonRequest(browser_id_, request, base::DoNothing());
return true;
}
diff --git a/chromium/chrome/browser/devtools/device/port_forwarding_controller.cc b/chromium/chrome/browser/devtools/device/port_forwarding_controller.cc
index 6fa683b864b..55e64f29bf4 100644
--- a/chromium/chrome/browser/devtools/device/port_forwarding_controller.cc
+++ b/chromium/chrome/browser/devtools/device/port_forwarding_controller.cc
@@ -45,7 +45,7 @@ enum {
kStatusOK = 0,
};
-net::NetworkTrafficAnnotationTag kTrafficAnnotation =
+net::NetworkTrafficAnnotationTag kPortForwardingControllerTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("port_forwarding_controller_socket",
R"(
semantics {
@@ -162,7 +162,7 @@ class SocketTunnel {
result = to->Write(drainable.get(), total,
base::Bind(&SocketTunnel::OnWritten,
base::Unretained(this), drainable, from, to),
- kTrafficAnnotation);
+ kPortForwardingControllerTrafficAnnotation);
if (result != net::ERR_IO_PENDING)
OnWritten(drainable, from, to, result);
}
@@ -184,7 +184,7 @@ class SocketTunnel {
to->Write(drainable.get(), drainable->BytesRemaining(),
base::Bind(&SocketTunnel::OnWritten, base::Unretained(this),
drainable, from, to),
- kTrafficAnnotation);
+ kPortForwardingControllerTrafficAnnotation);
if (result != net::ERR_IO_PENDING)
OnWritten(drainable, from, to, result);
return;
diff --git a/chromium/chrome/browser/devtools/device/usb/android_usb_browsertest.cc b/chromium/chrome/browser/devtools/device/usb/android_usb_browsertest.cc
index 84c364f5653..d2abbfb33cd 100644
--- a/chromium/chrome/browser/devtools/device/usb/android_usb_browsertest.cc
+++ b/chromium/chrome/browser/devtools/device/usb/android_usb_browsertest.cc
@@ -305,7 +305,7 @@ class MockUsbDeviceHandle : public UsbDeviceHandle {
AdbMessage::kCommandOKAY,
std::string());
local_sockets_[current_message_->arg0] =
- base::MakeUnique<MockLocalSocket>(
+ std::make_unique<MockLocalSocket>(
base::Bind(&MockUsbDeviceHandle::WriteResponse,
base::Unretained(this), last_local_socket_,
current_message_->arg0),
@@ -563,7 +563,7 @@ class AndroidUsbCountTest : public AndroidUsbDiscoveryTest {
class AndroidUsbTraitsTest : public AndroidUsbDiscoveryTest {
protected:
std::unique_ptr<MockUsbService> CreateMockService() override {
- return base::MakeUnique<MockUsbServiceForCheckingTraits>();
+ return std::make_unique<MockUsbServiceForCheckingTraits>();
}
};
diff --git a/chromium/chrome/browser/devtools/device/usb/android_usb_device.cc b/chromium/chrome/browser/devtools/device/usb/android_usb_device.cc
index 6b2615bd800..3697b0c4cf4 100644
--- a/chromium/chrome/browser/devtools/device/usb/android_usb_device.cc
+++ b/chromium/chrome/browser/devtools/device/usb/android_usb_device.cc
@@ -379,7 +379,7 @@ void AndroidUsbDevice::InitOnCallerThread() {
if (task_runner_)
return;
task_runner_ = base::ThreadTaskRunnerHandle::Get();
- Queue(base::MakeUnique<AdbMessage>(AdbMessage::kCommandCNXN, kVersion,
+ Queue(std::make_unique<AdbMessage>(AdbMessage::kCommandCNXN, kVersion,
kMaxPayload, kHostConnectMessage));
ReadHeader();
}
@@ -398,7 +398,7 @@ void AndroidUsbDevice::Send(uint32_t command,
uint32_t arg0,
uint32_t arg1,
const std::string& body) {
- auto message = base::MakeUnique<AdbMessage>(command, arg0, arg1, body);
+ auto message = std::make_unique<AdbMessage>(command, arg0, arg1, body);
// Delay open request if not yet connected.
if (!is_connected_) {
pending_messages_.push_back(std::move(message));
@@ -524,12 +524,11 @@ void AndroidUsbDevice::ParseHeader(UsbTransferStatus status,
if (data_length == 0) {
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&AndroidUsbDevice::HandleIncoming, this,
- base::Passed(&message)));
+ std::move(message)));
} else {
task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&AndroidUsbDevice::ReadBody, this,
- base::Passed(&message), data_length, data_check));
+ FROM_HERE, base::BindOnce(&AndroidUsbDevice::ReadBody, this,
+ std::move(message), data_length, data_check));
}
}
@@ -559,9 +558,8 @@ void AndroidUsbDevice::ParseBody(std::unique_ptr<AdbMessage> message,
if (status == UsbTransferStatus::TIMEOUT) {
task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&AndroidUsbDevice::ReadBody, this,
- base::Passed(&message), data_length, data_check));
+ FROM_HERE, base::BindOnce(&AndroidUsbDevice::ReadBody, this,
+ std::move(message), data_length, data_check));
return;
}
@@ -580,7 +578,7 @@ void AndroidUsbDevice::ParseBody(std::unique_ptr<AdbMessage> message,
task_runner_->PostTask(FROM_HERE,
base::BindOnce(&AndroidUsbDevice::HandleIncoming, this,
- base::Passed(&message)));
+ std::move(message)));
}
void AndroidUsbDevice::HandleIncoming(std::unique_ptr<AdbMessage> message) {
@@ -591,18 +589,18 @@ void AndroidUsbDevice::HandleIncoming(std::unique_ptr<AdbMessage> message) {
{
DCHECK_EQ(message->arg0, static_cast<uint32_t>(AdbMessage::kAuthToken));
if (signature_sent_) {
- Queue(base::MakeUnique<AdbMessage>(
+ Queue(std::make_unique<AdbMessage>(
AdbMessage::kCommandAUTH, AdbMessage::kAuthRSAPublicKey, 0,
AndroidRSAPublicKey(rsa_key_.get())));
} else {
signature_sent_ = true;
std::string signature = AndroidRSASign(rsa_key_.get(), message->body);
if (!signature.empty()) {
- Queue(base::MakeUnique<AdbMessage>(AdbMessage::kCommandAUTH,
+ Queue(std::make_unique<AdbMessage>(AdbMessage::kCommandAUTH,
AdbMessage::kAuthSignature, 0,
signature));
} else {
- Queue(base::MakeUnique<AdbMessage>(
+ Queue(std::make_unique<AdbMessage>(
AdbMessage::kCommandAUTH, AdbMessage::kAuthRSAPublicKey, 0,
AndroidRSAPublicKey(rsa_key_.get())));
}
diff --git a/chromium/chrome/browser/devtools/devtools_eye_dropper.cc b/chromium/chrome/browser/devtools/devtools_eye_dropper.cc
index 14b8b6e7860..742b7521876 100644
--- a/chromium/chrome/browser/devtools/devtools_eye_dropper.cc
+++ b/chromium/chrome/browser/devtools/devtools_eye_dropper.cc
@@ -95,8 +95,8 @@ void DevToolsEyeDropper::UpdateFrame() {
gfx::Size should_be_rendering_size = host_->GetView()->GetViewBounds().size();
host_->GetView()->CopyFromSurface(
gfx::Rect(), should_be_rendering_size,
- base::Bind(&DevToolsEyeDropper::FrameUpdated, weak_factory_.GetWeakPtr()),
- kN32_SkColorType);
+ base::BindOnce(&DevToolsEyeDropper::FrameUpdated,
+ weak_factory_.GetWeakPtr()));
}
void DevToolsEyeDropper::ResetFrame() {
@@ -105,12 +105,11 @@ void DevToolsEyeDropper::ResetFrame() {
last_cursor_y_ = -1;
}
-void DevToolsEyeDropper::FrameUpdated(const SkBitmap& bitmap,
- content::ReadbackResponse response) {
- if (response == content::READBACK_SUCCESS) {
- frame_ = bitmap;
- UpdateCursor();
- }
+void DevToolsEyeDropper::FrameUpdated(const SkBitmap& bitmap) {
+ if (bitmap.drawsNothing())
+ return;
+ frame_ = bitmap;
+ UpdateCursor();
}
bool DevToolsEyeDropper::HandleMouseEvent(const blink::WebMouseEvent& event) {
diff --git a/chromium/chrome/browser/devtools/devtools_eye_dropper.h b/chromium/chrome/browser/devtools/devtools_eye_dropper.h
index 8d92d53c4e0..4956dce6c86 100644
--- a/chromium/chrome/browser/devtools/devtools_eye_dropper.h
+++ b/chromium/chrome/browser/devtools/devtools_eye_dropper.h
@@ -7,7 +7,6 @@
#include "base/callback.h"
#include "base/macros.h"
-#include "content/public/browser/readback_types.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/web_contents_observer.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -37,7 +36,7 @@ class DevToolsEyeDropper : public content::WebContentsObserver {
void UpdateFrame();
void ResetFrame();
- void FrameUpdated(const SkBitmap&, content::ReadbackResponse);
+ void FrameUpdated(const SkBitmap&);
bool HandleMouseEvent(const blink::WebMouseEvent& event);
void UpdateCursor();
diff --git a/chromium/chrome/browser/devtools/devtools_file_helper.cc b/chromium/chrome/browser/devtools/devtools_file_helper.cc
index 6e848f38c9a..bd03a162387 100644
--- a/chromium/chrome/browser/devtools/devtools_file_helper.cc
+++ b/chromium/chrome/browser/devtools/devtools_file_helper.cc
@@ -368,7 +368,7 @@ void DevToolsFileHelper::AddUserConfirmedFileSystem(const std::string& type,
prefs::kDevToolsFileSystemPaths);
base::DictionaryValue* file_systems_paths_value = update.Get();
file_systems_paths_value->SetWithoutPathExpansion(
- file_system_path, base::MakeUnique<base::Value>(type));
+ file_system_path, std::make_unique<base::Value>(type));
}
void DevToolsFileHelper::FailedToAddFileSystem(const std::string& error) {
diff --git a/chromium/chrome/browser/devtools/devtools_file_system_indexer_unittest.cc b/chromium/chrome/browser/devtools/devtools_file_system_indexer_unittest.cc
index 029c360f206..eddbc3a809b 100644
--- a/chromium/chrome/browser/devtools/devtools_file_system_indexer_unittest.cc
+++ b/chromium/chrome/browser/devtools/devtools_file_system_indexer_unittest.cc
@@ -5,6 +5,7 @@
#include <set>
#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/run_loop.h"
@@ -49,8 +50,8 @@ TEST_F(DevToolsFileSystemIndexerTest, BasicUsage) {
.Append(FILE_PATH_LITERAL("indexer"));
scoped_refptr<DevToolsFileSystemIndexer::FileSystemIndexingJob> job =
- indexer_->IndexPath(index_path.AsUTF8Unsafe(), base::Bind([](int) {}),
- base::Bind([](int) {}),
+ indexer_->IndexPath(index_path.AsUTF8Unsafe(), base::DoNothing(),
+ base::DoNothing(),
base::Bind(&DevToolsFileSystemIndexerTest::SetDone,
base::Unretained(this)));
diff --git a/chromium/chrome/browser/devtools/devtools_protocol.cc b/chromium/chrome/browser/devtools/devtools_protocol.cc
index 1a2e6ccbc1e..3eb8251c266 100644
--- a/chromium/chrome/browser/devtools/devtools_protocol.cc
+++ b/chromium/chrome/browser/devtools/devtools_protocol.cc
@@ -48,7 +48,7 @@ DevToolsProtocol::CreateInvalidParamsResponse(int command_id,
const std::string& param) {
std::unique_ptr<base::DictionaryValue> response(new base::DictionaryValue());
response->SetInteger(kIdParam, command_id);
- auto error_object = base::MakeUnique<base::DictionaryValue>();
+ auto error_object = std::make_unique<base::DictionaryValue>();
error_object->SetInteger(kErrorCodeParam, kErrorInvalidParams);
error_object->SetString(kErrorMessageParam,
base::StringPrintf("Missing or invalid '%s' parameter", param.c_str()));
@@ -63,7 +63,7 @@ std::unique_ptr<base::DictionaryValue> DevToolsProtocol::CreateErrorResponse(
const std::string& error_message) {
std::unique_ptr<base::DictionaryValue> response(new base::DictionaryValue());
response->SetInteger(kIdParam, command_id);
- auto error_object = base::MakeUnique<base::DictionaryValue>();
+ auto error_object = std::make_unique<base::DictionaryValue>();
error_object->SetInteger(kErrorCodeParam, kErrorServerError);
error_object->SetString(kErrorMessageParam, error_message);
response->Set(kErrorParam, std::move(error_object));
@@ -79,7 +79,7 @@ std::unique_ptr<base::DictionaryValue> DevToolsProtocol::CreateSuccessResponse(
response->SetInteger(kIdParam, command_id);
response->Set(kResultParam, result
? std::move(result)
- : base::MakeUnique<base::DictionaryValue>());
+ : std::make_unique<base::DictionaryValue>());
return response;
}
diff --git a/chromium/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chromium/chrome/browser/devtools/devtools_sanity_browsertest.cc
index a47a3d0b56a..e35920bf95e 100644
--- a/chromium/chrome/browser/devtools/devtools_sanity_browsertest.cc
+++ b/chromium/chrome/browser/devtools/devtools_sanity_browsertest.cc
@@ -17,6 +17,7 @@
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
+#include "base/optional.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
@@ -33,7 +34,6 @@
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/test_extension_dir.h"
#include "chrome/browser/extensions/unpacked_installer.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/profiles/profile.h"
@@ -77,6 +77,7 @@
#include "extensions/browser/test_extension_registry_observer.h"
#include "extensions/common/switches.h"
#include "extensions/common/value_builder.h"
+#include "extensions/test/test_extension_dir.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "net/test/url_request/url_request_mock_http_job.h"
@@ -330,6 +331,20 @@ class DevToolsSanityTest : public InProcessBrowserTest {
DevToolsWindow* window_;
};
+class SitePerProcessDevToolsSanityTest : public DevToolsSanityTest {
+ public:
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ DevToolsSanityTest::SetUpCommandLine(command_line);
+ content::IsolateAllSitesForTesting(command_line);
+ };
+
+ void SetUpOnMainThread() override {
+ DevToolsSanityTest::SetUpOnMainThread();
+ content::SetupCrossSiteRedirector(embedded_test_server());
+ ASSERT_TRUE(embedded_test_server()->Start());
+ }
+};
+
// Used to block until a dev tools window gets beforeunload event.
class DevToolsWindowBeforeUnloadObserver
: public content::WebContentsObserver {
@@ -504,7 +519,7 @@ class DevToolsExtensionTest : public DevToolsSanityTest,
const std::string& devtools_page,
const std::string& panel_iframe_src) {
test_extension_dirs_.push_back(
- base::MakeUnique<extensions::TestExtensionDir>());
+ std::make_unique<extensions::TestExtensionDir>());
extensions::TestExtensionDir* dir = test_extension_dirs_.back().get();
extensions::DictionaryBuilder manifest;
@@ -2088,3 +2103,78 @@ IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestRawHeadersWithRedirectAndHSTS) {
redirect_url.spec().c_str());
CloseDevToolsWindow();
}
+
+// Tests that OpenInNewTab filters URLs.
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestOpenInNewTabFilter) {
+ OpenDevToolsWindow(kDebuggerTestPage, false);
+ DevToolsUIBindings::Delegate* bindings_delegate_ =
+ static_cast<DevToolsUIBindings::Delegate*>(window_);
+ std::string test_url =
+ spawned_test_server()->GetURL(kDebuggerTestPage).spec();
+ const std::string self_blob_url =
+ base::StringPrintf("blob:%s", test_url.c_str());
+ const std::string self_filesystem_url =
+ base::StringPrintf("filesystem:%s", test_url.c_str());
+
+ // Pairs include a URL string and boolean whether it should be allowed.
+ std::vector<std::pair<const std::string, const std::string>> tests = {
+ {test_url, test_url},
+ {"data:,foo", "data:,foo"},
+ {"about://inspect", "about:blank"},
+ {"chrome://inspect", "about:blank"},
+ {"chrome://inspect/#devices", "about:blank"},
+ {self_blob_url, self_blob_url},
+ {"blob:chrome://inspect", "about:blank"},
+ {self_filesystem_url, self_filesystem_url},
+ {"filesystem:chrome://inspect", "about:blank"},
+ {"view-source:http://chromium.org", "about:blank"},
+ {"file:///", "about:blank"},
+ {"about://gpu", "about:blank"},
+ {"chrome://gpu", "about:blank"},
+ {"chrome://crash", "about:blank"},
+ {"", "about:blank"},
+ };
+
+ TabStripModel* tabs = browser()->tab_strip_model();
+ int i = 0;
+ for (const std::pair<const std::string, const std::string> pair : tests) {
+ bindings_delegate_->OpenInNewTab(pair.first);
+ i++;
+
+ std::string opened_url = tabs->GetWebContentsAt(i)->GetVisibleURL().spec();
+ SCOPED_TRACE(
+ base::StringPrintf("while testing URL: %s", pair.first.c_str()));
+ EXPECT_EQ(opened_url, pair.second);
+ }
+}
+
+IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsSanityTest, InspectElement) {
+ GURL url(embedded_test_server()->GetURL("a.com", "/devtools/oopif.html"));
+ ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url, 2);
+
+ std::vector<RenderFrameHost*> frames = GetInspectedTab()->GetAllFrames();
+ ASSERT_EQ(2u, frames.size());
+ ASSERT_NE(frames[0]->GetProcess(), frames[1]->GetProcess());
+ RenderFrameHost* frame_host = frames[0]->GetParent() ? frames[0] : frames[1];
+
+ DevToolsWindowCreationObserver observer;
+ DevToolsWindow::InspectElement(frame_host, 100, 100);
+ observer.WaitForLoad();
+ DevToolsWindow* window = observer.devtools_window();
+
+ DispatchOnTestSuite(window, "testInspectedElementIs", "INSPECTED-DIV");
+ DevToolsWindowTesting::CloseDevToolsWindowSync(window);
+}
+
+IN_PROC_BROWSER_TEST_F(SitePerProcessDevToolsSanityTest,
+ InputDispatchEventsToOOPIF) {
+ GURL url(
+ embedded_test_server()->GetURL("a.com", "/devtools/oopif-input.html"));
+ ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url, 2);
+ for (auto* frame : GetInspectedTab()->GetAllFrames())
+ content::WaitForChildFrameSurfaceReady(frame);
+ DevToolsWindow* window =
+ DevToolsWindowTesting::OpenDevToolsWindowSync(GetInspectedTab(), false);
+ RunTestFunction(window, "testInputDispatchEventsToOOPIF");
+ DevToolsWindowTesting::CloseDevToolsWindowSync(window);
+}
diff --git a/chromium/chrome/browser/devtools/devtools_targets_ui.cc b/chromium/chrome/browser/devtools/devtools_targets_ui.cc
index 1776c366826..a5b599c5910 100644
--- a/chromium/chrome/browser/devtools/devtools_targets_ui.cc
+++ b/chromium/chrome/browser/devtools/devtools_targets_ui.cc
@@ -243,7 +243,7 @@ void AdbTargetsUIHandler::DeviceListChanged(
kAdbDeviceIdFormat,
device->serial().c_str());
device_data->SetString(kTargetIdField, device_id);
- auto browser_list = base::MakeUnique<base::ListValue>();
+ auto browser_list = std::make_unique<base::ListValue>();
DevToolsAndroidBridge::RemoteBrowsers& browsers = device->browsers();
for (DevToolsAndroidBridge::RemoteBrowsers::iterator bit =
@@ -263,7 +263,7 @@ void AdbTargetsUIHandler::DeviceListChanged(
browser_data->SetString(kTargetIdField, browser_id);
browser_data->SetString(kTargetSourceField, source_id());
- auto page_list = base::MakeUnique<base::ListValue>();
+ auto page_list = std::make_unique<base::ListValue>();
remote_browsers_[browser_id] = browser;
for (const auto& page : browser->pages()) {
scoped_refptr<DevToolsAgentHost> host = page->CreateTarget();
@@ -338,7 +338,7 @@ DevToolsTargetsUIHandler::GetBrowserAgentHost(const std::string& browser_id) {
std::unique_ptr<base::DictionaryValue> DevToolsTargetsUIHandler::Serialize(
DevToolsAgentHost* host) {
- auto target_data = base::MakeUnique<base::DictionaryValue>();
+ auto target_data = std::make_unique<base::DictionaryValue>();
target_data->SetString(kTargetSourceField, source_id_);
target_data->SetString(kTargetIdField, host->GetId());
target_data->SetString(kTargetTypeField, host->GetType());
@@ -382,14 +382,14 @@ void PortForwardingStatusSerializer::PortStatusChanged(
base::DictionaryValue result;
for (ForwardingStatus::const_iterator sit = status.begin();
sit != status.end(); ++sit) {
- auto port_status_dict = base::MakeUnique<base::DictionaryValue>();
+ auto port_status_dict = std::make_unique<base::DictionaryValue>();
const PortStatusMap& port_status_map = sit->second;
for (PortStatusMap::const_iterator it = port_status_map.begin();
it != port_status_map.end(); ++it) {
port_status_dict->SetInteger(base::IntToString(it->first), it->second);
}
- auto device_status_dict = base::MakeUnique<base::DictionaryValue>();
+ auto device_status_dict = std::make_unique<base::DictionaryValue>();
device_status_dict->Set(kPortForwardingPorts, std::move(port_status_dict));
device_status_dict->SetString(kPortForwardingBrowserId,
sit->first->GetId());
diff --git a/chromium/chrome/browser/devtools/devtools_ui_bindings.cc b/chromium/chrome/browser/devtools/devtools_ui_bindings.cc
index 6f54f8b2bc5..d9d36269208 100644
--- a/chromium/chrome/browser/devtools/devtools_ui_bindings.cc
+++ b/chromium/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -112,7 +112,7 @@ base::LazyInstance<DevToolsUIBindingsList>::Leaky
std::unique_ptr<base::DictionaryValue> CreateFileSystemValue(
DevToolsFileHelper::FileSystem file_system) {
- auto file_system_value = base::MakeUnique<base::DictionaryValue>();
+ auto file_system_value = std::make_unique<base::DictionaryValue>();
file_system_value->SetString("type", file_system.type);
file_system_value->SetString("fileSystemName", file_system.file_system_name);
file_system_value->SetString("rootURL", file_system.root_url);
@@ -1072,7 +1072,7 @@ void DevToolsUIBindings::OnURLFetchComplete(const net::URLFetcher* source) {
DCHECK(it != pending_requests_.end());
base::DictionaryValue response;
- auto headers = base::MakeUnique<base::DictionaryValue>();
+ auto headers = std::make_unique<base::DictionaryValue>();
net::HttpResponseHeaders* rh = source->GetResponseHeaders();
response.SetInteger("statusCode", rh ? rh->response_code() : 200);
diff --git a/chromium/chrome/browser/devtools/devtools_window.cc b/chromium/chrome/browser/devtools/devtools_window.cc
index 7e1ccbc335e..29fbdebd839 100644
--- a/chromium/chrome/browser/devtools/devtools_window.cc
+++ b/chromium/chrome/browser/devtools/devtools_window.cc
@@ -22,6 +22,7 @@
#include "chrome/browser/file_select_helper.h"
#include "chrome/browser/infobars/infobar_service.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/sessions/session_tab_helper.h"
#include "chrome/browser/task_manager/web_contents_tags.h"
#include "chrome/browser/ui/browser.h"
@@ -43,6 +44,7 @@
#include "components/zoom/page_zoom.h"
#include "components/zoom/zoom_controller.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/keyboard_event_processing_result.h"
#include "content/public/browser/native_web_keyboard_event.h"
@@ -83,6 +85,16 @@ base::LazyInstance<std::vector<base::Callback<void(DevToolsWindow*)>>>::Leaky
static const char kKeyUpEventName[] = "keyup";
static const char kKeyDownEventName[] = "keydown";
+static const char kDefaultFrontendURL[] =
+ "chrome-devtools://devtools/bundled/devtools_app.html";
+static const char kNodeFrontendURL[] =
+ "chrome-devtools://devtools/bundled/node_app.html";
+static const char kWorkerFrontendURL[] =
+ "chrome-devtools://devtools/bundled/worker_app.html";
+static const char kJSFrontendURL[] =
+ "chrome-devtools://devtools/bundled/js_app.html";
+static const char kFallbackFrontendURL[] =
+ "chrome-devtools://devtools/bundled/inspector.html";
bool FindInspectedBrowserAndTabIndex(
WebContents* inspected_web_contents, Browser** browser, int* tab) {
@@ -523,6 +535,21 @@ void DevToolsWindow::OpenDevToolsWindow(
void DevToolsWindow::OpenDevToolsWindow(
scoped_refptr<content::DevToolsAgentHost> agent_host,
Profile* profile) {
+ OpenDevToolsWindow(agent_host, profile, false /* use_bundled_frontend */);
+}
+
+// static
+void DevToolsWindow::OpenDevToolsWindowWithBundledFrontend(
+ scoped_refptr<content::DevToolsAgentHost> agent_host,
+ Profile* profile) {
+ OpenDevToolsWindow(agent_host, profile, true /* use_bundled_frontend */);
+}
+
+// static
+void DevToolsWindow::OpenDevToolsWindow(
+ scoped_refptr<content::DevToolsAgentHost> agent_host,
+ Profile* profile,
+ bool use_bundled_frontend) {
if (!profile)
profile = Profile::FromBrowserContext(agent_host->GetBrowserContext());
@@ -535,14 +562,8 @@ void DevToolsWindow::OpenDevToolsWindow(
type == DevToolsAgentHost::kTypeSharedWorker;
if (!agent_host->GetFrontendURL().empty()) {
- FrontendType frontend_type = kFrontendRemote;
- if (is_worker) {
- frontend_type = kFrontendWorker;
- } else if (type == "node") {
- frontend_type = kFrontendV8;
- }
DevToolsWindow::OpenExternalFrontend(profile, agent_host->GetFrontendURL(),
- agent_host, frontend_type);
+ agent_host, use_bundled_frontend);
return;
}
@@ -605,18 +626,35 @@ void DevToolsWindow::OpenExternalFrontend(
Profile* profile,
const std::string& frontend_url,
const scoped_refptr<content::DevToolsAgentHost>& agent_host,
- FrontendType frontend_type) {
+ bool use_bundled_frontend) {
DevToolsWindow* window = FindDevToolsWindow(agent_host.get());
- if (!window) {
- window = Create(profile, nullptr, frontend_type,
- DevToolsUI::GetProxyURL(frontend_url).spec(), false,
- std::string(), std::string(), agent_host->IsAttached());
- if (!window)
- return;
- window->bindings_->AttachTo(agent_host);
- window->close_on_detach_ = false;
+ if (window) {
+ window->ScheduleShow(DevToolsToggleAction::Show());
+ return;
}
+ std::string type = agent_host->GetType();
+ if (type == "node") {
+ // Direct node targets will always open using ToT front-end.
+ window = Create(profile, nullptr, kFrontendV8, std::string(), false,
+ std::string(), std::string(), agent_host->IsAttached());
+ } else {
+ bool is_worker = type == DevToolsAgentHost::kTypeServiceWorker ||
+ type == DevToolsAgentHost::kTypeSharedWorker;
+
+ FrontendType frontend_type =
+ is_worker ? kFrontendRemoteWorker : kFrontendRemote;
+ std::string effective_frontend_url =
+ use_bundled_frontend ? kFallbackFrontendURL
+ : DevToolsUI::GetProxyURL(frontend_url).spec();
+ window =
+ Create(profile, nullptr, frontend_type, effective_frontend_url, false,
+ std::string(), std::string(), agent_host->IsAttached());
+ }
+ if (!window)
+ return;
+ window->bindings_->AttachTo(agent_host);
+ window->close_on_detach_ = false;
window->ScheduleShow(DevToolsToggleAction::Show());
}
@@ -695,17 +733,15 @@ void DevToolsWindow::InspectElement(
WebContents::FromRenderFrameHost(inspected_frame_host);
scoped_refptr<DevToolsAgentHost> agent(
DevToolsAgentHost::GetOrCreateFor(web_contents));
+ agent->InspectElement(inspected_frame_host, x, y);
bool should_measure_time = FindDevToolsWindow(agent.get()) == NULL;
base::TimeTicks start_time = base::TimeTicks::Now();
// TODO(loislo): we should initiate DevTools window opening from within
// renderer. Otherwise, we still can hit a race condition here.
OpenDevToolsWindow(web_contents, DevToolsToggleAction::ShowElementsPanel());
DevToolsWindow* window = FindDevToolsWindow(agent.get());
- if (window) {
- agent->InspectElement(window->bindings_, x, y);
- if (should_measure_time)
- window->inspect_element_start_time_ = start_time;
- }
+ if (window && should_measure_time)
+ window->inspect_element_start_time_ = start_time;
}
// static
@@ -953,6 +989,14 @@ DevToolsWindow* DevToolsWindow::Create(
base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode))
return nullptr;
+#if defined(OS_CHROMEOS)
+ // Do not create DevTools if it's disabled for primary profile.
+ const Profile* primary_profile = ProfileManager::GetPrimaryUserProfile();
+ if (primary_profile &&
+ primary_profile->GetPrefs()->GetBoolean(prefs::kDevToolsDisabled))
+ return nullptr;
+#endif
+
if (inspected_web_contents) {
// Check for a place to dock.
Browser* browser = nullptr;
@@ -989,37 +1033,50 @@ GURL DevToolsWindow::GetDevToolsURL(Profile* profile,
bool can_dock,
const std::string& panel,
bool has_other_clients) {
- std::string url(!frontend_url.empty() ? frontend_url
- : chrome::kChromeUIDevToolsURL);
- std::string url_string(url +
- ((url.find("?") == std::string::npos) ? "?" : "&"));
+ std::string url;
+
+// Modules are always bundled in CrOS.
+#if defined(OS_CHROMEOS)
+ std::string remote_base = "?";
+#else
+ std::string remote_base =
+ "?remoteBase=" + DevToolsUI::GetRemoteBaseURL().spec();
+#endif
+
+ // remoteFrontend is here for backwards compatibility only.
+ std::string remote_frontend =
+ frontend_url + ((frontend_url.find("?") == std::string::npos)
+ ? "?remoteFrontend=true"
+ : "&remoteFrontend=true");
switch (frontend_type) {
- case kFrontendRemote:
- url_string += "&remoteFrontend=true";
+ case kFrontendDefault:
+ url = kDefaultFrontendURL + remote_base;
+ if (can_dock)
+ url += "&can_dock=true";
+ if (panel.size())
+ url += "&panel=" + panel;
break;
case kFrontendWorker:
- url_string += "&isSharedWorker=true";
+ url = kWorkerFrontendURL + remote_base;
break;
- case kFrontendNode:
- url_string += "&nodeFrontend=true";
- // Fall through
case kFrontendV8:
- url_string += "&v8only=true";
+ url = kJSFrontendURL + remote_base;
break;
- case kFrontendDefault:
- default:
+ case kFrontendNode:
+ url = kNodeFrontendURL + remote_base;
+ break;
+ case kFrontendRemote:
+ url = remote_frontend;
+ break;
+ case kFrontendRemoteWorker:
+ // isSharedWorker is here for backwards compatibility only.
+ url = remote_frontend + "&isSharedWorker=true";
break;
}
- if (frontend_url.empty())
- url_string += "&remoteBase=" + DevToolsUI::GetRemoteBaseURL().spec();
- if (can_dock)
- url_string += "&can_dock=true";
- if (panel.size())
- url_string += "&panel=" + panel;
if (has_other_clients)
- url_string += "&hasOtherClients=true";
- return DevToolsUIBindings::SanitizeFrontendURL(GURL(url_string));
+ url += "&hasOtherClients=true";
+ return DevToolsUIBindings::SanitizeFrontendURL(GURL(url));
}
// static
@@ -1284,13 +1341,27 @@ void DevToolsWindow::SetIsDocked(bool dock_requested) {
}
void DevToolsWindow::OpenInNewTab(const std::string& url) {
- content::OpenURLParams params(GURL(url), content::Referrer(),
+ GURL fixed_url(url);
+ WebContents* inspected_web_contents = GetInspectedWebContents();
+ int child_id = content::ChildProcessHost::kInvalidUniqueID;
+ if (inspected_web_contents) {
+ content::RenderViewHost* render_view_host =
+ inspected_web_contents->GetRenderViewHost();
+ if (render_view_host)
+ child_id = render_view_host->GetProcess()->GetID();
+ }
+ // Use about:blank instead of an empty GURL. The browser treats an empty GURL
+ // as navigating to the home page, which may be privileged (chrome://newtab/).
+ if (!content::ChildProcessSecurityPolicy::GetInstance()->CanRequestURL(
+ child_id, fixed_url))
+ fixed_url = GURL(url::kAboutBlankURL);
+
+ content::OpenURLParams params(fixed_url, content::Referrer(),
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui::PAGE_TRANSITION_LINK, false);
- WebContents* inspected_web_contents = GetInspectedWebContents();
if (!inspected_web_contents || !inspected_web_contents->OpenURL(params)) {
chrome::ScopedTabbedBrowserDisplayer displayer(profile_);
- chrome::AddSelectedTabWithURL(displayer.browser(), GURL(url),
+ chrome::AddSelectedTabWithURL(displayer.browser(), fixed_url,
ui::PAGE_TRANSITION_LINK);
}
}
@@ -1444,7 +1515,7 @@ void DevToolsWindow::CreateDevToolsBrowser() {
if (!prefs->GetDictionary(prefs::kAppWindowPlacement)->HasKey(kDevToolsApp)) {
DictionaryPrefUpdate update(prefs, prefs::kAppWindowPlacement);
base::DictionaryValue* wp_prefs = update.Get();
- auto dev_tools_defaults = base::MakeUnique<base::DictionaryValue>();
+ auto dev_tools_defaults = std::make_unique<base::DictionaryValue>();
dev_tools_defaults->SetInteger("left", 100);
dev_tools_defaults->SetInteger("top", 100);
dev_tools_defaults->SetInteger("right", 740);
diff --git a/chromium/chrome/browser/devtools/devtools_window.h b/chromium/chrome/browser/devtools/devtools_window.h
index 9aca0915dc1..928fea3dfab 100644
--- a/chromium/chrome/browser/devtools/devtools_window.h
+++ b/chromium/chrome/browser/devtools/devtools_window.h
@@ -84,6 +84,10 @@ class DevToolsWindow : public DevToolsUIBindings::Delegate,
static void OpenDevToolsWindow(
scoped_refptr<content::DevToolsAgentHost> host,
Profile* profile);
+ // Similar to previous one, but forces the bundled frontend to be used.
+ static void OpenDevToolsWindowWithBundledFrontend(
+ scoped_refptr<content::DevToolsAgentHost> host,
+ Profile* profile);
// Perform specified action for current WebContents inside a |browser|.
// This may close currently open DevTools window.
@@ -241,10 +245,11 @@ class DevToolsWindow : public DevToolsUIBindings::Delegate,
enum FrontendType {
kFrontendDefault,
- kFrontendRemote,
kFrontendWorker,
kFrontendV8,
- kFrontendNode
+ kFrontendNode,
+ kFrontendRemote,
+ kFrontendRemoteWorker,
};
DevToolsWindow(FrontendType frontend_type,
@@ -259,7 +264,10 @@ class DevToolsWindow : public DevToolsUIBindings::Delegate,
Profile* profile,
const std::string& frontend_uri,
const scoped_refptr<content::DevToolsAgentHost>& agent_host,
- FrontendType frontend_type);
+ bool use_bundled_frontend);
+ static void OpenDevToolsWindow(scoped_refptr<content::DevToolsAgentHost> host,
+ Profile* profile,
+ bool use_bundled_frontend);
static DevToolsWindow* Create(Profile* profile,
content::WebContents* inspected_web_contents,
diff --git a/chromium/chrome/browser/devtools/global_confirm_info_bar_browsertest.cc b/chromium/chrome/browser/devtools/global_confirm_info_bar_browsertest.cc
index 3860fef06c7..6f37575942e 100644
--- a/chromium/chrome/browser/devtools/global_confirm_info_bar_browsertest.cc
+++ b/chromium/chrome/browser/devtools/global_confirm_info_bar_browsertest.cc
@@ -84,7 +84,7 @@ IN_PROC_BROWSER_TEST_F(GlobalConfirmInfoBarTest, MultipleTabs) {
for (int i = 0; i < tab_strip_model->count(); i++)
EXPECT_EQ(0u, GetInfoBarServiceFromTabIndex(i)->infobar_count());
- auto delegate = base::MakeUnique<TestConfirmInfoBarDelegate>();
+ auto delegate = std::make_unique<TestConfirmInfoBarDelegate>();
TestConfirmInfoBarDelegate* delegate_ptr = delegate.get();
base::WeakPtr<GlobalConfirmInfoBar> global_confirm_info_bar =
@@ -116,7 +116,7 @@ IN_PROC_BROWSER_TEST_F(GlobalConfirmInfoBarTest, UserInteraction) {
for (int i = 0; i < tab_strip_model->count(); i++)
EXPECT_EQ(0u, GetInfoBarServiceFromTabIndex(i)->infobar_count());
- auto delegate = base::MakeUnique<TestConfirmInfoBarDelegate>();
+ auto delegate = std::make_unique<TestConfirmInfoBarDelegate>();
TestConfirmInfoBarDelegate* delegate_ptr = delegate.get();
base::WeakPtr<GlobalConfirmInfoBar> global_confirm_info_bar =
diff --git a/chromium/chrome/browser/devtools/protocol_string.cc b/chromium/chrome/browser/devtools/protocol_string.cc
index 822108467bd..b48641a7e2b 100644
--- a/chromium/chrome/browser/devtools/protocol_string.cc
+++ b/chromium/chrome/browser/devtools/protocol_string.cc
@@ -74,7 +74,7 @@ std::unique_ptr<base::Value> toBaseValue(protocol::Value* value, int depth) {
if (!value || !depth)
return nullptr;
if (value->type() == protocol::Value::TypeNull)
- return base::MakeUnique<base::Value>();
+ return std::make_unique<base::Value>();
if (value->type() == protocol::Value::TypeBoolean) {
bool inner;
value->asBoolean(&inner);
diff --git a/chromium/chrome/browser/devtools/remote_debugging_server.cc b/chromium/chrome/browser/devtools/remote_debugging_server.cc
index e07fe4cb62a..3650e41ce99 100644
--- a/chromium/chrome/browser/devtools/remote_debugging_server.cc
+++ b/chromium/chrome/browser/devtools/remote_debugging_server.cc
@@ -19,6 +19,7 @@
#include "chrome/common/chrome_paths.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/devtools_socket_factory.h"
+#include "content/public/common/content_switches.h"
#include "net/base/net_errors.h"
#include "net/log/net_log_source.h"
#include "net/socket/tcp_server_socket.h"
@@ -36,10 +37,8 @@ const int kBackLog = 10;
class TCPServerSocketFactory
: public content::DevToolsSocketFactory {
public:
- TCPServerSocketFactory(const std::string& address, uint16_t port)
- : address_(address),
- port_(port),
- last_tethering_port_(kMinTetheringPort) {}
+ explicit TCPServerSocketFactory(uint16_t port)
+ : port_(port), last_tethering_port_(kMinTetheringPort) {}
private:
std::unique_ptr<net::ServerSocket> CreateLocalHostServerSocket(int port) {
@@ -57,11 +56,7 @@ class TCPServerSocketFactory
std::unique_ptr<net::ServerSocket> CreateForHttpServer() override {
std::unique_ptr<net::ServerSocket> socket(
new net::TCPServerSocket(nullptr, net::NetLogSource()));
- if (address_.empty())
- return CreateLocalHostServerSocket(port_);
- if (socket->ListenWithAddressAndPort(address_, port_, kBackLog) == net::OK)
- return socket;
- return std::unique_ptr<net::ServerSocket>();
+ return CreateLocalHostServerSocket(port_);
}
std::unique_ptr<net::ServerSocket> CreateForTethering(
@@ -90,8 +85,20 @@ void RemoteDebuggingServer::EnableTetheringForDebug() {
g_tethering_enabled.Get() = true;
}
-RemoteDebuggingServer::RemoteDebuggingServer(const std::string& ip,
- uint16_t port) {
+RemoteDebuggingServer::RemoteDebuggingServer() {
+ const base::CommandLine& command_line =
+ *base::CommandLine::ForCurrentProcess();
+ if (command_line.HasSwitch(switches::kRemoteDebuggingPipe)) {
+ content::DevToolsAgentHost::StartRemoteDebuggingPipeHandler();
+ return;
+ }
+
+ std::string port_str =
+ command_line.GetSwitchValueASCII(::switches::kRemoteDebuggingPort);
+ int port;
+ if (!base::StringToInt(port_str, &port) || port < 0 || port >= 65535)
+ return;
+
base::FilePath output_dir;
if (!port) {
// The client requested an ephemeral port. Must write the selected
@@ -107,8 +114,8 @@ RemoteDebuggingServer::RemoteDebuggingServer(const std::string& ip,
#endif
content::DevToolsAgentHost::StartRemoteDebuggingServer(
- base::MakeUnique<TCPServerSocketFactory>(ip, port), std::string(),
- output_dir, debug_frontend_dir);
+ std::make_unique<TCPServerSocketFactory>(port), output_dir,
+ debug_frontend_dir);
}
RemoteDebuggingServer::~RemoteDebuggingServer() {
@@ -116,4 +123,5 @@ RemoteDebuggingServer::~RemoteDebuggingServer() {
// accesses it during shutdown.
DCHECK(g_browser_process->profile_manager());
content::DevToolsAgentHost::StopRemoteDebuggingServer();
+ content::DevToolsAgentHost::StopRemoteDebuggingPipeHandler();
}
diff --git a/chromium/chrome/browser/devtools/remote_debugging_server.h b/chromium/chrome/browser/devtools/remote_debugging_server.h
index e2ba3e78440..0f5a2f80621 100644
--- a/chromium/chrome/browser/devtools/remote_debugging_server.h
+++ b/chromium/chrome/browser/devtools/remote_debugging_server.h
@@ -16,9 +16,7 @@ class RemoteDebuggingServer {
public:
static void EnableTetheringForDebug();
- // Bind remote debugging service to the given |ip| and |port|.
- // Empty |ip| stands for 127.0.0.1 or ::1.
- RemoteDebuggingServer(const std::string& ip, uint16_t port);
+ RemoteDebuggingServer();
virtual ~RemoteDebuggingServer();
private:
diff --git a/chromium/chrome/browser/devtools/serialize_host_descriptions.cc b/chromium/chrome/browser/devtools/serialize_host_descriptions.cc
index 26aa0206369..e058819935b 100644
--- a/chromium/chrome/browser/devtools/serialize_host_descriptions.cc
+++ b/chromium/chrome/browser/devtools/serialize_host_descriptions.cc
@@ -24,7 +24,7 @@ base::DictionaryValue Serialize(
base::DictionaryValue* root,
const std::map<base::DictionaryValue*, std::vector<base::DictionaryValue*>>&
children) {
- auto children_list = base::MakeUnique<base::ListValue>();
+ auto children_list = std::make_unique<base::ListValue>();
auto child_it = children.find(root);
if (child_it != children.end()) {
for (base::DictionaryValue* child : child_it->second) {
diff --git a/chromium/chrome/browser/engagement/BUILD.gn b/chromium/chrome/browser/engagement/BUILD.gn
index 3bca05363d5..9bd92b61df9 100644
--- a/chromium/chrome/browser/engagement/BUILD.gn
+++ b/chromium/chrome/browser/engagement/BUILD.gn
@@ -10,7 +10,7 @@ mojom("mojo_bindings") {
]
public_deps = [
- "//url/mojo:url_mojom_gurl",
+ "//url/mojom:url_mojom_gurl",
]
# TODO(crbug.com/714018): Convert the implementation to use OnceCallback.
diff --git a/chromium/chrome/browser/engagement/site_engagement_details.mojom b/chromium/chrome/browser/engagement/site_engagement_details.mojom
index ec705aeae4a..42549b019ea 100644
--- a/chromium/chrome/browser/engagement/site_engagement_details.mojom
+++ b/chromium/chrome/browser/engagement/site_engagement_details.mojom
@@ -4,7 +4,7 @@
module mojom;
-import "url/mojo/url.mojom";
+import "url/mojom/url.mojom";
struct SiteEngagementDetails {
url.mojom.Url origin;
diff --git a/chromium/chrome/browser/extensions/BUILD.gn b/chromium/chrome/browser/extensions/BUILD.gn
index 6e19b623bae..cc5ef3e33eb 100644
--- a/chromium/chrome/browser/extensions/BUILD.gn
+++ b/chromium/chrome/browser/extensions/BUILD.gn
@@ -179,10 +179,6 @@ static_library("extensions") {
"api/easy_unlock_private/easy_unlock_private_connection_manager.h",
"api/easy_unlock_private/easy_unlock_private_crypto_delegate.h",
"api/easy_unlock_private/easy_unlock_private_crypto_delegate_chromeos.cc",
- "api/experience_sampling_private/experience_sampling.cc",
- "api/experience_sampling_private/experience_sampling.h",
- "api/experience_sampling_private/experience_sampling_private_api.cc",
- "api/experience_sampling_private/experience_sampling_private_api.h",
"api/extension_action/extension_action_api.cc",
"api/extension_action/extension_action_api.h",
"api/extension_action/extension_page_actions_api_constants.cc",
@@ -339,8 +335,6 @@ static_library("extensions") {
"api/resources_private/resources_private_api.h",
"api/runtime/chrome_runtime_api_delegate.cc",
"api/runtime/chrome_runtime_api_delegate.h",
- "api/screenlock_private/screenlock_private_api.cc",
- "api/screenlock_private/screenlock_private_api.h",
"api/sessions/session_id.cc",
"api/sessions/session_id.h",
"api/sessions/sessions_api.cc",
@@ -462,7 +456,6 @@ static_library("extensions") {
"bookmark_app_helper.h",
"bookmark_app_navigation_throttle.cc",
"bookmark_app_navigation_throttle.h",
- "browser_action_test_util.h",
"browser_context_keyed_service_factories.cc",
"browser_context_keyed_service_factories.h",
"browser_extension_window_controller.cc",
@@ -504,6 +497,8 @@ static_library("extensions") {
"chrome_process_manager_delegate.h",
"chrome_url_request_util.cc",
"chrome_url_request_util.h",
+ "chrome_zipfile_installer.cc",
+ "chrome_zipfile_installer.h",
"clipboard_extension_helper_chromeos.cc",
"clipboard_extension_helper_chromeos.h",
"component_extensions_whitelist/whitelist.cc",
@@ -558,10 +553,6 @@ static_library("extensions") {
"extension_context_menu_model.h",
"extension_cookie_notifier.cc",
"extension_cookie_notifier.h",
- "extension_creator.cc",
- "extension_creator.h",
- "extension_creator_filter.cc",
- "extension_creator_filter.h",
"extension_disabled_ui.cc",
"extension_disabled_ui.h",
"extension_error_controller.cc",
@@ -775,8 +766,6 @@ static_library("extensions") {
"window_controller_list.cc",
"window_controller_list.h",
"window_controller_list_observer.h",
- "zipfile_installer.cc",
- "zipfile_installer.h",
]
configs += [
@@ -815,7 +804,7 @@ static_library("extensions") {
"//chrome/common/extensions/api:extensions_features",
"//chrome/common/safe_browsing:proto",
"//chrome/services/media_gallery_util/public/cpp",
- "//chrome/services/removable_storage_writer/public/interfaces",
+ "//chrome/services/removable_storage_writer/public/mojom",
"//components/app_modal",
"//components/autofill/content/browser",
"//components/bookmarks/browser",
@@ -825,11 +814,11 @@ static_library("extensions") {
"//components/bubble",
"//components/content_settings/core/browser",
"//components/crx_file",
- "//components/crx_file:crx_creator",
"//components/cryptauth",
"//components/data_reduction_proxy/core/browser",
"//components/dom_distiller/core",
"//components/download/content/public",
+ "//components/download/public/common:public",
"//components/drive",
"//components/favicon/content",
"//components/feedback",
@@ -838,8 +827,9 @@ static_library("extensions") {
"//components/history/core/browser",
"//components/infobars/core",
"//components/keyed_service/content",
- "//components/language/core/browser:browser",
- "//components/nacl/common:features",
+ "//components/language/core/browser",
+ "//components/language/core/common",
+ "//components/nacl/common:buildflags",
"//components/navigation_interception",
"//components/net_log",
"//components/omnibox/browser",
@@ -892,12 +882,13 @@ static_library("extensions") {
"//ppapi/features",
"//printing/features",
"//rlz/features",
+ "//services/audio/public/cpp",
"//services/data_decoder/public/cpp",
- "//services/device/public/interfaces",
- "//services/identity/public/interfaces",
- "//services/network/public/interfaces",
+ "//services/device/public/mojom",
+ "//services/identity/public/mojom",
+ "//services/network/public/mojom",
"//services/service_manager/public/cpp",
- "//services/service_manager/public/interfaces",
+ "//services/service_manager/public/mojom",
"//skia",
"//sql",
"//storage/browser",
@@ -909,9 +900,8 @@ static_library("extensions") {
"//third_party/leveldatabase",
"//third_party/libaddressinput:util",
"//third_party/re2",
- "//third_party/webrtc/modules/desktop_capture",
"//third_party/zlib/google:zip",
- "//ui/accessibility:ax_gen",
+ "//ui/accessibility:ax_enums_mojo",
"//ui/base",
"//ui/base/ime",
"//ui/display/manager",
@@ -957,6 +947,8 @@ static_library("extensions") {
"api/platform_keys/platform_keys_api.h",
"api/platform_keys/verify_trust_api.cc",
"api/platform_keys/verify_trust_api.h",
+ "api/screenlock_private/screenlock_private_api.cc",
+ "api/screenlock_private/screenlock_private_api.h",
"api/settings_private/chromeos_resolve_time_zone_by_geolocation_method_short.cc",
"api/settings_private/chromeos_resolve_time_zone_by_geolocation_method_short.h",
"api/settings_private/chromeos_resolve_time_zone_by_geolocation_on_off.cc",
@@ -1092,7 +1084,7 @@ static_library("extensions") {
if (is_win) {
deps += [
- "//chrome/services/wifi_util_win/public/interfaces",
+ "//chrome/services/wifi_util_win/public/mojom",
"//third_party/iaccessible2",
"//third_party/isimpledom",
]
@@ -1119,6 +1111,7 @@ static_library("extensions") {
"api/cloud_print_private/cloud_print_private_api.cc",
"api/cloud_print_private/cloud_print_private_api.h",
]
+ deps += [ "//chrome/common:service_process_mojom" ]
}
if (enable_service_discovery) {
diff --git a/chromium/chrome/browser/extensions/api/DEPS b/chromium/chrome/browser/extensions/api/DEPS
index fea8d34597f..e6dcb249500 100644
--- a/chromium/chrome/browser/extensions/api/DEPS
+++ b/chromium/chrome/browser/extensions/api/DEPS
@@ -8,7 +8,7 @@ include_rules = [
# Enable remote assistance on Chrome OS
"+remoting/base",
"+remoting/host",
- "+services/network/public/interfaces",
+ "+services/network",
]
specific_include_rules = {
diff --git a/chromium/chrome/browser/extensions/api/automation/automation_apitest.cc b/chromium/chrome/browser/extensions/api/automation/automation_apitest.cc
index 81b27277daa..12183657a08 100644
--- a/chromium/chrome/browser/extensions/api/automation/automation_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/automation/automation_apitest.cc
@@ -120,7 +120,13 @@ IN_PROC_BROWSER_TEST_F(AutomationApiTest, Actions) {
<< message_;
}
-IN_PROC_BROWSER_TEST_F(AutomationApiTest, Location) {
+// TODO(https://crbug.com/622387): Disabled due to flakiness.
+#if defined(OS_CHROMEOS) && defined(NDEBUG)
+#define MAYBE_Location DISABLED_Location
+#else
+#define MAYBE_Location Location
+#endif
+IN_PROC_BROWSER_TEST_F(AutomationApiTest, MAYBE_Location) {
StartEmbeddedTestServer();
ASSERT_TRUE(RunExtensionSubtest("automation/tests/tabs", "location.html"))
<< message_;
@@ -174,14 +180,14 @@ IN_PROC_BROWSER_TEST_F(AutomationApiTest, TabsAutomationHostsPermissions) {
}
#if defined(USE_AURA)
-// Flaky, see http://crbug.com/637525
+// TODO(https://crbug.com/754870): Disabled due to flakiness.
IN_PROC_BROWSER_TEST_F(AutomationApiTest, DISABLED_Desktop) {
ASSERT_TRUE(RunExtensionSubtest("automation/tests/desktop", "desktop.html"))
<< message_;
}
#if defined(OS_CHROMEOS)
-// TODO(crbug.com/615908): Flaky on CrOS sanitizers.
+// TODO(https://crbug.com/754870): Flaky on CrOS sanitizers.
IN_PROC_BROWSER_TEST_F(AutomationApiTest, DISABLED_DesktopInitialFocus) {
ASSERT_TRUE(
RunExtensionSubtest("automation/tests/desktop", "initial_focus.html"))
@@ -194,8 +200,7 @@ IN_PROC_BROWSER_TEST_F(AutomationApiTest, DesktopFocusWeb) {
<< message_;
}
-// Flaky, see https://crbug.com/724923.
-IN_PROC_BROWSER_TEST_F(AutomationApiTest, DISABLED_DesktopFocusIframe) {
+IN_PROC_BROWSER_TEST_F(AutomationApiTest, DesktopFocusIframe) {
StartEmbeddedTestServer();
ASSERT_TRUE(
RunExtensionSubtest("automation/tests/desktop", "focus_iframe.html"))
@@ -247,7 +252,7 @@ IN_PROC_BROWSER_TEST_F(AutomationApiTest, DesktopHitTest) {
<< message_;
}
-// Flaky, see http://crbug.com/435449
+// TODO(https://crbug.com/754870): flaky.
IN_PROC_BROWSER_TEST_F(AutomationApiTest, DISABLED_DesktopLoadTabs) {
ASSERT_TRUE(RunExtensionSubtest("automation/tests/desktop", "load_tabs.html"))
<< message_;
@@ -280,13 +285,19 @@ IN_PROC_BROWSER_TEST_F(AutomationApiTest, Find) {
<< message_;
}
-// TODO(crbug.com/725420) Flaky
IN_PROC_BROWSER_TEST_F(AutomationApiTest, Attributes) {
StartEmbeddedTestServer();
ASSERT_TRUE(RunExtensionSubtest("automation/tests/tabs", "attributes.html"))
<< message_;
}
+IN_PROC_BROWSER_TEST_F(AutomationApiTest, ReverseRelations) {
+ StartEmbeddedTestServer();
+ ASSERT_TRUE(
+ RunExtensionSubtest("automation/tests/tabs", "reverse_relations.html"))
+ << message_;
+}
+
IN_PROC_BROWSER_TEST_F(AutomationApiTest, TreeChange) {
StartEmbeddedTestServer();
ASSERT_TRUE(RunExtensionSubtest("automation/tests/tabs", "tree_change.html"))
diff --git a/chromium/chrome/browser/extensions/api/automation_internal/automation_event_router.cc b/chromium/chrome/browser/extensions/api/automation_internal/automation_event_router.cc
index b54484f9b58..b87d67a8227 100644
--- a/chromium/chrome/browser/extensions/api/automation_internal/automation_event_router.cc
+++ b/chromium/chrome/browser/extensions/api/automation_internal/automation_event_router.cc
@@ -23,7 +23,7 @@
#include "extensions/browser/event_router.h"
#include "extensions/common/extension.h"
#include "ui/accessibility/ax_action_data.h"
-#include "ui/accessibility/ax_enums.h"
+#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
namespace extensions {
diff --git a/chromium/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc b/chromium/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc
index c468f587436..e8309a03a77 100644
--- a/chromium/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc
+++ b/chromium/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc
@@ -15,6 +15,7 @@
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/extensions/api/automation_internal/automation_event_router.h"
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
+#include "chrome/browser/extensions/chrome_extension_function_details.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
@@ -37,6 +38,7 @@
#include "extensions/common/extension_messages.h"
#include "extensions/common/permissions/permissions_data.h"
#include "ui/accessibility/ax_action_data.h"
+#include "ui/accessibility/ax_enum_util.h"
#include "ui/accessibility/ax_host_delegate.h"
#include "ui/accessibility/ax_tree_id_registry.h"
@@ -208,7 +210,7 @@ class AutomationWebContentsObserver
std::vector<content::AXEventNotificationDetails> details;
content::AXEventNotificationDetails detail;
detail.ax_tree_id = id.first->GetAXTreeID();
- detail.event_type = ui::AX_EVENT_MEDIA_STARTED_PLAYING;
+ detail.event_type = ax::mojom::Event::kMediaStartedPlaying;
details.push_back(detail);
AccessibilityEventReceived(details);
}
@@ -220,7 +222,7 @@ class AutomationWebContentsObserver
std::vector<content::AXEventNotificationDetails> details;
content::AXEventNotificationDetails detail;
detail.ax_tree_id = id.first->GetAXTreeID();
- detail.event_type = ui::AX_EVENT_MEDIA_STOPPED_PLAYING;
+ detail.event_type = ax::mojom::Event::kMediaStoppedPlaying;
details.push_back(detail);
AccessibilityEventReceived(details);
}
@@ -239,7 +241,7 @@ class AutomationWebContentsObserver
content::AXEventNotificationDetails detail;
detail.ax_tree_id = rfh->GetAXTreeID();
- detail.event_type = ui::AX_EVENT_MEDIA_STARTED_PLAYING;
+ detail.event_type = ax::mojom::Event::kMediaStartedPlaying;
details.push_back(detail);
AccessibilityEventReceived(details);
}
@@ -261,18 +263,19 @@ AutomationInternalEnableTabFunction::Run() {
content::WebContents* contents = NULL;
if (params->args.tab_id.get()) {
int tab_id = *params->args.tab_id;
- if (!ExtensionTabUtil::GetTabById(tab_id,
- GetProfile(),
- include_incognito(),
- NULL, /* browser out param*/
- NULL, /* tab_strip out param */
- &contents,
- NULL /* tab_index out param */)) {
+ if (!ExtensionTabUtil::GetTabById(
+ tab_id, browser_context(), include_incognito(),
+ NULL, /* browser out param*/
+ NULL, /* tab_strip out param */
+ &contents, NULL /* tab_index out param */)) {
return RespondNow(
Error(tabs_constants::kTabNotFoundError, base::IntToString(tab_id)));
}
} else {
- contents = GetCurrentBrowser()->tab_strip_model()->GetActiveWebContents();
+ contents = ChromeExtensionFunctionDetails(this)
+ .GetCurrentBrowser()
+ ->tab_strip_model()
+ ->GetActiveWebContents();
if (!contents)
return RespondNow(Error("No active tab"));
}
@@ -336,17 +339,17 @@ AutomationInternalPerformActionFunction::ConvertToAXActionData(
action->request_id = request_id ? *request_id : -1;
switch (params->args.action_type) {
case api::automation_internal::ACTION_TYPE_DODEFAULT:
- action->action = ui::AX_ACTION_DO_DEFAULT;
+ action->action = ax::mojom::Action::kDoDefault;
break;
case api::automation_internal::ACTION_TYPE_FOCUS:
- action->action = ui::AX_ACTION_FOCUS;
+ action->action = ax::mojom::Action::kFocus;
break;
case api::automation_internal::ACTION_TYPE_GETIMAGEDATA: {
api::automation_internal::GetImageDataParams get_image_data_params;
EXTENSION_FUNCTION_VALIDATE(
api::automation_internal::GetImageDataParams::Populate(
params->opt_args.additional_properties, &get_image_data_params));
- action->action = ui::AX_ACTION_GET_IMAGE_DATA;
+ action->action = ax::mojom::Action::kGetImageData;
action->target_rect = gfx::Rect(0, 0, get_image_data_params.max_width,
get_image_data_params.max_height);
break;
@@ -356,34 +359,34 @@ AutomationInternalPerformActionFunction::ConvertToAXActionData(
EXTENSION_FUNCTION_VALIDATE(
api::automation_internal::HitTestParams::Populate(
params->opt_args.additional_properties, &hit_test_params));
- action->action = ui::AX_ACTION_HIT_TEST;
+ action->action = ax::mojom::Action::kHitTest;
action->target_point = gfx::Point(hit_test_params.x, hit_test_params.y);
action->hit_test_event_to_fire =
- ui::ParseAXEvent(hit_test_params.event_to_fire);
- if (action->hit_test_event_to_fire == ui::AX_EVENT_NONE)
+ ui::ParseEvent(hit_test_params.event_to_fire.c_str());
+ if (action->hit_test_event_to_fire == ax::mojom::Event::kNone)
return RespondNow(NoArguments());
break;
}
case api::automation_internal::ACTION_TYPE_MAKEVISIBLE:
- action->action = ui::AX_ACTION_SCROLL_TO_MAKE_VISIBLE;
+ action->action = ax::mojom::Action::kScrollToMakeVisible;
break;
case api::automation_internal::ACTION_TYPE_SCROLLBACKWARD:
- action->action = ui::AX_ACTION_SCROLL_BACKWARD;
+ action->action = ax::mojom::Action::kScrollBackward;
break;
case api::automation_internal::ACTION_TYPE_SCROLLFORWARD:
- action->action = ui::AX_ACTION_SCROLL_FORWARD;
+ action->action = ax::mojom::Action::kScrollForward;
break;
case api::automation_internal::ACTION_TYPE_SCROLLUP:
- action->action = ui::AX_ACTION_SCROLL_UP;
+ action->action = ax::mojom::Action::kScrollUp;
break;
case api::automation_internal::ACTION_TYPE_SCROLLDOWN:
- action->action = ui::AX_ACTION_SCROLL_DOWN;
+ action->action = ax::mojom::Action::kScrollDown;
break;
case api::automation_internal::ACTION_TYPE_SCROLLLEFT:
- action->action = ui::AX_ACTION_SCROLL_LEFT;
+ action->action = ax::mojom::Action::kScrollLeft;
break;
case api::automation_internal::ACTION_TYPE_SCROLLRIGHT:
- action->action = ui::AX_ACTION_SCROLL_RIGHT;
+ action->action = ax::mojom::Action::kScrollRight;
break;
case api::automation_internal::ACTION_TYPE_SETSELECTION: {
api::automation_internal::SetSelectionParams selection_params;
@@ -394,17 +397,17 @@ AutomationInternalPerformActionFunction::ConvertToAXActionData(
action->anchor_offset = selection_params.anchor_offset;
action->focus_node_id = selection_params.focus_node_id;
action->focus_offset = selection_params.focus_offset;
- action->action = ui::AX_ACTION_SET_SELECTION;
+ action->action = ax::mojom::Action::kSetSelection;
break;
}
case api::automation_internal::ACTION_TYPE_SHOWCONTEXTMENU: {
- action->action = ui::AX_ACTION_SHOW_CONTEXT_MENU;
+ action->action = ax::mojom::Action::kShowContextMenu;
break;
}
case api::automation_internal::
ACTION_TYPE_SETSEQUENTIALFOCUSNAVIGATIONSTARTINGPOINT: {
action->action =
- ui::AX_ACTION_SET_SEQUENTIAL_FOCUS_NAVIGATION_STARTING_POINT;
+ ax::mojom::Action::kSetSequentialFocusNavigationStartingPoint;
break;
}
case api::automation_internal::ACTION_TYPE_CUSTOMACTION: {
@@ -414,7 +417,7 @@ AutomationInternalPerformActionFunction::ConvertToAXActionData(
api::automation_internal::PerformCustomActionParams::Populate(
params->opt_args.additional_properties,
&perform_custom_action_params));
- action->action = ui::AX_ACTION_CUSTOM_ACTION;
+ action->action = ax::mojom::Action::kCustomAction;
action->custom_action_id = perform_custom_action_params.custom_action_id;
break;
}
diff --git a/chromium/chrome/browser/extensions/api/automation_internal/automation_internal_api.h b/chromium/chrome/browser/extensions/api/automation_internal/automation_internal_api.h
index ffce688fc36..2971910dca3 100644
--- a/chromium/chrome/browser/extensions/api/automation_internal/automation_internal_api.h
+++ b/chromium/chrome/browser/extensions/api/automation_internal/automation_internal_api.h
@@ -7,9 +7,9 @@
#include <string>
-#include "chrome/browser/extensions/chrome_extension_function.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
+#include "extensions/browser/extension_function.h"
namespace extensions {
@@ -29,8 +29,7 @@ struct AXActionData;
namespace extensions {
// Implementation of the chrome.automation API.
-class AutomationInternalEnableTabFunction
- : public ChromeUIThreadExtensionFunction {
+class AutomationInternalEnableTabFunction : public UIThreadExtensionFunction {
DECLARE_EXTENSION_FUNCTION("automationInternal.enableTab",
AUTOMATIONINTERNAL_ENABLETAB)
protected:
diff --git a/chromium/chrome/browser/extensions/api/autotest_private/DEPS b/chromium/chrome/browser/extensions/api/autotest_private/DEPS
new file mode 100644
index 00000000000..b21e182ab37
--- /dev/null
+++ b/chromium/chrome/browser/extensions/api/autotest_private/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+ui/message_center",
+]
diff --git a/chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc b/chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc
index 7a2179a278f..8bdc5a72567 100644
--- a/chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc
+++ b/chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc
@@ -37,7 +37,7 @@
#include "chromeos/printing/printer_configuration.h"
#include "components/user_manager/user_manager.h"
#include "ui/message_center/message_center.h"
-#include "ui/message_center/notification.h"
+#include "ui/message_center/public/cpp/notification.h"
#endif
namespace extensions {
@@ -396,6 +396,10 @@ AutotestPrivateGetVisibleNotificationsFunction::Run() {
DVLOG(1) << "AutotestPrivateGetVisibleNotificationsFunction";
std::unique_ptr<base::ListValue> values(new base::ListValue);
#if defined(OS_CHROMEOS)
+ // TODO(estade): we can't rely on the message center being available in the
+ // browser process (in mash). Make autotests that use it fail loudly. See
+ // crbug.com/804570
+ CHECK(message_center::MessageCenter::Get());
for (auto* notification :
message_center::MessageCenter::Get()->GetVisibleNotifications()) {
auto result = std::make_unique<base::DictionaryValue>();
diff --git a/chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.h b/chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.h
index 7f55a387113..7eb44055070 100644
--- a/chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.h
+++ b/chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.h
@@ -10,7 +10,7 @@
#include "base/compiler_specific.h"
#include "chrome/browser/extensions/chrome_extension_function.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
-#include "ui/message_center/notification_types.h"
+#include "ui/message_center/public/cpp/notification_types.h"
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/printing/cups_printers_manager.h"
diff --git a/chromium/chrome/browser/extensions/api/bookmarks/bookmark_apitest.cc b/chromium/chrome/browser/extensions/api/bookmarks/bookmark_apitest.cc
index 8717bf94ac3..c4cdaf3aa3b 100644
--- a/chromium/chrome/browser/extensions/api/bookmarks/bookmark_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/bookmarks/bookmark_apitest.cc
@@ -21,13 +21,7 @@
using bookmarks::BookmarkModel;
-// Flaky on Windows and Linux. http://crbug.com/383452
-#if defined(OS_WIN) || defined(OS_LINUX)
-#define MAYBE_Bookmarks DISABLED_Bookmarks
-#else
-#define MAYBE_Bookmarks Bookmarks
-#endif
-IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_Bookmarks) {
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Bookmarks) {
// Add test managed bookmarks to verify that the bookmarks API can read them
// and can't modify them.
Profile* profile = browser()->profile();
diff --git a/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc b/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc
index 397e6f924bf..1e93c03bfd7 100644
--- a/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc
+++ b/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc
@@ -555,7 +555,7 @@ bool BookmarksSearchFunction::RunOnReady() {
return true;
}
-bool BookmarksRemoveFunction::RunOnReady() {
+bool BookmarksRemoveFunctionBase::RunOnReady() {
if (!EditBookmarksEnabled())
return false;
@@ -567,15 +567,21 @@ bool BookmarksRemoveFunction::RunOnReady() {
if (!GetBookmarkIdAsInt64(params->id, &id))
return false;
- bool recursive = false;
- if (name() == BookmarksRemoveTreeFunction::function_name())
- recursive = true;
-
BookmarkModel* model = GetBookmarkModel();
ManagedBookmarkService* managed = GetManagedBookmarkService();
- if (!bookmark_api_helpers::RemoveNode(model, managed, id, recursive, &error_))
+ if (!bookmark_api_helpers::RemoveNode(model, managed, id, is_recursive(),
+ &error_)) {
return false;
+ }
+
+ return true;
+}
+
+bool BookmarksRemoveFunction::is_recursive() const {
+ return false;
+}
+bool BookmarksRemoveTreeFunction::is_recursive() const {
return true;
}
diff --git a/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.h b/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.h
index f67548f71c8..610c6410c80 100644
--- a/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.h
+++ b/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.h
@@ -244,23 +244,36 @@ class BookmarksSearchFunction : public BookmarksFunction {
bool RunOnReady() override;
};
-class BookmarksRemoveFunction : public BookmarksFunction {
+class BookmarksRemoveFunctionBase : public BookmarksFunction {
+ protected:
+ ~BookmarksRemoveFunctionBase() override {}
+
+ virtual bool is_recursive() const = 0;
+
+ // BookmarksFunction:
+ bool RunOnReady() override;
+};
+
+class BookmarksRemoveFunction : public BookmarksRemoveFunctionBase {
public:
- DECLARE_EXTENSION_FUNCTION("bookmarks.remove", BOOKMARKS_REMOVE)
+ DECLARE_EXTENSION_FUNCTION("bookmarks.remove", BOOKMARKS_REMOVE);
protected:
~BookmarksRemoveFunction() override {}
- // BookmarksFunction:
- bool RunOnReady() override;
+ // BookmarksRemoveFunctionBase:
+ bool is_recursive() const override;
};
-class BookmarksRemoveTreeFunction : public BookmarksRemoveFunction {
+class BookmarksRemoveTreeFunction : public BookmarksRemoveFunctionBase {
public:
DECLARE_EXTENSION_FUNCTION("bookmarks.removeTree", BOOKMARKS_REMOVETREE)
protected:
~BookmarksRemoveTreeFunction() override {}
+
+ // BookmarksRemoveFunctionBase:
+ bool is_recursive() const override;
};
class BookmarksCreateFunction : public BookmarksFunction {
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 c16ed76a1da..244be025170 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
@@ -136,11 +136,8 @@ bool BrowsingDataSettingsFunction::isDataTypeSelected(
ExtensionFunction::ResponseAction BrowsingDataSettingsFunction::Run() {
prefs_ = Profile::FromBrowserContext(browser_context())->GetPrefs();
- ClearBrowsingDataTab tab =
- base::FeatureList::IsEnabled(features::kTabsInCbd)
- ? static_cast<ClearBrowsingDataTab>(prefs_->GetInteger(
- browsing_data::prefs::kLastClearBrowsingDataTab))
- : ClearBrowsingDataTab::ADVANCED;
+ ClearBrowsingDataTab tab = static_cast<ClearBrowsingDataTab>(
+ prefs_->GetInteger(browsing_data::prefs::kLastClearBrowsingDataTab));
// Fill origin types.
// The "cookies" and "hosted apps" UI checkboxes both map to
diff --git a/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_test.cc b/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_test.cc
index d777b98d233..f0e724e64a3 100644
--- a/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_test.cc
+++ b/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_test.cc
@@ -9,7 +9,6 @@
#include "base/memory/ref_counted.h"
#include "base/strings/pattern.h"
#include "base/strings/string_util.h"
-#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "chrome/browser/browsing_data/browsing_data_helper.h"
#include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
@@ -17,7 +16,6 @@
#include "chrome/browser/extensions/extension_function_test_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
-#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/browsing_data/core/browsing_data_utils.h"
@@ -62,7 +60,6 @@ class ExtensionBrowsingDataTest : public InProcessBrowserTest {
protected:
void SetUp() override {
- feature_list_.InitAndEnableFeature(features::kTabsInCbd);
InProcessBrowserTest::SetUp();
}
@@ -297,7 +294,6 @@ class ExtensionBrowsingDataTest : public InProcessBrowserTest {
}
private:
- base::test::ScopedFeatureList feature_list_;
// Cached pointer to BrowsingDataRemover for access to testing methods.
content::BrowsingDataRemover* remover_;
};
diff --git a/chromium/chrome/browser/extensions/api/cast_streaming/performance_test.cc b/chromium/chrome/browser/extensions/api/cast_streaming/performance_test.cc
index 8a53d8a19aa..3a6d2f4f736 100644
--- a/chromium/chrome/browser/extensions/api/cast_streaming/performance_test.cc
+++ b/chromium/chrome/browser/extensions/api/cast_streaming/performance_test.cc
@@ -68,13 +68,9 @@ constexpr size_t kTrimEvents = 24; // 1 sec at 24fps, or 0.4 sec at 60 fps.
constexpr size_t kMinDataPoints = 100; // 1 sec of audio, or ~5 sec at 24fps.
enum TestFlags {
- // TODO(miu): Remove kUseGpu (since the GPU is required), and maybe
- // kDisableVsync. http://crbug.com/567848
kUseGpu = 1 << 0, // Only execute test if --enable-gpu was given
// on the command line. This is required for
// tests that run on GPU.
- kDisableVsync = 1 << 1, // Do not limit framerate to vertical refresh.
- // when on GPU, nor to 60hz when not on GPU.
kSmallWindow = 1 << 2, // Window size: 1 = 800x600, 0 = 2000x1000
k24fps = 1 << 3, // Use 24 fps video.
k30fps = 1 << 4, // Use 30 fps video.
@@ -368,8 +364,6 @@ class CastV2PerformanceTest
std::string suffix;
if (HasFlag(kUseGpu))
suffix += "_gpu";
- if (HasFlag(kDisableVsync))
- suffix += "_novsync";
if (HasFlag(kSmallWindow))
suffix += "_small";
if (HasFlag(k24fps))
@@ -406,6 +400,8 @@ class CastV2PerformanceTest
void SetUp() override {
EnablePixelOutput();
+ if (!HasFlag(kUseGpu))
+ UseSoftwareCompositing();
ExtensionApiTest::SetUp();
}
@@ -425,12 +421,10 @@ class CastV2PerformanceTest
if (!HasFlag(kUseGpu))
command_line->AppendSwitch(switches::kDisableGpu);
- if (HasFlag(kDisableVsync))
- command_line->AppendSwitch(switches::kDisableGpuVsync);
-
command_line->AppendSwitchASCII(
extensions::switches::kWhitelistedExtensionID,
kExtensionId);
+
ExtensionApiTest::SetUpCommandLine(command_line);
}
@@ -721,7 +715,6 @@ INSTANTIATE_TEST_CASE_P(
testing::Values(kUseGpu | k24fps,
kUseGpu | k30fps,
kUseGpu | k60fps,
- kUseGpu | k24fps | kDisableVsync,
kUseGpu | k30fps | kProxyWifi,
kUseGpu | k30fps | kProxyBad,
kUseGpu | k30fps | kSlowClock,
diff --git a/chromium/chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.cc b/chromium/chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.cc
index 90c1889cb5e..17dbddd0d73 100644
--- a/chromium/chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.cc
+++ b/chromium/chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.cc
@@ -7,7 +7,6 @@
#include <memory>
#include <string>
-#include "base/threading/sequenced_worker_pool.h"
#include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h"
#include "chrome/browser/printing/cloud_print/cloud_print_proxy_service_factory.h"
#include "chrome/common/extensions/api/cloud_print_private.h"
diff --git a/chromium/chrome/browser/extensions/api/commands/command_service.cc b/chromium/chrome/browser/extensions/api/commands/command_service.cc
index 3f3661f362e..362c8e4bf5a 100644
--- a/chromium/chrome/browser/extensions/api/commands/command_service.cc
+++ b/chromium/chrome/browser/extensions/api/commands/command_service.cc
@@ -133,8 +133,8 @@ void CommandService::RegisterProfilePrefs(
CommandService::CommandService(content::BrowserContext* context)
: profile_(Profile::FromBrowserContext(context)),
extension_registry_observer_(this) {
- ExtensionFunctionRegistry::GetInstance()->
- RegisterFunction<GetAllCommandsFunction>();
+ ExtensionFunctionRegistry::GetInstance()
+ .RegisterFunction<GetAllCommandsFunction>();
extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
}
@@ -177,16 +177,16 @@ bool CommandService::GetBrowserActionCommand(const std::string& extension_id,
QueryType type,
Command* command,
bool* active) const {
- return GetExtensionActionCommand(
- extension_id, type, command, active, BROWSER_ACTION);
+ return GetExtensionActionCommand(extension_id, type, command, active,
+ Command::Type::kBrowserAction);
}
bool CommandService::GetPageActionCommand(const std::string& extension_id,
QueryType type,
Command* command,
bool* active) const {
- return GetExtensionActionCommand(
- extension_id, type, command, active, PAGE_ACTION);
+ return GetExtensionActionCommand(extension_id, type, command, active,
+ Command::Type::kPageAction);
}
bool CommandService::GetNamedCommands(const std::string& extension_id,
@@ -403,8 +403,7 @@ Command CommandService::FindCommandByName(const std::string& extension_id,
bool CommandService::GetSuggestedExtensionCommand(
const std::string& extension_id,
const ui::Accelerator& accelerator,
- Command* command,
- ExtensionCommandType* command_type) const {
+ Command* command) const {
const Extension* extension =
ExtensionRegistry::Get(profile_)
->GetExtensionById(extension_id, ExtensionRegistry::ENABLED);
@@ -419,8 +418,6 @@ bool CommandService::GetSuggestedExtensionCommand(
accelerator == prospective_command.accelerator()) {
if (command)
*command = prospective_command;
- if (command_type)
- *command_type = BROWSER_ACTION;
return true;
} else if (GetPageActionCommand(extension_id,
CommandService::SUGGESTED,
@@ -429,8 +426,6 @@ bool CommandService::GetSuggestedExtensionCommand(
accelerator == prospective_command.accelerator()) {
if (command)
*command = prospective_command;
- if (command_type)
- *command_type = PAGE_ACTION;
return true;
} else if (GetNamedCommands(extension_id,
CommandService::SUGGESTED,
@@ -442,8 +437,6 @@ bool CommandService::GetSuggestedExtensionCommand(
if (accelerator == it->second.accelerator()) {
if (command)
*command = it->second;
- if (command_type)
- *command_type = NAMED;
return true;
}
}
@@ -454,11 +447,10 @@ bool CommandService::GetSuggestedExtensionCommand(
bool CommandService::RequestsBookmarkShortcutOverride(
const Extension* extension) const {
return RemovesBookmarkShortcut(extension) &&
- GetSuggestedExtensionCommand(
- extension->id(),
- chrome::GetPrimaryChromeAcceleratorForCommandId(IDC_BOOKMARK_PAGE),
- NULL,
- NULL);
+ GetSuggestedExtensionCommand(
+ extension->id(),
+ chrome::GetPrimaryChromeAcceleratorForCommandId(IDC_BOOKMARK_PAGE),
+ nullptr);
}
void CommandService::AddObserver(Observer* observer) {
@@ -858,7 +850,7 @@ bool CommandService::GetExtensionActionCommand(
QueryType query_type,
Command* command,
bool* active,
- ExtensionCommandType action_type) const {
+ Command::Type action_type) const {
const ExtensionSet& extensions =
ExtensionRegistry::Get(profile_)->enabled_extensions();
const Extension* extension = extensions.GetByID(extension_id);
@@ -869,13 +861,13 @@ bool CommandService::GetExtensionActionCommand(
const Command* requested_command = NULL;
switch (action_type) {
- case BROWSER_ACTION:
+ case Command::Type::kBrowserAction:
requested_command = CommandsInfo::GetBrowserActionCommand(extension);
break;
- case PAGE_ACTION:
+ case Command::Type::kPageAction:
requested_command = CommandsInfo::GetPageActionCommand(extension);
break;
- case NAMED:
+ case Command::Type::kNamed:
NOTREACHED();
return false;
}
diff --git a/chromium/chrome/browser/extensions/api/commands/command_service.h b/chromium/chrome/browser/extensions/api/commands/command_service.h
index ebff2853236..378922ae5fe 100644
--- a/chromium/chrome/browser/extensions/api/commands/command_service.h
+++ b/chromium/chrome/browser/extensions/api/commands/command_service.h
@@ -68,13 +68,6 @@ class CommandService : public BrowserContextKeyedAPI,
ANY_SCOPE, // All commands, regardless of scope (used when querying).
};
- // An enum specifying the types of commands that can be used by an extension.
- enum ExtensionCommandType {
- NAMED,
- BROWSER_ACTION,
- PAGE_ACTION
- };
-
class Observer {
public:
// Called when an extension command is added.
@@ -186,8 +179,7 @@ class CommandService : public BrowserContextKeyedAPI,
// assigns the type to *|command_type| if non-null.
bool GetSuggestedExtensionCommand(const std::string& extension_id,
const ui::Accelerator& accelerator,
- Command* command,
- ExtensionCommandType* command_type) const;
+ Command* command) const;
// Returns true if |extension| requests to override the bookmark shortcut key
// and should be allowed to do so.
@@ -265,7 +257,7 @@ class CommandService : public BrowserContextKeyedAPI,
QueryType query_type,
Command* command,
bool* active,
- ExtensionCommandType action_type) const;
+ Command::Type type) const;
// A weak pointer to the profile we are associated with. Not owned by us.
Profile* profile_;
diff --git a/chromium/chrome/browser/extensions/api/cookies/cookies_api.cc b/chromium/chrome/browser/extensions/api/cookies/cookies_api.cc
index f77080331df..13790f2f9eb 100644
--- a/chromium/chrome/browser/extensions/api/cookies/cookies_api.cc
+++ b/chromium/chrome/browser/extensions/api/cookies/cookies_api.cc
@@ -33,7 +33,7 @@
#include "extensions/common/permissions/permissions_data.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_constants.h"
-#include "services/network/public/interfaces/network_service.mojom.h"
+#include "services/network/public/mojom/network_service.mojom.h"
using content::BrowserThread;
@@ -87,7 +87,8 @@ network::mojom::CookieManager* ParseStoreCookieManager(
// GetCurrentBrowser() already takes into account incognito settings.
// TODO(rdevlin.cronin): Relying on the current execution context is
// almost never the right answer; clean this up.
- Browser* current_browser = function->GetCurrentBrowser();
+ Browser* current_browser =
+ ChromeExtensionFunctionDetails(function).GetCurrentBrowser();
if (!current_browser) {
function->SetError(keys::kNoCookieStoreFoundError);
return nullptr;
diff --git a/chromium/chrome/browser/extensions/api/cookies/cookies_api.h b/chromium/chrome/browser/extensions/api/cookies/cookies_api.h
index b5a5ad044f1..991fb466e53 100644
--- a/chromium/chrome/browser/extensions/api/cookies/cookies_api.h
+++ b/chromium/chrome/browser/extensions/api/cookies/cookies_api.h
@@ -22,7 +22,7 @@
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/event_router.h"
#include "net/cookies/canonical_cookie.h"
-#include "services/network/public/interfaces/cookie_manager.mojom.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "url/gurl.h"
namespace extensions {
diff --git a/chromium/chrome/browser/extensions/api/cookies/cookies_helpers.h b/chromium/chrome/browser/extensions/api/cookies/cookies_helpers.h
index ec287ffbce9..0b21eb056db 100644
--- a/chromium/chrome/browser/extensions/api/cookies/cookies_helpers.h
+++ b/chromium/chrome/browser/extensions/api/cookies/cookies_helpers.h
@@ -18,7 +18,7 @@
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_monster.h"
#include "net/cookies/cookie_options.h"
-#include "services/network/public/interfaces/cookie_manager.mojom.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
class Browser;
class Profile;
diff --git a/chromium/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc b/chromium/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc
index 6e99d232a39..6b95797c994 100644
--- a/chromium/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc
+++ b/chromium/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc
@@ -10,20 +10,18 @@
#include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
-#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/permissions/permission_request.h"
+#include "chrome/browser/permissions/attestation_permission_request.h"
#include "chrome/browser/permissions/permission_request_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
-#include "chrome/grit/generated_resources.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "crypto/sha2.h"
#include "extensions/common/error_utils.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
-#include "ui/base/l10n/l10n_util.h"
+#include "url/origin.h"
namespace {
@@ -57,52 +55,6 @@ bool ContainsAppIdByHash(const base::ListValue& list,
return false;
}
-// AttestationPermissionRequest is a delegate class that provides information
-// and callbacks to the PermissionRequestManager.
-//
-// PermissionRequestManager has a reference to this object and so this object
-// must outlive it. Since attestation requests are never canceled,
-// PermissionRequestManager guarentees that |RequestFinished| will always,
-// eventually, be called. This object uses that fact to delete itself during
-// |RequestFinished| and thus owns itself.
-class AttestationPermissionRequest : public PermissionRequest {
- public:
- AttestationPermissionRequest(const GURL& app_id,
- base::OnceCallback<void(bool)> callback)
- : app_id_(app_id), callback_(std::move(callback)) {}
-
- PermissionRequest::IconId GetIconId() const override {
- return kUsbSecurityKeyIcon;
- }
-
- base::string16 GetMessageTextFragment() const override {
- return l10n_util::GetStringUTF16(
- IDS_SECURITY_KEY_ATTESTATION_PERMISSION_FRAGMENT);
- }
- GURL GetOrigin() const override { return app_id_; }
- void PermissionGranted() override { std::move(callback_).Run(true); }
- void PermissionDenied() override { std::move(callback_).Run(false); }
- void Cancelled() override { std::move(callback_).Run(false); }
-
- void RequestFinished() override {
- if (callback_)
- std::move(callback_).Run(false);
- delete this;
- }
-
- PermissionRequestType GetPermissionRequestType() const override {
- return PermissionRequestType::PERMISSION_SECURITY_KEY_ATTESTATION;
- }
-
- private:
- ~AttestationPermissionRequest() override = default;
-
- const GURL app_id_;
- base::OnceCallback<void(bool)> callback_;
-
- DISALLOW_COPY_AND_ASSIGN(AttestationPermissionRequest);
-};
-
} // namespace
namespace extensions {
@@ -241,8 +193,9 @@ CryptotokenPrivateCanAppIdGetAttestationFunction::Run() {
}
// The created AttestationPermissionRequest deletes itself once complete.
- permission_request_manager->AddRequest(new AttestationPermissionRequest(
- app_id_url,
+ const url::Origin origin(url::Origin::Create(app_id_url));
+ permission_request_manager->AddRequest(NewAttestationPermissionRequest(
+ origin,
base::BindOnce(
&CryptotokenPrivateCanAppIdGetAttestationFunction::Complete, this)));
return RespondLater();
diff --git a/chromium/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api_unittest.cc b/chromium/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api_unittest.cc
index a88874bd102..3c912638980 100644
--- a/chromium/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api_unittest.cc
@@ -69,8 +69,7 @@ class CryptoTokenPrivateApiTest : public extensions::ExtensionApiUnittest {
args->AppendString(app_id);
if (!extension_function_test_utils::RunFunction(
- function.get(), std::move(args), browser(),
- extension_function_test_utils::NONE)) {
+ function.get(), std::move(args), browser(), api_test_utils::NONE)) {
return false;
}
@@ -89,7 +88,7 @@ class CryptoTokenPrivateApiTest : public extensions::ExtensionApiUnittest {
if (!extension_function_test_utils::RunFunction(
function.get(), base::ListValue::From(std::move(args)), browser(),
- extension_function_test_utils::NONE)) {
+ api_test_utils::NONE)) {
return false;
}
@@ -214,8 +213,7 @@ class CryptoTokenPermissionTest : public ExtensionApiUnittest {
auto args_list = base::ListValue::From(std::move(args));
extension_function_test_utils::RunFunction(
- function.get(), std::move(args_list), browser(),
- extension_function_test_utils::NONE);
+ function.get(), std::move(args_list), browser(), api_test_utils::NONE);
return GetSingleBooleanResult(function.get(), out_result);
}
diff --git a/chromium/chrome/browser/extensions/api/dashboard_private/dashboard_private_api.cc b/chromium/chrome/browser/extensions/api/dashboard_private/dashboard_private_api.cc
index c7d8abdb460..c2bb77b851e 100644
--- a/chromium/chrome/browser/extensions/api/dashboard_private/dashboard_private_api.cc
+++ b/chromium/chrome/browser/extensions/api/dashboard_private/dashboard_private_api.cc
@@ -78,7 +78,8 @@ DashboardPrivateShowPermissionPromptForDelegatedInstallFunction::Run() {
if (!icon_url.is_empty()) {
loader_factory =
content::BrowserContext::GetDefaultStoragePartition(browser_context())
- ->GetURLLoaderFactoryForBrowserProcess();
+ ->GetURLLoaderFactoryForBrowserProcess()
+ .get();
}
scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper(
diff --git a/chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc b/chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc
index 615ee3a7276..92fae3336a0 100644
--- a/chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc
@@ -126,20 +126,20 @@ testing::AssertionResult DebuggerApiTest::RunAttachFunctionOnTarget(
attach_function->set_extension(extension_.get());
std::string actual_error;
- if (!RunFunction(attach_function.get(),
- base::StringPrintf("[%s, \"1.1\"]", debuggee_target.c_str()),
- browser(),
- extension_function_test_utils::NONE)) {
+ if (!extension_function_test_utils::RunFunction(
+ attach_function.get(),
+ base::StringPrintf("[%s, \"1.1\"]", debuggee_target.c_str()),
+ browser(), api_test_utils::NONE)) {
actual_error = attach_function->GetError();
} else {
// Clean up and detach.
scoped_refptr<DebuggerDetachFunction> detach_function =
new DebuggerDetachFunction();
detach_function->set_extension(extension_.get());
- if (!RunFunction(detach_function.get(),
- base::StringPrintf("[%s]", debuggee_target.c_str()),
- browser(),
- extension_function_test_utils::NONE)) {
+ if (!extension_function_test_utils::RunFunction(
+ detach_function.get(),
+ base::StringPrintf("[%s]", debuggee_target.c_str()), browser(),
+ api_test_utils::NONE)) {
return testing::AssertionFailure() << "Could not detach from "
<< debuggee_target << " : " << detach_function->GetError();
}
@@ -212,10 +212,10 @@ IN_PROC_BROWSER_TEST_F(DebuggerApiTest, InfoBar) {
// Attach should create infobars in both browsers.
attach_function = new DebuggerAttachFunction();
attach_function->set_extension(extension());
- ASSERT_TRUE(
- RunFunction(attach_function.get(),
- base::StringPrintf("[{\"tabId\": %d}, \"1.1\"]", tab_id),
- browser(), extension_function_test_utils::NONE));
+ ASSERT_TRUE(extension_function_test_utils::RunFunction(
+ attach_function.get(),
+ base::StringPrintf("[{\"tabId\": %d}, \"1.1\"]", tab_id), browser(),
+ api_test_utils::NONE));
EXPECT_EQ(1u, service1->infobar_count());
EXPECT_EQ(1u, service2->infobar_count());
EXPECT_EQ(1u, service3->infobar_count());
@@ -223,10 +223,10 @@ IN_PROC_BROWSER_TEST_F(DebuggerApiTest, InfoBar) {
// Second attach should not create infobars.
attach_function = new DebuggerAttachFunction();
attach_function->set_extension(extension());
- ASSERT_TRUE(
- RunFunction(attach_function.get(),
- base::StringPrintf("[{\"tabId\": %d}, \"1.1\"]", tab_id2),
- browser(), extension_function_test_utils::NONE));
+ ASSERT_TRUE(extension_function_test_utils::RunFunction(
+ attach_function.get(),
+ base::StringPrintf("[{\"tabId\": %d}, \"1.1\"]", tab_id2), browser(),
+ api_test_utils::NONE));
EXPECT_EQ(1u, service1->infobar_count());
EXPECT_EQ(1u, service2->infobar_count());
EXPECT_EQ(1u, service3->infobar_count());
@@ -234,9 +234,9 @@ IN_PROC_BROWSER_TEST_F(DebuggerApiTest, InfoBar) {
// Detach from one of the tabs should not remove infobars.
detach_function = new DebuggerDetachFunction();
detach_function->set_extension(extension());
- ASSERT_TRUE(RunFunction(detach_function.get(),
- base::StringPrintf("[{\"tabId\": %d}]", tab_id2),
- browser(), extension_function_test_utils::NONE));
+ ASSERT_TRUE(extension_function_test_utils::RunFunction(
+ detach_function.get(), base::StringPrintf("[{\"tabId\": %d}]", tab_id2),
+ browser(), api_test_utils::NONE));
EXPECT_EQ(1u, service1->infobar_count());
EXPECT_EQ(1u, service2->infobar_count());
EXPECT_EQ(1u, service3->infobar_count());
@@ -244,9 +244,9 @@ IN_PROC_BROWSER_TEST_F(DebuggerApiTest, InfoBar) {
// Detach should remove all infobars.
detach_function = new DebuggerDetachFunction();
detach_function->set_extension(extension());
- ASSERT_TRUE(RunFunction(detach_function.get(),
- base::StringPrintf("[{\"tabId\": %d}]", tab_id),
- browser(), extension_function_test_utils::NONE));
+ ASSERT_TRUE(extension_function_test_utils::RunFunction(
+ detach_function.get(), base::StringPrintf("[{\"tabId\": %d}]", tab_id),
+ browser(), api_test_utils::NONE));
EXPECT_EQ(0u, service1->infobar_count());
EXPECT_EQ(0u, service2->infobar_count());
EXPECT_EQ(0u, service3->infobar_count());
@@ -254,10 +254,10 @@ IN_PROC_BROWSER_TEST_F(DebuggerApiTest, InfoBar) {
// Attach again.
attach_function = new DebuggerAttachFunction();
attach_function->set_extension(extension());
- ASSERT_TRUE(
- RunFunction(attach_function.get(),
- base::StringPrintf("[{\"tabId\": %d}, \"1.1\"]", tab_id),
- browser(), extension_function_test_utils::NONE));
+ ASSERT_TRUE(extension_function_test_utils::RunFunction(
+ attach_function.get(),
+ base::StringPrintf("[{\"tabId\": %d}, \"1.1\"]", tab_id), browser(),
+ api_test_utils::NONE));
EXPECT_EQ(1u, service1->infobar_count());
EXPECT_EQ(1u, service2->infobar_count());
EXPECT_EQ(1u, service3->infobar_count());
@@ -275,17 +275,17 @@ IN_PROC_BROWSER_TEST_F(DebuggerApiTest, InfoBar) {
detach_function = new DebuggerDetachFunction();
detach_function->set_extension(extension());
// Cannot detach again.
- ASSERT_FALSE(RunFunction(detach_function.get(),
- base::StringPrintf("[{\"tabId\": %d}]", tab_id),
- browser(), extension_function_test_utils::NONE));
+ ASSERT_FALSE(extension_function_test_utils::RunFunction(
+ detach_function.get(), base::StringPrintf("[{\"tabId\": %d}]", tab_id),
+ browser(), api_test_utils::NONE));
// And again...
attach_function = new DebuggerAttachFunction();
attach_function->set_extension(extension());
- ASSERT_TRUE(
- RunFunction(attach_function.get(),
- base::StringPrintf("[{\"tabId\": %d}, \"1.1\"]", tab_id),
- browser(), extension_function_test_utils::NONE));
+ ASSERT_TRUE(extension_function_test_utils::RunFunction(
+ attach_function.get(),
+ base::StringPrintf("[{\"tabId\": %d}, \"1.1\"]", tab_id), browser(),
+ api_test_utils::NONE));
EXPECT_EQ(1u, service1->infobar_count());
EXPECT_EQ(1u, service2->infobar_count());
EXPECT_EQ(1u, service3->infobar_count());
@@ -305,9 +305,9 @@ IN_PROC_BROWSER_TEST_F(DebuggerApiTest, InfoBar) {
// Detach should remove the remaining infobar.
detach_function = new DebuggerDetachFunction();
detach_function->set_extension(extension());
- ASSERT_TRUE(RunFunction(detach_function.get(),
- base::StringPrintf("[{\"tabId\": %d}]", tab_id),
- browser(), extension_function_test_utils::NONE));
+ ASSERT_TRUE(extension_function_test_utils::RunFunction(
+ detach_function.get(), base::StringPrintf("[{\"tabId\": %d}]", tab_id),
+ browser(), api_test_utils::NONE));
EXPECT_EQ(0u, service1->infobar_count());
}
diff --git a/chromium/chrome/browser/extensions/api/debugger/extension_dev_tools_infobar.cc b/chromium/chrome/browser/extensions/api/debugger/extension_dev_tools_infobar.cc
index 6b187e3a93c..41b8582f234 100644
--- a/chromium/chrome/browser/extensions/api/debugger/extension_dev_tools_infobar.cc
+++ b/chromium/chrome/browser/extensions/api/debugger/extension_dev_tools_infobar.cc
@@ -26,7 +26,6 @@ class ExtensionDevToolsInfoBarDelegate : public ConfirmInfoBarDelegate {
~ExtensionDevToolsInfoBarDelegate() override;
// ConfirmInfoBarDelegate:
- Type GetInfoBarType() const override;
infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
bool ShouldExpire(const NavigationDetails& details) const override;
void InfoBarDismissed() override;
@@ -50,11 +49,6 @@ ExtensionDevToolsInfoBarDelegate::ExtensionDevToolsInfoBarDelegate(
ExtensionDevToolsInfoBarDelegate::~ExtensionDevToolsInfoBarDelegate() {}
-infobars::InfoBarDelegate::Type
-ExtensionDevToolsInfoBarDelegate::GetInfoBarType() const {
- return WARNING_TYPE;
-}
-
infobars::InfoBarDelegate::InfoBarIdentifier
ExtensionDevToolsInfoBarDelegate::GetIdentifier() const {
return EXTENSION_DEV_TOOLS_INFOBAR_DELEGATE;
diff --git a/chromium/chrome/browser/extensions/api/declarative/declarative_apitest.cc b/chromium/chrome/browser/extensions/api/declarative/declarative_apitest.cc
index 21fe2a8754a..24f23e44620 100644
--- a/chromium/chrome/browser/extensions/api/declarative/declarative_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/declarative/declarative_apitest.cc
@@ -13,7 +13,6 @@
#include "base/strings/utf_string_conversions.h"
#include "base/test/thread_test_helper.h"
#include "chrome/browser/extensions/extension_apitest.h"
-#include "chrome/browser/extensions/test_extension_dir.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/ui_test_utils.h"
@@ -24,6 +23,7 @@
#include "extensions/browser/extension_prefs.h"
#include "extensions/common/extension.h"
#include "extensions/test/extension_test_message_listener.h"
+#include "extensions/test/test_extension_dir.h"
using content::BrowserThread;
diff --git a/chromium/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc b/chromium/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc
index d9059dbe147..53906599f6b 100644
--- a/chromium/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/declarative/rules_registry_with_cache_unittest.cc
@@ -44,7 +44,8 @@ const int kRulesRegistryID = RulesRegistryService::kDefaultRulesRegistryID;
class RulesRegistryWithCacheTest : public testing::Test {
public:
RulesRegistryWithCacheTest()
- : cache_delegate_(/*log_storage_init_delay=*/false),
+ : cache_delegate_(RulesCacheDelegate::Type::kPersistent,
+ /*log_storage_init_delay=*/false),
registry_(new TestRulesRegistry(profile(),
/*event_name=*/"",
content::BrowserThread::UI,
@@ -233,8 +234,8 @@ TEST_F(RulesRegistryWithCacheTest, DeclarativeRulesStored) {
const std::string rules_stored_key(
RulesCacheDelegate::GetRulesStoredKey(
event_name, profile()->IsOffTheRecord()));
- std::unique_ptr<RulesCacheDelegate> cache_delegate(
- new RulesCacheDelegate(false));
+ auto cache_delegate = std::make_unique<RulesCacheDelegate>(
+ RulesCacheDelegate::Type::kPersistent, false);
scoped_refptr<RulesRegistry> registry(
new TestRulesRegistry(profile(), event_name, content::BrowserThread::UI,
cache_delegate.get(), kRulesRegistryID));
@@ -252,9 +253,11 @@ TEST_F(RulesRegistryWithCacheTest, DeclarativeRulesStored) {
EXPECT_TRUE(cache_delegate->GetDeclarativeRulesStored(extension1_->id()));
// 2. Test writing behavior.
- std::unique_ptr<base::ListValue> value(new base::ListValue);
- value->AppendBoolean(true);
- cache_delegate->WriteToStorage(extension1_->id(), std::move(value));
+ {
+ base::Value value(base::Value::Type::LIST);
+ value.GetList().push_back(base::Value(true));
+ cache_delegate->UpdateRules(extension1_->id(), std::move(value));
+ }
EXPECT_TRUE(cache_delegate->GetDeclarativeRulesStored(extension1_->id()));
content::RunAllTasksUntilIdle();
TestingValueStore* store = env_.GetExtensionSystem()->value_store();
@@ -262,17 +265,21 @@ TEST_F(RulesRegistryWithCacheTest, DeclarativeRulesStored) {
EXPECT_EQ(1, store->write_count());
int write_count = store->write_count();
- value.reset(new base::ListValue);
- cache_delegate->WriteToStorage(extension1_->id(), std::move(value));
- EXPECT_FALSE(cache_delegate->GetDeclarativeRulesStored(extension1_->id()));
+ {
+ base::Value value = base::Value(base::Value::Type::LIST);
+ cache_delegate->UpdateRules(extension1_->id(), std::move(value));
+ EXPECT_FALSE(cache_delegate->GetDeclarativeRulesStored(extension1_->id()));
+ }
content::RunAllTasksUntilIdle();
// No rules currently, but previously there were, so we expect a write.
EXPECT_EQ(write_count + 1, store->write_count());
write_count = store->write_count();
- value.reset(new base::ListValue);
- cache_delegate->WriteToStorage(extension1_->id(), std::move(value));
- EXPECT_FALSE(cache_delegate->GetDeclarativeRulesStored(extension1_->id()));
+ {
+ base::Value value = base::Value(base::Value::Type::LIST);
+ cache_delegate->UpdateRules(extension1_->id(), std::move(value));
+ EXPECT_FALSE(cache_delegate->GetDeclarativeRulesStored(extension1_->id()));
+ }
content::RunAllTasksUntilIdle();
EXPECT_EQ(write_count, store->write_count());
@@ -291,6 +298,18 @@ TEST_F(RulesRegistryWithCacheTest, DeclarativeRulesStored) {
EXPECT_EQ(read_count + 1, store->read_count());
}
+TEST_F(RulesRegistryWithCacheTest, EphemeralCacheIsEphemeral) {
+ auto cache_delegate = std::make_unique<RulesCacheDelegate>(
+ RulesCacheDelegate::Type::kEphemeral, false);
+ base::Value value(base::Value::Type::LIST);
+ value.GetList().push_back(base::Value(true));
+ cache_delegate->UpdateRules(extension1_->id(), std::move(value));
+ content::RunAllTasksUntilIdle();
+ TestingValueStore* store = env_.GetExtensionSystem()->value_store();
+ ASSERT_TRUE(store);
+ EXPECT_EQ(0, store->write_count());
+}
+
// Test that each registry has its own "are some rules stored" flag.
TEST_F(RulesRegistryWithCacheTest, RulesStoredFlagMultipleRegistries) {
ExtensionPrefs* extension_prefs = env_.GetExtensionPrefs();
@@ -303,14 +322,14 @@ TEST_F(RulesRegistryWithCacheTest, RulesStoredFlagMultipleRegistries) {
const std::string rules_stored_key2(
RulesCacheDelegate::GetRulesStoredKey(
event_name2, profile()->IsOffTheRecord()));
- std::unique_ptr<RulesCacheDelegate> cache_delegate1(
- new RulesCacheDelegate(false));
+ auto cache_delegate1 = std::make_unique<RulesCacheDelegate>(
+ RulesCacheDelegate::Type::kPersistent, false);
scoped_refptr<RulesRegistry> registry1(
new TestRulesRegistry(profile(), event_name1, content::BrowserThread::UI,
cache_delegate1.get(), kRulesRegistryID));
- std::unique_ptr<RulesCacheDelegate> cache_delegate2(
- new RulesCacheDelegate(false));
+ auto cache_delegate2 = std::make_unique<RulesCacheDelegate>(
+ RulesCacheDelegate::Type::kPersistent, false);
scoped_refptr<RulesRegistry> registry2(
new TestRulesRegistry(profile(), event_name2, content::BrowserThread::UI,
cache_delegate2.get(), kRulesRegistryID));
@@ -351,8 +370,8 @@ TEST_F(RulesRegistryWithCacheTest, RulesPreservedAcrossRestart) {
env_.GetExtensionSystem()->SetReady();
// 2. First run, adding a rule for the extension.
- std::unique_ptr<RulesCacheDelegate> cache_delegate(
- new RulesCacheDelegate(false));
+ auto cache_delegate = std::make_unique<RulesCacheDelegate>(
+ RulesCacheDelegate::Type::kPersistent, false);
scoped_refptr<TestRulesRegistry> registry(
new TestRulesRegistry(profile(), "testEvent", content::BrowserThread::UI,
cache_delegate.get(), kRulesRegistryID));
@@ -364,7 +383,8 @@ TEST_F(RulesRegistryWithCacheTest, RulesPreservedAcrossRestart) {
EXPECT_EQ(1, GetNumberOfRules(extension1_->id(), registry.get()));
// 3. Restart the TestRulesRegistry and see the rule still there.
- cache_delegate.reset(new RulesCacheDelegate(false));
+ cache_delegate = std::make_unique<RulesCacheDelegate>(
+ RulesCacheDelegate::Type::kPersistent, false);
registry =
new TestRulesRegistry(profile(), "testEvent", content::BrowserThread::UI,
cache_delegate.get(), kRulesRegistryID);
diff --git a/chromium/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc b/chromium/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc
index 88a0d6da572..4f2b43e14fd 100644
--- a/chromium/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc
@@ -16,7 +16,6 @@
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/extensions/test_extension_dir.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -26,6 +25,7 @@
#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/test_extension_dir.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace extensions {
diff --git a/chromium/chrome/browser/extensions/api/declarative_content/request_content_script_apitest.cc b/chromium/chrome/browser/extensions/api/declarative_content/request_content_script_apitest.cc
index ffa680bbcd2..42c42b760ce 100644
--- a/chromium/chrome/browser/extensions/api/declarative_content/request_content_script_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/declarative_content/request_content_script_apitest.cc
@@ -6,12 +6,12 @@
#include "base/macros.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/extensions/extension_browsertest.h"
-#include "chrome/browser/extensions/test_extension_dir.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/test/browser_test_utils.h"
#include "extensions/test/extension_test_message_listener.h"
+#include "extensions/test/test_extension_dir.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
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 5a91c7c006c..72a79c3313d 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
@@ -5,11 +5,11 @@
#include "chrome/browser/extensions/extension_action_manager.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/extensions/test_extension_dir.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "components/version_info/version_info.h"
#include "extensions/common/features/feature_channel.h"
#include "extensions/test/extension_test_message_listener.h"
+#include "extensions/test/test_extension_dir.h"
#include "ui/gfx/image/image.h"
namespace extensions {
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 3ae612a992c..7efb7086919 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
@@ -41,10 +41,13 @@
#include "extensions/browser/extension_util.h"
#include "extensions/common/api/declarative_net_request/constants.h"
#include "extensions/common/api/declarative_net_request/test_utils.h"
+#include "extensions/common/constants.h"
#include "extensions/common/extension_id.h"
+#include "extensions/common/url_pattern.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "net/test/test_data_directory.h"
+#include "services/network/public/cpp/features.h"
namespace extensions {
namespace declarative_net_request {
@@ -154,8 +157,11 @@ class DeclarativeNetRequestBrowserTest
// Loads an extension with the given declarative |rules| in the given
// |directory|. Generates a fatal failure if the extension failed to load.
+ // |hosts| specifies the host permissions, the extensions should
+ // have.
void LoadExtensionWithRules(const std::vector<TestRule>& rules,
- const std::string& directory) {
+ const std::string& directory,
+ const std::vector<std::string>& hosts) {
base::ScopedAllowBlockingForTesting scoped_allow_blocking;
base::HistogramTester tester;
@@ -163,12 +169,13 @@ class DeclarativeNetRequestBrowserTest
EXPECT_TRUE(base::CreateDirectory(extension_dir));
WriteManifestAndRuleset(extension_dir, kJSONRulesetFilepath,
- kJSONRulesFilename, rules);
+ kJSONRulesFilename, rules, hosts);
const Extension* extension = nullptr;
switch (GetParam()) {
case ExtensionLoadType::PACKED:
- extension = InstallExtension(extension_dir, 1 /* expected_change */);
+ extension = InstallExtensionWithPermissionsGranted(
+ extension_dir, 1 /* expected_change */);
break;
case ExtensionLoadType::UNPACKED:
extension = LoadExtension(extension_dir);
@@ -196,7 +203,8 @@ class DeclarativeNetRequestBrowserTest
}
void LoadExtensionWithRules(const std::vector<TestRule>& rules) {
- LoadExtensionWithRules(rules, "test_extension");
+ LoadExtensionWithRules(rules, "test_extension",
+ {URLPattern::kAllUrlsPattern});
}
private:
@@ -635,8 +643,10 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
rules_2.push_back(rule);
}
- ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(rules_1, "extension_1"));
- ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(rules_2, "extension_2"));
+ ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
+ rules_1, "extension_1", {URLPattern::kAllUrlsPattern}));
+ ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
+ rules_2, "extension_2", {URLPattern::kAllUrlsPattern}));
struct {
std::string host;
@@ -688,7 +698,8 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
// url.
for (size_t i = 1; i <= kNumExtensions; ++i) {
rule.action->redirect_url = redirect_url_for_extension_number(i);
- ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}, std::to_string(i)));
+ ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
+ {rule}, std::to_string(i), {URLPattern::kAllUrlsPattern}));
// Verify that the install time of this extension is greater than the last
// extension.
@@ -906,7 +917,8 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
}
}
-// Ensure extensions can't intercept chrome:// urls.
+// Ensure extensions can't intercept chrome:// urls, even after explicitly
+// requesting access to <all_urls>.
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, ChromeURLS) {
// Have the extension block all chrome:// urls.
TestRule rule = CreateGenericRule();
@@ -976,7 +988,12 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, RendererCacheCleared) {
ui_test_utils::NavigateToURL(browser(), url);
EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
EXPECT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
- EXPECT_TRUE(script_monitor.GetAndResetRequestSeen(false));
+
+ // NOTE: When the Network Service is enabled, the RulesetMatcher will not see
+ // network requests if no rulesets are active.
+ EXPECT_TRUE(
+ base::FeatureList::IsEnabled(network::features::kNetworkService) ||
+ script_monitor.GetAndResetRequestSeen(false));
// Another request to |url| should not cause a network request for
// script.js since it will be served by the renderer's in-memory
@@ -1010,7 +1027,9 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, RendererCacheCleared) {
ui_test_utils::NavigateToURL(browser(), url);
EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
EXPECT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
- EXPECT_TRUE(script_monitor.GetAndResetRequestSeen(false));
+ EXPECT_TRUE(
+ base::FeatureList::IsEnabled(network::features::kNetworkService) ||
+ script_monitor.GetAndResetRequestSeen(false));
// Clear RulesetManager's observer.
content::BrowserThread::PostTask(
@@ -1049,6 +1068,164 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
}
+// Ensure that an extension can intercept its own resources, but not those of
+// other extensions.
+IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
+ InterceptExtensionScheme) {
+ // Load two extensions. One blocks all urls, and the other blocks urls with
+ // "google.com" as a substring.
+ std::vector<TestRule> rules_1;
+ TestRule rule = CreateGenericRule();
+ rule.condition->url_filter = std::string("*");
+ rules_1.push_back(rule);
+
+ std::vector<TestRule> rules_2;
+ rule = CreateGenericRule();
+ rule.condition->url_filter = std::string("google.com");
+ rules_2.push_back(rule);
+
+ ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(
+ rules_1, "extension_1", {URLPattern::kAllUrlsPattern}));
+ const std::string 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();
+
+ auto get_manifest_url = [](const std::string& extension_id) {
+ return GURL(base::StringPrintf("%s://%s/manifest.json",
+ extensions::kExtensionScheme,
+ extension_id.c_str()));
+ };
+
+ // Extension 1 should be able to block the request to its own
+ // manifest.json.
+ ui_test_utils::NavigateToURL(browser(), get_manifest_url(extension_id_1));
+ GURL final_url = web_contents()->GetLastCommittedURL();
+ EXPECT_EQ(content::PAGE_TYPE_ERROR, GetPageType());
+
+ // But it should not be able to intercept requests to the second extensions's
+ // resources, even with "<all_urls>" host permissions.
+ ui_test_utils::NavigateToURL(browser(), get_manifest_url(extension_id_2));
+ EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
+}
+
+// Test fixture to verify that host permissions for the request url and the
+// request initiator are properly checked. Loads an example.com url with four
+// sub-frames named frame_[1..4] from hosts frame_[1..4].com. The initiator for
+// these frames will be example.com. Loads an extension set to block all sub-
+// frames. Verifies that the correct frames are blocked depending on the host
+// permissions for the extension.
+class DeclarativeNetRequestHostPermissionsBrowserTest
+ : public DeclarativeNetRequestBrowserTest {
+ public:
+ DeclarativeNetRequestHostPermissionsBrowserTest() {}
+
+ protected:
+ struct FrameLoadResult {
+ std::string child_frame_name;
+ bool expect_frame_loaded;
+ };
+
+ void LoadExtensionWithHostPermissions(const std::vector<std::string>& hosts) {
+ std::vector<TestRule> rules;
+
+ // Block all sub-frame requests.
+ TestRule rule = CreateGenericRule();
+ rule.condition->url_filter = std::string("*");
+ rule.condition->resource_types = std::vector<std::string>({"sub_frame"});
+ rules.push_back(rule);
+
+ ASSERT_NO_FATAL_FAILURE(
+ LoadExtensionWithRules(rules, "test_extension", hosts));
+ }
+
+ void RunTests(const std::vector<FrameLoadResult>& expected_results) {
+ ASSERT_EQ(4u, expected_results.size());
+
+ GURL url = embedded_test_server()->GetURL("example.com",
+ "/page_with_four_frames.html");
+ ui_test_utils::NavigateToURL(browser(), url);
+ ASSERT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
+
+ for (const auto& frame_result : expected_results) {
+ SCOPED_TRACE(base::StringPrintf("Testing child frame named %s",
+ frame_result.child_frame_name.c_str()));
+
+ content::RenderFrameHost* child =
+ GetFrameByName(frame_result.child_frame_name);
+ EXPECT_TRUE(child);
+ EXPECT_EQ(frame_result.expect_frame_loaded,
+ WasFrameWithScriptLoaded(child));
+ }
+ }
+
+ std::string GetMatchPatternForDomain(const std::string& domain) const {
+ return "*://*." + domain + ".com/*";
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DeclarativeNetRequestHostPermissionsBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestHostPermissionsBrowserTest,
+ AllURLs1) {
+ // All frames should be blocked since the extension has access to all hosts.
+ ASSERT_NO_FATAL_FAILURE(LoadExtensionWithHostPermissions({"<all_urls>"}));
+ RunTests({{"frame_1", false},
+ {"frame_2", false},
+ {"frame_3", false},
+ {"frame_4", false}});
+}
+
+IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestHostPermissionsBrowserTest,
+ AllURLs2) {
+ // All frames should be blocked since the extension has access to all hosts.
+ ASSERT_NO_FATAL_FAILURE(LoadExtensionWithHostPermissions({"*://*/*"}));
+ RunTests({{"frame_1", false},
+ {"frame_2", false},
+ {"frame_3", false},
+ {"frame_4", false}});
+}
+
+IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestHostPermissionsBrowserTest,
+ NoPermissions) {
+ // The extension has no host permissions. No frames should be blocked.
+ ASSERT_NO_FATAL_FAILURE(LoadExtensionWithHostPermissions({}));
+ RunTests({{"frame_1", true},
+ {"frame_2", true},
+ {"frame_3", true},
+ {"frame_4", true}});
+}
+
+IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestHostPermissionsBrowserTest,
+ SubframesWithNoInitiatorPermissions) {
+ // The extension has access to requests to "frame_1.com" and "frame_2.com",
+ // but not the initiator of those requests (example.com). No frames should be
+ // blocked.
+ ASSERT_NO_FATAL_FAILURE(
+ LoadExtensionWithHostPermissions({GetMatchPatternForDomain("frame_1"),
+ GetMatchPatternForDomain("frame_2")}));
+ RunTests({{"frame_1", true},
+ {"frame_2", true},
+ {"frame_3", true},
+ {"frame_4", true}});
+}
+
+IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestHostPermissionsBrowserTest,
+ SubframesWithInitiatorPermission) {
+ // The extension has access to requests to "frame_1.com" and "frame_4.com",
+ // and also the initiator of those requests (example.com). Hence |frame_1| and
+ // |frame_4| should be blocked.
+ ASSERT_NO_FATAL_FAILURE(LoadExtensionWithHostPermissions(
+ {GetMatchPatternForDomain("frame_1"), GetMatchPatternForDomain("frame_4"),
+ GetMatchPatternForDomain("example")}));
+ RunTests({{"frame_1", false},
+ {"frame_2", true},
+ {"frame_3", true},
+ {"frame_4", false}});
+}
+
// Fixture to test the "resourceTypes" and "excludedResourceTypes" fields of a
// declarative rule condition.
class DeclarativeNetRequestResourceTypeBrowserTest
@@ -1220,6 +1397,10 @@ INSTANTIATE_TEST_CASE_P(,
ExtensionLoadType::UNPACKED));
INSTANTIATE_TEST_CASE_P(,
+ DeclarativeNetRequestHostPermissionsBrowserTest,
+ ::testing::Values(ExtensionLoadType::PACKED,
+ ExtensionLoadType::UNPACKED));
+INSTANTIATE_TEST_CASE_P(,
DeclarativeNetRequestResourceTypeBrowserTest,
::testing::Values(ExtensionLoadType::PACKED,
ExtensionLoadType::UNPACKED));
diff --git a/chromium/chrome/browser/extensions/api/declarative_net_request/rule_indexing_unittest.cc b/chromium/chrome/browser/extensions/api/declarative_net_request/rule_indexing_unittest.cc
index e8b3fffeca9..0e5c1f20ae2 100644
--- a/chromium/chrome/browser/extensions/api/declarative_net_request/rule_indexing_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/declarative_net_request/rule_indexing_unittest.cc
@@ -24,6 +24,7 @@
#include "extensions/common/file_util.h"
#include "extensions/common/install_warning.h"
#include "extensions/common/manifest_constants.h"
+#include "extensions/common/url_pattern.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace extensions {
@@ -129,10 +130,12 @@ class RuleIndexingTest : public DNRTestBase {
if (rules_value_) {
WriteManifestAndRuleset(extension_dir_, kJSONRulesetFilepath,
- kJSONRulesFilename, *rules_value_);
+ kJSONRulesFilename, *rules_value_,
+ {URLPattern::kAllUrlsPattern});
} else {
WriteManifestAndRuleset(extension_dir_, kJSONRulesetFilepath,
- kJSONRulesFilename, rules_list_);
+ kJSONRulesFilename, rules_list_,
+ {URLPattern::kAllUrlsPattern});
}
// Overwrite the JSON rules file with some invalid json.
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 eec02539495..932c7add27b 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
@@ -6,6 +6,7 @@
#include "base/files/file_path.h"
#include "base/files/file_util.h"
+#include "base/optional.h"
#include "chrome/browser/extensions/api/declarative_net_request/dnr_test_base.h"
#include "chrome/browser/extensions/chrome_test_extension_loader.h"
#include "chrome/browser/extensions/extension_util.h"
@@ -19,10 +20,12 @@
#include "extensions/common/api/declarative_net_request/constants.h"
#include "extensions/common/api/declarative_net_request/test_utils.h"
#include "extensions/common/file_util.h"
+#include "extensions/common/url_pattern.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
+#include "url/origin.h"
namespace extensions {
namespace declarative_net_request {
@@ -40,14 +43,16 @@ class RulesetManagerTest : public DNRTestBase {
// Helper to create a ruleset matcher instance for the given |rules|.
void CreateMatcherForRules(const std::vector<TestRule>& rules,
const std::string& extension_dirname,
- std::unique_ptr<RulesetMatcher>* matcher) {
+ std::unique_ptr<RulesetMatcher>* matcher,
+ const std::vector<std::string>& host_permissions =
+ {URLPattern::kAllUrlsPattern}) {
base::FilePath extension_dir =
temp_dir().GetPath().AppendASCII(extension_dirname);
// Create extension directory.
ASSERT_TRUE(base::CreateDirectory(extension_dir));
WriteManifestAndRuleset(extension_dir, kJSONRulesetFilepath,
- kJSONRulesFilename, rules);
+ kJSONRulesFilename, rules, host_permissions);
last_loaded_extension_ =
CreateExtensionLoader()->LoadExtension(extension_dir);
@@ -229,23 +234,46 @@ TEST_P(RulesetManagerTest, Redirect) {
rule.action->redirect_url = std::string("http://google.com");
std::unique_ptr<RulesetMatcher> matcher;
ASSERT_NO_FATAL_FAILURE(
- CreateMatcherForRules({rule}, "test_extension", &matcher));
+ CreateMatcherForRules({rule}, "test_extension", &matcher,
+ {"*://example.com/*", "*://abc.com/*"}));
manager->AddRuleset(last_loaded_extension()->id(), std::move(matcher));
+ // Create a request to "example.com" with an empty initiator. It should be
+ // redirected to "google.com".
const bool is_incognito_context = false;
- GURL redirect_url;
+ GURL redirect_url1;
std::unique_ptr<net::URLRequest> request =
GetRequestForURL("http://example.com");
+ request->set_initiator(base::nullopt);
extensions::WebRequestInfo request_info1(request.get());
EXPECT_TRUE(manager->ShouldRedirectRequest(
- request_info1, is_incognito_context, &redirect_url));
- EXPECT_EQ(GURL("http://google.com"), redirect_url);
+ request_info1, is_incognito_context, &redirect_url1));
+ EXPECT_EQ(GURL("http://google.com"), redirect_url1);
+
+ // Change the initiator to "xyz.com". It should not be redirected since we
+ // don't have host permissions to the request initiator.
+ GURL redirect_url2;
+ request->set_initiator(url::Origin::Create(GURL("http://xyz.com")));
+ extensions::WebRequestInfo request_info2(request.get());
+ EXPECT_FALSE(manager->ShouldRedirectRequest(
+ request_info2, is_incognito_context, &redirect_url2));
+
+ // Change the initiator to "abc.com". It should be redirected since we have
+ // the required host permissions.
+ GURL redirect_url3;
+ request->set_initiator(url::Origin::Create(GURL("http://abc.com")));
+ extensions::WebRequestInfo request_info3(request.get());
+ EXPECT_TRUE(manager->ShouldRedirectRequest(
+ request_info3, is_incognito_context, &redirect_url3));
+ EXPECT_EQ(GURL("http://google.com"), redirect_url3);
// Ensure web-socket requests are not redirected.
+ GURL redirect_url4;
request = GetRequestForURL("ws://example.com");
- extensions::WebRequestInfo request_info2(request.get());
+ request->set_initiator(base::nullopt);
+ extensions::WebRequestInfo request_info4(request.get());
EXPECT_FALSE(manager->ShouldRedirectRequest(
- request_info2, is_incognito_context, &redirect_url));
+ request_info4, is_incognito_context, &redirect_url4));
}
INSTANTIATE_TEST_CASE_P(,
diff --git a/chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_matcher_unittest.cc b/chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_matcher_unittest.cc
index 700f504554a..90209860ec6 100644
--- a/chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_matcher_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_matcher_unittest.cc
@@ -18,6 +18,7 @@
#include "extensions/common/api/declarative_net_request/constants.h"
#include "extensions/common/api/declarative_net_request/test_utils.h"
#include "extensions/common/file_util.h"
+#include "extensions/common/url_pattern.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"
@@ -44,7 +45,8 @@ class RulesetMatcherTest : public DNRTestBase {
// Create extension directory.
ASSERT_TRUE(base::CreateDirectory(extension_dir));
WriteManifestAndRuleset(extension_dir, kJSONRulesetFilepath,
- kJSONRulesFilename, rules);
+ kJSONRulesFilename, rules,
+ {URLPattern::kAllUrlsPattern});
extension_ = CreateExtensionLoader()->LoadExtension(extension_dir);
ASSERT_TRUE(extension_);
diff --git a/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc b/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc
index 8bfaa066863..c6c75b48921 100644
--- a/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc
@@ -44,6 +44,8 @@ struct TestFlags {
bool picker_deleted;
};
+// TODO(crbug.com/805145): Uncomment this when test is re-enabled.
+#if 0
DesktopMediaID MakeFakeWebContentsMediaId(bool audio_share) {
DesktopMediaID media_id(DesktopMediaID::TYPE_WEB_CONTENTS,
DesktopMediaID::kNullId,
@@ -52,6 +54,7 @@ DesktopMediaID MakeFakeWebContentsMediaId(bool audio_share) {
media_id.audio_share = audio_share;
return media_id;
}
+#endif
class FakeDesktopMediaPicker : public DesktopMediaPicker {
public:
@@ -63,13 +66,8 @@ class FakeDesktopMediaPicker : public DesktopMediaPicker {
~FakeDesktopMediaPicker() override { expectation_->picker_deleted = true; }
// DesktopMediaPicker interface.
- void Show(content::WebContents* web_contents,
- gfx::NativeWindow context,
- gfx::NativeWindow parent,
- const base::string16& app_name,
- const base::string16& target_name,
+ void Show(const DesktopMediaPicker::Params& params,
std::vector<std::unique_ptr<DesktopMediaList>> source_lists,
- bool request_audio,
const DoneCallback& done_callback) override {
bool show_screens = false;
bool show_windows = false;
@@ -93,7 +91,8 @@ class FakeDesktopMediaPicker : public DesktopMediaPicker {
EXPECT_EQ(expectation_->expect_screens, show_screens);
EXPECT_EQ(expectation_->expect_windows, show_windows);
EXPECT_EQ(expectation_->expect_tabs, show_tabs);
- EXPECT_EQ(expectation_->expect_audio, request_audio);
+ EXPECT_EQ(expectation_->expect_audio, params.request_audio);
+ EXPECT_EQ(params.modality, ui::ModalType::MODAL_TYPE_CHILD);
if (!expectation_->cancelled) {
// Post a task to call the callback asynchronously.
@@ -187,7 +186,8 @@ class DesktopCaptureApiTest : public ExtensionApiTest {
// Flaky on Windows: http://crbug.com/301887
// Fails on Chrome OS: http://crbug.com/718512
-#if defined(OS_WIN) || defined(OS_CHROMEOS)
+// Flaky on macOS: http://crbug.com/804897
+#if defined(OS_WIN) || defined(OS_CHROMEOS) || defined(OS_MACOSX)
#define MAYBE_ChooseDesktopMedia DISABLED_ChooseDesktopMedia
#else
#define MAYBE_ChooseDesktopMedia ChooseDesktopMedia
@@ -196,52 +196,55 @@ IN_PROC_BROWSER_TEST_F(DesktopCaptureApiTest, MAYBE_ChooseDesktopMedia) {
// Each element in the following array corresponds to one test in
// chrome/test/data/extensions/api_test/desktop_capture/test.js .
TestFlags test_flags[] = {
- // pickerUiCanceled()
- {true, true, false, false, DesktopMediaID()},
- // chooseMedia()
- {true, true, false, false,
- DesktopMediaID(DesktopMediaID::TYPE_SCREEN, DesktopMediaID::kNullId)},
- // screensOnly()
- {true, false, false, false, DesktopMediaID()},
- // WindowsOnly()
- {false, true, false, false, DesktopMediaID()},
- // tabOnly()
- {false, false, true, false, DesktopMediaID()},
- // audioShareNoApproval()
- {true, true, true, true,
- DesktopMediaID(DesktopMediaID::TYPE_WEB_CONTENTS, 123, false)},
- // audioShareApproval()
- {true, true, true, true,
- DesktopMediaID(DesktopMediaID::TYPE_WEB_CONTENTS, 123, true)},
- // chooseMediaAndGetStream()
- {true, true, false, false,
- DesktopMediaID(DesktopMediaID::TYPE_SCREEN,
- webrtc::kFullDesktopScreenId)},
- // chooseMediaAndTryGetStreamWithInvalidId()
- {true, true, false, false,
- DesktopMediaID(DesktopMediaID::TYPE_SCREEN,
- webrtc::kFullDesktopScreenId)},
- // cancelDialog()
- {true, true, false, false, DesktopMediaID(), true},
+ // pickerUiCanceled()
+ {true, true, false, false, DesktopMediaID()},
+ // chooseMedia()
+ {true, true, false, false,
+ DesktopMediaID(DesktopMediaID::TYPE_SCREEN, DesktopMediaID::kNullId)},
+ // screensOnly()
+ {true, false, false, false, DesktopMediaID()},
+ // WindowsOnly()
+ {false, true, false, false, DesktopMediaID()},
+ // tabOnly()
+ {false, false, true, false, DesktopMediaID()},
+ // audioShareNoApproval()
+ {true, true, true, true,
+ DesktopMediaID(DesktopMediaID::TYPE_WEB_CONTENTS, 123, false)},
+ // audioShareApproval()
+ {true, true, true, true,
+ DesktopMediaID(DesktopMediaID::TYPE_WEB_CONTENTS, 123, true)},
+ // chooseMediaAndGetStream()
+ {true, true, false, false,
+ DesktopMediaID(DesktopMediaID::TYPE_SCREEN, webrtc::kFullDesktopScreenId)},
+ // chooseMediaAndTryGetStreamWithInvalidId()
+ {true, true, false, false,
+ DesktopMediaID(DesktopMediaID::TYPE_SCREEN, webrtc::kFullDesktopScreenId)},
+ // cancelDialog()
+ {true, true, false, false, DesktopMediaID(), true},
+// TODO(crbug.com/805145): Test fails; invalid device IDs being generated.
+#if 0
// tabShareWithAudioGetStream()
{false, false, true, true, MakeFakeWebContentsMediaId(true)},
- // windowShareWithAudioGetStream()
- {false, true, false, true,
- DesktopMediaID(DesktopMediaID::TYPE_WINDOW, DesktopMediaID::kFakeId,
- true)},
- // screenShareWithAudioGetStream()
- {true, false, false, true,
- DesktopMediaID(DesktopMediaID::TYPE_SCREEN, webrtc::kFullDesktopScreenId,
- true)},
+#endif
+ // windowShareWithAudioGetStream()
+ {false, true, false, true,
+ DesktopMediaID(DesktopMediaID::TYPE_WINDOW, DesktopMediaID::kFakeId,
+ true)},
+ // screenShareWithAudioGetStream()
+ {true, false, false, true,
+ DesktopMediaID(DesktopMediaID::TYPE_SCREEN, webrtc::kFullDesktopScreenId,
+ true)},
+// TODO(crbug.com/805145): Test fails; invalid device IDs being generated.
+#if 0
// tabShareWithoutAudioGetStream()
{false, false, true, true, MakeFakeWebContentsMediaId(false)},
- // windowShareWithoutAudioGetStream()
- {false, true, false, true,
- DesktopMediaID(DesktopMediaID::TYPE_WINDOW, DesktopMediaID::kFakeId)},
- // screenShareWithoutAudioGetStream()
- {true, false, false, true,
- DesktopMediaID(DesktopMediaID::TYPE_SCREEN,
- webrtc::kFullDesktopScreenId)},
+#endif
+ // windowShareWithoutAudioGetStream()
+ {false, true, false, true,
+ DesktopMediaID(DesktopMediaID::TYPE_WINDOW, DesktopMediaID::kFakeId)},
+ // screenShareWithoutAudioGetStream()
+ {true, false, false, true,
+ DesktopMediaID(DesktopMediaID::TYPE_SCREEN, webrtc::kFullDesktopScreenId)},
};
picker_factory_.SetTestFlags(test_flags, arraysize(test_flags));
ASSERT_TRUE(RunExtensionTest("desktop_capture")) << message_;
diff --git a/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc b/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc
index 392ecad385f..4e8b6cc9ffa 100644
--- a/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc
+++ b/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc
@@ -26,8 +26,6 @@
#include "content/public/browser/web_contents.h"
#include "extensions/common/manifest.h"
#include "extensions/common/switches.h"
-#include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
-#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h"
#include "ui/base/l10n/l10n_util.h"
using extensions::api::desktop_capture::ChooseDesktopMedia::Results::Options;
@@ -123,8 +121,7 @@ bool DesktopCaptureChooseDesktopMediaFunctionBase::Execute(
#else // !defined(OS_CHROMEOS)
screen_list = std::make_unique<NativeDesktopMediaList>(
content::DesktopMediaID::TYPE_SCREEN,
- webrtc::DesktopCapturer::CreateScreenCapturer(
- content::CreateDesktopCaptureOptions()));
+ content::desktop_capture::CreateScreenCapturer());
#endif // !defined(OS_CHROMEOS)
}
have_screen_list = true;
@@ -151,8 +148,7 @@ bool DesktopCaptureChooseDesktopMediaFunctionBase::Execute(
// used on multiple threads concurrently.
window_list = std::make_unique<NativeDesktopMediaList>(
content::DesktopMediaID::TYPE_WINDOW,
- webrtc::DesktopCapturer::CreateWindowCapturer(
- content::CreateDesktopCaptureOptions()));
+ content::desktop_capture::CreateWindowCapturer());
#endif // !defined(OS_CHROMEOS)
}
have_window_list = true;
@@ -208,10 +204,14 @@ bool DesktopCaptureChooseDesktopMediaFunctionBase::Execute(
DesktopMediaPicker::DoneCallback callback = base::Bind(
&DesktopCaptureChooseDesktopMediaFunctionBase::OnPickerDialogResults,
this);
-
- picker_->Show(web_contents, parent_window, parent_window,
- base::UTF8ToUTF16(GetCallerDisplayName()), target_name,
- std::move(source_lists), request_audio, callback);
+ DesktopMediaPicker::Params picker_params;
+ picker_params.web_contents = web_contents;
+ picker_params.context = parent_window;
+ picker_params.parent = parent_window;
+ picker_params.app_name = base::UTF8ToUTF16(GetCallerDisplayName());
+ picker_params.target_name = target_name;
+ picker_params.request_audio = request_audio;
+ picker_->Show(picker_params, std::move(source_lists), callback);
origin_ = origin;
return true;
}
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 dfa6519275e..0cdd23224d9 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
@@ -21,7 +21,6 @@
#include "chrome/browser/extensions/extension_service_test_with_install.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/extensions/scripting_permissions_modifier.h"
-#include "chrome/browser/extensions/test_extension_dir.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/browser/extensions/unpacked_installer.h"
#include "chrome/browser/ui/browser.h"
@@ -55,6 +54,7 @@
#include "extensions/common/feature_switch.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/value_builder.h"
+#include "extensions/test/test_extension_dir.h"
using testing::Return;
using testing::_;
@@ -156,8 +156,7 @@ bool DeveloperPrivateApiUnitTest::RunFunction(
const scoped_refptr<UIThreadExtensionFunction>& function,
const base::ListValue& args) {
return extension_function_test_utils::RunFunction(
- function.get(), args.CreateDeepCopy(), browser(),
- extension_function_test_utils::NONE);
+ function.get(), args.CreateDeepCopy(), browser(), api_test_utils::NONE);
}
const Extension* DeveloperPrivateApiUnitTest::LoadUnpackedExtension() {
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 a8a5fa6acda..aaa7aced75d 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
@@ -73,7 +73,7 @@ IN_PROC_BROWSER_TEST_F(DeveloperPrivateApiTest, InspectAppWindowView) {
base::StringPrintf("[{\"renderViewId\": %d, \"renderProcessId\": %d}]",
window_view->render_view_id,
window_view->render_process_id),
- browser(), extension_function_test_utils::NONE);
+ browser(), api_test_utils::NONE);
// Verify that dev tools opened.
std::list<AppWindow*> app_windows =
@@ -119,7 +119,7 @@ IN_PROC_BROWSER_TEST_F(DeveloperPrivateApiTest, InspectEmbeddedOptionsPage) {
function.get(),
base::StringPrintf("[{\"renderViewId\": %d, \"renderProcessId\": %d}]",
view.render_view_id, view.render_process_id),
- browser(), extension_function_test_utils::NONE);
+ browser(), api_test_utils::NONE);
// Verify that dev tools opened.
content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
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 53965e17392..15b031d88d6 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
@@ -262,7 +262,7 @@ void ExtensionInfoGenerator::CreateExtensionInfo(
if (pending_image_loads_ == 0) {
// Don't call the callback re-entrantly.
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(callback, base::Passed(&list_)));
+ FROM_HERE, base::BindOnce(callback, std::move(list_)));
list_.clear();
} else {
callback_ = callback;
@@ -300,7 +300,7 @@ void ExtensionInfoGenerator::CreateExtensionsInfo(
if (pending_image_loads_ == 0) {
// Don't call the callback re-entrantly.
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(callback, base::Passed(&list_)));
+ FROM_HERE, base::BindOnce(callback, std::move(list_)));
list_.clear();
} else {
callback_ = callback;
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 8eb8f33bd4f..72c1bc79a1a 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
@@ -82,7 +82,7 @@ class ExtensionInfoGeneratorUnitTest : public ExtensionServiceTestBase {
EXPECT_EQ(1u, list.size());
if (!list.empty())
info_out->reset(new developer::ExtensionInfo(std::move(list[0])));
- base::ResetAndReturn(&quit_closure_).Run();
+ std::move(quit_closure_).Run();
}
std::unique_ptr<developer::ExtensionInfo> GenerateExtensionInfo(
@@ -103,7 +103,7 @@ class ExtensionInfoGeneratorUnitTest : public ExtensionServiceTestBase {
void OnInfosGenerated(ExtensionInfoGenerator::ExtensionInfoList* out,
ExtensionInfoGenerator::ExtensionInfoList list) {
*out = std::move(list);
- base::ResetAndReturn(&quit_closure_).Run();
+ std::move(quit_closure_).Run();
}
ExtensionInfoGenerator::ExtensionInfoList GenerateExtensionsInfo() {
@@ -204,7 +204,7 @@ class ExtensionInfoGeneratorUnitTest : public ExtensionServiceTestBase {
}
private:
- base::Closure quit_closure_;
+ base::OnceClosure quit_closure_;
DISALLOW_COPY_AND_ASSIGN(ExtensionInfoGeneratorUnitTest);
};
diff --git a/chromium/chrome/browser/extensions/api/device_permissions_manager_unittest.cc b/chromium/chrome/browser/extensions/api/device_permissions_manager_unittest.cc
index e77604705a4..bb87fbcbfbd 100644
--- a/chromium/chrome/browser/extensions/api/device_permissions_manager_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/device_permissions_manager_unittest.cc
@@ -18,7 +18,7 @@
#include "extensions/browser/api/hid/hid_device_manager.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/common/extension.h"
-#include "services/device/public/interfaces/hid.mojom.h"
+#include "services/device/public/mojom/hid.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/chrome/browser/extensions/api/dial/dial_api.cc b/chromium/chrome/browser/extensions/api/dial/dial_api.cc
index a5922dc9eed..04c086f843e 100644
--- a/chromium/chrome/browser/extensions/api/dial/dial_api.cc
+++ b/chromium/chrome/browser/extensions/api/dial/dial_api.cc
@@ -255,7 +255,7 @@ void DialFetchDeviceDescriptionFunction::MaybeStartFetch(const GURL& url) {
}
device_description_fetcher_ = std::make_unique<DeviceDescriptionFetcher>(
- url, Profile::FromBrowserContext(browser_context())->GetRequestContext(),
+ url,
base::BindOnce(&DialFetchDeviceDescriptionFunction::OnFetchComplete,
this),
base::BindOnce(&DialFetchDeviceDescriptionFunction::OnFetchError, this));
diff --git a/chromium/chrome/browser/extensions/api/dial/dial_apitest.cc b/chromium/chrome/browser/extensions/api/dial/dial_apitest.cc
index 67049da9bf4..1a8413a289c 100644
--- a/chromium/chrome/browser/extensions/api/dial/dial_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/dial/dial_apitest.cc
@@ -9,6 +9,8 @@
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/media/router/discovery/dial/dial_registry.h"
+#include "chrome/browser/media/router/providers/cast/dual_media_sink_service.h"
+#include "chrome/browser/media/router/test/noop_dual_media_sink_service.h"
#include "extensions/common/switches.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/result_catcher.h"
@@ -33,6 +35,13 @@ class DialAPITest : public ExtensionApiTest {
extensions::switches::kWhitelistedExtensionID,
"ddchlicdkolnonkihahngkmmmjnjlkkf");
}
+
+ void SetUp() override {
+ // Stub out DualMediaSinkService so it does not interfere with the test.
+ media_router::DualMediaSinkService::SetInstanceForTest(
+ new media_router::NoopDualMediaSinkService());
+ ExtensionApiTest::SetUp();
+ }
};
} // namespace
diff --git a/chromium/chrome/browser/extensions/api/downloads/downloads_api.cc b/chromium/chrome/browser/extensions/api/downloads/downloads_api.cc
index e4fd3fb38a5..5e70f755f52 100644
--- a/chromium/chrome/browser/extensions/api/downloads/downloads_api.cc
+++ b/chromium/chrome/browser/extensions/api/downloads/downloads_api.cc
@@ -31,9 +31,9 @@
#include "base/strings/string16.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
#include "base/task/cancelable_task_tracker.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time_to_iso8601.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
@@ -56,11 +56,10 @@
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/common/extensions/api/downloads.h"
+#include "components/download/public/common/download_interrupt_reasons.h"
+#include "components/download/public/common/download_item.h"
+#include "components/download/public/common/download_url_parameters.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h"
-#include "content/public/browser/download_interrupt_reasons.h"
-#include "content/public/browser/download_item.h"
-#include "content/public/browser/download_save_info.h"
-#include "content/public/browser/download_url_parameters.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
@@ -87,7 +86,7 @@
using content::BrowserContext;
using content::BrowserThread;
-using content::DownloadItem;
+using download::DownloadItem;
using content::DownloadManager;
namespace download_extension_errors {
@@ -193,7 +192,7 @@ const char* const kDangerStrings[] = {
kDangerHost,
kDangerUnwanted
};
-static_assert(arraysize(kDangerStrings) == content::DOWNLOAD_DANGER_TYPE_MAX,
+static_assert(arraysize(kDangerStrings) == download::DOWNLOAD_DANGER_TYPE_MAX,
"kDangerStrings should have DOWNLOAD_DANGER_TYPE_MAX elements");
// Note: Any change to the state strings, should be accompanied by a
@@ -204,38 +203,40 @@ const char* const kStateStrings[] = {
kStateInterrupted,
kStateInterrupted,
};
-static_assert(arraysize(kStateStrings) == DownloadItem::MAX_DOWNLOAD_STATE,
+static_assert(arraysize(kStateStrings) ==
+ download::DownloadItem::MAX_DOWNLOAD_STATE,
"kStateStrings should have MAX_DOWNLOAD_STATE elements");
-const char* DangerString(content::DownloadDangerType danger) {
+const char* DangerString(download::DownloadDangerType danger) {
DCHECK(danger >= 0);
- DCHECK(danger < static_cast<content::DownloadDangerType>(
- arraysize(kDangerStrings)));
- if (danger < 0 || danger >= static_cast<content::DownloadDangerType>(
- arraysize(kDangerStrings)))
+ DCHECK(danger <
+ static_cast<download::DownloadDangerType>(arraysize(kDangerStrings)));
+ if (danger < 0 || danger >= static_cast<download::DownloadDangerType>(
+ arraysize(kDangerStrings)))
return "";
return kDangerStrings[danger];
}
-content::DownloadDangerType DangerEnumFromString(const std::string& danger) {
+download::DownloadDangerType DangerEnumFromString(const std::string& danger) {
for (size_t i = 0; i < arraysize(kDangerStrings); ++i) {
if (danger == kDangerStrings[i])
- return static_cast<content::DownloadDangerType>(i);
+ return static_cast<download::DownloadDangerType>(i);
}
- return content::DOWNLOAD_DANGER_TYPE_MAX;
+ return download::DOWNLOAD_DANGER_TYPE_MAX;
}
-const char* StateString(DownloadItem::DownloadState state) {
+const char* StateString(download::DownloadItem::DownloadState state) {
DCHECK(state >= 0);
- DCHECK(state < static_cast<DownloadItem::DownloadState>(
- arraysize(kStateStrings)));
- if (state < 0 || state >= static_cast<DownloadItem::DownloadState>(
- arraysize(kStateStrings)))
+ DCHECK(state < static_cast<download::DownloadItem::DownloadState>(
+ arraysize(kStateStrings)));
+ if (state < 0 || state >= static_cast<download::DownloadItem::DownloadState>(
+ arraysize(kStateStrings)))
return "";
return kStateStrings[state];
}
-DownloadItem::DownloadState StateEnumFromString(const std::string& state) {
+download::DownloadItem::DownloadState StateEnumFromString(
+ const std::string& state) {
for (size_t i = 0; i < arraysize(kStateStrings); ++i) {
if ((kStateStrings[i] != NULL) && (state == kStateStrings[i]))
return static_cast<DownloadItem::DownloadState>(i);
@@ -243,15 +244,6 @@ DownloadItem::DownloadState StateEnumFromString(const std::string& state) {
return DownloadItem::MAX_DOWNLOAD_STATE;
}
-std::string TimeToISO8601(const base::Time& t) {
- base::Time::Exploded exploded;
- t.UTCExplode(&exploded);
- return base::StringPrintf(
- "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", exploded.year, exploded.month,
- exploded.day_of_month, exploded.hour, exploded.minute, exploded.second,
- exploded.millisecond);
-}
-
std::unique_ptr<base::DictionaryValue> DownloadItemToJSON(
DownloadItem* download_item,
content::BrowserContext* browser_context) {
@@ -273,25 +265,27 @@ std::unique_ptr<base::DictionaryValue> DownloadItemToJSON(
json->SetBoolean(kCanResumeKey, download_item->CanResume());
json->SetBoolean(kPausedKey, download_item->IsPaused());
json->SetString(kMimeKey, download_item->GetMimeType());
- json->SetString(kStartTimeKey, TimeToISO8601(download_item->GetStartTime()));
+ json->SetString(kStartTimeKey,
+ base::TimeToISO8601(download_item->GetStartTime()));
json->SetDouble(kBytesReceivedKey, download_item->GetReceivedBytes());
json->SetDouble(kTotalBytesKey, download_item->GetTotalBytes());
json->SetBoolean(kIncognitoKey, browser_context->IsOffTheRecord());
if (download_item->GetState() == DownloadItem::INTERRUPTED) {
- json->SetString(kErrorKey,
- content::DownloadInterruptReasonToString(
- download_item->GetLastReason()));
+ json->SetString(kErrorKey, download::DownloadInterruptReasonToString(
+ download_item->GetLastReason()));
} else if (download_item->GetState() == DownloadItem::CANCELLED) {
json->SetString(kErrorKey,
- content::DownloadInterruptReasonToString(
- content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED));
+ download::DownloadInterruptReasonToString(
+ download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED));
}
if (!download_item->GetEndTime().is_null())
- json->SetString(kEndTimeKey, TimeToISO8601(download_item->GetEndTime()));
+ json->SetString(kEndTimeKey,
+ base::TimeToISO8601(download_item->GetEndTime()));
base::TimeDelta time_remaining;
if (download_item->TimeRemaining(&time_remaining)) {
base::Time now = base::Time::Now();
- json->SetString(kEstimatedEndTimeKey, TimeToISO8601(now + time_remaining));
+ json->SetString(kEstimatedEndTimeKey,
+ base::TimeToISO8601(now + time_remaining));
}
DownloadedByExtension* by_ext = DownloadedByExtension::Get(download_item);
if (by_ext) {
@@ -563,9 +557,9 @@ void RunDownloadQuery(
std::string danger_string =
downloads::ToString(query_in.danger);
if (!danger_string.empty()) {
- content::DownloadDangerType danger_type = DangerEnumFromString(
- danger_string);
- if (danger_type == content::DOWNLOAD_DANGER_TYPE_MAX) {
+ download::DownloadDangerType danger_type =
+ DangerEnumFromString(danger_string);
+ if (danger_type == download::DOWNLOAD_DANGER_TYPE_MAX) {
*error = errors::kInvalidDangerType;
return;
}
@@ -768,11 +762,10 @@ class ExtensionDownloadsEventRouterData : public base::SupportsUserData::Data {
// Returns false if this |extension_id| was not expected or if this
// |extension_id| has already reported. The caller is responsible for
// validating |filename|.
- bool DeterminerCallback(
- Profile* profile,
- const std::string& extension_id,
- const base::FilePath& filename,
- downloads::FilenameConflictAction conflict_action) {
+ bool DeterminerCallback(content::BrowserContext* browser_context,
+ const std::string& extension_id,
+ const base::FilePath& filename,
+ downloads::FilenameConflictAction conflict_action) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
bool found_info = false;
for (size_t index = 0; index < determiners_.size(); ++index) {
@@ -800,7 +793,7 @@ class ExtensionDownloadsEventRouterData : public base::SupportsUserData::Data {
&determined_conflict_action_,
&warnings);
if (!warnings.empty())
- WarningService::NotifyWarningsOnUI(profile, warnings);
+ WarningService::NotifyWarningsOnUI(browser_context, warnings);
if (winner_extension_id == determiners_[index].extension_id)
determiner_ = determiners_[index];
}
@@ -965,18 +958,16 @@ const char DownloadedByExtension::kKey[] =
"DownloadItem DownloadedByExtension";
DownloadedByExtension* DownloadedByExtension::Get(
- content::DownloadItem* item) {
+ download::DownloadItem* item) {
base::SupportsUserData::Data* data = item->GetUserData(kKey);
return (data == NULL) ? NULL :
static_cast<DownloadedByExtension*>(data);
}
-DownloadedByExtension::DownloadedByExtension(
- content::DownloadItem* item,
- const std::string& id,
- const std::string& name)
- : id_(id),
- name_(name) {
+DownloadedByExtension::DownloadedByExtension(download::DownloadItem* item,
+ const std::string& id,
+ const std::string& name)
+ : id_(id), name_(name) {
item->SetUserData(kKey, base::WrapUnique(this));
}
@@ -1030,8 +1021,8 @@ bool DownloadsDownloadFunction::RunAsync() {
}
}
})");
- std::unique_ptr<content::DownloadUrlParameters> download_params(
- new content::DownloadUrlParameters(
+ std::unique_ptr<download::DownloadUrlParameters> download_params(
+ new download::DownloadUrlParameters(
download_url, render_frame_host()->GetProcess()->GetID(),
render_frame_host()->GetRenderViewHost()->GetRoutingID(),
render_frame_host()->GetRoutingID(),
@@ -1082,14 +1073,18 @@ bool DownloadsDownloadFunction::RunAsync() {
downloads::ToString(options.method);
if (!method_string.empty())
download_params->set_method(method_string);
- if (options.body.get())
- download_params->set_post_body(*options.body);
+ if (options.body.get()) {
+ download_params->set_post_body(
+ network::ResourceRequestBody::CreateFromBytes(options.body->data(),
+ options.body->size()));
+ }
+
download_params->set_callback(base::Bind(
&DownloadsDownloadFunction::OnStarted, this,
creator_suggested_filename, options.conflict_action));
// Prevent login prompts for 401/407 responses.
download_params->set_do_not_prompt_for_login(true);
- download_params->set_download_source(content::DownloadSource::EXTENSION_API);
+ download_params->set_download_source(download::DownloadSource::EXTENSION_API);
DownloadManager* manager = BrowserContext::GetDownloadManager(
current_profile);
@@ -1102,11 +1097,11 @@ void DownloadsDownloadFunction::OnStarted(
const base::FilePath& creator_suggested_filename,
downloads::FilenameConflictAction creator_conflict_action,
DownloadItem* item,
- content::DownloadInterruptReason interrupt_reason) {
+ download::DownloadInterruptReason interrupt_reason) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
VLOG(1) << __func__ << " " << item << " " << interrupt_reason;
if (item) {
- DCHECK_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
+ DCHECK_EQ(download::DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
SetResult(std::make_unique<base::Value>(static_cast<int>(item->GetId())));
if (!creator_suggested_filename.empty() ||
(creator_conflict_action !=
@@ -1124,8 +1119,8 @@ void DownloadsDownloadFunction::OnStarted(
new DownloadedByExtension(item, extension()->id(), extension()->name());
item->UpdateObservers();
} else {
- DCHECK_NE(content::DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
- error_ = content::DownloadInterruptReasonToString(interrupt_reason);
+ DCHECK_NE(download::DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
+ error_ = download::DownloadInterruptReasonToString(interrupt_reason);
}
SendResponse(error_.empty());
}
@@ -1562,10 +1557,8 @@ bool DownloadsGetFileIconFunction::RunAsync() {
float scale = 1.0;
content::WebContents* web_contents =
dispatcher()->GetVisibleWebContents();
- if (web_contents) {
- scale = ui::GetScaleFactorForNativeView(
- web_contents->GetRenderWidgetHostView()->GetNativeView());
- }
+ if (web_contents && web_contents->GetRenderWidgetHostView())
+ scale = web_contents->GetRenderWidgetHostView()->GetDeviceScaleFactor();
EXTENSION_FUNCTION_VALIDATE(icon_extractor_->ExtractIconURLForPath(
download_item->GetTargetFilePath(),
scale,
@@ -1733,7 +1726,7 @@ void ExtensionDownloadsEventRouter::DetermineFilenameInternal(
}
bool ExtensionDownloadsEventRouter::DetermineFilename(
- Profile* profile,
+ content::BrowserContext* browser_context,
bool include_incognito,
const std::string& ext_id,
int download_id,
@@ -1742,7 +1735,8 @@ bool ExtensionDownloadsEventRouter::DetermineFilename(
std::string* error) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RecordApiFunctions(DOWNLOADS_FUNCTION_DETERMINE_FILENAME);
- DownloadItem* item = GetDownload(profile, include_incognito, download_id);
+ DownloadItem* item =
+ GetDownload(browser_context, include_incognito, download_id);
ExtensionDownloadsEventRouterData* data =
item ? ExtensionDownloadsEventRouterData::Get(item) : NULL;
// maxListeners=1 in downloads.idl and suggestCallback in
@@ -1766,8 +1760,8 @@ bool ExtensionDownloadsEventRouter::DetermineFilename(
base::FilePath());
// If the invalid filename check is moved to before DeterminerCallback(), then
// it will block forever waiting for this ext_id to report.
- if (Fault(!data->DeterminerCallback(
- profile, ext_id, filename, conflict_action),
+ if (Fault(!data->DeterminerCallback(browser_context, ext_id, filename,
+ conflict_action),
errors::kUnexpectedDeterminer, error) ||
Fault((!const_filename.empty() && !valid_filename),
errors::kInvalidFilename, error))
diff --git a/chromium/chrome/browser/extensions/api/downloads/downloads_api.h b/chromium/chrome/browser/extensions/api/downloads/downloads_api.h
index 084d39b0bc1..cd6e5759ce8 100644
--- a/chromium/chrome/browser/extensions/api/downloads/downloads_api.h
+++ b/chromium/chrome/browser/extensions/api/downloads/downloads_api.h
@@ -69,9 +69,9 @@ namespace extensions {
class DownloadedByExtension : public base::SupportsUserData::Data {
public:
- static DownloadedByExtension* Get(content::DownloadItem* item);
+ static DownloadedByExtension* Get(download::DownloadItem* item);
- DownloadedByExtension(content::DownloadItem* item,
+ DownloadedByExtension(download::DownloadItem* item,
const std::string& id,
const std::string& name);
@@ -100,8 +100,8 @@ class DownloadsDownloadFunction : public ChromeAsyncExtensionFunction {
void OnStarted(const base::FilePath& creator_suggested_filename,
extensions::api::downloads::FilenameConflictAction
creator_conflict_action,
- content::DownloadItem* item,
- content::DownloadInterruptReason interrupt_reason);
+ download::DownloadItem* item,
+ download::DownloadInterruptReason interrupt_reason);
DISALLOW_COPY_AND_ASSIGN(DownloadsDownloadFunction);
};
@@ -330,7 +330,7 @@ class ExtensionDownloadsEventRouter
// existing files, then |overwrite| will be true. Returns true on success,
// false otherwise.
static bool DetermineFilename(
- Profile* profile,
+ content::BrowserContext* browser_context,
bool include_incognito,
const std::string& ext_id,
int download_id,
@@ -352,19 +352,18 @@ class ExtensionDownloadsEventRouter
// an extension wants to change the target filename, then |change| will be
// called with the new filename and a flag indicating whether the new file
// should overwrite any old files of the same name.
- void OnDeterminingFilename(
- content::DownloadItem* item,
- const base::FilePath& suggested_path,
- const base::Closure& no_change,
- const FilenameChangedCallback& change);
+ void OnDeterminingFilename(download::DownloadItem* item,
+ const base::FilePath& suggested_path,
+ const base::Closure& no_change,
+ const FilenameChangedCallback& change);
// AllDownloadItemNotifier::Observer.
void OnDownloadCreated(content::DownloadManager* manager,
- content::DownloadItem* download_item) override;
+ download::DownloadItem* download_item) override;
void OnDownloadUpdated(content::DownloadManager* manager,
- content::DownloadItem* download_item) override;
+ download::DownloadItem* download_item) override;
void OnDownloadRemoved(content::DownloadManager* manager,
- content::DownloadItem* download_item) override;
+ download::DownloadItem* download_item) override;
// extensions::EventRouter::Observer.
void OnListenerRemoved(const extensions::EventListenerInfo& details) override;
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 ee607987815..618452122d6 100644
--- a/chromium/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
+++ b/chromium/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -26,28 +26,29 @@
#include "chrome/browser/download/download_file_icon_extractor.h"
#include "chrome/browser/download/download_test_file_activity_observer.h"
#include "chrome/browser/extensions/api/downloads/downloads_api.h"
-#include "chrome/browser/extensions/browser_action_test_util.h"
+#include "chrome/browser/extensions/api/downloads_internal/downloads_internal_api.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_function_test_utils.h"
#include "chrome/browser/net/url_request_mock_util.h"
+#include "chrome/browser/platform_util_internal.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/extensions/browser_action_test_util.h"
#include "chrome/common/extensions/api/downloads.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/download/public/common/download_item.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/download_item.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
-#include "content/public/test/controllable_http_response.h"
#include "content/public/test/download_test_observer.h"
#include "content/public/test/test_download_http_response.h"
#include "content/public/test/test_utils.h"
@@ -55,6 +56,7 @@
#include "extensions/browser/notification_types.h"
#include "net/base/data_url.h"
#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/controllable_http_response.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/url_request/url_request_slow_download_job.h"
#include "net/url_request/url_request.h"
@@ -69,7 +71,7 @@
using content::BrowserContext;
using content::BrowserThread;
-using content::DownloadItem;
+using download::DownloadItem;
using content::DownloadManager;
namespace errors = download_extension_errors;
@@ -270,7 +272,7 @@ class DownloadExtensionTest : public ExtensionApiTest {
// Danger type for the download. Only use DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS
// and DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT.
- content::DownloadDangerType danger_type;
+ download::DownloadDangerType danger_type;
};
void LoadExtension(const char* name) {
@@ -329,10 +331,12 @@ class DownloadExtensionTest : public ExtensionApiTest {
DownloadTestFileActivityObserver observer(current_browser()->profile());
observer.EnableFileChooser(false);
- first_download_ = std::make_unique<content::ControllableHttpResponse>(
- embedded_test_server(), kFirstDownloadUrl);
- second_download_ = std::make_unique<content::ControllableHttpResponse>(
- embedded_test_server(), kSecondDownloadUrl);
+ first_download_ =
+ std::make_unique<net::test_server::ControllableHttpResponse>(
+ embedded_test_server(), kFirstDownloadUrl);
+ second_download_ =
+ std::make_unique<net::test_server::ControllableHttpResponse>(
+ embedded_test_server(), kSecondDownloadUrl);
host_resolver()->AddRule("*", "127.0.0.1");
}
@@ -354,10 +358,9 @@ class DownloadExtensionTest : public ExtensionApiTest {
current_browser()->profile(), event_name, json_args);
}
- bool WaitForInterruption(
- DownloadItem* item,
- content::DownloadInterruptReason expected_error,
- const std::string& on_created_event) {
+ bool WaitForInterruption(DownloadItem* item,
+ download::DownloadInterruptReason expected_error,
+ const std::string& on_created_event) {
if (!WaitFor(downloads::OnCreated::kEventName, on_created_event))
return false;
// Now, onCreated is always fired before interruption.
@@ -370,7 +373,7 @@ class DownloadExtensionTest : public ExtensionApiTest {
" \"previous\": \"in_progress\","
" \"current\": \"interrupted\"}}]",
item->GetId(),
- content::DownloadInterruptReasonToString(expected_error).c_str()));
+ download::DownloadInterruptReasonToString(expected_error).c_str()));
}
void ClearEvents() {
@@ -426,7 +429,7 @@ class DownloadExtensionTest : public ExtensionApiTest {
url_chain.push_back(GURL());
for (size_t i = 0; i < count; ++i) {
DownloadItem* item = GetOnRecordManager()->CreateDownloadItem(
- base::GenerateGUID(), content::DownloadItem::kInvalidId + 1 + i,
+ base::GenerateGUID(), download::DownloadItem::kInvalidId + 1 + i,
downloads_directory().Append(history_info[i].filename),
downloads_directory().Append(history_info[i].filename), url_chain,
GURL(), GURL(), GURL(), GURL(), std::string(),
@@ -440,9 +443,9 @@ class DownloadExtensionTest : public ExtensionApiTest {
std::string(), // hash
history_info[i].state, // state
history_info[i].danger_type,
- (history_info[i].state != content::DownloadItem::CANCELLED
- ? content::DOWNLOAD_INTERRUPT_REASON_NONE
- : content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED),
+ (history_info[i].state != download::DownloadItem::CANCELLED
+ ? download::DOWNLOAD_INTERRUPT_REASON_NONE
+ : download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED),
false, // opened
current, // last_access_time
false, std::vector<DownloadItem::ReceivedSlice>());
@@ -477,7 +480,7 @@ class DownloadExtensionTest : public ExtensionApiTest {
}
DownloadItem* CreateSlowTestDownload(
- content::ControllableHttpResponse* response,
+ net::test_server::ControllableHttpResponse* response,
const std::string& path) {
if (!embedded_test_server()->Started())
StartEmbeddedTestServer();
@@ -515,7 +518,8 @@ class DownloadExtensionTest : public ExtensionApiTest {
FinishSlowDownloads(second_download_.get());
}
- void FinishSlowDownloads(content::ControllableHttpResponse* response) {
+ void FinishSlowDownloads(
+ net::test_server::ControllableHttpResponse* response) {
std::unique_ptr<content::DownloadTestObserver> observer(
CreateDownloadObserver(1));
response->Done();
@@ -547,10 +551,10 @@ class DownloadExtensionTest : public ExtensionApiTest {
return result;
}
- extension_function_test_utils::RunFunctionFlags GetFlags() {
- return current_browser()->profile()->IsOffTheRecord() ?
- extension_function_test_utils::INCLUDE_INCOGNITO :
- extension_function_test_utils::NONE;
+ api_test_utils::RunFunctionFlags GetFlags() {
+ return current_browser()->profile()->IsOffTheRecord()
+ ? api_test_utils::INCLUDE_INCOGNITO
+ : api_test_utils::NONE;
}
// extension_function_test_utils::RunFunction*() only uses browser for its
@@ -619,8 +623,8 @@ class DownloadExtensionTest : public ExtensionApiTest {
Browser* current_browser_;
std::unique_ptr<DownloadsEventsListener> events_listener_;
- std::unique_ptr<content::ControllableHttpResponse> first_download_;
- std::unique_ptr<content::ControllableHttpResponse> second_download_;
+ std::unique_ptr<net::test_server::ControllableHttpResponse> first_download_;
+ std::unique_ptr<net::test_server::ControllableHttpResponse> second_download_;
DISALLOW_COPY_AND_ASSIGN(DownloadExtensionTest);
};
@@ -786,30 +790,30 @@ bool ItemIsInterrupted(DownloadItem* item) {
return item->GetState() == DownloadItem::INTERRUPTED;
}
-content::DownloadInterruptReason InterruptReasonExtensionToContent(
+download::DownloadInterruptReason InterruptReasonExtensionToComponent(
downloads::InterruptReason error) {
switch (error) {
case downloads::INTERRUPT_REASON_NONE:
- return content::DOWNLOAD_INTERRUPT_REASON_NONE;
+ return download::DOWNLOAD_INTERRUPT_REASON_NONE;
#define INTERRUPT_REASON(name, value) \
case downloads::INTERRUPT_REASON_##name: \
- return content::DOWNLOAD_INTERRUPT_REASON_##name;
-#include "content/public/browser/download_interrupt_reason_values.h"
+ return download::DOWNLOAD_INTERRUPT_REASON_##name;
+#include "components/download/public/common/download_interrupt_reason_values.h"
#undef INTERRUPT_REASON
}
NOTREACHED();
- return content::DOWNLOAD_INTERRUPT_REASON_NONE;
+ return download::DOWNLOAD_INTERRUPT_REASON_NONE;
}
downloads::InterruptReason InterruptReasonContentToExtension(
- content::DownloadInterruptReason error) {
+ download::DownloadInterruptReason error) {
switch (error) {
- case content::DOWNLOAD_INTERRUPT_REASON_NONE:
+ case download::DOWNLOAD_INTERRUPT_REASON_NONE:
return downloads::INTERRUPT_REASON_NONE;
-#define INTERRUPT_REASON(name, value) \
- case content::DOWNLOAD_INTERRUPT_REASON_##name: \
+#define INTERRUPT_REASON(name, value) \
+ case download::DOWNLOAD_INTERRUPT_REASON_##name: \
return downloads::INTERRUPT_REASON_##name;
-#include "content/public/browser/download_interrupt_reason_values.h"
+#include "components/download/public/common/download_interrupt_reason_values.h"
#undef INTERRUPT_REASON
}
NOTREACHED();
@@ -1063,13 +1067,10 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
DownloadExtensionTest_FileIcon_History) {
const HistoryDownloadInfo kHistoryInfo[] = {
- { FILE_PATH_LITERAL("real.txt"),
- DownloadItem::COMPLETE,
- content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS },
- { FILE_PATH_LITERAL("fake.txt"),
- DownloadItem::COMPLETE,
- content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS }
- };
+ {FILE_PATH_LITERAL("real.txt"), DownloadItem::COMPLETE,
+ download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS},
+ {FILE_PATH_LITERAL("fake.txt"), DownloadItem::COMPLETE,
+ download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS}};
DownloadManager::DownloadVector all_downloads;
ASSERT_TRUE(CreateHistoryDownloads(kHistoryInfo, arraysize(kHistoryInfo),
&all_downloads));
@@ -1123,6 +1124,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
DownloadsShowDefaultFolderFunction) {
+ platform_util::internal::DisableShellOperationsForTesting();
ScopedCancellingItem item(CreateFirstSlowTestDownload());
ASSERT_TRUE(item.get());
@@ -1144,13 +1146,10 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
DownloadExtensionTest_SearchFilenameRegex) {
const HistoryDownloadInfo kHistoryInfo[] = {
- { FILE_PATH_LITERAL("foobar"),
- DownloadItem::COMPLETE,
- content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS },
- { FILE_PATH_LITERAL("baz"),
- DownloadItem::COMPLETE,
- content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS }
- };
+ {FILE_PATH_LITERAL("foobar"), DownloadItem::COMPLETE,
+ download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS},
+ {FILE_PATH_LITERAL("baz"), DownloadItem::COMPLETE,
+ download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS}};
DownloadManager::DownloadVector all_downloads;
ASSERT_TRUE(CreateHistoryDownloads(kHistoryInfo, arraysize(kHistoryInfo),
&all_downloads));
@@ -1223,13 +1222,10 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
DownloadExtensionTest_SearchOrderBy) {
const HistoryDownloadInfo kHistoryInfo[] = {
- { FILE_PATH_LITERAL("zzz"),
- DownloadItem::COMPLETE,
- content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS },
- { FILE_PATH_LITERAL("baz"),
- DownloadItem::COMPLETE,
- content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS }
- };
+ {FILE_PATH_LITERAL("zzz"), DownloadItem::COMPLETE,
+ download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS},
+ {FILE_PATH_LITERAL("baz"), DownloadItem::COMPLETE,
+ download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS}};
DownloadManager::DownloadVector items;
ASSERT_TRUE(CreateHistoryDownloads(kHistoryInfo, arraysize(kHistoryInfo),
&items));
@@ -1256,13 +1252,10 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
DownloadExtensionTest_SearchOrderByEmpty) {
const HistoryDownloadInfo kHistoryInfo[] = {
- { FILE_PATH_LITERAL("zzz"),
- DownloadItem::COMPLETE,
- content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS },
- { FILE_PATH_LITERAL("baz"),
- DownloadItem::COMPLETE,
- content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS }
- };
+ {FILE_PATH_LITERAL("zzz"), DownloadItem::COMPLETE,
+ download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS},
+ {FILE_PATH_LITERAL("baz"), DownloadItem::COMPLETE,
+ download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS}};
DownloadManager::DownloadVector items;
ASSERT_TRUE(CreateHistoryDownloads(kHistoryInfo, arraysize(kHistoryInfo),
&items));
@@ -1293,13 +1286,10 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
DownloadExtensionTest_SearchDanger) {
const HistoryDownloadInfo kHistoryInfo[] = {
- { FILE_PATH_LITERAL("zzz"),
- DownloadItem::COMPLETE,
- content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT },
- { FILE_PATH_LITERAL("baz"),
- DownloadItem::COMPLETE,
- content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS }
- };
+ {FILE_PATH_LITERAL("zzz"), DownloadItem::COMPLETE,
+ download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT},
+ {FILE_PATH_LITERAL("baz"), DownloadItem::COMPLETE,
+ download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS}};
DownloadManager::DownloadVector items;
ASSERT_TRUE(CreateHistoryDownloads(kHistoryInfo, arraysize(kHistoryInfo),
&items));
@@ -1379,15 +1369,12 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
DownloadExtensionTest_SearchPlural) {
const HistoryDownloadInfo kHistoryInfo[] = {
- { FILE_PATH_LITERAL("aaa"),
- DownloadItem::CANCELLED,
- content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS },
- { FILE_PATH_LITERAL("zzz"),
- DownloadItem::COMPLETE,
- content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT },
- { FILE_PATH_LITERAL("baz"),
- DownloadItem::COMPLETE,
- content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT },
+ {FILE_PATH_LITERAL("aaa"), DownloadItem::CANCELLED,
+ download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS},
+ {FILE_PATH_LITERAL("zzz"), DownloadItem::COMPLETE,
+ download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT},
+ {FILE_PATH_LITERAL("baz"), DownloadItem::COMPLETE,
+ download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT},
};
DownloadManager::DownloadVector items;
ASSERT_TRUE(CreateHistoryDownloads(kHistoryInfo, arraysize(kHistoryInfo),
@@ -1767,7 +1754,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
EXPECT_EQ(GetExtensionURL(), item->GetSiteUrl().spec());
item->SimulateErrorForTesting(
- content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED);
+ download::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED);
embedded_test_server_io_runner->PostTask(FROM_HERE, complete_callback);
ASSERT_TRUE(WaitFor(downloads::OnChanged::kEventName,
@@ -1991,7 +1978,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
DownloadItem* item = GetCurrentManager()->GetDownload(result_id);
ASSERT_TRUE(item);
ASSERT_TRUE(WaitForInterruption(
- item, content::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST,
+ item, download::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST,
"[{\"state\": \"in_progress\","
" \"url\": \"javascript:document.write(\\\"hello\\\");\"}]"));
@@ -2003,7 +1990,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
item = GetCurrentManager()->GetDownload(result_id);
ASSERT_TRUE(item);
ASSERT_TRUE(WaitForInterruption(
- item, content::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST,
+ item, download::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST,
"[{\"state\": \"in_progress\","
" \"url\": \"javascript:return false;\"}]"));
@@ -2015,7 +2002,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
item = GetCurrentManager()->GetDownload(result_id);
ASSERT_TRUE(item);
ASSERT_TRUE(WaitForInterruption(
- item, content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED,
+ item, download::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED,
"[{\"state\": \"in_progress\","
" \"url\": \"ftp://example.com/example.txt\"}]"));
}
@@ -2274,8 +2261,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
ASSERT_EQ(download_url, item->GetOriginalUrl().spec());
ASSERT_TRUE(WaitForInterruption(
- item,
- content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED,
+ item, download::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED,
base::StringPrintf("[{\"danger\": \"safe\","
" \"incognito\": false,"
" \"paused\": false,"
@@ -2367,8 +2353,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
ASSERT_EQ(download_url, item->GetOriginalUrl().spec());
ASSERT_TRUE(WaitForInterruption(
- item,
- content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT,
+ item, download::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT,
base::StringPrintf("[{\"danger\": \"safe\","
" \"incognito\": false,"
" \"bytesReceived\": 0.0,"
@@ -2509,16 +2494,14 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
ASSERT_EQ(download_url, item->GetOriginalUrl().spec());
ASSERT_TRUE(WaitForInterruption(
- item,
- content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT,
+ item, download::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT,
base::StringPrintf("[{\"danger\": \"safe\","
" \"incognito\": false,"
" \"mime\": \"\","
" \"paused\": false,"
" \"id\": %d,"
" \"url\": \"%s\"}]",
- result_id,
- download_url.c_str())));
+ result_id, download_url.c_str())));
}
// Test that downloadPostSuccess would fail if the resource requires the POST
@@ -2552,16 +2535,14 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
ASSERT_EQ(download_url, item->GetOriginalUrl().spec());
ASSERT_TRUE(WaitForInterruption(
- item,
- content::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT,
+ item, download::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT,
base::StringPrintf("[{\"danger\": \"safe\","
" \"incognito\": false,"
" \"mime\": \"\","
" \"paused\": false,"
" \"id\": %d,"
" \"url\": \"%s\"}]",
- result_id,
- download_url.c_str())));
+ result_id, download_url.c_str())));
}
// Test that cancel()ing an in-progress download causes its state to transition
@@ -2886,6 +2867,51 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
result_id)));
}
+// Tests downloadsInternal.determineFilename.
+// Regression test for https://crbug.com/815362.
+IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
+ DownloadsInternalDetermineFilename) {
+ GoOnTheRecord();
+ LoadExtension("downloads_split");
+ AddFilenameDeterminer();
+ ASSERT_TRUE(StartEmbeddedTestServer());
+ std::string download_url = embedded_test_server()->GetURL("/slow?0").spec();
+
+ // Start downloading a file.
+ std::unique_ptr<base::Value> result(RunFunctionAndReturnResult(
+ new DownloadsDownloadFunction(),
+ base::StringPrintf(R"([{"url": "%s"}])", download_url.c_str())));
+ ASSERT_TRUE(result.get());
+ int result_id = result->GetInt();
+ DownloadItem* item = GetCurrentManager()->GetDownload(result_id);
+ ASSERT_TRUE(item);
+ ScopedCancellingItem canceller(item);
+ ASSERT_EQ(download_url, item->GetOriginalUrl().spec());
+
+ // Wait for the onCreated and onDeterminingFilename events.
+ ASSERT_TRUE(WaitFor(downloads::OnCreated::kEventName,
+ base::StringPrintf(R"([{
+ "danger": "safe",
+ "incognito": false,
+ "id": %d,
+ "mime": "text/plain",
+ "paused": false,
+ "url": "%s"
+ }])",
+ result_id, download_url.c_str())));
+ ASSERT_TRUE(
+ WaitFor(downloads::OnDeterminingFilename::kEventName,
+ base::StringPrintf(
+ R"([{"id": %d, "filename": "slow.txt"}])", result_id)));
+ ASSERT_TRUE(item->GetTargetFilePath().empty());
+ ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
+
+ std::unique_ptr<base::Value> determine_result(RunFunctionAndReturnResult(
+ new DownloadsInternalDetermineFilenameFunction(),
+ base::StringPrintf(R"([%d, "", "uniquify"])", result_id)));
+ EXPECT_FALSE(determine_result.get()); // No return value.
+}
+
IN_PROC_BROWSER_TEST_F(
DownloadExtensionTest,
DownloadExtensionTest_OnDeterminingFilename_DangerousOverride) {
@@ -4315,14 +4341,14 @@ IN_PROC_BROWSER_TEST_F(DownloadsApiTest, DownloadsApiTest) {
TEST(DownloadInterruptReasonEnumsSynced,
DownloadInterruptReasonEnumsSynced) {
-#define INTERRUPT_REASON(name, value) \
- EXPECT_EQ(InterruptReasonContentToExtension( \
- content::DOWNLOAD_INTERRUPT_REASON_##name), \
- downloads::INTERRUPT_REASON_##name); \
- EXPECT_EQ( \
- InterruptReasonExtensionToContent(downloads::INTERRUPT_REASON_##name), \
- content::DOWNLOAD_INTERRUPT_REASON_##name);
-#include "content/public/browser/download_interrupt_reason_values.h" // NOLINT
+#define INTERRUPT_REASON(name, value) \
+ EXPECT_EQ(InterruptReasonContentToExtension( \
+ download::DOWNLOAD_INTERRUPT_REASON_##name), \
+ downloads::INTERRUPT_REASON_##name); \
+ EXPECT_EQ( \
+ InterruptReasonExtensionToComponent(downloads::INTERRUPT_REASON_##name), \
+ download::DOWNLOAD_INTERRUPT_REASON_##name);
+#include "components/download/public/common/download_interrupt_reason_values.h"
#undef INTERRUPT_REASON
}
diff --git a/chromium/chrome/browser/extensions/api/downloads_internal/downloads_internal_api.cc b/chromium/chrome/browser/extensions/api/downloads_internal/downloads_internal_api.cc
index 109d0b84a3e..db6580819bf 100644
--- a/chromium/chrome/browser/extensions/api/downloads_internal/downloads_internal_api.cc
+++ b/chromium/chrome/browser/extensions/api/downloads_internal/downloads_internal_api.cc
@@ -19,21 +19,21 @@ DownloadsInternalDetermineFilenameFunction::
typedef extensions::api::downloads_internal::DetermineFilename::Params
DetermineFilenameParams;
-bool DownloadsInternalDetermineFilenameFunction::RunAsync() {
+ExtensionFunction::ResponseAction
+DownloadsInternalDetermineFilenameFunction::Run() {
std::unique_ptr<DetermineFilenameParams> params(
DetermineFilenameParams::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
base::FilePath::StringType filename;
EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filename));
- return ExtensionDownloadsEventRouter::DetermineFilename(
- GetProfile(),
- include_incognito(),
- extension()->id(),
- params->download_id,
- base::FilePath(filename),
+ std::string error;
+ bool result = ExtensionDownloadsEventRouter::DetermineFilename(
+ browser_context(), include_incognito(), extension()->id(),
+ params->download_id, base::FilePath(filename),
extensions::api::downloads::ParseFilenameConflictAction(
params->conflict_action),
- &error_);
+ &error);
+ return RespondNow(result ? NoArguments() : Error(error));
}
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/downloads_internal/downloads_internal_api.h b/chromium/chrome/browser/extensions/api/downloads_internal/downloads_internal_api.h
index f7da2b58564..4d6acf505cf 100644
--- a/chromium/chrome/browser/extensions/api/downloads_internal/downloads_internal_api.h
+++ b/chromium/chrome/browser/extensions/api/downloads_internal/downloads_internal_api.h
@@ -6,17 +6,17 @@
#define CHROME_BROWSER_EXTENSIONS_API_DOWNLOADS_INTERNAL_DOWNLOADS_INTERNAL_API_H_
#include "base/macros.h"
-#include "chrome/browser/extensions/chrome_extension_function.h"
+#include "extensions/browser/extension_function.h"
namespace extensions {
class DownloadsInternalDetermineFilenameFunction
- : public ChromeAsyncExtensionFunction {
+ : public UIThreadExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("downloadsInternal.determineFilename",
DOWNLOADSINTERNAL_DETERMINEFILENAME);
DownloadsInternalDetermineFilenameFunction();
- bool RunAsync() override;
+ ResponseAction Run() override;
protected:
~DownloadsInternalDetermineFilenameFunction() override;
diff --git a/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc b/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc
index 7f41f2d8534..c8dc598aa2b 100644
--- a/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc
+++ b/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc
@@ -43,6 +43,7 @@
#include "components/proximity_auth/screenlock_state.h"
#include "components/proximity_auth/switches.h"
#include "components/signin/core/account_id/account_id.h"
+#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "ui/base/l10n/l10n_util.h"
@@ -157,9 +158,8 @@ ExtensionFunction::ResponseAction EasyUnlockPrivateGetStringsFunction::Run() {
#endif // defined(OS_CHROMEOS)
// Common strings.
- strings->SetString(
- "learnMoreLinkTitle",
- l10n_util::GetStringUTF16(IDS_EASY_UNLOCK_LEARN_MORE_LINK_TITLE));
+ strings->SetString("learnMoreLinkTitle",
+ l10n_util::GetStringUTF16(IDS_LEARN_MORE));
strings->SetString("deviceType", device_type);
// Setup notification strings.
@@ -302,10 +302,8 @@ ExtensionFunction::ResponseAction EasyUnlockPrivateGetStringsFunction::Run() {
"setupAndroidSmartLockDoneButtonText",
l10n_util::GetStringUTF16(
IDS_EASY_UNLOCK_SETUP_ANDROID_SMART_LOCK_DONE_BUTTON_LABEL));
- strings->SetString(
- "setupAndroidSmartLockAboutLinkText",
- l10n_util::GetStringUTF16(
- IDS_EASY_UNLOCK_SETUP_ANDROID_SMART_LOCK_ABOUT_LINK_TEXT));
+ strings->SetString("setupAndroidSmartLockAboutLinkText",
+ l10n_util::GetStringUTF16(IDS_LEARN_MORE));
// Step 3: Setup completed successfully.
strings->SetString(
"setupCompleteHeaderTitle",
diff --git a/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc b/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc
index 290b81d3093..69a148dabf9 100644
--- a/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc
@@ -184,10 +184,7 @@ TEST_F(EasyUnlockPrivateApiTest, GenerateEcP256KeyPair) {
function->set_has_callback(true);
ASSERT_TRUE(extension_function_test_utils::RunFunction(
- function.get(),
- "[]",
- browser(),
- extension_function_test_utils::NONE));
+ function.get(), "[]", browser(), extensions::api_test_utils::NONE));
const base::ListValue* result_list = function->GetResultList();
ASSERT_TRUE(result_list);
@@ -231,7 +228,7 @@ TEST_F(EasyUnlockPrivateApiTest, PerformECDHKeyAgreement) {
ASSERT_TRUE(extension_function_test_utils::RunFunction(
function.get(), std::move(args), browser(),
- extension_function_test_utils::NONE));
+ extensions::api_test_utils::NONE));
EXPECT_EQ(expected_result, GetSingleBinaryResultAsString(function.get()));
}
@@ -277,7 +274,7 @@ TEST_F(EasyUnlockPrivateApiTest, CreateSecureMessage) {
ASSERT_TRUE(extension_function_test_utils::RunFunction(
function.get(), std::move(args), browser(),
- extension_function_test_utils::NONE));
+ extensions::api_test_utils::NONE));
EXPECT_EQ(expected_result, GetSingleBinaryResultAsString(function.get()));
}
@@ -307,7 +304,7 @@ TEST_F(EasyUnlockPrivateApiTest, CreateSecureMessage_EmptyOptions) {
ASSERT_TRUE(extension_function_test_utils::RunFunction(
function.get(), std::move(args), browser(),
- extension_function_test_utils::NONE));
+ extensions::api_test_utils::NONE));
EXPECT_EQ(expected_result, GetSingleBinaryResultAsString(function.get()));
}
@@ -346,7 +343,7 @@ TEST_F(EasyUnlockPrivateApiTest, CreateSecureMessage_AsymmetricSign) {
ASSERT_TRUE(extension_function_test_utils::RunFunction(
function.get(), std::move(args), browser(),
- extension_function_test_utils::NONE));
+ extensions::api_test_utils::NONE));
EXPECT_EQ(expected_result, GetSingleBinaryResultAsString(function.get()));
}
@@ -384,7 +381,7 @@ TEST_F(EasyUnlockPrivateApiTest, UnwrapSecureMessage) {
ASSERT_TRUE(extension_function_test_utils::RunFunction(
function.get(), std::move(args), browser(),
- extension_function_test_utils::NONE));
+ extensions::api_test_utils::NONE));
EXPECT_EQ(expected_result, GetSingleBinaryResultAsString(function.get()));
}
@@ -414,7 +411,7 @@ TEST_F(EasyUnlockPrivateApiTest, UnwrapSecureMessage_EmptyOptions) {
ASSERT_TRUE(extension_function_test_utils::RunFunction(
function.get(), std::move(args), browser(),
- extension_function_test_utils::NONE));
+ extensions::api_test_utils::NONE));
EXPECT_EQ(expected_result, GetSingleBinaryResultAsString(function.get()));
}
@@ -450,7 +447,7 @@ TEST_F(EasyUnlockPrivateApiTest, UnwrapSecureMessage_AsymmetricSign) {
ASSERT_TRUE(extension_function_test_utils::RunFunction(
function.get(), std::move(args), browser(),
- extension_function_test_utils::NONE));
+ extensions::api_test_utils::NONE));
EXPECT_EQ(expected_result, GetSingleBinaryResultAsString(function.get()));
}
@@ -500,10 +497,8 @@ TEST_F(EasyUnlockPrivateApiTest, AutoPairing) {
scoped_refptr<EasyUnlockPrivateSetAutoPairingResultFunction> function(
new EasyUnlockPrivateSetAutoPairingResultFunction());
ASSERT_TRUE(extension_function_test_utils::RunFunction(
- function.get(),
- "[{\"success\":false, \"errorMessage\":\"fake_error\"}]",
- browser(),
- extension_function_test_utils::NONE));
+ function.get(), "[{\"success\":false, \"errorMessage\":\"fake_error\"}]",
+ browser(), extensions::api_test_utils::NONE));
EXPECT_FALSE(result.success);
EXPECT_EQ("fake_error", result.error);
@@ -512,10 +507,8 @@ TEST_F(EasyUnlockPrivateApiTest, AutoPairing) {
base::Unretained(&result)));
function = new EasyUnlockPrivateSetAutoPairingResultFunction();
ASSERT_TRUE(extension_function_test_utils::RunFunction(
- function.get(),
- "[{\"success\":true}]",
- browser(),
- extension_function_test_utils::NONE));
+ function.get(), "[{\"success\":true}]", browser(),
+ extensions::api_test_utils::NONE));
EXPECT_TRUE(result.success);
EXPECT_TRUE(result.error.empty());
}
diff --git a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.cc b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.cc
index 51661075426..35679e37510 100644
--- a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.cc
+++ b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.cc
@@ -11,6 +11,8 @@
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/enterprise_device_attributes.h"
+#include "chromeos/system/statistics_provider.h"
+#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
namespace extensions {
@@ -18,23 +20,13 @@ namespace extensions {
namespace {
// Checks for the current browser context if the user is affiliated.
-bool IsPermittedToGetDeviceId(content::BrowserContext* context) {
+bool IsPermittedToGetDeviceAttributes(content::BrowserContext* context) {
const user_manager::User* user =
chromeos::ProfileHelper::Get()->GetUserByProfile(
Profile::FromBrowserContext(context));
return user->IsAffiliated();
}
-// Returns the directory device id for the permitted extensions or an empty
-// string.
-std::string GetDirectoryDeviceId(content::BrowserContext* context) {
- return IsPermittedToGetDeviceId(context)
- ? g_browser_process->platform_part()
- ->browser_policy_connector_chromeos()
- ->GetDirectoryApiID()
- : std::string();
-}
-
} // namespace
EnterpriseDeviceAttributesGetDirectoryDeviceIdFunction::
@@ -45,10 +37,71 @@ EnterpriseDeviceAttributesGetDirectoryDeviceIdFunction::
ExtensionFunction::ResponseAction
EnterpriseDeviceAttributesGetDirectoryDeviceIdFunction::Run() {
- const std::string device_id = GetDirectoryDeviceId(browser_context());
+ std::string device_id;
+ if (IsPermittedToGetDeviceAttributes(browser_context())) {
+ device_id = g_browser_process->platform_part()
+ ->browser_policy_connector_chromeos()
+ ->GetDirectoryApiID();
+ }
return RespondNow(ArgumentList(
api::enterprise_device_attributes::GetDirectoryDeviceId::Results::Create(
device_id)));
}
+EnterpriseDeviceAttributesGetDeviceSerialNumberFunction::
+ EnterpriseDeviceAttributesGetDeviceSerialNumberFunction() {}
+
+EnterpriseDeviceAttributesGetDeviceSerialNumberFunction::
+ ~EnterpriseDeviceAttributesGetDeviceSerialNumberFunction() {}
+
+ExtensionFunction::ResponseAction
+EnterpriseDeviceAttributesGetDeviceSerialNumberFunction::Run() {
+ std::string serial_number;
+ if (IsPermittedToGetDeviceAttributes(browser_context())) {
+ serial_number = chromeos::system::StatisticsProvider::GetInstance()
+ ->GetEnterpriseMachineID();
+ }
+ return RespondNow(ArgumentList(
+ api::enterprise_device_attributes::GetDeviceSerialNumber::Results::Create(
+ serial_number)));
+}
+
+EnterpriseDeviceAttributesGetDeviceAssetIdFunction::
+ EnterpriseDeviceAttributesGetDeviceAssetIdFunction() {}
+
+EnterpriseDeviceAttributesGetDeviceAssetIdFunction::
+ ~EnterpriseDeviceAttributesGetDeviceAssetIdFunction() {}
+
+ExtensionFunction::ResponseAction
+EnterpriseDeviceAttributesGetDeviceAssetIdFunction::Run() {
+ std::string asset_id;
+ if (IsPermittedToGetDeviceAttributes(browser_context())) {
+ asset_id = g_browser_process->platform_part()
+ ->browser_policy_connector_chromeos()
+ ->GetDeviceAssetID();
+ }
+ return RespondNow(ArgumentList(
+ api::enterprise_device_attributes::GetDeviceAssetId::Results::Create(
+ asset_id)));
+}
+
+EnterpriseDeviceAttributesGetDeviceAnnotatedLocationFunction::
+ EnterpriseDeviceAttributesGetDeviceAnnotatedLocationFunction() {}
+
+EnterpriseDeviceAttributesGetDeviceAnnotatedLocationFunction::
+ ~EnterpriseDeviceAttributesGetDeviceAnnotatedLocationFunction() {}
+
+ExtensionFunction::ResponseAction
+EnterpriseDeviceAttributesGetDeviceAnnotatedLocationFunction::Run() {
+ std::string annotated_location;
+ if (IsPermittedToGetDeviceAttributes(browser_context())) {
+ annotated_location = g_browser_process->platform_part()
+ ->browser_policy_connector_chromeos()
+ ->GetDeviceAnnotatedLocation();
+ }
+ return RespondNow(ArgumentList(
+ api::enterprise_device_attributes::GetDeviceAnnotatedLocation::Results::
+ Create(annotated_location)));
+}
+
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.h b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.h
index ca1d02370c7..3f993c2174d 100644
--- a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.h
+++ b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.h
@@ -24,5 +24,52 @@ class EnterpriseDeviceAttributesGetDirectoryDeviceIdFunction
ENTERPRISE_DEVICEATTRIBUTES_GETDIRECTORYDEVICEID);
};
+class EnterpriseDeviceAttributesGetDeviceSerialNumberFunction
+ : public UIThreadExtensionFunction {
+ public:
+ EnterpriseDeviceAttributesGetDeviceSerialNumberFunction();
+
+ protected:
+ ~EnterpriseDeviceAttributesGetDeviceSerialNumberFunction() override;
+
+ ResponseAction Run() override;
+
+ private:
+ DECLARE_EXTENSION_FUNCTION(
+ "enterprise.deviceAttributes.getDeviceSerialNumber",
+ ENTERPRISE_DEVICEATTRIBUTES_GETDEVICESERIALNUMBER);
+};
+
+class EnterpriseDeviceAttributesGetDeviceAssetIdFunction
+ : public UIThreadExtensionFunction {
+ public:
+ EnterpriseDeviceAttributesGetDeviceAssetIdFunction();
+
+ protected:
+ ~EnterpriseDeviceAttributesGetDeviceAssetIdFunction() override;
+
+ ResponseAction Run() override;
+
+ private:
+ DECLARE_EXTENSION_FUNCTION("enterprise.deviceAttributes.getDeviceAssetId",
+ ENTERPRISE_DEVICEATTRIBUTES_GETDEVICEASSETID);
+};
+
+class EnterpriseDeviceAttributesGetDeviceAnnotatedLocationFunction
+ : public UIThreadExtensionFunction {
+ public:
+ EnterpriseDeviceAttributesGetDeviceAnnotatedLocationFunction();
+
+ protected:
+ ~EnterpriseDeviceAttributesGetDeviceAnnotatedLocationFunction() override;
+
+ ResponseAction Run() override;
+
+ private:
+ DECLARE_EXTENSION_FUNCTION(
+ "enterprise.deviceAttributes.getDeviceAnnotatedLocation",
+ ENTERPRISE_DEVICEATTRIBUTES_GETDEVICEANNOTATEDLOCATION);
+};
+
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_DEVICE_ATTRIBUTES_ENTERPRISE_DEVICE_ATTRIBUTES_API_H_
diff --git a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_apitest.cc b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_apitest.cc
index b4422a1d396..84603c4d929 100644
--- a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_apitest.cc
@@ -4,7 +4,9 @@
#include <memory>
+#include "base/json/json_writer.h"
#include "base/strings/stringprintf.h"
+#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/policy/affiliation_test_helper.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
@@ -14,6 +16,8 @@
#include "chrome/browser/net/url_request_mock_util.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chromeos/dbus/fake_session_manager_client.h"
+#include "chromeos/system/fake_statistics_provider.h"
+#include "chromeos/system/statistics_provider.h"
#include "components/policy/core/common/cloud/device_management_service.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/policy/policy_constants.h"
@@ -31,6 +35,9 @@
namespace {
constexpr char kDeviceId[] = "device_id";
+constexpr char kSerialNumber[] = "serial_number";
+constexpr char kAssetId[] = "asset_id";
+constexpr char kAnnotatedLocation[] = "annotated_location";
constexpr base::FilePath::CharType kTestExtensionDir[] =
FILE_PATH_LITERAL("extensions/api_test/enterprise_device_attributes");
constexpr base::FilePath::CharType kUpdateManifestFileName[] =
@@ -51,6 +58,21 @@ struct Params {
bool affiliated_;
};
+base::Value BuildCustomArg(const std::string& expected_directory_device_id,
+ const std::string& expected_serial_number,
+ const std::string& expected_asset_id,
+ const std::string& expected_annotated_location) {
+ base::Value custom_arg(base::Value::Type::DICTIONARY);
+ custom_arg.SetKey("expectedDirectoryDeviceId",
+ base::Value(expected_directory_device_id));
+ custom_arg.SetKey("expectedSerialNumber",
+ base::Value(expected_serial_number));
+ custom_arg.SetKey("expectedAssetId", base::Value(expected_asset_id));
+ custom_arg.SetKey("expectedAnnotatedLocation",
+ base::Value(expected_annotated_location));
+ return custom_arg;
+}
+
} // namespace
namespace extensions {
@@ -60,6 +82,8 @@ class EnterpriseDeviceAttributesTest :
public ::testing::WithParamInterface<Params> {
public:
EnterpriseDeviceAttributesTest() {
+ fake_statistics_provider_.SetMachineStatistic(
+ chromeos::system::kSerialNumberKey, kSerialNumber);
set_exit_when_last_browser_closes(false);
set_chromeos_user_ = false;
}
@@ -110,6 +134,8 @@ class EnterpriseDeviceAttributesTest :
policy::DevicePolicyBuilder* device_policy = test_helper_.device_policy();
device_policy->SetDefaultSigningKey();
device_policy->policy_data().set_directory_api_id(kDeviceId);
+ device_policy->policy_data().set_annotated_asset_id(kAssetId);
+ device_policy->policy_data().set_annotated_location(kAnnotatedLocation);
device_policy->Build();
fake_session_manager_client->set_device_policy(device_policy->GetBlob());
@@ -170,9 +196,15 @@ class EnterpriseDeviceAttributesTest :
// only very little functionality from RunExtensionSubtest(). Thus so that
// don't make RunExtensionSubtest() to complex we just introduce a new
// function.
- bool TestExtension(Browser* browser, const std::string& page_url) {
+ bool TestExtension(Browser* browser,
+ const std::string& page_url,
+ const base::Value& custom_arg_value) {
DCHECK(!page_url.empty()) << "page_url cannot be empty";
+ std::string custom_arg;
+ base::JSONWriter::Write(custom_arg_value, &custom_arg);
+ SetCustomArg(custom_arg);
+
extensions::ResultCatcher catcher;
ui_test_utils::NavigateToURL(browser, GURL(page_url));
@@ -190,6 +222,7 @@ class EnterpriseDeviceAttributesTest :
private:
policy::MockConfigurationPolicyProvider policy_provider_;
policy::DevicePolicyCrosTestHelper test_helper_;
+ chromeos::system::ScopedFakeStatisticsProvider fake_statistics_provider_;
};
IN_PROC_BROWSER_TEST_P(EnterpriseDeviceAttributesTest, PRE_Success) {
@@ -206,13 +239,21 @@ IN_PROC_BROWSER_TEST_P(EnterpriseDeviceAttributesTest, Success) {
EXPECT_EQ(GetParam().affiliated_, user_manager::UserManager::Get()->
FindUser(affiliated_account_id_)->IsAffiliated());
- // Device ID is available only for affiliated user.
- std::string device_id = GetParam().affiliated_ ? kDeviceId : "";
+ // Device attributes are available only for affiliated user.
+ std::string expected_directory_device_id =
+ GetParam().affiliated_ ? kDeviceId : "";
+ std::string expected_serial_number =
+ GetParam().affiliated_ ? kSerialNumber : "";
+ std::string expected_asset_id = GetParam().affiliated_ ? kAssetId : "";
+ std::string expected_annotated_location =
+ GetParam().affiliated_ ? kAnnotatedLocation : "";
// Pass the expected value (device_id) to test.
- ASSERT_TRUE(TestExtension(CreateBrowser(profile()),
- base::StringPrintf("chrome-extension://%s/basic.html?%s",
- kTestExtensionID, device_id.c_str())))
+ ASSERT_TRUE(TestExtension(
+ CreateBrowser(profile()),
+ base::StringPrintf("chrome-extension://%s/basic.html", kTestExtensionID),
+ BuildCustomArg(expected_directory_device_id, expected_serial_number,
+ expected_asset_id, expected_annotated_location)))
<< message_;
}
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 6ca6aa5a0ef..d974bf51ac0 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
@@ -181,7 +181,8 @@ class EPKChallengeKeyTestBase : public BrowserWithTestWindowTest {
std::string RunFunctionAndReturnError(UIThreadExtensionFunction* function,
std::unique_ptr<base::ListValue> args,
Browser* browser) {
- utils::RunFunction(function, std::move(args), browser, utils::NONE);
+ utils::RunFunction(function, std::move(args), browser,
+ extensions::api_test_utils::NONE);
EXPECT_EQ(ExtensionFunction::FAILED, *function->response_type());
return function->GetError();
}
@@ -195,7 +196,8 @@ class EPKChallengeKeyTestBase : public BrowserWithTestWindowTest {
scoped_refptr<ExtensionFunction> function_owner(function);
// Without a callback the function will not generate a result.
function->set_has_callback(true);
- utils::RunFunction(function, std::move(args), browser, utils::NONE);
+ utils::RunFunction(function, std::move(args), browser,
+ extensions::api_test_utils::NONE);
EXPECT_TRUE(function->GetError().empty()) << "Unexpected error: "
<< function->GetError();
const base::Value* single_result = NULL;
@@ -324,16 +326,16 @@ TEST_F(EPKChallengeMachineKeyTest, KeyExists) {
// GetCertificate must not be called if the key exists.
EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _)).Times(0);
- EXPECT_TRUE(
- utils::RunFunction(func_.get(), CreateArgs(), browser(), utils::NONE));
+ EXPECT_TRUE(utils::RunFunction(func_.get(), CreateArgs(), browser(),
+ extensions::api_test_utils::NONE));
}
TEST_F(EPKChallengeMachineKeyTest, KeyNotRegisteredByDefault) {
EXPECT_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _, _))
.Times(0);
- EXPECT_TRUE(
- utils::RunFunction(func_.get(), CreateArgs(), browser(), utils::NONE));
+ EXPECT_TRUE(utils::RunFunction(func_.get(), CreateArgs(), browser(),
+ extensions::api_test_utils::NONE));
}
TEST_F(EPKChallengeMachineKeyTest, KeyNotRegistered) {
@@ -341,7 +343,7 @@ TEST_F(EPKChallengeMachineKeyTest, KeyNotRegistered) {
.Times(0);
EXPECT_TRUE(utils::RunFunction(func_.get(), CreateArgsNoRegister(), browser(),
- utils::NONE));
+ extensions::api_test_utils::NONE));
}
TEST_F(EPKChallengeMachineKeyTest, Success) {
@@ -514,8 +516,8 @@ TEST_F(EPKChallengeUserKeyTest, KeyExists) {
// GetCertificate must not be called if the key exists.
EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _)).Times(0);
- EXPECT_TRUE(
- utils::RunFunction(func_.get(), CreateArgs(), browser(), utils::NONE));
+ EXPECT_TRUE(utils::RunFunction(func_.get(), CreateArgs(), browser(),
+ extensions::api_test_utils::NONE));
}
TEST_F(EPKChallengeUserKeyTest, KeyNotRegistered) {
@@ -523,7 +525,7 @@ TEST_F(EPKChallengeUserKeyTest, KeyNotRegistered) {
.Times(0);
EXPECT_TRUE(utils::RunFunction(func_.get(), CreateArgsNoRegister(), browser(),
- utils::NONE));
+ extensions::api_test_utils::NONE));
}
TEST_F(EPKChallengeUserKeyTest, PersonalDevice) {
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 f81fd5c960b..03541c19880 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
@@ -320,7 +320,8 @@ TEST_F(EPKPChallengeMachineKeyTest, KeyExists) {
EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _))
.Times(0);
- EXPECT_TRUE(utils::RunFunction(func_.get(), kArgs, browser(), utils::NONE));
+ EXPECT_TRUE(utils::RunFunction(func_.get(), kArgs, browser(),
+ extensions::api_test_utils::NONE));
}
TEST_F(EPKPChallengeMachineKeyTest, AttestationNotPrepared) {
@@ -363,7 +364,7 @@ TEST_P(EPKPChallengeMachineKeyAllProfilesTest, Success) {
.Times(1);
std::unique_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
- func_.get(), kArgs, browser(), utils::NONE));
+ func_.get(), kArgs, browser(), extensions::api_test_utils::NONE));
std::string response;
value->GetAsString(&response);
@@ -490,15 +491,16 @@ TEST_F(EPKPChallengeUserKeyTest, KeyExists) {
EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _))
.Times(0);
- EXPECT_TRUE(utils::RunFunction(func_.get(), kArgs, browser(), utils::NONE));
+ EXPECT_TRUE(utils::RunFunction(func_.get(), kArgs, browser(),
+ extensions::api_test_utils::NONE));
}
TEST_F(EPKPChallengeUserKeyTest, KeyNotRegistered) {
EXPECT_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _, _))
.Times(0);
- EXPECT_TRUE(utils::RunFunction(
- func_.get(), "[\"Y2hhbGxlbmdl\", false]", browser(), utils::NONE));
+ EXPECT_TRUE(utils::RunFunction(func_.get(), "[\"Y2hhbGxlbmdl\", false]",
+ browser(), extensions::api_test_utils::NONE));
}
TEST_F(EPKPChallengeUserKeyTest, PersonalDevice) {
@@ -533,7 +535,7 @@ TEST_F(EPKPChallengeUserKeyTest, Success) {
.Times(1);
std::unique_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
- func_.get(), kArgs, browser(), utils::NONE));
+ func_.get(), kArgs, browser(), extensions::api_test_utils::NONE));
std::string response;
value->GetAsString(&response);
diff --git a/chromium/chrome/browser/extensions/api/experience_sampling_private/OWNERS b/chromium/chrome/browser/extensions/api/experience_sampling_private/OWNERS
deleted file mode 100644
index 885f0ab7205..00000000000
--- a/chromium/chrome/browser/extensions/api/experience_sampling_private/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-felt@chromium.org
diff --git a/chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling.cc b/chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling.cc
deleted file mode 100644
index ebf4250039f..00000000000
--- a/chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling.cc
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/api/experience_sampling_private/experience_sampling.h"
-
-#include <utility>
-
-#include "base/values.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/common/extensions/api/experience_sampling_private.h"
-#include "extensions/browser/event_router.h"
-#include "url/gurl.h"
-
-namespace extensions {
-
-// static
-const char ExperienceSamplingEvent::kProceed[] = "proceed";
-const char ExperienceSamplingEvent::kDeny[] = "deny";
-const char ExperienceSamplingEvent::kIgnore[] = "ignore";
-const char ExperienceSamplingEvent::kCancel[] = "cancel";
-const char ExperienceSamplingEvent::kReload[] = "reload";
-
-// static
-const char ExperienceSamplingEvent::kMaliciousDownload[] =
- "download_warning_malicious";
-const char ExperienceSamplingEvent::kDangerousDownload[] =
- "download_warning_dangerous";
-const char ExperienceSamplingEvent::kDownloadDangerPrompt[] =
- "download_danger_prompt";
-const char ExperienceSamplingEvent::kExtensionInstallDialog[] =
- "extension_install_dialog_";
-
-// static
-std::unique_ptr<ExperienceSamplingEvent> ExperienceSamplingEvent::Create(
- const std::string& element_name,
- const GURL& destination,
- const GURL& referrer) {
- Profile* profile = NULL;
- if (g_browser_process->profile_manager())
- profile = g_browser_process->profile_manager()->GetLastUsedProfile();
- if (!profile)
- return std::unique_ptr<ExperienceSamplingEvent>();
- return std::unique_ptr<ExperienceSamplingEvent>(new ExperienceSamplingEvent(
- element_name, destination, referrer, profile));
-}
-
-// static
-std::unique_ptr<ExperienceSamplingEvent> ExperienceSamplingEvent::Create(
- const std::string& element_name) {
- return ExperienceSamplingEvent::Create(element_name, GURL(), GURL());
-}
-
-ExperienceSamplingEvent::ExperienceSamplingEvent(
- const std::string& element_name,
- const GURL& destination,
- const GURL& referrer,
- content::BrowserContext* browser_context)
- : has_viewed_details_(false),
- has_viewed_learn_more_(false),
- browser_context_(browser_context) {
- ui_element_.name = element_name;
- ui_element_.destination = destination.GetAsReferrer().possibly_invalid_spec();
- ui_element_.referrer = referrer.GetAsReferrer().possibly_invalid_spec();
- ui_element_.time = base::Time::Now().ToJsTime();
-}
-
-ExperienceSamplingEvent::~ExperienceSamplingEvent() {
-}
-
-void ExperienceSamplingEvent::CreateUserDecisionEvent(
- const std::string& decision_name) {
- // Check if this is from an incognito context. If it is, don't create and send
- // any events.
- if (browser_context_ && browser_context_->IsOffTheRecord())
- return;
- api::experience_sampling_private::UserDecision decision;
- decision.name = decision_name;
- decision.learn_more = has_viewed_learn_more();
- decision.details = has_viewed_details();
- decision.time = base::Time::Now().ToJsTime();
-
- std::unique_ptr<base::ListValue> args(new base::ListValue());
- args->Append(ui_element_.ToValue());
- args->Append(decision.ToValue());
- std::unique_ptr<Event> event(
- new Event(events::EXPERIENCE_SAMPLING_PRIVATE_ON_DECISION,
- api::experience_sampling_private::OnDecision::kEventName,
- std::move(args)));
- EventRouter* router = EventRouter::Get(browser_context_);
- if (router)
- router->BroadcastEvent(std::move(event));
-}
-
-void ExperienceSamplingEvent::CreateOnDisplayedEvent() {
- // Check if this is from an incognito context. If it is, don't create and send
- // any events.
- if (browser_context_ && browser_context_->IsOffTheRecord())
- return;
- std::unique_ptr<base::ListValue> args(new base::ListValue());
- args->Append(ui_element_.ToValue());
- std::unique_ptr<Event> event(
- new Event(events::EXPERIENCE_SAMPLING_PRIVATE_ON_DISPLAYED,
- api::experience_sampling_private::OnDisplayed::kEventName,
- std::move(args)));
- EventRouter* router = EventRouter::Get(browser_context_);
- if (router)
- router->BroadcastEvent(std::move(event));
-}
-
-} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling.h b/chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling.h
deleted file mode 100644
index 30f6a68f0d2..00000000000
--- a/chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_API_EXPERIENCE_SAMPLING_PRIVATE_EXPERIENCE_SAMPLING_H_
-#define CHROME_BROWSER_EXTENSIONS_API_EXPERIENCE_SAMPLING_PRIVATE_EXPERIENCE_SAMPLING_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "chrome/common/extensions/api/experience_sampling_private.h"
-
-namespace content {
-class BrowserContext;
-}
-
-class GURL;
-
-namespace extensions {
-
-using api::experience_sampling_private::UIElement;
-
-class ExperienceSamplingEvent {
- public:
- // String constants for user decision events.
- static const char kProceed[];
- static const char kDeny[];
- static const char kIgnore[];
- static const char kCancel[];
- static const char kReload[];
-
- // String constants for event names.
- static const char kMaliciousDownload[];
- static const char kDangerousDownload[];
- static const char kDownloadDangerPrompt[];
- static const char kExtensionInstallDialog[];
-
- // The Create() functions can return an empty scoped_ptr if they cannot find
- // the BrowserContext. Code using them should check the scoped pointer using
- // scoped_ptr::get().
- static std::unique_ptr<ExperienceSamplingEvent> Create(
- const std::string& element_name,
- const GURL& destination,
- const GURL& referrer);
-
- static std::unique_ptr<ExperienceSamplingEvent> Create(
- const std::string& element_name);
-
- ExperienceSamplingEvent(const std::string& element_name,
- const GURL& destination,
- const GURL& referrer,
- content::BrowserContext* browser_context);
- ~ExperienceSamplingEvent();
-
- // Sends an extension API event for the user seeing this event.
- void CreateOnDisplayedEvent();
- // Sends an extension API event for the user making a decision about this
- // event.
- void CreateUserDecisionEvent(const std::string& decision_name);
-
- bool has_viewed_details() const { return has_viewed_details_; }
- void set_has_viewed_details(bool viewed) { has_viewed_details_ = viewed; }
- bool has_viewed_learn_more() const { return has_viewed_learn_more_; }
- void set_has_viewed_learn_more(bool viewed) {
- has_viewed_learn_more_ = viewed;
- }
-
- private:
- bool has_viewed_details_;
- bool has_viewed_learn_more_;
- content::BrowserContext* browser_context_;
- UIElement ui_element_;
-
- DISALLOW_COPY_AND_ASSIGN(ExperienceSamplingEvent);
-};
-
-} // namespace extensions
-
-#endif // CHROME_BROWSER_EXTENSIONS_API_EXPERIENCE_SAMPLING_PRIVATE_EXPERIENCE_SAMPLING_H_
diff --git a/chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling_private_api.cc b/chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling_private_api.cc
deleted file mode 100644
index f212226b785..00000000000
--- a/chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling_private_api.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/api/experience_sampling_private/experience_sampling_private_api.h"
-
-#include "base/metrics/field_trial.h"
-#include "chrome/common/extensions/api/experience_sampling_private.h"
-
-namespace sampling = extensions::api::experience_sampling_private;
-
-namespace extensions {
-
-bool ExperienceSamplingPrivateGetBrowserInfoFunction::RunAsync() {
- std::string field_trials;
- sampling::BrowserInfo info;
-
- base::FieldTrialList::StatesToString(&field_trials);
- info.variations = field_trials;
-
- SetResult(info.ToValue());
- SendResponse(true /* success */);
- return true;
-}
-
-} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling_private_api.h b/chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling_private_api.h
deleted file mode 100644
index e199f288704..00000000000
--- a/chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling_private_api.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_API_EXPERIENCE_SAMPLING_PRIVATE_EXPERIENCE_SAMPLING_PRIVATE_API_H_
-#define CHROME_BROWSER_EXTENSIONS_API_EXPERIENCE_SAMPLING_PRIVATE_EXPERIENCE_SAMPLING_PRIVATE_API_H_
-
-#include "chrome/browser/extensions/chrome_extension_function.h"
-
-namespace extensions {
-
-class ExperienceSamplingPrivateGetBrowserInfoFunction
- : public ChromeAsyncExtensionFunction {
- protected:
- ~ExperienceSamplingPrivateGetBrowserInfoFunction() override {}
-
- // ExtensionFuction:
- bool RunAsync() override;
-
- private:
- DECLARE_EXTENSION_FUNCTION("experienceSamplingPrivate.getBrowserInfo",
- EXPERIENCESAMPLINGPRIVATE_GETBROWSERINFO);
-};
-
-} // namespace extensions
-
-#endif // CHROME_BROWSER_EXTENSIONS_API_EXPERIENCE_SAMPLING_PRIVATE_EXPERIENCE_SAMPLING_PRIVATE_API_H_
diff --git a/chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling_private_api_unittest.cc b/chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling_private_api_unittest.cc
deleted file mode 100644
index e71b6037c51..00000000000
--- a/chromium/chrome/browser/extensions/api/experience_sampling_private/experience_sampling_private_api_unittest.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/metrics/field_trial.h"
-#include "chrome/browser/extensions/api/experience_sampling_private/experience_sampling_private_api.h"
-#include "chrome/browser/extensions/extension_api_unittest.h"
-#include "chrome/browser/extensions/extension_function_test_utils.h"
-
-
-namespace utils = extension_function_test_utils;
-
-namespace extensions {
-
-typedef ExtensionApiUnittest ExperienceSamplingPrivateTest;
-
-// Tests that chrome.experienceSamplingPrivate.getBrowserInfo() returns expected
-// field trials and groups.
-TEST_F(ExperienceSamplingPrivateTest, GetBrowserInfoTest) {
- // Start with an empty FieldTrialList.
- std::unique_ptr<base::FieldTrialList> trial_list(
- new base::FieldTrialList(nullptr));
- std::unique_ptr<base::DictionaryValue> result(RunFunctionAndReturnDictionary(
- new ExperienceSamplingPrivateGetBrowserInfoFunction(), "[]"));
- ASSERT_TRUE(result->HasKey("variations"));
- std::string trials_string;
- EXPECT_TRUE(result->GetStringWithoutPathExpansion("variations",
- &trials_string));
- ASSERT_EQ("", trials_string);
-
- // Set field trials using a string.
- base::FieldTrialList::CreateTrialsFromString(
- "*Some name/Winner/*xxx/yyyy/*zzz/default/",
- std::set<std::string>());
- result = RunFunctionAndReturnDictionary(
- new ExperienceSamplingPrivateGetBrowserInfoFunction(), "[]");
- ASSERT_TRUE(result->HasKey("variations"));
- EXPECT_TRUE(result->GetStringWithoutPathExpansion("variations",
- &trials_string));
- ASSERT_EQ("Some name/Winner/xxx/yyyy/zzz/default/", trials_string);
-}
-
-} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc b/chromium/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc
index c057b5bebfb..35432dbe223 100644
--- a/chromium/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc
@@ -12,7 +12,6 @@
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
-#include "chrome/browser/extensions/browser_action_test_util.h"
#include "chrome/browser/extensions/extension_action.h"
#include "chrome/browser/extensions/extension_action_icon_factory.h"
#include "chrome/browser/extensions/extension_action_manager.h"
@@ -26,6 +25,7 @@
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/extensions/browser_action_test_util.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
@@ -1020,7 +1020,7 @@ IN_PROC_BROWSER_TEST_F(NavigatingExtensionPopupBrowserTest, DownloadViaPost) {
downloads_observer.WaitForFinished();
EXPECT_EQ(0u, downloads_observer.NumDangerousDownloadsSeen());
EXPECT_EQ(1u, downloads_observer.NumDownloadsSeenInState(
- content::DownloadItem::COMPLETE));
+ download::DownloadItem::COMPLETE));
EXPECT_TRUE(base::PathExists(downloads_directory->GetPath().AppendASCII(
"download-test3-attachment.gif")));
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 d2a853e242d..f709e6c9f33 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
@@ -7,7 +7,6 @@
#include "base/run_loop.h"
#include "base/test/test_timeouts.h"
#include "build/build_config.h"
-#include "chrome/browser/extensions/browser_action_test_util.h"
#include "chrome/browser/extensions/extension_action.h"
#include "chrome/browser/extensions/extension_action_manager.h"
#include "chrome/browser/extensions/extension_apitest.h"
@@ -18,6 +17,7 @@
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/extensions/browser_action_test_util.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/test/base/interactive_test_utils.h"
@@ -134,16 +134,23 @@ class BrowserActionInteractiveTest : public ExtensionApiTest {
}
// Open an extension popup via the chrome.browserAction.openPopup API.
+ // If |will_reply| is true, then the listener is responsible for having a
+ // test message listener that replies to the extension. Otherwise, this
+ // method will create a listener and reply to the extension before returning
+ // to avoid leaking an API function while waiting for a reply.
void OpenPopupViaAPI(bool will_reply) {
// Setup the notification observer to wait for the popup to finish loading.
content::WindowedNotificationObserver frame_observer(
content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
content::NotificationService::AllSources());
- ExtensionTestMessageListener listener("ready", will_reply);
+ std::unique_ptr<ExtensionTestMessageListener> listener;
+ if (!will_reply)
+ listener = std::make_unique<ExtensionTestMessageListener>("ready", false);
// Show first popup in first window and expect it to have loaded.
ASSERT_TRUE(RunExtensionSubtest("browser_action/open_popup",
"open_popup_succeeds.html")) << message_;
- EXPECT_TRUE(listener.WaitUntilSatisfied());
+ if (listener)
+ EXPECT_TRUE(listener->WaitUntilSatisfied());
frame_observer.Wait();
EnsurePopupActive();
}
@@ -446,21 +453,20 @@ IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,
}
#if defined(OS_WIN)
-// Test that forcibly closing the browser and popup HWND does not cause a crash.
-// http://crbug.com/400646
-IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,
- DISABLED_DestroyHWNDDoesNotCrash) {
+// Forcibly closing a browser HWND with a popup should not cause a crash.
+IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, DestroyHWNDDoesNotCrash) {
if (!ShouldRunPopupTest())
return;
OpenPopupViaAPI(false);
BrowserActionTestUtil test_util(browser());
- const gfx::NativeView view = test_util.GetPopupNativeView();
- EXPECT_NE(static_cast<gfx::NativeView>(NULL), view);
- const HWND hwnd = views::HWNDForNativeView(view);
- EXPECT_EQ(hwnd,
- views::HWNDForNativeView(browser()->window()->GetNativeWindow()));
- EXPECT_EQ(TRUE, ::IsWindow(hwnd));
+ const gfx::NativeView popup_view = test_util.GetPopupNativeView();
+ EXPECT_NE(static_cast<gfx::NativeView>(nullptr), popup_view);
+ const HWND popup_hwnd = views::HWNDForNativeView(popup_view);
+ EXPECT_EQ(TRUE, ::IsWindow(popup_hwnd));
+ const HWND browser_hwnd =
+ views::HWNDForNativeView(browser()->window()->GetNativeWindow());
+ EXPECT_EQ(TRUE, ::IsWindow(browser_hwnd));
// Create a new browser window to prevent the message loop from terminating.
browser()->OpenURL(content::OpenURLParams(GURL("about:"), content::Referrer(),
@@ -468,9 +474,10 @@ IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,
ui::PAGE_TRANSITION_TYPED, false));
// Forcibly closing the browser HWND should not cause a crash.
- EXPECT_EQ(TRUE, ::CloseWindow(hwnd));
- EXPECT_EQ(TRUE, ::DestroyWindow(hwnd));
- EXPECT_EQ(FALSE, ::IsWindow(hwnd));
+ EXPECT_EQ(TRUE, ::CloseWindow(browser_hwnd));
+ EXPECT_EQ(TRUE, ::DestroyWindow(browser_hwnd));
+ EXPECT_EQ(FALSE, ::IsWindow(browser_hwnd));
+ EXPECT_EQ(FALSE, ::IsWindow(popup_hwnd));
}
#endif // OS_WIN
diff --git a/chromium/chrome/browser/extensions/api/extension_action/extension_action_api.cc b/chromium/chrome/browser/extensions/api/extension_action/extension_action_api.cc
index 017614b8028..6466d63c75f 100644
--- a/chromium/chrome/browser/extensions/api/extension_action/extension_action_api.cc
+++ b/chromium/chrome/browser/extensions/api/extension_action/extension_action_api.cc
@@ -96,31 +96,31 @@ static base::LazyInstance<BrowserContextKeyedAPIFactory<ExtensionActionAPI>>::
ExtensionActionAPI::ExtensionActionAPI(content::BrowserContext* context)
: browser_context_(context),
extension_prefs_(nullptr) {
- ExtensionFunctionRegistry* registry =
+ ExtensionFunctionRegistry& registry =
ExtensionFunctionRegistry::GetInstance();
// Browser Actions
- registry->RegisterFunction<BrowserActionSetIconFunction>();
- registry->RegisterFunction<BrowserActionSetTitleFunction>();
- registry->RegisterFunction<BrowserActionSetBadgeTextFunction>();
- registry->RegisterFunction<BrowserActionSetBadgeBackgroundColorFunction>();
- registry->RegisterFunction<BrowserActionSetPopupFunction>();
- registry->RegisterFunction<BrowserActionGetTitleFunction>();
- registry->RegisterFunction<BrowserActionGetBadgeTextFunction>();
- registry->RegisterFunction<BrowserActionGetBadgeBackgroundColorFunction>();
- registry->RegisterFunction<BrowserActionGetPopupFunction>();
- registry->RegisterFunction<BrowserActionEnableFunction>();
- registry->RegisterFunction<BrowserActionDisableFunction>();
- registry->RegisterFunction<BrowserActionOpenPopupFunction>();
+ registry.RegisterFunction<BrowserActionSetIconFunction>();
+ registry.RegisterFunction<BrowserActionSetTitleFunction>();
+ registry.RegisterFunction<BrowserActionSetBadgeTextFunction>();
+ registry.RegisterFunction<BrowserActionSetBadgeBackgroundColorFunction>();
+ registry.RegisterFunction<BrowserActionSetPopupFunction>();
+ registry.RegisterFunction<BrowserActionGetTitleFunction>();
+ registry.RegisterFunction<BrowserActionGetBadgeTextFunction>();
+ registry.RegisterFunction<BrowserActionGetBadgeBackgroundColorFunction>();
+ registry.RegisterFunction<BrowserActionGetPopupFunction>();
+ registry.RegisterFunction<BrowserActionEnableFunction>();
+ registry.RegisterFunction<BrowserActionDisableFunction>();
+ registry.RegisterFunction<BrowserActionOpenPopupFunction>();
// Page Actions
- registry->RegisterFunction<PageActionShowFunction>();
- registry->RegisterFunction<PageActionHideFunction>();
- registry->RegisterFunction<PageActionSetIconFunction>();
- registry->RegisterFunction<PageActionSetTitleFunction>();
- registry->RegisterFunction<PageActionSetPopupFunction>();
- registry->RegisterFunction<PageActionGetTitleFunction>();
- registry->RegisterFunction<PageActionGetPopupFunction>();
+ registry.RegisterFunction<PageActionShowFunction>();
+ registry.RegisterFunction<PageActionHideFunction>();
+ registry.RegisterFunction<PageActionSetIconFunction>();
+ registry.RegisterFunction<PageActionSetTitleFunction>();
+ registry.RegisterFunction<PageActionSetPopupFunction>();
+ registry.RegisterFunction<PageActionGetTitleFunction>();
+ registry.RegisterFunction<PageActionGetPopupFunction>();
}
ExtensionActionAPI::~ExtensionActionAPI() {
@@ -226,8 +226,9 @@ void ExtensionActionAPI::DispatchExtensionActionClicked(
if (event_name) {
std::unique_ptr<base::ListValue> args(new base::ListValue());
- args->Append(
- ExtensionTabUtil::CreateTabObject(web_contents, extension)->ToValue());
+ args->Append(ExtensionTabUtil::CreateTabObject(
+ web_contents, ExtensionTabUtil::kScrubTab, extension)
+ ->ToValue());
DispatchEventToExtension(web_contents->GetBrowserContext(),
extension_action.extension_id(), histogram_value,
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 abc8ca885cc..8aaa5170ffd 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
@@ -11,12 +11,12 @@
#include "base/strings/stringprintf.h"
#include "chrome/browser/extensions/browsertest_util.h"
#include "chrome/browser/extensions/extension_apitest.h"
-#include "chrome/browser/extensions/test_extension_dir.h"
#include "chrome/browser/sessions/session_tab_helper.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "extensions/browser/state_store.h"
#include "extensions/test/extension_test_message_listener.h"
+#include "extensions/test/test_extension_dir.h"
namespace extensions {
namespace {
diff --git a/chromium/chrome/browser/extensions/api/file_system/request_file_system_notification.cc b/chromium/chrome/browser/extensions/api/file_system/request_file_system_notification.cc
index af719794f36..0815a0b5907 100644
--- a/chromium/chrome/browser/extensions/api/file_system/request_file_system_notification.cc
+++ b/chromium/chrome/browser/extensions/api/file_system/request_file_system_notification.cc
@@ -19,10 +19,10 @@
#include "chrome/grit/generated_resources.h"
#include "extensions/common/extension.h"
#include "ui/base/l10n/l10n_util.h"
-#include "ui/message_center/notification.h"
-#include "ui/message_center/notification_delegate.h"
-#include "ui/message_center/notification_types.h"
-#include "ui/message_center/notifier_id.h"
+#include "ui/message_center/public/cpp/notification.h"
+#include "ui/message_center/public/cpp/notification_delegate.h"
+#include "ui/message_center/public/cpp/notification_types.h"
+#include "ui/message_center/public/cpp/notifier_id.h"
using file_manager::Volume;
using message_center::Notification;
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc b/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc
index 7c80a0cf2c0..373c8081e5f 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -518,8 +518,8 @@ class IdentityGetAccountsFunctionTest : public IdentityTestWithSignin {
new IdentityGetAccountsFunction);
func->set_extension(
ExtensionBuilder("Test").SetID(kExtensionId).Build().get());
- if (!utils::RunFunction(
- func.get(), std::string("[]"), browser(), utils::NONE)) {
+ if (!utils::RunFunction(func.get(), std::string("[]"), browser(),
+ api_test_utils::NONE)) {
return GenerateFailureResult(accounts, NULL)
<< "getAccounts did not return a result.";
}
@@ -819,7 +819,7 @@ class GetAuthTokenFunctionTest
const OAuth2TokenService::ScopeSet& scopes) override {
if (on_access_token_requested_.is_null())
return;
- base::ResetAndReturn(&on_access_token_requested_).Run();
+ std::move(on_access_token_requested_).Run();
}
std::string extension_id_;
@@ -2020,10 +2020,8 @@ class RemoveCachedAuthTokenFunctionTest : public ExtensionBrowserTest {
func->set_extension(
ExtensionBuilder("Test").SetID(kExtensionId).Build().get());
return utils::RunFunction(
- func.get(),
- std::string("[{\"token\": \"") + kAccessToken + "\"}]",
- browser(),
- extension_function_test_utils::NONE);
+ func.get(), std::string("[{\"token\": \"") + kAccessToken + "\"}]",
+ browser(), api_test_utils::NONE);
}
IdentityAPI* id_api() {
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_get_accounts_function.cc b/chromium/chrome/browser/extensions/api/identity/identity_get_accounts_function.cc
index 6281011f2f0..00eb5916020 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_get_accounts_function.cc
+++ b/chromium/chrome/browser/extensions/api/identity/identity_get_accounts_function.cc
@@ -10,8 +10,8 @@
#include "components/signin/core/browser/profile_management_switches.h"
#include "content/public/browser/browser_context.h"
#include "content/public/common/service_manager_connection.h"
-#include "services/identity/public/interfaces/account.mojom.h"
-#include "services/identity/public/interfaces/constants.mojom.h"
+#include "services/identity/public/mojom/account.mojom.h"
+#include "services/identity/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
namespace extensions {
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_get_accounts_function.h b/chromium/chrome/browser/extensions/api/identity/identity_get_accounts_function.h
index 1e3973c8d5d..9f9aab7b280 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_get_accounts_function.h
+++ b/chromium/chrome/browser/extensions/api/identity/identity_get_accounts_function.h
@@ -7,7 +7,7 @@
#include "extensions/browser/extension_function.h"
#include "extensions/browser/extension_function_histogram_value.h"
-#include "services/identity/public/interfaces/identity_manager.mojom.h"
+#include "services/identity/public/mojom/identity_manager.mojom.h"
namespace extensions {
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 3d31a6e98d9..ad7069a0f30 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
@@ -28,7 +28,7 @@
#include "extensions/common/extension_l10n_util.h"
#include "google_apis/gaia/gaia_urls.h"
#include "services/identity/public/cpp/scope_set.h"
-#include "services/identity/public/interfaces/constants.mojom.h"
+#include "services/identity/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#if defined(OS_CHROMEOS)
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.h b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.h
index bfaca4ed14e..ed99f975a37 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.h
+++ b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.h
@@ -13,7 +13,7 @@
#include "google_apis/gaia/oauth2_mint_token_flow.h"
#include "google_apis/gaia/oauth2_token_service.h"
#include "services/identity/public/cpp/account_state.h"
-#include "services/identity/public/interfaces/identity_manager.mojom.h"
+#include "services/identity/public/mojom/identity_manager.mojom.h"
namespace extensions {
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_get_profile_user_info_function.cc b/chromium/chrome/browser/extensions/api/identity/identity_get_profile_user_info_function.cc
index 085f4c7b41c..b5bf6028018 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_get_profile_user_info_function.cc
+++ b/chromium/chrome/browser/extensions/api/identity/identity_get_profile_user_info_function.cc
@@ -10,7 +10,7 @@
#include "content/public/common/service_manager_connection.h"
#include "extensions/common/extension.h"
#include "extensions/common/permissions/permissions_data.h"
-#include "services/identity/public/interfaces/constants.mojom.h"
+#include "services/identity/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
namespace extensions {
diff --git a/chromium/chrome/browser/extensions/api/identity/identity_get_profile_user_info_function.h b/chromium/chrome/browser/extensions/api/identity/identity_get_profile_user_info_function.h
index 17a201c0649..72d93ab920a 100644
--- a/chromium/chrome/browser/extensions/api/identity/identity_get_profile_user_info_function.h
+++ b/chromium/chrome/browser/extensions/api/identity/identity_get_profile_user_info_function.h
@@ -9,7 +9,7 @@
#include "extensions/browser/extension_function.h"
#include "extensions/browser/extension_function_histogram_value.h"
#include "services/identity/public/cpp/account_state.h"
-#include "services/identity/public/interfaces/identity_manager.mojom.h"
+#include "services/identity/public/mojom/identity_manager.mojom.h"
namespace extensions {
diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_private_apitest.cc b/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_private_apitest.cc
index 2c4dc2e4cf4..ddcd1a3b317 100644
--- a/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_private_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_private_apitest.cc
@@ -69,7 +69,6 @@ class ImageWriterPrivateApiTest : public ExtensionApiTest {
protected:
- base::MessageLoopForUI message_loop_;
image_writer::ImageWriterTestUtils test_utils_;
};
diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.cc b/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.cc
index 7d81d3c4a6e..8510dfde7a0 100644
--- a/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.cc
+++ b/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.cc
@@ -11,7 +11,7 @@
#include "base/optional.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/grit/generated_resources.h"
-#include "chrome/services/removable_storage_writer/public/interfaces/constants.mojom.h"
+#include "chrome/services/removable_storage_writer/public/mojom/constants.mojom.h"
#include "content/public/browser/browser_thread.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "services/service_manager/public/cpp/connector.h"
diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h b/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h
index 3ab0136b017..6ac29f5ab5e 100644
--- a/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h
+++ b/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h
@@ -15,7 +15,7 @@
#include "base/memory/ref_counted.h"
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
-#include "chrome/services/removable_storage_writer/public/interfaces/removable_storage_writer.mojom.h"
+#include "chrome/services/removable_storage_writer/public/mojom/removable_storage_writer.mojom.h"
namespace service_manager {
class Connector;
diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc b/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc
index 68e923f6766..5c0a2ce7983 100644
--- a/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc
+++ b/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc
@@ -15,7 +15,7 @@
#include "base/task_scheduler/post_task.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/extensions/api/image_writer_private/operation.h"
-#include "chrome/services/removable_storage_writer/public/interfaces/removable_storage_writer.mojom.h"
+#include "chrome/services/removable_storage_writer/public/mojom/removable_storage_writer.mojom.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/service_manager_connection.h"
diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/operation.cc b/chromium/chrome/browser/extensions/api/image_writer_private/operation.cc
index 8c89c0334bb..58739bb2437 100644
--- a/chromium/chrome/browser/extensions/api/image_writer_private/operation.cc
+++ b/chromium/chrome/browser/extensions/api/image_writer_private/operation.cc
@@ -251,8 +251,8 @@ void Operation::GetMD5SumOfFile(
}
}
- PostTask(base::BindOnce(&Operation::MD5Chunk, this, Passed(std::move(file)),
- 0, file_size, progress_offset, progress_scale,
+ PostTask(base::BindOnce(&Operation::MD5Chunk, this, std::move(file), 0,
+ file_size, progress_offset, progress_scale,
std::move(callback)));
}
@@ -294,10 +294,9 @@ void Operation::MD5Chunk(
progress_offset;
SetProgress(percent_curr);
- PostTask(base::BindOnce(&Operation::MD5Chunk, this,
- Passed(std::move(file)), bytes_processed + len,
- bytes_total, progress_offset, progress_scale,
- std::move(callback)));
+ PostTask(base::BindOnce(
+ &Operation::MD5Chunk, this, std::move(file), bytes_processed + len,
+ bytes_total, progress_offset, progress_scale, std::move(callback)));
// Skip closing the file.
return;
} else {
diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/operation_unittest.cc b/chromium/chrome/browser/extensions/api/image_writer_private/operation_unittest.cc
index fcff500fa4c..3f0a8bceef5 100644
--- a/chromium/chrome/browser/extensions/api/image_writer_private/operation_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/image_writer_private/operation_unittest.cc
@@ -260,7 +260,7 @@ TEST_F(ImageWriterOperationTest, VerifyFileFailure) {
test_utils_.GetDevicePath(), kDevicePattern, kTestFileSize);
operation_->Start();
- operation_->VerifyWrite(base::Bind(&base::DoNothing));
+ operation_->VerifyWrite(base::DoNothing());
content::RunAllTasksUntilIdle();
}
#endif // !defined(OS_CHROMEOS)
diff --git a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api.cc b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api.cc
index efd57030204..6d1fd598890 100644
--- a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api.cc
+++ b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api.cc
@@ -13,6 +13,7 @@
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_service.h"
#include "extensions/browser/extension_registry.h"
+#include "ui/base/ime/ime_bridge.h"
#include "ui/keyboard/keyboard_util.h"
namespace input_ime = extensions::api::input_ime;
@@ -413,6 +414,9 @@ InputImeAPI::~InputImeAPI() = default;
void InputImeAPI::Shutdown() {
EventRouter::Get(browser_context_)->UnregisterObserver(this);
registrar_.RemoveAll();
+ if (observer_ && ui::IMEBridge::Get()) {
+ ui::IMEBridge::Get()->SetObserver(nullptr);
+ }
}
static base::LazyInstance<BrowserContextKeyedAPIFactory<InputImeAPI>>::
diff --git a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api.h b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api.h
index 511b1fc2dfb..6aba7251fcf 100644
--- a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api.h
+++ b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api.h
@@ -24,6 +24,7 @@
#include "extensions/browser/extension_function.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/common/extension.h"
+#include "ui/base/ime/ime_bridge_observer.h"
#include "ui/base/ime/ime_engine_handler_interface.h"
#include "ui/base/ime/text_input_flags.h"
@@ -62,7 +63,6 @@ class ImeObserver : public input_method::InputMethodEngineBase::Observer {
int cursor_pos,
int anchor_pos,
int offset_pos) override;
- void OnRequestEngineSwitch() override {}
protected:
// Helper function used to forward the given event to the |profile_|'s event
@@ -176,6 +176,7 @@ class InputImeAPI : public BrowserContextKeyedAPI,
// BrowserContextKeyedAPI implementation.
static BrowserContextKeyedAPIFactory<InputImeAPI>* GetFactoryInstance();
+
void Shutdown() override;
// ExtensionRegistryObserver implementation.
@@ -210,6 +211,8 @@ class InputImeAPI : public BrowserContextKeyedAPI,
extension_registry_observer_;
content::NotificationRegistrar registrar_;
+
+ std::unique_ptr<ui::IMEBridgeObserver> observer_;
};
InputImeEventRouter* GetInputImeEventRouter(Profile* profile);
diff --git a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_nonchromeos.cc b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_nonchromeos.cc
index 11bccc7e86d..76519202074 100644
--- a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_nonchromeos.cc
+++ b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_nonchromeos.cc
@@ -63,6 +63,20 @@ bool IsInputImeEnabled() {
switches::kDisableInputImeAPI);
}
+class ImeBridgeObserver : public ui::IMEBridgeObserver {
+ public:
+ void OnRequestSwitchEngine() override {
+ Browser* browser = chrome::FindLastActive();
+ if (!browser)
+ return;
+ extensions::InputImeEventRouter* router =
+ extensions::GetInputImeEventRouter(browser->profile());
+ if (!router)
+ return;
+ ui::IMEBridge::Get()->SetCurrentEngineHandler(router->active_engine());
+ }
+};
+
class ImeObserverNonChromeOS : public ui::ImeObserver {
public:
ImeObserverNonChromeOS(const std::string& extension_id, Profile* profile)
@@ -94,17 +108,6 @@ class ImeObserverNonChromeOS : public ui::ImeObserver {
OnCompositionBoundsChanged::kEventName, std::move(args));
}
- void OnRequestEngineSwitch() override {
- Browser* browser = chrome::FindLastActive();
- if (!browser)
- return;
- extensions::InputImeEventRouter* router =
- extensions::GetInputImeEventRouter(browser->profile());
- if (!router)
- return;
- ui::IMEBridge::Get()->SetCurrentEngineHandler(router->active_engine());
- }
-
private:
// ImeObserver overrides.
void DispatchEventToExtension(
@@ -145,6 +148,10 @@ void InputImeAPI::OnExtensionLoaded(content::BrowserContext* browser_context,
const Extension* extension) {
// No-op if called multiple times.
ui::IMEBridge::Initialize();
+ if (!observer_) {
+ observer_ = std::make_unique<ImeBridgeObserver>();
+ ui::IMEBridge::Get()->SetObserver(observer_.get());
+ }
// Set the preference kPrefNeverActivatedSinceLoaded true to indicate
// input.ime.activate API has been never called since loaded.
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 e42d686bd00..59177c3676e 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
@@ -12,6 +12,7 @@
#include <utility>
#include <vector>
+#include "base/containers/flat_set.h"
#include "base/feature_list.h"
#include "base/stl_util.h"
#include "base/strings/string16.h"
@@ -32,6 +33,7 @@
#include "chrome/common/extensions/api/language_settings_private.h"
#include "chrome/common/pref_names.h"
#include "components/language/core/browser/language_model.h"
+#include "components/language/core/common/locale_util.h"
#include "components/spellcheck/common/spellcheck_common.h"
#include "components/translate/core/browser/translate_download_manager.h"
#include "components/translate/core/browser/translate_prefs.h"
@@ -178,15 +180,14 @@ LanguageSettingsPrivateGetLanguageListFunction::Run() {
app_locale, translate_prefs->IsTranslateAllowedByPolicy(), &languages);
// Get the list of available locales (display languages) and convert to a set.
- const std::vector<std::string>& locales = l10n_util::GetAvailableLocales();
- const std::unordered_set<std::string> locale_set(locales.begin(),
- locales.end());
+ const base::flat_set<std::string> locale_set(
+ l10n_util::GetAvailableLocales());
// Get the list of spell check languages and convert to a set.
std::vector<std::string> spellcheck_languages =
spellcheck::SpellCheckLanguages();
- const std::unordered_set<std::string> spellcheck_language_set(
- spellcheck_languages.begin(), spellcheck_languages.end());
+ const base::flat_set<std::string> spellcheck_language_set(
+ std::move(spellcheck_languages));
// Build the language list.
std::unique_ptr<base::ListValue> language_list(new base::ListValue);
@@ -198,15 +199,20 @@ LanguageSettingsPrivateGetLanguageListFunction::Run() {
language.native_display_name = entry.native_display_name;
// Set optional fields only if they differ from the default.
- if (locale_set.count(entry.code) > 0) {
- language.supports_ui.reset(new bool(true));
- }
- if (spellcheck_language_set.count(entry.code) > 0) {
+ if (base::ContainsKey(spellcheck_language_set, entry.code)) {
language.supports_spellcheck.reset(new bool(true));
}
if (entry.supports_translate) {
language.supports_translate.reset(new bool(true));
}
+ if (base::FeatureList::IsEnabled(translate::kRegionalLocalesAsDisplayUI)) {
+ std::string temp_locale = entry.code;
+ if (language::ConvertToActualUILocale(&temp_locale)) {
+ language.supports_ui.reset(new bool(true));
+ }
+ } else if (base::ContainsKey(locale_set, entry.code)) {
+ language.supports_ui.reset(new bool(true));
+ }
language_list->Append(language.ToValue());
}
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 12e2e46e9c2..8e1ad435a91 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
@@ -27,8 +27,6 @@
#include "chrome/common/web_application_info.h"
#include "components/favicon/core/favicon_service.h"
#include "content/public/browser/browser_context.h"
-#include "content/public/browser/utility_process_host.h"
-#include "content/public/browser/utility_process_host_client.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/service_manager_connection.h"
#include "extensions/browser/api/management/management_api.h"
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 c4cae1b7845..e144c34a723 100644
--- a/chromium/chrome/browser/extensions/api/management/management_api_browsertest.cc
+++ b/chromium/chrome/browser/extensions/api/management/management_api_browsertest.cc
@@ -216,10 +216,8 @@ class ExtensionManagementApiEscalationTest :
function->SetRenderFrameHost(browser()->tab_strip_model()->
GetActiveWebContents()->GetMainFrame());
bool response = util::RunFunction(
- function.get(),
- base::StringPrintf("[\"%s\", %s]", kId, enabled_string),
- browser(),
- util::NONE);
+ function.get(), base::StringPrintf("[\"%s\", %s]", kId, enabled_string),
+ browser(), api_test_utils::NONE);
if (expected_error.empty()) {
EXPECT_EQ(true, response);
} else {
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 15518413297..4ffb76c9097 100644
--- a/chromium/chrome/browser/extensions/api/management/management_api_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/management/management_api_unittest.cc
@@ -81,7 +81,7 @@ bool ManagementApiUnitTest::RunFunction(
const base::ListValue& args) {
return extension_function_test_utils::RunFunction(
function.get(), base::WrapUnique(args.DeepCopy()), browser(),
- extension_function_test_utils::NONE);
+ api_test_utils::NONE);
}
void ManagementApiUnitTest::SetUp() {
diff --git a/chromium/chrome/browser/extensions/api/media_galleries/media_galleries_apitest.cc b/chromium/chrome/browser/extensions/api/media_galleries/media_galleries_apitest.cc
index b2e8d86bea0..b2acf5e8de8 100644
--- a/chromium/chrome/browser/extensions/api/media_galleries/media_galleries_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/media_galleries/media_galleries_apitest.cc
@@ -27,7 +27,7 @@
#include "chrome/browser/ui/extensions/app_launch_params.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/common/chrome_paths.h"
-#include "components/nacl/common/features.h"
+#include "components/nacl/common/buildflags.h"
#include "components/storage_monitor/storage_info.h"
#include "components/storage_monitor/storage_monitor.h"
#include "content/public/browser/web_contents.h"
diff --git a/chromium/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.cc b/chromium/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.cc
index 46218419192..4b96ce3fc8f 100644
--- a/chromium/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.cc
+++ b/chromium/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.cc
@@ -4,6 +4,8 @@
#include "chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.h"
+#include "base/bind.h"
+#include "chrome/browser/browser_process.h"
#include "chrome/browser/component_updater/cros_component_installer.h"
namespace media_perception = extensions::api::media_perception_private;
@@ -30,6 +32,13 @@ std::string GetComponentNameForComponentType(
return "";
}
+void OnLoadComponent(
+ MediaPerceptionAPIDelegate::LoadCrOSComponentCallback load_callback,
+ component_updater::CrOSComponentManager::Error error,
+ const base::FilePath& mount_point) {
+ std::move(load_callback).Run(mount_point);
+}
+
} // namespace
MediaPerceptionAPIDelegateChromeOS::MediaPerceptionAPIDelegateChromeOS() =
@@ -43,7 +52,7 @@ void MediaPerceptionAPIDelegateChromeOS::LoadCrOSComponent(
g_browser_process->platform_part()->cros_component_manager()->Load(
GetComponentNameForComponentType(type),
component_updater::CrOSComponentManager::MountPolicy::kMount,
- std::move(load_callback));
+ base::BindOnce(OnLoadComponent, std::move(load_callback)));
}
} // namespace extensions
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 eeaa8191412..37f434a3de3 100644
--- a/chromium/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc
+++ b/chromium/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc
@@ -82,7 +82,13 @@ std::unique_ptr<base::DictionaryValue> ChromeMessagingDelegate::MaybeGetTabInfo(
// Only the tab id is useful to platform apps for internal use. The
// unnecessary bits will be stripped out in
// MessagingBindings::DispatchOnConnect().
- return ExtensionTabUtil::CreateTabObject(web_contents)->ToValue();
+ // Note: We don't bother scrubbing the tab object, because this is only
+ // reached as a result of a tab (or content script) messaging the extension.
+ // We need the extension to see the sender so that it can validate if it
+ // trusts it or not.
+ return ExtensionTabUtil::CreateTabObject(
+ web_contents, ExtensionTabUtil::kDontScrubTab, nullptr)
+ ->ToValue();
}
return nullptr;
}
diff --git a/chromium/chrome/browser/extensions/api/messaging/incognito_connectability_infobar_delegate.cc b/chromium/chrome/browser/extensions/api/messaging/incognito_connectability_infobar_delegate.cc
index 17963d18b30..7171d6263c3 100644
--- a/chromium/chrome/browser/extensions/api/messaging/incognito_connectability_infobar_delegate.cc
+++ b/chromium/chrome/browser/extensions/api/messaging/incognito_connectability_infobar_delegate.cc
@@ -36,11 +36,6 @@ IncognitoConnectabilityInfoBarDelegate::
}
}
-infobars::InfoBarDelegate::Type
-IncognitoConnectabilityInfoBarDelegate::GetInfoBarType() const {
- return PAGE_ACTION_TYPE;
-}
-
infobars::InfoBarDelegate::InfoBarIdentifier
IncognitoConnectabilityInfoBarDelegate::GetIdentifier() const {
return INCOGNITO_CONNECTABILITY_INFOBAR_DELEGATE;
diff --git a/chromium/chrome/browser/extensions/api/messaging/incognito_connectability_infobar_delegate.h b/chromium/chrome/browser/extensions/api/messaging/incognito_connectability_infobar_delegate.h
index 8b067019516..002dfe9c733 100644
--- a/chromium/chrome/browser/extensions/api/messaging/incognito_connectability_infobar_delegate.h
+++ b/chromium/chrome/browser/extensions/api/messaging/incognito_connectability_infobar_delegate.h
@@ -36,7 +36,6 @@ class IncognitoConnectabilityInfoBarDelegate : public ConfirmInfoBarDelegate {
~IncognitoConnectabilityInfoBarDelegate() override;
// ConfirmInfoBarDelegate:
- Type GetInfoBarType() const override;
infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
base::string16 GetMessageText() const override;
base::string16 GetButtonLabel(InfoBarButton button) const override;
diff --git a/chromium/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc b/chromium/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc
index a0cf87625fd..53effb4a1dd 100644
--- a/chromium/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc
@@ -24,7 +24,6 @@
#include "base/strings/stringprintf.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
-#include "base/threading/sequenced_worker_pool.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/api/messaging/native_messaging_test_util.h"
diff --git a/chromium/chrome/browser/extensions/api/messaging/native_process_launcher.cc b/chromium/chrome/browser/extensions/api/messaging/native_process_launcher.cc
index ec99eaed500..0d36259d062 100644
--- a/chromium/chrome/browser/extensions/api/messaging/native_process_launcher.cc
+++ b/chromium/chrome/browser/extensions/api/messaging/native_process_launcher.cc
@@ -16,7 +16,6 @@
#include "base/memory/ref_counted.h"
#include "base/strings/stringprintf.h"
#include "base/task_scheduler/post_task.h"
-#include "base/threading/sequenced_worker_pool.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h"
#include "chrome/common/chrome_paths.h"
@@ -228,8 +227,8 @@ void NativeProcessLauncherImpl::Core::PostErrorResult(
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::BindOnce(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread,
- this, callback, error, Passed(base::Process()),
- Passed(base::File()), Passed(base::File())));
+ this, callback, error, base::Process(), base::File(),
+ base::File()));
}
void NativeProcessLauncherImpl::Core::PostResult(
@@ -240,8 +239,8 @@ void NativeProcessLauncherImpl::Core::PostResult(
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::BindOnce(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread,
- this, callback, RESULT_SUCCESS, Passed(&process),
- Passed(&read_file), Passed(&write_file)));
+ this, callback, RESULT_SUCCESS, std::move(process),
+ std::move(read_file), std::move(write_file)));
}
NativeProcessLauncherImpl::NativeProcessLauncherImpl(
diff --git a/chromium/chrome/browser/extensions/api/networking_config_chromeos_apitest_chromeos.cc b/chromium/chrome/browser/extensions/api/networking_config_chromeos_apitest_chromeos.cc
index e9994eab59b..ae13ce020fb 100644
--- a/chromium/chrome/browser/extensions/api/networking_config_chromeos_apitest_chromeos.cc
+++ b/chromium/chrome/browser/extensions/api/networking_config_chromeos_apitest_chromeos.cc
@@ -25,8 +25,8 @@
#include "extensions/test/result_catcher.h"
#include "net/base/net_errors.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
-#include "ui/message_center/notification.h"
-#include "ui/message_center/notification_delegate.h"
+#include "ui/message_center/public/cpp/notification.h"
+#include "ui/message_center/public/cpp/notification_delegate.h"
using chromeos::DBusThreadManager;
using chromeos::NetworkPortalDetector;
@@ -82,8 +82,7 @@ class NetworkingConfigTest
content::RunAllPendingInMessageLoop();
network_portal_detector_ = new NetworkPortalDetectorImpl(
- g_browser_process->system_request_context(),
- true /* create_notification_controller */);
+ test_loader_factory(), true /* create_notification_controller */);
chromeos::network_portal_detector::InitializeForTesting(
network_portal_detector_);
network_portal_detector_->Enable(false /* start_detection */);
diff --git a/chromium/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_browsertest.cc b/chromium/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_browsertest.cc
index a03527e9a0d..9ab7402b18d 100644
--- a/chromium/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_browsertest.cc
+++ b/chromium/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_browsertest.cc
@@ -8,7 +8,7 @@
#include "base/run_loop.h"
#include "base/task_scheduler/post_task.h"
#include "chrome/browser/extensions/api/networking_private/networking_private_credentials_getter.h"
-#include "chrome/services/wifi_util_win/public/interfaces/wifi_credentials_getter.mojom.h"
+#include "chrome/services/wifi_util_win/public/mojom/wifi_credentials_getter.mojom.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/browser/browser_thread.h"
diff --git a/chromium/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_win.cc b/chromium/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_win.cc
index 467a374a407..8a1bab0998d 100644
--- a/chromium/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_win.cc
+++ b/chromium/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_win.cc
@@ -11,8 +11,8 @@
#include "base/macros.h"
#include "base/strings/string_piece.h"
#include "chrome/browser/extensions/api/networking_private/networking_private_crypto.h"
-#include "chrome/services/wifi_util_win/public/interfaces/constants.mojom.h"
-#include "chrome/services/wifi_util_win/public/interfaces/wifi_credentials_getter.mojom.h"
+#include "chrome/services/wifi_util_win/public/mojom/constants.mojom.h"
+#include "chrome/services/wifi_util_win/public/mojom/wifi_credentials_getter.mojom.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/service_manager_connection.h"
#include "services/service_manager/public/cpp/connector.h"
diff --git a/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper.cc b/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper.cc
index 5df72d7fce9..53b6a30f029 100644
--- a/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper.cc
+++ b/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper.cc
@@ -11,7 +11,7 @@
#include "chrome/browser/notifications/notification_display_service.h"
#include "chrome/browser/notifications/notification_display_service_factory.h"
#include "chrome/browser/notifications/notification_handler.h"
-#include "ui/message_center/notification.h"
+#include "ui/message_center/public/cpp/notification.h"
#include "url/gurl.h"
namespace extensions {
diff --git a/chromium/chrome/browser/extensions/api/notifications/extension_notification_handler.cc b/chromium/chrome/browser/extensions/api/notifications/extension_notification_handler.cc
index 0a29eb6e9e4..da8e0798932 100644
--- a/chromium/chrome/browser/extensions/api/notifications/extension_notification_handler.cc
+++ b/chromium/chrome/browser/extensions/api/notifications/extension_notification_handler.cc
@@ -12,13 +12,15 @@
#include "chrome/browser/extensions/api/notifications/extension_notification_display_helper.h"
#include "chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h"
#include "chrome/browser/notifications/notification_common.h"
+#include "chrome/browser/notifications/notifier_state_tracker.h"
+#include "chrome/browser/notifications/notifier_state_tracker_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/notifications.h"
#include "extensions/browser/app_window/app_window.h"
#include "extensions/browser/app_window/app_window_registry.h"
#include "extensions/browser/app_window/native_app_window.h"
#include "extensions/common/constants.h"
-#include "ui/message_center/notifier_id.h"
+#include "ui/message_center/public/cpp/notifier_id.h"
#include "url/gurl.h"
namespace extensions {
@@ -107,6 +109,14 @@ void ExtensionNotificationHandler::OnClick(
std::move(completed_closure).Run();
}
+void ExtensionNotificationHandler::DisableNotifications(Profile* profile,
+ const GURL& origin) {
+ message_center::NotifierId notifier_id(
+ message_center::NotifierId::APPLICATION, origin.host());
+ NotifierStateTrackerFactory::GetForProfile(profile)->SetNotifierEnabled(
+ notifier_id, false /* enabled */);
+}
+
void ExtensionNotificationHandler::SendEvent(
Profile* profile,
const std::string& extension_id,
diff --git a/chromium/chrome/browser/extensions/api/notifications/extension_notification_handler.h b/chromium/chrome/browser/extensions/api/notifications/extension_notification_handler.h
index 062dab0ac8a..60464d79f35 100644
--- a/chromium/chrome/browser/extensions/api/notifications/extension_notification_handler.h
+++ b/chromium/chrome/browser/extensions/api/notifications/extension_notification_handler.h
@@ -36,6 +36,7 @@ class ExtensionNotificationHandler : public NotificationHandler {
const base::Optional<int>& action_index,
const base::Optional<base::string16>& reply,
base::OnceClosure completed_closure) override;
+ void DisableNotifications(Profile* profile, const GURL& origin) override;
protected:
// Overriden in unit tests.
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 03dcc746162..607fd500580 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
@@ -72,7 +72,7 @@ TEST_F(ExtensionNotificationHandlerTest, CloseHandler) {
handler.SetTestExpectations(kChromeExtensionId, "notifications.onClosed", 2);
handler.OnClose(profile.get(), GURL(kChromeExtensionOrigin),
kChromeNotificationId, false /* by_user */,
- base::BindOnce(&base::DoNothing));
+ base::DoNothing());
}
TEST_F(ExtensionNotificationHandlerTest, ClickHandler) {
@@ -84,7 +84,7 @@ TEST_F(ExtensionNotificationHandlerTest, ClickHandler) {
handler.SetTestExpectations(kChromeExtensionId, "notifications.onClicked", 1);
handler.OnClick(profile.get(), GURL(kChromeExtensionOrigin),
kChromeNotificationId, base::nullopt /* action_index */,
- base::nullopt /* reply */, base::BindOnce(&base::DoNothing));
+ base::nullopt /* reply */, base::DoNothing());
}
TEST_F(ExtensionNotificationHandlerTest, ClickHandlerButton) {
@@ -97,7 +97,7 @@ TEST_F(ExtensionNotificationHandlerTest, ClickHandlerButton) {
"notifications.onButtonClicked", 2);
handler.OnClick(profile.get(), GURL(kChromeExtensionOrigin),
kChromeNotificationId, 1 /* action_index */,
- base::nullopt /* reply */, base::BindOnce(&base::DoNothing));
+ base::nullopt /* reply */, base::DoNothing());
}
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/notifications/notifications_api.cc b/chromium/chrome/browser/extensions/api/notifications/notifications_api.cc
index fb63bc5e496..fa154f233e9 100644
--- a/chromium/chrome/browser/extensions/api/notifications/notifications_api.cc
+++ b/chromium/chrome/browser/extensions/api/notifications/notifications_api.cc
@@ -48,10 +48,11 @@
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/image/image_skia_rep.h"
#include "ui/gfx/skia_util.h"
-#include "ui/message_center/notification.h"
-#include "ui/message_center/notification_delegate.h"
-#include "ui/message_center/notifier_id.h"
+#include "ui/message_center/public/cpp/features.h"
#include "ui/message_center/public/cpp/message_center_constants.h"
+#include "ui/message_center/public/cpp/notification.h"
+#include "ui/message_center/public/cpp/notification_delegate.h"
+#include "ui/message_center/public/cpp/notifier_id.h"
#include "url/gurl.h"
using message_center::NotifierId;
@@ -352,15 +353,19 @@ bool NotificationsApiFunction::CreateNotification(
if (has_list_items) {
using api::notifications::NotificationItem;
for (const NotificationItem& api_item : *options->items) {
- optional_fields.items.push_back(message_center::NotificationItem(
- base::UTF8ToUTF16(api_item.title),
- base::UTF8ToUTF16(api_item.message)));
+ optional_fields.items.push_back({base::UTF8ToUTF16(api_item.title),
+ base::UTF8ToUTF16(api_item.message)});
}
}
if (options->is_clickable.get())
optional_fields.clickable = *options->is_clickable;
+ optional_fields.settings_button_handler =
+ base::FeatureList::IsEnabled(message_center::kNewStyleNotifications)
+ ? message_center::SettingsButtonHandler::INLINE
+ : message_center::SettingsButtonHandler::NONE;
+
// TODO(crbug.com/772004): Remove the manual limitation in favor of an IDL
// annotation once supported.
if (id.size() > kNotificationIdLengthLimit) {
@@ -510,9 +515,8 @@ bool NotificationsApiFunction::UpdateNotification(
std::vector<message_center::NotificationItem> items;
using api::notifications::NotificationItem;
for (const NotificationItem& api_item : *options->items) {
- items.push_back(message_center::NotificationItem(
- base::UTF8ToUTF16(api_item.title),
- base::UTF8ToUTF16(api_item.message)));
+ items.push_back({base::UTF8ToUTF16(api_item.title),
+ base::UTF8ToUTF16(api_item.message)});
}
notification->set_items(items);
}
diff --git a/chromium/chrome/browser/extensions/api/notifications/notifications_api.h b/chromium/chrome/browser/extensions/api/notifications/notifications_api.h
index 74278ac150f..b52400a65a6 100644
--- a/chromium/chrome/browser/extensions/api/notifications/notifications_api.h
+++ b/chromium/chrome/browser/extensions/api/notifications/notifications_api.h
@@ -11,7 +11,7 @@
#include "chrome/browser/extensions/chrome_extension_function.h"
#include "chrome/common/extensions/api/notifications.h"
#include "extensions/browser/extension_function.h"
-#include "ui/message_center/notification_types.h"
+#include "ui/message_center/public/cpp/notification_types.h"
namespace message_center {
class Notification;
diff --git a/chromium/chrome/browser/extensions/api/notifications/notifications_apitest.cc b/chromium/chrome/browser/extensions/api/notifications/notifications_apitest.cc
index 6b34b68c345..d999be59789 100644
--- a/chromium/chrome/browser/extensions/api/notifications/notifications_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/notifications/notifications_apitest.cc
@@ -38,8 +38,8 @@
#include "extensions/common/features/feature.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/result_catcher.h"
-#include "ui/message_center/notification.h"
-#include "ui/message_center/notifier_id.h"
+#include "ui/message_center/public/cpp/notification.h"
+#include "ui/message_center/public/cpp/notifier_id.h"
#if defined(OS_MACOSX)
#include "base/mac/mac_util.h"
@@ -311,7 +311,8 @@ IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestGetPermissionLevel) {
notification_function->set_has_callback(true);
std::unique_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
- notification_function.get(), "[]", browser(), utils::NONE));
+ notification_function.get(), "[]", browser(),
+ extensions::api_test_utils::NONE));
EXPECT_EQ(base::Value::Type::STRING, result->type());
std::string permission_level;
@@ -334,7 +335,8 @@ IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestGetPermissionLevel) {
GetNotifierStateTracker()->SetNotifierEnabled(notifier_id, false);
std::unique_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
- notification_function.get(), "[]", browser(), utils::NONE));
+ notification_function.get(), "[]", browser(),
+ extensions::api_test_utils::NONE));
EXPECT_EQ(base::Value::Type::STRING, result->type());
std::string permission_level;
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 bc265038114..0a6441b4d03 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
@@ -72,9 +72,9 @@ PageCaptureSaveAsMHTMLFunction::PageCaptureSaveAsMHTMLFunction() {
PageCaptureSaveAsMHTMLFunction::~PageCaptureSaveAsMHTMLFunction() {
if (mhtml_file_.get()) {
- BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
- base::BindOnce(&ClearFileReferenceOnIOThread,
- base::Passed(&mhtml_file_)));
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&ClearFileReferenceOnIOThread, std::move(mhtml_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 eebc8e3692e..f1b79559ce1 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
@@ -192,8 +192,50 @@ PasswordsPrivateExportPasswordsFunction::Run() {
PasswordsPrivateDelegate* delegate =
PasswordsPrivateDelegateFactory::GetForBrowserContext(browser_context(),
true /* create */);
- delegate->ExportPasswords(GetAssociatedWebContents());
+ delegate->ExportPasswords(
+ base::BindOnce(
+ &PasswordsPrivateExportPasswordsFunction::ExportRequestCompleted,
+ this),
+ GetAssociatedWebContents());
+ return RespondLater();
+}
+
+void PasswordsPrivateExportPasswordsFunction::ExportRequestCompleted(
+ const std::string& error) {
+ if (error.empty())
+ Respond(NoArguments());
+ else
+ Error(error);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PasswordsPrivateCancelExportPasswordsFunction
+
+PasswordsPrivateCancelExportPasswordsFunction::
+ ~PasswordsPrivateCancelExportPasswordsFunction() {}
+
+ExtensionFunction::ResponseAction
+PasswordsPrivateCancelExportPasswordsFunction::Run() {
+ PasswordsPrivateDelegate* delegate =
+ PasswordsPrivateDelegateFactory::GetForBrowserContext(browser_context(),
+ true /* create */);
+ delegate->CancelExportPasswords();
return RespondNow(NoArguments());
}
+////////////////////////////////////////////////////////////////////////////////
+// PasswordsPrivateRequestExportProgressStatusFunction
+
+PasswordsPrivateRequestExportProgressStatusFunction::
+ ~PasswordsPrivateRequestExportProgressStatusFunction() {}
+
+ExtensionFunction::ResponseAction
+PasswordsPrivateRequestExportProgressStatusFunction::Run() {
+ PasswordsPrivateDelegate* delegate =
+ PasswordsPrivateDelegateFactory::GetForBrowserContext(browser_context(),
+ true /* create */);
+ return RespondNow(OneArgument(std::make_unique<base::Value>(
+ ToString(delegate->GetExportProgressStatus()))));
+}
+
} // namespace extensions
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 59f7e3caf2a..f541487c4f4 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
@@ -155,9 +155,45 @@ class PasswordsPrivateExportPasswordsFunction
ResponseAction Run() override;
private:
+ void ExportRequestCompleted(const std::string& error);
+
DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateExportPasswordsFunction);
};
+class PasswordsPrivateCancelExportPasswordsFunction
+ : public UIThreadExtensionFunction {
+ public:
+ PasswordsPrivateCancelExportPasswordsFunction() {}
+ DECLARE_EXTENSION_FUNCTION("passwordsPrivate.cancelExportPasswords",
+ PASSWORDSPRIVATE_CANCELEXPORTPASSWORDS);
+
+ protected:
+ ~PasswordsPrivateCancelExportPasswordsFunction() override;
+
+ // ExtensionFunction overrides.
+ ResponseAction Run() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateCancelExportPasswordsFunction);
+};
+
+class PasswordsPrivateRequestExportProgressStatusFunction
+ : public UIThreadExtensionFunction {
+ public:
+ PasswordsPrivateRequestExportProgressStatusFunction() {}
+ DECLARE_EXTENSION_FUNCTION("passwordsPrivate.requestExportProgressStatus",
+ PASSWORDSPRIVATE_REQUESTEXPORTPROGRESSSTATUS);
+
+ protected:
+ ~PasswordsPrivateRequestExportProgressStatusFunction() override;
+
+ // ExtensionFunction overrides.
+ ResponseAction Run() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateRequestExportProgressStatusFunction);
+};
+
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_API_H_
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 09c69a450f0..6a1ac5b9d6b 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
@@ -150,15 +150,30 @@ class TestDelegate : public PasswordsPrivateDelegate {
importPasswordsTriggered = true;
}
- void ExportPasswords(content::WebContents* web_contents) override {
+ void ExportPasswords(base::OnceCallback<void(const std::string&)> callback,
+ content::WebContents* web_contents) override {
// The testing of password exporting itself should be handled via
// |PasswordManagerPorter|.
exportPasswordsTriggered = true;
+ std::move(callback).Run(std::string());
+ }
+
+ void CancelExportPasswords() override {
+ cancelExportPasswordsTriggered = true;
+ }
+
+ api::passwords_private::ExportProgressStatus GetExportProgressStatus()
+ override {
+ // The testing of password exporting itself should be handled via
+ // |PasswordManagerPorter|.
+ return api::passwords_private::ExportProgressStatus::
+ EXPORT_PROGRESS_STATUS_IN_PROGRESS;
}
// Flags for detecting whether import/export operations have been invoked.
bool importPasswordsTriggered = false;
bool exportPasswordsTriggered = false;
+ bool cancelExportPasswordsTriggered = false;
private:
// The current list of entries/exceptions. Cached here so that when new
@@ -220,6 +235,10 @@ class PasswordsPrivateApiTest : public ExtensionApiTest {
return s_test_delegate_->exportPasswordsTriggered;
}
+ bool cancelExportPasswordsWasTriggered() {
+ return s_test_delegate_->cancelExportPasswordsTriggered;
+ }
+
private:
static TestDelegate* s_test_delegate_;
@@ -281,4 +300,17 @@ IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, ExportPasswords) {
}
}
+IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, CancelExportPasswords) {
+ EXPECT_FALSE(cancelExportPasswordsWasTriggered());
+ EXPECT_TRUE(RunPasswordsSubtest("cancelExportPasswords")) << message_;
+
+ if (!ExtensionApiTest::ExtensionSubtestsAreSkipped()) {
+ EXPECT_TRUE(cancelExportPasswordsWasTriggered());
+ }
+}
+
+IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, RequestExportProgressStatus) {
+ EXPECT_TRUE(RunPasswordsSubtest("requestExportProgressStatus")) << message_;
+}
+
} // namespace extensions
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 4ac97e242d3..3b921ff9bc8 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
@@ -17,6 +17,7 @@
#include "chrome/browser/ui/passwords/password_ui_view.h"
#include "chrome/common/extensions/api/passwords_private.h"
#include "components/keyed_service/core/keyed_service.h"
+#include "components/password_manager/core/browser/ui/export_progress_status.h"
#include "extensions/browser/extension_function.h"
namespace content {
@@ -76,8 +77,18 @@ class PasswordsPrivateDelegate : public KeyedService {
virtual void ImportPasswords(content::WebContents* web_contents) = 0;
// Trigger the password export procedure, allowing the user to save a file
- // containing their passwords.
- virtual void ExportPasswords(content::WebContents* web_contents) = 0;
+ // containing their passwords. |callback| will be called with an error
+ // message if the request is rejected, because another export is in progress.
+ virtual void ExportPasswords(
+ 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;
};
} // namespace extensions
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 30280a6a7d5..8b975a1f16c 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
@@ -31,6 +31,43 @@
#include "chrome/browser/password_manager/password_manager_util_mac.h"
#endif
+namespace {
+
+// The error message returned to the UI when Chrome refuses to start multiple
+// exports.
+const char kExportInProgress[] = "in-progress";
+// The error message returned to the UI when the user fails to reauthenticate.
+const char kReauthenticationFailed[] = "reauth-failed";
+
+// Map password_manager::ExportProgressStatus to
+// extensions::api::passwords_private::ExportProgressStatus.
+extensions::api::passwords_private::ExportProgressStatus ConvertStatus(
+ password_manager::ExportProgressStatus status) {
+ switch (status) {
+ case password_manager::ExportProgressStatus::NOT_STARTED:
+ return extensions::api::passwords_private::ExportProgressStatus::
+ EXPORT_PROGRESS_STATUS_NOT_STARTED;
+ case password_manager::ExportProgressStatus::IN_PROGRESS:
+ return extensions::api::passwords_private::ExportProgressStatus::
+ EXPORT_PROGRESS_STATUS_IN_PROGRESS;
+ case password_manager::ExportProgressStatus::SUCCEEDED:
+ return extensions::api::passwords_private::ExportProgressStatus::
+ EXPORT_PROGRESS_STATUS_SUCCEEDED;
+ case password_manager::ExportProgressStatus::FAILED_CANCELLED:
+ return extensions::api::passwords_private::ExportProgressStatus::
+ EXPORT_PROGRESS_STATUS_FAILED_CANCELLED;
+ case password_manager::ExportProgressStatus::FAILED_WRITE_FAILED:
+ return extensions::api::passwords_private::ExportProgressStatus::
+ EXPORT_PROGRESS_STATUS_FAILED_WRITE_FAILED;
+ }
+
+ NOTREACHED();
+ return extensions::api::passwords_private::ExportProgressStatus::
+ EXPORT_PROGRESS_STATUS_NONE;
+}
+
+} // namespace
+
namespace extensions {
PasswordsPrivateDelegateImpl::PasswordsPrivateDelegateImpl(Profile* profile)
@@ -38,7 +75,10 @@ PasswordsPrivateDelegateImpl::PasswordsPrivateDelegateImpl(Profile* profile)
password_manager_presenter_(
std::make_unique<PasswordManagerPresenter>(this)),
password_manager_porter_(std::make_unique<PasswordManagerPorter>(
- password_manager_presenter_.get())),
+ password_manager_presenter_.get(),
+ base::BindRepeating(
+ &PasswordsPrivateDelegateImpl::OnPasswordsExportProgress,
+ base::Unretained(this)))),
password_access_authenticator_(
base::BindRepeating(&PasswordsPrivateDelegateImpl::OsReauthCall,
base::Unretained(this))),
@@ -132,7 +172,8 @@ void PasswordsPrivateDelegateImpl::RequestShowPasswordInternal(
// TODO(stevenjb): Pass this directly to RequestShowPassword(); see
// crbug.com/495290.
web_contents_ = web_contents;
- if (!password_access_authenticator_.EnsureUserIsAuthenticated()) {
+ if (!password_access_authenticator_.EnsureUserIsAuthenticated(
+ password_manager::ReauthPurpose::VIEW_PASSWORD)) {
return;
}
@@ -140,11 +181,13 @@ void PasswordsPrivateDelegateImpl::RequestShowPasswordInternal(
password_manager_presenter_->RequestShowPassword(index);
}
-bool PasswordsPrivateDelegateImpl::OsReauthCall() {
+bool PasswordsPrivateDelegateImpl::OsReauthCall(
+ password_manager::ReauthPurpose purpose) {
#if defined(OS_WIN)
- return password_manager_util_win::AuthenticateUser(GetNativeWindow());
+ return password_manager_util_win::AuthenticateUser(GetNativeWindow(),
+ purpose);
#elif defined(OS_MACOSX)
- return password_manager_util_mac::AuthenticateUser();
+ return password_manager_util_mac::AuthenticateUser(purpose);
#else
return true;
#endif
@@ -240,18 +283,31 @@ void PasswordsPrivateDelegateImpl::ImportPasswords(
}
void PasswordsPrivateDelegateImpl::ExportPasswords(
+ base::OnceCallback<void(const std::string&)> callback,
content::WebContents* web_contents) {
// Save |web_contents| so that it can be used later when GetNativeWindow() is
// called. Note: This is safe because the |web_contents| is used before
// exiting this method. TODO(crbug.com/495290): Pass the native window
// directly to the reauth-handling code.
web_contents_ = web_contents;
- if (!password_access_authenticator_.ForceUserReauthentication()) {
+ if (!password_access_authenticator_.ForceUserReauthentication(
+ password_manager::ReauthPurpose::EXPORT)) {
+ std::move(callback).Run(kReauthenticationFailed);
return;
}
password_manager_porter_->set_web_contents(web_contents);
- password_manager_porter_->Store();
+ bool accepted = password_manager_porter_->Store();
+ std::move(callback).Run(accepted ? std::string() : kExportInProgress);
+}
+
+void PasswordsPrivateDelegateImpl::CancelExportPasswords() {
+ password_manager_porter_->CancelStore();
+}
+
+api::passwords_private::ExportProgressStatus
+PasswordsPrivateDelegateImpl::GetExportProgressStatus() {
+ return ConvertStatus(password_manager_porter_->GetExportProgressStatus());
}
#if !defined(OS_ANDROID)
@@ -261,13 +317,23 @@ gfx::NativeWindow PasswordsPrivateDelegateImpl::GetNativeWindow() const {
}
#endif
+void PasswordsPrivateDelegateImpl::OnPasswordsExportProgress(
+ password_manager::ExportProgressStatus status,
+ const std::string& folder_name) {
+ PasswordsPrivateEventRouter* router =
+ PasswordsPrivateEventRouterFactory::GetForProfile(profile_);
+ if (router) {
+ router->OnPasswordsExportProgress(ConvertStatus(status), folder_name);
+ }
+}
+
void PasswordsPrivateDelegateImpl::Shutdown() {
password_manager_presenter_.reset();
password_manager_porter_.reset();
}
void PasswordsPrivateDelegateImpl::SetOsReauthCallForTesting(
- base::RepeatingCallback<bool()> os_reauth_call) {
+ PasswordAccessAuthenticator::ReauthCallback os_reauth_call) {
password_access_authenticator_.SetOsReauthCallForTesting(
std::move(os_reauth_call));
}
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 a7c7e4a6a3f..c9d02bcb6d0 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
@@ -16,12 +16,14 @@
#include "base/observer_list.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h"
+#include "chrome/browser/password_manager/reauth_purpose.h"
#include "chrome/browser/ui/passwords/password_access_authenticator.h"
#include "chrome/browser/ui/passwords/password_manager_porter.h"
#include "chrome/browser/ui/passwords/password_manager_presenter.h"
#include "chrome/browser/ui/passwords/password_ui_view.h"
#include "chrome/common/extensions/api/passwords_private.h"
#include "components/keyed_service/core/keyed_service.h"
+#include "components/password_manager/core/browser/ui/export_progress_status.h"
#include "extensions/browser/extension_function.h"
class Profile;
@@ -51,7 +53,11 @@ class PasswordsPrivateDelegateImpl : public PasswordsPrivateDelegate,
void RequestShowPassword(size_t index,
content::WebContents* web_contents) override;
void ImportPasswords(content::WebContents* web_contents) override;
- void ExportPasswords(content::WebContents* web_contents) override;
+ void ExportPasswords(base::OnceCallback<void(const std::string&)> accepted,
+ content::WebContents* web_contents) override;
+ void CancelExportPasswords() override;
+ api::passwords_private::ExportProgressStatus GetExportProgressStatus()
+ override;
// PasswordUIView implementation.
Profile* GetProfile() override;
@@ -68,12 +74,17 @@ class PasswordsPrivateDelegateImpl : public PasswordsPrivateDelegate,
gfx::NativeWindow GetNativeWindow() const override;
#endif
+ // Callback for when the password list has been written to the destination.
+ void OnPasswordsExportProgress(password_manager::ExportProgressStatus status,
+ const std::string& folder_name);
+
// KeyedService overrides:
void Shutdown() override;
// Use this in tests to mock the OS-level reauthentication.
void SetOsReauthCallForTesting(
- base::RepeatingCallback<bool()> os_reauth_call);
+ base::RepeatingCallback<bool(password_manager::ReauthPurpose)>
+ os_reauth_call);
private:
// Called after the lists are fetched. Once both lists have been set, the
@@ -93,7 +104,7 @@ class PasswordsPrivateDelegateImpl : public PasswordsPrivateDelegate,
// Triggers an OS-dependent UI to present OS account login challenge and
// returns true if the user passed that challenge.
- bool OsReauthCall();
+ bool OsReauthCall(password_manager::ReauthPurpose purpose);
// Not owned by this class.
Profile* profile_;
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 18816b55a21..4b8102b3239 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
@@ -13,6 +13,7 @@
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/mock_callback.h"
#include "base/values.h"
#include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h"
#include "chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h"
@@ -27,6 +28,8 @@
#include "testing/gtest/include/gtest/gtest.h"
using PasswordFormList = std::vector<std::unique_ptr<autofill::PasswordForm>>;
+using ::testing::Ne;
+using ::testing::StrictMock;
namespace extensions {
@@ -99,7 +102,9 @@ void PasswordEventObserver::OnBroadcastEvent(const extensions::Event& event) {
enum class ReauthResult { PASS, FAIL };
-bool FakeOsReauthCall(bool* reauth_called, ReauthResult result) {
+bool FakeOsReauthCall(bool* reauth_called,
+ ReauthResult result,
+ password_manager::ReauthPurpose purpose) {
*reauth_called = true;
return result == ReauthResult::PASS;
}
@@ -255,6 +260,8 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestFailedReauthOnView) {
TEST_F(PasswordsPrivateDelegateImplTest, TestReauthOnExport) {
SetUpPasswordStore({CreateSampleForm()});
+ StrictMock<base::MockCallback<base::OnceCallback<void(const std::string&)>>>
+ mock_accepted;
PasswordsPrivateDelegateImpl delegate(&profile_);
// Spin the loop to allow PasswordStore tasks posted on the creation of
@@ -265,17 +272,35 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestReauthOnExport) {
delegate.SetOsReauthCallForTesting(base::BindRepeating(
&FakeOsReauthCall, &reauth_called, ReauthResult::PASS));
- delegate.ExportPasswords(nullptr);
+ EXPECT_CALL(mock_accepted, Run(std::string())).Times(2);
+
+ delegate.ExportPasswords(mock_accepted.Get(), nullptr);
EXPECT_TRUE(reauth_called);
// Export should ignore previous reauthentication results.
reauth_called = false;
- delegate.ExportPasswords(nullptr);
+ delegate.ExportPasswords(mock_accepted.Get(), nullptr);
EXPECT_TRUE(reauth_called);
+}
+
+TEST_F(PasswordsPrivateDelegateImplTest, TestReauthFailedOnExport) {
+ SetUpPasswordStore({CreateSampleForm()});
+ StrictMock<base::MockCallback<base::OnceCallback<void(const std::string&)>>>
+ mock_accepted;
- // TODO(crbug.com/341477): Once the export flow has defined messages to UI,
- // such as progress indication, intercept them with PasswordEventObserver and
- // check that exporting is aborted if the authentication failed.
+ PasswordsPrivateDelegateImpl delegate(&profile_);
+ // Spin the loop to allow PasswordStore tasks posted on the creation of
+ // |delegate| to be completed.
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_CALL(mock_accepted, Run(std::string("reauth-failed")));
+
+ bool reauth_called = false;
+ delegate.SetOsReauthCallForTesting(base::BindRepeating(
+ &FakeOsReauthCall, &reauth_called, ReauthResult::FAIL));
+
+ delegate.ExportPasswords(mock_accepted.Get(), nullptr);
+ EXPECT_TRUE(reauth_called);
}
} // namespace extensions
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.cc b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.cc
index ca05c357e70..3851d77d807 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.cc
+++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.cc
@@ -42,10 +42,10 @@ void PasswordsPrivateEventRouter::SendSavedPasswordListToListeners() {
// If there is nothing to send, return early.
return;
- std::unique_ptr<Event> extension_event(
- new Event(events::PASSWORDS_PRIVATE_ON_SAVED_PASSWORDS_LIST_CHANGED,
- api::passwords_private::OnSavedPasswordsListChanged::kEventName,
- cached_saved_password_parameters_->CreateDeepCopy()));
+ auto extension_event = std::make_unique<Event>(
+ events::PASSWORDS_PRIVATE_ON_SAVED_PASSWORDS_LIST_CHANGED,
+ api::passwords_private::OnSavedPasswordsListChanged::kEventName,
+ cached_saved_password_parameters_->CreateDeepCopy());
event_router_->BroadcastEvent(std::move(extension_event));
}
@@ -62,10 +62,10 @@ void PasswordsPrivateEventRouter::SendPasswordExceptionListToListeners() {
// If there is nothing to send, return early.
return;
- std::unique_ptr<Event> extension_event(new Event(
+ auto extension_event = std::make_unique<Event>(
events::PASSWORDS_PRIVATE_ON_PASSWORD_EXCEPTIONS_LIST_CHANGED,
api::passwords_private::OnPasswordExceptionsListChanged::kEventName,
- cached_password_exception_parameters_->CreateDeepCopy()));
+ cached_password_exception_parameters_->CreateDeepCopy());
event_router_->BroadcastEvent(std::move(extension_event));
}
@@ -76,13 +76,30 @@ void PasswordsPrivateEventRouter::OnPlaintextPasswordFetched(
params.index = index;
params.plaintext_password = plaintext_password;
- std::unique_ptr<base::ListValue> event_value(new base::ListValue);
+ auto event_value = std::make_unique<base::ListValue>();
event_value->Append(params.ToValue());
- std::unique_ptr<Event> extension_event(new Event(
+ auto extension_event = std::make_unique<Event>(
events::PASSWORDS_PRIVATE_ON_PLAINTEXT_PASSWORD_RETRIEVED,
api::passwords_private::OnPlaintextPasswordRetrieved::kEventName,
- std::move(event_value)));
+ std::move(event_value));
+ event_router_->BroadcastEvent(std::move(extension_event));
+}
+
+void PasswordsPrivateEventRouter::OnPasswordsExportProgress(
+ api::passwords_private::ExportProgressStatus status,
+ const std::string& folder_name) {
+ api::passwords_private::PasswordExportProgress params;
+ params.status = status;
+ params.folder_name = std::make_unique<std::string>(std::move(folder_name));
+
+ auto event_value = std::make_unique<base::ListValue>();
+ event_value->Append(params.ToValue());
+
+ auto extension_event = std::make_unique<Event>(
+ events::PASSWORDS_PRIVATE_ON_PASSWORDS_FILE_EXPORT_PROGRESS,
+ api::passwords_private::OnPasswordsFileExportProgress::kEventName,
+ std::move(event_value));
event_router_->BroadcastEvent(std::move(extension_event));
}
diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h
index b1d529327d2..b4bcfa6284c 100644
--- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h
+++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h
@@ -5,6 +5,10 @@
#ifndef CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_EVENT_ROUTER_H_
#define CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_EVENT_ROUTER_H_
+#include <memory>
+#include <string>
+#include <vector>
+
#include "base/macros.h"
#include "chrome/common/extensions/api/passwords_private.h"
#include "components/keyed_service/core/keyed_service.h"
@@ -41,6 +45,14 @@ class PasswordsPrivateEventRouter : public KeyedService {
void OnPlaintextPasswordFetched(size_t index,
const std::string& plaintext_password);
+ // Notifies listeners after the passwords have been written to the export
+ // destination.
+ // |folder_name| In case of failure to export, this will describe destination
+ // we tried to write on.
+ void OnPasswordsExportProgress(
+ api::passwords_private::ExportProgressStatus status,
+ const std::string& folder_name);
+
protected:
explicit PasswordsPrivateEventRouter(content::BrowserContext* context);
diff --git a/chromium/chrome/browser/extensions/api/permissions/permissions_api_unittest.cc b/chromium/chrome/browser/extensions/api/permissions/permissions_api_unittest.cc
index 28b90107c8b..a365158dbde 100644
--- a/chromium/chrome/browser/extensions/api/permissions/permissions_api_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/permissions/permissions_api_unittest.cc
@@ -59,8 +59,7 @@ class PermissionsAPIUnitTest : public ExtensionServiceTestWithInstall {
new PermissionsContainsFunction());
function->set_extension(extension.get());
bool run_result = extension_function_test_utils::RunFunction(
- function.get(), args_string, browser(),
- extension_function_test_utils::NONE);
+ function.get(), args_string, browser(), api_test_utils::NONE);
EXPECT_TRUE(run_result) << function->GetError();
bool has_permission;
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 2964d6272fc..d66dd3a6f5b 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
@@ -212,8 +212,8 @@ class ControlledPrefsInstallIncognitoPersistent
new base::Value("val1"));
InstallExtensionControlledPrefIncognito(extension1(), kPref1,
new base::Value("val2"));
- std::unique_ptr<PrefService> incog_prefs(
- prefs_.CreateIncognitoPrefService());
+ std::unique_ptr<PrefService> incog_prefs =
+ prefs_.CreateIncognitoPrefService();
std::string actual = incog_prefs->GetString(kPref1);
EXPECT_EQ("val2", actual);
}
@@ -223,8 +223,8 @@ class ControlledPrefsInstallIncognitoPersistent
std::string actual = prefs()->pref_service()->GetString(kPref1);
EXPECT_EQ("val1", actual);
// Incognito pref service shall see incognito values.
- std::unique_ptr<PrefService> incog_prefs(
- prefs_.CreateIncognitoPrefService());
+ std::unique_ptr<PrefService> incog_prefs =
+ prefs_.CreateIncognitoPrefService();
actual = incog_prefs->GetString(kPref1);
EXPECT_EQ("val2", actual);
}
@@ -243,8 +243,8 @@ class ControlledPrefsInstallIncognitoSessionOnly
new base::Value("val1"));
InstallExtensionControlledPrefIncognitoSessionOnly(extension1(), kPref1,
new base::Value("val2"));
- std::unique_ptr<PrefService> incog_prefs(
- prefs_.CreateIncognitoPrefService());
+ std::unique_ptr<PrefService> incog_prefs =
+ prefs_.CreateIncognitoPrefService();
std::string actual = incog_prefs->GetString(kPref1);
EXPECT_EQ("val2", actual);
}
@@ -255,8 +255,8 @@ class ControlledPrefsInstallIncognitoSessionOnly
// Incognito pref service shall see session-only incognito values only
// during first run. Once the pref service was reloaded, all values shall be
// discarded.
- std::unique_ptr<PrefService> incog_prefs(
- prefs_.CreateIncognitoPrefService());
+ std::unique_ptr<PrefService> incog_prefs =
+ prefs_.CreateIncognitoPrefService();
actual = incog_prefs->GetString(kPref1);
if (iteration_ == 0) {
EXPECT_EQ("val2", actual);
@@ -314,8 +314,8 @@ class ControlledPrefsNotifyWhenNeeded : public ExtensionControlledPrefsTest {
registrar.Add(kPref1, observer.GetCallback());
MockPrefChangeCallback incognito_observer(prefs()->pref_service());
- std::unique_ptr<PrefService> incog_prefs(
- prefs_.CreateIncognitoPrefService());
+ std::unique_ptr<PrefService> incog_prefs =
+ prefs_.CreateIncognitoPrefService();
PrefChangeRegistrar incognito_registrar;
incognito_registrar.Init(incog_prefs.get());
incognito_registrar.Add(kPref1, incognito_observer.GetCallback());
diff --git a/chromium/chrome/browser/extensions/api/processes/processes_api.cc b/chromium/chrome/browser/extensions/api/processes/processes_api.cc
index 63c005ec295..4ec343e20b9 100644
--- a/chromium/chrome/browser/extensions/api/processes/processes_api.cc
+++ b/chromium/chrome/browser/extensions/api/processes/processes_api.cc
@@ -372,11 +372,12 @@ void ProcessesEventRouter::UpdateRefreshTypesFlagsBasedOnListeners() {
refresh_types |= GetRefreshTypesFlagOnlyEssentialData();
}
+ const int64_t on_updated_types = GetRefreshTypesForProcessOptionalData();
if (HasEventListeners(api::processes::OnUpdated::kEventName))
- refresh_types |= GetRefreshTypesForProcessOptionalData();
+ refresh_types |= on_updated_types;
if (HasEventListeners(api::processes::OnUpdatedWithMemory::kEventName))
- refresh_types |= task_manager::REFRESH_TYPE_MEMORY;
+ refresh_types |= (on_updated_types | task_manager::REFRESH_TYPE_MEMORY);
SetRefreshTypesFlags(refresh_types);
}
diff --git a/chromium/chrome/browser/extensions/api/processes/processes_apitest.cc b/chromium/chrome/browser/extensions/api/processes/processes_apitest.cc
index d75a4065760..42668f2615d 100644
--- a/chromium/chrome/browser/extensions/api/processes/processes_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/processes/processes_apitest.cc
@@ -9,6 +9,7 @@
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_dialogs.h"
#include "chrome/browser/ui/browser_window.h"
+#include "chrome/common/extensions/api/processes.h"
#include "extensions/common/switches.h"
#include "extensions/test/extension_test_message_listener.h"
@@ -69,6 +70,48 @@ IN_PROC_BROWSER_TEST_F(ProcessesApiTest, ProcessesApiListeners) {
EXPECT_EQ(0, GetListenersCount());
}
+IN_PROC_BROWSER_TEST_F(ProcessesApiTest, OnUpdatedWithMemoryRefreshTypes) {
+ EXPECT_EQ(0, GetListenersCount());
+
+ // Load an extension that listen to the onUpdatedWithMemory.
+ ExtensionTestMessageListener listener("ready", false /* will_reply */);
+ const extensions::Extension* extension =
+ LoadExtension(test_data_dir_.AppendASCII("processes")
+ .AppendASCII("onupdated_with_memory"));
+ ASSERT_TRUE(extension);
+ ASSERT_TRUE(listener.WaitUntilSatisfied());
+
+ // The memory refresh type must be enabled now.
+ const task_manager::TaskManagerInterface* task_manager =
+ task_manager::TaskManagerInterface::GetTaskManager();
+ EXPECT_EQ(1, GetListenersCount());
+ extensions::EventRouter* event_router =
+ extensions::EventRouter::Get(profile());
+ EXPECT_TRUE(event_router->HasEventListener(
+ extensions::api::processes::OnUpdatedWithMemory::kEventName));
+ EXPECT_FALSE(event_router->HasEventListener(
+ extensions::api::processes::OnUpdated::kEventName));
+ EXPECT_TRUE(task_manager->IsResourceRefreshEnabled(
+ task_manager::REFRESH_TYPE_MEMORY));
+
+ // Despite the fact that there are no onUpdated listeners, refresh types for
+ // CPU, Network, SQLite, V8 memory, and webcache stats should be enabled.
+ constexpr task_manager::RefreshType kOnUpdatedRefreshTypes[] = {
+ task_manager::REFRESH_TYPE_CPU,
+ task_manager::REFRESH_TYPE_NETWORK_USAGE,
+ task_manager::REFRESH_TYPE_SQLITE_MEMORY,
+ task_manager::REFRESH_TYPE_V8_MEMORY,
+ task_manager::REFRESH_TYPE_WEBCACHE_STATS,
+ };
+
+ for (const auto& type : kOnUpdatedRefreshTypes)
+ EXPECT_TRUE(task_manager->IsResourceRefreshEnabled(type));
+
+ // Unload the extensions and make sure the listeners count is updated.
+ UnloadExtension(extension->id());
+ EXPECT_EQ(0, GetListenersCount());
+}
+
IN_PROC_BROWSER_TEST_F(ProcessesApiTest, CannotTerminateBrowserProcess) {
ASSERT_TRUE(RunExtensionTest("processes/terminate-browser-process"))
<< message_;
diff --git a/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.cc b/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.cc
index 780aafeeb1e..8efe3706631 100644
--- a/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.cc
+++ b/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.cc
@@ -26,7 +26,7 @@
#include "components/proxy_config/proxy_config_dictionary.h"
#include "extensions/common/error_utils.h"
#include "net/base/data_url.h"
-#include "net/proxy/proxy_config.h"
+#include "net/proxy_resolution/proxy_config.h"
namespace extensions {
@@ -378,16 +378,16 @@ std::unique_ptr<base::DictionaryValue> CreateProxyRulesDict(
rules.ParseFromString(proxy_servers);
switch (rules.type) {
- case net::ProxyConfig::ProxyRules::TYPE_NO_RULES:
+ case net::ProxyConfig::ProxyRules::Type::EMPTY:
return NULL;
- case net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY:
+ case net::ProxyConfig::ProxyRules::Type::PROXY_LIST:
if (!rules.single_proxies.IsEmpty()) {
extension_proxy_rules->Set(
keys::field_name[keys::SCHEME_ALL],
CreateProxyServerDict(rules.single_proxies.Get()));
}
break;
- case net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME:
+ case net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME:
if (!rules.proxies_for_http.IsEmpty()) {
extension_proxy_rules->Set(
keys::field_name[keys::SCHEME_HTTP],
diff --git a/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.h b/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.h
index f572165276e..43428988aa6 100644
--- a/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.h
+++ b/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.h
@@ -11,7 +11,7 @@
#include <string>
#include "components/proxy_config/proxy_prefs.h"
-#include "net/proxy/proxy_config.h"
+#include "net/proxy_resolution/proxy_config.h"
class ProxyConfigDictionary;
diff --git a/chromium/chrome/browser/extensions/api/runtime/runtime_apitest.cc b/chromium/chrome/browser/extensions/api/runtime/runtime_apitest.cc
index 2999bdd5c46..a1f7840198a 100644
--- a/chromium/chrome/browser/extensions/api/runtime/runtime_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/runtime/runtime_apitest.cc
@@ -7,7 +7,6 @@
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_function_test_utils.h"
#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/test_extension_dir.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/test/browser_test_utils.h"
#include "extensions/browser/api/runtime/runtime_api.h"
@@ -17,6 +16,7 @@
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/test_extension_registry_observer.h"
#include "extensions/test/result_catcher.h"
+#include "extensions/test/test_extension_dir.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "url/url_constants.h"
diff --git a/chromium/chrome/browser/extensions/api/sessions/sessions_api.cc b/chromium/chrome/browser/extensions/api/sessions/sessions_api.cc
index aba5cc99f59..872c24eba79 100644
--- a/chromium/chrome/browser/extensions/api/sessions/sessions_api.cc
+++ b/chromium/chrome/browser/extensions/api/sessions/sessions_api.cc
@@ -6,6 +6,7 @@
#include <stddef.h>
+#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
@@ -380,8 +381,8 @@ ExtensionFunction::ResponseAction SessionsGetDevicesFunction::Run() {
ExtensionFunction::ResponseValue SessionsRestoreFunction::GetRestoredTabResult(
content::WebContents* contents) {
- std::unique_ptr<tabs::Tab> tab(
- ExtensionTabUtil::CreateTabObject(contents, extension()));
+ std::unique_ptr<tabs::Tab> tab(ExtensionTabUtil::CreateTabObject(
+ contents, ExtensionTabUtil::kScrubTab, extension()));
std::unique_ptr<api::sessions::Session> restored_session(
CreateSessionModelHelper(base::Time::Now().ToTimeT(), std::move(tab),
std::unique_ptr<windows::Window>()));
@@ -390,14 +391,15 @@ ExtensionFunction::ResponseValue SessionsRestoreFunction::GetRestoredTabResult(
ExtensionFunction::ResponseValue
SessionsRestoreFunction::GetRestoredWindowResult(int window_id) {
- WindowController* controller = NULL;
+ Browser* browser = nullptr;
std::string error;
- if (!windows_util::GetWindowFromWindowID(this, window_id, 0, &controller,
- &error)) {
+ if (!windows_util::GetBrowserFromWindowID(this, window_id, 0, &browser,
+ &error)) {
return Error(error);
}
std::unique_ptr<base::DictionaryValue> window_value(
- controller->CreateWindowValueWithTabs(extension()));
+ ExtensionTabUtil::CreateWindowValueForExtension(
+ *browser, extension(), ExtensionTabUtil::kPopulateTabs));
std::unique_ptr<windows::Window> window(
windows::Window::FromValue(*window_value));
return ArgumentList(Restore::Results::Create(*CreateSessionModelHelper(
diff --git a/chromium/chrome/browser/extensions/api/sessions/sessions_apitest.cc b/chromium/chrome/browser/extensions/api/sessions/sessions_apitest.cc
index 6f68a4cd4d0..9d457c2dcae 100644
--- a/chromium/chrome/browser/extensions/api/sessions/sessions_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/sessions/sessions_apitest.cc
@@ -30,8 +30,6 @@
#include "components/browser_sync/profile_sync_service_mock.h"
#include "components/sync/device_info/local_device_info_provider_mock.h"
#include "components/sync/driver/sync_api_component_factory_mock.h"
-#include "components/sync/model/attachments/attachment_id.h"
-#include "components/sync/model/attachments/attachment_service_proxy_for_test.h"
#include "components/sync/model/fake_sync_change_processor.h"
#include "components/sync/model/sync_error_factory_mock.h"
#include "components/sync_sessions/sessions_sync_manager.h"
@@ -266,16 +264,14 @@ void ExtensionSessionsTest::CreateSessionModels() {
sync_pb::EntitySpecifics entity;
entity.mutable_session()->CopyFrom(meta);
initial_data.push_back(syncer::SyncData::CreateRemoteData(
- 1, entity, base::Time(), syncer::AttachmentIdList(),
- syncer::AttachmentServiceProxyForTest::Create(),
+ 1, entity, base::Time(),
sync_sessions::SessionsSyncManager::TagHashFromSpecifics(
entity.session())));
for (size_t i = 0; i < tabs.size(); i++) {
sync_pb::EntitySpecifics entity;
entity.mutable_session()->CopyFrom(tabs[i]);
initial_data.push_back(syncer::SyncData::CreateRemoteData(
- i + 2, entity, base::Time(), syncer::AttachmentIdList(),
- syncer::AttachmentServiceProxyForTest::Create(),
+ i + 2, entity, base::Time(),
sync_sessions::SessionsSyncManager::TagHashFromSpecifics(
entity.session())));
}
@@ -329,7 +325,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionSessionsTest,
std::unique_ptr<base::DictionaryValue> restored_window_session(
utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
CreateFunction<SessionsRestoreFunction>(true).get(), "[\"tag3.3\"]",
- browser_, utils::INCLUDE_INCOGNITO)));
+ browser_, api_test_utils::INCLUDE_INCOGNITO)));
ASSERT_TRUE(restored_window_session);
std::unique_ptr<base::ListValue> result(
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 12188832837..ea8c4d19412 100644
--- a/chromium/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chromium/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -284,6 +284,8 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetWhitelistedKeys() {
settings_api::PrefType::PREF_TYPE_NUMBER;
(*s_whitelist)[ash::prefs::kAccessibilityScreenMagnifierEnabled] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
+ (*s_whitelist)[ash::prefs::kAccessibilityScreenMagnifierScale] =
+ settings_api::PrefType::PREF_TYPE_NUMBER;
(*s_whitelist)[ash::prefs::kAccessibilitySelectToSpeakEnabled] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
(*s_whitelist)[ash::prefs::kAccessibilityStickyKeysEnabled] =
@@ -364,6 +366,10 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetWhitelistedKeys() {
settings_api::PrefType::PREF_TYPE_NUMBER;
(*s_whitelist)[ash::prefs::kNightLightCustomEndTime] =
settings_api::PrefType::PREF_TYPE_NUMBER;
+ (*s_whitelist)[ash::prefs::kDockedMagnifierEnabled] =
+ settings_api::PrefType::PREF_TYPE_BOOLEAN;
+ (*s_whitelist)[ash::prefs::kDockedMagnifierScale] =
+ settings_api::PrefType::PREF_TYPE_NUMBER;
// Input method settings.
(*s_whitelist)[::prefs::kLanguagePreloadEngines] =
@@ -633,20 +639,24 @@ settings_private::SetPrefResult PrefsUtil::SetPref(const std::string& pref_name,
switch (pref->GetType()) {
case base::Value::Type::BOOLEAN:
- case base::Value::Type::DOUBLE:
case base::Value::Type::LIST:
case base::Value::Type::DICTIONARY:
pref_service->Set(pref_name, *value);
break;
- case base::Value::Type::INTEGER: {
- // In JS all numbers are doubles.
+ case base::Value::Type::DOUBLE:
+ case base::Value::Type::INTEGER:
+ // Explicitly set the double value or the integer value.
+ // Otherwise if the number is a whole number like 2.0, it will
+ // automatically be of type INTEGER causing type mismatches in
+ // PrefService::SetUserPrefValue for doubles, and vice versa.
double double_value;
if (!value->GetAsDouble(&double_value))
return settings_private::SetPrefResult::PREF_TYPE_MISMATCH;
-
- pref_service->SetInteger(pref_name, static_cast<int>(double_value));
+ if (pref->GetType() == base::Value::Type::DOUBLE)
+ pref_service->SetDouble(pref_name, double_value);
+ else
+ pref_service->SetInteger(pref_name, static_cast<int>(double_value));
break;
- }
case base::Value::Type::STRING: {
std::string string_value;
if (!value->GetAsString(&string_value))
diff --git a/chromium/chrome/browser/extensions/api/socket/udp_socket_unittest.cc b/chromium/chrome/browser/extensions/api/socket/udp_socket_unittest.cc
index 27fd02882f8..68b294cade7 100644
--- a/chromium/chrome/browser/extensions/api/socket/udp_socket_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/socket/udp_socket_unittest.cc
@@ -15,17 +15,37 @@
#include "base/single_thread_task_runner.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/extensions/extension_service_test_base.h"
#include "chrome/test/base/browser_with_test_window_test.h"
+#include "content/public/browser/storage_partition.h"
#include "extensions/browser/api/socket/udp_socket.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_address.h"
+#include "net/base/test_completion_callback.h"
+#include "services/network/network_context.h"
+#include "services/network/public/mojom/udp_socket.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace extensions {
-// UDPSocketUnitTest exists solely to make it easier to pass a specific
-// gtest_filter argument during development.
-class UDPSocketUnitTest : public BrowserWithTestWindowTest {
+class UDPSocketUnitTest : public extensions::ExtensionServiceTestBase {
+ protected:
+ // extensions::ExtensionServiceTestBase:
+ void SetUp() override { InitializeEmptyExtensionService(); }
+
+ std::unique_ptr<UDPSocket> CreateSocket() {
+ network::mojom::NetworkContext* network_context =
+ content::BrowserContext::GetDefaultStoragePartition(profile())
+ ->GetNetworkContext();
+ network::mojom::UDPSocketPtrInfo socket;
+ network::mojom::UDPSocketReceiverPtr receiver_ptr;
+ network::mojom::UDPSocketReceiverRequest receiver_request =
+ mojo::MakeRequest(&receiver_ptr);
+ network_context->CreateUDPSocket(mojo::MakeRequest(&socket),
+ std::move(receiver_ptr));
+ return std::make_unique<UDPSocket>(
+ std::move(socket), std::move(receiver_request), "abcdefghijklmnopqrst");
+ }
};
static void OnConnected(int result) {
@@ -40,8 +60,8 @@ static void OnCompleted(int bytes_read,
// Do nothing; don't care.
}
-static const char test_message[] = "$$TESTMESSAGETESTMESSAGETESTMESSAGETEST$$";
-static const int test_message_length = arraysize(test_message);
+static const char kTestMessage[] = "$$TESTMESSAGETESTMESSAGETESTMESSAGETEST$$";
+static const int kTestMessageLength = arraysize(kTestMessage);
net::AddressList CreateAddressList(const char* address_string, int port) {
net::IPAddress ip;
@@ -50,50 +70,69 @@ net::AddressList CreateAddressList(const char* address_string, int port) {
}
static void OnSendCompleted(int result) {
- EXPECT_EQ(test_message_length, result);
+ EXPECT_EQ(kTestMessageLength, result);
}
-TEST(UDPSocketUnitTest, TestUDPSocketRecvFrom) {
- base::MessageLoopForIO io_loop; // For RecvFrom to do its threaded work.
- UDPSocket socket("abcdefghijklmnopqrst");
+TEST_F(UDPSocketUnitTest, TestUDPSocketRecvFrom) {
+ std::unique_ptr<UDPSocket> socket = CreateSocket();
// Confirm that we can call two RecvFroms in quick succession without
// triggering crbug.com/146606.
- socket.Connect(CreateAddressList("127.0.0.1", 40000),
- base::Bind(&OnConnected));
- socket.RecvFrom(4096, base::Bind(&OnCompleted));
- socket.RecvFrom(4096, base::Bind(&OnCompleted));
+ socket->Connect(CreateAddressList("127.0.0.1", 40000),
+ base::BindRepeating(&OnConnected));
+ socket->RecvFrom(4096, base::BindRepeating(&OnCompleted));
+ socket->RecvFrom(4096, base::BindRepeating(&OnCompleted));
}
-TEST(UDPSocketUnitTest, TestUDPMulticastJoinGroup) {
+TEST_F(UDPSocketUnitTest, TestUDPMulticastJoinGroup) {
const char kGroup[] = "237.132.100.17";
- UDPSocket src("abcdefghijklmnopqrst");
- UDPSocket dest("abcdefghijklmnopqrst");
+ std::unique_ptr<UDPSocket> src = CreateSocket();
+ std::unique_ptr<UDPSocket> dest = CreateSocket();
- EXPECT_EQ(0, dest.Bind("0.0.0.0", 13333));
- EXPECT_EQ(0, dest.JoinGroup(kGroup));
- std::vector<std::string> groups = dest.GetJoinedGroups();
+ {
+ net::TestCompletionCallback callback;
+ dest->Bind("0.0.0.0", 13333, callback.callback());
+ EXPECT_EQ(net::OK, callback.WaitForResult());
+ }
+ {
+ net::TestCompletionCallback callback;
+ dest->JoinGroup(kGroup, callback.callback());
+ EXPECT_EQ(net::OK, callback.WaitForResult());
+ }
+ std::vector<std::string> groups = dest->GetJoinedGroups();
EXPECT_EQ(static_cast<size_t>(1), groups.size());
EXPECT_EQ(kGroup, *groups.begin());
- EXPECT_NE(0, dest.LeaveGroup("237.132.100.13"));
- EXPECT_EQ(0, dest.LeaveGroup(kGroup));
- groups = dest.GetJoinedGroups();
+ {
+ net::TestCompletionCallback callback;
+ dest->LeaveGroup("237.132.100.13", callback.callback());
+ EXPECT_NE(net::OK, callback.WaitForResult());
+ }
+ {
+ net::TestCompletionCallback callback;
+ dest->LeaveGroup(kGroup, callback.callback());
+ EXPECT_EQ(net::OK, callback.WaitForResult());
+ }
+ groups = dest->GetJoinedGroups();
EXPECT_EQ(static_cast<size_t>(0), groups.size());
}
-TEST(UDPSocketUnitTest, TestUDPMulticastTimeToLive) {
+TEST_F(UDPSocketUnitTest, TestUDPMulticastTimeToLive) {
const char kGroup[] = "237.132.100.17";
- UDPSocket socket("abcdefghijklmnopqrst");
- EXPECT_NE(0, socket.SetMulticastTimeToLive(-1)); // Negative TTL shall fail.
- EXPECT_EQ(0, socket.SetMulticastTimeToLive(3));
- socket.Connect(CreateAddressList(kGroup, 13333), base::Bind(&OnConnected));
+ std::unique_ptr<UDPSocket> socket = CreateSocket();
+
+ EXPECT_NE(0, socket->SetMulticastTimeToLive(-1)); // Negative TTL shall fail.
+ EXPECT_EQ(0, socket->SetMulticastTimeToLive(3));
+ socket->Connect(CreateAddressList(kGroup, 13333),
+ base::BindRepeating(&OnConnected));
}
-TEST(UDPSocketUnitTest, TestUDPMulticastLoopbackMode) {
+TEST_F(UDPSocketUnitTest, TestUDPMulticastLoopbackMode) {
const char kGroup[] = "237.132.100.17";
- UDPSocket socket("abcdefghijklmnopqrst");
- EXPECT_EQ(0, socket.SetMulticastLoopbackMode(false));
- socket.Connect(CreateAddressList(kGroup, 13333), base::Bind(&OnConnected));
+ std::unique_ptr<UDPSocket> socket = CreateSocket();
+
+ EXPECT_EQ(0, socket->SetMulticastLoopbackMode(false));
+ socket->Connect(CreateAddressList(kGroup, 13333),
+ base::BindRepeating(&OnConnected));
}
// Send a test multicast packet every second.
@@ -102,8 +141,8 @@ static void SendMulticastPacket(const base::Closure& quit_run_loop,
UDPSocket* src,
int result) {
if (result == 0) {
- scoped_refptr<net::IOBuffer> data = new net::WrappedIOBuffer(test_message);
- src->Write(data, test_message_length, base::Bind(&OnSendCompleted));
+ scoped_refptr<net::IOBuffer> data = new net::WrappedIOBuffer(kTestMessage);
+ src->Write(data, kTestMessageLength, base::BindRepeating(&OnSendCompleted));
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&SendMulticastPacket, quit_run_loop, src, result),
@@ -118,37 +157,48 @@ static void OnMulticastReadCompleted(const base::Closure& quit_run_loop,
bool* packet_received,
int count,
scoped_refptr<net::IOBuffer> io_buffer,
- bool socket_destroying) {
- EXPECT_EQ(test_message_length, count);
- EXPECT_EQ(0, strncmp(io_buffer->data(), test_message, test_message_length));
+ bool socket_destroying,
+ const std::string& ip,
+ uint16_t port) {
+ EXPECT_EQ(kTestMessageLength, count);
+ EXPECT_EQ(0, strncmp(io_buffer->data(), kTestMessage, kTestMessageLength));
*packet_received = true;
quit_run_loop.Run();
}
-TEST(UDPSocketUnitTest, TestUDPMulticastRecv) {
+TEST_F(UDPSocketUnitTest, TestUDPMulticastRecv) {
const int kPort = 9999;
const char kGroup[] = "237.132.100.17";
bool packet_received = false;
- base::MessageLoopForIO io_loop; // For Read to do its threaded work.
- UDPSocket dest("abcdefghijklmnopqrst");
- UDPSocket src("abcdefghijklmnopqrst");
-
- base::RunLoop run_loop;
+ std::unique_ptr<UDPSocket> src = CreateSocket();
+ std::unique_ptr<UDPSocket> dest = CreateSocket();
// Receiver
- EXPECT_EQ(0, dest.Bind("0.0.0.0", kPort));
- EXPECT_EQ(0, dest.JoinGroup(kGroup));
- dest.Read(1024, base::Bind(&OnMulticastReadCompleted, run_loop.QuitClosure(),
- &packet_received));
+ {
+ net::TestCompletionCallback callback;
+ dest->Bind("0.0.0.0", kPort, callback.callback());
+ EXPECT_EQ(net::OK, callback.WaitForResult());
+ }
+ {
+ net::TestCompletionCallback callback;
+ dest->JoinGroup(kGroup, callback.callback());
+ EXPECT_EQ(net::OK, callback.WaitForResult());
+ }
+ base::RunLoop run_loop;
+ // |dest| is used with Bind(), so use RecvFrom() instead of Read().
+ dest->RecvFrom(1024,
+ base::BindRepeating(&OnMulticastReadCompleted,
+ run_loop.QuitClosure(), &packet_received));
// Sender
- EXPECT_EQ(0, src.SetMulticastTimeToLive(0));
- src.Connect(CreateAddressList(kGroup, kPort),
- base::Bind(&SendMulticastPacket, run_loop.QuitClosure(), &src));
+ EXPECT_EQ(0, src->SetMulticastTimeToLive(0));
+ src->Connect(CreateAddressList(kGroup, kPort),
+ base::BindRepeating(&SendMulticastPacket, run_loop.QuitClosure(),
+ src.get()));
// If not received within the test action timeout, quit the message loop.
- io_loop.task_runner()->PostDelayedTask(FROM_HERE, run_loop.QuitClosure(),
- TestTimeouts::action_timeout());
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, run_loop.QuitClosure(), TestTimeouts::action_timeout());
run_loop.Run();
diff --git a/chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.cc b/chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.cc
index 8997b290ed0..2e38a78801c 100644
--- a/chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.cc
+++ b/chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.cc
@@ -173,9 +173,8 @@ void ManagedValueStoreCache::ExtensionTracker::LoadSchemas(
}
GetExtensionFileTaskRunner()->PostTask(
- FROM_HERE,
- base::BindOnce(&ExtensionTracker::LoadSchemasOnFileTaskRunner,
- base::Passed(&added), weak_factory_.GetWeakPtr()));
+ FROM_HERE, base::BindOnce(&ExtensionTracker::LoadSchemasOnFileTaskRunner,
+ std::move(added), weak_factory_.GetWeakPtr()));
}
bool ManagedValueStoreCache::ExtensionTracker::UsesManagedStorage(
@@ -327,7 +326,7 @@ void ManagedValueStoreCache::OnPolicyUpdated(const policy::PolicyNamespace& ns,
GetBackendTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&ManagedValueStoreCache::UpdatePolicyOnBackend,
base::Unretained(this), ns.component_id,
- base::Passed(current.DeepCopy())));
+ current.DeepCopy()));
}
// static
diff --git a/chromium/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc b/chromium/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc
index b62b194dcff..7cc6951d468 100644
--- a/chromium/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc
@@ -16,9 +16,10 @@
#include "chrome/common/extensions/api/streams_private.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/ui_test_utils.h"
+#include "components/download/public/common/download_item.h"
#include "components/prefs/pref_service.h"
-#include "content/public/browser/download_item.h"
#include "content/public/browser/download_manager.h"
+#include "content/public/browser/download_request_utils.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/download_test_observer.h"
@@ -35,10 +36,10 @@
using content::BrowserContext;
using content::BrowserThread;
-using content::DownloadItem;
using content::DownloadManager;
-using content::DownloadUrlParameters;
using content::WebContents;
+using download::DownloadItem;
+using download::DownloadUrlParameters;
using extensions::Event;
using extensions::ExtensionSystem;
using extensions::ResultCatcher;
@@ -399,7 +400,7 @@ IN_PROC_BROWSER_TEST_F(StreamsPrivateApiTest, MAYBE_DirectDownload) {
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(web_contents);
std::unique_ptr<DownloadUrlParameters> params(
- DownloadUrlParameters::CreateForWebContentsMainFrame(
+ content::DownloadRequestUtils::CreateDownloadForWebContentsMainFrame(
web_contents, url, TRAFFIC_ANNOTATION_FOR_TESTS));
params->set_file_path(target_path);
diff --git a/chromium/chrome/browser/extensions/api/sync_file_system/sync_file_system_api.h b/chromium/chrome/browser/extensions/api/sync_file_system/sync_file_system_api.h
index cb4ad6f0b90..3e792d1bf2c 100644
--- a/chromium/chrome/browser/extensions/api/sync_file_system/sync_file_system_api.h
+++ b/chromium/chrome/browser/extensions/api/sync_file_system/sync_file_system_api.h
@@ -15,7 +15,7 @@
#include "chrome/browser/sync_file_system/sync_status_code.h"
#include "chrome/common/extensions/api/sync_file_system.h"
#include "storage/browser/fileapi/file_system_url.h"
-#include "third_party/WebKit/common/quota/quota_types.mojom.h"
+#include "third_party/WebKit/public/mojom/quota/quota_types.mojom.h"
namespace storage {
class FileSystemContext;
diff --git a/chromium/chrome/browser/extensions/api/sync_file_system/sync_file_system_browsertest.cc b/chromium/chrome/browser/extensions/api/sync_file_system/sync_file_system_browsertest.cc
index 4a19a726f23..2ff9bc7e96f 100644
--- a/chromium/chrome/browser/extensions/api/sync_file_system/sync_file_system_browsertest.cc
+++ b/chromium/chrome/browser/extensions/api/sync_file_system/sync_file_system_browsertest.cc
@@ -10,7 +10,6 @@
#include "base/run_loop.h"
#include "base/sequenced_task_runner.h"
#include "base/task_scheduler/post_task.h"
-#include "base/threading/sequenced_worker_pool.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "chrome/browser/apps/app_browsertest_util.h"
diff --git a/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc b/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
index 44d733eacea..520f0c136d0 100644
--- a/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
@@ -59,6 +59,7 @@ class TabCaptureApiTest : public ExtensionApiTest {
class TabCaptureApiPixelTest : public TabCaptureApiTest {
public:
void SetUp() override {
+ // TODO(crbug/754872): Update this to match WCVCD content_browsertests.
if (!IsTooIntensiveForThisPlatform())
EnablePixelOutput();
TabCaptureApiTest::SetUp();
@@ -178,8 +179,10 @@ IN_PROC_BROWSER_TEST_F(TabCaptureApiPixelTest, EndToEndWithoutRemoting) {
return;
}
AddExtensionToCommandLineWhitelist();
+ // TODO(crbug/758057): Determine why color accuracy went down in this test
+ // with the new VIZ-based tab capturer.
ASSERT_TRUE(RunExtensionSubtest(
- "tab_capture", "end_to_end.html?method=local&colorDeviation=10"))
+ "tab_capture", "end_to_end.html?method=local&colorDeviation=50"))
<< message_;
}
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 e1984cfcaaa..38e56a88a20 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
@@ -42,15 +42,9 @@ constexpr size_t kTrimEvents = 24; // 1 sec at 24fps, or 0.4 sec at 60 fps.
constexpr size_t kMinDataPoints = 100; // ~5 sec at 24fps.
enum TestFlags {
- // TODO(miu): Remove kUseGpu (since the GPU is required), kForceGpuComposited
- // (because there's no longer a such thing as Chrome w/o a compositor), and
- // maybe kDisableVsync. http://crbug.com/567848
kUseGpu = 1 << 0, // Only execute test if --enable-gpu was given
// on the command line. This is required for
// tests that run on GPU.
- kForceGpuComposited = 1 << 1, // Force the test to use the compositor.
- kDisableVsync = 1 << 2, // Do not limit framerate to vertical refresh.
- // when on GPU, nor to 60hz when not on GPU.
kTestThroughWebRTC = 1 << 3, // Send video through a webrtc loopback.
kSmallWindow = 1 << 4, // Window size: 1 = 800x600, 0 = 2000x1000
};
@@ -71,12 +65,8 @@ class TabCapturePerformanceTest
std::string GetSuffixForTestFlags() {
std::string suffix;
- if (HasFlag(kForceGpuComposited))
- suffix += "_comp";
if (HasFlag(kUseGpu))
- suffix += "_gpu";
- if (HasFlag(kDisableVsync))
- suffix += "_novsync";
+ suffix += "_comp_gpu";
if (HasFlag(kTestThroughWebRTC))
suffix += "_webrtc";
if (HasFlag(kSmallWindow))
@@ -86,6 +76,8 @@ class TabCapturePerformanceTest
void SetUp() override {
EnablePixelOutput();
+ if (!HasFlag(kUseGpu))
+ UseSoftwareCompositing();
ExtensionApiTest::SetUp();
}
@@ -105,12 +97,10 @@ class TabCapturePerformanceTest
if (!HasFlag(kUseGpu))
command_line->AppendSwitch(switches::kDisableGpu);
- if (HasFlag(kDisableVsync))
- command_line->AppendSwitch(switches::kDisableGpuVsync);
-
command_line->AppendSwitchASCII(
extensions::switches::kWhitelistedExtensionID,
kExtensionId);
+
ExtensionApiTest::SetUpCommandLine(command_line);
}
@@ -258,9 +248,6 @@ class TabCapturePerformanceTest
ASSERT_TRUE(tracing::BeginTracing("gpu,gpu.capture"));
std::string page = "performance.html";
page += HasFlag(kTestThroughWebRTC) ? "?WebRTC=1" : "?WebRTC=0";
- // Ideally we'd like to run a higher capture rate when vsync is disabled,
- // but libjingle currently doesn't allow that.
- // page += HasFlag(kDisableVsync) ? "&fps=300" : "&fps=30";
page += "&fps=60";
ASSERT_TRUE(RunExtensionSubtest("tab_capture", page)) << message_;
ASSERT_TRUE(tracing::EndTracing(&json_events));
@@ -303,15 +290,9 @@ IN_PROC_BROWSER_TEST_P(TabCapturePerformanceTest, Performance) {
// Note: First argument is optional and intentionally left blank.
// (it's a prefix for the generated test cases)
-INSTANTIATE_TEST_CASE_P(
- ,
- TabCapturePerformanceTest,
- testing::Values(
- 0,
- kUseGpu | kForceGpuComposited,
- kDisableVsync,
- kDisableVsync | kUseGpu | kForceGpuComposited,
- kTestThroughWebRTC,
- kTestThroughWebRTC | kUseGpu | kForceGpuComposited,
- kTestThroughWebRTC | kDisableVsync,
- kTestThroughWebRTC | kDisableVsync | kUseGpu | kForceGpuComposited));
+INSTANTIATE_TEST_CASE_P(,
+ TabCapturePerformanceTest,
+ testing::Values(0,
+ kUseGpu,
+ kTestThroughWebRTC,
+ kTestThroughWebRTC | kUseGpu));
diff --git a/chromium/chrome/browser/extensions/api/tabs/app_window_controller.cc b/chromium/chrome/browser/extensions/api/tabs/app_window_controller.cc
index 3d0bd3e606b..458fc5bdf09 100644
--- a/chromium/chrome/browser/extensions/api/tabs/app_window_controller.cc
+++ b/chromium/chrome/browser/extensions/api/tabs/app_window_controller.cc
@@ -7,14 +7,11 @@
#include <memory>
#include <utility>
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
#include "chrome/browser/extensions/api/tabs/app_base_window.h"
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/extensions/window_controller.h"
#include "chrome/browser/extensions/window_controller_list.h"
-#include "chrome/browser/sessions/session_tab_helper.h"
#include "chrome/common/url_constants.h"
#include "extensions/browser/app_window/app_window.h"
#include "extensions/browser/app_window/native_app_window.h"
@@ -46,62 +43,6 @@ std::string AppWindowController::GetWindowTypeText() const {
return tabs_constants::kWindowTypeValueApp;
}
-std::unique_ptr<base::DictionaryValue>
-AppWindowController::CreateWindowValueWithTabs(
- const Extension* extension) const {
- std::unique_ptr<base::DictionaryValue> result = CreateWindowValue();
-
- std::unique_ptr<base::DictionaryValue> tab_value =
- CreateTabObject(extension, 0)->ToValue();
- if (!tab_value)
- return result;
-
- auto tab_list = std::make_unique<base::ListValue>();
- tab_list->Append(std::move(tab_value));
- result->Set(tabs_constants::kTabsKey, std::move(tab_list));
-
- return result;
-}
-
-std::unique_ptr<api::tabs::Tab> AppWindowController::CreateTabObject(
- const extensions::Extension* extension,
- int tab_index) const {
- if (tab_index > 0)
- return nullptr;
-
- content::WebContents* web_contents = app_window_->web_contents();
- if (!web_contents)
- return nullptr;
-
- std::unique_ptr<api::tabs::Tab> tab_object(new api::tabs::Tab);
- tab_object->id.reset(new int(SessionTabHelper::IdForTab(web_contents)));
- tab_object->index = 0;
- tab_object->window_id =
- SessionTabHelper::IdForWindowContainingTab(web_contents);
- tab_object->url.reset(new std::string(web_contents->GetURL().spec()));
- tab_object->status.reset(new std::string(
- ExtensionTabUtil::GetTabStatusText(web_contents->IsLoading())));
- tab_object->active = app_window_->GetBaseWindow()->IsActive();
- tab_object->selected = true;
- tab_object->highlighted = true;
- tab_object->pinned = false;
- tab_object->title.reset(
- new std::string(base::UTF16ToUTF8(web_contents->GetTitle())));
- tab_object->incognito = app_window_->GetBaseWindow()->IsActive();
- gfx::Rect bounds = app_window_->GetBaseWindow()->GetBounds();
- tab_object->width.reset(new int(bounds.width()));
- tab_object->height.reset(new int(bounds.height()));
-
- const Extension* ext = app_window_->GetExtension();
- if (ext) {
- std::string icon_str(chrome::kChromeUIFaviconURL);
- icon_str.append(app_window_->GetExtension()->url().spec());
- tab_object->fav_icon_url.reset(new std::string(icon_str));
- }
-
- return tab_object;
-}
-
bool AppWindowController::CanClose(Reason* reason) const {
return true;
}
@@ -121,8 +62,9 @@ Browser* AppWindowController::GetBrowser() const {
return nullptr;
}
-bool AppWindowController::IsVisibleToExtension(
- const Extension* extension) const {
+bool AppWindowController::IsVisibleToTabsAPIForExtension(
+ const Extension* extension,
+ bool allow_dev_tools_windows) const {
DCHECK(extension);
return extension->id() == app_window_->extension_id();
}
diff --git a/chromium/chrome/browser/extensions/api/tabs/app_window_controller.h b/chromium/chrome/browser/extensions/api/tabs/app_window_controller.h
index 8b8511a7a65..a5fd5850302 100644
--- a/chromium/chrome/browser/extensions/api/tabs/app_window_controller.h
+++ b/chromium/chrome/browser/extensions/api/tabs/app_window_controller.h
@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_EXTENSIONS_API_TABS_APP_WINDOW_CONTROLLER_H_
#define CHROME_BROWSER_EXTENSIONS_API_TABS_APP_WINDOW_CONTROLLER_H_
+#include <memory>
#include <string>
#include "base/macros.h"
@@ -28,17 +29,13 @@ class AppWindowController : public WindowController {
// extensions::WindowController:
int GetWindowId() const override;
std::string GetWindowTypeText() const override;
- std::unique_ptr<base::DictionaryValue> CreateWindowValueWithTabs(
- const Extension* extension) const override;
- std::unique_ptr<api::tabs::Tab> CreateTabObject(
- const extensions::Extension* extension,
- int tab_index) const override;
-
bool CanClose(Reason* reason) const override;
void SetFullscreenMode(bool is_fullscreen,
const GURL& extension_url) const override;
Browser* GetBrowser() const override;
- bool IsVisibleToExtension(const Extension* extension) const override;
+ bool IsVisibleToTabsAPIForExtension(
+ const Extension* extension,
+ bool allow_dev_tools_windows) const override;
private:
AppWindow* app_window_; // Owns us.
diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc b/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc
index e802ba0057a..1376d685bf8 100644
--- a/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -95,11 +95,17 @@
#include "ui/base/ui_base_types.h"
#if defined(OS_CHROMEOS)
+#include "ash/public/cpp/config.h"
#include "ash/public/cpp/window_pin_type.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/public/interfaces/window_pin_type.mojom.h"
+#include "chrome/browser/chromeos/ash_config.h"
+#include "chrome/browser/ui/ash/chrome_screenshot_grabber.h"
#include "chrome/browser/ui/browser_command_controller.h"
+#include "content/public/browser/devtools_agent_host.h"
#include "ui/aura/window.h"
+#include "ui/base/clipboard/clipboard.h"
+#include "ui/base/clipboard/clipboard_types.h"
#endif
using content::BrowserThread;
@@ -256,19 +262,34 @@ bool IsValidStateForWindowsCreateFunction(
}
#if defined(OS_CHROMEOS)
-void SetWindowTrustedPinned(ui::BaseWindow* base_window, bool trusted_pinned) {
- aura::Window* window = base_window->GetNativeWindow();
+bool ExtensionHasLockedFullscreenPermission(const Extension* extension) {
+ return extension->permissions_data()->HasAPIPermission(
+ APIPermission::kLockWindowFullscreenPrivate);
+}
+
+void SetLockedFullscreenState(Browser* browser, bool locked) {
+ aura::Window* window = browser->window()->GetNativeWindow();
// TRUSTED_PINNED is used here because that one locks the window fullscreen
// without allowing the user to exit (as opposed to regular PINNED).
window->SetProperty(ash::kWindowPinTypeKey,
- trusted_pinned ? ash::mojom::WindowPinType::TRUSTED_PINNED
- : ash::mojom::WindowPinType::NONE);
-}
+ locked ? ash::mojom::WindowPinType::TRUSTED_PINNED
+ : ash::mojom::WindowPinType::NONE);
-bool ExtensionHasLockedFullscreenPermission(const Extension* extension) {
- return extension->permissions_data()->HasAPIPermission(
- APIPermission::kLockWindowFullscreenPrivate);
+ // Update the set of available browser commands.
+ browser->command_controller()->LockedFullscreenStateChanged();
+
+ // Disallow screenshots in locked fullscreen mode.
+ // TODO(isandrk, 816900): ChromeScreenshotGrabber isn't implemented in Mash
+ // yet, remove this conditional when it becomes available.
+ if (chromeos::GetAshConfig() != ash::Config::MASH)
+ ChromeScreenshotGrabber::Get()->set_screenshots_allowed(!locked);
+
+ // Reset the clipboard and kill dev tools when entering or exiting locked
+ // fullscreen (security concerns).
+ ui::Clipboard::GetForCurrentThread()->Clear(ui::CLIPBOARD_TYPE_COPY_PASTE);
+ content::DevToolsAgentHost::DetachAllClients();
}
+
#endif // defined(OS_CHROMEOS)
} // namespace
@@ -304,18 +325,20 @@ ExtensionFunction::ResponseAction WindowsGetFunction::Run() {
EXTENSION_FUNCTION_VALIDATE(params.get());
ApiParameterExtractor<windows::Get::Params> extractor(params.get());
- WindowController* controller = nullptr;
+ Browser* browser = nullptr;
std::string error;
- if (!windows_util::GetWindowFromWindowID(this, params->window_id,
- extractor.type_filters(),
- &controller, &error)) {
+ if (!windows_util::GetBrowserFromWindowID(this, params->window_id,
+ extractor.type_filters(), &browser,
+ &error)) {
return RespondNow(Error(error));
}
+ ExtensionTabUtil::PopulateTabBehavior populate_tab_behavior =
+ extractor.populate_tabs() ? ExtensionTabUtil::kPopulateTabs
+ : ExtensionTabUtil::kDontPopulateTabs;
std::unique_ptr<base::DictionaryValue> windows =
- extractor.populate_tabs()
- ? controller->CreateWindowValueWithTabs(extension())
- : controller->CreateWindowValue();
+ ExtensionTabUtil::CreateWindowValueForExtension(*browser, extension(),
+ populate_tab_behavior);
return RespondNow(OneArgument(std::move(windows)));
}
@@ -325,18 +348,20 @@ ExtensionFunction::ResponseAction WindowsGetCurrentFunction::Run() {
EXTENSION_FUNCTION_VALIDATE(params.get());
ApiParameterExtractor<windows::GetCurrent::Params> extractor(params.get());
- WindowController* controller = nullptr;
+ Browser* browser = nullptr;
std::string error;
- if (!windows_util::GetWindowFromWindowID(
+ if (!windows_util::GetBrowserFromWindowID(
this, extension_misc::kCurrentWindowId, extractor.type_filters(),
- &controller, &error)) {
+ &browser, &error)) {
return RespondNow(Error(error));
}
+ ExtensionTabUtil::PopulateTabBehavior populate_tab_behavior =
+ extractor.populate_tabs() ? ExtensionTabUtil::kPopulateTabs
+ : ExtensionTabUtil::kDontPopulateTabs;
std::unique_ptr<base::DictionaryValue> windows =
- extractor.populate_tabs()
- ? controller->CreateWindowValueWithTabs(extension())
- : controller->CreateWindowValue();
+ ExtensionTabUtil::CreateWindowValueForExtension(*browser, extension(),
+ populate_tab_behavior);
return RespondNow(OneArgument(std::move(windows)));
}
@@ -349,22 +374,28 @@ ExtensionFunction::ResponseAction WindowsGetLastFocusedFunction::Run() {
params.get());
// The WindowControllerList should contain a list of application,
// browser and devtools windows.
- WindowController* controller = nullptr;
- for (auto* iter : WindowControllerList::GetInstance()->windows()) {
- if (windows_util::CanOperateOnWindow(this, iter,
+ Browser* browser = nullptr;
+ for (auto* controller : WindowControllerList::GetInstance()->windows()) {
+ if (controller->GetBrowser() &&
+ windows_util::CanOperateOnWindow(this, controller,
extractor.type_filters())) {
- controller = iter;
+ // TODO(devlin): Doesn't this mean that we'll use the last window in the
+ // list if there is no active window? That seems wrong.
+ // See https://crbug.com/809822.
+ browser = controller->GetBrowser();
if (controller->window()->IsActive())
break; // Use focused window.
}
}
- if (!controller)
+ if (!browser)
return RespondNow(Error(keys::kNoLastFocusedWindowError));
+ ExtensionTabUtil::PopulateTabBehavior populate_tab_behavior =
+ extractor.populate_tabs() ? ExtensionTabUtil::kPopulateTabs
+ : ExtensionTabUtil::kDontPopulateTabs;
std::unique_ptr<base::DictionaryValue> windows =
- extractor.populate_tabs()
- ? controller->CreateWindowValueWithTabs(extension())
- : controller->CreateWindowValue();
+ ExtensionTabUtil::CreateWindowValueForExtension(*browser, extension(),
+ populate_tab_behavior);
return RespondNow(OneArgument(std::move(windows)));
}
@@ -375,18 +406,17 @@ ExtensionFunction::ResponseAction WindowsGetAllFunction::Run() {
ApiParameterExtractor<windows::GetAll::Params> extractor(params.get());
std::unique_ptr<base::ListValue> window_list(new base::ListValue());
- const WindowControllerList::ControllerList& windows =
- WindowControllerList::GetInstance()->windows();
- for (WindowControllerList::ControllerList::const_iterator iter =
- windows.begin();
- iter != windows.end(); ++iter) {
- if (!windows_util::CanOperateOnWindow(this, *iter,
- extractor.type_filters()))
+ ExtensionTabUtil::PopulateTabBehavior populate_tab_behavior =
+ extractor.populate_tabs() ? ExtensionTabUtil::kPopulateTabs
+ : ExtensionTabUtil::kDontPopulateTabs;
+ for (auto* controller : WindowControllerList::GetInstance()->windows()) {
+ if (!controller->GetBrowser() ||
+ !windows_util::CanOperateOnWindow(this, controller,
+ extractor.type_filters())) {
continue;
- if (extractor.populate_tabs())
- window_list->Append((*iter)->CreateWindowValueWithTabs(extension()));
- else
- window_list->Append((*iter)->CreateWindowValue());
+ }
+ window_list->Append(ExtensionTabUtil::CreateWindowValueForExtension(
+ *controller->GetBrowser(), extension(), populate_tab_behavior));
}
return RespondNow(OneArgument(std::move(window_list)));
@@ -639,8 +669,7 @@ ExtensionFunction::ResponseAction WindowsCreateFunction::Run() {
// (otherwise the tabstrip is empty).
if (create_data &&
create_data->state == windows::WINDOW_STATE_LOCKED_FULLSCREEN) {
- SetWindowTrustedPinned(new_window->window(), true);
- new_window->command_controller()->LockedFullscreenStateChanged();
+ SetLockedFullscreenState(new_window, true);
}
#endif
@@ -649,8 +678,6 @@ ExtensionFunction::ResponseAction WindowsCreateFunction::Run() {
else
new_window->window()->ShowInactive();
- WindowController* controller = new_window->extension_window_controller();
-
std::unique_ptr<base::Value> result;
if (new_window->profile()->IsOffTheRecord() &&
!browser_context()->IsOffTheRecord() && !include_incognito()) {
@@ -658,7 +685,8 @@ ExtensionFunction::ResponseAction WindowsCreateFunction::Run() {
// profile and CanCrossIncognito isn't allowed.
result = std::make_unique<base::Value>();
} else {
- result = controller->CreateWindowValueWithTabs(extension());
+ result = ExtensionTabUtil::CreateWindowValueForExtension(
+ *new_window, extension(), ExtensionTabUtil::kPopulateTabs);
}
return RespondNow(OneArgument(std::move(result)));
@@ -669,11 +697,11 @@ ExtensionFunction::ResponseAction WindowsUpdateFunction::Run() {
windows::Update::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
- WindowController* controller;
+ Browser* browser = nullptr;
std::string error;
- if (!windows_util::GetWindowFromWindowID(
+ if (!windows_util::GetBrowserFromWindowID(
this, params->window_id, WindowController::GetAllWindowFilter(),
- &controller, &error)) {
+ &browser, &error)) {
return RespondNow(Error(error));
}
@@ -683,7 +711,7 @@ ExtensionFunction::ResponseAction WindowsUpdateFunction::Run() {
#if defined(OS_CHROMEOS)
const bool is_window_trusted_pinned =
- ash::IsWindowTrustedPinned(controller->window());
+ ash::IsWindowTrustedPinned(browser->window());
// Don't allow locked fullscreen operations on a window without the proper
// permission (also don't allow any operations on a locked window if the
// extension doesn't have the permission).
@@ -698,15 +726,11 @@ ExtensionFunction::ResponseAction WindowsUpdateFunction::Run() {
if (is_window_trusted_pinned &&
params->update_info.state != windows::WINDOW_STATE_LOCKED_FULLSCREEN &&
params->update_info.state != windows::WINDOW_STATE_NONE) {
- SetWindowTrustedPinned(controller->window(), false);
- controller->GetBrowser()->command_controller()->
- LockedFullscreenStateChanged();
+ SetLockedFullscreenState(browser, false);
} else if (!is_window_trusted_pinned &&
params->update_info.state ==
windows::WINDOW_STATE_LOCKED_FULLSCREEN) {
- SetWindowTrustedPinned(controller->window(), true);
- controller->GetBrowser()->command_controller()->
- LockedFullscreenStateChanged();
+ SetLockedFullscreenState(browser, true);
}
#endif
@@ -714,34 +738,38 @@ ExtensionFunction::ResponseAction WindowsUpdateFunction::Run() {
ConvertToWindowShowState(params->update_info.state);
if (show_state != ui::SHOW_STATE_FULLSCREEN &&
- show_state != ui::SHOW_STATE_DEFAULT)
- controller->SetFullscreenMode(false, extension()->url());
+ show_state != ui::SHOW_STATE_DEFAULT) {
+ browser->extension_window_controller()->SetFullscreenMode(
+ false, extension()->url());
+ }
switch (show_state) {
case ui::SHOW_STATE_MINIMIZED:
- controller->window()->Minimize();
+ browser->window()->Minimize();
break;
case ui::SHOW_STATE_MAXIMIZED:
- controller->window()->Maximize();
+ browser->window()->Maximize();
break;
case ui::SHOW_STATE_FULLSCREEN:
- if (controller->window()->IsMinimized() ||
- controller->window()->IsMaximized())
- controller->window()->Restore();
- controller->SetFullscreenMode(true, extension()->url());
+ if (browser->window()->IsMinimized() ||
+ browser->window()->IsMaximized()) {
+ browser->window()->Restore();
+ }
+ browser->extension_window_controller()->SetFullscreenMode(
+ true, extension()->url());
break;
case ui::SHOW_STATE_NORMAL:
- controller->window()->Restore();
+ browser->window()->Restore();
break;
default:
break;
}
gfx::Rect bounds;
- if (controller->window()->IsMinimized())
- bounds = controller->window()->GetRestoredBounds();
+ if (browser->window()->IsMinimized())
+ bounds = browser->window()->GetRestoredBounds();
else
- bounds = controller->window()->GetBounds();
+ bounds = browser->window()->GetBounds();
bool set_bounds = false;
// Any part of the bounds can optionally be set by the caller.
@@ -773,27 +801,28 @@ ExtensionFunction::ResponseAction WindowsUpdateFunction::Run() {
}
// TODO(varkha): Updating bounds during a drag can cause problems and a more
// general solution is needed. See http://crbug.com/251813 .
- controller->window()->SetBounds(bounds);
+ browser->window()->SetBounds(bounds);
}
if (params->update_info.focused) {
if (*params->update_info.focused) {
if (show_state == ui::SHOW_STATE_MINIMIZED)
return RespondNow(Error(keys::kInvalidWindowStateError));
- controller->window()->Activate();
+ browser->window()->Activate();
} else {
if (show_state == ui::SHOW_STATE_MAXIMIZED ||
show_state == ui::SHOW_STATE_FULLSCREEN) {
return RespondNow(Error(keys::kInvalidWindowStateError));
}
- controller->window()->Deactivate();
+ browser->window()->Deactivate();
}
}
if (params->update_info.draw_attention)
- controller->window()->FlashFrame(*params->update_info.draw_attention);
+ browser->window()->FlashFrame(*params->update_info.draw_attention);
- return RespondNow(OneArgument(controller->CreateWindowValue()));
+ return RespondNow(OneArgument(ExtensionTabUtil::CreateWindowValueForExtension(
+ *browser, extension(), ExtensionTabUtil::kDontPopulateTabs)));
}
ExtensionFunction::ResponseAction WindowsRemoveFunction::Run() {
@@ -801,22 +830,23 @@ ExtensionFunction::ResponseAction WindowsRemoveFunction::Run() {
windows::Remove::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
- WindowController* controller = nullptr;
+ Browser* browser = nullptr;
std::string error;
- if (!windows_util::GetWindowFromWindowID(this, params->window_id,
- WindowController::kNoWindowFilter,
- &controller, &error)) {
+ if (!windows_util::GetBrowserFromWindowID(this, params->window_id,
+ WindowController::kNoWindowFilter,
+ &browser, &error)) {
return RespondNow(Error(error));
}
#if defined(OS_CHROMEOS)
- if (ash::IsWindowTrustedPinned(controller->window()) &&
+ if (ash::IsWindowTrustedPinned(browser->window()) &&
!ExtensionHasLockedFullscreenPermission(extension())) {
return RespondNow(
Error(keys::kMissingLockWindowFullscreenPrivatePermission));
}
#endif
+ WindowController* controller = browser->extension_window_controller();
WindowController::Reason reason;
if (!controller->CanClose(&reason)) {
return RespondNow(Error(reason == WindowController::REASON_NOT_EDITABLE
@@ -850,7 +880,8 @@ ExtensionFunction::ResponseAction TabsGetSelectedFunction::Run() {
return RespondNow(Error(keys::kNoSelectedTabError));
return RespondNow(ArgumentList(
tabs::Get::Results::Create(*ExtensionTabUtil::CreateTabObject(
- contents, tab_strip, tab_strip->active_index(), extension()))));
+ contents, ExtensionTabUtil::kScrubTab, extension(), tab_strip,
+ tab_strip->active_index()))));
}
ExtensionFunction::ResponseAction TabsGetAllInWindowFunction::Run() {
@@ -928,8 +959,8 @@ ExtensionFunction::ResponseAction TabsQueryFunction::Run() {
if (!include_incognito() && profile != browser->profile())
continue;
- if (!browser->extension_window_controller()->IsVisibleToExtension(
- extension())) {
+ if (!browser->extension_window_controller()->IsVisibleToTabsAPIForExtension(
+ extension(), false /*allow_dev_tools_windows*/)) {
continue;
}
@@ -1033,8 +1064,9 @@ ExtensionFunction::ResponseAction TabsQueryFunction::Run() {
if (loading_status_set && loading != web_contents->IsLoading())
continue;
- result->Append(ExtensionTabUtil::CreateTabObject(web_contents, tab_strip,
- i, extension())
+ result->Append(ExtensionTabUtil::CreateTabObject(
+ web_contents, ExtensionTabUtil::kScrubTab, extension(),
+ tab_strip, i)
->ToValue());
}
}
@@ -1101,7 +1133,8 @@ ExtensionFunction::ResponseAction TabsDuplicateFunction::Run() {
return RespondNow(ArgumentList(
tabs::Get::Results::Create(*ExtensionTabUtil::CreateTabObject(
- new_contents, new_tab_strip, new_tab_index, extension()))));
+ new_contents, ExtensionTabUtil::kScrubTab, extension(), new_tab_strip,
+ new_tab_index))));
}
ExtensionFunction::ResponseAction TabsGetFunction::Run() {
@@ -1118,9 +1151,9 @@ ExtensionFunction::ResponseAction TabsGetFunction::Run() {
return RespondNow(Error(error));
}
- return RespondNow(ArgumentList(
- tabs::Get::Results::Create(*ExtensionTabUtil::CreateTabObject(
- contents, tab_strip, tab_index, extension()))));
+ return RespondNow(ArgumentList(tabs::Get::Results::Create(
+ *ExtensionTabUtil::CreateTabObject(contents, ExtensionTabUtil::kScrubTab,
+ extension(), tab_strip, tab_index))));
}
ExtensionFunction::ResponseAction TabsGetCurrentFunction::Run() {
@@ -1131,8 +1164,8 @@ ExtensionFunction::ResponseAction TabsGetCurrentFunction::Run() {
WebContents* caller_contents = GetSenderWebContents();
std::unique_ptr<base::ListValue> results;
if (caller_contents && ExtensionTabUtil::GetTabId(caller_contents) >= 0) {
- results = tabs::Get::Results::Create(
- *ExtensionTabUtil::CreateTabObject(caller_contents, extension()));
+ results = tabs::Get::Results::Create(*ExtensionTabUtil::CreateTabObject(
+ caller_contents, ExtensionTabUtil::kScrubTab, extension()));
}
return RespondNow(results ? ArgumentList(std::move(results)) : NoArguments());
}
@@ -1179,9 +1212,8 @@ ExtensionFunction::ResponseAction TabsHighlightFunction::Run() {
selection.set_active(active_index);
browser->tab_strip_model()->SetSelectionFromModel(std::move(selection));
- return RespondNow(OneArgument(
- browser->extension_window_controller()->CreateWindowValueWithTabs(
- extension())));
+ return RespondNow(OneArgument(ExtensionTabUtil::CreateWindowValueForExtension(
+ *browser, extension(), ExtensionTabUtil::kPopulateTabs)));
}
bool TabsHighlightFunction::HighlightTab(TabStripModel* tabstrip,
@@ -1215,7 +1247,7 @@ bool TabsUpdateFunction::RunAsync() {
int tab_id = -1;
WebContents* contents = NULL;
if (!params->tab_id.get()) {
- Browser* browser = GetCurrentBrowser();
+ Browser* browser = ChromeExtensionFunctionDetails(this).GetCurrentBrowser();
if (!browser) {
error_ = keys::kNoCurrentWindowError;
return false;
@@ -1389,7 +1421,7 @@ bool TabsUpdateFunction::UpdateURL(const std::string &url_string,
ScriptExecutor::SINGLE_FRAME, ExtensionApiFrameIdMap::kTopFrameId,
ScriptExecutor::DONT_MATCH_ABOUT_BLANK, UserScript::DOCUMENT_IDLE,
ScriptExecutor::MAIN_WORLD, ScriptExecutor::DEFAULT_PROCESS, GURL(),
- GURL(), user_gesture(), ScriptExecutor::NO_RESULT,
+ GURL(), user_gesture(), base::nullopt, ScriptExecutor::NO_RESULT,
base::Bind(&TabsUpdateFunction::OnExecuteCodeFinished, this));
*is_async = true;
@@ -1423,8 +1455,8 @@ void TabsUpdateFunction::PopulateResult() {
if (!has_callback())
return;
- results_ = tabs::Get::Results::Create(
- *ExtensionTabUtil::CreateTabObject(web_contents_, extension()));
+ results_ = tabs::Get::Results::Create(*ExtensionTabUtil::CreateTabObject(
+ web_contents_, ExtensionTabUtil::kScrubTab, extension()));
}
void TabsUpdateFunction::OnExecuteCodeFinished(
@@ -1552,10 +1584,10 @@ bool TabsMoveFunction::MoveTab(int tab_id,
*new_index, web_contents, TabStripModel::ADD_NONE);
if (has_callback()) {
- tab_values->Append(
- ExtensionTabUtil::CreateTabObject(web_contents, target_tab_strip,
- *new_index, extension())
- ->ToValue());
+ tab_values->Append(ExtensionTabUtil::CreateTabObject(
+ web_contents, ExtensionTabUtil::kScrubTab,
+ extension(), target_tab_strip, *new_index)
+ ->ToValue());
}
return true;
@@ -1574,7 +1606,8 @@ bool TabsMoveFunction::MoveTab(int tab_id,
if (has_callback()) {
tab_values->Append(ExtensionTabUtil::CreateTabObject(
- contents, source_tab_strip, *new_index, extension())
+ contents, ExtensionTabUtil::kScrubTab, extension(),
+ source_tab_strip, *new_index)
->ToValue());
}
@@ -1682,10 +1715,9 @@ bool TabsCaptureVisibleTabFunction::HasPermission() {
return true;
}
-bool TabsCaptureVisibleTabFunction::IsScreenshotEnabled() {
+bool TabsCaptureVisibleTabFunction::IsScreenshotEnabled() const {
PrefService* service = chrome_details_.GetProfile()->GetPrefs();
if (service->GetBoolean(prefs::kDisableScreenshots)) {
- error_ = keys::kScreenshotsDisabled;
return false;
}
return true;
@@ -1730,10 +1762,14 @@ bool TabsCaptureVisibleTabFunction::RunAsync() {
WebContents* contents = GetWebContentsForID(context_id);
- return CaptureAsync(
+ const CaptureResult capture_result = CaptureAsync(
contents, image_details.get(),
- base::Bind(&TabsCaptureVisibleTabFunction::CopyFromSurfaceComplete,
- this));
+ base::BindOnce(&TabsCaptureVisibleTabFunction::CopyFromSurfaceComplete,
+ this));
+ if (capture_result == OK)
+ return true;
+ SetErrorMessage(capture_result);
+ return false;
}
void TabsCaptureVisibleTabFunction::OnCaptureSuccess(const SkBitmap& bitmap) {
@@ -1747,11 +1783,16 @@ void TabsCaptureVisibleTabFunction::OnCaptureSuccess(const SkBitmap& bitmap) {
SendResponse(true);
}
-void TabsCaptureVisibleTabFunction::OnCaptureFailure(FailureReason reason) {
+void TabsCaptureVisibleTabFunction::OnCaptureFailure(CaptureResult result) {
+ SetErrorMessage(result);
+ SendResponse(false);
+}
+
+void TabsCaptureVisibleTabFunction::SetErrorMessage(CaptureResult result) {
const char* reason_description = "internal error";
- switch (reason) {
- case FAILURE_REASON_UNKNOWN:
- reason_description = "unknown error";
+ switch (result) {
+ case FAILURE_REASON_READBACK_FAILED:
+ reason_description = "image readback failed";
break;
case FAILURE_REASON_ENCODING_FAILED:
reason_description = "encoding failed";
@@ -1759,10 +1800,16 @@ void TabsCaptureVisibleTabFunction::OnCaptureFailure(FailureReason reason) {
case FAILURE_REASON_VIEW_INVISIBLE:
reason_description = "view is invisible";
break;
+ case FAILURE_REASON_SCREEN_SHOTS_DISABLED:
+ error_ = keys::kScreenshotsDisabled;
+ return;
+ case OK:
+ NOTREACHED()
+ << "SetErrorMessage should not be called with a successful result";
+ return;
}
error_ = ErrorUtils::FormatErrorMessage("Failed to capture tab: *",
reason_description);
- SendResponse(false);
}
void TabsCaptureVisibleTabFunction::RegisterProfilePrefs(
@@ -1790,7 +1837,7 @@ bool TabsDetectLanguageFunction::RunAsync() {
if (!browser || !contents)
return false;
} else {
- browser = GetCurrentBrowser();
+ browser = ChromeExtensionFunctionDetails(this).GetCurrentBrowser();
if (!browser)
return false;
contents = browser->tab_strip_model()->GetActiveWebContents();
@@ -2020,7 +2067,7 @@ content::WebContents* ZoomAPIFunction::GetWebContents(int tab_id) {
nullptr /* ignore TabStripModel* output */, &web_contents,
nullptr /* ignore int tab_index output */, &error_);
} else {
- Browser* browser = GetCurrentBrowser();
+ Browser* browser = ChromeExtensionFunctionDetails(this).GetCurrentBrowser();
if (!browser)
error_ = keys::kNoCurrentWindowError;
else if (!ExtensionTabUtil::GetDefaultTab(browser, &web_contents, NULL))
@@ -2177,8 +2224,9 @@ ExtensionFunction::ResponseAction TabsDiscardFunction::Run() {
// Create the Tab object and return it in case of success.
if (contents) {
- return RespondNow(ArgumentList(tabs::Discard::Results::Create(
- *ExtensionTabUtil::CreateTabObject(contents))));
+ return RespondNow(ArgumentList(
+ tabs::Discard::Results::Create(*ExtensionTabUtil::CreateTabObject(
+ contents, ExtensionTabUtil::kScrubTab, extension()))));
}
// Return appropriate error message otherwise.
diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_api.h b/chromium/chrome/browser/extensions/api/tabs/tabs_api.h
index c8e4071ec5a..7fe5521cd41 100644
--- a/chromium/chrome/browser/extensions/api/tabs/tabs_api.h
+++ b/chromium/chrome/browser/extensions/api/tabs/tabs_api.h
@@ -215,10 +215,13 @@ class TabsCaptureVisibleTabFunction
content::WebContents* GetWebContentsForID(int window_id);
// extensions::WebContentsCaptureClient:
- bool IsScreenshotEnabled() override;
+ bool IsScreenshotEnabled() const override;
bool ClientAllowsTransparency() override;
void OnCaptureSuccess(const SkBitmap& bitmap) override;
- void OnCaptureFailure(FailureReason reason) override;
+ void OnCaptureFailure(CaptureResult result) override;
+
+ private:
+ void SetErrorMessage(CaptureResult result);
DECLARE_EXTENSION_FUNCTION("tabs.captureVisibleTab", TABS_CAPTUREVISIBLETAB)
};
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 d06d5513d00..220421eef7b 100644
--- a/chromium/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
@@ -15,12 +15,12 @@
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/test_browser_window.h"
#include "content/public/browser/navigation_entry.h"
-#include "content/public/common/browser_side_navigation_policy.h"
#include "content/public/test/browser_side_navigation_test_utils.h"
#include "content/public/test/web_contents_tester.h"
#include "extensions/browser/api_test_utils.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension_builder.h"
+#include "ui/display/test/test_screen.h"
namespace extensions {
@@ -34,8 +34,7 @@ std::unique_ptr<base::ListValue> RunTabsQueryFunction(
function->set_extension(extension);
std::unique_ptr<base::Value> value(
extension_function_test_utils::RunFunctionAndReturnSingleResult(
- function.get(), query_info, browser,
- extension_function_test_utils::NONE));
+ function.get(), query_info, browser, api_test_utils::NONE));
return base::ListValue::From(std::move(value));
}
@@ -57,28 +56,28 @@ class TabsApiUnitTest : public ExtensionServiceTestBase {
std::unique_ptr<TestBrowserWindow> browser_window_;
std::unique_ptr<Browser> browser_;
+ display::test::TestScreen test_screen_;
+
DISALLOW_COPY_AND_ASSIGN(TabsApiUnitTest);
};
void TabsApiUnitTest::SetUp() {
ExtensionServiceTestBase::SetUp();
InitializeEmptyExtensionService();
-
- if (content::IsBrowserSideNavigationEnabled())
- content::BrowserSideNavigationSetUp();
+ content::BrowserSideNavigationSetUp();
browser_window_.reset(new TestBrowserWindow());
Browser::CreateParams params(profile(), true);
params.type = Browser::TYPE_TABBED;
params.window = browser_window_.get();
browser_.reset(new Browser(params));
+ display::Screen::SetScreenInstance(&test_screen_);
}
void TabsApiUnitTest::TearDown() {
browser_.reset();
browser_window_.reset();
- if (content::IsBrowserSideNavigationEnabled())
- content::BrowserSideNavigationTearDown();
+ content::BrowserSideNavigationTearDown();
ExtensionServiceTestBase::TearDown();
}
@@ -293,7 +292,7 @@ TEST_F(TabsApiUnitTest, ExecuteScriptNoTabIsNonFatalError) {
std::string error = extension_function_test_utils::RunFunctionAndReturnError(
function.get(), kArgs,
browser(), // browser() doesn't have any tabs.
- extension_function_test_utils::NONE);
+ api_test_utils::NONE);
EXPECT_EQ(tabs_constants::kNoTabInBrowserWindowError, error);
}
diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_event_router.cc b/chromium/chrome/browser/extensions/api/tabs/tabs_event_router.cc
index 1ab063b1ea9..af63faf2661 100644
--- a/chromium/chrome/browser/extensions/api/tabs/tabs_event_router.cc
+++ b/chromium/chrome/browser/extensions/api/tabs/tabs_event_router.cc
@@ -45,7 +45,8 @@ bool WillDispatchTabUpdatedEvent(
Event* event,
const base::DictionaryValue* listener_filter) {
std::unique_ptr<api::tabs::Tab> tab_object =
- ExtensionTabUtil::CreateTabObject(contents, extension);
+ ExtensionTabUtil::CreateTabObject(contents, ExtensionTabUtil::kScrubTab,
+ extension);
std::unique_ptr<base::DictionaryValue> tab_value = tab_object->ToValue();
@@ -190,7 +191,9 @@ static bool WillDispatchTabCreatedEvent(
const base::DictionaryValue* listener_filter) {
event->event_args->Clear();
std::unique_ptr<base::DictionaryValue> tab_value =
- ExtensionTabUtil::CreateTabObject(contents, extension)->ToValue();
+ ExtensionTabUtil::CreateTabObject(contents, ExtensionTabUtil::kScrubTab,
+ extension)
+ ->ToValue();
tab_value->SetBoolean(tabs_constants::kSelectedKey, active);
tab_value->SetBoolean(tabs_constants::kActiveKey, active);
event->event_args->Append(std::move(tab_value));
diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_test.cc b/chromium/chrome/browser/extensions/api/tabs/tabs_test.cc
index 01abcd19bea..82fa8cfe3d3 100644
--- a/chromium/chrome/browser/extensions/api/tabs/tabs_test.cc
+++ b/chromium/chrome/browser/extensions/api/tabs/tabs_test.cc
@@ -225,12 +225,9 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, MAYBE_GetWindow) {
// With "include_incognito".
function = new WindowsGetFunction();
function->set_extension(extension.get());
- result.reset(utils::ToDictionary(
- utils::RunFunctionAndReturnSingleResult(
- function.get(),
- base::StringPrintf("[%u]", incognito_window_id),
- browser(),
- utils::INCLUDE_INCOGNITO)));
+ result.reset(utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
+ function.get(), base::StringPrintf("[%u]", incognito_window_id),
+ browser(), api_test_utils::INCLUDE_INCOGNITO)));
EXPECT_TRUE(api_test_utils::GetBoolean(result.get(), "incognito"));
// DevTools window.
@@ -244,7 +241,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, MAYBE_GetWindow) {
base::StringPrintf("[%u, {\"windowTypes\": [\"devtools\"]}]",
ExtensionTabUtil::GetWindowId(
DevToolsWindowTesting::Get(devtools)->browser())),
- browser(), utils::INCLUDE_INCOGNITO)));
+ browser(), api_test_utils::INCLUDE_INCOGNITO)));
EXPECT_EQ("devtools", api_test_utils::GetString(result.get(), "type"));
DevToolsWindowTesting::CloseDevToolsWindowSync(devtools);
@@ -301,12 +298,12 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetAllWindows) {
window_ids.insert(ExtensionTabUtil::GetWindowId(new_browser));
}
- // Application windows should not be accessible, unless allWindowTypes is set
- // to true.
+ // Application windows should not be accessible to extensions (app windows are
+ // only accessible to the owning item).
AppWindow* app_window = CreateTestAppWindow("{}");
- // Undocked DevTools window should not be accessible, unless allWindowTypes is
- // set to true.
+ // Undocked DevTools window should not be accessible, unless included in the
+ // type filter mask.
DevToolsWindow* devtools = DevToolsWindowTesting::OpenDevToolsWindowSync(
browser()->tab_strip_model()->GetWebContentsAt(0), false /* is_docked */);
@@ -370,11 +367,12 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetAllWindowsAllTypes) {
window_ids.insert(ExtensionTabUtil::GetWindowId(new_browser));
}
- // Application windows should be accessible.
+ // Application windows should not be accessible to extensions (app windows are
+ // only accessible to the owning item).
AppWindow* app_window = CreateTestAppWindow("{}");
- window_ids.insert(app_window->session_id().id());
- // Undocked DevTools window should be accessible too.
+ // Undocked DevTools window should be accessible too, since they have been
+ // explicitly requested as part of the type filter mask.
DevToolsWindow* devtools = DevToolsWindowTesting::OpenDevToolsWindowSync(
browser()->tab_strip_model()->GetWebContentsAt(0), false /* is_docked */);
window_ids.insert(ExtensionTabUtil::GetWindowId(
@@ -401,8 +399,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetAllWindowsAllTypes) {
base::ListValue* tabs = nullptr;
EXPECT_FALSE(result_window->GetList(keys::kTabsKey, &tabs));
}
- // The returned ids should contain all the current app, browser and
- // devtools instance ids.
+ // The returned ids should contain all the browser and devtools instance ids.
EXPECT_EQ(window_ids, result_ids);
result_ids.clear();
@@ -425,8 +422,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetAllWindowsAllTypes) {
base::ListValue* tabs = nullptr;
EXPECT_TRUE(result_window->GetList(keys::kTabsKey, &tabs));
}
- // The returned ids should contain all the current app, browser and
- // devtools instance ids.
+ // The returned ids should contain all the browser and devtools instance ids.
EXPECT_EQ(window_ids, result_ids);
DevToolsWindowTesting::CloseDevToolsWindowSync(devtools);
@@ -468,7 +464,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest,
std::string error = extension_function_test_utils::RunFunctionAndReturnError(
update_tab_function.get(), kArgsWithNonIncognitoUrl,
incognito, // incognito doesn't have any tabs.
- extension_function_test_utils::NONE);
+ api_test_utils::NONE);
EXPECT_EQ(ErrorUtils::FormatErrorMessage(
tabs_constants::kURLsNotAllowedInIncognitoError,
"chrome://extensions/configureCommands"),
@@ -496,7 +492,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, DefaultToIncognitoWhenItIsForced) {
std::unique_ptr<base::DictionaryValue> result(
utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
function.get(), kArgsWithoutExplicitIncognitoParam, browser(),
- utils::INCLUDE_INCOGNITO)));
+ api_test_utils::INCLUDE_INCOGNITO)));
// Make sure it is a new(different) window.
EXPECT_NE(ExtensionTabUtil::GetWindowId(browser()),
@@ -511,12 +507,9 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, DefaultToIncognitoWhenItIsForced) {
function->SetRenderFrameHost(
browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame());
function->set_extension(extension.get());
- result.reset(utils::ToDictionary(
- utils::RunFunctionAndReturnSingleResult(
- function.get(),
- kArgsWithoutExplicitIncognitoParam,
- incognito_browser,
- utils::INCLUDE_INCOGNITO)));
+ result.reset(utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
+ function.get(), kArgsWithoutExplicitIncognitoParam, incognito_browser,
+ api_test_utils::INCLUDE_INCOGNITO)));
// Make sure it is a new(different) window.
EXPECT_NE(ExtensionTabUtil::GetWindowId(incognito_browser),
GetWindowId(result.get()));
@@ -536,7 +529,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest,
function->set_extension(extension.get());
std::unique_ptr<base::DictionaryValue> result(
utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
- function.get(), kEmptyArgs, browser(), utils::INCLUDE_INCOGNITO)));
+ function.get(), kEmptyArgs, browser(),
+ api_test_utils::INCLUDE_INCOGNITO)));
// Make sure it is a new(different) window.
EXPECT_NE(ExtensionTabUtil::GetWindowId(browser()),
@@ -549,11 +543,9 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest,
// Run without an explicit "incognito" param.
function = new WindowsCreateFunction();
function->set_extension(extension.get());
- result.reset(utils::ToDictionary(
- utils::RunFunctionAndReturnSingleResult(function.get(),
- kEmptyArgs,
- incognito_browser,
- utils::INCLUDE_INCOGNITO)));
+ result.reset(utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
+ function.get(), kEmptyArgs, incognito_browser,
+ api_test_utils::INCLUDE_INCOGNITO)));
// Make sure it is a new(different) window.
EXPECT_NE(ExtensionTabUtil::GetWindowId(incognito_browser),
GetWindowId(result.get()));
@@ -762,55 +754,6 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, InvalidUpdateWindowState) {
keys::kInvalidWindowStateError));
}
-IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, UpdateAppWindowSizeConstraint) {
- AppWindow* app_window = CreateTestAppWindow(
- "{\"outerBounds\": "
- "{\"width\": 300, \"height\": 300,"
- " \"minWidth\": 200, \"minHeight\": 200,"
- " \"maxWidth\": 400, \"maxHeight\": 400}}");
-
- scoped_refptr<WindowsGetFunction> get_function = new WindowsGetFunction();
- scoped_refptr<Extension> extension(ExtensionBuilder("Test").Build().get());
- get_function->set_extension(extension.get());
- std::unique_ptr<base::DictionaryValue> result(
- utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
- get_function.get(),
- base::StringPrintf("[%u, {\"windowTypes\": [\"app\"]}]",
- app_window->session_id().id()),
- browser())));
-
- EXPECT_EQ(300, api_test_utils::GetInteger(result.get(), "width"));
- EXPECT_EQ(300, api_test_utils::GetInteger(result.get(), "height"));
-
- // Verify the min width/height of the application window are
- // respected.
- scoped_refptr<WindowsUpdateFunction> update_min_function =
- new WindowsUpdateFunction();
- result.reset(utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
- update_min_function.get(),
- base::StringPrintf("[%u, {\"width\": 100, \"height\": 100}]",
- app_window->session_id().id()),
- browser())));
-
- EXPECT_EQ(200, api_test_utils::GetInteger(result.get(), "width"));
- EXPECT_EQ(200, api_test_utils::GetInteger(result.get(), "height"));
-
- // Verify the max width/height of the application window are
- // respected.
- scoped_refptr<WindowsUpdateFunction> update_max_function =
- new WindowsUpdateFunction();
- result.reset(utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
- update_max_function.get(),
- base::StringPrintf("[%u, {\"width\": 500, \"height\": 500}]",
- app_window->session_id().id()),
- browser())));
-
- EXPECT_EQ(400, api_test_utils::GetInteger(result.get(), "width"));
- EXPECT_EQ(400, api_test_utils::GetInteger(result.get(), "height"));
-
- CloseAppWindow(app_window);
-}
-
IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, UpdateDevToolsWindow) {
DevToolsWindow* devtools = DevToolsWindowTesting::OpenDevToolsWindowSync(
browser()->tab_strip_model()->GetWebContentsAt(0), false /* is_docked */);
@@ -866,6 +809,8 @@ class ExtensionWindowLastFocusedTest : public ExtensionTabsTest {
base::Value* RunFunction(UIThreadExtensionFunction* function,
const std::string& params);
+ const Extension* extension() { return extension_.get(); }
+
private:
// A helper class to wait for an views::Widget to become activated.
class WidgetActivatedWaiter : public views::WidgetObserver {
@@ -1083,13 +1028,12 @@ IN_PROC_BROWSER_TEST_F(ExtensionWindowLastFocusedTest,
scoped_refptr<WindowsGetLastFocusedFunction> get_current_app_function =
new WindowsGetLastFocusedFunction();
- std::unique_ptr<base::DictionaryValue> result(utils::ToDictionary(
- RunFunction(get_current_app_function.get(),
- "[{\"populate\": true, \"windowTypes\": [ \"app\" ]}]")));
- int app_window_id = app_window->session_id().id();
- EXPECT_EQ(app_window_id, api_test_utils::GetInteger(result.get(), "id"));
- EXPECT_EQ(-1, GetTabId(result.get()));
- EXPECT_EQ("app", api_test_utils::GetString(result.get(), "type"));
+ get_current_app_function->set_extension(extension());
+ EXPECT_EQ(
+ tabs_constants::kNoLastFocusedWindowError,
+ extension_function_test_utils::RunFunctionAndReturnError(
+ get_current_app_function.get(),
+ "[{\"populate\": true, \"windowTypes\": [ \"app\" ]}]", browser()));
}
chrome::CloseWindow(normal_browser);
@@ -1113,7 +1057,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionWindowCreateTest, AcceptState) {
std::unique_ptr<base::DictionaryValue> result(
utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
function.get(), "[{\"state\": \"minimized\"}]", browser(),
- utils::INCLUDE_INCOGNITO)));
+ api_test_utils::INCLUDE_INCOGNITO)));
int window_id = GetWindowId(result.get());
std::string error;
Browser* new_window = ExtensionTabUtil::GetBrowserFromWindowID(
@@ -1129,7 +1073,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionWindowCreateTest, AcceptState) {
function->set_extension(extension.get());
result.reset(utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
function.get(), "[{\"state\": \"fullscreen\"}]", browser(),
- utils::INCLUDE_INCOGNITO)));
+ api_test_utils::INCLUDE_INCOGNITO)));
window_id = GetWindowId(result.get());
new_window = ExtensionTabUtil::GetBrowserFromWindowID(
ChromeExtensionFunctionDetails(function.get()), window_id, &error);
@@ -1406,7 +1350,9 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, DiscardedProperty) {
// Creates Tab object to ensure the property is correct for the extension.
std::unique_ptr<api::tabs::Tab> tab_object_a =
- ExtensionTabUtil::CreateTabObject(web_contents_a, tab_strip_model, 0);
+ ExtensionTabUtil::CreateTabObject(web_contents_a,
+ ExtensionTabUtil::kDontScrubTab,
+ nullptr, tab_strip_model, 0);
EXPECT_FALSE(tab_object_a->discarded);
// Discards one tab.
@@ -1414,8 +1360,9 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, DiscardedProperty) {
web_contents_a = tab_strip_model->GetWebContentsAt(1);
// Make sure the property is changed accordingly after discarding the tab.
- tab_object_a =
- ExtensionTabUtil::CreateTabObject(web_contents_a, tab_strip_model, 0);
+ tab_object_a = ExtensionTabUtil::CreateTabObject(
+ web_contents_a, ExtensionTabUtil::kDontScrubTab, nullptr, tab_strip_model,
+ 0);
EXPECT_TRUE(tab_object_a->discarded);
// Get non-discarded tabs after discarding one tab.
@@ -1520,6 +1467,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, DiscardWithId) {
tab_id = ExtensionTabUtil::GetTabId(web_contents);
EXPECT_EQ(tab_id, api_test_utils::GetInteger(result.get(), "id"));
EXPECT_TRUE(api_test_utils::GetBoolean(result.get(), "discarded"));
+ // The result should be scrubbed.
+ EXPECT_FALSE(result->FindKey("url"));
// Tests chrome.tabs.discard(tabId) with an already discarded tab. It has to
// return the error stating that the tab couldn't be discarded.
@@ -1592,6 +1541,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, DiscardWithoutId) {
EXPECT_EQ(ExtensionTabUtil::GetTabId(web_contents),
api_test_utils::GetInteger(result.get(), "id"));
EXPECT_TRUE(api_test_utils::GetBoolean(result.get(), "discarded"));
+ // The result should be scrubbed.
+ EXPECT_FALSE(result->FindKey("url"));
}
// Tests chrome.tabs.discard() without disabling protection time.
@@ -1633,7 +1584,9 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, AutoDiscardableProperty) {
// Creates Tab object to ensure the property is correct for the extension.
TabStripModel* tab_strip_model = browser()->tab_strip_model();
std::unique_ptr<api::tabs::Tab> tab_object_a =
- ExtensionTabUtil::CreateTabObject(web_contents_a, tab_strip_model, 0);
+ ExtensionTabUtil::CreateTabObject(web_contents_a,
+ ExtensionTabUtil::kDontScrubTab,
+ nullptr, tab_strip_model, 0);
EXPECT_TRUE(tab_object_a->auto_discardable);
// Set up query and update functions with the extension.
@@ -1675,8 +1628,9 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, AutoDiscardableProperty) {
api_test_utils::GetBoolean(update_result.get(), "autoDiscardable"));
// Make sure the property is changed accordingly after updating the tab.
- tab_object_a =
- ExtensionTabUtil::CreateTabObject(web_contents_a, tab_strip_model, 0);
+ tab_object_a = ExtensionTabUtil::CreateTabObject(
+ web_contents_a, ExtensionTabUtil::kDontScrubTab, nullptr, tab_strip_model,
+ 0);
EXPECT_FALSE(tab_object_a->auto_discardable);
// Get auto-discardable tabs after changing the status of web contents A.
@@ -1786,7 +1740,7 @@ bool ExtensionTabsZoomTest::RunSetZoom(int tab_id, double zoom_factor) {
return utils::RunFunction(
set_zoom_function.get(),
base::StringPrintf("[%u, %lf]", tab_id, zoom_factor), browser(),
- extension_function_test_utils::NONE);
+ api_test_utils::NONE);
}
testing::AssertionResult ExtensionTabsZoomTest::RunGetZoom(
@@ -1825,10 +1779,8 @@ bool ExtensionTabsZoomTest::RunSetZoomSettings(int tab_id,
args = base::StringPrintf("[%u, {\"mode\": \"%s\"}]", tab_id, mode);
}
- return utils::RunFunction(set_zoom_settings_function.get(),
- args,
- browser(),
- extension_function_test_utils::NONE);
+ return utils::RunFunction(set_zoom_settings_function.get(), args, browser(),
+ api_test_utils::NONE);
}
testing::AssertionResult ExtensionTabsZoomTest::RunGetZoomSettings(
diff --git a/chromium/chrome/browser/extensions/api/tabs/windows_event_router.cc b/chromium/chrome/browser/extensions/api/tabs/windows_event_router.cc
index 0ef3e6f98d1..7e4412108a2 100644
--- a/chromium/chrome/browser/extensions/api/tabs/windows_event_router.cc
+++ b/chromium/chrome/browser/extensions/api/tabs/windows_event_router.cc
@@ -14,6 +14,7 @@
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
#include "chrome/browser/extensions/api/tabs/windows_util.h"
#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/extensions/window_controller.h"
#include "chrome/browser/extensions/window_controller_list.h"
@@ -42,14 +43,19 @@ bool ControllerVisibleToListener(WindowController* window_controller,
// If there is no filter the visibility is based on the extension.
const base::ListValue* filter_value = nullptr;
- if (!listener_filter ||
- !listener_filter->GetList(keys::kWindowTypesKey, &filter_value))
- return window_controller->IsVisibleToExtension(extension);
-
- // Otherwise it's based on the type filter.
- WindowController::TypeFilter filter =
- WindowController::GetFilterFromWindowTypesValues(filter_value);
- return window_controller->MatchesFilter(filter);
+ if (listener_filter)
+ listener_filter->GetList(keys::kWindowTypesKey, &filter_value);
+
+ // TODO(https://crbug.com/807313): Remove this.
+ bool allow_dev_tools_windows = !!filter_value;
+ if (!window_controller->IsVisibleToTabsAPIForExtension(
+ extension, allow_dev_tools_windows)) {
+ return false;
+ }
+
+ return !filter_value ||
+ window_controller->MatchesFilter(
+ WindowController::GetFilterFromWindowTypesValues(filter_value));
}
bool WillDispatchWindowEvent(WindowController* window_controller,
@@ -59,6 +65,13 @@ bool WillDispatchWindowEvent(WindowController* window_controller,
const base::DictionaryValue* listener_filter) {
bool has_filter =
listener_filter && listener_filter->HasKey(keys::kWindowTypesKey);
+ // TODO(https://crbug.com/807313): Remove this.
+ bool allow_dev_tools_windows = has_filter;
+ if (!window_controller->IsVisibleToTabsAPIForExtension(
+ extension, allow_dev_tools_windows)) {
+ return false;
+ }
+
// Cleanup previous values.
event->filter_info = EventFilteringInfo();
// Only set the window type if the listener has set a filter.
@@ -66,8 +79,7 @@ bool WillDispatchWindowEvent(WindowController* window_controller,
if (has_filter) {
event->filter_info.window_type = window_controller->GetWindowTypeText();
} else {
- event->filter_info.window_exposed_by_default =
- window_controller->IsVisibleToExtension(extension);
+ event->filter_info.window_exposed_by_default = true;
}
return true;
}
@@ -194,9 +206,14 @@ void WindowsEventRouter::OnWindowControllerAdded(
return;
if (!profile_->IsSameProfile(window_controller->profile()))
return;
+ // Ignore any windows without an associated browser (e.g., AppWindows).
+ if (!window_controller->GetBrowser())
+ return;
std::unique_ptr<base::ListValue> args(new base::ListValue());
- args->Append(window_controller->CreateWindowValue());
+ args->Append(ExtensionTabUtil::CreateWindowValueForExtension(
+ *window_controller->GetBrowser(), nullptr,
+ ExtensionTabUtil::kDontPopulateTabs));
DispatchEvent(events::WINDOWS_ON_CREATED, windows::OnCreated::kEventName,
window_controller, std::move(args));
}
@@ -207,6 +224,9 @@ void WindowsEventRouter::OnWindowControllerRemoved(
return;
if (!profile_->IsSameProfile(window_controller->profile()))
return;
+ // Ignore any windows without an associated browser (e.g., AppWindows).
+ if (!window_controller->GetBrowser())
+ return;
int window_id = window_controller->GetWindowId();
std::unique_ptr<base::ListValue> args(new base::ListValue());
diff --git a/chromium/chrome/browser/extensions/api/tabs/windows_util.cc b/chromium/chrome/browser/extensions/api/tabs/windows_util.cc
index e7179e62225..3d75225bf78 100644
--- a/chromium/chrome/browser/extensions/api/tabs/windows_util.cc
+++ b/chromium/chrome/browser/extensions/api/tabs/windows_util.cc
@@ -23,38 +23,48 @@
namespace windows_util {
-bool GetWindowFromWindowID(UIThreadExtensionFunction* function,
- int window_id,
- extensions::WindowController::TypeFilter filter,
- extensions::WindowController** controller,
- std::string* error) {
+bool GetBrowserFromWindowID(UIThreadExtensionFunction* function,
+ int window_id,
+ extensions::WindowController::TypeFilter filter,
+ Browser** browser,
+ std::string* error) {
+ DCHECK(browser);
DCHECK(error);
+
+ *browser = nullptr;
if (window_id == extension_misc::kCurrentWindowId) {
- extensions::WindowController* extension_window_controller =
- function->dispatcher()->GetExtensionWindowController();
// If there is a window controller associated with this extension, use that.
- if (extension_window_controller) {
- *controller = extension_window_controller;
- } else {
+ extensions::WindowController* window_controller =
+ function->dispatcher()->GetExtensionWindowController();
+ if (!window_controller) {
// Otherwise get the focused or most recently added window.
- *controller = extensions::WindowControllerList::GetInstance()
- ->CurrentWindowForFunctionWithFilter(function, filter);
+ window_controller =
+ extensions::WindowControllerList::GetInstance()
+ ->CurrentWindowForFunctionWithFilter(function, filter);
}
- if (!(*controller)) {
+
+ if (window_controller)
+ *browser = window_controller->GetBrowser();
+
+ if (!(*browser)) {
*error = extensions::tabs_constants::kNoCurrentWindowError;
return false;
}
} else {
- *controller =
+ extensions::WindowController* window_controller =
extensions::WindowControllerList::GetInstance()
->FindWindowForFunctionByIdWithFilter(function, window_id, filter);
- if (!(*controller)) {
+ if (window_controller)
+ *browser = window_controller->GetBrowser();
+
+ if (!(*browser)) {
*error = extensions::ErrorUtils::FormatErrorMessage(
extensions::tabs_constants::kWindowNotFoundError,
base::IntToString(window_id));
return false;
}
}
+ DCHECK(*browser);
return true;
}
@@ -64,9 +74,13 @@ bool CanOperateOnWindow(const UIThreadExtensionFunction* function,
if (filter && !controller->MatchesFilter(filter))
return false;
- if (!filter && function->extension() &&
- !controller->IsVisibleToExtension(function->extension()))
+ // TODO(https://crbug.com/807313): Remove this.
+ bool allow_dev_tools_windows = !!filter;
+ if (function->extension() &&
+ !controller->IsVisibleToTabsAPIForExtension(function->extension(),
+ allow_dev_tools_windows)) {
return false;
+ }
if (function->browser_context() == controller->profile())
return true;
diff --git a/chromium/chrome/browser/extensions/api/tabs/windows_util.h b/chromium/chrome/browser/extensions/api/tabs/windows_util.h
index d5526e10c30..f080d068c72 100644
--- a/chromium/chrome/browser/extensions/api/tabs/windows_util.h
+++ b/chromium/chrome/browser/extensions/api/tabs/windows_util.h
@@ -5,6 +5,8 @@
#ifndef CHROME_BROWSER_EXTENSIONS_API_TABS_WINDOWS_UTIL_H__
#define CHROME_BROWSER_EXTENSIONS_API_TABS_WINDOWS_UTIL_H__
+#include <string>
+
#include "chrome/browser/extensions/window_controller_list.h"
class UIThreadExtensionFunction;
@@ -15,13 +17,13 @@ class WindowController;
namespace windows_util {
-// Populates |controller| for given |window_id|. If the window is not found,
+// Populates |browser| for given |window_id|. If the window is not found,
// returns false and sets |error|.
-bool GetWindowFromWindowID(UIThreadExtensionFunction* function,
- int window_id,
- extensions::WindowController::TypeFilter filter,
- extensions::WindowController** controller,
- std::string* error);
+bool GetBrowserFromWindowID(UIThreadExtensionFunction* function,
+ int window_id,
+ extensions::WindowController::TypeFilter filter,
+ Browser** browser,
+ std::string* error);
// Returns true if |function| (and the profile and extension that it was
// invoked from) can operate on the window wrapped by |window_controller|.
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 ee44ebfbdde..6a0a795e8bb 100644
--- a/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.cc
+++ b/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.cc
@@ -6,6 +6,7 @@
#include <memory>
#include <utility>
+#include <vector>
#include "base/bind.h"
#include "base/command_line.h"
@@ -25,6 +26,7 @@
#include "extensions/browser/app_window/app_window.h"
#include "extensions/browser/app_window/app_window_registry.h"
#include "extensions/browser/event_router.h"
+#include "extensions/browser/extensions_browser_client.h"
namespace terminal_private = extensions::api::terminal_private;
namespace OnTerminalResize =
@@ -43,6 +45,9 @@ const char kCroshCommand[] = "/usr/bin/crosh";
// We make stubbed crosh just echo back input.
const char kStubbedCroshCommand[] = "cat";
+const char kVmShellName[] = "vmshell";
+const char kVmShellCommand[] = "/usr/bin/vsh";
+
std::string GetCroshPath() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kCroshCommand))
@@ -54,13 +59,24 @@ std::string GetCroshPath() {
return std::string(kStubbedCroshCommand);
}
+// Get the program to run based on the openTerminalProcess JS request.
std::string GetProcessCommandForName(const std::string& name) {
if (name == kCroshName)
return GetCroshPath();
+ else if (name == kVmShellName)
+ return kVmShellCommand;
else
return std::string();
}
+// Whether the program accepts arbitrary command line arguments.
+bool CommandSupportsArguments(const std::string& name) {
+ if (name == kVmShellName)
+ return true;
+
+ return false;
+}
+
void NotifyProcessOutput(content::BrowserContext* browser_context,
const std::string& extension_id,
int tab_id,
@@ -119,10 +135,24 @@ TerminalPrivateOpenTerminalProcessFunction::Run() {
OpenTerminalProcess::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
- command_ = GetProcessCommandForName(params->process_name);
- if (command_.empty())
+ const std::string command = GetProcessCommandForName(params->process_name);
+ if (command.empty())
return RespondNow(Error("Invalid process name."));
+ const std::string user_id_hash =
+ ExtensionsBrowserClient::Get()->GetUserIdHashFromContext(
+ browser_context());
+
+ std::vector<std::string> arguments;
+ arguments.push_back(command);
+ if (params->args) {
+ for (const std::string& arg : *params->args)
+ arguments.push_back(arg);
+ }
+
+ if (arguments.size() > 1 && !CommandSupportsArguments(params->process_name))
+ return RespondNow(Error("Specified command does not support arguments."));
+
content::WebContents* caller_contents = GetSenderWebContents();
if (!caller_contents)
return RespondNow(Error("No web contents."));
@@ -151,19 +181,23 @@ TerminalPrivateOpenTerminalProcessFunction::Run() {
tab_id),
base::Bind(
&TerminalPrivateOpenTerminalProcessFunction::RespondOnUIThread,
- this)));
+ this),
+ arguments,
+ user_id_hash));
return RespondLater();
}
void TerminalPrivateOpenTerminalProcessFunction::OpenOnRegistryTaskRunner(
const ProcessOutputCallback& output_callback,
- const OpenProcessCallback& callback) {
- DCHECK(!command_.empty());
-
+ const OpenProcessCallback& callback,
+ const std::vector<std::string>& arguments,
+ const std::string& user_id_hash) {
chromeos::ProcessProxyRegistry* registry =
chromeos::ProcessProxyRegistry::Get();
+ const base::CommandLine cmdline{arguments};
- int terminal_id = registry->OpenProcess(command_.c_str(), output_callback);
+ int terminal_id =
+ registry->OpenProcess(cmdline, user_id_hash, output_callback);
content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
base::BindOnce(callback, terminal_id));
diff --git a/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.h b/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.h
index c6455784059..cd7018fb2e4 100644
--- a/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.h
+++ b/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.h
@@ -6,6 +6,7 @@
#define CHROME_BROWSER_EXTENSIONS_API_TERMINAL_TERMINAL_PRIVATE_API_H_
#include <string>
+#include <vector>
#include "extensions/browser/extension_function.h"
@@ -33,10 +34,10 @@ class TerminalPrivateOpenTerminalProcessFunction
using OpenProcessCallback = base::Callback<void(int terminal_id)>;
void OpenOnRegistryTaskRunner(const ProcessOutputCallback& output_callback,
- const OpenProcessCallback& callback);
+ const OpenProcessCallback& callback,
+ const std::vector<std::string>& arguments,
+ const std::string& user_id_hash);
void RespondOnUIThread(int terminal_id);
-
- std::string command_;
};
// Send input to the terminal process specified by the terminal ID, which is set
diff --git a/chromium/chrome/browser/extensions/api/virtual_keyboard_private/DEPS b/chromium/chrome/browser/extensions/api/virtual_keyboard_private/DEPS
index fee9b1012b1..381dab83266 100644
--- a/chromium/chrome/browser/extensions/api/virtual_keyboard_private/DEPS
+++ b/chromium/chrome/browser/extensions/api/virtual_keyboard_private/DEPS
@@ -2,4 +2,5 @@ include_rules = [
# TODO(mash): Remove. http://crbug.com/678705
"+ash/shell.h",
"+media/audio"
+ "+services/audio/public/cpp"
]
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 02d77771c79..7e58654da56 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
@@ -21,10 +21,13 @@
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/common/url_constants.h"
#include "components/user_manager/user_manager.h"
+#include "content/public/common/service_manager_connection.h"
#include "extensions/browser/event_router.h"
#include "extensions/common/api/virtual_keyboard.h"
#include "extensions/common/api/virtual_keyboard_private.h"
#include "media/audio/audio_system.h"
+#include "services/audio/public/cpp/audio_system_factory.h"
+#include "services/service_manager/public/cpp/connector.h"
#include "ui/aura/window_tree_host.h"
#include "ui/keyboard/keyboard_controller.h"
#include "ui/keyboard/keyboard_switches.h"
@@ -74,7 +77,10 @@ void ChromeVirtualKeyboardDelegate::GetKeyboardConfig(
OnKeyboardSettingsCallback on_settings_callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!audio_system_)
- audio_system_ = media::AudioSystem::CreateInstance();
+ audio_system_ = audio::CreateAudioSystem(
+ content::ServiceManagerConnection::GetForProcess()
+ ->GetConnector()
+ ->Clone());
audio_system_->HasInputDevices(
base::BindOnce(&ChromeVirtualKeyboardDelegate::OnHasInputDevices,
weak_this_, std::move(on_settings_callback)));
@@ -152,24 +158,31 @@ bool ChromeVirtualKeyboardDelegate::ShowLanguageSettings() {
return true;
}
-bool ChromeVirtualKeyboardDelegate::SetVirtualKeyboardMode(int mode_enum) {
+bool ChromeVirtualKeyboardDelegate::SetVirtualKeyboardMode(
+ int mode_enum,
+ OnSetModeCallback on_set_mode_callback) {
keyboard::KeyboardController* controller =
keyboard::KeyboardController::GetInstance();
if (!controller)
return false;
- switch (mode_enum) {
+ controller->SetContainerType(ConvertKeyboardModeToContainerType(mode_enum),
+ std::move(on_set_mode_callback));
+ return true;
+}
+
+keyboard::ContainerType
+ChromeVirtualKeyboardDelegate::ConvertKeyboardModeToContainerType(
+ int mode) const {
+ switch (mode) {
case keyboard_api::KEYBOARD_MODE_FULL_WIDTH:
- controller->SetContainerType(keyboard::ContainerType::FULL_WIDTH);
- break;
+ return keyboard::ContainerType::FULL_WIDTH;
case keyboard_api::KEYBOARD_MODE_FLOATING:
- controller->SetContainerType(keyboard::ContainerType::FLOATING);
- break;
- default:
- NOTREACHED();
- break;
+ return keyboard::ContainerType::FLOATING;
}
- return true;
+
+ NOTREACHED();
+ return keyboard::ContainerType::FULL_WIDTH;
}
bool ChromeVirtualKeyboardDelegate::SetDraggableArea(
diff --git a/chromium/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.h b/chromium/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.h
index 1ec4b52b068..d0af47f0095 100644
--- a/chromium/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.h
+++ b/chromium/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.h
@@ -13,6 +13,7 @@
#include "content/public/browser/browser_context.h"
#include "extensions/browser/api/virtual_keyboard_private/virtual_keyboard_delegate.h"
#include "extensions/common/api/virtual_keyboard.h"
+#include "ui/keyboard/container_type.h"
namespace media {
class AudioSystem;
@@ -44,7 +45,8 @@ class ChromeVirtualKeyboardDelegate : public VirtualKeyboardDelegate {
int modifiers) override;
bool ShowLanguageSettings() override;
bool IsLanguageSettingsEnabled() override;
- bool SetVirtualKeyboardMode(int mode_enum) override;
+ bool SetVirtualKeyboardMode(int mode_enum,
+ OnSetModeCallback on_set_mode_callback) override;
bool SetDraggableArea(
const api::virtual_keyboard_private::Bounds& rect) override;
bool SetRequestedKeyboardState(int state_enum) override;
@@ -57,6 +59,7 @@ class ChromeVirtualKeyboardDelegate : public VirtualKeyboardDelegate {
bool has_audio_input_devices);
void DispatchConfigChangeEvent(
std::unique_ptr<base::DictionaryValue> settings);
+ keyboard::ContainerType ConvertKeyboardModeToContainerType(int mode) const;
content::BrowserContext* browser_context_;
std::unique_ptr<media::AudioSystem> audio_system_;
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 43798f375e0..cd090aab65c 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
@@ -197,8 +197,8 @@ class VpnProviderApiTest : public ExtensionApiTest,
void TriggerInternalRemove() {
NetworkHandler::Get()->network_configuration_handler()->RemoveConfiguration(
GetSingleServicePath(),
- NetworkConfigurationObserver::SOURCE_USER_ACTION,
- base::Bind(base::DoNothing), base::Bind(DoNothingFailureCallback));
+ NetworkConfigurationObserver::SOURCE_USER_ACTION, base::DoNothing(),
+ base::Bind(DoNothingFailureCallback));
}
// NetworkConfigurationObserver:
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 28dbc774e12..747efc84c3b 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
@@ -37,7 +37,6 @@
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/web_contents.h"
-#include "content/public/common/browser_side_navigation_policy.h"
#include "content/public/common/context_menu_params.h"
#include "content/public/common/resource_type.h"
#include "content/public/common/url_constants.h"
@@ -276,8 +275,6 @@ IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, ServerRedirectSingleProcess) {
}
IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, ForwardBack) {
- if (content::IsBrowserSideNavigationEnabled())
- return; // TODO(jam): investigate
ASSERT_TRUE(RunExtensionTest("webnavigation/forwardBack")) << message_;
}
@@ -384,9 +381,9 @@ IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, RequestOpenTab) {
ui_test_utils::NavigateToURL(browser(), url);
// There's a link on a.html. Middle-click on it to open it in a new tab.
- blink::WebMouseEvent mouse_event(blink::WebInputEvent::kMouseDown,
- blink::WebInputEvent::kNoModifiers,
- blink::WebInputEvent::kTimeStampForTesting);
+ blink::WebMouseEvent mouse_event(
+ blink::WebInputEvent::kMouseDown, blink::WebInputEvent::kNoModifiers,
+ blink::WebInputEvent::GetStaticTimeStampForTests());
mouse_event.button = blink::WebMouseEvent::Button::kMiddle;
mouse_event.SetPositionInWidget(7, 7);
mouse_event.click_count = 1;
@@ -416,9 +413,9 @@ IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, TargetBlank) {
// There's a link with target=_blank on a.html. Click on it to open it in a
// new tab.
- blink::WebMouseEvent mouse_event(blink::WebInputEvent::kMouseDown,
- blink::WebInputEvent::kNoModifiers,
- blink::WebInputEvent::kTimeStampForTesting);
+ blink::WebMouseEvent mouse_event(
+ blink::WebInputEvent::kMouseDown, blink::WebInputEvent::kNoModifiers,
+ blink::WebInputEvent::GetStaticTimeStampForTests());
mouse_event.button = blink::WebMouseEvent::Button::kLeft;
mouse_event.SetPositionInWidget(7, 7);
mouse_event.click_count = 1;
@@ -446,9 +443,9 @@ IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, TargetBlankIncognito) {
// There's a link with target=_blank on a.html. Click on it to open it in a
// new tab.
- blink::WebMouseEvent mouse_event(blink::WebInputEvent::kMouseDown,
- blink::WebInputEvent::kNoModifiers,
- blink::WebInputEvent::kTimeStampForTesting);
+ blink::WebMouseEvent mouse_event(
+ blink::WebInputEvent::kMouseDown, blink::WebInputEvent::kNoModifiers,
+ blink::WebInputEvent::GetStaticTimeStampForTests());
mouse_event.button = blink::WebMouseEvent::Button::kLeft;
mouse_event.SetPositionInWidget(7, 7);
mouse_event.click_count = 1;
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 dd86f9b6972..b43a05f9a61 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
@@ -139,6 +139,19 @@ void GetPartOfMessageArguments(IPC::Message* message,
ASSERT_TRUE(list.GetDictionary(0, out));
}
+base::Value FormBinaryValue(base::StringPiece str) {
+ base::Value list(base::Value::Type::LIST);
+ list.GetList().emplace_back(base::Value(
+ base::Value::BlobStorage(str.data(), str.data() + str.size())));
+ return list;
+}
+
+base::Value FormStringValue(base::StringPiece str) {
+ base::Value list(base::Value::Type::LIST);
+ list.GetList().emplace_back(base::Value(str));
+ return list;
+}
+
} // namespace
// A mock event router that responds to events with a pre-arranged queue of
@@ -566,7 +579,7 @@ void ExtensionWebRequestTest::FireURLRequestWithData(
&(bytes_2[0]), bytes_2.size()));
request->set_upload(std::make_unique<net::ElementsUploadDataStream>(
std::move(element_readers), 0));
- ipc_sender_.PushTask(base::Bind(&base::DoNothing));
+ ipc_sender_.PushTask(base::DoNothing());
request->Start();
}
@@ -592,17 +605,26 @@ TEST_F(ExtensionWebRequestTest, AccessRequestBodyData) {
const size_t kPlainBlock2Length = sizeof(kPlainBlock2) - 1;
std::vector<char> plain_2(kPlainBlock2, kPlainBlock2 + kPlainBlock2Length);
#define kBoundary "THIS_IS_A_BOUNDARY"
- const char kFormBlock1[] = "--" kBoundary "\r\n"
+ const char kFormBlock1[] =
+ "--" kBoundary
+ "\r\n"
"Content-Disposition: form-data; name=\"A\"\r\n"
"\r\n"
"test text\r\n"
- "--" kBoundary "\r\n"
+ "--" kBoundary
+ "\r\n"
"Content-Disposition: form-data; name=\"B\"; filename=\"\"\r\n"
"Content-Type: application/octet-stream\r\n"
- "\r\n";
+ "\r\n"
+ "--" kBoundary
+ "\r\n"
+ "Content-Disposition: form-data; name=\"B_content\"\r\n"
+ "Content-Type: application/octet-stream\r\n"
+ "\r\n"
+ "\uffff\uffff\uffff\uffff\r\n"
+ "--" kBoundary "\r\n";
std::vector<char> form_1(kFormBlock1, kFormBlock1 + sizeof(kFormBlock1) - 1);
- const char kFormBlock2[] = "\r\n"
- "--" kBoundary "\r\n"
+ const char kFormBlock2[] =
"Content-Disposition: form-data; name=\"C\"\r\n"
"\r\n"
"test password\r\n"
@@ -623,10 +645,21 @@ TEST_F(ExtensionWebRequestTest, AccessRequestBodyData) {
&kRawPath
};
// Contents of formData.
- const char kFormData[] =
- "{\"A\":[\"test text\"],\"B\":[\"\"],\"C\":[\"test password\"]}";
- std::unique_ptr<const base::Value> form_data =
- base::JSONReader::Read(kFormData);
+ struct KeyValuePairs {
+ const char* key;
+ base::Value value;
+ };
+ KeyValuePairs kFormDataPairs[] = {
+ {"A", FormStringValue("test text")},
+ {"B", FormStringValue("")},
+ {"B_content", FormBinaryValue("\uffff\uffff\uffff\uffff")},
+ {"C", FormStringValue("test password")}};
+ std::unique_ptr<base::Value> form_data =
+ std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
+ for (auto& pair : kFormDataPairs) {
+ form_data->SetKey(pair.key, std::move(pair.value));
+ }
+
ASSERT_TRUE(form_data.get() != NULL);
ASSERT_TRUE(form_data->type() == base::Value::Type::DICTIONARY);
// Contents of raw.
@@ -782,7 +815,7 @@ TEST_F(ExtensionWebRequestTest, MinimalAccessRequestBodyData) {
// Only one request is sent, but more than one event will be triggered.
for (size_t i = 1; i < arraysize(kExpected); ++i)
- ipc_sender_.PushTask(base::Bind(&base::DoNothing));
+ ipc_sender_.PushTask(base::DoNothing());
const std::vector<char> part_of_body(1);
FireURLRequestWithData("POST", nullptr, part_of_body, part_of_body);
@@ -855,7 +888,7 @@ TEST_F(ExtensionWebRequestTest, ProperFilteringInPublicSession) {
// Only one request is sent, but more than one event will be triggered.
for (size_t i = 1; i < arraysize(kExpected); ++i)
- ipc_sender_.PushTask(base::Bind(&base::DoNothing));
+ ipc_sender_.PushTask(base::DoNothing());
const std::vector<char> part_of_body(1);
FireURLRequestWithData("POST", nullptr, part_of_body, part_of_body);
@@ -920,7 +953,7 @@ TEST_F(ExtensionWebRequestTest, NoAccessRequestBodyData) {
context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_,
TRAFFIC_ANNOTATION_FOR_TESTS));
request->set_method(kMethods[i]);
- ipc_sender_.PushTask(base::Bind(&base::DoNothing));
+ ipc_sender_.PushTask(base::DoNothing());
request->Start();
}
@@ -1195,7 +1228,7 @@ TEST_P(ExtensionWebRequestHeaderModificationTest, TestModifications) {
}
// Don't do anything for the onSendHeaders message.
- ipc_sender_.PushTask(base::Bind(&base::DoNothing));
+ ipc_sender_.PushTask(base::DoNothing());
// Note that we mess up the headers slightly:
// request->Start() will first add additional headers (e.g. the User-Agent)
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 e0b875c7d40..bd3b124bbf5 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
@@ -21,7 +21,6 @@
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_with_management_policy_apitest.h"
#include "chrome/browser/extensions/tab_helper.h"
-#include "chrome/browser/extensions/test_extension_dir.h"
#include "chrome/browser/net/profile_network_context_service.h"
#include "chrome/browser/net/profile_network_context_service_factory.h"
#include "chrome/browser/profiles/profile.h"
@@ -48,6 +47,7 @@
#include "content/public/browser/web_contents.h"
#include "content/public/common/page_type.h"
#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/url_loader_interceptor.h"
#include "extensions/browser/api/web_request/web_request_api.h"
#include "extensions/browser/blocked_action_type.h"
#include "extensions/browser/extension_system.h"
@@ -55,6 +55,7 @@
#include "extensions/common/features/feature.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/result_catcher.h"
+#include "extensions/test/test_extension_dir.h"
#include "google_apis/gaia/gaia_switches.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_util.h"
@@ -69,6 +70,7 @@
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_filter.h"
#include "net/url_request/url_request_interceptor.h"
+#include "services/network/public/cpp/features.h"
#include "third_party/WebKit/public/platform/WebInputEvent.h"
#if defined(OS_CHROMEOS)
@@ -283,22 +285,33 @@ class DevToolsFrontendInWebRequestApiTest : public ExtensionApiTest {
host_resolver()->AddRule("*", "127.0.0.1");
int port = embedded_test_server()->port();
- base::RunLoop run_loop;
- content::BrowserThread::PostTaskAndReply(
- content::BrowserThread::IO, FROM_HERE,
- base::BindOnce(&SetUpDevToolsFrontendInterceptorOnIO, port,
- test_root_dir_),
- run_loop.QuitClosure());
- run_loop.Run();
+
+ if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+ url_loader_interceptor_ = std::make_unique<content::URLLoaderInterceptor>(
+ base::BindRepeating(&DevToolsFrontendInWebRequestApiTest::OnIntercept,
+ base::Unretained(this), port));
+ } else {
+ base::RunLoop run_loop;
+ content::BrowserThread::PostTaskAndReply(
+ content::BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&SetUpDevToolsFrontendInterceptorOnIO, port,
+ test_root_dir_),
+ run_loop.QuitClosure());
+ run_loop.Run();
+ }
}
void TearDownOnMainThread() override {
- base::RunLoop run_loop;
- content::BrowserThread::PostTaskAndReply(
- content::BrowserThread::IO, FROM_HERE,
- base::BindOnce(&TearDownDevToolsFrontendInterceptorOnIO),
- run_loop.QuitClosure());
- run_loop.Run();
+ if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+ url_loader_interceptor_.reset();
+ } else {
+ base::RunLoop run_loop;
+ content::BrowserThread::PostTaskAndReply(
+ content::BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&TearDownDevToolsFrontendInterceptorOnIO),
+ run_loop.QuitClosure());
+ run_loop.Run();
+ }
ExtensionApiTest::TearDownOnMainThread();
}
@@ -317,7 +330,49 @@ class DevToolsFrontendInWebRequestApiTest : public ExtensionApiTest {
}
private:
+ bool OnIntercept(int test_server_port,
+ content::URLLoaderInterceptor::RequestParams* params) {
+ // See comments in DevToolsFrontendInterceptor above. The devtools remote
+ // frontend URLs are hardcoded into Chrome and are requested by some of the
+ // tests here to exercise their behavior with respect to WebRequest.
+ //
+ // We treat any URL request not targeting the test server as targeting the
+ // remote frontend, and we intercept them to fulfill from test data rather
+ // than hitting the network.
+ if (params->url_request.url.EffectiveIntPort() == test_server_port)
+ return false;
+
+ std::string status_line;
+ std::string contents;
+ GetFileContents(
+ test_root_dir_.AppendASCII(params->url_request.url.path().substr(1)),
+ &status_line, &contents);
+ content::URLLoaderInterceptor::WriteResponse(status_line, contents,
+ params->client.get());
+ return true;
+ }
+
+ static void GetFileContents(const base::FilePath& path,
+ std::string* status_line,
+ std::string* contents) {
+ base::ScopedAllowBlockingForTesting allow_io;
+ if (!base::ReadFileToString(path, contents)) {
+ *status_line = "HTTP/1.0 404 Not Found\n\n";
+ return;
+ }
+
+ std::string content_type;
+ if (path.Extension() == FILE_PATH_LITERAL(".html"))
+ content_type = "Content-type: text/html\n";
+ else if (path.Extension() == FILE_PATH_LITERAL(".js"))
+ content_type = "Content-type: application/javascript\n";
+
+ *status_line =
+ base::StringPrintf("HTTP/1.0 200 OK\n%s\n", content_type.c_str());
+ }
+
base::FilePath test_root_dir_;
+ std::unique_ptr<content::URLLoaderInterceptor> url_loader_interceptor_;
};
IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, WebRequestApi) {
@@ -467,9 +522,9 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, MAYBE_WebRequestNewTab) {
// There's a link on a.html with target=_blank. Click on it to open it in a
// new tab.
- blink::WebMouseEvent mouse_event(blink::WebInputEvent::kMouseDown,
- blink::WebInputEvent::kNoModifiers,
- blink::WebInputEvent::kTimeStampForTesting);
+ blink::WebMouseEvent mouse_event(
+ blink::WebInputEvent::kMouseDown, blink::WebInputEvent::kNoModifiers,
+ blink::WebInputEvent::GetStaticTimeStampForTests());
mouse_event.button = blink::WebMouseEvent::Button::kLeft;
mouse_event.SetPositionInWidget(7, 7);
mouse_event.click_count = 1;
@@ -1297,7 +1352,21 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, MinimumAccessInitiator) {
// Ensure that devtools frontend requests are hidden from the webRequest API.
IN_PROC_BROWSER_TEST_F(DevToolsFrontendInWebRequestApiTest, HiddenRequests) {
- ASSERT_TRUE(RunExtensionSubtest("webrequest", "test_devtools.html"))
+ // Test expectations differ with the Network Service because of the way
+ // request interception is done for the test. In the legacy networking path a
+ // URLRequestMockHTTPJob is used, which does not generate
+ // |onBeforeHeadersSent| events. With the Network Service enabled, requests
+ // issued to HTTP URLs by these tests look like real HTTP requests and
+ // therefore do generate |onBeforeHeadersSent| events.
+ //
+ // These tests adjust their expectations accordingly based on whether or not
+ // the Network Service is enabled.
+ const char* network_service_arg =
+ base::FeatureList::IsEnabled(network::features::kNetworkService)
+ ? "NetworkServiceEnabled"
+ : "NetworkServiceDisabled";
+ ASSERT_TRUE(RunExtensionSubtestWithArg("webrequest", "test_devtools.html",
+ network_service_arg))
<< message_;
}
diff --git a/chromium/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc b/chromium/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc
index 8bcd3345fe3..b2723a15ed6 100644
--- a/chromium/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc
+++ b/chromium/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc
@@ -193,6 +193,7 @@ TEST_F(ExtensionWebRequestHelpersTestWithThreadsTest, TestHideRequestForURL) {
TEST_F(ExtensionWebRequestHelpersTestWithThreadsTest,
TestCanExtensionAccessURL_HostPermissions) {
+ // Request with empty initiator.
std::unique_ptr<net::URLRequest> request(
context.CreateRequest(GURL("http://example.com"), net::DEFAULT_PRIORITY,
NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
@@ -211,15 +212,23 @@ TEST_F(ExtensionWebRequestHelpersTestWithThreadsTest,
request->url(),
-1, // No tab id.
false, // crosses_incognito
- WebRequestPermissions::REQUIRE_HOST_PERMISSION,
+ WebRequestPermissions::REQUIRE_HOST_PERMISSION_FOR_URL,
request->initiator()));
EXPECT_EQ(PermissionsData::ACCESS_ALLOWED,
WebRequestPermissions::CanExtensionAccessURL(
extension_info_map_.get(), com_extension_->id(), request->url(),
-1, // No tab id.
false, // crosses_incognito
- WebRequestPermissions::REQUIRE_HOST_PERMISSION,
+ WebRequestPermissions::REQUIRE_HOST_PERMISSION_FOR_URL,
request->initiator()));
+ EXPECT_EQ(
+ PermissionsData::ACCESS_ALLOWED,
+ WebRequestPermissions::CanExtensionAccessURL(
+ extension_info_map_.get(), com_extension_->id(), request->url(),
+ -1, // No tab id.
+ false, // crosses_incognito
+ WebRequestPermissions::REQUIRE_HOST_PERMISSION_FOR_URL_AND_INITIATOR,
+ request->initiator()));
EXPECT_EQ(PermissionsData::ACCESS_DENIED,
WebRequestPermissions::CanExtensionAccessURL(
extension_info_map_.get(), com_extension_->id(), request->url(),
@@ -227,6 +236,54 @@ TEST_F(ExtensionWebRequestHelpersTestWithThreadsTest,
false, // crosses_incognito
WebRequestPermissions::REQUIRE_ALL_URLS, request->initiator()));
+ std::unique_ptr<net::URLRequest> request_with_initiator(
+ context.CreateRequest(GURL("http://example.com"), net::DEFAULT_PRIORITY,
+ nullptr, TRAFFIC_ANNOTATION_FOR_TESTS));
+ request_with_initiator->set_initiator(
+ url::Origin::Create(GURL("http://www.example.org")));
+
+ EXPECT_EQ(PermissionsData::ACCESS_ALLOWED,
+ WebRequestPermissions::CanExtensionAccessURL(
+ extension_info_map_.get(), permissionless_extension_->id(),
+ request_with_initiator->url(),
+ -1, // No tab id.
+ false, // crosses_incognito
+ WebRequestPermissions::DO_NOT_CHECK_HOST,
+ request_with_initiator->initiator()));
+ EXPECT_EQ(PermissionsData::ACCESS_DENIED,
+ WebRequestPermissions::CanExtensionAccessURL(
+ extension_info_map_.get(), permissionless_extension_->id(),
+ request_with_initiator->url(),
+ -1, // No tab id.
+ false, // crosses_incognito
+ WebRequestPermissions::REQUIRE_HOST_PERMISSION_FOR_URL,
+ request_with_initiator->initiator()));
+ EXPECT_EQ(PermissionsData::ACCESS_ALLOWED,
+ WebRequestPermissions::CanExtensionAccessURL(
+ extension_info_map_.get(), com_extension_->id(),
+ request_with_initiator->url(),
+ -1, // No tab id.
+ false, // crosses_incognito
+ WebRequestPermissions::REQUIRE_HOST_PERMISSION_FOR_URL,
+ request_with_initiator->initiator()));
+ EXPECT_EQ(
+ PermissionsData::ACCESS_DENIED,
+ WebRequestPermissions::CanExtensionAccessURL(
+ extension_info_map_.get(), com_extension_->id(),
+ request_with_initiator->url(),
+ -1, // No tab id.
+ false, // crosses_incognito
+ WebRequestPermissions::REQUIRE_HOST_PERMISSION_FOR_URL_AND_INITIATOR,
+ request_with_initiator->initiator()));
+ EXPECT_EQ(PermissionsData::ACCESS_DENIED,
+ WebRequestPermissions::CanExtensionAccessURL(
+ extension_info_map_.get(), com_extension_->id(),
+ request_with_initiator->url(),
+ -1, // No tab id.
+ false, // crosses_incognito
+ WebRequestPermissions::REQUIRE_ALL_URLS,
+ request_with_initiator->initiator()));
+
// Public Sessions tests.
#if defined(OS_CHROMEOS)
std::unique_ptr<net::URLRequest> org_request(context.CreateRequest(
@@ -239,7 +296,7 @@ TEST_F(ExtensionWebRequestHelpersTestWithThreadsTest,
org_request->url(),
-1, // No tab id.
false, // crosses_incognito
- WebRequestPermissions::REQUIRE_HOST_PERMISSION,
+ WebRequestPermissions::REQUIRE_HOST_PERMISSION_FOR_URL,
org_request->initiator()));
chromeos::ScopedTestPublicSessionLoginState login_state;
@@ -252,7 +309,7 @@ TEST_F(ExtensionWebRequestHelpersTestWithThreadsTest,
org_request->url(),
-1, // No tab id.
false, // crosses_incognito
- WebRequestPermissions::REQUIRE_HOST_PERMISSION,
+ WebRequestPermissions::REQUIRE_HOST_PERMISSION_FOR_URL,
org_request->initiator()));
EXPECT_EQ(
@@ -275,7 +332,7 @@ TEST_F(ExtensionWebRequestHelpersTestWithThreadsTest,
chrome_request->url(),
-1, // No tab id.
false, // crosses_incognito
- WebRequestPermissions::REQUIRE_HOST_PERMISSION,
+ WebRequestPermissions::REQUIRE_HOST_PERMISSION_FOR_URL,
chrome_request->initiator()));
#endif
}
diff --git a/chromium/chrome/browser/extensions/api/webrtc_audio_private/DEPS b/chromium/chrome/browser/extensions/api/webrtc_audio_private/DEPS
index 67c01d06986..287341aed80 100644
--- a/chromium/chrome/browser/extensions/api/webrtc_audio_private/DEPS
+++ b/chromium/chrome/browser/extensions/api/webrtc_audio_private/DEPS
@@ -1,3 +1,4 @@
include_rules = [
"+media/audio"
+ "services/audio/public/cpp"
]
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 fa1e2bdda32..22301de62d9 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
@@ -18,11 +18,14 @@
#include "content/public/browser/media_device_id.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
+#include "content/public/common/service_manager_connection.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/permissions/permissions_data.h"
#include "media/audio/audio_system.h"
+#include "services/audio/public/cpp/audio_system_factory.h"
+#include "services/service_manager/public/cpp/connector.h"
#include "url/gurl.h"
#include "url/origin.h"
@@ -106,7 +109,7 @@ WebrtcAudioPrivateFunction::~WebrtcAudioPrivateFunction() {}
std::string WebrtcAudioPrivateFunction::CalculateHMAC(
const std::string& raw_id) {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
// We don't hash the default device description, and we always return
// "default" for the default device. There is code in SetActiveSink
@@ -130,9 +133,13 @@ std::string WebrtcAudioPrivateFunction::device_id_salt() const {
}
media::AudioSystem* WebrtcAudioPrivateFunction::GetAudioSystem() {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
- if (!audio_system_)
- audio_system_ = media::AudioSystem::CreateInstance();
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (!audio_system_) {
+ audio_system_ = audio::CreateAudioSystem(
+ content::ServiceManagerConnection::GetForProcess()
+ ->GetConnector()
+ ->Clone());
+ }
return audio_system_.get();
}
@@ -176,27 +183,17 @@ WebrtcAudioPrivateFunction::GetRenderProcessHostFromRequest(
bool WebrtcAudioPrivateGetSinksFunction::RunAsync() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
InitDeviceIDSalt();
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::BindOnce(&WebrtcAudioPrivateGetSinksFunction::
- GetOutputDeviceDescriptionsOnIOThread,
- this));
- return true;
-}
-
-void WebrtcAudioPrivateGetSinksFunction::
- GetOutputDeviceDescriptionsOnIOThread() {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
GetAudioSystem()->GetDeviceDescriptions(
- false, base::BindOnce(&WebrtcAudioPrivateGetSinksFunction::
- ReceiveOutputDeviceDescriptionsOnIOThread,
- this));
+ false,
+ base::BindOnce(
+ &WebrtcAudioPrivateGetSinksFunction::ReceiveOutputDeviceDescriptions,
+ this));
+ return true;
}
-void WebrtcAudioPrivateGetSinksFunction::
- ReceiveOutputDeviceDescriptionsOnIOThread(
- media::AudioDeviceDescriptions sink_devices) {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
+void WebrtcAudioPrivateGetSinksFunction::ReceiveOutputDeviceDescriptions(
+ 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;
@@ -205,15 +202,6 @@ void WebrtcAudioPrivateGetSinksFunction::
// TODO(joi): Add other parameters.
results->push_back(std::move(info));
}
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::BindOnce(&WebrtcAudioPrivateGetSinksFunction::DoneOnUIThread, this,
- base::Passed(&results)));
-}
-
-void WebrtcAudioPrivateGetSinksFunction::DoneOnUIThread(
- std::unique_ptr<SinkInfoVector> results) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
results_ = wap::GetSinks::Results::Create(*results);
SendResponse(true);
}
@@ -230,28 +218,17 @@ bool WebrtcAudioPrivateGetAssociatedSinkFunction::RunAsync() {
EXTENSION_FUNCTION_VALIDATE(params_.get());
InitDeviceIDSalt();
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::BindOnce(&WebrtcAudioPrivateGetAssociatedSinkFunction::
- GetInputDeviceDescriptionsOnIOThread,
- this));
-
- return true;
-}
-
-void WebrtcAudioPrivateGetAssociatedSinkFunction::
- GetInputDeviceDescriptionsOnIOThread() {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
GetAudioSystem()->GetDeviceDescriptions(
true, base::BindOnce(&WebrtcAudioPrivateGetAssociatedSinkFunction::
- ReceiveInputDeviceDescriptionsOnIOThread,
+ ReceiveInputDeviceDescriptions,
this));
+ return true;
}
void WebrtcAudioPrivateGetAssociatedSinkFunction::
- ReceiveInputDeviceDescriptionsOnIOThread(
+ ReceiveInputDeviceDescriptions(
media::AudioDeviceDescriptions source_devices) {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ 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);
@@ -269,29 +246,25 @@ void WebrtcAudioPrivateGetAssociatedSinkFunction::
}
}
if (raw_source_id.empty()) {
- CalculateHMACOnIOThread(std::string());
+ CalculateHMACAndReply(base::nullopt);
return;
}
GetAudioSystem()->GetAssociatedOutputDeviceID(
raw_source_id,
base::BindOnce(
- &WebrtcAudioPrivateGetAssociatedSinkFunction::CalculateHMACOnIOThread,
+ &WebrtcAudioPrivateGetAssociatedSinkFunction::CalculateHMACAndReply,
this));
}
-void WebrtcAudioPrivateGetAssociatedSinkFunction::CalculateHMACOnIOThread(
+void WebrtcAudioPrivateGetAssociatedSinkFunction::CalculateHMACAndReply(
const base::Optional<std::string>& raw_sink_id) {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ 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.
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::BindOnce(
- &WebrtcAudioPrivateGetAssociatedSinkFunction::ReceiveHMACOnUIThread,
- this, CalculateHMAC(raw_sink_id.value_or(std::string()))));
+ Reply(CalculateHMAC(raw_sink_id.value_or(std::string())));
}
-void WebrtcAudioPrivateGetAssociatedSinkFunction::ReceiveHMACOnUIThread(
+void WebrtcAudioPrivateGetAssociatedSinkFunction::Reply(
const std::string& associated_sink_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (associated_sink_id == media::AudioDeviceDescription::kDefaultDeviceId) {
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 9f022383542..7e85f87df1b 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
@@ -59,7 +59,6 @@ class WebrtcAudioPrivateFunction : public ChromeAsyncExtensionFunction {
protected:
// Calculates a single HMAC, using the extension ID as the security origin.
- // Call only on IO thread.
std::string CalculateHMAC(const std::string& raw_id);
// Initializes |device_id_salt_|. Must be called on the UI thread,
@@ -98,16 +97,10 @@ class WebrtcAudioPrivateGetSinksFunction : public WebrtcAudioPrivateFunction {
bool RunAsync() override;
- // Requests output device descriptions.
- void GetOutputDeviceDescriptionsOnIOThread();
-
- // Receives output device descriptions, calculates HMACs for them and replies
- // to UI thread with DoneOnUIThread().
- void ReceiveOutputDeviceDescriptionsOnIOThread(
+ // Receives output device descriptions, calculates HMACs for them and sends
+ // the response.
+ void ReceiveOutputDeviceDescriptions(
media::AudioDeviceDescriptions sink_devices);
-
- // Sends the response.
- void DoneOnUIThread(std::unique_ptr<SinkInfoVector> results);
};
class WebrtcAudioPrivateGetAssociatedSinkFunction
@@ -125,23 +118,17 @@ class WebrtcAudioPrivateGetAssociatedSinkFunction
// UI thread: Entry point, posts GetInputDeviceDescriptions() to IO thread.
bool RunAsync() override;
- // Enumerates input devices.
- void GetInputDeviceDescriptionsOnIOThread();
-
// 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 ReceiveInputDeviceDescriptionsOnIOThread(
+ void ReceiveInputDeviceDescriptions(
media::AudioDeviceDescriptions source_devices);
- // IO thread: Receives the raw sink ID, calculates HMAC and replies to IO
- // thread with ReceiveHMACOnUIThread().
- void CalculateHMACOnIOThread(const base::Optional<std::string>& raw_sink_id);
+ // Receives the raw sink ID, calculates HMAC and calls Reply().
+ void CalculateHMACAndReply(const base::Optional<std::string>& raw_sink_id);
// Receives the associated sink ID as HMAC and sends the response.
- void ReceiveHMACOnUIThread(const std::string& hmac);
+ void Reply(const std::string& hmac);
- // Initialized on UI thread in RunAsync(), read-only access on IO thread - no
- // locking needed.
std::unique_ptr<api::webrtc_audio_private::GetAssociatedSink::Params> params_;
};
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 b09a14d684f..5b8a78852b1 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
@@ -25,12 +25,13 @@
#include "chrome/browser/media/webrtc/webrtc_log_uploader.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/features.h"
+#include "chrome/common/buildflags.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/media_device_id.h"
#include "content/public/browser/web_contents.h"
+#include "content/public/common/service_manager_connection.h"
#include "content/public/test/browser_test_utils.h"
#include "extensions/common/permissions/permission_set.h"
#include "extensions/common/permissions/permissions_data.h"
@@ -38,6 +39,8 @@
#include "media/audio/audio_system.h"
#include "media/base/media_switches.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "services/audio/public/cpp/audio_system_factory.h"
+#include "services/service_manager/public/cpp/connector.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_WIN)
@@ -63,8 +66,10 @@ namespace {
void GetAudioDeviceDescriptions(bool for_input,
AudioDeviceDescriptions* device_descriptions) {
base::RunLoop run_loop;
- std::unique_ptr<media::AudioSystem> audio_system =
- media::AudioSystem::CreateInstance();
+ std::unique_ptr<media::AudioSystem> audio_system = audio::CreateAudioSystem(
+ content::ServiceManagerConnection::GetForProcess()
+ ->GetConnector()
+ ->Clone());
audio_system->GetDeviceDescriptions(
for_input,
base::BindOnce(
@@ -73,7 +78,7 @@ void GetAudioDeviceDescriptions(bool for_input,
*result = std::move(received);
finished_callback.Run();
},
- base::Passed(run_loop.QuitClosure()), device_descriptions));
+ run_loop.QuitClosure(), device_descriptions));
run_loop.Run();
}
diff --git a/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc b/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc
index 233fb3e34d2..af8a700f354 100644
--- a/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc
+++ b/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc
@@ -238,7 +238,7 @@ bool WebrtcLoggingPrivateSetMetaDataFunction::RunAsync() {
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::BindOnce(&WebRtcLoggingHandlerHost::SetMetaData,
webrtc_logging_handler_host,
- base::Passed(&meta_data), callback));
+ std::move(meta_data), callback));
return true;
}
diff --git a/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_browsertest.cc b/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_browsertest.cc
index c1a9c2fd7ae..07b4d99c5b0 100644
--- a/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_browsertest.cc
+++ b/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_browsertest.cc
@@ -7,8 +7,8 @@
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "chrome/browser/apps/app_browsertest_util.h"
-#include "chrome/browser/media/webrtc/webrtc_log_list.h"
#include "chrome/common/chrome_switches.h"
+#include "components/webrtc_logging/browser/log_list.h"
class WebrtcLoggingPrivateApiBrowserTest
: public extensions::PlatformAppBrowserTest {
@@ -17,7 +17,7 @@ class WebrtcLoggingPrivateApiBrowserTest
~WebrtcLoggingPrivateApiBrowserTest() override = default;
base::FilePath webrtc_logs_path() {
- return WebRtcLogList::GetWebRtcLogDirectoryForBrowserContextPath(
+ return webrtc_logging::LogList::GetWebRtcLogDirectoryForBrowserContextPath(
profile()->GetPath());
}
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 1b82d5446c9..71f464d8fb9 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
@@ -221,7 +221,8 @@ WebstorePrivateBeginInstallWithManifest3Function::Run() {
if (!icon_url.is_empty()) {
loader_factory =
content::BrowserContext::GetDefaultStoragePartition(browser_context())
- ->GetURLLoaderFactoryForBrowserProcess();
+ ->GetURLLoaderFactoryForBrowserProcess()
+ .get();
}
scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper(
diff --git a/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc b/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
index 85228f0e23a..620a8b9b3a3 100644
--- a/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
+++ b/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
@@ -22,9 +22,9 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension_test_util.h"
-#include "chrome/common/features.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/gpu_data_manager.h"
#include "content/public/browser/notification_observer.h"
diff --git a/chromium/chrome/browser/media/BUILD.gn b/chromium/chrome/browser/media/BUILD.gn
index 434a2623f7d..00bd50db06a 100644
--- a/chromium/chrome/browser/media/BUILD.gn
+++ b/chromium/chrome/browser/media/BUILD.gn
@@ -12,7 +12,7 @@ mojom("mojo_bindings") {
public_deps = [
"//mojo/common:common_custom_types",
- "//url/mojo:url_mojom_gurl",
+ "//url/mojom:url_mojom_gurl",
]
}
diff --git a/chromium/chrome/browser/media/media_engagement_score_details.mojom b/chromium/chrome/browser/media/media_engagement_score_details.mojom
index 25a7e9bd076..6ee85659821 100644
--- a/chromium/chrome/browser/media/media_engagement_score_details.mojom
+++ b/chromium/chrome/browser/media/media_engagement_score_details.mojom
@@ -4,7 +4,7 @@
module media.mojom;
-import "url/mojo/url.mojom";
+import "url/mojom/url.mojom";
struct MediaEngagementScoreDetails {
url.mojom.Url origin;
@@ -23,6 +23,9 @@ struct MediaEngagementScoreDetails {
// Data used for experiments.
int32 audible_playbacks;
int32 significant_playbacks;
+
+ // How many time the score changed `is_high` status.
+ int32 high_score_changes;
};
struct MediaEngagementConfig {
diff --git a/chromium/chrome/browser/media/router/BUILD.gn b/chromium/chrome/browser/media/router/BUILD.gn
index 8f58165a4e8..ec64441b2c0 100644
--- a/chromium/chrome/browser/media/router/BUILD.gn
+++ b/chromium/chrome/browser/media/router/BUILD.gn
@@ -64,6 +64,7 @@ static_library("router") {
"discovery",
"//extensions/browser",
"//mojo/public/cpp/bindings",
+ "//ui/base:ui_features",
]
sources += [
"event_page_request_manager.cc",
@@ -80,10 +81,22 @@ static_library("router") {
"mojo/media_router_mojo_impl.h",
"mojo/media_router_mojo_metrics.cc",
"mojo/media_router_mojo_metrics.h",
+ "mojo/media_sink_service_status.cc",
+ "mojo/media_sink_service_status.h",
"presentation/independent_otr_profile_manager.cc",
"presentation/independent_otr_profile_manager.h",
"presentation/presentation_navigation_policy.cc",
"presentation/presentation_navigation_policy.h",
+ "providers/cast/cast_app_availability_tracker.cc",
+ "providers/cast/cast_app_availability_tracker.h",
+ "providers/cast/cast_app_discovery_service.cc",
+ "providers/cast/cast_app_discovery_service.h",
+ "providers/cast/cast_media_route_provider.cc",
+ "providers/cast/cast_media_route_provider.h",
+ "providers/cast/cast_media_route_provider_metrics.cc",
+ "providers/cast/cast_media_route_provider_metrics.h",
+ "providers/cast/chrome_cast_message_handler.cc",
+ "providers/cast/chrome_cast_message_handler.h",
"providers/cast/dual_media_sink_service.cc",
"providers/cast/dual_media_sink_service.h",
"providers/extension/extension_media_route_provider_proxy.cc",
@@ -130,6 +143,8 @@ static_library("test_support") {
"test/mock_dns_sd_registry.h",
"test/mock_mojo_media_router.cc",
"test/mock_mojo_media_router.h",
+ "test/noop_dual_media_sink_service.cc",
+ "test/noop_dual_media_sink_service.h",
]
}
}
diff --git a/chromium/chrome/browser/net/chrome_mojo_proxy_resolver_factory.h b/chromium/chrome/browser/net/chrome_mojo_proxy_resolver_factory.h
index 8e5199b05e1..ec743c3d1d7 100644
--- a/chromium/chrome/browser/net/chrome_mojo_proxy_resolver_factory.h
+++ b/chromium/chrome/browser/net/chrome_mojo_proxy_resolver_factory.h
@@ -9,9 +9,9 @@
#include <string>
#include "base/macros.h"
-#include "services/proxy_resolver/public/interfaces/proxy_resolver.mojom.h"
+#include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
-#include "services/service_manager/public/interfaces/connector.mojom.h"
+#include "services/service_manager/public/mojom/connector.mojom.h"
// ProxyResolverFactory that acts as a proxy to the proxy resolver service.
// Starts the service as needed, and maintains no active mojo pipes to it,
diff --git a/chromium/chrome/browser/printing/background_printing_manager.cc b/chromium/chrome/browser/printing/background_printing_manager.cc
index d1ca0dcc18e..a204adf02be 100644
--- a/chromium/chrome/browser/printing/background_printing_manager.cc
+++ b/chromium/chrome/browser/printing/background_printing_manager.cc
@@ -72,7 +72,7 @@ void BackgroundPrintingManager::OwnPrintPreviewDialog(
CHECK(!HasPrintPreviewDialog(preview_dialog));
printing_contents_map_[preview_dialog] =
- base::MakeUnique<Observer>(this, preview_dialog);
+ std::make_unique<Observer>(this, preview_dialog);
// Watch for print jobs finishing. Everything else is watched for by the
// Observer. TODO(avi, cait): finish the job of removing this last
diff --git a/chromium/chrome/browser/printing/cloud_print/cloud_print_proxy_service_unittest.cc b/chromium/chrome/browser/printing/cloud_print/cloud_print_proxy_service_unittest.cc
index 6de22a51161..3edbd44c2ab 100644
--- a/chromium/chrome/browser/printing/cloud_print/cloud_print_proxy_service_unittest.cc
+++ b/chromium/chrome/browser/printing/cloud_print/cloud_print_proxy_service_unittest.cc
@@ -242,7 +242,7 @@ TEST_F(CloudPrintProxyPolicyTest, StartWithNoPolicyProxyDisabled) {
sync_preferences::TestingPrefServiceSyncable* prefs =
profile_.GetTestingPrefService();
prefs->SetUserPref(prefs::kCloudPrintEmail,
- base::MakeUnique<base::Value>(
+ std::make_unique<base::Value>(
MockServiceProcessControl::EnabledUserId()));
service.Initialize();
@@ -260,7 +260,7 @@ TEST_F(CloudPrintProxyPolicyTest, StartWithNoPolicyProxyEnabled) {
sync_preferences::TestingPrefServiceSyncable* prefs =
profile_.GetTestingPrefService();
prefs->SetUserPref(prefs::kCloudPrintEmail,
- base::MakeUnique<base::Value>(std::string()));
+ std::make_unique<base::Value>(std::string()));
service.Initialize();
service.RefreshStatusFromService();
@@ -279,9 +279,9 @@ TEST_F(CloudPrintProxyPolicyTest, StartWithPolicySetProxyDisabled) {
sync_preferences::TestingPrefServiceSyncable* prefs =
profile_.GetTestingPrefService();
prefs->SetUserPref(prefs::kCloudPrintEmail,
- base::MakeUnique<base::Value>(std::string()));
+ std::make_unique<base::Value>(std::string()));
prefs->SetManagedPref(prefs::kCloudPrintProxyEnabled,
- base::MakeUnique<base::Value>(false));
+ std::make_unique<base::Value>(false));
service.Initialize();
@@ -298,9 +298,9 @@ TEST_F(CloudPrintProxyPolicyTest, StartWithPolicySetProxyEnabled) {
sync_preferences::TestingPrefServiceSyncable* prefs =
profile_.GetTestingPrefService();
prefs->SetUserPref(prefs::kCloudPrintEmail,
- base::MakeUnique<base::Value>(std::string()));
+ std::make_unique<base::Value>(std::string()));
prefs->SetManagedPref(prefs::kCloudPrintProxyEnabled,
- base::MakeUnique<base::Value>(false));
+ std::make_unique<base::Value>(false));
service.Initialize();
@@ -318,7 +318,7 @@ TEST_F(CloudPrintProxyPolicyTest, StartWithNoPolicyProxyDisabledThenSetPolicy) {
sync_preferences::TestingPrefServiceSyncable* prefs =
profile_.GetTestingPrefService();
prefs->SetUserPref(prefs::kCloudPrintEmail,
- base::MakeUnique<base::Value>(
+ std::make_unique<base::Value>(
MockServiceProcessControl::EnabledUserId()));
service.Initialize();
@@ -326,7 +326,7 @@ TEST_F(CloudPrintProxyPolicyTest, StartWithNoPolicyProxyDisabledThenSetPolicy) {
EXPECT_EQ(std::string(), prefs->GetString(prefs::kCloudPrintEmail));
prefs->SetManagedPref(prefs::kCloudPrintProxyEnabled,
- base::MakeUnique<base::Value>(false));
+ std::make_unique<base::Value>(false));
EXPECT_EQ(std::string(), prefs->GetString(prefs::kCloudPrintEmail));
}
@@ -341,7 +341,7 @@ TEST_F(CloudPrintProxyPolicyTest, StartWithNoPolicyProxyEnabledThenSetPolicy) {
sync_preferences::TestingPrefServiceSyncable* prefs =
profile_.GetTestingPrefService();
prefs->SetUserPref(prefs::kCloudPrintEmail,
- base::MakeUnique<base::Value>(std::string()));
+ std::make_unique<base::Value>(std::string()));
service.Initialize();
service.RefreshStatusFromService();
@@ -350,7 +350,7 @@ TEST_F(CloudPrintProxyPolicyTest, StartWithNoPolicyProxyEnabledThenSetPolicy) {
prefs->GetString(prefs::kCloudPrintEmail));
prefs->SetManagedPref(prefs::kCloudPrintProxyEnabled,
- base::MakeUnique<base::Value>(false));
+ std::make_unique<base::Value>(false));
EXPECT_EQ(std::string(), prefs->GetString(prefs::kCloudPrintEmail));
EXPECT_TRUE(service.GetMockCloudPrintProxy().has_been_disabled());
@@ -367,9 +367,9 @@ TEST_F(CloudPrintProxyPolicyTest,
sync_preferences::TestingPrefServiceSyncable* prefs =
profile_.GetTestingPrefService();
prefs->SetUserPref(prefs::kCloudPrintEmail,
- base::MakeUnique<base::Value>(std::string()));
+ std::make_unique<base::Value>(std::string()));
prefs->SetManagedPref(prefs::kCloudPrintProxyEnabled,
- base::MakeUnique<base::Value>(false));
+ std::make_unique<base::Value>(false));
service.Initialize();
@@ -389,9 +389,9 @@ TEST_F(CloudPrintProxyPolicyTest,
sync_preferences::TestingPrefServiceSyncable* prefs =
profile_.GetTestingPrefService();
prefs->SetUserPref(prefs::kCloudPrintEmail,
- base::MakeUnique<base::Value>(std::string()));
+ std::make_unique<base::Value>(std::string()));
prefs->SetManagedPref(prefs::kCloudPrintProxyEnabled,
- base::MakeUnique<base::Value>(false));
+ std::make_unique<base::Value>(false));
service.Initialize();
@@ -411,7 +411,7 @@ TEST_F(CloudPrintProxyPolicyTest, StartWithNoPolicyProxyDisabledThenEnable) {
sync_preferences::TestingPrefServiceSyncable* prefs =
profile_.GetTestingPrefService();
prefs->SetUserPref(prefs::kCloudPrintEmail,
- base::MakeUnique<base::Value>(
+ std::make_unique<base::Value>(
MockServiceProcessControl::EnabledUserId()));
service.Initialize();
@@ -435,9 +435,9 @@ TEST_F(CloudPrintProxyPolicyTest,
sync_preferences::TestingPrefServiceSyncable* prefs =
profile_.GetTestingPrefService();
prefs->SetUserPref(prefs::kCloudPrintEmail,
- base::MakeUnique<base::Value>(std::string()));
+ std::make_unique<base::Value>(std::string()));
prefs->SetManagedPref(prefs::kCloudPrintProxyEnabled,
- base::MakeUnique<base::Value>(false));
+ std::make_unique<base::Value>(false));
service.Initialize();
diff --git a/chromium/chrome/browser/printing/cloud_print/gcd_api_flow_unittest.cc b/chromium/chrome/browser/printing/cloud_print/gcd_api_flow_unittest.cc
index 161e082abe0..2f5c919f387 100644
--- a/chromium/chrome/browser/printing/cloud_print/gcd_api_flow_unittest.cc
+++ b/chromium/chrome/browser/printing/cloud_print/gcd_api_flow_unittest.cc
@@ -59,12 +59,12 @@ class GCDApiFlowTest : public testing::Test {
request_context_.get());
token_service_.AddAccount(account_id_);
- std::unique_ptr<MockDelegate> delegate = base::MakeUnique<MockDelegate>();
+ std::unique_ptr<MockDelegate> delegate = std::make_unique<MockDelegate>();
mock_delegate_ = delegate.get();
EXPECT_CALL(*mock_delegate_, GetURL())
.WillRepeatedly(Return(
GURL("https://www.google.com/cloudprint/confirm?token=SomeToken")));
- gcd_flow_ = base::MakeUnique<GCDApiFlowImpl>(request_context_.get(),
+ gcd_flow_ = std::make_unique<GCDApiFlowImpl>(request_context_.get(),
&token_service_, account_id_);
gcd_flow_->Start(std::move(delegate));
}
diff --git a/chromium/chrome/browser/printing/cloud_print/privet_device_lister_impl.cc b/chromium/chrome/browser/printing/cloud_print/privet_device_lister_impl.cc
index 46c8ebfcbcc..5c92422bdad 100644
--- a/chromium/chrome/browser/printing/cloud_print/privet_device_lister_impl.cc
+++ b/chromium/chrome/browser/printing/cloud_print/privet_device_lister_impl.cc
@@ -14,21 +14,24 @@ PrivetDeviceListerImpl::PrivetDeviceListerImpl(
local_discovery::ServiceDiscoveryClient* service_discovery_client,
PrivetDeviceLister::Delegate* delegate)
: delegate_(delegate),
- device_lister_(this, service_discovery_client, kPrivetDefaultDeviceType) {
-}
+ device_lister_(local_discovery::ServiceDiscoveryDeviceLister::Create(
+ this,
+ service_discovery_client,
+ kPrivetDefaultDeviceType)) {}
PrivetDeviceListerImpl::~PrivetDeviceListerImpl() {
}
void PrivetDeviceListerImpl::Start() {
- device_lister_.Start();
+ device_lister_->Start();
}
void PrivetDeviceListerImpl::DiscoverNewDevices() {
- device_lister_.DiscoverNewDevices();
+ device_lister_->DiscoverNewDevices();
}
void PrivetDeviceListerImpl::OnDeviceChanged(
+ const std::string& service_type,
bool added,
const local_discovery::ServiceDescription& service_description) {
if (!delegate_)
@@ -38,12 +41,14 @@ void PrivetDeviceListerImpl::OnDeviceChanged(
DeviceDescription(service_description));
}
-void PrivetDeviceListerImpl::OnDeviceRemoved(const std::string& service_name) {
+void PrivetDeviceListerImpl::OnDeviceRemoved(const std::string& service_type,
+ const std::string& service_name) {
if (delegate_)
delegate_->DeviceRemoved(service_name);
}
-void PrivetDeviceListerImpl::OnDeviceCacheFlushed() {
+void PrivetDeviceListerImpl::OnDeviceCacheFlushed(
+ const std::string& service_type) {
if (delegate_)
delegate_->DeviceCacheFlushed();
}
diff --git a/chromium/chrome/browser/printing/cloud_print/privet_device_lister_impl.h b/chromium/chrome/browser/printing/cloud_print/privet_device_lister_impl.h
index 5eeff67fec1..5d91da407cb 100644
--- a/chromium/chrome/browser/printing/cloud_print/privet_device_lister_impl.h
+++ b/chromium/chrome/browser/printing/cloud_print/privet_device_lister_impl.h
@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_PRINTING_CLOUD_PRINT_PRIVET_DEVICE_LISTER_IMPL_H_
#define CHROME_BROWSER_PRINTING_CLOUD_PRINT_PRIVET_DEVICE_LISTER_IMPL_H_
+#include <memory>
#include <string>
#include "chrome/browser/local_discovery/service_discovery_device_lister.h"
@@ -32,14 +33,16 @@ class PrivetDeviceListerImpl
protected:
// ServiceDiscoveryDeviceLister:
void OnDeviceChanged(
+ const std::string& service_type,
bool added,
const local_discovery::ServiceDescription& service_description) override;
- void OnDeviceRemoved(const std::string& service_name) override;
- void OnDeviceCacheFlushed() override;
+ void OnDeviceRemoved(const std::string& service_type,
+ const std::string& service_name) override;
+ void OnDeviceCacheFlushed(const std::string& service_type) override;
private:
PrivetDeviceLister::Delegate* const delegate_;
- local_discovery::ServiceDiscoveryDeviceLister device_lister_;
+ std::unique_ptr<local_discovery::ServiceDiscoveryDeviceLister> device_lister_;
};
} // namespace cloud_print
diff --git a/chromium/chrome/browser/printing/cloud_print/privet_device_lister_unittest.cc b/chromium/chrome/browser/printing/cloud_print/privet_device_lister_unittest.cc
index 846e6586bc3..6d79bcf3b9a 100644
--- a/chromium/chrome/browser/printing/cloud_print/privet_device_lister_unittest.cc
+++ b/chromium/chrome/browser/printing/cloud_print/privet_device_lister_unittest.cc
@@ -120,7 +120,7 @@ class MockServiceDiscoveryClient : public ServiceDiscoveryClient {
std::unique_ptr<ServiceWatcher> CreateServiceWatcher(
const std::string& service_type,
const ServiceWatcher::UpdatedCallback& callback) override {
- return base::MakeUnique<MockServiceWatcher>(service_type, callback,
+ return std::make_unique<MockServiceWatcher>(service_type, callback,
mock_delegate_);
}
@@ -129,7 +129,7 @@ class MockServiceDiscoveryClient : public ServiceDiscoveryClient {
std::unique_ptr<ServiceResolver> CreateServiceResolver(
const std::string& service_name,
ServiceResolver::ResolveCompleteCallback callback) override {
- return base::MakeUnique<MockServiceResolver>(
+ return std::make_unique<MockServiceResolver>(
service_name, std::move(callback), mock_delegate_);
}
diff --git a/chromium/chrome/browser/printing/cloud_print/privet_http_impl.cc b/chromium/chrome/browser/printing/cloud_print/privet_http_impl.cc
index 156fe3ab55a..ec57fcf4268 100644
--- a/chromium/chrome/browser/printing/cloud_print/privet_http_impl.cc
+++ b/chromium/chrome/browser/printing/cloud_print/privet_http_impl.cc
@@ -510,8 +510,8 @@ void PrivetLocalPrintOperationImpl::StartConvertToPWG() {
data_.get(),
PwgRasterConverter::GetConversionSettings(capabilities_, page_size_),
PwgRasterConverter::GetBitmapSettings(capabilities_, ticket_),
- base::Bind(&PrivetLocalPrintOperationImpl::OnPWGRasterConverted,
- weak_factory_.GetWeakPtr()));
+ base::BindOnce(&PrivetLocalPrintOperationImpl::OnPWGRasterConverted,
+ weak_factory_.GetWeakPtr()));
}
void PrivetLocalPrintOperationImpl::OnSubmitdocResponse(
diff --git a/chromium/chrome/browser/printing/cloud_print/privet_notifications.cc b/chromium/chrome/browser/printing/cloud_print/privet_notifications.cc
index 771b1910378..a9bca9731e7 100644
--- a/chromium/chrome/browser/printing/cloud_print/privet_notifications.cc
+++ b/chromium/chrome/browser/printing/cloud_print/privet_notifications.cc
@@ -43,8 +43,8 @@
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/page_transition_types.h"
#include "ui/base/resource/resource_bundle.h"
-#include "ui/message_center/notification.h"
-#include "ui/message_center/notifier_id.h"
+#include "ui/message_center/public/cpp/notification.h"
+#include "ui/message_center/public/cpp/notifier_id.h"
#if BUILDFLAG(ENABLE_MDNS)
#include "chrome/browser/printing/cloud_print/privet_traffic_detector.h"
diff --git a/chromium/chrome/browser/printing/cloud_print/privet_notifications.h b/chromium/chrome/browser/printing/cloud_print/privet_notifications.h
index c322f0ee71f..80477dea1e4 100644
--- a/chromium/chrome/browser/printing/cloud_print/privet_notifications.h
+++ b/chromium/chrome/browser/printing/cloud_print/privet_notifications.h
@@ -14,7 +14,7 @@
#include "components/keyed_service/core/keyed_service.h"
#include "components/prefs/pref_member.h"
#include "net/net_features.h"
-#include "ui/message_center/notification_delegate.h"
+#include "ui/message_center/public/cpp/notification_delegate.h"
class Profile;
diff --git a/chromium/chrome/browser/printing/cloud_print/privet_notifications_unittest.cc b/chromium/chrome/browser/printing/cloud_print/privet_notifications_unittest.cc
index dad0f0ade18..bfb271c0691 100644
--- a/chromium/chrome/browser/printing/cloud_print/privet_notifications_unittest.cc
+++ b/chromium/chrome/browser/printing/cloud_print/privet_notifications_unittest.cc
@@ -9,6 +9,7 @@
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/notifications/notification_display_service_tester.h"
#include "chrome/browser/notifications/notification_test_util.h"
#include "chrome/browser/printing/cloud_print/privet_http_asynchronous_factory.h"
#include "chrome/browser/printing/cloud_print/privet_http_impl.h"
@@ -79,7 +80,7 @@ class MockPrivetHttpFactory : public PrivetHTTPAsynchronousFactory {
std::unique_ptr<PrivetHTTPResolution> CreatePrivetHTTP(
const std::string& name) override {
- return base::MakeUnique<MockResolution>(name, request_context_.get());
+ return std::make_unique<MockResolution>(name, request_context_.get());
}
private:
@@ -255,13 +256,15 @@ class PrivetNotificationsNotificationTest : public testing::Test {
void SetUp() override {
testing::Test::SetUp();
- profile_manager_ = base::MakeUnique<TestingProfileManager>(
+ profile_manager_ = std::make_unique<TestingProfileManager>(
TestingBrowserProcess::GetGlobal());
ASSERT_TRUE(profile_manager_->SetUp());
profile_ = profile_manager_->CreateTestingProfile("test-user");
+ display_service_ =
+ std::make_unique<NotificationDisplayServiceTester>(profile_);
TestingBrowserProcess::GetGlobal()->SetNotificationUIManager(
- base::MakeUnique<StubNotificationUIManager>());
+ std::make_unique<StubNotificationUIManager>());
}
void TearDown() override {
@@ -269,21 +272,17 @@ class PrivetNotificationsNotificationTest : public testing::Test {
testing::Test::TearDown();
}
- protected:
- StubNotificationUIManager* ui_manager() const {
- return static_cast<StubNotificationUIManager*>(
- TestingBrowserProcess::GetGlobal()->notification_ui_manager());
- }
-
Profile* profile() { return profile_; }
- private:
// The thread bundle must be first so it is destroyed last.
content::TestBrowserThreadBundle thread_bundle_;
+ std::unique_ptr<NotificationDisplayServiceTester> display_service_;
+
std::unique_ptr<TestingProfileManager> profile_manager_;
Profile* profile_;
+ private:
DISALLOW_COPY_AND_ASSIGN(PrivetNotificationsNotificationTest);
};
@@ -293,14 +292,20 @@ TEST_F(PrivetNotificationsNotificationTest, AddToCloudPrint) {
// The notification is added asynchronously.
base::RunLoop().RunUntilIdle();
- ASSERT_EQ(1U, ui_manager()->GetNotificationCount());
- const auto& notification = ui_manager()->GetNotificationAt(0);
- notification.ButtonClick(0 /* add */);
+ auto notifications = display_service_->GetDisplayedNotificationsForType(
+ NotificationHandler::Type::TRANSIENT);
+ ASSERT_EQ(1U, notifications.size());
+ display_service_->SimulateClick(NotificationHandler::Type::TRANSIENT,
+ notifications[0].id(), 0 /* add */,
+ base::nullopt);
EXPECT_EQ("chrome://devices/", service.open_tab_url().spec());
EXPECT_EQ(1U, service.open_tab_count());
EXPECT_EQ(0U, service.disable_notifications_count());
- EXPECT_EQ(0U, ui_manager()->GetNotificationCount());
+ EXPECT_EQ(0U, display_service_
+ ->GetDisplayedNotificationsForType(
+ NotificationHandler::Type::TRANSIENT)
+ .size());
}
TEST_F(PrivetNotificationsNotificationTest, DontShowAgain) {
@@ -309,14 +314,20 @@ TEST_F(PrivetNotificationsNotificationTest, DontShowAgain) {
// The notification is added asynchronously.
base::RunLoop().RunUntilIdle();
- ASSERT_EQ(1U, ui_manager()->GetNotificationCount());
- const auto& notification = ui_manager()->GetNotificationAt(0);
- notification.ButtonClick(1 /* don't show again */);
+ auto notifications = display_service_->GetDisplayedNotificationsForType(
+ NotificationHandler::Type::TRANSIENT);
+ ASSERT_EQ(1U, notifications.size());
+ display_service_->SimulateClick(NotificationHandler::Type::TRANSIENT,
+ notifications[0].id(),
+ 1 /* don't show again */, base::nullopt);
EXPECT_EQ("", service.open_tab_url().spec());
EXPECT_EQ(0U, service.open_tab_count());
EXPECT_EQ(1U, service.disable_notifications_count());
- EXPECT_EQ(0U, ui_manager()->GetNotificationCount());
+ EXPECT_EQ(0U, display_service_
+ ->GetDisplayedNotificationsForType(
+ NotificationHandler::Type::TRANSIENT)
+ .size());
}
} // namespace
diff --git a/chromium/chrome/browser/printing/pdf_to_emf_converter.cc b/chromium/chrome/browser/printing/pdf_to_emf_converter.cc
index 358341995d0..4715ec3bed8 100644
--- a/chromium/chrome/browser/printing/pdf_to_emf_converter.cc
+++ b/chromium/chrome/browser/printing/pdf_to_emf_converter.cc
@@ -26,8 +26,8 @@
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_restrictions.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "chrome/services/printing/public/interfaces/constants.mojom.h"
-#include "chrome/services/printing/public/interfaces/pdf_to_emf_converter.mojom.h"
+#include "chrome/services/printing/public/mojom/constants.mojom.h"
+#include "chrome/services/printing/public/mojom/pdf_to_emf_converter.mojom.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_data.h"
#include "content/public/common/service_manager_connection.h"
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 c0ab2f79d47..db7992cda30 100644
--- a/chromium/chrome/browser/printing/pdf_to_emf_converter_browsertest.cc
+++ b/chromium/chrome/browser/printing/pdf_to_emf_converter_browsertest.cc
@@ -119,7 +119,8 @@ IN_PROC_BROWSER_TEST_F(PDFToEMFConverterBrowserTest, TestSuccess) {
// A4 page format.
PdfRenderSettings pdf_settings(gfx::Rect(0, 0, 1700, 2200), gfx::Point(0, 0),
- /*dpi=*/200, /*autorotate=*/false,
+ /*dpi=*/gfx::Size(200, 200),
+ /*autorotate=*/false,
PdfRenderSettings::Mode::NORMAL);
constexpr int kNumberOfPages = 3;
diff --git a/chromium/chrome/browser/printing/print_browsertest.cc b/chromium/chrome/browser/printing/print_browsertest.cc
index 036681e9063..09f7e7b9c15 100644
--- a/chromium/chrome/browser/printing/print_browsertest.cc
+++ b/chromium/chrome/browser/printing/print_browsertest.cc
@@ -2,23 +2,47 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <memory>
+
#include "base/auto_reset.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
#include "base/run_loop.h"
+#include "base/threading/thread_restrictions.h"
+#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/printing/print_view_manager_common.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
+#include "chrome/common/chrome_paths.h"
#include "chrome/common/pref_names.h"
+#include "chrome/common/webui_url_constants.cc"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/prefs/pref_service.h"
+#include "components/printing/browser/print_composite_client.h"
+#include "components/printing/browser/print_manager_utils.h"
+#include "components/printing/common/print_messages.h"
+#include "content/public/browser/browser_message_filter.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
+#include "extensions/common/extension.h"
+#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
+#if defined(OS_CHROMEOS)
+#include "ui/aura/env.h"
+#endif
+
namespace printing {
namespace {
+static constexpr int kDefaultDocumentCookie = 1234;
+
class PrintPreviewObserver : PrintPreviewUI::TestingDelegate {
public:
PrintPreviewObserver() { PrintPreviewUI::SetDelegateForTesting(this); }
@@ -53,6 +77,69 @@ class PrintPreviewObserver : PrintPreviewUI::TestingDelegate {
base::RunLoop* run_loop_ = nullptr;
};
+class TestPrintFrameContentMsgFilter : public content::BrowserMessageFilter {
+ public:
+ TestPrintFrameContentMsgFilter(int document_cookie,
+ base::RepeatingClosure msg_callback)
+ : content::BrowserMessageFilter(PrintMsgStart),
+ document_cookie_(document_cookie),
+ task_runner_(base::SequencedTaskRunnerHandle::Get()),
+ msg_callback_(msg_callback) {}
+
+ bool OnMessageReceived(const IPC::Message& message) override {
+ // Only expect PrintHostMsg_DidPrintFrameContent message.
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(TestPrintFrameContentMsgFilter, message)
+ IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrintFrameContent, CheckMessage)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ EXPECT_TRUE(handled);
+ task_runner_->PostTask(FROM_HERE, msg_callback_);
+ return true;
+ }
+
+ private:
+ ~TestPrintFrameContentMsgFilter() override {}
+
+ void CheckMessage(int document_cookie,
+ const PrintHostMsg_DidPrintContent_Params& param) {
+ EXPECT_EQ(document_cookie, document_cookie_);
+ EXPECT_TRUE(param.metafile_data_handle.IsValid());
+ EXPECT_GT(param.data_size, 0u);
+ }
+
+ const int document_cookie_;
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+ base::RepeatingClosure msg_callback_;
+};
+
+class KillPrintFrameContentMsgFilter : public content::BrowserMessageFilter {
+ public:
+ explicit KillPrintFrameContentMsgFilter(content::RenderProcessHost* rph)
+ : content::BrowserMessageFilter(PrintMsgStart), rph_(rph) {}
+
+ bool OnMessageReceived(const IPC::Message& message) override {
+ // Only handle PrintHostMsg_DidPrintFrameContent message.
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(KillPrintFrameContentMsgFilter, message)
+ IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrintFrameContent, KillRenderProcess)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+ }
+
+ private:
+ ~KillPrintFrameContentMsgFilter() override {}
+
+ void KillRenderProcess(int document_cookie,
+ const PrintHostMsg_DidPrintContent_Params& param) {
+ base::ScopedAllowBaseSyncPrimitivesForTesting allow_sync_primitives;
+ rph_->Shutdown(0, true);
+ }
+
+ content::RenderProcessHost* rph_;
+};
+
} // namespace
class PrintBrowserTest : public InProcessBrowserTest {
@@ -60,6 +147,19 @@ class PrintBrowserTest : public InProcessBrowserTest {
PrintBrowserTest() {}
~PrintBrowserTest() override {}
+ void SetUp() override {
+ num_expected_messages_ = 1; // By default, only wait on one message.
+ num_received_messages_ = 0;
+ run_loop_.reset();
+ InProcessBrowserTest::SetUp();
+ }
+
+ void SetUpOnMainThread() override {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ content::SetupCrossSiteRedirector(embedded_test_server());
+ ASSERT_TRUE(embedded_test_server()->Start());
+ }
+
void PrintAndWaitUntilPreviewIsReady(bool print_only_selection) {
PrintPreviewObserver print_preview_observer;
@@ -69,6 +169,123 @@ class PrintBrowserTest : public InProcessBrowserTest {
print_preview_observer.WaitUntilPreviewIsReady();
}
+
+ // The following are helper functions for having a wait loop in the test and
+ // exit when all expected messages are received.
+ void SetNumExpectedMessages(unsigned int num) {
+ num_expected_messages_ = num;
+ }
+
+ void WaitUntilMessagesReceived() {
+ run_loop_ = std::make_unique<base::RunLoop>();
+ run_loop_->Run();
+ }
+
+ void CheckForQuit() {
+ if (++num_received_messages_ == num_expected_messages_) {
+ run_loop_->QuitWhenIdle();
+ }
+ }
+
+ void AddFilterForFrame(content::RenderFrameHost* frame_host) {
+ auto filter = base::MakeRefCounted<TestPrintFrameContentMsgFilter>(
+ kDefaultDocumentCookie,
+ base::BindRepeating(&PrintBrowserTest::CheckForQuit,
+ base::Unretained(this)));
+ frame_host->GetProcess()->AddFilter(filter.get());
+ }
+
+ PrintMsg_PrintFrame_Params GetDefaultPrintParams() {
+ PrintMsg_PrintFrame_Params params;
+ gfx::Rect area(800, 600);
+ params.printable_area = area;
+ params.document_cookie = kDefaultDocumentCookie;
+ return params;
+ }
+
+ private:
+ unsigned int num_expected_messages_;
+ unsigned int num_received_messages_;
+ std::unique_ptr<base::RunLoop> run_loop_;
+};
+
+class SitePerProcessPrintBrowserTest : public PrintBrowserTest {
+ public:
+ SitePerProcessPrintBrowserTest() {}
+ ~SitePerProcessPrintBrowserTest() override {}
+
+ // content::BrowserTestBase
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ content::IsolateAllSitesForTesting(command_line);
+ }
+};
+
+class IsolateOriginsPrintBrowserTest : public PrintBrowserTest {
+ public:
+ static constexpr char kIsolatedSite[] = "b.com";
+
+ IsolateOriginsPrintBrowserTest() {}
+ ~IsolateOriginsPrintBrowserTest() override {}
+
+ // content::BrowserTestBase
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ std::string origin_list =
+ embedded_test_server()->GetURL(kIsolatedSite, "/").spec();
+ command_line->AppendSwitchASCII(switches::kIsolateOrigins, origin_list);
+ }
+
+ void SetUpOnMainThread() override {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ }
+};
+
+constexpr char IsolateOriginsPrintBrowserTest::kIsolatedSite[];
+
+class PrintExtensionBrowserTest : public ExtensionBrowserTest {
+ public:
+ PrintExtensionBrowserTest() {}
+ ~PrintExtensionBrowserTest() override {}
+
+ void PrintAndWaitUntilPreviewIsReady(bool print_only_selection) {
+ PrintPreviewObserver print_preview_observer;
+
+ printing::StartPrint(browser()->tab_strip_model()->GetActiveWebContents(),
+ /*print_preview_disabled=*/false,
+ print_only_selection);
+
+ print_preview_observer.WaitUntilPreviewIsReady();
+ }
+
+ void LoadExtensionAndNavigateToOptionPage() {
+ const extensions::Extension* extension = nullptr;
+ {
+ base::ScopedAllowBlockingForTesting allow_blocking;
+ base::FilePath test_data_dir;
+ PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
+ extension = LoadExtension(
+ test_data_dir.AppendASCII("printing").AppendASCII("test_extension"));
+ ASSERT_TRUE(extension);
+ }
+
+ GURL url(chrome::kChromeUIExtensionsURL);
+ std::string query =
+ base::StringPrintf("options=%s", extension->id().c_str());
+ GURL::Replacements replacements;
+ replacements.SetQueryStr(query);
+ url = url.ReplaceComponents(replacements);
+ ui_test_utils::NavigateToURL(browser(), url);
+ }
+};
+
+class SitePerProcessPrintExtensionBrowserTest
+ : public PrintExtensionBrowserTest {
+ public:
+ // content::BrowserTestBase
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ content::IsolateAllSitesForTesting(command_line);
+ }
};
// Printing only a selection containing iframes is partially supported.
@@ -76,11 +293,275 @@ class PrintBrowserTest : public InProcessBrowserTest {
// preview is rendered (i.e. no timeout in the test).
// This test shouldn't crash. See https://crbug.com/732780.
IN_PROC_BROWSER_TEST_F(PrintBrowserTest, SelectionContainsIframe) {
- ASSERT_TRUE(embedded_test_server()->Start());
+ ASSERT_TRUE(embedded_test_server()->Started());
GURL url(embedded_test_server()->GetURL("/printing/selection_iframe.html"));
ui_test_utils::NavigateToURL(browser(), url);
PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/true);
}
+// Printing frame content for the main frame of a generic webpage.
+// This test passes when the printed result is sent back and checked in
+// TestPrintFrameContentMsgFilter::CheckMessage().
+IN_PROC_BROWSER_TEST_F(PrintBrowserTest, PrintFrameContent) {
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(embedded_test_server()->GetURL("/printing/test1.html"));
+ ui_test_utils::NavigateToURL(browser(), url);
+
+ content::WebContents* original_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ content::RenderFrameHost* rfh = original_contents->GetMainFrame();
+ AddFilterForFrame(rfh);
+
+ rfh->Send(new PrintMsg_PrintFrameContent(rfh->GetRoutingID(),
+ GetDefaultPrintParams()));
+
+ // The printed result will be received and checked in
+ // TestPrintFrameContentMsgFilter.
+ WaitUntilMessagesReceived();
+}
+
+// Printing frame content for a cross-site iframe.
+// This test passes when the iframe responds to the print message.
+// The response is checked in TestPrintFrameContentMsgFilter::CheckMessage().
+IN_PROC_BROWSER_TEST_F(PrintBrowserTest, PrintSubframeContent) {
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(
+ embedded_test_server()->GetURL("/printing/content_with_iframe.html"));
+ ui_test_utils::NavigateToURL(browser(), url);
+
+ content::WebContents* original_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_EQ(2u, original_contents->GetAllFrames().size());
+ content::RenderFrameHost* test_frame = original_contents->GetAllFrames()[1];
+ ASSERT_TRUE(test_frame);
+
+ AddFilterForFrame(test_frame);
+
+ test_frame->Send(new PrintMsg_PrintFrameContent(test_frame->GetRoutingID(),
+ GetDefaultPrintParams()));
+
+ // The printed result will be received and checked in
+ // TestPrintFrameContentMsgFilter.
+ WaitUntilMessagesReceived();
+}
+
+// Printing frame content with a cross-site iframe which also has a cross-site
+// iframe. The site reference chain is a.com --> b.com --> c.com.
+// This test passes when both cross-site frames are printed and their
+// responses which are checked in
+// TestPrintFrameContentMsgFilter::CheckMessage().
+IN_PROC_BROWSER_TEST_F(PrintBrowserTest, PrintSubframeChain) {
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(embedded_test_server()->GetURL(
+ "/printing/content_with_iframe_chain.html"));
+ ui_test_utils::NavigateToURL(browser(), url);
+ content::WebContents* original_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_EQ(3u, original_contents->GetAllFrames().size());
+ // Create composite client so subframe print message can be forwarded.
+ PrintCompositeClient::CreateForWebContents(original_contents);
+
+ content::RenderFrameHost* main_frame = original_contents->GetMainFrame();
+ content::RenderFrameHost* child_frame = content::ChildFrameAt(main_frame, 0);
+ ASSERT_TRUE(child_frame);
+ ASSERT_NE(child_frame, main_frame);
+ bool oopif_enabled = child_frame->GetProcess() != main_frame->GetProcess();
+
+ content::RenderFrameHost* grandchild_frame =
+ content::ChildFrameAt(child_frame, 0);
+ ASSERT_TRUE(grandchild_frame);
+ ASSERT_NE(grandchild_frame, child_frame);
+ if (oopif_enabled) {
+ ASSERT_NE(grandchild_frame->GetProcess(), child_frame->GetProcess());
+ ASSERT_NE(grandchild_frame->GetProcess(), main_frame->GetProcess());
+ }
+
+ AddFilterForFrame(main_frame);
+ if (oopif_enabled) {
+ AddFilterForFrame(child_frame);
+ AddFilterForFrame(grandchild_frame);
+ }
+
+ main_frame->Send(new PrintMsg_PrintFrameContent(main_frame->GetRoutingID(),
+ GetDefaultPrintParams()));
+
+ // The printed result will be received and checked in
+ // TestPrintFrameContentMsgFilter.
+ SetNumExpectedMessages(oopif_enabled ? 3 : 1);
+ WaitUntilMessagesReceived();
+}
+
+// Printing frame content with a cross-site iframe who also has a cross site
+// iframe, but this iframe resides in the same site as the main frame.
+// The site reference loop is a.com --> b.com --> a.com.
+// This test passes when both cross-site frames are printed and send back
+// responses which are checked in
+// TestPrintFrameContentMsgFilter::CheckMessage().
+IN_PROC_BROWSER_TEST_F(PrintBrowserTest, PrintSubframeABA) {
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(embedded_test_server()->GetURL(
+ "a.com", "/printing/content_with_iframe_loop.html"));
+ ui_test_utils::NavigateToURL(browser(), url);
+ content::WebContents* original_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_EQ(3u, original_contents->GetAllFrames().size());
+ // Create composite client so subframe print message can be forwarded.
+ PrintCompositeClient::CreateForWebContents(original_contents);
+
+ content::RenderFrameHost* main_frame = original_contents->GetMainFrame();
+ content::RenderFrameHost* child_frame = content::ChildFrameAt(main_frame, 0);
+ ASSERT_TRUE(child_frame);
+ ASSERT_NE(child_frame, main_frame);
+ bool oopif_enabled = main_frame->GetProcess() != child_frame->GetProcess();
+
+ content::RenderFrameHost* grandchild_frame =
+ content::ChildFrameAt(child_frame, 0);
+ ASSERT_TRUE(grandchild_frame);
+ ASSERT_NE(grandchild_frame, child_frame);
+ // |grandchild_frame| is in the same site as |frame|, so whether OOPIF is
+ // enabled, they will be in the same process.
+ ASSERT_EQ(grandchild_frame->GetProcess(), main_frame->GetProcess());
+
+ AddFilterForFrame(main_frame);
+ if (oopif_enabled)
+ AddFilterForFrame(child_frame);
+
+ main_frame->Send(new PrintMsg_PrintFrameContent(main_frame->GetRoutingID(),
+ GetDefaultPrintParams()));
+
+ // The printed result will be received and checked in
+ // TestPrintFrameContentMsgFilter.
+ SetNumExpectedMessages(oopif_enabled ? 3 : 1);
+ WaitUntilMessagesReceived();
+}
+
+// Printing a web page with a dead subframe for site per process should succeed.
+// This test passes whenever the print preview is rendered. This should not be
+// a timed out test which indicates the print preview hung.
+IN_PROC_BROWSER_TEST_F(SitePerProcessPrintBrowserTest,
+ SubframeUnavailableBeforePrint) {
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(
+ embedded_test_server()->GetURL("/printing/content_with_iframe.html"));
+ ui_test_utils::NavigateToURL(browser(), url);
+
+ content::WebContents* original_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_EQ(2u, original_contents->GetAllFrames().size());
+ content::RenderFrameHost* test_frame = original_contents->GetAllFrames()[1];
+ ASSERT_TRUE(test_frame);
+ ASSERT_TRUE(test_frame->IsRenderFrameLive());
+ // Wait for the renderer to be down.
+ content::RenderProcessHostWatcher render_process_watcher(
+ test_frame->GetProcess(),
+ content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+ // Shutdown the subframe.
+ ASSERT_TRUE(test_frame->GetProcess()->Shutdown(0, false));
+ render_process_watcher.Wait();
+ ASSERT_FALSE(test_frame->IsRenderFrameLive());
+
+ PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/false);
+}
+
+// If a subframe dies during printing, the page printing should still succeed.
+// This test passes whenever the print preview is rendered. This should not be
+// a timed out test which indicates the print preview hung.
+IN_PROC_BROWSER_TEST_F(SitePerProcessPrintBrowserTest,
+ SubframeUnavailableDuringPrint) {
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(
+ embedded_test_server()->GetURL("/printing/content_with_iframe.html"));
+ ui_test_utils::NavigateToURL(browser(), url);
+
+ content::WebContents* original_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_EQ(2u, original_contents->GetAllFrames().size());
+ content::RenderFrameHost* subframe = original_contents->GetAllFrames()[1];
+ ASSERT_TRUE(subframe);
+ auto* subframe_rph = subframe->GetProcess();
+
+ auto filter =
+ base::MakeRefCounted<KillPrintFrameContentMsgFilter>(subframe_rph);
+ subframe_rph->AddFilter(filter.get());
+
+ PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/false);
+}
+
+// Printing preview a web page with an iframe from an isolated origin.
+// This test passes whenever the print preview is rendered. This should not be
+// a timed out test which indicates the print preview hung or crash.
+IN_PROC_BROWSER_TEST_F(IsolateOriginsPrintBrowserTest, PrintIsolatedSubframe) {
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(embedded_test_server()->GetURL(
+ "/printing/content_with_same_site_iframe.html"));
+ GURL isolated_url(
+ embedded_test_server()->GetURL(kIsolatedSite, "/printing/test1.html"));
+ ui_test_utils::NavigateToURL(browser(), url);
+
+ content::WebContents* original_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ EXPECT_TRUE(NavigateIframeToURL(original_contents, "iframe", isolated_url));
+
+ ASSERT_EQ(2u, original_contents->GetAllFrames().size());
+ auto* main_frame = original_contents->GetMainFrame();
+ auto* subframe = original_contents->GetAllFrames()[1];
+ ASSERT_NE(main_frame->GetProcess(), subframe->GetProcess());
+
+ PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/false);
+}
+
+// Printing preview a webpage.
+// Test that we won't use oopif printing by default, unless the
+// test is run with site-per-process flag enabled.
+IN_PROC_BROWSER_TEST_F(PrintBrowserTest, RegularPrinting) {
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(embedded_test_server()->GetURL("/printing/test1.html"));
+ ui_test_utils::NavigateToURL(browser(), url);
+
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kSitePerProcess)) {
+ EXPECT_TRUE(IsOopifEnabled());
+ } else {
+ EXPECT_FALSE(IsOopifEnabled());
+ }
+}
+
+// Printing preview a webpage with isolate-origins enabled.
+// Test that we will use oopif printing for this case.
+IN_PROC_BROWSER_TEST_F(IsolateOriginsPrintBrowserTest, OopifPrinting) {
+ ASSERT_TRUE(embedded_test_server()->Started());
+ GURL url(embedded_test_server()->GetURL("/printing/test1.html"));
+ ui_test_utils::NavigateToURL(browser(), url);
+
+ EXPECT_TRUE(IsOopifEnabled());
+}
+
+// Printing an extension option page.
+// The test should not crash or timeout.
+IN_PROC_BROWSER_TEST_F(PrintExtensionBrowserTest, PrintOptionPage) {
+#if defined(OS_CHROMEOS)
+ // Mus can not support this test now https://crbug.com/823782.
+ if (aura::Env::GetInstance()->mode() == aura::Env::Mode::MUS)
+ return;
+#endif
+
+ LoadExtensionAndNavigateToOptionPage();
+ PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/false);
+}
+
+// Printing an extension option page with site per process is enabled.
+// The test should not crash or timeout.
+IN_PROC_BROWSER_TEST_F(SitePerProcessPrintExtensionBrowserTest,
+ PrintOptionPage) {
+#if defined(OS_CHROMEOS)
+ // Mus can not support this test now https://crbug.com/823782.
+ if (aura::Env::GetInstance()->mode() == aura::Env::Mode::MUS)
+ return;
+#endif
+
+ LoadExtensionAndNavigateToOptionPage();
+ PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/false);
+}
+
} // namespace printing
diff --git a/chromium/chrome/browser/printing/print_job.cc b/chromium/chrome/browser/printing/print_job.cc
index 7bb027c7f57..9c91c3d2530 100644
--- a/chromium/chrome/browser/printing/print_job.cc
+++ b/chromium/chrome/browser/printing/print_job.cc
@@ -15,7 +15,6 @@
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/task_scheduler/post_task.h"
-#include "base/threading/sequenced_worker_pool.h"
#include "base/threading/thread_restrictions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
@@ -35,9 +34,9 @@ using base::TimeDelta;
namespace printing {
// Helper function to ensure |owner| is valid until at least |callback| returns.
-void HoldRefCallback(const scoped_refptr<PrintJobWorkerOwner>& owner,
- const base::Closure& callback) {
- callback.Run();
+void HoldRefCallback(scoped_refptr<PrintJobWorkerOwner> owner,
+ base::OnceClosure callback) {
+ std::move(callback).Run();
}
PrintJob::PrintJob()
@@ -86,6 +85,23 @@ void PrintJob::Initialize(PrintJobWorkerOwner* job,
content::Source<PrintJob>(this));
}
+#if defined(OS_WIN)
+void PrintJob::ResetPageMapping() {
+ std::vector<int> pages = PageRange::GetPages(settings_.ranges());
+ if (pages.empty())
+ return;
+
+ pdf_page_mapping_ = std::vector<int>(document_->page_count(), -1);
+ for (int page_number : pages) {
+ // Make sure the page is in range.
+ if (page_number >= 0 &&
+ page_number < static_cast<int>(pdf_page_mapping_.size())) {
+ pdf_page_mapping_[page_number] = page_number;
+ }
+ }
+}
+#endif
+
void PrintJob::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
@@ -125,11 +141,11 @@ void PrintJob::StartPrinting() {
}
// Real work is done in PrintJobWorker::StartPrinting().
- worker_->PostTask(FROM_HERE,
- base::Bind(&HoldRefCallback, base::WrapRefCounted(this),
- base::Bind(&PrintJobWorker::StartPrinting,
- base::Unretained(worker_.get()),
- base::RetainedRef(document_))));
+ worker_->PostTask(
+ FROM_HERE, base::BindOnce(&HoldRefCallback, base::WrapRefCounted(this),
+ base::BindOnce(&PrintJobWorker::StartPrinting,
+ base::Unretained(worker_.get()),
+ base::RetainedRef(document_))));
// Set the flag right now.
is_job_pending_ = true;
@@ -261,14 +277,15 @@ void PrintJob::StartPdfToEmfConversion(
bool print_text_with_gdi) {
DCHECK(!pdf_conversion_state_);
pdf_conversion_state_ =
- base::MakeUnique<PdfConversionState>(page_size, content_area);
- const int kPrinterDpi = settings().dpi();
- PdfRenderSettings settings(
- content_area, gfx::Point(0, 0), kPrinterDpi, /*autorotate=*/true,
+ std::make_unique<PdfConversionState>(page_size, content_area);
+ PdfRenderSettings render_settings(
+ content_area, gfx::Point(0, 0), settings().dpi_size(),
+ /*autorotate=*/true,
print_text_with_gdi ? PdfRenderSettings::Mode::GDI_TEXT
: PdfRenderSettings::Mode::NORMAL);
pdf_conversion_state_->Start(
- bytes, settings, base::BindOnce(&PrintJob::OnPdfConversionStarted, this));
+ bytes, render_settings,
+ base::BindOnce(&PrintJob::OnPdfConversionStarted, this));
}
void PrintJob::OnPdfConversionStarted(int page_count) {
@@ -281,7 +298,7 @@ void PrintJob::OnPdfConversionStarted(int page_count) {
}
pdf_conversion_state_->set_page_count(page_count);
pdf_conversion_state_->GetMorePages(
- base::Bind(&PrintJob::OnPdfPageConverted, this));
+ base::BindRepeating(&PrintJob::OnPdfPageConverted, this));
}
void PrintJob::OnPdfPageConverted(int page_number,
@@ -297,10 +314,14 @@ void PrintJob::OnPdfPageConverted(int page_number,
return;
}
- // Update the rendered document. It will send notifications to the listener.
- document_->SetPage(pdf_page_mapping_[page_number], std::move(metafile),
- scale_factor, pdf_conversion_state_->page_size(),
- pdf_conversion_state_->content_area());
+ // 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] != -1) {
+ // Update the rendered document. It will send notifications to the listener.
+ document_->SetPage(pdf_page_mapping_[page_number], std::move(metafile),
+ scale_factor, pdf_conversion_state_->page_size(),
+ pdf_conversion_state_->content_area());
+ }
pdf_conversion_state_->GetMorePages(
base::Bind(&PrintJob::OnPdfPageConverted, this));
@@ -311,14 +332,14 @@ void PrintJob::StartPdfToTextConversion(
const gfx::Size& page_size) {
DCHECK(!pdf_conversion_state_);
pdf_conversion_state_ =
- base::MakeUnique<PdfConversionState>(gfx::Size(), gfx::Rect());
- const int kPrinterDpi = settings().dpi();
+ std::make_unique<PdfConversionState>(gfx::Size(), gfx::Rect());
gfx::Rect page_area = gfx::Rect(0, 0, page_size.width(), page_size.height());
- PdfRenderSettings settings(page_area, gfx::Point(0, 0), kPrinterDpi,
- /*autorotate=*/true,
- PdfRenderSettings::Mode::TEXTONLY);
+ PdfRenderSettings render_settings(
+ page_area, gfx::Point(0, 0), settings().dpi_size(),
+ /*autorotate=*/true, PdfRenderSettings::Mode::TEXTONLY);
pdf_conversion_state_->Start(
- bytes, settings, base::BindOnce(&PrintJob::OnPdfConversionStarted, this));
+ bytes, render_settings,
+ base::BindOnce(&PrintJob::OnPdfConversionStarted, this));
}
void PrintJob::StartPdfToPostScriptConversion(
@@ -327,15 +348,16 @@ void PrintJob::StartPdfToPostScriptConversion(
const gfx::Point& physical_offsets,
bool ps_level2) {
DCHECK(!pdf_conversion_state_);
- pdf_conversion_state_ = base::MakeUnique<PdfConversionState>(
+ pdf_conversion_state_ = std::make_unique<PdfConversionState>(
gfx::Size(), gfx::Rect());
- const int kPrinterDpi = settings().dpi();
- PdfRenderSettings settings(
- content_area, physical_offsets, kPrinterDpi, /*autorotate=*/true,
+ PdfRenderSettings render_settings(
+ content_area, physical_offsets, settings().dpi_size(),
+ /*autorotate=*/true,
ps_level2 ? PdfRenderSettings::Mode::POSTSCRIPT_LEVEL2
: PdfRenderSettings::Mode::POSTSCRIPT_LEVEL3);
pdf_conversion_state_->Start(
- bytes, settings, base::BindOnce(&PrintJob::OnPdfConversionStarted, this));
+ bytes, render_settings,
+ base::BindOnce(&PrintJob::OnPdfConversionStarted, this));
}
#endif // defined(OS_WIN)
@@ -351,11 +373,12 @@ void PrintJob::UpdatePrintedDocument(PrintedDocument* new_document) {
if (worker_) {
DCHECK(!is_job_pending_);
// Sync the document with the worker.
- worker_->PostTask(FROM_HERE,
- base::Bind(&HoldRefCallback, base::WrapRefCounted(this),
- base::Bind(&PrintJobWorker::OnDocumentChanged,
- base::Unretained(worker_.get()),
- base::RetainedRef(document_))));
+ worker_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&HoldRefCallback, base::WrapRefCounted(this),
+ base::BindOnce(&PrintJobWorker::OnDocumentChanged,
+ base::Unretained(worker_.get()),
+ base::RetainedRef(document_))));
}
}
diff --git a/chromium/chrome/browser/printing/print_job.h b/chromium/chrome/browser/printing/print_job.h
index b594c3dec07..b12753257a0 100644
--- a/chromium/chrome/browser/printing/print_job.h
+++ b/chromium/chrome/browser/printing/print_job.h
@@ -31,8 +31,8 @@ class PrintedPage;
#endif
class PrinterQuery;
-void HoldRefCallback(const scoped_refptr<PrintJobWorkerOwner>& owner,
- const base::Closure& callback);
+void HoldRefCallback(scoped_refptr<PrintJobWorkerOwner> owner,
+ base::OnceClosure callback);
// Manages the print work for a specific document. Talks to the printer through
// PrintingContext through PrintJobWorker. Hides access to PrintingContext in a
@@ -53,6 +53,15 @@ class PrintJob : public PrintJobWorkerOwner,
const base::string16& name,
int page_count);
+#if defined(OS_WIN)
+ // 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
+ // dialog from the link in Print Preview on Windows and then sets a selection
+ // of pages, because all PDF pages will be converted, but only the user's
+ // selected pages should be sent to the printer. See https://crbug.com/823876.
+ void ResetPageMapping();
+#endif
+
// content::NotificationObserver implementation.
void Observe(int type,
const content::NotificationSource& source,
diff --git a/chromium/chrome/browser/printing/print_job_manager.cc b/chromium/chrome/browser/printing/print_job_manager.cc
index 35a516b69f9..7d4d9c6e79a 100644
--- a/chromium/chrome/browser/printing/print_job_manager.cc
+++ b/chromium/chrome/browser/printing/print_job_manager.cc
@@ -61,7 +61,8 @@ void PrintQueriesQueue::Shutdown() {
// corresponding PrintJob, so any pending preview requests are not covered
// by PrintJobManager::StopJobs and should be stopped explicitly.
for (auto& query : queries_to_stop) {
- query->PostTask(FROM_HERE, base::Bind(&PrinterQuery::StopWorker, query));
+ query->PostTask(FROM_HERE,
+ base::BindOnce(&PrinterQuery::StopWorker, query));
}
}
diff --git a/chromium/chrome/browser/printing/print_job_unittest.cc b/chromium/chrome/browser/printing/print_job_unittest.cc
index 481d3631694..fc49e35accc 100644
--- a/chromium/chrome/browser/printing/print_job_unittest.cc
+++ b/chromium/chrome/browser/printing/print_job_unittest.cc
@@ -45,7 +45,7 @@ class TestOwner : public PrintJobWorkerOwner {
PrintJobWorkerOwner* new_owner) override {
// We're screwing up here since we're calling worker from the main thread.
// That's fine for testing. It is actually simulating PrinterQuery behavior.
- auto worker = base::MakeUnique<TestPrintJobWorker>(new_owner);
+ auto worker = std::make_unique<TestPrintJobWorker>(new_owner);
EXPECT_TRUE(worker->Start());
worker->printing_context()->UseDefaultSettings();
settings_ = worker->printing_context()->settings();
diff --git a/chromium/chrome/browser/printing/print_job_worker.cc b/chromium/chrome/browser/printing/print_job_worker.cc
index 4f093cb61d6..5bee7d3a553 100644
--- a/chromium/chrome/browser/printing/print_job_worker.cc
+++ b/chromium/chrome/browser/printing/print_job_worker.cc
@@ -6,6 +6,7 @@
#include <memory>
#include <string>
+#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -102,11 +103,12 @@ void NotificationCallback(PrintJobWorkerOwner* print_job,
content::Details<JobEventDetails>(details));
}
-void PostOnOwnerThread(const scoped_refptr<PrintJobWorkerOwner>& owner,
- const PrintingContext::PrintSettingsCallback& callback,
+void PostOnOwnerThread(scoped_refptr<PrintJobWorkerOwner> owner,
+ PrintingContext::PrintSettingsCallback callback,
PrintingContext::Result result) {
- owner->PostTask(FROM_HERE, base::Bind(&HoldRefCallback, owner,
- base::Bind(callback, result)));
+ owner->PostTask(FROM_HERE,
+ base::BindOnce(&HoldRefCallback, owner,
+ base::BindOnce(std::move(callback), result)));
}
#if defined(OS_WIN)
@@ -178,16 +180,17 @@ void PrintJobWorker::GetSettings(bool ask_user_for_settings,
if (ask_user_for_settings) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
- base::BindOnce(&HoldRefCallback, base::WrapRefCounted(owner_),
- base::Bind(&PrintJobWorker::GetSettingsWithUI,
- base::Unretained(this), document_page_count,
- has_selection, is_scripted)));
+ base::BindOnce(
+ &HoldRefCallback, base::WrapRefCounted(owner_),
+ base::BindOnce(&PrintJobWorker::GetSettingsWithUI,
+ base::Unretained(this), document_page_count,
+ has_selection, is_scripted)));
} else {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(&HoldRefCallback, base::WrapRefCounted(owner_),
- base::Bind(&PrintJobWorker::UseDefaultSettings,
- base::Unretained(this))));
+ base::BindOnce(&PrintJobWorker::UseDefaultSettings,
+ base::Unretained(this))));
}
}
@@ -199,9 +202,23 @@ void PrintJobWorker::SetSettings(
BrowserThread::UI, FROM_HERE,
base::BindOnce(
&HoldRefCallback, base::WrapRefCounted(owner_),
- base::Bind(&PrintJobWorker::UpdatePrintSettings,
- base::Unretained(this), base::Passed(&new_settings))));
+ base::BindOnce(&PrintJobWorker::UpdatePrintSettings,
+ base::Unretained(this), std::move(new_settings))));
+}
+
+#if defined(OS_CHROMEOS)
+void PrintJobWorker::SetSettingsFromPOD(
+ std::unique_ptr<printing::PrintSettings> new_settings) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::BindOnce(
+ &HoldRefCallback, base::WrapRefCounted(owner_),
+ base::BindOnce(&PrintJobWorker::UpdatePrintSettingsFromPOD,
+ base::Unretained(this), std::move(new_settings))));
}
+#endif
void PrintJobWorker::UpdatePrintSettings(
std::unique_ptr<base::DictionaryValue> new_settings) {
@@ -211,6 +228,16 @@ void PrintJobWorker::UpdatePrintSettings(
GetSettingsDone(result);
}
+#if defined(OS_CHROMEOS)
+void PrintJobWorker::UpdatePrintSettingsFromPOD(
+ std::unique_ptr<printing::PrintSettings> new_settings) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ PrintingContext::Result result =
+ printing_context_->UpdatePrintSettingsFromPOD(std::move(new_settings));
+ GetSettingsDone(result);
+}
+#endif
+
void PrintJobWorker::GetSettingsDone(PrintingContext::Result result) {
// Most PrintingContext functions may start a message loop and process
// message recursively, so disable recursive task processing.
@@ -223,9 +250,9 @@ void PrintJobWorker::GetSettingsDone(PrintingContext::Result result) {
// PrintJob will create the new PrintedDocument.
owner_->PostTask(FROM_HERE,
- base::Bind(&PrintJobWorkerOwner::GetSettingsDone,
- base::WrapRefCounted(owner_),
- printing_context_->settings(), result));
+ base::BindOnce(&PrintJobWorkerOwner::GetSettingsDone,
+ base::WrapRefCounted(owner_),
+ printing_context_->settings(), result));
}
void PrintJobWorker::GetSettingsWithUI(
@@ -262,9 +289,9 @@ void PrintJobWorker::GetSettingsWithUI(
// weak_factory_ creates pointers valid only on owner_ thread.
printing_context_->AskUserForSettings(
document_page_count, has_selection, is_scripted,
- base::Bind(&PostOnOwnerThread, base::WrapRefCounted(owner_),
- base::Bind(&PrintJobWorker::GetSettingsDone,
- weak_factory_.GetWeakPtr())));
+ base::BindOnce(&PostOnOwnerThread, base::WrapRefCounted(owner_),
+ base::BindOnce(&PrintJobWorker::GetSettingsDone,
+ weak_factory_.GetWeakPtr())));
}
void PrintJobWorker::UseDefaultSettings() {
@@ -295,8 +322,7 @@ void PrintJobWorker::StartPrinting(PrintedDocument* new_document) {
return;
}
- // Try to print already cached data. It may already have been generated for
- // the print preview.
+ // This will start a loop to wait for the page data.
OnNewPage();
// Don't touch this anymore since the instance could be destroyed. It happens
// if all the pages are printed a one sweep and the client doesn't have a
@@ -391,9 +417,9 @@ bool PrintJobWorker::IsRunning() const {
}
bool PrintJobWorker::PostTask(const base::Location& from_here,
- const base::Closure& task) {
+ base::OnceClosure task) {
if (task_runner_.get())
- return task_runner_->PostTask(from_here, task);
+ return task_runner_->PostTask(from_here, std::move(task));
return false;
}
@@ -422,10 +448,10 @@ void PrintJobWorker::OnDocumentDone() {
return;
}
- owner_->PostTask(FROM_HERE,
- base::Bind(&NotificationCallback, base::RetainedRef(owner_),
- JobEventDetails::DOC_DONE, job_id,
- base::RetainedRef(document_)));
+ owner_->PostTask(FROM_HERE, base::BindOnce(&NotificationCallback,
+ base::RetainedRef(owner_),
+ JobEventDetails::DOC_DONE, job_id,
+ base::RetainedRef(document_)));
// Makes sure the variables are reinitialized.
document_ = NULL;
@@ -473,8 +499,8 @@ void PrintJobWorker::OnFailure() {
scoped_refptr<PrintJobWorkerOwner> handle(owner_);
owner_->PostTask(
- FROM_HERE, base::BindRepeating(
- &NotificationCallback, base::RetainedRef(owner_),
+ FROM_HERE,
+ base::BindOnce(&NotificationCallback, base::RetainedRef(owner_),
JobEventDetails::FAILED, 0, base::RetainedRef(document_)));
Cancel();
diff --git a/chromium/chrome/browser/printing/print_job_worker.h b/chromium/chrome/browser/printing/print_job_worker.h
index 11e2c85813e..4ad10b420a8 100644
--- a/chromium/chrome/browser/printing/print_job_worker.h
+++ b/chromium/chrome/browser/printing/print_job_worker.h
@@ -54,9 +54,15 @@ class PrintJobWorker {
bool is_scripted,
bool is_modifiable);
- // Set the new print settings.
+ // Set the new print settings from a dictionary value.
void SetSettings(std::unique_ptr<base::DictionaryValue> new_settings);
+#if defined(OS_CHROMEOS)
+ // Set the new print settings from a POD type.
+ void SetSettingsFromPOD(
+ std::unique_ptr<printing::PrintSettings> new_settings);
+#endif
+
// Starts the printing loop. Every pages are printed as soon as the data is
// available. Makes sure the new_document is the right one.
void StartPrinting(PrintedDocument* new_document);
@@ -76,7 +82,7 @@ class PrintJobWorker {
bool IsRunning() const;
// Posts the given task to be run.
- bool PostTask(const base::Location& from_here, const base::Closure& task);
+ bool PostTask(const base::Location& from_here, base::OnceClosure task);
// Signals the thread to exit in the near future.
void StopSoon();
@@ -129,6 +135,12 @@ class PrintJobWorker {
// Called on the UI thread to update the print settings.
void UpdatePrintSettings(std::unique_ptr<base::DictionaryValue> new_settings);
+#if defined(OS_CHROMEOS)
+ // Called on the UI thread to update the print settings.
+ void UpdatePrintSettingsFromPOD(
+ std::unique_ptr<printing::PrintSettings> new_settings);
+#endif
+
// Reports settings back to owner_.
void GetSettingsDone(PrintingContext::Result result);
diff --git a/chromium/chrome/browser/printing/print_job_worker_owner.cc b/chromium/chrome/browser/printing/print_job_worker_owner.cc
index 60b8473f0fb..decac596362 100644
--- a/chromium/chrome/browser/printing/print_job_worker_owner.cc
+++ b/chromium/chrome/browser/printing/print_job_worker_owner.cc
@@ -21,8 +21,8 @@ bool PrintJobWorkerOwner::RunsTasksInCurrentSequence() const {
}
bool PrintJobWorkerOwner::PostTask(const base::Location& from_here,
- const base::Closure& task) {
- return task_runner_->PostTask(from_here, task);
+ base::OnceClosure task) {
+ return task_runner_->PostTask(from_here, std::move(task));
}
} // namespace printing
diff --git a/chromium/chrome/browser/printing/print_job_worker_owner.h b/chromium/chrome/browser/printing/print_job_worker_owner.h
index d94ea6e1447..8625ac71be3 100644
--- a/chromium/chrome/browser/printing/print_job_worker_owner.h
+++ b/chromium/chrome/browser/printing/print_job_worker_owner.h
@@ -47,7 +47,7 @@ class PrintJobWorkerOwner
bool RunsTasksInCurrentSequence() const;
// Posts the given task to be run.
- bool PostTask(const base::Location& from_here, const base::Closure& task);
+ bool PostTask(const base::Location& from_here, base::OnceClosure task);
protected:
friend class base::RefCountedThreadSafe<PrintJobWorkerOwner>;
diff --git a/chromium/chrome/browser/printing/print_preview_data_service.cc b/chromium/chrome/browser/printing/print_preview_data_service.cc
index 0b40be8c036..b26a6cf8ebc 100644
--- a/chromium/chrome/browser/printing/print_preview_data_service.cc
+++ b/chromium/chrome/browser/printing/print_preview_data_service.cc
@@ -106,7 +106,7 @@ void PrintPreviewDataService::SetDataEntry(
int index,
scoped_refptr<base::RefCountedBytes> data_bytes) {
if (!base::ContainsKey(data_store_map_, preview_ui_id))
- data_store_map_[preview_ui_id] = base::MakeUnique<PrintPreviewDataStore>();
+ data_store_map_[preview_ui_id] = std::make_unique<PrintPreviewDataStore>();
data_store_map_[preview_ui_id]->SetPreviewDataForIndex(index,
std::move(data_bytes));
}
diff --git a/chromium/chrome/browser/printing/print_preview_dialog_controller.cc b/chromium/chrome/browser/printing/print_preview_dialog_controller.cc
index 449ec7465b0..0abdaa583c3 100644
--- a/chromium/chrome/browser/printing/print_preview_dialog_controller.cc
+++ b/chromium/chrome/browser/printing/print_preview_dialog_controller.cc
@@ -377,7 +377,7 @@ WebContents* PrintPreviewDialogController::CreatePrintPreviewDialog(
content::HostZoomMap::Get(preview_dialog->GetSiteInstance())
->SetZoomLevelForHostAndScheme(print_url.scheme(), print_url.host(), 0);
PrintViewManager::CreateForWebContents(preview_dialog);
- CreateCompositeClientIfNeeded(preview_dialog, true /* for_preview */);
+ CreateCompositeClientIfNeeded(preview_dialog);
extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
preview_dialog);
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 1de6aeb87da..9c5cf7bf89c 100644
--- a/chromium/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc
+++ b/chromium/chrome/browser/printing/print_preview_dialog_controller_browsertest.cc
@@ -94,7 +94,7 @@ class PrintPreviewDialogClonedObserver : public WebContentsObserver {
void DidCloneToNewWebContents(WebContents* old_web_contents,
WebContents* new_web_contents) override {
request_preview_dialog_observer_ =
- base::MakeUnique<RequestPrintPreviewObserver>(new_web_contents);
+ std::make_unique<RequestPrintPreviewObserver>(new_web_contents);
}
std::unique_ptr<RequestPrintPreviewObserver> request_preview_dialog_observer_;
@@ -192,7 +192,7 @@ class PrintPreviewDialogControllerBrowserTest : public InProcessBrowserTest {
// RequestPrintPreviewObserver to get messages first for the purposes of
// this test.
cloned_tab_observer_ =
- base::MakeUnique<PrintPreviewDialogClonedObserver>(first_tab);
+ std::make_unique<PrintPreviewDialogClonedObserver>(first_tab);
chrome::DuplicateTab(browser());
initiator_ = browser()->tab_strip_model()->GetActiveWebContents();
diff --git a/chromium/chrome/browser/printing/print_preview_message_handler.cc b/chromium/chrome/browser/printing/print_preview_message_handler.cc
index baacab9508e..d088fc05909 100644
--- a/chromium/chrome/browser/printing/print_preview_message_handler.cc
+++ b/chromium/chrome/browser/printing/print_preview_message_handler.cc
@@ -24,8 +24,11 @@
#include "components/printing/browser/print_composite_client.h"
#include "components/printing/browser/print_manager_utils.h"
#include "components/printing/common/print_messages.h"
+#include "components/printing/service/public/cpp/pdf_service_mojo_types.h"
#include "components/printing/service/public/cpp/pdf_service_mojo_utils.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "printing/page_size_margins.h"
@@ -118,16 +121,16 @@ void PrintPreviewMessageHandler::OnDidGetPreviewPageCount(
if (!print_preview_ui)
return;
- if (params.clear_preview_data)
- print_preview_ui->ClearAllPreviewData();
-
+ print_preview_ui->ClearAllPreviewData();
print_preview_ui->OnDidGetPreviewPageCount(params);
}
void PrintPreviewMessageHandler::OnDidPreviewPage(
+ content::RenderFrameHost* render_frame_host,
const PrintHostMsg_DidPreviewPage_Params& params) {
int page_number = params.page_number;
- if (page_number < FIRST_PAGE_INDEX || !params.data_size)
+ const PrintHostMsg_DidPrintContent_Params& content = params.content;
+ if (page_number < FIRST_PAGE_INDEX || !content.data_size)
return;
PrintPreviewUI* print_preview_ui = GetPrintPreviewUI();
@@ -139,19 +142,22 @@ void PrintPreviewMessageHandler::OnDidPreviewPage(
DCHECK(client);
// Use utility process to convert skia metafile to pdf.
- client->DoComposite(
- params.metafile_data_handle, params.data_size,
+ client->DoCompositePageToPdf(
+ params.document_cookie, render_frame_host, params.page_number,
+ content.metafile_data_handle, content.data_size,
+ content.subframe_content_info,
base::BindOnce(&PrintPreviewMessageHandler::OnCompositePdfPageDone,
weak_ptr_factory_.GetWeakPtr(), params.page_number,
params.preview_request_id));
} else {
NotifyUIPreviewPageReady(
page_number, params.preview_request_id,
- GetDataFromHandle(params.metafile_data_handle, params.data_size));
+ GetDataFromHandle(content.metafile_data_handle, content.data_size));
}
}
void PrintPreviewMessageHandler::OnMetafileReadyForPrinting(
+ content::RenderFrameHost* render_frame_host,
const PrintHostMsg_DidPreviewDocument_Params& params) {
// Always try to stop the worker.
StopWorker(params.document_cookie);
@@ -165,19 +171,21 @@ void PrintPreviewMessageHandler::OnMetafileReadyForPrinting(
if (!print_preview_ui)
return;
+ const PrintHostMsg_DidPrintContent_Params& content = params.content;
if (IsOopifEnabled() && print_preview_ui->source_is_modifiable()) {
auto* client = PrintCompositeClient::FromWebContents(web_contents());
DCHECK(client);
- client->DoComposite(
- params.metafile_data_handle, params.data_size,
+ client->DoCompositeDocumentToPdf(
+ params.document_cookie, render_frame_host, content.metafile_data_handle,
+ content.data_size, content.subframe_content_info,
base::BindOnce(&PrintPreviewMessageHandler::OnCompositePdfDocumentDone,
weak_ptr_factory_.GetWeakPtr(),
params.expected_pages_count, params.preview_request_id));
} else {
NotifyUIPreviewDocumentReady(
params.expected_pages_count, params.preview_request_id,
- GetDataFromHandle(params.metafile_data_handle, params.data_size));
+ GetDataFromHandle(content.metafile_data_handle, content.data_size));
}
}
@@ -294,6 +302,9 @@ bool PrintPreviewMessageHandler::OnMessageReceived(
render_frame_host)
IPC_MESSAGE_HANDLER(PrintHostMsg_RequestPrintPreview,
OnRequestPrintPreview)
+ IPC_MESSAGE_HANDLER(PrintHostMsg_DidPreviewPage, OnDidPreviewPage)
+ IPC_MESSAGE_HANDLER(PrintHostMsg_MetafileReadyForPrinting,
+ OnMetafileReadyForPrinting)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
if (handled)
@@ -303,9 +314,6 @@ bool PrintPreviewMessageHandler::OnMessageReceived(
IPC_BEGIN_MESSAGE_MAP(PrintPreviewMessageHandler, message)
IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetPreviewPageCount,
OnDidGetPreviewPageCount)
- IPC_MESSAGE_HANDLER(PrintHostMsg_DidPreviewPage, OnDidPreviewPage)
- IPC_MESSAGE_HANDLER(PrintHostMsg_MetafileReadyForPrinting,
- OnMetafileReadyForPrinting)
IPC_MESSAGE_HANDLER(PrintHostMsg_PrintPreviewFailed,
OnPrintPreviewFailed)
IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetDefaultPageLayout,
diff --git a/chromium/chrome/browser/printing/print_preview_message_handler.h b/chromium/chrome/browser/printing/print_preview_message_handler.h
index 71ca9d5bb1e..1c30f2c1a44 100644
--- a/chromium/chrome/browser/printing/print_preview_message_handler.h
+++ b/chromium/chrome/browser/printing/print_preview_message_handler.h
@@ -64,8 +64,10 @@ class PrintPreviewMessageHandler
bool has_custom_page_size_style);
void OnDidGetPreviewPageCount(
const PrintHostMsg_DidGetPreviewPageCount_Params& params);
- void OnDidPreviewPage(const PrintHostMsg_DidPreviewPage_Params& params);
+ void OnDidPreviewPage(content::RenderFrameHost* render_frame_host,
+ const PrintHostMsg_DidPreviewPage_Params& params);
void OnMetafileReadyForPrinting(
+ content::RenderFrameHost* render_frame_host,
const PrintHostMsg_DidPreviewDocument_Params& params);
void OnPrintPreviewFailed(int document_cookie);
void OnPrintPreviewCancelled(int document_cookie);
diff --git a/chromium/chrome/browser/printing/print_preview_pdf_generated_browsertest.cc b/chromium/chrome/browser/printing/print_preview_pdf_generated_browsertest.cc
index 3a47bb1a1a1..afd223480e8 100644
--- a/chromium/chrome/browser/printing/print_preview_pdf_generated_browsertest.cc
+++ b/chromium/chrome/browser/printing/print_preview_pdf_generated_browsertest.cc
@@ -214,7 +214,7 @@ class PrintPreviewObserver : public WebContentsObserver {
// Saves the print preview settings to be sent to the print preview dialog.
void SetPrintPreviewSettings(const PrintPreviewSettings& settings) {
- settings_ = base::MakeUnique<PrintPreviewSettings>(settings);
+ settings_ = std::make_unique<PrintPreviewSettings>(settings);
}
// Returns the setting that could not be set in the preview dialog.
@@ -281,7 +281,7 @@ class PrintPreviewObserver : public WebContentsObserver {
ASSERT_TRUE(ui->web_ui());
ui->web_ui()->AddMessageHandler(
- base::MakeUnique<UIDoneLoadingMessageHandler>(this));
+ std::make_unique<UIDoneLoadingMessageHandler>(this));
ui->SendEnableManipulateSettingsForTest();
}
@@ -369,8 +369,8 @@ class PrintPreviewPdfGeneratedBrowserTest : public InProcessBrowserTest {
total_height_in_pixels += height_in_pixels;
gfx::Rect rect(width_in_pixels, height_in_pixels);
- PdfRenderSettings settings(rect, gfx::Point(0, 0), kDpi, true,
- PdfRenderSettings::Mode::NORMAL);
+ PdfRenderSettings settings(rect, gfx::Point(0, 0), gfx::Size(kDpi, kDpi),
+ true, PdfRenderSettings::Mode::NORMAL);
int int_max = std::numeric_limits<int>::max();
if (settings.area.width() > int_max / kColorChannels ||
@@ -386,7 +386,7 @@ class PrintPreviewPdfGeneratedBrowserTest : public InProcessBrowserTest {
ASSERT_TRUE(chrome_pdf::RenderPDFPageToBitmap(
pdf_data.data(), pdf_data.size(), i, page_bitmap_data.data(),
settings.area.size().width(), settings.area.size().height(),
- settings.dpi, settings.autorotate));
+ settings.dpi.width(), settings.dpi.height(), settings.autorotate));
FillPng(&page_bitmap_data, width_in_pixels, max_width_in_pixels,
settings.area.size().height());
bitmap_data.insert(bitmap_data.end(),
@@ -456,7 +456,7 @@ class PrintPreviewPdfGeneratedBrowserTest : public InProcessBrowserTest {
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(tab);
- print_preview_observer_ = base::MakeUnique<PrintPreviewObserver>(
+ print_preview_observer_ = std::make_unique<PrintPreviewObserver>(
browser(), tab, pdf_file_save_path_);
chrome::DuplicateTab(browser());
diff --git a/chromium/chrome/browser/printing/print_view_manager.cc b/chromium/chrome/browser/printing/print_view_manager.cc
index 9ce7794365e..8e115370d1d 100644
--- a/chromium/chrome/browser/printing/print_view_manager.cc
+++ b/chromium/chrome/browser/printing/print_view_manager.cc
@@ -96,7 +96,7 @@ bool PrintViewManager::PrintForSystemDialogNow(
SetPrintingRFH(print_preview_rfh_);
int32_t id = print_preview_rfh_->GetRoutingID();
return PrintNowInternal(print_preview_rfh_,
- base::MakeUnique<PrintMsg_PrintForSystemDialog>(id));
+ std::make_unique<PrintMsg_PrintForSystemDialog>(id));
}
bool PrintViewManager::BasicPrint(content::RenderFrameHost* rfh) {
@@ -122,7 +122,7 @@ bool PrintViewManager::PrintPreviewNow(content::RenderFrameHost* rfh,
if (print_preview_state_ != NOT_PREVIEWING)
return false;
- auto message = base::MakeUnique<PrintMsg_InitiatePrintPreview>(
+ auto message = std::make_unique<PrintMsg_InitiatePrintPreview>(
rfh->GetRoutingID(), has_selection);
if (!PrintNowInternal(rfh, std::move(message)))
return false;
diff --git a/chromium/chrome/browser/printing/print_view_manager_base.cc b/chromium/chrome/browser/printing/print_view_manager_base.cc
index 9c3ba082e01..ba01494ae09 100644
--- a/chromium/chrome/browser/printing/print_view_manager_base.cc
+++ b/chromium/chrome/browser/printing/print_view_manager_base.cc
@@ -24,6 +24,7 @@
#include "chrome/browser/chrome_notification_types.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"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/simple_message_box.h"
@@ -34,6 +35,7 @@
#include "components/printing/browser/print_composite_client.h"
#include "components/printing/browser/print_manager_utils.h"
#include "components/printing/common/print_messages.h"
+#include "components/printing/service/public/cpp/pdf_service_mojo_types.h"
#include "components/printing/service/public/cpp/pdf_service_mojo_utils.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_details.h"
@@ -134,7 +136,7 @@ bool PrintViewManagerBase::PrintNow(content::RenderFrameHost* rfh) {
SetPrintingRFH(rfh);
int32_t id = rfh->GetRoutingID();
- return PrintNowInternal(rfh, base::MakeUnique<PrintMsg_PrintPages>(id));
+ return PrintNowInternal(rfh, std::make_unique<PrintMsg_PrintPages>(id));
}
#endif
@@ -197,7 +199,7 @@ void PrintViewManagerBase::PrintDocument(
document->SetConvertingPdf();
#else
std::unique_ptr<PdfMetafileSkia> metafile =
- std::make_unique<PdfMetafileSkia>(SkiaDocumentType::PDF);
+ std::make_unique<PdfMetafileSkia>();
CHECK(metafile->InitFromData(print_data->front(), print_data->size()));
// Update the rendered document. It will send notifications to the listener.
@@ -261,6 +263,10 @@ void PrintViewManagerBase::StartLocalPrintJob(
return;
}
+#if defined(OS_WIN)
+ print_job_->ResetPageMapping();
+#endif
+
const printing::PrintSettings& settings = printer_query->settings();
gfx::Size page_size = settings.page_setup_device_units().physical_size();
gfx::Rect content_area =
@@ -336,29 +342,31 @@ void PrintViewManagerBase::OnComposePdfDone(
}
void PrintViewManagerBase::OnDidPrintDocument(
+ content::RenderFrameHost* render_frame_host,
const PrintHostMsg_DidPrintDocument_Params& params) {
PrintedDocument* document = GetDocument(params.document_cookie);
if (!document)
return;
- if (!base::SharedMemory::IsHandleValid(params.metafile_data_handle)) {
+ const PrintHostMsg_DidPrintContent_Params& content = params.content;
+ if (!base::SharedMemory::IsHandleValid(content.metafile_data_handle)) {
NOTREACHED() << "invalid memory handle";
web_contents()->Stop();
return;
}
auto* client = PrintCompositeClient::FromWebContents(web_contents());
- if (IsOopifEnabled() && !client->for_preview() &&
- !document->settings().is_modifiable()) {
- client->DoComposite(params.metafile_data_handle, params.data_size,
- base::BindOnce(&PrintViewManagerBase::OnComposePdfDone,
- weak_ptr_factory_.GetWeakPtr(), params));
+ if (IsOopifEnabled() && !PrintingPdfContent(render_frame_host)) {
+ client->DoCompositeDocumentToPdf(
+ params.document_cookie, render_frame_host, content.metafile_data_handle,
+ content.data_size, content.subframe_content_info,
+ base::BindOnce(&PrintViewManagerBase::OnComposePdfDone,
+ weak_ptr_factory_.GetWeakPtr(), params));
return;
}
-
- std::unique_ptr<base::SharedMemory> shared_buf =
- std::make_unique<base::SharedMemory>(params.metafile_data_handle, true);
- if (!shared_buf->Map(params.data_size)) {
+ auto shared_buf =
+ std::make_unique<base::SharedMemory>(content.metafile_data_handle, true);
+ if (!shared_buf->Map(content.data_size)) {
NOTREACHED() << "couldn't map";
web_contents()->Stop();
return;
@@ -366,7 +374,7 @@ void PrintViewManagerBase::OnDidPrintDocument(
scoped_refptr<base::RefCountedBytes> bytes =
base::MakeRefCounted<base::RefCountedBytes>(
reinterpret_cast<const unsigned char*>(shared_buf->memory()),
- params.data_size);
+ content.data_size);
PrintDocument(document, bytes, params.page_size, params.content_area,
params.physical_offsets);
}
@@ -438,8 +446,16 @@ bool PrintViewManagerBase::OnMessageReceived(
const IPC::Message& message,
content::RenderFrameHost* render_frame_host) {
bool handled = true;
- IPC_BEGIN_MESSAGE_MAP(PrintViewManagerBase, message)
+ IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(PrintViewManagerBase, message,
+ render_frame_host)
IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrintDocument, OnDidPrintDocument)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ if (handled)
+ return true;
+
+ handled = true;
+ IPC_BEGIN_MESSAGE_MAP(PrintViewManagerBase, message)
IPC_MESSAGE_HANDLER(PrintHostMsg_ShowInvalidPrinterSettingsError,
OnShowInvalidPrinterSettingsError)
IPC_MESSAGE_UNHANDLED(handled = false)
@@ -566,8 +582,6 @@ bool PrintViewManagerBase::CreateNewPrintJob(PrintJobWorkerOwner* job) {
return false;
}
- // Ask the renderer to generate the print preview, create the print preview
- // view and switch to it, initialize the printer and show the print dialog.
DCHECK(!print_job_.get());
DCHECK(job);
if (!job)
@@ -627,7 +641,7 @@ void PrintViewManagerBase::ReleasePrintJob() {
return;
if (rfh) {
- auto msg = base::MakeUnique<PrintMsg_PrintingDone>(rfh->GetRoutingID(),
+ auto msg = std::make_unique<PrintMsg_PrintingDone>(rfh->GetRoutingID(),
printing_succeeded_);
rfh->Send(msg.release());
}
diff --git a/chromium/chrome/browser/printing/print_view_manager_base.h b/chromium/chrome/browser/printing/print_view_manager_base.h
index 7cf2d0d81fd..6daa186202a 100644
--- a/chromium/chrome/browser/printing/print_view_manager_base.h
+++ b/chromium/chrome/browser/printing/print_view_manager_base.h
@@ -106,7 +106,8 @@ class PrintViewManagerBase : public content::NotificationObserver,
void OnDidGetPrintedPagesCount(int cookie, int number_pages) override;
void OnPrintingFailed(int cookie) override;
void OnShowInvalidPrinterSettingsError();
- void OnDidPrintDocument(const PrintHostMsg_DidPrintDocument_Params& params);
+ void OnDidPrintDocument(content::RenderFrameHost* render_frame_host,
+ const PrintHostMsg_DidPrintDocument_Params& params);
// IPC message handlers for service.
void OnComposePdfDone(const PrintHostMsg_DidPrintDocument_Params& params,
diff --git a/chromium/chrome/browser/printing/print_view_manager_common.cc b/chromium/chrome/browser/printing/print_view_manager_common.cc
index 1832389b903..eb2cb0b2e8c 100644
--- a/chromium/chrome/browser/printing/print_view_manager_common.cc
+++ b/chromium/chrome/browser/printing/print_view_manager_common.cc
@@ -4,9 +4,12 @@
#include "chrome/browser/printing/print_view_manager_common.h"
+#include "chrome/common/webui_url_constants.h"
#include "content/public/browser/render_frame_host.h"
+#include "extensions/common/constants.h"
#include "extensions/features/features.h"
#include "printing/features/features.h"
+#include "url/gurl.h"
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "components/guest_view/browser/guest_view_manager.h"
@@ -122,4 +125,12 @@ content::RenderFrameHost* GetFrameToPrint(content::WebContents* contents) {
: contents->GetMainFrame();
}
+bool PrintingPdfContent(content::RenderFrameHost* rfh) {
+ GURL url = rfh->GetLastCommittedURL();
+ // Whether it is inside print preview or pdf plugin extension.
+ return url.GetOrigin() == chrome::kChromeUIPrintURL ||
+ (url.SchemeIs(extensions::kExtensionScheme) &&
+ url.host_piece() == extension_misc::kPdfExtensionId);
+}
+
} // namespace printing
diff --git a/chromium/chrome/browser/printing/print_view_manager_common.h b/chromium/chrome/browser/printing/print_view_manager_common.h
index da97bc7503d..dff9e61b1cc 100644
--- a/chromium/chrome/browser/printing/print_view_manager_common.h
+++ b/chromium/chrome/browser/printing/print_view_manager_common.h
@@ -28,6 +28,13 @@ void StartBasicPrint(content::WebContents* contents);
// frame (this makes print selection work for multiple frames).
content::RenderFrameHost* GetFrameToPrint(content::WebContents* contents);
+// Whether the content sent to |rfh| is in PDF format.
+// When print preview dialog is printed, the content returned is always
+// in PDF format because print preview already stores the PDF file for
+// the previewed web page; When a full page PDF plugin is printed, the document
+// in it is in PDF format so will return in PDF also.
+bool PrintingPdfContent(content::RenderFrameHost* rfh);
+
} // namespace printing
#endif // CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_COMMON_H_
diff --git a/chromium/chrome/browser/printing/printer_manager_dialog_linux.cc b/chromium/chrome/browser/printing/printer_manager_dialog_linux.cc
index 8e62d44ce6f..0c46c5fdcd2 100644
--- a/chromium/chrome/browser/printing/printer_manager_dialog_linux.cc
+++ b/chromium/chrome/browser/printing/printer_manager_dialog_linux.cc
@@ -17,13 +17,21 @@
namespace {
-// KDE printer config command ("system-config-printer-kde") causes the
-// OptionWidget to crash (https://bugs.kde.org/show_bug.cgi?id=271957).
-// Therefore, use GNOME printer config command for KDE.
-const char* const kSystemConfigPrinterCommand[] = {"system-config-printer",
- nullptr};
+// Older KDE shipped with system-config-printer-kde, which was buggy. Thus do
+// not bother with system-config-printer-kde and just always use
+// system-config-printer.
+// https://bugs.kde.org/show_bug.cgi?id=271957.
+constexpr const char* kSystemConfigPrinterCommand[] = {"system-config-printer",
+ nullptr};
-const char* const kGnomeControlCenterPrintersCommand[] = {
+// Newer KDE has an improved print manager.
+constexpr const char* kKde4KcmPrinterCommand[] = {
+ "kcmshell4", "kcm_printer_manager", nullptr};
+constexpr const char* kKde5KcmPrinterCommand[] = {
+ "kcmshell5", "kcm_printer_manager", nullptr};
+
+// Older GNOME printer manager. Used as a fallback.
+constexpr const char* kGnomeControlCenterPrintersCommand[] = {
"gnome-control-center", "printers", nullptr};
// Returns true if the dialog was opened successfully.
@@ -54,10 +62,16 @@ void DetectAndOpenPrinterConfigDialog() {
opened = OpenPrinterConfigDialog(kSystemConfigPrinterCommand) ||
OpenPrinterConfigDialog(kGnomeControlCenterPrintersCommand);
break;
- case base::nix::DESKTOP_ENVIRONMENT_CINNAMON:
- case base::nix::DESKTOP_ENVIRONMENT_KDE3:
case base::nix::DESKTOP_ENVIRONMENT_KDE4:
+ opened = OpenPrinterConfigDialog(kKde4KcmPrinterCommand) ||
+ OpenPrinterConfigDialog(kSystemConfigPrinterCommand);
+ break;
case base::nix::DESKTOP_ENVIRONMENT_KDE5:
+ opened = OpenPrinterConfigDialog(kKde5KcmPrinterCommand) ||
+ OpenPrinterConfigDialog(kSystemConfigPrinterCommand);
+ break;
+ case base::nix::DESKTOP_ENVIRONMENT_CINNAMON:
+ case base::nix::DESKTOP_ENVIRONMENT_KDE3:
case base::nix::DESKTOP_ENVIRONMENT_PANTHEON:
case base::nix::DESKTOP_ENVIRONMENT_UNITY:
case base::nix::DESKTOP_ENVIRONMENT_XFCE:
diff --git a/chromium/chrome/browser/printing/printer_query.cc b/chromium/chrome/browser/printing/printer_query.cc
index a03a60c0835..4bf550558ce 100644
--- a/chromium/chrome/browser/printing/printer_query.cc
+++ b/chromium/chrome/browser/printing/printer_query.cc
@@ -83,9 +83,10 @@ void PrinterQuery::GetSettings(GetSettingsAskParam ask_user_for_settings,
ask_user_for_settings == GetSettingsAskParam::ASK_USER;
worker_->PostTask(
FROM_HERE,
- base::Bind(&PrintJobWorker::GetSettings, base::Unretained(worker_.get()),
- is_print_dialog_box_shown_, expected_page_count, has_selection,
- margin_type, is_scripted, is_modifiable));
+ base::BindOnce(&PrintJobWorker::GetSettings,
+ base::Unretained(worker_.get()),
+ is_print_dialog_box_shown_, expected_page_count,
+ has_selection, margin_type, is_scripted, is_modifiable));
}
void PrinterQuery::SetSettings(
@@ -93,12 +94,24 @@ void PrinterQuery::SetSettings(
base::OnceClosure callback) {
StartWorker(std::move(callback));
- worker_->PostTask(FROM_HERE,
- base::Bind(&PrintJobWorker::SetSettings,
- base::Unretained(worker_.get()),
- base::Passed(&new_settings)));
+ worker_->PostTask(FROM_HERE, base::BindOnce(&PrintJobWorker::SetSettings,
+ base::Unretained(worker_.get()),
+ std::move(new_settings)));
}
+#if defined(OS_CHROMEOS)
+void PrinterQuery::SetSettingsFromPOD(
+ std::unique_ptr<printing::PrintSettings> new_settings,
+ base::OnceClosure callback) {
+ StartWorker(std::move(callback));
+
+ worker_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&PrintJobWorker::SetSettingsFromPOD,
+ base::Unretained(worker_.get()), std::move(new_settings)));
+}
+#endif
+
void PrinterQuery::StartWorker(base::OnceClosure callback) {
DCHECK(!callback_);
DCHECK(worker_);
diff --git a/chromium/chrome/browser/printing/printer_query.h b/chromium/chrome/browser/printing/printer_query.h
index 769d653cd53..e0251a48fb2 100644
--- a/chromium/chrome/browser/printing/printer_query.h
+++ b/chromium/chrome/browser/printing/printer_query.h
@@ -55,6 +55,12 @@ class PrinterQuery : public PrintJobWorkerOwner {
void SetSettings(std::unique_ptr<base::DictionaryValue> new_settings,
base::OnceClosure callback);
+#if defined(OS_CHROMEOS)
+ // Updates the current settings with |new_settings|.
+ void SetSettingsFromPOD(std::unique_ptr<printing::PrintSettings> new_settings,
+ base::OnceClosure callback);
+#endif
+
// Stops the worker thread since the client is done with this object.
void StopWorker();
diff --git a/chromium/chrome/browser/printing/printing_init.cc b/chromium/chrome/browser/printing/printing_init.cc
index 3560d1c7939..37562cc1213 100644
--- a/chromium/chrome/browser/printing/printing_init.cc
+++ b/chromium/chrome/browser/printing/printing_init.cc
@@ -24,7 +24,7 @@ void InitializePrinting(content::WebContents* web_contents) {
#else
printing::PrintViewManagerBasic::CreateForWebContents(web_contents);
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
- CreateCompositeClientIfNeeded(web_contents, false /* for_preview */);
+ CreateCompositeClientIfNeeded(web_contents);
}
} // namespace printing
diff --git a/chromium/chrome/browser/printing/printing_message_filter.cc b/chromium/chrome/browser/printing/printing_message_filter.cc
index 8e535f71587..096407abc20 100644
--- a/chromium/chrome/browser/printing/printing_message_filter.cc
+++ b/chromium/chrome/browser/printing/printing_message_filter.cc
@@ -269,7 +269,7 @@ void PrintingMessageFilter::OnScriptedPrintReply(
}
PrintHostMsg_ScriptedPrint::WriteReplyParams(reply_msg, params);
Send(reply_msg);
- if (params.params.dpi && params.params.document_cookie) {
+ if (!params.params.dpi.IsEmpty() && params.params.document_cookie) {
#if defined(OS_ANDROID)
int file_descriptor;
const base::string16& device_name = printer_query->settings().device_name();
diff --git a/chromium/chrome/browser/printing/pwg_raster_converter.cc b/chromium/chrome/browser/printing/pwg_raster_converter.cc
index 14f13aff11a..c9517f12e65 100644
--- a/chromium/chrome/browser/printing/pwg_raster_converter.cc
+++ b/chromium/chrome/browser/printing/pwg_raster_converter.cc
@@ -21,9 +21,8 @@
#include "base/task_scheduler/post_task.h"
#include "base/threading/thread_restrictions.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "chrome/common/chrome_utility_printing_messages.h"
-#include "chrome/services/printing/public/interfaces/constants.mojom.h"
-#include "chrome/services/printing/public/interfaces/pdf_to_pwg_raster_converter.mojom.h"
+#include "chrome/services/printing/public/mojom/constants.mojom.h"
+#include "chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom.h"
#include "components/cloud_devices/common/cloud_device_description.h"
#include "components/cloud_devices/common/printer_description.h"
#include "content/public/browser/browser_thread.h"
@@ -208,63 +207,64 @@ class PwgRasterConverterImpl : public PwgRasterConverter {
ResultCallback callback) override;
private:
- // TODO (rbpotter): Once CancelableOnceCallback is added, remove this and
- // change callback_ to a CancelableOnceCallback.
- void RunCallback(bool success, const base::FilePath& temp_file);
-
scoped_refptr<PwgRasterConverterHelper> utility_client_;
- ResultCallback callback_;
- base::WeakPtrFactory<PwgRasterConverterImpl> weak_ptr_factory_;
+
+ // Cancelable version of ResultCallback.
+ base::CancelableOnceCallback<void(bool, const base::FilePath&)> callback_;
DISALLOW_COPY_AND_ASSIGN(PwgRasterConverterImpl);
};
-PwgRasterConverterImpl::PwgRasterConverterImpl() : weak_ptr_factory_(this) {}
+PwgRasterConverterImpl::PwgRasterConverterImpl() = default;
-PwgRasterConverterImpl::~PwgRasterConverterImpl() {}
+PwgRasterConverterImpl::~PwgRasterConverterImpl() = default;
void PwgRasterConverterImpl::Start(base::RefCountedMemory* data,
const PdfRenderSettings& conversion_settings,
const PwgRasterSettings& bitmap_settings,
ResultCallback callback) {
- // Bind callback here and pass a wrapper to the utility client to avoid
- // calling callback if PwgRasterConverterImpl is destroyed.
- callback_ = std::move(callback);
+ callback_.Reset(std::move(callback));
utility_client_ = base::MakeRefCounted<PwgRasterConverterHelper>(
conversion_settings, bitmap_settings);
- utility_client_->Convert(data,
- base::BindOnce(&PwgRasterConverterImpl::RunCallback,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
-void PwgRasterConverterImpl::RunCallback(bool success,
- const base::FilePath& temp_file) {
- std::move(callback_).Run(success, temp_file);
+ utility_client_->Convert(data, callback_.callback());
}
} // namespace
// static
std::unique_ptr<PwgRasterConverter> PwgRasterConverter::CreateDefault() {
- return base::MakeUnique<PwgRasterConverterImpl>();
+ return std::make_unique<PwgRasterConverterImpl>();
}
// static
PdfRenderSettings PwgRasterConverter::GetConversionSettings(
const cloud_devices::CloudDeviceDescription& printer_capabilities,
const gfx::Size& page_size) {
- int dpi = kDefaultPdfDpi;
+ gfx::Size dpi = gfx::Size(kDefaultPdfDpi, kDefaultPdfDpi);
cloud_devices::printer::DpiCapability dpis;
if (dpis.LoadFrom(printer_capabilities))
- dpi = std::max(dpis.GetDefault().horizontal, dpis.GetDefault().vertical);
-
- const double scale = static_cast<double>(dpi) / kPointsPerInch;
+ dpi = gfx::Size(dpis.GetDefault().horizontal, dpis.GetDefault().vertical);
+
+ bool page_is_landscape =
+ static_cast<double>(page_size.width()) / dpi.width() >
+ static_cast<double>(page_size.height()) / dpi.height();
+
+ // Pdfium assumes that page width is given in dpi.width(), and height in
+ // dpi.height(). If we rotate the page, we need to also swap the DPIs.
+ gfx::Size final_page_size = page_size;
+ if (page_is_landscape) {
+ final_page_size = gfx::Size(page_size.height(), page_size.width());
+ dpi = gfx::Size(dpi.height(), dpi.width());
+ }
+ double scale_x = static_cast<double>(dpi.width()) / kPointsPerInch;
+ double scale_y = static_cast<double>(dpi.height()) / kPointsPerInch;
// Make vertical rectangle to optimize streaming to printer. Fix orientation
// by autorotate.
- gfx::Rect area(std::min(page_size.width(), page_size.height()) * scale,
- std::max(page_size.width(), page_size.height()) * scale);
- return PdfRenderSettings(area, gfx::Point(0, 0), dpi, /*autorotate=*/true,
+ gfx::Rect area(final_page_size.width() * scale_x,
+ final_page_size.height() * scale_y);
+ return PdfRenderSettings(area, gfx::Point(0, 0), dpi,
+ /*autorotate=*/true,
PdfRenderSettings::Mode::NORMAL);
}
diff --git a/chromium/chrome/browser/printing/pwg_raster_converter_browsertest.cc b/chromium/chrome/browser/printing/pwg_raster_converter_browsertest.cc
index 5532b8e4002..570dd88c7a1 100644
--- a/chromium/chrome/browser/printing/pwg_raster_converter_browsertest.cc
+++ b/chromium/chrome/browser/printing/pwg_raster_converter_browsertest.cc
@@ -87,8 +87,8 @@ class PdfToPwgRasterBrowserTest : public InProcessBrowserTest {
bool success = false;
base::RunLoop run_loop;
converter_->Start(pdf_data, conversion_settings, bitmap_settings,
- base::Bind(&ResultCallbackImpl, &called, &success,
- temp_file, run_loop.QuitClosure()));
+ base::BindOnce(&ResultCallbackImpl, &called, &success,
+ temp_file, run_loop.QuitClosure()));
run_loop.Run();
ASSERT_TRUE(called);
EXPECT_EQ(success, expect_success);
@@ -116,7 +116,8 @@ IN_PROC_BROWSER_TEST_F(PdfToPwgRasterBrowserTest, TestSuccessColor) {
GetPdfData("pdf_to_pwg_raster_test.pdf", &test_data_dir, &pdf_data);
PdfRenderSettings pdf_settings(gfx::Rect(0, 0, 500, 500), gfx::Point(0, 0),
- /*dpi=*/1000, /*autorotate=*/false,
+ /*dpi=*/gfx::Size(1000, 1000),
+ /*autorotate=*/false,
PdfRenderSettings::Mode::NORMAL);
PwgRasterSettings pwg_settings;
pwg_settings.odd_page_transform = PwgRasterTransformType::TRANSFORM_NORMAL;
@@ -142,7 +143,8 @@ IN_PROC_BROWSER_TEST_F(PdfToPwgRasterBrowserTest, TestSuccessMono) {
GetPdfData("pdf_to_pwg_raster_test.pdf", &test_data_dir, &pdf_data);
PdfRenderSettings pdf_settings(gfx::Rect(0, 0, 500, 500), gfx::Point(0, 0),
- /*dpi=*/1000, /*autorotate=*/false,
+ /*dpi=*/gfx::Size(1000, 1000),
+ /*autorotate=*/false,
PdfRenderSettings::Mode::NORMAL);
PwgRasterSettings pwg_settings;
pwg_settings.odd_page_transform = PwgRasterTransformType::TRANSFORM_NORMAL;
diff --git a/chromium/chrome/browser/profiling_host/BUILD.gn b/chromium/chrome/browser/profiling_host/BUILD.gn
index 9d7dbf3ddc1..7b90c77e756 100644
--- a/chromium/chrome/browser/profiling_host/BUILD.gn
+++ b/chromium/chrome/browser/profiling_host/BUILD.gn
@@ -16,7 +16,7 @@ if (!is_android) {
deps = [
"//base",
- "//base/allocator:features",
+ "//base/allocator:buildflags",
"//chrome/test:test_support_ui",
"//testing/gmock",
"//testing/gtest",
diff --git a/chromium/chrome/browser/renderer_host/pepper/DEPS b/chromium/chrome/browser/renderer_host/pepper/DEPS
index 90188c90290..4aa9156f0d9 100644
--- a/chromium/chrome/browser/renderer_host/pepper/DEPS
+++ b/chromium/chrome/browser/renderer_host/pepper/DEPS
@@ -1,6 +1,6 @@
include_rules = [
# ppapi/host and ppapi/proxy are already added at a higher level DEPS file.
"+ppapi/shared_impl",
- "+services/device/public/interfaces",
+ "+services/device/public/mojom",
]
diff --git a/chromium/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.cc b/chromium/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.cc
index d23b6a7a55a..1c1844a9eb7 100644
--- a/chromium/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.cc
+++ b/chromium/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.cc
@@ -22,8 +22,8 @@
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/proxy/resource_message_params.h"
#include "ppapi/shared_impl/time_conversion.h"
-#include "services/device/public/interfaces/constants.mojom.h"
-#include "services/device/public/interfaces/wake_lock_provider.mojom.h"
+#include "services/device/public/mojom/constants.mojom.h"
+#include "services/device/public/mojom/wake_lock_provider.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#include "url/gurl.h"
@@ -54,7 +54,7 @@ scoped_refptr<content_settings::CookieSettings> GetCookieSettings(
return NULL;
}
-void BindConnectorRequest(
+void PepperBindConnectorRequest(
service_manager::mojom::ConnectorRequest connector_request) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(ServiceManagerConnection::GetForProcess());
@@ -194,9 +194,9 @@ device::mojom::WakeLock* PepperFlashBrowserHost::GetWakeLock() {
// The existing connector is bound to the UI thread, the current thread is
// IO thread. So bind the ConnectorRequest of IO thread to the connector
// in UI thread.
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::BindOnce(&BindConnectorRequest, std::move(connector_request)));
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::BindOnce(&PepperBindConnectorRequest,
+ std::move(connector_request)));
device::mojom::WakeLockProviderPtr wake_lock_provider;
connector->BindInterface(device::mojom::kServiceName,
diff --git a/chromium/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.h b/chromium/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.h
index f2c631ea59f..154120ce515 100644
--- a/chromium/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.h
+++ b/chromium/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.h
@@ -13,7 +13,7 @@
#include "base/timer/timer.h"
#include "ppapi/host/host_message_context.h"
#include "ppapi/host/resource_host.h"
-#include "services/device/public/interfaces/wake_lock.mojom.h"
+#include "services/device/public/mojom/wake_lock.mojom.h"
namespace base {
class Time;
diff --git a/chromium/chrome/browser/ui/tabs/BUILD.gn b/chromium/chrome/browser/resource_coordinator/BUILD.gn
index 25778d9f60c..25778d9f60c 100644
--- a/chromium/chrome/browser/ui/tabs/BUILD.gn
+++ b/chromium/chrome/browser/resource_coordinator/BUILD.gn
diff --git a/chromium/chrome/browser/resources/BUILD.gn b/chromium/chrome/browser/resources/BUILD.gn
index f0559f28e62..c9f770b9a8e 100644
--- a/chromium/chrome/browser/resources/BUILD.gn
+++ b/chromium/chrome/browser/resources/BUILD.gn
@@ -166,15 +166,3 @@ if (enable_print_preview) {
output_dir = "$root_gen_dir/chrome"
}
}
-
-if (enable_vr) {
- grit("vr_shell_resources") {
- source = "vr_shell_resources.grd"
- defines = chrome_grit_defines
- outputs = [
- "grit/vr_shell_resources.h",
- "vr_shell_resources.pak",
- ]
- output_dir = "$root_gen_dir/chrome"
- }
-}
diff --git a/chromium/chrome/browser/resources/PRESUBMIT.py b/chromium/chrome/browser/resources/PRESUBMIT.py
index 9ebdb0ab8e6..e2a39c26688 100644
--- a/chromium/chrome/browser/resources/PRESUBMIT.py
+++ b/chromium/chrome/browser/resources/PRESUBMIT.py
@@ -8,8 +8,6 @@ See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into depot_tools.
"""
-import re
-
ACTION_XML_PATH = '../../../tools/metrics/actions/actions.xml'
diff --git a/chromium/chrome/browser/resources/about_voicesearch.html b/chromium/chrome/browser/resources/about_voicesearch.html
deleted file mode 100644
index 93a43dc6485..00000000000
--- a/chromium/chrome/browser/resources/about_voicesearch.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<!doctype html>
-<html i18n-values="dir:textdirection;lang:language">
-<head>
-<meta charset="utf-8">
-<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
-<style>
-.key {
- font-weight: bold;
-}
-
-.value {
- margin-left: 15px;
-}
-</style>
-</head>
-<body>
-<div id="loading-message" i18n-content="loadingMessage">LOADING_MESSAGE</div>
-<div id="body-container" hidden>
- <div id="header">
- <h1 i18n-content="voiceSearchLongTitle">ABOUT_VOICESEARCH</h1>
- </div>
- <div id="voice-search-info-template">
- <table cellpadding="2" cellspacing="0" border="0">
- <tr jsselect="voiceSearchInfo">
- <td><span dir="ltr" jscontent="key" class="key">KEY</span></td>
- <td><span dir="ltr" jscontent="value" class="value">VALUE</span></td>
- </tr>
- </table>
- </div>
-</div>
-<script src="chrome://resources/js/load_time_data.js"></script>
-<script src="chrome://voicesearch/about_voicesearch.js"></script>
-<script src="chrome://voicesearch/strings.js"></script>
-<script src="chrome://resources/js/i18n_template.js"></script>
-<script src="chrome://resources/js/jstemplate_compiled.js"></script>
-<script src="chrome://resources/js/util.js"></script>
-</body>
-</html>
diff --git a/chromium/chrome/browser/resources/about_voicesearch.js b/chromium/chrome/browser/resources/about_voicesearch.js
deleted file mode 100644
index bcf8cbd5f7b..00000000000
--- a/chromium/chrome/browser/resources/about_voicesearch.js
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * Takes the |moduleListData| input argument which represents data about
- * the currently available modules and populates the html jstemplate
- * with that data. It expects an object structure like the above.
- * @param {Object} moduleListData Information about available modules
- */
-function renderTemplate(moduleListData) {
- var input = new JsEvalContext(moduleListData);
- var output = $('voice-search-info-template');
- jstProcess(input, output);
-}
-
-/**
- * Asks the C++ VoiceSearchUIDOMHandler to get details about voice search and
- * return the data in returnVoiceSearchInfo() (below).
- */
-function requestVoiceSearchInfo() {
- chrome.send('requestVoiceSearchInfo');
-}
-
-/**
- * Called by the WebUI to re-populate the page with data representing the
- * current state of voice search.
- * @param {Object} moduleListData Information about available modules.
- */
-function returnVoiceSearchInfo(moduleListData) {
- $('loading-message').hidden = true;
- $('body-container').hidden = false;
- renderTemplate(moduleListData);
-}
-
-// Get data and have it displayed upon loading.
-document.addEventListener('DOMContentLoaded', requestVoiceSearchInfo);
diff --git a/chromium/chrome/browser/resources/app_list/OWNERS b/chromium/chrome/browser/resources/app_list/OWNERS
deleted file mode 100644
index ed54f09c40c..00000000000
--- a/chromium/chrome/browser/resources/app_list/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-calamity@chromium.org
-khmel@chromium.org
diff --git a/chromium/chrome/browser/resources/app_list/start_page.css b/chromium/chrome/browser/resources/app_list/start_page.css
deleted file mode 100644
index 3efaef1955a..00000000000
--- a/chromium/chrome/browser/resources/app_list/start_page.css
+++ /dev/null
@@ -1,32 +0,0 @@
-/* Copyright 2013 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-html,
-body {
- height: 100%;
- margin: 0;
- overflow: hidden;
- padding: 0;
- user-select: none;
- width: 100%;
-}
-
-#doodle {
- display: none;
- justify-content: center;
-}
-
-#default_logo {
- background-image: url(../../../../ui/webui/resources/images/google_logo.svg);
- background-repeat: no-repeat;
- height: 92px;
- margin: auto;
- width: 272px;
-}
-
-#logo_container {
- bottom: 0;
- position: absolute;
- width: 100%;
-}
diff --git a/chromium/chrome/browser/resources/app_list/start_page.html b/chromium/chrome/browser/resources/app_list/start_page.html
deleted file mode 100644
index 5388e65d334..00000000000
--- a/chromium/chrome/browser/resources/app_list/start_page.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!doctype html>
-<html i18n-values="dir:textdirection;lang:language">
-<head>
- <meta charset="utf-8">
- <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
- <link rel="stylesheet" href="chrome://app-list/start_page.css">
- <script src="chrome://resources/js/load_time_data.js"></script>
- <script src="chrome://resources/js/cr.js"></script>
- <script src="chrome://resources/js/cr/event_target.js"></script>
- <script src="chrome://resources/js/cr/ui.js"></script>
- <script src="chrome://resources/js/util.js"></script>
- <script src="chrome://app-list/strings.js"></script>
- <script src="chrome://app-list/start_page.js"></script>
- <base id="base">
-</head>
-
-<body>
- <div id="logo_container">
- <div id="default_logo"></div>
- </div>
- <script src="chrome://resources/js/i18n_template.js"></script>
-</body>
-</html>
diff --git a/chromium/chrome/browser/resources/app_list/start_page.js b/chromium/chrome/browser/resources/app_list/start_page.js
deleted file mode 100644
index 2a01e136998..00000000000
--- a/chromium/chrome/browser/resources/app_list/start_page.js
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview App launcher start page implementation.
- */
-
-/**
- * The maximum height of the Google Doodle. Note this value should be consistent
- * with kWebViewHeight in start_page_view.cc.
- */
-var doodleMaxHeight = 224;
-
-cr.define('appList.startPage', function() {
- 'use strict';
-
- // The element containing the current Google Doodle.
- var doodle = null;
-
- /**
- * Initialize the page.
- */
- function initialize() {
- chrome.send('initialize');
- }
-
- /**
- * Invoked when the app-list bubble is shown.
- */
- function onAppListShown() {
- chrome.send('appListShown', [this.doodle != null]);
- }
-
- /**
- * Sets the doodle's visibility, hiding or showing the default logo.
- *
- * @param {boolean} visible Whether the doodle should be made visible.
- */
- function setDoodleVisible(visible) {
- var doodle = $('doodle');
- var defaultLogo = $('default_logo');
- if (visible) {
- doodle.style.display = 'flex';
- defaultLogo.style.display = 'none';
- } else {
- if (doodle)
- doodle.style.display = 'none';
-
- defaultLogo.style.display = 'block';
- }
- }
-
- /**
- * Invoked when the app-list doodle is updated.
- *
- * @param {Object} data The data object representing the current doodle.
- */
- function onAppListDoodleUpdated(data, base_url) {
- if (this.doodle) {
- this.doodle.parentNode.removeChild(this.doodle);
- this.doodle = null;
- }
-
- var doodleData = data.ddljson;
- if (!doodleData || !doodleData.transparent_large_image) {
- setDoodleVisible(false);
- return;
- }
-
- // Set the page's base URL so that links will resolve relative to the Google
- // homepage.
- $('base').href = base_url;
-
- this.doodle = document.createElement('div');
- this.doodle.id = 'doodle';
- this.doodle.style.display = 'none';
-
- var doodleImage = document.createElement('img');
- doodleImage.id = 'doodle_image';
- if (doodleData.transparent_large_image.height > doodleMaxHeight)
- doodleImage.setAttribute('height', doodleMaxHeight);
- if (doodleData.alt_text) {
- doodleImage.alt = doodleData.alt_text;
- doodleImage.title = doodleData.alt_text;
- }
-
- doodleImage.onload = function() {
- setDoodleVisible(true);
- };
- doodleImage.src = doodleData.transparent_large_image.url;
-
- if (doodleData.target_url) {
- var doodleLink = document.createElement('a');
- doodleLink.id = 'doodle_link';
- doodleLink.href = doodleData.target_url;
- doodleLink.target = '_blank';
- doodleLink.appendChild(doodleImage);
- doodleLink.onclick = function() {
- chrome.send('doodleClicked');
- return true;
- };
- this.doodle.appendChild(doodleLink);
- } else {
- this.doodle.appendChild(doodleImage);
- }
- $('logo_container').appendChild(this.doodle);
- }
-
- return {
- initialize: initialize,
- onAppListDoodleUpdated: onAppListDoodleUpdated,
- onAppListShown: onAppListShown,
- };
-});
-
-document.addEventListener('contextmenu', function(e) {
- e.preventDefault();
-});
-document.addEventListener('DOMContentLoaded', appList.startPage.initialize);
diff --git a/chromium/chrome/browser/resources/bluetooth_internals/adapter_broker.js b/chromium/chrome/browser/resources/bluetooth_internals/adapter_broker.js
index e357336fe7e..d345167a581 100644
--- a/chromium/chrome/browser/resources/bluetooth_internals/adapter_broker.js
+++ b/chromium/chrome/browser/resources/bluetooth_internals/adapter_broker.js
@@ -111,7 +111,7 @@ cr.define('adapter_broker', function() {
/**
* The implementation of AdapterClient in
- * device/bluetooth/public/interfaces/adapter.mojom. Dispatches events
+ * device/bluetooth/public/mojom/adapter.mojom. Dispatches events
* through AdapterBroker to notify client objects of changes to the Adapter
* service.
* @constructor
diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/BUILD.gn b/chromium/chrome/browser/resources/chromeos/chromevox/BUILD.gn
index 98fdb591fca..329a2f37113 100644
--- a/chromium/chrome/browser/resources/chromeos/chromevox/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/chromevox/BUILD.gn
@@ -126,6 +126,7 @@ chromevox_modules = [
"cvox2/background/automation_util.js",
"cvox2/background/background.js",
"cvox2/background/base_automation_handler.js",
+ "cvox2/background/braille_command_data.js",
"cvox2/background/braille_command_handler.js",
"cvox2/background/chromevox_state.js",
"cvox2/background/command_handler.js",
diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd b/chromium/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
index ed7b45e0e20..f47bdfdc16b 100644
--- a/chromium/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
+++ b/chromium/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
@@ -2783,6 +2783,27 @@ If you're done with the tutorial, use ChromeVox to navigate to the Close button
<message desc="Shown to a user when they invoke the read current title command in a context without a title." name="IDS_CHROMEVOX_NO_TITLE">
No title
</message>
+ <message desc="Spoken when a user issues a command when nothing is focused." name="IDS_CHROMEVOX_WARNING_NO_CURRENT_RANGE">
+ No focus. Press Ctrl+T to open a new tab.
+ </message>
+ <message desc="A hint to the user that the current control is checkable." name="IDS_CHROMEVOX_HINT_CHECKABLE">
+ Press Search+Space to toggle.
+ </message>
+ <message desc="A hint to the user that the current control is clickable." name="IDS_CHROMEVOX_HINT_CLICKABLE">
+ Press Search+Space to activate.
+ </message>
+ <message desc="A hint to the user that the current control has a list of auto completions." name="IDS_CHROMEVOX_HINT_AUTOCOMPLETE_LIST">
+ Press up or down arrow for auto completions.
+ </message>
+ <message desc="A hint to the user that the current control has inline auto completions." name="IDS_CHROMEVOX_HINT_AUTOCOMPLETE_INLINE">
+ Type to auto complete.
+ </message>
+ <message desc="A hint to the user for interacting with the table control." name="IDS_CHROMEVOX_HINT_TABLE">
+ Press Search+Ctrl+Alt with arrows to navigate by cell.
+ </message>
+ <message desc="A hint to the user for interacting with the menu control." name="IDS_CHROMEVOX_HINT_MENU">
+ Press up or down arrow to navigate; enter to activate.
+ </message>
</messages>
</release>
</grit>
diff --git a/chromium/chrome/browser/resources/chromeos/genius_app/manifest.json b/chromium/chrome/browser/resources/chromeos/genius_app/manifest.json
index 6a625f766a7..e0f1b720490 100644
--- a/chromium/chrome/browser/resources/chromeos/genius_app/manifest.json
+++ b/chromium/chrome/browser/resources/chromeos/genius_app/manifest.json
@@ -46,6 +46,7 @@
"https://www.googleapis.com/auth/supportcontent",
"https://www.googleapis.com/auth/cases",
"https://www.googleapis.com/auth/cases.readonly",
+ "https://www.googleapis.com/auth/pixelbook.email.preferences",
"https://www.google.com/accounts/OAuthLogin"
]
},
diff --git a/chromium/chrome/browser/resources/chromeos/login/BUILD.gn b/chromium/chrome/browser/resources/chromeos/login/BUILD.gn
deleted file mode 100644
index 8ad38cb8443..00000000000
--- a/chromium/chrome/browser/resources/chromeos/login/BUILD.gn
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//third_party/closure_compiler/compile_js.gni")
-
-group("closure_compile") {
- deps = [
- ":offline_ad_login",
- ":oobe_change_picture",
- ]
-}
-
-js_binary("offline_ad_login") {
- deps = [
- "//ui/webui/resources/js:load_time_data",
- ]
-}
-
-js_binary("oobe_change_picture") {
- deps = [
- "//ui/webui/resources/cr_elements/chromeos/cr_picture:cr_picture_list",
- "//ui/webui/resources/cr_elements/chromeos/cr_picture:cr_picture_pane",
- "//ui/webui/resources/cr_elements/chromeos/cr_picture:cr_picture_types",
- "//ui/webui/resources/js:assert",
- "//ui/webui/resources/js:i18n_behavior",
- "//ui/webui/resources/js:load_time_data",
- "//ui/webui/resources/js:util",
- ]
-}
diff --git a/chromium/chrome/browser/resources/chromeos/login/compiled_resources2.gyp b/chromium/chrome/browser/resources/chromeos/login/compiled_resources2.gyp
index de8e38ce6b7..dbe606c4978 100644
--- a/chromium/chrome/browser/resources/chromeos/login/compiled_resources2.gyp
+++ b/chromium/chrome/browser/resources/chromeos/login/compiled_resources2.gyp
@@ -4,9 +4,14 @@
{
'targets': [
{
+ 'target_name': 'oobe_select',
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
'target_name': 'offline_ad_login',
'dependencies': [
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
+ ':oobe_select',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
diff --git a/chromium/chrome/browser/resources/chromeos/quick_unlock/compiled_resources2.gyp b/chromium/chrome/browser/resources/chromeos/quick_unlock/compiled_resources2.gyp
deleted file mode 100644
index a953cb9433b..00000000000
--- a/chromium/chrome/browser/resources/chromeos/quick_unlock/compiled_resources2.gyp
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-{
- 'targets': [
- {
- 'target_name': 'md_pin_keyboard',
- 'dependencies': [
- '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
- '<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-button/compiled_resources2.gyp:paper-button-extracted',
- '<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-input/compiled_resources2.gyp:paper-input-extracted',
- ],
- 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
- },
- {
- 'target_name': 'pin_keyboard',
- 'dependencies': [
- '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
- '<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-button/compiled_resources2.gyp:paper-button-extracted',
- '<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-input/compiled_resources2.gyp:paper-input-extracted',
- ],
- 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
- },
- ],
-}
diff --git a/chromium/chrome/browser/resources/chromeos/select_to_speak/BUILD.gn b/chromium/chrome/browser/resources/chromeos/select_to_speak/BUILD.gn
index 3a2962e1cab..d01029bf728 100644
--- a/chromium/chrome/browser/resources/chromeos/select_to_speak/BUILD.gn
+++ b/chromium/chrome/browser/resources/chromeos/select_to_speak/BUILD.gn
@@ -32,14 +32,18 @@ run_jsbundler("select_to_speak_copied_files") {
"../chromevox/cvox2/background/tree_walker.js",
"checked.png",
"closure_shim.js",
+ "earcons/null_selection.ogg",
+ "node_utils.js",
"options.css",
"options.html",
"paragraph_utils.js",
+ "rect_utils.js",
"select_to_speak.js",
"select_to_speak_gdocs_script.js",
"select_to_speak_main.js",
"select_to_speak_options.js",
"unchecked.png",
+ "word_utils.js",
]
rewrite_rules = [
rebase_path(".", root_build_dir) + ":",
@@ -146,11 +150,13 @@ js2gtest("select_to_speak_extjs_tests") {
test_type = "extension"
sources = [
"select_to_speak_keystroke_selection_test.extjs",
+ "select_to_speak_mouse_selection_test.extjs",
]
gen_include_files = [
"../chromevox/testing/callback_helper.js",
"mock_tts.js",
"select_to_speak_e2e_test_base.js",
+ "pipe.jpg",
]
defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
}
diff --git a/chromium/chrome/browser/resources/chromeos/select_to_speak/compiled_resources2.gyp b/chromium/chrome/browser/resources/chromeos/select_to_speak/compiled_resources2.gyp
index ec7035886ae..b7a05836e6f 100644
--- a/chromium/chrome/browser/resources/chromeos/select_to_speak/compiled_resources2.gyp
+++ b/chromium/chrome/browser/resources/chromeos/select_to_speak/compiled_resources2.gyp
@@ -8,24 +8,28 @@
'dependencies': [
'../chromevox/cvox2/background/constants',
'../chromevox/cvox2/background/automation_util',
- 'externs',
- 'paragraph_utils',
- '<(EXTERNS_GYP):accessibility_private',
- '<(EXTERNS_GYP):automation',
- '<(EXTERNS_GYP):chrome_extensions',
- '<(EXTERNS_GYP):command_line_private',
- '<(EXTERNS_GYP):metrics_private',
+ 'externs',
+ 'rect_utils',
+ 'paragraph_utils',
+ 'word_utils',
+ 'node_utils',
+ '<(EXTERNS_GYP):accessibility_private',
+ '<(EXTERNS_GYP):automation',
+ '<(EXTERNS_GYP):chrome_extensions',
+ '<(EXTERNS_GYP):clipboard',
+ '<(EXTERNS_GYP):command_line_private',
+ '<(EXTERNS_GYP):metrics_private',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
{
'target_name': 'select_to_speak_options',
'dependencies': [
- 'externs',
- '<(EXTERNS_GYP):accessibility_private',
- '<(EXTERNS_GYP):automation',
- '<(EXTERNS_GYP):chrome_extensions',
- '<(EXTERNS_GYP):metrics_private',
+ 'externs',
+ '<(EXTERNS_GYP):accessibility_private',
+ '<(EXTERNS_GYP):automation',
+ '<(EXTERNS_GYP):chrome_extensions',
+ '<(EXTERNS_GYP):metrics_private',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
@@ -34,42 +38,65 @@
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
{
+ 'target_name': 'node_utils',
+ 'dependencies': [
+ 'externs',
+ 'rect_utils',
+ 'paragraph_utils',
+ '<(EXTERNS_GYP):automation',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'word_utils',
+ 'dependencies': [
+ 'externs',
+ 'paragraph_utils',
+ '<(EXTERNS_GYP):automation',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
'target_name': 'paragraph_utils',
'dependencies': [
- 'externs',
- '<(EXTERNS_GYP):accessibility_private',
- '<(EXTERNS_GYP):automation',
- '<(EXTERNS_GYP):chrome_extensions',
+ 'externs',
+ '<(EXTERNS_GYP):accessibility_private',
+ '<(EXTERNS_GYP):automation',
+ '<(EXTERNS_GYP):chrome_extensions',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
{
+ 'target_name': 'rect_utils',
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
'target_name': '../chromevox/cvox2/background/automation_util',
'dependencies': [
- '../chromevox/cvox2/background/automation_predicate',
- '../chromevox/cvox2/background/tree_walker',
- '../chromevox/cvox2/background/constants',
- '<(EXTERNS_GYP):automation',
- '<(EXTERNS_GYP):chrome_extensions',
+ '../chromevox/cvox2/background/automation_predicate',
+ '../chromevox/cvox2/background/tree_walker',
+ '../chromevox/cvox2/background/constants',
+ '<(EXTERNS_GYP):automation',
+ '<(EXTERNS_GYP):chrome_extensions',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
{
'target_name': '../chromevox/cvox2/background/tree_walker',
'dependencies': [
- '../chromevox/cvox2/background/automation_predicate',
- '../chromevox/cvox2/background/constants',
- '<(EXTERNS_GYP):automation',
- '<(EXTERNS_GYP):chrome_extensions',
+ '../chromevox/cvox2/background/automation_predicate',
+ '../chromevox/cvox2/background/constants',
+ '<(EXTERNS_GYP):automation',
+ '<(EXTERNS_GYP):chrome_extensions',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
{
'target_name': '../chromevox/cvox2/background/automation_predicate',
'dependencies': [
- '../chromevox/cvox2/background/constants',
- '<(EXTERNS_GYP):automation',
- '<(EXTERNS_GYP):chrome_extensions',
+ '../chromevox/cvox2/background/constants',
+ '<(EXTERNS_GYP):automation',
+ '<(EXTERNS_GYP):chrome_extensions',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
diff --git a/chromium/chrome/browser/resources/chromeos/wallpaper_manager/manifest.json b/chromium/chrome/browser/resources/chromeos/wallpaper_manager/manifest.json
index 8feeb9309b5..d4c6c37d202 100644
--- a/chromium/chrome/browser/resources/chromeos/wallpaper_manager/manifest.json
+++ b/chromium/chrome/browser/resources/chromeos/wallpaper_manager/manifest.json
@@ -20,6 +20,7 @@
"alarms",
"app.window.alpha",
"chrome://resources/",
+ "commandLinePrivate",
"experimental",
"storage",
"unlimitedStorage",
diff --git a/chromium/chrome/browser/resources/components.js b/chromium/chrome/browser/resources/components.js
index f1247bbc0b3..8d29201a3d0 100644
--- a/chromium/chrome/browser/resources/components.js
+++ b/chromium/chrome/browser/resources/components.js
@@ -5,6 +5,13 @@
'use strict';
/**
+ * An array of the latest component data including ID, name, status and
+ * version. This is populated in returnComponentsData() for the convenience of
+ * tests.
+ */
+var currentComponentsData = null;
+
+/**
* Takes the |componentsData| input argument which represents data about the
* currently installed components and populates the html jstemplate with
* that data. It expects an object structure like the above.
@@ -32,7 +39,8 @@ function requestComponentsData() {
/**
* Called by the WebUI to re-populate the page with data representing the
- * current state of installed components.
+ * current state of installed components. The componentsData will also be
+ * stored in currentComponentsData to be available to JS for testing purposes.
* @param {Object} componentsData Detailed info about installed components. The
* template expects each component's format to match the following
* structure to correctly populate the page:
@@ -56,6 +64,10 @@ function returnComponentsData(componentsData) {
bodyContainer.style.visibility = 'hidden';
body.className = '';
+ // Initialize |currentComponentsData|, which can also be updated in
+ // onComponentEvent() later.
+ currentComponentsData = componentsData.components;
+
renderTemplate(componentsData);
// Add handlers to dynamically created HTML elements.
@@ -85,12 +97,24 @@ function returnComponentsData(componentsData) {
* optional.
*/
function onComponentEvent(eventArgs) {
- if (eventArgs['id']) {
- var id = eventArgs['id'];
- $('status-' + id).textContent = eventArgs['event'];
- }
+ if (!eventArgs['id'])
+ return;
+
+ var id = eventArgs['id'];
+
+ var filteredComponents = currentComponentsData.filter(function(entry) {
+ return entry.id === id;
+ });
+ var component = filteredComponents[0];
+
+ var status = eventArgs['event'];
+ $('status-' + id).textContent = status;
+ component['status'] = status;
+
if (eventArgs['version']) {
- $('version-' + id).textContent = eventArgs['version'];
+ var version = eventArgs['version'];
+ $('version-' + id).textContent = version;
+ component['version'] = version;
}
}
diff --git a/chromium/chrome/browser/resources/cryptotoken/enroller.js b/chromium/chrome/browser/resources/cryptotoken/enroller.js
index 9b1349281cd..4bc7bb02d16 100644
--- a/chromium/chrome/browser/resources/cryptotoken/enroller.js
+++ b/chromium/chrome/browser/resources/cryptotoken/enroller.js
@@ -144,7 +144,7 @@ async function makeCertAndKey(original) {
b.addASN1(Tag.SET, (b) => {
b.addASN1(Tag.SEQUENCE, (b) => {
b.addASN1ObjectIdentifier(commonName);
- b.addASN1PrintableString('U2F');
+ b.addASN1PrintableString('U2F Issuer');
});
});
});
@@ -160,7 +160,7 @@ async function makeCertAndKey(original) {
b.addASN1(Tag.SET, (b) => {
b.addASN1(Tag.SEQUENCE, (b) => {
b.addASN1ObjectIdentifier(commonName);
- b.addASN1PrintableString('U2F');
+ b.addASN1PrintableString('U2F Device');
});
});
});
@@ -192,7 +192,21 @@ async function makeCertAndKey(original) {
b.addASN1ObjectIdentifier(ecdsaWithSHA256);
});
b.addASN1(Tag.BITSTRING, (b) => { // Signature
- b.addBytesFromString('\x00'); // (not valid, obviously.)
+ // This signature is obviously not correct since it's constant and the
+ // rest of the certificate is not. However, since the issuer certificate
+ // doesn't exist, there's no way for anyone to check the signature on this
+ // certificate and thus this sufficies. However, at least fastmail.com
+ // expects to be able to parse out a valid ECDSA signature and so one is
+ // provided.
+ b.addBytes(new Uint8Array([
+ 0x00, 0x30, 0x45, 0x02, 0x21, 0x00, 0xc1, 0xa3, 0xa6, 0x8e, 0x2f,
+ 0x16, 0xa7, 0x21, 0x46, 0x27, 0x05, 0x7f, 0x62, 0xbb, 0x72, 0x8c,
+ 0x9e, 0x03, 0xe7, 0xa1, 0xba, 0x62, 0xd0, 0x46, 0x52, 0x4e, 0x45,
+ 0x6d, 0x2c, 0x2f, 0x3f, 0x73, 0x02, 0x20, 0x0b, 0x5f, 0x78, 0xe5,
+ 0x11, 0xaa, 0x18, 0x12, 0x9f, 0x6f, 0x23, 0x6d, 0x92, 0x13, 0x22,
+ 0x7d, 0x92, 0xb4, 0xe6, 0x7e, 0xdf, 0x53, 0xe8, 0x16, 0xdf, 0xb0,
+ 0x5d, 0x9d, 0xc8, 0xb9, 0x0f, 0xde
+ ]));
});
});
return {privateKey: keypair.privateKey, certDER: certBuilder.data};
@@ -207,11 +221,13 @@ const Registration = class {
* @param {string} registrationData the registration response message,
* base64-encoded.
* @param {string} appId the application identifier.
- * @param {string=} opt_clientData the client data, base64-encoded. This
- * field is not really optional; it is an error if it is empty or missing.
+ * @param {string} challenge the server-generated challenge parameter. This
+ * is only used if opt_clientData is null and, in that case, is expected
+ * to be a webSafeBase64-encoded, 32-byte value.
+ * @param {string=} opt_clientData the client data, base64-encoded.
* @throws {Error}
*/
- constructor(registrationData, appId, opt_clientData) {
+ constructor(registrationData, appId, challenge, opt_clientData) {
var data = new ByteString(decodeWebSafeBase64ToArray(registrationData));
var magic = data.getBytes(1);
if (magic[0] != 5) {
@@ -231,12 +247,21 @@ const Registration = class {
throw Error('extra trailing bytes');
}
+ var challengeHash;
if (!opt_clientData) {
- throw Error('missing client data');
+ // U2F_V1 - deprecated
+ challengeHash = decodeWebSafeBase64ToArray(challenge);
+ if (challengeHash.length != 32) {
+ throw Error('bad challenge length for U2F_V1');
+ }
+ } else {
+ // U2F_V2
+ challengeHash =
+ sha256HashOfString(atob(webSafeBase64ToNormal(opt_clientData)));
}
+
/** @private {string} */
- this.clientData_ = atob(webSafeBase64ToNormal(opt_clientData));
- JSON.parse(this.clientData_); // Just checking.
+ this.challengeHash_ = challengeHash;
/** @private {string} */
this.appId_ = appId;
@@ -262,7 +287,7 @@ const Registration = class {
var tbs = new ByteBuilder();
tbs.addBytesFromString('\0');
tbs.addBytes(sha256HashOfString(this.appId_));
- tbs.addBytes(sha256HashOfString(this.clientData_));
+ tbs.addBytes(this.challengeHash_);
tbs.addBytes(this.keyHandle_);
tbs.addBytes(this.publicKey_);
return tbs.data;
@@ -338,10 +363,11 @@ var ConveyancePreference = {
*/
function conveyancePreference(enrollChallenge) {
if (enrollChallenge.hasOwnProperty('attestation') &&
- enrollChallenge['attestation'] == 'none') {
- return ConveyancePreference.NONE;
+ (enrollChallenge['attestation'] == 'direct' ||
+ enrollChallenge['attestation'] == 'indirect')) {
+ return ConveyancePreference.DIRECT;
}
- return ConveyancePreference.DIRECT;
+ return ConveyancePreference.NONE;
}
/**
@@ -379,7 +405,8 @@ function handleU2fEnrollRequest(messageSender, request, sendResponse) {
return registrationData;
}
- const reg = new Registration(registrationData, appId, opt_clientData);
+ const reg = new Registration(
+ registrationData, appId, enrollChallenge['challenge'], opt_clientData);
const keypair = await makeCertAndKey(reg.certificate);
const signature = await reg.sign(keypair.privateKey);
return reg.withReplacement(keypair.certDER, signature);
@@ -538,7 +565,7 @@ function isValidEnrollChallengeArray(enrollChallenges, appIdRequired) {
}
/**
- * Finds the enroll challenge of the given version in the enroll challlenge
+ * Finds the enroll challenge of the given version in the enroll challenge
* array.
* @param {Array<EnrollChallenge>} enrollChallenges The enroll challenges to
* search.
diff --git a/chromium/chrome/browser/resources/cryptotoken/gnubby-u2f.js b/chromium/chrome/browser/resources/cryptotoken/gnubby-u2f.js
index d0335ec9ddf..4d219debe02 100644
--- a/chromium/chrome/browser/resources/cryptotoken/gnubby-u2f.js
+++ b/chromium/chrome/browser/resources/cryptotoken/gnubby-u2f.js
@@ -151,8 +151,8 @@ Gnubby.prototype.version = function(cb) {
cb(-GnubbyDevice.OK, v1.buffer);
return;
}
- if (rc == 0x6700) {
- // Wrong length. Try with non-ISO 7816-4-conforming layout defined in
+ if (rc) {
+ // Error. Try with non-ISO 7816-4-conforming layout defined in
// earlier U2F drafts.
apdu = new Uint8Array(
[0x00, Gnubby.U2F_VERSION, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
diff --git a/chromium/chrome/browser/resources/cryptotoken/manifest.json b/chromium/chrome/browser/resources/cryptotoken/manifest.json
index fecac5426a2..2f86ab26a09 100644
--- a/chromium/chrome/browser/resources/cryptotoken/manifest.json
+++ b/chromium/chrome/browser/resources/cryptotoken/manifest.json
@@ -24,7 +24,7 @@
],
"externally_connectable": {
"matches": [
- "<all_urls>"
+ "https://*/*"
],
"ids": [
"fjajfjhkeibgmiggdfehjplbhmfkialk"
diff --git a/chromium/chrome/browser/resources/download_internals/download_internals.html b/chromium/chrome/browser/resources/download_internals/download_internals.html
index 49e67cdce47..acb85302d75 100644
--- a/chromium/chrome/browser/resources/download_internals/download_internals.html
+++ b/chromium/chrome/browser/resources/download_internals/download_internals.html
@@ -21,6 +21,12 @@
</head>
<body>
<h1>Download Internals</h1>
+ <h4>Start Download</h4>
+ <div id="download-service-request-info">
+ <input id="download-url" type="url"
+ placeholder="http://www.example.com">
+ <button id="start-download">Download</button>
+ </div>
<h4>Service State</h4>
<div>
State: <span id="service-state" class="status"></span>
diff --git a/chromium/chrome/browser/resources/download_internals/download_internals.js b/chromium/chrome/browser/resources/download_internals/download_internals.js
index 62f1f1f0ab8..d46a2106ca7 100644
--- a/chromium/chrome/browser/resources/download_internals/download_internals.js
+++ b/chromium/chrome/browser/resources/download_internals/download_internals.js
@@ -122,6 +122,10 @@ cr.define('downloadInternals', function() {
cr.addWebUIListener('service-download-failed', onServiceDownloadFailed);
cr.addWebUIListener('service-request-made', onServiceRequestMade);
+ $('start-download').onclick = function() {
+ browserProxy.startDownload($('download-url').value);
+ };
+
// Kick off requests for the current system state.
browserProxy.getServiceStatus().then(onServiceStatusChanged);
browserProxy.getServiceDownloads().then(onServiceDownloadsAvailable);
diff --git a/chromium/chrome/browser/resources/download_internals/download_internals_browser_proxy.js b/chromium/chrome/browser/resources/download_internals/download_internals_browser_proxy.js
index 89642e117d5..a79453ac7f3 100644
--- a/chromium/chrome/browser/resources/download_internals/download_internals_browser_proxy.js
+++ b/chromium/chrome/browser/resources/download_internals/download_internals_browser_proxy.js
@@ -105,6 +105,12 @@ cr.define('downloadInternals', function() {
* of downloads is fetched.
*/
getServiceDownloads() {}
+
+ /**
+ * Starts a download with the Download Service.
+ * @param {string} url The download URL.
+ */
+ startDownload(url) {}
}
/**
@@ -120,6 +126,11 @@ cr.define('downloadInternals', function() {
getServiceDownloads() {
return cr.sendWithPromise('getServiceDownloads');
}
+
+ /** @override */
+ startDownload(url) {
+ return cr.sendWithPromise('startDownload', url);
+ }
}
cr.addSingletonGetter(DownloadInternalsBrowserProxyImpl);
diff --git a/chromium/chrome/browser/resources/extensions/extensions.js b/chromium/chrome/browser/resources/extensions/extensions.js
index 663f8382b3d..4f9d1b2beb6 100644
--- a/chromium/chrome/browser/resources/extensions/extensions.js
+++ b/chromium/chrome/browser/resources/extensions/extensions.js
@@ -19,10 +19,6 @@
// <include src="chromeos/kiosk_apps.js">
// </if>
-// Used for observing function of the backend datasource for this page by
-// tests.
-var webuiResponded = false;
-
cr.define('extensions', function() {
var ExtensionList = extensions.ExtensionList;
@@ -185,7 +181,6 @@ cr.define('extensions', function() {
// don't need to display the interstitial spinner.
if (!this.hasLoaded_)
this.setLoading_(true);
- webuiResponded = true;
/** @const */
var supervised = profileInfo.isSupervised;
diff --git a/chromium/chrome/browser/resources/feedback/js/feedback.js b/chromium/chrome/browser/resources/feedback/js/feedback.js
index 909a5e50a68..538d7c825e4 100644
--- a/chromium/chrome/browser/resources/feedback/js/feedback.js
+++ b/chromium/chrome/browser/resources/feedback/js/feedback.js
@@ -334,6 +334,13 @@ function initialize() {
});
chrome.app.window.current().show();
+ // Allow feedback to be sent even if the screenshot failed.
+ if (!screenshotCanvas) {
+ $('screenshot-checkbox').disabled = true;
+ $('screenshot-checkbox').checked = false;
+ return;
+ }
+
screenshotCanvas.toBlob(function(blob) {
$('screenshot-image').src = URL.createObjectURL(blob);
// Only set the alt text when the src url is available, otherwise we'd
diff --git a/chromium/chrome/browser/resources/feedback/js/take_screenshot.js b/chromium/chrome/browser/resources/feedback/js/take_screenshot.js
index 3f099724440..830fd726004 100644
--- a/chromium/chrome/browser/resources/feedback/js/take_screenshot.js
+++ b/chromium/chrome/browser/resources/feedback/js/take_screenshot.js
@@ -5,7 +5,7 @@
/**
* Function to take the screenshot of the current screen.
* @param {function(HTMLCanvasElement)} callback Callback for returning the
- * canvas with the screenshot on it.
+ * canvas with the screenshot. Called with null if the screenshot failed.
*/
function takeScreenshot(callback) {
var screenshotStream = null;
@@ -47,5 +47,6 @@ function takeScreenshot(callback) {
console.error(
'takeScreenshot failed: ' + err.name + '; ' + err.message + '; ' +
err.constraintName);
+ callback(null);
});
}
diff --git a/chromium/chrome/browser/resources/gaia_auth_host/saml_handler.js b/chromium/chrome/browser/resources/gaia_auth_host/saml_handler.js
index 670bf7f90af..ba62489f2e2 100644
--- a/chromium/chrome/browser/resources/gaia_auth_host/saml_handler.js
+++ b/chromium/chrome/browser/resources/gaia_auth_host/saml_handler.js
@@ -469,7 +469,7 @@ cr.define('cr.login', function() {
this.authDomain = extractDomain(msg.url);
this.dispatchEvent(new CustomEvent('authPageLoaded', {
detail: {
- url: url,
+ url: msg.url,
isSAMLPage: this.isSamlPage_,
domain: this.authDomain
}
diff --git a/chromium/chrome/browser/resources/identity_internals.html b/chromium/chrome/browser/resources/identity_internals.html
index fcfcc431028..3be44786a73 100644
--- a/chromium/chrome/browser/resources/identity_internals.html
+++ b/chromium/chrome/browser/resources/identity_internals.html
@@ -1,8 +1,8 @@
<!doctype html>
-<html i18n-values="dir:textdirection;lang:language">
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
<head>
<meta charset="utf-8">
- <title i18n-content="tokenCacheHeader"></title>
+ <title>$i18n{tokenCacheHeader}</title>
<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
<link rel="stylesheet" href="identity_internals.css">
<script src="chrome://resources/js/cr.js"></script>
@@ -13,7 +13,7 @@
<script src="identity_internals.js"></script>
</head>
<body>
- <h2 class="header" i18n-content="tokenCacheHeader"></h2>
+ <h2 class="header">$i18n{tokenCacheHeader}</h2>
<div id="token-list"></div>
<script src="chrome://resources/js/i18n_template.js"></script>
</body>
diff --git a/chromium/chrome/browser/resources/input_ime/ime_window_close.png b/chromium/chrome/browser/resources/input_ime/ime_window_close.png
index 0010118ca99..6b3bd5d836f 100644
--- a/chromium/chrome/browser/resources/input_ime/ime_window_close.png
+++ b/chromium/chrome/browser/resources/input_ime/ime_window_close.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/input_ime/ime_window_close_click.png b/chromium/chrome/browser/resources/input_ime/ime_window_close_click.png
index a954503b6d3..60a218cf395 100644
--- a/chromium/chrome/browser/resources/input_ime/ime_window_close_click.png
+++ b/chromium/chrome/browser/resources/input_ime/ime_window_close_click.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/input_ime/ime_window_close_hover.png b/chromium/chrome/browser/resources/input_ime/ime_window_close_hover.png
index a954503b6d3..60a218cf395 100644
--- a/chromium/chrome/browser/resources/input_ime/ime_window_close_hover.png
+++ b/chromium/chrome/browser/resources/input_ime/ime_window_close_hover.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/inspect/inspect.css b/chromium/chrome/browser/resources/inspect/inspect.css
index 5bd5a239edb..eefce85827b 100644
--- a/chromium/chrome/browser/resources/inspect/inspect.css
+++ b/chromium/chrome/browser/resources/inspect/inspect.css
@@ -178,6 +178,14 @@ img {
margin-left: 6px;
}
+.browser-fallback-note {
+ display: flex;
+ flex-flow: row wrap;
+ margin-left: 4px;
+ margin-top: 5px;
+ min-height: 15px;
+}
+
.used-for-port-forwarding {
background-image: url(../../../../ui/webui/resources/images/info.svg);
height: 15px;
diff --git a/chromium/chrome/browser/resources/inspect/inspect.js b/chromium/chrome/browser/resources/inspect/inspect.js
index 64288b83e80..1901d601dd6 100644
--- a/chromium/chrome/browser/resources/inspect/inspect.js
+++ b/chromium/chrome/browser/resources/inspect/inspect.js
@@ -7,12 +7,17 @@ var MIN_VERSION_TARGET_ID = 26;
var MIN_VERSION_NEW_TAB = 29;
var MIN_VERSION_TAB_ACTIVATE = 30;
var WEBRTC_SERIAL = 'WEBRTC';
+var HOST_CHROME_VERSION;
var queryParamsObject = {};
var browserInspector;
var browserInspectorTitle;
(function() {
+var chromeMatch = navigator.userAgent.match(/(?:^|\W)Chrome\/(\S+)/);
+if (chromeMatch && chromeMatch.length > 1)
+ HOST_CHROME_VERSION = chromeMatch[1].split('.').map(s => Number(s) || 0);
+
var queryParams = window.location.search;
if (!queryParams)
return;
@@ -31,6 +36,21 @@ if ('trace' in queryParamsObject || 'tracing' in queryParamsObject) {
}
})();
+function isVersionNewerThanHost(version) {
+ if (!HOST_CHROME_VERSION)
+ return false;
+ version = version.split('.').map(s => Number(s) || 0);
+ for (var i = 0; i < HOST_CHROME_VERSION.length; i++) {
+ if (i > version.length)
+ return false;
+ if (HOST_CHROME_VERSION[i] > version[i])
+ return false;
+ if (HOST_CHROME_VERSION[i] < version[i])
+ return true;
+ }
+ return false;
+}
+
function sendCommand(command, args) {
chrome.send(command, Array.prototype.slice.call(arguments, 1));
}
@@ -295,6 +315,8 @@ function populateRemoteTargets(devices) {
var majorChromeVersion = browser.adbBrowserChromeVersion;
var pageList;
var browserSection = $(browser.id);
+ var browserNeedsFallback =
+ isVersionNewerThanHost(browser.adbBrowserVersion);
if (browserSection) {
pageList = browserSection.querySelector('.pages');
} else {
@@ -320,6 +342,15 @@ function populateRemoteTargets(devices) {
}
browserSection.appendChild(browserHeader);
+ if (browserNeedsFallback) {
+ var browserFallbackNote = document.createElement('div');
+ browserFallbackNote.className = 'browser-fallback-note';
+ browserFallbackNote.textContent =
+ '\u26A0 Remote browser is newer than client browser. ' +
+ 'Try `inspect fallback` if inspection fails.';
+ browserSection.appendChild(browserFallbackNote);
+ }
+
if (majorChromeVersion >= MIN_VERSION_NEW_TAB) {
var newPage = document.createElement('div');
newPage.className = 'open';
@@ -401,6 +432,12 @@ function populateRemoteTargets(devices) {
row, 'close', sendTargetCommand.bind(null, 'close', page),
false);
}
+ if (browserNeedsFallback) {
+ addActionLink(
+ row, 'inspect fallback',
+ sendTargetCommand.bind(null, 'inspect-fallback', page),
+ page.hasNoUniqueId || page.adbAttachedForeign);
+ }
}
}
updateBrowserVisibility(browserSection);
diff --git a/chromium/chrome/browser/resources/local_discovery/local_discovery.html b/chromium/chrome/browser/resources/local_discovery/local_discovery.html
index 1ba6126155d..a99ecdcb5c5 100644
--- a/chromium/chrome/browser/resources/local_discovery/local_discovery.html
+++ b/chromium/chrome/browser/resources/local_discovery/local_discovery.html
@@ -26,6 +26,7 @@
<h1>$i18n{confirmRegistration}</h1>
<div class="dialog-contents">
<div id="register-message">
+ $i18nRaw{registerPrinterInformationMessage}
</div>
<div class="button-list">
@@ -38,10 +39,8 @@
</a>
</div>
<button class="register-cancel">$i18n{cancel}</button>
- <button id="register-continue-button">
- $i18n{serviceRegister}
- </button>
- </div>
+ <button id="register-continue">$i18n{confirm}</button>
+ </div>
</div>
</div>
diff --git a/chromium/chrome/browser/resources/local_discovery/local_discovery.js b/chromium/chrome/browser/resources/local_discovery/local_discovery.js
index 6906a948908..0b9bc336cc9 100644
--- a/chromium/chrome/browser/resources/local_discovery/local_discovery.js
+++ b/chromium/chrome/browser/resources/local_discovery/local_discovery.js
@@ -118,6 +118,7 @@ cr.define('local_discovery', function() {
deviceContainer: function() {
return $('register-device-list');
},
+
/**
* Register the device.
*/
@@ -133,11 +134,8 @@ cr.define('local_discovery', function() {
*/
showRegister: function() {
recordUmaEvent(DEVICES_PAGE_EVENTS.REGISTER_CLICKED);
- $('register-message').textContent = loadTimeData.getStringF(
- isPrinter(this.info.type) ? 'registerPrinterConfirmMessage' :
- 'registerDeviceConfirmMessage',
- this.info.display_name);
- $('register-continue-button').onclick = this.register.bind(this);
+ $('register-continue').onclick = this.register.bind(this);
+
showRegisterOverlay();
},
/**
@@ -521,7 +519,7 @@ cr.define('local_discovery', function() {
isUserLoggedIn || isUserSupervisedOrOffTheRecord;
$('register-overlay-login-promo').hidden =
isUserLoggedIn || isUserSupervisedOrOffTheRecord;
- $('register-continue-button').disabled =
+ $('register-continue').disabled =
!isUserLoggedIn || isUserSupervisedOrOffTheRecord;
$('my-devices-container').hidden = userSupervisedOrOffTheRecord;
diff --git a/chromium/chrome/browser/resources/local_ntp/local_ntp.css b/chromium/chrome/browser/resources/local_ntp/local_ntp.css
index 2b38a36fc16..343afa804b9 100644
--- a/chromium/chrome/browser/resources/local_ntp/local_ntp.css
+++ b/chromium/chrome/browser/resources/local_ntp/local_ntp.css
@@ -92,6 +92,8 @@ button {
display: flex;
flex-direction: column;
height: 100%;
+ position: relative;
+ z-index: 1;
}
#logo,
@@ -536,7 +538,6 @@ html[dir=rtl] #attribution,
right: 0;
top: 0;
transition: opacity 130ms;
- z-index: 1;
}
#one-google.hidden {
diff --git a/chromium/chrome/browser/resources/local_ntp/most_visited_single.js b/chromium/chrome/browser/resources/local_ntp/most_visited_single.js
index c4c0bfe2da7..9bdd106296f 100644
--- a/chromium/chrome/browser/resources/local_ntp/most_visited_single.js
+++ b/chromium/chrome/browser/resources/local_ntp/most_visited_single.js
@@ -27,35 +27,6 @@ var LOG_TYPE = {
/**
- * The different sources where an NTP tile's title can originate from.
- * Note: Keep in sync with components/ntp_tiles/tile_title_source.h
- * @enum {number}
- * @const
- */
-var TileTitleSource = {
- UNKNOWN: 0,
- MANIFEST: 1,
- META_TAG: 2,
- TITLE: 3,
- INFERRED: 4
-};
-
-
-/**
- * The different sources that an NTP tile can have.
- * Note: Keep in sync with components/ntp_tiles/tile_source.h
- * @enum {number}
- * @const
- */
-var TileSource = {
- TOP_SITES: 0,
- SUGGESTIONS_SERVICE: 1,
- POPULAR: 3,
- WHITELIST: 4,
-};
-
-
-/**
* The different (visual) types that an NTP tile can have.
* Note: Keep in sync with components/ntp_tiles/tile_visual_type.h
* @enum {number}
@@ -128,9 +99,11 @@ var logEvent = function(eventType) {
/**
* Log impression of an NTP tile.
* @param {number} tileIndex Position of the tile, >= 0 and < NUMBER_OF_TILES.
- * @param {number} tileTitleSource The title's source from TileTitleSource.
- * @param {number} tileSource The source from TileSource.
- * @param {number} tileType The type from TileVisualType.
+ * @param {number} tileTitleSource The source of the tile's title as received
+ * from getMostVisitedItemData.
+ * @param {number} tileSource The tile's source as received from
+ * getMostVisitedItemData.
+ * @param {number} tileType The tile's visual type from TileVisualType.
* @param {Date} dataGenerationTime Timestamp representing when the tile was
* produced by a ranking algorithm.
*/
@@ -143,9 +116,11 @@ function logMostVisitedImpression(
/**
* Log click on an NTP tile.
* @param {number} tileIndex Position of the tile, >= 0 and < NUMBER_OF_TILES.
- * @param {number} tileTitleSource The title's source from TileTitleSource.
- * @param {number} tileSource The source from TileSource.
- * @param {number} tileType The type from TileVisualType.
+ * @param {number} tileTitleSource The source of the tile's title as received
+ * from getMostVisitedItemData.
+ * @param {number} tileSource The tile's source as received from
+ * getMostVisitedItemData.
+ * @param {number} tileType The tile's visual type from TileVisualType.
* @param {Date} dataGenerationTime Timestamp representing when the tile was
* produced by a ranking algorithm.
*/
diff --git a/chromium/chrome/browser/resources/md_bookmarks/app.html b/chromium/chrome/browser/resources/md_bookmarks/app.html
index 64393cde7f8..82c01245dd4 100644
--- a/chromium/chrome/browser/resources/md_bookmarks/app.html
+++ b/chromium/chrome/browser/resources/md_bookmarks/app.html
@@ -30,6 +30,7 @@
display: flex;
flex-direction: row;
flex-grow: 1;
+ overflow: hidden;
}
#splitter {
diff --git a/chromium/chrome/browser/resources/md_bookmarks/bookmarks.html b/chromium/chrome/browser/resources/md_bookmarks/bookmarks.html
index 6b15d439bb8..a12bb061aab 100644
--- a/chromium/chrome/browser/resources/md_bookmarks/bookmarks.html
+++ b/chromium/chrome/browser/resources/md_bookmarks/bookmarks.html
@@ -6,6 +6,11 @@
<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
<link rel="stylesheet" href="chrome://resources/css/md_colors.css">
<style>
+ html {
+ /* Remove 300ms delay for 'click' event, when using touch interface. */
+ touch-action: manipulation;
+ }
+
html,
body {
background: var(--md-background-color);
diff --git a/chromium/chrome/browser/resources/md_bookmarks/command_manager.html b/chromium/chrome/browser/resources/md_bookmarks/command_manager.html
index 508b0501a43..13b59a66782 100644
--- a/chromium/chrome/browser/resources/md_bookmarks/command_manager.html
+++ b/chromium/chrome/browser/resources/md_bookmarks/command_manager.html
@@ -2,6 +2,7 @@
<link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html">
<link rel="import" href="chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.html">
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
<link rel="import" href="chrome://resources/html/cr/ui/command.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
<link rel="import" href="chrome://bookmarks/dialog_focus_manager.html">
@@ -11,7 +12,7 @@
<dom-module id="bookmarks-command-manager">
<template>
- <style include="shared-style">
+ <style include="shared-style paper-button-style">
.label {
flex: 1;
}
@@ -30,7 +31,7 @@
<template is="cr-lazy-render" id="dropdown">
<dialog is="cr-action-menu" on-mousedown="onMenuMousedown_">
<template is="dom-repeat" items="[[menuCommands_]]" as="command">
- <button class="dropdown-item"
+ <button slot="item" class="dropdown-item"
command$="[[command]]"
hidden$="[[!isCommandVisible_(command, menuIds_)]]"
disabled$="[[!isCommandEnabled_(command, menuIds_)]]"
@@ -56,10 +57,10 @@
<div slot="title">$i18n{openDialogTitle}</div>
<div slot="body"></div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onOpenCancelTap_">
+ <paper-button class="cancel-button" on-click="onOpenCancelTap_">
$i18n{cancel}
</paper-button>
- <paper-button class="action-button" on-tap="onOpenConfirmTap_">
+ <paper-button class="action-button" on-click="onOpenConfirmTap_">
$i18n{openDialogConfirm}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/md_bookmarks/command_manager.js b/chromium/chrome/browser/resources/md_bookmarks/command_manager.js
index 668a4497522..900da62a5f6 100644
--- a/chromium/chrome/browser/resources/md_bookmarks/command_manager.js
+++ b/chromium/chrome/browser/resources/md_bookmarks/command_manager.js
@@ -58,21 +58,6 @@ cr.define('bookmarks', function() {
});
this.updateFromStore();
- /** @private {function(!Event)} */
- this.boundOnOpenCommandMenu_ = this.onOpenCommandMenu_.bind(this);
- document.addEventListener(
- 'open-command-menu', this.boundOnOpenCommandMenu_);
-
- /** @private {function()} */
- this.boundOnCommandUndo_ = () => {
- this.handle(Command.UNDO, new Set());
- };
- document.addEventListener('command-undo', this.boundOnCommandUndo_);
-
- /** @private {function(!Event)} */
- this.boundOnKeydown_ = this.onKeydown_.bind(this);
- document.addEventListener('keydown', this.boundOnKeydown_);
-
/** @private {!Map<Command, cr.ui.KeyboardShortcutList>} */
this.shortcuts_ = new Map();
@@ -92,14 +77,40 @@ cr.define('bookmarks', function() {
this.addShortcut_(Command.CUT, 'Ctrl|x', 'Meta|x');
this.addShortcut_(Command.COPY, 'Ctrl|c', 'Meta|c');
this.addShortcut_(Command.PASTE, 'Ctrl|v', 'Meta|v');
+
+ /** @private {!Map<string, Function>} */
+ this.boundListeners_ = new Map();
+
+ const addDocumentListener = (eventName, handler) => {
+ assert(!this.boundListeners_.has(eventName));
+ const boundListener = handler.bind(this);
+ this.boundListeners_.set(eventName, boundListener);
+ document.addEventListener(eventName, boundListener);
+ };
+ addDocumentListener('open-command-menu', this.onOpenCommandMenu_);
+ addDocumentListener('keydown', this.onKeydown_);
+
+ const addDocumentListenerForCommand = (eventName, command) => {
+ addDocumentListener(eventName, (e) => {
+ if (e.path[0].tagName == 'INPUT')
+ return;
+
+ const items = this.getState().selection.items;
+ if (this.canExecute(command, items))
+ this.handle(command, items);
+ });
+ };
+ addDocumentListenerForCommand('command-undo', Command.UNDO);
+ addDocumentListenerForCommand('cut', Command.CUT);
+ addDocumentListenerForCommand('copy', Command.COPY);
+ addDocumentListenerForCommand('paste', Command.PASTE);
},
detached: function() {
CommandManager.instance_ = null;
- document.removeEventListener(
- 'open-command-menu', this.boundOnOpenCommandMenu_);
- document.removeEventListener('command-undo', this.boundOnCommandUndo_);
- document.removeEventListener('keydown', this.boundOnKeydown_);
+ this.boundListeners_.forEach(
+ (handler, eventName) =>
+ document.removeEventListener(eventName, handler));
},
/**
diff --git a/chromium/chrome/browser/resources/md_bookmarks/edit_dialog.html b/chromium/chrome/browser/resources/md_bookmarks/edit_dialog.html
index c9e6ce563d9..2be1feb461a 100644
--- a/chromium/chrome/browser/resources/md_bookmarks/edit_dialog.html
+++ b/chromium/chrome/browser/resources/md_bookmarks/edit_dialog.html
@@ -2,6 +2,7 @@
<link rel="import" href="chrome://resources/html/assert.html">
<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
@@ -9,7 +10,7 @@
<dom-module id="bookmarks-edit-dialog">
<template>
- <style include="cr-shared-style"></style>
+ <style include="cr-shared-style paper-button-style"></style>
<dialog is="cr-dialog" id="dialog">
<div slot="title">
[[getDialogTitle_(isFolder_, isEdit_)]]
@@ -30,11 +31,11 @@
</paper-input>
</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onCancelButtonTap_">
+ <paper-button class="cancel-button" on-click="onCancelButtonTap_">
$i18n{cancel}
</paper-button>
<paper-button id="saveButton" class="action-button"
- on-tap="onSaveButtonTap_">
+ on-click="onSaveButtonTap_">
$i18n{saveEdit}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/md_bookmarks/folder_node.html b/chromium/chrome/browser/resources/md_bookmarks/folder_node.html
index e8d13978fb4..c1a85652d3b 100644
--- a/chromium/chrome/browser/resources/md_bookmarks/folder_node.html
+++ b/chromium/chrome/browser/resources/md_bookmarks/folder_node.html
@@ -76,7 +76,7 @@
<div id="container"
class="v-centered"
- on-tap="selectFolder_"
+ on-click="selectFolder_"
on-dblclick="toggleFolder_"
on-contextmenu="onContextMenu_"
tabindex$="[[getTabIndex_(selectedFolder_, itemId)]]"
@@ -85,7 +85,7 @@
<template is="dom-if" if="[[hasChildFolder_]]">
<button is="paper-icon-button-light"
id="arrow"
- on-tap="toggleFolder_"
+ on-click="toggleFolder_"
on-mousedown="preventDefault_"
tabindex="-1"
aria-label$="[[getButtonAriaLabel_(isOpen, item_)]]">
@@ -98,7 +98,7 @@
open$="[[isSelectedFolder_]]"
no-children$="[[!hasChildFolder_]]">
</div>
- <div class="menu-label elided-text"" title="[[item_.title]]">
+ <div class="menu-label elided-text" title="[[item_.title]]">
[[item_.title]]
</div>
</div>
diff --git a/chromium/chrome/browser/resources/md_bookmarks/folder_node.js b/chromium/chrome/browser/resources/md_bookmarks/folder_node.js
index 78008a9655f..4b913c3ea09 100644
--- a/chromium/chrome/browser/resources/md_bookmarks/folder_node.js
+++ b/chromium/chrome/browser/resources/md_bookmarks/folder_node.js
@@ -141,7 +141,7 @@ Polymer({
changeKeyboardSelection_: function(xDirection, yDirection, currentFocus) {
let newFocusFolderNode = null;
const isChildFolderNodeFocused =
- currentFocus.tagName == 'BOOKMARKS-FOLDER-NODE';
+ currentFocus && currentFocus.tagName == 'BOOKMARKS-FOLDER-NODE';
if (xDirection == 1) {
// The right arrow opens a folder if closed and goes to the first child
diff --git a/chromium/chrome/browser/resources/md_bookmarks/list.html b/chromium/chrome/browser/resources/md_bookmarks/list.html
index 1927916322b..ddbf28f533c 100644
--- a/chromium/chrome/browser/resources/md_bookmarks/list.html
+++ b/chromium/chrome/browser/resources/md_bookmarks/list.html
@@ -21,7 +21,7 @@
}
#list {
- @apply(--shadow-elevation-2dp);
+ @apply --shadow-elevation-2dp;
background-color: #fff;
margin: 0 auto;
max-width: var(--card-max-width);
diff --git a/chromium/chrome/browser/resources/md_bookmarks/list.js b/chromium/chrome/browser/resources/md_bookmarks/list.js
index 3b6aa847a67..11b1580b221 100644
--- a/chromium/chrome/browser/resources/md_bookmarks/list.js
+++ b/chromium/chrome/browser/resources/md_bookmarks/list.js
@@ -142,7 +142,13 @@ Polymer({
/** @private */
emptyListMessage_: function() {
- const emptyListMessage = this.searchTerm_ ? 'noSearchResults' : 'emptyList';
+ let emptyListMessage = 'noSearchResults';
+ if (!this.searchTerm_) {
+ emptyListMessage = bookmarks.util.canReorderChildren(
+ this.getState(), this.getState().selectedFolder) ?
+ 'emptyList' :
+ 'emptyUnmodifiableList';
+ }
return loadTimeData.getString(emptyListMessage);
},
diff --git a/chromium/chrome/browser/resources/md_bookmarks/toast_manager.html b/chromium/chrome/browser/resources/md_bookmarks/toast_manager.html
index f94f6342f80..454d1f1ab70 100644
--- a/chromium/chrome/browser/resources/md_bookmarks/toast_manager.html
+++ b/chromium/chrome/browser/resources/md_bookmarks/toast_manager.html
@@ -2,11 +2,12 @@
<link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-announcer/iron-a11y-announcer.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
<link rel="import" href="chrome://resources/cr_elements/cr_toast/cr_toast.html">
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
<link rel="import" href="chrome://bookmarks/shared_style.html">
<dom-module id="bookmarks-toast-manager">
<template>
- <style include="shared-style">
+ <style include="shared-style paper-button-style">
#content {
color: #fff;
display: flex;
@@ -17,9 +18,7 @@
-webkit-margin-end: 0;
-webkit-margin-start: 32px;
color: var(--google-blue-300);
- font-weight: 500;
height: 32px;
- min-width: 52px;
padding: 8px;
}
@@ -34,7 +33,7 @@
</style>
<cr-toast id="toast" duration="[[duration]]">
<div id="content" class="elided-text"></div>
- <paper-button id="button" hidden$="[[!showUndo_]]" on-tap="onUndoTap_">
+ <paper-button id="button" hidden$="[[!showUndo_]]" on-click="onUndoTap_">
$i18n{undo}
</paper-button>
</cr-toast>
diff --git a/chromium/chrome/browser/resources/md_bookmarks/toolbar.html b/chromium/chrome/browser/resources/md_bookmarks/toolbar.html
index 219c383cd75..4f8b2786f45 100644
--- a/chromium/chrome/browser/resources/md_bookmarks/toolbar.html
+++ b/chromium/chrome/browser/resources/md_bookmarks/toolbar.html
@@ -52,7 +52,7 @@
id="menuButton"
class="more-actions more-vert-button"
title="$i18n{organizeButtonTitle}"
- on-tap="onMenuButtonOpenTap_"
+ on-click="onMenuButtonOpenTap_"
aria-haspopup="menu">
<div></div>
<div></div>
diff --git a/chromium/chrome/browser/resources/md_downloads/1x/incognito_marker.png b/chromium/chrome/browser/resources/md_downloads/1x/incognito_marker.png
index 2668850b677..4d7b4f0457a 100644
--- a/chromium/chrome/browser/resources/md_downloads/1x/incognito_marker.png
+++ b/chromium/chrome/browser/resources/md_downloads/1x/incognito_marker.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/md_downloads/1x/no_downloads.png b/chromium/chrome/browser/resources/md_downloads/1x/no_downloads.png
index e445faebe3b..9cb8a7cab08 100644
--- a/chromium/chrome/browser/resources/md_downloads/1x/no_downloads.png
+++ b/chromium/chrome/browser/resources/md_downloads/1x/no_downloads.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/md_downloads/2x/incognito_marker.png b/chromium/chrome/browser/resources/md_downloads/2x/incognito_marker.png
index 3ee7c32a700..af60569dd0d 100644
--- a/chromium/chrome/browser/resources/md_downloads/2x/incognito_marker.png
+++ b/chromium/chrome/browser/resources/md_downloads/2x/incognito_marker.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/md_downloads/2x/no_downloads.png b/chromium/chrome/browser/resources/md_downloads/2x/no_downloads.png
index 90edf87a248..6181df50770 100644
--- a/chromium/chrome/browser/resources/md_downloads/2x/no_downloads.png
+++ b/chromium/chrome/browser/resources/md_downloads/2x/no_downloads.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/md_downloads/compiled_resources2.gyp b/chromium/chrome/browser/resources/md_downloads/compiled_resources2.gyp
index b9e2ca905c3..4adfce2fe86 100644
--- a/chromium/chrome/browser/resources/md_downloads/compiled_resources2.gyp
+++ b/chromium/chrome/browser/resources/md_downloads/compiled_resources2.gyp
@@ -75,7 +75,6 @@
'<(DEPTH)/ui/webui/resources/cr_elements/cr_action_menu/compiled_resources2.gyp:cr_action_menu',
'<(DEPTH)/ui/webui/resources/cr_elements/cr_toolbar/compiled_resources2.gyp:cr_toolbar',
'<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-a11y-announcer/compiled_resources2.gyp:iron-a11y-announcer-extracted',
- '<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-menu/compiled_resources2.gyp:paper-menu-extracted',
'browser_proxy',
'search_service',
],
diff --git a/chromium/chrome/browser/resources/md_downloads/downloads.html b/chromium/chrome/browser/resources/md_downloads/downloads.html
index bc126bb68c6..07eaf62e051 100644
--- a/chromium/chrome/browser/resources/md_downloads/downloads.html
+++ b/chromium/chrome/browser/resources/md_downloads/downloads.html
@@ -9,6 +9,9 @@
--downloads-card-margin: 24px;
--downloads-card-width: 680px;
background: #f1f1f1;
+
+ /* Remove 300ms delay for 'click' event, when using touch interface. */
+ touch-action: manipulation;
}
.loading {
diff --git a/chromium/chrome/browser/resources/md_downloads/item.html b/chromium/chrome/browser/resources/md_downloads/item.html
index a230d16a3d3..3696956b9a7 100644
--- a/chromium/chrome/browser/resources/md_downloads/item.html
+++ b/chromium/chrome/browser/resources/md_downloads/item.html
@@ -54,7 +54,7 @@
}
#content.is-active {
- @apply(--shadow-elevation-2dp);
+ @apply --shadow-elevation-2dp;
}
#content:not(.is-active) {
@@ -244,7 +244,7 @@
<div id="title-area"><!--
Can't have any line breaks.
--><a is="action-link" id="file-link" href="[[data.url]]"
- on-tap="onFileLinkTap_"
+ on-click="onFileLinkTap_"
hidden="[[!completelyOnDisk_]]">[[data.file_name]]</a><!--
Before #name.
--><span id="name"
@@ -263,20 +263,20 @@
</template>
<div id="safe" class="controls" hidden="[[isDangerous_]]">
- <a is="action-link" id="show" on-tap="onShowTap_"
+ <a is="action-link" id="show" on-click="onShowTap_"
hidden="[[!completelyOnDisk_]]">$i18n{controlShowInFolder}</a>
<template is="dom-if" if="[[data.retry]]">
- <paper-button id="retry" on-tap="onRetryTap_">
+ <paper-button id="retry" on-click="onRetryTap_">
$i18n{controlRetry}
</paper-button>
</template>
<template is="dom-if" if="[[pauseOrResumeText_]]">
- <paper-button id="pause-or-resume" on-tap="onPauseOrResumeTap_">
+ <paper-button id="pause-or-resume" on-click="onPauseOrResumeTap_">
[[pauseOrResumeText_]]
</paper-button>
</template>
<template is="dom-if" if="[[showCancel_]]">
- <paper-button id="cancel" on-tap="onCancelTap_">
+ <paper-button id="cancel" on-click="onCancelTap_">
$i18n{controlCancel}
</paper-button>
</template>
@@ -287,17 +287,17 @@
<div id="dangerous" class="controls">
<!-- Dangerous file types (e.g. .exe, .jar). -->
<template is="dom-if" if="[[!isMalware_]]">
- <paper-button id="discard" on-tap="onDiscardDangerousTap_"
+ <paper-button id="discard" on-click="onDiscardDangerousTap_"
class="discard">$i18n{dangerDiscard}</paper-button>
- <paper-button id="save" on-tap="onSaveDangerousTap_"
+ <paper-button id="save" on-click="onSaveDangerousTap_"
class="keep">$i18n{dangerSave}</paper-button>
</template>
<!-- Things that safe browsing has determined to be dangerous. -->
<template is="dom-if" if="[[isMalware_]]">
- <paper-button id="danger-remove" on-tap="onDiscardDangerousTap_"
+ <paper-button id="danger-remove" on-click="onDiscardDangerousTap_"
class="discard">$i18n{controlRemoveFromList}</paper-button>
- <paper-button id="restore" on-tap="onSaveDangerousTap_"
+ <paper-button id="restore" on-click="onSaveDangerousTap_"
class="keep">$i18n{dangerRestore}</paper-button>
</template>
</div>
@@ -307,8 +307,9 @@
<div id="remove-wrapper" class="icon-wrapper">
<button is="paper-icon-button-light" id="remove"
title="$i18n{controlRemoveFromList}"
+ aria-label="$i18n{controlRemoveFromList}"
style$="[[computeRemoveStyle_(isDangerous_, showCancel_)]]"
- on-tap="onRemoveTap_">&#x2715;</button>
+ on-click="onRemoveTap_">&#x2715;</button>
</div>
<div id="incognito" title="$i18n{inIncognito}" hidden="[[!data.otr]]">
diff --git a/chromium/chrome/browser/resources/md_downloads/manager.html b/chromium/chrome/browser/resources/md_downloads/manager.html
index c8c68366be5..3662b4d2e1a 100644
--- a/chromium/chrome/browser/resources/md_downloads/manager.html
+++ b/chromium/chrome/browser/resources/md_downloads/manager.html
@@ -23,6 +23,7 @@
flex: 1 0;
flex-direction: column;
height: 100%;
+ overflow: hidden;
z-index: 0;
}
@@ -39,7 +40,7 @@
}
#drop-shadow {
- @apply(--cr-container-shadow);
+ @apply --cr-container-shadow;
}
:host([has-shadow_]) #drop-shadow {
diff --git a/chromium/chrome/browser/resources/md_downloads/toolbar.html b/chromium/chrome/browser/resources/md_downloads/toolbar.html
index 24209d11177..a98f6b02ca5 100644
--- a/chromium/chrome/browser/resources/md_downloads/toolbar.html
+++ b/chromium/chrome/browser/resources/md_downloads/toolbar.html
@@ -41,15 +41,17 @@
spinner-active="{{spinnerActive}}" on-search-changed="onSearchChanged_">
<button is="paper-icon-button-light" id="moreActions"
title="$i18n{moreActions}" class="dropdown-trigger"
- on-tap="onMoreActionsTap_">
+ on-click="onMoreActionsTap_">
<iron-icon icon="cr:more-vert"></iron-icon>
</button>
</cr-toolbar>
<dialog is="cr-action-menu" id="moreActionsMenu">
- <button class="dropdown-item clear-all" on-tap="onClearAllTap_">
+ <button slot="item" class="dropdown-item clear-all"
+ on-click="onClearAllTap_">
$i18n{clearAll}
- </div>
- <button class="dropdown-item" on-tap="onOpenDownloadsFolderTap_">
+ </button>
+ <button slot="item" class="dropdown-item"
+ on-click="onOpenDownloadsFolderTap_">
$i18n{openDownloadsFolder}
</button>
</dialog>
diff --git a/chromium/chrome/browser/resources/md_extensions/code_section.html b/chromium/chrome/browser/resources/md_extensions/code_section.html
index 093e9a62042..5d575bae3f5 100644
--- a/chromium/chrome/browser/resources/md_extensions/code_section.html
+++ b/chromium/chrome/browser/resources/md_extensions/code_section.html
@@ -2,6 +2,7 @@
<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
<link rel="import" href="chrome://resources/html/cr.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
<link rel="import" href="chrome://resources/html/load_time_data.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
@@ -54,10 +55,16 @@
.more-code {
color: var(--paper-grey-500);
}
+
+ #highlight-description {
+ height: 0;
+ overflow: hidden;
+ }
</style>
- <div id="scroll-container" hidden="[[!codeText_]]">
+ <div id="scroll-container" hidden="[[!highlighted_]]">
<div id="main">
- <div id="line-numbers">
+ <!-- Line numbers are not useful to a screenreader -->
+ <div id="line-numbers" aria-hidden="true">
<div class="more-code before" hidden="[[!truncatedBefore_]]">
...
</div>
@@ -73,7 +80,14 @@
'$i18nPolymer{errorLinesNotShownSingular}',
'$i18nPolymer{errorLinesNotShownPlural}')]]
</div>
- <span>[[codeText_]]</span>
+ <span><!-- Whitespace is preserved in this span. Ignore new lines.
+ --><span>[[before_]]</span><!--
+ --><mark aria-label$="[[highlighted_]]"
+ aria-describedby="highlight-description"><!--
+ --><span aria-hidden="true">[[highlighted_]]</span><!--
+ --></mark><!--
+ --><span>[[after_]]</span><!--
+ --></span>
<div class="more-code after" hidden="[[!truncatedAfter_]]">
[[getLinesNotShownLabel_(
truncatedAfter_,
@@ -83,7 +97,10 @@
</div>
</div>
</div>
- <div id="no-code" hidden="[[codeText_]]">[[couldNotDisplayCode]]</div>
+ <div id="no-code" hidden="[[highlighted_]]">[[couldNotDisplayCode]]</div>
+ <div id="highlight-description" aria-hidden="true">
+ [[highlightDescription_]]
+ </div>
</template>
<script src="code_section.js"></script>
</dom-module>
diff --git a/chromium/chrome/browser/resources/md_extensions/code_section.js b/chromium/chrome/browser/resources/md_extensions/code_section.js
index 149d5d7d054..001dad3a045 100644
--- a/chromium/chrome/browser/resources/md_extensions/code_section.js
+++ b/chromium/chrome/browser/resources/md_extensions/code_section.js
@@ -21,6 +21,8 @@ cr.define('extensions', function() {
const CodeSection = Polymer({
is: 'extensions-code-section',
+ behaviors: [I18nBehavior],
+
properties: {
/**
* The code this object is displaying.
@@ -31,13 +33,17 @@ cr.define('extensions', function() {
value: null,
},
- /**
- * The text of the entire source file. This value does not update on
- * highlight changes; it only updates if the content of the source
- * changes.
- * @private
- */
- codeText_: String,
+ /** @private Highlighted code. */
+ highlighted_: String,
+
+ /** @private Code before the highlighted section. */
+ before_: String,
+
+ /** @private Code after the highlighted section. */
+ after_: String,
+
+ /** @private Description for the highlighted section. */
+ highlightDescription_: String,
/** @private */
lineNumbers_: String,
@@ -67,7 +73,10 @@ cr.define('extensions', function() {
if (!this.code ||
(!this.code.beforeHighlight && !this.code.highlight &&
!this.code.afterHighlight)) {
- this.codeText_ = '';
+ this.highlighted_ = '';
+ this.highlightDescription_ = '';
+ this.before_ = '';
+ this.after_ = '';
this.lineNumbers_ = '';
return;
}
@@ -91,15 +100,19 @@ cr.define('extensions', function() {
if (visibleAfter.charAt(visibleAfter.length - 1) == '\n')
visibleAfter += ' ';
- this.codeText_ = visibleBefore + highlight + visibleAfter;
+ this.highlighted_ = highlight;
+ this.highlightDescription_ = this.getAccessibilityHighlightDescription_(
+ linesBefore.length, highlight.split('\n').length);
+ this.before_ = visibleBefore;
+ this.after_ = visibleAfter;
this.truncatedBefore_ = linesBefore.length - visibleLineCountBefore;
this.truncatedAfter_ = linesAfter.length - visibleLineCountAfter;
+ let visibleCode = visibleBefore + highlight + visibleAfter;
+
this.setLineNumbers_(
this.truncatedBefore_ + 1,
- this.truncatedBefore_ + this.codeText_.split('\n').length);
- this.createHighlight_(
- visibleBefore.length, visibleBefore.length + highlight.length);
+ this.truncatedBefore_ + visibleCode.split('\n').length);
this.scrollToHighlight_(visibleLineCountBefore);
},
@@ -130,23 +143,6 @@ cr.define('extensions', function() {
},
/**
- * Uses the native text-selection API to highlight desired code.
- * @param {number} start
- * @param {number} end
- * @private
- */
- createHighlight_: function(start, end) {
- const range = document.createRange();
- const node = this.$.source.querySelector('span').firstChild;
- range.setStart(node, start);
- range.setEnd(node, end);
-
- const selection = window.getSelection();
- selection.removeAllRanges();
- selection.addRange(range);
- },
-
- /**
* @param {number} linesBeforeHighlight
* @private
*/
@@ -161,6 +157,22 @@ cr.define('extensions', function() {
this.$['scroll-container'].scrollTo({top: targetTop});
},
+
+ /**
+ * @param {number} lineStart
+ * @param {number} numLines
+ * @return {string}
+ * @private
+ */
+ getAccessibilityHighlightDescription_: function(lineStart, numLines) {
+ if (numLines > 1) {
+ return this.i18n(
+ 'accessibilityErrorMultiLine', lineStart.toString(),
+ (lineStart + numLines - 1).toString());
+ } else {
+ return this.i18n('accessibilityErrorLine', lineStart.toString());
+ }
+ },
});
return {CodeSection: CodeSection};
diff --git a/chromium/chrome/browser/resources/md_extensions/compiled_resources2.gyp b/chromium/chrome/browser/resources/md_extensions/compiled_resources2.gyp
index 74850796458..c4c7924b659 100644
--- a/chromium/chrome/browser/resources/md_extensions/compiled_resources2.gyp
+++ b/chromium/chrome/browser/resources/md_extensions/compiled_resources2.gyp
@@ -7,6 +7,7 @@
'target_name': 'code_section',
'dependencies': [
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
'<(EXTERNS_GYP):developer_private',
],
@@ -50,7 +51,6 @@
{
'target_name': 'error_page',
'dependencies': [
- '<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-menu/compiled_resources2.gyp:paper-menu-extracted',
'<(DEPTH)/ui/webui/resources/cr_elements/compiled_resources2.gyp:cr_container_shadow_behavior',
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
'<(DEPTH)/ui/webui/resources/js/cr/ui/compiled_resources2.gyp:focus_outline_manager',
diff --git a/chromium/chrome/browser/resources/md_extensions/detail_view.html b/chromium/chrome/browser/resources/md_extensions/detail_view.html
index a111266af18..f730ba9363d 100644
--- a/chromium/chrome/browser/resources/md_extensions/detail_view.html
+++ b/chromium/chrome/browser/resources/md_extensions/detail_view.html
@@ -55,7 +55,7 @@
}
#main {
- @apply(--shadow-elevation-2dp);
+ @apply --shadow-elevation-2dp;
background-color: white;
margin: auto;
min-height: 100%;
@@ -80,7 +80,7 @@
#name {
flex-grow: 1;
- @apply(--cr-title-text);
+ @apply --cr-title-text;
}
#learn-more-link {
@@ -93,7 +93,7 @@
}
.section {
- @apply(--cr-section);
+ @apply --cr-section;
}
.section.block {
@@ -144,6 +144,10 @@
width: 78px;
}
+ #reload-button {
+ color: var(--google-blue-500);
+ }
+
.warning div {
display: flex;
}
@@ -155,9 +159,20 @@
.warning-icon {
--iron-icon-fill-color: var(--paper-red-700);
- --iron-icon-height: 19px;
- --iron-icon-width: 19px;
- margin-right: 16px;
+ -webkit-margin-end: 16px;
+ height: 19px;
+ width: 19px;
+ }
+
+ #error-icon {
+ --iron-icon-fill-color: var(--google-red-700);
+ -webkit-margin-end: 4px;
+ height: 18px;
+ width: 18px;
+ }
+
+ #runtime-warnings {
+ color: var(--google-red-700);
}
ul {
@@ -187,7 +202,7 @@
}
paper-spinner-lite {
- @apply(--cr-icon-height-width);
+ @apply --cr-icon-height-width;
}
</style>
<div id="container">
@@ -195,7 +210,7 @@
<div id="top-bar">
<button id="close-button" is="paper-icon-button-light"
aria-label="$i18n{back}" class="icon-arrow-back no-overlap"
- on-tap="onCloseButtonTap_"></button>
+ on-click="onCloseButtonTap_"></button>
<img id="icon" src="[[data.iconUrl]]"
alt$="[[appOrExtension(
data.type,
@@ -227,6 +242,19 @@
</div>
</div>
<div id="warnings" hidden$="[[!hasWarnings_(data.*)]]">
+ <div id="runtime-warnings" aria-describedby="a11yAssociation"
+ hidden$="[[!data.runtimeWarnings.length]]"
+ class="section continuation control-line">
+ <div>
+ <iron-icon id="error-icon" icon="error"></iron-icon>
+ <template is="dom-repeat" items="[[data.runtimeWarnings]]">
+ [[item]]
+ </template>
+ </div>
+ <paper-button id="reload-button" on-click="onReloadTap_">
+ $i18n{itemReload}
+ </paper-button>
+ </div>
<div class="section continuation warning" id="suspicious-warning"
hidden$="[[!data.disableReasons.suspiciousInstall]]">
<div>
@@ -247,7 +275,7 @@
<span>$i18n{itemCorruptInstall}</span>
</div>
<paper-button id="repair-button" class="action-button"
- on-tap="onRepairTap_">
+ on-click="onRepairTap_">
$i18n{itemRepair}
</paper-button>
</div>
@@ -299,7 +327,7 @@
<template is="dom-repeat" items="[[data.views]]">
<li>
<a is="action-link" class="inspectable-view"
- on-tap="onInspectTap_">
+ on-click="onInspectTap_">
[[computeInspectLabel_(item)]]
</a>
</li>
@@ -382,15 +410,15 @@
disabled="[[!isEnabled_(data.state)]]"
hidden="[[!shouldShowOptionsLink_(data.*)]]"
icon-class="icon-external" label="$i18n{itemOptions}"
- on-tap="onExtensionOptionsTap_">
+ on-click="onExtensionOptionsTap_">
</button>
<button class="hr" hidden="[[!data.manifestHomePageUrl.length]]"
is="cr-link-row" icon-class="icon-external" id="extensionWebsite"
- label="$i18n{extensionWebsite}" on-tap="onExtensionWebSiteTap_">
+ label="$i18n{extensionWebsite}" on-click="onExtensionWebSiteTap_">
</button>
<button class="hr" hidden="[[!data.webStoreUrl.length]]"
is="cr-link-row" icon-class="icon-external" id="viewInStore"
- label="$i18n{viewInStore}" on-tap="onViewInStoreTap_">
+ label="$i18n{viewInStore}" on-click="onViewInStoreTap_">
</button>
<div class="section block">
<div class="section-title">$i18n{itemSource}</div>
@@ -400,7 +428,7 @@
<div id="load-path" class="section-content"
hidden$="[[!data.prettifiedPath]]">
<span>$i18n{itemExtensionPath}</span>
- <a is="action-link" on-tap="onLoadPathTap_">
+ <a is="action-link" on-click="onLoadPathTap_">
[[data.prettifiedPath]]
</a>
</div>
@@ -408,7 +436,7 @@
<button class="hr" is="cr-link-row"
hidden="[[isControlled_(data.controlledInfo)]]"
icon-class="subpage-arrow" id="remove-extension"
- label="$i18n{itemRemoveExtension}" on-tap="onRemoveTap_">
+ label="$i18n{itemRemoveExtension}" on-click="onRemoveTap_">
</button>
</div>
</div>
diff --git a/chromium/chrome/browser/resources/md_extensions/detail_view.js b/chromium/chrome/browser/resources/md_extensions/detail_view.js
index 6115bc07b77..364eb45cf4a 100644
--- a/chromium/chrome/browser/resources/md_extensions/detail_view.js
+++ b/chromium/chrome/browser/resources/md_extensions/detail_view.js
@@ -101,7 +101,8 @@ cr.define('extensions', function() {
hasWarnings_: function() {
return this.data.disableReasons.corruptInstall ||
this.data.disableReasons.suspiciousInstall ||
- this.data.disableReasons.updateRequired || !!this.data.blacklistText;
+ this.data.disableReasons.updateRequired ||
+ !!this.data.blacklistText || this.data.runtimeWarnings.length > 0;
},
/**
@@ -179,6 +180,13 @@ cr.define('extensions', function() {
},
/** @private */
+ onReloadTap_: function() {
+ this.delegate.reloadItem(this.data.id).catch(loadError => {
+ this.fire('load-error', loadError);
+ });
+ },
+
+ /** @private */
onRemoveTap_: function() {
this.delegate.deleteItem(this.data.id);
},
diff --git a/chromium/chrome/browser/resources/md_extensions/error_page.html b/chromium/chrome/browser/resources/md_extensions/error_page.html
index 2cae6446d16..e872d27f2a3 100644
--- a/chromium/chrome/browser/resources/md_extensions/error_page.html
+++ b/chromium/chrome/browser/resources/md_extensions/error_page.html
@@ -31,7 +31,7 @@
iron-icon {
--iron-icon-fill-color: var(--paper-grey-500);
- @apply(--cr-icon-height-width);
+ @apply --cr-icon-height-width;
flex-shrink: 0;
}
@@ -47,7 +47,7 @@
* detail_view.html and error_page.html. Refactor such that no duplication
* happens.*/
#main {
- @apply(--shadow-elevation-2dp);
+ @apply --shadow-elevation-2dp;
background-color: white;
margin: auto;
min-height: 100%;
@@ -64,7 +64,7 @@
height: 40px;
margin-bottom: 30px;
padding: 8px 12px 0;
- @apply(--cr-title-text);
+ @apply --cr-title-text;
}
#heading span {
@@ -77,7 +77,7 @@
}
.error-item {
- @apply(--cr-section);
+ @apply --cr-section;
padding-left: 0;
}
@@ -108,7 +108,7 @@
}
.details-heading {
- @apply(--cr-title-text);
+ @apply --cr-title-text;
align-items: center;
display: flex;
height: var(--cr-section-min-height);
@@ -177,10 +177,10 @@
<div id="heading">
<button id="close-button" is="paper-icon-button-light"
aria-label="$i18n{back}"
- class="icon-arrow-back no-overlap" on-tap="onCloseButtonTap_">
+ class="icon-arrow-back no-overlap" on-click="onCloseButtonTap_">
</button>
<span>$i18n{errorsPageHeading}</span>
- <paper-button on-tap="onClearAllTap_" hidden="[[!entries_.length]]">
+ <paper-button on-click="onClearAllTap_" hidden="[[!entries_.length]]">
$i18n{clearAll}
</paper-button>
</div>
@@ -190,7 +190,7 @@
<div class="item-container">
<div class$="error-item
[[computeErrorClass_(item, selectedEntry_)]]">
- <div actionable class=" start" on-tap="onErrorItemAction_"
+ <div actionable class=" start" on-click="onErrorItemAction_"
on-keydown="onErrorItemAction_" tabindex="0"
role="button">
<iron-icon icon$="[[computeErrorIcon_(item)]]"
@@ -204,7 +204,7 @@
</div>
<div class="separator"></div>
<button is="paper-icon-button-light" class="icon-delete-gray"
- on-tap="onDeleteErrorAction_"
+ on-click="onDeleteErrorAction_"
aria-describedby$="[[item.id]]"
aria-label="$i18n{clearEntry}"
on-keydown="onDeleteErrorAction_">
@@ -226,7 +226,7 @@
</div>
<ul class="stack-trace-container">
<template is="dom-repeat" items="[[item.stackTrace]]">
- <li on-tap="onStackFrameTap_"
+ <li on-click="onStackFrameTap_"
hidden="[[!shouldDisplayFrame_(item.url)]]"
class$="[[getStackFrameClass_(item,
selectedStackFrame_)]]">
@@ -244,7 +244,7 @@
<paper-button class="devtool-button action-button"
hidden$="[[!computeIsRuntimeError_(item)]]"
disabled="[[!item.canInspect]]"
- on-tap="onDevToolButtonTap_">
+ on-click="onDevToolButtonTap_">
$i18n{openInDevtool}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/md_extensions/extensions.html b/chromium/chrome/browser/resources/md_extensions/extensions.html
index 5b728b9ca9d..5b851ed3990 100644
--- a/chromium/chrome/browser/resources/md_extensions/extensions.html
+++ b/chromium/chrome/browser/resources/md_extensions/extensions.html
@@ -12,6 +12,9 @@
/* --md-background-color in disguise. Not using the var for increased
* performance. */
background-color: #f1f1f1;
+
+ /* Remove 300ms delay for 'click' event, when using touch interface. */
+ touch-action: manipulation;
}
.loading {
diff --git a/chromium/chrome/browser/resources/md_extensions/icons.html b/chromium/chrome/browser/resources/md_extensions/icons.html
index 34714cc4e7b..f05904be4f3 100644
--- a/chromium/chrome/browser/resources/md_extensions/icons.html
+++ b/chromium/chrome/browser/resources/md_extensions/icons.html
@@ -57,4 +57,4 @@
</g>
</defs>
</svg>
-</iron-icon-set>
+</iron-iconset-svg>
diff --git a/chromium/chrome/browser/resources/md_extensions/install_warnings_dialog.html b/chromium/chrome/browser/resources/md_extensions/install_warnings_dialog.html
index d50ce39ff17..286b25e54d6 100644
--- a/chromium/chrome/browser/resources/md_extensions/install_warnings_dialog.html
+++ b/chromium/chrome/browser/resources/md_extensions/install_warnings_dialog.html
@@ -30,7 +30,7 @@
</ul>
</div>
<div slot="button-container">
- <paper-button class="action-button" on-tap="onOkTap_">
+ <paper-button class="action-button" on-click="onOkTap_">
$i18n{ok}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/md_extensions/item.html b/chromium/chrome/browser/resources/md_extensions/item.html
index f9f2d770906..f2fe3442f46 100644
--- a/chromium/chrome/browser/resources/md_extensions/item.html
+++ b/chromium/chrome/browser/resources/md_extensions/item.html
@@ -60,7 +60,7 @@
}
#card {
- @apply(--shadow-elevation-2dp);
+ @apply --shadow-elevation-2dp;
background: white;
border-radius: 2px;
display: flex;
@@ -90,7 +90,7 @@
}
#name-and-version {
- @apply(--cr-primary-text);
+ @apply --cr-primary-text;
margin-bottom: 4px;
}
@@ -108,6 +108,13 @@
margin-bottom: 8px;
}
+ #error-icon {
+ --iron-icon-fill-color: var(--google-red-700);
+ -webkit-margin-end: 4px;
+ height: 18px;
+ width: 18px;
+ }
+
#description,
#version,
#extension-id,
@@ -177,7 +184,7 @@
paper-tooltip {
--paper-tooltip: {
- @apply(--cr-tooltip);
+ @apply --cr-tooltip;
min-width: 0;
};
}
@@ -255,28 +262,29 @@
[[data.description]]
</div>
<template is="dom-if" if="[[hasWarnings_(data.*)]]">
- <div id="warnings" >
- <div id="runtime-warnings" aria-describedby="a11yAssociation"
+ <div id="warnings">
+ <iron-icon id="error-icon" icon="error"></iron-icon>
+ <span id="runtime-warnings" aria-describedby="a11yAssociation"
hidden$="[[!data.runtimeWarnings.length]]">
<template is="dom-repeat" items="[[data.runtimeWarnings]]">
[[item]]
</template>
- </div>
- <div id="suspicious-warning" aria-describedby="a11yAssociation"
+ </span>
+ <span id="suspicious-warning" aria-describedby="a11yAssociation"
hidden$="[[!data.disableReasons.suspiciousInstall]]">
$i18n{itemSuspiciousInstall}
<a target="_blank" id="learn-more-link"
href="$i18n{suspiciousInstallHelpUrl}">
$i18n{learnMore}
</a>
- </div>
- <div id="corrupted-warning" aria-describedby="a11yAssociation"
+ </span>
+ <span id="corrupted-warning" aria-describedby="a11yAssociation"
hidden$="[[!data.disableReasons.corruptInstall]]">
$i18n{itemCorruptInstall}
- </div>
- <div id="blacklisted-warning"><!-- No whitespace
+ </span>
+ <span id="blacklisted-warning"><!-- No whitespace
-->[[data.blacklistText]]<!-- so we can use :empty in css.
- --></div>
+ --></span>
</div>
</template>
<template is="dom-if" if="[[inDevMode]]">
@@ -292,12 +300,12 @@
</span>
<a class="clippable-flex-text" is="action-link"
title="[[computeFirstInspectTitle_(data.views)]]"
- on-tap="onInspectTap_">
+ on-click="onInspectTap_">
[[computeFirstInspectLabel_(data.views)]]
</a>
<a is="action-link"
hidden$="[[computeExtraViewsHidden_(data.views)]]"
- on-tap="onExtraInspectTap_">
+ on-click="onExtraInspectTap_">
[[computeExtraInspectLabel_(data.views)]]
</a>
</div>
@@ -308,17 +316,17 @@
</div>
<div id="button-strip" class="layout horizontal center">
<div class="layout flex horizontal center">
- <paper-button id="details-button" on-tap="onDetailsTap_"
+ <paper-button id="details-button" on-click="onDetailsTap_"
aria-describedby="a11yAssociation">
$i18n{itemDetails}
</paper-button>
- <paper-button id="remove-button" on-tap="onRemoveTap_"
+ <paper-button id="remove-button" on-click="onRemoveTap_"
aria-describedby="a11yAssociation"
hidden="[[isControlled_(data.controlledInfo)]]">
$i18n{itemRemove}
</paper-button>
<template is="dom-if" if="[[shouldShowErrorsButton_(data.*)]]">
- <paper-button id="errors-button" on-tap="onErrorsTap_"
+ <paper-button id="errors-button" on-click="onErrorsTap_"
aria-describedby="a11yAssociation">
$i18n{itemErrors}
</paper-button>
@@ -327,22 +335,22 @@
<template is="dom-if" if="[[!computeDevReloadButtonHidden_(data.*)]]">
<button id="dev-reload-button" is="paper-icon-button-light"
aria-label="$i18n{itemReload}" aria-describedby="a11yAssociation"
- class="icon-refresh no-overlap" on-tap="onReloadTap_">
+ class="icon-refresh no-overlap" on-click="onReloadTap_">
</button>
</template>
<template is="dom-if" if="[[data.disableReasons.corruptInstall]]">
<paper-button id="repair-button" class="action-button"
- aria-describedby="a11yAssociation" on-tap="onRepairTap_">
+ aria-describedby="a11yAssociation" on-click="onRepairTap_">
$i18n{itemRepair}
</paper-button>
</template>
<template is="dom-if" if="[[isTerminated_(data.state)]]">
- <paper-button id="terminated-reload-button" on-tap="onReloadTap_"
+ <paper-button id="terminated-reload-button" on-click="onReloadTap_"
aria-describedby="a11yAssociation" class="action-button">
$i18n{itemReload}
</paper-button>
</template>
- <cr-toggle id="enable-toggle" class="action-button"
+ <cr-toggle id="enable-toggle"
aria-label$="[[appOrExtension(
data.type,
'$i18nPolymer{appEnabled}',
diff --git a/chromium/chrome/browser/resources/md_extensions/item.js b/chromium/chrome/browser/resources/md_extensions/item.js
index 116f96d50ea..bdf69ab5bf5 100644
--- a/chromium/chrome/browser/resources/md_extensions/item.js
+++ b/chromium/chrome/browser/resources/md_extensions/item.js
@@ -109,7 +109,12 @@ cr.define('extensions', function() {
/** @private string */
a11yAssociation_: function() {
- return this.i18n('extensionA11yAssociation', this.data.name);
+ // Don't use I18nBehavior.i18n because of additional checks it performs.
+ // Polymer ensures that this string is not stamped into arbitrary HTML.
+ // |this.data.name| can contain any data including html tags.
+ // ex: "My <video> download extension!"
+ return loadTimeData.getStringF(
+ 'extensionA11yAssociation', this.data.name);
},
/** @private */
diff --git a/chromium/chrome/browser/resources/md_extensions/item_list.html b/chromium/chrome/browser/resources/md_extensions/item_list.html
index 2ac11babaa0..849590719b1 100644
--- a/chromium/chrome/browser/resources/md_extensions/item_list.html
+++ b/chromium/chrome/browser/resources/md_extensions/item_list.html
@@ -49,7 +49,7 @@
}
#app-title {
- @apply(--cr-section-text);
+ @apply --cr-section-text;
margin-bottom: 12px;
margin-top: 21px;
}
@@ -61,7 +61,7 @@
<div id="no-items" class="empty-list-message"
hidden$="[[!shouldShowEmptyItemsMessage_(
apps.length, extensions.length)]]">
- <span on-tap="onNoExtensionsTap_">$i18nRaw{noExtensionsOrApps}</span>
+ <span on-click="onNoExtensionsTap_">$i18nRaw{noExtensionsOrApps}</span>
</div>
<div id="no-search-results" class="empty-list-message"
hidden$="[[!shouldShowEmptySearchMessage_(
diff --git a/chromium/chrome/browser/resources/md_extensions/item_util.js b/chromium/chrome/browser/resources/md_extensions/item_util.js
index aaefd16eb47..5f4aac7e18c 100644
--- a/chromium/chrome/browser/resources/md_extensions/item_util.js
+++ b/chromium/chrome/browser/resources/md_extensions/item_util.js
@@ -24,6 +24,7 @@ cr.define('extensions', function() {
case chrome.developerPrivate.ExtensionState.ENABLED:
case chrome.developerPrivate.ExtensionState.TERMINATED:
return true;
+ case chrome.developerPrivate.ExtensionState.BLACKLISTED:
case chrome.developerPrivate.ExtensionState.DISABLED:
return false;
}
diff --git a/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.html b/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.html
index cb443c889fc..5bdefc60481 100644
--- a/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.html
+++ b/chromium/chrome/browser/resources/md_extensions/keyboard_shortcuts.html
@@ -74,15 +74,15 @@
.icon {
-webkit-margin-end: 20px;
- height: 16px;
- width: 16px;
+ height: 20px;
+ width: 20px;
}
.card-controls {
/* We line up the controls with the name, which is after the
- 20px left padding + 16px icon + 20px margin on the icon. */
+ 20px left padding + 20px icon + 20px margin on the icon. */
-webkit-margin-end: 20px;
- -webkit-margin-start: 56px;
+ -webkit-margin-start: 60px;
}
</style>
<div id="container">
diff --git a/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.html b/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.html
index 2709a38694b..fa86c94ec05 100644
--- a/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.html
+++ b/chromium/chrome/browser/resources/md_extensions/kiosk_dialog.html
@@ -95,13 +95,13 @@
</div>
<div class="item-controls">
<paper-button hidden="[[!canEditAutoLaunch_]]"
- on-tap="onAutoLaunchButtonTap_">
+ on-click="onAutoLaunchButtonTap_">
[[getAutoLaunchButtonLabel_(item.autoLaunch,
'$i18nPolymer{kioskDisableAutoLaunch}',
'$i18nPolymer{kioskEnableAutoLaunch}')]]
</paper-button>
<button is="paper-icon-button-light" class="icon-delete-gray"
- on-tap="onDeleteAppTap_"></button>
+ on-click="onDeleteAppTap_"></button>
</div>
</div>
</template>
@@ -114,7 +114,7 @@
'$i18nPolymer{kioskInvalidApp}', errorAppId_)]]"
on-keydown="clearInputInvalid_">
</paper-input>
- <paper-button id="add-button" on-tap="onAddAppTap_"
+ <paper-button id="add-button" on-click="onAddAppTap_"
disabled="[[!addAppInput_]]">
$i18n{add}
</paper-button>
@@ -126,7 +126,7 @@
</paper-checkbox>
</div>
<div slot="button-container">
- <paper-button class="action-button" on-tap="onDoneTap_">
+ <paper-button class="action-button" on-click="onDoneTap_">
$i18n{done}
</paper-button>
</div>
@@ -136,10 +136,12 @@
<div slot="title">$i18n{kioskDisableBailoutWarningTitle}</div>
<div slot="body">$i18n{kioskDisableBailoutWarningBody}</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onBailoutDialogCancelTap_">
+ <paper-button class="cancel-button"
+ on-click="onBailoutDialogCancelTap_">
$i18n{cancel}
</paper-button>
- <paper-button class="action-button" on-tap="onBailoutDialogConfirmTap_">
+ <paper-button class="action-button"
+ on-click="onBailoutDialogConfirmTap_">
$i18n{confirm}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/md_extensions/load_error.html b/chromium/chrome/browser/resources/md_extensions/load_error.html
index e5de72a0497..10080e154d5 100644
--- a/chromium/chrome/browser/resources/md_extensions/load_error.html
+++ b/chromium/chrome/browser/resources/md_extensions/load_error.html
@@ -40,11 +40,11 @@
</div>
<div slot="button-container">
<paper-spinner-lite active="[[retrying_]]"></paper-spinner-lite>
- <paper-button class="cancel-button" on-tap="close">
+ <paper-button class="cancel-button" on-click="close">
$i18n{cancel}
</paper-button>
<paper-button class="action-button" disabled="[[retrying_]]"
- on-tap="onRetryTap_">
+ on-click="onRetryTap_">
$i18n{loadErrorRetry}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/md_extensions/options_dialog.html b/chromium/chrome/browser/resources/md_extensions/options_dialog.html
index 3c959b13644..5c5abc51db6 100644
--- a/chromium/chrome/browser/resources/md_extensions/options_dialog.html
+++ b/chromium/chrome/browser/resources/md_extensions/options_dialog.html
@@ -23,12 +23,13 @@
ExtensionOptions {
display: block;
min-width: 300px;
+ overflow: hidden;
}
dialog {
+ --scroll-border: 0;
width: fit-content;
--cr-dialog-body: {
- overflow: hidden;
padding: 0;
};
diff --git a/chromium/chrome/browser/resources/md_extensions/options_dialog.js b/chromium/chrome/browser/resources/md_extensions/options_dialog.js
index adffadd9da2..d9ecf142ee3 100644
--- a/chromium/chrome/browser/resources/md_extensions/options_dialog.js
+++ b/chromium/chrome/browser/resources/md_extensions/options_dialog.js
@@ -5,6 +5,25 @@
cr.define('extensions', function() {
'use strict';
+ /**
+ * @return {!Promise} A signal that the document is ready. Need to wait for
+ * this, otherwise the custom ExtensionOptions element might not have been
+ * registered yet.
+ */
+ function whenDocumentReady() {
+ if (document.readyState == 'complete')
+ return Promise.resolve();
+
+ return new Promise(function(resolve) {
+ document.addEventListener('readystatechange', function f() {
+ if (document.readyState == 'complete') {
+ document.removeEventListener('readystatechange', f);
+ resolve();
+ }
+ });
+ });
+ }
+
const OptionsDialog = Polymer({
is: 'extensions-options-dialog',
@@ -25,21 +44,23 @@ cr.define('extensions', function() {
/** @param {chrome.developerPrivate.ExtensionInfo} data */
show: function(data) {
this.data_ = data;
- if (!this.extensionOptions_)
- this.extensionOptions_ = document.createElement('ExtensionOptions');
- this.extensionOptions_.extension = this.data_.id;
- this.extensionOptions_.onclose = this.close.bind(this);
+ whenDocumentReady().then(() => {
+ if (!this.extensionOptions_)
+ this.extensionOptions_ = document.createElement('ExtensionOptions');
+ this.extensionOptions_.extension = this.data_.id;
+ this.extensionOptions_.onclose = this.close.bind(this);
- const onSizeChanged = e => {
- this.extensionOptions_.style.height = e.height + 'px';
- this.extensionOptions_.style.width = e.width + 'px';
+ const onSizeChanged = e => {
+ this.extensionOptions_.style.height = e.height + 'px';
+ this.extensionOptions_.style.width = e.width + 'px';
- if (!this.$$('dialog').open)
- this.$$('dialog').showModal();
- };
+ if (!this.$$('dialog').open)
+ this.$$('dialog').showModal();
+ };
- this.extensionOptions_.onpreferredsizechanged = onSizeChanged;
- this.$.body.appendChild(this.extensionOptions_);
+ this.extensionOptions_.onpreferredsizechanged = onSizeChanged;
+ this.$.body.appendChild(this.extensionOptions_);
+ });
},
close: function() {
diff --git a/chromium/chrome/browser/resources/md_extensions/pack_dialog.html b/chromium/chrome/browser/resources/md_extensions/pack_dialog.html
index 1ee1ae2c232..e984b85658b 100644
--- a/chromium/chrome/browser/resources/md_extensions/pack_dialog.html
+++ b/chromium/chrome/browser/resources/md_extensions/pack_dialog.html
@@ -36,7 +36,7 @@
<paper-input id="root-dir" label="$i18n{packDialogExtensionRoot}"
always-float-label value="{{packDirectory_}}">
</paper-input>
- <paper-button id="root-dir-browse" on-tap="onRootBrowse_">
+ <paper-button id="root-dir-browse" on-click="onRootBrowse_">
$i18n{packDialogBrowse}
</paper-button>
</div>
@@ -44,16 +44,16 @@
<paper-input id="key-file" label="$i18n{packDialogKeyFile}"
always-float-label value="{{keyFile_}}">
</paper-input>
- <paper-button id="key-file-browse" on-tap="onKeyBrowse_">
+ <paper-button id="key-file-browse" on-click="onKeyBrowse_">
$i18n{packDialogBrowse}
</paper-button>
</div>
</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onCancelTap_">
+ <paper-button class="cancel-button" on-click="onCancelTap_">
$i18n{cancel}
</paper-button>
- <paper-button class="action-button" on-tap="onConfirmTap_"
+ <paper-button class="action-button" on-click="onConfirmTap_"
disabled="[[!packDirectory_]]">
$i18n{packDialogConfirm}
</paper-button>
diff --git a/chromium/chrome/browser/resources/md_extensions/pack_dialog_alert.html b/chromium/chrome/browser/resources/md_extensions/pack_dialog_alert.html
index 68bc231c581..ebf69a90b1b 100644
--- a/chromium/chrome/browser/resources/md_extensions/pack_dialog_alert.html
+++ b/chromium/chrome/browser/resources/md_extensions/pack_dialog_alert.html
@@ -22,10 +22,10 @@
<div class="body" slot="body">[[model.message]]</div>
<div class="button-container" slot="button-container">
<paper-button class$="[[getCancelButtonClass_(confirmLabel_)]]"
- on-tap="onCancelTap_" hidden="[[!cancelLabel_]]">
+ on-click="onCancelTap_" hidden="[[!cancelLabel_]]">
[[cancelLabel_]]
</paper-button>
- <paper-button class="action-button" on-tap="onConfirmTap_"
+ <paper-button class="action-button" on-click="onConfirmTap_"
hidden="[[!confirmLabel_]]">
[[confirmLabel_]]
</paper-button>
diff --git a/chromium/chrome/browser/resources/md_extensions/shortcut_input.html b/chromium/chrome/browser/resources/md_extensions/shortcut_input.html
index ad5c9bb03b9..94b9d0d31f0 100644
--- a/chromium/chrome/browser/resources/md_extensions/shortcut_input.html
+++ b/chromium/chrome/browser/resources/md_extensions/shortcut_input.html
@@ -46,7 +46,7 @@
no-label-float>
</paper-input>
<button id="clear" is="paper-icon-button-light"
- class="icon-clear no-overlap" on-tap="onClearTap_"
+ class="icon-clear no-overlap" on-click="onClearTap_"
hidden$="[[computeClearHidden_(capturing_, shortcut)]]">
</button>
</div>
diff --git a/chromium/chrome/browser/resources/md_extensions/sidebar.html b/chromium/chrome/browser/resources/md_extensions/sidebar.html
index a8857075a54..5e89ab45503 100644
--- a/chromium/chrome/browser/resources/md_extensions/sidebar.html
+++ b/chromium/chrome/browser/resources/md_extensions/sidebar.html
@@ -62,12 +62,12 @@
<iron-selector id="sectionMenu">
<!-- Values for "data-path" attribute must match the "Page" enum. -->
<a class="section-item" id="sections-extensions" href="/"
- on-tap="onLinkTap_" data-path="items-list">
+ on-click="onLinkTap_" data-path="items-list">
$i18n{sidebarExtensions}
<paper-ripple></paper-ripple>
</a>
<a class="section-item" id="sections-shortcuts" href="/shortcuts"
- on-tap="onLinkTap_" data-path="keyboard-shortcuts">
+ on-click="onLinkTap_" data-path="keyboard-shortcuts">
$i18n{keyboardShortcuts}
<paper-ripple></paper-ripple>
</a>
@@ -75,7 +75,7 @@
<div hidden="[[isSupervised]]">
<div class="separator"></div>
<a class="section-item" id="more-extensions" target="_blank"
- href="$i18n{getMoreExtensionsUrl}" on-tap="onMoreExtensionsTap_">
+ href="$i18n{getMoreExtensionsUrl}" on-click="onMoreExtensionsTap_">
<span>$i18n{openChromeWebStore}</span>
<div class="cr-icon icon-external"></div>
<paper-ripple></paper-ripple>
diff --git a/chromium/chrome/browser/resources/md_extensions/toolbar.html b/chromium/chrome/browser/resources/md_extensions/toolbar.html
index b2d3b2f9af8..8e554185091 100644
--- a/chromium/chrome/browser/resources/md_extensions/toolbar.html
+++ b/chromium/chrome/browser/resources/md_extensions/toolbar.html
@@ -1,5 +1,6 @@
<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_toast/cr_toast.html">
<link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
<link rel="import" href="chrome://resources/cr_elements/cr_toolbar/cr_toolbar.html">
<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
@@ -40,7 +41,7 @@
-webkit-margin-end: 20px;
}
- #devDrawer[expanded] #button-strip {
+ #devDrawer[expanded] #buttonStrip {
top: 0;
}
@@ -55,7 +56,7 @@
height: var(--button-row-height);
}
- #button-strip {
+ #buttonStrip {
-webkit-margin-end: auto;
-webkit-margin-start: auto;
background: var(--toolbar-color);
@@ -69,7 +70,7 @@
width: 100%;
}
- #button-strip paper-button {
+ #buttonStrip paper-button {
-webkit-margin-end: 16px;
color: white;
/* Increase contrast compared to default values. */
@@ -87,6 +88,15 @@
.more-actions span {
-webkit-margin-end: 16px;
}
+
+ cr-toast > div {
+ color: #fff;
+ display: flex;
+ flex: 1;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
</style>
<cr-toolbar page-name="$i18n{toolbarTitle}"
search-prompt="$i18n{search}"
@@ -101,7 +111,7 @@
icon-class="cr20:domain"
icon-aria-label="$i18n{controlledSettingPolicy}">
</cr-tooltip-icon>
- <cr-toggle id="dev-mode" on-change="onDevModeToggleChange_"
+ <cr-toggle id="devMode" on-change="onDevModeToggleChange_"
disabled="[[shouldDisableDevMode_(
devModeControlledByPolicy, isSupervised)]]"
checked="[[inDevMode]]" aria-labelledby="devModeLabel">
@@ -109,20 +119,23 @@
</div>
</cr-toolbar>
<div id="devDrawer" expanded$="[[expanded_]]">
- <div id="button-strip">
- <paper-button hidden$="[[!canLoadUnpacked]]" id="load-unpacked"
- on-tap="onLoadUnpackedTap_">
+ <div id="buttonStrip">
+ <paper-button hidden$="[[!canLoadUnpacked]]" id="loadUnpacked"
+ on-click="onLoadUnpackedTap_">
$i18n{toolbarLoadUnpacked}
</paper-button>
- <paper-button id="pack-extensions" on-tap="onPackTap_">
+ <paper-button id="packExtensions" on-click="onPackTap_">
$i18n{toolbarPack}
</paper-button>
- <paper-button id="update-now" on-tap="onUpdateNowTap_"
+ <paper-button id="updateNow" on-click="onUpdateNowTap_"
title="$i18n{toolbarUpdateNowTooltip}">
$i18n{toolbarUpdateNow}
</paper-button>
+ <cr-toast duration="3000">
+ <div>[[toastLabel_]]</div>
+ </cr-toast>
<if expr="chromeos">
- <paper-button id="kiosk-extensions" on-tap="onKioskTap_"
+ <paper-button id="kioskExtensions" on-click="onKioskTap_"
hidden$="[[!kioskEnabled]]">
$i18n{manageKioskApp}
</paper-button>
diff --git a/chromium/chrome/browser/resources/md_extensions/toolbar.js b/chromium/chrome/browser/resources/md_extensions/toolbar.js
index 3205f8f4f21..b5d3a5cfd37 100644
--- a/chromium/chrome/browser/resources/md_extensions/toolbar.js
+++ b/chromium/chrome/browser/resources/md_extensions/toolbar.js
@@ -53,6 +53,12 @@ cr.define('extensions', function() {
/** @private */
expanded_: Boolean,
+
+ /**
+ * Text to display in update toast
+ * @private
+ */
+ toastLabel_: String,
},
behaviors: [I18nBehavior],
@@ -130,12 +136,24 @@ cr.define('extensions', function() {
/** @private */
onUpdateNowTap_: function() {
- this.delegate.updateAllExtensions().then(() => {
- Polymer.IronA11yAnnouncer.requestAvailability();
- this.fire('iron-announce', {
- text: this.i18n('toolbarUpdateDone'),
- });
- });
+ const updateButton = this.$.updateNow;
+ assert(!updateButton.disabled);
+ updateButton.disabled = true;
+ const toastElement = this.$$('cr-toast');
+ this.toastLabel_ = this.i18n('toolbarUpdatingToast');
+ toastElement.show();
+ this.delegate.updateAllExtensions()
+ .then(() => {
+ Polymer.IronA11yAnnouncer.requestAvailability();
+ const doneText = this.i18n('toolbarUpdateDone');
+ this.fire('iron-announce', {text: doneText});
+ this.toastLabel_ = doneText;
+ toastElement.show();
+ updateButton.disabled = false;
+ })
+ .catch(function() {
+ updateButton.disabled = false;
+ });
},
});
diff --git a/chromium/chrome/browser/resources/md_history/app.html b/chromium/chrome/browser/resources/md_history/app.html
index 5afaa5d63a1..a6bfb168ffd 100644
--- a/chromium/chrome/browser/resources/md_history/app.html
+++ b/chromium/chrome/browser/resources/md_history/app.html
@@ -49,7 +49,7 @@
}
#drop-shadow {
- @apply(--cr-container-shadow);
+ @apply --cr-container-shadow;
}
:host([toolbar-shadow_]) #drop-shadow {
diff --git a/chromium/chrome/browser/resources/md_history/app.js b/chromium/chrome/browser/resources/md_history/app.js
index c187aca222c..56f975fb23f 100644
--- a/chromium/chrome/browser/resources/md_history/app.js
+++ b/chromium/chrome/browser/resources/md_history/app.js
@@ -168,6 +168,13 @@ Polymer({
.getSelectedItemCount();
},
+ selectOrUnselectAll: function() {
+ const list = /** @type {HistoryListElement} */ (this.$.history);
+ const toolbar = /** @type {HistoryToolbarElement} */ (this.$.toolbar);
+ list.selectOrUnselectAll();
+ toolbar.count = list.getSelectedItemCount();
+ },
+
/**
* Listens for call to cancel selection and loops through all items to set
* checkbox to be unselected.
@@ -218,6 +225,9 @@ Polymer({
case 'delete-command':
e.canExecute = this.$.toolbar.count > 0;
break;
+ case 'select-all-command':
+ e.canExecute = !this.$.toolbar.searchField.isSearchFocused();
+ break;
}
},
@@ -230,6 +240,8 @@ Polymer({
this.focusToolbarSearchField();
else if (e.command.id == 'delete-command')
this.deleteSelected();
+ else if (e.command.id == 'select-all-command')
+ this.selectOrUnselectAll();
},
/**
diff --git a/chromium/chrome/browser/resources/md_history/history.html b/chromium/chrome/browser/resources/md_history/history.html
index cc221baf1bd..bdce86e6070 100644
--- a/chromium/chrome/browser/resources/md_history/history.html
+++ b/chromium/chrome/browser/resources/md_history/history.html
@@ -8,6 +8,11 @@
<link rel="stylesheet" href="chrome://resources/css/md_colors.css">
<style>
+ html {
+ /* Remove 300ms delay for 'click' event, when using touch interface. */
+ touch-action: manipulation;
+ }
+
html,
body {
height: 100%;
@@ -79,6 +84,12 @@
</if>
<command id="delete-command" shortcut="Delete Backspace">
<command id="slash-command" shortcut="/">
+<if expr="is_macosx">
+ <command id="select-all-command" shortcut="Meta|a">
+</if>
+<if expr="not is_macosx">
+ <command id="select-all-command" shortcut="Ctrl|a">
+</if>
<link rel="import" href="chrome://resources/html/util.html">
<link rel="import" href="chrome://resources/html/load_time_data.html">
diff --git a/chromium/chrome/browser/resources/md_history/history_item.html b/chromium/chrome/browser/resources/md_history/history_item.html
index 958032b7386..221f09b7af3 100644
--- a/chromium/chrome/browser/resources/md_history/history_item.html
+++ b/chromium/chrome/browser/resources/md_history/history_item.html
@@ -169,7 +169,7 @@
}
#background {
- @apply(--shadow-elevation-2dp);
+ @apply --shadow-elevation-2dp;
background: #fff;
bottom: 0;
left: 0;
diff --git a/chromium/chrome/browser/resources/md_history/history_item.js b/chromium/chrome/browser/resources/md_history/history_item.js
index fa0dd0ec630..3329aa189f7 100644
--- a/chromium/chrome/browser/resources/md_history/history_item.js
+++ b/chromium/chrome/browser/resources/md_history/history_item.js
@@ -278,13 +278,13 @@ cr.define('md_history', function() {
item: this.item,
});
- // Stops the 'tap' event from closing the menu when it opens.
+ // Stops the 'click' event from closing the menu when it opens.
e.stopPropagation();
},
/**
- * Record metrics when a result is clicked. This is deliberately tied to
- * on-click rather than on-tap, as on-click triggers from middle clicks.
+ * Record metrics when a result is clicked.
+ * @private
*/
onLinkClick_: function() {
const browserService = md_history.BrowserService.getInstance();
diff --git a/chromium/chrome/browser/resources/md_history/history_list.html b/chromium/chrome/browser/resources/md_history/history_list.html
index b27ce8d74e2..ed46d9fcda9 100644
--- a/chromium/chrome/browser/resources/md_history/history_list.html
+++ b/chromium/chrome/browser/resources/md_history/history_list.html
@@ -1,6 +1,7 @@
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.html">
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-announcer/iron-a11y-announcer.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
@@ -14,7 +15,7 @@
<dom-module id="history-list">
<template>
- <style include="shared-style cr-shared-style">
+ <style include="shared-style cr-shared-style paper-button-style">
:host {
box-sizing: border-box;
display: block;
@@ -22,7 +23,7 @@
}
iron-list {
- @apply(--card-sizing);
+ @apply --card-sizing;
margin-top: var(--first-card-padding-top);
}
@@ -62,10 +63,10 @@
<div slot="title">$i18n{removeSelected}</div>
<div slot="body">$i18n{deleteWarning}</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onDialogCancelTap_">
+ <paper-button class="cancel-button" on-click="onDialogCancelTap_">
$i18n{cancel}
</paper-button>
- <paper-button class="action-button" on-tap="onDialogConfirmTap_">
+ <paper-button class="action-button" on-click="onDialogConfirmTap_">
$i18n{deleteConfirm}
</paper-button>
</div>
@@ -74,15 +75,15 @@
<template is="cr-lazy-render" id="sharedMenu">
<dialog is="cr-action-menu">
- <button id="menuMoreButton" class="dropdown-item"
+ <button id="menuMoreButton" slot="item" class="dropdown-item"
hidden="[[!canSearchMoreFromSite_(
searchedTerm, actionMenuModel_.item.domain)]]"
- on-tap="onMoreFromSiteTap_">
+ on-click="onMoreFromSiteTap_">
$i18n{moreFromSite}
</button>
- <button id="menuRemoveButton" class="dropdown-item"
+ <button id="menuRemoveButton" slot="item" class="dropdown-item"
hidden="[[!canDeleteHistory_]]"
- on-tap="onRemoveFromHistoryTap_">
+ on-click="onRemoveFromHistoryTap_">
$i18n{removeFromHistory}
</button>
</dialog>
diff --git a/chromium/chrome/browser/resources/md_history/history_list.js b/chromium/chrome/browser/resources/md_history/history_list.js
index 8b94ed48774..9cd061e5c60 100644
--- a/chromium/chrome/browser/resources/md_history/history_list.js
+++ b/chromium/chrome/browser/resources/md_history/history_list.js
@@ -138,6 +138,25 @@ Polymer({
this.fire('query-history', false);
},
+ selectOrUnselectAll: function() {
+ if (this.historyData_.length == this.getSelectedItemCount())
+ this.unselectAllItems();
+ else
+ this.selectAllItems();
+ },
+
+ /**
+ * Select each item in |historyData|.
+ */
+ selectAllItems: function() {
+ if (this.historyData_.length == this.getSelectedItemCount())
+ return;
+
+ this.historyData_.forEach((item, index) => {
+ this.changeSelection_(index, true);
+ });
+ },
+
/**
* Deselect each item in |selectedItems|.
*/
@@ -205,6 +224,10 @@ Polymer({
.then((items) => {
this.removeItemsByIndex_(Array.from(this.selectedItems));
this.fire('unselect-all');
+ if (this.historyData_.length == 0) {
+ // Try reloading if nothing is rendered.
+ this.fire('query-history', false);
+ }
});
},
diff --git a/chromium/chrome/browser/resources/md_history/side_bar.html b/chromium/chrome/browser/resources/md_history/side_bar.html
index b49c3e2fe5c..49b11840b7b 100644
--- a/chromium/chrome/browser/resources/md_history/side_bar.html
+++ b/chromium/chrome/browser/resources/md_history/side_bar.html
@@ -107,7 +107,7 @@
<div class="separator"></div>
<a id="clear-browsing-data"
href="chrome://settings/clearBrowserData"
- on-tap="onClearBrowsingDataTap_"
+ on-click="onClearBrowsingDataTap_"
disabled$="[[guestSession_]]"
tabindex$="[[computeClearBrowsingDataTabIndex_(guestSession_)]]">
$i18n{clearBrowsingData}
diff --git a/chromium/chrome/browser/resources/md_history/synced_device_card.html b/chromium/chrome/browser/resources/md_history/synced_device_card.html
index 708f60bcf3c..a9cc4154ac8 100644
--- a/chromium/chrome/browser/resources/md_history/synced_device_card.html
+++ b/chromium/chrome/browser/resources/md_history/synced_device_card.html
@@ -16,7 +16,7 @@
<template>
<style include="shared-style">
:host {
- @apply(--card-sizing);
+ @apply --card-sizing;
-webkit-tap-highlight-color: transparent;
display: block;
padding-bottom: var(--card-padding-between);
@@ -57,7 +57,7 @@
}
#history-item-container {
- @apply(--shadow-elevation-2dp);
+ @apply --shadow-elevation-2dp;
background: #fff;
border-radius: 2px;
}
diff --git a/chromium/chrome/browser/resources/md_history/synced_device_card.js b/chromium/chrome/browser/resources/md_history/synced_device_card.js
index 3206eb236eb..bbd109a64a9 100644
--- a/chromium/chrome/browser/resources/md_history/synced_device_card.js
+++ b/chromium/chrome/browser/resources/md_history/synced_device_card.js
@@ -68,8 +68,7 @@ Polymer({
},
/**
- * Open a single synced tab. Listens to 'click' rather than 'tap'
- * to determine what modifier keys were pressed.
+ * Open a single synced tab.
* @param {DomRepeatClickEvent} e
* @private
*/
diff --git a/chromium/chrome/browser/resources/md_history/synced_device_manager.html b/chromium/chrome/browser/resources/md_history/synced_device_manager.html
index 55e529c83e5..7b10c59e6cb 100644
--- a/chromium/chrome/browser/resources/md_history/synced_device_manager.html
+++ b/chromium/chrome/browser/resources/md_history/synced_device_manager.html
@@ -4,13 +4,14 @@
<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
<link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html">
<link rel="import" href="chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.html">
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
<link rel="import" href="chrome://history/shared_style.html">
<link rel="import" href="chrome://history/synced_device_card.html">
<dom-module id="history-synced-device-manager">
<template>
- <style include="shared-style cr-shared-style">
+ <style include="shared-style cr-shared-style paper-button-style">
:host {
display: block;
overflow: auto;
@@ -81,19 +82,19 @@
<div id="sign-in-promo">$i18n{signInPromo}</div>
<div id="sign-in-promo-desc">$i18n{signInPromoDesc}</div>
<paper-button id="sign-in-button" class="action-button"
- on-tap="onSignInTap_">
+ on-click="onSignInTap_">
$i18n{signInButton}
</paper-button>
</div>
<template is="cr-lazy-render" id="menu">
<dialog is="cr-action-menu">
- <button id="menuOpenButton" class="dropdown-item"
- on-tap="onOpenAllTap_">
+ <button id="menuOpenButton" slot="item" class="dropdown-item"
+ on-click="onOpenAllTap_">
$i18n{openAll}
</button>
- <button id="menuDeleteButton" class="dropdown-item"
- on-tap="onDeleteSessionTap_">
+ <button id="menuDeleteButton" slot="item" class="dropdown-item"
+ on-click="onDeleteSessionTap_">
$i18n{deleteSession}
</button>
</dialog>
diff --git a/chromium/chrome/browser/resources/md_user_manager/shared_styles.html b/chromium/chrome/browser/resources/md_user_manager/shared_styles.html
index 7c9d1aa16ab..02eac8fb435 100644
--- a/chromium/chrome/browser/resources/md_user_manager/shared_styles.html
+++ b/chromium/chrome/browser/resources/md_user_manager/shared_styles.html
@@ -26,7 +26,7 @@
}
paper-button.action {
- @apply(--action-button);
+ @apply --action-button;
}
paper-button.action.primary {
diff --git a/chromium/chrome/browser/resources/md_user_manager/user_manager.html b/chromium/chrome/browser/resources/md_user_manager/user_manager.html
index 8dbe1d9def7..244e159517d 100644
--- a/chromium/chrome/browser/resources/md_user_manager/user_manager.html
+++ b/chromium/chrome/browser/resources/md_user_manager/user_manager.html
@@ -322,7 +322,7 @@
--paper-button-flat-keyboard-focus: {
background: rgb(173, 50, 36);
};
- @apply(--action-button);
+ @apply --action-button;
}
#user-manager-prompt-message {
diff --git a/chromium/chrome/browser/resources/md_user_manager/user_manager_pages.html b/chromium/chrome/browser/resources/md_user_manager/user_manager_pages.html
index 8a4acecdaf6..c2f8945b653 100644
--- a/chromium/chrome/browser/resources/md_user_manager/user_manager_pages.html
+++ b/chromium/chrome/browser/resources/md_user_manager/user_manager_pages.html
@@ -7,6 +7,7 @@
<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/animations/fade-out-animation.html">
<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable.html">
<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animated-pages.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/web-animations.html">
<dom-module id="user-manager-pages">
<template>
diff --git a/chromium/chrome/browser/resources/media/media_engagement.html b/chromium/chrome/browser/resources/media/media_engagement.html
index fd374ce52d3..af4c2d8e1e2 100644
--- a/chromium/chrome/browser/resources/media/media_engagement.html
+++ b/chromium/chrome/browser/resources/media/media_engagement.html
@@ -18,6 +18,10 @@
font-size: 14px;
}
+ button {
+ margin-bottom: 20px;
+ }
+
table {
border-collapse: collapse;
margin-bottom: 20px;
@@ -50,7 +54,6 @@
.origin-cell {
background-color: rgba(230, 230, 230, 0.5);
- min-width: 500px;
}
.visits-count-cell,
@@ -60,6 +63,7 @@
.significant-playbacks-count-cell {
background-color: rgba(230, 230, 230, 0.5);
text-align: right;
+ white-space: nowrap;
}
.base-score-input {
@@ -96,6 +100,7 @@
</head>
<body>
<h1>Media Engagement</h1>
+ <button id="copy-all-to-clipboard">Copy all to clipboard</button>
<table>
<thead>
<tr id="config-table-header">
@@ -110,6 +115,12 @@
<tbody id="config-table-body">
</tbody>
</table>
+ <p>
+ <label>
+ <input id="show-no-playbacks" type="checkbox">
+ Show sessions with no playbacks
+ </label>
+ </p>
<table>
<thead>
<tr id="engagement-table-header">
@@ -117,10 +128,10 @@
Origin
</th>
<th sort-key="visits" sort-reverse>
- Visits
+ Sessions
</th>
<th sort-key="mediaPlaybacks" sort-reverse>
- Playbacks
+ Sessions with playback
</th>
<th sort-key="audiblePlaybacks" sort-reverse>
Audible Playbacks*
@@ -134,6 +145,9 @@
<th sort-key="isHigh" sort-reverse>
Is High
</th>
+ <th sort-key="highScoreChanges" sort-reverse>
+ Is High Changes
+ </th>
<th sort-key="totalScore" class="sort-column" sort-reverse>
Score
</th>
@@ -156,6 +170,7 @@
<td class="significant-playbacks-count-cell"></td>
<td class="last-playback-time-cell"></td>
<td class="is-high-cell"></td>
+ <td class="is-high-changes-cell"></td>
<td class="total-score-cell"></td>
<td class="engagement-bar-cell">
<div class="engagement-bar"></div>
diff --git a/chromium/chrome/browser/resources/media/media_engagement.js b/chromium/chrome/browser/resources/media/media_engagement.js
index 0ca0a3cd94c..a4a8af7841f 100644
--- a/chromium/chrome/browser/resources/media/media_engagement.js
+++ b/chromium/chrome/browser/resources/media/media_engagement.js
@@ -19,6 +19,7 @@ var engagementTableBody = null;
var sortReverse = true;
var sortKey = 'totalScore';
var configTableBody = null;
+var showNoPlaybacks = false;
/**
* Creates a single row in the engagement table.
@@ -37,8 +38,9 @@ function createRow(rowInfo) {
new Date(rowInfo.lastMediaPlaybackTime).toISOString() :
'';
td[6].textContent = rowInfo.isHigh ? 'Yes' : 'No';
- td[7].textContent = rowInfo.totalScore ? rowInfo.totalScore.toFixed(2) : '0';
- td[8].getElementsByClassName('engagement-bar')[0].style.width =
+ td[7].textContent = rowInfo.highScoreChanges;
+ td[8].textContent = rowInfo.totalScore ? rowInfo.totalScore.toFixed(2) : '0';
+ td[9].getElementsByClassName('engagement-bar')[0].style.width =
(rowInfo.totalScore * 50) + 'px';
return document.importNode(template.content, true);
}
@@ -77,7 +79,8 @@ function compareTableItem(sortKey, a, b) {
if (sortKey == 'visits' || sortKey == 'mediaPlaybacks' ||
sortKey == 'lastMediaPlaybackTime' || sortKey == 'totalScore' ||
- sortKey == 'audiblePlaybacks' || sortKey == 'significantPlaybacks') {
+ sortKey == 'audiblePlaybacks' || sortKey == 'significantPlaybacks' ||
+ sortKey == 'highScoreChanges') {
return val1 - val2;
}
@@ -108,7 +111,7 @@ function renderConfigTable(config) {
configTableBody.innerHTML = '';
configTableBody.appendChild(
- createConfigRow('Min Visits', config.scoreMinVisits));
+ createConfigRow('Min Sessions', config.scoreMinVisits));
configTableBody.appendChild(
createConfigRow('Lower Threshold', config.highScoreLowerThreshold));
configTableBody.appendChild(
@@ -121,7 +124,8 @@ function renderConfigTable(config) {
function renderTable() {
clearTable();
sortInfo();
- info.forEach(rowInfo => engagementTableBody.appendChild(createRow(rowInfo)));
+ info.filter(rowInfo => (showNoPlaybacks || rowInfo.mediaPlaybacks > 0))
+ .forEach(rowInfo => engagementTableBody.appendChild(createRow(rowInfo)));
}
/**
@@ -173,5 +177,27 @@ document.addEventListener('DOMContentLoaded', function() {
renderTable();
});
}
+
+ // Add handler to 'copy all to clipboard' button
+ var copyAllToClipboardButton = $('copy-all-to-clipboard');
+ copyAllToClipboardButton.addEventListener('click', (e) => {
+ // Make sure nothing is selected
+ window.getSelection().removeAllRanges();
+
+ document.execCommand('selectAll');
+ document.execCommand('copy');
+
+ // And deselect everything at the end.
+ window.getSelection().removeAllRanges();
+ });
+
+ // Add handler to 'show no playbacks' checkbox
+ var showNoPlaybacksCheckbox = $('show-no-playbacks');
+ showNoPlaybacksCheckbox.addEventListener('change', (e) => {
+ showNoPlaybacks = e.target.checked;
+ renderTable();
+ });
+
});
+
})();
diff --git a/chromium/chrome/browser/resources/media/mei_preload/preloaded_data.pb b/chromium/chrome/browser/resources/media/mei_preload/preloaded_data.pb
index 1302db9033b..1652cafc98a 100644
--- a/chromium/chrome/browser/resources/media/mei_preload/preloaded_data.pb
+++ b/chromium/chrome/browser/resources/media/mei_preload/preloaded_data.pb
Binary files differ
diff --git a/chromium/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.css b/chromium/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.css
index 7ae1c39ff5a..9cc54236cca 100644
--- a/chromium/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.css
+++ b/chromium/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.css
@@ -137,7 +137,7 @@ paper-item:hover {
border: 0;
}
-paper-menu {
+paper-listbox {
color: rgba(0, 0, 0, 0.87);
overflow-x: hidden;
overflow-y: auto;
diff --git a/chromium/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.html b/chromium/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.html
index 9a9e85029f4..6b606df8ece 100644
--- a/chromium/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.html
+++ b/chromium/chrome/browser/resources/media_router/elements/media_router_container/media_router_container.html
@@ -3,7 +3,7 @@
<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-item/paper-item.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-menu/paper-menu.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-listbox/paper-listbox.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
<link rel="import" href="../media_router_header/media_router_header.html">
<link rel="import" href="../route_details/route_details.html">
@@ -57,7 +57,7 @@
</media-router-header>
<div id="content">
<template is="dom-if" if="[[!computeCastModeListHidden_(currentView_)]]">
- <paper-menu id="cast-mode-list" role="presentation"
+ <paper-listbox id="cast-mode-list" role="presentation"
selectable="paper-item" selected="{{selectedCastModeMenuItem_}}">
<template is="dom-repeat" id="presentationCastModeList"
items="[[computePresentationCastModeList_(castModeList)]]">
@@ -94,7 +94,7 @@
<div><span>[[item.description]]</span></div>
</paper-item>
</template>
- </paper-menu>
+ </paper-listbox>
</template>
<template is="dom-if"
if="[[!computeRouteDetailsHidden_(currentView_, issue)]]">
@@ -121,7 +121,7 @@
</div>
<template is="dom-if" if="[[!computeSinkListHidden_(sinksToShow_)]]">
<div id="sink-list" hidden$="[[hideSinkListForAnimation_]]">
- <paper-menu id="sink-list-paper-menu" role="presentation">
+ <paper-listbox id="sink-list-paper-menu" role="presentation">
<template is="dom-repeat" id="sinkList" items="[[sinksToShow_]]">
<paper-item on-tap="onSinkClick_">
<div class="sink-content">
@@ -158,7 +158,7 @@
</div>
</paper-item>
</template>
- </paper-menu>
+ </paper-listbox>
</div>
</template>
<template is="dom-if" if="[[searchEnabled_]]">
@@ -185,7 +185,8 @@
</div>
<div id="search-results"
hidden$="[[computeSearchResultsHidden_(searchResultsToShow_, isSearchListHidden_)]]">
- <paper-menu id="search-results-paper-menu" selected="0" role="presentation">
+ <paper-listbox id="search-results-paper-menu" selected="0"
+ role="presentation">
<template is="dom-repeat" id="searchResults"
items="[[searchResultsToShow_]]">
<paper-item class="search-item" on-tap="onSinkClick_">
@@ -226,7 +227,7 @@
</div>
</paper-item>
</template>
- </paper-menu>
+ </paper-listbox>
</div>
</div>
</template>
diff --git a/chromium/chrome/browser/resources/media_router/elements/route_details/route_details.css b/chromium/chrome/browser/resources/media_router/elements/route_details/route_details.css
index 92f8b1a7dd1..e5b7f3e72da 100644
--- a/chromium/chrome/browser/resources/media_router/elements/route_details/route_details.css
+++ b/chromium/chrome/browser/resources/media_router/elements/route_details/route_details.css
@@ -3,8 +3,8 @@
* found in the LICENSE file. */
#route-action-buttons {
- @apply(--layout-horizontal);
- @apply(--layout-end-justified);
+ @apply --layout-horizontal;
+ @apply --layout-end-justified;
margin: 0 10px;
padding: 0;
white-space: nowrap;
diff --git a/chromium/chrome/browser/resources/media_router/extension/BUILD.gn b/chromium/chrome/browser/resources/media_router/extension/BUILD.gn
new file mode 100644
index 00000000000..3b002567f96
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/BUILD.gn
@@ -0,0 +1,78 @@
+import("src/files.gni")
+
+group("all") {
+ deps = [
+ ":media_router",
+ ]
+}
+
+declare_args() {
+ # Determines whether JSCompiler should be used to typecheck
+ # JavaScript code for the Media Router extension.
+ enable_media_router_jscompile = false
+}
+
+if (enable_media_router_jscompile) {
+ # Run JSCompiler for typechecking.
+ action("media_router_type_check") {
+ script = "//third_party/closure_compiler/compile2.py"
+ inputs = rebase_path(mr_module_files, ".", "src") + [
+ "src/externs.js",
+ "src/mojo_externs.js",
+ ]
+ outputs = [
+ target_gen_dir + "/$target_name.stamp",
+ ]
+ args = [
+ "--out_file",
+ rebase_path(outputs[0], root_build_dir),
+ "--closure_args",
+ "dependency_mode=LOOSE",
+ "checks_only",
+ "--",
+ ] + rebase_path(inputs, root_build_dir)
+ }
+}
+
+# Concatentate JS files to produce "module" JS files that can be
+# loaded at runtime. This could be done by JSCompiler, but it depends
+# on Java, and Java isn't always available.
+action("media_router_modules") {
+ script = "concat_js_modules.py"
+ module_inputs = rebase_path(mr_module_files, ".", "src")
+ inputs = module_inputs + [ "prelude.js" ]
+ outputs = []
+ foreach(module_name, mr_module_names) {
+ outputs += [ "${target_gen_dir}/${module_name}.js" ]
+ }
+ args = [ "--module-specs" ] + mr_module_specs + [
+ "--prelude-file",
+ rebase_path("prelude.js", root_build_dir),
+ "--output-dir",
+ rebase_path(target_gen_dir + "/", root_build_dir),
+ "--",
+ ] + rebase_path(module_inputs, root_build_dir)
+}
+
+# Produce the Media Router extension. At present, the extension isn't
+# included in the Chromium distribution, but it can be sideloaded into
+# Chromium for testing.
+action("media_router") {
+ script = "assemble_extension.py"
+ inputs = [
+ "manifest.yaml",
+ ]
+ outputs = [
+ "$target_gen_dir/manifest.json",
+ ]
+ deps = [
+ ":media_router_modules",
+ ]
+ if (enable_media_router_jscompile) {
+ deps += [ ":media_router_type_check" ]
+ }
+ args = [
+ "--manifest_in=" + rebase_path("manifest.yaml", root_build_dir),
+ "--output_dir=" + rebase_path(target_gen_dir, root_build_dir),
+ ]
+}
diff --git a/chromium/chrome/browser/resources/media_router/extension/assemble_extension.py b/chromium/chrome/browser/resources/media_router/extension/assemble_extension.py
new file mode 100644
index 00000000000..7b2df0a255b
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/assemble_extension.py
@@ -0,0 +1,41 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""This script generates manifest.json from manifest.yaml.
+
+In the future it may copy other non-JS files into the target
+directory, hence the name of the script.
+"""
+
+import argparse
+import json
+import re
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--manifest_in")
+ parser.add_argument("--output_dir")
+ args = parser.parse_args()
+
+ # Load the input file, removing YAML-style comment lines to produce
+ # valid JSON.
+ json_data = ''
+ with open(args.manifest_in) as manifest_in:
+ for line in manifest_in:
+ if re.match("^ *#", line):
+ # Insert an empty line so line numbers aren't changed.
+ json_data += '\n'
+ else:
+ json_data += line
+
+ # Verify that the result is valid JSON.
+ json.loads(json_data)
+
+ # Dump the output to the requested location.
+ with open(args.output_dir + "/manifest.json", "w") as manifest_out:
+ manifest_out.writelines(json_data)
+
+
+main()
diff --git a/chromium/chrome/browser/resources/media_router/extension/concat_js_modules.py b/chromium/chrome/browser/resources/media_router/extension/concat_js_modules.py
new file mode 100644
index 00000000000..cda9ca1dd47
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/concat_js_modules.py
@@ -0,0 +1,87 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""This script concatenates JS files into a set of output files.
+
+Modules are specified as a list of strings of the form
+module=<name>:<num>[:...], where <name> is the name of the module and
+<num> is the number of JS files that should be concatenates to make
+the module file, and if a second : is present, everything after it is
+ignored. (This strange format is used because it is compatible with
+JSCompiler's --module flag.)
+"""
+
+import argparse
+import os.path
+import re
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--module-specs", nargs="+",
+ help="List of module specifiations.")
+ parser.add_argument(
+ "--output-dir",
+ help="Directory where output files are written.")
+ parser.add_argument(
+ "--prelude-file",
+ help="A prelude file included in every output module.")
+ parser.add_argument(
+ "sources", nargs="+",
+ help="JS input files.")
+ args = parser.parse_args()
+
+ # Read the prelude file, which contains code to be placed at the
+ # start of each module.
+ with open(args.prelude_file, "r") as prelude_in:
+ prelude = prelude_in.read()
+
+ # Loop over all specified modules, and simulaneously traverse the
+ # list of source files using `source_index`.
+ source_index = 0
+ for spec in args.module_specs:
+ # Split the module spec into a module name and a count of source
+ # files to include in the module.
+ pre, sep, post = spec.partition("module=")
+ assert pre == ""
+ assert sep
+ parts = post.split(":")
+ module_name = parts[0]
+ input_count = int(parts[1])
+
+ # Write the module file.
+ module_file = os.path.join(args.output_dir, module_name + ".js")
+ with open(module_file, "w") as module_out:
+ module_out.write(prelude)
+
+ # Append as many input files as requested by the module spec.
+ for i in range(input_count):
+ source_file = args.sources[source_index]
+ source_index += 1
+ module_name = None
+ with open(source_file, "r") as source_in:
+ module_out.write("goog.scope(() => {\n")
+ # Copy input line by line.
+ for line in source_in:
+ m = re.match(r"goog\.module\('([^']+)'\);\n", line)
+ if m:
+ # Handle goog.module statements specially.
+ module_name = m.group(1)
+ module_out.write("let exports = {};\n")
+ else:
+ # Typical case: just copy the line verbatim.
+ module_out.write(line)
+ if module_name:
+ # Put exported names into the global namespace. That's
+ # not now goog.module is supposed to work, but it's close
+ # enough.
+ module_out.write(
+ "__setGlobal('{}', exports);\n".format(module_name));
+ module_out.write("});\n")
+
+ # Check that every source file has been appended to a module.
+ if source_index != len(args.sources):
+ sys.exit("Too many input files.")
+
+main()
diff --git a/chromium/chrome/browser/resources/media_router/extension/manifest.yaml b/chromium/chrome/browser/resources/media_router/extension/manifest.yaml
new file mode 100644
index 00000000000..4eeb52af9d8
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/manifest.yaml
@@ -0,0 +1,53 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# NOTE: Only a subset of YAML syntax is allowed in this file. Lines
+# containing comments are stripped, and the remaining lines must be
+# valid JSON.
+
+{
+ "name": "Chromium Media Router",
+ "version": "0.1",
+ "manifest_version": 2,
+ "description": "Provider for discovery and services for mirroring of Chromium Media Router",
+ "minimum_chrome_version": "37",
+
+ "permissions": [
+ "alarms",
+ "declarativeWebRequest",
+ "desktopCapture",
+ "dial",
+ "http://*/*",
+ "mediaRouterPrivate",
+ "metricsPrivate",
+ "storage",
+ "settingsPrivate",
+ "tabCapture",
+ "tabs"
+ ],
+
+ # Background script.
+ "background": {
+ "scripts": [
+ "common.js",
+ "mirroring_common.js",
+ "background_script.js"
+ ],
+ "persistent": false
+ },
+
+ # Google Feedback requires:
+ # script-src: https://feedback.googleusercontent.com https://www.google.com
+ # https://www.gstatic.com/feedback
+ # child-src: https://www.google.com
+ #
+ # Webview elements are implemented as a custom <object> elements that loads
+ # dynamic plugin data. Without an "object-src 'self'" permission in the CSP,
+ # webview elements fail to attach to extension pages (crbug.com/509854).
+ "content_security_policy": "default-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; script-src 'self' https://feedback.googleusercontent.com https://www.google.com https://www.gstatic.com; child-src https://www.google.com; connect-src 'self' http://*:* https://*:*; font-src https://fonts.gstatic.com; object-src 'self';",
+
+ # Setting the public key fixes the extension id to:
+ # enhhojjnijigcajfphajepfemndkmdlo
+ "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+hlN5FB+tjCsBszmBIvIcD/djLLQm2zZfFygP4U4/o++ZM91EWtgII10LisoS47qT2TIOg4Un4+G57elZ9PjEIhcJfANqkYrD3t9dpEzMNr936TLB2u683B5qmbB68Nq1Eel7KVc+F0BqhBondDqhvDvGPEV0vBsbErJFlNH7SQIDAQAB"
+}
diff --git a/chromium/chrome/browser/resources/media_router/extension/prelude.js b/chromium/chrome/browser/resources/media_router/extension/prelude.js
new file mode 100644
index 00000000000..6140042e26d
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/prelude.js
@@ -0,0 +1,50 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Implementation of JSCompiler intrinsics for use when JSCompiler is
+// not available.
+
+/**
+ * Sets the values of a global expression consisting of a
+ * dot-delimited list of identifiers.
+ */
+const __setGlobal = (name, value) => {
+ let parent = window;
+ const parts = name.split('.');
+ for (let i = 0; i < parts.length; i++) {
+ const part = parts[i];
+ if (i == parts.length - 1) {
+ parent[part] = value;
+ } else {
+ if (!parent[part]) {
+ parent[part] = {};
+ }
+ parent = parent[part];
+ }
+ }
+};
+
+const goog = {
+ provide(name) {
+ __setGlobal(name, {});
+ },
+
+ require(name) {
+ let parent = window;
+ name.split('.').forEach(part => {
+ parent = parent[part];
+ });
+ return parent;
+ },
+
+ module: {
+ declareLegacyNamespace() {},
+ },
+
+ forwardDeclare() {},
+
+ scope(body) {
+ body.call(window);
+ },
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/background.js b/chromium/chrome/browser/resources/media_router/extension/src/background.js
new file mode 100644
index 00000000000..e1b87123e7f
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/background.js
@@ -0,0 +1,9 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+goog.require('mr.Init');
+
+
+mr.Init.init().then(undefined, err => window.close());
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/config.js b/chromium/chrome/browser/resources/media_router/extension/src/config.js
new file mode 100644
index 00000000000..fc339414189
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/config.js
@@ -0,0 +1,26 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Configs.
+ */
+
+goog.provide('mr.Config');
+
+
+/**
+ * Compiler flag used to enable debug/testing only components. The default
+ * value defined here is only used in open-source builds.
+ * @define {boolean} True if this extension was released through debug channel.
+ */
+mr.Config.isDebugChannel = true;
+
+
+/**
+ * Compiler flag used to set logging level and other privacy sensitive config
+ * for public release. The default value defined here is only used in
+ * open-source builds.
+ * @define {boolean} True if this extension was released through public channel.
+ */
+mr.Config.isPublicChannel = false;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/event_listener.js b/chromium/chrome/browser/resources/media_router/extension/src/event_listener.js
new file mode 100644
index 00000000000..8cef915b1a3
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/event_listener.js
@@ -0,0 +1,188 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Interface for registration of listeners of events that can
+ * wake the extension. All EventListeners must invoke
+ * |addOnStartup| in the first event loop in order to
+ * properly receive events that woke the extension. When adding a new
+ * EventListener, be sure to also add it to |mr.Init.addEventListeners_|.
+ */
+
+goog.provide('mr.EventListener');
+
+goog.require('mr.EventAnalytics');
+goog.require('mr.Module');
+goog.require('mr.PersistentData');
+goog.require('mr.PersistentDataManager');
+
+
+
+/**
+ * Listens for an extension event conditionally, and forwards the event to a
+ * designated module.
+ *
+ * This is useful for event listeners that are not added when the extension
+ * initially starts up (mDNS listeners, for instance). When an event
+ * listener is ready to be added for the first time (and the module is ready
+ * to handle the event), |addListener()| should be called. By doing so, the
+ * event listener will be automatically added back at the top level the next
+ * time the extension wakes up from suspension, unless |removeListener()| is
+ * called.
+ *
+ * @implements {mr.PersistentData}
+ * @template EVENT
+ */
+mr.EventListener = class {
+ /**
+ * @param {!mr.EventAnalytics.Event} eventType The event type for this
+ * listener to record with analytics.
+ * @param {string} name Name of the handler for PersistentData.
+ * @param {mr.ModuleId} moduleId Name of the module handling the events.
+ * @param {!EVENT} eventHandler The event handler to listen to.
+ * @param {...*} listenerArgs Additional arguments when adding the listener,
+ * such as filters.
+ */
+ constructor(eventType, name, moduleId, eventHandler, ...listenerArgs) {
+ /** @private @const {!mr.EventAnalytics.Event} */
+ this.eventType_ = eventType;
+
+ /** @private @const {string} */
+ this.name_ = name;
+
+ /** @private @const {mr.ModuleId} */
+ this.moduleId_ = moduleId;
+
+ /** @private @const {!EVENT} */
+ this.eventHandler_ = eventHandler;
+
+ /** @private @const {!Array<*>} */
+ this.listenerArgs_ = listenerArgs;
+
+ /**
+ * This field is stored as temporary data. Set to true if the listener was
+ * added, and will be added back the next time the extension wakes up via
+ * |addOnStartup()|.
+ * @private {boolean}
+ */
+ this.hasListener_ = false;
+
+ /** @private @const {?function(*)} */
+ this.listener_ = (...args) => this.dispatchEvent_(...args);
+ }
+
+ /**
+ * Adds back the event listener that was added before the last suspension.
+ * This method is called by mr.Init during the first event loop only.
+ */
+ addOnStartup() {
+ mr.PersistentDataManager.register(this);
+ }
+
+ /**
+ * Helper method to add the listener to the event.
+ * @private
+ */
+ doAddListener_() {
+ this.eventHandler_.addListener(this.listener_, ...this.listenerArgs_);
+ }
+
+ /**
+ * Adds event listener. No-ops if listener is already added. Event
+ * listeners will be added back automatically the next time extension wakes
+ * up, during |addOnStartup|.
+ */
+ addListener() {
+ if (this.hasListener_) {
+ return;
+ }
+ this.hasListener_ = true;
+ this.doAddListener_();
+ }
+
+ /**
+ * Removes event listener. The next time extension wakes up, the event
+ * listener will not be added back during |addOnStartup|.
+ */
+ removeListener() {
+ if (!this.hasListener_) {
+ return;
+ }
+ this.eventHandler_.removeListener(this.listener_);
+ this.hasListener_ = false;
+ }
+
+ /**
+ * Implementations may override this method validate an incoming event before
+ * it is forwarded to the designated module.
+ * @param {...*} args
+ * @return {boolean} true if the event can be forwarded to the module.
+ */
+ validateEvent(...args) {
+ return true;
+ }
+
+ /**
+ * The entry point for incoming events. First the event will be validated.
+ * After that, the event will be forwarded to the module, which is loaded if
+ * necessary. Since asynchronous event handling is required, a value
+ * representing asynchronous handling will be returned synchronously, as
+ * required by the event.
+ * @param {...*} args Parameters for the incoming event.
+ * @return {*} false if the event is invalid. Otherwise, a value that
+ * represents that event will be handled asynchronously will be returned.
+ * @private
+ */
+ dispatchEvent_(...args) {
+ mr.EventAnalytics.recordEvent(this.eventType_);
+ if (!this.validateEvent(...args)) {
+ return false;
+ }
+ mr.Module.load(this.moduleId_)
+ .then(module => module.handleEvent(this.eventHandler_, ...args));
+ return this.deferredReturnValue();
+ }
+
+ /**
+ * Returns |true| if the event listener is already registered.
+ * @return {boolean}
+ */
+ isRegistered() {
+ return this.hasListener_;
+ }
+
+ /**
+ * @override
+ */
+ getStorageKey() {
+ return 'mr.EventListener.' + this.name_;
+ }
+
+ /**
+ * @override
+ */
+ getData() {
+ return [this.hasListener_];
+ }
+
+ /**
+ * @override
+ */
+ loadSavedData() {
+ const hadListener =
+ /** @type {boolean} */ (
+ mr.PersistentDataManager.getTemporaryData(this));
+ if (hadListener) {
+ this.addListener();
+ }
+ }
+
+ /**
+ * Implementations may override this method to provide a return value in the
+ * case the event is not handled synchronously. The default value is
+ * undefined.
+ * @return {*}
+ */
+ deferredReturnValue() {}
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/event_listener_test.js b/chromium/chrome/browser/resources/media_router/extension/src/event_listener_test.js
new file mode 100644
index 00000000000..a64895f5318
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/event_listener_test.js
@@ -0,0 +1,147 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.setTestOnly('event_listener_test');
+
+goog.require('mr.EventAnalytics');
+goog.require('mr.EventListener');
+goog.require('mr.Module');
+goog.require('mr.PersistentDataManager');
+goog.require('mr.PromiseResolver');
+goog.require('mr.UnitTestUtils');
+
+
+describe('Tests event listeners', function() {
+ let mockEvent;
+
+ beforeEach(function() {
+ mr.UnitTestUtils.mockChromeApi();
+ mockEvent = jasmine.createSpyObj(
+ 'mockEvent', ['addListener', 'hasListener', 'removeListener']);
+ });
+
+ afterEach(function() {
+ mr.Module.clearForTest();
+ mr.PersistentDataManager.clear();
+ mr.UnitTestUtils.restoreChromeApi();
+ });
+
+ it('EventListener addListener no listener args', function() {
+ const listener = new mr.EventListener(
+ mr.EventAnalytics.Event.DIAL_ON_ERROR, 'MockEventListener',
+ 'SomeModule', mockEvent);
+ listener.addListener();
+ expect(mockEvent.addListener).toHaveBeenCalled();
+ expect(listener.isRegistered()).toBe(true);
+
+ listener.removeListener();
+ expect(mockEvent.removeListener).toHaveBeenCalled();
+ expect(listener.isRegistered()).toBe(false);
+ });
+
+ it('EventListener addListener with listener args', function() {
+ const listenerArgs = ['foo', 1];
+ const listener = new mr.EventListener(
+ mr.EventAnalytics.Event.DIAL_ON_ERROR, 'MockEventListener',
+ 'SomeModule', mockEvent, ...listenerArgs);
+ listener.addListener();
+ expect(mockEvent.addListener)
+ .toHaveBeenCalledWith(jasmine.any(Function), ...listenerArgs);
+ expect(listener.isRegistered()).toBe(true);
+
+ listener.removeListener();
+ expect(mockEvent.removeListener).toHaveBeenCalled();
+ expect(listener.isRegistered()).toBe(false);
+ });
+
+ it('EventListener addOnStartup no prior register', function() {
+ const listener = new mr.EventListener(
+ mr.EventAnalytics.Event.DIAL_ON_ERROR, 'MockEventListener',
+ 'SomeModule', mockEvent);
+ listener.addOnStartup();
+ expect(mockEvent.addListener).not.toHaveBeenCalled();
+ });
+
+ it('EventListener addOnStartup registered before', function() {
+ const listener = new mr.EventListener(
+ mr.EventAnalytics.Event.DIAL_ON_ERROR, 'MockEventListener',
+ 'SomeModule', mockEvent);
+ listener.addOnStartup();
+ expect(mockEvent.addListener.calls.count()).toBe(0);
+ listener.addListener();
+ expect(mockEvent.addListener.calls.count()).toBe(1);
+
+ mr.PersistentDataManager.suspendForTest();
+
+ const listener2 = new mr.EventListener(
+ mr.EventAnalytics.Event.DIAL_ON_ERROR, 'MockEventListener',
+ 'SomeModule', mockEvent);
+ // Registers with mr.PersistentDataManager again. It should see that it was
+ // previously registered before suspend, and re-adds the listener
+ // auatomatically.
+ listener2.addOnStartup();
+ expect(mockEvent.addListener.calls.count()).toBe(2);
+ });
+
+ it('EventListener rejected invalid event', function() {
+ let savedListener = null;
+ const listener = new mr.EventListener(
+ mr.EventAnalytics.Event.DIAL_ON_ERROR, 'MockEventListener',
+ 'SomeModule', mockEvent);
+ mockEvent.addListener.and.callFake(listener => {
+ savedListener = listener;
+ });
+ listener.addListener();
+ expect(mockEvent.addListener).toHaveBeenCalled();
+ expect(savedListener).not.toBeNull();
+ spyOn(listener, 'validateEvent').and.returnValue(false);
+
+ // Module won't be loaded since event did not pass validation.
+ spyOn(mr.Module, 'load');
+ let returnedValue = savedListener('foo', 1);
+ expect(returnedValue).toBe(false);
+ expect(mr.Module.load.calls.count()).toBe(0);
+ });
+
+ it('EventListener dispatch event', function(done) {
+ spyOn(mr.EventAnalytics, 'recordEvent');
+ let savedListener = null;
+ const listener = new mr.EventListener(
+ mr.EventAnalytics.Event.DIAL_ON_ERROR, 'MockEventListener',
+ 'SomeModule', mockEvent);
+ spyOn(listener, 'deferredReturnValue').and.returnValue(123);
+ mockEvent.addListener.and.callFake(listener => {
+ savedListener = listener;
+ });
+ listener.addListener();
+ expect(mockEvent.addListener).toHaveBeenCalled();
+ expect(savedListener).not.toBeNull();
+
+ // Module not ready yet; events are queued up, and deferred value is
+ // returned synchronously.
+ let resolver = new mr.PromiseResolver();
+ spyOn(mr.Module, 'load').and.returnValue(resolver.promise);
+ let returnedValue = savedListener('foo', 1);
+ expect(returnedValue).toBe(123);
+ returnedValue = savedListener('bar', 2);
+ expect(returnedValue).toBe(123);
+ expect(mr.Module.load.calls.count()).toBe(2);
+
+ expect(mr.EventAnalytics.recordEvent)
+ .toHaveBeenCalledWith(mr.EventAnalytics.Event.DIAL_ON_ERROR);
+ expect(mr.EventAnalytics.recordEvent.calls.count()).toEqual(2);
+
+ const module = jasmine.createSpyObj('mockModule', ['handleEvent']);
+ module.handleEvent.and.callFake((e, arg1, arg2) => {
+ if (arg1 == 'bar') {
+ expect(module.handleEvent).toHaveBeenCalledWith(mockEvent, 'foo', 1);
+ expect(module.handleEvent).toHaveBeenCalledWith(mockEvent, 'bar', 2);
+ done();
+ }
+ });
+
+ // Fake loading the module.
+ resolver.resolve(module);
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/extension_selector.js b/chromium/chrome/browser/resources/media_router/extension/src/extension_selector.js
new file mode 100644
index 00000000000..652a20d0aa6
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/extension_selector.js
@@ -0,0 +1,47 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Selector for picking an MR extension to use.
+
+
+ */
+
+goog.provide('mr.ExtensionId');
+goog.provide('mr.ExtensionSelector');
+
+
+/**
+ * @enum {string}
+ */
+mr.ExtensionId = {
+ PUBLIC: 'pkedcjkdefgpdelpbcmbmeomcjbeemfm',
+ DEV: 'enhhojjnijigcajfphajepfemndkmdlo'
+};
+
+
+/**
+ * @return {Promise} Resolves if this extension should start itself,
+ * rejects otherwise.
+ */
+mr.ExtensionSelector.shouldStart = function() {
+ return new Promise((resolve, reject) => {
+ switch (window.location.host) {
+ case mr.ExtensionId.DEV:
+ resolve();
+ break;
+ case mr.ExtensionId.PUBLIC:
+ chrome.management.get(mr.ExtensionId.DEV, result => {
+ if (chrome.runtime.lastError || !result.enabled) {
+ resolve();
+ } else {
+ reject(Error('Dev extension is enabled'));
+ }
+ });
+ break;
+ default:
+ reject(Error('Unknown extension id'));
+ }
+ });
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/external_message_listener.js b/chromium/chrome/browser/resources/media_router/extension/src/external_message_listener.js
new file mode 100644
index 00000000000..3f4e0800dfa
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/external_message_listener.js
@@ -0,0 +1,80 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Listener for messages received from external apps/extensions or
+ * web pages.
+ *
+
+ */
+
+goog.provide('mr.ExternalMessageListener');
+
+goog.require('mr.EventAnalytics');
+goog.require('mr.EventListener');
+goog.require('mr.InternalMessageType');
+goog.require('mr.ModuleId');
+
+
+mr.ExternalMessageListener = class extends mr.EventListener {
+ constructor() {
+ super(
+ mr.EventAnalytics.Event.RUNTIME_ON_MESSAGE_EXTERNAL,
+ 'ExternalMessageListener', mr.ModuleId.PROVIDER_MANAGER,
+ chrome.runtime.onMessageExternal);
+ }
+
+ /**
+ * @override
+ */
+ validateEvent(message, sender, sendResponse) {
+ // Make sure all messages have a sender |id| and that the ID is whitelisted.
+ // If messages have a |tab| they are from a web page (most likely the Cast
+ // setup page).
+ if (!sender.id ||
+ mr.ExternalMessageListener.WHITELIST_.indexOf(sender.id) == -1) {
+ return false;
+ }
+
+ // Check if message type is valid.
+ return message.type == mr.InternalMessageType.START ||
+ message.type == mr.InternalMessageType.STOP ||
+ message.type == mr.InternalMessageType.SUBSCRIBE_LOG_DATA;
+ }
+
+ /**
+ * @override
+ */
+ deferredReturnValue() {
+ // Indicates the messaging channel should be kept open until
+ // sendResponse() is called.
+ return true;
+ }
+
+ /** @return {!mr.ExternalMessageListener} */
+ static get() {
+ if (!mr.ExternalMessageListener.listener_) {
+ mr.ExternalMessageListener.listener_ = new mr.ExternalMessageListener();
+ }
+ return mr.ExternalMessageListener.listener_;
+ }
+};
+
+
+/** @private {?mr.ExternalMessageListener} */
+mr.ExternalMessageListener.listener_ = null;
+
+
+/**
+ * List of app ids which are allowed to use the command messages.
+ * These must also be included in the manifest 'externally_connectable' list.
+ *
+ * @private @const {!Array<string>}
+ */
+mr.ExternalMessageListener.WHITELIST_ = [
+ // ghire kiosk app
+ 'idmofbkcelhplfjnmmdolenpigiiiecc', // prod
+ 'ggedfkijiiammpnbdadhllnehapomdge', // staging
+ 'njjegkblellcjnakomndbaloifhcoccg' // dev
+];
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/external_message_listener_test.js b/chromium/chrome/browser/resources/media_router/extension/src/external_message_listener_test.js
new file mode 100644
index 00000000000..e1f5480a27a
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/external_message_listener_test.js
@@ -0,0 +1,67 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.setTestOnly('external_message_listener_test');
+
+goog.require('mr.ExternalMessageListener');
+goog.require('mr.InternalMessageType');
+goog.require('mr.UnitTestUtils');
+
+describe('Tests mr.ExternalMessageListener', () => {
+ let listener;
+
+ beforeEach(() => {
+ mr.UnitTestUtils.mockChromeApi();
+ listener = new mr.ExternalMessageListener();
+ });
+
+ afterEach(() => {
+ mr.UnitTestUtils.restoreChromeApi();
+ });
+
+ it('rejects sender not in whitelist', () => {
+ const sender = {'id': 'invalid'};
+ const callback = response => {
+ fail('should not have called back');
+ };
+
+ expect(listener.validateEvent({}, sender, callback)).toBe(false);
+ });
+
+ it('invalid type returns empty and closes channel', () => {
+ const sender = {'id': 'njjegkblellcjnakomndbaloifhcoccg'};
+ const callback = response => {
+ fail('should not have called back');
+ };
+
+ expect(listener.validateEvent({}, sender, callback)).toBe(false);
+ });
+
+ it('valid start message', () => {
+ const sender = {'id': 'njjegkblellcjnakomndbaloifhcoccg'};
+ const callback = response => {
+ fail('should not have called back');
+ };
+ const message = {'type': mr.InternalMessageType.START};
+ expect(listener.validateEvent(message, sender, callback)).toBe(true);
+ });
+
+ it('valid stop message', () => {
+ const sender = {'id': 'njjegkblellcjnakomndbaloifhcoccg'};
+ const callback = response => {
+ fail('should not have called back');
+ };
+ const message = {'type': mr.InternalMessageType.STOP};
+ expect(listener.validateEvent(message, sender, callback)).toBe(true);
+ });
+
+ it('valid log subscription message', () => {
+ const sender = {'id': 'njjegkblellcjnakomndbaloifhcoccg'};
+ const callback = response => {
+ fail('should not have called back');
+ };
+ const message = {'type': mr.InternalMessageType.SUBSCRIBE_LOG_DATA};
+ expect(listener.validateEvent(message, sender, callback)).toBe(true);
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/externs.js b/chromium/chrome/browser/resources/media_router/extension/src/externs.js
new file mode 100644
index 00000000000..eac0a60d527
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/externs.js
@@ -0,0 +1,917 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// NOTE: Througout this file, we use constructors instead of @typedefs to
+// declare object properties. This prevents the JSCompiler from renaming these
+// properties. These can be converted to shorter @typedef declarations when the
+// JSCompiler adds full support for @typedef in externs.
+
+//////////////////////////////////////////////////////////////////////////////
+// Externs for WebRTC PeerConnection as in
+// http://www.w3.org/TR/2012/WD-webrtc-20120821/
+//////////////////////////////////////////////////////////////////////////////
+
+
+/** @type {string} */
+MediaStreamEvent.prototype.type;
+
+
+/** @type {string} */
+RTCPeerConnection.prototype.iceConnectionState;
+
+//////////////////////////////////////////////////////////////////////////////
+// Externs for processes API
+// See: https://developer.chrome.com/extensions/processes
+//////////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * @const
+ */
+chrome.processes = {};
+
+
+/**
+ * @param {number} tabId
+ * @param {Function} callback
+ */
+chrome.processes.getProcessIdForTab = function(tabId, callback) {};
+
+
+/**
+ * @const
+ */
+chrome.processes.onUpdated = {};
+
+
+/**
+ * @param {Function} callback
+ */
+chrome.processes.onUpdated.addListener = function(callback) {};
+
+
+/**
+ * @param {Function} callback
+ */
+chrome.processes.onUpdated.removeListener = function(callback) {};
+
+//////////////////////////////////////////////////////////////////////////////
+// Externs for Tab Capture API
+// See: https://developer.chrome.com/extensions/tabCapture.html
+//////////////////////////////////////////////////////////////////////////////
+
+
+/** @const {*} */
+chrome.tabCapture = {};
+
+
+/**
+ * @param {MediaConstraints} constraints
+ * @param {function(MediaStream)} callback
+ */
+chrome.tabCapture.capture = function(constraints, callback) {};
+
+
+/**
+ * @param {string} startUrl
+ * @param {MediaConstraints} constraints
+ * @param {function(MediaStream)} callback
+ */
+chrome.tabCapture.captureOffscreenTab = function(
+ startUrl, constraints, callback) {};
+
+
+/**
+ * @type {ChromeEvent}
+ */
+chrome.tabCapture.onStatusChanged;
+
+//////////////////////////////////////////////////////////////////////////////
+// Externs for Desktop Capture API
+// See: https://developer.chrome.com/extensions/desktopCapture.html
+//////////////////////////////////////////////////////////////////////////////
+
+
+/** @const */
+chrome.desktopCapture = {};
+
+
+/**
+ * @param {Array<string>} sources
+ * @param {function(string)} callback
+ * @return {number} desktopMediaRequestId
+ */
+chrome.desktopCapture.chooseDesktopMedia = function(sources, callback) {};
+
+
+/**
+ * @param {number} desktopMediaRequestId
+ */
+chrome.desktopCapture.cancelChooseDesktopMedia = function(
+ desktopMediaRequestId) {};
+
+//////////////////////////////////////////////////////////////////////////////
+// Externs for chrome.dial API
+// IDL: http://goo.gl/qmKqro
+//////////////////////////////////////////////////////////////////////////////
+
+
+/** @const {*} */
+chrome.dial = {};
+
+
+
+/** @constructor */
+chrome.dial.DialDevice = function() {};
+
+
+/** @type {string} */
+chrome.dial.DialDevice.prototype.deviceLabel;
+
+
+/** @type {string} */
+chrome.dial.DialDevice.prototype.deviceDescriptionUrl;
+
+
+/** @type {number} */
+chrome.dial.DialDevice.prototype.configId;
+
+
+/** @constructor */
+chrome.dial.DialDeviceDescription = function() {};
+
+
+/** @type {string} */
+chrome.dial.DialDeviceDescription.prototype.deviceLabel;
+
+
+/** @type {string} */
+chrome.dial.DialDeviceDescription.prototype.appUrl;
+
+
+/** @type {string} */
+chrome.dial.DialDeviceDescription.prototype.deviceDescription;
+
+
+
+/** @constructor */
+chrome.dial.DialError = function() {};
+
+
+/** @type {string} */
+chrome.dial.DialError.prototype.code;
+
+
+/**
+ * @param {function(boolean)} callback The result (true if successful).
+ */
+chrome.dial.discoverNow = function(callback) {};
+
+/**
+ * @param {string} deviceLabel
+ * @param {function(?chrome.dial.DialDeviceDescription)} callback
+ */
+chrome.dial.fetchDeviceDescription = function(deviceLabel, callback) {};
+
+
+/** @type {ChromeEvent} */
+chrome.dial.onDeviceList;
+
+
+/** @type {ChromeEvent} */
+chrome.dial.onError;
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Externs for the chrome.cast.channel API
+// IDL: http://goo.gl/G1hmAI
+//////////////////////////////////////////////////////////////////////////////
+
+
+/** @const */
+chrome.cast = chrome.cast || {};
+
+
+/** @const */
+chrome.cast.channel = {};
+
+
+
+/** @constructor */
+chrome.cast.channel.ChannelInfo = function() {};
+
+
+/** @type {number} */
+chrome.cast.channel.ChannelInfo.prototype.channelId;
+
+
+/** @type {string} */
+chrome.cast.channel.ChannelInfo.prototype.readyState;
+
+
+/** @type {?string} */
+chrome.cast.channel.ChannelInfo.prototype.errorState;
+
+
+/** @type {?boolean} */
+chrome.cast.channel.ChannelInfo.prototype.keepAlive;
+
+
+/** @type {?boolean} */
+chrome.cast.channel.ChannelInfo.prototype.audioOnly;
+
+
+/** @type {!chrome.cast.channel.ConnectInfo} */
+chrome.cast.channel.ChannelInfo.prototype.connectInfo;
+
+
+
+/** @constructor */
+chrome.cast.channel.MessageInfo = function() {};
+
+
+/** @type {string} */
+chrome.cast.channel.MessageInfo.prototype.namespace_;
+
+
+/** @type {*} */
+chrome.cast.channel.MessageInfo.prototype.data;
+
+
+/** @type {string} */
+chrome.cast.channel.MessageInfo.prototype.sourceId;
+
+
+/** @type {string} */
+chrome.cast.channel.MessageInfo.prototype.destinationId;
+
+
+
+/** @constructor */
+chrome.cast.channel.ConnectInfo = function() {};
+
+
+/** @type {string} */
+chrome.cast.channel.ConnectInfo.prototype.ipAddress;
+
+
+/** @type {number} */
+chrome.cast.channel.ConnectInfo.prototype.port;
+
+
+/** @type {string} */
+chrome.cast.channel.ConnectInfo.prototype.auth;
+
+
+/** @type {number} */
+chrome.cast.channel.ConnectInfo.prototype.timeout;
+
+
+/** @type {number} */
+chrome.cast.channel.ConnectInfo.prototype.livenessTimeout;
+
+
+/** @type {number} */
+chrome.cast.channel.ConnectInfo.prototype.pingInterval;
+
+
+
+/** @constructor */
+chrome.cast.channel.ErrorInfo = function() {};
+
+
+/** @type {string} */
+chrome.cast.channel.ErrorInfo.prototype.errorState;
+
+
+/** @type {?number} */
+chrome.cast.channel.ErrorInfo.prototype.eventType;
+
+
+/** @type {?number} */
+chrome.cast.channel.ErrorInfo.prototype.challengeReplyErrorType;
+
+
+/** @type {?number} */
+chrome.cast.channel.ErrorInfo.prototype.netReturnValue;
+
+
+/** @type {?number} */
+chrome.cast.channel.ErrorInfo.prototype.nssErrorCode;
+
+
+/**
+ * @param {!chrome.cast.channel.ConnectInfo} connectInfo
+ * @param {function(!chrome.cast.channel.ChannelInfo=)} callback
+ */
+chrome.cast.channel.open = function(connectInfo, callback) {};
+
+
+/**
+ * @param {!chrome.cast.channel.ChannelInfo} channel
+ * @param {!chrome.cast.channel.MessageInfo} message
+ * @param {function(!chrome.cast.channel.ChannelInfo=)} callback
+ */
+chrome.cast.channel.send = function(channel, message, callback) {};
+
+
+/**
+ * @param {!chrome.cast.channel.ChannelInfo} channel
+ * @param {function(!chrome.cast.channel.ChannelInfo=)} callback
+ */
+chrome.cast.channel.close = function(channel, callback) {};
+
+
+
+/** @const */
+chrome.cast.channel.onMessage;
+
+
+/**
+ * @param {!function(!chrome.cast.channel.ChannelInfo,
+ * !chrome.cast.channel.MessageInfo)} listener
+ */
+chrome.cast.channel.onMessage.addListener = function(listener) {};
+
+
+/**
+ * @param {!function(!chrome.cast.channel.ChannelInfo,
+ * !chrome.cast.channel.MessageInfo)} listener
+ */
+chrome.cast.channel.onMessage.removeListener = function(listener) {};
+
+
+/** @const */
+chrome.cast.channel.onError;
+
+
+/**
+ * @param {!function(!chrome.cast.channel.ChannelInfo,
+ * (!chrome.cast.channel.ErrorInfo|undefined))} listener
+ */
+chrome.cast.channel.onError.addListener = function(listener) {};
+
+
+/**
+ * @param {!function(!chrome.cast.channel.ChannelInfo,
+ * (!chrome.cast.channel.ErrorInfo|undefined))} listener
+ */
+chrome.cast.channel.onError.removeListener = function(listener) {};
+
+
+/** @const */
+chrome.cast.media = {};
+
+
+/**
+ * @param {function(ChromeWindow)} callback
+ */
+chrome.browserAction.openPopup = function(callback) {};
+
+//////////////////////////////////////////////////////////////////////////////
+// Externs for the chrome.cast.streaming APIs
+// See: http://goo.gl/yInHUU
+//////////////////////////////////////////////////////////////////////////////
+
+
+/** @const {*} */
+chrome.cast.streaming = {};
+
+
+/** @const {*} */
+chrome.cast.streaming.session = {};
+
+
+/**
+ * @param {?MediaStreamTrack} audio
+ * @param {?MediaStreamTrack} video
+ * @param {!function(?number, ?number, !number)} callback
+ */
+chrome.cast.streaming.session.create = function(audio, video, callback) {};
+
+
+/** @const {*} */
+chrome.cast.streaming.rtpStream = {};
+
+
+
+/** @constructor */
+chrome.cast.streaming.rtpStream.CodecSpecificParams = function() {};
+
+
+
+/** @constructor */
+chrome.cast.streaming.rtpStream.RtpPayloadParams = function() {};
+
+
+/** @type {number} */
+chrome.cast.streaming.rtpStream.RtpPayloadParams.prototype.maxLatency;
+
+
+/** @type {number} */
+chrome.cast.streaming.rtpStream.RtpPayloadParams.prototype.minLatency;
+
+
+/** @type {number} */
+chrome.cast.streaming.rtpStream.RtpPayloadParams.prototype.animatedLatency;
+
+
+/** @type {number} */
+chrome.cast.streaming.rtpStream.RtpPayloadParams.prototype.payloadType;
+
+
+/** @type {string} */
+chrome.cast.streaming.rtpStream.RtpPayloadParams.prototype.codecName;
+
+
+/** @type {number} */
+chrome.cast.streaming.rtpStream.RtpPayloadParams.prototype.ssrc;
+
+
+/** @type {number} */
+chrome.cast.streaming.rtpStream.RtpPayloadParams.prototype.feedbackSsrc;
+
+
+/** @type {number} */
+chrome.cast.streaming.rtpStream.RtpPayloadParams.prototype.clockRate;
+
+
+/** @type {number} */
+chrome.cast.streaming.rtpStream.RtpPayloadParams.prototype.minBitrate;
+
+
+/** @type {number} */
+chrome.cast.streaming.rtpStream.RtpPayloadParams.prototype.maxBitrate;
+
+
+/** @type {number} */
+chrome.cast.streaming.rtpStream.RtpPayloadParams.prototype.channels;
+
+
+/** @type {number} */
+chrome.cast.streaming.rtpStream.RtpPayloadParams.prototype.maxFrameRate;
+
+
+/** @type {number} */
+chrome.cast.streaming.rtpStream.RtpPayloadParams.prototype.width;
+
+
+/** @type {number} */
+chrome.cast.streaming.rtpStream.RtpPayloadParams.prototype.height;
+
+
+/** @type {string} */
+chrome.cast.streaming.rtpStream.RtpPayloadParams.prototype.aesKey;
+
+
+/** @type {string} */
+chrome.cast.streaming.rtpStream.RtpPayloadParams.prototype.aesIvMask;
+
+
+/** @type {Array.<chrome.cast.streaming.rtpStream.CodecSpecificParams>} */
+chrome.cast.streaming.rtpStream.RtpPayloadParams.prototype.codecSpecificParams;
+
+
+
+/** @constructor */
+chrome.cast.streaming.rtpStream.RtpParams = function() {};
+
+
+/** @type {chrome.cast.streaming.rtpStream.RtpPayloadParams} */
+chrome.cast.streaming.rtpStream.RtpParams.prototype.payload;
+
+
+/** @type {Array.<string>} */
+chrome.cast.streaming.rtpStream.RtpParams.prototype.rtcpFeatures;
+
+
+/**
+ * @param {!number} streamId
+ */
+chrome.cast.streaming.rtpStream.destroy = function(streamId) {};
+
+
+/**
+ * @param {!number} streamId
+ * @return {!Array.<!chrome.cast.streaming.rtpStream.RtpParams>}
+ */
+chrome.cast.streaming.rtpStream.getSupportedParams = function(streamId) {
+ return [new chrome.cast.streaming.rtpStream.RtpParams];
+};
+
+
+/**
+ * @param {!number} streamId
+ * @param {!chrome.cast.streaming.rtpStream.RtpParams} params
+ */
+chrome.cast.streaming.rtpStream.start = function(streamId, params) {};
+
+
+/**
+ * @param {!number} streamId
+ */
+chrome.cast.streaming.rtpStream.stop = function(streamId) {};
+
+
+/**
+ * @param {!number} streamId
+ * @param {!boolean} enable
+ */
+chrome.cast.streaming.rtpStream.toggleLogging = function(streamId, enable) {};
+
+
+/**
+ * @param {!number} streamId
+ * @param {!string} extraData
+ * @param {!function(ArrayBuffer)} callback
+ */
+chrome.cast.streaming.rtpStream.getRawEvents = function(
+ streamId, extraData, callback) {};
+
+
+/**
+ * @param {!number} streamId
+ * @param {!function(Object)} callback
+ */
+chrome.cast.streaming.rtpStream.getStats = function(streamId, callback) {};
+
+
+/** @const {*} */
+chrome.cast.streaming.rtpStream.onStarted = {};
+
+
+/**
+ * @param {!function(!number)} listener
+ */
+chrome.cast.streaming.rtpStream.onStarted.addListener = function(listener) {};
+
+
+/**
+ * @param {!function(!number)} listener
+ */
+chrome.cast.streaming.rtpStream.onStarted.removeListener = function(listener) {
+};
+
+
+/** @const {*} */
+chrome.cast.streaming.rtpStream.onStopped = {};
+
+
+/**
+ * @param {!function(!number)} listener
+ */
+chrome.cast.streaming.rtpStream.onStopped.addListener = function(listener) {};
+
+
+/**
+ * @param {!function(!number)} listener
+ */
+chrome.cast.streaming.rtpStream.onStopped.removeListener = function(listener) {
+};
+
+
+/** @const {*} */
+chrome.cast.streaming.rtpStream.onError = {};
+
+
+/**
+ * @param {!function(number, string)} listener
+ */
+chrome.cast.streaming.rtpStream.onError.addListener = function(listener) {};
+
+
+/**
+ * @param {!function(number, string)} listener
+ */
+chrome.cast.streaming.rtpStream.onError.removeListener = function(listener) {};
+
+
+
+/** @constructor */
+chrome.cast.streaming.udpTransport.IPEndPoint = function() {};
+
+
+/** @type {string} */
+chrome.cast.streaming.udpTransport.IPEndPoint.prototype.address;
+
+
+/** @type {number} */
+chrome.cast.streaming.udpTransport.IPEndPoint.prototype.port;
+
+
+/**
+ * @param {!number} streamId
+ */
+chrome.cast.streaming.udpTransport.destroy = function(streamId) {};
+
+
+/** @const {*} */
+chrome.cast.streaming.udpTransport = {};
+
+
+/**
+ * @param {!number} transportId
+ * @param {!chrome.cast.streaming.udpTransport.IPEndPoint} ipEndPoint
+ */
+chrome.cast.streaming.udpTransport.setDestination = function(
+ transportId, ipEndPoint) {};
+
+
+/**
+ * @param {!number} transportId
+ * @param {!Object} options
+ */
+chrome.cast.streaming.udpTransport.setOptions = function(transportId, options) {
+};
+
+
+/** @const {*} */
+chrome.mojoPrivate = {};
+
+
+/**
+ * @param {!string} moduleName
+ * @return {!Promise.<T>}
+ * @template T
+ */
+chrome.mojoPrivate.requireAsync = function(moduleName) {
+ return Promise.resolve(Object.create(null));
+};
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Externs for UMA analytics
+//////////////////////////////////////////////////////////////////////////////
+
+
+/** @const {*} */
+chrome.metricsPrivate = {};
+
+
+/**
+ * Records an elapsed time of no more than 10 seconds. The sample value is
+ * specified in milliseconds.
+ * @param {string} metricName
+ * @param {number} value
+ */
+chrome.metricsPrivate.recordTime = function(metricName, value) {};
+
+
+/**
+ * Records an elapsed time of no more than 3 minutes. The sample value is
+ * specified in milliseconds.
+ * @param {string} metricName
+ * @param {number} value
+ */
+chrome.metricsPrivate.recordMediumTime = function(metricName, value) {};
+
+
+/**
+ * Records an elapsed time of no more than 1 hour. The sample value is
+ * specified in milliseconds.
+ * @param {string} metricName
+ * @param {number} value
+ */
+chrome.metricsPrivate.recordLongTime = function(metricName, value) {};
+
+
+/**
+ * Records an action performed by the user.
+ * @param {string} name
+ */
+chrome.metricsPrivate.recordUserAction = function(name) {};
+
+
+/**
+ * Records a value than can range from 1 to 100.
+ * @param {string} metricName
+ * @param {number} count
+ */
+chrome.metricsPrivate.recordSmallCount = function(metricName, count) {};
+
+
+/**
+ * Returns true if the user opted in to sending crash reports.
+ * @param {!function(boolean)} callback
+ */
+chrome.metricsPrivate.getIsCrashReportingEnabled = function(callback) {};
+
+
+/**
+ * Describes the type of metric that is to be collected.
+ * @typedef {{
+ * metricName: string,
+ * type: string,
+ * min: number,
+ * max: number,
+ * buckets: number
+ * }}
+ */
+var MetricType;
+
+
+/**
+ * Adds a value to the given metric.
+ * @param {MetricType} metricType
+ * @param {number} value
+ */
+chrome.metricsPrivate.recordValue = function(metricType, value) {};
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Externs subset for settingsPrivate API.
+// see chromium/src/third_party/closure_compiler/externs/settings_private.js
+//////////////////////////////////////////////////////////////////////////////
+
+
+/** @const {*} */
+chrome.settingsPrivate = {};
+
+
+/**
+ * @enum {string}
+ */
+chrome.settingsPrivate.PrefType = {
+ BOOLEAN: 'BOOLEAN',
+ NUMBER: 'NUMBER',
+ STRING: 'STRING',
+ URL: 'URL',
+ LIST: 'LIST',
+ DICTIONARY: 'DICTIONARY',
+};
+
+
+/**
+ * @typedef {{
+ * key: string,
+ * type: !chrome.settingsPrivate.PrefType,
+ * value: *,
+ * }}
+ */
+chrome.settingsPrivate.PrefObject;
+
+
+/**
+ * @param {string} name
+ * @param {function(!chrome.settingsPrivate.PrefObject):void} callback
+ */
+chrome.settingsPrivate.getPref = function(name, callback) {};
+
+
+/**
+ * @const
+ */
+chrome.settingsPrivate.onPrefsChanged = {};
+
+
+/**
+ * @param {function(!Array<!Object>):void} callback
+ */
+chrome.settingsPrivate.onPrefsChanged.hasListener = function(callback) {};
+
+
+/**
+ * @param {function(!Array<!Object>):void} callback
+ */
+chrome.settingsPrivate.onPrefsChanged.addListener = function(callback) {};
+
+
+/**
+ * @param {function(!Array<!Object>):void} callback
+ */
+chrome.settingsPrivate.onPrefsChanged.removeListener = function(callback) {};
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Externs for Google Calendar v3 API as in
+// developers.google.com/google-apps/calendar/v3/reference/events#resource
+//////////////////////////////////////////////////////////////////////////////
+
+
+/** @const */
+chrome.cast.calendar = {};
+
+
+
+/** @constructor */
+chrome.cast.calendar.Calendar = function() {};
+
+
+/** @type {string} */
+chrome.cast.calendar.Calendar.prototype.id;
+
+
+
+/** @constructor */
+chrome.cast.calendar.Event = function() {};
+
+
+/** @type {string} */
+chrome.cast.calendar.Event.prototype.summary;
+
+
+/** @type {string} */
+chrome.cast.calendar.Event.prototype.hangoutLink;
+
+
+/**
+ * @typedef {{
+ * date: string,
+ * dateTime: string
+ * }}
+ */
+chrome.cast.calendar.Event.prototype.start;
+
+
+/**
+ * @typedef {{
+ * date: string,
+ * dateTime: string
+ * }}
+ */
+chrome.cast.calendar.Event.prototype.end;
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Externs for Google Hangouts v1 API
+//////////////////////////////////////////////////////////////////////////////
+
+
+/** @const */
+chrome.cast.hangout = {};
+
+
+/**
+ * @typedef {{
+ * 'service': (string|undefined),
+ * 'value': (string|undefined)
+ * }}
+ */
+chrome.cast.hangout.ExternalKey;
+
+
+/**
+ * @typedef {{
+ * 'hangout_id': (string|undefined),
+ * 'participant_id': (string|undefined),
+ * 'user_id': (string|undefined),
+ * 'display_name': (string|undefined),
+ * 'role': (string|undefined),
+ * 'client_type': (string|undefined),
+ * 'participant_state': (string|undefined),
+ * 'joined': (boolean|undefined)
+ * }}
+ */
+chrome.cast.hangout.Participant;
+
+
+/**
+ * @typedef {{
+ * 'hangout_id': (string|undefined),
+ * 'type': (string|undefined),
+ * 'external_key': (chrome.cast.hangout.ExternalKey|undefined),
+ * 'company_title': (string|undefined),
+ * 'meeting_room_name': (string|undefined),
+ * 'meeting_domain': (string|undefined),
+ * "sharing_url": (string|undefined),
+ * }}
+ */
+chrome.cast.hangout.Hangout;
+
+
+
+/**
+
+ * @see https://developer.chrome.com/extensions/tabs#event-onUpdated
+ * @constructor
+ */
+function TabChangeInfo() {}
+
+
+/** @type {string} */
+TabChangeInfo.prototype.status;
+
+
+/** @type {string} */
+TabChangeInfo.prototype.url;
+
+
+/** @type {boolean} */
+TabChangeInfo.prototype.pinned;
+
+
+/** @type {boolean} */
+TabChangeInfo.prototype.audible;
+
+
+/** @type {string} */
+TabChangeInfo.prototype.favIconUrl;
+
+//////////////////////////////////////////////////////////////////////////////
+// Externs for declarativeWebRequest (not used except for channel checking)
+//////////////////////////////////////////////////////////////////////////////
+
+
+/** @type {Object} */
+chrome.declarativeWebRequest;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/files.gni b/chromium/chrome/browser/resources/media_router/extension/src/files.gni
new file mode 100644
index 00000000000..3770bbca1cd
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/files.gni
@@ -0,0 +1,145 @@
+# Generated file. Do not modify!
+
+mr_module_names = [
+ "common",
+ "mirroring_common",
+ "background_script",
+]
+mr_module_specs = [
+ "module=common:8",
+ "module=mirroring_common:73:common",
+ "module=background_script:4:mirroring_common",
+]
+mr_module_files = [
+ # common files
+ "config.js",
+ "utils/assertions.js",
+ "utils/logger.js",
+ "utils/analytics.js",
+ "utils/promise_resolver.js",
+ "providers/common/retry.js",
+ "utils/xhr_manager.js",
+ "internal_message.js",
+
+ # mirroring_common files
+ "manager/route_message_port.js",
+ "interface_data/issue.js",
+ "manager/cancellable_promise.js",
+ "mirror_services/mirror_analytics.js",
+ "utils/platform_utils.js",
+ "mirror_services/mirror_config.js",
+ "mirror_services/stream_capture/capture_parameters.js",
+ "mirror_services/stream_capture/mirror_media_stream.js",
+ "module.js",
+ "utils/event_analytics.js",
+ "utils/media_source_utils.js",
+ "mirror_services/mirror_service.js",
+ "mirror_services/mirror_service_name.js",
+ "mirror_services/mirror_activity.js",
+ "utils/tab_utils.js",
+ "mirror_services/mirror_session.js",
+ "mirror_services/mirror_settings.js",
+ "webrtc/messages.js",
+ "webrtc/peer_connection_analytics.js",
+ "webrtc/peer_connection.js",
+ "interface_data/sink.js",
+ "persistent_data.js",
+ "utils/base64.js",
+ "utils/sha1.js",
+ "utils/string_utils.js",
+ "providers/common/sink_utils.js",
+ "providers/dial/sink_app_status.js",
+ "providers/dial/dial_sink.js",
+ "event_listener.js",
+ "utils/device_counts.js",
+ "utils/device_counts_provider.js",
+ "utils/promise_utils.js",
+ "providers/common/id_generator.js",
+ "interface_data/media_route_controller.js",
+ "utils/mojo_utils.js",
+ "manager/route_id.js",
+ "interface_data/route.js",
+ "manager/provider.js",
+ "providers/common/runtime_error_utils.js",
+ "interface_data/route_request_error.js",
+ "interface_data/sink_list.js",
+ "manager/presentation_enums.js",
+ "manager/sink_availability.js",
+ "providers/dial/dial_analytics.js",
+ "providers/dial/dial_provider_callbacks.js",
+ "utils/event_target.js",
+ "manager/provider_manager_callbacks.js",
+ "providers/common/net_utils.js",
+ "providers/common/xhr_utils.js",
+ "providers/dial/dial_client.js",
+ "providers/dial/dial_activity.js",
+ "providers/dial/dial_activity_records.js",
+ "providers/dial/dial_sink_discovery_service.js",
+ "providers/dial/dial_app_discovery_service.js",
+ "providers/dial/presentation_url.js",
+ "providers/dial/dial_provider.js",
+ "interface_data/sink_search_criteria.js",
+ "utils/fixed_size_queue.js",
+ "log_manager.js",
+ "utils/object_utils.js",
+ "presentation_services/presentation_session.js",
+ "presentation_services/cloud_webrtc/webrtc_presentation_session.js",
+ "external_message_listener.js",
+ "internal_message_listener.js",
+ "init_helper.js",
+ "interface_data/route_message.js",
+ "interface_data/mojo.js",
+ "manager/mr_event_senders/throttling_sender.js",
+ "manager/mr_event_senders/route_message_sender.js",
+ "manager/provider_events.js",
+ "manager/route_message_port_impl.js",
+ "utils/throttle.js",
+ "manager/provider_manager.js",
+
+ # background_script files
+ "extension_selector.js",
+ "providers/test/test_provider.js",
+ "init.js",
+ "background.js",
+]
+mr_test_files = [
+ "event_listener_test.js",
+ "external_message_listener_test.js",
+ "init_test.js",
+ "interface_data/media_route_controller_test.js",
+ "internal_message_listener_test.js",
+ "manager/mr_event_senders/route_message_sender_test.js",
+ "manager/mr_event_senders/throttling_sender_test.js",
+ "manager/provider_manager_test.js",
+ "manager/route_id_test.js",
+ "mirror_services/mirror_activity_test.js",
+ "mirror_services/mirror_analytics_test.js",
+ "mirror_services/mirror_session_test.js",
+ "mirror_services/mirror_settings_test.js",
+ "mirror_services/stream_capture/mirror_media_stream_test.js",
+ "module_test.js",
+ "persistent_data_test.js",
+ "providers/common/id_generator_test.js",
+ "providers/common/net_utils_test.js",
+ "providers/dial/dial_activity_records_test.js",
+ "providers/dial/dial_analytics_test.js",
+ "providers/dial/dial_app_discovery_service_test.js",
+ "providers/dial/dial_client_test.js",
+ "providers/dial/dial_provider_test.js",
+ "providers/dial/dial_sink_discovery_service_test.js",
+ "providers/dial/dial_sink_test.js",
+ "providers/dial/presentation_url_test.js",
+ "providers/test/test_provider_test.js",
+ "utils/analytics_test.js",
+ "utils/base64_test.js",
+ "utils/event_analytics_test.js",
+ "utils/fixed_size_queue_test.js",
+ "utils/logger_test.js",
+ "utils/media_source_utils_test.js",
+ "utils/mock_promise_test.js",
+ "utils/object_utils_test.js",
+ "utils/sha1_test.js",
+ "utils/throttle_test.js",
+ "utils/xhr_manager_test.js",
+ "webrtc/peer_connection_test.js",
+]
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/init.js b/chromium/chrome/browser/resources/media_router/extension/src/init.js
new file mode 100644
index 00000000000..6e6140e6c94
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/init.js
@@ -0,0 +1,157 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.provide('mr.Init');
+
+goog.require('mr.Config');
+goog.require('mr.ExtensionSelector');
+goog.require('mr.InitHelper');
+goog.require('mr.LogManager');
+goog.require('mr.Logger');
+goog.require('mr.MediaRouterService');
+goog.require('mr.MediumTiming');
+goog.require('mr.PersistentDataManager');
+goog.require('mr.ProviderManager');
+goog.require('mr.TestProvider');
+
+
+/** @private {mr.Logger} */
+mr.Init.logger_ = mr.Logger.getInstance('mr.Init');
+
+
+/** @type {string} */
+mr.Init.FIRST_WAKE_DURATION = 'MediaRouter.Provider.FirstWakeDuration';
+
+
+/** @type {string} */
+mr.Init.WAKE_DURATION = 'MediaRouter.Provider.WakeDuration';
+
+
+/** @private {?mr.MediumTiming} */
+mr.Init.wakeTiming_;
+
+
+/** @private {?mr.ProviderManager} */
+mr.Init.providerManager_;
+
+
+/**
+ * @param {!mr.ProviderManager} providerManager
+ * @return {!Array.<!mr.Provider>}
+ * @private
+ */
+mr.Init.getProviders_ = function(providerManager) {
+ const providers = mr.InitHelper.getProviders(providerManager);
+ if (!mr.Config.isPublicChannel) {
+ providers.push(new mr.TestProvider(providerManager));
+ }
+ return providers;
+};
+
+
+/**
+ * @return {!Promise}
+ * @private
+ */
+mr.Init.initProviderManager_ = function() {
+ return mr.ExtensionSelector.shouldStart()
+ .then(mr.MediaRouterService.getInstance)
+ .then(result => {
+ if (!result['mrService']) {
+ throw Error('Failed to get MR service');
+ }
+ const mrInstanceId = result['mrInstanceId'];
+ if (!mrInstanceId) {
+ throw Error('Failed to get MR instance ID.');
+ }
+ mr.Init.logger_.info('MR instance ID: ' + mrInstanceId);
+ const mediaRouterService =
+ /** @type {!mr.MediaRouterService} */ (result['mrService']);
+ if (!mr.Init.providerManager_) {
+ throw Error('providerManager not initialized.');
+ }
+ /** @type {!mr.ProviderManager} */
+ const providerManager = mr.Init.providerManager_;
+ mediaRouterService.setHandlers(providerManager);
+
+ if (mr.PersistentDataManager.isChromeReloaded(mrInstanceId)) {
+ mr.Init.wakeTiming_.setName(mr.Init.FIRST_WAKE_DURATION);
+ }
+ chrome.runtime.onSuspend.addListener(
+ mr.Init.wakeTiming_.end.bind(mr.Init.wakeTiming_));
+
+ mr.PersistentDataManager.initialize(mrInstanceId);
+
+ mr.LogManager.getInstance().registerDataManager();
+
+ const providers = mr.Init.getProviders_(providerManager);
+ if (!mr.Config.isDebugChannel) {
+ // Log unhandled promise rejections for external channels,
+ // but leave them as thrown exceptions for internal.
+ window.addEventListener('unhandledrejection', event => {
+ let e = event.reason;
+ if (!e.stack) {
+ e = Error(e);
+ }
+ mr.Init.logger_.error(
+ 'Unhandled promise rejection.',
+ /** @type {Error} */ (e));
+ });
+ }
+ providerManager.initialize(
+ mediaRouterService, providers, result['mrConfig']);
+ })
+ .then(undefined, error => {
+ mr.Init.logger_.warning(error.message);
+ throw error;
+ });
+};
+
+
+/**
+ * Exposed for testing.
+
+ * @return {!Array<!mr.EventListener>}
+ * @private
+ */
+mr.Init.getAllListeners_ = function() {
+ return [
+ ...mr.InitHelper.getListeners(),
+ ];
+};
+
+
+/**
+ * Registers all event listeners.
+ * @private
+ */
+mr.Init.addEventListeners_ = function() {
+ mr.Init.getAllListeners_().forEach(
+ eventListener => eventListener.addOnStartup());
+ mr.InitHelper.addEventListeners();
+
+ // Listen for an event that always get invoked on browser startup. This is
+ // necessary because Media Router must know the extension ID in order to wake
+ // the extension up, and MR gets the ID when the event page activates for the
+ // first time. If the event page never activates, then MR will never be able
+ // to connect to it.
+
+ chrome.runtime.onStartup.addListener(() => {});
+};
+
+
+/**
+ * @return {!Promise}
+ */
+mr.Init.init = function() {
+ mr.LogManager.getInstance().init();
+ mr.Init.wakeTiming_ = new mr.MediumTiming(mr.Init.WAKE_DURATION);
+
+ /** @type {!mr.ProviderManager} */
+ const providerManager = new mr.ProviderManager();
+ mr.Init.providerManager_ = providerManager;
+ const promise = mr.Init.initProviderManager_();
+ mr.Init.addEventListeners_();
+ return promise;
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/init_helper.js b/chromium/chrome/browser/resources/media_router/extension/src/init_helper.js
new file mode 100644
index 00000000000..320629a8547
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/init_helper.js
@@ -0,0 +1,61 @@
+/**
+ * @fileoverview Definitions that are specific to the open-source
+ * version of the extension. The functions here don't do anything
+ * useful, but they need to be here because they're called by code
+ * that's shared with the closed-source version.
+ */
+goog.module('mr.InitHelper');
+goog.module.declareLegacyNamespace();
+
+const DialProvider = goog.require('mr.DialProvider');
+const EventListener = goog.require('mr.EventListener');
+const Provider = goog.require('mr.Provider');
+const ProviderManager = goog.forwardDeclare('mr.ProviderManager');
+
+
+/**
+ * @param {!ProviderManager} providerManager
+ * @return {!Array<!Provider>}
+ */
+function getProviders(providerManager) {
+ return [new DialProvider(providerManager)];
+}
+
+
+/**
+ * @return {!Array<!EventListener>}
+ */
+function getListeners() {
+ return [];
+}
+
+
+/**
+ * @return {void}
+ */
+function addEventListeners() {}
+
+
+/**
+ * @return {!Function}
+ */
+function getInternalMessageHandler() {
+ return () => {};
+}
+
+
+/**
+ * @return {!Function}
+ */
+function getExternalMessageHandler() {
+ return () => {};
+}
+
+
+exports = {
+ getProviders,
+ getListeners,
+ addEventListeners,
+ getInternalMessageHandler,
+ getExternalMessageHandler,
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/init_test.js b/chromium/chrome/browser/resources/media_router/extension/src/init_test.js
new file mode 100644
index 00000000000..25ba7d36419
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/init_test.js
@@ -0,0 +1,82 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.setTestOnly('init_test');
+
+goog.require('mr.Init');
+goog.require('mr.MockClock');
+goog.require('mr.Module');
+goog.require('mr.UnitTestUtils');
+
+
+
+describe('Tests init', function() {
+ let mockClock;
+ let savedCallback;
+
+ beforeEach(function() {
+ mr.Module.clearForTest();
+ mockClock = new mr.MockClock(true);
+ mr.UnitTestUtils.mockChromeApi();
+
+ for (let listener of mr.Init.getAllListeners_()) {
+ spyOn(listener, 'addOnStartup').and.callThrough();
+ }
+
+ spyOn(mr.ExtensionSelector, 'shouldStart')
+ .and.returnValue(Promise.resolve());
+ spyOn(mr.MediaRouterService, 'getInstance').and.returnValue({
+ 'mrService': jasmine.createSpyObj(
+ 'mrService', ['setHandlers', 'onRouteMessagesReceived']),
+ 'mrInstanceId': 'mrInstanceId'
+ });
+
+ spyOn(mr.PersistentDataManager, 'initialize');
+ spyOn(mr.PersistentDataManager, 'register');
+ spyOn(mr.Init, 'getProviders_').and.returnValue([]);
+
+ savedCallback = null;
+ chrome.runtime.onSuspend.addListener.and.callFake(callback => {
+ savedCallback = callback;
+ });
+ });
+
+ afterEach(function() {
+ mockClock.uninstall();
+ mr.UnitTestUtils.restoreChromeApi();
+ });
+
+ it('records first wake duration after Chrome reload', function(done) {
+ spyOn(mr.PersistentDataManager, 'isChromeReloaded').and.returnValue(true);
+ mr.Init.init().then(() => {
+ expect(chrome.runtime.onSuspend.addListener).toHaveBeenCalled();
+ expect(savedCallback).not.toBeNull();
+ mockClock.tick(12345);
+ savedCallback();
+ expect(chrome.metricsPrivate.recordMediumTime)
+ .toHaveBeenCalledWith(mr.Init.FIRST_WAKE_DURATION, 12345);
+ done();
+ });
+ });
+
+ it('records wake duration after Chrome reload', function(done) {
+ spyOn(mr.PersistentDataManager, 'isChromeReloaded').and.returnValue(false);
+ mr.Init.init().then(() => {
+ expect(chrome.runtime.onSuspend.addListener).toHaveBeenCalled();
+ expect(savedCallback).not.toBeNull();
+ mockClock.tick(54321);
+ savedCallback();
+ expect(chrome.metricsPrivate.recordMediumTime)
+ .toHaveBeenCalledWith(mr.Init.WAKE_DURATION, 54321);
+ done();
+ });
+ });
+
+ it('Registers event listeners on bootstrap', function(done) {
+ mr.Init.init().then(done, done.fail);
+ for (let listener of mr.Init.getAllListeners_()) {
+ expect(listener.addOnStartup).toHaveBeenCalled();
+ }
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/interface_data/issue.js b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/issue.js
new file mode 100644
index 00000000000..c0da74b3fd1
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/issue.js
@@ -0,0 +1,150 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview The issue object shared between component extension
+ * and Chrome media router.
+ */
+
+goog.provide('mr.Issue');
+goog.provide('mr.IssueAction');
+goog.provide('mr.IssueSeverity');
+
+goog.require('mr.Assertions');
+
+/**
+ * Note: keep synced with the issue severity supported by MR's issue manager.
+ * @enum {string}
+ * @export
+ */
+mr.IssueSeverity = {
+ FATAL: 'fatal',
+ WARNING: 'warning',
+ NOTIFICATION: 'notification'
+};
+
+
+/**
+ * Note: keep synced with the issue actions supported by MR's issue manager.
+ * @enum {string}
+ * @export
+ */
+mr.IssueAction = {
+ DISMISS: 'dismiss',
+ LEARN_MORE: 'learn_more'
+};
+
+mr.Issue = class {
+ /**
+ * @param {string} title
+ * @param {mr.IssueSeverity} severity
+ */
+ constructor(title, severity) {
+ /**
+ * @type {?string}
+ * @export
+ */
+ this.routeId = null;
+
+ /**
+ * @type {mr.IssueSeverity}
+ * @export
+ */
+ this.severity = severity;
+
+ /**
+ * When true, this issue takes the whole dialog.
+ * @type {boolean}
+ * @export
+ */
+ this.isBlocking = this.severity == mr.IssueSeverity.FATAL ? true : false;
+
+ /**
+ * Short description about the issue. Localized string.
+ * @type {string}
+ * @export
+ */
+ this.title = title;
+
+ /**
+ * Message about issue detail or how to handle issue.
+ * Messages should be suitable for end users to decide which actions to
+ * take.
+ * @type {?string}
+ * @export
+ */
+ this.message = null;
+
+ /**
+ * @type {mr.IssueAction}
+ * @export
+ */
+ this.defaultAction = mr.IssueAction.DISMISS;
+
+ /**
+ * @type {Array.<mr.IssueAction>}
+ * @export
+ */
+ this.secondaryActions = null;
+
+ /**
+ * Required if one action is LEARN_MORE.
+ * @type {?number}
+ * @export
+ */
+ this.helpPageId = null;
+ }
+
+ /**
+ * Sets the action to LEARN_MORE and sets the pageId that is required by the
+ * action for targeting.
+ * @param {number} pageId
+ * @return {!mr.Issue} This object.
+ */
+ setDefaultActionLearnMore(pageId) {
+ mr.Assertions.assert(pageId > 0);
+ this.helpPageId = pageId;
+ this.defaultAction = mr.IssueAction.LEARN_MORE;
+ return this;
+ }
+
+ /**
+ * @param {Array.<mr.IssueAction>} secondaryActions
+ * @return {!mr.Issue} This object.
+ */
+ setSecondaryActions(secondaryActions) {
+ this.secondaryActions = secondaryActions;
+ return this;
+ }
+
+ /**
+ * @param {string} message
+ * @return {!mr.Issue} This object.
+ */
+ setMessage(message) {
+ this.message = message;
+ return this;
+ }
+
+ /**
+ * @param {boolean} isBlocking
+ * @return {!mr.Issue} This object.
+ */
+ setIsBlocking(isBlocking) {
+ if (!isBlocking && this.severity == mr.IssueSeverity.FATAL) {
+ throw Error('All FATAL issues must be blocking.');
+ }
+ this.isBlocking = isBlocking;
+ return this;
+ }
+
+ /**
+ * @param {string} routeId
+ * @return {!mr.Issue} This object.
+ */
+ setRouteId(routeId) {
+ this.routeId = routeId;
+ return this;
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/interface_data/media_route_controller.js b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/media_route_controller.js
new file mode 100644
index 00000000000..0b9494b4194
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/media_route_controller.js
@@ -0,0 +1,166 @@
+goog.module('mr.MediaRouteController');
+goog.module.declareLegacyNamespace();
+
+const Logger = goog.require('mr.Logger');
+
+
+/**
+ * Controls a media that is being routed. This base class defines the same set
+ * of APIs as the MediaController Mojo interface defined in
+ * media_controller.mojom in Chromium, and contains a MojoBinding to the
+ * browser (so that MediaController commands from the browser are routed here).
+ * MRPs may extend this class with additional commands.
+ */
+const MediaRouteController = class {
+ /**
+ * @param {string} name Name of the controller (e.g. HangoutsRouteController).
+ * @param {!mojo.InterfaceRequest} controllerRequest The Mojo interface
+ * request to be bound to this controller.
+ * @param {!mojo.MediaStatusObserverPtr} observer The Mojo pointer to the
+ * MediaStatusObserver.
+ */
+ constructor(name, controllerRequest, observer) {
+ /** @protected @const {!Logger} */
+ this.logger = Logger.getInstance('mr.MediaRouteController.' + name);
+
+ /**
+ * The binding used to maintain a Mojo connection with the browser process.
+ * @private {?mojo.Binding}
+ */
+ this.binding_ =
+ new mojo.Binding(mojo.MediaController, this, controllerRequest);
+
+ /**
+ * The observer to send media status updates to.
+ * @private {?mojo.MediaStatusObserverPtr}
+ */
+ this.observer_ = observer;
+
+ /**
+ * @protected @const {!mojo.MediaStatus}
+ */
+ this.currentMediaStatus = new mojo.MediaStatus({
+ title: '',
+ description: '',
+ duration: new mojo.TimeDelta({microseconds: 0}),
+ current_time: new mojo.TimeDelta({microseconds: 0})
+ });
+
+ /**
+ * @private {boolean}
+ */
+ this.disposed_ = false;
+
+ this.binding_.setConnectionErrorHandler(
+ this.onMojoConnectionError.bind(this));
+ this.observer_.ptr.setConnectionErrorHandler(
+ this.onMojoConnectionError.bind(this));
+ }
+
+ /**
+ * Drops the reference to the binding.
+ * @protected
+ */
+ onMojoConnectionError() {
+ this.dispose();
+ this.onControllerInvalidated();
+ }
+
+ /**
+ * Closes the connection in the binding and the observer. There will be no
+ * more incoming or outgoing calls after this.
+ * @final
+ */
+ dispose() {
+ if (this.disposed_) {
+ return;
+ }
+ this.disposed_ = true;
+ this.disposeInternal();
+ if (this.binding_) {
+ this.binding_.close();
+ this.binding_ = null;
+ }
+ if (this.observer_) {
+ this.observer_.ptr.reset();
+ this.observer_ = null;
+ }
+ }
+
+ /**
+ * Notifies the observer of media status update, if it exists.
+ * @protected
+ */
+ notifyObserver() {
+ if (this.observer_) {
+ this.observer_.onMediaStatusUpdated(this.currentMediaStatus);
+ }
+ }
+
+ /**
+ * Performs additional cleanup when the controller is being disposed.
+ * @protected
+ */
+ disposeInternal() {}
+
+ /**
+ * Performs final cleanup after the controller is invalidated by a Mojo
+ * connection error. By the time this is called, dispose() has already
+ * happened. This gives an opportunity for clients to drop their references
+ * to the controller.
+ * @protected
+ */
+ onControllerInvalidated() {}
+
+ // The following are methods for handling incoming media commands. The method
+ // names must match the ones defined in media_controller.mojom in Chromium
+ // and be exported.
+
+ /**
+ * Plays the media.
+ * @export
+ */
+ play() {}
+
+ /**
+ * Pauses the media.
+ * @export
+ */
+ pause() {}
+
+ /**
+ * Mutes or unmutes the media.
+ * @param {boolean} mute
+ * @export
+ */
+ setMute(mute) {}
+
+ /**
+ * Sets the volume on the media. The given volume must be between 0 and 1.
+ * @param {number} volume
+ * @export
+ */
+ setVolume(volume) {}
+
+ /**
+ * Seeks to the given time. The given time must be non-negative and less than
+ * or equal to the duration of the media.
+ * @param {!mojo.TimeDelta} time
+ * @export
+ */
+ seek(time) {}
+
+ /**
+ * Binds the given request to an implementation that accepts Hangouts-specific
+ * commands.
+ * @param {!mojo.InterfaceRequest} hangoutsControllerRequest Interface request
+ * for Hangouts controller.
+ * @export
+ */
+ connectHangoutsMediaRouteController(hangoutsControllerRequest) {
+ hangoutsControllerRequest.close();
+ throw new Error('Not implemented');
+ }
+};
+
+exports = MediaRouteController;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/interface_data/media_route_controller_test.js b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/media_route_controller_test.js
new file mode 100644
index 00000000000..b0fd150f7e3
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/media_route_controller_test.js
@@ -0,0 +1,70 @@
+goog.module('mr.MediaRouteControllerTest');
+goog.setTestOnly('mr.MediaRouteControllerTest');
+
+const MediaRouteController = goog.require('mr.MediaRouteController');
+const UnitTestUtils = goog.require('mr.UnitTestUtils');
+
+describe('MediaRouteController test', () => {
+ let binding;
+ let observer;
+
+ beforeEach(() => {
+ UnitTestUtils.mockMojoApi();
+ binding = UnitTestUtils.createMojoBindingSpyObj();
+ observer = UnitTestUtils.createMojoMediaStatusObserverSpyObj();
+ spyOn(mojo, 'Binding').and.returnValue(binding);
+ });
+
+ function createController() {
+ const controllerRequest = {};
+ const controller =
+ new MediaRouteController('TestController', controllerRequest, observer);
+ expect(mojo.Binding)
+ .toHaveBeenCalledWith(
+ mojo.MediaController, controller, controllerRequest);
+ expect(binding.setConnectionErrorHandler).toHaveBeenCalled();
+ expect(observer.ptr.setConnectionErrorHandler).toHaveBeenCalled();
+ return controller;
+ }
+
+ it('Sets things up on construction', () => {
+ createController();
+ });
+
+ it('Cleans up on Mojo connection error', () => {
+ const controller = createController();
+ spyOn(controller, 'onControllerInvalidated').and.callThrough();
+ const onMojoConnectionError =
+ binding.setConnectionErrorHandler.calls.argsFor(0)[0];
+ onMojoConnectionError();
+ expect(binding.close).toHaveBeenCalled();
+ expect(observer.ptr.reset).toHaveBeenCalled();
+ expect(controller.onControllerInvalidated.calls.count()).toBe(1);
+ });
+
+ it('Cleans up on calling dispose()', () => {
+ const controller = createController();
+ spyOn(controller, 'disposeInternal').and.callThrough();
+ controller.dispose();
+ expect(binding.close).toHaveBeenCalled();
+ expect(observer.ptr.reset).toHaveBeenCalled();
+ expect(controller.disposeInternal.calls.count()).toBe(1);
+ // Calling dispose twice has no effect.
+ controller.dispose();
+ expect(controller.disposeInternal.calls.count()).toBe(1);
+ });
+
+ it('Notifies observer', () => {
+ const controller = createController();
+ controller.notifyObserver();
+ expect(observer.onMediaStatusUpdated)
+ .toHaveBeenCalledWith(controller.currentMediaStatus);
+ });
+
+ it('Does not notify observer after cleanup', () => {
+ const controller = createController();
+ controller.dispose();
+ controller.notifyObserver();
+ expect(observer.onMediaStatusUpdated).not.toHaveBeenCalled();
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/interface_data/mojo.js b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/mojo.js
new file mode 100644
index 00000000000..3cacd0478af
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/mojo.js
@@ -0,0 +1,415 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Closure definitions of Mojo service objects.
+ */
+
+goog.provide('mr.MediaRouterRequestHandler');
+goog.provide('mr.MediaRouterService');
+
+goog.require('mr.RouteMessage');
+
+
+/**
+ * @interface
+ *
+ * Don't convert this interface to an ES6 class! Doing so causes things to
+ * break in strange ways.
+ */
+mr.MediaRouterService = function() {};
+
+
+
+/**
+ * @return {!Promise<!Object>}
+ */
+mr.MediaRouterService.getInstance = function() {
+ if (!chrome.mojoPrivate || !chrome.mojoPrivate.requireAsync) {
+ return Promise.reject(Error('No mojo service loaded'));
+ }
+ return new Promise((resolve, reject) => {
+ // requireAsync does not return failures by design, so there is no error
+ // handler here.
+ chrome.mojoPrivate.requireAsync('media_router_bindings').then(mr => {
+ const mediaRouter = /** @type {!mr.MediaRouterService} */ (mr);
+ // Import all definitions into mojo namespace.
+
+ mojo = mediaRouter.getMojoExports && mediaRouter.getMojoExports();
+ mediaRouter.start().then(result => {
+ resolve({
+ 'mrService': mediaRouter,
+
+ 'mrInstanceId': result['instance_id'] || result,
+ 'mrConfig': result['config']
+ });
+ });
+ });
+ });
+};
+
+
+/**
+ * @param {!mr.MediaRouterRequestHandler} handlers
+ * @export
+ */
+mr.MediaRouterService.prototype.setHandlers;
+
+
+/**
+ * Starts the MediaRouterService.
+ * @return {!Promise<!{
+ * instance_id: string,
+ * config: !mojo.MediaRouteProviderConfig
+ * }>} Resolved with an Object containing the result when MediaRouterService is
+ * started.
+ * @export
+ */
+mr.MediaRouterService.prototype.start;
+
+
+/**
+ * Returns an Object containing Mojo definitions exported by
+ * media_router_bindings.
+ * @return {!Object}
+ * @export
+ */
+mr.MediaRouterService.prototype.getMojoExports;
+
+
+/**
+ * @param {string} source
+ * @param {!Array.<!mr.Sink>} sinks
+ * @param {!Array<!mojo.Origin>} origins
+ * @export
+ */
+mr.MediaRouterService.prototype.onSinksReceived;
+
+
+/**
+ * @param {string} pseudoSinkId
+ * @param {string} sinkId
+ * @export
+ */
+mr.MediaRouterService.prototype.onSearchSinkIdReceived;
+
+
+/**
+ * Used by Media Router Provider Manager to inform Media Router that there is an
+ * issue.
+ * @param {!mr.Issue} issue
+ * @export
+ */
+mr.MediaRouterService.prototype.onIssue;
+
+
+/**
+ * Used by Media Router Provider Manager to inform Media Router that the list
+ * of routes has been updated.
+ * @param {Array.<mr.Route>} routes
+ * @param {string=} opt_source
+ * @param {Array.<string>=} opt_nonLocalJoinableRouteIds
+ * @export
+ */
+mr.MediaRouterService.prototype.onRoutesUpdated;
+
+
+/**
+ * Used by Media Router Provider Manager to inform Media Router that sink
+ * availability has changed.
+ * @param {!mr.SinkAvailability} availability
+ * @export
+ */
+mr.MediaRouterService.prototype.onSinkAvailabilityUpdated;
+
+
+/**
+ * Used by Media Router Provider Manager to inform Media Router that the state
+ * of a presentation connected to a route has changed.
+ * @param {string} routeId
+ * @param {string} state
+ * @export
+ */
+mr.MediaRouterService.prototype.onPresentationConnectionStateChanged;
+
+
+/**
+ * Used by Media Router Provider Manager to inform Media Router that the
+ * presentation connected to a route has closed.
+ * @param {string} routeId
+ * @param {string} reason
+ * @param {string} message
+ * @export
+ */
+mr.MediaRouterService.prototype.onPresentationConnectionClosed;
+
+
+/**
+ * Informs Media Router of route messages received from the media sink to which
+ * the route is connected.
+ * @param {string} routeId
+ * @param {!Array<!mr.RouteMessage>} messages
+ * @export
+ */
+mr.MediaRouterService.prototype.onRouteMessagesReceived;
+
+
+/**
+ * Disables or enables extension event page suspension.
+ * @param {boolean} keepAlive
+ * @export
+ */
+mr.MediaRouterService.prototype.setKeepAlive;
+
+
+/**
+ * Gets the current keep alive state.
+ * @return {boolean}
+ * @export
+ */
+mr.MediaRouterService.prototype.getKeepAlive;
+
+
+/**
+ * Informs Media Router that a MirrorServiceRemoter is created for the given
+ * tab. The Media Router may use remoterPtr to control media remoting. The Media
+ * Router should also bind sourceRequest to an implementation to receive updates
+ * on the remoting session.
+ * @param {number} tabId
+ * @param {!mojo.MirrorServiceRemoterPtr} remoterPtr
+ * @param {!mojo.InterfaceRequest} sourceRequest
+ * @export
+ */
+mr.MediaRouterService.prototype.onMediaRemoterCreated;
+
+
+/**
+ * @interface
+ */
+mr.MediaRouterRequestHandler = function() {};
+
+
+/**
+ * Will be called immediately before any other handler method is invoked.
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.onBeforeInvokeHandler;
+
+
+/**
+ * Creates a route for |mediaSource| to |sinkId|.
+ * @param {string} sourceUrn The URN of the media being displayed.
+ * @param {string} sinkId
+ * @param {string} presentationId A presentation ID to use.
+
+ * @param {!mojo.Origin|string=} origin
+ * @param {number=} tabId
+ * @param {number=} timeoutMillis If positive, the timeout to use in place
+ * of default timeout.
+ * @param {boolean=} offTheRecord If true, the request is from an off the
+ * record (incognito) browser profile.
+ * @return {!Promise<!mr.Route>} Fulfilled with route created if successful.
+ * Rejected otherwise.
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.createRoute;
+
+
+/**
+ * Joins an existing route.
+ *
+ * @param {string} sourceUrn
+ * @param {string} presentationId A presentation ID for Presentation API
+ * client; A Cast session ID for Cast join; and 'autojoin' for Cast auto Join
+ * case;
+ * @param {!mojo.Origin|string} origin
+ * @param {number} tabId
+ * @param {number=} timeoutMillis If positive, the timeout to use in place
+ * of default timeout.
+ * @param {boolean=} offTheRecord If true, the request is from an off the
+ * record (incognito) browser profile.
+ * @return {!Promise<!mr.Route>} Fulfilled with route joined if successful.
+ * Rejected otherwise.
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.joinRoute;
+
+
+/**
+ * Joins an existing route by route Id.
+ *
+ * @param {string} sourceUrn
+ * @param {string} routeId An existing route Id to join.
+ * @param {string} presentationId The presentation Id of the route being
+ * created.
+ * @param {!mojo.Origin|string} origin
+ * @param {number} tabId
+ * @param {number=} timeoutMillis If positive, the timeout to use in place
+ * of default timeout.
+ * @return {!Promise<!mr.Route>} Fulfilled with route connected if successful.
+ * Rejected otherwise.
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.connectRouteByRouteId;
+
+
+/**
+ * Terminates the route specified by |routeId|.
+ * @param {string} routeId The ID of the route to be terminated.
+ * @return {!Promise<void>} Resolved if the route was terminated, rejected
+ * otherwise.
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.terminateRoute;
+
+
+/**
+ * Starts querying for sinks capable of displaying |sourceUrn|.
+ * @param {string} sourceUrn The URN of the media.
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.startObservingMediaSinks;
+
+
+/**
+ * Stops querying for sinks capable of displaying |sourceUrn|.
+ * @param {string} sourceUrn The URN of the media.
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.stopObservingMediaSinks;
+
+
+/**
+ * Sends a message to the sink via a media route.
+ * @param {string} routeId
+ * @param {!Object|string} message The message to post. Object will be
+ * serialized to a JSON string.
+ * @param {Object=} opt_extraInfo Extra info about how to send a message.
+ * @return {!Promise} Fulfilled when the message is posted, or rejected
+ * if there an error.
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.sendRouteMessage;
+
+
+/**
+ * Sends a binary message to the sink via a media route.
+ * @param {string} routeId
+ * @param {!Uint8Array} data The binary message to send.
+ * @return {!Promise} Fulfilled when the message is sent, or rejected
+ * if there an error.
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.sendRouteBinaryMessage;
+
+
+/**
+ * Called when the MediaRouter wants to start receiving messages from the media
+ * sink for the route identified by |routeId|.
+ * @param {!string} routeId
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.startListeningForRouteMessages;
+
+
+/**
+ * Called when the MediaRouter wants to stop getting further messages
+ * associated with the routeId.
+ * @param {!string} routeId
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.stopListeningForRouteMessages;
+
+
+/**
+ * Informs the Media Router Provider Manager to send updates on routes list.
+ * @param {string} sourceUrn The URN of the media.
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.startObservingMediaRoutes;
+
+
+/**
+ * Informs the Media Router Provider Manager to stop sending updates on routes
+ * list.
+ * @param {string} sourceUrn The URN of the media.
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.stopObservingMediaRoutes;
+
+
+/**
+ * Informs the Media Router Provider Manager that a presentation connection has
+ * detached from its underlying media route due to garbage collection or
+ * explicit close().
+ * @param {!string} routeId
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.detachRoute;
+
+
+/**
+ * Enables mDNS discovery. No-ops if it is already enabled. Calling this will
+ * trigger a firewall prompt on Windows if there is not already a firewall rule
+ * for mDNS.
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.enableMdnsDiscovery;
+
+
+/**
+ * Searches the appropriate provider for a sink matching |searchCriteria| that
+ * is compatible with |sourceUrn|. The provider that is searched is chosen to be
+ * the one that owns the pseudo sink identified by |sinkId|. If any sinks are
+ * found, sinks observers for |sourceUrn| will be invoked via onSinksReceived()
+ * with those sinks included. The function will return the ID of a sink to which
+ * a route can be created or the empty string if no such sink exists.
+ *
+ * Note that returning the empty string does not necessarily mean no matching
+ * sinks were found. The provider could find multiple matching sinks and not
+ * know how to choose a single one for the route creation. The MRPM will return
+ * the sink on which the user is most likely to create the next route. However,
+ * the manager does not enforce that the provider creates at most one sink in
+ * response to a search.
+ *
+ * @param {string} sinkId Sink ID of the pseudo sink that generated the request.
+ * @param {string} sourceUrn Source to be used with the sink.
+ * @param {!mr.SinkSearchCriteria} searchCriteria Sink search criteria for the
+ * MRP's which includes the user's current domain.
+ * @return {!Promise<string>} Fulfilled with the ID of a sink to which a route
+ * can be created. Rejected otherwise.
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.searchSinks;
+
+/**
+ * Called when Media Router finishes sink discovery.
+ * @param {string} providerName Name of provider where the sinks come from.
+ * @param {!Array<!mojo.Sink>} list of sinks discovered by Media Router.
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.provideSinks;
+
+/**
+ * Updates sinks even if sinkAvailability is UNAVAILABLE to allow for query
+ * based discovery providers an opportuntity to find sinks.
+ * @param {string} sourceUrn The URN of the media.
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.updateMediaSinks;
+
+
+/**
+ * Creates and returns the MediaRouteController instance for the given route.
+ * Also sets the media status observer for the given route.
+ * Rejects if the controller cannot be created, or if the controller
+ * already exists.
+ * @param {string} routeId
+ * @param {!mojo.InterfaceRequest} controllerRequest The Mojo request object to
+ * be bound to the controller created.
+ * @param {!mojo.MediaStatusObserverPtr} observer The observer's Mojo pointer.
+ * @return {!Promise<void>}
+ * @export
+ */
+mr.MediaRouterRequestHandler.prototype.createMediaRouteController;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/interface_data/route.js b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/route.js
new file mode 100644
index 00000000000..b42e4fe42b1
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/route.js
@@ -0,0 +1,178 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview The media route data object shared between component extension
+ * and Chrome media router.
+ */
+
+goog.provide('mr.Route');
+goog.require('mr.RouteId');
+
+mr.Route = class {
+ /**
+ * @param {string} id The route ID.
+ * @param {string} presentationId The ID of the associated presentation.
+ * @param {string} sinkId The ID of the sink running this route.
+ * @param {?string} sourceUrn The media source being sent through this route.
+ * @param {boolean} isLocal Whether the route is requested locally.
+ * @param {string} description
+ * @param {?string} iconUrl
+ */
+ constructor(
+ id, presentationId, sinkId, sourceUrn, isLocal, description, iconUrl) {
+ /**
+ * @type {string}
+ * @export
+ */
+ this.id = id;
+
+ /**
+ * The ID of the presentation associated with this route.
+ * @type {string}
+ * @export
+ */
+ this.presentationId = presentationId;
+
+ /**
+ * The ID of the sink associated with this route.
+ * @type {string}
+ * @export
+ */
+ this.sinkId = sinkId;
+
+ /**
+ * The media source being sent through this route.
+ * Non-null if |isLocal| is true.
+ * For discovered routes, the media source may not be available.
+ * @type {?string}
+ * @export
+ */
+ this.mediaSource = sourceUrn;
+
+ /**
+ * Whether the route was created locally or is associated with a Cast
+ * session
+ * that was created locally.
+ * @type {boolean}
+ * @export
+ */
+ this.isLocal = isLocal;
+
+ /**
+ * @type {string}
+ * @export
+ */
+ this.description = description;
+
+ /**
+ * @type {?string}
+ * @export
+ */
+ this.iconUrl = iconUrl;
+
+ /**
+ * When false, this route cannot be stopped by the provider managing it.
+ * @type {boolean}
+ * @export
+ */
+ this.allowStop = true;
+
+ /**
+
+ * @type {?string}
+ * @export
+ */
+ this.customControllerPath = null;
+
+ /**
+
+ * @type {boolean}
+ * @export
+ */
+ this.supportsMediaRouteController = false;
+
+ /**
+ * The type of controller associated with this route, or kNone if controller
+ * is not supported for the route.
+
+ * @type {mojo.RouteControllerType}
+ * @export
+ */
+ this.controllerType =
+ mojo && mojo.RouteControllerType && mojo.RouteControllerType.kNone;
+
+ /**
+ * If set to true, this route should be displayed for |sinkId| in UI.
+ * @type {boolean}
+ * @export
+ */
+ this.forDisplay = true;
+
+ /**
+ * If true, the route was created by an off the record (incognito) browser
+ * profile.
+ * @type {boolean}
+ * @export
+ */
+ this.offTheRecord = false;
+
+ /**
+ * This field is used to identify routes that were created locally as
+ * opposed
+ * to isLocal which can identify both locally created routes and non-local
+ * routes associated with a Cast session that was created locally. The
+ * initial
+ * value of this field is the same as isLocal.
+ * @type {boolean}
+ */
+ this.createdLocally = isLocal;
+
+ /**
+ * If true, the route was created for offscreen presentation (1-UA mode).
+ * @type {boolean}
+ * @export
+ */
+ this.isOffscreenPresentation = false;
+ }
+
+ /**
+ * @param {!string} presentationId
+ * @param {!string} providerName
+ * @param {!string} sinkId The ID of the sink running this route.
+ * @param {?string} source The media source being sent through this route.
+ * @param {!boolean} isLocal Whether the route is requested locally.
+ * @param {!string} description
+ * @param {?string} iconUrl
+ * @return {!mr.Route}
+ */
+ static createRoute(
+ presentationId, providerName, sinkId, source, isLocal, description,
+ iconUrl) {
+ return new mr.Route(
+ mr.RouteId.getRouteId(presentationId, providerName, sinkId, source),
+ presentationId, sinkId, source, isLocal, description, iconUrl);
+ }
+};
+
+/**
+ * @typedef {{
+ * tabId: (?number|undefined),
+ * sessionId: string,
+ * sinkIpAddress: string,
+ * sinkModelName: string,
+ * sinkFriendlyName: (string|undefined),
+ * activity: (!mr.mirror.Activity|undefined)
+ * }}
+ */
+mr.Route.MirrorInitData;
+
+
+/**
+ * The field is only used inside component extension to pass MRP specific data
+ * to the corresponding mirroring service. For example, cast streaming needs
+ * sink IP address and session ID.
+ * @type {mr.Route.MirrorInitData}
+ */
+mr.Route.prototype.mirrorInitData;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/interface_data/route_message.js b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/route_message.js
new file mode 100644
index 00000000000..a796a271b80
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/route_message.js
@@ -0,0 +1,48 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview The route message object shared between component extension
+ * and Chrome media router.
+ */
+
+goog.provide('mr.RouteMessage');
+
+mr.RouteMessage = class {
+ /**
+ * @param {string} routeId
+ * @param {string|!Uint8Array} message
+ */
+ constructor(routeId, message) {
+ /**
+ * @type {string}
+ * @export
+ */
+ this.routeId = routeId;
+
+ /**
+ * @type {string|!Uint8Array}
+ * @export
+ */
+ this.message = message;
+ }
+
+ /**
+ * @param {!mr.RouteMessage} routeMessage
+ * @return {boolean} true if the message is in binary format.
+ */
+ static isBinary(routeMessage) {
+ return typeof routeMessage.message != 'string';
+ }
+
+ /**
+ * @param {!mr.RouteMessage} routeMessage
+ * Returns the length of the message if it is a string, otherwise 0.
+ * @return {number}
+ */
+ static stringLength(routeMessage) {
+ return mr.RouteMessage.isBinary(routeMessage) ? 0 :
+ routeMessage.message.length;
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/interface_data/route_request_error.js b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/route_request_error.js
new file mode 100644
index 00000000000..ac0092f098e
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/route_request_error.js
@@ -0,0 +1,89 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Extension of Error for route request errors.
+ */
+
+goog.provide('mr.RouteRequestError');
+goog.provide('mr.RouteRequestResultCode');
+
+
+/**
+ * Keep in sync with:
+ * - RouteRequestResultCode in media_router.mojom
+ * - RouteRequestResult::ResultCode in route_request_result.h
+ * - MediaRouteProviderResult enum in tools/metrics/histograms.xml
+ * @enum {number}
+ */
+mr.RouteRequestResultCode = {
+ UNKNOWN_ERROR: 0,
+ OK: 1,
+ TIMED_OUT: 2,
+ ROUTE_NOT_FOUND: 3,
+ SINK_NOT_FOUND: 4,
+ INVALID_ORIGIN: 5,
+ OFF_THE_RECORD_MISMATCH: 6,
+ NO_SUPPORTED_PROVIDER: 7,
+ CANCELLED: 8,
+};
+
+mr.RouteRequestError = class extends Error {
+ /**
+ * @param {!mr.RouteRequestResultCode} errorCode
+ * @param {string=} opt_message
+ * @param {string=} opt_stack
+ */
+ constructor(errorCode, opt_message, opt_stack) {
+ super();
+
+ this.name = 'RouteRequestError';
+ this.message = opt_message || '';
+ if (opt_stack) {
+ this.stack = opt_stack;
+ } else {
+ // Attempt to ensure there is a stack trace.
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, mr.RouteRequestError);
+ } else {
+ const stack = new Error().stack;
+ if (stack) {
+ this.stack = stack;
+ }
+ }
+ }
+
+ /** @type {!mr.RouteRequestResultCode} */
+ this.errorCode = errorCode;
+ }
+
+ /**
+ * If the given error is a mr.RouteRequestError, returns it. Otherwise, a
+ * returns a mr.RouteRequestError with the error's message and UNKNOWN error
+ * code.
+ * @param {*} error
+ * @return {!mr.RouteRequestError}
+ */
+ static wrap(error) {
+ if (error instanceof mr.RouteRequestError) {
+ return error;
+ } else if (error instanceof Error) {
+ return new mr.RouteRequestError(
+ mr.RouteRequestResultCode.UNKNOWN_ERROR, error.message, error.stack);
+ } else {
+ return new mr.RouteRequestError(mr.RouteRequestResultCode.UNKNOWN_ERROR);
+ }
+ }
+
+ /**
+ * @param {*} error Possibly an instance of mr.RouteRequestError.
+ * @return {boolean} True if the argument represents a timeout.
+ */
+ static isTimeout(error) {
+ if (error instanceof mr.RouteRequestError) {
+ return error.errorCode == mr.RouteRequestResultCode.TIMED_OUT;
+ }
+ return false;
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/interface_data/sink.js b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/sink.js
new file mode 100644
index 00000000000..e11b6dcc597
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/sink.js
@@ -0,0 +1,79 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.provide('mr.Sink');
+goog.provide('mr.SinkIconType');
+
+
+/**
+ * Sink icon types defined in Chrome.
+ * To define a new icon, add it to Chrome first and then here.
+ * @enum {string}
+ */
+mr.SinkIconType = {
+ CAST: 'cast',
+ CAST_AUDIO_GROUP: 'cast_audio_group',
+ CAST_AUDIO: 'cast_audio',
+ MEETING: 'meeting',
+ HANGOUT: 'hangout',
+ EDUCATION: 'education',
+ GENERIC: 'generic',
+};
+
+
+
+/**
+ * Represents a device which can be a destination for a media route.
+ */
+mr.Sink = class {
+ /**
+ * @param {string} id The ID of the sink.
+ * @param {string} friendlyName The human readable name of the sink.
+ * @param {mr.SinkIconType=} iconType
+ * @param {?string=} description The human readable description of
+ * the sink. The description is displayed below the sink name,
+ * and may contain more detailed information about the sink
+ * (e.g., meeting description).
+ * @param {?string=} domain The Dasher domain associated with the
+ * sink.
+ */
+ constructor(
+ id, friendlyName, iconType = undefined, description = null,
+ domain = null) {
+ // For some reason the current public release of jscompiler gets upset if
+ // mr.SinkIconType.GENERIC is specified as a default argument.
+ iconType = iconType || mr.SinkIconType.GENERIC;
+
+ /**
+ * @type {string}
+ * @export
+ */
+ this.id = id;
+
+ /**
+ * @type {string}
+ * @export
+ */
+ this.friendlyName = friendlyName;
+
+ /**
+ * @type {mr.SinkIconType}
+ * @export
+ */
+ this.iconType = iconType;
+
+ /**
+ * @type {?string}
+ * @export
+ */
+ this.description = description;
+
+ /**
+ * A non-null domain signifies the sink is tied to a Dasher domain.
+ * @type {?string}
+ * @export
+ */
+ this.domain = domain;
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/interface_data/sink_list.js b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/sink_list.js
new file mode 100644
index 00000000000..f430d2f0428
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/sink_list.js
@@ -0,0 +1,34 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Data structure returned by mr.Provider.getAvailableSinks.
+ */
+
+goog.provide('mr.SinkList');
+
+mr.SinkList = class {
+ /**
+ * @param {!Array<mr.Sink>} sinks
+ * @param {Array<string>=} opt_origins List of origins that have access to the
+ * sink list. If not provided, the sink list is accessible from all origins.
+ */
+ constructor(sinks, opt_origins) {
+ /**
+ * @type {!Array<!mr.Sink>}
+ * @export
+ */
+ this.sinks = sinks;
+
+ /**
+ * @type {?Array<string>}
+ * @export
+ */
+ this.origins = opt_origins || null;
+ }
+};
+
+
+/** @const {!mr.SinkList} */
+mr.SinkList.EMPTY = new mr.SinkList([]);
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/interface_data/sink_search_criteria.js b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/sink_search_criteria.js
new file mode 100644
index 00000000000..9dd8ea094af
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/interface_data/sink_search_criteria.js
@@ -0,0 +1,30 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview The sink search criteria shared between component extension and
+ * Chrome media router.
+ */
+
+goog.provide('mr.SinkSearchCriteria');
+
+mr.SinkSearchCriteria = class {
+ /**
+ * @param {string} input
+ * @param {?string} domain
+ */
+ constructor(input, domain) {
+ /**
+ * @type {string}
+ * @export
+ */
+ this.input = input;
+
+ /**
+ * @type {?string}
+ * @export
+ */
+ this.domain = domain;
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/internal_message.js b/chromium/chrome/browser/resources/media_router/extension/src/internal_message.js
new file mode 100644
index 00000000000..debdaab4d6f
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/internal_message.js
@@ -0,0 +1,62 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Internal messages between parts of the extension, e.g., event
+ * page, background page, e2e test page.
+ *
+
+ */
+
+goog.provide('mr.InternalMessage');
+goog.provide('mr.InternalMessageType');
+
+
+/**
+ * @enum {string}
+ */
+mr.InternalMessageType = {
+ // Feedback ==> event page
+ SUBSCRIBE_LOG_DATA: 'subscribe_log_data',
+ RETRIEVE_LOG_DATA: 'retrieve_log_data',
+ // Control Messages
+ // App ==> Cloud MRP
+ START: 'start',
+ STOP: 'stop',
+ // Responses
+ // Cloud MRP ==> App
+ ROUTE: 'route',
+ STOPPED: 'stopped',
+ ERROR: 'error',
+ // App ==> Log Subscribers
+ SUBSCRIBED: 'subscribed',
+ LOG_MESSAGE: 'log_message',
+};
+
+mr.InternalMessage = class {
+ /**
+ * @param {string} source ID of source of message.
+ * @param {mr.InternalMessageType} type The message type.
+ * @param {*=} opt_message
+ */
+ constructor(source, type, opt_message) {
+ /**
+ * @type {string}
+ * @export
+ */
+ this.source = source;
+
+ /**
+ * @type {mr.InternalMessageType}
+ * @export
+ */
+ this.type = type;
+
+ /**
+ * @type {*}
+ * @export
+ */
+ this.message = opt_message;
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/internal_message_listener.js b/chromium/chrome/browser/resources/media_router/extension/src/internal_message_listener.js
new file mode 100644
index 00000000000..c55869c2b4a
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/internal_message_listener.js
@@ -0,0 +1,61 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Handles internal messages that arrive in the
+ * chrome.runtime.onMessage event. It is assumed that all incoming messages have
+ * type mr.InternalMessage.
+ */
+
+goog.provide('mr.InternalMessageListener');
+
+goog.require('mr.EventAnalytics');
+goog.require('mr.EventListener');
+goog.require('mr.InternalMessage');
+goog.require('mr.InternalMessageType');
+
+
+/**
+ * @extends {mr.EventListener<ChromeEvent>}
+ */
+mr.InternalMessageListener = class extends mr.EventListener {
+ constructor() {
+ super(
+ mr.EventAnalytics.Event.RUNTIME_ON_MESSAGE, 'InternalMessageListener',
+ mr.ModuleId.PROVIDER_MANAGER, chrome.runtime.onMessage);
+ }
+
+ /**
+ * @override
+ */
+ validateEvent(message, sender, sendResponse) {
+ const internalMessage = /** @type {mr.InternalMessage} */ (message);
+ return internalMessage.type == mr.InternalMessageType.RETRIEVE_LOG_DATA &&
+ sender.id == chrome.runtime.id &&
+ sender.url == `chrome-extension://${sender.id}/feedback.html`;
+ }
+
+ /**
+ * @override
+ */
+ deferredReturnValue() {
+ // Indicates the messaging channel should be kept open until
+ // sendResponse() is called.
+ return true;
+ }
+
+ /**
+ * @return {!mr.InternalMessageListener}
+ */
+ static get() {
+ if (!mr.InternalMessageListener.listener_) {
+ mr.InternalMessageListener.listener_ = new mr.InternalMessageListener();
+ }
+ return mr.InternalMessageListener.listener_;
+ }
+};
+
+
+/** @private {?mr.InternalMessageListener} */
+mr.InternalMessageListener.listener_ = null;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/internal_message_listener_test.js b/chromium/chrome/browser/resources/media_router/extension/src/internal_message_listener_test.js
new file mode 100644
index 00000000000..f9e64ae45cb
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/internal_message_listener_test.js
@@ -0,0 +1,52 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.setTestOnly('internal_message_listener_test');
+
+goog.require('mr.InternalMessageListener');
+
+describe('Tests mr.InternalMessageListener', () => {
+ let listener;
+
+ const failCallback = response => {
+ fail('should not have called back');
+ };
+ const validEvent = {'type': mr.InternalMessageType.RETRIEVE_LOG_DATA};
+ const validSender = {
+ 'id': 'foo',
+ 'url': 'chrome-extension://foo/feedback.html'
+ };
+ beforeEach(() => {
+ chrome.runtime = {id: 'foo'};
+ listener = new mr.InternalMessageListener();
+ });
+
+ it('rejects invalid extension id', () => {
+ const sender = {
+ 'id': 'invalid',
+ 'url': 'chrome-extension://invalid/feedback.html'
+ };
+
+ const result = listener.validateEvent(validEvent, sender, failCallback);
+ expect(result).toBe(false);
+ });
+
+ it('rejects invalid extension url', () => {
+ const sender = {'id': 'foo', 'url': 'chrome-extension://foo/invalid.html'};
+
+ const result = listener.validateEvent(validEvent, sender, failCallback);
+ expect(result).toBe(false);
+ });
+
+ it('rejects invalid message type', () => {
+ const result = listener.validateEvent(
+ {'type': mr.InternalMessageType.START}, validSender, failCallback);
+ expect(result).toBe(false);
+ });
+
+ it('passes validation', () => {
+ const result = listener.validateEvent(validEvent, validSender, () => {});
+ expect(result).toBe(true);
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/log_manager.js b/chromium/chrome/browser/resources/media_router/extension/src/log_manager.js
new file mode 100644
index 00000000000..791aed8c247
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/log_manager.js
@@ -0,0 +1,241 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Log manager which enables and collects both fine and info logs,
+ * and provides method to get logs with parameter to include or exclude
+ * fine logs.
+
+ */
+
+goog.provide('mr.LogManager');
+
+goog.require('mr.Config');
+goog.require('mr.FixedSizeQueue');
+goog.require('mr.Logger');
+goog.require('mr.PersistentData');
+goog.require('mr.PersistentDataManager');
+
+
+/**
+ * @implements {mr.PersistentData}
+ */
+mr.LogManager = class {
+ constructor() {
+ /** @private @const */
+ this.buffer_ = new mr.FixedSizeQueue(mr.LogManager.BUFFER_SIZE);
+
+ /** @private @const */
+ this.startTime_ = Date.now();
+ }
+
+ /**
+ * @return {!mr.LogManager}
+ */
+ static getInstance() {
+ if (mr.LogManager.instance_ == null) {
+ mr.LogManager.instance_ = new mr.LogManager();
+ }
+ return mr.LogManager.instance_;
+ }
+
+ /**
+ * Init
+ */
+ init() {
+ mr.Logger.level = this.getDefaultLogLevel_();
+ const browserLogger = mr.Logger.getInstance('browser');
+ const oldErrorHandler = window.onerror;
+ /**
+ * @param {string} message
+ * @param {string} url
+ * @param {number} line
+ * @param {number=} col
+ * @param {*=} error
+ */
+ window.onerror = (message, url, line, col, error) => {
+ if (oldErrorHandler) {
+ oldErrorHandler(message, url, line, col, error);
+ }
+ browserLogger.error(`Error: ${message} (${url} @ Line: ${line})`, error);
+ };
+ mr.Logger.addHandler(this.onNewLog_.bind(this));
+
+ // Override log level via localStorage setting
+ const debugKey = 'debug.logs';
+ const debugLevel = window.localStorage[debugKey];
+ if (debugLevel) {
+ mr.Logger.level = mr.Logger.stringToLevel(
+ debugLevel.toUpperCase(), mr.Logger.Level.FINE);
+ } else if (!mr.Config.isPublicChannel) {
+ // Record the default local level in local settings so developers can
+ // easily change it without having to look up the name of the setting.
+ window.localStorage[debugKey] =
+ mr.Logger.levelToString(mr.Logger.DEFAULT_LEVEL);
+ }
+
+ const consoleKey = 'debug.console';
+ if (!mr.Config.isPublicChannel && window.localStorage[consoleKey] == null) {
+ // Enable console logging by default in internal builds. Any value other
+ // than 'false' or '' is treated as true.
+ window.localStorage[consoleKey] = 'true';
+ }
+ const consoleValue = window.localStorage[consoleKey];
+ if (consoleValue && consoleValue.toLowerCase() != 'false') {
+ mr.Logger.addHandler(this.logToConsole_.bind(this));
+ }
+ }
+
+ /**
+ * Saves logs in the internal buffer.
+ *
+ * @param {mr.Logger.Record} logRecord The log entry.
+ * @private
+ */
+ onNewLog_(logRecord) {
+ this.buffer_.enqueue(this.formatRecord_(logRecord, false));
+ const exception = logRecord.exception;
+ if (exception instanceof Error && exception.stack) {
+ this.buffer_.enqueue(exception.stack);
+ }
+ }
+
+ /**
+ * @param {mr.Logger.Record} logRecord The log entry.
+ * @private
+ */
+ logToConsole_(logRecord) {
+ const args = [this.formatRecord_(logRecord, true)];
+ if (logRecord.exception) {
+ args.push(logRecord.exception);
+ }
+ switch (logRecord.level) {
+ case mr.Logger.Level.SEVERE:
+ console.error(...args);
+ break;
+ case mr.Logger.Level.WARNING:
+ console.warn(...args);
+ break;
+ case mr.Logger.Level.INFO:
+ console.log(...args);
+ break;
+ default:
+ console.debug(...args);
+ }
+ }
+
+ /**
+ * @param {!mr.Logger.Record} record
+ * @param {boolean} forConsole
+ * @return {string}
+ * @private
+ */
+ formatRecord_(record, forConsole) {
+ const sb = ['['];
+ if (forConsole) {
+ // Format relative timestamp.
+ const seconds = (Date.now() - this.startTime_) / 1000;
+ sb.push((' ' + seconds.toFixed(3)).slice(-7));
+ } else {
+ // Format absolute timestamp.
+ const date = new Date(record.time);
+ const twoDigitStr = num => num < 10 ? '0' + num : num;
+ sb.push(
+ date.getFullYear().toString(), '-', twoDigitStr(date.getMonth() + 1),
+ '-', twoDigitStr(date.getDate()), ' ', twoDigitStr(date.getHours()),
+ ':', twoDigitStr(date.getMinutes()), ':',
+ twoDigitStr(date.getSeconds()), '.',
+ twoDigitStr(Math.floor(date.getMilliseconds() / 10)));
+ }
+ sb.push(
+ '][', mr.Logger.levelToString(record.level), '][', record.logger, '] ',
+ record.message);
+ // Don't append the exception when logging to the console, because it will
+ // be handled specially later.
+ if (!forConsole && record.exception != null) {
+ sb.push('\n');
+ if (record.exception instanceof Error) {
+ sb.push(record.exception.message);
+ } else {
+ try {
+ sb.push(JSON.stringify(record.exception));
+ } catch (e) {
+ sb.push(record.exception.toString());
+ }
+ }
+ }
+ sb.push('\n');
+ return sb.join('');
+ }
+
+ /**
+ * Get the logs in log buffer.
+ * @return {string}
+ */
+ getLogs() {
+ if (this.buffer_.getCount() == 0) {
+ return 'NA';
+ }
+
+ return this.buffer_.getValues().join('');
+ }
+
+ /**
+ * @return {!mr.Logger.Level} The default log level.
+ * @private
+ */
+ getDefaultLogLevel_() {
+ return mr.Config.isPublicChannel ? mr.Logger.Level.INFO :
+ mr.Logger.Level.FINE;
+ }
+
+ /**
+ * Registers with the data manager and loads any previous logs.
+ */
+ registerDataManager() {
+ mr.PersistentDataManager.register(this);
+ }
+
+ /**
+ * @override
+ */
+ getStorageKey() {
+ return 'LogManager';
+ }
+
+ /**
+ * @override
+ */
+ getData() {
+ return [this.buffer_.getValues()];
+ }
+
+ /**
+ * @override
+ */
+ loadSavedData() {
+ const currentLogs = this.buffer_.getValues();
+ this.buffer_.clear();
+ for (let log of mr.PersistentDataManager.getTemporaryData(this) || []) {
+ this.buffer_.enqueue(log);
+ }
+ for (let log of currentLogs) {
+ this.buffer_.enqueue(log);
+ }
+ }
+};
+
+
+/**
+ * @private {mr.LogManager}
+ */
+mr.LogManager.instance_ = null;
+
+
+/**
+ * The max number of logs in buffer. The old logs get pushed out when the buffer
+ * is full.
+ * @const
+ */
+mr.LogManager.BUFFER_SIZE = 1000;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/manager/cancellable_promise.js b/chromium/chrome/browser/resources/media_router/extension/src/manager/cancellable_promise.js
new file mode 100755
index 00000000000..0e0807b4575
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/manager/cancellable_promise.js
@@ -0,0 +1,255 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.CancellablePromise');
+goog.module.declareLegacyNamespace();
+
+
+/**
+ * A promise packaged together with a cancel() method.
+ *
+ * To understand cancellation, for consider a chain of ordinary Promise objects
+ * P, Q, and R, where Q is the result of calling P.then, and R is the result of
+ * calling Q.then. Whenever Q is rejected, the rejection is propagated "down"
+ * the chain to R, but P is unaffected.
+ *
+ * Now consider a chain of CancellablePromise objects P, Q, and R, created using
+ * the chain() method below. Rejection behaves the same as with ordinary
+ * Promise objects, but whenever Q is cancelled, the cancellation is propagated
+ * "up" the chain to P. Because a cancelled promise is also rejected, calling
+ * Q.cancel() also causes the rejection to be propagated to R. In this way,
+ * cancellation propagates both up and down a chain of related promises.
+ *
+ * For an explanation of how this class is used in practice (in the interaction
+ * between ProviderManager and RouteProvider classes), see the diagram in
+ * <route-creation-timeout.svg.gz>.
+ *
+ * @template T
+ */
+class CancellablePromise {
+ /**
+ * @param {function(function(T), function(*))} init Function
+ * called immediatiately with resolve and reject functions passed as
+ * arguments.
+ * @param {?function(*)=} onCancelled Optional function to be called when this
+ * CancellablePromise is cancelled.
+ */
+ constructor(init, onCancelled = null) {
+ /**
+ * @private {function(*)}
+ */
+ this.reject_;
+
+ /**
+ * @private {?function(*)}
+ */
+ this.onCancelled_ = onCancelled;
+
+ /**
+ * @const
+ */
+ this.promise = new Promise((resolve, reject) => {
+ const resolve1 = value => {
+ this.onCancelled_ = null;
+ resolve(value);
+ };
+ const reject1 = reason => {
+ this.onCancelled_ = null;
+ reject(reason);
+ };
+ this.reject_ = reject1;
+ init(resolve1, reject1);
+ });
+ }
+
+ /**
+ * Cancels this promise.
+ *
+ * Does nothing if |this.promise| is already settled. Otherwise, causes
+ * |this.promise| to be rejected with |reason|, and causes the |onHandled|
+ * function passed to this class's constructor to be called with |reason|.
+ *
+ * @param {*} reason
+ */
+ cancel(reason) {
+ this.reject_(reason);
+ if (this.onCancelled_) {
+ const onCancelled = this.onCancelled_;
+ this.onCancelled_ = null; // ensure cancel() method is idempotent
+ setTimeout(() => onCancelled(reason), 0);
+ }
+ }
+
+ /**
+ * Chains CancellablePromises together like the then() method of ordinary
+ * promises, with one extra feature: if the "child" promise returned by this
+ * method is cancelled, then this promise is automatically cancelled as well.
+ *
+ * @param {?function(T):U} onResolved Function called when this promise is
+ * resolved. The argument is the value with which this promise was
+ * resolved. If the function returns, the return value is used to resolve
+ * the child promise. If the function throws an error, the child promise
+ * is rejected with that error. If this argument is null, it is
+ * equivalent to passing a function that returns its argument.
+ * @param {?function(*):U=} onRejected Function called when this promise is
+ * rejected. The argument is the reason with which this promise was
+ * rejected. If the function returns, the return value is used to resolve
+ * the child promise. If the function throws an error, the child promise
+ * is rejected with that error. If this argument is missing or null, it
+ * is equivalent to passing a function that throws its argument.
+ * @return {!CancellablePromise<U>} A child promise which is resolved or
+ * rejected depending on the result of calling |onResolved| or
+ * |onRejected|.
+ * @template U
+ */
+ chain(onResolved, onRejected = null) {
+ return new CancellablePromise(
+ (resolve, reject) => {
+ this.promise.then(
+ value => {
+ if (onResolved) {
+ try {
+ resolve(onResolved(value));
+ } catch (reason) {
+ reject(reason);
+ }
+ } else {
+ resolve(value);
+ }
+ },
+ reason => {
+ if (onRejected) {
+ try {
+ resolve(onRejected(reason));
+ } catch (reason2) {
+ reject(reason2);
+ }
+ } else {
+ reject(reason);
+ }
+ });
+ },
+ reason => {
+ this.cancel(reason);
+ });
+ }
+
+ /**
+ * Make it Promise by expose then.
+ * @param {?function(T):U} onResolved Function called when this promise is
+ * resolved. The argument is the value with which this promise was
+ * resolved. If the function returns, the return value is used to resolve
+ * the child promise. If the function throws an error, the child promise
+ * is rejected with that error. If this argument is null, it is
+ * equivalent to passing a function that returns its argument.
+ * @param {?function(*):U=} onRejected Function called when this promise is
+ * rejected. The argument is the reason with which this promise was
+ * rejected. If the function returns, the return value is used to resolve
+ * the child promise. If the function throws an error, the child promise
+ * is rejected with that error. If this argument is missing or null, it
+ * is equivalent to passing a function that throws its argument.
+ * @return {!CancellablePromise<U>} A child promise which is resolved or
+ * rejected depending on the result of calling |onResolved| or
+ * |onRejected|.
+ * @template U
+ */
+ then(onResolved, onRejected = null) {
+ return this.chain(onResolved, onRejected);
+ }
+
+ /**
+ * Shorthand for .chain(null, onRejected).
+ * @param {?function(*):T} onRejected
+ * @return {!CancellablePromise<T>}
+ */
+ catch(onRejected) {
+ return this.chain(null, onRejected);
+ }
+
+ /**
+ * Utility function create a promise in a resolved state.
+ * @param {T} value
+ * @return {!CancellablePromise<T>}
+ * @template T
+ */
+ static resolve(value) {
+ return new CancellablePromise((resolve, reject) => {
+ resolve(value);
+ });
+ }
+
+ /**
+ * Utility function create a promise in a rejected state.
+ * @param {*} reason
+ * @return {!CancellablePromise<T>}
+ * @template T
+ */
+ static reject(reason) {
+ return new CancellablePromise((resolve, reject) => {
+ reject(reason);
+ });
+ }
+
+ /**
+ * Utility function to wrap a Promise.
+ *
+
+ *
+ * @param {!Promise<T>} promise
+ * @return {!CancellablePromise<T>}
+ * @template T
+ */
+ static forPromise(promise) {
+ return new CancellablePromise((resolve, reject) => {
+ promise.then(resolve, reject);
+ });
+ }
+
+ /**
+ * Produces a CancellablePromise |outer| that runs the following steps:
+ *
+ * In the normal case, |outer| waits for a regular (non-cancellable) Promise
+ * |promise| to resolve to |value|. Then it calls |startCancellableStep|,
+ * passing |value| as the argument, to produce a CancellablePromise |inner|.
+ * When |inner| is settled, the result is used to settle |outer|.
+ *
+ * If |outer| is cancelled before |promise| is resolved, then |value| is
+ * discarded and |startCancellableStep| is not called.
+ *
+ * If |outer| is cancelled after |promise| is resolved, then |inner| is
+ * cancelled as well.
+ *
+ * If |promise| is rejected, then |outer| is rejected as well.
+ *
+ * @param {!Promise<A>} promise
+ * @param {function(A):!CancellablePromise<B>} startCancellableStep
+ * @return {!CancellablePromise<B>}
+ * @template A, B
+ */
+ static withUncancellableStep(promise, startCancellableStep) {
+ /** @type {boolean} */
+ let wasCancelled = false;
+ /** @type {CancellablePromise} */
+ let innerPromise = null;
+
+ return new CancellablePromise(
+ (resolve, reject) => {
+ promise.then(value => {
+ if (!wasCancelled) {
+ innerPromise = startCancellableStep(value);
+ innerPromise.promise.then(resolve, reject);
+ }
+ }, reject);
+ },
+ reason => {
+ if (innerPromise) {
+ innerPromise.cancel(reason);
+ } else {
+ wasCancelled = true;
+ }
+ });
+ }
+}
+
+exports = CancellablePromise;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/manager/mr_event_senders/route_message_sender.js b/chromium/chrome/browser/resources/media_router/extension/src/manager/mr_event_senders/route_message_sender.js
new file mode 100644
index 00000000000..ca16b6a578c
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/manager/mr_event_senders/route_message_sender.js
@@ -0,0 +1,344 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview A sender for sending route message to MR. When MR asks for
+ * the next batch of messages, this sender sends matching messages if
+ * available, or buffer the request till message arrives.
+ *
+
+ */
+
+goog.provide('mr.RouteMessageSender');
+
+goog.require('mr.Assertions');
+goog.require('mr.Logger');
+goog.require('mr.PersistentData');
+goog.require('mr.PersistentDataManager');
+goog.require('mr.RouteMessage');
+goog.require('mr.ThrottlingSender');
+
+
+/**
+ * @implements {mr.PersistentData}
+ */
+mr.RouteMessageSender = class extends mr.ThrottlingSender {
+ /**
+ * @param {!mr.ProviderManagerCallbacks} providerManagerCallbacks
+ * @param {number} messageSizeKeepAliveThreshold
+ * @final
+ */
+ constructor(providerManagerCallbacks, messageSizeKeepAliveThreshold) {
+ super(mr.RouteMessageSender.SEND_MESSAGE_INTERVAL_MILLIS);
+
+ /**
+ * Route ID to queue map. One queue per route.
+ * @private {!Map<string, !Array<!mr.RouteMessage>>}
+ */
+ this.queues_ = new Map();
+
+ /**
+ * Set of routes being listened for messages.
+ * @private {!Set<string>}
+ */
+ this.listeningRouteIds_ = new Set();
+
+ /**
+ * Callback to invoke to send a batch of route messages. Set during
+ * |init()|. The first argument is the route id, and the second argument is
+ * the batch of messages to send.
+ * @private {?function(string, !Array<!mr.RouteMessage>)}
+ */
+ this.sendMessagesCallback_ = null;
+
+ /**
+ * Sum of sizes of all string messages currently queued up. When this is
+ * above a certain threshold, the extension will be kept alive due to
+ * performance reasons.
+ * @private {number}
+ */
+ this.totalMessageSize_ = 0;
+
+ /**
+ * Number of enqueued binary messages. The extension will be kept alive
+ * while there are enqueued binary messages.
+ * @private {number}
+ */
+ this.binaryMessageCount_ = 0;
+
+ /**
+ * See |totalMessageSize_| and |binaryMessageCount_|.
+ * @private {boolean}
+ */
+ this.shouldKeepAlive_ = false;
+
+ /**
+ * @private {!mr.ProviderManagerCallbacks}
+ */
+ this.providerManagerCallbacks_ = providerManagerCallbacks;
+
+ /**
+ * @private {number}
+ */
+ this.messageSizeKeepAliveThreshold_ = messageSizeKeepAliveThreshold;
+
+ /** @private {mr.Logger} */
+ this.logger_ = mr.Logger.getInstance('mr.RouteMessageSender');
+ }
+
+ /**
+ * @param {!function(string, !Array<!mr.RouteMessage>)} sendMessagesCallback
+ * The callback to invoke to send a batch of route messages. See comments
+ * on |sendMessagesCallback_|.
+ */
+ init(sendMessagesCallback) {
+ this.sendMessagesCallback_ = sendMessagesCallback;
+ mr.PersistentDataManager.register(this);
+ }
+
+ /**
+ * Starts listening for route messages associated with |routeId|, and schedule
+ * a task to send any available messages to the Media Router.
+ *
+ * @param {string} routeId
+ */
+ listenForRouteMessages(routeId) {
+ if (this.listeningRouteIds_.has(routeId)) {
+ return;
+ }
+
+ this.listeningRouteIds_.add(routeId);
+ if (this.hasMessageFrom_(routeId)) {
+ this.scheduleSend();
+ }
+ }
+
+ /**
+ * The media router wants to stop getting further messages associated with the
+ * routeId until it issues listenForRouteMessages again.
+ *
+ * @param {string} routeId
+ */
+ stopListeningForRouteMessages(routeId) {
+ this.listeningRouteIds_.delete(routeId);
+ }
+
+ /**
+ * Called when there is a |message| for |routeId| available to be sent.
+ * @param {string} routeId
+ * @param {string|!Uint8Array} message
+ */
+ send(routeId, message) {
+ let queue = this.queues_.get(routeId);
+ if (!queue) {
+ queue = [];
+ this.queues_.set(routeId, queue);
+ }
+
+ const routeMessage = new mr.RouteMessage(routeId, message);
+ queue.push(routeMessage);
+
+ // If the queue size for this route has grown suspiciously large, log
+ // warnings as the queue size grows past the warning threshold.
+ //
+
+ if (queue.length > mr.RouteMessageSender.QUEUE_SIZE_WARNING_THRESHOLD_ &&
+ queue.length % mr.RouteMessageSender.QUEUE_SIZE_WARNING_THRESHOLD_ ==
+ 1) {
+ this.logger_.warning(
+ () => `Message queue length is excessively large ` +
+ `(${queue.length}) for route ${routeId}`);
+ }
+
+ this.totalMessageSize_ += mr.RouteMessage.stringLength(routeMessage);
+ if (mr.RouteMessage.isBinary(routeMessage)) {
+ this.binaryMessageCount_++;
+ }
+
+ this.updateShouldKeepAlive_();
+ if (this.listeningRouteIds_.has(routeId)) {
+ this.scheduleSend();
+ }
+ }
+
+ /**
+ * Removes queue on route removal.
+ * @param {string} routeId
+ */
+ onRouteRemoved(routeId) {
+ this.listeningRouteIds_.delete(routeId);
+ const queue = this.queues_.get(routeId);
+ if (queue) {
+ this.queues_.delete(routeId);
+ this.onMessagesRemoved_(queue);
+ this.updateShouldKeepAlive_();
+ }
+ }
+
+ /**
+ * Update message size and binary message counters as |messages| are being
+ * removed from the queue.
+ * @param {!Array<!mr.RouteMessage>} messages
+ * @private
+ */
+ onMessagesRemoved_(messages) {
+ if (messages.length == 0) {
+ return;
+ }
+
+ for (let message of messages) {
+ this.totalMessageSize_ -= mr.RouteMessage.stringLength(message);
+ if (mr.RouteMessage.isBinary(message)) {
+ this.binaryMessageCount_--;
+ }
+ }
+ }
+
+ /**
+ * @param {string} routeId
+ * @return {boolean} True if there is at least one message from the route.
+ * @private
+ */
+ hasMessageFrom_(routeId) {
+ const queue = this.queues_.get(routeId);
+ return !!queue && queue.length > 0;
+ }
+
+ /**
+ * Computes whether the extension should be kept alive, and informs the
+ * Provider Manager if that value changed.
+ * @private
+ */
+ updateShouldKeepAlive_() {
+ const newShouldKeepAlive = this.binaryMessageCount_ > 0 ||
+ this.totalMessageSize_ > this.messageSizeKeepAliveThreshold_;
+ if (newShouldKeepAlive != this.shouldKeepAlive_) {
+ this.shouldKeepAlive_ = newShouldKeepAlive;
+ this.providerManagerCallbacks_.requestKeepAlive(
+ this.getStorageKey(), newShouldKeepAlive);
+ }
+ }
+
+ /**
+ * @override
+ */
+ doSend() {
+ if (!this.sendMessagesCallback_) {
+ this.logger_.error(
+ 'sendMessagesCallback not set. Messages not delivered.');
+ return;
+ }
+
+ for (const routeId of this.listeningRouteIds_) {
+ const queue = this.queues_.get(routeId);
+ if (!queue || (queue.length == 0)) {
+ continue;
+ }
+ this.sendMessagesCallback_(routeId, queue);
+ this.onMessagesRemoved_(queue);
+ this.queues_.set(routeId, []);
+ }
+ this.updateShouldKeepAlive_();
+ }
+
+ /**
+ * @override
+ */
+ getStorageKey() {
+ return 'mr.RouteMessageSender';
+ }
+
+ /**
+ * @override
+ */
+ getData() {
+ // Assumption: While there are binary messages in any queue, the extension
+ // keep-alive should be turned on. Thus, we should not encounter binary
+ // messages while persisting the queues here.
+ const persistableQueues = [...this.queues_.entries()].map(entry => {
+ return [
+ entry[0],
+ entry[1].map(
+ message => mr.Assertions.assertString(
+ message.message, 'No support for persisting binary messages'))
+ ];
+ });
+ return [new mr.RouteMessageSender.PersistentData_(
+ persistableQueues, Array.from(this.listeningRouteIds_),
+ this.totalMessageSize_)];
+ }
+
+ /**
+ * @override
+ */
+ loadSavedData() {
+ const savedData = /** @type {?mr.RouteMessageSender.PersistentData_} */
+ (mr.PersistentDataManager.getTemporaryData(this));
+ if (savedData) {
+ this.queues_ = new Map();
+ for (const entry of savedData.queues) {
+ const routeId = /** @type {string} */ (entry[0]);
+ // Assumption: In getData(), there should not have been any binary
+ // messages persisted. Therefore, only string messages should be
+ // restored here.
+ const queue = (/** @type {!Array<*>} */ (entry[1])).map(message => {
+ return new mr.RouteMessage(
+ routeId,
+ mr.Assertions.assertString(
+ message, 'No support for restoring binary messages'));
+ });
+ this.queues_.set(routeId, queue);
+ }
+ this.listeningRouteIds_ = new Set(savedData.listeningRouteIds);
+ this.totalMessageSize_ = savedData.totalMessageSize;
+ }
+ }
+};
+
+
+/**
+ * The interval at which messages will be sent back to Media Router.
+ * @const {number}
+ */
+mr.RouteMessageSender.SEND_MESSAGE_INTERVAL_MILLIS = 20;
+
+
+/**
+ * Generally, no route should have more than 50 messages in queue. However,
+ * there may be momentary spikes for high-volume communications (e.g., RPC
+ * traffic).
+ * @private @const {number}
+ */
+mr.RouteMessageSender.QUEUE_SIZE_WARNING_THRESHOLD_ = 50;
+
+
+/**
+ * If the total number of characters in all enqueued string messages exceeds
+ * this threshold, the extension will be kept alive for performance reasons.
+ * @const {number}
+ */
+mr.RouteMessageSender.MESSAGE_SIZE_KEEP_ALIVE_THRESHOLD = 512 * 1024;
+
+
+/**
+ * @private
+ */
+mr.RouteMessageSender.PersistentData_ = class {
+ /**
+ * @param {!Array<Array<*>>} queues An array where each element is an
+ * 2-element array of [routeId, messages].
+ * @param {!Array<string>} listeningRouteIds
+ * @param {number} totalMessageSize
+ */
+ constructor(queues, listeningRouteIds, totalMessageSize) {
+ /** @type {!Array<Array<*>>} */
+ this.queues = queues;
+
+ /** @type {!Array<string>} */
+ this.listeningRouteIds = listeningRouteIds;
+
+ /** @type {number} */
+ this.totalMessageSize = totalMessageSize;
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/manager/mr_event_senders/route_message_sender_test.js b/chromium/chrome/browser/resources/media_router/extension/src/manager/mr_event_senders/route_message_sender_test.js
new file mode 100644
index 00000000000..31b885e82dc
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/manager/mr_event_senders/route_message_sender_test.js
@@ -0,0 +1,173 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.setTestOnly();
+goog.require('mr.MockClock');
+goog.require('mr.PersistentDataManager');
+goog.require('mr.RouteMessage');
+goog.require('mr.RouteMessageSender');
+goog.require('mr.UnitTestUtils');
+
+
+
+describe('Tests RouteMessageSender', function() {
+ let mockClock;
+ let providerManagerCallbacks;
+ let callback;
+ let sender;
+ const routeId1 = 'r1';
+ const routeId2 = 'r2';
+ const text1 = 'message1';
+ const messageSizeThreshold = 100;
+
+ beforeEach(function() {
+ mr.UnitTestUtils.mockChromeApi();
+ chrome.runtime.onSuspend.addListener = l => {
+ onSuspendListener = l;
+ };
+
+ mr.PersistentDataManager.clear();
+ mockClock = new mr.MockClock(true);
+ providerManagerCallbacks =
+ jasmine.createSpyObj('pmCallbacks', ['requestKeepAlive']);
+ sender = new mr.RouteMessageSender(
+ providerManagerCallbacks, messageSizeThreshold);
+ callback = jasmine.createSpy('sendCallback');
+ sender.init(callback);
+ });
+
+ afterEach(function() {
+ mr.PersistentDataManager.clear();
+ mockClock.uninstall();
+ mr.UnitTestUtils.restoreChromeApi();
+ });
+
+ it('hasMessageFrom_', function() {
+ expect(sender.hasMessageFrom_(routeId1)).toBe(false);
+ sender.send(routeId1, text1);
+ expect(sender.hasMessageFrom_(routeId1)).toBe(true);
+ });
+
+ it('No msg when requested; new msg sent when arrives', function() {
+ sender.listenForRouteMessages(routeId1);
+ sender.send(routeId1, text1);
+ expect(callback).toHaveBeenCalledWith(
+ routeId1, [new mr.RouteMessage(routeId1, text1)]);
+ expect(sender.hasMessageFrom_(routeId1)).toBe(false);
+ });
+
+ it('Has msg when requested', function() {
+ sender.send(routeId1, text1);
+ expect(callback).not.toHaveBeenCalled();
+
+ sender.listenForRouteMessages(routeId1);
+ expect(callback).toHaveBeenCalledWith(
+ routeId1, [new mr.RouteMessage(routeId1, text1)]);
+ mockClock.tick(mr.RouteMessageSender.SEND_MESSAGE_INTERVAL_MILLIS);
+ expect(sender.hasMessageFrom_(routeId1)).toBe(false);
+ });
+
+ it('onRouteRemoved removes messages', function() {
+ sender.send(routeId1, text1);
+ expect(sender.hasMessageFrom_(routeId1)).toBe(true);
+ sender.onRouteRemoved(routeId1);
+ expect(sender.hasMessageFrom_(routeId1)).toBe(false);
+
+ expect(sender.totalMessageSize_).toEqual(0);
+ expect(sender.binaryMessageCount_).toEqual(0);
+ expect(sender.shouldKeepAlive_).toBe(false);
+ });
+
+ it('stopListeningForRouteMessages does not remove messages', function() {
+ sender.send(routeId1, text1);
+ expect(sender.hasMessageFrom_(routeId1)).toBe(true);
+ sender.stopListeningForRouteMessages(routeId1);
+ expect(sender.hasMessageFrom_(routeId1)).toBe(true);
+ });
+
+ it('requestKeepAlive due to binary message', function() {
+ const binaryArray1 = new Uint8Array(12);
+ const binaryArray2 = new Uint8Array(34);
+ sender.send(routeId1, binaryArray1);
+ expect(providerManagerCallbacks.requestKeepAlive)
+ .toHaveBeenCalledWith(sender.getStorageKey(), true);
+ providerManagerCallbacks.requestKeepAlive.calls.reset();
+
+ sender.send(routeId2, binaryArray2);
+ expect(providerManagerCallbacks.requestKeepAlive).not.toHaveBeenCalled();
+
+ sender.listenForRouteMessages(routeId1);
+ mockClock.tick(mr.RouteMessageSender.SEND_MESSAGE_INTERVAL_MILLIS);
+ expect(callback).toHaveBeenCalledWith(
+ routeId1, [new mr.RouteMessage(routeId1, binaryArray1)]);
+ expect(providerManagerCallbacks.requestKeepAlive).not.toHaveBeenCalled();
+
+ sender.listenForRouteMessages(routeId2);
+ mockClock.tick(mr.RouteMessageSender.SEND_MESSAGE_INTERVAL_MILLIS);
+ expect(callback).toHaveBeenCalledWith(
+ routeId2, [new mr.RouteMessage(routeId2, binaryArray2)]);
+ expect(providerManagerCallbacks.requestKeepAlive)
+ .toHaveBeenCalledWith(sender.getStorageKey(), false);
+
+ expect(sender.totalMessageSize_).toEqual(0);
+ expect(sender.binaryMessageCount_).toEqual(0);
+ expect(sender.shouldKeepAlive_).toBe(false);
+ });
+
+ it('requestKeepAlive due to message size threshold', function() {
+ const message1 = 'a'.repeat(messageSizeThreshold / 2);
+ const message2 = 'b'.repeat(messageSizeThreshold / 2 + 1);
+
+ sender.send(routeId1, message1);
+ expect(providerManagerCallbacks.requestKeepAlive).not.toHaveBeenCalled();
+
+ sender.send(routeId2, message2);
+ expect(providerManagerCallbacks.requestKeepAlive)
+ .toHaveBeenCalledWith(sender.getStorageKey(), true);
+ providerManagerCallbacks.requestKeepAlive.calls.reset();
+
+ sender.listenForRouteMessages(routeId1);
+ mockClock.tick(mr.RouteMessageSender.SEND_MESSAGE_INTERVAL_MILLIS);
+ expect(callback).toHaveBeenCalledWith(
+ routeId1, [new mr.RouteMessage(routeId1, message1)]);
+ expect(providerManagerCallbacks.requestKeepAlive)
+ .toHaveBeenCalledWith(sender.getStorageKey(), false);
+
+ sender.listenForRouteMessages(routeId2);
+ mockClock.tick(mr.RouteMessageSender.SEND_MESSAGE_INTERVAL_MILLIS);
+ expect(callback).toHaveBeenCalledWith(
+ routeId2, [new mr.RouteMessage(routeId2, message2)]);
+
+ expect(sender.totalMessageSize_).toEqual(0);
+ expect(sender.binaryMessageCount_).toEqual(0);
+ expect(sender.shouldKeepAlive_).toBe(false);
+ });
+
+ it('saves pending messages, then restores and sends them', function() {
+ const textMessage = 'this is a text message';
+ const textMessage2 = 'this is another text message';
+
+ // Queue up the messages in the RouteMessageSender. They will not be sent
+ // because listenForRouteMessages() has not been called yet.
+ sender.send(routeId1, textMessage);
+ sender.send(routeId1, textMessage2);
+
+ // Persists pending messages.
+ mr.PersistentDataManager.suspendForTest();
+
+ // Re-creating a new RouteMessageSender restores the pending messages.
+ sender = new mr.RouteMessageSender(
+ providerManagerCallbacks, messageSizeThreshold);
+ sender.init(callback);
+
+ // Now, call listenForRouteMessages() and the restored pending messages
+ // should be processed.
+ sender.listenForRouteMessages(routeId1);
+ mockClock.tick(mr.RouteMessageSender.SEND_MESSAGE_INTERVAL_MILLIS);
+ expect(callback).toHaveBeenCalledWith(routeId1, [
+ new mr.RouteMessage(routeId1, textMessage),
+ new mr.RouteMessage(routeId1, textMessage2),
+ ]);
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/manager/mr_event_senders/throttling_sender.js b/chromium/chrome/browser/resources/media_router/extension/src/manager/mr_event_senders/throttling_sender.js
new file mode 100644
index 00000000000..730959a9b91
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/manager/mr_event_senders/throttling_sender.js
@@ -0,0 +1,89 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview An abstract throttling sender. It sends right away if there is
+ * no sending in the past 'interval' time. Otherwise, it waits till 'interval'
+ * is passed.
+ *
+
+ */
+
+goog.provide('mr.ThrottlingSender');
+
+
+/**
+ * Note: Strictly speaking, this class should implement PersistentData to store
+ * lastSendTime_. But since we never specify an interval large enough for the
+ * extension to become suspended in between, it is not needed in practice.
+ */
+mr.ThrottlingSender = class {
+ /**
+ * @param {number} interval in milliseconds.
+ */
+ constructor(interval) {
+ /** @private {number} */
+ this.interval_ = interval;
+
+ /** @private {?number} */
+ this.lastSendTime_ = null;
+
+ /** @private {?number} */
+ this.timerId_ = null;
+ }
+
+ /**
+ * Clears the sender timer and sets it to null.
+ * @private
+ */
+ clearTimer_() {
+ if (this.timerId_ != null) {
+ clearTimeout(this.timerId_);
+ this.timerId_ = null;
+ }
+ }
+
+ /**
+ * Schedule a send.
+ * @protected
+ */
+ scheduleSend() {
+ if (this.timerId_ != null) {
+ return;
+ }
+ if (this.lastSendTime_ == null ||
+ Date.now() - this.lastSendTime_ >= this.interval_) {
+ // Send right away
+ this.send_();
+ } else {
+ // Delay a while
+ const delay =
+ Math.max(this.lastSendTime_ + this.interval_ - Date.now(), 5);
+ this.timerId_ = setTimeout(this.send_.bind(this), delay);
+ }
+ }
+
+ /**
+ * Sends messages immediately.
+ */
+ sendImmediately() {
+ this.send_();
+ }
+
+ /**
+ * Sends right away and schedule another send if there is more to send.
+ * @private
+ */
+ send_() {
+ this.clearTimer_();
+ this.doSend();
+ this.lastSendTime_ = Date.now();
+ }
+
+ /**
+ * Sends the message if available.
+ * @protected
+ */
+ doSend() {}
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/manager/mr_event_senders/throttling_sender_test.js b/chromium/chrome/browser/resources/media_router/extension/src/manager/mr_event_senders/throttling_sender_test.js
new file mode 100644
index 00000000000..fd3de97a161
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/manager/mr_event_senders/throttling_sender_test.js
@@ -0,0 +1,69 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.setTestOnly();
+goog.require('mr.MockClock');
+goog.require('mr.ThrottlingSender');
+
+describe('Tests ThrottlingSender', function() {
+ let mockClock;
+ let sender;
+ const interval = 50;
+ let numOfMessages;
+
+ beforeEach(function() {
+ mockClock = new mr.MockClock(true);
+ sender = new mr.ThrottlingSender(interval);
+ numOfMessages = 0;
+ sender.doSend = jasmine.createSpy('doSend').and.callFake(() => {
+ numOfMessages--;
+ });
+ });
+
+ afterEach(function() {
+ mockClock.uninstall();
+ });
+
+ it('first msg is sent right away', function() {
+ numOfMessages++;
+ sender.scheduleSend();
+ expect(sender.doSend.calls.count()).toEqual(1);
+ expect(numOfMessages).toEqual(0);
+ mockClock.tick(interval);
+ expect(sender.doSend.calls.count()).toEqual(1);
+ expect(numOfMessages).toEqual(0);
+ });
+
+ it('messages are throttled', function() {
+ numOfMessages++;
+ sender.scheduleSend();
+ expect(sender.doSend.calls.count()).toEqual(1);
+ expect(numOfMessages).toEqual(0);
+
+ numOfMessages++;
+ sender.scheduleSend();
+ expect(sender.doSend.calls.count()).toEqual(1);
+ expect(numOfMessages).toEqual(1);
+
+ mockClock.tick(interval);
+ expect(sender.doSend.calls.count()).toEqual(2);
+ expect(numOfMessages).toEqual(0);
+ });
+
+ it('messages are sent gradually 1', function() {
+ numOfMessages++;
+ sender.scheduleSend();
+ expect(sender.doSend.calls.count()).toEqual(1);
+ mockClock.tick(interval / 2);
+ expect(sender.doSend.calls.count()).toEqual(1);
+
+ numOfMessages++;
+ sender.scheduleSend();
+ expect(sender.doSend.calls.count()).toEqual(1);
+ mockClock.tick(interval / 2);
+ expect(sender.doSend.calls.count()).toEqual(2);
+ mockClock.tick(interval);
+ expect(sender.doSend.calls.count()).toEqual(2);
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/manager/presentation_enums.js b/chromium/chrome/browser/resources/media_router/extension/src/manager/presentation_enums.js
new file mode 100644
index 00000000000..3ce170b66ea
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/manager/presentation_enums.js
@@ -0,0 +1,35 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Defines presentation connection related enums.
+ */
+
+goog.provide('mr.PresentationConnectionCloseReason');
+goog.provide('mr.PresentationConnectionState');
+
+
+/**
+ * Presentation connection states. Keep in sync with PresentationConnection.idl
+ * in the Chromium code base.
+ *
+ * @enum {string}
+ */
+mr.PresentationConnectionState = {
+ CONNECTED: 'connected',
+ TERMINATED: 'terminated',
+ CLOSED: 'closed'
+};
+
+
+/**
+ * Keep in sync with PresentationConnectionCloseEvent.idl in Chromium code base.
+ *
+ * @enum {string}
+ */
+mr.PresentationConnectionCloseReason = {
+ ERROR: 'error',
+ CLOSED: 'closed',
+ WENT_AWAY: 'went_away'
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/manager/provider.js b/chromium/chrome/browser/resources/media_router/extension/src/manager/provider.js
new file mode 100644
index 00000000000..fe0217eff71
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/manager/provider.js
@@ -0,0 +1,258 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview API for media route providers.
+ *
+
+ */
+
+goog.provide('mr.Provider');
+goog.provide('mr.ProviderName');
+
+goog.require('mr.CancellablePromise');
+
+/**
+ * @enum {string}
+ */
+mr.ProviderName = {
+ CAST: 'cast',
+ DIAL: 'dial',
+ CLOUD: 'cloud',
+ TEST: 'test'
+};
+
+
+
+/**
+ * @record
+ */
+mr.Provider = class {
+ /**
+ * Gets provider name.
+ * @return {string}
+ */
+ getName() {}
+
+ /**
+ * Called to send a message to the sink via a media route.
+ * @param {string} routeId
+ * @param {!Object|string} message The message to send. Object will be
+ * serialized to a JSON string.
+ * @param {Object=} opt_extraInfo Extra info about how to send a message.
+ * @return {!Promise<void>} Fulfilled when the message is posted, or rejected
+ * if there an error.
+ */
+ sendRouteMessage(routeId, message, opt_extraInfo) {}
+
+ /**
+ * Called to send a binary message to the sink via a media route.
+ * @param {string} routeId
+ * @param {!Uint8Array} data The message to send.
+ * @return {!Promise<void>} Fulfilled when the message is posted, or rejected
+ * if there an error.
+ */
+ sendRouteBinaryMessage(routeId, data) {}
+
+ /**
+ * Called the first time the provider is loaded.
+ * Should do any one-time initialization (i.e. register event filters, etc.)
+ * @param {!mojo.MediaRouteProviderConfig=} config The config object from the
+ * browser to initialize the provider with.
+ */
+ initialize(config = undefined) {}
+
+ /**
+ * Queries this provider for existing routes.
+ *
+ * @return {!Array.<!mr.Route>}
+ */
+ getRoutes() {}
+
+ /**
+ * Queries this provider for available sinks.
+ *
+ * @param {string} sourceUrn
+ * @return {!mr.SinkList}
+ */
+ getAvailableSinks(sourceUrn) {}
+
+ /**
+ * Starts querying for sinks capable of displaying |sourceUrn|.
+ * @param {string} sourceUrn The URN of the media.
+ */
+ startObservingMediaSinks(sourceUrn) {}
+
+ /**
+ * Stops querying for sinks capable of displaying |sourceUrn|.
+ * @param {string} sourceUrn The URN of the media.
+ */
+ stopObservingMediaSinks(sourceUrn) {}
+
+ /**
+ * Informs the provider to send updates on routes list.
+ * @param {string} sourceUrn The URN of the media.
+ */
+ startObservingMediaRoutes(sourceUrn) {}
+
+ /**
+ * Informs the provider to stop sending updates on routes list.
+ * @param {string} sourceUrn The URN of the media.
+ */
+ stopObservingMediaRoutes(sourceUrn) {}
+
+ /**
+ * Queries this provider for a sink by id.
+ *
+ * @param {string} sinkId
+ * @return {?mr.Sink}
+ * Null if no sink exists with sinkId.
+ */
+ getSinkById(sinkId) {}
+
+ /**
+ * Creates a new route to the sink.
+ * @param {string} sourceUrn
+ * @param {string} sinkId The ID of the target sink.
+ * @param {string} presentationId A presentation ID to use.
+ * @param {boolean} offTheRecord True if the request is from an
+ * off the record (incognito) browser profile.
+ * @param {number} timeoutMillis Request timeout in milliseconds.
+ * @param {string=} opt_origin
+ * @param {number=} opt_tabId
+ * @return {!mr.CancellablePromise<!mr.Route>} Fulfilled with route created if
+ * successful. Rejected otherwise.
+ */
+ createRoute(
+ sourceUrn, sinkId, presentationId, offTheRecord, timeoutMillis,
+ opt_origin, opt_tabId) {}
+
+ /**
+ * Terminates the media route owned by this provider.
+ * @param {string} routeId The media route id.
+ * @return {!Promise} Fulfilled when route is terminated, or rejected with
+ * an error.
+ */
+ terminateRoute(routeId) {}
+
+ /**
+ * Creates and computes mirror settings appropriate for the given sink (and
+ * the sender's capabilities). See class comments for mr.mirror.Settings when
+ * overriding this method. Returns null if this provider does not support the
+ * sink.
+ *
+ * The provider must return valid, frozen settings if
+ * provider.canRoute(sourceUrn, sinkId) is true.
+ *
+ * @param {string} sinkId The ID of the sink to mirror to.
+ * @return {!mr.mirror.Settings}
+ */
+ getMirrorSettings(sinkId) {}
+
+ /**
+ * Gets the name of the best mirror service supported by this provider
+ * on the sink |sinkId|.
+ *
+ * Note that provider must return a valid mirror service name if
+ * provider.canRoute(sourceUrn, sinkId) is true, where sourcerUrn is any
+ * valid mirroring URN.
+ *
+ * @param {string} sinkId
+ * @return {?mr.mirror.ServiceName}
+ * Null if no mirror service is supported on the sink.
+ */
+ getMirrorServiceName(sinkId) {}
+
+ /**
+ * Tells the provider that the mirroring activity description for the
+ * mirroring route |routeId| has changed. The provider can synchronize this
+ * with its own state.
+ * @param {string} routeId
+ */
+ onMirrorActivityUpdated(routeId) {}
+
+ /**
+ * Whether this provider can route media |sourceUrn| to sink |sinkId|.
+ * @param {string} sourceUrn The URN of the media being displayed.
+ * @param {string} sinkId
+ * @return {boolean} True if the provider can handle it.
+ */
+ canRoute(sourceUrn, sinkId) {}
+
+ /**
+ * Whether this provider can join a given route from |sourceUrn| and,
+ * optionally, the specific |route| in question.
+ * @param {string} sourceUrn The URN of the media being displayed.
+ * @param {string=} presentationId The presentation ID to join.
+ * @param {mr.Route=} route The route to join.
+ * @return {boolean} True if the provider can handle it.
+ */
+ canJoin(sourceUrn, presentationId = undefined, route = undefined) {}
+
+ /**
+ * Joins a route identified by by |sourceUrn| and |presentationId|.
+ * @param {string} sourceUrn
+ * @param {string} presentationId A presentation ID for Presentation API
+ * client; A Cast session ID for Cast join; and 'autojoin' for Cast auto Join
+ * case;
+ * @param {boolean} offTheRecord True if the request is from an
+ * off the record (incognito) browser profile.
+ * @param {number} timeoutMillis Request timeout in milliseconds.
+ * @param {string} origin
+ * @param {?number} tabId null for packaged app.
+ * @return {!mr.CancellablePromise<!mr.Route>} Fulfilled with the route if
+ * joined; Rejected otherwise.
+ */
+ joinRoute(
+ sourceUrn, presentationId, offTheRecord, timeoutMillis, origin, tabId) {}
+
+ /**
+ * Joins a route identified by by |sourceUrn| and |routeId|.
+ * @param {string} sourceUrn
+ * @param {string} routeId A route ID to join.
+ * @param {string} presentationId The presentation ID of the route to be
+ * created.
+ * @param {string} origin
+ * @param {?number} tabId null for packaged app.
+ * @param {number=} opt_timeoutMillis If positive, the timeout to use in place
+ * of default timeout.
+ * @return {!mr.CancellablePromise<!mr.Route>} Fulfilled with the route if
+ * joined; Rejected otherwise.
+ */
+ connectRouteByRouteId(
+ sourceUrn, routeId, presentationId, origin, tabId, opt_timeoutMillis) {}
+
+ /**
+ * Detaches a presentation connection from the underlying media route given by
+ * |routeId|.
+ * @param {!string} routeId
+ */
+ detachRoute(routeId) {}
+
+ /**
+ * Searches this provider for a sink that matches |searchCriteria| that is
+ * compatible with the source |sourceUrn|. Returns a promise which resolves
+ * to the matching sink, or rejected if not found.
+ * @param {string} sourceUrn
+ * @param {!mr.SinkSearchCriteria} searchCriteria
+ * @return {!Promise<!mr.Sink>}
+ */
+ searchSinks(sourceUrn, searchCriteria) {}
+
+ /**
+ * Called when Media Router finishes sink discovery. Store |sinks| in this
+ * provider.
+ * @param {!Array<!mojo.Sink>} sinks list of discovered sinks
+ */
+ provideSinks(sinks) {}
+
+ /**
+ * See documentation in interface_data/mojo.js.
+ * @param {string} routeId
+ * @param {!mojo.InterfaceRequest} controllerRequest
+ * @param {!mojo.MediaStatusObserverPtr} observer
+ * @return {!Promise<void>}
+ */
+ createMediaRouteController(routeId, controllerRequest, observer) {}
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/manager/provider_events.js b/chromium/chrome/browser/resources/media_router/extension/src/manager/provider_events.js
new file mode 100644
index 00000000000..564180e723f
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/manager/provider_events.js
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Provider events.
+
+ */
+
+goog.provide('mr.InternalMessageEvent');
+goog.provide('mr.ProviderEventType');
+
+
+/**
+ * @enum {string}
+ */
+mr.ProviderEventType = {
+ INTERNAL_MESSAGE: 'internal_message'
+};
+
+
+/**
+ * @template T
+ */
+mr.InternalMessageEvent = class {
+ /**
+ * @param {string} routeId
+ * @param {T} message
+ */
+ constructor(routeId, message) {
+ /**
+ * @const
+ */
+ this.type = mr.ProviderEventType.INTERNAL_MESSAGE;
+
+ /**
+ * @type {string}
+ */
+ this.routeId = routeId;
+
+ /**
+ * @type {T}
+ */
+ this.message = message;
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/manager/provider_manager.js b/chromium/chrome/browser/resources/media_router/extension/src/manager/provider_manager.js
new file mode 100755
index 00000000000..94e50f9792f
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/manager/provider_manager.js
@@ -0,0 +1,1280 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview API for registering Media Route Providers.
+ *
+
+ *
+ * For now, we will always load every provider each time the extension is
+ * loaded.
+ */
+
+goog.provide('mr.ProviderManager');
+
+goog.require('mr.Assertions');
+goog.require('mr.CancellablePromise');
+goog.require('mr.EventAnalytics');
+goog.require('mr.EventTarget');
+goog.require('mr.InitHelper');
+goog.require('mr.InternalMessageEvent');
+goog.require('mr.Logger');
+goog.require('mr.MediaRouterRequestHandler');
+goog.require('mr.MessagePortService');
+goog.require('mr.MessagePortServiceImpl');
+goog.require('mr.MirrorAnalytics');
+goog.require('mr.Module');
+goog.require('mr.MojoUtils');
+goog.require('mr.PersistentData');
+goog.require('mr.PersistentDataManager');
+goog.require('mr.ProviderManagerCallbacks');
+goog.require('mr.RouteMessageSender');
+goog.require('mr.RouteRequestError');
+goog.require('mr.RouteRequestResultCode');
+goog.require('mr.Sink');
+goog.require('mr.SinkAvailability');
+goog.require('mr.SinkSearchCriteria');
+goog.require('mr.Throttle');
+goog.require('mr.mirror.Error');
+goog.require('mr.mirror.ServiceName');
+
+
+/**
+ * Tracks registered MediaRouteProviders and loads them on-demand.
+ * @implements {mr.MediaRouterRequestHandler}
+ * @implements {mr.PersistentData}
+ * @implements {mr.ProviderManagerCallbacks}
+ */
+mr.ProviderManager = class extends mr.Module {
+ constructor() {
+ super();
+
+ /**
+ * @private {mr.Logger}
+ */
+ this.logger_ = mr.Logger.getInstance('mr.ProviderManager');
+
+ /**
+ * Holds a an array of registered Media Route Providers.
+ * @private {!Array<!mr.Provider>}
+ */
+ this.providers_ = [];
+
+ /**
+ * Holds a map of route ID to the provider that is managing the route.
+ * @private {!Map<string, mr.Provider>}
+ */
+ this.routeIdToProvider_ = new Map();
+
+ /**
+ * Holds a map of route ID to the mirror service name if the route is for
+ * tab/desktop mirroring.
+ * @private {!Map<string, mr.mirror.ServiceName>}
+ */
+ this.routeIdToMirrorServiceName_ = new Map();
+
+ /**
+ * Holds a set of active sink queries.
+ * @private {!Set<string>}
+ */
+ this.sinkQueries_ = new Set();
+
+ /**
+ * Holds a set of active route queries.
+ * @private {!Set<string>}
+ */
+ this.routeQueries_ = new Set();
+
+ /**
+ * Holds a map of mirror service names to modules.
+ * @private {!Map<mr.mirror.ServiceName, mr.ModuleId>}
+ */
+ this.mirrorServiceModules_ = new Map();
+
+ /**
+ * Keeps track of last used mirror service for gathering logs for feedback.
+ * @private {?mr.mirror.ServiceName}
+ */
+ this.lastUsedMirrorService_ = null;
+
+ /** @private {?mr.MediaRouterService} */
+ this.mediaRouterService_ = null;
+
+ /** @private {!mr.EventTarget} */
+ this.routeMessageEventTarget_ = new mr.EventTarget();
+
+
+ /** @private {!mr.Throttle} */
+ this.routeUpdateEventThrottle_ = new mr.Throttle(
+ this.sendRoutesQueryResultToMr_,
+ mr.ProviderManager.ALL_QUERIES_INTERVAL_MS_, this);
+
+ /** @private {!mr.Throttle} */
+ this.sinkUpdateEventThrottle_ = new mr.Throttle(
+ this.executeSinkQueries_, mr.ProviderManager.ALL_QUERIES_INTERVAL_MS_,
+ this);
+
+ /**
+ * MR message sender with build-in rate throttle.
+ * @private {!mr.RouteMessageSender}
+ */
+ this.mrRouteMessageSender_ = new mr.RouteMessageSender(
+ this, mr.RouteMessageSender.MESSAGE_SIZE_KEEP_ALIVE_THRESHOLD);
+
+ /**
+ * The number of pending createRoute
+ * @private {number}
+ */
+ this.pendingRequestRoutes_ = 0;
+
+ /**
+ * Maps provider name to that provider's last reported sink availability.
+ * @private {!Map<string, mr.SinkAvailability>}
+ */
+ this.sinkAvailabilityMap_ = new Map();
+
+ /**
+ * Whether mDNS is currently enabled.
+ * @private {boolean}
+ */
+ this.mdnsEnabled_ = window.navigator.userAgent.indexOf('Windows') == -1;
+
+ /**
+ * Functions that should be executed when |mdnsEnabled_| goes from |false|
+ * to
+ * |true|.
+ * @private {!Array<function()>}
+ */
+ this.mdnsEnabledCallbacks_ = [];
+
+ /**
+ * Names of components that have requested to keep the extension alive.
+ * @private {!Array<string>}
+ */
+ this.keepAliveComponents_ = [];
+
+ /**
+ * Handler for chrome.runtime.onMessage events.
+ * @private @const
+ */
+ this.internalMessageHandler_ =
+ mr.InitHelper.getInternalMessageHandler(this);
+
+ /**
+ * Handler for chrome.runtime.onMessageExternal events.
+ * @private @const
+ */
+ this.externalMessageHandler_ =
+ mr.InitHelper.getExternalMessageHandler(this);
+
+ mr.ProviderManager.exportProperties_(this);
+ }
+
+ /**
+ * @override
+ */
+ handleEvent(event, ...args) {
+ if (event == chrome.runtime.onMessage) {
+ this.internalMessageHandler_(...args);
+ } else if (event == chrome.runtime.onMessageExternal) {
+ this.externalMessageHandler_(...args);
+ } else {
+ throw new Error('Unhandled event');
+ }
+ }
+
+ /**
+ * Gets the mirror service with the given name.
+ * @param {mr.mirror.ServiceName} serviceName Name of the mirror service.
+ * @return {!Promise<!mr.mirror.Service>} Resolved with the requested
+ * mirror service.
+ */
+ getMirrorService(serviceName) {
+ const moduleId = this.mirrorServiceModules_.get(serviceName);
+ return mr.Module.load(moduleId).then(module => {
+ let service = /** @type {!mr.mirror.Service} */ (module);
+ service.initialize(this);
+ return service;
+ });
+ }
+
+ /**
+ * Registers and initalizes providers.
+ *
+ * @param {!Array<!mr.Provider>} providers
+ * @param {!mojo.MediaRouteProviderConfig=} config
+ */
+ registerAllProviders(providers, config = undefined) {
+ providers.forEach(provider => {
+ this.registerProvider_(provider, config);
+ });
+ }
+
+ /**
+ * Initializes the provider manager, register / initialize given providers,
+ * and registers itself with PersistentDataManager.
+ * @param {!mr.MediaRouterService} mediaRouterService
+ * @param {!Array<!mr.Provider>} providers
+ * @param {!mojo.MediaRouteProviderConfig=} config
+ */
+ initialize(mediaRouterService, providers, config = undefined) {
+ this.mirrorServiceModules_.set(
+ mr.mirror.ServiceName.WEBRTC, mr.ModuleId.WEBRTC_STREAMING_SERVICE);
+ this.mirrorServiceModules_.set(
+ mr.mirror.ServiceName.CAST_STREAMING,
+ mr.ModuleId.CAST_STREAMING_SERVICE);
+ this.mirrorServiceModules_.set(
+ mr.mirror.ServiceName.HANGOUTS, mr.ModuleId.HANGOUTS_SERVICE);
+ this.mirrorServiceModules_.set(
+ mr.mirror.ServiceName.MEETINGS, mr.ModuleId.MEETINGS_SERVICE);
+
+ mr.MessagePortService.setService(new mr.MessagePortServiceImpl(this));
+
+ this.mediaRouterService_ = mediaRouterService;
+ this.mrRouteMessageSender_.init(
+ this.mediaRouterService_.onRouteMessagesReceived.bind(
+ this.mediaRouterService_));
+ this.registerAllProviders(providers, config);
+
+ mr.PersistentDataManager.register(this);
+ mr.Module.onModuleLoaded(mr.ModuleId.PROVIDER_MANAGER, this);
+ }
+
+ /**
+ * Registers a provider and initializes it.
+ * @param {!mr.Provider} provider
+ * @param {!mojo.MediaRouteProviderConfig=} config
+ * @private
+ */
+ registerProvider_(provider, config = undefined) {
+ if (this.getProviderByName(provider.getName())) {
+ this.logger_.warning(
+ 'Provider ' + provider.getName() + ' already registered.');
+ return;
+ }
+
+ try {
+ provider.initialize(config);
+ this.providers_.push(provider);
+ this.sinkAvailabilityMap_.set(
+ provider.getName(), mr.SinkAvailability.UNAVAILABLE);
+ } catch (/** Error */ error) {
+ this.logger_.warning(
+ 'Provider ' + provider.getName() + ' failed to initialize.', error);
+ }
+ }
+
+ /**
+ * @return {!Array<!mr.Provider>} Registered Media Route Providers.
+ */
+ getProviders() {
+ return this.providers_;
+ }
+
+ /**
+ * @param {!mr.CancellablePromise<!mr.Route>} routePromise
+ * @param {number} timeout Timeout in milliseconds. When timeout
+ * fires, the return value of this method is rejected. Must be a positive
+ * number.
+ * @return {!Promise<!mr.Route>}
+ * @private
+ */
+ addTimeout_(routePromise, timeout) {
+ return new Promise((resolve, reject) => {
+ let timerId = null;
+ this.preventSuspend_();
+ timerId = window.setTimeout(() => {
+ timerId = null;
+ // Refer to the CancellablePromise class for more details on the
+ // cancel() method, and <route-creation-timeout.svg.gz> for a diagram of
+ // how promise cancellation works with this class.
+ routePromise.cancel(new mr.RouteRequestError(
+ mr.RouteRequestResultCode.TIMED_OUT,
+ 'timeout after ' + timeout + ' ms.'));
+ }, timeout);
+ const cleanup = () => {
+ this.allowSuspend_();
+ if (timerId != null) {
+ window.clearTimeout(timerId);
+ }
+ };
+ routePromise.promise.then(
+ route => {
+ cleanup();
+ resolve(route);
+ },
+ err => {
+ cleanup();
+ reject(mr.RouteRequestError.wrap(err));
+ });
+ });
+ }
+
+ /**
+ * @param {!Promise<T>} promise
+ * @param {number} timeout Timeout in milliseconds. When timeout
+ * fires, the return value of this method is rejected. Must be a positive
+ * number.
+ * @return {!Promise<T>}
+ * @template T
+ * @private
+ */
+ addIgnoredTimeout_(promise, timeout) {
+ return this.addTimeout_(mr.CancellablePromise.forPromise(promise), timeout);
+ }
+
+ /**
+ * Increments the number of pending request for routes and checks if the
+ * extension should be kept alive.
+ * @private
+ */
+ preventSuspend_() {
+ this.pendingRequestRoutes_++;
+ this.maybeUpdateKeepAlive_();
+ }
+
+ /**
+ * Decrements the number of pending request for routes and checks if the
+ * extension should be kept alive.
+ * @private
+ */
+ allowSuspend_() {
+ this.pendingRequestRoutes_--;
+ this.maybeUpdateKeepAlive_();
+ }
+
+ /**
+ * If override timeout is provided and is positive, returns it.
+ * Otherwise, returns the default timeout.
+ * @param {number} defaultTimeoutMillis Default timeout.
+ * @param {number=} opt_overrideTimeoutMillis Optional override timeout.
+ * @return {number}
+ * @private
+ */
+ static getTimeoutToUse_(defaultTimeoutMillis, opt_overrideTimeoutMillis) {
+ return opt_overrideTimeoutMillis && opt_overrideTimeoutMillis > 0 ?
+ opt_overrideTimeoutMillis :
+ defaultTimeoutMillis;
+ }
+
+ /** @override */
+ onBeforeInvokeHandler() {
+ mr.EventAnalytics.recordEvent(mr.EventAnalytics.Event.MEDIA_ROUTER);
+ }
+
+ /**
+ * Helper method to return a string origin from a string or a mojo.Origin
+ * object.
+
+ * @param {!mojo.Origin|string} origin
+ * @return {string}
+ * @private
+ */
+ mojoOriginToString_(origin) {
+ if (typeof origin == 'string') {
+ return origin;
+ }
+ return mr.MojoUtils.mojoOriginToString(/** @type{!mojo.Origin} */ (origin));
+ }
+
+ /**
+ * @override
+ */
+ createRoute(
+ sourceUrn, sinkId, presentationId, origin = undefined, tabId = undefined,
+ timeoutMillis = undefined, offTheRecord = false) {
+ const provider = this.getProviderFor_(sourceUrn, sinkId);
+ if (!provider) {
+ return Promise.reject(new mr.RouteRequestError(
+ mr.RouteRequestResultCode.NO_SUPPORTED_PROVIDER,
+ 'No provider supports createRoute with source: ' + sourceUrn +
+ ' and sink: ' + sinkId));
+ }
+ let originString = undefined;
+ if (origin !== undefined) {
+ originString = this.mojoOriginToString_(origin);
+ }
+ timeoutMillis = mr.ProviderManager.getTimeoutToUse_(
+ mr.ProviderManager.CREATE_ROUTE_TIMEOUT_MS, timeoutMillis);
+ const routePromise = provider.createRoute(
+ sourceUrn, sinkId, presentationId, offTheRecord, timeoutMillis,
+ originString, tabId);
+ return this.addTimeout_(routePromise, timeoutMillis)
+ .then(
+ route => route,
+ /** Error */ err => {
+ this.logger_.error('Error creating route.', err);
+ throw err;
+ });
+ }
+
+ /**
+ * @override
+ */
+ connectRouteByRouteId(
+ sourceUrn, routeId, presentationId, origin, tabId,
+ timeoutMillis = undefined) {
+ const provider = this.routeIdToProvider_.get(routeId);
+ if (!provider) {
+ return Promise.reject(new mr.RouteRequestError(
+ mr.RouteRequestResultCode.NO_SUPPORTED_PROVIDER,
+ 'No provider supports join ' + routeId));
+ }
+
+ // connectRouteByRouteId may take a while. Prevent extension suspension.
+ const routePromise = provider.connectRouteByRouteId(
+ sourceUrn, routeId, presentationId, this.mojoOriginToString_(origin),
+ tabId);
+ timeoutMillis = mr.ProviderManager.getTimeoutToUse_(
+ mr.ProviderManager.JOIN_ROUTE_TIMEOUT_MS_, timeoutMillis);
+ return this.addTimeout_(routePromise, timeoutMillis);
+ }
+
+ /**
+ * @override
+ */
+ joinRoute(
+ sourceUrn, presentationId, origin, tabId, timeoutMillis = undefined,
+ offTheRecord = false) {
+ const provider = this.getProviderForJoin_(sourceUrn, presentationId);
+ if (!provider) {
+ return Promise.reject(new mr.RouteRequestError(
+ mr.RouteRequestResultCode.NO_SUPPORTED_PROVIDER,
+ 'No provider supports join ' + presentationId));
+ }
+
+ // joinRoute may take a while. Prevent extension suspension.
+ timeoutMillis = mr.ProviderManager.getTimeoutToUse_(
+ mr.ProviderManager.JOIN_ROUTE_TIMEOUT_MS_, timeoutMillis);
+ const routePromise = provider.joinRoute(
+ sourceUrn, presentationId, offTheRecord, timeoutMillis,
+ this.mojoOriginToString_(origin), tabId);
+ return this.addTimeout_(routePromise, timeoutMillis);
+ }
+
+ /**
+ * @override
+ */
+ terminateRoute(routeId) {
+ const provider = this.routeIdToProvider_.get(routeId);
+ if (!provider) {
+ return Promise.reject(new mr.RouteRequestError(
+ mr.RouteRequestResultCode.ROUTE_NOT_FOUND,
+ 'Route not found for routeId ' + routeId));
+ }
+ return this.maybeStopMirrorSession_(routeId).then(
+ () => provider.terminateRoute(routeId));
+ }
+
+ /**
+ * @override
+ */
+ startObservingMediaSinks(sourceUrn) {
+ if (!this.sinkQueries_.has(sourceUrn)) {
+ this.sinkQueries_.add(sourceUrn);
+ this.providers_.forEach(p => {
+ p.startObservingMediaSinks(sourceUrn);
+ });
+ }
+ this.querySinks_(sourceUrn);
+ }
+
+ /**
+ * @override
+ */
+ stopObservingMediaSinks(sourceUrn) {
+ if (!this.sinkQueries_.delete(sourceUrn)) {
+ this.logger_.info('No existing query ' + sourceUrn);
+ } else {
+ this.doStopObservingMediaSinks_(sourceUrn);
+ }
+ }
+
+ /**
+ * Helper method to stop a sink query on all providers.
+ * @param {string} sourceUrn The URN of the media.
+ * @private
+ */
+ doStopObservingMediaSinks_(sourceUrn) {
+ this.providers_.forEach(p => {
+ p.stopObservingMediaSinks(sourceUrn);
+ });
+ }
+
+ /**
+ * Queries for sinks capable of displaying |sourceUrn|.
+ * @param {string} sourceUrn The URN of the media.
+ * @private
+ */
+ querySinks_(sourceUrn) {
+
+ if (sourceUrn == 'urn:x-org.chromium.media:source:tab:-1') {
+ this.logger_.warning('No sinks for sourceUrn: ' + sourceUrn);
+ } else {
+ /** @type {!Map<string, !mr.Sink>} */
+ const sinks = new Map();
+ let origins = [];
+ // Get available sinks from current providers.
+ this.providers_.forEach(p => {
+ const sinkList = p.getAvailableSinks(sourceUrn);
+ if (sinkList.sinks.length > 0) {
+ origins = sinkList.origins;
+ }
+ sinkList.sinks.forEach(sink => {
+ // There shouldn't be duplicate sinks. Log a message if we encounter
+ // it.
+ if (sinks.has(sink.id)) {
+ this.logger_.warning(
+ 'Detected duplicate sink ' + sink.id +
+ ' from provider: ' + p.getName());
+ } else {
+ sinks.set(sink.id, sink);
+ }
+ });
+ });
+
+ this.sendSinksToMr_(sourceUrn, Array.from(sinks.values()), origins || []);
+ }
+ }
+
+ /**
+ * Sends sinks that support |sourceUrn| to media router.
+ * @param {string} sourceUrn
+ * @param {!Array<!mr.Sink>} sinkList Sinks that support |sourceUrn|
+ * @param {!Array<string>} origins Origins that can access the sink list.
+ * @private
+ */
+ sendSinksToMr_(sourceUrn, sinkList, origins) {
+ this.logger_.info(
+ 'Sending ' + sinkList.length + ' sinks to MR for ' + sourceUrn);
+ this.mediaRouterService_.onSinksReceived(
+ sourceUrn, sinkList, origins.map(mr.MojoUtils.stringToMojoOrigin));
+ }
+
+ /**
+ * @override
+ */
+ sendRouteMessage(routeId, message, opt_extraInfo) {
+ const provider = this.routeIdToProvider_.get(routeId);
+ if (!provider) {
+ return Promise.reject(Error(`Invalid route ID ${routeId}`));
+ }
+ return this.addIgnoredTimeout_(
+ provider.sendRouteMessage(routeId, message, opt_extraInfo),
+ mr.ProviderManager.SEND_MESSAGE_TIMEOUT_MS_);
+ }
+
+ /**
+ * @override
+ */
+ sendRouteBinaryMessage(routeId, data) {
+ const provider = this.routeIdToProvider_.get(routeId);
+ if (!provider) {
+ return Promise.reject(Error(`Invalid route ID ${routeId}`));
+ }
+ return this.addIgnoredTimeout_(
+ provider.sendRouteBinaryMessage(routeId, data),
+ mr.ProviderManager.SEND_MESSAGE_TIMEOUT_MS_);
+ }
+
+ /**
+ * @override
+ */
+ startListeningForRouteMessages(routeId) {
+ this.mrRouteMessageSender_.listenForRouteMessages(routeId);
+ }
+
+ /**
+ * @override
+ */
+ stopListeningForRouteMessages(routeId) {
+ this.mrRouteMessageSender_.stopListeningForRouteMessages(routeId);
+ }
+
+ /**
+ * @override
+ */
+ detachRoute(routeId) {
+ const provider = this.routeIdToProvider_.get(routeId);
+ if (!provider) {
+ this.logger_.info('Route ' + routeId + ' does not exist.');
+ return;
+ }
+ provider.detachRoute(routeId);
+ }
+
+ /**
+ * @override
+ */
+ enableMdnsDiscovery() {
+ this.mdnsEnabled_ = true;
+ this.mdnsEnabledCallbacks_.forEach(callback => {
+ callback();
+ });
+ this.mdnsEnabledCallbacks_.length = 0;
+ }
+
+ /**
+ * @param {string} sourceUrn The URN of the media being displayed.
+ * @param {string} sinkId
+ * @return {?mr.Provider} The provider that can handle |sourceUrn| on |sinkId|.
+ * Null if none exists.
+ * @private
+ */
+ getProviderFor_(sourceUrn, sinkId) {
+ return this.providers_.find(
+ provider => provider.canRoute(sourceUrn, sinkId)) ||
+ null;
+ }
+
+ /**
+ * @param {string} sourceUrn The URN of the media being displayed.
+ * @param {string} presentationId The presentation ID to join.
+ * @return {?mr.Provider} The provider that can join a route identified by
+ * |sourceUrn| and |presentationId|. Null if none exists.
+ * @private
+ */
+ getProviderForJoin_(sourceUrn, presentationId) {
+ return this.providers_.find(
+ provider => provider.canJoin(sourceUrn, presentationId)) ||
+ null;
+ }
+
+ /**
+ * @private
+ */
+ executeSinkQueries_() {
+ this.sinkQueries_.forEach(sourceUrn => {
+ this.querySinks_(sourceUrn);
+ });
+ }
+
+ /**
+ * @private
+ */
+ maybeUpdateKeepAlive_() {
+ this.mediaRouterService_.setKeepAlive(
+ this.pendingRequestRoutes_ > 0 || this.keepAliveComponents_.length > 0);
+ }
+
+ /**
+ * @override
+ */
+ startObservingMediaRoutes(sourceUrn) {
+ if (!this.routeQueries_.has(sourceUrn)) {
+ this.routeQueries_.add(sourceUrn);
+ this.providers_.forEach(p => {
+ p.startObservingMediaRoutes(sourceUrn);
+ });
+ }
+ this.routeUpdateEventThrottle_.fire();
+ }
+
+ /**
+ * @override
+ */
+ stopObservingMediaRoutes(sourceUrn) {
+ if (!this.routeQueries_.delete(sourceUrn)) {
+ this.logger_.info('No existing route query ' + sourceUrn);
+ } else {
+ this.providers_.forEach(provider => {
+ provider.stopObservingMediaRoutes(sourceUrn);
+ });
+ }
+ }
+
+ /**
+ * Send routes query result to MR immediately.
+ * @private
+ */
+ sendRoutesQueryResultToMr_() {
+ if (this.routeQueries_.size == 0) return;
+
+ let routeList = [];
+ this.providers_.forEach(p => {
+ routeList = routeList.concat(p.getRoutes());
+ });
+
+ this.routeQueries_.forEach(sourceUrn => {
+ const nonLocalJoinableRouteIds = [];
+ this.providers_.forEach(p => {
+ const routes = p.getRoutes();
+ routes.forEach(route => {
+ if (!route.createdLocally && p.canJoin(sourceUrn, undefined, route)) {
+ nonLocalJoinableRouteIds.push(route.id);
+ }
+ });
+ });
+ this.mediaRouterService_.onRoutesUpdated(
+ routeList, sourceUrn, nonLocalJoinableRouteIds);
+ });
+ }
+
+ /**
+ * @override
+ */
+ getStorageKey() {
+ return 'ProviderManager';
+ }
+
+ /**
+ * @override
+ */
+ getData() {
+ return [new mr.ProviderManager.PersistentData_(
+ this.providers_.map(p => p.getName()), Array.from(this.sinkQueries_),
+ Array.from(this.routeQueries_),
+ Array.from(
+ this.routeIdToProvider_,
+ ([routeId, provider]) => [routeId, provider.getName()]),
+ Array.from(this.sinkAvailabilityMap_), this.mdnsEnabled_,
+ this.lastUsedMirrorService_)];
+ }
+
+ /**
+ * @override
+ */
+ loadSavedData() {
+ const savedData = /** @type {mr.ProviderManager.PersistentData_} */
+ (mr.PersistentDataManager.getTemporaryData(this));
+ if (savedData) {
+ this.sinkQueries_ = new Set(savedData.sinkQueries);
+ this.routeQueries_ = new Set(savedData.routeQueries);
+
+ for (let [routeId, providerName] of savedData.routeIdToProviderName) {
+ const provider = this.getProviderByName(providerName);
+ mr.Assertions.assert(provider, 'Provider not found: ' + providerName);
+ this.routeIdToProvider_.set(routeId, provider);
+ }
+ this.sinkAvailabilityMap_ = new Map(savedData.sinkAvailabilityMap);
+ this.lastUsedMirrorService_ = savedData.lastUsedMirrorService || null;
+ if (savedData.mdnsEnabled) {
+ this.enableMdnsDiscovery();
+ }
+ }
+ }
+
+ /**
+ * @override
+ */
+ getProviderFromRouteId(routeId) {
+ return this.routeIdToProvider_.get(routeId);
+ }
+
+ /**
+ * @override
+ */
+ onRouteAdded(provider, route) {
+ this.routeIdToProvider_.set(route.id, provider);
+ this.onRouteUpdated(provider, route);
+ }
+
+ /**
+ * @override
+ */
+ onRouteRemoved(provider, route) {
+ // Stopping mirroring does not affect message delivery.
+ this.maybeStopMirrorSession_(route.id);
+
+ this.routeIdToProvider_.delete(route.id);
+ this.mrRouteMessageSender_.onRouteRemoved(route.id);
+ this.onRouteUpdated(provider, route);
+ }
+
+ /**
+ * @override
+ */
+ onRouteUpdated(provider, route) {
+ if (this.routeQueries_.size > 0) this.routeUpdateEventThrottle_.fire();
+ }
+
+ /**
+ * @override
+ */
+ handleMirrorActivityUpdate(route, mirrorActivity) {
+ const provider = this.routeIdToProvider_.get(route.id);
+ if (provider) {
+ provider.onMirrorActivityUpdated(route.id);
+ // Bypass the throttle, since an update to the route description may have
+ // happened before the throttle interval has elapsed.
+ if (this.routeQueries_.size > 0) this.sendRoutesQueryResultToMr_();
+ }
+ }
+
+ /**
+ * @override
+ */
+ onRouteMessage(provider, routeId, message) {
+ if (!this.routeIdToProvider_.has(routeId)) {
+ this.logger_.warning('Got route message for closed route ' + routeId);
+ return;
+ }
+ // If message is not a string or an Uint8Array, encode it into a JSON string
+ // for mr.cast.InternalMessage.
+ if ((typeof message !== 'string') && !(message instanceof Uint8Array)) {
+ message = JSON.stringify(message);
+ }
+ this.mrRouteMessageSender_.send(routeId, message);
+ }
+
+ /**
+ * @override
+ */
+ onPresentationConnectionStateChanged(routeId, state) {
+ if (state == mr.PresentationConnectionState.TERMINATED) {
+ this.mrRouteMessageSender_.sendImmediately();
+ }
+ this.mediaRouterService_.onPresentationConnectionStateChanged(
+ routeId, /** @type {string} */ (state));
+ }
+
+ /**
+ * @override
+ */
+ onPresentationConnectionClosed(routeId, reason, message) {
+ this.mrRouteMessageSender_.sendImmediately();
+ this.mediaRouterService_.onPresentationConnectionClosed(
+ routeId, /** @type {string} */ (reason), message);
+ }
+
+ /**
+ * @override
+ */
+ onMirrorSessionEnded(routeId) {
+ // When mirror service invokes this method, it already cleaned its session.
+ // So provider manager only needs to close the route via provider and does
+ // not
+ // need to invoke mirrorService.stopCurrentMirroring.
+ this.routeIdToMirrorServiceName_.delete(routeId);
+ const provider = this.routeIdToProvider_.get(routeId);
+ if (provider) {
+ provider.terminateRoute(routeId);
+ }
+ }
+
+ /**
+ * @override
+ */
+ onInternalMessage(provider, routeId, message) {
+ this.routeMessageEventTarget_.dispatchEvent(
+ new mr.InternalMessageEvent(routeId, message));
+ }
+
+ /**
+ * @override
+ */
+ onSinksUpdated() {
+ this.sinkUpdateEventThrottle_.fire();
+ }
+
+ /**
+ * @override
+ */
+ onSinkAvailabilityUpdated(provider, availability) {
+ const oldValue = this.sinkAvailabilityMap_.get(provider.getName());
+ mr.Assertions.assert(oldValue !== undefined, 'oldValue != undefined');
+ if (oldValue == availability) {
+ return;
+ }
+
+ const currentAvailability = this.computeSinkAvailability_();
+ this.sinkAvailabilityMap_.set(provider.getName(), availability);
+ const newAvailability = this.computeSinkAvailability_();
+ if (currentAvailability == newAvailability) {
+ return;
+ }
+
+ // When the overall SinkAvailability becomes UNAVAILABLE, all sink queries
+ // will be removed. Before that happens, we need to update the sink query
+ // results with empty results. Importantly, however, this also allows
+ // pseudo sinks to be sent as well, which clearing the sinks on the browser
+ // side would not maintain.
+ if (newAvailability == mr.SinkAvailability.UNAVAILABLE) {
+ this.executeSinkQueries_();
+ }
+ this.mediaRouterService_.onSinkAvailabilityUpdated(newAvailability);
+ if (newAvailability == mr.SinkAvailability.UNAVAILABLE) {
+ this.sinkQueries_.forEach(sourceUrn => {
+ this.doStopObservingMediaSinks_(sourceUrn);
+ });
+ this.sinkQueries_.clear();
+ }
+ }
+
+ /**
+ * @return {!mr.SinkAvailability} Overall sink availability based on providers'
+ * availability.
+ * @private
+ */
+ computeSinkAvailability_() {
+ return Array.from(this.sinkAvailabilityMap_.values())
+ .reduce(
+ (prev, cur) => Math.max(prev, cur),
+ mr.SinkAvailability.UNAVAILABLE);
+ }
+
+ /**
+ * @override
+ */
+ getRouteMessageEventTarget() {
+ return this.routeMessageEventTarget_;
+ }
+
+ /**
+ * @override
+ */
+ sendIssue(issue) {
+ this.mediaRouterService_.onIssue(issue);
+ }
+
+ /**
+ * @param {string} routeId
+ * @return {!Promise<boolean>} Fulfilled with true if the route is for
+ * a mirror session and the mirroring was stopped, and with false
+ * otherwise.
+ * @private
+ */
+ maybeStopMirrorSession_(routeId) {
+ if (this.routeIdToMirrorServiceName_.has(routeId)) {
+ // Stop mirroring first
+ return this
+ .getMirrorService(this.routeIdToMirrorServiceName_.get(routeId))
+ .then(mirrorService => {
+ this.routeIdToMirrorServiceName_.delete(routeId);
+ return mirrorService.stopCurrentMirroring();
+ });
+ }
+ return Promise.resolve(false);
+ }
+
+ /**
+ * @override
+ */
+ startMirroring(
+ provider, route, opt_presentationId, opt_streamStartedCallback) {
+ // The provider can handle the mirroring URN, thus the mirror
+ // service name is non-null.
+ const mirrorServiceName =
+ /** @type {mr.mirror.ServiceName} */ (mr.Assertions.assertString(
+ provider.getMirrorServiceName(route.sinkId)));
+ this.logger_.info(`Starting mirroring using service: ${mirrorServiceName}`);
+ this.routeIdToMirrorServiceName_.set(route.id, mirrorServiceName);
+ let innerPromise = null;
+ let cancellationReason = null;
+ return mr.CancellablePromise.withUncancellableStep(
+ this.getMirrorService(mirrorServiceName), mirrorService => {
+ this.lastUsedMirrorService_ = mirrorServiceName;
+ return mirrorService
+ .startMirroring(
+
+ route, mr.Assertions.assertString(route.mediaSource),
+ provider.getMirrorSettings(route.sinkId), opt_presentationId,
+ opt_streamStartedCallback)
+ .catch(err => {
+ if (err instanceof mr.mirror.Error &&
+ err.reason ==
+ mr.MirrorAnalytics.CapturingFailure
+ .CAPTURE_DESKTOP_FAIL_ERROR_USER_CANCEL) {
+ throw new mr.RouteRequestError(
+ mr.RouteRequestResultCode.CANCELLED);
+ }
+ throw err;
+ });
+ });
+ }
+
+ /**
+ * @override
+ */
+ updateMirroring(
+ provider, route, sourceUrn, opt_presentationId, opt_tabId,
+ opt_streamStartedCallback) {
+ const mirrorServiceName = this.routeIdToMirrorServiceName_.get(route.id);
+ if (!mirrorServiceName) {
+ return mr.CancellablePromise.reject(
+ Error('Route ' + route.id + ' is not mirroring'));
+ }
+ return mr.CancellablePromise.withUncancellableStep(
+ this.getMirrorService(mirrorServiceName), mirrorService => {
+ this.lastUsedMirrorService_ = mirrorServiceName;
+ return mirrorService.updateMirroring(
+ route, sourceUrn, provider.getMirrorSettings(route.sinkId),
+ opt_presentationId, opt_tabId, opt_streamStartedCallback);
+ });
+ }
+
+ /**
+ * @override
+ */
+ registerMdnsDiscoveryEnabledCallback(callback) {
+ if (this.mdnsEnabled_) {
+ callback();
+ return;
+ }
+ if (this.mdnsEnabledCallbacks_.indexOf(callback) != -1) {
+ return;
+ }
+ this.mdnsEnabledCallbacks_.push(callback);
+ }
+
+ /**
+ * @override
+ */
+ isMdnsDiscoveryEnabled() {
+ return this.mdnsEnabled_;
+ }
+
+ /**
+ * @override
+ */
+ requestKeepAlive(componentId, keepAlive) {
+ const index = this.keepAliveComponents_.indexOf(componentId);
+ const componentKeepAlive = (index >= 0);
+ if (keepAlive && !componentKeepAlive) {
+ this.keepAliveComponents_.push(componentId);
+ } else if (!keepAlive && componentKeepAlive) {
+ this.keepAliveComponents_.splice(index, 1);
+ }
+ this.maybeUpdateKeepAlive_();
+ }
+
+ /**
+ * @param {!string} name
+ * @return {?mr.Provider}
+ */
+ getProviderByName(name) {
+ return this.providers_.find(p => p.getName() == name) || null;
+ }
+
+ /**
+ * Returns the name of the provider that created the pseudo sink ID |sinkId|
+ * or null if it is not a valid pseudo sink ID.
+ * @param {string} sinkId
+ * @return {?string}
+ * @private
+ */
+ getProviderNameFromPseudoSinkId_(sinkId) {
+ if (!sinkId.startsWith(mr.ProviderManager.PSEUDO_SINK_NAME_PREFIX_)) {
+ return null;
+ }
+ return sinkId.substring(mr.ProviderManager.PSEUDO_SINK_NAME_PREFIX_.length);
+ }
+
+ /**
+ * Get the rejection promise when sink is not found.
+ * @param {string} sinkId Sink ID of the pseudo sink that generated the
+ * request.
+ * @param {string} sourceUrn Source to be used with the sink.
+ * @param {!mr.SinkSearchCriteria} searchCriteria Sink search criteria for the
+ * MRP's which includes the user's current domain.
+ * @return {!Promise}
+ * @private
+ */
+ rejectWithSinkNotFoundError_(sinkId, sourceUrn, searchCriteria) {
+ this.mediaRouterService_.onSearchSinkIdReceived(sinkId, '');
+ return Promise.reject(new mr.RouteRequestError(
+ mr.RouteRequestResultCode.UNKNOWN_ERROR,
+ 'No sink found for search input: ' + searchCriteria.input +
+ ' and source: ' + sourceUrn));
+ }
+
+ /**
+ * @override
+ */
+ searchSinks(sinkId, sourceUrn, searchCriteria) {
+ const providerName = this.getProviderNameFromPseudoSinkId_(sinkId);
+ const searchOwner =
+ providerName ? this.getProviderByName(providerName) : null;
+ if (!searchOwner) {
+ return Promise.reject(new Error('No provider supports ' + sinkId));
+ }
+ return searchOwner.searchSinks(sourceUrn, searchCriteria)
+ .then((sink) => sink.id);
+ }
+
+ /**
+ * @override
+ */
+ provideSinks(providerName, sinks) {
+ const provider = this.getProviderByName(providerName);
+ if (!provider) {
+ this.logger_.error(
+ `provideSinks: Provider not found for providerName ${providerName}`);
+ return;
+ }
+ provider.provideSinks(sinks);
+ }
+
+ /**
+ * @override
+ */
+ updateMediaSinks(sourceUrn) {
+ // Don't add to sinkQueries as the providers may already be unavailable, but
+ // if the sink queries already contain the sourceUrn just return as
+ // discovery
+ // is already ongoing.
+ if (this.sinkQueries_.has(sourceUrn)) {
+ return;
+ }
+ this.querySinks_(sourceUrn);
+ }
+
+ /**
+ * @override
+ */
+ createMediaRouteController(routeId, controllerRequest, observer) {
+ const cleanUpOnError = () => {
+ controllerRequest.close();
+ observer.ptr.reset();
+ };
+ const provider = this.routeIdToProvider_.get(routeId);
+ if (!provider) {
+ const errorMessage =
+ `createMediaRouteController: Provider not found for ${routeId}`;
+ this.logger_.error(errorMessage);
+ cleanUpOnError();
+ return Promise.reject(new Error(errorMessage));
+ }
+ return provider
+ .createMediaRouteController(routeId, controllerRequest, observer)
+ .catch(e => {
+ this.logger_.error(`createMediaRouteController failed: ${e.message}`);
+ cleanUpOnError();
+ throw e;
+ });
+ }
+
+ /**
+ * @param {number} tabId
+ * @param {!mojo.MirrorServiceRemoterPtr} remoter
+ * @param {!mojo.InterfaceRequest} remotingSourceRequest
+ */
+ onMediaRemoterCreated(tabId, remoter, remotingSourceRequest) {
+ this.mediaRouterService_.onMediaRemoterCreated(
+ tabId, remoter, remotingSourceRequest);
+ }
+
+ /**
+ * @return {?mr.mirror.ServiceName} The name of most recently used mirror
+ * service, or null if mirror service has not been used.
+ */
+ getLastUsedMirrorService() {
+ return this.lastUsedMirrorService_;
+ }
+
+ /**
+ * Exports methods for Mojo handler.
+ * @param {!mr.ProviderManager} providerManager
+ * @private
+ */
+ static exportProperties_(providerManager) {
+ // Cast to {!Object} so the compiler doesn't complain about accessing fields
+ // using subscript notation.
+ const obj = /** @type {!Object} */ (providerManager);
+ obj['onBeforeInvokeHandler'] = providerManager.onBeforeInvokeHandler;
+ obj['createRoute'] = providerManager.createRoute;
+ obj['joinRoute'] = providerManager.joinRoute;
+ obj['connectRouteByRouteId'] = providerManager.connectRouteByRouteId;
+ obj['terminateRoute'] = providerManager.terminateRoute;
+ obj['startObservingMediaSinks'] = providerManager.startObservingMediaSinks;
+ obj['stopObservingMediaSinks'] = providerManager.stopObservingMediaSinks;
+ obj['sendRouteMessage'] = providerManager.sendRouteMessage;
+ obj['sendRouteBinaryMessage'] = providerManager.sendRouteBinaryMessage;
+ obj['startListeningForRouteMessages'] =
+ providerManager.startListeningForRouteMessages;
+ obj['stopListeningForRouteMessages'] =
+ providerManager.stopListeningForRouteMessages;
+ obj['startObservingMediaRoutes'] =
+ providerManager.startObservingMediaRoutes;
+ obj['stopObservingMediaRoutes'] = providerManager.stopObservingMediaRoutes;
+ obj['detachRoute'] = providerManager.detachRoute;
+ obj['enableMdnsDiscovery'] = providerManager.enableMdnsDiscovery;
+ obj['searchSinks'] = providerManager.searchSinks;
+ obj['provideSinks'] = providerManager.provideSinks;
+ obj['updateMediaSinks'] = providerManager.updateMediaSinks;
+ obj['createMediaRouteController'] =
+ providerManager.createMediaRouteController;
+ }
+};
+
+/**
+ * Provider may fire sink/route list change event frequently. To avoid run all
+ * sink queries too frequently, only one set of queries at the end of each
+ * interval if there is an update event in the interval.
+ * @private @const {number}
+ */
+mr.ProviderManager.ALL_QUERIES_INTERVAL_MS_ = 500;
+
+// The timeout values below are to ensure provider manager resolve or reject
+// a pending MR request after a reasonable delay. This is a safe guard in case
+// provider has some unforeseen error.
+
+/** @const {number} */
+mr.ProviderManager.CREATE_ROUTE_TIMEOUT_MS = 60 * 1000;
+
+/** @private @const {number} */
+mr.ProviderManager.JOIN_ROUTE_TIMEOUT_MS_ = 30 * 1000;
+
+/** @private @const {number} */
+mr.ProviderManager.SEND_MESSAGE_TIMEOUT_MS_ = 30 * 1000;
+
+/** @private @const {string} */
+mr.ProviderManager.PSEUDO_SINK_NAME_PREFIX_ = 'pseudo:';
+
+
+/**
+ * The Data to be saved in storage when event page suspends.
+ * @private
+ */
+mr.ProviderManager.PersistentData_ = class {
+ /**
+ * @param {!Array<string>} providerNames
+ * @param {!Array<string>} sinkQueries
+ * @param {!Array<string>} routeQueries
+ * @param {!Array<!Array>} routeIdToProviderName
+ * @param {!Array<!Array>} sinkAvailabilityMap
+ * @param {boolean} mdnsEnabled
+ * @param {?mr.mirror.ServiceName} lastUsedMirrorService
+ */
+ constructor(
+ providerNames, sinkQueries, routeQueries, routeIdToProviderName,
+ sinkAvailabilityMap, mdnsEnabled, lastUsedMirrorService) {
+ /**
+ * @type {!Array<string>}
+ */
+ this.providerNames = providerNames;
+
+ /**
+ * @type {!Array<string>}
+ */
+ this.sinkQueries = sinkQueries;
+
+ /**
+ * @type {!Array<string>}
+ */
+ this.routeQueries = routeQueries;
+
+ /**
+ * Map encoded as an array.
+ * @type {!Array<!Array>}
+ */
+ this.routeIdToProviderName = routeIdToProviderName;
+
+ /**
+ * Map encoded as an array.
+ * @type {!Array<!Array>}
+ */
+ this.sinkAvailabilityMap = sinkAvailabilityMap;
+
+ /**
+ * @type {boolean}
+ */
+ this.mdnsEnabled = mdnsEnabled;
+
+ /**
+ * @type {?mr.mirror.ServiceName}
+ */
+ this.lastUsedMirrorService = lastUsedMirrorService;
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/manager/provider_manager_callbacks.js b/chromium/chrome/browser/resources/media_router/extension/src/manager/provider_manager_callbacks.js
new file mode 100644
index 00000000000..01ff1f5d3fa
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/manager/provider_manager_callbacks.js
@@ -0,0 +1,223 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview API used by Media Route Providers to interact with the Media
+ * Route Provider Manager.
+ */
+
+goog.provide('mr.ProviderManagerCallbacks');
+goog.provide('mr.ProviderManagerIssueCallbacks');
+goog.provide('mr.ProviderManagerMirrorServiceCallbacks');
+goog.provide('mr.ProviderManagerRouteCallbacks');
+goog.provide('mr.ProviderManagerSinkCallbacks');
+
+goog.require('mr.EventTarget');
+goog.require('mr.mirror.Activity');
+
+
+/**
+ * @record
+ */
+mr.ProviderManagerSinkCallbacks = class {
+ /**
+ * Called to notify that sinks have been added, removed, or updated.
+ */
+ onSinksUpdated() {}
+
+ /**
+ * Called to notify that sink availability has changed.
+ *
+
+ *
+ * @param {!mr.Provider} provider
+ * @param {!mr.SinkAvailability} availability
+ */
+ onSinkAvailabilityUpdated(provider, availability) {}
+};
+
+
+
+/**
+ * @record
+ */
+mr.ProviderManagerRouteCallbacks = class {
+ /**
+ * @param {!mr.Provider} provider
+ * @param {!mr.Route} route
+ */
+ onRouteAdded(provider, route) {}
+
+ /**
+ * @param {!mr.Provider} provider
+ * @param {!mr.Route} route
+ */
+ onRouteRemoved(provider, route) {}
+
+ /**
+ * @param {!mr.Provider} provider
+ * @param {!mr.Route} route
+ */
+ onRouteUpdated(provider, route) {}
+
+ /**
+ * Sends the provided message to the web app. If the message should also be
+ * sent internally via the MessagePort, also call onInternalMessage.
+ * @param {!mr.Provider} provider
+ * @param {string} routeId
+ * @param {string|!Uint8Array|!Object} message If not a string (text message)
+ * or Uint8Array (binary message), then the message will be serialized to
+ * a JSON string and sent as a text message.
+ */
+ onRouteMessage(provider, routeId, message) {}
+
+ /**
+ * Called to notify the presentation connected to a route has changed state.
+ * If the state is CLOSED, onPresentationConnectionClosed is called instead of
+ * this method.
+ * @param {!string} routeId
+ * @param {!mr.PresentationConnectionState} state
+ */
+ onPresentationConnectionStateChanged(routeId, state) {}
+
+ /**
+ * Called to notify the presentation connected to a route has closed.
+ * @param {string} routeId
+ * @param {!mr.PresentationConnectionCloseReason} reason
+ * @param {string} message
+ */
+ onPresentationConnectionClosed(routeId, reason, message) {}
+};
+
+
+
+/**
+ * @record
+ */
+mr.ProviderManagerIssueCallbacks = class {
+ /**
+ * Sends the given issue to MediaRouter.
+ * @param {!mr.Issue} issue
+ */
+ sendIssue(issue) {}
+};
+
+
+
+/**
+ * @record
+ * @extends {mr.ProviderManagerIssueCallbacks}
+ */
+mr.ProviderManagerMirrorServiceCallbacks = class {
+ /**
+ * Invoked by mirror service when it stops a mirror session due to error,
+ * the end of a stream etc. Because provider manager is unaware of mirror
+ * session's internal state, this method is used by mirror service to tell
+ * provider manager to close the corresponding route.
+ * @param {string} routeId
+ */
+ onMirrorSessionEnded(routeId) {}
+
+ /**
+ * Invoked by the mirror service when the mirroring activity description for
+ * mirroring route |route| has changed.
+ *
+ * @param {!mr.Route} route
+ * @param {!mr.mirror.Activity} mirrorActivity
+ */
+ handleMirrorActivityUpdate(route, mirrorActivity) {}
+};
+
+
+
+/**
+ * @record
+ * @extends {mr.ProviderManagerIssueCallbacks}
+ * @extends {mr.ProviderManagerSinkCallbacks}
+ * @extends {mr.ProviderManagerRouteCallbacks}
+ * @extends {mr.ProviderManagerMirrorServiceCallbacks}
+ */
+mr.ProviderManagerCallbacks = class {
+ /**
+ * @param {string} routeId
+ * @return {mr.Provider}
+ */
+ getProviderFromRouteId(routeId) {}
+
+ /**
+ * @return {!mr.EventTarget}
+ */
+ getRouteMessageEventTarget() {}
+
+ /**
+ * Sends the message internally, to the mirror session associated with the
+ * provided route ID, via the MessagePort.
+ * @param {!mr.Provider} provider
+ * @param {!string} routeId
+ * @param {!Object|string} message
+ */
+ onInternalMessage(provider, routeId, message) {}
+
+ /**
+ * Called by a provider to request the mirroring service to start mirroring.
+ * @param {!mr.Provider} provider
+ * @param {!mr.Route} route
+ * @param {string=} opt_presentationId
+ * @param {(function(!mr.Route): !mr.CancellablePromise<!mr.Route>)=}
+ * opt_streamStartedCallback Callback to invoke after stream capture
+ * succeeded and before the mirror session is created. This allows the
+ * provider to perform additional setup and update the route for the
+ * mirror session.
+ * @return {!mr.CancellablePromise<!mr.Route>}
+ */
+ startMirroring(
+ provider, route, opt_presentationId, opt_streamStartedCallback) {}
+
+ /**
+ * Called by a provider to request the mirroring service to update |route| to
+ * the new source |sourceUrn|.
+ * @param {!mr.Provider} provider
+ * @param {!mr.Route} route
+ * @param {string} sourceUrn
+ * @param {string=} opt_presentationId
+ * @param {number=} opt_tabId
+ * @param {(function(!mr.Route): !mr.CancellablePromise)=}
+ * opt_streamStartedCallback Callback to invoke after stream capture
+ * succeeded and before the mirror session is created. This allows the
+ * provider to perform additional setup and update the route for the
+ * mirror session.
+ * @return {!mr.CancellablePromise<!mr.Route>}
+ */
+ updateMirroring(
+ provider, route, sourceUrn, opt_presentationId, opt_tabId,
+ opt_streamStartedCallback) {}
+
+ /**
+ * Register a callback with the provider manager that will either be executed
+ * immediately if mDNS discovery is currently enabled or saved to be executed
+ * when mDNS discovery becomes enabled. This should allow duplicate calls to
+ * be
+ * made with the same function address but only call the function once.
+ * @param {function()} callback Callback that depends on mDNS discovery.
+ */
+ registerMdnsDiscoveryEnabledCallback(callback) {}
+
+ /**
+ * Called by a component with |keepAlive| set to true when it requires the
+ * extension to be kept alive. When a component no longer requires the
+ * extension
+ * to be kept alive, this method should be called with |keepAlive| set to
+ * false.
+ * The extension will be prevented from suspending as long as at least one
+ * component requested keep alive.
+ * @param {string} componentId Globally unique id of the requesting component.
+ * @param {boolean} keepAlive
+ */
+ requestKeepAlive(componentId, keepAlive) {}
+
+ /**
+ * @return {boolean} Whether mDNS discovery is currently enabled.
+ */
+ isMdnsDiscoveryEnabled() {}
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/manager/provider_manager_test.js b/chromium/chrome/browser/resources/media_router/extension/src/manager/provider_manager_test.js
new file mode 100644
index 00000000000..9453ed678b2
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/manager/provider_manager_test.js
@@ -0,0 +1,1076 @@
+/**
+ * @fileoverview Tests for provider_manager.
+ */
+goog.setTestOnly('provider_manager_test');
+
+goog.require('mr.CancellablePromise');
+goog.require('mr.MediaSourceUtils');
+goog.require('mr.MirrorAnalytics');
+goog.require('mr.Module');
+goog.require('mr.PersistentDataManager');
+goog.require('mr.PresentationConnectionCloseReason');
+goog.require('mr.PresentationConnectionState');
+goog.require('mr.ProviderManager');
+goog.require('mr.Route');
+goog.require('mr.RouteMessage');
+goog.require('mr.RouteRequestError');
+goog.require('mr.RouteRequestResultCode');
+goog.require('mr.Sink');
+goog.require('mr.SinkAvailability');
+goog.require('mr.SinkList');
+goog.require('mr.UnitTestUtils');
+goog.require('mr.mirror.Error');
+goog.require('mr.mirror.ServiceName');
+
+describe('Tests ProviderManager', function() {
+ let sourceUrn;
+ let mockMediaRouterService;
+ let providerManager;
+ let mockProvider1;
+ let mockProvider1Name;
+ let mockProvider2;
+ let mockBrokenProvider;
+ let mockCastMirrorService;
+ let mockWebrtcMirrorService;
+ let mirrorServiceMap;
+ const provider1Routes = [];
+ const provider2Routes = [];
+ const providerMethods = [
+ 'getName', 'initialize', 'getAvailableSinks', 'createRoute',
+ 'terminateRoute', 'sendRouteMessage', 'getSinkById', 'getMirrorSettings',
+ 'getMirrorServiceName', 'canRoute', 'startObservingMediaSinks',
+ 'stopObservingMediaSinks', 'startObservingMediaRoutes',
+ 'stopObservingMediaRoutes', 'getRoutes', 'canJoin', 'searchSinks',
+ 'createMediaRouteController'
+ ];
+ const mirrorServiceMethods = [
+ 'initialize',
+ 'getName',
+ 'startMirroring',
+ 'stopCurrentMirroring',
+ 'createMirrorSession',
+ 'updateMirroring',
+ ];
+ const castMirrorServiceMethods = mirrorServiceMethods.slice();
+ const presentationId = 'presentationId';
+ const routeId = '123';
+
+ let mockClock;
+
+ beforeEach(function() {
+ mr.PersistentDataManager.clear();
+ mr.Module.clearForTest();
+ mr.UnitTestUtils.mockMojoApi();
+ sourceUrn = 'urn:dial-multiscreen-org:dial:application:YouTube';
+ mockClock = null;
+ window['chrome'] = chrome || {};
+ chrome['runtime'] = chrome.runtime || {};
+ chrome.runtime.id = '123';
+ chrome.runtime.getManifest = function() {
+ return {version: 'fakeVersion'};
+ };
+ mockProvider1 = jasmine.createSpyObj('provider1', providerMethods);
+ mockProvider1Name = 'p1';
+ mockProvider1.getName.and.returnValue(mockProvider1Name);
+ mockProvider1.getRoutes.and.callFake(() => provider1Routes);
+ mockProvider2 = jasmine.createSpyObj('provider2', providerMethods);
+ mockProvider2.getName.and.returnValue('p2');
+ mockProvider2.getRoutes.and.callFake(() => provider2Routes);
+ mockBrokenProvider =
+ jasmine.createSpyObj('brokenProvider', providerMethods);
+ mockBrokenProvider.initialize.and.throwError(
+ new Error('I forgot how to initialize. @_@'));
+ mockCastMirrorService =
+ jasmine.createSpyObj('castMirrorService', castMirrorServiceMethods);
+ mockCastMirrorService.getName.and.returnValue(
+ mr.mirror.ServiceName.CAST_STREAMING);
+ mockWebrtcMirrorService =
+ jasmine.createSpyObj('webrtcMirrorService', mirrorServiceMethods);
+ mockWebrtcMirrorService.getName.and.returnValue(
+ mr.mirror.ServiceName.WEBRTC);
+ mirrorServiceMap = new Map();
+ mirrorServiceMap.set(
+ mr.mirror.ServiceName.CAST_STREAMING, mockCastMirrorService);
+ mirrorServiceMap.set(mr.mirror.ServiceName.WEBRTC, mockWebrtcMirrorService);
+
+ // These two not needed?
+ spyOn(mr.mirror.cast, 'Service').and.returnValue(mockCastMirrorService);
+ spyOn(mr.mirror.webrtc, 'WebRtcService')
+ .and.returnValue(mockWebrtcMirrorService);
+ mockMediaRouterService = jasmine.createSpyObj('mrService', [
+ 'setKeepAlive', 'getKeepAlive', 'setHandlers',
+ 'onPresentationConnectionClosed', 'onPresentationConnectionStateChanged',
+ 'onRoutesUpdated', 'onSinkAvailabilityUpdated', 'onSinksReceived',
+ 'start', 'onSearchSinkIdReceived', 'onRouteMessagesReceived'
+ ]);
+ const mockCloudComponentProvider = {
+ getIdentityService: function() {
+ return {};
+ }
+ };
+ spyOn(mr.cloud.CloudComponentProvider, 'getInstance')
+ .and.returnValue(mockCloudComponentProvider);
+ providerManager = new mr.ProviderManager();
+ expect(mockMediaRouterService.start.calls.count()).toBe(0);
+ providerManager.initialize(mockMediaRouterService, []);
+ mockMediaRouterService.start.and.returnValue('instance123');
+ spyOn(providerManager.mrRouteMessageSender_, 'send').and.callThrough();
+ spyOn(providerManager, 'getMirrorService').and.callFake(serviceName => {
+ return Promise.resolve(mirrorServiceMap.get(serviceName));
+ });
+ });
+
+ afterEach(function() {
+ if (mockClock) {
+ mr.UnitTestUtils.restoreRealClockAndPromises();
+ }
+ });
+
+ describe('Test presentation connection state changes', function() {
+ it('onPresentationConnectionStateChanged', function() {
+ const state = mr.PresentationConnectionState.TERMINATED;
+ providerManager.onPresentationConnectionStateChanged(routeId, state);
+ expect(mockMediaRouterService.onPresentationConnectionStateChanged)
+ .toHaveBeenCalledWith(routeId, state);
+ });
+
+ it('onPresentationConnectionStateClosed', function() {
+ const closeReason = mr.PresentationConnectionCloseReason.WENT_AWAY;
+ const message = 'Connection went away';
+ providerManager.onPresentationConnectionClosed(
+ routeId, closeReason, message);
+ expect(mockMediaRouterService.onPresentationConnectionClosed)
+ .toHaveBeenCalledWith(routeId, closeReason, message);
+ });
+ });
+
+ describe('Test registerAllProviders', function() {
+ it('Provider is initialized', function() {
+ providerManager.registerAllProviders(
+ [mockProvider1, mockProvider2, mockBrokenProvider]);
+ expect(mockProvider1.initialize.calls.count()).toBe(1);
+ expect(mockProvider2.initialize.calls.count()).toBe(1);
+ expect(mockBrokenProvider.initialize.calls.count()).toBe(1);
+ expect(providerManager.getProviders().some(
+ p => p.getName() == mockProvider1.getName()))
+ .toBe(true);
+ expect(providerManager.getProviders().some(
+ p => p.getName() == mockProvider2.getName()))
+ .toBe(true);
+ expect(!providerManager.getProviders().some(
+ p => p.getName() == mockBrokenProvider.getName()))
+ .toBe(true);
+ });
+
+ it('Route message event is listened to', function() {
+ mockClock = mr.UnitTestUtils.useMockClockAndPromises();
+ const route = new mr.Route(routeId, 'pId', '0', null, false, '', null);
+ const message = 'msg';
+ providerManager.registerAllProviders([mockProvider1]);
+ providerManager.onRouteAdded(mockProvider1, route);
+ providerManager.onRouteMessage(mockProvider1, routeId, message, true);
+ providerManager.startListeningForRouteMessages(routeId);
+ mockClock.tick(mr.RouteMessageSender.SEND_MESSAGE_INTERVAL_MILLIS);
+ expect(mockMediaRouterService.onRouteMessagesReceived)
+ .toHaveBeenCalledWith(
+ routeId, [new mr.RouteMessage(routeId, message)]);
+ });
+
+ it('Message of a closed route is not forwarded', function() {
+ const message = 'msg';
+ providerManager.registerAllProviders([mockProvider1]);
+ providerManager.onRouteMessage(mockProvider1, routeId, message);
+ expect(providerManager.mrRouteMessageSender_.send).not.toHaveBeenCalled();
+ });
+
+ it('InternalMessage sends and is not forwarded to app', function() {
+ const route = new mr.Route(routeId, 'pId', '0', null, false, '', null);
+ const message = 'msg';
+ providerManager.registerAllProviders([mockProvider1]);
+ providerManager.onRouteAdded(mockProvider1, route);
+ providerManager.onInternalMessage(mockProvider1, routeId, message);
+ expect(providerManager.mrRouteMessageSender_.send).not.toHaveBeenCalled();
+ });
+
+ it('start/stopObservingMediaRoutes is passed to provider', function() {
+ mockProvider1.getRoutes.and.returnValue([]);
+ mockProvider2.getRoutes.and.returnValue([]);
+ providerManager.registerAllProviders([mockProvider1, mockProvider2]);
+
+ expect(mockProvider1.startObservingMediaRoutes.calls.count()).toBe(0);
+ expect(mockProvider2.startObservingMediaRoutes.calls.count()).toBe(0);
+ expect(mockProvider1.stopObservingMediaRoutes.calls.count()).toBe(0);
+ expect(mockProvider2.stopObservingMediaRoutes.calls.count()).toBe(0);
+
+ providerManager.startObservingMediaRoutes(sourceUrn);
+ expect(mockProvider1.startObservingMediaRoutes.calls.count()).toBe(1);
+ expect(mockProvider2.startObservingMediaRoutes.calls.count()).toBe(1);
+ expect(mockProvider1.stopObservingMediaRoutes.calls.count()).toBe(0);
+ expect(mockProvider2.stopObservingMediaRoutes.calls.count()).toBe(0);
+
+ providerManager.stopObservingMediaRoutes(sourceUrn);
+ expect(mockProvider1.startObservingMediaRoutes.calls.count()).toBe(1);
+ expect(mockProvider2.startObservingMediaRoutes.calls.count()).toBe(1);
+ expect(mockProvider1.stopObservingMediaRoutes.calls.count()).toBe(1);
+ expect(mockProvider2.stopObservingMediaRoutes.calls.count()).toBe(1);
+ });
+
+ it('Routes query result is sent back to MR initially', function() {
+ providerManager.startObservingMediaRoutes(sourceUrn);
+ expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
+ providerManager.stopObservingMediaRoutes(sourceUrn);
+ expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
+ });
+
+ it('Routes query result is sent back to MR on events', function() {
+ mockClock = mr.UnitTestUtils.useMockClockAndPromises();
+
+ const route = new mr.Route(routeId, 'pId', '0', null, false, '', null);
+
+ providerManager.startObservingMediaRoutes(sourceUrn);
+ // Send routes right away
+ expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
+
+ // Call to MR is throttled.
+ mockClock.tick(mr.ProviderManager.ALL_QUERIES_INTERVAL_MS_ / 2);
+ providerManager.onRouteAdded(mockProvider1, route);
+ expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
+ providerManager.onRouteRemoved(mockProvider1, route);
+ expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
+ providerManager.onRouteAdded(mockProvider1, route);
+ expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
+ mockClock.tick(mr.ProviderManager.ALL_QUERIES_INTERVAL_MS_ + 1);
+ expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(2);
+ });
+
+ it('Routes query result is not sent back to MR if stopped', function() {
+ mockClock = mr.UnitTestUtils.useMockClockAndPromises();
+
+ const route = new mr.Route(routeId, 'pId', '0', null, false, '', null);
+
+ providerManager.startObservingMediaRoutes(sourceUrn);
+ expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
+
+ mockClock.tick(mr.ProviderManager.ALL_QUERIES_INTERVAL_MS_ / 2);
+ providerManager.onRouteAdded(mockProvider1, route);
+ expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
+ providerManager.onRouteRemoved(mockProvider1, route);
+ providerManager.onRouteAdded(mockProvider1, route);
+ providerManager.stopObservingMediaRoutes(sourceUrn);
+ // Query was stopped to MR is not called.
+ mockClock.tick(mr.ProviderManager.ALL_QUERIES_INTERVAL_MS_ / 2 + 1);
+ expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
+
+ });
+
+ it('Multiple route queries do not cause duplicate routes', function() {
+ mockClock = mr.UnitTestUtils.useMockClockAndPromises();
+
+ const route = new mr.Route(routeId, 'pId', '0', null, false, '', null);
+ const sourceUrn2 = '';
+ // Only one route will be returned.
+ mockProvider1.getRoutes.and.returnValue([route]);
+ mockProvider1.canJoin.and.returnValue(false);
+ providerManager.registerAllProviders([mockProvider1]);
+
+ // Multiple route queries means the one route should be returned for
+ // multiple route queries.
+ providerManager.startObservingMediaRoutes(sourceUrn);
+ providerManager.startObservingMediaRoutes(sourceUrn2);
+ // Call to MR is throttled - so this will only happen once.
+ expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
+ expect(mockMediaRouterService.onRoutesUpdated)
+ .toHaveBeenCalledWith([route], sourceUrn, []);
+ mockClock.tick(mr.ProviderManager.ALL_QUERIES_INTERVAL_MS_ + 1);
+ // This will fire twice because there are two route queries, bringing the
+ // total to 3 times.
+ expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(3);
+ expect(mockMediaRouterService.onRoutesUpdated)
+ .toHaveBeenCalledWith([route], sourceUrn, []);
+ expect(mockMediaRouterService.onRoutesUpdated)
+ .toHaveBeenCalledWith([route], sourceUrn2, []);
+ });
+ });
+
+ describe('Test keep alive', function() {
+ it('Keep alive for 1 provider', function() {
+ providerManager.requestKeepAlive(mockProvider1, true);
+ expect(mockMediaRouterService.setKeepAlive).toHaveBeenCalledWith(true);
+ providerManager.requestKeepAlive(mockProvider1, false);
+ expect(mockMediaRouterService.setKeepAlive).toHaveBeenCalledWith(false);
+ });
+
+ it('Keep alive for more than 1 provider', function() {
+ providerManager.requestKeepAlive(mockProvider1, true);
+ expect(mockMediaRouterService.setKeepAlive).toHaveBeenCalledWith(true);
+ providerManager.requestKeepAlive(mockProvider2, true);
+ expect(mockMediaRouterService.setKeepAlive).toHaveBeenCalledWith(true);
+ providerManager.requestKeepAlive(mockProvider1, false);
+ expect(mockMediaRouterService.setKeepAlive).toHaveBeenCalledWith(true);
+ providerManager.requestKeepAlive(mockProvider2, false);
+ expect(mockMediaRouterService.setKeepAlive).toHaveBeenCalledWith(false);
+ });
+ });
+
+ describe('Test createRoute', function() {
+ let sinkId;
+ let localRoute;
+
+ beforeEach(function() {
+ sourceUrn = 'urn:dial-multiscreen-org:dial:application:YouTube';
+ sinkId = 'sink1';
+ localRoute = new mr.Route('r2', 'pId', sinkId, sourceUrn, true, '', null);
+ providerManager.registerAllProviders([mockProvider1, mockProvider2]);
+ });
+
+ it('No provider can handle it', function(done) {
+ mockProvider1.canRoute.and.returnValue(false);
+ mockProvider2.canRoute.and.returnValue(false);
+ providerManager.createRoute(sourceUrn, sinkId, presentationId)
+ .catch(e => {
+ expect(e instanceof mr.RouteRequestError).toBe(true);
+ expect(e.errorCode)
+ .toEqual(mr.RouteRequestResultCode.NO_SUPPORTED_PROVIDER);
+ expect(mockProvider1.canRoute)
+ .toHaveBeenCalledWith(sourceUrn, sinkId);
+ expect(mockProvider2.canRoute)
+ .toHaveBeenCalledWith(sourceUrn, sinkId);
+
+ expect(mockMediaRouterService.setKeepAlive.calls.count()).toBe(0);
+
+ done();
+ });
+ });
+
+ it('One provider can handle, but fail to create session', function(done) {
+ mockProvider1.canRoute.and.returnValue(true);
+ mockProvider2.canRoute.and.returnValue(false);
+ mockProvider1.createRoute.and.returnValue(
+ mr.CancellablePromise.reject(Error('err')));
+ providerManager.createRoute(sourceUrn, sinkId, presentationId)
+ .catch(e => {
+ expect(e instanceof mr.RouteRequestError).toBe(true);
+ expect(e.errorCode)
+ .toEqual(mr.RouteRequestResultCode.UNKNOWN_ERROR);
+ expect(mockProvider1.createRoute.calls.count()).toBe(1);
+ expect(mockProvider1.createRoute)
+ .toHaveBeenCalledWith(
+ sourceUrn, sinkId, presentationId, false,
+ mr.ProviderManager.CREATE_ROUTE_TIMEOUT_MS, undefined,
+ undefined);
+ expect(mockProvider2.createRoute.calls.count()).toBe(0);
+
+ expect(mockMediaRouterService.setKeepAlive.calls.argsFor(0))
+ .toEqual([true]);
+ expect(mockMediaRouterService.setKeepAlive.calls.argsFor(1))
+ .toEqual([false]);
+
+ done();
+ });
+ });
+
+ it('One provider can handle it, and created session', function(done) {
+ mockProvider1.canRoute.and.returnValue(true);
+ mockProvider2.canRoute.and.returnValue(false);
+ mockProvider1.createRoute.and.callFake(() => {
+ providerManager.onRouteAdded(mockProvider1, localRoute);
+ return mr.CancellablePromise.resolve(localRoute);
+ });
+ providerManager.createRoute(sourceUrn, sinkId, presentationId).then(r => {
+ expect(r).toEqual(localRoute);
+ expect(mockProvider1.createRoute.calls.count()).toBe(1);
+ expect(mockProvider1.createRoute)
+ .toHaveBeenCalledWith(
+ sourceUrn, sinkId, presentationId, false,
+ mr.ProviderManager.CREATE_ROUTE_TIMEOUT_MS, undefined,
+ undefined);
+ expect(mockProvider2.createRoute.calls.count()).toBe(0);
+
+ done();
+ });
+ });
+
+ it('createRoute with custom timeout', function(done) {
+ mockProvider1.canRoute.and.returnValue(true);
+ mockProvider2.canRoute.and.returnValue(false);
+ mockProvider1.createRoute.and.callFake(() => {
+ providerManager.onRouteAdded(mockProvider1, localRoute);
+ return mr.CancellablePromise.resolve(localRoute);
+ });
+ const timeoutMillis = 12345;
+ providerManager
+ .createRoute(
+ sourceUrn, sinkId, presentationId, undefined, undefined,
+ timeoutMillis)
+ .then(r => {
+ expect(r).toEqual(localRoute);
+ expect(mockProvider1.createRoute.calls.count()).toBe(1);
+ expect(mockProvider1.createRoute)
+ .toHaveBeenCalledWith(
+ sourceUrn, sinkId, presentationId, false, timeoutMillis,
+ undefined, undefined);
+ expect(mockProvider2.createRoute.calls.count()).toBe(0);
+
+ done();
+ });
+ });
+
+ it('a mirror session is created', function(done) {
+ sourceUrn = mr.MediaSourceUtils.DESKTOP_MIRROR_URN;
+ localRoute = new mr.Route('r2', 'pId', sinkId, sourceUrn, true, '', null);
+ mockProvider1.canRoute.and.returnValue(true);
+ mockProvider2.canRoute.and.returnValue(false);
+ const mirrorSettings = {};
+ mockProvider1.getMirrorSettings.and.returnValue(mirrorSettings);
+ mockProvider1.getMirrorServiceName.and.returnValue(
+ mr.mirror.ServiceName.CAST_STREAMING);
+ mockProvider1.createRoute.and.callFake(() => {
+ providerManager.onRouteAdded(this, localRoute);
+ return mr.CancellablePromise.resolve(localRoute);
+ });
+ mockCastMirrorService.startMirroring.and.returnValue(
+ mr.CancellablePromise.resolve(localRoute));
+ expect(providerManager.getLastUsedMirrorService()).toBeNull();
+ providerManager.createRoute(sourceUrn, sinkId, presentationId)
+ .then(route => {
+ providerManager.startMirroring(mockProvider1, route, presentationId)
+ .promise.then(r => {
+ expect(mockProvider1.createRoute.calls.count()).toBe(1);
+ expect(mockProvider1.createRoute)
+ .toHaveBeenCalledWith(
+ sourceUrn, sinkId, presentationId, false,
+ mr.ProviderManager.CREATE_ROUTE_TIMEOUT_MS, undefined,
+ undefined);
+ expect(mockProvider2.createRoute.calls.count()).toBe(0);
+ expect(mockCastMirrorService.startMirroring.calls.count())
+ .toBe(1);
+ expect(mockCastMirrorService.startMirroring)
+ .toHaveBeenCalledWith(
+ localRoute, sourceUrn, mirrorSettings, presentationId,
+ undefined);
+ expect(providerManager.routeIdToProvider_.has(localRoute.id))
+ .toBe(true);
+ expect(providerManager.routeIdToMirrorServiceName_.has(
+ localRoute.id))
+ .toBe(true);
+ expect(providerManager.getLastUsedMirrorService())
+ .toBe(mr.mirror.ServiceName.CAST_STREAMING);
+ done();
+ });
+ });
+ });
+
+ it('a mirror session is not created', function(done) {
+ sourceUrn = mr.MediaSourceUtils.DESKTOP_MIRROR_URN;
+ localRoute = new mr.Route('r2', 'pId', sinkId, sourceUrn, true, '', null);
+ mockProvider1.canRoute.and.returnValue(true);
+ mockProvider2.canRoute.and.returnValue(false);
+ const mirrorSettings = {};
+ mockProvider1.getMirrorSettings.and.returnValue(mirrorSettings);
+ mockProvider1.getMirrorServiceName.and.returnValue(
+ mr.mirror.ServiceName.CAST_STREAMING);
+ mockProvider1.createRoute.and.callFake(() => {
+ providerManager.onRouteAdded(mockProvider1, localRoute);
+ return mr.CancellablePromise.resolve(localRoute);
+ });
+ const error = Error('failed to mirror');
+ mockCastMirrorService.startMirroring.and.callFake(
+ () => mr.CancellablePromise.reject(error));
+
+ providerManager.createRoute(sourceUrn, sinkId, presentationId)
+ .then(route => {
+ providerManager.startMirroring(mockProvider1, route, presentationId)
+ .promise.catch(err => {
+ expect(err).toEqual(error);
+ expect(mockProvider1.createRoute.calls.count()).toBe(1);
+ expect(mockProvider1.createRoute)
+ .toHaveBeenCalledWith(
+ sourceUrn, sinkId, presentationId, false,
+ mr.ProviderManager.CREATE_ROUTE_TIMEOUT_MS, undefined,
+ undefined);
+ expect(mockProvider2.createRoute.calls.count()).toBe(0);
+ expect(mockCastMirrorService.startMirroring.calls.count())
+ .toBe(1);
+ expect(mockCastMirrorService.startMirroring)
+ .toHaveBeenCalledWith(
+ localRoute, sourceUrn, mirrorSettings, presentationId,
+ undefined);
+ // Call onMirrorSessionEnded as the mirror service would.
+
+ providerManager.onMirrorSessionEnded(route.id);
+ // Call onRouteRemoved as the provider would.
+
+ providerManager.onRouteRemoved(mockProvider1, route);
+ expect(providerManager.routeIdToProvider_.has(localRoute.id))
+ .toBe(false);
+ expect(providerManager.routeIdToMirrorServiceName_.has(
+ localRoute.id))
+ .toBe(false);
+
+ done();
+ });
+ });
+ });
+
+ it('an mr.RouteRequestError is returned for user cancellation', (done) => {
+ mockProvider1.canRoute.and.returnValue(true);
+ mockProvider1.getMirrorSettings.and.returnValue({});
+ mockProvider1.getMirrorServiceName.and.returnValue(
+ mr.mirror.ServiceName.CAST_STREAMING);
+ const error = new mr.mirror.Error(
+ '',
+ mr.MirrorAnalytics.CapturingFailure
+ .CAPTURE_DESKTOP_FAIL_ERROR_USER_CANCEL);
+ mockCastMirrorService.startMirroring.and.callFake(
+ () => mr.CancellablePromise.reject(error));
+
+ providerManager
+ .startMirroring(
+ mockProvider1,
+ new mr.Route(
+ 'r2', 'pId', sinkId, mr.MediaSourceUtils.DESKTOP_MIRROR_URN,
+ true, '', null),
+ presentationId)
+ .promise.catch(err => {
+ expect(err instanceof mr.RouteRequestError).toBe(true);
+ expect(err.errorCode).toBe(mr.RouteRequestResultCode.CANCELLED);
+ done();
+ });
+ });
+
+ it('createRoute with default timeout', function(done) {
+ mockClock = mr.UnitTestUtils.useMockClockAndPromises();
+
+ mockProvider1.canRoute.and.returnValue(true);
+ mockProvider2.canRoute.and.returnValue(false);
+ // The provider will never resolve the promise. Provider manager will
+ // reject it on timeout.
+ mockProvider1.createRoute.and.returnValue(
+ new mr.CancellablePromise(() => {}));
+ providerManager.createRoute(sourceUrn, sinkId, presentationId)
+ .then(
+ r => {
+ fail('Route unexpectedly created');
+ },
+ e => {
+ expect(e instanceof mr.RouteRequestError).toBe(true);
+ expect(e.errorCode)
+ .toEqual(mr.RouteRequestResultCode.TIMED_OUT);
+ expect(e.message).toMatch('timeout');
+ done();
+ });
+ mockClock.tick(mr.ProviderManager.CREATE_ROUTE_TIMEOUT_MS + 1);
+ });
+
+ it('createRoute with custom timeout', function(done) {
+ mockClock = mr.UnitTestUtils.useMockClockAndPromises();
+
+ mockProvider1.canRoute.and.returnValue(true);
+ mockProvider2.canRoute.and.returnValue(false);
+ // The provider will never resolve the promise. Provider manager will
+ // reject it on timeout.
+ mockProvider1.createRoute.and.returnValue(
+ new mr.CancellablePromise(() => {}));
+ const timeoutMillis = 20 * 1000;
+ providerManager
+ .createRoute(
+ sourceUrn, sinkId, 'presentationId', 'origin', 0, timeoutMillis)
+ .then(
+ r => {
+ fail('Route unexpectedly created');
+ },
+ e => {
+ expect(e instanceof mr.RouteRequestError).toBe(true);
+ expect(e.errorCode)
+ .toEqual(mr.RouteRequestResultCode.TIMED_OUT);
+ expect(e.message).toMatch('timeout');
+ done();
+ });
+ mockClock.tick(timeoutMillis + 1);
+ });
+
+ it('update mirror session', function(done) {
+ localRoute = new mr.Route('r2', 'pId', sinkId, sourceUrn, true, '', null);
+ mockProvider1.canRoute.and.returnValue(true);
+ mockProvider2.canRoute.and.returnValue(false);
+ const mirrorSettings = {};
+ mockProvider1.getMirrorSettings.and.returnValue(mirrorSettings);
+ mockProvider1.getMirrorServiceName.and.returnValue(
+ mr.mirror.ServiceName.CAST_STREAMING);
+ mockProvider1.createRoute.and.callFake(() => {
+ providerManager.onRouteAdded(this, localRoute);
+ return mr.CancellablePromise.resolve(localRoute);
+ });
+ mockCastMirrorService.startMirroring.and.returnValue(
+ mr.CancellablePromise.resolve(localRoute));
+ mockCastMirrorService.updateMirroring.and.returnValue(
+ mr.CancellablePromise.resolve(localRoute));
+ providerManager.createRoute(sourceUrn, sinkId, presentationId)
+ .then(route => {
+ providerManager.startMirroring(mockProvider1, route, presentationId)
+ .promise.then(r => {
+ expect(mockProvider1.createRoute.calls.count()).toBe(1);
+ expect(mockProvider1.createRoute)
+ .toHaveBeenCalledWith(
+ sourceUrn, sinkId, presentationId, false,
+ mr.ProviderManager.CREATE_ROUTE_TIMEOUT_MS, undefined,
+ undefined);
+ expect(mockProvider2.createRoute.calls.count()).toBe(0);
+ expect(mockCastMirrorService.startMirroring.calls.count())
+ .toBe(1);
+ expect(mockCastMirrorService.startMirroring)
+ .toHaveBeenCalledWith(
+ localRoute, sourceUrn, mirrorSettings, presentationId,
+ undefined);
+ expect(providerManager.routeIdToProvider_.has(localRoute.id))
+ .toBe(true);
+ expect(providerManager.routeIdToMirrorServiceName_.has(
+ localRoute.id))
+ .toBe(true);
+
+ const newSourceUrn = mr.MediaSourceUtils.DESKTOP_MIRROR_URN;
+ const callback = function() {};
+ providerManager
+ .updateMirroring(
+ mockProvider1, r, newSourceUrn, presentationId,
+ undefined, callback)
+ .promise.then(updatedRoute => {
+ expect(
+ mockCastMirrorService.updateMirroring.calls.count())
+ .toBe(1);
+ expect(mockCastMirrorService.updateMirroring)
+ .toHaveBeenCalledWith(
+ r, newSourceUrn, mirrorSettings, presentationId,
+ undefined, callback);
+ done();
+ });
+ });
+ });
+ });
+
+ it('updateMirroring before startMirroring fails', function(done) {
+ localRoute = new mr.Route('r2', 'pId', sinkId, sourceUrn, true, '', null);
+ const callback = function() {};
+
+ providerManager
+ .updateMirroring(
+ mockProvider1, localRoute, sourceUrn, undefined, callback)
+ .promise.catch(done);
+ });
+ });
+
+ describe('Test PersistentData', function() {
+ it('ProviderManager is restored properly as PersistentData', function() {
+ providerManager.registerAllProviders([mockProvider1]);
+ providerManager.sinkQueries_.add(sourceUrn);
+ providerManager.routeQueries_.add(sourceUrn);
+ providerManager.routeIdToProvider_.set(routeId, mockProvider1);
+ providerManager.sinkAvailabilityMap_.set(
+ mockProvider1Name, mr.SinkAvailability.AVAILABLE);
+
+ const expectedSinkQueries = new Set(providerManager.sinkQueries_);
+ const expectedRouteQueries = new Set(providerManager.routeQueries_);
+
+ const data = providerManager.getData();
+ expect(data.length).toEqual(1);
+ const tempData = data[0];
+ expect(tempData.providerNames).toEqual([mockProvider1Name]);
+ expect(tempData.sinkQueries).toEqual([sourceUrn]);
+ expect(tempData.routeQueries).toEqual([sourceUrn]);
+ expect(tempData.routeIdToProviderName).toEqual([
+ [routeId, mockProvider1Name]
+ ]);
+ expect(tempData.sinkAvailabilityMap).toEqual([
+ [mockProvider1Name, mr.SinkAvailability.AVAILABLE]
+ ]);
+
+ // Data is saved to localStorage.
+ mr.PersistentDataManager.onSuspend_();
+
+ // Make PersistentDataManager forget providerManager, so it can be
+ // registered again.
+ mr.PersistentDataManager.dataInstances_.clear();
+
+ providerManager.routeIdToProvider_.clear();
+ providerManager.sinkQueries_.clear();
+ providerManager.routeQueries_.clear();
+ providerManager.sinkAvailabilityMap_.clear();
+
+ // Load data back to providerManager.
+ mockProvider1.getAvailableSinks.and.returnValue(mr.SinkList.EMPTY);
+ mr.PersistentDataManager.register(providerManager);
+ expect(providerManager.sinkQueries_).toEqual(expectedSinkQueries);
+ expect(providerManager.routeQueries_).toEqual(expectedRouteQueries);
+ expect(providerManager.routeIdToProvider_.get(routeId))
+ .toEqual(mockProvider1);
+ expect(providerManager.sinkAvailabilityMap_.get(mockProvider1Name))
+ .toEqual(mr.SinkAvailability.AVAILABLE);
+ });
+
+ it('ProviderManager calls mDNS callbacks if mDNS enabled', function() {
+ let callbackRuns = 0;
+ const callback = function() {
+ ++callbackRuns;
+ };
+ providerManager.mdnsEnabled_ = true;
+
+ // Data is saved to localStorage.
+ mr.PersistentDataManager.onSuspend_();
+
+ // Prevent immediate callback so we are sure it runs in loadSavedData
+ providerManager.mdnsEnabled_ = false;
+ providerManager.registerMdnsDiscoveryEnabledCallback(callback);
+ expect(callbackRuns).toBe(0);
+
+ // Make PersistentDataManager forget providerManager, so it can be
+ // registered again.
+ mr.PersistentDataManager.dataInstances_.clear();
+
+ // Load data back to providerManager.
+ mockProvider1.getAvailableSinks.and.returnValue(mr.SinkList.EMPTY);
+ mr.PersistentDataManager.register(providerManager);
+ expect(callbackRuns).toBe(1);
+ });
+ });
+
+ describe('Test onRouteRemoved', function() {
+ let sourceUrn;
+ let sinkId;
+ let localRoute;
+
+ beforeEach(function() {
+ sourceUrn = 'urn:dial-multiscreen-org:dial:application:YouTube';
+ sinkId = 'sink1';
+ localRoute = new mr.Route('r2', 'pId', sinkId, sourceUrn, true, '', null);
+ providerManager.registerAllProviders([mockProvider1]);
+ spyOn(providerManager.mrRouteMessageSender_, 'onRouteRemoved')
+ .and.callThrough();
+ sourceUrn = mr.MediaSourceUtils.DESKTOP_MIRROR_URN;
+ localRoute = new mr.Route('r2', 'pId', sinkId, sourceUrn, true, '', null);
+ mockProvider1.canRoute.and.returnValue(true);
+ const mirrorSettings = {};
+ mockProvider1.getMirrorSettings.and.returnValue(mirrorSettings);
+ mockProvider1.getMirrorServiceName.and.returnValue(
+ mr.mirror.ServiceName.CAST_STREAMING);
+ mockProvider1.createRoute.and.callFake(() => {
+ providerManager.onRouteAdded(mockProvider1, localRoute);
+ return mr.CancellablePromise.resolve(localRoute);
+ });
+ mockCastMirrorService.startMirroring.and.returnValue(
+ mr.CancellablePromise.resolve(localRoute));
+ });
+
+ it('On a mirror session stopped', function(done) {
+ let count = 0;
+ const checkDone = () => {
+ if (++count == 2) {
+ expect(providerManager.routeIdToProvider_.has(localRoute.id))
+ .toBe(false);
+ expect(providerManager.routeIdToMirrorServiceName_.has(localRoute.id))
+ .toBe(false);
+ expect(mockCastMirrorService.stopCurrentMirroring).toHaveBeenCalled();
+ expect(providerManager.mrRouteMessageSender_.onRouteRemoved)
+ .toHaveBeenCalledWith(localRoute.id);
+ done();
+ }
+ };
+ mockCastMirrorService.stopCurrentMirroring.and.callFake(checkDone);
+ providerManager.mrRouteMessageSender_.onRouteRemoved.and.callFake(
+ checkDone);
+ providerManager.createRoute(sourceUrn, sinkId, presentationId)
+ .then(route => {
+ providerManager.startMirroring(mockProvider1, route)
+ .promise.then(r => {
+ expect(route.id).toEqual(localRoute.id);
+ expect(providerManager.routeIdToProvider_.has(localRoute.id))
+ .toBe(true);
+ expect(providerManager.routeIdToMirrorServiceName_.has(
+ localRoute.id))
+ .toBe(true);
+ providerManager.onRouteRemoved(mockProvider1, localRoute);
+ });
+ });
+ });
+ });
+
+ it('Test add/remove MediaSinksQuery', function() {
+ const sourceUrn = 'urn:x-org.chromium.media:source:desktop';
+ const sink1 = new mr.Sink('s1', 'sink1');
+ const sink2 = new mr.Sink('s2', 'sink2');
+ const origins = ['https://www.google.com', 'https://youtube.com'];
+ providerManager.registerAllProviders([mockProvider1, mockProvider2]);
+ mockProvider1.getAvailableSinks.and.returnValue(
+ new mr.SinkList([sink1, sink2], origins));
+ mockProvider2.getAvailableSinks.and.returnValue(mr.SinkList.EMPTY);
+ providerManager.startObservingMediaSinks(sourceUrn);
+ expect(mockProvider1.startObservingMediaSinks)
+ .toHaveBeenCalledWith(sourceUrn);
+ expect(mockProvider2.startObservingMediaSinks)
+ .toHaveBeenCalledWith(sourceUrn);
+ expect(mockMediaRouterService.onSinksReceived)
+ .toHaveBeenCalledWith(sourceUrn, [sink1, sink2], jasmine.any(Object));
+ const originList =
+ mockMediaRouterService.onSinksReceived.calls.argsFor(0)[2];
+ expect(originList.length).toBe(2);
+ expect(originList[0].scheme).toBe('https');
+ expect(originList[0].host).toBe('www.google.com');
+ expect(originList[1].scheme).toBe('https');
+ expect(originList[1].host).toBe('youtube.com');
+ // add again
+ providerManager.startObservingMediaSinks(sourceUrn);
+ expect(mockProvider1.startObservingMediaSinks.calls.count()).toBe(1);
+ expect(mockProvider2.startObservingMediaSinks.calls.count()).toBe(1);
+ expect(mockMediaRouterService.onSinksReceived.calls.count()).toBe(2);
+ // remove
+ providerManager.stopObservingMediaSinks(sourceUrn);
+ expect(mockProvider1.stopObservingMediaSinks)
+ .toHaveBeenCalledWith(sourceUrn);
+ expect(mockProvider2.stopObservingMediaSinks)
+ .toHaveBeenCalledWith(sourceUrn);
+ // add again
+ providerManager.startObservingMediaSinks(sourceUrn);
+ expect(mockProvider1.startObservingMediaSinks.calls.count()).toBe(2);
+ expect(mockProvider2.startObservingMediaSinks.calls.count()).toBe(2);
+ expect(mockMediaRouterService.onSinksReceived.calls.count()).toBe(3);
+ });
+
+ it('Test onSinkAvailabilityUpdated', function() {
+ providerManager.registerAllProviders([mockProvider1, mockProvider2]);
+
+ providerManager.onSinkAvailabilityUpdated(
+ mockProvider1, mr.SinkAvailability.UNAVAILABLE);
+ expect(mockMediaRouterService.onSinkAvailabilityUpdated)
+ .not.toHaveBeenCalled();
+
+ providerManager.onSinkAvailabilityUpdated(
+ mockProvider1, mr.SinkAvailability.PER_SOURCE);
+ expect(mockMediaRouterService.onSinkAvailabilityUpdated)
+ .toHaveBeenCalledWith(mr.SinkAvailability.PER_SOURCE);
+
+ providerManager.onSinkAvailabilityUpdated(
+ mockProvider2, mr.SinkAvailability.PER_SOURCE);
+ expect(mockMediaRouterService.onSinkAvailabilityUpdated.calls.count())
+ .toBe(1);
+
+ providerManager.onSinkAvailabilityUpdated(
+ mockProvider2, mr.SinkAvailability.AVAILABLE);
+ expect(mockMediaRouterService.onSinkAvailabilityUpdated)
+ .toHaveBeenCalledWith(mr.SinkAvailability.AVAILABLE);
+
+ providerManager.onSinkAvailabilityUpdated(
+ mockProvider1, mr.SinkAvailability.UNAVAILABLE);
+ expect(mockMediaRouterService.onSinkAvailabilityUpdated.calls.count())
+ .toBe(2);
+
+ providerManager.onSinkAvailabilityUpdated(
+ mockProvider2, mr.SinkAvailability.UNAVAILABLE);
+ expect(mockMediaRouterService.onSinkAvailabilityUpdated)
+ .toHaveBeenCalledWith(mr.SinkAvailability.UNAVAILABLE);
+ });
+
+ it('Test mDNS callbacks wait until mDNS is enabled', function() {
+ providerManager.mdnsEnabled_ = true;
+ let callbackRuns = 0;
+ const callback = function() {
+ ++callbackRuns;
+ };
+ providerManager.registerMdnsDiscoveryEnabledCallback(callback);
+ // Execute immediately when mDNS discovery is enabled.
+ expect(callbackRuns).toBe(1);
+
+ callbackRuns = 0;
+ providerManager.mdnsEnabled_ = false;
+ providerManager.registerMdnsDiscoveryEnabledCallback(callback);
+ // Defer execution until mDNS discovery is enabled.
+ expect(callbackRuns).toBe(0);
+ providerManager.enableMdnsDiscovery();
+ expect(callbackRuns).toBe(1);
+ });
+
+ it('Test mDNS callback registration disallows duplicates', function() {
+ providerManager.mdnsEnabled_ = true;
+ let callbackRuns = 0;
+ const callback = function() {
+ ++callbackRuns;
+ };
+
+ providerManager.mdnsEnabled_ = false;
+ providerManager.registerMdnsDiscoveryEnabledCallback(callback);
+ providerManager.registerMdnsDiscoveryEnabledCallback(callback);
+ // Defer execution until mDNS discovery is enabled and ensure only called
+ // once.
+ expect(callbackRuns).toBe(0);
+ providerManager.enableMdnsDiscovery();
+ expect(callbackRuns).toBe(1);
+ });
+
+ describe('searchSinks', function() {
+ const getSearchCriteria = function(input) {
+ return {'input': input, 'domain': 'google.com'};
+ };
+ let pseudoId;
+
+ beforeEach(function() {
+ sourceUrn = 'urn:x-org.chromium.media:source:desktop';
+ pseudoId = 'pseudo:' + mockProvider1Name;
+ providerManager.registerAllProviders([mockProvider1, mockProvider2]);
+ });
+
+ it('returns the sink id if the provider returns a sink', done => {
+ const sinkId = 'sinkId1';
+ const sinkName = 'sink name';
+ const foundSink = new mr.Sink(sinkId, sinkName);
+ mockProvider1.searchSinks.and.returnValue(Promise.resolve(foundSink));
+
+ providerManager
+ .searchSinks(pseudoId, sourceUrn, getSearchCriteria(sinkName))
+ .then((resolvedSinkId) => {
+ expect(resolvedSinkId).toBe(sinkId);
+ done();
+ });
+ });
+
+ it('returns nothing if the provider returns nothing', done => {
+ mockProvider1.searchSinks.and.returnValue(
+ Promise.resolve(new mr.Sink('', '')));
+ providerManager
+ .searchSinks(pseudoId, sourceUrn, getSearchCriteria('sink name'))
+ .then((resolvedSinkId) => expect(resolvedSinkId).toBe(''))
+ .then(done, done.fail);
+ });
+
+ it('returns nothing when no provider owns the pseudo sink', done => {
+ pending(
+ 'This may never have worked, because the test ' +
+ 'wasn\'t checking what it was supposed to check.');
+ pseudoId = 'pseudo:bad';
+ providerManager
+ .searchSinks(pseudoId, sourceUrn, getSearchCriteria('sink name'))
+ .then((resolvedSinkId) => expect(resolvedSinkId).toBe(''))
+ .then(done, done.fail);
+ });
+ });
+
+ describe('updateMediaSinks', function() {
+ const sink1 = new mr.Sink('s1', 'sink1');
+ const sink2 = new mr.Sink('s2', 'sink2');
+
+ beforeEach(function() {
+ sourceUrn = 'urn:x-org.chromium.media:source:desktop';
+ providerManager.registerAllProviders([mockProvider1, mockProvider2]);
+ mockProvider1.getAvailableSinks.and.returnValue(
+ new mr.SinkList([sink1, sink2], ['https://www.google.com']));
+ mockProvider2.getAvailableSinks.and.returnValue(mr.SinkList.EMPTY);
+ });
+
+ it('queries providers with desktop source', function() {
+ providerManager.updateMediaSinks(sourceUrn);
+ expect(mockProvider1.getAvailableSinks).toHaveBeenCalledWith(sourceUrn);
+ expect(mockProvider2.getAvailableSinks).toHaveBeenCalledWith(sourceUrn);
+ expect(mockMediaRouterService.onSinksReceived)
+ .toHaveBeenCalledWith(
+ sourceUrn, [sink1, sink2], [jasmine.any(Object)]);
+ const originList =
+ mockMediaRouterService.onSinksReceived.calls.argsFor(0)[2];
+ expect(originList.length).toBe(1);
+ expect(originList[0].scheme).toBe('https');
+ expect(originList[0].host).toBe('www.google.com');
+ });
+
+ it('returns immediately if query already exists', function() {
+ providerManager.sinkQueries_.add(sourceUrn);
+ // updateMediaSinks should return and not touch providers
+ providerManager.updateMediaSinks(sourceUrn);
+ expect(mockProvider1.getAvailableSinks).not.toHaveBeenCalled();
+ expect(mockProvider2.getAvailableSinks).not.toHaveBeenCalled();
+ expect(mockMediaRouterService.onSinksReceived).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('calls terminateRoute', function() {
+ const routeIdToTerminate = 'deadbeef';
+
+ beforeEach(function() {
+ providerManager.registerAllProviders([mockProvider1]);
+ providerManager.routeIdToProvider_.set(routeIdToTerminate, mockProvider1);
+ });
+
+ it('with correct routeId', function(done) {
+ mockProvider1.terminateRoute.and.callFake(routeId => {
+ expect(routeId).toBe(routeIdToTerminate);
+ done();
+ });
+ providerManager.terminateRoute(routeIdToTerminate);
+ });
+
+ it('and returns a rejected promise when it fails', function(done) {
+ mockProvider1.terminateRoute.and.callFake(routeId => {
+ expect(routeId).toBe(routeIdToTerminate);
+ return Promise.reject(new Error('Some error'));
+ });
+ providerManager.terminateRoute(routeIdToTerminate).then(done.fail, done);
+ });
+
+ it('and terminates a mirroring route', function(done) {
+ providerManager.routeIdToMirrorServiceName_.set(
+ routeIdToTerminate, mr.mirror.ServiceName.CAST_STREAMING);
+ providerManager.terminateRoute(routeIdToTerminate).then(() => {
+ expect(mockProvider1.terminateRoute)
+ .toHaveBeenCalledWith(routeIdToTerminate);
+ expect(mockCastMirrorService.stopCurrentMirroring).toHaveBeenCalled();
+ expect(
+ providerManager.routeIdToMirrorServiceName_.has(routeIdToTerminate))
+ .toBe(false);
+ done();
+ });
+ });
+ });
+
+ describe('createMediaRouteController tests', () => {
+ const routeId = 'deadbeef';
+ let controller;
+ let observer;
+
+ beforeEach(() => {
+ controller = mr.UnitTestUtils.createMojoRequestSpyObj();
+ observer = mr.UnitTestUtils.createMojoMediaStatusObserverSpyObj();
+ providerManager.registerAllProviders([mockProvider1]);
+ providerManager.routeIdToProvider_.set(routeId, mockProvider1);
+ });
+
+ it('succeeds', done => {
+ mockProvider1.createMediaRouteController.and.callFake(
+ () => Promise.resolve());
+ providerManager.createMediaRouteController(routeId, controller, observer)
+ .then(() => {
+ expect(mockProvider1.createMediaRouteController).toHaveBeenCalled();
+ expect(controller.close).not.toHaveBeenCalled();
+ expect(observer.ptr.reset).not.toHaveBeenCalled();
+ done();
+ }, fail);
+ });
+
+ it('fails if route does not exist', done => {
+ providerManager
+ .createMediaRouteController(
+ 'nonexistentRouteId', controller, observer)
+ .then(fail, () => {
+ expect(controller.close).toHaveBeenCalled();
+ expect(observer.ptr.reset).toHaveBeenCalled();
+ done();
+ });
+ });
+
+ it('fails if provider fails', done => {
+ mockProvider1.createMediaRouteController.and.callFake(
+ () => Promise.reject('Foo'));
+ providerManager.createMediaRouteController(routeId, controller, observer)
+ .then(fail, () => {
+ expect(mockProvider1.createMediaRouteController).toHaveBeenCalled();
+ expect(controller.close).toHaveBeenCalled();
+ expect(observer.ptr.reset).toHaveBeenCalled();
+ done();
+ });
+ });
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/manager/route_id.js b/chromium/chrome/browser/resources/media_router/extension/src/manager/route_id.js
new file mode 100644
index 00000000000..a5d9f37a9d0
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/manager/route_id.js
@@ -0,0 +1,114 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview A routeId includes presentation ID, provider name,
+ * sink ID and media source. See mr.RouteId.getRouteId method for its
+ * format (note the different formatting of routeId for 2UA cases).
+ */
+
+goog.provide('mr.RouteId');
+
+goog.require('mr.MediaSourceUtils');
+
+/**
+ * @final
+ */
+mr.RouteId = class {
+ constructor() {
+ /** @private {string} */
+ this.routeId_;
+
+ /** @private {string} */
+ this.presentationId_;
+
+ /** @private {string} */
+ this.providerName_;
+
+ /** @private {string} */
+ this.sinkId_;
+
+ /** @private {string} */
+ this.source_;
+ }
+
+ /**
+ * @param {string} routeId
+ * @return {?mr.RouteId} A route ID object if the input string is valid;
+ * null otherwise.
+ */
+ static create(routeId) {
+ if (!routeId.startsWith(mr.RouteId.PREFIX_)) {
+ return null;
+ }
+ const suffix = routeId.substring(mr.RouteId.PREFIX_.length);
+ if (!suffix) {
+ return null;
+ }
+ const match = suffix.match(/([^/]*)\/([^-/]*)-([^/]*)\/(.*)/);
+ if (!match) {
+ return null;
+ }
+ const obj = new mr.RouteId();
+ obj.routeId_ = routeId;
+ [, obj.presentationId_, obj.providerName_, obj.sinkId_, obj.source_] =
+ match;
+ return obj;
+ }
+
+ /**
+ * @param {string} presentationId
+ * @param {string} providerName
+ * @param {string} sinkId
+ * @param {?string} source
+ * @return {string}
+ */
+ static getRouteId(presentationId, providerName, sinkId, source) {
+ // In a 2UA mode, the routeId should be the presentationId.
+ if (source && mr.MediaSourceUtils.isPresentationSource(source)) {
+ return presentationId;
+ }
+ return mr.RouteId.PREFIX_ + presentationId + '/' + providerName + '-' +
+ sinkId + '/' + source;
+ }
+
+ /**
+ * @return {string}
+ */
+ getRouteId() {
+ return this.routeId_;
+ }
+
+ /**
+ * @return {string}
+ */
+ getPresentationId() {
+ return this.presentationId_;
+ }
+
+ /**
+ * @return {string}
+ */
+ getProviderName() {
+ return this.providerName_;
+ }
+
+ /**
+ * @return {string}
+ */
+ getSinkId() {
+ return this.sinkId_;
+ }
+
+ /**
+ * @return {string}
+ */
+ getSource() {
+ return this.source_;
+ }
+};
+
+
+/** @private @const {string} */
+mr.RouteId.PREFIX_ = 'urn:x-org.chromium:media:route:';
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/manager/route_id_test.js b/chromium/chrome/browser/resources/media_router/extension/src/manager/route_id_test.js
new file mode 100644
index 00000000000..daff81e818f
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/manager/route_id_test.js
@@ -0,0 +1,41 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.require('mr.RouteId');
+
+describe('Tests RouteId', function() {
+ it('Test getRouteId', function() {
+ expect(mr.RouteId.getRouteId('123', 'cast', 'sink1', 'some-source'))
+ .toEqual(mr.RouteId.PREFIX_ + '123/cast-sink1/some-source');
+ });
+
+ it('Test getRouteId in 2UA mode', function() {
+ expect(mr.RouteId.getRouteId('123', 'cast', 'sink1', 'http://mysite.com'))
+ .toEqual('123');
+ });
+
+ it('Test getRouteId with valid input', function() {
+ let routeId = mr.RouteId.PREFIX_ + '123/cast-sink1/some-source';
+ let obj = mr.RouteId.create(routeId);
+ expect(obj.getRouteId()).toEqual(routeId);
+ expect(obj.getPresentationId()).toEqual('123');
+ expect(obj.getProviderName()).toEqual('cast');
+ expect(obj.getSinkId()).toEqual('sink1');
+ expect(obj.getSource()).toEqual('some-source');
+
+ routeId = mr.RouteId.PREFIX_ + '/cast-sink1/some-source';
+ obj = mr.RouteId.create(routeId);
+ expect(obj.getRouteId()).toEqual(routeId);
+ expect(obj.getPresentationId()).toEqual('');
+ expect(obj.getProviderName()).toEqual('cast');
+ expect(obj.getSinkId()).toEqual('sink1');
+ expect(obj.getSource()).toEqual('some-source');
+ });
+
+ it('Test getRouteId with invalid input 1', function() {
+ expect(mr.RouteId.create('123')).toBe(null);
+ expect(mr.RouteId.create(mr.RouteId.PREFIX_ + '123/cast-sink1')).toBe(null);
+ expect(mr.RouteId.create(mr.RouteId.PREFIX_ + '123/cast-')).toBe(null);
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/manager/route_message_port.js b/chromium/chrome/browser/resources/media_router/extension/src/manager/route_message_port.js
new file mode 100644
index 00000000000..d6cae814fe9
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/manager/route_message_port.js
@@ -0,0 +1,74 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview API for posting messages to a route and getting messages from
+ * the route.
+ */
+
+goog.provide('mr.MessagePort');
+goog.provide('mr.MessagePortService');
+
+
+
+/**
+ * @record
+ */
+mr.MessagePort = class {
+ /**
+ * Called to post a message to the sink via a media route.
+ * @param {!Object|string} message The message to post. Object will be
+ * serialized to a JSON string.
+ * @param {Object=} opt_extraInfo Extra info about how to send a message.
+ * @return {!Promise} Fulfilled when the message is posted, or rejected
+ * if there an error.
+ */
+ sendMessage(message, opt_extraInfo) {}
+
+ /**
+ * Releases any resources used by this object.
+ */
+ dispose() {}
+};
+
+
+/**
+ * The message handler to invoke when messages associated
+ * with the route arrives.
+ * @type {function((!Object|string))}
+ */
+mr.MessagePort.prototype.onMessage;
+
+
+
+/**
+ * @record
+ */
+mr.MessagePortService = class {
+ /**
+ * Returns the MessagePort for internal communication with the provider.
+ * @param {string} routeId
+ * @return {!mr.MessagePort}
+ */
+ getInternalMessenger(routeId) {}
+ /**
+ * @return {!mr.MessagePortService}
+ */
+ static getService() {
+ return mr.MessagePortService.service_;
+ }
+
+ /**
+ * @param {!mr.MessagePortService} service
+ */
+ static setService(service) {
+ if (!mr.MessagePortService.service_) {
+ mr.MessagePortService.service_ = service;
+ }
+ }
+};
+
+
+/** @private {!mr.MessagePortService} */
+mr.MessagePortService.service_;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/manager/route_message_port_impl.js b/chromium/chrome/browser/resources/media_router/extension/src/manager/route_message_port_impl.js
new file mode 100644
index 00000000000..78efd2d018c
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/manager/route_message_port_impl.js
@@ -0,0 +1,107 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+
+goog.provide('mr.MessagePortServiceImpl');
+
+goog.require('mr.EventTarget');
+goog.require('mr.MessagePort');
+goog.require('mr.MessagePortService');
+goog.require('mr.ProviderEventType');
+
+
+/**
+ * @template T
+ * @implements {mr.MessagePort}
+ * @private
+ */
+mr.MessagePortImpl_ = class {
+ /**
+ * @param {string} routeId
+ * @param {function(string, (!Object|string), Object=): !Promise}
+ * sendMessage
+ * @param {!mr.EventTarget} messageEventTarget
+ */
+ constructor(routeId, sendMessage, messageEventTarget) {
+ /** @private {string} */
+ this.routeId_ = routeId;
+
+ /** @private {function(string, (!Object|string), Object=): !Promise} */
+ this.sendMessage_ = sendMessage;
+
+ /** @private {!mr.EventTarget} */
+ this.messageEventTarget_ = messageEventTarget;
+
+ /** @type {function((!Object|string))} */
+ this.onMessage = () => {};
+
+ messageEventTarget.listen(
+ mr.ProviderEventType.INTERNAL_MESSAGE, this.onRouteMessage_, this);
+ }
+
+ /**
+ * @override
+ */
+ sendMessage(message, opt_extraInfo) {
+ return this.sendMessage_(this.routeId_, message, opt_extraInfo);
+ }
+
+ /**
+ * Called when a message is received.
+ *
+ * @param {mr.InternalMessageEvent.<!Object|string>} event
+ * @private
+ */
+ onRouteMessage_(event) {
+ if (event.routeId != this.routeId_) {
+ return;
+ }
+ this.onMessage(event.message);
+ }
+
+ /**
+ * @override
+ */
+ dispose() {
+
+ this.onMessage = () => {};
+ this.messageEventTarget_.unlisten(
+ mr.ProviderEventType.INTERNAL_MESSAGE, this.onRouteMessage_, this);
+ }
+};
+
+
+/** @private @const {!mr.MessagePort} */
+mr.MessagePortImpl_.NULL_INSTANCE_ =
+ new mr.MessagePortImpl_('', () => Promise.resolve(), new mr.EventTarget());
+
+
+/**
+ * @implements {mr.MessagePortService}
+ */
+mr.MessagePortServiceImpl = class {
+ /**
+ * @param {!mr.ProviderManagerCallbacks} providerManagerCallbacks
+ */
+ constructor(providerManagerCallbacks) {
+ /**
+ * @private {!mr.ProviderManagerCallbacks}
+ */
+ this.providerManagerCallbacks_ = providerManagerCallbacks;
+ }
+
+ /**
+ * @override
+ */
+ getInternalMessenger(routeId) {
+ const provider =
+ this.providerManagerCallbacks_.getProviderFromRouteId(routeId);
+ return !provider ?
+ mr.MessagePortImpl_.NULL_INSTANCE_ :
+ new mr.MessagePortImpl_(
+ routeId, provider.sendRouteMessage.bind(provider),
+ this.providerManagerCallbacks_.getRouteMessageEventTarget());
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/manager/sink_availability.js b/chromium/chrome/browser/resources/media_router/extension/src/manager/sink_availability.js
new file mode 100644
index 00000000000..5c3c1ce0862
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/manager/sink_availability.js
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Enum for per-media-route-provider sink availability.
+ */
+
+goog.provide('mr.SinkAvailability');
+
+
+/**
+ * Per-provider sink availability.
+ * Keep in sync with MediaRouter.SinkAvailability in media_router.mojom.
+ * @enum {number}
+ */
+mr.SinkAvailability = {
+ UNAVAILABLE: 0,
+ PER_SOURCE: 1,
+ AVAILABLE: 2
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_activity.js b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_activity.js
new file mode 100644
index 00000000000..df658d67a66
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_activity.js
@@ -0,0 +1,150 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Mirroring activities for display in the UI.
+ */
+
+goog.module('mr.mirror.Activity');
+goog.module.declareLegacyNamespace();
+
+const Assertions = goog.require('mr.Assertions');
+const MediaSourceUtils = goog.require('mr.MediaSourceUtils');
+
+
+/**
+ * Possible mirroring activities.
+ * @enum {string}
+ */
+const Type = {
+ MIRROR_TAB: 'mirror_tab',
+ MIRROR_DESKTOP: 'mirror_desktop',
+ MIRROR_FILE: 'mirror_file',
+ MEDIA_REMOTING: 'media_remoting',
+ PRESENTATION: 'presentation'
+};
+
+
+/**
+ * Describes a mirroring activity for display in the UI.
+ */
+const Activity = class {
+ /**
+ * Constructs a new Activity describing a mirroring route. origin is
+ * required, unless the activityType is MIRROR_DESKTOP. Both contentTitle and
+ * origin may change over the course of a mirroring activity from tab
+ * navigation.
+ *
+ * @param {!Type} type
+ * @param {boolean} incognito
+ * @param {?string=} origin The top level origin of the tab being cast.
+ */
+ constructor(type, incognito, origin = null) {
+ /** @private {!Type} */
+ this.type_ = type;
+
+ /** @private {boolean} */
+ this.incognito_ = incognito;
+
+ /** @private {?string} */
+ this.origin_ = origin;
+
+ /** @private {?string} */
+ this.contentTitle_ = null;
+ }
+
+ /**
+ * @param {!mr.Route} route A mirroring route
+ * @return {!Activity} Intital activity object for route
+ */
+ static createFromRoute(route) {
+ let type;
+ let origin = null;
+ const source = /** @type {string} */ (Assertions.assert(route.mediaSource));
+ if (MediaSourceUtils.isPresentationSource(source)) {
+ type = Type.PRESENTATION;
+ origin = new URL(source).origin;
+ } else if (MediaSourceUtils.isTabMirrorSource(source)) {
+ // Tab mirroring routes may switch to MEDIA_REMOTING or MIRROR_FILE after
+ // they have begun.
+ type = Type.MIRROR_TAB;
+ } else if (MediaSourceUtils.isDesktopMirrorSource(source)) {
+ type = Type.MIRROR_DESKTOP;
+ }
+ Assertions.assert(type, `Unexpected mediaSource ${source}`);
+ return new Activity(
+ /** @type {Type} */ (type), route.offTheRecord, origin);
+ }
+
+ /** @param {Type} type Sets the activity type. */
+ setType(type) {
+ this.type_ = type;
+ }
+
+ /** @param {?string} origin Sets the current origin of the activity. */
+ setOrigin(origin) {
+ this.origin_ = origin;
+ }
+
+ /** @param {?string} contentTitle Sets the content title of the activity. */
+ setContentTitle(contentTitle) {
+ this.contentTitle_ = contentTitle;
+ }
+
+ /** @param {boolean} incognito Sets the incognito status of the activity. */
+ setIncognito(incognito) {
+ this.incognito_ = incognito;
+ }
+
+ /** @return {string} The string to use for the route's description. */
+ getRouteDescription() {
+ switch (this.type_) {
+ case Type.MIRROR_TAB:
+ return this.origin_ ? `Casting tab (${this.origin_})` : 'Casting tab';
+ case Type.MIRROR_DESKTOP:
+ return 'Casting desktop';
+ case Type.MIRROR_FILE:
+ return 'Casting local content';
+ case Type.MEDIA_REMOTING:
+ return this.origin_ ? `Casting media (${this.origin_})` :
+ `Casting media`;
+ case Type.PRESENTATION:
+ return `Casting ${this.origin_ || 'site'}`;
+ default:
+ Assertions.assert(false, 'Unexpected type ' + this.type_);
+ return '';
+ }
+ }
+
+ /** @return {string} The string to use for the route's media status. */
+ getRouteMediaStatus() {
+ if (this.type_ == Type.MIRROR_DESKTOP) {
+ return '';
+ }
+ return this.contentTitle_ || '';
+ }
+
+ /** @return {string} The string to broadcast to other Cast senders. */
+ getCastRemoteTitle() {
+ if (this.incognito_) return 'Casting active';
+ switch (this.type_) {
+ case Type.MIRROR_TAB:
+ return 'Casting tab';
+ case Type.MIRROR_DESKTOP:
+ return 'Casting desktop';
+ case Type.MIRROR_FILE:
+ return 'Casting local content';
+ case Type.MEDIA_REMOTING:
+ return 'Casting media';
+ case Type.PRESENTATION:
+ return 'Casting site';
+ default:
+ Assertions.assert(false, 'Unexpected type ' + this.type_);
+ return '';
+ }
+ }
+};
+
+exports = Activity;
+exports.Type = Type;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_activity_test.js b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_activity_test.js
new file mode 100644
index 00000000000..e431bd7dd14
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_activity_test.js
@@ -0,0 +1,93 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.setTestOnly();
+goog.require('mr.Route');
+goog.require('mr.mirror.Activity');
+
+describe('Tests mr.mirror.Activity', () => {
+ let tabMirrorRoute;
+ let desktopMirrorRoute;
+ let presentationRoute;
+
+ beforeEach(() => {
+ tabMirrorRoute = new mr.Route(
+ 'routeId', 'presentationId', 'sinkId',
+ 'urn:x-org.chromium.media:source:tab:47', true, null);
+ desktopMirrorRoute = new mr.Route(
+ 'routeId2', 'presentationId2', 'sinkId2',
+ 'urn:x-org.chromium.media:source:desktop', true, null);
+ presentationRoute = new mr.Route(
+ 'routeId3', 'presentationId3', 'sinkId3', 'https://www.example.com',
+ true, null);
+ });
+
+ it('creates activity from a tab mirroring route', () => {
+ let activity = mr.mirror.Activity.createFromRoute(tabMirrorRoute);
+ expect(activity.getRouteDescription()).toBe('Casting tab');
+ expect(activity.getRouteMediaStatus()).toBe('');
+ expect(activity.getCastRemoteTitle()).toBe('Casting tab');
+ });
+
+ it('creates activity from a desktop mirroring route', () => {
+ let activity = mr.mirror.Activity.createFromRoute(desktopMirrorRoute);
+ expect(activity.getRouteDescription()).toBe('Casting desktop');
+ expect(activity.getRouteMediaStatus()).toBe('');
+ expect(activity.getCastRemoteTitle()).toBe('Casting desktop');
+ });
+
+ it('creates activity from a presentation route', () => {
+ let activity = mr.mirror.Activity.createFromRoute(presentationRoute);
+ expect(activity.getRouteDescription())
+ .toBe('Casting https://www.example.com');
+ expect(activity.getRouteMediaStatus()).toBe('');
+ expect(activity.getCastRemoteTitle()).toBe('Casting site');
+ });
+
+ it('uses the tab origin and page title for tab mirroring', () => {
+ let activity = mr.mirror.Activity.createFromRoute(tabMirrorRoute);
+ activity.setOrigin('news.google.com');
+ activity.setContentTitle('Google News');
+ expect(activity.getRouteDescription())
+ .toBe('Casting tab (news.google.com)');
+ expect(activity.getRouteMediaStatus()).toBe('Google News');
+ expect(activity.getCastRemoteTitle()).toBe('Casting tab');
+ });
+
+ it('uses the tab origin and page title for presentation', () => {
+ let activity = mr.mirror.Activity.createFromRoute(presentationRoute);
+ activity.setOrigin('www.example.com');
+ activity.setContentTitle('Some Presentation');
+ expect(activity.getRouteDescription()).toBe('Casting www.example.com');
+ expect(activity.getRouteMediaStatus()).toBe('Some Presentation');
+ expect(activity.getCastRemoteTitle()).toBe('Casting site');
+ });
+
+ it('updates the remote title for incognito', () => {
+ let activity = mr.mirror.Activity.createFromRoute(tabMirrorRoute);
+ activity.setIncognito(true);
+ expect(activity.getCastRemoteTitle()).toBe('Casting active');
+ });
+
+ it('updates the activity for remoting', () => {
+ let activity = mr.mirror.Activity.createFromRoute(tabMirrorRoute);
+ activity.setOrigin('www.vimeo.com');
+ activity.setContentTitle('Vimeo');
+ activity.setType(mr.mirror.Activity.Type.MEDIA_REMOTING);
+ expect(activity.getRouteDescription())
+ .toBe('Casting media (www.vimeo.com)');
+ expect(activity.getRouteMediaStatus()).toBe('Vimeo');
+ expect(activity.getCastRemoteTitle()).toBe('Casting media');
+ });
+
+ it('updates the activity for mirroring local media', () => {
+ let activity = mr.mirror.Activity.createFromRoute(tabMirrorRoute);
+ activity.setOrigin('');
+ activity.setContentTitle('some_file.mp4');
+ activity.setType(mr.mirror.Activity.Type.MIRROR_FILE);
+ expect(activity.getRouteDescription()).toBe('Casting local content');
+ expect(activity.getRouteMediaStatus()).toBe('some_file.mp4');
+ expect(activity.getCastRemoteTitle()).toBe('Casting local content');
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_analytics.js b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_analytics.js
new file mode 100644
index 00000000000..20ea156371e
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_analytics.js
@@ -0,0 +1,74 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Defines UMA analytics specific to Mirroring.
+ */
+
+goog.provide('mr.MirrorAnalytics');
+goog.provide('mr.mirror.Error');
+
+goog.require('mr.Analytics');
+
+
+/**
+ * Contains all common analytics logic for Mirroring.
+ * @const {*}
+ */
+mr.MirrorAnalytics = {};
+
+
+/**
+ * Histogram name for mirroring start failure.
+ * @private @const {string}
+ */
+mr.MirrorAnalytics.CAPTURING_FAILURE_HISTOGRAM_ =
+ 'MediaRouter.Mirror.Capturing.Failure';
+
+
+/**
+ * Possible values for the start failure analytics.
+ * @enum {number}
+ */
+mr.MirrorAnalytics.CapturingFailure = {
+ CAPTURE_TAB_FAIL_EMPTY_STREAM: 0,
+ CAPTURE_DESKTOP_FAIL_ERROR_TIMEOUT: 1,
+ CAPTURE_TAB_TIMEOUT: 2,
+ CAPTURE_DESKTOP_FAIL_ERROR_USER_CANCEL: 3,
+ ANSWER_NOT_RECEIVED: 4,
+ CAPTURE_TAB_FAIL_ERROR_TIMEOUT: 5,
+ ICE_CONNECTION_CLOSED: 6,
+ TAB_FAIL: 7,
+ DESKTOP_FAIL: 8,
+ UNKNOWN: 9,
+};
+
+
+/**
+ * Records a mirroring start failure.
+ * @param {mr.MirrorAnalytics.CapturingFailure} reason The type of failure.
+ * @param {string} name The name of the histogram.
+ */
+mr.MirrorAnalytics.recordCapturingFailureWithName = function(reason, name) {
+ mr.Analytics.recordEnum(name, reason, mr.MirrorAnalytics.CapturingFailure);
+};
+
+
+/**
+ * Error thrown when initiating a mirroring session.
+ */
+mr.mirror.Error = class extends Error {
+ /**
+ * @param {string} message The error message.
+ * @param {mr.MirrorAnalytics.CapturingFailure=} reason The failure reason.
+ */
+ constructor(message, reason = undefined) {
+ super(message);
+ /** {!mr.MirrorAnalytics.CapturingFailure} The failure reason. */
+ this.reason =
+ (reason >= 0 && reason <= mr.MirrorAnalytics.CapturingFailure.UNKNOWN) ?
+ reason :
+ mr.MirrorAnalytics.CapturingFailure.UNKNOWN;
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_analytics_test.js b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_analytics_test.js
new file mode 100644
index 00000000000..5bc5596eb43
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_analytics_test.js
@@ -0,0 +1,111 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.setTestOnly();
+goog.require('mr.MirrorAnalytics');
+
+describe('Tests Analytics', function() {
+ const metricName = 'MediaRouter.Fake.Start.Failure';
+
+ beforeEach(function() {
+ chrome.metricsPrivate = {
+ recordTime: jasmine.createSpy('recordTime'),
+ recordMediumTime: jasmine.createSpy('recordMediumTime'),
+ recordLongTime: jasmine.createSpy('recordLongTime'),
+ recordUserAction: jasmine.createSpy('recordUserAction'),
+ recordValue: jasmine.createSpy('recordValue'),
+ };
+ });
+
+ describe('Test Mirror Analytics', function() {
+ describe('Test recordCapturingFailure', function() {
+ const testConfig = {
+ 'metricName': metricName,
+ 'type': 'histogram-linear',
+ 'min': 1,
+ 'max': 10,
+ 'buckets': 11
+ };
+ it('Should record an empty stream error', function() {
+ mr.MirrorAnalytics.recordCapturingFailureWithName(
+ mr.MirrorAnalytics.CapturingFailure.CAPTURE_TAB_FAIL_EMPTY_STREAM,
+ metricName);
+ expect(chrome.metricsPrivate.recordValue.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordValue)
+ .toHaveBeenCalledWith(testConfig, 0);
+ });
+ it('Should record a desktop timeout error', function() {
+ mr.MirrorAnalytics.recordCapturingFailureWithName(
+ mr.MirrorAnalytics.CapturingFailure
+ .CAPTURE_DESKTOP_FAIL_ERROR_TIMEOUT,
+ metricName);
+ expect(chrome.metricsPrivate.recordValue.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordValue)
+ .toHaveBeenCalledWith(testConfig, 1);
+ });
+ it('Should record a tab timeout error', function() {
+ mr.MirrorAnalytics.recordCapturingFailureWithName(
+ mr.MirrorAnalytics.CapturingFailure.CAPTURE_TAB_TIMEOUT,
+ metricName);
+ expect(chrome.metricsPrivate.recordValue.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordValue)
+ .toHaveBeenCalledWith(testConfig, 2);
+ });
+ it('Should record an user cancel error', function() {
+ mr.MirrorAnalytics.recordCapturingFailureWithName(
+ mr.MirrorAnalytics.CapturingFailure
+ .CAPTURE_DESKTOP_FAIL_ERROR_USER_CANCEL,
+ metricName);
+ expect(chrome.metricsPrivate.recordValue.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordValue)
+ .toHaveBeenCalledWith(testConfig, 3);
+ });
+ it('Should record an answer not received error', function() {
+ mr.MirrorAnalytics.recordCapturingFailureWithName(
+ mr.MirrorAnalytics.CapturingFailure.ANSWER_NOT_RECEIVED,
+ metricName);
+ expect(chrome.metricsPrivate.recordValue.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordValue)
+ .toHaveBeenCalledWith(testConfig, 4);
+ });
+ it('Should record a tab fail error', function() {
+ mr.MirrorAnalytics.recordCapturingFailureWithName(
+ mr.MirrorAnalytics.CapturingFailure.CAPTURE_TAB_FAIL_ERROR_TIMEOUT,
+ metricName);
+ expect(chrome.metricsPrivate.recordValue.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordValue)
+ .toHaveBeenCalledWith(testConfig, 5);
+ });
+ it('Should record an ice connection closed error', function() {
+ mr.MirrorAnalytics.recordCapturingFailureWithName(
+ mr.MirrorAnalytics.CapturingFailure.ICE_CONNECTION_CLOSED,
+ metricName);
+ expect(chrome.metricsPrivate.recordValue.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordValue)
+ .toHaveBeenCalledWith(testConfig, 6);
+ });
+ it('Should record a tab failure', function() {
+ mr.MirrorAnalytics.recordCapturingFailureWithName(
+ mr.MirrorAnalytics.CapturingFailure.TAB_FAIL, metricName);
+ expect(chrome.metricsPrivate.recordValue.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordValue)
+ .toHaveBeenCalledWith(testConfig, 7);
+ });
+ it('Should record a desktop failure', function() {
+ mr.MirrorAnalytics.recordCapturingFailureWithName(
+ mr.MirrorAnalytics.CapturingFailure.DESKTOP_FAIL, metricName);
+ expect(chrome.metricsPrivate.recordValue.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordValue)
+ .toHaveBeenCalledWith(testConfig, 8);
+ });
+ it('Should record an unknown error', function() {
+ mr.MirrorAnalytics.recordCapturingFailureWithName(
+ mr.MirrorAnalytics.CapturingFailure.UNKNOWN, metricName);
+ expect(chrome.metricsPrivate.recordValue.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordValue)
+ .toHaveBeenCalledWith(testConfig, 9);
+ });
+ });
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_config.js b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_config.js
new file mode 100644
index 00000000000..fe079b2053b
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_config.js
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Mirroring configs.
+ */
+
+goog.provide('mr.mirror.Config');
+goog.require('mr.PlatformUtils');
+
+
+/**
+ * True if TDLS is supported by the browser and platform.
+ * @const {boolean}
+ */
+mr.mirror.Config.isTDLSSupportedByPlatform = Boolean(
+ typeof chrome != 'undefined' && chrome.networkingPrivate &&
+ chrome.networkingPrivate.setWifiTDLSEnabledState &&
+ mr.PlatformUtils.getCurrentOS() == mr.PlatformUtils.OS.CHROMEOS);
+
+
+/**
+ * True if desktop audio capture is available.
+ * @const {boolean}
+ */
+mr.mirror.Config.isDesktopAudioCaptureAvailable =
+ // Audio capture is supported on ChromeOS with Chrome version
+ // 30.0.1584.0 and up, and Chrome 31 on Windows.
+ [mr.PlatformUtils.OS.CHROMEOS, mr.PlatformUtils.OS.WINDOWS].includes(
+ mr.PlatformUtils.getCurrentOS());
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_service.js b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_service.js
new file mode 100644
index 00000000000..c1bc326c9cf
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_service.js
@@ -0,0 +1,522 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Service supporting tab and screen mirroring.
+ *
+ * This object is a singleton that controls all capture MediaStreams and all
+ * instances of MirrorSession.
+ *
+
+ */
+
+goog.provide('mr.mirror.Service');
+
+goog.require('mr.Assertions');
+goog.require('mr.CancellablePromise');
+goog.require('mr.EventAnalytics');
+goog.require('mr.Issue');
+goog.require('mr.IssueSeverity');
+goog.require('mr.Logger');
+goog.require('mr.MediaSourceUtils');
+goog.require('mr.MirrorAnalytics');
+goog.require('mr.Module');
+goog.require('mr.mirror.CaptureParameters');
+goog.require('mr.mirror.CaptureSurfaceType');
+goog.require('mr.mirror.Error');
+goog.require('mr.mirror.Messages');
+goog.require('mr.mirror.MirrorMediaStream');
+
+mr.mirror.Service = class extends mr.Module {
+ /**
+ * @param {!mr.mirror.ServiceName} serviceName
+ * @param {mr.ProviderManagerMirrorServiceCallbacks=} mirrorServiceCallbacks
+ */
+ constructor(serviceName, mirrorServiceCallbacks) {
+ super();
+
+ /** @private @const {!mr.mirror.ServiceName} */
+ this.serviceName_ = serviceName;
+
+ /** @private {?mr.ProviderManagerMirrorServiceCallbacks} */
+ this.mirrorServiceCallbacks_ = mirrorServiceCallbacks || null;
+
+ /** @protected {?mr.mirror.Session} */
+ this.currentSession = null;
+
+ /** @private {?mr.mirror.MirrorMediaStream} */
+ this.currentMediaStream_ = null;
+
+ /** @protected @const */
+ this.logger = mr.Logger.getInstance('mr.mirror.Service.' + serviceName);
+
+ /** @private @const */
+ this.onTabUpdated_ = this.handleTabUpdate_.bind(this);
+
+ /** @private {boolean} */
+ this.initialized_ = false;
+ }
+
+ /**
+ * Initializes the service. Sets callbacks to provider manager. No-ops if
+ * already initialized.
+ * @param {!mr.ProviderManagerMirrorServiceCallbacks} mirrorServiceCallbacks
+ * Callbacks to provider manager.
+ */
+ initialize(mirrorServiceCallbacks) {
+ if (this.initialized_) {
+ return;
+ }
+ this.mirrorServiceCallbacks_ = mirrorServiceCallbacks;
+ this.initialized_ = true;
+ this.doInitialize();
+ }
+
+ /**
+ * Called during initialization to perform service-specific initialization.
+ * @protected
+ */
+ doInitialize() {}
+
+ /**
+ * @return {mr.mirror.ServiceName}
+ */
+ getName() {
+ return this.serviceName_;
+ }
+
+ /**
+ * @param {!mr.Route} route
+ * @param {string} sourceUrn
+ * @param {!mr.mirror.Settings} mirrorSettings
+ * @param {string=} opt_presentationId
+ * @param {(function(!mr.Route): !mr.CancellablePromise)=}
+ * opt_streamStartedCallback Callback to invoke after stream capture
+ * succeeded and before the mirror session is created. The callback
+ * may update the route.
+ * @return {!mr.CancellablePromise<!mr.Route>} A promise fulfilled
+ * when mirroring has started successfully.
+ */
+ startMirroring(
+ route, sourceUrn, mirrorSettings, opt_presentationId,
+ opt_streamStartedCallback) {
+ this.logger.info('Start mirroring on route ' + route.id);
+ if (!this.initialized_) {
+ return mr.CancellablePromise.reject(Error('Not initialized'));
+ }
+ const promise = new Promise((resolve, reject) => {
+ this.stopCurrentMirroring()
+ .then(() => {
+ const captureParams = mr.mirror.Service.createCaptureParameters_(
+ sourceUrn, mirrorSettings, opt_presentationId);
+ return new mr.mirror.MirrorMediaStream(captureParams).start();
+ })
+ .then(stream => {
+ if (this.currentMediaStream_) {
+ stream.stop();
+ throw new mr.mirror.Error('Cannot start multiple streams');
+ }
+ this.currentMediaStream_ = stream;
+ this.currentMediaStream_.setOnStreamEnded(this.cleanup_.bind(this));
+ if (opt_streamStartedCallback) {
+ // Yuck. Converting a CancellablePromise to a plain Promise
+ // prevents cancellation from propagating correctly.
+ return opt_streamStartedCallback(route).promise;
+ }
+ return route;
+ })
+ .then(updatedRoute => {
+ if (this.currentSession) {
+ throw new mr.mirror.Error('Cannot start multiple sessions');
+ }
+ if (!this.currentMediaStream_) {
+ throw new mr.mirror.Error(
+ 'Media stream ended before session could start.');
+ }
+ this.currentSession =
+ this.createMirrorSession(mirrorSettings, updatedRoute);
+ updatedRoute.mirrorInitData.activity =
+ this.currentSession.getActivity();
+ this.currentSession.setOnActivityUpdate(
+ this.mirrorServiceCallbacks_.handleMirrorActivityUpdate.bind(
+ this.mirrorServiceCallbacks_));
+ return this.currentSession.start(/** @type {!MediaStream} */ (
+ this.currentMediaStream_.getMediaStream()));
+ })
+ .then(() => {
+ if (mr.MediaSourceUtils.isTabMirrorSource(sourceUrn) &&
+ !chrome.tabs.onUpdated.hasListener(this.onTabUpdated_)) {
+ chrome.tabs.onUpdated.addListener(this.onTabUpdated_);
+ }
+ return this.postProcessMirroring_(route, sourceUrn, mirrorSettings);
+ })
+ .then(() => {
+ resolve(route);
+ })
+ .catch(err => {
+
+ this.onStartError_(/** @type {!Error} */ (err));
+ return this.cleanup_().then(() => {
+ reject(err);
+ });
+ });
+ });
+ return mr.CancellablePromise.forPromise(promise);
+ }
+
+ /**
+ * @param {!mr.Route} route
+ * @param {string} sourceUrn
+ * @param {!mr.mirror.Settings} mirrorSettings
+ * @param {string=} opt_presentationId
+ * @param {number=} opt_tabId
+ * @param {(function(!mr.Route): !mr.CancellablePromise<!mr.Route>)=}
+ * opt_streamStartedCallback Callback to invoke after stream capture
+ * succeeded and before the mirror session is created. The callback may
+ * update the route.
+ * @return {!mr.CancellablePromise<!mr.Route>} A promise fulfilled
+ * when mirroring has started successfully.
+ */
+ updateMirroring(
+ route, sourceUrn, mirrorSettings, opt_presentationId, opt_tabId,
+ opt_streamStartedCallback) {
+ this.logger.info('Update mirroring on route ' + route.id);
+ if (!this.initialized_) {
+ return mr.CancellablePromise.reject(Error('Not initialized'));
+ }
+ return mr.CancellablePromise.forPromise(this.doUpdateMirroring_(
+ route, sourceUrn, mirrorSettings, opt_presentationId,
+ opt_streamStartedCallback));
+ }
+
+ /**
+ * A helper method for updateMirroring.
+ * @param {!mr.Route} route
+ * @param {string} sourceUrn
+ * @param {!mr.mirror.Settings} mirrorSettings
+ * @param {string=} opt_presentationId
+ * @param {(function(!mr.Route): !mr.CancellablePromise<!mr.Route>)=}
+ * opt_streamStartedCallback Callback to invoke after stream capture
+ * succeeded and before the mirror session is created. The callback may
+ * update the route.
+ * @return {!Promise<!mr.Route>} A promise fulfilled
+ * when mirroring has started successfully.
+ * @private
+ */
+ doUpdateMirroring_(
+ route, sourceUrn, mirrorSettings, opt_presentationId,
+ opt_streamStartedCallback) {
+ if (!this.currentSession) {
+ return Promise.reject(new mr.mirror.Error(
+ 'No session to update streams on',
+ mr.MirrorAnalytics.CapturingFailure.TAB_FAIL));
+ }
+ if (!this.currentSession.supportsUpdateStream()) {
+ return Promise.reject(new mr.mirror.Error(
+ 'Session does not support updating stream',
+ mr.MirrorAnalytics.CapturingFailure.TAB_FAIL));
+ }
+
+ let streamSwapped = false;
+ return new Promise((resolve, reject) => {
+ const captureParams = mr.mirror.Service.createCaptureParameters_(
+ sourceUrn, mirrorSettings, opt_presentationId);
+ new mr.mirror.MirrorMediaStream(captureParams)
+ .start()
+ .then(stream => {
+ if (this.currentMediaStream_) {
+ this.currentMediaStream_.setOnStreamEnded(null);
+ this.currentMediaStream_.stop();
+ this.recordStreamEnded();
+ }
+ this.currentMediaStream_ = stream;
+ this.currentMediaStream_.setOnStreamEnded(this.cleanup_.bind(this));
+ streamSwapped = true;
+
+ if (opt_streamStartedCallback) {
+ return opt_streamStartedCallback(route).promise;
+ }
+ return route;
+ })
+ .then(_ => {
+ if (!this.currentSession) {
+ throw new mr.mirror.Error('Session ended while updating stream');
+ }
+ if (!this.currentMediaStream_) {
+ throw new mr.mirror.Error(
+ 'Media stream ended before session could be updated.');
+ }
+ return this.currentSession.updateStream(
+ /** @type {!MediaStream} */ (
+ this.currentMediaStream_.getMediaStream()));
+ })
+ .then(this.postProcessMirroring_.bind(
+ this, route, sourceUrn, mirrorSettings))
+ .then(() => resolve(route))
+ .catch(err => {
+ this.onStartError_(/** @type {!Error} */ (err));
+ if (streamSwapped) {
+ return this.cleanup_().then(() => {
+ reject(err);
+ });
+ } else {
+ reject(err);
+ }
+ });
+ });
+ }
+
+ /**
+ * @param {!mr.Route} route
+ * @param {string} sourceUrn
+ * @param {!mr.mirror.Settings} mirrorSettings
+ * @return {!Promise<void>} Resolves when done.
+ * @private
+ */
+ postProcessMirroring_(route, sourceUrn, mirrorSettings) {
+ return new Promise((resolve, reject) => {
+ if (!this.currentSession) {
+ reject(new mr.mirror.Error(
+ 'Session gone before executing post-startup steps',
+ mr.MirrorAnalytics.CapturingFailure.TAB_FAIL));
+ return;
+ }
+ if (mr.MediaSourceUtils.isTabMirrorSource(sourceUrn)) {
+ this.currentSession.setTabId(
+ /** @type {number} */ (route.mirrorInitData.tabId));
+ this.recordTabMirrorStartSuccess();
+ } else if (mr.MediaSourceUtils.isPresentationSource(sourceUrn)) {
+ this.currentSession.setTabId(
+ /** @type {number} */ (route.mirrorInitData.tabId));
+ this.recordOffscreenTabMirrorStartSuccess();
+ } else {
+ this.recordDesktopMirrorStartSuccess();
+ }
+ this.checkCaptureIssues_(
+ mirrorSettings,
+ /** @type {!mr.mirror.MirrorMediaStream} */
+ (this.currentMediaStream_));
+ this.currentSession.onActivityUpdated();
+ resolve();
+ });
+ }
+
+ /**
+ * @param {!Error|!mr.mirror.Error} error The error that occurred when
+ * starting mirroring.
+ * @private
+ */
+ onStartError_(error) {
+ error.reason = (error.reason != null) ?
+ error.reason :
+ mr.MirrorAnalytics.CapturingFailure.UNKNOWN;
+
+ this.logger.error(
+ `Failed to start mirroring: ${error.message}` +
+ `, reason = ${error.reason}: ${error.stack}`);
+ this.recordMirrorStartFailure(error.reason);
+ }
+
+ /**
+ * @return {!Promise<boolean>} Fulfilled with true if there was a session
+ * and it was stopped, and with false if there is no session to stop.
+ */
+ stopCurrentMirroring() {
+ if (!this.initialized_) {
+ return Promise.reject('Not initialized');
+ }
+ return this.cleanup_().then(hadSession => {
+ if (hadSession) this.recordMirrorSessionEnded();
+
+ return hadSession;
+ });
+ }
+
+ /**
+ * @return {!Promise<boolean>} Fulfilled with true if there was a session
+ * and it was stopped. This promise never rejects.
+ * @private
+ */
+ cleanup_() {
+ // No-op if the listener was already removed.
+ chrome.tabs.onUpdated.removeListener(this.onTabUpdated_);
+
+ const streamToCleanUp = this.currentMediaStream_;
+ this.currentMediaStream_ = null;
+ if (streamToCleanUp) {
+ // Clear the "on ended" callback to prevent recursive calls to this method
+ // while the session and MediaStream are being torn down (below).
+ streamToCleanUp.setOnStreamEnded(null);
+ }
+
+ const sessionToCleanUp = this.currentSession;
+ this.currentSession = null;
+
+ // Create a promise chain to execute the stopping of the session, invoke the
+ // before/after stop callbacks, and thereafter stop the MediaStream. The
+ // MediaStream is stopped after the session to avoid any extra churn in the
+ // session shutdown process. All of this is conditional on whether a session
+ // and/or MediaStream was ever started since cleanup_() is also used as an
+ // all-purpose failed-start recovery handler.
+ let cleanupPromise;
+ if (sessionToCleanUp) {
+ cleanupPromise =
+ this.beforeCleanUpSession(sessionToCleanUp)
+ .catch(
+ err =>
+ this.logger.error('Error in before-cleanup steps', err))
+ .then(() => sessionToCleanUp.stop())
+ .catch(err => this.logger.error('Error stopping session', err))
+ .then(() => {
+ this.mirrorServiceCallbacks_.onMirrorSessionEnded(
+ sessionToCleanUp.getRoute().id);
+ })
+ .catch(err => this.logger.error('Error in ended callbacks', err))
+ .then(() => true);
+ } else {
+ cleanupPromise = Promise.resolve(false);
+ }
+ if (streamToCleanUp) {
+ cleanupPromise = cleanupPromise.then(hadSession => {
+ streamToCleanUp.stop();
+ this.recordStreamEnded();
+ return hadSession;
+ });
+ }
+
+ return cleanupPromise;
+ }
+
+ /**
+ * @param {!mr.mirror.Session} session the session about to be cleaned up.
+ * @return {!Promise<void>} Fulfilled when session has been cleaned up.
+ * @protected
+ */
+ beforeCleanUpSession(session) {
+ return Promise.resolve();
+ }
+
+ /**
+ * Handles tab updated event.
+ *
+ * @param {number} tabId the ID of the tab.
+ * @param {!TabChangeInfo} changeInfo The changes to the state of the tab.
+ * @param {!Tab} tab The tab.
+ * @private
+ */
+ handleTabUpdate_(tabId, changeInfo, tab) {
+ mr.EventAnalytics.recordEvent(mr.EventAnalytics.Event.TABS_ON_UPDATED);
+ this.currentSession &&
+ this.currentSession.onTabUpdated(tabId, changeInfo, tab);
+ }
+
+ /**
+ * Creates a new mr.mirror.CaptureParameters from the given inputs.
+ *
+ * @param {string} sourceUrn
+ * @param {!mr.mirror.Settings} mirrorSettings
+ * @param {string=} opt_presentationId
+ * @return {!mr.mirror.CaptureParameters}
+ * @private
+ */
+ static createCaptureParameters_(
+ sourceUrn, mirrorSettings, opt_presentationId) {
+ if (mr.MediaSourceUtils.isTabMirrorSource(sourceUrn)) {
+ return new mr.mirror.CaptureParameters(
+ mr.mirror.CaptureSurfaceType.TAB, mirrorSettings);
+ } else if (mr.MediaSourceUtils.isDesktopMirrorSource(sourceUrn)) {
+ return new mr.mirror.CaptureParameters(
+ mr.mirror.CaptureSurfaceType.DESKTOP, mirrorSettings);
+ } else if (mr.MediaSourceUtils.isPresentationSource(sourceUrn)) {
+ if (!opt_presentationId) {
+ throw new mr.mirror.Error('Missing offscreen tab presentation id');
+ }
+ return new mr.mirror.CaptureParameters(
+ mr.mirror.CaptureSurfaceType.OFFSCREEN_TAB, mirrorSettings, sourceUrn,
+ /** @type {!string} */ (opt_presentationId));
+ } else {
+ throw new mr.mirror.Error(
+ 'Source URN does not suggest a known capture type.');
+ }
+ }
+
+ /**
+ * Checks for any capture issues after mirroring has started successfully.
+ *
+ * @param {!mr.mirror.Settings} settings The requested settings.
+ * @param {!mr.mirror.MirrorMediaStream} mediaStream The captured stream.
+ * @private
+ */
+ checkCaptureIssues_(settings, mediaStream) {
+ if (settings.shouldCaptureAudio && mediaStream.getMediaStream() &&
+ !mediaStream.getMediaStream().getAudioTracks().length) {
+ this.mirrorServiceCallbacks_.sendIssue(new mr.Issue(
+ mr.mirror.Messages.MSG_MR_MIRROR_NO_AUDIO_CAPTURED,
+ mr.IssueSeverity.NOTIFICATION));
+ }
+ }
+
+ /**
+ * @return {?mr.mirror.Session}
+ * @deprecated Use of this getter is dangerous, since mr.mirror.Service could
+ * be in the middle of a sequence of asynchronous steps to start up or
+ * shut down the current session.
+ */
+ getCurrentSession() {
+ return this.currentSession;
+ }
+
+ /**
+ * @param {!mr.mirror.Settings} mirrorSettings
+ * @param {!mr.Route} route
+ * @return {!mr.mirror.Session}
+ */
+ createMirrorSession(mirrorSettings, route) {}
+ /**
+ * Records that Tab Mirroring successfully started.
+ */
+ recordTabMirrorStartSuccess() {}
+ /**
+ * Records that Desktop Mirroring successfully started.
+ */
+ recordDesktopMirrorStartSuccess() {}
+ /**
+ * Records that Offscreen Tab (1UA mode) Mirroring successfully started.
+ */
+ recordOffscreenTabMirrorStartSuccess() {}
+ /**
+ * Records that the session has ended.
+ */
+ recordMirrorSessionEnded() {}
+ /**
+ * Records that a Mirroring session failed to start.
+ * @param {mr.MirrorAnalytics.CapturingFailure} reason
+ * The reason for the failure.
+ */
+ recordMirrorStartFailure(reason) {}
+ /**
+ * Records that a Mirroring stream ended.
+ */
+ recordStreamEnded() {}
+ /**
+ * Asynchronously uploads logs for the most recent mirroring session.
+ * @param {string} feedbackId ID of feedback requesting log upload.
+ * @return {!Promise<string|undefined>} Resolved with an identifier (e.g.,
+ * URL) of the log that will be uploaded (which will be undefined if there
+ * is no such identifier), or rejected if upload failed.
+ */
+ requestLogUpload(feedbackId) {
+ return mr.Assertions.rejectNotImplemented();
+ }
+ /**
+ * See documentation in interface_data/mojo.js.
+ * @param {string} routeId
+ * @param {!mojo.InterfaceRequest} controllerRequest
+ * @param {!mojo.MediaStatusObserverPtr} observer
+ * @return {!Promise<void>}
+ */
+ createMediaRouteController(routeId, controllerRequest, observer) {
+ return mr.Assertions.rejectNotImplemented();
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_service_loader.js b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_service_loader.js
new file mode 100644
index 00000000000..4d0eefeb307
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_service_loader.js
@@ -0,0 +1,47 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.provide('mr.mirror.DefaultServiceLoader');
+goog.provide('mr.mirror.ServiceLoader');
+
+
+
+/**
+ * Loads a mirror.Service. Note that this loader does not need to handle event
+ * page suspending and waking up (the event page is running when there is a
+ * local mirroring route).
+ * @record
+ */
+mr.mirror.ServiceLoader = class {
+ /**
+ * Loads and returns the service.
+ * @return {!Promise<!mr.mirror.Service>}
+ */
+ loadService() {}
+};
+
+
+/**
+ * A lightweight implementation of ServiceLoader which just returns the instance
+ * provided to the constructor.
+ * @implements {mr.mirror.ServiceLoader}
+ */
+mr.mirror.DefaultServiceLoader = class {
+ /**
+ * @param {!mr.mirror.Service} serviceInstance
+ */
+ constructor(serviceInstance) {
+ /**
+ * @private {!mr.mirror.Service}
+ */
+ this.serviceInstance_ = serviceInstance;
+ }
+
+ /**
+ * @override
+ */
+ loadService() {
+ return Promise.resolve(this.serviceInstance_);
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_service_name.js b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_service_name.js
new file mode 100644
index 00000000000..0a588727732
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_service_name.js
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Service name uniquely identifying each service.
+ */
+
+
+goog.provide('mr.mirror.ServiceName');
+
+
+/**
+ * @enum {string}
+ */
+mr.mirror.ServiceName = {
+ CAST_STREAMING: 'cast_streaming',
+ HANGOUTS: 'hangouts',
+ MEETINGS: 'meetings',
+ WEBRTC: 'webrtc'
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_session.js b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_session.js
new file mode 100644
index 00000000000..7c3a0a3442c
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_session.js
@@ -0,0 +1,179 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Interface to a mirroring session.
+
+ */
+
+goog.provide('mr.mirror.Session');
+
+goog.require('mr.TabUtils');
+goog.require('mr.mirror.Activity');
+
+
+/**
+ * Creates a new MirrorSession. Do not call, as this is an abstract base class.
+ */
+mr.mirror.Session = class {
+ /**
+ * @param {!mr.Route} route
+ * @param {?function(!mr.Route, !mr.mirror.Activity)=} onActivityUpdate
+ */
+ constructor(route, onActivityUpdate = null) {
+ /**
+ * The media route associated with this mirror session.
+ * @protected @const {!mr.Route}
+ */
+ this.route = route;
+
+ /**
+ * Called when this.activity_ has changed.
+ * @private {?function(!mr.Route, !mr.mirror.Activity)}
+ */
+ this.onActivityUpdate_ = onActivityUpdate;
+
+ /**
+ * The activity description for keeping UI strings up to date.
+ * @protected {!mr.mirror.Activity}
+ */
+ this.activity = mr.mirror.Activity.createFromRoute(route);
+
+ /** @type {?number} */
+ this.tabId = null;
+
+ /** @type {?Tab} */
+ this.tab = null;
+
+ /** @type {boolean} */
+ this.isRemoting = false;
+ }
+
+ /**
+ * Sets the callback for handling activity updates.
+ * @param {!function(!mr.Route, !mr.mirror.Activity)} onActivityUpdate
+ */
+ setOnActivityUpdate(onActivityUpdate) {
+ this.onActivityUpdate_ = onActivityUpdate;
+ }
+
+ /**
+ * @param {number} tabId The id of the tab being captured, if any.
+ * @return {!Promise<void>}
+ */
+ setTabId(tabId) {
+ if (this.tabId != tabId) {
+ this.tabId = tabId;
+ return mr.TabUtils.getTab(tabId).then((tab) => {
+ this.tab = tab;
+ this.onActivityUpdated();
+ });
+ } else {
+ return Promise.resolve();
+ }
+ }
+
+ /**
+ * Handles tab updated event.
+ *
+ * @param {number} tabId the ID of the tab.
+ * @param {!TabChangeInfo} changeInfo The changes to the state of the tab.
+ * @param {!Tab} tab The tab.
+ */
+ onTabUpdated(tabId, changeInfo, tab) {
+ if (tabId != this.tabId) return;
+ if (changeInfo.status == 'complete' ||
+ (!!changeInfo.favIconUrl && tab.status == 'complete')) {
+ this.tab = tab;
+ this.onActivityUpdated();
+ }
+ }
+
+ /**
+ * Called when the activity object for the mirror session needs to be updated.
+ * This happens when e.g. the tab being mirrored navigates, changes title, or
+ * switches in or out of media remoting.
+ */
+ onActivityUpdated() {
+ if (this.tab) {
+ this.activity.setContentTitle(this.tab.title);
+ this.activity.setIncognito(this.tab.incognito);
+ const url = new URL(this.tab.url);
+ if (url.protocol == 'file:') {
+ this.activity.setType(mr.mirror.Activity.Type.MIRROR_FILE);
+ this.activity.setOrigin(null);
+ } else {
+ this.activity.setType(
+ this.isRemoting ? mr.mirror.Activity.Type.MEDIA_REMOTING :
+ mr.mirror.Activity.Type.MIRROR_TAB);
+ if (url.protocol == 'https:') {
+ // OK to drop the protocol for secure origins.
+ this.activity.setOrigin(url.origin.substr(8));
+ } else {
+ this.activity.setOrigin(url.origin);
+ }
+ }
+ }
+ this.route.description = this.activity.getRouteDescription();
+ this.onActivityUpdate_ && this.onActivityUpdate_(this.route, this.activity);
+ this.sendActivityToSink();
+ }
+
+ /**
+ * @return {!mr.Route}
+ */
+ getRoute() {
+ return this.route;
+ }
+
+ /**
+ * @return {!mr.mirror.Activity}
+ */
+ getActivity() {
+ return this.activity;
+ }
+
+ /**
+ * Starts the mirroring session. The |mediaStream| must provide one audio
+ * and/or one video track. It is illegal to call start() more than once on the
+ * same session, even after stop() has been called. See updateStream(), or
+ * else create a new instance to re-start a session.
+ * @param {!MediaStream} mediaStream The media stream that has the audio
+ * and/or video track.
+ * @return {!Promise<mr.mirror.Session>} Fulfilled when the
+ * session has been created and transports have started for the streams.
+ */
+ start(mediaStream) {}
+
+ /**
+ * @return {boolean} Whether the session supports updating its media stream
+ * while it is active.
+ */
+ supportsUpdateStream() {
+ return false;
+ }
+
+ /**
+ * Updates the media stream that the session is using.
+ * @param {!MediaStream} mediaStream
+ * @return {!Promise}
+ */
+ updateStream(mediaStream) {}
+
+ /**
+ * Stops the mirroring session. The underlying streams and transports are
+ * stopped and destroyed. Sessions cannot be re-started. Instead, either use
+ * updateStream() or create a new instance to re-start a session.
+ * @return {!Promise<void>} Fulfilled with once the session has been stopped.
+ * The promise is rejected on error.
+ */
+ stop() {
+ return Promise.resolve();
+ }
+
+ /**
+ * Sends updated activity info directly to the sink.
+ */
+ sendActivityToSink() {}
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_session_test.js b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_session_test.js
new file mode 100644
index 00000000000..52f0536c11a
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_session_test.js
@@ -0,0 +1,118 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.setTestOnly();
+goog.require('mr.Route');
+goog.require('mr.TabUtils');
+goog.require('mr.mirror.Session');
+
+describe('Tests mr.mirror.Session', () => {
+ let mirrorRoute;
+ let session;
+ let onActivityUpdated;
+
+ function expectNormalSession(s) {
+ expect(s.tabId).toBe(47);
+ expect(s.tab).not.toBe(null);
+ expect(s.isRemoting).toBe(false);
+ expect(s.activity.getRouteDescription())
+ .toBe('Casting tab (news.google.com)');
+ expect(s.activity.getRouteMediaStatus()).toBe('Google News');
+ expect(s.activity.getCastRemoteTitle()).toBe('Casting tab');
+ }
+
+ function expectIncognitoSession(s) {
+ expect(s.tabId).toBe(47);
+ expect(s.tab).not.toBe(null);
+ expect(s.isRemoting).toBe(false);
+ expect(s.activity.getRouteDescription())
+ .toBe('Casting tab (news.google.com)');
+ expect(s.activity.getRouteMediaStatus()).toBe('Google News');
+ expect(s.activity.getCastRemoteTitle()).toBe('Casting active');
+ }
+
+ beforeEach(() => {
+ window['mojo'] = null; // Workaround to allow tests to run in Jasmine
+ // without mojo bindings
+ mirrorRoute = new mr.Route(
+ 'routeId', 'presentationId', 'sinkId',
+ 'urn:x-org.chromium.media:source:tab:47', true, null);
+ onActivityUpdated = jasmine.createSpy();
+ session = new mr.mirror.Session(mirrorRoute, onActivityUpdated);
+ session.tabId = 47;
+ spyOn(session, 'sendActivityToSink');
+ });
+
+ it('has default data for a tab mirroring route', () => {
+ expect(session.route).toBe(mirrorRoute);
+ expect(session.tabId).toBe(47);
+ expect(session.tab).toBe(null);
+ expect(session.isRemoting).toBe(false);
+ expect(session.activity.getRouteDescription()).toBe('Casting tab');
+ expect(session.activity.getRouteMediaStatus()).toBe('');
+ expect(session.activity.getCastRemoteTitle()).toBe('Casting tab');
+ });
+
+ describe('onTabUpdated', () => {
+ it('sets all fields with normal tab', () => {
+ session.onTabUpdated(47, {'status': 'complete'}, {
+ 'title': 'Google News',
+ 'url': 'https://news.google.com',
+ 'incognito': false
+ });
+ expect(session.sendActivityToSink).toHaveBeenCalled();
+ expect(onActivityUpdated).toHaveBeenCalled();
+ expectNormalSession(session);
+ });
+ it('sets some fields with incognito tab', () => {
+ session.onTabUpdated(47, {'status': 'complete'}, {
+ 'title': 'Google News',
+ 'url': 'https://news.google.com',
+ 'incognito': true
+ });
+ expect(session.sendActivityToSink).toHaveBeenCalled();
+ expect(onActivityUpdated).toHaveBeenCalled();
+ expectIncognitoSession(session);
+ });
+ it('sets some fields with OTR route', () => {
+ mirrorRoute.offTheRecord = true;
+ otrSession = new mr.mirror.Session(mirrorRoute, onActivityUpdated);
+ otrSession.tabId = 47;
+ spyOn(otrSession, 'sendActivityToSink');
+ otrSession.onTabUpdated(47, {'status': 'complete'}, {
+ 'title': 'Google News',
+ 'url': 'https://news.google.com',
+ 'incognito': true
+ });
+ expect(otrSession.sendActivityToSink).toHaveBeenCalled();
+ expect(onActivityUpdated).toHaveBeenCalled();
+ expectIncognitoSession(otrSession);
+ });
+ });
+
+ describe('setTabId', () => {
+ beforeEach((done) => {
+ spyOn(mr.TabUtils, 'getTab').and.returnValue(Promise.resolve({
+ 'title': 'CNN',
+ 'url': 'https://www.cnn.com',
+ 'incognito': false
+ }));
+ session.setTabId(48);
+ done();
+ });
+
+ it('updates the tab', (done) => {
+ expect(session.sendActivityToSink).toHaveBeenCalled();
+ expect(onActivityUpdated).toHaveBeenCalled();
+ expect(session.tabId).toBe(48);
+ expect(session.tab).not.toBe(null);
+ expect(session.isRemoting).toBe(false);
+ expect(session.activity.getRouteDescription())
+ .toBe('Casting tab (www.cnn.com)');
+ expect(session.activity.getRouteMediaStatus()).toBe('CNN');
+ expect(session.activity.getCastRemoteTitle()).toBe('Casting tab');
+ done();
+ });
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_settings.js b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_settings.js
new file mode 100644
index 00000000000..74499bbacbc
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_settings.js
@@ -0,0 +1,400 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Tab-mirroring settings, including video bitrate, video
+ * resolution, and audio bitrate.
+ *
+
+ */
+
+goog.provide('mr.mirror.Settings');
+goog.provide('mr.mirror.VideoCodec');
+
+goog.require('mr.Logger');
+goog.require('mr.PlatformUtils');
+
+
+/**
+ * @enum {string}
+ */
+mr.mirror.VideoCodec = {
+ VP8: 'VP8',
+ // This is the internal codename for hardware-encoded H264. for V1 mirroring.
+ CAST1: 'CAST1',
+ H264: 'H264',
+
+ // This is a fake codec for retransmissions used in WebRTC.
+ RTX: 'rtx'
+};
+
+
+
+/**
+ * Settings that affect capture and transport (via Cast Streaming or WebRTC).
+ * Generally, these should all be left unchanged from their defaults.
+ * Overriding them is only meant for development or user experiments.
+ *
+ * WARNING: If making any changes to defaults here, it is on *you* to confirm
+ * that all downstream consumers of these settings will behave correctly with
+ * the new values. If not, see usage notes below for per-sink adjustments.
+ *
+ * Usage: Generally, this class should be instantiated by mr.Provider
+ * implementations only. The provider creates a new instance, and then must
+ * adjust any properties based on both the sender's and sink's capabilities.
+ * It should also, as a final step, freeze the settings object to prevent any
+ * downstream code from making changes. Example provider code:
+ *
+ * getMirrorSettings(sinkId) {
+ * // Constrain default settings to a sink that is only capable of standard
+ * // definition (lower resolution and no high frame rate support).
+ * const settings = new mr.mirror.Settings();
+ * settings.maxWidth = Math.min(settings.maxWidth, 640);
+ * settings.maxHeight = Math.min(settings.maxWidth, 360);
+ * settings.maxFrameRate = Math.min(settings.maxFrameRate, 30);
+ *
+ * // Override: Some sinks might require the sender to do the letterboxing.
+ * settings.senderSideLetterboxing =
+ * !this.canSinkHandleLetterboxing_(sinkId);
+ *
+ * settings.makeFinalAdjustmentsAndFreeze();
+ * return settings;
+ * }
+ *
+ */
+mr.mirror.Settings = class {
+ constructor() {
+ /**
+ * Maximum video width in pixels.
+ *
+ * @export {number}
+ */
+ this.maxWidth = 1920;
+
+ /**
+ * Maximum video height in pixels.
+ *
+ * @export {number}
+ */
+ this.maxHeight = 1080;
+
+ /**
+ * Minimum video width in pixels.
+ *
+ * @export {number}
+ */
+ this.minWidth = 180;
+
+ /**
+ * Minimum video height in pixels.
+ *
+ * @export {number}
+ */
+ this.minHeight = 180;
+
+ /**
+ * Whether the screen capture must handle letterboxing/pillarboxing. If
+ * false (more desired), the receiver will handle it. When setting this to
+ * true, please see comments for getMinDimensionsToMatchAspectRatio().
+ *
+ * @export {boolean}
+ */
+ this.senderSideLetterboxing = false;
+
+ /**
+ * The minimum frame rate for captures. Well-behaved clients can handle a
+ * minimum frame rate of zero, which prevents wasting system resources
+ * sender-side. Unfortunately, not all clients are well-behaved...
+ *
+ * @export {number}
+ */
+ this.minFrameRate = 0;
+
+ /**
+ * The maximum frame rate for captures.
+ *
+ * @export {number}
+ */
+ this.maxFrameRate = 30;
+
+ /**
+ * Minimum video bitrate in kbps.
+ *
+ * @export {number}
+ */
+ this.minVideoBitrate = 300;
+
+ /**
+ * Maximum video bitrate in kbps.
+ *
+ * @export {number}
+ */
+ this.maxVideoBitrate = 5000;
+
+ /**
+ * Target audio bitrate in kbps (0 means automatic).
+ *
+ * @export {number}
+ */
+ this.audioBitrate = 0;
+
+ /**
+ * Maximum end-to-end latency (in milliseconds).
+ *
+ * @export {number}
+ */
+ this.maxLatencyMillis = 800;
+
+ /**
+ * Minimum end-to-end latency (in milliseconds). This allows cast streaming
+ * to adaptively lower latency in interactive streaming scenarios.
+ * This setting currently applies to cast streaming only.
+ *
+
+ *
+ * @export {number}
+ */
+ this.minLatencyMillis = 400;
+
+ /**
+ * Starting end-to-end latency for animated content (in milliseconds).
+ *
+ * @export {number}
+ */
+ this.animatedLatencyMillis = 400;
+
+ /**
+ * Enable DSCP?
+ * This setting currently applies to cast streaming only.
+ *
+ * @export {boolean}
+ */
+ this.dscpEnabled =
+ [
+ mr.PlatformUtils.OS.MAC, mr.PlatformUtils.OS.LINUX,
+ mr.PlatformUtils.OS.CHROMEOS
+ ].includes(mr.PlatformUtils.getCurrentOS()) ||
+ mr.PlatformUtils.isWindows8OrNewer();
+
+ /**
+ * Whether to enable network transport logging.
+ *
+ * @export {boolean}
+ */
+ this.enableLogging = true;
+
+ /**
+ * Whether an attempt should be made to use TDLS.
+ * This setting currently applies to cast streaming only.
+ *
+ * @export {boolean}
+ */
+ this.useTdls = false;
+
+
+ /**
+ * Whether video should be captured. This could be influenced by the
+ * application and/or the sink's capabilities.
+ * @export {boolean}
+ */
+ this.shouldCaptureVideo = true;
+
+ /**
+ * Whether audio should be captured. This could be influenced by the
+ * application and/or the sink's capabilities.
+ * @export {boolean}
+ */
+ this.shouldCaptureAudio = true;
+
+ // For development, debugging, or integration testing use only!
+ const overrides = window.localStorage ?
+ window.localStorage.getItem(mr.mirror.Settings.OverridesKey) :
+ null;
+ if (overrides) {
+ try {
+ const parsedOverrides = JSON.parse(String(overrides));
+ if (parsedOverrides instanceof Object) {
+ this.update_(/** @type {!Object} */ (parsedOverrides));
+ mr.Logger.getInstance('mr.mirror.Settings')
+ .warning(
+ () => 'Initial mr.mirror.Settings overridden to: ' +
+ this.toJsonString());
+ } else {
+ throw Error(
+ `localStorage[${mr.mirror.Settings.OverridesKey}] ` +
+ `does not parse as an Object: ${overrides}`);
+ }
+ } catch (exception) {
+ mr.Logger.getInstance('mr.mirror.Settings')
+ .error(
+ mr.mirror.Settings.OverridesKey + ' must be of the form ' +
+ '\'{"maxWidth":640, "maxHeight":360}\'.',
+ exception);
+ // Prevent mirroring from starting if overrides are present and not
+ // parseable.
+ throw new Error('Overrides not parseable. See ERROR log for details.');
+ }
+ }
+ }
+
+ /**
+ * @return {!mr.mirror.Settings}
+ */
+ clone() {
+ const settings = new mr.mirror.Settings();
+ settings.update_(this);
+ return settings;
+ }
+
+ /**
+ * Returns the properties of this Settings object as a JSON-formatted string.
+ * @return {!string}
+ */
+ toJsonString() {
+ return JSON.stringify(this, (key, value) => {
+ // Only public fields are included in the stringified output.
+ if (key.length == 0 || !key.endsWith('_')) {
+ return value;
+ }
+ return undefined;
+ });
+ }
+
+ /**
+ * Update this object to have the same settings as another object.
+ * @param {!Object} settings The properties to apply, which may be any subset
+ * of all the public fields of this class.
+ * @private
+ */
+ update_(settings) {
+ // Override Closure-compiler "access on a struct" error.
+ const self = /** @type {!Object} */ (this);
+ for (const key of Object.keys(settings)) {
+ if (key.endsWith('_') || (typeof settings[key] !== typeof self[key])) {
+ continue;
+ }
+ self[key] = settings[key];
+ }
+ }
+
+ /**
+ * Make mandatory system-wide bounds adjustments and then freeze this Settings
+ * object. See example usage in class-level comments.
+ */
+ makeFinalAdjustmentsAndFreeze() {
+ this.clampMaxDimensionsToScreenSize_();
+ Object.freeze(this);
+ }
+
+ /**
+ * Adjusts the maxWidth/maxHeight to within the size of the user's screen, and
+ * rounds down to a standard 16:9 resolution (i.e., width is 0 modulo 160 and
+ * height is 0 modulo 90). This prevents performance problems due to:
+ * 1. The pre-capture fullscreen size being something way larger than the
+ * system was designed for (e.g., 1080p on a Daisy Chromebook).
+ * 2. The receiver dealing with scaling from an oddball resolution to a
+ * standard resolution (e.g., 1366x768 --> 1280x720).
+ * @private
+ */
+ clampMaxDimensionsToScreenSize_() {
+ const widthStep = 160;
+ const heightStep = 90;
+ const screenWidth = mr.mirror.Settings.getScreenWidth();
+ const screenHeight = mr.mirror.Settings.getScreenHeight();
+ const x = this.maxWidth * screenHeight;
+ const y = this.maxHeight * screenWidth;
+ let clampedWidth = 0;
+ let clampedHeight = 0;
+ if (y < x) {
+ clampedWidth = Math.min(this.maxWidth, screenWidth);
+ clampedWidth = clampedWidth - (clampedWidth % widthStep);
+ clampedHeight = clampedWidth * heightStep / widthStep;
+ } else {
+ clampedHeight = Math.min(this.maxHeight, screenHeight);
+ clampedHeight = clampedHeight - (clampedHeight % heightStep);
+ clampedWidth = clampedHeight * widthStep / heightStep;
+ }
+ if (clampedWidth < Math.max(widthStep, this.minWidth) ||
+ clampedHeight < Math.max(heightStep, this.minHeight)) {
+ clampedWidth = Math.max(widthStep, this.minWidth);
+ clampedHeight = Math.max(heightStep, this.minHeight);
+ }
+
+ this.maxWidth = clampedWidth;
+ this.maxHeight = clampedHeight;
+ }
+
+ /**
+ * Returns alternate |minWidth| and |minHeight| values that match the aspect
+ * ratio of |maxWidth| and |maxHeight| to the nearest integer. Some of the
+ * capture APIs will then interpret the matching aspect ratios to mean that
+ * the sender should letterbox/pillarbox the video, rather than allowing the
+ * receiver to handle it. This method does NOT modify any properties of this
+ * Settings object.
+ * @return {!{width: number, height: number}} New minimum width/height values,
+ * as described.
+ */
+ getMinDimensionsToMatchAspectRatio() {
+ if (this.minAndMaxAspectRatiosAreSimilar()) {
+ return {width: this.minWidth, height: this.minHeight};
+ }
+
+ let a = this.maxWidth;
+ let b = this.maxHeight;
+ while (b != 0) {
+ const remainder = a % b;
+ a = b;
+ b = remainder;
+ }
+ // Note: |a| now contains the greatest common divisor.
+ let width = this.maxWidth / a;
+ let height = this.maxHeight / a;
+ if (width < this.minWidth || height < this.minHeight) {
+ // Increase to respect the current min width/Height setting.
+ const upFactor = Math.max(this.minWidth / width, this.minHeight / height);
+ width *= upFactor;
+ height *= upFactor;
+ // ...and make sure the increase does not now exceed the max width/height.
+ if (width > this.maxWidth || height > this.maxHeight) {
+ width = this.maxWidth;
+ height = this.maxHeight;
+ }
+ }
+ width = Math.round(width);
+ height = Math.round(height);
+ return {width, height};
+ }
+
+ /**
+ * @return {boolean} Returns true if the aspect ratios of the min and max
+ * dimensions are within one part in one hundred of each other.
+ */
+ minAndMaxAspectRatiosAreSimilar() {
+ if (this.minHeight == 0 || this.maxHeight == 0) {
+ return false;
+ }
+ // Source: content/renderer/media/media_stream_video_capturer_source.cc (in
+ // Chromium project, circa 2016).
+ const ratioOfMinSize = Math.floor(100.0 * this.minWidth / this.minHeight);
+ const ratioOfMaxSize = Math.floor(100.0 * this.maxWidth / this.maxHeight);
+ return ratioOfMinSize == ratioOfMaxSize;
+ }
+
+ /** @return {number} */
+ static getScreenWidth() {
+ return screen.width;
+ }
+
+ /** @return {number} */
+ static getScreenHeight() {
+ return screen.height;
+ }
+};
+
+
+/**
+ * The key for retrieving settings overrides from localStorage.
+ * @type {string}
+ */
+mr.mirror.Settings.OverridesKey = 'mr.mirror.Settings.Overrides';
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_settings_test.js b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_settings_test.js
new file mode 100644
index 00000000000..066e92f3198
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/mirror_settings_test.js
@@ -0,0 +1,123 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.require('mr.mirror.Settings');
+
+describe('Tests Mirror Settings', function() {
+ it('produces JSON strings containing public fields', function() {
+ // Emit Settings as a JSON-format string.
+ const settings = new mr.mirror.Settings();
+ const jsonString = settings.toJsonString();
+
+ // The string should be parseable as JSON and contain only public fields.
+ const parsed = JSON.parse(jsonString);
+ expect(typeof parsed['maxWidth']).toBe('number');
+ expect(typeof parsed['enableLogging']).toBe('boolean');
+ expect(Object.keys(parsed).sort())
+ .toEqual(Object.keys(settings).filter(x => !x.endsWith('_')).sort());
+ });
+
+ it('only updates public fields from localStorage overrides', function() {
+ const settings = new mr.mirror.Settings();
+
+ // Normal case: A public field is updated with a new value of the same type.
+ const originalMaxWidth = settings.maxWidth;
+ settings.update_({'maxWidth': originalMaxWidth / 2});
+ expect(settings.maxWidth).toBe(originalMaxWidth / 2);
+
+ // Bad case: A public field being set to a value of a different type.
+ const jsonBefore = settings.toJsonString();
+ settings.update_({'maxWidth': true});
+ expect(settings.toJsonString()).toEqual(jsonBefore);
+
+ // Bad case: A non-existant field.
+ settings.update_({'fooey': true});
+ expect(settings.toJsonString()).toEqual(jsonBefore);
+
+ // Bad case: Attempt to set private field.
+ const loggerBefore = settings.logger_;
+ const injectionAttackFunc = function() {
+ return 'MUAHAHAHAHA!';
+ };
+ settings.update_({'logger_': injectionAttackFunc});
+ expect(settings.logger_).not.toBe(injectionAttackFunc);
+ expect(settings.logger_).toBe(loggerBefore);
+ expect(settings.toJsonString()).toEqual(jsonBefore);
+ });
+
+ it('clamps max dimensions to 1920x1080 screen size', function() {
+ spyOn(mr.mirror.Settings, 'getScreenWidth').and.returnValue(1920);
+ spyOn(mr.mirror.Settings, 'getScreenHeight').and.returnValue(1080);
+ const settings = new mr.mirror.Settings();
+ settings.makeFinalAdjustmentsAndFreeze();
+ expect(settings.maxWidth).toBe(1920);
+ expect(settings.maxHeight).toBe(1080);
+ });
+
+ it('clamps max dimensions to 1366x768 screen size', function() {
+ spyOn(mr.mirror.Settings, 'getScreenWidth').and.returnValue(1366);
+ spyOn(mr.mirror.Settings, 'getScreenHeight').and.returnValue(768);
+ const settings = new mr.mirror.Settings();
+ settings.makeFinalAdjustmentsAndFreeze();
+ expect(settings.maxWidth).toBe(1280);
+ expect(settings.maxHeight).toBe(720);
+ });
+
+ it('returns min size matching aspect ratio of max size', function() {
+ const settings = new mr.mirror.Settings();
+ settings.maxWidth = 1920;
+ settings.maxHeight = 1080;
+ settings.minWidth = 320;
+ settings.minHeight = 180;
+ expect(settings.minAndMaxAspectRatiosAreSimilar()).toBe(true);
+ expect(settings.getMinDimensionsToMatchAspectRatio())
+ .toEqual({width: 320, height: 180});
+
+ settings.minWidth = 0;
+ settings.minHeight = 0;
+ expect(settings.minAndMaxAspectRatiosAreSimilar()).toBe(false);
+ expect(settings.getMinDimensionsToMatchAspectRatio())
+ .toEqual({width: 16, height: 9});
+ settings.minWidth = 1;
+ settings.minHeight = 1;
+ expect(settings.minAndMaxAspectRatiosAreSimilar()).toBe(false);
+ expect(settings.getMinDimensionsToMatchAspectRatio())
+ .toEqual({width: 16, height: 9});
+ settings.minWidth = 16;
+ settings.minHeight = 9;
+ expect(settings.minAndMaxAspectRatiosAreSimilar()).toBe(true);
+
+ settings.minWidth = 320;
+ settings.minHeight = 240;
+ expect(settings.minAndMaxAspectRatiosAreSimilar()).toBe(false);
+ expect(settings.getMinDimensionsToMatchAspectRatio())
+ .toEqual({width: 427, height: 240});
+
+ settings.maxWidth = 1000;
+ settings.maxHeight = 1000;
+ settings.minWidth = 48;
+ settings.minHeight = 27;
+ expect(settings.minAndMaxAspectRatiosAreSimilar()).toBe(false);
+ expect(settings.getMinDimensionsToMatchAspectRatio())
+ .toEqual({width: 48, height: 48});
+
+ settings.maxWidth = 1001;
+ settings.maxHeight = 999;
+ settings.minWidth = 0;
+ settings.minHeight = 0;
+ expect(settings.getMinDimensionsToMatchAspectRatio())
+ .toEqual({width: 1001, height: 999});
+ });
+
+ it('is frozen after final adjustments are made', function() {
+ 'use strict';
+ const settings = new mr.mirror.Settings();
+ expect(Object.isFrozen(settings)).toBe(false);
+ settings.makeFinalAdjustmentsAndFreeze();
+ expect(Object.isFrozen(settings)).toBe(true);
+ expect(() => {
+ settings.maxWidth = 999;
+ }).toThrow();
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/stream_capture/capture_parameters.js b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/stream_capture/capture_parameters.js
new file mode 100644
index 00000000000..dbf1d77fa8c
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/stream_capture/capture_parameters.js
@@ -0,0 +1,208 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Stream capture parameters.
+ */
+
+goog.provide('mr.mirror.CaptureParameters');
+goog.provide('mr.mirror.CaptureSurfaceType');
+
+goog.require('mr.Assertions');
+goog.require('mr.mirror.Config');
+
+
+/**
+ * Parameters that configure and control local media capture.
+ */
+mr.mirror.CaptureParameters = class {
+ /**
+ * @param {!mr.mirror.CaptureSurfaceType} captureSurface
+ * @param {!mr.mirror.Settings} mirrorSettings
+ * @param {string=} opt_offscreenTabUrl
+ * @param {string=} opt_presentationId
+ */
+ constructor(
+ captureSurface, mirrorSettings, opt_offscreenTabUrl, opt_presentationId) {
+ if (opt_offscreenTabUrl) {
+ mr.Assertions.assert(
+ captureSurface == mr.mirror.CaptureSurfaceType.OFFSCREEN_TAB);
+ mr.Assertions.assert(opt_presentationId);
+ }
+
+ /** @type {!mr.mirror.CaptureSurfaceType} */
+ this.captureSurface = captureSurface;
+
+ /** @type {!mr.mirror.Settings} */
+ this.mirrorSettings = mirrorSettings;
+
+ /** @type {?string} */
+ this.offscreenTabUrl = opt_offscreenTabUrl || null;
+
+ /** @type {?string} */
+ this.presentationId = opt_presentationId || null;
+ }
+
+ /**
+ * @return {boolean} True if this is for tab capture
+ */
+ isTab() {
+ return this.captureSurface == mr.mirror.CaptureSurfaceType.TAB;
+ }
+
+ /**
+ * @return {boolean} True if this is for desktop capture
+ */
+ isDesktop() {
+ return this.captureSurface == mr.mirror.CaptureSurfaceType.DESKTOP;
+ }
+
+ /**
+ * @return {boolean} True if this is for offscreen tab capture
+ */
+ isOffscreenTab() {
+ return this.captureSurface == mr.mirror.CaptureSurfaceType.OFFSCREEN_TAB;
+ }
+
+ /**
+ * @param {string=} sourceId The source ID of the desktop media.
+ * @return {!MediaStreamConstraints|!Object} Media constraints for use with
+ * platform capture APIs.
+ */
+ toMediaConstraints(sourceId = undefined) {
+ if (this.isTab()) {
+ return this.toTabMediaConstraints_();
+ } else if (this.isOffscreenTab()) {
+ return this.toOffscreenTabMediaConstraints_();
+ } else {
+ return this.toDesktopMediaConstraints_(sourceId);
+ }
+ }
+
+ /**
+ * @return {!MediaConstraints} Media constraints for use with platform
+ * capture APIs.
+ * @private
+ */
+ toTabMediaConstraints_() {
+
+ //
+ // Also the tabCapture API shape doesn't match getUserMedia, wich combines
+ // audio and video constraints into a single dictionary.
+ //
+ // Both of these issues make it hard to type toMediaConstraints() properly.
+ const constraints = /** @type {!MediaConstraints} */
+ ({
+ 'audio': this.mirrorSettings.shouldCaptureAudio,
+ 'video': this.mirrorSettings.shouldCaptureVideo
+ });
+
+ if (this.mirrorSettings.shouldCaptureVideo) {
+ constraints['videoConstraints'] = {
+ 'mandatory': {'enableAutoThrottling': true}
+ };
+ this.setCommonVideoConstraints_(
+ constraints['videoConstraints']['mandatory']);
+ }
+ return constraints;
+ }
+
+ /**
+ * @return {!MediaConstraints} Media constraints for use with offscreen
+ * capture APIs.
+ * @private
+ */
+ toOffscreenTabMediaConstraints_() {
+ mr.Assertions.assert(
+ this.presentationId, 'Missing offscreen capture presentation id');
+ const constraints = this.toTabMediaConstraints_();
+ constraints['presentationId'] = this.presentationId;
+ return constraints;
+ };
+
+ /**
+ * @param {string=} sourceId The source id of the desktop media. Only required
+ * if capturing video.
+ * @return {!MediaStreamConstraints} Media constraints for use with platform
+ * capture APIs.
+ * @private
+ */
+ toDesktopMediaConstraints_(sourceId = undefined) {
+ const constraints = /** @type {!MediaStreamConstraints} */
+ ({'audio': false, 'video': false});
+
+ if (this.mirrorSettings.shouldCaptureVideo) {
+ mr.Assertions.assert(sourceId);
+ constraints['video'] = {
+ 'mandatory': {
+ 'chromeMediaSource': 'desktop',
+ 'chromeMediaSourceId': sourceId,
+ }
+ };
+ this.setCommonVideoConstraints_(constraints['video']['mandatory']);
+
+ if (mr.mirror.Config.isDesktopAudioCaptureAvailable &&
+ this.mirrorSettings.shouldCaptureAudio) {
+ // NOTE(mfoltz): Nothing in Chrome seems to consume the audio sourceId;
+ // however, continuing to pass it in case something changes in the
+ // future.
+ constraints['audio'] = {
+ 'mandatory': {
+ 'chromeMediaSource': 'system',
+ 'chromeMediaSourceId': sourceId,
+ }
+ };
+ }
+ } else if (this.mirrorSettings.shouldCaptureAudio) {
+ mr.Assertions.assert(!sourceId);
+ mr.Assertions.assert(mr.mirror.Config.isDesktopAudioCaptureAvailable);
+ constraints['audio'] = {
+ 'mandatory': {
+ 'chromeMediaSource': 'system',
+ }
+ };
+ }
+
+ return constraints;
+ }
+
+ /**
+ * Helper to populate common video constraints fields for both capture APIs.
+ * @param {!MediaStreamConstraints|!MediaConstraints} constraints
+ * @private
+ */
+ setCommonVideoConstraints_(constraints) {
+ // If sender-side letterboxing is being used, pass altered min dimensions to
+ // the capture API which match the aspect ratio of the max dimensions. The
+ // capture API will interpret this to mean that it must perform
+ // letterboxing/pillarboxing.
+ let minWidth = this.mirrorSettings.minWidth;
+ let minHeight = this.mirrorSettings.minHeight;
+ if (this.mirrorSettings.senderSideLetterboxing) {
+ const altMin = this.mirrorSettings.getMinDimensionsToMatchAspectRatio();
+ minWidth = altMin.width;
+ minHeight = altMin.height;
+ }
+
+ Object.assign(constraints, {
+ 'minWidth': minWidth,
+ 'minHeight': minHeight,
+ 'maxWidth': this.mirrorSettings.maxWidth,
+ 'maxHeight': this.mirrorSettings.maxHeight,
+ 'minFrameRate': this.mirrorSettings.minFrameRate,
+ 'maxFrameRate': this.mirrorSettings.maxFrameRate,
+ });
+ }
+};
+
+
+/**
+ * Possible capture modes.
+ * @enum {string}
+ */
+mr.mirror.CaptureSurfaceType = {
+ TAB: 'tab',
+ DESKTOP: 'desktop',
+ OFFSCREEN_TAB: 'offscreen_tab'
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/stream_capture/mirror_media_stream.js b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/stream_capture/mirror_media_stream.js
new file mode 100644
index 00000000000..370b91fcb15
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/stream_capture/mirror_media_stream.js
@@ -0,0 +1,340 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Media stream for use with tab and desktop capture.
+ *
+ * A media stream has a video and/or audio track. Each capture source (i.e.,
+ * tab) should use its own MediaStream object and call start() to initiate
+ * capture.
+ */
+
+goog.provide('mr.mirror.MirrorMediaStream');
+
+goog.require('mr.Assertions');
+goog.require('mr.Logger');
+goog.require('mr.MirrorAnalytics');
+goog.require('mr.PlatformUtils');
+goog.require('mr.mirror.Config');
+goog.require('mr.mirror.Error');
+
+/**
+ * Constructs a new MediaStream that will capture media according to
+ * captureParams.
+ */
+mr.mirror.MirrorMediaStream = class {
+ /**
+ * @param {!mr.mirror.CaptureParameters} captureParams
+ */
+ constructor(captureParams) {
+ /** @private {!mr.mirror.CaptureParameters} */
+ this.captureParams_ = captureParams;
+
+ /** @private {?function()} */
+ this.onStreamEnded_ = null;
+
+ /** @private {?MediaStream} */
+ this.mediaStream_ = null;
+
+ /** @private {mr.Logger} */
+ this.logger_ = mr.Logger.getInstance('mr.mirror.MirrorMediaStream');
+ }
+
+ /**
+ * @param {?function()} onStreamEnded Invoked when the stream fires an
+ * onended event.
+ */
+ setOnStreamEnded(onStreamEnded) {
+ this.onStreamEnded_ = onStreamEnded;
+ }
+
+ /**
+ * @return {!mr.mirror.CaptureParameters}
+ */
+ getCaptureParams() {
+ return this.captureParams_;
+ }
+
+ /**
+ * @return {?MediaStream}
+ */
+ getMediaStream() {
+ return this.mediaStream_;
+ }
+
+ /**
+ * Starts capturing media and sets audioTrack and videoTrack.
+ * @return {!Promise<!mr.mirror.MirrorMediaStream>} Fulfilled when capture
+ * has started.
+ */
+ start() {
+ if (this.captureParams_.isTab()) {
+ return this.startTabCapturing_();
+ } else if (this.captureParams_.isOffscreenTab()) {
+ return this.startOffscreenTabCapturing_();
+ } else {
+ return this.startDesktopCapturing_();
+ }
+ }
+
+ /**
+ * @return {!Promise<!mr.mirror.MirrorMediaStream>} Fulfilled when capture
+ * has started.
+ * @private
+ */
+ startTabCapturing_() {
+ const constraints = this.captureParams_.toMediaConstraints();
+ this.logger_.info(
+ 'Starting tab capture with constraints ' + JSON.stringify(constraints));
+
+ return new Promise((resolve, reject) => {
+ // Note: There's a subtle reason to NOT pass |resolve| as the
+ // callback function to the chrome.tabCapture.capture() call here.
+ // Because this is an extension API, when an error occurs, the value
+ // in chrome.runtime.lastError is only available during the call to
+ // the callback function. However, the Promise.then() method runs
+ // its function at a later time, when chrome.runtime.lastError is no
+ // longer set.
+ chrome.tabCapture.capture(constraints, stream => {
+ if (stream) {
+ this.setStream_(stream);
+ resolve(this);
+ } else {
+ reject(this.createTabCaptureError_());
+ }
+ });
+
+ // Set a timer to reject the promise after a delay.
+ //
+
+ window.setTimeout(() => {
+ // In normal usage, this will be a no-op because this promise will
+ // already have been resolved by the call to
+ // chrome.tabCapture.capture above.
+ reject(new mr.mirror.Error(
+ 'chrome.tabCapture.capture failed to call its callback',
+ mr.MirrorAnalytics.CapturingFailure.CAPTURE_TAB_TIMEOUT));
+ }, mr.mirror.MirrorMediaStream.TAB_CAPTURE_TIMEOUT_);
+ });
+ }
+
+ /**
+ * @param {?Event} event The ended event.
+ * @private
+ */
+ handleTrackEnded_(event) {
+ if (event) {
+ this.logger_.info(
+ () => 'Track ' + JSON.stringify(event.target) + ' ended');
+ }
+ this.stop();
+ }
+
+ /**
+ * Returns an Error corresponding to a chrome.tabCapture error.
+ * @return {!mr.mirror.Error} The corresponding error.
+ * @private
+ */
+ createTabCaptureError_() {
+ // As of Chrome 51, when |stream| is null, chrome.runtime.lastError.message
+ // should always be set to a non-empty string. If it is not, fall back to
+ // the default error message so everyone can yell at miu@.
+ if (chrome.runtime.lastError && chrome.runtime.lastError.message) {
+ return new mr.mirror.Error(
+ chrome.runtime.lastError.message,
+ mr.MirrorAnalytics.CapturingFailure.TAB_FAIL);
+ } else {
+ return new mr.mirror.Error(
+ mr.mirror.MirrorMediaStream.EMPTY_STREAM_,
+ mr.MirrorAnalytics.CapturingFailure.CAPTURE_TAB_FAIL_EMPTY_STREAM);
+ }
+ }
+
+ /**
+ * Requests a screen capture source from the user via a native dialog and
+ * returns the source ID, or rejects if a timeout is reached or the user
+ * cancels.
+ * @param {number=} timeoutMillis The timeout in milliseconds.
+ * @return {!Promise<string>} Fulfilled with the source ID.
+ * @private
+ */
+ requestScreenCaptureSourceId_(
+ timeoutMillis = mr.mirror.MirrorMediaStream.WINDOW_PICKER_TIMEOUT_) {
+ return new Promise((resolve, reject) => {
+ const desktopChooserConfig = ['screen', 'audio'];
+ if (mr.PlatformUtils.getCurrentOS() == mr.PlatformUtils.OS.LINUX) {
+ desktopChooserConfig.push('window');
+ }
+ let requestId;
+ // Wait 60 seconds and then cancel the picker and reject the
+ // promise.
+
+ const timeoutId = window.setTimeout(() => {
+ if (requestId) {
+ chrome.desktopCapture.cancelChooseDesktopMedia(requestId);
+ }
+ reject(new mr.mirror.Error(
+ 'timeout',
+ mr.MirrorAnalytics.CapturingFailure
+ .CAPTURE_DESKTOP_FAIL_ERROR_TIMEOUT));
+ }, timeoutMillis);
+ // https://developer.chrome.com/extensions/desktopCapture#method-chooseDesktopMedia
+ requestId = chrome.desktopCapture.chooseDesktopMedia(
+ desktopChooserConfig, sourceId => {
+ window.clearTimeout(timeoutId);
+ if (!sourceId) {
+ // User cancelled the desktop media selector prompt.
+ reject(new mr.mirror.Error(
+ 'User cancelled capture dialog',
+ mr.MirrorAnalytics.CapturingFailure
+ .CAPTURE_DESKTOP_FAIL_ERROR_USER_CANCEL));
+ } else {
+ resolve(sourceId);
+ }
+ });
+ });
+ }
+
+ /**
+ * Generates a screen capture MediaStream from the given
+ * MediaStreamConstraints.
+ * @param {!MediaStreamConstraints} constraints The constraints object to use.
+ * @return {!Promise<MediaStream>} Fulfilled with the MediaStream from
+ * capture.
+ * @private
+ */
+ generateScreenCaptureStream_(constraints) {
+ return new Promise((resolve, reject) => {
+ this.logger_.info(
+ () => 'Starting desktop capture with constraints ' +
+ JSON.stringify(constraints));
+ navigator.mediaDevices.getUserMedia(constraints)
+ .then(
+ stream => {
+ if (!stream) {
+ // NOTE(miu): This implies that getUserMedia is broken, and it
+ // may also be breaking chrome.tabCapture.
+ reject(new mr.mirror.Error(
+ mr.mirror.MirrorMediaStream.EMPTY_STREAM_,
+ mr.MirrorAnalytics.CapturingFailure.DESKTOP_FAIL));
+ }
+ this.setStream_(stream);
+ resolve(stream);
+ },
+ error => {
+ let errorReason =
+ mr.MirrorAnalytics.CapturingFailure.DESKTOP_FAIL;
+ // Certain errors indicate the user cancelled the request.
+ // https://www.w3.org/TR/mediacapture-streams/#methods-5
+ if (error.name == 'NotAllowedError') {
+ errorReason = mr.MirrorAnalytics.CapturingFailure
+ .CAPTURE_DESKTOP_FAIL_ERROR_USER_CANCEL;
+ }
+ reject(new mr.mirror.Error(
+ `${error.name} ${error.constraintName}: ${error.message}`,
+ errorReason));
+ });
+ });
+ }
+
+ /**
+ * @return {!Promise<!mr.mirror.MirrorMediaStream>} Fulfilled when capture
+ * has started.
+ * @private
+ */
+ startDesktopCapturing_() {
+ if (mr.mirror.Config.isDesktopAudioCaptureAvailable &&
+ this.captureParams_.mirrorSettings.shouldCaptureAudio &&
+ !this.captureParams_.mirrorSettings.shouldCaptureVideo) {
+ return this
+ .generateScreenCaptureStream_(
+ this.captureParams_.toMediaConstraints())
+ .then(_ => this);
+ }
+
+ // Video capture requires asking the user to pick which screen to capture.
+
+ return this.requestScreenCaptureSourceId_().then(sourceId => {
+ const constraints = this.captureParams_.toMediaConstraints(sourceId);
+ return this.generateScreenCaptureStream_(constraints).then(_ => this);
+ });
+ }
+
+ /**
+ * @return {!Promise<!mr.mirror.MirrorMediaStream>} Fulfilled when capture
+ * has started.
+ * @private
+ */
+ startOffscreenTabCapturing_() {
+ mr.Assertions.assert(!!this.captureParams_.offscreenTabUrl);
+ const constraints = this.captureParams_.toMediaConstraints();
+ this.logger_.info(
+ () => 'Starting offscreen tab capture with constraints ' +
+ JSON.stringify(constraints));
+ return new Promise((resolve, reject) => {
+ chrome.tabCapture.captureOffscreenTab(
+ this.captureParams_.offscreenTabUrl.toString(), constraints,
+ stream => {
+ if (stream) {
+ this.setStream_(stream);
+ resolve(this);
+ } else {
+ reject(this.createTabCaptureError_());
+ }
+ });
+ });
+ }
+
+ /**
+ * @param {!MediaStream} stream
+ * @private
+ */
+ setStream_(stream) {
+ this.mediaStream_ = stream;
+ mr.Assertions.assert(
+ stream.getAudioTracks().length || stream.getVideoTracks().length,
+ 'Expecting at least one audio or video track.');
+ // For desktop capturing, users may stop capturing via desktop capturing's
+ // own stop button, which triggers onended event.
+ stream.getTracks().forEach(track => {
+ track.onended = this.handleTrackEnded_.bind(this);
+ });
+ }
+
+ /**
+ * Stops captured streams.
+ */
+ stop() {
+ if (!this.mediaStream_) return;
+ this.mediaStream_.getTracks().forEach(track => {
+ track.onended = null;
+ track.stop();
+ });
+ this.mediaStream_ = null;
+ if (this.onStreamEnded_) {
+ this.onStreamEnded_();
+ }
+ }
+};
+
+
+/**
+ * The number of milliseconds to wait after for the browser to call the callback
+ * function after calling chrome.tabCapture.capture.
+ * @private @const {number}
+ */
+mr.mirror.MirrorMediaStream.TAB_CAPTURE_TIMEOUT_ = 5000;
+
+
+/**
+ * @private @const {number}
+ */
+mr.mirror.MirrorMediaStream.WINDOW_PICKER_TIMEOUT_ = 60000;
+
+
+/**
+ * Error messaging for reporting of empty streams.
+ * @private @const {string}
+ */
+mr.mirror.MirrorMediaStream.EMPTY_STREAM_ = 'empty_stream';
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/stream_capture/mirror_media_stream_test.js b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/stream_capture/mirror_media_stream_test.js
new file mode 100644
index 00000000000..45746847799
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/mirror_services/stream_capture/mirror_media_stream_test.js
@@ -0,0 +1,407 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.setTestOnly();
+
+goog.require('mr.PlatformUtils');
+goog.require('mr.mirror.CaptureParameters');
+goog.require('mr.mirror.CaptureSurfaceType');
+goog.require('mr.mirror.Config');
+goog.require('mr.mirror.Error');
+goog.require('mr.mirror.MirrorMediaStream');
+goog.require('mr.mirror.Settings');
+
+describe('mr.mirror.MirrorMediaStream', () => {
+ let captureParams;
+ let instance;
+ let mediaStream;
+ let mirrorSettings;
+
+ beforeEach(() => {
+ chrome.runtime.lastError = null;
+ chrome.tabCapture =
+ jasmine.createSpyObj('tabCapture', ['capture', 'captureOffscreenTab']);
+ chrome.desktopCapture = jasmine.createSpyObj(
+ 'desktopCapture', ['chooseDesktopMedia', 'cancelChooseDesktopMedia']);
+ spyOn(navigator.mediaDevices, 'getUserMedia');
+ spyOn(mr.PlatformUtils, 'getCurrentOS');
+
+ mediaStream = jasmine.createSpyObj(
+ 'mediaStream', ['getAudioTracks', 'getVideoTracks', 'getTracks']);
+ mirrorSettings = new mr.mirror.Settings();
+ captureParams = new mr.mirror.CaptureParameters(
+ mr.mirror.CaptureSurfaceType.DESKTOP, mirrorSettings);
+ instance = new mr.mirror.MirrorMediaStream(captureParams);
+ jasmine.clock().install();
+ });
+
+ afterEach(() => {
+ jasmine.clock().uninstall();
+ });
+
+ it('accessor for capture params returns initial value', () => {
+ expect(instance.getCaptureParams()).toBe(captureParams);
+ });
+
+ describe('when capturing a tab', () => {
+ beforeEach(() => {
+ captureParams.captureSurface = mr.mirror.CaptureSurfaceType.TAB;
+ });
+
+ it('stores the stream upon successful capture', (done) => {
+ mediaStream.getVideoTracks.and.returnValue([{}]);
+ mediaStream.getAudioTracks.and.returnValue([{}]);
+ mediaStream.getTracks.and.returnValue([{}]);
+ chrome.tabCapture.capture.and.callFake((constraints, callback) => {
+ callback(mediaStream);
+ });
+
+ instance.start()
+ .then(() => {
+ expect(chrome.tabCapture.capture).toHaveBeenCalled();
+ expect(instance.getMediaStream()).toBe(mediaStream);
+ done();
+ })
+ .catch(fail);
+ });
+
+ it('rejects with an error upon empty stream with error message', (done) => {
+ chrome.runtime.lastError = {message: 'expected-message'};
+ chrome.tabCapture.capture.and.callFake((constraints, callback) => {
+ callback();
+ });
+
+ instance.start().catch((err) => {
+ expect(err instanceof mr.mirror.Error).toBe(true);
+ expect(err.reason).toBe(mr.MirrorAnalytics.CapturingFailure.TAB_FAIL);
+ expect(err.message).toBe('expected-message');
+ expect(instance.getMediaStream()).toBe(null);
+ done();
+ });
+ });
+
+ it('rejects with an error upon empty stream with empty message', (done) => {
+ chrome.tabCapture.capture.and.callFake((constraints, callback) => {
+ callback();
+ });
+
+ instance.start().catch((err) => {
+ expect(err instanceof mr.mirror.Error).toBe(true);
+ expect(err.reason)
+ .toBe(mr.MirrorAnalytics.CapturingFailure
+ .CAPTURE_TAB_FAIL_EMPTY_STREAM);
+ expect(instance.getMediaStream()).toBe(null);
+ done();
+ });
+ });
+
+ it('rejects with an error upon timeout', (done) => {
+ instance.start().catch((err) => {
+ expect(window.chrome.tabCapture.capture).toHaveBeenCalled();
+ expect(err instanceof mr.mirror.Error).toBe(true);
+ expect(err.reason)
+ .toBe(mr.MirrorAnalytics.CapturingFailure.CAPTURE_TAB_TIMEOUT);
+ expect(instance.getMediaStream()).toBe(null);
+ done();
+ });
+ jasmine.clock().tick(5001);
+ });
+ });
+
+ describe('when capturing an off-screen tab', () => {
+ beforeEach(() => {
+ captureParams.offscreenTabUrl = 'offscreen-tab-url';
+ captureParams.presentationId = 'offscreen-tab-presentation-id';
+ captureParams.captureSurface = mr.mirror.CaptureSurfaceType.OFFSCREEN_TAB;
+ });
+
+ it('stores the stream upon successful capture', (done) => {
+ mediaStream.getVideoTracks.and.returnValue([{}]);
+ mediaStream.getAudioTracks.and.returnValue([{}]);
+ mediaStream.getTracks.and.returnValue([{}]);
+ chrome.tabCapture.captureOffscreenTab.and.callFake(
+ (tabUrl, constraints, callback) => {
+ expect(tabUrl).toBe('offscreen-tab-url');
+ callback(mediaStream);
+ });
+
+ instance.start()
+ .then(() => {
+ expect(window.chrome.tabCapture.captureOffscreenTab)
+ .toHaveBeenCalled();
+ expect(instance.getMediaStream()).toBe(mediaStream);
+ done();
+ })
+ .catch(fail);
+ });
+
+ it('rejects with an error upon empty stream with error message', (done) => {
+ chrome.runtime.lastError = {message: 'expected-message'};
+ chrome.tabCapture.captureOffscreenTab.and.callFake(
+ (tabUrl, constraints, callback) => callback());
+
+ instance.start().catch((err) => {
+ expect(err instanceof mr.mirror.Error).toBe(true);
+ expect(err.reason).toBe(mr.MirrorAnalytics.CapturingFailure.TAB_FAIL);
+ expect(err.message).toBe('expected-message');
+ expect(instance.getMediaStream()).toBe(null);
+ done();
+ });
+ });
+
+ it('rejects with an error upon empty stream with empty message', (done) => {
+ chrome.tabCapture.captureOffscreenTab.and.callFake(
+ (tabUrl, constraints, callback) => callback());
+
+ instance.start().catch((err) => {
+ expect(err instanceof mr.mirror.Error).toBe(true);
+ expect(err.reason)
+ .toBe(mr.MirrorAnalytics.CapturingFailure
+ .CAPTURE_TAB_FAIL_EMPTY_STREAM);
+ expect(instance.getMediaStream()).toBe(null);
+ done();
+ });
+ });
+ });
+
+ describe('when capturing the desktop', () => {
+ const supportsAudioCapture =
+ mr.mirror.Config.isDesktopAudioCaptureAvailable;
+
+ beforeEach(() => {
+ captureParams.captureSurface = mr.mirror.CaptureSurfaceType.DESKTOP;
+ });
+
+ afterEach(() => {
+ mr.mirror.Config.isDesktopAudioCaptureAvailable = supportsAudioCapture;
+ });
+
+ it('stores the audio and video streams upon successful capture', (done) => {
+ mediaStream.getVideoTracks.and.returnValue([{}]);
+ mediaStream.getAudioTracks.and.returnValue([{}]);
+ mediaStream.getTracks.and.returnValue([{}]);
+
+ chrome.desktopCapture.chooseDesktopMedia.and.callFake(
+ (config, callback) => {
+ callback('source-id');
+ });
+ navigator.mediaDevices.getUserMedia.and.callFake(
+ constraints => Promise.resolve(mediaStream));
+
+ instance.start()
+ .then(() => {
+ expect(chrome.desktopCapture.chooseDesktopMedia).toHaveBeenCalled();
+ expect(navigator.mediaDevices.getUserMedia).toHaveBeenCalled();
+ expect(instance.getMediaStream()).toBe(mediaStream);
+ done();
+ })
+ .catch(fail);
+ });
+
+ it('stores the audio stream upon successful capture', (done) => {
+ mr.mirror.Config.isDesktopAudioCaptureAvailable = true;
+ const audioOnlyMirrorSettings = new mr.mirror.Settings();
+ audioOnlyMirrorSettings.shouldCaptureVideo = false;
+ const audioOnlyCaptureParams = new mr.mirror.CaptureParameters(
+ mr.mirror.CaptureSurfaceType.DESKTOP, audioOnlyMirrorSettings);
+ const audioOnlyInstance =
+ new mr.mirror.MirrorMediaStream(audioOnlyCaptureParams);
+
+ mediaStream.getVideoTracks.and.returnValue([{}]);
+ mediaStream.getAudioTracks.and.returnValue([{}]);
+ mediaStream.getTracks.and.returnValue([{}]);
+
+ navigator.mediaDevices.getUserMedia.and.callFake(
+ constraints => Promise.resolve(mediaStream));
+
+ audioOnlyInstance.start()
+ .then(() => {
+ expect(chrome.desktopCapture.chooseDesktopMedia)
+ .not.toHaveBeenCalled();
+ expect(navigator.mediaDevices.getUserMedia).toHaveBeenCalled();
+ expect(audioOnlyInstance.getMediaStream()).toBe(mediaStream);
+ done();
+ })
+ .catch(fail);
+ });
+
+ it('allows choosing only screen, audio for non-linux platforms', (done) => {
+ mr.PlatformUtils.getCurrentOS.and.returnValue(
+ mr.PlatformUtils.OS.WINDOWS);
+ chrome.desktopCapture.chooseDesktopMedia.and.callFake(
+ (config, callback) => {
+ expect(config).toContain('screen');
+ expect(config).toContain('audio');
+ expect(config).not.toContain('window');
+ done();
+ });
+ instance.start();
+ });
+
+ it('allows choosing screen, audio, window for linux platforms', (done) => {
+ mr.PlatformUtils.getCurrentOS.and.returnValue(mr.PlatformUtils.OS.LINUX);
+ chrome.desktopCapture.chooseDesktopMedia.and.callFake(
+ (config, callback) => {
+ expect(config).toContain('screen');
+ expect(config).toContain('audio');
+ expect(config).toContain('window');
+ done();
+ });
+ instance.start();
+ });
+
+ it('rejects with an error upon timeout in desktop chooser', (done) => {
+ chrome.desktopCapture.chooseDesktopMedia.and.returnValue('expected-id');
+
+ instance.start().catch((err) => {
+ expect(chrome.desktopCapture.chooseDesktopMedia).toHaveBeenCalled();
+ expect(chrome.desktopCapture.cancelChooseDesktopMedia)
+ .toHaveBeenCalledWith('expected-id');
+ expect(navigator.mediaDevices.getUserMedia).not.toHaveBeenCalled();
+ expect(err instanceof mr.mirror.Error).toBe(true);
+ expect(err.reason)
+ .toBe(mr.MirrorAnalytics.CapturingFailure
+ .CAPTURE_DESKTOP_FAIL_ERROR_TIMEOUT);
+ expect(err.message).toBe('timeout');
+ expect(instance.getMediaStream()).toBe(null);
+ done();
+ });
+
+ jasmine.clock().tick(60001);
+ });
+
+ it('rejects with an error when user cancels desktop picker', (done) => {
+ chrome.desktopCapture.chooseDesktopMedia.and.callFake(
+ (config, callback) => {
+ callback(/* no source id */);
+ });
+
+ instance.start().catch((err) => {
+ expect(chrome.desktopCapture.chooseDesktopMedia).toHaveBeenCalled();
+ expect(navigator.mediaDevices.getUserMedia).not.toHaveBeenCalled();
+ expect(err instanceof mr.mirror.Error).toBe(true);
+ expect(err.reason)
+ .toBe(mr.MirrorAnalytics.CapturingFailure
+ .CAPTURE_DESKTOP_FAIL_ERROR_USER_CANCEL);
+ expect(err.message).toMatch(/cancelled/i);
+ expect(instance.getMediaStream()).toBe(null);
+ done();
+ });
+ });
+
+ it('rejects with an error upon getUserMedia error', (done) => {
+ chrome.desktopCapture.chooseDesktopMedia.and.callFake(
+ (config, callback) => {
+ callback('source-id');
+ });
+
+ navigator.mediaDevices.getUserMedia.and.callFake(
+ constraints => Promise.reject(
+ new DOMException('expected-message', 'SecurityError')));
+
+ instance.start().catch((err) => {
+ expect(err instanceof mr.mirror.Error).toBe(true);
+ expect(err.reason)
+ .toBe(mr.MirrorAnalytics.CapturingFailure.DESKTOP_FAIL);
+ expect(err.message).toMatch(/\bSecurityError\b/);
+ expect(err.message).toMatch(/\bexpected-message\b/);
+ expect(instance.getMediaStream()).toBe(null);
+ done();
+ });
+ });
+
+ it('rejects with an cancelled error upon NotAllowedError', (done) => {
+ chrome.desktopCapture.chooseDesktopMedia.and.callFake(
+ (config, callback) => {
+ callback('source-id');
+ });
+ navigator.mediaDevices.getUserMedia.and.callFake(
+ constraints => Promise.reject(
+ new DOMException('expected-message', 'NotAllowedError')));
+
+ instance.start().catch((err) => {
+ expect(err instanceof mr.mirror.Error).toBe(true);
+ expect(err.reason)
+ .toBe(mr.MirrorAnalytics.CapturingFailure
+ .CAPTURE_DESKTOP_FAIL_ERROR_USER_CANCEL);
+ expect(err.message).toMatch(/\bNotAllowedError\b/);
+ expect(err.message).toMatch(/\bexpected-message\b/);
+ expect(instance.getMediaStream()).toBe(null);
+ done();
+ });
+ });
+
+ it('rejects with an error upon empty stream object', (done) => {
+ chrome.desktopCapture.chooseDesktopMedia.and.callFake(
+ (config, callback) => {
+ callback('source-id');
+ });
+ navigator.mediaDevices.getUserMedia.and.callFake(
+ constraints => Promise.resolve(null));
+
+ instance.start().catch((err) => {
+ expect(err instanceof mr.mirror.Error).toBe(true);
+ expect(err.reason)
+ .toBe(mr.MirrorAnalytics.CapturingFailure.DESKTOP_FAIL);
+ expect(instance.getMediaStream()).toBe(null);
+ done();
+ });
+ });
+ });
+
+ describe('when stopping a stream', () => {
+ let startPromise;
+ let track;
+
+ beforeEach(() => {
+ track = jasmine.createSpyObj('track', ['stop']);
+ mediaStream.getVideoTracks.and.returnValue([{}]);
+ mediaStream.getAudioTracks.and.returnValue([{}]);
+ mediaStream.getTracks.and.returnValue([track]);
+ captureParams.captureSurface = mr.mirror.CaptureSurfaceType.TAB;
+ chrome.tabCapture.capture.and.callFake((constraints, callback) => {
+ callback(mediaStream);
+ });
+
+ startPromise = instance.start();
+ });
+
+ it('calls stop() on the tracks', (done) => {
+ startPromise
+ .then(() => {
+ instance.stop();
+ expect(track.stop).toHaveBeenCalled();
+ expect(track.onended).toBe(null);
+ expect(instance.getMediaStream()).toBe(null);
+ done();
+ })
+ .catch(fail);
+ });
+
+ it('automatically stops when track ends', (done) => {
+ startPromise
+ .then(() => {
+ track.onended();
+ expect(track.stop).toHaveBeenCalled();
+ expect(track.onended).toBe(null);
+ expect(instance.getMediaStream()).toBe(null);
+ done();
+ })
+ .catch(fail);
+ });
+
+ it('calls the onStreamEnded callback if it exists', (done) => {
+ const onStreamEndedSpy = jasmine.createSpy('onStreamEnded');
+ instance.setOnStreamEnded(onStreamEndedSpy);
+
+ startPromise
+ .then(() => {
+ instance.stop();
+ expect(onStreamEndedSpy).toHaveBeenCalled();
+ done();
+ })
+ .catch(fail);
+ });
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/module.js b/chromium/chrome/browser/resources/media_router/extension/src/module.js
new file mode 100644
index 00000000000..248c6cb6097
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/module.js
@@ -0,0 +1,247 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Contains definition for modules and the module loader.
+ * The Media Router extension is logically separated into modules. Each module
+ * and their corresponding bundle path are registered at startup
+ * time. When a module is required, they will be loaded on-demand.
+ */
+
+goog.provide('mr.Module');
+goog.provide('mr.ModuleId');
+
+goog.require('mr.Logger');
+goog.require('mr.PromiseResolver');
+
+
+/**
+ * Identifier for each module.
+ * @enum {string}
+ */
+mr.ModuleId = {
+ CAST_CHANNEL_SERVICE: 'mr.cast.ChannelService',
+ CAST_SINK_DISCOVERY_SERVICE: 'mr.cast.SinkDiscoveryService',
+ CAST_STREAMING_SERVICE: 'mr.mirror.cast.Service',
+ SLARTI_SINK_DISCOVERY_SERVICE: 'mr.cloud.slarti.SinkDiscoveryService',
+ WEAVE_SINK_DISCOVERY_SERVICE: 'mr.cloud.discovery.WeaveSinkDiscoveryService',
+ HANGOUTS_SERVICE: 'mr.mirror.hangouts.HangoutsService',
+ MEETINGS_SERVICE: 'mr.mirror.hangouts.MeetingsService',
+ PROVIDER_MANAGER: 'mr.ProviderManager',
+ WEBRTC_STREAMING_SERVICE: 'mr.mirror.webrtc.WebRtcService'
+};
+
+
+/**
+ * Identifier for bundles. A bundle is a js file containing a collection of
+ * modules.
+
+ * @enum {string}
+ */
+mr.Bundle = {
+ MAIN: 'background_script.js',
+ MIRRORING_CAST_STREAMING: 'mirroring_cast_streaming.js',
+ MIRRORING_HANGOUTS: 'mirroring_hangouts.js',
+ MIRRORING_WEBRTC: 'mirroring_webrtc.js'
+};
+
+
+/**
+ * Base class for a module. When a module is loaded and initialized, it should
+ * call mr.Module.onModuleLoaded to inform its dependencies that it is ready.
+ */
+mr.Module = class {
+ /**
+ * Returns the module with the given ID if it is already initialized, null
+ * otherwise.
+ * @param {mr.ModuleId} moduleId
+ * @return {?mr.Module}
+ */
+ static get(moduleId) {
+ return mr.Module.moduleById_.get(moduleId) || null;
+ }
+
+ /**
+ * Loads the module with the given ID. If the module is already loaded, the
+ * Promise is resolved immediately.
+ * @param {mr.ModuleId} moduleId
+ * @return {!Promise<!mr.Module>} Resolved with the module when
+ * it is loaded.
+ */
+ static load(moduleId) {
+ const module = mr.Module.get(moduleId);
+ if (module) {
+ return Promise.resolve(module);
+ }
+ let resolver = mr.Module.resolverByModuleId_.get(moduleId);
+ if (!resolver) {
+ resolver = new mr.PromiseResolver();
+ mr.Module.resolverByModuleId_.set(moduleId, resolver);
+ mr.Module.loadBundleForModule_(moduleId, resolver);
+ }
+
+ return resolver.promise;
+ }
+
+ /**
+ * Loads the bundle corresponding to the given module. Called the first time
+ * a module is requested.
+ * @param {mr.ModuleId} moduleId
+ * @param {!mr.PromiseResolver<!mr.Module>} resolver Rejected if the bundle
+ * associated with the module won't be loaded due to permanent error.
+ * @private
+ */
+ static loadBundleForModule_(moduleId, resolver) {
+ const bundle = mr.Module.getBundle_(moduleId);
+ if (!bundle) {
+ resolver.reject(new Error(`No corresponding bundle for ${moduleId}`));
+ return;
+ }
+
+ if (mr.Module.DEFAULT_LOAD_BUNDLES_.has(bundle)) {
+ return;
+ }
+
+ // Check if the bundle has been not requested previously.
+ let bundlePromise = mr.Module.bundlePromises_.get(bundle);
+ if (!bundlePromise) {
+
+ mr.Module.logger_.info(`Loading bundle ${bundle} for module ${moduleId}`);
+ bundlePromise = mr.Module.doLoadBundle_(bundle);
+ mr.Module.bundlePromises_.set(bundle, bundlePromise);
+ }
+
+ // Add an error handler to reject the module load request.
+ bundlePromise.catch(e => {
+ resolver.reject(e);
+ });
+ }
+
+ /**
+ * Returns the bundle associated with a module.
+ * @param {mr.ModuleId} moduleId
+ * @return {?mr.Bundle}
+ * @private
+ */
+ static getBundle_(moduleId) {
+ return mr.Module.MODULE_TO_BUNDLE_MAPPING_.get(moduleId) || null;
+ }
+
+ /**
+ * Called when a bundle needs to be loaded.
+ * @param {mr.Bundle} bundle Name of bundle to load.
+ * @return {!Promise<void>} Resolves when the bundle is loaded or rejected if
+ * it failed to load.
+ * @private
+ */
+ static doLoadBundle_(bundle) {
+ let resolver = new mr.PromiseResolver();
+ resolver.promise.then(
+ () => {
+ mr.Module.logger_.info(`Bundle ${bundle} loaded`);
+ },
+ e => {
+ mr.Module.logger_.error(`Failed to load bundle ${bundle}`);
+ throw e;
+ });
+ let script = document.createElement('script');
+ script.src = chrome.extension.getURL(bundle);
+ script.setAttribute('type', 'text/javascript');
+ script.async = true;
+
+ script.onload = () => resolver.resolve(undefined);
+ script.onerror = () =>
+ resolver.reject(new Error(`Failed to load bundle ${bundle}`));
+ document.head.appendChild(script);
+ return resolver.promise;
+ }
+
+ /**
+ * Called by a module when it is done loading and initializing. Registers the
+ * module and resolves the outstanding promise returned by |load(moduleId)|.
+ * @param {mr.ModuleId} moduleId Identifier of the module. No two modules can
+ * have the same identifier.
+ * @param {!mr.Module} module The module that is ready.
+ */
+ static onModuleLoaded(moduleId, module) {
+ if (mr.Module.moduleById_.has(moduleId)) {
+ throw new Error('Duplicate module ' + moduleId);
+ }
+ mr.Module.moduleById_.set(moduleId, module);
+ const resolver = mr.Module.resolverByModuleId_.get(moduleId);
+ if (resolver) {
+ resolver.resolve(module);
+ }
+ }
+
+ /**
+ * Used for testing only.
+ */
+ static clearForTest() {
+ mr.Module.moduleById_.clear();
+ mr.Module.resolverByModuleId_.clear();
+ mr.Module.bundlePromises_.clear();
+ }
+
+ /**
+ * Subclasses should override this if a mr.EventListener designated this
+ * module to forward the events to.
+ * @param {*} event The event delivered to the handler. It is the handler's
+ * responsibility to verify that it can handle the event.
+ * @param {...*} args Arguments for the event.
+ */
+ handleEvent(event, ...args) {
+ throw new Error('Not implemented');
+ }
+};
+
+
+/**
+ * Maps a module ID to a bundle ID. Used for loading the bundle that contains
+ * a required module.
+ * @private @const {!Map<mr.ModuleId, mr.Bundle>}
+ */
+mr.Module.MODULE_TO_BUNDLE_MAPPING_ = new Map([
+ [mr.ModuleId.CAST_CHANNEL_SERVICE, mr.Bundle.MAIN],
+ [mr.ModuleId.CAST_SINK_DISCOVERY_SERVICE, mr.Bundle.MAIN],
+ [mr.ModuleId.CAST_STREAMING_SERVICE, mr.Bundle.MIRRORING_CAST_STREAMING],
+ [mr.ModuleId.SLARTI_SINK_DISCOVERY_SERVICE, mr.Bundle.MAIN],
+ [mr.ModuleId.WEAVE_SINK_DISCOVERY_SERVICE, mr.Bundle.MAIN],
+ [mr.ModuleId.HANGOUTS_SERVICE, mr.Bundle.MIRRORING_HANGOUTS],
+ [mr.ModuleId.MEETINGS_SERVICE, mr.Bundle.MIRRORING_HANGOUTS],
+ [mr.ModuleId.PROVIDER_MANAGER, mr.Bundle.MAIN],
+ [mr.ModuleId.WEBRTC_STREAMING_SERVICE, mr.Bundle.MIRRORING_WEBRTC]
+]);
+
+
+/**
+ * Set of bundles that are loaded by default.
+ * @private @const {!Set<mr.Bundle>}
+ */
+mr.Module.DEFAULT_LOAD_BUNDLES_ = new Set([mr.Bundle.MAIN]);
+
+
+/** @private {mr.Logger} */
+mr.Module.logger_ = mr.Logger.getInstance('mr.Module');
+
+/**
+ * The set of modules currently loaded and initialized in the extension, keyed
+ * by their IDs.
+ * @private {!Map<mr.ModuleId, !mr.Module>}
+ */
+mr.Module.moduleById_ = new Map();
+
+
+/**
+ * Holds the outstanding promise while a module is being loaded.
+ * @private {!Map<mr.ModuleId, !mr.PromiseResolver<!mr.Module>>}
+ */
+mr.Module.resolverByModuleId_ = new Map();
+
+
+/**
+ * Holds the outstanding promise while a bundle is being loaded.
+ * @private {!Map<mr.Bundle, !Promise<void>>}
+ */
+mr.Module.bundlePromises_ = new Map();
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/module_test.js b/chromium/chrome/browser/resources/media_router/extension/src/module_test.js
new file mode 100644
index 00000000000..1d2fed8c118
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/module_test.js
@@ -0,0 +1,90 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.setTestOnly('module_test');
+
+goog.require('mr.Module');
+goog.require('mr.PromiseUtils');
+
+
+
+describe('Tests modules', function() {
+ let mockModule;
+
+ beforeEach(function() {
+ mockModule = {'id': 1};
+ });
+
+ afterEach(function() {
+ mr.Module.clearForTest();
+ });
+
+ it('gets a module before and after it is loaded', function() {
+ let module = mr.Module.get('SomeModule');
+ expect(module).toBeNull();
+
+ const mockModule2 = {'id': 2};
+ mr.Module.onModuleLoaded('AnotherModule', mockModule2);
+
+ module = mr.Module.get('SomeModule');
+ expect(module).toBeNull();
+
+ mr.Module.onModuleLoaded('SomeModule', mockModule);
+ module = mr.Module.get('SomeModule');
+ expect(module).toBe(mockModule);
+
+ module = mr.Module.get('AnotherModule');
+ expect(module).toBe(mockModule2);
+ });
+
+ it('loads a module which loads a bundle', function(done) {
+ spyOn(mr.Module, 'getBundle_').and.returnValue('SomeBundle');
+ spyOn(mr.Module, 'doLoadBundle_').and.returnValue(Promise.resolve());
+
+ const promise = mr.Module.load('SomeModule');
+ const promise2 = mr.Module.load('SomeModule');
+
+ expect(mr.Module.getBundle_).toHaveBeenCalledWith('SomeModule');
+ expect(mr.Module.doLoadBundle_).toHaveBeenCalledWith('SomeBundle');
+ expect(mr.Module.doLoadBundle_.calls.count()).toBe(1);
+
+ mr.Module.onModuleLoaded('SomeModule', mockModule);
+
+ const promise3 = mr.Module.load('SomeModule');
+ Promise.all([promise, promise2, promise3]).then(modules => {
+ for (let module of modules) {
+ expect(module).toBe(mockModule);
+ }
+ done();
+ });
+ });
+
+ it('load rejects if failed to load a bundle', function(done) {
+ spyOn(mr.Module, 'getBundle_').and.returnValue('SomeBundle');
+ spyOn(mr.Module, 'doLoadBundle_')
+ .and.returnValue(Promise.reject(new Error('failed to load bundle')));
+
+ const promise = mr.Module.load('SomeModule');
+ const promise2 = mr.Module.load('SomeModule');
+
+ expect(mr.Module.getBundle_).toHaveBeenCalledWith('SomeModule');
+ expect(mr.Module.doLoadBundle_).toHaveBeenCalledWith('SomeBundle');
+ expect(mr.Module.doLoadBundle_.calls.count()).toBe(1);
+
+ const promise3 = mr.Module.load('SomeModule');
+ mr.PromiseUtils.allSettled([promise, promise2, promise3]).then(results => {
+ for (let result of results) {
+ expect(result.fulfilled).toBe(false);
+ }
+ done();
+ });
+ });
+
+ it('each module is mapped to a bundle', () => {
+ for (const key in mr.ModuleId) {
+ expect(mr.Module.getBundle_(mr.ModuleId[key]))
+ .not.toBeNull(`${mr.ModuleId[key]} does not map to a bundle`);
+ }
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/mojo_externs.js b/chromium/chrome/browser/resources/media_router/extension/src/mojo_externs.js
new file mode 100644
index 00000000000..93f4805ccde
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/mojo_externs.js
@@ -0,0 +1,522 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Closure definitions of Mojo service objects. Note that these
+ * definitions are bound only after chrome.mojoPrivate.requireAsync resolves
+ * in mojo.js.
+ */
+
+
+var mojo = {};
+
+
+
+// Core Mojo.
+
+/**
+ * @param {!Object} interfaceType
+ * @param {!Object} impl
+ * @param {mojo.InterfaceRequest=} request
+ * @constructor
+ */
+mojo.Binding = function(interfaceType, impl, request) {};
+
+
+/**
+ * Closes the message pipe. The bound object will no longer receive messages.
+ */
+mojo.Binding.prototype.close = function() {};
+
+
+/**
+ * Binds to the given request.
+ * @param {!mojo.InterfaceRequest} request
+ */
+mojo.Binding.prototype.bind = function(request) {};
+
+
+/** @param {function()} callback */
+mojo.Binding.prototype.setConnectionErrorHandler = function(callback) {};
+
+
+/**
+ * Creates an interface ptr and bind it to this instance.
+ * @return {?} The interface ptr.
+ */
+mojo.Binding.prototype.createInterfacePtrAndBind = function() {};
+
+
+
+/** @constructor */
+mojo.InterfaceRequest = function() {};
+
+
+/**
+ * Closes the message pipe. The object can no longer be bound to an
+ * implementation.
+ */
+mojo.InterfaceRequest.prototype.close = function() {};
+
+
+
+/** @constructor */
+mojo.InterfacePtrController = function() {};
+
+
+/**
+ * Closes the message pipe. Messages can no longer be sent with this object.
+ */
+mojo.InterfacePtrController.prototype.reset = function() {};
+
+
+/** @param {function()} callback */
+mojo.InterfacePtrController.prototype.setConnectionErrorHandler = function(
+ callback) {};
+
+
+
+/**
+ * @param {!Object} interfacePtr
+ */
+mojo.makeRequest = function(interfacePtr) {};
+
+
+// Mojom structs.
+
+/**
+ * @constructor
+ * @struct
+ */
+mojo.IPAddress = function() {};
+
+
+
+/** @type {!Array<number>} */
+mojo.IPAddress.prototype.address;
+
+
+/** @type {!Array<number>} */
+mojo.IPAddress.prototype.address_bytes;
+
+
+
+/**
+ * @constructor
+ * @struct
+ */
+mojo.IPEndPoint = function() {};
+
+
+/** @type {mojo.IPAddress} */
+mojo.IPEndPoint.prototype.address;
+
+
+/** @type {number} */
+mojo.IPEndPoint.prototype.port;
+
+
+
+/** @constructor */
+mojo.Url = function() {};
+
+
+/** @type {string} */
+mojo.Url.prototype.url;
+
+
+
+/**
+ * @constructor
+ * @struct
+ */
+mojo.DialMediaSink = function() {};
+
+
+/** @type {mojo.IPAddress} */
+mojo.DialMediaSink.prototype.ip_address;
+
+
+/** @type {string} */
+mojo.DialMediaSink.prototype.model_name;
+
+
+/** @type {mojo.Url} */
+mojo.DialMediaSink.prototype.app_url;
+
+
+
+/**
+ * @constructor
+ * @struct
+ */
+mojo.CastMediaSink = function() {};
+
+
+
+/** @type {mojo.IPAddress} */
+mojo.CastMediaSink.prototype.ip_address;
+
+
+/** @type {mojo.IPEndPoint} */
+mojo.CastMediaSink.prototype.ip_endpoint;
+
+
+/** @type {string} */
+mojo.CastMediaSink.prototype.model_name;
+
+
+/** @type {number} */
+mojo.CastMediaSink.prototype.capabilities;
+
+
+/** @type {number} */
+mojo.CastMediaSink.prototype.cast_channel_id;
+
+
+
+/**
+ * @constructor
+ * @struct
+ */
+mojo.SinkExtraData = function() {};
+
+
+/** @type {mojo.DialMediaSink} */
+mojo.SinkExtraData.prototype.dial_media_sink;
+
+
+/** @type {mojo.CastMediaSink} */
+mojo.SinkExtraData.prototype.cast_media_sink;
+
+
+
+/**
+ * @constructor
+ * @struct
+ */
+mojo.Sink = function() {};
+
+
+/** @constructor */
+mojo.SinkIconType = function() {};
+mojo.SinkIconType.CAST = 0;
+mojo.SinkIconType.CAST_AUDIO_GROUP = 1;
+mojo.SinkIconType.CAST_AUDIO = 2;
+mojo.SinkIconType.MEETING = 3;
+mojo.SinkIconType.HANGOUT = 4;
+mojo.SinkIconType.EDUCATION = 5;
+mojo.SinkIconType.GENERIC = 6;
+
+
+/** @type {string} */
+mojo.Sink.prototype.sink_id;
+
+
+/** @type {string} */
+mojo.Sink.prototype.name;
+
+
+/** @type {?string} */
+mojo.Sink.prototype.description;
+
+
+/** @type {?string} */
+mojo.Sink.prototype.domain;
+
+
+/** @type {?mojo.SinkIconType} */
+mojo.Sink.prototype.icon_type;
+
+
+/** @type {?mojo.SinkExtraData} */
+mojo.Sink.prototype.extra_data;
+
+
+
+/**
+ * @param {!Object} values An object mapping from property names to values.
+ * @constructor
+ * @struct
+ */
+mojo.TimeDelta = function(values) {};
+
+
+/** @type {number} */
+mojo.TimeDelta.prototype.microseconds;
+
+
+
+/**
+ * @param {!Object} values An object mapping from property names to values.
+ * @constructor
+ * @struct
+ */
+mojo.MediaStatus = function(values) {};
+
+
+/** @constructor */
+mojo.MediaStatus.PlayState = function() {};
+/** @type {!mojo.MediaStatus.PlayState} */
+mojo.MediaStatus.PlayState.PLAYING;
+/** @type {!mojo.MediaStatus.PlayState} */
+mojo.MediaStatus.PlayState.PAUSED;
+/** @type {!mojo.MediaStatus.PlayState} */
+mojo.MediaStatus.PlayState.BUFFERING;
+
+
+/** @type {?string} */
+mojo.MediaStatus.prototype.title;
+
+
+/** @type {?string} */
+mojo.MediaStatus.prototype.description;
+
+
+/** @type {boolean} */
+mojo.MediaStatus.prototype.can_play_pause;
+
+
+/** @type {boolean} */
+mojo.MediaStatus.prototype.can_mute;
+
+
+/** @type {boolean} */
+mojo.MediaStatus.prototype.can_set_volume;
+
+
+/** @type {boolean} */
+mojo.MediaStatus.prototype.can_seek;
+
+
+/** @type {?mojo.MediaStatus.PlayState} */
+mojo.MediaStatus.prototype.play_state;
+
+
+/** @type {boolean} */
+mojo.MediaStatus.prototype.is_muted;
+
+
+/** @type {number} */
+mojo.MediaStatus.prototype.volume;
+
+
+/** @type {?mojo.TimeDelta} */
+mojo.MediaStatus.prototype.duration;
+
+
+/** @type {?mojo.TimeDelta} */
+mojo.MediaStatus.prototype.current_time;
+
+
+/** @type {mojo.HangoutsMediaStatusExtraData} */
+mojo.MediaStatus.prototype.hangouts_extra_data;
+
+
+
+/**
+ * @param {!Object} values
+ * @constructor
+ * @struct
+ */
+mojo.HangoutsMediaStatusExtraData = function(values) {};
+
+
+/** @type {boolean} */
+mojo.HangoutsMediaStatusExtraData.prototype.local_present;
+
+
+
+/**
+ * @param {!Object} values An object mapping from property names to values.
+ * @constructor
+ * @struct
+ */
+mojo.Origin = function(values) {};
+
+
+/** @type {string} */
+mojo.Origin.prototype.scheme;
+
+
+/** @type {string} */
+mojo.Origin.prototype.host;
+
+
+/** @type {number} */
+mojo.Origin.prototype.port;
+
+
+/** @type {string} */
+mojo.Origin.prototype.suborigin;
+
+
+/** @type {boolean} */
+mojo.Origin.prototype.unique;
+
+
+
+/** @constructor */
+mojo.RouteControllerType = function() {};
+/** @type {mojo.RouteControllerType} */
+mojo.RouteControllerType.kNone;
+/** @type {mojo.RouteControllerType} */
+mojo.RouteControllerType.kGeneric;
+/** @type {mojo.RouteControllerType} */
+mojo.RouteControllerType.kHangouts;
+/** @type {mojo.RouteControllerType} */
+mojo.RouteControllerType.kMirroring;
+
+
+// Mojom interfaces.
+
+/**
+ * @constructor
+ * @struct
+ */
+mojo.MediaStatusObserverPtr = function() {};
+
+
+/** @param {!mojo.MediaStatus} status */
+mojo.MediaStatusObserverPtr.prototype.onMediaStatusUpdated = function(status) {
+};
+
+
+/** @type {!mojo.InterfacePtrController} */
+mojo.MediaStatusObserverPtr.prototype.ptr;
+
+
+
+/** @type {!Object} */
+mojo.MediaController;
+
+
+/** @type {!Object} */
+mojo.HangoutsMediaRouteController;
+
+
+/** @constructor */
+mojo.MediaRouteProviderConfig = function() {};
+
+
+/** @type {boolean} */
+mojo.MediaRouteProviderConfig.prototype.enable_dial_discovery;
+
+
+/** @type {boolean} */
+mojo.MediaRouteProviderConfig.prototype.enable_dial_sink_query;
+
+
+/** @type {boolean} */
+mojo.MediaRouteProviderConfig.prototype.enable_cast_discovery;
+
+
+
+/** @constructor */
+mojo.RemotingStopReason = function() {};
+/** @type {!mojo.RemotingStopReason} */
+mojo.RemotingStopReason.ROUTE_TERMINATED;
+/** @type {!mojo.RemotingStopReason} */
+mojo.RemotingStopReason.USER_DISABLED;
+
+
+
+/** @constructor */
+mojo.RemotingSinkFeature = function() {};
+
+
+
+/** @constructor */
+mojo.RemotingSinkAudioCapability = function() {};
+/** @type {!mojo.RemotingSinkAudioCapability} */
+mojo.RemotingSinkAudioCapability.CODEC_BASELINE_SET;
+/** @type {!mojo.RemotingSinkAudioCapability} */
+mojo.RemotingSinkAudioCapability.CODEC_AAC;
+/** @type {!mojo.RemotingSinkAudioCapability} */
+mojo.RemotingSinkAudioCapability.CODEC_OPUS;
+
+
+
+/** @constructor */
+mojo.RemotingSinkVideoCapability = function() {};
+/** @type {!mojo.RemotingSinkVideoCapability} */
+mojo.RemotingSinkVideoCapability.SUPPORT_4K;
+/** @type {!mojo.RemotingSinkVideoCapability} */
+mojo.RemotingSinkVideoCapability.CODEC_BASELINE_SET;
+/** @type {!mojo.RemotingSinkVideoCapability} */
+mojo.RemotingSinkVideoCapability.CODEC_H264;
+/** @type {!mojo.RemotingSinkVideoCapability} */
+mojo.RemotingSinkVideoCapability.CODEC_VP8;
+/** @type {!mojo.RemotingSinkVideoCapability} */
+mojo.RemotingSinkVideoCapability.CODEC_VP9;
+/** @type {!mojo.RemotingSinkVideoCapability} */
+mojo.RemotingSinkVideoCapability.CODEC_HEVC;
+
+
+
+/**
+ * @constructor
+ * @struct
+ */
+mojo.RemotingSinkMetadata = function() {};
+
+
+/** @type {!Array<mojo.RemotingSinkFeature>} */
+mojo.RemotingSinkMetadata.prototype.features;
+
+
+/** @type {!Array<mojo.RemotingSinkAudioCapability>} */
+mojo.RemotingSinkMetadata.prototype.audio_capabilities;
+
+
+/** @type {!Array<mojo.RemotingSinkVideoCapability>} */
+mojo.RemotingSinkMetadata.prototype.video_capabilities;
+
+
+/** @type {!string} */
+mojo.RemotingSinkMetadata.prototype.friendly_name;
+
+
+
+/** @type {!Object} */
+mojo.MirrorServiceRemoter;
+
+
+
+/**
+ * @constructor
+ */
+mojo.MirrorServiceRemoterPtr = function() {};
+
+
+/** @type {!mojo.InterfacePtrController} */
+mojo.MirrorServiceRemoterPtr.prototype.ptr;
+
+
+
+/**
+ * @constructor
+ */
+mojo.MirrorServiceRemotingSourcePtr = function() {};
+
+
+/** @param {!mojo.RemotingSinkMetadata} capabilities */
+mojo.MirrorServiceRemotingSourcePtr.prototype.onSinkAvailable = function(
+ capabilities) {};
+
+/** @param {!Uint8Array} message */
+mojo.MirrorServiceRemotingSourcePtr.prototype.onMessageFromSink = function(
+ message) {};
+
+
+/** @param {!mojo.RemotingStopReason} reason */
+mojo.MirrorServiceRemotingSourcePtr.prototype.onStopped = function(reason) {};
+
+
+/** Notifies the remoting source when streaming error occurs. */
+mojo.MirrorServiceRemotingSourcePtr.prototype.onError = function() {};
+
+
+/** @type {!mojo.InterfacePtrController} */
+mojo.MirrorServiceRemotingSourcePtr.prototype.ptr;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/persistent_data.js b/chromium/chrome/browser/resources/media_router/extension/src/persistent_data.js
new file mode 100644
index 00000000000..a7490f357e6
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/persistent_data.js
@@ -0,0 +1,463 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview API for classes to save data and cleanup before the event page
+ * is shut down.
+ *
+ * Temporary data is removed once the extension version is changed, and thus
+ * objects should never write anything to temporary data that needs to survive
+ * an extension update.
+ *
+ * Persistent data is retained across extension versions and browser restarts;
+ * it can be removed when the user clears local storage from their profile. Do
+ * not store any sensitive or per-route data persistently.
+ */
+
+goog.provide('mr.PersistentData');
+goog.provide('mr.PersistentDataManager');
+goog.require('mr.Logger');
+
+
+
+/**
+ * @interface
+ */
+mr.PersistentData = class {};
+
+
+/**
+ * Alias for localStorage to deal with broken Storage interface.
+ * @const @private {!Object}
+ */
+mr.PersistentData.storageObj_ = /** @type {!Object} */ (window.localStorage);
+
+
+/**
+ * Get the unique name of the object instance that has data to be saved.
+ * The name is used to isolate data from different objects.
+ * @return {string}
+ */
+mr.PersistentData.prototype.getStorageKey;
+
+
+/**
+ * Invoked by persistent data manager when an object registers itself with the
+ * manager. The object should load its saved data in this method.
+ */
+mr.PersistentData.prototype.loadSavedData;
+
+
+/**
+ * Implements this method to cleanup and return data that needs to be saved
+ * before the event page is shut down. The method should normally return a one-
+ * or two-element array of temporary and optional persistent data to save.
+ *
+ * Temporary data is cleared on browser restart or extension version change.
+ * Persistent data is retained until local storage is cleared by the browser
+ * profile.
+ *
+ * Any Objects must be serializable with JSON.stringify.
+ *
+ * The implementation of getData should not make any asynchronous
+ * calls; otherwise, there is no guarantee that asynchronous calls can finish.
+ *
+ * @return {!Array<!Object>} A one- or two-element array of data that needs to
+ * be saved. The first element is temporary data and the second element is
+ * persistent data. Return an empty array if there is no data to save. If
+ * only persistent data needs to be persisted, pass in undefined for the
+ * first element in the two-element array.
+ */
+mr.PersistentData.prototype.getData;
+
+
+/**
+ * The total number of characters that can be stored in localStorage,
+ * approximately.
+ * @const {number}
+ */
+mr.PersistentDataManager.QUOTA_CHARS = 5200000;
+
+
+/**
+ * The total number of characters used by persistent data. Note that writes that
+ * access localStorage directly may not be counted here.
+
+ * @private {number}
+ */
+mr.PersistentDataManager.charsUsed_ = 0;
+
+
+/**
+ * @param {!mr.PersistentData} obj The object that may have temporary data.
+ * @return {T} The data saved before. Null if no data is saved.
+ * @template T
+ */
+mr.PersistentDataManager.getTemporaryData = function(obj) {
+ const data = window.localStorage.getItem(
+ mr.PersistentDataManager.getStorageKey_(obj, false));
+ return data ? JSON.parse(data) : null;
+};
+
+
+/**
+ * @param {!mr.PersistentData} obj The object that may have peristent data.
+ * @return {T} The data saved before. Null if no data is saved.
+ * @template T
+ */
+mr.PersistentDataManager.getPersistentData = function(obj) {
+ const data = window.localStorage.getItem(
+ mr.PersistentDataManager.getStorageKey_(obj, true));
+ return data ? JSON.parse(data) : null;
+};
+
+
+/**
+ * Registers an object so that it gets informed about onSuspend events.
+ *
+ * @param {!mr.PersistentData} obj An object that has data to save.
+ */
+mr.PersistentDataManager.register = function(obj) {
+ if (mr.PersistentDataManager.dataInstances_.has(obj.getStorageKey())) {
+ throw Error('Duplicate instance name ' + obj.getStorageKey());
+ }
+ mr.PersistentDataManager.dataInstances_.set(obj.getStorageKey(), obj);
+ obj.loadSavedData();
+};
+
+
+/**
+ * Un-Registers an object from being informed of onSuspend/Resume events.
+ *
+ * @param {!mr.PersistentData} obj An object to remove from tracking.
+ */
+mr.PersistentDataManager.unregister = function(obj) {
+ mr.PersistentDataManager.dataInstances_.delete(obj.getStorageKey());
+};
+
+
+/**
+ * @param {string} mrInstanceId The media router instance ID, which stays the
+ * same till Chrome restarts.
+ */
+mr.PersistentDataManager.initialize = function(mrInstanceId) {
+ let otherChars = 0;
+ for (let key of Object.keys(mr.PersistentData.storageObj_)) {
+ const itemSize = key.length + window.localStorage.getItem(key).length;
+ if (key.startsWith(mr.PersistentDataManager.KEY_PREFIX_)) {
+ mr.PersistentDataManager.charsUsed_ += itemSize;
+ } else {
+ otherChars += itemSize;
+ }
+ }
+ mr.PersistentDataManager.mrInstanceId_ = mrInstanceId;
+ if (mr.PersistentDataManager.isVersionChanged_() ||
+ mr.PersistentDataManager.isChromeReloaded(mrInstanceId)) {
+ mr.PersistentDataManager.removeTemporary_();
+ }
+ mr.PersistentDataManager.logger_.info(
+ 'initialize: ' + mr.PersistentDataManager.charsUsed_ + ' chars used, ' +
+ otherChars + ' other chars');
+ chrome.runtime.onSuspend.addListener(mr.PersistentDataManager.onSuspend_);
+};
+
+
+/**
+ * @private {?string}
+ */
+mr.PersistentDataManager.mrInstanceId_ = null;
+
+
+/**
+ * @const {mr.Logger}
+ * @private
+ */
+mr.PersistentDataManager.logger_ =
+ mr.Logger.getInstance('mr.PersistentDataManager');
+
+
+/**
+ * A map from object's instance name to the object.
+ * @private @const {!Map<string, !mr.PersistentData>}
+ */
+mr.PersistentDataManager.dataInstances_ = new Map();
+
+
+/** @private @const {string} */
+mr.PersistentDataManager.KEY_PREFIX_ = 'mr.';
+
+
+/**
+ * @param {!mr.PersistentData} obj
+ * @param {boolean} persistent
+ * @return {string}
+ * @private
+ */
+mr.PersistentDataManager.getStorageKey_ = function(obj, persistent) {
+ return mr.PersistentDataManager.KEY_PREFIX_ +
+ (persistent ? 'persistent.' : 'temp.') + obj.getStorageKey();
+};
+
+
+/**
+ * Checks if the extension version has changed.
+ * @return {boolean} True if current extension has a different version as
+ * the saved version.
+ * @private
+ */
+mr.PersistentDataManager.isVersionChanged_ = function() {
+ return !!window.localStorage.getItem('version') &&
+ window.localStorage.getItem('version') !==
+ chrome.runtime.getManifest().version;
+};
+
+
+/**
+ * Checks if the chrome has been reloaded since the last time the extension is
+ * loaded.
+ * @param {string} mrInstanceId The media router instance ID, which stays the
+ * same till Chrome restarts.
+ * @return {boolean} True if Chrome was reloaded.
+ */
+mr.PersistentDataManager.isChromeReloaded = function(mrInstanceId) {
+ return !!window.localStorage.getItem('mrInstanceId') &&
+ window.localStorage.getItem('mrInstanceId') !== mrInstanceId;
+};
+
+
+/**
+ * Handles onSuspend event.
+ * @private
+ */
+mr.PersistentDataManager.onSuspend_ = function() {
+ mr.PersistentDataManager.logger_.info('onSuspend');
+
+ mr.PersistentDataManager.write(
+ 'version', chrome.runtime.getManifest().version);
+ if (mr.PersistentDataManager.mrInstanceId_) {
+ mr.PersistentDataManager.write(
+ 'mrInstanceId', mr.PersistentDataManager.mrInstanceId_);
+ }
+ const logManager = mr.PersistentDataManager.dataInstances_.get('LogManager');
+ for (const [key, obj] of mr.PersistentDataManager.dataInstances_) {
+ if (obj != logManager) {
+ mr.PersistentDataManager.saveData(
+ /** @type {!mr.PersistentData} */ (obj));
+ }
+ }
+ // Save the data for LogManager last, so that we save the logs generated
+ // during saveData() calls.
+ if (logManager) {
+ mr.PersistentDataManager.saveData(logManager);
+ }
+};
+
+
+/**
+ * Save PersistentData object to local storage.
+ * @param {!mr.PersistentData} obj
+ */
+mr.PersistentDataManager.saveData = function(obj) {
+ try {
+ const data = obj.getData();
+ if (data && data[0] != undefined) {
+ mr.PersistentDataManager.write(
+ mr.PersistentDataManager.getStorageKey_(obj, false),
+ JSON.stringify(data[0]));
+ }
+ if (data && data[1] != undefined) {
+ mr.PersistentDataManager.write(
+ mr.PersistentDataManager.getStorageKey_(obj, true),
+ JSON.stringify(data[1]));
+ }
+ } catch (e) {
+ mr.PersistentDataManager.logger_.error(
+ `Error while saving data for ${obj.getStorageKey()}: ${e.message}`);
+ }
+};
+
+
+/**
+ * Writes value to localStorage under key. If the value is too large to fit
+ * into the remaining localStorage quota, temporary data is first removed. If
+ * the value still won't fit, an exception is thrown.
+
+ * @param {string} key The localStorage key.
+ * @param {string} value The value to write.
+ */
+mr.PersistentDataManager.write = function(key, value) {
+ const dm = mr.PersistentDataManager;
+ let sizeDelta = 0;
+ const currentValue = window.localStorage.getItem(key);
+ if (currentValue != null) {
+ sizeDelta = value.length - currentValue.length;
+ } else {
+ sizeDelta = key.length + value.length;
+ }
+
+ if (dm.charsUsed_ + sizeDelta > dm.QUOTA_CHARS) {
+ mr.PersistentDataManager.logger_.warning(
+ 'Unable to write ' + sizeDelta + ' chars');
+ dm.removeTemporary_();
+ }
+
+ if (dm.charsUsed_ + sizeDelta > dm.QUOTA_CHARS) {
+ dm.logger_.error(
+ 'Unable to write ' + sizeDelta + ' chars after clearing temporary');
+ throw Error(
+ `Setting the value of '${key}' would exceed the quota, ` +
+ 'according to accounting.');
+ }
+
+ try {
+ window.localStorage.setItem(key, value);
+ } catch (error) {
+ throw Error(
+ `Setting the value of '${key}' would exceed the quota, ` +
+ 'according to the browser.');
+ }
+ // Adjusting dm.charsUsed_ only after the call to setItem() has succeeded.
+ dm.charsUsed_ += sizeDelta;
+};
+
+
+/**
+ * Writes a Blob to localStorage under the given key, making space-efficient use
+ * of localStorage by encoding two of the Blob's bytes into each DOMString
+ * character. Use readBlob() to read the value back.
+ * @param {string} key The localStorage key.
+ * @param {!Blob} value The value to write.
+ * @return {!Promise<void>} Resolves once the Blob has been written; or rejects
+ * if it won't fit.
+ */
+mr.PersistentDataManager.writeBlob = function(key, value) {
+ // The byte size needs to be a multiple of two, since each string character
+ // code is a 16-bit unsigned integer. Thus, append padding byte(s) to the end
+ // of the Blob. These will also be used to indicate whether the original Blob
+ // was of even or odd length when reading back later.
+ if (value.size % 2 == 0) {
+ value = new Blob([value, new Uint8Array([0, 0])]);
+ } else {
+ value = new Blob([value, new Uint8Array([1])]);
+ }
+
+ return new Promise((resolve, reject) => {
+ // Use FileReader to gain access to the Blob content via an ArrayBuffer.
+ const reader = new FileReader();
+ reader.onloadend = () => {
+ if (reader.error) {
+ reject(reader.error);
+ return;
+ }
+
+ try {
+ const buffer = /** @type {!ArrayBuffer} */ (reader.result);
+
+ // Convert the buffer bytes into a string, storing two bytes in each of
+ // the string's characters for space efficiency. This is done in batches
+ // to avoid smashing the call stack when calling String.fromCharCode().
+ const batchSize = 8192;
+ const pieces = [];
+ for (let pos = 0, end = buffer.byteLength; pos < end;
+ pos += batchSize) {
+ // Note: The byteLength will always be an even number since all input
+ // values to the following expression must be even numbers:
+ const byteLengthOfChunk = Math.min(end - pos, batchSize);
+ pieces.push(String.fromCharCode.apply(
+ null, new Uint16Array(buffer, pos, byteLengthOfChunk / 2)));
+ }
+
+ // Finally, join the pieces into a single string and attempt to store
+ // the string using the quota management heuristics in write().
+ mr.PersistentDataManager.write(key, pieces.join(''));
+
+ resolve();
+ } catch (error) {
+ reject(error);
+ }
+ };
+ reader.readAsArrayBuffer(value);
+ });
+};
+
+
+/**
+ * Reads a Blob from localStorage under the given key. Returns null if it does
+ * not exist.
+ * @param {string} key The localStorage key.
+ * @param {Object=} blobOptions The options for the reconstituted Blob (e.g.,
+ * {'type': 'application/gzip'}).
+ * @return {?Blob}
+ */
+mr.PersistentDataManager.readBlob = function(key, blobOptions) {
+ const asString = window.localStorage.getItem(key);
+ if (asString == null || asString.length < 1) {
+ return null;
+ }
+ const charCodes = new Uint16Array(asString.length);
+ for (let i = 0; i < asString.length; ++i) {
+ charCodes[i] = asString.charCodeAt(i);
+ }
+ // Determine, from the last byte value, whether the original Blob was of even
+ // or odd length (see writeBlob()). Create a Blob from a view of all but the
+ // padding byte(s) at the end of the buffer.
+ const buffer = charCodes.buffer;
+ const flagByte = (new Uint8Array(buffer, buffer.byteLength - 1, 1))[0];
+ const viewOfPayload =
+ new Uint8Array(buffer, 0, buffer.byteLength - ((flagByte == 0) ? 2 : 1));
+ return new Blob([viewOfPayload], blobOptions);
+};
+
+
+/**
+ * Removes temporary data.
+ * @private
+ */
+mr.PersistentDataManager.removeTemporary_ = function() {
+ for (let key of Object.keys(mr.PersistentData.storageObj_)) {
+ if (key.startsWith(mr.PersistentDataManager.KEY_PREFIX_ + 'temp.')) {
+ mr.PersistentDataManager.charsUsed_ -=
+ (key.length + window.localStorage.getItem(key).length);
+ delete window.localStorage[key];
+ }
+ }
+ mr.PersistentDataManager.logger_.info(
+ 'removeTemporary_: ' + mr.PersistentDataManager.charsUsed_ +
+ ' chars used');
+};
+
+
+/**
+ * Removes all data.
+ * @private
+ */
+mr.PersistentDataManager.removeAll_ = function() {
+ for (let key of Object.keys(mr.PersistentData.storageObj_)) {
+ if (key.startsWith(mr.PersistentDataManager.KEY_PREFIX_))
+ window.localStorage.removeItem(key);
+ }
+ mr.PersistentDataManager.charsUsed_ = 0;
+};
+
+
+/**
+ * Clears internal state and remove all saved data.
+ * Utility method for unit test.
+
+ */
+mr.PersistentDataManager.clear = function() {
+ mr.PersistentDataManager.removeAll_();
+ mr.PersistentDataManager.dataInstances_.clear();
+};
+
+
+/**
+ * Simulates suspend.
+ * Utility method for unit test.
+
+ */
+mr.PersistentDataManager.suspendForTest = function() {
+ mr.PersistentDataManager.onSuspend_();
+ mr.PersistentDataManager.dataInstances_.clear();
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/persistent_data_test.js b/chromium/chrome/browser/resources/media_router/extension/src/persistent_data_test.js
new file mode 100644
index 00000000000..6708e9fdb4f
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/persistent_data_test.js
@@ -0,0 +1,361 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.setTestOnly('persistent_data_test');
+
+goog.require('mr.PersistentData');
+goog.require('mr.PersistentDataManager');
+goog.require('mr.UnitTestUtils');
+
+
+/**
+ * @implements {mr.PersistentData}
+ * @private
+ */
+DummyData_ = class {
+ /**
+ * @param {string} id
+ */
+ constructor(id) {
+ /** @private {string} */
+ this.id_ = id;
+ }
+
+ /**
+ * @override
+ */
+ getData() {
+ return [];
+ }
+
+ /**
+ * @override
+ */
+ getStorageKey() {
+ return 'dummy-data' + this.id_;
+ }
+
+ /**
+ * @override
+ */
+ loadSavedData() {}
+};
+
+
+describe('Tests PersistentDataManager', () => {
+ let dataInstance1;
+ let dataInstance2;
+ let onSuspendListener;
+ let version;
+ let mrInstanceId;
+ const originalQuota = mr.PersistentDataManager.QUOTA_CHARS;
+
+ beforeEach(() => {
+ window.localStorage.clear();
+ mr.PersistentDataManager.charsUsed_ = 0;
+ dataInstance1 = new DummyData_('1');
+ dataInstance2 = new DummyData_('2');
+ version = '1.0';
+ mrInstanceId = '123';
+ mr.UnitTestUtils.mockChromeApi();
+ chrome.runtime.onSuspend = {
+ addListener: l => {
+ onSuspendListener = l;
+ }
+ };
+ chrome.runtime.getManifest = () => ({'version': version});
+ mr.PersistentDataManager.initialize(mrInstanceId);
+ dataInstance1.loadSavedData = jasmine.createSpy('loadSavedData');
+ dataInstance2.loadSavedData = jasmine.createSpy('loadSavedData');
+ mr.PersistentDataManager.register(dataInstance1);
+ mr.PersistentDataManager.register(dataInstance2);
+ expect(dataInstance1.loadSavedData).toHaveBeenCalled();
+ expect(dataInstance2.loadSavedData).toHaveBeenCalled();
+ });
+
+ afterEach(() => {
+ mr.PersistentDataManager.clear();
+ mr.PersistentDataManager.QUOTA_CHARS = originalQuota;
+ mr.UnitTestUtils.restoreChromeApi();
+ });
+
+ it('returns null with no saved data', () => {
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance1)).toBe(null);
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance2)).toBe(null);
+ });
+
+ describe('handles onSuspend', () => {
+ it('with one instance with data', () => {
+ dataInstance1.getData = () => [{'d': 1}, {'e': 1}];
+ dataInstance2.getData = () => null;
+ onSuspendListener();
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance1)).toEqual({
+ 'd': 1
+ });
+ expect(mr.PersistentDataManager.getPersistentData(dataInstance1))
+ .toEqual({'e': 1});
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance2))
+ .toBe(null);
+ expect(mr.PersistentDataManager.charsUsed_).toBe(83);
+ });
+
+ it('with two instances with data', () => {
+ dataInstance1.getData = () => [{'d': 1}, {'e': 1}];
+ dataInstance2.getData = () => [{'d': 2}, {'e': 2}];
+ onSuspendListener();
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance1)).toEqual({
+ 'd': 1
+ });
+ expect(mr.PersistentDataManager.getPersistentData(dataInstance1))
+ .toEqual({'e': 1});
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance2)).toEqual({
+ 'd': 2
+ });
+ expect(mr.PersistentDataManager.getPersistentData(dataInstance2))
+ .toEqual({'e': 2});
+ expect(mr.PersistentDataManager.charsUsed_).toBe(141);
+ });
+
+ it('with no instance with data', () => {
+ dataInstance1.getData = () => null;
+ dataInstance2.getData = () => null;
+ onSuspendListener();
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance1))
+ .toBe(null);
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance2))
+ .toBe(null);
+ expect(mr.PersistentDataManager.charsUsed_).toBe(25);
+ });
+
+ it('with an instance with a circular dependency', () => {
+ const data1 = {};
+ data1.data = data1;
+ dataInstance1.getData = () => [{'d': data1}, {'e': data1}];
+ dataInstance2.getData = () => [{'d': 2}, {'e': 2}];
+ onSuspendListener();
+
+ // If there is a circular dependency of objects, that data cannot be
+ // converted into JSON or saved. However other data should still be saved.
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance2)).toEqual({
+ 'd': 2
+ });
+ expect(mr.PersistentDataManager.getPersistentData(dataInstance2))
+ .toEqual({'e': 2});
+ });
+ });
+
+ it('Test saveData', () => {
+ dataInstance1.getData = () => [{'d': 1}, {'e': 1}];
+ dataInstance2.getData = () => [{'d': 2}, {'e': 2}];
+ const dataInstance3 = new DummyData_('3');
+ dataInstance3.getData = () => [false, {'e': 3}];
+ const dataInstance4 = new DummyData_('4');
+ dataInstance4.getData = () => [undefined, 0];
+
+ // Note that dataInstance2 is not saved.
+ mr.PersistentDataManager.saveData(dataInstance1);
+ mr.PersistentDataManager.saveData(dataInstance3);
+ mr.PersistentDataManager.saveData(dataInstance4);
+
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance1)).toEqual({
+ 'd': 1
+ });
+ expect(mr.PersistentDataManager.getPersistentData(dataInstance1)).toEqual({
+ 'e': 1
+ });
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance2)).toBeNull();
+ expect(mr.PersistentDataManager.getPersistentData(dataInstance2))
+ .toBeNull();
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance3))
+ .toEqual(false);
+ expect(mr.PersistentDataManager.getPersistentData(dataInstance3)).toEqual({
+ 'e': 3
+ });
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance4)).toBeNull();
+ expect(mr.PersistentDataManager.getPersistentData(dataInstance4))
+ .toEqual(0);
+ });
+
+ it('Test unregister', () => {
+ dataInstance1.getData = () => [{'d': 1}, {'e': 1}];
+ dataInstance2.getData = () => [{'d': 2}, {'e': 2}];
+ const dataInstance3 = new DummyData_('3');
+ dataInstance3.getData = () => [{'d': 3}, {'e': 3}];
+ mr.PersistentDataManager.register(dataInstance3);
+ onSuspendListener();
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance3)).toEqual({
+ 'd': 3
+ });
+ mr.PersistentDataManager.unregister(dataInstance3);
+ // Get data should not be called again.
+ dataInstance3.getData = () => {
+ fail();
+ };
+ onSuspendListener();
+ });
+
+ it('handles version change', () => {
+ dataInstance1.getData = () => [{'d': 1}, {'e': 1}];
+ onSuspendListener();
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance1)).toEqual({
+ 'd': 1
+ });
+ expect(mr.PersistentDataManager.getPersistentData(dataInstance1)).toEqual({
+ 'e': 1
+ });
+ version = '1.1';
+ expect(mr.PersistentDataManager.isChromeReloaded(mrInstanceId)).toBe(false);
+ mr.PersistentDataManager.initialize(mrInstanceId);
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance1)).toBe(null);
+ expect(mr.PersistentDataManager.getPersistentData(dataInstance1)).toEqual({
+ 'e': 1
+ });
+ });
+
+ it('handles mrInstanceId change', () => {
+ dataInstance1.getData = () => [{'d': 1}, {'e': 1}];
+ onSuspendListener();
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance1)).toEqual({
+ 'd': 1
+ });
+ expect(mr.PersistentDataManager.getPersistentData(dataInstance1)).toEqual({
+ 'e': 1
+ });
+ mrInstanceId = '321';
+ expect(mr.PersistentDataManager.isChromeReloaded(mrInstanceId)).toBe(true);
+ mr.PersistentDataManager.initialize(mrInstanceId);
+ expect(mr.PersistentDataManager.getTemporaryData(dataInstance1)).toBe(null);
+ expect(mr.PersistentDataManager.getPersistentData(dataInstance1)).toEqual({
+ 'e': 1
+ });
+ });
+
+ describe('allows writes', () => {
+ it('of small values', () => {
+ mr.PersistentDataManager.write('mr.temp.Buckaroo', 'Bonzai');
+ expect(window.localStorage.getItem('mr.temp.Buckaroo')).toBe('Bonzai');
+ expect(mr.PersistentDataManager.charsUsed_).toBe(22);
+ });
+
+ it('of large values over quota', () => {
+ mr.PersistentDataManager.QUOTA_CHARS = 100;
+ [1, 2, 3, 4].forEach(index => {
+ mr.PersistentDataManager.write(
+ 'mr.temp.' + index, 'Only the dead have seen the end of war.');
+ });
+ expect(mr.PersistentDataManager.charsUsed_).toBe(96);
+ // Normally this would go over QUOTA_CHARS, but clearing temporary
+ // values allows it to succeed.
+ mr.PersistentDataManager.write(
+ 'mr.persistent.5', 'Courage is knowing what not to fear.');
+ expect(mr.PersistentDataManager.charsUsed_).toBe(51);
+ [1, 2, 3, 4].forEach(index => {
+ expect(window.localStorage.getItem('mr.temp.' + index)).toBeNull();
+ });
+ expect(window.localStorage.getItem('mr.persistent.5'))
+ .toBe('Courage is knowing what not to fear.');
+ });
+ });
+
+ describe('stores and reads Blobs', () => {
+ const dm = mr.PersistentDataManager;
+ const testKey = 'test';
+
+ const allUint16Values = [];
+ for (let i = 0; i < (1 << 16); ++i) {
+ allUint16Values.push(i);
+ }
+
+ // Takes an array of byte values, creates a Blob, stores and retrieves it
+ // back, and then maps the bytes of the Blob back to an array of byte
+ // values.
+ function writeThenReadByteValues(values) {
+ return new Promise((resolve, reject) => {
+ dm.writeBlob(testKey, new Blob([new Uint8Array(values)])).then(() => {
+ const reader = new FileReader();
+ reader.onloadend = () => {
+ if (reader.error) {
+ reject(reader.error);
+ } else {
+ resolve(Array.from(new Uint8Array(reader.result)));
+ }
+ };
+ reader.readAsArrayBuffer(dm.readBlob(testKey));
+ }, reject);
+ });
+ }
+
+ // Same as writeThenReadByteValues(), except operate on an array of uint16.
+ function writeThenReadShortValues(values) {
+ return new Promise((resolve, reject) => {
+ dm.writeBlob(testKey, new Blob([new Uint16Array(values)])).then(() => {
+ const reader = new FileReader();
+ reader.onloadend = () => {
+ if (reader.error) {
+ reject(reader.error);
+ } else {
+ resolve(Array.from(new Uint16Array(reader.result)));
+ }
+ };
+ reader.readAsArrayBuffer(dm.readBlob(testKey));
+ }, reject);
+ });
+ }
+
+ it('of zero size', done => {
+ writeThenReadByteValues([]).then(values => {
+ expect(values).toEqual([]);
+ done();
+ }, fail);
+ });
+
+ it('of even size', done => {
+ writeThenReadByteValues([42, 1]).then(values => {
+ expect(values).toEqual([42, 1]);
+ done();
+ }, fail);
+ });
+
+ it('of odd size', done => {
+ writeThenReadByteValues([42, 1, 0]).then(values => {
+ expect(values).toEqual([42, 1, 0]);
+ done();
+ }, fail);
+ });
+
+ it('containing all possible uint16 values', done => {
+ const original = allUint16Values.concat(allUint16Values.reverse());
+ writeThenReadShortValues(original).then(values => {
+ expect(values).toEqual(original);
+ done();
+ }, fail);
+ });
+
+ it('that are just within quota', done => {
+ // Note on quota space needed: The string length of the key plus value
+ // must be available. A blob of 7 uint16 values will have a byte size of
+ // 14. The impl will add two padding bytes to the end, making the total 16
+ // bytes. Thus, 16/2 = 8 chars of quota must be available for the value.
+ // In total, 4 + 8 (key + value) chars must be available.
+ mr.PersistentDataManager.QUOTA_CHARS =
+ mr.PersistentDataManager.charsUsed_ + testKey.length + 8;
+ const original = allUint16Values.slice(0, 7);
+ writeThenReadShortValues(original).then(values => {
+ expect(values).toEqual(original);
+ done();
+ }, fail);
+ });
+
+ it('that exceed quota', done => {
+ // See note above on how quota accounting works.
+ mr.PersistentDataManager.QUOTA_CHARS =
+ mr.PersistentDataManager.charsUsed_ + testKey.length + 8;
+ const original = allUint16Values.slice(0, 8);
+ writeThenReadShortValues(original).then(fail, error => {
+ expect(dm.readBlob(testKey)).toBeNull();
+ done();
+ });
+ });
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/presentation.js b/chromium/chrome/browser/resources/media_router/extension/src/presentation.js
new file mode 100644
index 00000000000..07ed021276b
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/presentation.js
@@ -0,0 +1,191 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Presentation API.
+ * @externs
+ * @see http://w3c.github.io/presentation-api
+ * @see https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/modules/presentation/
+ */
+
+
+
+/**
+ * @interface
+ * @see http://w3c.github.io/presentation-api/#idl-def-presentationconnection
+ */
+function PresentationConnection() {}
+
+
+/**
+ * @type {string}
+ * @see http://w3c.github.io/presentation-api/#dom-presentationconnection-id
+ */
+PresentationConnection.prototype.id;
+
+
+/**
+ * @type {string}
+ * @see http://w3c.github.io/presentation-api/#dom-presentationconnection-url
+ */
+PresentationConnection.prototype.url;
+
+
+/**
+ * @type {string}
+ * @see http://w3c.github.io/presentation-api/#dom-presentationconnection-state
+ */
+PresentationConnection.prototype.state;
+
+
+/**
+ * @type {?function(!MessageEvent)}
+ * @see http://w3c.github.io/presentation-api/#dom-presentationconnection-onmessage
+ */
+PresentationConnection.prototype.onmessage;
+
+
+/**
+ * @type {?function(!PresentationConnectionCloseEvent)}
+ * @see http://w3c.github.io/presentation-api/#dom-presentationconnection-onclose
+ */
+PresentationConnection.prototype.onclose;
+
+
+/**
+ * @type {?function()}
+ * @see https://www.w3.org/TR/presentation-api/#dom-presentationconnection-onterminate
+ */
+PresentationConnection.prototype.onterminate;
+
+
+/**
+ * @param {string} message
+ * @see http://w3c.github.io/presentation-api/#dom-presentationconnection-send
+ */
+PresentationConnection.prototype.send = function(message) {};
+
+/**
+ * @see https://w3c.github.io/presentation-api/#dom-presentationconnection-terminate
+ */
+PresentationConnection.prototype.terminate = function() {};
+
+/**
+ * @see http://w3c.github.io/presentation-api/#dom-presentationconnection-close
+ */
+PresentationConnection.prototype.close = function() {};
+
+
+
+/**
+ * @constructor
+ * @extends {Event}
+ */
+function PresentationConnectionCloseEvent() {}
+
+
+/** @type {string} */
+PresentationConnectionCloseEvent.prototype.reason;
+
+
+/** @type {string} */
+PresentationConnectionCloseEvent.prototype.message;
+
+
+
+/**
+ * @constructor
+ * @extends {Event}
+ * @see http://w3c.github.io/presentation-api/#presentationavailability
+ */
+function PresentationAvailability() {}
+
+
+/** @type {boolean} */
+PresentationAvailability.prototype.value;
+
+
+/** @type {?function()} */
+PresentationAvailability.prototype.onchange;
+
+
+
+/**
+ * @constructor
+ * @extends {Event}
+ * @see http://w3c.github.io/presentation-api/#availablechangeevent
+ */
+function AvailableChangeEvent() {}
+
+
+/**
+ * @type {boolean}
+ */
+AvailableChangeEvent.prototype.available;
+
+
+
+/**
+ * @constructor
+ * @extends {Event}
+ */
+function PresentationConnectionAvailableEvent() {}
+
+
+/**
+ * @type {!PresentationConnection}
+ */
+PresentationConnectionAvailableEvent.prototype.connection;
+
+
+
+/**
+ * @param {string|!Array<string>} url
+ * @constructor
+ */
+function PresentationRequest(url) {}
+
+
+/**
+ * @return {!Promise<!PresentationConnection>}
+ */
+PresentationRequest.prototype.start = function() {};
+
+
+/**
+ * @param {string} presentationId
+ * @return {!Promise<!PresentationConnection>}
+ */
+PresentationRequest.prototype.reconnect = function(presentationId) {};
+
+
+/**
+ * @return {!Promise<!PresentationAvailability>}
+ */
+PresentationRequest.prototype.getAvailability = function() {};
+
+
+/**
+ * @type {?function(!PresentationConnectionAvailableEvent)}
+ */
+PresentationRequest.prototype.onconnectionavailable;
+
+
+
+/**
+ * @interface
+ */
+function Presentation() {}
+
+
+/**
+ * @type {PresentationRequest}
+ */
+Presentation.prototype.defaultRequest;
+
+
+/**
+ * @type {!Presentation}
+ */
+Navigator.prototype.presentation;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/presentation_services/cloud_webrtc/webrtc_presentation_session.js b/chromium/chrome/browser/resources/media_router/extension/src/presentation_services/cloud_webrtc/webrtc_presentation_session.js
new file mode 100644
index 00000000000..907ac9b5aab
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/presentation_services/cloud_webrtc/webrtc_presentation_session.js
@@ -0,0 +1,192 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Implementation of PresentationSession that uses the WebRTC
+ * PeerConnection.
+ */
+goog.provide('mr.presentation.webrtc.CloudWebRtcSession');
+
+goog.require('mr.MessagePortService');
+goog.require('mr.PromiseResolver');
+goog.require('mr.presentation.Session');
+goog.require('mr.webrtc.Message');
+goog.require('mr.webrtc.MessageType');
+goog.require('mr.webrtc.OfferMessageData');
+goog.require('mr.webrtc.PeerConnection');
+
+goog.scope(function() {
+
+
+
+
+/**
+ * Constructs a new WebRTC presentation session.
+ * @implements {mr.presentation.Session}
+ */
+mr.presentation.webrtc.CloudWebRtcSession = class {
+ /**
+ * @param {!mr.Route} route
+ * @param {!string} sourceUrn
+ */
+ constructor(route, sourceUrn) {
+ /** @type {!mr.Route} */
+ this.route = route;
+
+ /** @type {!string} */
+ this.sourceUrn = sourceUrn;
+
+ /** @private {!mr.PromiseResolver<!mr.webrtc.PeerConnection>} */
+ this.peerConnectionResolver_ = new mr.PromiseResolver();
+
+ /** @private {!Promise<!mr.webrtc.PeerConnection>} */
+ this.peerConnection_ = this.peerConnectionResolver_.promise;
+
+ /** @private {!mr.MessagePort} */
+ this.messagePort_ =
+ mr.MessagePortService.getService().getInternalMessenger(route.id);
+
+ /** @private {!mr.PromiseResolver<!mr.Route>} */
+ this.startResolver_ = new mr.PromiseResolver();
+
+ /** @private {boolean} */
+ this.started_ = false;
+
+ this.setUpMessagePort_();
+ this.setUpPeerConnection_();
+
+ // Send a request for TURN credentials, then expect a response message with
+ // type TURN_CREDENTIALS.
+ this.sendMessageToMrp_(
+ new mr.webrtc.Message(mr.webrtc.MessageType.GET_TURN_CREDENTIALS));
+ }
+
+ /**
+ * @override
+ */
+ start() {
+ return this.peerConnection_.then(pc => {
+ if (pc.isStarted()) {
+ return Promise.reject(Error('Presentation already started'));
+ }
+
+ pc.start();
+ return this.startResolver_.promise;
+ });
+ }
+
+ /**
+ * @override
+ */
+ stop() {
+ this.started_ = false;
+ return this.peerConnection_.then(pc => {
+ pc.stop();
+ this.peerConnection_ =
+ Promise.reject(Error('Peer connection has already been stopped'));
+ });
+ }
+
+ /**
+ * Sets up the message port to receive incoming messages.
+ * @private
+ */
+ setUpMessagePort_() {
+ this.messagePort_.onMessage = message => {
+ if (!message.type) {
+ // Wrap message and send it along as a presentation message.
+ this.peerConnection_.then(pc => {
+ pc.sendDataChannelMessage({
+ type: mr.webrtc.MessageType.PRESENTATION_CONNECTION_MESSAGE,
+ data: message
+ });
+ });
+ return;
+ }
+ switch (message.type) {
+ case mr.webrtc.MessageType.TURN_CREDENTIALS:
+ // Response to a GET_TURN_CREDENTIALS message. This causes the
+ // PeerConnection to be created.
+ this.peerConnectionResolver_.resolve(new mr.webrtc.PeerConnection(
+ this.route.id,
+ /** @type {!Array<!mr.webrtc.TurnCredential>} */
+ (message.data['credentials'])));
+ break;
+ case mr.webrtc.MessageType.ANSWER:
+ this.peerConnection_.then(pc => {
+ pc.setRemoteDescription(message.data);
+ });
+ break;
+ case mr.webrtc.MessageType.STOP:
+ this.startResolver_.reject('Stop signal received');
+ this.stop();
+ break;
+ default:
+ throw Error('Unknown message type: ' + message.type);
+ }
+ };
+ }
+
+ /**
+ * Sets up the peer connection (with callbacks).
+ * @private
+ */
+ setUpPeerConnection_() {
+ this.peerConnection_.then(pc => {
+ // Pass the description up the MessagePort.
+ pc.setOnOfferDescriptionReady(description => {
+ const offerData = new mr.webrtc.OfferMessageData(
+ description,
+ /* opt_settings_ */ null,
+ /* opt_mediaConstraints */ null, this.sourceUrn, this.route.id);
+ const message =
+ new mr.webrtc.Message(mr.webrtc.MessageType.OFFER, offerData);
+ this.sendMessageToMrp_(message);
+ });
+ // Pass along the data channel message up the MessagePort.
+ pc.setOnDataChannelMessage(message => {
+ // Check if message is a STOP message and calls stop() before sending it
+ // through the message port to MRP.
+ const webRtcMessage = mr.webrtc.Message.fromString(message);
+ if (webRtcMessage.type == mr.webrtc.MessageType.STOP) {
+ this.stop();
+ }
+ this.sendMessageToMrp_(webRtcMessage);
+ });
+
+ // Send the connection success/closed/failed events up the MessagePort,
+ // and
+ // also resolve or reject the start() promise.
+ pc.setOnConnectionSuccess(event => {
+ this.started_ = true;
+ this.sendMessageToMrp_(
+ new mr.webrtc.Message(mr.webrtc.MessageType.SESSION_START_SUCCESS));
+ this.startResolver_.resolve(this.route);
+ });
+ pc.setOnConnectionClosed(event => {
+ this.sendMessageToMrp_(
+ new mr.webrtc.Message(mr.webrtc.MessageType.SESSION_END));
+ });
+ pc.setOnConnectionFailure(error => {
+ // If we haven't started yet, reject the start promise.
+ if (!this.started_) {
+ this.startResolver_.reject(error);
+ }
+ this.sendMessageToMrp_(
+ new mr.webrtc.Message(mr.webrtc.MessageType.SESSION_FAILURE));
+ });
+ });
+ }
+
+ /**
+ * Sends the provided message to the MRP via the message port.
+ * @param {!Object} message
+ * @private
+ */
+ sendMessageToMrp_(message) {
+ this.messagePort_.sendMessage(message);
+ }
+};
+
+}); // goog.scope
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/presentation_services/presentation_session.js b/chromium/chrome/browser/resources/media_router/extension/src/presentation_services/presentation_session.js
new file mode 100644
index 00000000000..02b2ad5253b
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/presentation_services/presentation_session.js
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Interface to a presentation session.
+ */
+
+goog.provide('mr.presentation.Session');
+
+
+
+/**
+ * Creates a new PresentationSession.
+ * @record
+ */
+mr.presentation.Session = class {
+ /**
+ * Starts the presentation session.
+ * @return {!Promise<!mr.Route>} Fulfilled when the
+ * session has been created and transports have started.
+ */
+ start() {}
+ /**
+ * Stops the presentation session. The underlying streams and transports are
+ * stopped and destroyed.
+ *
+ * @return {!Promise} Promise that resolves when the session is stopped.
+ */
+ stop() {}
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/common/id_generator.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/common/id_generator.js
new file mode 100644
index 00000000000..91096c736f8
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/common/id_generator.js
@@ -0,0 +1,80 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview An ID generator that keeps its state across extension
+ * suspend/wakeup cycles.
+ *
+
+ */
+
+goog.provide('mr.IdGenerator');
+
+goog.require('mr.PersistentData');
+goog.require('mr.PersistentDataManager');
+
+
+/**
+ * @implements {mr.PersistentData}
+ */
+mr.IdGenerator = class {
+ /**
+ * @param {!string} storageKey
+ */
+ constructor(storageKey) {
+ /** @private @const {!string} */
+ this.storageKey_ = storageKey;
+
+ /** @private {!number} */
+ this.nextId_ = Math.floor(Math.random() * 1e6) * 1000;
+ }
+
+ /**
+ * Enables persistent
+ */
+ enablePersistent() {
+ mr.PersistentDataManager.register(this);
+ }
+
+ /**
+ * @return {!number} The next ID. 0 will never be returned.
+ */
+ getNext() {
+ let nextId = this.nextId_++;
+ if (nextId == 0) {
+ // Skip 0, which is used by Cast receiver to
+ // indicate that the broadcast status message is not coming from a
+ // specific
+ // sender (it is an autonomous status change, not triggered by a command
+ // from any sender). Strange usage of 0 though; could be a null / optional
+ // field.
+ nextId = this.nextId_++;
+ }
+ return nextId;
+ }
+
+ /**
+ * @override
+ */
+ getStorageKey() {
+ return 'IdGenerator.' + this.storageKey_;
+ }
+
+ /**
+ * @override
+ */
+ getData() {
+ return [this.nextId_];
+ }
+
+ /**
+ * @override
+ */
+ loadSavedData() {
+ const savedData = mr.PersistentDataManager.getTemporaryData(this);
+ if (savedData) {
+ this.nextId_ = savedData;
+ }
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/common/id_generator_test.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/common/id_generator_test.js
new file mode 100644
index 00000000000..1e9e37a220d
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/common/id_generator_test.js
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.require('mr.IdGenerator');
+goog.require('mr.PersistentDataManager');
+
+describe('IdGenerator Tests', function() {
+ let generator;
+
+ beforeEach(function() {
+ spyOn(Math, 'random').and.returnValue(0);
+ generator = new mr.IdGenerator('test');
+ generator.enablePersistent();
+ });
+
+ afterEach(function() {
+ mr.PersistentDataManager.clear();
+ });
+
+ it('getNext', function() {
+ expect(generator.getNext()).toBe(1);
+ expect(generator.getNext()).toBe(2);
+ expect(generator.getNext()).toBe(3);
+ });
+
+ it('Persistent', function() {
+ expect(generator.getNext()).toBe(1);
+ expect(generator.getNext()).toBe(2);
+ mr.PersistentDataManager.suspendForTest();
+ generator = new mr.IdGenerator('test');
+ generator.loadSavedData();
+ expect(generator.getNext()).toBe(3);
+ });
+
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/common/net_utils.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/common/net_utils.js
new file mode 100644
index 00000000000..fe52d1d5ce2
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/common/net_utils.js
@@ -0,0 +1,98 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Utilites for handling network data.
+
+ */
+
+goog.module('mr.NetUtils');
+goog.module.declareLegacyNamespace();
+
+
+
+/** @const @private {!RegExp} */
+exports.IPV4_REGEXP_ =
+ new RegExp('^([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$');
+
+/** @const @private {!Array<number>} */
+exports.IPV4_PRIVATE_ADDRESS_MASKS_ = [
+ // 10.0.0.0/8
+ 4278190080,
+ // 172.16.0.0/12
+ 4293918720,
+ // 192.168.0.0/16
+ 4294901760
+];
+
+/** @const @private {!Array<number>} */
+exports.IPV4_PRIVATE_ADDRESS_SUBNETS_ = [
+ // 10.0.0.0/8
+ 167772160,
+ // 172.16.0.0/12
+ 2886729728,
+ // 192.168.0.0/16
+ 3232235520
+];
+
+/**
+ * Parses an IPv4 dotted-quad address into an array of four numbers, or
+ * returns null if the argument cannot be parsed.
+ *
+ * @param {string} ipAddress
+ * @return {?Array<number>} Array of four integers 0-255 or null.
+ */
+exports.parseIPv4Address = function(ipAddress) {
+ const matches = ipAddress.match(exports.IPV4_REGEXP_);
+ if (!matches || matches.length != 5) return null;
+ const result = [];
+ for (let i = 0; i < 4; i++) {
+ result[i] = Number.parseInt(matches[i + 1], 10);
+ if (result[i] < 0 || result[i] > 255) return null;
+ }
+ return result;
+};
+
+/**
+ * Returns true if ipAddress can be parsed into a valid IPv4 private network
+ * address.
+ *
+ * @param {string} ipAddress
+ * @return {boolean} True if ipAddress is a valid IPv4 private network address.
+ */
+exports.isPrivateIPv4Address = function(ipAddress) {
+ const parsedAddress = exports.parseIPv4Address(ipAddress);
+ if (!parsedAddress) return false;
+ // >>> 0 converts a signed integer to unsigned, so bitwise operations are
+ // sensible.
+ const addressValue = (parsedAddress[0] << 24 | parsedAddress[1] << 16 |
+ parsedAddress[2] << 8 | parsedAddress[0]) >>>
+ 0;
+ for (let i = 0; i < 3; i++) {
+ if ((addressValue & exports.IPV4_PRIVATE_ADDRESS_MASKS_[i]) >>> 0 ==
+ exports.IPV4_PRIVATE_ADDRESS_SUBNETS_[i])
+ return true;
+ }
+ return false;
+};
+
+/**
+ * @param {string} url A url.
+ * @return {!HTMLAnchorElement} The result of parsing url.
+ */
+exports.parseUrl = function(url) {
+ const a = document.createElement('a');
+ a.href = url;
+ return /** @type {!HTMLAnchorElement} */ (a);
+};
+
+/**
+ * Non-exhaustive list of HTTP status codes. Add new codes as needed here.
+ * @enum {number}
+ */
+const HttpStatus = {
+ NOT_FOUND: 404,
+};
+
+exports.HttpStatus = HttpStatus;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/common/net_utils_test.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/common/net_utils_test.js
new file mode 100644
index 00000000000..4143201990b
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/common/net_utils_test.js
@@ -0,0 +1,61 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Unit tests for mr.NetUtils.
+
+ */
+
+goog.module('mr.NetUtilsTest');
+goog.setTestOnly('mr.NetUtilsTest');
+
+const n = goog.require('mr.NetUtils');
+
+describe('mr.NetUtils', () => {
+
+ it('parses a valid IPv4 address', () => {
+ expect(n.parseIPv4Address('128.164.100.104')).toEqual([128, 164, 100, 104]);
+ expect(n.parseIPv4Address('0.0.0.0')).toEqual([0, 0, 0, 0]);
+ expect(n.parseIPv4Address('255.255.255.255')).toEqual([255, 255, 255, 255]);
+ });
+
+ it('does not parse an invalid IPv4 address', () => {
+ expect(n.parseIPv4Address('')).toBeNull();
+ expect(n.parseIPv4Address('deadbeef')).toBeNull();
+ expect(n.parseIPv4Address('128.164.100')).toBeNull();
+ expect(n.parseIPv4Address('128.164.100.104.333')).toBeNull();
+ expect(n.parseIPv4Address('256.164.100.104')).toBeNull();
+ expect(n.parseIPv4Address('-1.164.100.104')).toBeNull();
+ });
+
+ it('validates an IPv4 private network address', () => {
+ expect(n.isPrivateIPv4Address('10.0.0.0')).toBe(true);
+ expect(n.isPrivateIPv4Address('10.255.255.255')).toBe(true);
+ expect(n.isPrivateIPv4Address('172.16.0.0')).toBe(true);
+ expect(n.isPrivateIPv4Address('172.31.255.255')).toBe(true);
+ expect(n.isPrivateIPv4Address('192.168.0.0')).toBe(true);
+ expect(n.isPrivateIPv4Address('192.168.255.255')).toBe(true);
+ });
+
+ it('does not validate an IPv4 public network address', () => {
+ expect(n.isPrivateIPv4Address('9.255.255.255')).toBe(false);
+ expect(n.isPrivateIPv4Address('11.0.0.0')).toBe(false);
+ expect(n.isPrivateIPv4Address('172.15.255.255')).toBe(false);
+ expect(n.isPrivateIPv4Address('172.32.0.0')).toBe(false);
+ expect(n.isPrivateIPv4Address('193.167.255.255')).toBe(false);
+ expect(n.isPrivateIPv4Address('193.169.0.0')).toBe(false);
+ });
+
+ it('parses a URL', () => {
+ const url =
+ n.parseUrl('https://www.example.com:8080/a/path?a_query#a_fragment');
+ expect(url.protocol).toBe('https:');
+ expect(url.hostname).toBe('www.example.com');
+ expect(url.port).toBe('8080');
+ expect(url.pathname).toBe('/a/path');
+ expect(url.search).toBe('?a_query');
+ expect(url.hash).toBe('#a_fragment');
+ expect(url.host).toBe('www.example.com:8080');
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/common/retry.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/common/retry.js
new file mode 100644
index 00000000000..326dbfa1d54
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/common/retry.js
@@ -0,0 +1,198 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * A class for attempting some unreliable operation until it succeeds. Sort of
+ * an asynchronous while loop.
+ *
+ * (new mr.Retry(attempt, 500, 10)).start().then(onSuccess, onFailure);
+ *
+ * The next attempt is driven by the failure of current attempt. Thus, there is
+ * at most one attempt invocation at any time.
+ */
+
+goog.provide('mr.Retry');
+
+goog.require('mr.Assertions');
+goog.require('mr.PromiseResolver');
+
+
+/**
+ * An object that attempts some operation until it succeeds.
+ *
+ * @template R
+ */
+mr.Retry = class {
+ /**
+ * @param {function():!Promise<R>} onAttempt An
+ * idempotent function to call repeatedly until it succeeds.
+ * @param {number} retryDelay The number of milliseconds to wait between the
+ * start of one attempt and the start of the next. It must be positive.
+ * More precisely, this is the amount of time to wait between the failure
+ * of one call to onAttempt().
+ * @param {number} maxAttempts The maximum number of attempts.
+ * It must be positive.
+ */
+ constructor(onAttempt, retryDelay, maxAttempts) {
+ /**
+ * @private {!function():!Promise<R>}
+ */
+ this.onAttempt_ = onAttempt;
+
+ /**
+ * @private {number}
+ */
+ this.retryDelay_ = retryDelay > 0 ? retryDelay : 10;
+
+ /**
+ * @private {number}
+ */
+ this.maxAttempts_ = maxAttempts > 0 ? maxAttempts : 1;
+
+ /**
+ * @private {number}
+ */
+ this.maxRetryDelay_ = 0;
+
+ /**
+ * @private {number}
+ */
+ this.backoffFactor_ = 1;
+
+ /**
+ * The number of times `onAttempt_` has been called.
+ * @private {number}
+ */
+ this.numAttemptsStarted_ = 0;
+
+ /**
+ * @private {boolean}
+ */
+ this.isFinished_ = false;
+
+ /**
+ * The ID of the most recently created timer.
+ * @private {?number}
+ */
+ this.timerId_ = null;
+
+ /** @private {mr.PromiseResolver} */
+ this.resolver_ = null;
+ }
+
+ /**
+ * Starts running this object.
+ *
+ * This method starts an asynchronous process that repeatedly calls
+ * `onAttempt`.
+ *
+ * For each attempt, `onAttempt` is called. When `onAttempt`
+ * resolves, the returned promise is resolved with the same result.
+ * The returned promise rejects if `abort` is called on this object,
+ * or the number of attempts specified by `setMaxAttempts` is reached.
+ *
+ * @return {!Promise<R>}
+ * @template R
+ */
+ start() {
+ if (this.resolver_ != null) {
+ return Promise.reject(Error('Cannot call Retry.start more than once.'));
+ }
+ this.resolver_ = new mr.PromiseResolver();
+ this.retryOnce_();
+ return this.resolver_.promise;
+ }
+
+ /**
+ * Makes the next call to `onAttempt_`.
+ * @private
+ */
+ retryOnce_() {
+ this.timerId_ = null;
+ if (this.isFinished_) {
+ // The abort method has been called, don't start a new attempt.
+ return;
+ }
+
+ this.numAttemptsStarted_++;
+ this.onAttempt_().then(
+ result => {
+ this.cleanup_();
+ this.resolver_.resolve(result);
+ },
+ error => {
+ if (this.numAttemptsStarted_ >= this.maxAttempts_) {
+ // Maximum number of attempts has been reached, do not try again.
+ this.cleanup_();
+ this.resolver_.reject(Error('Max attempts reached'));
+ } else {
+ this.timerId_ =
+ setTimeout(this.retryOnce_.bind(this), this.retryDelay_);
+ this.updateRetryDelay_();
+ }
+ });
+ }
+
+ /**
+ * Implement exponential backoff.
+ * @private
+ */
+ updateRetryDelay_() {
+ let newRetryDelay = this.retryDelay_ * this.backoffFactor_;
+ if (this.maxRetryDelay_ > 0) {
+ newRetryDelay = Math.min(newRetryDelay, this.maxRetryDelay_);
+ }
+ this.retryDelay_ = newRetryDelay;
+ }
+
+ /**
+ * Sets the backoff factor. After each attempt, the retry delay is
+ * multiplied by the backoff factor, which must be at least 1.
+ *
+ * @param {number} backoffFactor The factor by which the retry delay should be
+ * increased after each attempt.
+ * @return {!mr.Retry} this
+ */
+ setBackoffFactor(backoffFactor) {
+ mr.Assertions.assert(backoffFactor >= 1);
+ this.backoffFactor_ = backoffFactor;
+ return this;
+ }
+
+ /**
+ * Sets the maximum retry delay that can be used as a result of the backoff
+ * factor. If 0, there is no maximum.
+ * @param {number} maxRetryDelay The maximum number of milliseconds to wait
+ * before retrying.
+ * @return {!mr.Retry} this
+ */
+ setMaxRetryDelay(maxRetryDelay) {
+ mr.Assertions.assert(maxRetryDelay >= 0);
+ this.maxRetryDelay_ = maxRetryDelay;
+ return this;
+ }
+
+ /**
+ * Causes this object to stop making attempts and puts it in a
+ * finished state. May be called any time after `start`.
+ */
+ abort() {
+ this.cleanup_();
+ this.resolver_.reject(Error('abort'));
+ }
+
+ /**
+ * @private
+ */
+ cleanup_() {
+ if (this.timerId_ != null) {
+ // Clean up any timer, because it will be a no-op when it fires.
+ clearTimeout(this.timerId_);
+ this.timerId_ = null;
+ }
+
+ this.isFinished_ = true;
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/common/runtime_error_utils.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/common/runtime_error_utils.js
new file mode 100644
index 00000000000..f3eab537456
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/common/runtime_error_utils.js
@@ -0,0 +1,38 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Utilities for handling extension API errors.
+ */
+
+goog.module('mr.RunTimeErrorUtils');
+goog.module.declareLegacyNamespace();
+
+const Logger = goog.require('mr.Logger');
+
+
+/**
+ * Converts chrome.runtime.lastError into an Error object. Should only be
+ * called from an extension function callback that returns null or undefined.
+ * By accessing chrome.runtime.lastError, this method has a side effect of
+ * "handling" the error by preventing it from turning into an unchecked error.
+
+ *
+ * @param {string} functionName The name of the extension API function.
+ * @param {!Logger=} logger If there is an error, logs a FINE error message
+ * with the logger, if provided.
+ * @return {?Error} An Error object with chrome.runtime.lastError.message, if
+ * any.
+ */
+exports.getError = function(functionName, logger = undefined) {
+ if (!chrome.runtime.lastError) {
+ return null;
+ }
+ const message = functionName + ' failed, chrome.runtime.lastError: ' +
+ (chrome.runtime.lastError.message || 'Unknown error');
+ if (logger) {
+ logger.fine(message);
+ }
+ return new Error(message);
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/common/sink_utils.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/common/sink_utils.js
new file mode 100644
index 00000000000..830ecbe5895
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/common/sink_utils.js
@@ -0,0 +1,187 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Utilites and settings for media sinks.
+
+ */
+
+goog.module('mr.SinkUtils');
+goog.module.declareLegacyNamespace();
+
+const PersistentData = goog.require('mr.PersistentData');
+const PersistentDataManager = goog.require('mr.PersistentDataManager');
+const Sha1 = goog.require('mr.Sha1');
+const StringUtils = goog.require('mr.StringUtils');
+const base64 = goog.require('mr.base64');
+
+
+/**
+ * @implements {PersistentData}
+ */
+class SinkUtils {
+ constructor() {
+ /**
+ * Used to generate per-profile media sink ids. Persistent data.
+ * @private {string}
+ */
+ this.receiverIdToken_ = StringUtils.getRandomString();
+
+ /**
+ * The model name of the device that was most recently used to start a media
+ * route. Temporary data.
+ * @type {!SinkUtils.DeviceData}
+ */
+ this.recentLaunchedDevice = new SinkUtils.DeviceData(null, null);
+
+ /**
+ * The model name of the device that was most recently discovered. Temporary
+ * data.
+ * @type {!SinkUtils.DeviceData}
+ */
+ this.recentDiscoveredDevice = new SinkUtils.DeviceData(null, null);
+
+ /**
+ * List of IP addresses of devices that are assumed to exist (and not
+ * discvered on the LAN), for debugging purposes. Persistent data.
+ * @type {!Array<string>}
+ */
+ this.fixedIpList = [];
+
+ /**
+ * Persistent data.
+ * @type {number}
+ */
+ this.castControlPort = 0;
+
+ /**
+ * The last time cloud sinks were checked.
+ * @type {number}
+ */
+ this.lastCloudSinkCheckTimeMillis = 0;
+
+ PersistentDataManager.register(this);
+ }
+
+ /**
+ * @return {!SinkUtils}
+ */
+ static getInstance() {
+ if (!SinkUtils.instance_) {
+ SinkUtils.instance_ = new SinkUtils();
+ }
+ return SinkUtils.instance_;
+ }
+
+ /**
+ * Generates ID from the receiver UUID and a per-profile token saved in
+ * localStorage.
+ *
+ * Both DIAL and mDNS use this to generate receiver ID so that it is
+ * consistent and can be used to deduplicate receivers. For a given token, the
+ * ID is the same for the same device no matter when it is discovered.
+ *
+ * @param {string} uniqueId
+ * @return {string} receiver ID.
+ */
+ generateId(uniqueId) {
+ uniqueId = uniqueId.toLowerCase();
+ const sha1 = new Sha1();
+ sha1.update(uniqueId);
+ sha1.update(this.receiverIdToken_);
+ return 'r' + base64.encodeArray(sha1.digest(), true);
+ }
+
+ /**
+ * @return {!SinkUtils.DeviceData} Most recent device launched or
+ * discovered.
+ */
+ getRecentDevice() {
+ if (this.recentLaunchedDevice.model) return this.recentLaunchedDevice;
+
+ if (this.recentDiscoveredDevice.model) return this.recentDiscoveredDevice;
+
+ return new SinkUtils.DeviceData(null, null);
+ }
+
+ /**
+ * @override
+ */
+ getStorageKey() {
+ return 'SinkUtils';
+ }
+
+ /**
+ * @override
+ */
+ getData() {
+ return [
+ {
+ 'recentLaunchedDevice': this.recentLaunchedDevice,
+ 'recentDiscoveredDevice': this.recentDiscoveredDevice
+ },
+ {
+ 'receiverIdToken': this.receiverIdToken_,
+ 'fixedIpList': this.fixedIpList.join(','),
+ 'castControlPort': this.castControlPort,
+ 'lastCloudSinkCheckTimeMillis': this.lastCloudSinkCheckTimeMillis
+ }
+ ];
+ }
+
+ /**
+ * @override
+ */
+ loadSavedData() {
+ const tempData = PersistentDataManager.getTemporaryData(this);
+ if (tempData) {
+ this.recentLaunchedDevice = tempData['recentLaunchedDevice'] ||
+ new SinkUtils.DeviceData(null, null);
+ this.recentDiscoveredDevice = tempData['recentDiscoveredDevice'] ||
+ new SinkUtils.DeviceData(null, null);
+ }
+
+ const persistentData = PersistentDataManager.getPersistentData(this);
+ if (persistentData) {
+ this.receiverIdToken_ =
+ persistentData['receiverIdToken'] || StringUtils.getRandomString();
+ this.fixedIpList = (persistentData['fixedIpList'] &&
+ persistentData['fixedIpList'].split(',')) ||
+ [];
+ this.castControlPort = persistentData['castControlPort'] || 0;
+ this.lastCloudSinkCheckTimeMillis =
+ persistentData['lastCloudSinkCheckTimeMillis'] || 0;
+ }
+ }
+}
+
+
+/** @private {SinkUtils} */
+SinkUtils.instance_ = null;
+
+
+/**
+ * The device data to keep track of.
+ */
+SinkUtils.DeviceData = class {
+ /**
+ * @param {?string} modelName
+ * @param {?string} ipAddress
+ */
+ constructor(modelName, ipAddress) {
+ /**
+ * @type {?string}
+ * @export
+ */
+ this.model = modelName;
+
+ /**
+ * @type {?string}
+ * @export
+ */
+ this.ip = ipAddress;
+ }
+};
+
+exports = SinkUtils;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/common/xhr_utils.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/common/xhr_utils.js
new file mode 100644
index 00000000000..0ce839efb46
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/common/xhr_utils.js
@@ -0,0 +1,72 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Utilities for dealing with XmlHttpRequests.
+ */
+
+goog.provide('mr.XhrUtils');
+
+goog.require('mr.Logger');
+
+
+/**
+ * Logs the outcome of a XmlHttpRequest.
+ * @param {mr.Logger} logger Where to log.
+ * @param {string} action The operation that created the Fetch.
+ * @param {string} method The HTTP method.
+ * @param {!XMLHttpRequest} xhr The response from Fetch.
+ */
+mr.XhrUtils.logRawXhr = function(logger, action, method, xhr) {
+ const logString = mr.XhrUtils.getStatusString_(
+ action, method, xhr.responseURL, xhr.status, xhr.statusText);
+ if (mr.XhrUtils.isSuccess(xhr)) {
+ logger.info(logString);
+ } else {
+ logger.fine(logString);
+ }
+};
+
+
+/**
+ * Returns true if the given XMLHttpRequest represents a successful response.
+ * @param {!XMLHttpRequest} xhr
+ * @return {boolean}
+ */
+mr.XhrUtils.isSuccess = function(xhr) {
+ return xhr.status >= 200 && xhr.status <= 299;
+};
+
+
+/**
+ * Returns a loggable string with the given parameters.
+ * @param {string} action
+ * @param {string} method
+ * @param {string} url
+ * @param {number} status
+ * @param {string} statusText
+ * @return {string}
+ * @private
+ */
+mr.XhrUtils.getStatusString_ = function(
+ action, method, url, status, statusText) {
+ return `[${action}]: ${method} ${url} => ${status} (${statusText})`;
+};
+
+
+/**
+ * Parses xmlText into an XML document.
+ * @param {string} xmlText A serialized XML document.
+ * @return {?Document} An XML document if the parse was successful, null
+ * otherwise.
+ */
+mr.XhrUtils.parseXml = function(xmlText) {
+ const xml = new DOMParser().parseFromString(xmlText, 'text/xml');
+ // A failed parse returns an HTML document with a <parsererror> element.
+ return xml.getElementsByTagNameNS(
+ 'http://www.w3.org/1999/xhtml', 'parsererror')
+ .length ?
+ null :
+ xml;
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_activity.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_activity.js
new file mode 100644
index 00000000000..264e20928c6
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_activity.js
@@ -0,0 +1,22 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview DIAL activity (locally launched or discovered).
+ */
+
+goog.provide('mr.dial.Activity');
+
+mr.dial.Activity = class {
+ /**
+ * @param {!mr.Route} route
+ * @param {!string} appName
+ */
+ constructor(route, appName) {
+ /** @type {!mr.Route} */
+ this.route = route;
+ /** @type {string} */
+ this.appName = appName;
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_activity_records.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_activity_records.js
new file mode 100644
index 00000000000..112ec31bd15
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_activity_records.js
@@ -0,0 +1,156 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.dial.ActivityRecords');
+
+const ActivityCallbacks = goog.require('mr.dial.ActivityCallbacks');
+const PersistentData = goog.require('mr.PersistentData');
+const PersistentDataManager = goog.require('mr.PersistentDataManager');
+
+
+/**
+ * Keeps track of DIAL activities.
+ * @implements {PersistentData}
+ */
+const ActivityRecords = class {
+ /**
+ * @param {!ActivityCallbacks} activityCallbacks
+ */
+ constructor(activityCallbacks) {
+ /**
+ * Maps route ID to the corresponding Activity.
+ * @private {!Map<string, !mr.dial.Activity>}
+ */
+ this.routeIdToAppInfo_ = new Map();
+
+ /** @private @const {!ActivityCallbacks} */
+ this.activityCallbacks_ = activityCallbacks;
+ }
+
+ /**
+ * Restores routeIdToAppInfo_ from saved data.
+ */
+ init() {
+ PersistentDataManager.register(this);
+ }
+
+ /**
+ * Removes all activities.
+ */
+ clear() {
+ this.routeIdToAppInfo_.clear();
+ }
+
+ /**
+ * Adds a new activity. If the activity already exists, this is no-op.
+ * @param {!mr.dial.Activity} activity
+ */
+ add(activity) {
+ if (this.getByRouteId(activity.route.id)) {
+ return;
+ }
+ this.routeIdToAppInfo_.set(activity.route.id, activity);
+ this.activityCallbacks_.onActivityAdded(activity);
+ }
+
+ /**
+ * Returns the activity corresponding to the given route ID, or null if there
+ * is none.
+ * @param {string} routeId
+ * @return {?mr.dial.Activity}
+ */
+ getByRouteId(routeId) {
+ return this.routeIdToAppInfo_.get(routeId) || null;
+ }
+
+ /**
+ * Returns the activity corresponding to the given sink ID, or null if there
+ * is none.
+ * @param {string} sinkId
+ * @return {?mr.dial.Activity}
+ */
+ getBySinkId(sinkId) {
+ for (let [routeId, activity] of this.routeIdToAppInfo_) {
+ if (activity.route.sinkId == sinkId) {
+ return /** @type {!mr.dial.Activity} */ (activity);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Removes the activity associated with the given sink ID.
+ * @param {string} sinkId
+ */
+ removeBySinkId(sinkId) {
+ const activity = this.getBySinkId(sinkId);
+ if (activity) {
+ this.routeIdToAppInfo_.delete(activity.route.id);
+ this.activityCallbacks_.onActivityRemoved(activity);
+ }
+ }
+
+ /**
+ * Removes the activity associated with the given route ID.
+ * @param {string} routeId
+ */
+ removeByRouteId(routeId) {
+ const activity = this.routeIdToAppInfo_.get(routeId);
+ if (activity) {
+ this.routeIdToAppInfo_.delete(routeId);
+ this.activityCallbacks_.onActivityRemoved(activity);
+ }
+ }
+
+ /**
+ * Returns the list of routes associated with the current list of activities.
+ * @return {!Array<!mr.Route>}
+ */
+ getRoutes() {
+ return Array.from(
+ this.routeIdToAppInfo_.values(), activity => activity.route);
+ }
+
+ /**
+ * Returns the current list of acitvities.
+ * @return {!Array<!mr.dial.Activity>}
+ */
+ getActivities() {
+ return Array.from(this.routeIdToAppInfo_.values());
+ }
+
+ /**
+ * Returns the number of activities.
+ * @return {number}
+ */
+ getActivityCount() {
+ return this.routeIdToAppInfo_.size;
+ }
+
+ /**
+ * @override
+ */
+ getStorageKey() {
+ return 'dial.ActivityRecords';
+ }
+
+ /**
+ * @override
+ */
+ getData() {
+ return [Array.from(this.routeIdToAppInfo_)];
+ }
+
+ /**
+ * @override
+ */
+ loadSavedData() {
+ const savedData = PersistentDataManager.getTemporaryData(this);
+ if (savedData) {
+ this.routeIdToAppInfo_ = new Map(savedData);
+ }
+ }
+};
+
+exports = ActivityRecords;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_activity_records_test.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_activity_records_test.js
new file mode 100644
index 00000000000..38ff262fba0
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_activity_records_test.js
@@ -0,0 +1,100 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.dial.ActivityRecordsTest');
+goog.setTestOnly('mr.dial.ActivityRecordsTest');
+
+const Activity = goog.require('mr.dial.Activity');
+const ActivityRecords = goog.require('mr.dial.ActivityRecords');
+const PersistentDataManager = goog.require('mr.PersistentDataManager');
+const Route = goog.require('mr.Route');
+const UnitTestUtils = goog.require('mr.UnitTestUtils');
+
+describe('DIAL ActivityRecords Tests', function() {
+ let records;
+ let mockCallbacks;
+ let activity1;
+ let activity2;
+
+ beforeEach(function() {
+ UnitTestUtils.mockMojoApi();
+ UnitTestUtils.mockChromeApi();
+ let route = new Route(
+ 'routeId1', 'presentationId1', 'sinkId1', null, false, 'description1',
+ 'imageUrl1');
+ activity1 = new Activity(route, 'app1');
+ route = new Route(
+ 'routeId2', 'presentationId2', 'sinkId2', null, true, 'description2',
+ 'imageUrl2');
+ activity2 = new Activity(route, 'app2');
+ mockCallbacks = jasmine.createSpyObj(
+ 'ActivityCallbacks',
+ ['onActivityAdded', 'onActivityRemoved', 'onActivityUpdated']);
+ records = new ActivityRecords(mockCallbacks);
+ records.init();
+ });
+
+ afterEach(function() {
+ PersistentDataManager.clear();
+ UnitTestUtils.restoreChromeApi();
+ });
+
+ it('Add activity', function() {
+ records.add(activity1);
+ expect(records.getByRouteId(activity1.route.id)).toEqual(activity1);
+ expect(mockCallbacks.onActivityAdded.calls.count()).toBe(1);
+ expect(mockCallbacks.onActivityAdded).toHaveBeenCalledWith(activity1);
+ records.add(activity1);
+ expect(mockCallbacks.onActivityAdded.calls.count()).toBe(1);
+ });
+
+ it('Get activity and route', function() {
+ records.add(activity1);
+ expect(records.getByRouteId(activity1.route.id)).toEqual(activity1);
+ expect(records.getBySinkId(activity1.route.sinkId)).toEqual(activity1);
+ expect(records.getRoutes()).toEqual([activity1.route]);
+ records.add(activity2);
+ expect(records.getByRouteId(activity2.route.id)).toEqual(activity2);
+ expect(records.getBySinkId(activity2.route.sinkId)).toEqual(activity2);
+ expect(records.getRoutes()).toEqual([activity1.route, activity2.route]);
+ });
+
+ it('Remove activity', function() {
+ records.add(activity1);
+ records.add(activity2);
+ records.removeByRouteId(activity2.route.id);
+ expect(mockCallbacks.onActivityRemoved.calls.count()).toBe(1);
+ expect(mockCallbacks.onActivityRemoved).toHaveBeenCalledWith(activity2);
+ records.removeByRouteId(activity2.route.id);
+ expect(mockCallbacks.onActivityRemoved.calls.count()).toBe(1);
+ expect(records.getByRouteId(activity2.route.id)).toEqual(null);
+ expect(records.getBySinkId(activity2.route.sinkId)).toEqual(null);
+ expect(records.getRoutes()).toEqual([activity1.route]);
+
+ records.removeBySinkId(activity1.route.sinkId);
+ expect(mockCallbacks.onActivityRemoved.calls.count()).toBe(2);
+ expect(mockCallbacks.onActivityRemoved).toHaveBeenCalledWith(activity1);
+ expect(records.getRoutes()).toEqual([]);
+ });
+
+ it('Save without any data', function() {
+ expect(records.getRoutes()).toEqual([]);
+ PersistentDataManager.suspendForTest();
+ records = new ActivityRecords(mockCallbacks);
+ records.loadSavedData();
+ expect(records.getRoutes()).toEqual([]);
+ });
+
+ it('Save with data', function() {
+ records.add(activity1);
+ records.add(activity2);
+ PersistentDataManager.suspendForTest();
+ records = new ActivityRecords(mockCallbacks);
+ records.loadSavedData();
+ expect(JSON.stringify(records.getRoutes())).toEqual(JSON.stringify([
+ activity1.route, activity2.route
+ ]));
+ });
+
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_analytics.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_analytics.js
new file mode 100644
index 00000000000..9abe0f835a5
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_analytics.js
@@ -0,0 +1,119 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Defines UMA analytics specific to the DIAL provider.
+ */
+
+goog.provide('mr.DialAnalytics');
+
+goog.require('mr.Analytics');
+
+
+/**
+ * Contains all analytics logic for the DIAL provider.
+ * @const {*}
+ */
+mr.DialAnalytics = {};
+
+
+/** @enum {string} */
+mr.DialAnalytics.Metric = {
+ DEVICE_DESCRIPTION_FAILURE: 'MediaRouter.Dial.Device.Description.Failure',
+ DEVICE_DESCRIPTION_FROM_CACHE: 'MediaRouter.Dial.Device.Description.Cached',
+ DIAL_CREATE_ROUTE: 'MediaRouter.Dial.Create.Route',
+ NON_CAST_DISCOVERY: 'MediaRouter.Dial.Sink.Discovered.NonCast'
+};
+
+
+/**
+ * Possible values for the route creation analytics.
+ * @enum {number}
+ */
+mr.DialAnalytics.DialRouteCreation = {
+ FAILED_NO_SINK: 0,
+ ROUTE_CREATED: 1,
+ NO_APP_INFO: 2,
+ FAILED_LAUNCH_APP: 3
+};
+
+
+/**
+ * Possible values for device description failures.
+ * @enum {number}
+ */
+mr.DialAnalytics.DeviceDescriptionFailures = {
+ ERROR: 0,
+ PARSE: 1,
+ EMPTY: 2
+};
+
+
+/**
+ * Records analytics around route creation.
+ * @param {mr.DialAnalytics.DialRouteCreation} value
+ */
+mr.DialAnalytics.recordCreateRoute = function(value) {
+ mr.Analytics.recordEnum(
+ mr.DialAnalytics.Metric.DIAL_CREATE_ROUTE, value,
+ mr.DialAnalytics.DialRouteCreation);
+};
+
+
+/**
+ * Records a failure with the device description.
+ * @param {!mr.DialAnalytics.DeviceDescriptionFailures} value The failure
+ * reason.
+ */
+mr.DialAnalytics.recordDeviceDescriptionFailure = function(value) {
+ mr.Analytics.recordEnum(
+ mr.DialAnalytics.Metric.DEVICE_DESCRIPTION_FAILURE, value,
+ mr.DialAnalytics.DeviceDescriptionFailures);
+};
+
+
+/**
+ * Records that device description was retreived from the cache.
+ */
+mr.DialAnalytics.recordDeviceDescriptionFromCache = function() {
+ mr.Analytics.recordEvent(
+ mr.DialAnalytics.Metric.DEVICE_DESCRIPTION_FROM_CACHE);
+};
+
+
+/**
+ * Records that a device was discovered by DIAL that didn't support the cast
+ * protocol.
+ */
+mr.DialAnalytics.recordNonCastDiscovery = function() {
+ mr.Analytics.recordEvent(mr.DialAnalytics.Metric.NON_CAST_DISCOVERY);
+};
+
+
+/**
+ * Histogram name for available DIAL devices count.
+ * @private @const {string}
+ */
+mr.DialAnalytics.AVAILABLE_DEVICES_COUNT_ =
+ 'MediaRouter.Dial.AvailableDevicesCount';
+
+
+/**
+ * Histogram name for known DIAL devices count.
+ * @private @const {string}
+ */
+mr.DialAnalytics.KNOWN_DEVICES_COUNT_ = 'MediaRouter.Dial.KnownDevicesCount';
+
+
+/**
+ * Records device counts.
+ * @param {!mr.DeviceCounts} deviceCounts
+ */
+mr.DialAnalytics.recordDeviceCounts = function(deviceCounts) {
+ mr.Analytics.recordSmallCount(
+ mr.DialAnalytics.AVAILABLE_DEVICES_COUNT_,
+ deviceCounts.availableDeviceCount);
+ mr.Analytics.recordSmallCount(
+ mr.DialAnalytics.KNOWN_DEVICES_COUNT_, deviceCounts.knownDeviceCount);
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_analytics_test.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_analytics_test.js
new file mode 100644
index 00000000000..37a762b4bf4
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_analytics_test.js
@@ -0,0 +1,88 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.setTestOnly();
+goog.require('mr.DialAnalytics');
+
+describe('Dial Analytics', function() {
+
+ beforeEach(function() {
+ chrome.metricsPrivate = jasmine.createSpyObj(
+ ['recordSmallCount', 'recordUserAction', 'recordValue']);
+ });
+
+ it('should record a Dial.Create.Route result', function() {
+ const testConfig = {
+ 'metricName': 'MediaRouter.Dial.Create.Route',
+ 'type': 'histogram-linear',
+ 'min': 1,
+ 'max': 4,
+ 'buckets': 5
+ };
+ let numCalls = 0;
+ for (key in mr.DialAnalytics.DialRouteCreation) {
+ const value = mr.DialAnalytics.DialRouteCreation[key];
+ mr.DialAnalytics.recordCreateRoute(value);
+ expect(chrome.metricsPrivate.recordValue.calls.count()).toBe(++numCalls);
+ expect(chrome.metricsPrivate.recordValue)
+ .toHaveBeenCalledWith(testConfig, value);
+ }
+ });
+
+ it('should not record an unknown Dial.Create.Route result', function() {
+ mr.DialAnalytics.recordCreateRoute('test');
+ expect(chrome.metricsPrivate.recordValue).not.toHaveBeenCalled();
+ });
+
+ it('should record a Dial.Device.Description.Failure result', function() {
+ const testConfig = {
+ 'metricName': 'MediaRouter.Dial.Device.Description.Failure',
+ 'type': 'histogram-linear',
+ 'min': 1,
+ 'max': 3,
+ 'buckets': 4
+ };
+ let numCalls = 0;
+ for (key in mr.DialAnalytics.DeviceDescriptionFailures) {
+ const value = mr.DialAnalytics.DeviceDescriptionFailures[key];
+ mr.DialAnalytics.recordDeviceDescriptionFailure(value);
+ expect(chrome.metricsPrivate.recordValue.calls.count()).toBe(++numCalls);
+ expect(chrome.metricsPrivate.recordValue)
+ .toHaveBeenCalledWith(testConfig, value);
+ }
+ });
+
+ it('should not record an unknown Dial.Device.Description.Failure',
+ function() {
+ mr.DialAnalytics.recordDeviceDescriptionFailure('test');
+ expect(chrome.metricsPrivate.recordValue).not.toHaveBeenCalled();
+ });
+
+ it('should record a Device Description From Cache action', function() {
+ mr.DialAnalytics.recordDeviceDescriptionFromCache();
+ expect(chrome.metricsPrivate.recordUserAction.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordUserAction)
+ .toHaveBeenCalledWith('MediaRouter.Dial.Device.Description.Cached');
+ });
+
+ it('should record a non-Cast Sink Discovery action', function() {
+ mr.DialAnalytics.recordNonCastDiscovery();
+ expect(chrome.metricsPrivate.recordUserAction.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordUserAction)
+ .toHaveBeenCalledWith('MediaRouter.Dial.Sink.Discovered.NonCast');
+ });
+
+ it('DeviceCounts is recorded with recordSmallCount', () => {
+ const deviceCounts = {availableDeviceCount: 5, knownDeviceCount: 8};
+ mr.DialAnalytics.recordDeviceCounts(deviceCounts);
+ expect(chrome.metricsPrivate.recordSmallCount.calls.count()).toBe(2);
+ let args = chrome.metricsPrivate.recordSmallCount.calls.argsFor(0);
+ expect(args[0]).toBe(mr.DialAnalytics.AVAILABLE_DEVICES_COUNT_);
+ expect(args[1]).toBe(deviceCounts.availableDeviceCount);
+
+ args = chrome.metricsPrivate.recordSmallCount.calls.argsFor(1);
+ expect(args[0]).toBe(mr.DialAnalytics.KNOWN_DEVICES_COUNT_);
+ expect(args[1]).toBe(deviceCounts.knownDeviceCount);
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_app_discovery_service.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_app_discovery_service.js
new file mode 100644
index 00000000000..d612fa90702
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_app_discovery_service.js
@@ -0,0 +1,480 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.dial.AppDiscoveryService');
+
+const ActivityRecords = goog.require('mr.dial.ActivityRecords');
+const DialClient = goog.require('mr.dial.Client');
+const DialSink = goog.require('mr.dial.Sink');
+const DialSinkAppStatus = goog.require('mr.dial.SinkAppStatus');
+const Logger = goog.require('mr.Logger');
+const PersistentData = goog.require('mr.PersistentData');
+const PersistentDataManager = goog.require('mr.PersistentDataManager');
+const PromiseUtils = goog.require('mr.PromiseUtils');
+const SinkDiscoveryService = goog.require('mr.dial.SinkDiscoveryService');
+
+
+/**
+ * Service for determining whether a sink supports a given DIAL app.
+ * @implements {PersistentData}
+ */
+const AppDiscoveryService = class {
+ /**
+ * @param {!SinkDiscoveryService} discoveryService
+ * @param {!ActivityRecords} activityRecords
+ */
+ constructor(discoveryService, activityRecords) {
+ /** @private @const {?Logger} */
+ this.logger_ = Logger.getInstance('mr.dial.AppDiscoveryService');
+
+ /**
+ * Names of applications that are actively being queried for.
+ * @private {!Set<string>}
+ */
+ this.registeredApps_ = new Set();
+
+ /**
+ * @private @const {!SinkDiscoveryService}
+ */
+ this.discoveryService_ = discoveryService;
+
+ /**
+ * @private @const {!ActivityRecords}
+ */
+ this.activityRecords_ = activityRecords;
+
+ /**
+ * Keeps track of pending requests to avoid duplication. Values are
+ * Promises returning strings of the form <sink.getId()>:<appName>.
+ * @private @const {!Map<string, !Promise<!DialClient.AppInfo>>}
+ */
+ this.pendingRequests_ = new Map();
+
+ /**
+ * DIAL clients used by this service. Keys are sink ids.
+ * @private @const {!Map<string, !DialClient.Client>}
+ */
+ this.clientsBySinkId_ = new Map();
+
+ /**
+ * The timeout ID for the next scan.
+ * @private {?number}
+ */
+ this.timeoutId_ = null;
+
+ /**
+ * Whether the service is running.
+ * @private {boolean}
+ */
+ this.running_ = false;
+ }
+
+ init() {
+ PersistentDataManager.register(this);
+ }
+
+ /**
+ * Starts discovery.
+ */
+ start() {
+ this.logger_.info('Starting periodic scanning.');
+ if (!this.running_) {
+ this.running_ = true;
+ this.doScan_();
+ }
+ }
+
+ /**
+ * Stops discovery. Also aborts all outstanding discovery requests.
+ */
+ stop() {
+ this.logger_.info('Stopping periodic scanning.');
+ if (!this.running_) return;
+ this.running_ = false;
+ if (this.timeoutId_) {
+ clearTimeout(this.timeoutId_);
+ this.timeoutId_ = null;
+ }
+ this.pendingRequests_.clear();
+ }
+
+ /**
+ * Registers an application name that should be queried. Starts periodic
+ * scanning if it hasn't already started. Otherwise, issues an out-of-band
+ * query for the app against all discovered sinks.
+ * @param {string} appName
+ */
+ registerApp(appName) {
+ if (this.registeredApps_.has(appName) &&
+ !this.anyUnknownAppStatus_(appName)) {
+ // No need to scan since status for appName is known for all sinks.
+ return;
+ }
+
+ this.registeredApps_.add(appName);
+
+ if (this.discoveryService_.getSinkCount() == 0) {
+ // No sinks, no need to scan.
+ return;
+ }
+ if (!this.running_) {
+ this.start();
+ } else {
+ this.discoveryService_.getSinks().forEach(
+ this.doScanSinkForApp_.bind(this, appName));
+ }
+ }
+
+ /**
+ * Unregisters an application name.
+ * @param {string} appName
+ */
+ unregisterApp(appName) {
+ this.registeredApps_.delete(appName);
+ }
+
+ /**
+ * @return {!Array<string>}
+ */
+ getRegisteredApps() {
+ return Array.from(this.registeredApps_);
+ }
+
+ /**
+ * @return {number}
+ */
+ getAppCount() {
+ return this.registeredApps_.size;
+ }
+
+ /**
+ * Issues app info queries and updates sink app status and activities
+ * according to results. Queries will be made for the following:
+ * 1) All registered apps, against all discovered sinks. Note that a (app,
+ * sink) combination is only queried if its status is unknown, or if enough
+ * time has elapsed since its previous status was known.
+ * 2) Each activity's app against its sink.
+ * Once all queries have returned and all updates have been made, schedules a
+ * scan in the future.
+ * @private
+ */
+ doScan_() {
+ this.logger_.info('Start app status scan.');
+ const promises = [];
+
+ // Scans all sinks against all registered apps.
+ this.discoveryService_.getSinks().forEach(sink => {
+ promises.push.apply(promises, this.scanSink(sink));
+ });
+
+ // Scans all activities.
+ this.activityRecords_.getActivities().forEach(activity => {
+ promises.push(this.scanActivity_(activity));
+ });
+
+ PromiseUtils.allSettled(promises).then(() => {
+ if (this.running_ && !this.timeoutId_) {
+ this.logger_.fine('Scan complete; scheduling for next scan.');
+ // NOTE(imcheng): setTimeout with a large delay does not work well in
+ // event pages. Instead we should be using chrome.alarms API, but note
+ // that it is subject to a minmum delay of 1 minute.
+ this.timeoutId_ = setTimeout(() => {
+ this.doRescan_();
+ }, AppDiscoveryService.CHECK_INTERVAL_MILLIS);
+ }
+ });
+ }
+
+ /**
+ * Clears the timer and starts a round of scanning. Only valid when called
+ * from a timer.
+ * @private
+ */
+ doRescan_() {
+ this.logger_.fine('Start app status scan (timer-based)');
+ this.timeoutId_ = null;
+ this.doScan_();
+ }
+
+ /**
+ * Asynchronously scans a sink against all registered apps and updates its app
+ * status map if needed.
+ * @param {!DialSink} sink
+ * @return {!Array<!Promise<void>>} If the sink does not support app
+ * the getting app info, returns an empty array. Otherwise, returns a list
+ * of Promises, each corresponding to a registered app, resolved when the
+ * sink's app status has been updated.
+ */
+ scanSink(sink) {
+ if (!sink.supportsAppAvailability()) {
+ return [];
+ }
+
+ const promises = [];
+ for (let appName of this.registeredApps_) {
+ promises.push(this.doScanSinkForApp_(appName, sink));
+ }
+ return promises;
+ }
+
+ /**
+ * Issues an app info query for the given app to the given sink, and updates
+ * the sink's app status map with the response.
+ * @param {string} appName
+ * @param {!DialSink} sink
+ * @return {!Promise<void>} Resolved when the sink's app status has been
+ * updated.
+ * @private
+ */
+ doScanSinkForApp_(appName, sink) {
+ if (sink.getAppStatus(appName) != DialSinkAppStatus.UNKNOWN &&
+ Date.now() - sink.getAppStatusTimeStamp(appName) <
+ AppDiscoveryService.CACHE_PERIOD_) {
+ // App status already known and not expired.
+ return Promise.resolve();
+ }
+ if (!sink.supportsAppAvailability()) {
+ return Promise.resolve();
+ }
+ this.logger_.fine(
+ 'Querying ' + sink.getId() + ' for ' + appName +
+ ' to update app status');
+ return this.getAppInfo_(sink, appName)
+ .then(
+ appInfo => {
+ const newAppStatus = this.getAvailabilityFromAppInfo_(appInfo);
+ this.maybeUpdateAppStatus_(sink, appName, newAppStatus);
+ },
+ e => {
+ // Some devices return NOT_FOUND for GetAppInfo to indicate the
+ // app is unavailable.
+ if (e instanceof DialClient.AppInfoNotFoundError) {
+ this.maybeUpdateAppStatus_(
+ sink, appName, DialSinkAppStatus.UNAVAILABLE);
+ return;
+ }
+ this.logger_.warning(
+ 'Failed to process app availability; ' + sink.getId() +
+ ' does not support app availability');
+ sink.setSupportsAppAvailability(false);
+ });
+ }
+
+ /**
+ * Issues an app info query for the given activity's app to the activity's
+ * sink, and updates the activity records if the app is no longer running.
+ * @param {!mr.dial.Activity} activity
+ * @return {!Promise<void>} Resolved when the activity record has possibly
+ * been updated.
+ * @private
+ */
+ scanActivity_(activity) {
+ const sink = this.discoveryService_.getSinkById(activity.route.sinkId);
+ if (!sink) {
+ this.logger_.warning(
+ 'Activity refers to nonexistent sink: ' + activity.route.id);
+ return Promise.resolve();
+ }
+ if (!sink.supportsAppAvailability()) {
+ return Promise.resolve();
+ }
+ const appName = activity.appName;
+ this.logger_.fine(
+ 'Querying ' + sink.getId() + ' for ' + appName + ' to update activity');
+ return this.getAppInfo_(sink, appName)
+ .then(
+ appInfo => this.maybeUpdateActivityRecord_(
+ /** @type {!DialSink} */ (sink), appName, appInfo),
+ e => this.doRemoveActivityRecord_(
+ /** @type {!DialSink} */ (sink), appName));
+ }
+
+ /**
+ * Gets the DIAL client associated with the given sink, or creates one if it
+ * does not exist.
+ * @param {!DialSink} sink
+ * @return {!DialClient.Client} A client instance for the given sink.
+ * @private
+ */
+ getDialClient_(sink) {
+ let client = this.clientsBySinkId_.get(sink.getId());
+ if (!client) {
+ client = new DialClient.Client(sink);
+ this.logger_.fine('Created DIAL client for ' + sink.getId());
+ this.clientsBySinkId_.set(sink.getId(), client);
+ }
+ return client;
+ }
+
+ /**
+ * Issues an app info query with the given sink's DIAL client. A query will
+ * not be issued if there is already a same one pending.
+ * @param {!DialSink} sink
+ * @param {string} appName
+ * @return {!Promise<!DialClient.AppInfo>} Resolved with the query response,
+ * or rejected on error.
+ * @private
+ */
+ getAppInfo_(sink, appName) {
+ const requestId = AppDiscoveryService.getRequestId_(sink, appName);
+ let promise = this.pendingRequests_.get(requestId);
+ if (promise) {
+ return promise;
+ }
+
+ promise = this.getDialClient_(sink).getAppInfo(appName);
+ this.pendingRequests_.set(requestId, promise);
+ const cleanup = () => {
+ this.pendingRequests_.delete(requestId);
+ };
+ promise.then(cleanup, cleanup);
+ return promise;
+ }
+
+ /**
+ * Translates the given app info result into a DialSinkAppStatus value.
+ * @param {!DialClient.AppInfo} appInfo
+ * @return {DialSinkAppStatus}
+ * @private
+ */
+ getAvailabilityFromAppInfo_(appInfo) {
+ if (appInfo.name == 'Netflix') {
+ return this.getAppStatusFromNetflixAppInfo_(appInfo);
+ } else {
+ switch (appInfo.state) {
+ case DialClient.DialAppState.RUNNING:
+ case DialClient.DialAppState.STOPPED:
+ return DialSinkAppStatus.AVAILABLE;
+ default:
+ return DialSinkAppStatus.UNAVAILABLE;
+ }
+ }
+ }
+
+ /**
+ * Returns app status from a Netflix app info.
+ * @param {!DialClient.AppInfo} appInfo The app info from DIAL GET.
+ * @return {DialSinkAppStatus}
+ * @private
+ */
+ getAppStatusFromNetflixAppInfo_(appInfo) {
+ const isNetflixWebsocket =
+ appInfo.extraData && appInfo.extraData['capabilities'] == 'websocket';
+ if (isNetflixWebsocket &&
+ (appInfo.state == DialClient.DialAppState.RUNNING ||
+ appInfo.state == DialClient.DialAppState.STOPPED)) {
+ return DialSinkAppStatus.AVAILABLE;
+ } else {
+ return DialSinkAppStatus.UNAVAILABLE;
+ }
+ }
+
+ /**
+ * Returns the request ID to use for an app info request.
+ * @param {!DialSink} sink
+ * @param {string} appName
+ * @return {string}
+ * @private
+ */
+ static getRequestId_(sink, appName) {
+ return sink.getId() + ':' + appName;
+ }
+
+ /**
+ * Checks whether the status of an app on a sink has changed, and if so
+ * notifies discovery service.
+ * @param {!DialSink} sink
+ * @param {string} appName
+ * @param {DialSinkAppStatus} newAppStatus
+ * @private
+ */
+ maybeUpdateAppStatus_(sink, appName, newAppStatus) {
+ this.logger_.fine(
+ 'Got app status ' + newAppStatus + ' from ' + sink.getId() + ' for ' +
+ appName);
+ const oldAppStatus = sink.getAppStatus(appName);
+ sink.setAppStatus(appName, newAppStatus);
+ if (newAppStatus != oldAppStatus) {
+ this.discoveryService_.onAppStatusChanged(appName, sink);
+ }
+ }
+
+ /**
+ * Checks whether the given app is no longer running on the given sink, and if
+ * so notifies activity records.
+ * @param {!DialSink} sink
+ * @param {string} appName
+ * @param {!DialClient.AppInfo} appInfo
+ * @private
+ */
+ maybeUpdateActivityRecord_(sink, appName, appInfo) {
+ if (appInfo.state != DialClient.DialAppState.RUNNING) {
+ this.doRemoveActivityRecord_(sink, appName);
+ }
+ }
+
+ /**
+ * Removes the activity record with the given sink and app name, if it exists.
+ * @param {!DialSink} sink
+ * @param {string} appName
+ * @private
+ */
+ doRemoveActivityRecord_(sink, appName) {
+ const activity = this.activityRecords_.getBySinkId(sink.getId());
+ if (activity && activity.appName == appName) {
+ this.activityRecords_.removeByRouteId(activity.route.id);
+ }
+ }
+
+ /**
+ * Checks if there is a sink whose status of appName is unknown.
+ * @param {string} appName
+ * @return {boolean}
+ * @private
+ */
+ anyUnknownAppStatus_(appName) {
+ return this.discoveryService_.getSinks().some(
+ s => s.getAppStatus(appName) == DialSinkAppStatus.UNKNOWN);
+ }
+
+ /**
+ * @override
+ */
+ getStorageKey() {
+ return 'dial.AppDiscoveryService';
+ }
+
+ /**
+ * @override
+ */
+ getData() {
+ return [this.getRegisteredApps()];
+ }
+
+ /**
+ * @override
+ */
+ loadSavedData() {
+ const savedData = PersistentDataManager.getTemporaryData(this);
+ this.registeredApps_ = new Set(savedData || []);
+ }
+};
+
+
+/**
+ * The interval for periodic scanning.
+ * @package @const {number}
+ */
+AppDiscoveryService.CHECK_INTERVAL_MILLIS = 60 * 1000;
+
+
+/**
+ * The amount of time an availability result for an app (both AVAILABLE and
+ * UNAVAILABLE) will be cached for.
+ * @private @const {number}
+ */
+AppDiscoveryService.CACHE_PERIOD_ = 60 * 60 * 1000;
+
+
+exports = AppDiscoveryService;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_app_discovery_service_test.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_app_discovery_service_test.js
new file mode 100644
index 00000000000..cd7edeac16b
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_app_discovery_service_test.js
@@ -0,0 +1,362 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.dial.AppDiscoveryServiceTest');
+goog.setTestOnly('mr.dial.AppDiscoveryServiceTest');
+
+const Activity = goog.require('mr.dial.Activity');
+const ActivityRecords = goog.require('mr.dial.ActivityRecords');
+const AppDiscoveryService = goog.require('mr.dial.AppDiscoveryService');
+const DialClient = goog.require('mr.dial.Client');
+const DialSink = goog.require('mr.dial.Sink');
+const DialSinkAppStatus = goog.require('mr.dial.SinkAppStatus');
+const PersistentDataManager = goog.require('mr.PersistentDataManager');
+const Route = goog.require('mr.Route');
+const UnitTestUtils = goog.require('mr.UnitTestUtils');
+
+describe('DIAL AppDiscoveryService Tests', function() {
+ let activityRecords;
+ let service;
+ let mockClock;
+ let mockDiscoveryService;
+ let mockActivityCallbacks;
+ let sink1;
+ let sink2;
+ let sink3;
+ let mockDialClient;
+ let stoppedAppInfo;
+ let runningAppInfo;
+ let installableAppInfo;
+ let stoppedAppInfoWithWebsocket;
+
+ const setUpGetAppInfoResponse = function(appInfo) {
+ mockDialClient.getAppInfo.and.callFake(() => Promise.resolve(appInfo));
+ };
+
+ const setUpGetAppInfoError = function() {
+ mockDialClient.getAppInfo.and.callFake(
+ () => Promise.reject(new Error('getAppInfo failed')));
+ };
+
+ const setUpGetAppInfoNotFoundError = function() {
+ mockDialClient.getAppInfo.and.callFake(
+ () => Promise.reject(new DialClient.AppInfoNotFoundError()));
+ };
+
+ beforeEach(function() {
+ mockClock = UnitTestUtils.useMockClockAndPromises();
+
+ mockDiscoveryService = jasmine.createSpyObj(
+ 'discoveryService',
+ ['getSinks', 'getSinkById', 'getSinkCount', 'onAppStatusChanged']);
+ mockActivityCallbacks = jasmine.createSpyObj(
+ 'activityCallbacks',
+ ['onActivityAdded', 'onActivityRemoved', 'onActivityUpdated']);
+ activityRecords = new ActivityRecords(mockActivityCallbacks);
+ service = new AppDiscoveryService(mockDiscoveryService, activityRecords);
+
+ sink1 = new DialSink('sink1', '1').setSupportsAppAvailability(true);
+ sink2 = new DialSink('sink2', '2').setSupportsAppAvailability(true);
+ sink3 = new DialSink('sink3', '3').setSupportsAppAvailability(false);
+ mockDialClient = jasmine.createSpyObj('dialClient', ['getAppInfo']);
+ spyOn(AppDiscoveryService.prototype, 'getDialClient_')
+ .and.returnValue(mockDialClient);
+ stoppedAppInfo = {'state': DialClient.DialAppState.STOPPED};
+ runningAppInfo = {'state': DialClient.DialAppState.RUNNING};
+ installableAppInfo = {'state': DialClient.DialAppState.INSTALLABLE};
+ stoppedAppInfoWithWebsocket = {
+ 'name': 'Netflix',
+ 'state': DialClient.DialAppState.STOPPED,
+ 'extraData': {'capabilities': 'websocket'}
+ };
+
+ chrome.runtime = {
+ id: 'fakeId',
+ getManifest: function() {
+ return {version: 'fakeVersion'};
+ }
+ };
+ });
+
+ afterEach(function() {
+ service.stop();
+ UnitTestUtils.restoreRealClockAndPromises();
+ PersistentDataManager.clear();
+ });
+
+ describe('Tests registerApp', function() {
+ beforeEach(function() {
+ mockDiscoveryService.getSinks.and.returnValue([sink1, sink2, sink3]);
+ mockDiscoveryService.getSinkCount.and.returnValue(3);
+ });
+
+ const expectAppStatus = function(expectedAppStatus, appName) {
+ service.init();
+
+ expect(sink1.getAppStatus(appName)).toEqual(DialSinkAppStatus.UNKNOWN);
+ expect(sink2.getAppStatus(appName)).toEqual(DialSinkAppStatus.UNKNOWN);
+ expect(sink3.getAppStatus(appName)).toEqual(DialSinkAppStatus.UNKNOWN);
+
+ service.registerApp(appName);
+
+ service.start();
+ // Let internal promises to resolve or reject.
+ mockClock.tick(1);
+
+ expect(sink1.getAppStatus(appName)).toEqual(expectedAppStatus);
+ expect(sink2.getAppStatus(appName)).toEqual(expectedAppStatus);
+ expect(sink3.getAppStatus(appName)).toEqual(DialSinkAppStatus.UNKNOWN);
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(2);
+ };
+
+ it('Response indicates app was stopped', function() {
+ setUpGetAppInfoResponse(stoppedAppInfo);
+ expectAppStatus(DialSinkAppStatus.AVAILABLE, 'YouTube');
+ });
+
+ it('Response indicates app was running', function() {
+ setUpGetAppInfoResponse(runningAppInfo);
+ expectAppStatus(DialSinkAppStatus.AVAILABLE, 'YouTube');
+ });
+
+ it('Response indicates app was installable', function() {
+ setUpGetAppInfoResponse(installableAppInfo);
+ expectAppStatus(DialSinkAppStatus.UNAVAILABLE, 'YouTube');
+ });
+
+ it('Response has invalid app info', function() {
+ setUpGetAppInfoError();
+ expectAppStatus(DialSinkAppStatus.UNKNOWN, 'YouTube');
+ expect(sink1.supportsAppAvailability()).toBe(false);
+ expect(sink2.supportsAppAvailability()).toBe(false);
+ });
+
+ it('Response indicates not found', function() {
+ setUpGetAppInfoNotFoundError();
+ expectAppStatus(DialSinkAppStatus.UNAVAILABLE, 'YouTube');
+ });
+
+ it('Netflix with special stopped info', function() {
+ setUpGetAppInfoResponse(stoppedAppInfoWithWebsocket);
+ expectAppStatus(DialSinkAppStatus.AVAILABLE, 'Netflix');
+ });
+
+ it('Netflix with normal stopped info', function() {
+ stoppedAppInfo.name = 'Netflix';
+ setUpGetAppInfoResponse(stoppedAppInfo);
+ expectAppStatus(DialSinkAppStatus.UNAVAILABLE, 'Netflix');
+ });
+
+ it('No new query generated when registering existing app with known status',
+ function() {
+ setUpGetAppInfoResponse(stoppedAppInfo);
+ expectAppStatus(DialSinkAppStatus.AVAILABLE, 'YouTube');
+ // register again
+ service.registerApp('YouTube');
+ mockClock.tick(1);
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(2);
+ // unregister does nothing because sink already had the app status.
+ service.unregisterApp('YouTube');
+ service.registerApp('YouTube');
+ mockClock.tick(1);
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(2);
+ // unless clear app status first.
+ sink1.clearAppStatus();
+ sink2.clearAppStatus();
+ service.unregisterApp('YouTube');
+ service.registerApp('YouTube');
+ mockClock.tick(1);
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(4);
+ });
+
+ it('No new query generated when register existing app with unknown status',
+ function() {
+ setUpGetAppInfoError();
+ expectAppStatus(DialSinkAppStatus.UNKNOWN, 'YouTube');
+ // Make sink1 have known status. But sink2 has unknown status.
+ sink1.setAppStatus('YouTube', DialSinkAppStatus.UNAVAILABLE);
+ // register again
+ service.registerApp('YouTube');
+ mockClock.tick(1);
+ // No re-query
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(2);
+ });
+ });
+
+ describe('Tests app status caching and onAppStatusChanged', function() {
+ beforeEach(function() {
+ mockDiscoveryService.getSinks.and.returnValue([sink1]);
+ mockDiscoveryService.getSinkCount.and.returnValue(1);
+ });
+
+ it('Known app status does not change during caching period', function() {
+ service.init();
+ setUpGetAppInfoResponse(stoppedAppInfo);
+ expect(sink1.getAppStatus('YouTube')).toEqual(DialSinkAppStatus.UNKNOWN);
+ service.registerApp('YouTube');
+ service.start();
+ mockClock.tick(1);
+ expect(sink1.getAppStatus('YouTube'))
+ .toEqual(DialSinkAppStatus.AVAILABLE);
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(1);
+ expect(mockDiscoveryService.onAppStatusChanged.calls.count()).toBe(1);
+
+ // Make app unavailable
+ setUpGetAppInfoNotFoundError();
+ service.doScan_();
+ mockClock.tick(1);
+ // No new query and app is still available since cache is not expired.
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(1);
+ expect(sink1.getAppStatus('YouTube'))
+ .toEqual(DialSinkAppStatus.AVAILABLE);
+ expect(mockDiscoveryService.onAppStatusChanged.calls.count()).toBe(1);
+ });
+
+ it('Does not re-query app status when getAppInfo throws error', function() {
+ service.init();
+ setUpGetAppInfoError();
+ expect(sink1.getAppStatus('YouTube')).toEqual(DialSinkAppStatus.UNKNOWN);
+ service.registerApp('YouTube');
+ service.start();
+ mockClock.tick(1);
+ expect(sink1.getAppStatus('YouTube')).toEqual(DialSinkAppStatus.UNKNOWN);
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(1);
+ expect(mockDiscoveryService.onAppStatusChanged.calls.count()).toBe(0);
+
+ service.doScan_();
+ mockClock.tick(1);
+ // No new query
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(1);
+ expect(sink1.getAppStatus('YouTube')).toEqual(DialSinkAppStatus.UNKNOWN);
+ expect(mockDiscoveryService.onAppStatusChanged.calls.count()).toBe(0);
+ });
+
+ it('Cache expires, re-query, different status', function() {
+ service.init();
+ setUpGetAppInfoResponse(stoppedAppInfo);
+ expect(sink1.getAppStatus('YouTube')).toEqual(DialSinkAppStatus.UNKNOWN);
+ service.registerApp('YouTube');
+ service.start();
+ mockClock.tick(1);
+ expect(sink1.getAppStatus('YouTube'))
+ .toEqual(DialSinkAppStatus.AVAILABLE);
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(1);
+ expect(mockDiscoveryService.onAppStatusChanged.calls.count()).toBe(1);
+
+ // Make app unavailable
+ setUpGetAppInfoNotFoundError();
+ // Make cache expire
+ mockClock.tick(AppDiscoveryService.CACHE_PERIOD_);
+ service.doScan_();
+ mockClock.tick(1);
+ // new query and app becomes unavailable
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(2);
+ expect(sink1.getAppStatus('YouTube'))
+ .toEqual(DialSinkAppStatus.UNAVAILABLE);
+ expect(mockDiscoveryService.onAppStatusChanged.calls.count()).toBe(2);
+ });
+
+ it('Cache expires, re-query, same status', function() {
+ service.init();
+ setUpGetAppInfoResponse(stoppedAppInfo);
+ expect(sink1.getAppStatus('YouTube')).toEqual(DialSinkAppStatus.UNKNOWN);
+ service.registerApp('YouTube');
+ service.start();
+ mockClock.tick(1);
+ expect(sink1.getAppStatus('YouTube'))
+ .toEqual(DialSinkAppStatus.AVAILABLE);
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(1);
+ expect(mockDiscoveryService.onAppStatusChanged.calls.count()).toBe(1);
+
+ // Make cache expire
+ mockClock.tick(AppDiscoveryService.CACHE_PERIOD_);
+ service.doScan_();
+ mockClock.tick(1);
+ // New query and app becomes unavailable
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(2);
+ expect(sink1.getAppStatus('YouTube'))
+ .toEqual(DialSinkAppStatus.AVAILABLE);
+ // Did not trigger app status changed event
+ expect(mockDiscoveryService.onAppStatusChanged.calls.count()).toBe(1);
+ });
+ });
+
+ describe('Tests activity scanning', function() {
+ beforeEach(function() {
+ mockDiscoveryService.getSinks.and.returnValue([sink1, sink2, sink3]);
+ mockDiscoveryService.getSinkCount.and.returnValue(3);
+ mockDiscoveryService.getSinkById.and.returnValue(sink1);
+
+ service.init();
+ const route = Route.createRoute(
+ 'presentationId', 'providerName', sink1.getId(), 'source', true,
+ 'description', null);
+ const activity = new Activity(route, 'YouTube');
+ activityRecords.add(activity);
+ expect(mockActivityCallbacks.onActivityAdded).toHaveBeenCalled();
+ });
+
+ it('Activity is removed when app is no longer running', function() {
+ setUpGetAppInfoResponse(stoppedAppInfo);
+ service.start();
+ mockClock.tick(1);
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(1);
+ expect(mockActivityCallbacks.onActivityRemoved.calls.count()).toBe(1);
+ expect(activityRecords.getActivityCount()).toBe(0);
+ });
+
+ it('Activity is not removed when app is still running', function() {
+ setUpGetAppInfoResponse(runningAppInfo);
+ service.start();
+ mockClock.tick(1);
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(1);
+ expect(mockActivityCallbacks.onActivityRemoved).not.toHaveBeenCalled();
+ expect(activityRecords.getActivityCount()).toBe(1);
+
+ // Trigger periodic rescan
+ mockClock.tick(AppDiscoveryService.CHECK_INTERVAL_MILLIS + 1);
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(2);
+ expect(mockActivityCallbacks.onActivityRemoved).not.toHaveBeenCalled();
+ expect(activityRecords.getActivityCount()).toBe(1);
+
+ // No more periodic rescan.
+ service.stop();
+ mockClock.tick(AppDiscoveryService.CHECK_INTERVAL_MILLIS + 1);
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(2);
+ });
+
+ it('Activity scanning not duplicated with app scanning', function() {
+ setUpGetAppInfoResponse(stoppedAppInfo);
+ service.registerApp('YouTube');
+ mockClock.tick(1);
+ // One for sink1 and one for sink2. Activity scanning does not issue a
+ // duplicated request. Both app status and activity status can be updated
+ // with the same response.
+ expect(mockDialClient.getAppInfo.calls.count()).toBe(2);
+ expect(sink1.getAppStatus('YouTube'))
+ .toEqual(DialSinkAppStatus.AVAILABLE);
+ expect(sink2.getAppStatus('YouTube'))
+ .toEqual(DialSinkAppStatus.AVAILABLE);
+ expect(mockActivityCallbacks.onActivityRemoved.calls.count()).toBe(1);
+ expect(activityRecords.getActivityCount()).toBe(0);
+ });
+ });
+
+ it('Persistent with data', function() {
+ mockDiscoveryService.getSinks.and.returnValue([]);
+ mockDiscoveryService.getSinkCount.and.returnValue(0);
+ const mockServiceToSuspend = new AppDiscoveryService(
+ mockDiscoveryService, new ActivityRecords(mockActivityCallbacks));
+ mockServiceToSuspend.init();
+ mockServiceToSuspend.registerApp('app1');
+ mockServiceToSuspend.registerApp('app2');
+ PersistentDataManager.suspendForTest();
+
+ const mockServiceToLoad = new AppDiscoveryService(
+ mockDiscoveryService, new ActivityRecords(mockActivityCallbacks));
+ mockServiceToLoad.loadSavedData();
+ expect(mockServiceToLoad.getRegisteredApps()).toEqual(['app1', 'app2']);
+ expect(mockServiceToLoad.getAppCount()).toEqual(2);
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_client.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_client.js
new file mode 100644
index 00000000000..52759a070fd
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_client.js
@@ -0,0 +1,325 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Client wrapper for interacting with DIAL devices and data
+ * structures for responses.
+ */
+
+goog.module('mr.dial.Client');
+goog.module.declareLegacyNamespace();
+
+const Assertions = goog.require('mr.Assertions');
+const Logger = goog.require('mr.Logger');
+const NetUtils = goog.require('mr.NetUtils');
+const XhrManager = goog.require('mr.XhrManager');
+const XhrUtils = goog.require('mr.XhrUtils');
+
+/**
+ * Possible states of a DIAL application.
+ * @enum {string}
+ */
+const DialAppState = {
+ /** The app is running. */
+ RUNNING: 'running',
+ /** The app is not running. */
+ STOPPED: 'stopped',
+ /** The app can be installed. */
+ INSTALLABLE: 'installable',
+ /**
+ * An error was encountered getting the state.
+
+ */
+ ERROR: 'error'
+};
+
+
+/**
+ * Holds data parsed from a DIAL GET response.
+ */
+const AppInfo = class {
+ constructor() {
+ /**
+ * The application name. Mandatory.
+ * @type {string}
+ */
+ this.name = 'unknown';
+
+ /**
+ * The reported state of the application.
+ * @type {DialAppState}
+ */
+ this.state = DialAppState.ERROR;
+
+ /**
+ * If the application's state is INSTALLABLE, then the URL where the app
+ * can be installed.
+ * @type {?string}
+ */
+ this.installUrl = null;
+
+ /**
+ * Whether the DELETE operation is supported.
+ * @type {boolean}
+ */
+ this.allowStop = true;
+
+ /**
+ * If the applications's state is RUNNING, a resource identifier for the
+ * running application.
+ * @type {?string}
+ */
+ this.resource = null;
+
+ /**
+ * Application-specific data included with the GET response that is not part
+ * of the official specifciations.
+ * @type {!Object<string, string>}
+ */
+ this.extraData = {};
+ }
+};
+
+
+/**
+ * Indicates that the DIAL sink returned NOT_FOUND in response to a GET request.
+ */
+const AppInfoNotFoundError = class extends Error {
+ constructor() {
+ super();
+ }
+};
+
+
+/**
+ * Wrapper for a DIAL sink used for communicating with it.
+ */
+const Client = class {
+ /**
+ * @param {!mr.dial.Sink} sink
+ * @param {!XhrManager=} xhrManager Manager for all requests.
+ */
+ constructor(sink, xhrManager = Client.getXhrManager_()) {
+ // NOTE(mfoltz,haibinlu): We do not assert if the sink supports DIAL,
+ // since combined discovery uses DialClient to check app info to see if a
+ // mDNS-discovered sink is also a DIAL sink.
+ Assertions.assert(
+ sink.getDialAppUrl(), 'Receiver must have a DIAL app URL set.');
+
+ /** @private @const {!mr.dial.Sink} */
+ this.sink_ = sink;
+
+ /** @private @const {!XhrManager} */
+ this.xhrManager_ = xhrManager;
+
+ /** @private @const {?Logger} */
+ this.logger_ = Logger.getInstance('mr.dial.Client');
+ }
+
+ /**
+ * @param {!mr.dial.Sink} sink
+ * @return {!Client}
+ */
+ static create(sink) {
+ return new Client(sink);
+ }
+
+ /**
+ * Returns the default XhrManager, creating it if necessary.
+ * @return {!XhrManager}
+ * @private
+ */
+ static getXhrManager_() {
+ if (!Client.xhrManager_) {
+ Client.xhrManager_ = new XhrManager(
+ /* maxRequests */ 10,
+ /* defaultTimeoutMillis */ 2000,
+ /* defaultNumAttempts */ 1);
+ }
+ return Client.xhrManager_;
+ }
+
+ /**
+ * @param {string} state A string representing a DIAL application state.
+ * @return {DialAppState} The corresponding state or ERROR if the
+ * state is invalid.
+ * @private
+ */
+ static parseDialAppState_(state) {
+ switch (state) {
+ case 'running':
+ return DialAppState.RUNNING;
+ case 'stopped':
+ return DialAppState.STOPPED;
+ default:
+ return DialAppState.ERROR;
+ }
+ }
+
+ /**
+ * Launches an application on the sink.
+ * @param {string} appName Name of the DIAL application to launch.
+ * @param {string} postData Data to include in the HTTP POST request.
+ * @return {!Promise<void>} Fulfilled when the operation completes
+ * successfully. Rejected otherwise.
+ */
+ launchApp(appName, postData) {
+ return this.xhrManager_
+ .send(
+ this.getAppUrl_(appName), 'POST', postData, {timeoutMillis: 15000})
+ .then(xhr => this.handleResponse_('launchApp', 'POST', xhr));
+ }
+
+ /**
+ * Stops a running application on the sink.
+ * @param {string} appName Name of the DIAL application to stop.
+ * @return {!Promise<void>} Fulfilled when the operation completes
+ * successfully. Rejected otherwise.
+ */
+ stopApp(appName) {
+ return this.xhrManager_.send(this.getAppUrl_(appName), 'DELETE')
+ .then(xhr => this.handleResponse_('stopApp', 'DELETE', xhr));
+ }
+
+ /**
+ * Gets information about a running application on the sink.
+ * @param {string} appName Name of the DIAL application to get info from.
+ * @return {!Promise<!AppInfo>} Fulfilled with AppInfo. Rejected if the
+ * operation did not complete successfully. In the case of the sink
+ * returning NOT_FOUND for the request, AppInfoNotFoundError will be
+ * thrown.
+ */
+ getAppInfo(appName) {
+ return this.xhrManager_
+ .send(this.getAppUrl_(appName), 'GET', undefined, {numAttempts: 3})
+ .then(xhr => this.handleGetAppInfoResponse_(appName, xhr));
+ }
+
+ /**
+ * Parses the response from a Xhr GET request.
+ * @param {string} appName App nam used in the request.
+ * @param {!XMLHttpRequest} xhr
+ * @return {!AppInfo}
+ * @private
+ */
+ handleGetAppInfoResponse_(appName, xhr) {
+ XhrUtils.logRawXhr(this.logger_, 'GetAppInfo', 'GET', xhr);
+ if (!XhrUtils.isSuccess(xhr)) {
+ if (xhr.status == NetUtils.HttpStatus.NOT_FOUND) {
+ throw new AppInfoNotFoundError();
+ } else {
+ throw new Error(`Response error: ${xhr.status}`);
+ }
+ }
+
+ const xml = XhrUtils.parseXml(xhr.responseText);
+ if (!xml) {
+ this.logger_.info('Invalid or empty response');
+ throw new Error('Invalid or empty response');
+ }
+
+ const service = xml.getElementsByTagName('service');
+ if (!service || service.length != 1) {
+ this.logger_.info('Invalid GET response (invalid service)');
+ throw new Error('Invalid GET response (invalid service)');
+ }
+ const appInfo = new AppInfo();
+ for (var i = 0, l = service[0].childNodes.length; i < l; i++) {
+ const node = service[0].childNodes[i];
+ if (node.nodeName == 'state') {
+ appInfo.state = Client.parseDialAppState_(node.textContent);
+ } else if (node.nodeName == 'name') {
+ appInfo.name = node.textContent;
+ } else if (node.nodeName == 'link') {
+ appInfo.resource = node.getAttribute('href');
+ } else if (node.nodeName == 'options') {
+ // The default value for allowStop is true per DIAL spec.
+ appInfo.allowStop = (node.getAttribute('allowStop') != 'false');
+ } else {
+ appInfo.extraData[node.nodeName] = node.innerHTML;
+ }
+ }
+
+ // Validate mandatory fields (name, state).
+ if (appInfo.name == 'unknown') {
+ this.logger_.info('GET response missing name value');
+ throw new Error('GET response missing name value');
+ }
+
+ if (appInfo.name != appName) {
+ this.logger_.info('GET app name mismatch');
+ throw new Error('GET app name mismatch');
+ }
+
+ if (appInfo.state == DialAppState.ERROR) {
+ this.logger_.info('GET response missing state value');
+ throw new Error('GET response missing state value');
+ }
+
+ // Parse state.
+ const installable = /installable=(.+)/.exec(appInfo.state);
+ if (installable && installable[1]) {
+ appInfo.state = DialAppState.INSTALLABLE;
+ appInfo.installUrl = installable[1];
+ } else if (
+ appInfo.state == DialAppState.RUNNING ||
+ appInfo.state == DialAppState.STOPPED) {
+ // Valid state. Continue.
+ } else {
+ this.logger_.info('GET response has invalid state value');
+ throw new Error('GET response has invalid state value');
+ }
+
+ // Success!
+ return appInfo;
+ }
+
+ /**
+ * Returns the URL used to communicate with a given DIAL application.
+ * @param {string} appName The name of the DIAL application.
+ * @return {string} The URL for the activity.
+ * @private
+ */
+ getAppUrl_(appName) {
+ let appUrl = this.sink_.getDialAppUrl();
+ if (appUrl.charAt(appUrl.length - 1) != '/') {
+ appUrl += '/';
+ }
+ return appUrl + appName;
+ }
+
+ /**
+ * Logs the given response and returns a Promise that resolves if it indicates
+ * success.
+ * @param {string} action Name of the operation that created the request.
+ * @param {string} method The HTTP method.
+ * @param {!XMLHttpRequest} xhr
+ * @return {!Promise<void>} Resolves if the response indicates success,
+ * rejected otherwise.
+ * @private
+ */
+ handleResponse_(action, method, xhr) {
+ return new Promise((resolve, reject) => {
+ XhrUtils.logRawXhr(this.logger_, action, method, xhr);
+ if (XhrUtils.isSuccess(xhr)) {
+ resolve();
+ } else {
+ reject(Error(xhr.statusText));
+ }
+ });
+ }
+};
+
+
+/**
+ * Lazily instantiated and shared between DialClient instances.
+ * @private {?XhrManager}
+ */
+Client.xhrManager_ = null;
+
+
+exports.AppInfo = AppInfo;
+exports.AppInfoNotFoundError = AppInfoNotFoundError;
+exports.Client = Client;
+exports.DialAppState = DialAppState;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_client_test.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_client_test.js
new file mode 100644
index 00000000000..0c1979d405f
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_client_test.js
@@ -0,0 +1,284 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.dial.ClientTest');
+goog.setTestOnly('mr.dial.ClientTest');
+
+const DialClient = goog.require('mr.dial.Client');
+const DialSink = goog.require('mr.dial.Sink');
+const XhrUtils = goog.require('mr.XhrUtils');
+
+describe('Dial Client Tests', function() {
+ let client;
+ let mockXhrManager;
+ let sink;
+ const appUrl = 'http://198.0.0.100/apps';
+
+
+ beforeEach(function() {
+ sink = new DialSink('sink', 'sinkid');
+ sink.setDialAppUrl(appUrl);
+ mockXhrManager = jasmine.createSpyObj('XhrManager', ['send']);
+ spyOn(XhrUtils, 'logRawXhr');
+ client = new DialClient.Client(sink, mockXhrManager);
+ });
+
+ const setMockXhrResponse = function(xml) {
+ mockXhrManager.send.and.returnValue(
+ Promise.resolve({responseText: xml, status: 200}));
+ };
+
+ const setMockXhrErrorResponse = function() {
+ mockXhrManager.send.and.returnValue(
+ Promise.resolve({responseText: null, status: 403 /* Forbidden */}));
+ };
+
+ const setMockXhrNotFoundResponse = function() {
+ mockXhrManager.send.and.returnValue(
+ Promise.resolve({responseText: null, status: 404}));
+ };
+
+ const setMockXhrReject = function() {
+ mockXhrManager.send.and.returnValue(
+ Promise.reject(new Error('send failed')));
+ };
+
+ // Suppress Jasmine warning about a spec with no expectations.
+ const noExpectations = function() {
+ expect(true).toBe(true);
+ };
+
+ describe('Tests launchApp', function() {
+ const expectLaunchAppFails = function(done) {
+ client.launchApp('YouTube', 'v=12345678').then(() => {
+ fail('launchApp unexpectedly succeeded.');
+ }, done);
+ };
+
+ it('Resolves', done => {
+ setMockXhrResponse('');
+ client.launchApp('YouTube', 'v=12345678').then(() => {
+ expect(mockXhrManager.send)
+ .toHaveBeenCalledWith(
+ appUrl + '/YouTube', 'POST', 'v=12345678', jasmine.any(Object));
+ done();
+ });
+ });
+
+ it('Rejects on error response', done => {
+ setMockXhrErrorResponse();
+ expectLaunchAppFails(done);
+ noExpectations();
+ });
+
+ it('Rejects on send rejection', done => {
+ setMockXhrReject();
+ expectLaunchAppFails(done);
+ noExpectations();
+ });
+ });
+
+ describe('Tests stopApp', function() {
+ const expectStopAppFails = function(done) {
+ client.stopApp('YouTube').then(() => {
+ fail('stopApp unexpectedly succeeded.');
+ }, done);
+ };
+
+ it('Resolves', done => {
+ setMockXhrResponse('');
+ client.stopApp('YouTube').then(() => {
+ expect(mockXhrManager.send)
+ .toHaveBeenCalledWith(appUrl + '/YouTube', 'DELETE');
+ done();
+ });
+ });
+
+ it('Rejects on error response', done => {
+ setMockXhrErrorResponse();
+ expectStopAppFails(done);
+ noExpectations();
+ });
+
+ it('Rejects on send rejection', done => {
+ setMockXhrReject();
+ expectStopAppFails(done);
+ noExpectations();
+ });
+ });
+
+ const expectMockSendGet = function() {
+ expect(mockXhrManager.send)
+ .toHaveBeenCalledWith(
+ 'http://198.0.0.100/apps/YouTube', 'GET', undefined,
+ jasmine.any(Object));
+ };
+
+ const VALID_GET_RESPONSE_ = '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<service xmlns="urn:dial-multiscreen-org:schemas:dial">' +
+ '<name>YouTube</name>' +
+ '<options allowStop="false"/>' +
+ '<state>running</state>' +
+ '<link rel="run" href="run"/>' +
+ '</service>';
+
+ const VALID_GET_RESPONSE_EXTRA_DATA_ =
+ '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<service xmlns="urn:dial-multiscreen-org:schemas:dial">' +
+ '<name>YouTube</name>' +
+ '<state>running</state>' +
+ '<link rel="run" href="run"/>' +
+ '<port>8080</port>' +
+ '<additionalData>' +
+ '<screenId>e5n3112oskr42pg0td55b38nh4</screenId>' +
+ '<otherField>2</otherField>' +
+ '</additionalData>' +
+ '</service>';
+
+ const INVALID_GET_RESPONSE_NO_SERVICE_ =
+ '<?xml version="1.0" encoding="UTF-8"?>';
+
+ const INVALID_GET_RESPONSE_NO_STATE_ =
+ '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<service xmlns="urn:dial-multiscreen-org:schemas:dial">' +
+ '<name>YouTube</name>' +
+ '<options allowStop="true"/>' +
+ '<link rel="run" href="run"/>' +
+ '</service>';
+
+ const INVALID_GET_RESPONSE_INVALID_STATE_ =
+ '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<service xmlns="urn:dial-multiscreen-org:schemas:dial">' +
+ '<name>YouTube</name>' +
+ '<options allowStop="true"/>' +
+ '<state>xyzzy</state>' +
+ '<link rel="run" href="run"/>' +
+ '</service>';
+
+ const INVALID_GET_RESPONSE_INSTALLABLE_ =
+ '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<service xmlns="urn:dial-multiscreen-org:schemas:dial">' +
+ '<name>YouTube</name>' +
+ '<options allowStop="true"/>' +
+ '<state>installable=http://play.google.com/youtube</state>' +
+ '<link rel="run" href="run"/>' +
+ '</service>';
+
+ const INVALID_GET_RESPONSE_NO_NAME_ =
+ '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<service xmlns="urn:dial-multiscreen-org:schemas:dial">' +
+ '<options allowStop="true"/>' +
+ '<state>running</state>' +
+ '<link rel="run" href="run"/>' +
+ '</service>';
+
+ const INVALID_GET_RESPONSE_WRONG_APP_NAME_ =
+ '<?xml version="1.0" encoding="UTF-8"?>' +
+ '<service xmlns="urn:dial-multiscreen-org:schemas:dial">' +
+ '<name>WrongAppName</name>' +
+ '<options allowStop="true"/>' +
+ '<state>running</state>' +
+ '<link rel="run" href="run"/>' +
+ '</service>';
+
+ describe('Tests getAppInfo', function() {
+ it('Returns info from valid response', done => {
+ setMockXhrResponse(VALID_GET_RESPONSE_);
+ client.getAppInfo('YouTube').then(appInfo => {
+ expect(appInfo.name).toEqual('YouTube');
+ expect(appInfo.state).toEqual('running');
+ expect(appInfo.allowStop).toBe(false);
+ expect(appInfo.resource).toEqual('run');
+ expectMockSendGet();
+ done();
+ });
+ });
+
+ it('Returns info with extraData', done => {
+ setMockXhrResponse(VALID_GET_RESPONSE_EXTRA_DATA_);
+ client.getAppInfo('YouTube').then(appInfo => {
+ expect(appInfo.name).toEqual('YouTube');
+ expect(appInfo.state).toEqual('running');
+ expect(appInfo.allowStop).toBe(true);
+ expect(appInfo.resource).toEqual('run');
+ expect(appInfo.extraData.port).toEqual('8080');
+ expect(appInfo.extraData.additionalData)
+ .toEqual(
+ '<screenId xmlns="urn:dial-multiscreen-org:schemas:dial">' +
+ 'e5n3112oskr42pg0td55b38nh4</screenId>' +
+ '<otherField xmlns="urn:dial-multiscreen-org:schemas:dial">2' +
+ '</otherField>');
+ expectMockSendGet();
+ done();
+ });
+ });
+
+ const expectGetAppInfoFails = function(done) {
+ client.getAppInfo('YouTube').then(
+ _ => {
+ fail('getAppInfo unexpectedly succeeded.');
+ },
+ e => {
+ expectMockSendGet();
+ done();
+ });
+ };
+
+ const testInvalidResponse = function(response, done) {
+ setMockXhrResponse(response);
+ expectGetAppInfoFails(done);
+ };
+
+ it('Rejects on invalid response 1', done => {
+ testInvalidResponse('blarg', done);
+ });
+
+ it('Rejects on invalid response 2', done => {
+ testInvalidResponse(INVALID_GET_RESPONSE_NO_SERVICE_, done);
+ });
+
+ it('Rejects on invalid response 3', done => {
+ testInvalidResponse(INVALID_GET_RESPONSE_NO_STATE_, done);
+ });
+
+ it('Rejects on invalid response 4', done => {
+ testInvalidResponse(INVALID_GET_RESPONSE_NO_NAME_, done);
+ });
+
+ it('Rejects on invalid response 5', done => {
+ testInvalidResponse(INVALID_GET_RESPONSE_INVALID_STATE_, done);
+ });
+
+ it('Rejects on invalid response 6', done => {
+ testInvalidResponse(INVALID_GET_RESPONSE_INSTALLABLE_, done);
+ });
+
+ it('Rejects on mismatched app name', done => {
+ testInvalidResponse(INVALID_GET_RESPONSE_WRONG_APP_NAME_, done);
+ });
+
+ it('Rejects on error response', done => {
+ setMockXhrErrorResponse();
+ expectGetAppInfoFails(done);
+ });
+
+ it('Rejects on send rejection', done => {
+ setMockXhrReject();
+ expectGetAppInfoFails(done);
+ });
+
+ it('Rejects with AppInfoNotFoundError', done => {
+ setMockXhrNotFoundResponse();
+ client.getAppInfo('YouTube').then(
+ _ => {
+ fail('getAppInfo unexpectedly succeeded.');
+ },
+ e => {
+ expect(e instanceof DialClient.AppInfoNotFoundError).toBe(true);
+ expectMockSendGet();
+ done();
+ });
+ });
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_provider.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_provider.js
new file mode 100644
index 00000000000..f5c471a82a5
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_provider.js
@@ -0,0 +1,474 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.DialProvider');
+goog.module.declareLegacyNamespace();
+
+const Activity = goog.require('mr.dial.Activity');
+const ActivityRecords = goog.require('mr.dial.ActivityRecords');
+const AppDiscoveryService = goog.require('mr.dial.AppDiscoveryService');
+const Assertions = goog.require('mr.Assertions');
+const CancellablePromise = goog.require('mr.CancellablePromise');
+const DeviceCountsProvider = goog.require('mr.DeviceCountsProvider');
+const DialAnalytics = goog.require('mr.DialAnalytics');
+const DialClient = goog.require('mr.dial.Client');
+const DialPresentationUrl = goog.require('mr.dial.PresentationUrl');
+const DialSink = goog.require('mr.dial.Sink');
+const Logger = goog.require('mr.Logger');
+const MediaSourceUtils = goog.require('mr.MediaSourceUtils');
+const PresentationConnectionState = goog.require('mr.PresentationConnectionState');
+const Provider = goog.require('mr.Provider');
+const ProviderCallbacks = goog.require('mr.dial.ProviderCallbacks');
+const ProviderManagerCallbacks = goog.require('mr.ProviderManagerCallbacks');
+const ProviderName = goog.require('mr.ProviderName');
+const Route = goog.require('mr.Route');
+const RouteRequestError = goog.require('mr.RouteRequestError');
+const RouteRequestResultCode = goog.require('mr.RouteRequestResultCode');
+const SinkAppStatus = goog.require('mr.dial.SinkAppStatus');
+const SinkAvailability = goog.require('mr.SinkAvailability');
+const SinkDiscoveryService = goog.require('mr.dial.SinkDiscoveryService');
+const SinkList = goog.require('mr.SinkList');
+const SinkUtils = goog.require('mr.SinkUtils');
+
+/**
+ * DIAL implementation of Media Route Provider.
+ * @implements {Provider}
+ * @implements {ProviderCallbacks}
+ * @implements {DeviceCountsProvider}
+ */
+const DialProvider = class {
+ /**
+ * @param {!ProviderManagerCallbacks} providerManagerCallbacks
+ * @param {!SinkDiscoveryService=} sinkDiscoveryService
+ * @param {!AppDiscoveryService=} appDiscoveryService
+ * @final
+ */
+ constructor(
+ providerManagerCallbacks, sinkDiscoveryService = undefined,
+ appDiscoveryService = undefined) {
+ /** @private @const {!ProviderManagerCallbacks} */
+ this.providerManagerCallbacks_ = providerManagerCallbacks;
+
+ /** @private @const {!SinkDiscoveryService} */
+ this.sinkDiscoveryService_ =
+ sinkDiscoveryService || new SinkDiscoveryService(this);
+
+ /** @private @const {!ActivityRecords} */
+ this.activityRecords_ = new ActivityRecords(this);
+
+ /** @private {?AppDiscoveryService} */
+ this.appDiscoveryService_ = appDiscoveryService || null;
+
+ /** @private @const {?Logger} */
+ this.logger_ = Logger.getInstance('mr.DialProvider');
+ }
+
+ /**
+ * @override
+ */
+ getName() {
+ return ProviderName.DIAL;
+ }
+
+ /**
+ * @override
+ */
+ getDeviceCounts() {
+ return this.sinkDiscoveryService_.getDeviceCounts();
+ }
+
+ /**
+ * @override
+ */
+ initialize(config) {
+ const sinkQueryEnabled =
+ (config && config.enable_dial_sink_query == false) ? false : true;
+ this.logger_.info('Dial sink query enabled: ' + sinkQueryEnabled + '...');
+
+ this.activityRecords_.init();
+ this.sinkDiscoveryService_.init();
+
+ if (sinkQueryEnabled) {
+ this.appDiscoveryService_ = this.appDiscoveryService_ ||
+ new AppDiscoveryService(this.sinkDiscoveryService_,
+ this.activityRecords_);
+ this.appDiscoveryService_.init();
+ } else {
+ this.appDiscoveryService_ = null;
+ }
+
+ this.maybeStartAppDiscovery_();
+ }
+
+ /**
+ * @override
+ */
+ getAvailableSinks(sourceUrn) {
+ // Prevent SinkDiscoveryService to return cached available sinks.
+ if (!this.appDiscoveryService_) {
+ return SinkList.EMPTY;
+ }
+
+ this.logger_.fine('GetAvailableSinks for ' + sourceUrn);
+ const dialMediaSource = DialPresentationUrl.create(sourceUrn);
+ return dialMediaSource ?
+ this.sinkDiscoveryService_.getSinksByAppName(dialMediaSource.appName) :
+ SinkList.EMPTY;
+ }
+
+ /**
+ * @override
+ */
+ startObservingMediaSinks(sourceUrn) {
+ if (!this.appDiscoveryService_) {
+ return;
+ }
+
+ const dialMediaSource = DialPresentationUrl.create(sourceUrn);
+ if (dialMediaSource) {
+ this.appDiscoveryService_.registerApp(dialMediaSource.appName);
+ this.maybeStartAppDiscovery_();
+ }
+ }
+
+ /**
+ * @override
+ */
+ stopObservingMediaSinks(sourceUrn) {
+ if (!this.appDiscoveryService_) {
+ return;
+ }
+
+ const dialMediaSource = DialPresentationUrl.create(sourceUrn);
+ if (dialMediaSource) {
+ this.appDiscoveryService_.unregisterApp(dialMediaSource.appName);
+ this.maybeStopAppDiscovery_();
+ }
+ }
+
+ /**
+ * @override
+ */
+ startObservingMediaRoutes(sourceUrn) {
+ this.maybeStartAppDiscovery_();
+ }
+
+ /**
+ * @override
+ */
+ stopObservingMediaRoutes(sourceUrn) {
+ this.maybeStopAppDiscovery_();
+ }
+
+ /**
+ * @private
+ */
+ maybeStopAppDiscovery_() {
+ if (!this.appDiscoveryService_) {
+ return;
+ }
+
+ if (this.sinkDiscoveryService_.getSinkCount() == 0 ||
+ (this.appDiscoveryService_.getAppCount() == 0 &&
+ this.activityRecords_.getActivityCount() == 0)) {
+ this.appDiscoveryService_.stop();
+ }
+ }
+
+ /**
+ * @private
+ */
+ maybeStartAppDiscovery_() {
+ if (!this.appDiscoveryService_) {
+ return;
+ }
+
+ if (this.sinkDiscoveryService_.getSinkCount() > 0 &&
+ (this.appDiscoveryService_.getAppCount() > 0 ||
+ this.activityRecords_.getActivityCount() > 0)) {
+ this.appDiscoveryService_.start();
+ }
+ }
+
+ /**
+ * @override
+ */
+ getSinkById(id) {
+ const dialSink = this.sinkDiscoveryService_.getSinkById(id);
+ return dialSink ? dialSink.getMrSink() : null;
+ }
+
+ /**
+ * @override
+ */
+ getRoutes() {
+ return this.activityRecords_.getRoutes();
+ }
+
+ /**
+ * @override
+ */
+ createRoute(
+ sourceUrn, sinkId, presentationId, offTheRecord, timeoutMillis,
+ opt_origin, opt_tabId) {
+ const sink = this.sinkDiscoveryService_.getSinkById(sinkId);
+ if (!sink) {
+ DialAnalytics.recordCreateRoute(
+ DialAnalytics.DialRouteCreation.FAILED_NO_SINK);
+ return CancellablePromise.reject(Error('Unkown sink: ' + sinkId));
+ }
+ SinkUtils.getInstance().recentLaunchedDevice =
+ new SinkUtils.DeviceData(sink.getModelName(), sink.getIpAddress());
+ const dialMediaSource = DialPresentationUrl.create(sourceUrn);
+ if (!dialMediaSource) {
+ return CancellablePromise.reject(Error('No app name set.'));
+ }
+ const appName = dialMediaSource.appName;
+ const dialClient = this.newClient_(sink);
+
+ return CancellablePromise.forPromise(
+ /** @type {!Promise<!Route>} */
+ (dialClient.getAppInfo(appName)
+ .then(appInfo => {
+ if (appInfo.state == DialClient.DialAppState.RUNNING) {
+ return dialClient.stopApp(appName);
+ }
+ })
+ .then(() => {
+ return dialClient.launchApp(
+ appName, dialMediaSource.launchParameter);
+ })
+ .then(() => {
+ return this.addRoute(
+ sinkId, sourceUrn, true, appName, presentationId,
+ offTheRecord);
+ })
+ .catch(err => {
+ DialAnalytics.recordCreateRoute(
+ DialAnalytics.DialRouteCreation.FAILED_LAUNCH_APP);
+ throw err;
+ })));
+ }
+
+ /**
+ * @override
+ */
+ joinRoute(
+ sourceUrn, presentationId, offTheRecord, timeoutMillis, origin, tabId) {
+ return CancellablePromise.reject(Error('Not supported'));
+ }
+
+ /**
+ * @override
+ */
+ connectRouteByRouteId(sourceUrn, routeId, presentationId, origin, tabId) {
+ return CancellablePromise.reject(Error('Not supported'));
+ }
+
+ /**
+ * @override
+ */
+ detachRoute(routeId) {}
+
+ /**
+ * @param {string} sinkId
+ * @param {?string} sourceUrn
+ * @param {boolean} isLocal
+ * @param {string} appName
+ * @param {string} presentationId
+ * @param {boolean} offTheRecord
+ * @return {!Route} The route that was just added.
+ */
+ addRoute(sinkId, sourceUrn, isLocal, appName, presentationId, offTheRecord) {
+ DialAnalytics.recordCreateRoute(
+ DialAnalytics.DialRouteCreation.ROUTE_CREATED);
+ const route = Route.createRoute(
+ presentationId, this.getName(), sinkId, sourceUrn, isLocal, appName,
+ null);
+ route.offTheRecord = offTheRecord;
+ this.activityRecords_.add(new Activity(route, appName));
+ return route;
+ }
+
+ /**
+ * @override
+ */
+ onSinkAdded(sink) {
+ this.providerManagerCallbacks_.onSinkAvailabilityUpdated(
+ this, SinkAvailability.PER_SOURCE);
+ if (this.appDiscoveryService_) {
+ this.maybeStartAppDiscovery_();
+ this.appDiscoveryService_.scanSink(sink);
+ }
+ this.providerManagerCallbacks_.onSinksUpdated();
+ SinkUtils.getInstance().recentDiscoveredDevice =
+ new SinkUtils.DeviceData(sink.getModelName(), sink.getIpAddress());
+ }
+
+ /**
+ * @override
+ */
+ onSinksRemoved(sinks) {
+ if (this.sinkDiscoveryService_.getSinkCount() == 0) {
+ this.providerManagerCallbacks_.onSinkAvailabilityUpdated(
+ this, SinkAvailability.UNAVAILABLE);
+ }
+ this.maybeStopAppDiscovery_();
+ sinks.forEach(sink => {
+ this.activityRecords_.removeBySinkId(sink.getId());
+ });
+ this.providerManagerCallbacks_.onSinksUpdated();
+ }
+
+ /**
+ * @override
+ */
+ onSinkUpdated(sink) {
+ this.providerManagerCallbacks_.onSinksUpdated();
+ }
+
+ /**
+ * @override
+ */
+ onActivityAdded(activity) {
+ this.maybeStartAppDiscovery_();
+ this.providerManagerCallbacks_.onRouteAdded(this, activity.route);
+ }
+
+ /**
+ * @override
+ */
+ onActivityRemoved(activity) {
+ const route = activity.route;
+ if (route.isLocal) {
+ this.providerManagerCallbacks_.onPresentationConnectionStateChanged(
+ route.id, PresentationConnectionState.TERMINATED);
+ }
+ this.maybeStopAppDiscovery_();
+ this.providerManagerCallbacks_.onRouteRemoved(this, route);
+ }
+
+ /**
+ * @override
+ */
+ onActivityUpdated(activity) {
+ this.providerManagerCallbacks_.onRouteUpdated(this, activity.route);
+ }
+
+ /**
+ * @override
+ */
+ terminateRoute(routeId) {
+ const activity = this.activityRecords_.getByRouteId(routeId);
+ if (!activity) {
+ return Promise.reject(new RouteRequestError(
+ RouteRequestResultCode.ROUTE_NOT_FOUND,
+ 'Route in DIAL provider not found for routeId ' + routeId));
+ }
+ this.activityRecords_.removeByRouteId(routeId);
+ const sink = this.sinkDiscoveryService_.getSinkById(activity.route.sinkId);
+ if (!sink) {
+ return Promise.reject(new RouteRequestError(
+ RouteRequestResultCode.ROUTE_NOT_FOUND,
+ 'Sink in DIAL provider not found for sinkId ' +
+ activity.route.sinkId));
+ }
+ return this.newClient_(sink).stopApp(activity.appName);
+ }
+
+ /**
+ * @override
+ */
+ getMirrorSettings(sinkId) {
+ throw new Error('Not implemented.');
+ }
+
+ /**
+ * @override
+ */
+ getMirrorServiceName(sinkId) {
+ return null;
+ }
+
+ /**
+ * @override
+ */
+ onMirrorActivityUpdated(routeId) {}
+
+ /**
+ * @override
+ */
+ sendRouteMessage(routeId, message, opt_extraInfo) {
+ return Promise.reject(Error('DIAL sending messages is not supported'));
+ }
+
+ /**
+ * @override
+ */
+ sendRouteBinaryMessage(routeId, message) {
+ return Promise.reject(Error('DIAL sending messages is not supported'));
+ }
+
+ /**
+ * @override
+ */
+ canRoute(sourceUrn, sinkId) {
+ const sink = this.sinkDiscoveryService_.getSinkById(sinkId);
+ if (!sink) {
+ return false;
+ }
+
+ if (MediaSourceUtils.isMirrorSource(sourceUrn)) {
+ return false;
+ }
+
+ const dialMediaSource = DialPresentationUrl.create(sourceUrn);
+ if (!dialMediaSource) {
+ return false;
+ }
+ return sink.getAppStatus(dialMediaSource.appName) ==
+ SinkAppStatus.AVAILABLE;
+ }
+
+ /**
+ * @override
+ */
+ canJoin(sourceUrn, presentationId, route) {
+ return false;
+ }
+
+ /**
+ * @override
+ */
+ searchSinks(sourceUrn, searchCriteria) {
+ // Not implemented.
+ return Assertions.rejectNotImplemented();
+ }
+
+ /**
+ * @override
+ */
+ createMediaRouteController(routeId, controllerRequest, observer) {
+ // Not implemented.
+ return Assertions.rejectNotImplemented();
+ }
+
+ /**
+ * @override
+ */
+ provideSinks(sinks) {
+ this.sinkDiscoveryService_.addSinks(sinks);
+ }
+
+ /**
+ * @param {!DialSink} sink
+ * @return {!DialClient.Client}
+ * @private
+ */
+ newClient_(sink) {
+ return new DialClient.Client(sink);
+ }
+};
+
+exports = DialProvider;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_provider_callbacks.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_provider_callbacks.js
new file mode 100644
index 00000000000..2c7284922b0
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_provider_callbacks.js
@@ -0,0 +1,65 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview DIAL provider callbacks used for other DIAL services to inform
+ * activity or sink updates.
+ */
+
+goog.provide('mr.dial.ActivityCallbacks');
+goog.provide('mr.dial.ProviderCallbacks');
+goog.provide('mr.dial.SinkDiscoveryCallbacks');
+
+
+
+/**
+ * @record
+ */
+mr.dial.ActivityCallbacks = class {
+ /**
+ * @param {!mr.dial.Activity } activity
+ */
+ onActivityAdded(activity) {}
+
+ /**
+ * @param {!mr.dial.Activity } activity
+ */
+ onActivityRemoved(activity) {}
+
+ /**
+ * @param {!mr.dial.Activity } activity
+ */
+ onActivityUpdated(activity) {}
+};
+
+
+
+/**
+ * @record
+ */
+mr.dial.SinkDiscoveryCallbacks = class {
+ /**
+ * @param {!mr.dial.Sink} sink Sink that has been added.
+ */
+ onSinkAdded(sink) {}
+
+ /**
+ * @param {!Array.<!mr.dial.Sink>} sinks Sinks that have been removed.
+ */
+ onSinksRemoved(sinks) {}
+
+ /**
+ * @param {!mr.dial.Sink} sink Sink that has been updated.
+ */
+ onSinkUpdated(sink) {}
+};
+
+
+
+/**
+ * @record
+ * @extends {mr.dial.ActivityCallbacks}
+ * @extends {mr.dial.SinkDiscoveryCallbacks}
+ */
+mr.dial.ProviderCallbacks = class {};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_provider_test.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_provider_test.js
new file mode 100644
index 00000000000..c82f8b9931e
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_provider_test.js
@@ -0,0 +1,256 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.DialProviderTest');
+goog.setTestOnly('mr.DialProviderTest');
+
+const Activity = goog.require('mr.dial.Activity');
+const DialClient = goog.require('mr.dial.Client');
+const DialProvider = goog.require('mr.DialProvider');
+const DialSink = goog.require('mr.dial.Sink');
+const PersistentDataManager = goog.require('mr.PersistentDataManager');
+const PresentationConnectionState = goog.require('mr.PresentationConnectionState');
+const Route = goog.require('mr.Route');
+const SinkAvailability = goog.require('mr.SinkAvailability');
+
+
+describe('DialProvider tests', function() {
+ let provider;
+ let mockPmCallbacks;
+ const pmCallbackMethods = [
+ 'getRouteMessageEventTarget', 'getProviderFromRouteId',
+ 'onPresentationConnectionClosed', 'onPresentationConnectionStateChanged',
+ 'onRouteMessage', 'onRouteRemoved', 'onRouteAdded', 'onSinksUpdated',
+ 'onSinkAvailabilityUpdated'
+ ];
+ let mockSinkDiscoveryService;
+ const sinkDiscoveryServiceMethods =
+ ['init', 'getSinkCount', 'init', 'getSinksByAppName', 'getSinkById'];
+ let mockAppDiscoveryService;
+ const appDiscoveryServiceMethods = [
+ 'init', 'start', 'stop', 'registerApp', 'unregisterApp', 'getAppCount',
+ 'scanSink'
+ ];
+ let mockDialClient;
+
+ const appInfo = new DialClient.AppInfo();
+ const youTubeUrl = 'dial:YouTube?postData=dj0xMjM=';
+
+ beforeEach(function() {
+ mockPmCallbacks =
+ jasmine.createSpyObj('ProviderManagerCallbacks', pmCallbackMethods);
+
+ mockSinkDiscoveryService = jasmine.createSpyObj(
+ 'SinkDiscoveryService', sinkDiscoveryServiceMethods);
+ mockAppDiscoveryService =
+ jasmine.createSpyObj('AppDiscoveryService', appDiscoveryServiceMethods);
+
+ provider = new DialProvider(
+ mockPmCallbacks, mockSinkDiscoveryService, mockAppDiscoveryService);
+ provider.initialize({enable_dial_discovery: true});
+ expect(mockAppDiscoveryService.init).toHaveBeenCalled();
+
+ const fakeDialSink = new DialSink('Fake DIAL sink', 'uniqueId');
+ fakeDialSink.setDialAppUrl(youTubeUrl);
+ mockDialClient = jasmine.createSpyObj(
+ 'dialClient', ['getAppInfo', 'launchApp', 'stopApp']);
+ spyOn(DialProvider.prototype, 'newClient_').and.returnValue(mockDialClient);
+ mockSinkDiscoveryService.getSinkById.and.returnValue(fakeDialSink);
+ appInfo.name = 'YouTube';
+ appInfo.state = DialClient.DialAppState.STOPPED;
+ mr.DialAnalytics.recordCreateRoute = jasmine.createSpy('recordCreateRoute');
+ });
+
+ afterEach(function() {
+ PersistentDataManager.clear();
+ });
+
+ describe('startObservingMediaSinks Test', function() {
+ it('Handles non-dial sink query', function() {
+ provider.startObservingMediaSinks('urn:not-dial:YouTube');
+ expect(mockAppDiscoveryService.registerApp).not.toHaveBeenCalled();
+ });
+
+ it('Handles valid dial sink query, no sinks', function() {
+ mockSinkDiscoveryService.getSinkCount.and.returnValue(0);
+ mockAppDiscoveryService.getAppCount.and.returnValue(1);
+ provider.startObservingMediaSinks(youTubeUrl);
+ expect(mockAppDiscoveryService.registerApp)
+ .toHaveBeenCalledWith('YouTube');
+ expect(mockAppDiscoveryService.start).not.toHaveBeenCalled();
+ });
+
+ it('Handles valid dial sink query, at least one sink', function() {
+ mockSinkDiscoveryService.getSinkCount.and.returnValue(1);
+ mockAppDiscoveryService.getAppCount.and.returnValue(1);
+ provider.startObservingMediaSinks(youTubeUrl);
+ expect(mockAppDiscoveryService.registerApp)
+ .toHaveBeenCalledWith('YouTube');
+ expect(mockAppDiscoveryService.start).toHaveBeenCalled();
+ });
+ });
+
+ describe('stopObservingMediaSinks Test', function() {
+ it('Handles non-dial sink query', function() {
+ provider.stopObservingMediaSinks('urn:not-dial:YouTube');
+ expect(mockAppDiscoveryService.unregisterApp).not.toHaveBeenCalled();
+ });
+
+ it('Handles valid dial sink query', function() {
+ mockAppDiscoveryService.getAppCount.and.returnValue(0);
+ provider.stopObservingMediaSinks(youTubeUrl);
+ expect(mockAppDiscoveryService.unregisterApp)
+ .toHaveBeenCalledWith('YouTube');
+ });
+
+ it('Handles valid dial sink query, app query remains', function() {
+ mockAppDiscoveryService.getAppCount.and.returnValue(1);
+ provider.stopObservingMediaSinks(youTubeUrl);
+ expect(mockAppDiscoveryService.unregisterApp)
+ .toHaveBeenCalledWith('YouTube');
+ });
+ });
+
+ describe('onPresentationConnectionStateChanged Test', function() {
+ it('Changes presentation state to terminated', function() {
+ const route = provider.addRoute(
+ 'sink1', youTubeUrl, true, 'app1', 'presentationId1');
+ provider.onActivityRemoved(new Activity(route, 'app1'));
+ expect(mockPmCallbacks.onPresentationConnectionStateChanged)
+ .toHaveBeenCalledWith(
+ route.id, PresentationConnectionState.TERMINATED);
+ expect(mockPmCallbacks.onRouteRemoved).toHaveBeenCalled();
+ });
+
+ it('Does not change state for non-local presentation', function() {
+ const route = provider.addRoute(
+ 'sink1', youTubeUrl, false, 'app1', 'presentationId1');
+ provider.onActivityRemoved(new Activity(route, 'app1'));
+ expect(mockPmCallbacks.onPresentationConnectionStateChanged)
+ .not.toHaveBeenCalled();
+ expect(mockPmCallbacks.onRouteRemoved).toHaveBeenCalled();
+ });
+ });
+
+ describe('createRoute Test', function() {
+ it('Creates a route', function(done) {
+ mockDialClient.getAppInfo.and.returnValue(Promise.resolve(appInfo));
+ mockDialClient.launchApp.and.returnValue(Promise.resolve());
+ provider.createRoute(youTubeUrl, 'sink1', 'presentationId1', false)
+ .promise.then(
+ route => {
+ expect(route.sinkId).toBe('sink1');
+ expect(route.mediaSource).toBe(youTubeUrl);
+ expect(route.offTheRecord).toBe(false);
+ expect(mr.DialAnalytics.recordCreateRoute)
+ .toHaveBeenCalledWith(
+ mr.DialAnalytics.DialRouteCreation.ROUTE_CREATED);
+ done();
+ },
+ e => {
+ done.fail('Unexpected error: ' + e.message);
+ });
+ });
+
+ it('Creates an off-the-record route', function(done) {
+ mockDialClient.getAppInfo.and.returnValue(Promise.resolve(appInfo));
+ mockDialClient.launchApp.and.returnValue(Promise.resolve());
+ provider.createRoute(youTubeUrl, 'sink1', 'presentationId1', true)
+ .promise.then(
+ route => {
+ expect(route.sinkId).toBe('sink1');
+ expect(route.mediaSource).toBe(youTubeUrl);
+ expect(route.offTheRecord).toBe(true);
+ expect(mr.DialAnalytics.recordCreateRoute)
+ .toHaveBeenCalledWith(
+ mr.DialAnalytics.DialRouteCreation.ROUTE_CREATED);
+ done();
+ },
+ e => {
+ done.fail('Unexpected error: ' + e.message);
+ });
+ });
+
+ it('Fails to create a route when get app info fails', function(done) {
+ mockDialClient.getAppInfo.and.returnValue(Promise.reject('fail'));
+ provider.createRoute(youTubeUrl, 'sink1', 'presentationId1', true)
+ .promise.then(done.fail, e => {
+ expect(mr.DialAnalytics.recordCreateRoute)
+ .toHaveBeenCalledWith(
+ mr.DialAnalytics.DialRouteCreation.FAILED_LAUNCH_APP);
+ done();
+ });
+ });
+
+ it('Fails to create a route when launch app fails', function(done) {
+ mockDialClient.getAppInfo.and.returnValue(Promise.resolve(appInfo));
+ mockDialClient.launchApp.and.returnValue(Promise.reject('fail'));
+ provider.createRoute(youTubeUrl, 'sink1', 'presentationId1', true)
+ .promise.then(done.fail, e => {
+ expect(mr.DialAnalytics.recordCreateRoute)
+ .toHaveBeenCalledWith(
+ mr.DialAnalytics.DialRouteCreation.FAILED_LAUNCH_APP);
+ done();
+ });
+ });
+
+ it('Starts and stop app discovery', function() {
+ mockSinkDiscoveryService.getSinkCount.and.returnValue(1);
+ let appCount = 0;
+ mockAppDiscoveryService.getAppCount.and.callFake(() => appCount);
+ const route = Route.createRoute(
+ 'presentationId', 'providerName', 'sinkId', 'source', true,
+ 'description', null);
+ const activity = new Activity(route, 'YouTube');
+ provider.activityRecords_.add(activity);
+ expect(mockAppDiscoveryService.start).toHaveBeenCalled();
+
+ appCount++;
+ provider.startObservingMediaSinks(youTubeUrl);
+ expect(mockAppDiscoveryService.registerApp)
+ .toHaveBeenCalledWith('YouTube');
+
+ appCount--;
+ provider.stopObservingMediaSinks(youTubeUrl);
+ expect(mockAppDiscoveryService.stop).not.toHaveBeenCalled();
+
+ provider.activityRecords_.removeByRouteId(route.id);
+ expect(mockAppDiscoveryService.stop).toHaveBeenCalled();
+ });
+
+ it('Sets SinkAvailability to UNAVAILABLE if no more sinks', () => {
+ mockSinkDiscoveryService.getSinkCount.and.returnValue(0);
+ // Note: This should also work if an non-empty list if passed in. For
+ // simplicity, an empty list is used here.
+ provider.onSinksRemoved([]);
+ expect(mockPmCallbacks.onSinkAvailabilityUpdated)
+ .toHaveBeenCalledWith(provider, SinkAvailability.UNAVAILABLE);
+ });
+ });
+
+ describe('Disables Dial sink query', function() {
+ beforeEach(function() {
+ PersistentDataManager.clear();
+ provider.initialize({enable_dial_sink_query: false});
+ expect(mockAppDiscoveryService.start).not.toHaveBeenCalled();
+ });
+
+ it('Starting and stopping observing media sinks does nothing', function() {
+ provider.startObservingMediaSinks(youTubeUrl);
+ expect(mockAppDiscoveryService.registerApp).not.toHaveBeenCalled();
+
+ provider.stopObservingMediaSinks(youTubeUrl);
+ expect(mockAppDiscoveryService.unregisterApp).not.toHaveBeenCalled();
+ });
+
+ it('onSinkAdded does not start app discovery', function() {
+ const sink = new DialSink('s1', 'sink1');
+ provider.onSinkAdded(sink);
+ expect(mockAppDiscoveryService.scanSink).not.toHaveBeenCalled();
+
+ const sinkList = provider.getAvailableSinks();
+ expect(sinkList.sinks.length).toBe(0);
+ });
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_sink.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_sink.js
new file mode 100644
index 00000000000..8cf0e1fe39d
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_sink.js
@@ -0,0 +1,309 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.dial.Sink');
+goog.module.declareLegacyNamespace();
+
+const Sink = goog.require('mr.Sink');
+const SinkAppStatus = goog.require('mr.dial.SinkAppStatus');
+
+
+/**
+ * A wrapper for Sink containing DIAL specific data.
+ */
+const DialSink = class {
+ /**
+ * @param {string} friendlyName
+ * @param {string} uniqueId
+ * @final
+ */
+ constructor(friendlyName, uniqueId) {
+ /** @private {?string} */
+ this.ipAddress_ = null;
+
+ /** @private {?number} */
+ this.port_ = null;
+
+ /** @private {?string} */
+ this.dialAppUrl_ = null;
+
+ /** @private {?string} */
+ this.deviceDescriptionUrl_ = null;
+
+ /** @private {?string} */
+ this.modelName_ = null;
+
+ /** @private @const {!Sink} */
+ this.mrSink_ = new Sink(uniqueId, friendlyName);
+
+ /**
+ * Holds the status of applications that may be available on the sink.
+ * Keys are application Names.
+ * @private {!Object<string, SinkAppStatus>}
+ */
+ this.appStatusMap_ = {};
+
+ /**
+ * Holds the timestamp when the status of applications was set.
+ * @private {!Object<string, number>}
+ */
+ this.appStatusTimeStamp_ = {};
+
+ /** @private {boolean} */
+ this.supportsAppAvailability_ = false;
+ }
+
+ /**
+ * @return {!Sink}
+ */
+ getMrSink() {
+ return this.mrSink_;
+ }
+
+ /**
+ * @return {string} A human readable name for the sink.
+ */
+ getFriendlyName() {
+ return this.mrSink_.friendlyName;
+ }
+
+ /**
+ * @param {string} friendlyName
+ * @return {!mr.dial.Sink} This sink.
+ */
+ setFriendlyName(friendlyName) {
+ this.mrSink_.friendlyName = friendlyName;
+ return this;
+ }
+
+ /**
+ * @return {?string} sink model name if known.
+ */
+ getModelName() {
+ return this.modelName_;
+ }
+
+ /**
+ * @param {?string} modelName
+ * @return {!mr.dial.Sink} This sink.
+ */
+ setModelName(modelName) {
+ this.modelName_ = modelName;
+ return this;
+ }
+
+ /**
+ * @return {string} An identifier for this sink.
+ */
+ getId() {
+ return this.mrSink_.id;
+ }
+
+ /**
+ * @param {string} id
+ * @return {!mr.dial.Sink} This sink.
+ */
+ setId(id) {
+ this.mrSink_.id = id;
+ return this;
+ }
+
+ /**
+ * @return {boolean} Whether this sink supports queries for DIAL app
+ * availability.
+ */
+ supportsAppAvailability() {
+ return this.supportsAppAvailability_;
+ }
+
+ /**
+ * Sets whether this sink supports DIAL app availability queries.
+ * @param {boolean} availability
+ * @return {!mr.dial.Sink} This sink.
+ */
+ setSupportsAppAvailability(availability) {
+ this.supportsAppAvailability_ = availability;
+ return this;
+ }
+
+ /**
+ * Updates sink properties.
+ * Fields that can be updated: friendlyName, dialAppUrl_,
+ * deviceDescriptionUrl_, ipAddress_, port_.
+ * @param {!mr.dial.Sink} sink
+ * @return {boolean} Whether the update resulted in changes to the sink.
+ */
+ update(sink) {
+ if (this.getId() != sink.getId()) {
+ return false;
+ }
+
+ let updated = false;
+
+ if (this.mrSink_.friendlyName != sink.mrSink_.friendlyName) {
+ this.mrSink_.friendlyName = sink.mrSink_.friendlyName;
+ updated = true;
+ }
+
+ if (this.dialAppUrl_ != sink.dialAppUrl_) {
+ this.dialAppUrl_ = sink.dialAppUrl_;
+ updated = true;
+ }
+
+ if (this.deviceDescriptionUrl_ != sink.deviceDescriptionUrl_) {
+ this.deviceDescriptionUrl_ = sink.deviceDescriptionUrl_;
+ updated = true;
+ }
+
+ if (this.ipAddress_ != sink.ipAddress_) {
+ this.ipAddress_ = sink.ipAddress_;
+ updated = true;
+ }
+
+ if (this.port_ != sink.port_) {
+ this.port_ = sink.port_;
+ updated = true;
+ }
+
+ return updated;
+ }
+
+ /**
+ * @return {?string} The IP address of the sink, if any.
+ */
+ getIpAddress() {
+ return this.ipAddress_;
+ }
+
+ /**
+ * @param {?string} ipAddress The sink IP address.
+ * @return {!mr.dial.Sink} This sink.
+ */
+ setIpAddress(ipAddress) {
+ this.ipAddress_ = ipAddress;
+ return this;
+ }
+
+ /**
+ * @return {?number} The port number of the secure channel service.
+ */
+ getPort() {
+ return this.port_;
+ }
+
+ /**
+ * @param {?number} port
+ * @return {!mr.dial.Sink} This sink.
+ */
+ setPort(port) {
+ this.port_ = port;
+ return this;
+ }
+
+ /**
+ * @return {?string} The DIAL application URL, if any.
+ */
+ getDialAppUrl() {
+ return this.dialAppUrl_;
+ }
+
+ /**
+ * @param {string} url The DIAL app URL.
+ * @return {!mr.dial.Sink} This sink.
+ */
+ setDialAppUrl(url) {
+ this.dialAppUrl_ = url;
+ return this;
+ }
+
+ /**
+ * @return {?string} The DIAL device description URL, if any.
+ */
+ getDeviceDescriptionUrl() {
+ return this.deviceDescriptionUrl_;
+ }
+
+ /**
+ * @param {string} url The DIAL device description URL.
+ * @return {!mr.dial.Sink} This sink.
+ */
+ setDeviceDescriptionUrl(url) {
+ this.deviceDescriptionUrl_ = url;
+ return this;
+ }
+
+ /**
+ * Gets the availability of an application.
+ * @param {string} appName
+ * @return {SinkAppStatus} The status of the application, or null if it was
+ * not set.
+ */
+ getAppStatus(appName) {
+ return this.appStatusMap_[appName] || SinkAppStatus.UNKNOWN;
+ }
+
+ /**
+ * Gets the time stamp of the availability of an application was set.
+ * @param {string} appName
+ * @return {?number} the number of milliseconds between midnight, January 1,
+ * 1970 and the current time, or null if availability was not set.
+ */
+ getAppStatusTimeStamp(appName) {
+ return this.appStatusTimeStamp_[appName] || null;
+ }
+
+ /**
+ * Sets the availability of an application.
+ * @param {string} appName
+ * @param {SinkAppStatus} status
+ * @return {!mr.dial.Sink} This sink.
+ */
+ setAppStatus(appName, status) {
+ this.appStatusMap_[appName] = status;
+ this.appStatusTimeStamp_[appName] = Date.now();
+ return this;
+ }
+
+ /**
+ * Clears all app status from the sink.
+ * @return {!mr.dial.Sink} This sink.
+ */
+ clearAppStatus() {
+ this.appStatusMap_ = {};
+ this.appStatusTimeStamp_ = {};
+ return this;
+ }
+
+ /**
+ * @return {string} String suitable for fine logging.
+ */
+ toDebugString() {
+ return 'name = ' + this.mrSink_.friendlyName +
+ (this.ipAddress_ ? ', ip = ' + this.ipAddress_ : '') +
+ (this.modelName_ ? ', model = ' + this.modelName_ : '') +
+ ', apps = ' + JSON.stringify(this.appStatusMap_);
+ }
+
+ /**
+ * Creates a new sink and copies the fields of the input sink to this.
+ * @param {!Object<string, *>} sink The object containing data fields.
+ * @return {!mr.dial.Sink} A newly created sink.
+ */
+ static createFrom(sink) {
+ const newSink = new DialSink(sink.mrSink_.friendlyName, '');
+ newSink.mrSink_.id = sink.mrSink_.id; // Override the sink ID.
+ newSink.ipAddress_ = sink.ipAddress_;
+ newSink.port_ = sink.port_;
+ newSink.dialAppUrl_ = sink.dialAppUrl_;
+ newSink.deviceDescriptionUrl_ = sink.deviceDescriptionUrl_;
+ newSink.modelName_ = sink.modelName_;
+ newSink.appStatusMap_ = sink.appStatusMap_;
+ newSink.appStatusTimeStamp_ = sink.appStatusTimeStamp_;
+ newSink.supportsAppAvailability_ = sink.supportsAppAvailability_;
+ return newSink;
+ }
+};
+
+
+exports = DialSink;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_sink_discovery_service.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_sink_discovery_service.js
new file mode 100644
index 00000000000..93d062e3519
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_sink_discovery_service.js
@@ -0,0 +1,334 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.dial.SinkDiscoveryService');
+goog.module.declareLegacyNamespace();
+
+const DeviceCounts = goog.require('mr.DeviceCounts');
+const DeviceCountsProvider = goog.require('mr.DeviceCountsProvider');
+const DialAnalytics = goog.require('mr.DialAnalytics');
+const DialSink = goog.require('mr.dial.Sink');
+const Logger = goog.require('mr.Logger');
+const PersistentData = goog.require('mr.PersistentData');
+const PersistentDataManager = goog.require('mr.PersistentDataManager');
+const SinkAppStatus = goog.require('mr.dial.SinkAppStatus');
+const SinkDiscoveryCallbacks = goog.require('mr.dial.SinkDiscoveryCallbacks');
+const SinkList = goog.require('mr.SinkList');
+
+
+/**
+ * Implements local discovery using DIAL.
+ * DIAL specification:
+ * http://www.dial-multiscreen.org/dial-protocol-specification
+ * @implements {PersistentData}
+ * @implements {DeviceCountsProvider}
+ */
+class SinkDiscoveryService {
+ /**
+ * @param {!SinkDiscoveryCallbacks} sinkCallBacks
+ * @final
+ */
+ constructor(sinkCallBacks) {
+ /**
+ * @private @const {!SinkDiscoveryCallbacks}
+ */
+ this.sinkCallBacks_ = sinkCallBacks;
+
+ /**
+ * @private @const {?Logger}
+ */
+ this.logger_ = Logger.getInstance('mr.dial.SinkDiscoveryService');
+
+ /**
+ * The current set of *accessible* receivers, indexed by id.
+ * @private @const {!Map<string, !DialSink>}
+ */
+ this.sinkMap_ = new Map();
+
+ /**
+ * The most recent snapshot of device counts.
+ * Updated when a DIAL onDeviceList or onError event is received.
+ * Part of PersistentData.
+ * @private {!DeviceCounts}
+ */
+ this.deviceCounts_ = {availableDeviceCount: 0, knownDeviceCount: 0};
+
+ /**
+ * The last time device counts were recorded in DialAnalytics.
+ * Persistent data.
+ * @private {number}
+ */
+ this.deviceCountMetricsRecordTime_ = 0;
+ }
+
+ /**
+ * Initializes the service. Must be called before any other methods.
+ */
+ init() {
+ PersistentDataManager.register(this);
+ }
+
+ /**
+ * Add |sinks| to sink map. Remove outdated sinks that are in sink map but not
+ * in |sinks|.
+ * @param {!Array<!mojo.Sink>} sinks list of sinks discovered by Media Router.
+ */
+ addSinks(sinks) {
+ this.logger_.info('addSinks returned ' + sinks.length + ' sinks');
+ this.logger_.fine(() => '....the list is: ' + JSON.stringify(sinks));
+
+ const oldSinkIds = new Set(this.sinkMap_.keys());
+ sinks.forEach(mojoSink => {
+ const dialSink = SinkDiscoveryService.convertSink_(mojoSink);
+ this.mayAddSink_(dialSink);
+ oldSinkIds.delete(dialSink.getId());
+ });
+
+ let removedSinks = [];
+ oldSinkIds.forEach(sinkId => {
+ const sink = this.sinkMap_.get(sinkId);
+ removedSinks.push(sink);
+ this.sinkMap_.delete(sinkId);
+ });
+
+ if (removedSinks.length > 0) {
+ this.sinkCallBacks_.onSinksRemoved(removedSinks);
+ }
+
+ // Record device count for feedback.
+ const sinkCount = this.getSinkCount();
+ this.deviceCounts_ = {
+ availableDeviceCount: sinkCount,
+ knownDeviceCount: sinkCount
+ };
+ }
+
+ /**
+ * Updates deviceCounts_ with the given counts, and reports to analytics if
+ * applicable.
+ * @param {number} availableDeviceCount
+ * @param {number} knownDeviceCount
+ * @private
+ */
+ recordDeviceCounts_(availableDeviceCount, knownDeviceCount) {
+ this.deviceCounts_ = {
+ availableDeviceCount: availableDeviceCount,
+ knownDeviceCount: knownDeviceCount
+ };
+ if (Date.now() - this.deviceCountMetricsRecordTime_ <
+ SinkDiscoveryService.DEVICE_COUNT_METRIC_THRESHOLD_MS_) {
+ return;
+ }
+ DialAnalytics.recordDeviceCounts(this.deviceCounts_);
+ this.deviceCountMetricsRecordTime_ = Date.now();
+ }
+
+ /**
+ * Adds or updates an existing sink with the given sink.
+ * @param {!DialSink} sink The new or updated sink.
+ * @private
+ */
+ mayAddSink_(sink) {
+ this.logger_.fine('mayAddSink, id = ' + sink.getId());
+ const sinkToUpdate = this.sinkMap_.get(sink.getId());
+ if (sinkToUpdate) {
+ if (sinkToUpdate.update(sink)) {
+ this.logger_.fine('Updated sink ' + sinkToUpdate.getId());
+ this.sinkCallBacks_.onSinkUpdated(sinkToUpdate);
+ }
+ } else {
+ this.logger_.fine(
+ () => `Adding new sink ${sink.getId()}: ${sink.toDebugString()}`);
+ this.sinkMap_.set(sink.getId(), sink);
+ this.sinkCallBacks_.onSinkAdded(sink);
+ }
+ }
+
+ /**
+ * Converts a mojo.Sink to a DialSink.
+ * @param {!mojo.Sink} mojoSink returned by Media Router at browser side.
+ * @return {!DialSink} DIAL sink.
+ * @private
+ */
+ static convertSink_(mojoSink) {
+
+ const uniqueId = mojoSink.sink_id;
+ const extraData = mojoSink.extra_data.dial_media_sink;
+ const isDiscoveryOnly =
+ SinkDiscoveryService.isDiscoveryOnly_(extraData.model_name);
+
+ const ip_address = extraData.ip_address.address_bytes ?
+ extraData.ip_address.address_bytes.join('.') :
+ extraData.ip_address.address.join('.');
+ return new DialSink(mojoSink.name, uniqueId)
+ .setIpAddress(ip_address)
+ .setDialAppUrl(extraData.app_url.url)
+ .setModelName(extraData.model_name)
+ .setSupportsAppAvailability(!isDiscoveryOnly);
+ }
+
+ /**
+ * Returns true if DIAL (SSDP) was only used to discover this sink, and it is
+ * not expected to support other DIAL features (app discovery, activity
+ * discovery, etc.)
+ * @param {string} modelName
+ * @return {boolean}
+ * @private
+ */
+ static isDiscoveryOnly_(modelName) {
+ return SinkDiscoveryService.DISCOVERY_ONLY_RE_.test(modelName);
+ }
+
+ /**
+ * Returns the sink with the given ID, or null if not found.
+ * @param {string} sinkId
+ * @return {?DialSink}
+ */
+ getSinkById(sinkId) {
+ return this.sinkMap_.get(sinkId) || null;
+ }
+
+ /**
+ * Returns sinks that report availability of the given app name.
+ * @param {string} appName
+ * @return {!SinkList}
+ */
+ getSinksByAppName(appName) {
+ const sinks = [];
+ this.sinkMap_.forEach(dialSink => {
+ if (dialSink.getAppStatus(appName) == SinkAppStatus.AVAILABLE)
+ sinks.push(dialSink.getMrSink());
+ });
+ return new SinkList(
+ sinks, SinkDiscoveryService.APP_ORIGIN_WHITELIST_[appName]);
+ }
+
+ /**
+ * Returns current sinks.
+ * @return {!Array<!DialSink>}
+ */
+ getSinks() {
+ return Array.from(this.sinkMap_.values());
+ }
+
+ /**
+ * @override
+ */
+ getDeviceCounts() {
+ return this.deviceCounts_;
+ }
+
+ /**
+ * @return {number}
+ */
+ getSinkCount() {
+ return this.sinkMap_.size;
+ }
+
+ /**
+ * Invoked when the app status of a sink changes.
+ * @param {string} appName
+ * @param {!DialSink} sink The sink whose status changed.
+ */
+ onAppStatusChanged(appName, sink) {
+ this.sinkCallBacks_.onSinkUpdated(sink);
+ }
+
+ /**
+ * @override
+ */
+ getStorageKey() {
+ return 'dial.DialSinkDiscoveryService';
+ }
+
+ /**
+ * @override
+ */
+ getData() {
+ return [
+ new SinkDiscoveryService.PersistentData_(
+ Array.from(this.sinkMap_), this.deviceCounts_),
+ {'deviceCountMetricsRecordTime': this.deviceCountMetricsRecordTime_}
+ ];
+ }
+
+ /**
+ * @override
+ */
+ loadSavedData() {
+ const tempData =
+ /** @type {?SinkDiscoveryService.PersistentData_} */ (
+ PersistentDataManager.getTemporaryData(this));
+ if (tempData) {
+ for (const entry of tempData.sinks) {
+ this.sinkMap_.set(entry[0], DialSink.createFrom(entry[1]));
+ }
+ this.deviceCounts_ = tempData.deviceCounts;
+ }
+
+ const permanentData = PersistentDataManager.getPersistentData(this);
+ if (permanentData) {
+ this.deviceCountMetricsRecordTime_ =
+ permanentData['deviceCountMetricsRecordTime'];
+ }
+ }
+}
+
+
+/**
+ * @private @const {!Object<string, !Array<string>>}
+ */
+SinkDiscoveryService.APP_ORIGIN_WHITELIST_ = {
+ 'YouTube': [
+ 'https://tv.youtube.com', 'https://tv-green-qa.youtube.com',
+ 'https://tv-release-qa.youtube.com', 'https://web-green-qa.youtube.com',
+ 'https://web-release-qa.youtube.com', 'https://www.youtube.com'
+ ],
+ 'Netflix': ['https://www.netflix.com'],
+ 'Pandora': ['https://www.pandora.com'],
+ 'Radio': ['https://www.pandora.com'],
+ 'Hulu': ['https://www.hulu.com'],
+ 'Vimeo': ['https://www.vimeo.com'],
+ 'Dailymotion': ['https://www.dailymotion.com'],
+ 'com.dailymotion': ['https://www.dailymotion.com'],
+};
+
+
+/**
+ * Matches DIAL model names that only support discovery.
+
+ * @private @const {!RegExp}
+ */
+SinkDiscoveryService.DISCOVERY_ONLY_RE_ =
+ new RegExp('Eureka Dongle|Chromecast Audio|Chromecast Ultra', 'i');
+
+/**
+ * How long to wait between device counts metrics are recorded. Set to 1 hour.
+ * @private @const {number}
+ */
+SinkDiscoveryService.DEVICE_COUNT_METRIC_THRESHOLD_MS_ = 60 * 60 * 1000;
+
+
+/**
+ * @private
+ */
+SinkDiscoveryService.PersistentData_ = class {
+ /**
+ * @param {!Array} sinks
+ * @param {!DeviceCounts} deviceCounts
+ */
+ constructor(sinks, deviceCounts) {
+ /**
+ * @const {!Array}
+ */
+ this.sinks = sinks;
+
+ /**
+ * @const {!DeviceCounts}
+ */
+ this.deviceCounts = deviceCounts;
+ }
+};
+
+exports = SinkDiscoveryService;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_sink_discovery_service_test.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_sink_discovery_service_test.js
new file mode 100644
index 00000000000..1aad198346c
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_sink_discovery_service_test.js
@@ -0,0 +1,163 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.dial.SinkDiscoveryServiceTest');
+goog.setTestOnly('mr.dial.SinkDiscoveryServiceTest');
+
+const DialAnalytics = goog.require('mr.DialAnalytics');
+const PersistentDataManager = goog.require('mr.PersistentDataManager');
+const SinkAppStatus = goog.require('mr.dial.SinkAppStatus');
+const SinkDiscoveryService = goog.require('mr.dial.SinkDiscoveryService');
+const UnitTestUtils = goog.require('mr.UnitTestUtils');
+
+describe('DIAL SinkDiscoveryService Tests', function() {
+ let service;
+ let mockClock;
+ let mockSinkCallbacks;
+
+ beforeEach(function() {
+ mockClock = UnitTestUtils.useMockClockAndPromises();
+ mockSinkCallbacks = jasmine.createSpyObj(
+ 'SinkCallbacks', ['onSinkAdded', 'onSinksRemoved', 'onSinkUpdated']);
+
+ chrome.metricsPrivate = {
+ recordTime: jasmine.createSpy('recordTime'),
+ recordMediumTime: jasmine.createSpy('recordMediumTime'),
+ recordLongTime: jasmine.createSpy('recordLongTime'),
+ recordUserAction: jasmine.createSpy('recordUserAction')
+ };
+
+ service = new SinkDiscoveryService(mockSinkCallbacks);
+ spyOn(DialAnalytics, 'recordDeviceCounts');
+ });
+
+ afterEach(function() {
+ UnitTestUtils.restoreRealClockAndPromises();
+ PersistentDataManager.clear();
+ });
+
+
+ /**
+ * Creates mojo sink instances.
+ * @param {number} numSinks The number of mojo sinks to create.
+ * @return {!Array<!mojo.Sink>} The mojo sinks.
+ */
+ function createMojoSinks(numSinks) {
+ const mojoSinks = [];
+ for (var i = 1; i <= numSinks; i++) {
+ const dialMediaSink = {
+ ip_address: {address_bytes: [127, 0, 0, i]},
+ model_name: 'Eureka Dongle',
+ app_url: {url: 'http://127.0.0.' + i + ':8008/apps'}
+ };
+
+ mojoSinks.push({
+ sink_id: 'sinkId ' + i,
+ name: 'TV ' + i,
+ extra_data: {dial_media_sink: dialMediaSink}
+ });
+ }
+ return mojoSinks;
+ }
+
+ describe('addSinks tests', function() {
+ beforeEach(function() {
+ service.init();
+ });
+
+ it('add mojo sinks to sink map', function() {
+ expect(service.getSinks().length).toBe(0);
+ const mojoSinks = createMojoSinks(1);
+ service.addSinks(mojoSinks);
+
+ // sinks were added
+ const actualSinks = service.getSinks();
+ expect(actualSinks.length).toBe(1);
+
+ const actualSink = actualSinks[0];
+ const mojoSink = mojoSinks[0];
+ const extraData = mojoSink.extra_data.dial_media_sink;
+ expect(actualSink.getFriendlyName()).toEqual(mojoSink.name);
+ expect(actualSink.getIpAddress())
+ .toEqual(extraData.ip_address.address_bytes.join('.'));
+ expect(actualSink.getDialAppUrl()).toEqual(extraData.app_url.url);
+ expect(actualSink.getModelName()).toEqual(extraData.model_name);
+ expect(actualSink.supportsAppAvailability()).toEqual(false);
+
+ // add-sink-events were fired.
+ expect(mockSinkCallbacks.onSinkAdded.calls.count()).toBe(1);
+ expect(mockSinkCallbacks.onSinksRemoved.calls.count()).toBe(0);
+ });
+
+ it('remove outdated sinks', function() {
+ expect(service.getSinks().length).toBe(0);
+ const mojoSinks = createMojoSinks(3);
+ // First round discover sink 1, 2, 3
+ service.addSinks(mojoSinks);
+
+ // Second round discover sink 1
+ const mojoSinks2 = createMojoSinks(1);
+ service.addSinks(mojoSinks2);
+ expect(mockSinkCallbacks.onSinkAdded.calls.count()).toBe(3);
+
+ // 2 devices were removed
+ expect(mockSinkCallbacks.onSinksRemoved.calls.count()).toBe(1);
+ const sinks = mockSinkCallbacks.onSinksRemoved.calls.argsFor(0)[0];
+ expect(sinks.length).toBe(2);
+ expect(sinks[0].getFriendlyName()).toEqual(mojoSinks[1].name);
+ expect(sinks[1].getFriendlyName()).toEqual(mojoSinks[2].name);
+
+ expect(mockSinkCallbacks.onSinkUpdated.calls.count()).toBe(0);
+ });
+
+ it('Gets sinks by app name', function() {
+ const mojoSinks = createMojoSinks(3);
+ service.addSinks(mojoSinks);
+ service.getSinkById(mojoSinks[0].sink_id)
+ .setAppStatus('YouTube', SinkAppStatus.AVAILABLE);
+ service.getSinkById(mojoSinks[1].sink_id)
+ .setAppStatus('Netflix', SinkAppStatus.AVAILABLE);
+ service.getSinkById(mojoSinks[1].sink_id)
+ .setAppStatus('YouTube', SinkAppStatus.AVAILABLE);
+ service.getSinkById(mojoSinks[2].sink_id)
+ .setAppStatus('Pandora', SinkAppStatus.UNAVAILABLE);
+ expect(service.getSinksByAppName('YouTube').sinks.length).toBe(2);
+ expect(service.getSinksByAppName('Netflix').sinks.length).toBe(1);
+ expect(service.getSinksByAppName('Netflix').sinks[0].id)
+ .toEqual(mojoSinks[1].sink_id);
+ expect(service.getSinksByAppName('Pandora').sinks.length).toBe(0);
+ });
+
+ });
+
+ it('Saves PersistentData without any data', function() {
+ service.init();
+ expect(service.getSinks()).toEqual([]);
+ PersistentDataManager.suspendForTest();
+ service = new SinkDiscoveryService(mockSinkCallbacks);
+ service.loadSavedData();
+ expect(service.getSinks()).toEqual([]);
+ });
+
+ it('Saves PersistentData with data', function() {
+ service.init();
+ service.addSinks(createMojoSinks(3));
+ mockClock.tick(1);
+ const sinks = service.getSinks();
+ expect(sinks.length).toBe(3);
+ const expectedDeviceCounts = {availableDeviceCount: 3, knownDeviceCount: 3};
+ expect(service.getDeviceCounts()).toEqual(expectedDeviceCounts);
+
+ PersistentDataManager.suspendForTest();
+ service = new SinkDiscoveryService(mockSinkCallbacks);
+ service.loadSavedData();
+ const restoredSinks = service.getSinks();
+ expect(restoredSinks.length).toBe(3);
+ expect(service.getDeviceCounts()).toEqual(expectedDeviceCounts);
+ for (let index = 0; index < 3; index++) {
+ expect(sinks[0].getId()).toEqual(restoredSinks[0].getId());
+ }
+ });
+
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_sink_test.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_sink_test.js
new file mode 100644
index 00000000000..03a467550bd
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/dial_sink_test.js
@@ -0,0 +1,127 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.dial.SinkTest');
+goog.setTestOnly('mr.dial.SinkTest');
+
+const DialSink = goog.require('mr.dial.Sink');
+const MockClock = goog.require('mr.MockClock');
+const Sink = goog.require('mr.Sink');
+const SinkAppStatus = goog.require('mr.dial.SinkAppStatus');
+
+describe('DIAL Sink Tests', function() {
+ let mockClock;
+
+ beforeEach(function() {
+ mockClock = new MockClock(true);
+ });
+
+ afterEach(function() {
+ mockClock.uninstall();
+ });
+
+ it('Gets and sets fields', function() {
+ const sink = new DialSink('name', 'id1');
+ expect(sink.getMrSink()).toEqual(new Sink('id1', 'name'));
+
+ expect(sink.getId()).toEqual('id1');
+ sink.setId('id2');
+ expect(sink.getId()).toEqual('id2');
+
+ expect(sink.getIpAddress()).toBeNull();
+ sink.setIpAddress('192.168.111.1');
+ expect(sink.getIpAddress()).toEqual('192.168.111.1');
+
+ expect(sink.getDialAppUrl()).toBeNull();
+ sink.setDialAppUrl('http://192.168.111.1/apps');
+ expect(sink.getDialAppUrl()).toEqual('http://192.168.111.1/apps');
+
+ expect(sink.getDeviceDescriptionUrl()).toBeNull();
+ sink.setDeviceDescriptionUrl('http://192.168.111.1/desc');
+ expect(sink.getDeviceDescriptionUrl()).toEqual('http://192.168.111.1/desc');
+
+ expect(sink.getModelName()).toBeNull();
+ sink.setModelName('chromecast');
+ expect(sink.getModelName()).toEqual('chromecast');
+
+ expect(sink.getFriendlyName()).toEqual('name');
+ sink.setFriendlyName('newname');
+ expect(sink.getFriendlyName()).toEqual('newname');
+
+ expect(sink.getPort()).toEqual(null);
+ sink.setPort(8009);
+ expect(sink.getPort()).toEqual(8009);
+ });
+
+ it('Gets and sets sink app status', function() {
+ const sink = new DialSink('name', 'uniqueId');
+ expect(sink.getAppStatus('youtube')).toBe(SinkAppStatus.UNKNOWN);
+ expect(sink.getAppStatusTimeStamp('youtube')).toBe(null);
+
+ mockClock.tick(10);
+ const now1 = Date.now();
+ sink.setAppStatus('youtube', SinkAppStatus.AVAILABLE);
+ mockClock.tick(10);
+ const now2 = Date.now();
+ sink.setAppStatus('app2', SinkAppStatus.UNAVAILABLE);
+ expect(sink.getAppStatus('youtube')).toBe(SinkAppStatus.AVAILABLE);
+ expect(sink.getAppStatusTimeStamp('youtube')).toBe(now1);
+ expect(sink.getAppStatus('app2')).toBe(SinkAppStatus.UNAVAILABLE);
+ expect(sink.getAppStatusTimeStamp('app2')).toBe(now2);
+
+ sink.clearAppStatus();
+ expect(sink.getAppStatus('youtube')).toBe(SinkAppStatus.UNKNOWN);
+ expect(sink.getAppStatusTimeStamp('youtube')).toBe(null);
+ expect(sink.getAppStatus('app2')).toBe(SinkAppStatus.UNKNOWN);
+ expect(sink.getAppStatusTimeStamp('app2')).toBe(null);
+ });
+
+ it('Updates sink from another sink', function() {
+ const sink = new DialSink('name', 'uniqueId')
+ .setDialAppUrl('http://192.168.111.1/apps')
+ .setPort(8009)
+ .setDeviceDescriptionUrl('http://192.168.111.1/desc');
+
+ let updatedSink = new DialSink('name2', 'uniqueId');
+ expect(sink.update(updatedSink)).toBe(true);
+ expect(sink.getFriendlyName()).toEqual('name2');
+
+ updatedSink = new DialSink('name', 'uniqueId')
+ .setDialAppUrl('http://192.168.111.1/apps/app2');
+ expect(sink.update(updatedSink)).toBe(true);
+ expect(sink.getDialAppUrl()).toEqual('http://192.168.111.1/apps/app2');
+
+ updatedSink = new DialSink('name', 'uniqueId').setId('id2');
+ expect(sink.update(updatedSink)).toBe(false);
+ });
+
+ it('Updates ip address', function() {
+ const sink = new DialSink('name', 'uniqueId').setIpAddress('192.168.111.1');
+ const updatedSink =
+ new DialSink('name', 'uniqueId').setIpAddress('192.168.111.2');
+ expect(sink.update(updatedSink)).toBe(true);
+ expect(sink.getIpAddress()).toEqual('192.168.111.2');
+ });
+
+ it('Updates device description url', function() {
+ const sink = new DialSink('name', 'uniqueId')
+ .setDeviceDescriptionUrl('http://192.168.111.1/desc');
+ const updatedSink =
+ new DialSink('name', 'uniqueId')
+ .setDeviceDescriptionUrl('http://192.168.111.2/desc');
+ expect(sink.update(updatedSink)).toBe(true);
+ expect(sink.getDeviceDescriptionUrl()).toEqual('http://192.168.111.2/desc');
+ });
+
+ it('Creates sink from an Object', function() {
+ const sink = new DialSink('name', 'uniqueId')
+ .setDialAppUrl('http://192.168.111.1/apps')
+ .setPort(8009)
+ .setDeviceDescriptionUrl('http://192.168.111.1/desc')
+ .setAppStatus('youtube', SinkAppStatus.AVAILABLE)
+ .setIpAddress('192.168.111.1')
+ .setModelName('chromecast');
+ expect(DialSink.createFrom(sink)).toEqual(sink);
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/presentation_url.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/presentation_url.js
new file mode 100644
index 00000000000..203b40e134f
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/presentation_url.js
@@ -0,0 +1,146 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.dial.PresentationUrl');
+
+const Logger = goog.require('mr.Logger');
+const base64 = goog.require('mr.base64');
+
+
+/**
+ * Represents a DIAL media source containing information specific to a DIAL
+ * launch.
+ */
+const PresentationUrl = class {
+ /**
+ * @param {string} appName The DIAL application name.
+ * @param {string=} launchParameter DIAL application launch parameter.
+ */
+ constructor(appName, launchParameter = '') {
+ /** @const {string} */
+ this.appName = appName;
+ /** @const {string} */
+ this.launchParameter = launchParameter;
+ }
+
+ /**
+ * Generates a DIAL Presentation URL using given parameters.
+ * @param {string} dialAppName Name of the DIAL app.
+ * @param {?string} dialPostData base-64 encoded string of the data for the
+ * DIAL launch.
+ * @return {string}
+ */
+ static getPresentationUrlAsString(dialAppName, dialPostData) {
+ const url = new URL('dial:' + dialAppName);
+ if (dialPostData) {
+ url.searchParams.set('postData', dialPostData);
+ }
+ return url.toString();
+ }
+
+ /**
+ * Constructs a DIAL media source from a URL. The URL can take on the new
+ * format (with dial: protocol) or the old format (with https: protocol).
+ * @param {string} urlString The media source URL.
+ * @return {?PresentationUrl} A DIAL media source if the parse was
+ * successful, null otherwise.
+ */
+ static create(urlString) {
+ let url;
+ try {
+ url = new URL(urlString);
+ } catch (err) {
+ PresentationUrl.logger_.info('Invalid URL: ' + urlString);
+ return null;
+ }
+ switch (url.protocol) {
+ case 'dial:':
+ return PresentationUrl.parseDialUrl_(url);
+ case 'https:':
+
+ return PresentationUrl.parseLegacyUrl_(url);
+ default:
+ PresentationUrl.logger_.fine('Unhandled protocol: ' + url.protocol);
+ return null;
+ }
+ }
+
+ /**
+ * Parses the given URL using the new DIAL URL format, which takes the form:
+ * dial:<App name>?postData=<base64-encoded launch parameters>
+ * @param {!URL} url
+ * @return {?PresentationUrl}
+ * @private
+ */
+ static parseDialUrl_(url) {
+ const appName = url.pathname;
+ if (!appName.match(/^\w+$/)) {
+ PresentationUrl.logger_.warning('Invalid app name: ' + appName);
+ return null;
+ }
+ let postData = url.searchParams.get('postData') || undefined;
+ if (postData) {
+ try {
+ postData = base64.decodeString(postData);
+ } catch (err) {
+ PresentationUrl.logger_.warning(
+ 'Invalid base64 encoded postData:' + postData);
+ return null;
+ }
+ }
+ return new PresentationUrl(appName, postData);
+ }
+
+ /**
+ * Parses the given URL using the legacy format specified in
+ * http://goo.gl/8qKAE7
+ * Example:
+ * http://www.youtube.com/tv#__dialAppName__=YouTube/__dialPostData__=dj0xMjM=
+ * @param {!URL} url
+ * @return {?PresentationUrl}
+ * @private
+ */
+ static parseLegacyUrl_(url) {
+ // Parse URI and get fragment.
+ const fragment = url.hash;
+ if (!fragment) return null;
+ let appName = PresentationUrl.APP_NAME_REGEX_.exec(fragment);
+ appName = appName ? appName[1] : null;
+ if (!appName) return null;
+ appName = decodeURIComponent(appName);
+
+ let postData = PresentationUrl.LAUNCH_PARAM_REGEX_.exec(fragment);
+ postData = postData ? postData[1] : undefined;
+ if (postData) {
+ try {
+ postData = base64.decodeString(postData);
+ } catch (err) {
+ PresentationUrl.logger_.warning(
+ 'Invalid base64 encoded postData:' + postData);
+ return null;
+ }
+ }
+ return new PresentationUrl(appName, postData);
+ }
+};
+
+
+/** @const @private {?Logger} */
+PresentationUrl.logger_ = Logger.getInstance('mr.dial.PresentationUrl');
+
+
+/** @const {string} */
+PresentationUrl.URN_PREFIX = 'urn:dial-multiscreen-org:dial:application:';
+
+
+/** @private @const {!RegExp} */
+PresentationUrl.APP_NAME_REGEX_ =
+ /__dialAppName__=([A-Za-z0-9-._~!$&'()*+,;=%]+)/;
+
+
+/** @private @const {!RegExp} */
+PresentationUrl.LAUNCH_PARAM_REGEX_ = /__dialPostData__=([A-Za-z0-9]+={0,2})/;
+
+
+exports = PresentationUrl;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/presentation_url_test.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/presentation_url_test.js
new file mode 100644
index 00000000000..4477e5ee404
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/presentation_url_test.js
@@ -0,0 +1,75 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('PresentationUrlTest');
+goog.setTestOnly('PresentationUrlTest');
+
+const PresentationUrl = goog.require('mr.dial.PresentationUrl');
+
+describe('Tests PresentationUrl', function() {
+ it('Does not create from empty input', function() {
+ expect(PresentationUrl.create('')).toBeNull();
+ });
+
+ it('Creates from a valid URL', function() {
+ expect(PresentationUrl.create(
+ 'https://www.youtube.com/tv#__dialAppName__=YouTube'))
+ .toEqual(new PresentationUrl('YouTube'));
+ });
+
+ it('Creates from a valid URL with launch parameters', function() {
+ expect(PresentationUrl.create(
+ 'https://www.youtube.com/tv#' +
+ '__dialAppName__=YouTube/__dialPostData__=dj0xMjM='))
+ .toEqual(new PresentationUrl('YouTube', 'v=123'));
+ expect(PresentationUrl.create(
+ 'https://www.youtube.com/tv#' +
+ '__dialAppName__=YouTube/__dialPostData__=dj1NSnlKS3d6eEZwWQ=='))
+ .toEqual(new PresentationUrl('YouTube', 'v=MJyJKwzxFpY'));
+ });
+
+ it('Does not create from an invalid URL', function() {
+ expect(PresentationUrl.create(
+ 'https://www.youtube.com/tv#___emanPpaLiad__=YouTube'))
+ .toBeNull();
+ });
+
+ it('Does not create from an invalid postData', function() {
+ expect(PresentationUrl.create(
+ 'https://www.youtube.com/tv#___emanPpaLiad__=YouTube' +
+ '/__dialPostData__=dj1=N'))
+ .toBeNull();
+ });
+
+ it('Creates from DIAL URL', () => {
+ expect(PresentationUrl.create('dial:YouTube'))
+ .toEqual(new PresentationUrl('YouTube'));
+ expect(PresentationUrl.create('dial:YouTube?foo=bar'))
+ .toEqual(new PresentationUrl('YouTube'));
+ expect(PresentationUrl.create('dial:YouTube?foo=bar&postData=dj0xMjM='))
+ .toEqual(new PresentationUrl('YouTube', 'v=123'));
+ expect(PresentationUrl.create('dial:YouTube?postData=dj0xMjM%3D'))
+ .toEqual(new PresentationUrl('YouTube', 'v=123'));
+ });
+
+ it('Does not create from invalid DIAL URL', () => {
+ expect(PresentationUrl.create('dial:')).toBeNull();
+ expect(PresentationUrl.create('dial://')).toBeNull();
+ expect(PresentationUrl.create('dial://YouTube')).toBeNull();
+ expect(
+ PresentationUrl.create('dial:YouTube?postData=notEncodedProperly111'))
+ .toBeNull();
+ });
+
+ it('Does not create from URL of unknown protocol', () => {
+ expect(PresentationUrl.create('unknown:YouTube')).toBeNull();
+ });
+
+ it('getPresentationUrl returns DIAL presentation URLs', () => {
+ expect(PresentationUrl.getPresentationUrlAsString('YouTube', null))
+ .toEqual('dial:YouTube');
+ expect(PresentationUrl.getPresentationUrlAsString('YouTube', 'dj0xMjM='))
+ .toEqual('dial:YouTube?postData=dj0xMjM%3D');
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/sink_app_status.js b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/sink_app_status.js
new file mode 100644
index 00000000000..5acd9a9a8fa
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/providers/dial/sink_app_status.js
@@ -0,0 +1,23 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview The availability of an app on a sink.
+ */
+
+goog.provide('mr.dial.SinkAppStatus');
+
+
+/**
+ * Tracks the availability of an app on a sink. Apps start out in an
+ * UNKNOWN status and are changed to AVAILABLE or UNAVAILABLE once the status is
+ * known, i.e. after we query the sink for the app.
+ *
+ * @enum {string}
+ */
+mr.dial.SinkAppStatus = {
+ AVAILABLE: 'available',
+ UNAVAILABLE: 'unavailable',
+ UNKNOWN: 'unknown'
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/analytics.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/analytics.js
new file mode 100644
index 00000000000..8761cc404d7
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/analytics.js
@@ -0,0 +1,305 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/** @fileoverview API for Analytics events. */
+
+goog.provide('mr.Analytics');
+goog.provide('mr.LongTiming');
+goog.provide('mr.MediumTiming');
+goog.provide('mr.Timing');
+
+goog.require('mr.Logger');
+
+
+/**
+ * Begins the timing period.
+ */
+mr.Timing = class {
+ /**
+ * @param {!string} name
+ */
+ constructor(name) {
+ /** @private {!string} */
+ this.name_ = name;
+
+ /** @private {number} */
+ this.startTime_ = Date.now();
+ }
+
+ /**
+ * Gets the full name with the suffix appended if provided.
+ * @param {string} name The name of the event or histogram.
+ * @param {string=} opt_suffix The optional suffix to add.
+ * @return {string} The full name with suffix added.
+ * @private
+ */
+ getFullName_(name, opt_suffix) {
+ if (opt_suffix != null) {
+ name += '_' + opt_suffix;
+ }
+ return name;
+ }
+
+ /**
+ * Sets the name for the timing object.
+ * @param {string} name The new name.
+ */
+ setName(name) {
+ this.name_ = name;
+ }
+
+ /**
+ * Ends the timing period and reports to UMA.
+ * @param {string=} opt_suffix An optional suffix.
+ */
+ end(opt_suffix) {
+ const duration = Date.now() - this.startTime_;
+ const name = this.getFullName_(this.name_, opt_suffix);
+ mr.Timing.recordDuration(name, duration);
+ }
+
+ /**
+ * Sends a short duration value (up to 10 seconds) for analytics collection.
+ * @param {string} name
+ * @param {number} duration Duration in milliseconds.
+ */
+ static recordDuration(name, duration) {
+ if (duration < 0) {
+ mr.Timing.logger_.warning('Timing analytics event with negative time');
+ duration = 0;
+ }
+
+ if (duration > mr.Timing.TEN_SECONDS_) {
+ duration = mr.Timing.TEN_SECONDS_;
+ }
+
+ try {
+ chrome.metricsPrivate.recordTime(name, duration);
+ } catch (e) {
+ mr.Timing.logger_.warning(
+ 'Failed to record time ' + duration + ' in ' + name);
+ }
+ }
+};
+
+
+/** @private const */
+mr.Timing.logger_ = mr.Logger.getInstance('mr.Timing');
+
+
+/**
+ * Ten seconds in milliseconds.
+ * @const {number}
+ * @private
+ */
+mr.Timing.TEN_SECONDS_ = 10 * 1000;
+
+
+/**
+ * Begins a medium timing period (should be measured in seconds).
+ */
+mr.MediumTiming = class extends mr.Timing {
+ /**
+ * @param {string} name The histogram name.
+ */
+ constructor(name) {
+ super(name);
+ }
+
+ /**
+ * @override
+ */
+ end(opt_suffix) {
+ const duration = Date.now() - this.startTime_;
+ const name = this.getFullName_(this.name_, opt_suffix);
+ mr.MediumTiming.recordDuration(name, duration);
+ }
+
+ /**
+ * Sends a medium duration value (up to 3 minutes) for analytics collection.
+ * @param {string} name
+ * @param {number} duration Duration in milliseconds.
+ */
+ static recordDuration(name, duration) {
+ if (duration < 0) {
+ mr.MediumTiming.logger_.warning(
+ 'Timing analytics event with negative time');
+ return;
+ }
+
+ if (duration < mr.Timing.TEN_SECONDS_) {
+ duration = mr.Timing.TEN_SECONDS_;
+ }
+
+ if (duration > mr.MediumTiming.THREE_MINUTES_) {
+ duration = mr.MediumTiming.THREE_MINUTES_;
+ }
+
+ try {
+ chrome.metricsPrivate.recordMediumTime(name, duration);
+ } catch (e) {
+ mr.MediumTiming.logger_.warning(
+ 'Failed to record time ' + duration + ' in ' + name);
+ }
+ }
+};
+
+
+/** @private @const */
+mr.MediumTiming.logger_ = mr.Logger.getInstance('mr.MediumTiming');
+
+
+/**
+ * Constant of 3 minutes (in milliseconds).
+ * @private @const {number}
+ **/
+mr.MediumTiming.THREE_MINUTES_ = 3 * 60 * 1000;
+
+
+/**
+ * Begins a long timing period (up to 1 hour).
+ */
+mr.LongTiming = class extends mr.Timing {
+ /**
+ * @param {string} name The name of the histogram.
+ */
+ constructor(name) {
+ super(name);
+ }
+
+ /**
+ * @override
+ */
+ end(opt_suffix) {
+ const duration = Date.now() - this.startTime_;
+ const name = this.getFullName_(this.name_, opt_suffix);
+ mr.LongTiming.recordDuration(name, duration);
+ }
+
+ /**
+ * Sends a long duration value (up to 1 hour) for analytics collection.
+ * @param {string} name
+ * @param {number} duration Duration in milliseconds.
+ */
+ static recordDuration(name, duration) {
+ if (duration < 0) {
+ mr.LongTiming.logger_.warning(
+ 'Timing analytics event with negative time');
+ return;
+ }
+
+ if (duration < mr.MediumTiming.THREE_MINUTES_) {
+ duration = mr.MediumTiming.THREE_MINUTES_;
+ }
+
+ if (duration > mr.LongTiming.ONE_HOUR_) {
+ duration = mr.LongTiming.ONE_HOUR_;
+ }
+
+ try {
+ chrome.metricsPrivate.recordLongTime(name, duration);
+ } catch (e) {
+ mr.LongTiming.logger_.warning(
+ 'Failed to record time ' + duration + ' in ' + name);
+ }
+ }
+};
+
+
+/** @private @const */
+mr.LongTiming.logger_ = mr.Logger.getInstance('mr.LongTiming');
+
+
+/**
+ * Constant of 1 hour (in milliseconds).
+ * @private @const {number}
+ **/
+mr.LongTiming.ONE_HOUR_ = 60 * 60 * 1000;
+
+
+/** @const {*} */
+mr.Analytics = {};
+
+
+/**
+ * @const {mr.Logger}
+ * @private
+ */
+mr.Analytics.logger_ = mr.Logger.getInstance('mr.Analytics');
+
+
+/**
+ * Sends a user action for analytics collection.
+ * @param {!string} name
+ */
+mr.Analytics.recordEvent = function(name) {
+ try {
+ chrome.metricsPrivate.recordUserAction(name);
+ } catch (e) {
+ mr.Analytics.logger_.warning('Failed to record event ' + name);
+ }
+};
+
+
+/**
+ * Send a value for analytics collection.
+ * @param {!string} name
+ * @param {!number} value
+ * @param {!Object<string,number>} values
+ */
+mr.Analytics.recordEnum = function(name, value, values) {
+ let foundKey;
+ let size = 0;
+ for (let key in values) {
+ size++;
+ if (values[key] == value) {
+ foundKey = key;
+ }
+ }
+ if (!foundKey) {
+ mr.Analytics.logger_.error(
+ 'Unknown analytics value, ' + value + ' for histogram, ' + name,
+ Error() /* for stack trace */);
+ return;
+ }
+
+ const config = {
+ 'metricName': name,
+ 'type': 'histogram-linear',
+ 'min': 1,
+ 'max': size,
+ // Add one for the underflow bucket.
+ 'buckets': size + 1
+ };
+
+ try {
+ chrome.metricsPrivate.recordValue(config, value);
+ } catch (/** Error */ e) {
+ mr.Analytics.logger_.warning(
+ 'Failed to record enum value ' + foundKey + ' (' + value + ') in ' +
+ name,
+ e);
+ }
+};
+
+
+/**
+ * Records a small count (0 to 100) for analytics collection.
+ * @param {string} name
+ * @param {number} count
+ */
+mr.Analytics.recordSmallCount = function(name, count) {
+ try {
+ if (count < 0) {
+ throw new Error(`Invalid count for ${name}: ${count}`);
+ } else if (count > 100) {
+ mr.Analytics.logger_.warning(
+ `Small count for ${name} exceeded limits: ${count}`, Error());
+ }
+ chrome.metricsPrivate.recordSmallCount(name, count);
+ } catch (/** Error */ e) {
+ mr.Analytics.logger_.warning(
+ `Failed to record small count ${name} (${count})`, e);
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/analytics_test.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/analytics_test.js
new file mode 100644
index 00000000000..e9f6ee2274c
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/analytics_test.js
@@ -0,0 +1,246 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.setTestOnly();
+goog.require('mr.Analytics');
+goog.require('mr.LongTiming');
+goog.require('mr.MediumTiming');
+goog.require('mr.MockClock');
+goog.require('mr.Timing');
+
+describe('Tests Analytics', function() {
+ let mockClock;
+
+ const TEN_SECONDS = 10 * 1000;
+ const THREE_MINUTES = 3 * 60 * 1000;
+ const ONE_HOUR = 60 * 60 * 1000;
+
+ beforeEach(function() {
+ mockClock = new mr.MockClock(true);
+ chrome.metricsPrivate = {
+ recordTime: jasmine.createSpy('recordTime'),
+ recordMediumTime: jasmine.createSpy('recordMediumTime'),
+ recordLongTime: jasmine.createSpy('recordLongTime'),
+ recordUserAction: jasmine.createSpy('recordUserAction'),
+ recordValue: jasmine.createSpy('recordValue'),
+ recordSmallCount: jasmine.createSpy('recordSmallCount'),
+ };
+ });
+
+ afterEach(function() {
+ mockClock.uninstall();
+ });
+
+ describe('Test Timing Events', function() {
+ describe('Test mr.Timing', function() {
+ it('Should record the time passing', function() {
+ const histogramName = 'Test';
+ const timeToPass = 34;
+ const timing = new mr.Timing(histogramName);
+ mockClock.tick(timeToPass);
+ timing.end();
+ expect(chrome.metricsPrivate.recordTime.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordTime)
+ .toHaveBeenCalledWith(histogramName, timeToPass);
+ });
+ it('Should record the time passing with a suffix', function() {
+ const histogramName = 'Test';
+ const suffixName = 'Test';
+ const expectedFinalName = histogramName + '_' + suffixName;
+ const timeToPass = 34;
+ const timing = new mr.Timing(histogramName);
+ mockClock.tick(timeToPass);
+ timing.end(suffixName);
+ expect(chrome.metricsPrivate.recordTime.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordTime)
+ .toHaveBeenCalledWith(expectedFinalName, timeToPass);
+ });
+ it('Should record the max if duration exceeds ten seconds', function() {
+ const histogramName = 'Test';
+ const timeToPass = TEN_SECONDS + 1;
+ const timing = new mr.Timing(histogramName);
+ mockClock.tick(timeToPass);
+ timing.end();
+ expect(chrome.metricsPrivate.recordTime.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordTime)
+ .toHaveBeenCalledWith(histogramName, TEN_SECONDS);
+ });
+ it('Should record the minimum if duration is negative', function() {
+ const histogramName = 'Test';
+ const timeToPass = -1;
+ const timing = new mr.Timing(histogramName);
+ mockClock.tick(timeToPass);
+ timing.end();
+ expect(chrome.metricsPrivate.recordTime.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordTime)
+ .toHaveBeenCalledWith(histogramName, 0);
+ });
+ });
+ describe('Test mr.MediumTiming', function() {
+ it('Should record the time passing', function() {
+ const histogramName = 'Test';
+ const timeToPass = 34 * 1000;
+ const timing = new mr.MediumTiming(histogramName);
+ mockClock.tick(timeToPass);
+ timing.end();
+ expect(chrome.metricsPrivate.recordMediumTime.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordMediumTime)
+ .toHaveBeenCalledWith(histogramName, timeToPass);
+ });
+ it('Should record the time passing with a suffix', function() {
+ const histogramName = 'Test';
+ const suffixName = 'Test';
+ const expectedFinalName = histogramName + '_' + suffixName;
+ const timeToPass = 34 * 1000;
+ const timing = new mr.MediumTiming(histogramName);
+ mockClock.tick(timeToPass);
+ timing.end(suffixName);
+ expect(chrome.metricsPrivate.recordMediumTime.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordMediumTime)
+ .toHaveBeenCalledWith(expectedFinalName, timeToPass);
+ });
+ it('Should record ten seconds if duration is below ten seconds',
+ function() {
+ const histogramName = 'Test';
+ const timeToPass = 34;
+ const timing = new mr.MediumTiming(histogramName);
+ mockClock.tick(timeToPass);
+ timing.end();
+ expect(chrome.metricsPrivate.recordMediumTime.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordMediumTime)
+ .toHaveBeenCalledWith(histogramName, TEN_SECONDS);
+ });
+ it('Should record three minutes if duration exceeds three minutes',
+ function() {
+ const histogramName = 'Test';
+ const timeToPass = THREE_MINUTES + 1;
+ const timing = new mr.MediumTiming(histogramName);
+ mockClock.tick(timeToPass);
+ timing.end();
+ expect(chrome.metricsPrivate.recordMediumTime.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordMediumTime)
+ .toHaveBeenCalledWith(histogramName, THREE_MINUTES);
+ });
+ });
+ describe('Test mr.LongTiming', function() {
+ it('Should record the time passing', function() {
+ const histogramName = 'Test';
+ const timeToPass = 34 * 60 * 1000;
+ const timing = new mr.LongTiming(histogramName);
+ mockClock.tick(timeToPass);
+ timing.end();
+ expect(chrome.metricsPrivate.recordLongTime.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordLongTime)
+ .toHaveBeenCalledWith(histogramName, timeToPass);
+ });
+ it('Should record the time passing with a suffix', function() {
+ const histogramName = 'Test';
+ const suffixName = 'Test';
+ const expectedFinalName = histogramName + '_' + suffixName;
+ const timeToPass = 34 * 60 * 1000;
+ const timing = new mr.LongTiming(histogramName);
+ mockClock.tick(timeToPass);
+ timing.end(suffixName);
+ expect(chrome.metricsPrivate.recordLongTime.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordLongTime)
+ .toHaveBeenCalledWith(expectedFinalName, timeToPass);
+ });
+ it('Should record three minutes if duration is below three minutes',
+ function() {
+ const histogramName = 'Test';
+ const timeToPass = 2 * 60 * 1000;
+ const timing = new mr.LongTiming(histogramName);
+ mockClock.tick(timeToPass);
+ timing.end();
+ expect(chrome.metricsPrivate.recordLongTime.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordLongTime)
+ .toHaveBeenCalledWith(histogramName, THREE_MINUTES);
+ });
+ it('Should record one hour if duration exceeds one hour', function() {
+ const histogramName = 'Test';
+ const timeToPass = ONE_HOUR + 1;
+ const timing = new mr.LongTiming(histogramName);
+ mockClock.tick(timeToPass);
+ timing.end();
+ expect(chrome.metricsPrivate.recordLongTime.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordLongTime)
+ .toHaveBeenCalledWith(histogramName, ONE_HOUR);
+ });
+ it('Should not record the time if it we went back in time', function() {
+ const histogramName = 'Test';
+ const timeToPass = -1;
+ const timing = new mr.LongTiming(histogramName);
+ mockClock.tick(timeToPass);
+ timing.end();
+ expect(chrome.metricsPrivate.recordLongTime.calls.count()).toBe(0);
+ });
+ });
+ });
+ describe('Test recordEvent', function() {
+ it('Should record an event', function() {
+ const eventName = 'Test';
+ mr.Analytics.recordEvent(eventName);
+ expect(chrome.metricsPrivate.recordUserAction.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordUserAction)
+ .toHaveBeenCalledWith(eventName);
+ });
+ });
+ describe('Test recordEnum', function() {
+ const testHistogram = 'Test';
+ const testValues = {TEST1: 0, TEST2: 1, TEST3: 2};
+ const testConfig = {
+ 'metricName': testHistogram,
+ 'type': 'histogram-linear',
+ 'min': 1,
+ 'max': 3,
+ 'buckets': 4
+ };
+ it('Should record an event with corrct index of 0', function() {
+ mr.Analytics.recordEnum(testHistogram, testValues.TEST1, testValues);
+ expect(chrome.metricsPrivate.recordValue.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordValue)
+ .toHaveBeenCalledWith(testConfig, 0);
+ });
+ it('Should record an event with corrct index of 1', function() {
+ mr.Analytics.recordEnum(testHistogram, testValues.TEST2, testValues);
+ expect(chrome.metricsPrivate.recordValue.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordValue)
+ .toHaveBeenCalledWith(testConfig, 1);
+ });
+ it('Should record an event with correct index of 2', function() {
+ mr.Analytics.recordEnum(testHistogram, testValues.TEST3, testValues);
+ expect(chrome.metricsPrivate.recordValue.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordValue)
+ .toHaveBeenCalledWith(testConfig, 2);
+ });
+ it('Should not record an event with an unknown value', function() {
+ mr.Analytics.recordEnum(testHistogram, 3, testValues);
+ expect(chrome.metricsPrivate.recordValue.calls.count()).toBe(0);
+ });
+ });
+ describe('Test recordSmallCount', () => {
+ it('Record 0 count succeeds', () => {
+ mr.Analytics.recordSmallCount('smallCount', 0);
+ expect(chrome.metricsPrivate.recordSmallCount.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordSmallCount)
+ .toHaveBeenCalledWith('smallCount', 0);
+ });
+ it('Record negative count fails', () => {
+ mr.Analytics.recordSmallCount('smallCount', -1);
+ expect(chrome.metricsPrivate.recordSmallCount.calls.count()).toBe(0);
+ });
+ it('Record large count succeeds', () => {
+ mr.Analytics.recordSmallCount('smallCount', 200);
+ expect(chrome.metricsPrivate.recordSmallCount.calls.count()).toBe(1);
+ expect(chrome.metricsPrivate.recordSmallCount)
+ .toHaveBeenCalledWith('smallCount', 200);
+ });
+ it('Record regular count succeeds', () => {
+ mr.Analytics.recordSmallCount('smallCount', 1);
+ mr.Analytics.recordSmallCount('smallCount', 50);
+ mr.Analytics.recordSmallCount('smallCount', 100);
+ expect(chrome.metricsPrivate.recordSmallCount.calls.count()).toBe(3);
+ });
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/assertions.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/assertions.js
new file mode 100644
index 00000000000..93abbb7d8db
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/assertions.js
@@ -0,0 +1,95 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Various assert-like functions.
+ */
+goog.module('mr.Assertions');
+goog.module.declareLegacyNamespace();
+
+const Config = goog.require('mr.Config');
+
+
+/**
+ * Given an unknown value, return it if it is an Error, or return a new Error
+ * otherwise. Note that unlike other methods in the module, it does not throw
+ * exceptions.
+ *
+ * @param {*} err The purported error
+ * @param {string=} opt_message The message used to construct an Error if |err|
+ * is not an error.
+ * @return {!Error}
+ */
+exports.toError = function(err, opt_message) {
+ if (err instanceof Error) {
+ return err;
+ } else {
+ return Error(opt_message || `Expected an Error value, got ${err}`);
+ }
+};
+
+
+/**
+ * Represents an assertion failure.
+ */
+const AssertionError = class extends Error {
+ /**
+ * @param {string=} message Error message.
+ */
+ constructor(message = '') {
+ super();
+ this.name = 'AssertionError';
+ this.message = message;
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, AssertionError);
+ } else {
+ this.stack = new Error().stack;
+ }
+ }
+};
+
+
+/**
+ * Checks if the condition evaluates to true if mr.Config.isDebugChannel is
+ * true.
+ * @template T
+ * @param {T} condition The condition to check.
+ * @param {string=} message Error message if condition evaluates to false.
+ * @throws {AssertionError} When the condition evaluates to false.
+ * @return {T} The condition.
+ */
+exports.assert = function(condition, message = undefined) {
+ if (Config.isDebugChannel && !condition) {
+ throw new AssertionError(message);
+ }
+ return condition;
+};
+
+
+/**
+ * Checks that a value is a string if mr.Config.isDebugChannel is true.
+ * @param {*} value The value to check
+ * @param {string=} message The message
+ * @return {string} The value
+ * @throws {AssertionError} if the value is not a string
+ */
+exports.assertString = function(value, message = undefined) {
+ if (Config.isDebugChannel && typeof value !== 'string') {
+ throw new AssertionError();
+ }
+ return /** @type {string} */ (value);
+};
+
+
+/**
+ * Returns a Promise that rejects with 'Not implemented' as the error
+ * message.
+ * @template T
+ * @return {!Promise<T>}
+ */
+exports.rejectNotImplemented = function() {
+ return Promise.reject(new Error('Not implemented'));
+};
+
+exports.AssertionError = AssertionError;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/base64.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/base64.js
new file mode 100644
index 00000000000..5d8b3d1ed08
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/base64.js
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.base64');
+
+/**
+ * @const {!Map<string, string>}
+ */
+const URL_SAFE_MAP = new Map().set('+', '-').set('/', '_').set('=', '.');
+
+
+/**
+ * @const {!Map<string, string>}
+ */
+const INVERSE_URL_SAFE_MAP =
+ new Map().set('-', '+').set('_', '/').set('.', '=');
+
+
+/**
+ * Decodes a base64 string using either the normal or URL-safe alphabet.
+ * @param {string} encoded
+ * @return {string}
+ */
+function decodeString(encoded) {
+ return atob(encoded.replace(/[-_.]/g, c => INVERSE_URL_SAFE_MAP.get(c)));
+}
+
+
+/**
+ * Encodes an array of byte values in base64.
+ * @param {!Array<number>} data An array of byte values.
+ * @param {boolean} urlSafe If true, uses a URL-safe base64 alphabet.
+ * @return {string} The encoded data.
+ */
+function encodeArray(data, urlSafe) {
+ const encoded = btoa(String.fromCharCode(...data));
+ return urlSafe ? encoded.replace(/[+/=]/g, c => URL_SAFE_MAP.get(c)) :
+ encoded;
+}
+
+
+exports = {
+ decodeString,
+ encodeArray,
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/base64_test.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/base64_test.js
new file mode 100644
index 00000000000..07240a4f82d
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/base64_test.js
@@ -0,0 +1,72 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.base64.test');
+goog.setTestOnly();
+
+const {encodeArray, decodeString} = goog.require('mr.base64');
+
+/**
+ * Converts a ASCII string to an array of bytes.
+ * @param {string} s
+ * @return {!Array<number>}
+ */
+function strBytes(s) {
+ return s.split('').map(c => c.codePointAt(0));
+}
+
+describe('mr.base64.encodeArray', () => {
+ it('encodes well-known values correctly', () => {
+ expect(encodeArray(strBytes(''))).toBe('');
+ expect(encodeArray(strBytes('f'))).toBe('Zg==');
+ expect(encodeArray(strBytes('fo'))).toBe('Zm8=');
+ expect(encodeArray(strBytes('foo'))).toBe('Zm9v');
+ expect(encodeArray(strBytes('foob'))).toBe('Zm9vYg==');
+ expect(encodeArray(strBytes('fooba'))).toBe('Zm9vYmE=');
+ expect(encodeArray(strBytes('foobar'))).toBe('Zm9vYmFy');
+ expect(
+ encodeArray(strBytes(
+ '\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89\xe5\x9b\x9b\xe4\xba\x94\xe5' +
+ '\x85\xad\xe4\xb8\x83\xe5\x85\xab\xe4\xb9\x9d\xe5\x8d\x81')))
+ .toBe('5LiA5LqM5LiJ5Zub5LqU5YWt5LiD5YWr5Lmd5Y2B');
+ expect(encodeArray(strBytes('>>>???>>>???=/+')))
+ .toBe('Pj4+Pz8/Pj4+Pz8/PS8r');
+ });
+
+ it('handles the urlSafe parameter correctly', () => {
+ expect(encodeArray(strBytes('f'), true)).toBe('Zg..');
+ expect(encodeArray(strBytes('fo'), true)).toBe('Zm8.');
+ expect(encodeArray(strBytes('foo'), true)).toBe('Zm9v');
+ expect(encodeArray(strBytes('foob'), true)).toBe('Zm9vYg..');
+ expect(encodeArray(strBytes('fooba'), true)).toBe('Zm9vYmE.');
+ expect(encodeArray(strBytes('foobar'), true)).toBe('Zm9vYmFy');
+ expect(encodeArray(strBytes('>>>???>>>???=/+'), true))
+ .toBe('Pj4-Pz8_Pj4-Pz8_PS8r');
+ });
+
+ it('decodes correctly with the standard alphabet', () => {
+ expect(decodeString('')).toBe('');
+ expect(decodeString('Zg==')).toBe('f');
+ expect(decodeString('Zm8=')).toBe('fo');
+ expect(decodeString('Zm9v')).toBe('foo');
+ expect(decodeString('Zm9vYg==')).toBe('foob');
+ expect(decodeString('Zm9vYmE=')).toBe('fooba');
+ expect(decodeString('Zm9vYmFy')).toBe('foobar');
+ expect(decodeString('5LiA5LqM5LiJ5Zub5LqU5YWt5LiD5YWr5Lmd5Y2B'))
+ .toBe(
+ '\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89\xe5\x9b\x9b\xe4\xba\x94\xe5' +
+ '\x85\xad\xe4\xb8\x83\xe5\x85\xab\xe4\xb9\x9d\xe5\x8d\x81');
+ expect(decodeString('Pj4+Pz8/Pj4+Pz8/PS8r')).toBe('>>>???>>>???=/+');
+ });
+
+ it('decodes correctly with the URL-safe alphabet', () => {
+ expect(decodeString('Zg..')).toBe('f');
+ expect(decodeString('Zm8.')).toBe('fo');
+ expect(decodeString('Zm9v')).toBe('foo');
+ expect(decodeString('Zm9vYg..')).toBe('foob');
+ expect(decodeString('Zm9vYmE.')).toBe('fooba');
+ expect(decodeString('Zm9vYmFy')).toBe('foobar');
+ expect(decodeString('Pj4-Pz8_Pj4-Pz8_PS8r')).toBe('>>>???>>>???=/+');
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/device_counts.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/device_counts.js
new file mode 100644
index 00000000000..d8f7392dc69
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/device_counts.js
@@ -0,0 +1,18 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.DeviceCounts');
+goog.module.declareLegacyNamespace();
+
+
+/**
+ * A struct to hold a snapshot of device counts for a sink discovery service.
+ * @typedef {{
+ * availableDeviceCount: number,
+ * knownDeviceCount: number
+ * }}
+ */
+let DeviceCounts;
+
+exports = DeviceCounts;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/device_counts_provider.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/device_counts_provider.js
new file mode 100644
index 00000000000..a76ecc07702
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/device_counts_provider.js
@@ -0,0 +1,23 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.DeviceCountsProvider');
+goog.module.declareLegacyNamespace();
+
+const DeviceCounts = goog.require('mr.DeviceCounts');
+
+/**
+ * Implemented by services that are capable of providing counts of devices that
+ * they manage.
+ * @record
+ */
+const DeviceCountsProvider = class {
+ /**
+ * Returns the device counts currently known to the service.
+ * @return {!DeviceCounts}
+ */
+ getDeviceCounts() {}
+};
+
+exports = DeviceCountsProvider;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/event_analytics.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/event_analytics.js
new file mode 100644
index 00000000000..f9fbde5e388
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/event_analytics.js
@@ -0,0 +1,57 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/** @fileoverview Analytics for events. */
+
+goog.provide('mr.EventAnalytics');
+goog.provide('mr.EventAnalytics.Event');
+
+goog.require('mr.Analytics');
+
+/**
+ * Possible event types that can wake the event page. Keep names in sync with
+ * extensions/browser/extension_event_histogram_value.h in Chromium and values
+ * in sync with MediaRouterWakeEventType in
+ * google3/analysis/uma/configs/chrome/histograms.xml.
+ *
+ * @enum {number}
+ */
+mr.EventAnalytics.Event = {
+ // Special value meaning the event page was woken by the Media Router and not
+ // a regular extension event.
+ MEDIA_ROUTER: 0,
+ CAST_CHANNEL_ON_ERROR: 1,
+ CAST_CHANNEL_ON_MESSAGE: 2,
+ DIAL_ON_DEVICE_LIST: 3,
+ DIAL_ON_ERROR: 4,
+ GCM_ON_MESSAGE: 5,
+ IDENTITY_ON_SIGN_IN_CHANGED: 6,
+ MDNS_ON_SERVICE_LIST: 7,
+ NETWORKING_PRIVATE_ON_NETWORKS_CHANGED: 8,
+ NETWORKING_PRIVATE_ON_NETWORK_LIST_CHANGED: 9,
+ PROCESSES_ON_UPDATED: 10,
+ RUNTIME_ON_MESSAGE: 11,
+ RUNTIME_ON_MESSAGE_EXTERNAL: 12,
+ SETTINGS_PRIVATE_ON_PREFS_CHANGED: 13,
+ TABS_ON_UPDATED: 14,
+};
+
+/**
+ * @private {mr.EventAnalytics.Event} The event that woke the event page.
+ */
+mr.EventAnalytics.firstEvent_;
+
+/**
+ * Records an event handler invocation in the event page. If it is the first
+ * event that woke the page, a histogram is recorded. Subsequent events are a
+ * no-op.
+ *
+ * @param {!mr.EventAnalytics.Event} eventType The event type.
+ */
+mr.EventAnalytics.recordEvent = function(eventType) {
+ if (mr.EventAnalytics.firstEvent_ != undefined) return;
+ mr.Analytics.recordEnum(
+ 'MediaRouter.Provider.WakeEvent', eventType, mr.EventAnalytics.Event);
+ mr.EventAnalytics.firstEvent_ = eventType;
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/event_analytics_test.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/event_analytics_test.js
new file mode 100644
index 00000000000..5be671d2ccb
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/event_analytics_test.js
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.setTestOnly();
+goog.require('mr.Analytics');
+goog.require('mr.EventAnalytics');
+
+describe('Tests EventAnalytics', () => {
+
+ beforeEach(() => {
+ mr.EventAnalytics.firstEvent_ = undefined;
+ });
+
+ describe('Test recordEvent', () => {
+ it('should record only the first event', () => {
+ spyOn(mr.Analytics, 'recordEnum');
+ mr.EventAnalytics.recordEvent(mr.EventAnalytics.Event.DIAL_ON_ERROR);
+ mr.EventAnalytics.recordEvent(mr.EventAnalytics.Event.TABS_ON_UPDATED);
+ expect(mr.Analytics.recordEnum.calls.count()).toEqual(1);
+ expect(mr.Analytics.recordEnum)
+ .toHaveBeenCalledWith(
+ 'MediaRouter.Provider.WakeEvent',
+ mr.EventAnalytics.Event.DIAL_ON_ERROR, mr.EventAnalytics.Event);
+ });
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/event_target.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/event_target.js
new file mode 100644
index 00000000000..8fbae73ddf1
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/event_target.js
@@ -0,0 +1,69 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.EventTarget');
+goog.module.declareLegacyNamespace();
+
+
+/** @final */
+class EventTarget {
+ constructor() {
+ /** @private @const {!Array<!Subscription>} */
+ this.subscriptions_ = [];
+ }
+
+ /**
+ * @param {string} type The event type id.
+ * @param {function(this:T, ?)} handler Callback
+ * @param {T=} target
+ * @template T
+ */
+ listen(type, handler, target = undefined) {
+ this.subscriptions_.push({type, handler, target});
+ }
+
+ /**
+ * @param {string} type The event type id.
+ * @param {function(this:T, ?)} handler Callback
+ * @param {T=} target
+ * @template T
+ */
+ unlisten(type, handler, target = undefined) {
+ const index = this.subscriptions_.findIndex(
+ sub =>
+ sub.type == type && sub.handler == handler && sub.target == target);
+ if (index != -1) {
+ this.subscriptions_.splice(index, 1);
+ }
+ }
+
+ /**
+ * @param {{type: string}} event
+ */
+ dispatchEvent(event) {
+ this.subscriptions_.forEach(sub => {
+ if (sub.type == event.type) {
+ // Call handler asynchronously so exceptions don't show up at the source
+ // of the event.
+ Promise.resolve().then(() => sub.handler.call(sub.target, event));
+ }
+ });
+ }
+}
+
+
+/** @record */
+const Subscription = class {};
+
+/** @type {string} */
+Subscription.prototype.type;
+
+/** @type {function(?)} */
+Subscription.prototype.handler;
+
+/** @type {Object|undefined} */
+Subscription.prototype.target;
+
+
+exports = EventTarget;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/fixed_size_queue.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/fixed_size_queue.js
new file mode 100644
index 00000000000..7f8cb7c23a1
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/fixed_size_queue.js
@@ -0,0 +1,126 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview FIFO with a hard limit on its size.
+
+ */
+
+goog.module('mr.FixedSizeQueue');
+goog.module.declareLegacyNamespace();
+
+
+/**
+ * A fixed-sized buffer with FIFO semantics.
+ * @template T
+ */
+class FixedSizeQueue {
+ /**
+ * @param {number} maxSize The size of the buffer.
+ */
+ constructor(maxSize) {
+ if (maxSize <= 0) {
+ throw Error('invalid buffer size');
+ }
+
+ /**
+ * Items are popped from here. Elements are stored in reverse
+ * insertion order.
+ * @private @type {!Array<T>}
+ */
+ this.head_ = [];
+
+ /**
+ * Items are pushed here. Elements are stored in insertion order.
+ * @private @type {!Array<T>}
+ */
+ this.tail_ = [];
+
+ /**
+ * @private @const
+ */
+ this.maxSize_ = maxSize;
+ }
+
+ /**
+ * Adds an item to the buffer. Drops the last added item if the
+ * buffer is full.
+ * @param {T} item
+ */
+ enqueue(item) {
+ if (this.getCount() >= this.maxSize_) {
+ this.dequeue();
+ }
+ this.tail_.push(item);
+ }
+
+ /**
+ * Removes the oldest item from the buffer, which must be non-empty.
+ * @return {T} The removed item.
+ */
+ dequeue() {
+ if (this.isEmpty()) {
+ throw Error('Empty queue');
+ }
+ if (this.head_.length == 0) {
+ this.head_ = this.tail_;
+ this.head_.reverse();
+ this.tail_ = [];
+ }
+ return this.head_.pop();
+ }
+
+ /**
+ * Removes and returns all items in the buffer in insertion order.
+ * @return {!Array<T>}
+ */
+ dequeueAll() {
+ const result = this.getValues();
+ this.clear();
+ return result;
+ }
+
+ /**
+ * @return {number} The number of items in the buffer.
+ */
+ getCount() {
+ return this.head_.length + this.tail_.length;
+ }
+
+ /**
+ * @return {boolean} True if the buffer is full.
+ */
+ isFull() {
+ return this.getCount() == this.maxSize_;
+ }
+
+ /**
+ * @return {boolean} True if the buffer is empty.
+ */
+ isEmpty() {
+ return this.getCount() == 0;
+ }
+
+ /**
+ * Gets all the items in the buffer in insertion order.
+ * @return {!Array<T>}
+ */
+ getValues() {
+ const result = this.head_.slice(); // clones array
+ result.reverse();
+ result.push(...this.tail_);
+ return result;
+ }
+
+ /**
+ * Makes the buffer empty.
+ */
+ clear() {
+ this.head_ = [];
+ this.tail_ = [];
+ }
+}
+
+
+exports = FixedSizeQueue;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/fixed_size_queue_test.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/fixed_size_queue_test.js
new file mode 100644
index 00000000000..09f0bff6ae9
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/fixed_size_queue_test.js
@@ -0,0 +1,74 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.require('mr.FixedSizeQueue');
+
+describe('mr.FixedSizeQueue', function() {
+ let queue;
+
+ beforeEach(function() {
+ queue = new mr.FixedSizeQueue(3);
+ });
+
+ it('works', function() {
+ expect(queue.getCount()).toBe(0);
+ expect(queue.isFull()).toBe(false);
+ expect(queue.isEmpty()).toBe(true);
+ expect(queue.getValues()).toEqual([]);
+
+ queue.enqueue(1);
+ expect(queue.getCount()).toBe(1);
+ expect(queue.isFull()).toBe(false);
+ expect(queue.isEmpty()).toBe(false);
+ expect(queue.getValues()).toEqual([1]);
+
+ queue.enqueue(2);
+ queue.enqueue(3);
+ expect(queue.getCount()).toBe(3);
+ expect(queue.isFull()).toBe(true);
+ expect(queue.isEmpty()).toBe(false);
+ expect(queue.getValues()).toEqual([1, 2, 3]);
+
+ queue.enqueue(4);
+ expect(queue.getCount()).toBe(3);
+ expect(queue.isFull()).toBe(true);
+ expect(queue.isEmpty()).toBe(false);
+ expect(queue.getValues()).toEqual([2, 3, 4]);
+
+ queue.clear();
+ expect(queue.getCount()).toBe(0);
+ expect(queue.isFull()).toBe(false);
+ expect(queue.isEmpty()).toBe(true);
+ expect(queue.getValues()).toEqual([]);
+ });
+
+ describe('when full', function() {
+ beforeEach(function() {
+ queue.enqueue(1);
+ queue.enqueue(2);
+ queue.enqueue(3);
+ expect(queue.getCount()).toBe(3);
+ expect(queue.isFull()).toBe(true);
+ });
+
+ it('supports dequeue', function() {
+ expect(queue.dequeue()).toBe(1);
+ expect(queue.getCount()).toBe(2);
+ expect(queue.getValues()).toEqual([2, 3]);
+ expect(queue.dequeue()).toBe(2);
+ expect(queue.getCount()).toBe(1);
+ expect(queue.getValues()).toEqual([3]);
+ expect(queue.dequeue()).toBe(3);
+ expect(queue.getCount()).toBe(0);
+ expect(queue.getValues()).toEqual([]);
+ expect(queue.isFull()).toBe(false);
+ expect(queue.isEmpty()).toBe(true);
+ });
+
+ it('supports deqeueAll', function() {
+ expect(queue.dequeueAll()).toEqual([1, 2, 3]);
+ expect(queue.isEmpty()).toBe(true);
+ });
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/logger.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/logger.js
new file mode 100644
index 00000000000..49b214a4b90
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/logger.js
@@ -0,0 +1,261 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.provide('mr.Logger');
+
+goog.require('mr.Assertions');
+goog.require('mr.Config');
+
+
+/**
+ * An object for recording logs.
+ */
+mr.Logger = class {
+ /**
+ * @param {string} name
+ */
+ constructor(name) {
+ /**
+ * @private @const {string}
+ */
+ this.name_ = name;
+ }
+
+ /**
+ * @param {string} name
+ * @return {!mr.Logger}
+ */
+ static getInstance(name) {
+ let instance = mr.Logger.instances_.get(name);
+ if (!instance) {
+ instance = new mr.Logger(name);
+ mr.Logger.instances_.set(name, instance);
+ }
+ return instance;
+ }
+
+ /**
+ * @param {function(!mr.Logger.Record)} handler
+ */
+ static addHandler(handler) {
+ mr.Logger.handlers_.push(handler);
+ }
+
+ /**
+ * Logs a pre-built record if its level is high enough.
+ * @param {!mr.Logger.Record} record
+ */
+ static logRecord(record) {
+ if (record.level >= mr.Logger.level) {
+ mr.Logger.handlers_.forEach(handler => handler(record));
+ }
+ }
+
+ /**
+ * Logs a message at the specified log level with an optional exception.
+ *
+ * @param {mr.Logger.Level} level
+ * @param {mr.Logger.Loggable} message
+ * @param {*=} exception An exception to associate with the log message. This
+ * should normally be an Error instance for best results, but any type is
+ * acceptable.
+ */
+ log(level, message, exception = undefined) {
+ if (level < mr.Logger.level) {
+ return;
+ }
+
+ // Logging will occur at the current logging level. If the message is a
+ // lazy-evaluated one, eval now.
+ if (typeof message == 'function') {
+ message = message();
+ }
+
+ // For non-debug builds, make an effort to programmatically scrub message
+ // text that potentially contains personally-identifying information.
+ // However, note that this only covers some of the more-obvious forms of
+ // PII, and no heuristic can ever hope to provide 100% safety. Also, some of
+ // the regular expressions may sometimes match more or less than what was
+ // intended.
+ mr.Assertions.assert(
+ typeof message == 'string', 'Expected message to be a string.');
+ if (!mr.Config.isDebugChannel) {
+ message = message.replace(mr.Logger.URL_REGEXP_, '[Redacted URL]');
+ message = message.replace(
+ mr.Logger.DOMAIN_OR_EMAIL_REGEXP_, '[Redacted domain/email]');
+ message = message.replace(mr.Logger.SINK_ID_REGEXP_, (match, p1, p2) => {
+ return p1 + ':<' + p2.substr(-4) + '>';
+ });
+ }
+
+ const record = {
+ logger: this.name_,
+ level: level,
+ time: Date.now(),
+ message: message,
+ exception: exception,
+ };
+ mr.Logger.handlers_.forEach(handler => handler(record));
+ }
+
+ /**
+ * @param {mr.Logger.Loggable} message
+ * @param {*=} exception
+ */
+ error(message, exception = undefined) {
+ this.log(mr.Logger.Level.SEVERE, message, exception);
+ }
+
+ /**
+ * @param {mr.Logger.Loggable} message
+ * @param {*=} exception
+ */
+ warning(message, exception = undefined) {
+ this.log(mr.Logger.Level.WARNING, message, exception);
+ }
+
+ /**
+ * @param {mr.Logger.Loggable} message
+ * @param {*=} exception
+ */
+ info(message, exception = undefined) {
+ this.log(mr.Logger.Level.INFO, message, exception);
+ }
+
+ /**
+ * @param {mr.Logger.Loggable} message
+ * @param {*=} exception
+ */
+ fine(message, exception = undefined) {
+ this.log(mr.Logger.Level.FINE, message, exception);
+ }
+
+ /**
+ * @param {mr.Logger.Level} level
+ * @return {string}
+ */
+ static levelToString(level) {
+ return mr.Logger.LEVEL_NAMES_[level];
+ }
+
+ /**
+ * @param {string} levelName
+ * @param {mr.Logger.Level} defaultLevel
+ * @return {mr.Logger.Level}
+ */
+ static stringToLevel(levelName, defaultLevel) {
+ const index = mr.Logger.LEVEL_NAMES_.indexOf(levelName);
+ return index == -1 ? defaultLevel : /** @type {mr.Logger.Level} */ (index);
+ }
+
+ /**
+ * Converts a numeric log level (as used in the Closure library) into a log
+ * level constant.
+ * @param {number} levelValue
+ * @return {mr.Logger.Level}
+ */
+ static numberToLevel(levelValue) {
+ if (levelValue <= 600) {
+ return mr.Logger.Level.FINE;
+ } else if (levelValue <= 850) {
+ return mr.Logger.Level.INFO;
+ } else if (levelValue <= 950) {
+ return mr.Logger.Level.WARNING;
+ } else {
+ return mr.Logger.Level.SEVERE;
+ }
+ }
+};
+
+
+/**
+ * @private @const {!Array<function(mr.Logger.Record)>}
+ */
+mr.Logger.handlers_ = [];
+
+
+/**
+ * @private @const {!Map<string, !mr.Logger>}
+ */
+mr.Logger.instances_ = new Map();
+
+
+/**
+ * The available log levels.
+ * @enum {number}
+ */
+mr.Logger.Level = {
+ FINE: 0,
+ INFO: 1,
+ WARNING: 2,
+ SEVERE: 3,
+};
+
+
+/**
+ * The canonical names of log levels in ascending order of severity.
+ * @private const {!Array<string>}
+ */
+mr.Logger.LEVEL_NAMES_ = ['FINE', 'INFO', 'WARNING', 'SEVERE'];
+
+
+/**
+ * A regular expression that matches a very broad-range of text that looks like
+ * it could be a domain name or an e-mail address.
+ * @private const {!RegExp}
+ */
+mr.Logger.DOMAIN_OR_EMAIL_REGEXP_ =
+ /(([\w.+-]+@)|((www|m|mail|ftp)[.]))[\w.-]+[.][\w-]{2,4}/gi;
+
+
+/**
+ * A regular expression that matches a very broad-range of text that looks like
+ * it could be an URL.
+ * @private const {!RegExp}
+ */
+mr.Logger.URL_REGEXP_ = /(data:|https?:\/\/)\S+/gi;
+
+
+/**
+ * A regular expression that matches a very broad-range of text that looks like
+ * it could be a sink ID.
+ * @private const {!RegExp}
+ */
+mr.Logger.SINK_ID_REGEXP_ = /(dial|cast):<([a-zA-Z0-9]+)>/gi;
+
+
+/**
+ * An abstract represenation of a log message.
+ *
+ * The `time` field should be in the format returned by `Date.now()`. The
+ * `exception` field will typically be an Error instance, but code that handles
+ * log records must be prepared to handle any type.
+ *
+ * @typedef {{
+ * level: mr.Logger.Level,
+ * logger: string,
+ * time: number,
+ * message: string,
+ * exception: *,
+ * }}
+ */
+mr.Logger.Record;
+
+
+/**
+ * @typedef {string|function():string}
+ */
+mr.Logger.Loggable;
+
+
+/**
+ * @const
+ */
+mr.Logger.DEFAULT_LEVEL = mr.Logger.Level.INFO;
+
+
+/**
+ * @type {mr.Logger.Level}
+ */
+mr.Logger.level = mr.Logger.DEFAULT_LEVEL;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/logger_test.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/logger_test.js
new file mode 100644
index 00000000000..bb5048642ad
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/logger_test.js
@@ -0,0 +1,166 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.LoggerTest');
+goog.setTestOnly('mr.LoggerTest');
+
+const Config = goog.require('mr.Config');
+const Logger = goog.require('mr.Logger');
+
+describe('Test mr.Logger', function() {
+ let originalLevel;
+ let logger;
+
+ beforeEach(() => {
+ originalLevel = Logger.level;
+ logger = new Logger('test');
+ });
+
+ afterEach(() => {
+ Logger.level = originalLevel;
+ Logger.handlers_ = [];
+ });
+
+ it('logs string messages only at INFO and above', () => {
+ Logger.level = Logger.Level.WARNING;
+ const loggedMessages = [];
+ Logger.addHandler(record => {
+ expect(record.level).not.toBeLessThan(Logger.Level.WARNING);
+ expect(record.logger).toEqual('test');
+ expect(typeof record.time).toBe('number');
+ expect(typeof record.message).toBe('string');
+ loggedMessages.push(record.message);
+ });
+
+ logger.fine('Should not log this message.');
+ logger.info('Should not log this message either.');
+ logger.warning('Should log this warning message.');
+ logger.error('Should log this error message.');
+
+ expect(loggedMessages).toEqual([
+ 'Should log this warning message.', 'Should log this error message.'
+ ]);
+ });
+
+ it('logs lazy-evaluated messages', () => {
+ Logger.level = Logger.Level.FINE;
+ const loggedMessages = [];
+ Logger.addHandler(record => {
+ expect(record.level).not.toBeLessThan(Logger.Level.FINE);
+ expect(record.logger).toEqual('test');
+ expect(typeof record.time).toBe('number');
+ expect(typeof record.message).toBe('string');
+ loggedMessages.push(record.message);
+ });
+
+ logger.fine(() => 'Should log this fine message.');
+ logger.info(() => 'Should log this info message.');
+ logger.warning(() => 'Should log this warning message.');
+ logger.error(() => 'Should log this error message.');
+
+ expect(loggedMessages).toEqual([
+ 'Should log this fine message.', 'Should log this info message.',
+ 'Should log this warning message.', 'Should log this error message.'
+ ]);
+ });
+
+ describe('Personally-identifying info scrubbbing tests', () => {
+ let loggedMessages;
+ let isDebugChannelDefault = Config.isDebugChannel;
+
+ beforeEach(() => {
+ Config.isDebugChannel = false;
+ loggedMessages = [];
+ Logger.level = Logger.Level.FINE;
+ Logger.addHandler(record => {
+ expect(typeof record.message).toBe('string');
+ loggedMessages.push(record.message);
+ });
+ });
+
+ afterEach(() => {
+ Config.isDebugChannel = isDebugChannelDefault;
+ });
+
+ it('does not scrub non-PII from messages', () => {
+ // Things that shouldn't be scrubbed.
+ logger.info('');
+ logger.info('42');
+ logger.info(
+ 'Found sink with id: ac6982d68e687faf6ebf8cc (Chromecast Ultra)');
+ logger.info('The event occurred at 20:21:22 on 29 Mar 2017.');
+
+ expect(loggedMessages).toEqual([
+ '', '42',
+ 'Found sink with id: ac6982d68e687faf6ebf8cc (Chromecast Ultra)',
+ 'The event occurred at 20:21:22 on 29 Mar 2017.'
+ ]);
+ });
+
+ it('scrubs domains', () => {
+ // Things that look like domain names.
+ logger.info('Visiting www.google.com...');
+ logger.info('Tab favicon domain is: ftp.myfilez.net');
+ // The following example shows the RegExp currently used does not match
+ // against all possible domains perfectly.
+ logger.info(
+ 'mail.personaldata.security.biz mapped to ' +
+ 'personaldata.security.biz.');
+
+ expect(loggedMessages).toEqual([
+ 'Visiting [Redacted domain/email]...',
+ 'Tab favicon domain is: [Redacted domain/email]',
+ '[Redacted domain/email] mapped to personaldata.security.biz.'
+ ]);
+ });
+
+ it('scrubs email addresses', () => {
+ // Things that look like e-mail addresses.
+ logger.info('Reply to nobody@love-spam.net, and see what happens.');
+ logger.info(
+ 'This CL was written by somebody@developers.chromium.org, ' +
+ 'or was it somebody@hooli.com?');
+
+ expect(loggedMessages).toEqual([
+ 'Reply to [Redacted domain/email], and see what happens.',
+ 'This CL was written by [Redacted domain/email], or was it ' +
+ '[Redacted domain/email]?'
+ ]);
+ });
+
+ it('scrubs URLs', () => {
+ // Things that look like URLs.
+ logger.info(
+ 'Downloading from http://www.pictures.com/gifs/' +
+ 'kittens%20falling%20of%20furniture.png...');
+ logger.info('Page navigation detected: https://youtube.com/profile');
+ logger.info(
+ 'Relpacing content with: ' +
+ 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D');
+
+ expect(loggedMessages).toEqual([
+ 'Downloading from [Redacted URL]',
+ 'Page navigation detected: [Redacted URL]',
+ 'Relpacing content with: [Redacted URL]'
+ ]);
+ });
+
+ it('scrubs sink IDs', () => {
+ logger.info(
+ 'Sink has pending connection' +
+ ' dial:<05f5e10100641000bc6f90f1aaa0bd90>');
+ logger.info(
+ 'Adding new session: cast:<de51d94921f15f8af6dbf65592bb3610>, ' +
+ '5d85e5da-b773-4382-ba06-43c2a6dc6ba6');
+ logger.info('Connecting to (id 1) rf72niQ3FPe8VpTz_tIEGNSkfGUo.');
+ logger.info('');
+
+ expect(loggedMessages).toEqual([
+ 'Sink has pending connection dial:<bd90>',
+ 'Adding new session: cast:<3610>, 5d85e5da-b773-4382-ba06-43c2a6dc6ba6',
+ 'Connecting to (id 1) rf72niQ3FPe8VpTz_tIEGNSkfGUo.', ''
+ ]);
+ });
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/media_source_utils.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/media_source_utils.js
new file mode 100644
index 00000000000..e0392b46cf4
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/media_source_utils.js
@@ -0,0 +1,127 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview The media source URN related utilities methods.
+ */
+
+goog.provide('mr.MediaSourceUtils');
+
+goog.require('mr.Config');
+
+
+
+
+/**
+ * @param {string} sourceUrn
+ * @return {boolean} True if it is a mirror URN.
+ */
+mr.MediaSourceUtils.isMirrorSource = function(sourceUrn) {
+ return mr.MediaSourceUtils.isTabMirrorSource(sourceUrn) ||
+ mr.MediaSourceUtils.isDesktopMirrorSource(sourceUrn);
+};
+
+
+/**
+ * @param {string} sourceUrn
+ * @return {boolean} True if it is a two UA mode presentation source.
+ * A presentation source has a sourceUrn that is a valid uri and
+ * is not a Cast custom receiver app.
+ */
+mr.MediaSourceUtils.isPresentationSource = function(sourceUrn) {
+
+ if (!sourceUrn.startsWith('http:') && !sourceUrn.startsWith('https:')) {
+ return false;
+ }
+ // Use the DOM to parse sourceUrn.
+ const link = document.createElement('a');
+ link.href = sourceUrn;
+ // Protocol must be http or https.
+ if (link.protocol != 'http:' && link.protocol != 'https:') {
+ return false;
+ }
+
+ // Must not be a custom Cast receiver app.
+ return link.hash.indexOf(mr.MediaSourceUtils.CAST_APP_ID_) == -1;
+};
+
+
+
+/** @const {string} */
+mr.MediaSourceUtils.CAST_STREAMING_APP_ID = '0F5096E8';
+
+
+/** @const {string} */
+mr.MediaSourceUtils.TAB_MIRROR_URN_PREFIX =
+ 'urn:x-org.chromium.media:source:tab:';
+
+/** @const {string} */
+mr.MediaSourceUtils.TAB_REMOTING_URN_PREFIX =
+ 'urn:x-org.chromium.media:source:tab_content_remoting:';
+
+/** @const {string} */
+mr.MediaSourceUtils.DESKTOP_MIRROR_URN =
+ 'urn:x-org.chromium.media:source:desktop';
+
+
+/** @private @const {string} */
+mr.MediaSourceUtils.CAST_APP_ID_ = '__castAppId__';
+
+
+/**
+ * @private @const {!Array<string>}
+ */
+mr.MediaSourceUtils.MIRROR_APP_ID_ORIGIN_WHITELIST_ = [
+ 'https://docs.google.com', // slides
+];
+
+
+/**
+ * @param {string} sourceUrn
+ * @return {?Array<string>} array of origins whitelisted for the sourceUrn or
+ * |null| if any origin is allowed for the sourceUrn.
+ */
+mr.MediaSourceUtils.getWhitelistedOrigins = function(sourceUrn) {
+ if (mr.Config.isDebugChannel &&
+ window.localStorage['debug.allowAllOrigins']) {
+ return null;
+ }
+ return sourceUrn.indexOf(mr.MediaSourceUtils.CAST_STREAMING_APP_ID) != -1 ?
+ mr.MediaSourceUtils.MIRROR_APP_ID_ORIGIN_WHITELIST_ :
+ null;
+};
+
+
+/**
+ * @param {string} sourceUrn
+ * @return {boolean} True if it is a tab mirror URN.
+ */
+mr.MediaSourceUtils.isTabMirrorSource = function(sourceUrn) {
+ return sourceUrn.startsWith(mr.MediaSourceUtils.TAB_MIRROR_URN_PREFIX) ||
+ sourceUrn.indexOf(mr.MediaSourceUtils.CAST_STREAMING_APP_ID) != -1;
+};
+
+
+/**
+ * @param {string} sourceUrn
+ * @return {boolean} True if it is a desktop mirror URN.
+ */
+mr.MediaSourceUtils.isDesktopMirrorSource = function(sourceUrn) {
+ return sourceUrn == mr.MediaSourceUtils.DESKTOP_MIRROR_URN;
+};
+
+
+/**
+ * Get the tab ID from |sourceUrn|. Returns null if the sourceUrn is not a tab
+ * mirror URN or if it doesn't contain a valid tab ID.
+ * @param {string} sourceUrn
+ * @return {?number}
+ */
+mr.MediaSourceUtils.getMirrorTabId = function(sourceUrn) {
+ const pos = sourceUrn.search(mr.MediaSourceUtils.TAB_MIRROR_URN_PREFIX);
+ if (pos == -1) return null;
+ const tabIdStr =
+ sourceUrn.substr(pos + mr.MediaSourceUtils.TAB_MIRROR_URN_PREFIX.length);
+ return parseInt(tabIdStr, 10) || null;
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/media_source_utils_test.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/media_source_utils_test.js
new file mode 100644
index 00000000000..9647921874f
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/media_source_utils_test.js
@@ -0,0 +1,66 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.require('mr.MediaSourceUtils');
+
+describe('Tests MediaSourceUtils', function() {
+ describe('Tests isTabMirrorSource', function() {
+ it('should return true for tab mirror source', function() {
+ expect(mr.MediaSourceUtils.isTabMirrorSource(
+ 'urn:x-org.chromium.media:source:tab:2'))
+ .toBe(true);
+ expect(mr.MediaSourceUtils.isTabMirrorSource(
+ 'urn:x-org.chromium.media:source:tab:666'))
+ .toBe(true);
+ });
+
+ it('should return false for non tab mirror source', function() {
+ expect(mr.MediaSourceUtils.isTabMirrorSource(
+ 'urn:x-org.chromium.media:source:desktop'))
+ .toBe(false);
+ });
+ });
+
+ describe('Tests isPresentationSource', function() {
+ it('should return true for presentation source', function() {
+ expect(mr.MediaSourceUtils.isPresentationSource('http://www.google.com'))
+ .toBe(true);
+ expect(mr.MediaSourceUtils.isPresentationSource('https://www.google.com'))
+ .toBe(true);
+ });
+
+ it('should return false for non tab mirror source', function() {
+ expect(
+ mr.MediaSourceUtils.isPresentationSource('Invalid media source urn'))
+ .toBe(false);
+ });
+
+ it('should return false for cast receiver app', function() {
+ expect(mr.MediaSourceUtils.isPresentationSource(
+ 'http://www.google.com/cast#__castAppId__=deadbeef'))
+ .toBe(false);
+ });
+ });
+
+ describe('Tests getMirrorTabId', function() {
+ it('should return null for non tab mirror source or invalid ID',
+ function() {
+ expect(mr.MediaSourceUtils.getMirrorTabId('http://www.google.com'))
+ .toBeNull();
+ expect(mr.MediaSourceUtils.getMirrorTabId(
+ 'urn:x-org.chromium.media:source:tab:'))
+ .toBeNull();
+ });
+
+ it('should return correct ID for correct tab mirror source', function() {
+ expect(mr.MediaSourceUtils.getMirrorTabId(
+ 'urn:x-org.chromium.media:source:tab:2'))
+ .toBe(2);
+ expect(mr.MediaSourceUtils.getMirrorTabId(
+ 'urn:x-org.chromium.media:source:tab:666'))
+ .toBe(666);
+ });
+ });
+
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/mock_clock.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/mock_clock.js
new file mode 100644
index 00000000000..dbb165cfb14
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/mock_clock.js
@@ -0,0 +1,391 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.setTestOnly('mr.MockClock');
+goog.provide('mr.MockClock');
+
+goog.require('mr.MockPromise');
+
+
+/**
+ * Class for unit testing code that uses setTimeout, clearTimeout, etc.
+ * @final
+ */
+mr.MockClock = class {
+ /**
+ * Installs the MockClock by overriding the global object's
+ * implementation of setTimeout, setInterval, clearTimeout and
+ * clearInterval.
+ */
+ constructor() {
+ if (window.setTimeout !== mr.MockClock.REAL_SETTIMEOUT_) {
+ throw Error('MockClock already installed.');
+ }
+
+ /**
+ * List of times to fire, sorted in reverse order of when they
+ * will be executed.
+ *
+ * @type {!Array<mr.MockClock.Timeout_>}
+ * @private
+ */
+ this.queue_ = [];
+
+ /**
+ * The current simulated time in milliseconds.
+ * @type {number}
+ * @private
+ */
+ this.nowMillis_ = 0;
+
+ mr.MockClock.installedHere_ = Error('MockClock was installed here.');
+
+ window.setTimeout = this.setTimeout_.bind(this);
+ window.setInterval = this.setInterval_.bind(this);
+ window.setImmediate = this.setImmediate_.bind(this);
+ window.clearTimeout = this.clearTimeout_.bind(this);
+ window.clearInterval = this.clearTimeout_.bind(this);
+ Date.now = this.getCurrentTime_.bind(this);
+ }
+
+ /**
+ * Removes the MockClock's hooks into the global object's functions
+ * and revert to their original values.
+ */
+ uninstall() {
+ if (window.setTimeout === mr.MockClock.REAL_SETTIMEOUT_) {
+ throw Error('MockClock not installed.');
+ }
+
+ mr.MockClock.installedHere_ = null;
+
+ window.setTimeout = mr.MockClock.REAL_SETTIMEOUT_;
+ window.setInterval = mr.MockClock.REAL_SETINTERVAL_;
+ window.setImmediate = mr.MockClock.REAL_SETIMMEDIATE_;
+ window.clearTimeout = mr.MockClock.REAL_CLEARTIMEOUT_;
+ window.clearInterval = mr.MockClock.REAL_CLEARINTERVAL_;
+ Date.now = mr.MockClock.REAL_DATENOW_;
+ }
+
+ /**
+ * Restores this clock to the state it was in just after it was
+ * created.
+ */
+ reset() {
+ this.queue_ = [];
+ this.nowMillis_ = 0;
+ }
+
+ /**
+ * Increments the MockClock's time by a given number of
+ * milliseconds, running any functions that are now overdue.
+ * @param {number=} millis Number of milliseconds to increment the
+ * counter. If not specified, clock ticks 1 millisecond.
+ * @return {number} Current mock time in milliseconds.
+ */
+ tick(millis = 1) {
+ const endTime = this.nowMillis_ + millis;
+ this.runFunctionsWithinRange_(endTime);
+ this.nowMillis_ = endTime;
+ return endTime;
+ }
+
+ /**
+ * Ticks the clock until there are no more actions scheduled to run.
+ */
+ flush() {
+ this.tick(Infinity);
+ }
+
+ /**
+ * Takes a promise and then ticks the mock clock. If the promise
+ * successfully resolves, returns the value produced by the
+ * promise. If the promise is rejected, it throws the rejection as
+ * an exception. If the promise is not resolved at all, throws an
+ * exception. Also ticks the general clock by the specified amount.
+ *
+ * @param {!mr.MockPromise<T>} promise A promise that should be
+ * resolved after the mockClock is ticked for the given
+ * opt_millis.
+ * @param {number=} millis Number of milliseconds to increment the
+ * counter. If not specified, clock ticks 1 millisecond.
+ * @return {T}
+ * @template T
+ */
+ tickPromise(promise, millis = 1) {
+ let value;
+ let error;
+ let resolved = false;
+ promise.then(
+ v => {
+ value = v;
+ resolved = true;
+ },
+ e => {
+ error = e;
+ resolved = true;
+ });
+ this.tick(millis);
+ if (!resolved) {
+ throw new Error(
+ 'Promise was expected to be resolved ' +
+ 'after mock clock tick.');
+ }
+ if (error) {
+ throw error;
+ }
+ return value;
+ }
+
+ /**
+ * Takes a promise and then ticks the mock clock. If the promise
+ * rejects, returns the error produced by the promise. If the
+ * promise is rejected, it throws the rejection as an exception. If
+ * the promise is not rejected at all, throws an exception. Also
+ * ticks the general clock by the specified amount.
+ *
+ * @param {!mr.MockPromise<T>} promise A promise that should be
+ * rejected after the mockClock is ticked for the given
+ * opt_millis.
+ * @param {number=} millis Number of milliseconds to increment the
+ * counter. If not specified, clock ticks 1 millisecond.
+ * @return {*} Error produced by the promise.
+ * @template T
+ */
+ tickRejectingPromise(promise, millis = 1) {
+ let error;
+ let rejected = false;
+ promise.catch(e => {
+ error = e;
+ rejected = true;
+ });
+ this.tick(millis);
+ if (!rejected) {
+ throw new Error(
+ 'Promise was expected to be rejected after mock clock tick.');
+ }
+ return error;
+ }
+
+ /**
+ * @return {number} The MockClock's current time in milliseconds.
+ * @private
+ */
+ getCurrentTime_() {
+ return this.nowMillis_;
+ }
+
+ /**
+ * Runs any function that is scheduled before a certain time.
+ * @param {number} endTime The latest time in the range, in
+ * milliseconds.
+ * @private
+ */
+ runFunctionsWithinRange_(endTime) {
+ mr.MockPromise.callPendingHandlers();
+
+ // Repeatedly pop off the last item since the queue is always
+ // sorted.
+ while (this.queue_ && this.queue_.length &&
+ this.queue_[this.queue_.length - 1].runAtMillis <= endTime) {
+ const timeout = this.queue_.pop();
+ // Only move time forwards.
+ this.nowMillis_ = Math.max(this.nowMillis_, timeout.runAtMillis);
+ if (timeout.recurring) {
+ // Reschedule before calling the function so that if the
+ // function deletes the timeout, it's in the queue to be
+ // removed.
+ this.scheduleFunction_(
+ timeout.timeoutKey, timeout.funcToCall, timeout.millis, true);
+ }
+ timeout.funcToCall.call(undefined);
+ mr.MockPromise.callPendingHandlers();
+ }
+ }
+
+ /**
+ * Schedules a function to be run at a certain time.
+ * @param {number} timeoutKey The timeout key.
+ * @param {!Function} funcToCall The function to call.
+ * @param {number} millis The number of milliseconds to call it in.
+ * @param {boolean} recurring Whether to function call should recur.
+ * @private
+ */
+ scheduleFunction_(timeoutKey, funcToCall, millis, recurring) {
+ const timeout = {
+ runAtMillis: this.nowMillis_ + millis,
+ funcToCall: funcToCall,
+ recurring: recurring,
+ timeoutKey: timeoutKey,
+ millis: millis,
+ };
+
+ // Insert a timer descriptor into a descending-order queue.
+ //
+ // Later-inserted duplicates appear at lower indices. For
+ // example, the asterisk in (5,4,*,3,2,1) would be the insertion
+ // point for 3. (The numbers here refer to timestamps.)
+ //
+ // Insertion of N items is quadratic, but unit tests are normally
+ // small, so scalability is not a primary issue.
+ //
+ // Since the queue is in reverse order (so we can pop rather than
+ // unshift), and later timers with the same time stamp should be
+ // executed later, we look for the element strictly greater than
+ // the one we are inserting.
+ let i;
+ for (i = this.queue_.length; i != 0; i--) {
+ if (this.queue_[i - 1].runAtMillis > timeout.runAtMillis) {
+ break;
+ }
+ this.queue_[i] = this.queue_[i - 1];
+ }
+ this.queue_[i] = timeout;
+ }
+
+ /**
+ * Schedules a function to be called after `millis`
+ * milliseconds. Mock implementation for setTimeout.
+ * @param {!Function} funcToCall The function to call.
+ * @param {number=} millis The number of milliseconds to call it
+ * after.
+ * @param {...*} args Arguments to pass to the function.
+ * @return {number} The number of timeouts created.
+ * @private
+ */
+ setTimeout_(funcToCall, millis = 0, ...args) {
+ if (millis > mr.MockClock.MAX_INT_) {
+ throw Error(`Bad timeout value: ${millis}`);
+ }
+ this.scheduleFunction_(
+ mr.MockClock.nextId_, funcToCall.bind(undefined, ...args), millis,
+ false);
+ return mr.MockClock.nextId_++;
+ }
+
+ /**
+ * Schedules a function to be called every `millis` milliseconds.
+ * Mock implementation for setInterval.
+ * @param {!Function} funcToCall The function to call.
+ * @param {number=} millis The number of milliseconds between calls.
+ * @param {...*} args Arguments to pass to the function.
+ * @return {number} The number of timeouts created.
+ * @private
+ */
+ setInterval_(funcToCall, millis = 0, ...args) {
+ this.scheduleFunction_(
+ mr.MockClock.nextId_, funcToCall.bind(undefined, ...args), millis,
+ true);
+ return mr.MockClock.nextId_++;
+ }
+
+ /**
+ * Schedules a function to be called immediately after the current JS
+ * execution.
+ * Mock implementation for setImmediate.
+ * @param {!Function} funcToCall The function to call.
+ * @param {...*} args Arguments to pass to the function.
+ * @return {number} The number of timeouts created.
+ * @private
+ */
+ setImmediate_(funcToCall, ...args) {
+ return this.setTimeout_(funcToCall, 0, ...args);
+ }
+
+ /**
+ * Clears a timeout.
+ * Mock implementation for clearTimeout and clearInterval.
+ * @param {number} timeoutKey The timeout key to clear.
+ * @private
+ */
+ clearTimeout_(timeoutKey) {
+ const newQueue =
+ this.queue_.filter(timeout => timeout.timeoutKey != timeoutKey);
+ if (newQueue.length == this.queue_.length_) {
+ // The real versions of clearTimeout and clearInterval silently
+ // ignore invalid keys, but we hold ourselves to a higher
+ // standard :-)
+ throw Error('Invalid timeoutKey');
+ }
+ this.queue_ = newQueue;
+ }
+};
+
+
+/**
+ * ID to use for next timeout. Timeout IDs must never be reused, even
+ * across MockClock instances.
+ * @private {number}
+ */
+mr.MockClock.nextId_ = 0;
+
+
+/**
+ * @private @const
+ */
+mr.MockClock.REAL_SETTIMEOUT_ = window.setTimeout;
+
+
+/**
+ * @private @const
+ */
+mr.MockClock.REAL_SETINTERVAL_ = window.setInterval;
+
+
+/**
+ * @private @const
+ */
+mr.MockClock.REAL_SETIMMEDIATE_ = window.setImmediate;
+
+
+/**
+ * @private @const
+ */
+mr.MockClock.REAL_CLEARTIMEOUT_ = window.clearTimeout;
+
+
+/**
+ * @private @const
+ */
+mr.MockClock.REAL_CLEARINTERVAL_ = window.clearInterval;
+
+
+/**
+ * @private @const
+ */
+mr.MockClock.REAL_DATENOW_ = Date.now;
+
+
+/**
+ * Maximum 32-bit signed integer.
+ *
+ * Timeouts over this time return immediately in many browsers, due to
+ * integer overflow. Such known browsers include Firefox, Chrome, and
+ * Safari, but not IE.
+ *
+ * @type {number}
+ * @private
+ */
+mr.MockClock.MAX_INT_ = 2147483647;
+
+
+/**
+ * @typedef {{
+ * runAtMillis: number,
+ * funcToCall: !Function,
+ * recurring: boolean,
+ * timeoutKey: number,
+ * millis: number,
+ * }}
+ * @private
+ */
+mr.MockClock.Timeout_;
+
+
+/**
+ * Exception used to record where the current MockClock was created. Helpful
+ * for diagnosing unit tests that fail to uninstall their mock clocks.
+ * @private {Error}
+ */
+mr.MockClock.installedHere_ = null;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/mock_promise.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/mock_promise.js
new file mode 100644
index 00000000000..511ddbdec0a
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/mock_promise.js
@@ -0,0 +1,602 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.provide('mr.MockPromise');
+goog.setTestOnly('mr.MockPromise');
+
+
+/**
+ * Why does this class exist?
+ *
+ * Most of our unit tests that involve promises are written in "synchronous
+ * style". In this style, everything happens in one iteration of the JS event
+ * loop. With native promises, this style isn't possible, because a call like
+ * p.then(f) won't run f until at least the next event loop iteration, even if p
+ * is already resolved.
+ *
+ * When the tests were originally written, they relied on a particular
+ * interaction between goog.testing.MockClock and goog.Promise, where calling
+ * mockClock.tick() would force any code scheduled by goog.Promise to be
+ * executed immediately. Since then, the non-test code has been changed to use
+ * native promises, and the test code has been changed to use this class and
+ * mr.MockClock, but the same principle applies.
+ *
+ * The long-term plan is to convert the tests to use asynchronous style, and get
+ * rid of this class and mr.MockClock entirely.
+ *
+ * @template TYPE
+ * @final
+ */
+mr.MockPromise = class {
+ /**
+ * @param {function(
+ * function((TYPE|mr.MockPromise<TYPE>)=),
+ * function(*=)): void} resolver
+ */
+ constructor(resolver) {
+ /**
+ * The internal state of this Promise. Either PENDING, FULFILLED, REJECTED,
+ * or BLOCKED.
+ * @private {mr.MockPromise.State_}
+ */
+ this.state_ = mr.MockPromise.State_.PENDING;
+
+ /**
+ * The settled result of the Promise. Immutable once set with either a
+ * fulfillment value or rejection reason.
+ * @private {*}
+ */
+ this.result_ = undefined;
+
+ /**
+ * The linked list of `onFulfilled` and `onRejected` callbacks
+ * added to this Promise by calls to {@code then()}.
+ * @private {!Array<!mr.MockPromise.CallbackEntry_>}
+ */
+ this.callbackEntries_ = [];
+
+ /**
+ * Whether the Promise is in the queue of Promises to execute.
+ * @private {boolean}
+ */
+ this.executing_ = false;
+
+ /**
+ * A boolean that is set if the Promise is rejected, and reset to false if
+ * an `onRejected` callback is invoked for the Promise (or one of its
+ * descendants). If the rejection is not handled before the next timestep,
+ * the rejection reason is passed to the unhandled rejection handler.
+ * @private {boolean}
+ */
+ this.hadUnhandledRejection_ = false;
+
+ try {
+ resolver.call(
+ null,
+ value => {
+ this.resolve_(mr.MockPromise.State_.FULFILLED, value);
+ },
+ reason => {
+ try {
+ // Promise was rejected. Step up one call frame to see why.
+ if (reason instanceof Error) {
+ throw reason;
+ } else {
+ throw new Error('Promise rejected.');
+ }
+ } catch (e) {
+ // Only thrown so browser dev tools can catch rejections of
+ // promises when the option to break on caught exceptions is
+ // activated.
+ }
+ this.resolve_(mr.MockPromise.State_.REJECTED, reason);
+ });
+ } catch (e) {
+ this.resolve_(mr.MockPromise.State_.REJECTED, e);
+ }
+ }
+
+ /**
+ * Replaces the native Promise class with this class.
+ */
+ static install() {
+ if (window.Promise !== mr.MockPromise.origPromise_) {
+ throw Error('Error installing mr.MockPromise');
+ }
+ if (mr.MockPromise.pendingHandlers_.length) {
+ throw Error('Expected no pending handlers.');
+ }
+ window.Promise = mr.MockPromise;
+ mr.MockPromise.ignoreUnhandledRejections = false;
+ }
+
+ /**
+ * Undoes the effect of calling install().
+ */
+ static uninstall() {
+ if (window.Promise !== mr.MockPromise) {
+ throw Error('mr.MockPromise not installed');
+ }
+ if (mr.MockPromise.pendingHandlers_.length) {
+ console.warn('Discarding pending handlers.');
+ mr.MockPromise.pendingHandlers_.length = 0;
+ // throw Error('Expected no pending handlers.');
+ }
+ window.Promise = mr.MockPromise.origPromise_;
+ }
+
+ /**
+ * @return {void}
+ */
+ static callPendingHandlers() {
+ while (mr.MockPromise.pendingHandlers_.length) {
+ const handler = mr.MockPromise.pendingHandlers_.shift();
+ handler();
+ }
+ }
+
+ /**
+ * @param {Function} onFulfilled
+ * @param {Function} onRejected
+ * @return {!mr.MockPromise.CallbackEntry_}
+ * @private
+ */
+ static getCallbackEntry_(onFulfilled, onRejected) {
+ const entry = new mr.MockPromise.CallbackEntry_();
+ entry.onFulfilled = onFulfilled;
+ entry.onRejected = onRejected;
+ return entry;
+ }
+
+ /**
+ * @param {*=} opt_value
+ * @return {!mr.MockPromise} A new Promise that is immediately resolved
+ * with the given value. If the input value is already a mr.MockPromise,
+ * it will be returned immediately without creating a new instance.
+ */
+ static resolve(opt_value) {
+ if (opt_value instanceof mr.MockPromise) {
+ // Avoid creating a new object if we already have a promise object
+ // of the correct type.
+ return opt_value;
+ }
+
+ return new mr.MockPromise((resolve, reject) => {
+ resolve(opt_value);
+ });
+ }
+
+ /**
+ * @param {*=} opt_reason
+ * @return {!mr.MockPromise} A new Promise that is immediately rejected with the
+ * given reason.
+ */
+ static reject(opt_reason) {
+ return new mr.MockPromise((resolve, reject) => {
+ reject(opt_reason);
+ });
+ }
+
+ /**
+ * @param {!Array} promises
+ * @return {!mr.MockPromise} A Promise that receives the result of the first
+ * Promise input to settle immediately after it settles.
+ */
+ static race(promises) {
+ return new mr.MockPromise((resolve, reject) => {
+ if (!promises.length) {
+ resolve(undefined);
+ }
+ for (let i = 0, promise; i < promises.length; i++) {
+ promise = promises[i];
+ mr.MockPromise.resolve(promise).then(resolve, reject);
+ }
+ });
+ }
+
+ /**
+ * @param {!Array} promises
+ * @return {!mr.MockPromise<!Array>} A Promise that receives a list of
+ * every fulfilled value once every input Promise is successfully
+ * fulfilled, or is rejected with the first rejection reason immediately
+ * after it is rejected.
+ */
+ static all(promises) {
+ return new mr.MockPromise((resolve, reject) => {
+ let toFulfill = promises.length;
+ const values = [];
+
+ if (!toFulfill) {
+ resolve(values);
+ return;
+ }
+
+ const onFulfill = (index, value) => {
+ toFulfill--;
+ values[index] = value;
+ if (toFulfill == 0) {
+ resolve(values);
+ }
+ };
+
+ const onReject = reason => {
+ reject(reason);
+ };
+
+ for (let i = 0, promise; i < promises.length; i++) {
+ promise = promises[i];
+ mr.MockPromise.resolve(promise).then(onFulfill.bind(null, i), onReject);
+ }
+ });
+ }
+
+ /**
+ * Adds callbacks that will operate on the result of the Promise, returning a
+ * new child Promise.
+ *
+ * If the Promise is fulfilled, the `onFulfilled` callback will be
+ * invoked with the fulfillment value as argument, and the child Promise will
+ * be fulfilled with the return value of the callback. If the callback throws
+ * an exception, the child Promise will be rejected with the thrown value
+ * instead.
+ *
+ * If the Promise is rejected, the `onRejected` callback will be invoked
+ * with the rejection reason as argument, and the child Promise will be
+ * resolved with the return value or rejected with the thrown value of the
+ * callback.
+ *
+ * @param {?(function(TYPE):?)=} onFulfilled A
+ * function that will be invoked with the fulfillment value if the Promise
+ * is fulfilled.
+ * @param {?(function(*): *)=} onRejected A function that will
+ * be invoked with the rejection reason if the Promise is rejected.
+ * @return {!mr.MockPromise} A new Promise that will receive the result of the
+ * callback.
+ * @override
+ */
+ then(onFulfilled = null, onRejected = null) {
+ if (onFulfilled != null && typeof onFulfilled != 'function') {
+ throw Error('onFulfilled should be a function.');
+ }
+ if (onRejected != null && typeof onRejected != 'function') {
+ throw Error('onRejected should be a function.');
+ }
+
+ return this.addChildPromise_(onFulfilled, onRejected);
+ }
+
+ /**
+ * Adds a callback that will be invoked only if the Promise is rejected. This
+ * is equivalent to {@code then(null, onRejected)}.
+ *
+ * @param {function(*): *} onRejected A function that will be
+ * invoked with the rejection reason if the Promise is rejected.
+ * @return {!mr.MockPromise} A new Promise that will receive the result of the
+ * callback.
+ */
+ catch(onRejected) {
+ return this.addChildPromise_(null, onRejected);
+ }
+
+ /**
+ * Adds a callback entry to the current Promise, and schedules callback
+ * execution if the Promise has already been settled.
+ *
+ * @param {mr.MockPromise.CallbackEntry_} callbackEntry Record containing
+ * {
+ * @private
+ */
+ addCallbackEntry_(callbackEntry) {
+ if (!this.hasEntry_() &&
+ (this.state_ == mr.MockPromise.State_.FULFILLED ||
+ this.state_ == mr.MockPromise.State_.REJECTED)) {
+ this.scheduleCallbacks_();
+ }
+ this.queueEntry_(callbackEntry);
+ }
+
+ /**
+ * Creates a child Promise and adds it to the callback entry list. The result
+ * of the child Promise is determined by the result of the `onFulfilled`
+ * or `onRejected` callbacks as specified in the Promise resolution
+ * procedure.
+ *
+ * @param {?function(TYPE):
+ * (RESULT|mr.MockPromise<RESULT>)} onFulfilled A callback that
+ * will be invoked if the Promise is fulfilled, or null.
+ * @param {?function(*): *} onRejected A callback that will be
+ * invoked if the Promise is rejected, or null.
+ * @return {!mr.MockPromise} The child Promise.
+ * @template RESULT
+ * @private
+ */
+ addChildPromise_(onFulfilled, onRejected) {
+ /** @type {mr.MockPromise.CallbackEntry_} */
+ const callbackEntry = mr.MockPromise.getCallbackEntry_(null, null, null);
+
+ callbackEntry.child = new mr.MockPromise((resolve, reject) => {
+ // Invoke onFulfilled, or resolve with the parent's value if absent.
+ callbackEntry.onFulfilled = onFulfilled ? value => {
+ try {
+ const result = onFulfilled.call(null, value);
+ resolve(result);
+ } catch (err) {
+ reject(err);
+ }
+ } : resolve;
+
+ // Invoke onRejected, or reject with the parent's reason if absent.
+ callbackEntry.onRejected = onRejected ? reason => {
+ try {
+ resolve(onRejected.call(null, reason));
+ } catch (err) {
+ reject(err);
+ }
+ } : reject;
+ });
+
+ this.addCallbackEntry_(callbackEntry);
+ return callbackEntry.child;
+ }
+
+ /**
+ * Unblocks the Promise and fulfills it with the given value.
+ *
+ * @param {TYPE} value
+ * @private
+ */
+ unblockAndFulfill_(value) {
+ if (this.state_ != mr.MockPromise.State_.BLOCKED) {
+ throw Error('Expected state to be BLOCKED.');
+ }
+ this.state_ = mr.MockPromise.State_.PENDING;
+ this.resolve_(mr.MockPromise.State_.FULFILLED, value);
+ }
+
+ /**
+ * Unblocks the Promise and rejects it with the given rejection reason.
+ *
+ * @param {*} reason
+ * @private
+ */
+ unblockAndReject_(reason) {
+ if (this.state_ != mr.MockPromise.State_.BLOCKED) {
+ throw Error('Expected state to be BLOCKED.');
+ }
+ this.state_ = mr.MockPromise.State_.PENDING;
+ this.resolve_(mr.MockPromise.State_.REJECTED, reason);
+ }
+
+ /**
+ * Attempts to resolve a Promise with a given resolution state and value. This
+ * is a no-op if the given Promise has already been resolved.
+ *
+ * If the given result is a Promise, the Promise will be settled with the same
+ * state and result as the Thenable once it is itself settled.
+ *
+ * If the given result is not a Promise, the Promise will be settled
+ * (fulfilled or rejected) with that result based on the given state.
+ *
+ * @param {mr.MockPromise.State_} state
+ * @param {*} x The result to apply to the Promise.
+ * @private
+ */
+ resolve_(state, x) {
+ if (this.state_ != mr.MockPromise.State_.PENDING) {
+ return;
+ }
+
+ if (this === x) {
+ state = mr.MockPromise.State_.REJECTED;
+ x = new TypeError('Promise cannot resolve to itself');
+ }
+
+ this.state_ = mr.MockPromise.State_.BLOCKED;
+
+ if (x instanceof mr.MockPromise) {
+ x.addCallbackEntry_(mr.MockPromise.getCallbackEntry_(
+ this.unblockAndFulfill_.bind(this),
+ this.unblockAndReject_.bind(this)));
+ return;
+ }
+
+ this.result_ = x;
+ this.state_ = state;
+ this.scheduleCallbacks_();
+
+ if (state == mr.MockPromise.State_.REJECTED) {
+ mr.MockPromise.addUnhandledRejection_(this, x);
+ }
+ }
+
+ /**
+ * Executes the pending callbacks of a settled Promise after a timeout.
+ * @private
+ */
+ scheduleCallbacks_() {
+ if (!this.executing_) {
+ this.executing_ = true;
+ mr.MockPromise.pendingHandlers_.push(this.executeCallbacks_.bind(this));
+ }
+ }
+
+ /**
+ * @return {boolean} Whether there are any pending callbacks queued.
+ * @private
+ */
+ hasEntry_() {
+ return this.callbackEntries_.size > 0;
+ }
+
+ /**
+ * @param {mr.MockPromise.CallbackEntry_} entry
+ * @private
+ */
+ queueEntry_(entry) {
+ if (entry.onFulfilled == null) {
+ throw Error('entry.onFulfilled == null');
+ }
+ this.callbackEntries_.push(entry);
+ }
+
+ /**
+ * @return {mr.MockPromise.CallbackEntry_} entry
+ * @private
+ */
+ popEntry_() {
+ const entry = this.callbackEntries_.shift() || null;
+ if (entry && entry.onFulfilled == null) {
+ throw Error('entry.onFulfulled == null');
+ }
+ return entry;
+ }
+
+ /**
+ * Executes all pending callbacks for this Promise.
+ *
+ * @private
+ */
+ executeCallbacks_() {
+ let entry = null;
+ while (entry = this.popEntry_()) {
+ this.executeCallback_(entry, this.state_, this.result_);
+ }
+ this.executing_ = false;
+ }
+
+ /**
+ * Executes a pending callback for this Promise. Invokes an
+ * `onFulfilled` or `onRejected` callback based on the settled
+ * state of the Promise.
+ *
+ * @param {!mr.MockPromise.CallbackEntry_} callbackEntry An entry containing
+ * the onFulfilled and/or onRejected callbacks for this step.
+ * @param {mr.MockPromise.State_} state The resolution status of the Promise,
+ * either FULFILLED or REJECTED.
+ * @param {*} result The settled result of the Promise.
+ * @private
+ */
+ executeCallback_(callbackEntry, state, result) {
+ // Cancel an unhandled rejection if the then call had an onRejected.
+ if (state == mr.MockPromise.State_.REJECTED && callbackEntry.onRejected) {
+ this.hadUnhandledRejection_ = false;
+ }
+
+ if (callbackEntry.child) {
+ mr.MockPromise.invokeCallback_(callbackEntry, state, result);
+ } else {
+ try {
+ mr.MockPromise.invokeCallback_(callbackEntry, state, result);
+ } catch (err) {
+ mr.MockPromise.handleRejection_(err);
+ }
+ }
+ }
+
+ /**
+ * Executes the onFulfilled or onRejected callback for a callbackEntry.
+ *
+ * @param {!mr.MockPromise.CallbackEntry_} callbackEntry
+ * @param {mr.MockPromise.State_} state
+ * @param {*} result
+ * @private
+ */
+ static invokeCallback_(callbackEntry, state, result) {
+ if (state == mr.MockPromise.State_.FULFILLED) {
+ callbackEntry.onFulfilled.call(null, result);
+ } else if (callbackEntry.onRejected) {
+ callbackEntry.onRejected.call(null, result);
+ }
+ }
+
+ /**
+ * Marks this rejected Promise as unhandled. If no `onRejected` callback
+ * is called for this Promise before the `UNHANDLED_REJECTION_DELAY`
+ * expires, the reason will be passed to the unhandled rejection handler. The
+ * handler typically rethrows the rejection reason so that it becomes visible
+ * in
+ * the developer console.
+ *
+ * @param {!mr.MockPromise} promise The rejected Promise.
+ * @param {*} reason The Promise rejection reason.
+ * @private
+ */
+ static addUnhandledRejection_(promise, reason) {
+ promise.hadUnhandledRejection_ = true;
+ mr.MockPromise.pendingHandlers_.push(() => {
+ if (promise.hadUnhandledRejection_) {
+ mr.MockPromise.handleRejection_(reason);
+ }
+ });
+ }
+
+ /**
+ * @param {*} reason
+ * @private
+ */
+ static handleRejection_(reason) {
+ if (!mr.MockPromise.ignoreUnhandledRejections) {
+ throw reason;
+ }
+ }
+};
+
+
+/**
+ * @type {boolean}
+ */
+mr.MockPromise.ignoreUnhandledRejections = false;
+
+
+
+/**
+ * @private @const
+ */
+mr.MockPromise.origPromise_ = Promise;
+
+
+/**
+ * The possible internal states for a Promise. These states are not directly
+ * observable to external callers.
+ * @enum {number}
+ * @private
+ */
+mr.MockPromise.State_ = {
+ /** The Promise is waiting for resolution. */
+ PENDING: 0,
+
+ /** The Promise is blocked waiting for the result of another Thenable. */
+ BLOCKED: 1,
+
+ /** The Promise has been resolved with a fulfillment value. */
+ FULFILLED: 2,
+
+ /** The Promise has been resolved with a rejection reason. */
+ REJECTED: 3
+};
+
+
+/**
+ * @private @const {!Array<function()>}
+ */
+mr.MockPromise.pendingHandlers_ = [];
+
+
+/**
+ * Entries in the callback chain. Each call to `then` or
+ * `catch` creates an entry containing the
+ * functions that may be invoked once the Promise is settled.
+ *
+ * @private @final
+ */
+mr.MockPromise.CallbackEntry_ = class {
+ constructor() {
+ /** @type {?mr.MockPromise} */
+ this.child = null;
+ /** @type {Function} */
+ this.onFulfilled = null;
+ /** @type {Function} */
+ this.onRejected = null;
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/mock_promise_test.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/mock_promise_test.js
new file mode 100644
index 00000000000..dc02a027eb3
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/mock_promise_test.js
@@ -0,0 +1,850 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.provide('mr.MockPromiseTest');
+goog.setTestOnly('mr.MockPromiseTest');
+
+goog.require('mr.MockPromise');
+
+
+describe('mr.MockPromise', () => {
+ afterEach(() => {
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ // Simple shared objects used as test values.
+ const dummy = {
+ toString() {
+ return '[object dummy]';
+ }
+ };
+ const sentinel = {
+ toString() {
+ return '[object sentinel]';
+ }
+ };
+
+ /**
+ * Dummy onfulfilled or onrejected function that should not be called.
+ *
+ * @param {*} result The result passed into the callback.
+ */
+ function shouldNotCall(result) {
+ fail('This should not have been called (result: ' + String(result) + ')');
+ }
+
+ function resolveSoon(value) {
+ let resolveFunc;
+ const promise = new mr.MockPromise((resolve, reject) => {
+ resolveFunc = resolve.bind(null, value);
+ });
+ return [promise, resolveFunc];
+ }
+
+ function rejectSoon(value) {
+ let rejectFunc;
+ const promise = new mr.MockPromise((resolve, reject) => {
+ rejectFunc = reject.bind(null, value);
+ });
+ return [promise, rejectFunc];
+ }
+
+ // A trivial expectation test to suppress Jasmine warnings.
+ function noExpectations() {
+ expect(true).toBe(true);
+ }
+
+ it('testThenIsFulfilled', () => {
+ let timesCalled = 0;
+
+ const p = new mr.MockPromise((resolve, reject) => {
+ resolve(sentinel);
+ });
+ p.then(value => {
+ timesCalled++;
+ expect(value).toBe(sentinel);
+ });
+
+ expect(timesCalled).toBe(0);
+
+ mr.MockPromise.callPendingHandlers();
+ expect(timesCalled).toBe(1);
+ });
+
+ it('testThenIsRejected', () => {
+ let timesCalled = 0;
+
+ const p = mr.MockPromise.reject(sentinel);
+ p.then(shouldNotCall, value => {
+ timesCalled++;
+ expect(value).toBe(sentinel);
+ });
+
+ expect(timesCalled).toBe(0);
+
+ mr.MockPromise.callPendingHandlers();
+ expect(timesCalled).toBe(1);
+ });
+
+ it('testThenAsserts', () => {
+ const p = mr.MockPromise.resolve();
+
+ expect(() => {
+ p.then({});
+ }).toThrowError(/onFulfilled should be a function./);
+
+ expect(() => {
+ p.then(() => {}, {});
+ }).toThrowError(/onRejected should be a function./);
+ });
+
+ it('testOptionalOnFulfilled', done => {
+ mr.MockPromise.resolve(sentinel)
+ .then(null, null)
+ .then(null, shouldNotCall)
+ .then(value => {
+ expect(value).toBe(sentinel);
+ done();
+ });
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testOptionalOnRejected', done => {
+ mr.MockPromise.reject(sentinel)
+ .then(null, null)
+ .then(shouldNotCall)
+ .then(null, reason => {
+ expect(reason).toBe(sentinel);
+ done();
+ });
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testMultipleResolves', () => {
+ let timesCalled = 0;
+ let resolvePromise;
+
+ const p = new mr.MockPromise((resolve, reject) => {
+ resolvePromise = resolve;
+ resolve('foo');
+ resolve('bar');
+ });
+
+ p.then(value => {
+ timesCalled++;
+ expect(timesCalled).toBe(1);
+ });
+
+ mr.MockPromise.callPendingHandlers();
+
+ resolvePromise('baz');
+ expect(timesCalled).toBe(1);
+ });
+
+ it('testMultipleRejects', () => {
+ let timesCalled = 0;
+ let rejectPromise;
+
+ const p = new mr.MockPromise((resolve, reject) => {
+ rejectPromise = reject;
+ reject('foo');
+ reject('bar');
+ });
+
+ p.then(shouldNotCall, value => {
+ timesCalled++;
+ expect(timesCalled).toBe(1);
+ });
+
+ mr.MockPromise.callPendingHandlers();
+
+ rejectPromise('baz');
+ expect(timesCalled).toBe(1);
+ });
+
+ it('testResolveWithPromise', () => {
+ let resolveBlocker;
+ let hasFulfilled = false;
+ const blocker = new mr.MockPromise((resolve, reject) => {
+ resolveBlocker = resolve;
+ });
+
+ const p = mr.MockPromise.resolve(blocker);
+ p.then(value => {
+ hasFulfilled = true;
+ expect(value).toBe(sentinel);
+ }, shouldNotCall);
+
+ expect(hasFulfilled).toBe(false);
+ resolveBlocker(sentinel);
+
+ mr.MockPromise.callPendingHandlers();
+ expect(hasFulfilled).toBe(true);
+ });
+
+ it('testResolveWithRejectedPromise', () => {
+ let rejectBlocker;
+ let hasRejected = false;
+ const blocker = new mr.MockPromise((resolve, reject) => {
+ rejectBlocker = reject;
+ });
+
+ const p = mr.MockPromise.resolve(blocker);
+ const child = p.then(shouldNotCall, reason => {
+ hasRejected = true;
+ expect(reason).toBe(sentinel);
+ });
+
+ expect(hasRejected).toBe(false);
+ rejectBlocker(sentinel);
+
+ mr.MockPromise.callPendingHandlers();
+ expect(hasRejected).toBe(true);
+ });
+
+ it('testRejectWithPromise', () => {
+ let resolveBlocker;
+ let hasFulfilled = false;
+ const blocker = new mr.MockPromise((resolve, reject) => {
+ resolveBlocker = resolve;
+ });
+
+ const p = mr.MockPromise.reject(blocker);
+ const child = p.then(value => {
+ hasFulfilled = true;
+ expect(value).toBe(sentinel);
+ }, shouldNotCall);
+
+ expect(hasFulfilled).toBe(false);
+ resolveBlocker(sentinel);
+
+ mr.MockPromise.callPendingHandlers();
+ expect(hasFulfilled).toBe(true);
+ });
+
+ it('testRejectWithRejectedPromise', () => {
+ let rejectBlocker;
+ let hasRejected = false;
+ const blocker = new mr.MockPromise((resolve, reject) => {
+ rejectBlocker = reject;
+ });
+
+ const p = mr.MockPromise.reject(blocker);
+ const child = p.then(shouldNotCall, reason => {
+ hasRejected = true;
+ expect(reason).toBe(sentinel);
+ });
+
+ expect(hasRejected).toBe(false);
+ rejectBlocker(sentinel);
+
+ mr.MockPromise.callPendingHandlers();
+ expect(hasRejected).toBe(true);
+ });
+
+ it('testResolveAndReject', () => {
+ let onFulfilledCalled = false;
+ let onRejectedCalled = false;
+ const p = new mr.MockPromise((resolve, reject) => {
+ resolve();
+ reject();
+ });
+
+ p.then(
+ () => {
+ onFulfilledCalled = true;
+ },
+ () => {
+ onRejectedCalled = true;
+ });
+
+ mr.MockPromise.callPendingHandlers();
+ expect(onFulfilledCalled).toBe(true);
+ expect(onRejectedCalled).toBe(false);
+ });
+
+ it('testResolveWithSelfRejects', done => {
+ let r;
+ const p = new mr.MockPromise(resolve => {
+ r = resolve;
+ });
+ r(p);
+ p.then(shouldNotCall, e => {
+ expect(e.message).toBe('Promise cannot resolve to itself');
+ done();
+ });
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testResolveWithObjectStringResolves', done => {
+ mr.MockPromise.resolve('[object Object]').then(v => {
+ expect(v).toBe('[object Object]');
+ done();
+ });
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testRejectAndResolve', done => {
+ noExpectations();
+ new mr
+ .MockPromise((resolve, reject) => {
+ reject();
+ resolve();
+ })
+ .then(shouldNotCall, done);
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testThenReturnsBeforeCallbackWithFulfill', done => {
+ let thenHasReturned = false;
+ const p = mr.MockPromise.resolve();
+
+ p.then(() => {
+ expect(thenHasReturned).toBe(true);
+ done();
+ });
+ thenHasReturned = true;
+
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testThenReturnsBeforeCallbackWithReject', done => {
+ let thenHasReturned = false;
+ const p = mr.MockPromise.reject();
+
+ const child = p.then(shouldNotCall, () => {
+ expect(thenHasReturned).toBe(true);
+ done();
+ });
+ thenHasReturned = true;
+
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testResolutionOrder', () => {
+ const callbacks = [];
+ mr.MockPromise.resolve()
+ .then(
+ () => {
+ callbacks.push(1);
+ },
+ shouldNotCall)
+ .then(
+ () => {
+ callbacks.push(2);
+ },
+ shouldNotCall)
+ .then(() => {
+ callbacks.push(3);
+ }, shouldNotCall);
+
+ mr.MockPromise.callPendingHandlers();
+ expect(callbacks).toEqual([1, 2, 3]);
+ });
+
+ it('testResolutionOrderWithThrow', done => {
+ const callbacks = [];
+ const p = mr.MockPromise.resolve();
+
+ p.then(() => {
+ callbacks.push(1);
+ }, shouldNotCall);
+ const child = p.then(() => {
+ callbacks.push(2);
+ throw Error();
+ }, shouldNotCall);
+
+ child.then(shouldNotCall, () => {
+ // The parent callbacks should be evaluated before the child.
+ callbacks.push(4);
+ });
+
+ p.then(() => {
+ callbacks.push(3);
+ }, shouldNotCall);
+
+ child.then(shouldNotCall, () => {
+ callbacks.push(5);
+ expect(callbacks).toEqual([1, 2, 3, 4, 5]);
+ done();
+ });
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testResolutionOrderWithNestedThen', done => {
+ const callbacks = [];
+ const promise = new mr.MockPromise((resolve, reject) => {
+ const p = mr.MockPromise.resolve();
+
+ p.then(() => {
+ callbacks.push(1);
+ p.then(() => {
+ callbacks.push(3);
+ resolve();
+ });
+ });
+ p.then(() => {
+ callbacks.push(2);
+ });
+ });
+
+ promise.then(() => {
+ expect(callbacks).toEqual([1, 2, 3]);
+ done();
+ });
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testRejectionOrder', () => {
+ const callbacks = [];
+ const p = mr.MockPromise.reject();
+
+ p.then(shouldNotCall, () => {
+ callbacks.push(1);
+ });
+ p.then(shouldNotCall, () => {
+ callbacks.push(2);
+ });
+ p.then(shouldNotCall, () => {
+ callbacks.push(3);
+ });
+
+ mr.MockPromise.callPendingHandlers();
+ expect(callbacks).toEqual([1, 2, 3]);
+ });
+
+ it('testRejectionOrderWithThrow', () => {
+ const callbacks = [];
+ const p = mr.MockPromise.reject();
+
+ p.then(shouldNotCall, () => {
+ callbacks.push(1);
+ });
+ p.then(shouldNotCall, () => {
+ callbacks.push(2);
+ throw Error();
+ }).catch(() => {});
+ p.then(shouldNotCall, () => {
+ callbacks.push(3);
+ });
+
+ mr.MockPromise.callPendingHandlers();
+ expect(callbacks).toEqual([1, 2, 3]);
+ });
+
+ it('testRejectionOrderWithNestedThen', done => {
+ const callbacks = [];
+ const promise = new mr.MockPromise((resolve, reject) => {
+
+ const p = mr.MockPromise.reject();
+
+ p.then(shouldNotCall, () => {
+ callbacks.push(1);
+ p.then(shouldNotCall, () => {
+ callbacks.push(3);
+ resolve();
+ });
+ });
+ p.then(shouldNotCall, () => {
+ callbacks.push(2);
+ });
+ });
+
+ promise.then(() => {
+ expect(callbacks).toEqual([1, 2, 3]);
+ done();
+ });
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testBranching', () => {
+ const p = mr.MockPromise.resolve(2);
+ let branchesResolved = 0;
+
+ const branch1 = p.then(value => {
+ expect(value).toBe(2);
+ return value / 2;
+ }).then(value => {
+ expect(value).toBe(1);
+ ++branchesResolved;
+ });
+
+ const branch2 = p.then(value => {
+ expect(value).toBe(2);
+ throw value + 1;
+ }).then(shouldNotCall, reason => {
+ expect(reason).toBe(3);
+ ++branchesResolved;
+ });
+
+ const branch3 = p.then(value => {
+ expect(value).toBe(2);
+ return value * 2;
+ }).then(value => {
+ expect(value).toBe(4);
+ ++branchesResolved;
+ });
+
+ mr.MockPromise.all([branch1, branch2, branch3]);
+ mr.MockPromise.callPendingHandlers();
+ expect(branchesResolved).toBe(3);
+ });
+
+ it('testThenReturnsPromise', () => {
+ const parent = mr.MockPromise.resolve();
+ const child = parent.then();
+
+ expect(child instanceof mr.MockPromise).toBe(true);
+ expect(child).not.toEqual(parent);
+ });
+
+ it('testBlockingPromise', () => {
+ const p = mr.MockPromise.resolve();
+ let wasFulfilled = false;
+ let wasRejected = false;
+
+ const p2 = p.then(() => new mr.MockPromise((resolve, reject) => {}));
+
+ p2.then(
+ () => {
+ wasFulfilled = true;
+ },
+ () => {
+ wasRejected = true;
+ });
+
+ mr.MockPromise.callPendingHandlers();
+ expect(wasFulfilled).toBe(false);
+ expect(wasRejected).toBe(false);
+ });
+
+ it('testBlockingPromiseFulfilled', done => {
+ let resolveBlockingPromise;
+ const blockingPromise = new mr.MockPromise((resolve, reject) => {
+ resolveBlockingPromise = resolve;
+ });
+
+ const p = mr.MockPromise.resolve(dummy);
+ const p2 = p.then(value => blockingPromise);
+
+ p2.then(value => {
+ expect(value).toBe(sentinel);
+ done();
+ });
+ resolveBlockingPromise(sentinel);
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testBlockingPromiseRejected', done => {
+ let rejectBlockingPromise;
+ const blockingPromise = new mr.MockPromise((resolve, reject) => {
+ rejectBlockingPromise = reject;
+ });
+
+ const p = mr.MockPromise.resolve(blockingPromise);
+
+ p.then(shouldNotCall, reason => {
+ expect(reason).toBe(sentinel);
+ done();
+ });
+ rejectBlockingPromise(sentinel);
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testCatch', done => {
+ let catchCalled = false;
+ mr.MockPromise.reject()
+ .catch(reason => {
+ catchCalled = true;
+ return sentinel;
+ })
+ .then(value => {
+ expect(catchCalled).toBe(true);
+ expect(value).toBe(sentinel);
+ done();
+ });
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testRaceWithEmptyList', done => {
+ mr.MockPromise.race([]).then(value => {
+ expect(value).toBe(undefined);
+ done();
+ });
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testRaceWithFulfill', done => {
+ const [a, resolveA] = resolveSoon('a');
+ const [b, resolveB] = resolveSoon('b');
+ const [c, resolveC] = resolveSoon('c');
+ const [d, resolveD] = resolveSoon('d');
+
+ mr.MockPromise.race([a, b, c, d])
+ .then(value => {
+ expect(value).toBe('c');
+ // Return the slowest input promise to wait for it to complete.
+ return a;
+ })
+ .then(value => {
+ expect(value).toBe('a');
+ done();
+ });
+ resolveC();
+ resolveD();
+ resolveB();
+ resolveA();
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testRaceWithNonThenable', done => {
+ const [a, resolveA] = resolveSoon('a');
+ const b = 'b';
+ const [c, resolveC] = resolveSoon('c');
+ const [d, resolveD] = resolveSoon('d');
+
+ mr.MockPromise.race([a, b, c, d])
+ .then(value => {
+ expect(value).toBe('b');
+ // Return the slowest input promise to wait for it to complete.
+ return a;
+ })
+ .then(value => {
+ expect(value).toBe('a');
+ done();
+ });
+ resolveC();
+ resolveD();
+ resolveA();
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testRaceWithFalseyNonThenable', done => {
+ const [a, resolveA] = resolveSoon('a');
+ const b = 0;
+ const [c, resolveC] = resolveSoon('c');
+ const [d, resolveD] = resolveSoon('d');
+
+ mr.MockPromise.race([a, b, c, d])
+ .then(value => {
+ expect(value).toBe(0);
+ // Return the slowest input promise to wait for it to complete.
+ return a;
+ })
+ .then(value => {
+ expect(value).toBe('a');
+ done();
+ });
+ resolveC();
+ resolveD();
+ resolveA();
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testRaceWithFulfilledBeforeNonThenable', done => {
+ const [a, resolveA] = resolveSoon('a');
+ const b = mr.MockPromise.resolve('b');
+ const c = 'c';
+ const [d, resolveD] = resolveSoon('d');
+
+ mr.MockPromise.race([a, b, c, d])
+ .then(value => {
+ expect(value).toBe('b');
+ // Return the slowest input promise to wait for it to complete.
+ return a;
+ })
+ .then(value => {
+ expect(value).toBe('a');
+ done();
+ });
+ resolveD();
+ resolveA();
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testRaceWithReject', done => {
+ const [a, rejectA] = rejectSoon('rejected-a', 40);
+ const [b, rejectB] = rejectSoon('rejected-b', 30);
+ const [c, rejectC] = rejectSoon('rejected-c', 10);
+ const [d, rejectD] = rejectSoon('rejected-d', 20);
+
+ mr.MockPromise.race([a, b, c, d])
+ .then(
+ shouldNotCall,
+ value => {
+ expect(value).toBe('rejected-c');
+ return a;
+ })
+ .then(shouldNotCall, reason => {
+ expect(reason).toBe('rejected-a');
+ done();
+ });
+ rejectC();
+ rejectD();
+ rejectB();
+ rejectA();
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testAllWithEmptyList', done => {
+ mr.MockPromise.all([]).then(value => {
+ expect(value).toEqual([]);
+ done();
+ });
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testAllWithFulfill', done => {
+ const [a, resolveA] = resolveSoon('a');
+ const [b, resolveB] = resolveSoon('b');
+ const [c, resolveC] = resolveSoon('c');
+ const [d, resolveD] = resolveSoon('d');
+ // Test a falsey value.
+ const [z, resolveZ] = resolveSoon(0);
+
+ mr.MockPromise.all([a, b, c, d, z]).then(value => {
+ expect(value).toEqual(['a', 'b', 'c', 'd', 0]);
+ done();
+ });
+ resolveC();
+ resolveD();
+ resolveB();
+ resolveZ();
+ resolveA();
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testAllWithNonThenable', done => {
+ const [a, resolveA] = resolveSoon('a');
+ const b = 'b';
+ const [c, resolveC] = resolveSoon('c');
+ const [d, resolveD] = resolveSoon('d');
+ // Test a falsey value.
+ const z = 0;
+
+ mr.MockPromise.all([a, b, c, d, z]).then(value => {
+ expect(value).toEqual(['a', 'b', 'c', 'd', 0]);
+ done();
+ });
+ resolveC();
+ resolveD();
+ resolveA();
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testAllWithReject', done => {
+ const [a, resolveA] = resolveSoon('a');
+ const [b, rejectB] = rejectSoon('rejected-b');
+ const [c, resolveC] = resolveSoon('c');
+ const [d, resolveD] = resolveSoon('d');
+
+ mr.MockPromise.all([a, b, c, d])
+ .then(
+ shouldNotCall,
+ reason => {
+ expect(reason).toBe('rejected-b');
+ return a;
+ })
+ .then(value => {
+ expect(value).toBe('a');
+ done();
+ });
+ resolveC();
+ resolveD();
+ rejectB();
+ resolveA();
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testMockClock', () => {
+ let resolveA;
+ let resolveB;
+ const calls = [];
+
+ const p = new mr.MockPromise((resolve, reject) => {
+ resolveA = resolve;
+ });
+
+ p.then(value => {
+ expect(value).toBe(sentinel);
+ calls.push('then');
+ });
+
+ const fulfilledChild = p.then(value => {
+ expect(value).toBe(sentinel);
+ return mr.MockPromise.resolve(1);
+ }).then(value => {
+ expect(value).toBe(1);
+ calls.push('fulfilledChild');
+
+ });
+
+ const rejectedChild = p.then(value => {
+ expect(value).toBe(sentinel);
+ return mr.MockPromise.reject(2);
+ }).then(shouldNotCall, reason => {
+ expect(reason).toBe(2);
+ calls.push('rejectedChild');
+ });
+
+ const unresolvedChild = p.then(value => {
+ expect(value).toBe(sentinel);
+ return new mr.MockPromise(r => {
+ resolveB = r;
+ });
+ }).then(value => {
+ expect(value).toBe(3);
+ calls.push('unresolvedChild');
+ });
+
+ resolveA(sentinel);
+ expect(calls).toEqual([]);
+
+ mr.MockPromise.callPendingHandlers();
+ expect(calls).toEqual(['then', 'fulfilledChild', 'rejectedChild']);
+
+ resolveB(3);
+ expect(calls).toEqual(['then', 'fulfilledChild', 'rejectedChild']);
+
+ mr.MockPromise.callPendingHandlers();
+ expect(calls).toEqual(
+ ['then', 'fulfilledChild', 'rejectedChild', 'unresolvedChild']);
+ });
+
+ it('testHandledRejection', () => {
+ noExpectations();
+ mr.MockPromise.reject(sentinel).then(shouldNotCall, reason => {});
+ mr.MockPromise.callPendingHandlers();
+ });
+
+ it('testUnhandledRejection1', () => {
+ mr.MockPromise.reject(sentinel);
+ expect(mr.MockPromise.callPendingHandlers).toThrow();
+ });
+
+ it('testUnhandledRejection2', () => {
+ mr.MockPromise.reject(sentinel).then(shouldNotCall);
+ expect(mr.MockPromise.callPendingHandlers).toThrow();
+ });
+
+ it('testUnhandledThrow', () => {
+ mr.MockPromise.resolve().then(() => {
+ throw sentinel;
+ });
+ expect(mr.MockPromise.callPendingHandlers).toThrow();
+ });
+
+ it('testUnhandledBlockingRejection', () => {
+ const blocker = mr.MockPromise.reject(sentinel);
+ mr.MockPromise.resolve(blocker);
+ expect(mr.MockPromise.callPendingHandlers).toThrow();
+ });
+
+ it('testHandledBlockingRejection', () => {
+ noExpectations();
+ const blocker = mr.MockPromise.reject(sentinel);
+ mr.MockPromise.resolve(blocker).then(shouldNotCall, reason => {});
+ mr.MockPromise.callPendingHandlers();
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/mojo_utils.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/mojo_utils.js
new file mode 100644
index 00000000000..f9d98798f8a
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/mojo_utils.js
@@ -0,0 +1,63 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.MojoUtils');
+goog.module.declareLegacyNamespace();
+
+
+/**
+ * Converts a mojo.Origin object to a string.
+ * @param {!mojo.Origin} origin
+ * @return {string}
+ */
+exports.mojoOriginToString = function(origin) {
+ if (origin.unique) {
+ return '';
+ } else {
+ return `${origin.scheme}:\/\/${origin.host}
+ ${origin.port ? `:${origin.port}` : ''}/`;
+ }
+};
+
+
+/**
+ * Converts an origin string to a mojo.Origin object.
+ * @param {string} origin
+ * @return {!mojo.Origin}
+ */
+exports.stringToMojoOrigin = function(origin) {
+ const url = new URL(origin);
+ const mojoOrigin = {};
+ mojoOrigin.scheme = url.protocol.replace(':', '');
+ mojoOrigin.host = url.hostname;
+ var port = url.port ? Number.parseInt(url.port, 10) : 0;
+ switch (mojoOrigin.scheme) {
+ case 'http':
+ mojoOrigin.port = port || 80;
+ break;
+ case 'https':
+ mojoOrigin.port = port || 443;
+ break;
+ default:
+ throw new Error('Scheme must be http or https');
+ }
+ mojoOrigin.suborigin = '';
+ return new mojo.Origin(mojoOrigin);
+};
+
+/**
+ * @param {?mojo.TimeDelta} timeDelta
+ * @return {number}
+ */
+exports.timeDeltaToSeconds = function(timeDelta) {
+ return timeDelta ? timeDelta.microseconds / 1000000 : 0;
+};
+
+/**
+ * @param {number} seconds
+ * @return {!mojo.TimeDelta}
+ */
+exports.secondsToTimeDelta = function(seconds) {
+ return new mojo.TimeDelta({microseconds: Math.floor(seconds * 1000000)});
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/object_utils.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/object_utils.js
new file mode 100644
index 00000000000..699c9f0be27
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/object_utils.js
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Utility methods for Objects.
+ */
+
+goog.provide('mr.ObjectUtils');
+
+mr.ObjectUtils = class {
+ /**
+ * Gets the value in an Object with the given list of keys by traversing the
+ * objects with them.
+ * @param {!Object} obj
+ * @param {...string} path
+ * @return {*} The value in the object with the given path, or undefined if
+ * it does not exist due to missing either one of the intermediate keys
+ * or the final key.
+ */
+ static getPath(obj, ...path) {
+ let value = obj;
+ for (const key of path) {
+ if (value == undefined || typeof value != 'object') {
+ return undefined;
+ }
+ value = value[key];
+ }
+ return value;
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/object_utils_test.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/object_utils_test.js
new file mode 100644
index 00000000000..253c6142f4e
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/object_utils_test.js
@@ -0,0 +1,33 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.setTestOnly('object_utils_test');
+
+goog.require('mr.ObjectUtils');
+
+describe('mr.ObjectUtils test', () => {
+ const thudObj = {thud: 2};
+ const obj = {foo: {bar: {xyzzy: null, baz: {qux: 1, corge: thudObj}}}};
+
+ it('getPath returns undefined for nonexistent path', () => {
+ expect(mr.ObjectUtils.getPath(obj, 'foo', 'bar', 'nonexistent'))
+ .toBeUndefined();
+ expect(
+ mr.ObjectUtils.getPath(obj, 'foo', 'bar', 'baz', 'qux', 'nonexistent'))
+ .toBeUndefined();
+ expect(mr.ObjectUtils.getPath(obj, 'foo', 'bar', 'xyzzy', 'nonexistent'))
+ .toBeUndefined();
+ });
+
+ it('getPath returns value', () => {
+ expect(mr.ObjectUtils.getPath(obj, 'foo', 'bar', 'baz', 'qux')).toBe(1);
+ expect(mr.ObjectUtils.getPath(obj, 'foo', 'bar', 'baz', 'corge'))
+ .toBe(thudObj);
+ expect(mr.ObjectUtils.getPath(obj, 'foo', 'bar', 'xyzzy')).toBeNull();
+ });
+
+ it('getPath returns itself if no path provided', () => {
+ expect(mr.ObjectUtils.getPath(obj)).toBe(obj);
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/platform_utils.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/platform_utils.js
new file mode 100644
index 00000000000..26828b03848
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/platform_utils.js
@@ -0,0 +1,62 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Platform related utilities methods.
+
+ */
+
+goog.provide('mr.PlatformUtils');
+
+
+/**
+ * Returns true if the extension is running on Windows and Windows 8 or
+ * newer.
+ *
+ * @return {boolean} Whether the extension is running on Windows 8 or
+ * newer.
+ */
+mr.PlatformUtils.isWindows8OrNewer = function() {
+ // See MSDN for different windows versions:
+ // http://msdn.microsoft.com/en-us/library/ms537503(v=vs.85).aspx
+ const [, version] = navigator.userAgent.match(/Windows NT (\d+.\d+)/) || [];
+
+ // Windows 8 has version number 6.2.
+ return parseFloat(version) >= 6.2;
+};
+
+
+/**
+ * Enumeration of possible current operating systems.
+ * @enum {string}
+ */
+mr.PlatformUtils.OS = {
+ CHROMEOS: 'ChromeOS',
+ WINDOWS: 'Windows',
+ MAC: 'Mac',
+ LINUX: 'Linux',
+ OTHER: 'Other'
+};
+
+
+/**
+ * Returns the current OS.
+ * @return {!mr.PlatformUtils.OS}
+ */
+mr.PlatformUtils.getCurrentOS = function() {
+ const userAgent = navigator.userAgent;
+ if (userAgent.includes('CrOS')) {
+ return mr.PlatformUtils.OS.CHROMEOS;
+ }
+ if (userAgent.includes('Windows')) {
+ return mr.PlatformUtils.OS.WINDOWS;
+ }
+ if (userAgent.includes('Macintosh')) {
+ return mr.PlatformUtils.OS.MAC;
+ }
+ if (userAgent.includes('Linux')) {
+ return mr.PlatformUtils.OS.LINUX;
+ }
+ return mr.PlatformUtils.OS.OTHER;
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/promise_resolver.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/promise_resolver.js
new file mode 100644
index 00000000000..f7645339199
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/promise_resolver.js
@@ -0,0 +1,54 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+
+ */
+goog.module('mr.PromiseResolver');
+goog.module.declareLegacyNamespace();
+
+
+/**
+ * Wrapper around a Promise that allows the Promise to be resolved by
+ * calling a method.
+ *
+ * @template T
+ */
+exports = class {
+ constructor() {
+ /**
+ * @private {function(T)}
+ */
+ this.resolveFunc_;
+
+ /**
+ * @private {function(*)}
+ */
+ this.rejectFunc_;
+
+ /**
+ * @const {!Promise<T>}
+ */
+ this.promise = new Promise((resolve, reject) => {
+ this.resolveFunc_ = resolve;
+ this.rejectFunc_ = reject;
+ });
+ }
+
+ /**
+ * Resolves the promise.
+ * @param {T} value
+ */
+ resolve(value) {
+ this.resolveFunc_(value);
+ }
+
+ /**
+ * Rejects the promise.
+ * @param {*} reason
+ */
+ reject(reason) {
+ this.rejectFunc_(reason);
+ }
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/promise_utils.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/promise_utils.js
new file mode 100644
index 00000000000..0dcd2e23b12
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/promise_utils.js
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Utility functions for dealing with promises.
+ */
+goog.module('mr.PromiseUtils');
+goog.module.declareLegacyNamespace();
+
+
+/**
+ * Given a list of promises, waits for all promises to settle, and produces a
+ * list indicating whether each input promise was fulfilled (i.e. resolved) or
+ * rejected.
+ *
+ * Each object in the output contains two fields: the |fulfilled| field is true
+ * if the corresponding input promise was resolved, or false if it was rejected.
+ * When |fulfilled| is true, the |value| field contains the value with which the
+ * promise was resolved. Otherwise, the |reason| field contains the reason with
+ * which the promise was rejected.
+ *
+ * @param {!Array<!Promise<T>>} promises
+ * @return {!Promise<!Array<{
+ * fulfilled: boolean,
+ * value: (T | undefined),
+ * reason: *
+ * }>>}
+ * @template T
+ */
+exports.allSettled = function(promises) {
+ return Promise.all(promises.map(
+ promise => promise.then(
+ value => ({fulfilled: true, value: value}),
+ reason => ({fulfilled: false, reason: reason}))));
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/sha1.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/sha1.js
new file mode 100644
index 00000000000..8b537c9f02e
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/sha1.js
@@ -0,0 +1,239 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.Sha1');
+
+const BLOCK_SIZE = 512 / 8;
+
+/**
+ * SHA-1 cryptographic hash constructor.
+ * @final
+ */
+class Sha1 {
+ constructor() {
+ /**
+ * Holds the previous values of accumulated variables a-e in the compress_
+ * function.
+ * @private @const {!Array<number>}
+ */
+ this.chain_ = [];
+
+ /**
+ * A buffer holding the partially computed hash result.
+ * @private @const {!Array<number>}
+ */
+ this.buf_ = [];
+
+ /**
+ * An array of 80 bytes, each a part of the message to be hashed. Referred
+ * to as the message schedule in the docs.
+ * @private @const {!Array<number>}
+ */
+ this.W_ = [];
+
+ /**
+ * Contains data needed to pad messages less than 64 bytes.
+ * @private @const {!Array<number>}
+ */
+ this.pad_ = [];
+
+ this.pad_[0] = 128;
+ for (let i = 1; i < BLOCK_SIZE; ++i) {
+ this.pad_[i] = 0;
+ }
+
+ /**
+ * @private {number}
+ */
+ this.inbuf_ = 0;
+
+ /**
+ * @private {number}
+ */
+ this.total_ = 0;
+
+ this.reset();
+ }
+
+ /**
+ * Resets the internal accumulator.
+ */
+ reset() {
+ this.chain_[0] = 0x67452301;
+ this.chain_[1] = 0xefcdab89;
+ this.chain_[2] = 0x98badcfe;
+ this.chain_[3] = 0x10325476;
+ this.chain_[4] = 0xc3d2e1f0;
+
+ this.inbuf_ = 0;
+ this.total_ = 0;
+ }
+
+ /**
+ * Internal compress helper function.
+ * @param {!Array<number>|string} buf Block to compress.
+ * @param {number=} offset Offset of the block in the buffer.
+ * @private
+ */
+ compress_(buf, offset = 0) {
+ let W = this.W_;
+
+ // get 16 big endian words
+ if (typeof buf === 'string') {
+ for (let i = 0; i < 16; i++) {
+ W[i] = (buf.charCodeAt(offset) << 24) |
+ (buf.charCodeAt(offset + 1) << 16) |
+ (buf.charCodeAt(offset + 2) << 8) | (buf.charCodeAt(offset + 3));
+ offset += 4;
+ }
+ } else {
+ for (let i = 0; i < 16; i++) {
+ W[i] = (buf[offset] << 24) | (buf[offset + 1] << 16) |
+ (buf[offset + 2] << 8) | (buf[offset + 3]);
+ offset += 4;
+ }
+ }
+
+ // expand to 80 words
+ for (let i = 16; i < 80; i++) {
+ let t = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];
+ W[i] = ((t << 1) | (t >>> 31)) & 0xffffffff;
+ }
+
+ let a = this.chain_[0];
+ let b = this.chain_[1];
+ let c = this.chain_[2];
+ let d = this.chain_[3];
+ let e = this.chain_[4];
+ let f, k;
+
+ for (let i = 0; i < 80; i++) {
+ if (i < 40) {
+ if (i < 20) {
+ f = d ^ (b & (c ^ d));
+ k = 0x5a827999;
+ } else {
+ f = b ^ c ^ d;
+ k = 0x6ed9eba1;
+ }
+ } else {
+ if (i < 60) {
+ f = (b & c) | (d & (b | c));
+ k = 0x8f1bbcdc;
+ } else {
+ f = b ^ c ^ d;
+ k = 0xca62c1d6;
+ }
+ }
+
+ let t = (((a << 5) | (a >>> 27)) + f + e + k + W[i]) & 0xffffffff;
+ e = d;
+ d = c;
+ c = ((b << 30) | (b >>> 2)) & 0xffffffff;
+ b = a;
+ a = t;
+ }
+
+ this.chain_[0] = (this.chain_[0] + a) & 0xffffffff;
+ this.chain_[1] = (this.chain_[1] + b) & 0xffffffff;
+ this.chain_[2] = (this.chain_[2] + c) & 0xffffffff;
+ this.chain_[3] = (this.chain_[3] + d) & 0xffffffff;
+ this.chain_[4] = (this.chain_[4] + e) & 0xffffffff;
+ }
+
+ /**
+ * Adds a string (must only contain 8-bit, i.e., Latin1 characters)
+ * to the internal accumulator.
+ *
+ * @param {string|!Array<number>} bytes Data used for the update.
+ * @param {number=} length
+ */
+ update(bytes, length = bytes.length) {
+ let lengthMinusBlock = length - BLOCK_SIZE;
+ let n = 0;
+ // Using local instead of member variables gives ~5% speedup on Firefox 16.
+ let buf = this.buf_;
+ let inbuf = this.inbuf_;
+
+ // The outer while loop should execute at most twice.
+ while (n < length) {
+ // When we have no data in the block to top up, we can directly process
+ // the input buffer (assuming it contains sufficient data). This gives
+ // ~25% speedup on Chrome 23 and ~15% speedup on Firefox 16, but requires
+ // that the data is provided in large chunks (or in multiples of 64
+ // bytes).
+ if (inbuf == 0) {
+ while (n <= lengthMinusBlock) {
+ this.compress_(bytes, n);
+ n += BLOCK_SIZE;
+ }
+ }
+
+ if (typeof bytes === 'string') {
+ while (n < length) {
+ buf[inbuf] = bytes.charCodeAt(n);
+ ++inbuf;
+ ++n;
+ if (inbuf == BLOCK_SIZE) {
+ this.compress_(buf);
+ inbuf = 0;
+ // Jump to the outer loop so we use the full-block optimization.
+ break;
+ }
+ }
+ } else {
+ while (n < length) {
+ buf[inbuf] = bytes[n];
+ ++inbuf;
+ ++n;
+ if (inbuf == BLOCK_SIZE) {
+ this.compress_(buf);
+ inbuf = 0;
+ // Jump to the outer loop so we use the full-block optimization.
+ break;
+ }
+ }
+ }
+ }
+
+ this.inbuf_ = inbuf;
+ this.total_ += length;
+ }
+
+ /**
+ * @return {!Array<number>} The finalized hash computed
+ * from the internal accumulator.
+ */
+ digest() {
+ let digest = [];
+ let totalBits = this.total_ * 8;
+
+ // Add pad 0x80 0x00*.
+ if (this.inbuf_ < 56) {
+ this.update(this.pad_, 56 - this.inbuf_);
+ } else {
+ this.update(this.pad_, BLOCK_SIZE - (this.inbuf_ - 56));
+ }
+
+ // Add # bits.
+ for (let i = BLOCK_SIZE - 1; i >= 56; i--) {
+ this.buf_[i] = totalBits & 255;
+ totalBits /= 256; // Don't use bit-shifting here!
+ }
+
+ this.compress_(this.buf_);
+
+ let n = 0;
+ for (let i = 0; i < 5; i++) {
+ for (let j = 24; j >= 0; j -= 8) {
+ digest[n] = (this.chain_[i] >> j) & 255;
+ ++n;
+ }
+ }
+
+ return digest;
+ }
+}
+
+exports = Sha1;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/sha1_test.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/sha1_test.js
new file mode 100644
index 00000000000..1385d776a0d
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/sha1_test.js
@@ -0,0 +1,60 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.Sha1.test');
+goog.setTestOnly();
+
+const Sha1 = goog.require('mr.Sha1');
+
+/**
+ * Converts an array of byte values to a hexadecimal string.
+ * @param {!Array<number>} bytes
+ * @return {string}
+ */
+function toHex(bytes) {
+ return bytes.map(byte => byte.toString(16).padStart(2, '0')).join('');
+}
+
+describe('mr.Sha1', () => {
+ // Test vectors from:
+ // csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
+
+ let sha1;
+
+ beforeEach(() => {
+ sha1 = new Sha1();
+ });
+
+ it('hashes an empty stream correctly', () => {
+ expect(toHex(sha1.digest()))
+ .toBe('da39a3ee5e6b4b0d3255bfef95601890afd80709');
+ });
+
+ it('hashes a one-block message correctly', () => {
+ sha1.update('abc');
+ expect(toHex(sha1.digest()))
+ .toBe('a9993e364706816aba3e25717850c26c9cd0d89d');
+ });
+
+ it('hashes a multi-block message correctly', () => {
+ sha1.update('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq');
+ expect(toHex(sha1.digest()))
+ .toBe('84983e441c3bd26ebaae4aa1f95129e5e54670f1');
+ });
+
+ it('hashes a long message correctly', () => {
+ const thousandAs = 'a'.repeat(1000);
+ for (let i = 0; i < 1000; ++i) {
+ sha1.update(thousandAs);
+ }
+ expect(toHex(sha1.digest()))
+ .toBe('34aa973cd4c4daa4f61eeb2bdbad27316534016f');
+ });
+
+ it('hashes a standard message correctly', () => {
+ sha1.update('The quick brown fox jumps over the lazy dog');
+ expect(toHex(sha1.digest()))
+ .toBe('2fd4e1c67a2d28fced849ee1bb76e7391b93eb12');
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/string_utils.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/string_utils.js
new file mode 100644
index 00000000000..c21aee6dafc
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/string_utils.js
@@ -0,0 +1,24 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.StringUtils');
+
+class StringUtils {
+ /**
+ * Returns a string with at least 64-bits of randomness.
+ *
+ * Doesn't trust Javascript's random function entirely. Uses a combination of
+ * random and current timestamp, and then encodes the string in base-36 to
+ * make it shorter.
+ *
+ * @return {string} A random string, e.g. sn1s7vb4gcic.
+ */
+ static getRandomString() {
+ var x = 2147483648;
+ return Math.floor(Math.random() * x).toString(36) +
+ Math.abs(Math.floor(Math.random() * x) ^ Date.now()).toString(36);
+ }
+}
+
+exports = StringUtils;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/tab_utils.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/tab_utils.js
new file mode 100644
index 00000000000..730322e3d67
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/tab_utils.js
@@ -0,0 +1,28 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Tab-related utilities methods.
+
+ */
+
+goog.provide('mr.TabUtils');
+
+
+/**
+ * @param {number} tabId
+ * @return {!Promise<!Tab>} A promise fulfilled with tab, or rejected if
+ * tab does not exist.
+ */
+mr.TabUtils.getTab = function(tabId) {
+ return new Promise((resolve, reject) => {
+ chrome.tabs.get(tabId, resolve);
+ })
+ .then(tab => {
+ if (!tab) {
+ throw Error('No such tab ' + tabId);
+ }
+ return tab;
+ });
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/throttle.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/throttle.js
new file mode 100644
index 00000000000..ecc2967e9d5
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/throttle.js
@@ -0,0 +1,93 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.Throttle');
+goog.module.declareLegacyNamespace();
+
+
+/**
+ * Throttle will perform an action that is passed in no more than once
+ * per interval (specified in milliseconds). If it gets multiple signals
+ * to perform the action while it is waiting, it will only perform the action
+ * once at the end of the interval.
+ * @final
+ * @template T
+ */
+class Throttle {
+ /**
+ * @param {function(this: T, ...?)} listener Function to callback when the
+ * action is triggered.
+ * @param {number} interval Interval over which to throttle. The listener can
+ * only be called once per interval.
+ * @param {T=} handler Object in whose scope to call the listener.
+ */
+ constructor(listener, interval, handler = undefined) {
+ /** @private @const */
+ this.listener_ = handler != null ? listener.bind(handler) : listener;
+
+ /** @private @const */
+ this.interval_ = interval;
+
+ /** @private @const */
+ this.callback_ = this.onTimer_.bind(this);
+
+ /**
+ * The last arguments passed into `fire`.
+ * @private {!Array<?>}
+ */
+ this.args_ = [];
+
+ /**
+ * Indicates that the action is pending and needs to be fired.
+ * @private {boolean}
+ */
+ this.shouldFire_ = false;
+
+ /**
+ * Timer for scheduling the next callback
+ * @private {?number}
+ */
+ this.timerId_ = null;
+ }
+
+ /**
+ * Notifies the throttle that the action has happened. It will throttle the
+ * call so that the callback is not called too often according to the interval
+ * parameter passed to the constructor, passing the arguments from the last
+ * call of this function into the throttled function.
+ * @param {...?} args Arguments to pass on to the throttled function.
+ */
+ fire(...args) {
+ this.args_ = [...args];
+ if (this.timerId_ == null) {
+ this.doAction_();
+ } else {
+ this.shouldFire_ = true;
+ }
+ }
+
+ /**
+ * Calls the callback
+ * @private
+ */
+ doAction_() {
+ this.timerId_ = setTimeout(this.callback_, this.interval_);
+ this.listener_(...this.args_);
+ }
+
+ /**
+ * Handler for the timer to fire the throttle
+ * @private
+ */
+ onTimer_() {
+ this.timerId_ = null;
+
+ if (this.shouldFire_) {
+ this.shouldFire_ = false;
+ this.doAction_();
+ }
+ }
+}
+
+exports = Throttle;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/throttle_test.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/throttle_test.js
new file mode 100644
index 00000000000..b048a452b5e
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/throttle_test.js
@@ -0,0 +1,100 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.Throttle.test');
+goog.setTestOnly();
+
+const Throttle = goog.require('mr.Throttle');
+const UnitTestUtils = goog.require('mr.UnitTestUtils');
+
+
+describe('mr.Timer', () => {
+ let clock;
+
+ beforeEach(() => {
+ clock = UnitTestUtils.useMockClockAndPromises();
+ });
+
+ afterEach(() => {
+ UnitTestUtils.restoreRealClockAndPromises();
+ });
+
+ it('works', () => {
+ let callBackCount = 0;
+ const callBackFunction = () => {
+ callBackCount++;
+ };
+
+ const throttle = new Throttle(callBackFunction, 100);
+ expect(callBackCount).toBe(0);
+ throttle.fire();
+ expect(callBackCount).toBe(1);
+ throttle.fire();
+ expect(callBackCount).toBe(1);
+ throttle.fire();
+ throttle.fire();
+ expect(callBackCount).toBe(1);
+ clock.tick(101);
+ expect(callBackCount).toBe(2);
+ clock.tick(101);
+ expect(callBackCount).toBe(2);
+ });
+
+ it('binds scope correctly', () => {
+ const interval = 500;
+ const x = {'y': 0};
+ new Throttle(function() {
+ ++this['y'];
+ }, interval, x).fire();
+ expect(x['y']).toBe(1);
+ });
+
+
+ it('binds arguments correctly', () => {
+ const interval = 500;
+ let calls = 0;
+ const throttle = new Throttle((a, b, c) => {
+ ++calls;
+ expect(a).toBe(3);
+ expect(b).toBe('string');
+ expect(c).toBe(false);
+ }, interval);
+
+ throttle.fire(3, 'string', false);
+ expect(calls).toBe(1);
+
+ // fire should always pass the last arguments passed to it into the
+ // decorated function, even if called multiple times.
+ throttle.fire();
+ clock.tick(interval / 2);
+ throttle.fire(8, null, true);
+ throttle.fire(3, 'string', false);
+ clock.tick(interval);
+ expect(calls).toBe(2);
+ });
+
+
+ it('binds arguments and scope correctly', () => {
+ const interval = 500;
+ const x = {'calls': 0};
+ const throttle = new Throttle(function(a, b, c) {
+ ++this['calls'];
+ expect(a).toBe(3);
+ expect(b).toBe('string');
+ expect(c).toBe(false);
+ }, interval, x);
+
+ throttle.fire(3, 'string', false);
+ expect(x['calls']).toBe(1);
+
+ // fire should always pass the last arguments passed to it into the
+ // decorated function, even if called multiple times.
+ throttle.fire();
+ clock.tick(interval / 2);
+ throttle.fire(8, null, true);
+ throttle.fire(3, 'string', false);
+ clock.tick(interval);
+ expect(x['calls']).toBe(2);
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/unit_test_utils.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/unit_test_utils.js
new file mode 100644
index 00000000000..e2d8461b2ab
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/unit_test_utils.js
@@ -0,0 +1,307 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+
+ * @fileoverview Test utilities for unit tests.
+ */
+goog.provide('mr.UnitTestUtils');
+goog.setTestOnly('mr.UnitTestUtils');
+
+goog.require('mr.Assertions');
+goog.require('mr.MockClock');
+goog.require('mr.MockPromise');
+
+
+/**
+ * Creates a mock implementation of the RouteControllerCallbacks interface.
+ * @return {!Object}
+ */
+mr.UnitTestUtils.createRouteControllerCallbacksSpyObj = function() {
+ return jasmine.createSpyObj('RouteControllerCallbacks', [
+ 'onRouteControllerInvalidated', 'sendMediaControlRequest',
+ 'sendVolumeRequest'
+ ]);
+};
+
+
+/**
+ * Creates a Mojo InterfaceRequest-like object with Jasmine spy.
+ * @return {!Object}
+ */
+mr.UnitTestUtils.createMojoRequestSpyObj = function() {
+ return jasmine.createSpyObj('Request', ['close']);
+};
+
+
+/**
+ * Creates a Mojo Binding-like object with Jasmine spy.
+ * @return {!Object}
+ */
+mr.UnitTestUtils.createMojoBindingSpyObj = function() {
+ return jasmine.createSpyObj(
+ 'Binding', ['bind', 'close', 'setConnectionErrorHandler']);
+};
+
+
+/**
+ * Creates a Mojo MediaStatusObserver-like object with Jasmine spies.
+ * @return {!Object}
+ */
+mr.UnitTestUtils.createMojoMediaStatusObserverSpyObj = function() {
+ return {
+ ptr: jasmine.createSpyObj(
+ 'PtrController', ['reset', 'setConnectionErrorHandler']),
+ onMediaStatusUpdated: jasmine.createSpy('onMediaStatusUpdated')
+ };
+};
+
+
+/**
+ * Creates a Jasmine spy object with the same methods of the given constructor
+ * type.
+ * @param {Function} constructor The object's constructor to be mocked. E.g.
+ * MyClass.prototype.
+ * @param {string} mockName The mock's name (will show up in failed test
+ * results).
+ * @return {Object}
+ */
+mr.UnitTestUtils.createMock = function(constructor, mockName) {
+ const methodNames = new Set();
+ while (constructor) {
+ for (let name of Object.getOwnPropertyNames(constructor)) {
+ methodNames.add(name);
+ }
+ constructor = Object.getPrototypeOf(constructor);
+ }
+ return jasmine.createSpyObj(mockName, [...methodNames]);
+};
+
+/**
+ * Replaces parts of the window.mojo API with Jasmine spys used by unit tests.
+ */
+mr.UnitTestUtils.mockMojoApi = function() {
+ window.mojo = {
+ Binding: {},
+ HangoutsMediaRouteController: {},
+ HangoutsMediaStatusExtraData: {},
+ MediaStatus: {
+ PlayState: {PLAYING: 0, PAUSED: 1, BUFFERING: 2},
+ },
+ MediaController: {},
+ RouteControllerType: {kNone: 0, kGeneric: 1, kHangouts: 2},
+ TimeDelta: {},
+ Origin: {}
+ };
+ // We return a copy of the object used to construct the MediaStatus.
+ // This works because the fields in the object maps directly to the fields
+ // in MediaStatus.
+ spyOn(window.mojo, 'HangoutsMediaStatusExtraData')
+ .and.callFake(obj => Object.assign({}, obj));
+ spyOn(window.mojo, 'MediaStatus').and.callFake(obj => Object.assign({}, obj));
+ spyOn(window.mojo, 'TimeDelta').and.callFake(obj => Object.assign({}, obj));
+ spyOn(window.mojo, 'Origin').and.callFake(obj => Object.assign({}, obj));
+};
+
+/**
+ * Replaces parts of the chrome API with Jasmine spy objects. After calling this
+ * function, call restoreChromeApi() during test teardown to restore the
+ * original API.
+ */
+mr.UnitTestUtils.mockChromeApi = function() {
+ mr.UnitTestUtils.originalChromeApi_ = chrome;
+ chrome = {
+ cast: {
+ channel: {
+ onError: jasmine.createSpy('chrome.cast.channel.onError spy'),
+ onMessage: jasmine.createSpy('chrome.cast.channel.onMessage spy')
+ },
+ SessionStatus: {
+ CONNECTED: 'connected',
+ DISCONNECTED: 'disconnected',
+ STOPPED: 'stopped'
+ },
+ VolumeControlType:
+ {ATTENUATION: 'attenuation', FIXED: 'fixed', MASTER: 'master'}
+ },
+ dial: {
+ onDeviceList: jasmine.createSpy('chrome.dial.onDeviceList spy'),
+ onError: jasmine.createSpy('chrome.dial.onError spy')
+ },
+ identity: {
+ onSignInChanged: {
+ addListener:
+ jasmine.createSpy('chrome.identity.onSignInChanged.addListener spy')
+ }
+ },
+ runtime: {
+ onMessage: {
+ addListener:
+ jasmine.createSpy('chrome.runtime.onMessage.addListener spy')
+ },
+ onMessageExternal: {
+ addListener: jasmine.createSpy(
+ 'chrome.runtime.onMessageExternal.addListener spy')
+ },
+ onStartup: {
+ addListener:
+ jasmine.createSpy('chrome.runtime.onStartup.addListener spy')
+ },
+ onSuspend: {
+ addListener:
+ jasmine.createSpy('chrome.runtime.onSuspend.addListener spy')
+ },
+ getManifest: () => {
+ return {version: '1.0'};
+ },
+ sendMessage: jasmine.createSpy('chrome.runtime.sendMessage spy'),
+ },
+ mdns: {onSerivceList: jasmine.createSpy('chrome.mdns.onServiceList spy')},
+ metricsPrivate: {
+ recordMediumTime:
+ jasmine.createSpy('chrome.metricsPrivate.recordMediumTime spy'),
+ recordUserAction:
+ jasmine.createSpy('chrome.metricsPrivate.recordUserAction spy'),
+ },
+ networkingPrivate: {
+ onNetworksChanged:
+ jasmine.createSpy('chrome.networkingPrivate.onNetworksChanged spy')
+ },
+ gcm: {
+ onMessage: {
+ addListener: jasmine.createSpy('chrome.gcm.onMessage.addListener spy')
+ }
+ },
+ };
+};
+
+/**
+ * Restores the original chrome API after it's been mocked by a mockChromeApi()
+ * call.
+ */
+mr.UnitTestUtils.restoreChromeApi = function() {
+ chrome = mr.UnitTestUtils.originalChromeApi_;
+};
+
+/**
+ * Stores a reference to the original chrome API while it is mocked.
+ * @private {?Object}
+ */
+mr.UnitTestUtils.originalChromeApi_ = null;
+
+
+
+/**
+ * @private {mr.MockClock}
+ */
+mr.UnitTestUtils.mockClock_ = null;
+
+
+/**
+ * Installs a mock clock and replaces the native Promise type with goog.Promise.
+ *
+
+ *
+ * @return {!mr.MockClock}
+ */
+mr.UnitTestUtils.useMockClockAndPromises = function() {
+ mr.Assertions.assert(mr.UnitTestUtils.mockClock_ == null);
+ mr.MockPromise.install();
+ const mockClock = new mr.MockClock(true);
+ mr.UnitTestUtils.mockClock_ = mockClock;
+ return mockClock;
+};
+
+
+/**
+ * Undoes the effect of calling useMockClockAndPromises().
+ */
+mr.UnitTestUtils.restoreRealClockAndPromises = function() {
+ mr.UnitTestUtils.mockClock_.uninstall();
+ mr.UnitTestUtils.mockClock_ = null;
+ mr.MockPromise.uninstall();
+};
+
+
+/**
+ * Asserts that mock clock and mock promises are in use.
+ * @private
+ */
+mr.UnitTestUtils.assertUsingMockPromises_ = function() {
+ mr.Assertions.assert(Promise === mr.MockPromise);
+};
+
+
+/**
+ * Waits for the provided promise to resolve.
+ * @param {!Promise<T>} promise The promise to resolve.
+ * @template T
+ */
+mr.UnitTestUtils.awaitPromiseResolved = function(promise) {
+ mr.UnitTestUtils.assertUsingMockPromises_();
+ let resolved = false;
+ promise.then(result => {
+ resolved = true;
+ });
+ mr.MockPromise.callPendingHandlers();
+ expect(resolved).toBe(true);
+};
+
+
+/**
+ * Expects the provided promise to resolve to a value that makes the given
+ * predicate true.
+ * @param {!Promise<T>} promise The promise to resolve.
+ * @param {function(T):boolean} isCorrect A function called with the resolved
+ * value of the promise; expected to return true.
+ * @template T
+ */
+mr.UnitTestUtils.expectPromiseResult = function(promise, isCorrect) {
+ mr.UnitTestUtils.assertUsingMockPromises_();
+ let resolved = false;
+ let actual;
+ promise.then(result => {
+ resolved = true;
+ actual = result;
+ });
+ mr.MockPromise.callPendingHandlers();
+ expect(resolved).toBe(true);
+ expect(isCorrect(actual)).toBe(true);
+};
+
+
+/**
+ * Expects the provided promise to resolve to the given result.
+ * @param {!Promise} promise
+ * @param {*} expectedResult
+ */
+mr.UnitTestUtils.expectPromiseResultToEqual = function(
+ promise, expectedResult) {
+ mr.UnitTestUtils.assertUsingMockPromises_();
+ let resolved = false;
+ let actual;
+ promise.then(result => {
+ resolved = true;
+ actual = result;
+ });
+ mr.MockPromise.callPendingHandlers();
+ expect(resolved).toBe(true);
+ expect(actual).toEqual(expectedResult);
+};
+
+
+/**
+ * Expects the provided promise to resolve to the given result.
+ * @param {!Promise} promise
+ * @param {Error} expectedError
+ */
+mr.UnitTestUtils.expectPromiseRejection = function(promise, expectedError) {
+ mr.UnitTestUtils.assertUsingMockPromises_();
+ let actual;
+ promise.catch(error => {
+ actual = error;
+ });
+ mr.MockPromise.callPendingHandlers();
+ expect(actual).toEqual(expectedError);
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/xhr_manager.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/xhr_manager.js
new file mode 100644
index 00000000000..41017f63493
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/xhr_manager.js
@@ -0,0 +1,191 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.XhrManager');
+goog.module.declareLegacyNamespace();
+
+const Assertions = goog.require('mr.Assertions');
+const PromiseResolver = goog.require('mr.PromiseResolver');
+
+/**
+ * Wraps XmlHttpRequest API with additional functionalities such as queueing,
+ * timeout, and retries.
+ */
+class XhrManager {
+ /**
+ * @param {number} maxRequests The maximum number of concurrent XHR
+ * requests.
+ * @param {number} defaultTimeoutMillis The default timeout for each XHR
+ * request.
+ * @param {number} defaultNumAttempts The default number of attempts for each
+ * operation.
+ */
+ constructor(maxRequests, defaultTimeoutMillis, defaultNumAttempts) {
+ /** @private @const {number} */
+ this.maxRequests_ = maxRequests;
+
+ /** @private @const {number} */
+ this.defaultTimeoutMillis_ = defaultTimeoutMillis;
+
+ /** @private @const {number} */
+ this.defaultNumAttempts_ = defaultNumAttempts;
+
+ /**
+ * Number of pending requests. Does not exceed this.maxRequests_.
+ * @private {number}
+ */
+ this.numPendingRequests_ = 0;
+
+ /**
+ * Holds requests that have not yet been executed.
+ * @private {!Array<!QueueEntry_>}
+ */
+ this.queuedRequests_ = [];
+ }
+
+ /**
+ * Adds a request with the given parameters.
+ * @param {string} url
+ * @param {string} method
+ * @param {string=} body
+ * @param {{timeoutMillis: (number|undefined),
+ * numAttempts: (number|undefined),
+ * headers: (!Array<!Array<string>>|undefined),
+ * responseType: (string|undefined)}=} overrides "headers" is an Array
+ * of pairs of strings. "responseType" is a valid
+ * XMLHttpRequest.responseType enum value.
+ * @return {!Promise<!XMLHttpRequest>} Resolves with the response. Rejects if
+ * the request timed out on all attempts.
+ */
+ send(url, method, body = undefined, {
+ timeoutMillis = this.defaultTimeoutMillis_,
+ numAttempts = this.defaultNumAttempts_,
+ headers = null,
+ responseType = '',
+ } = {}) {
+ const entry = {
+ resolver: new PromiseResolver(),
+ url: url,
+ method: method,
+ headers: headers,
+ responseType: responseType,
+ body: body,
+ timeoutMillis: timeoutMillis,
+ numAttemptsLeft: numAttempts
+ };
+
+ if (this.numPendingRequests_ < this.maxRequests_) {
+ this.startRequest_(entry);
+ } else {
+ this.queuedRequests_.push(entry);
+ }
+
+ return entry.resolver.promise;
+ }
+
+ /**
+ * Starts a request from the request queue if there is room for an additional
+ * pending request.
+ * @private
+ */
+ startNextRequestFromQueue_() {
+ if (this.queuedRequests_.length > 0 &&
+ this.numPendingRequests_ < this.maxRequests_) {
+ const request = this.queuedRequests_.shift();
+ this.startRequest_(request);
+ }
+ }
+
+ /**
+ * Attempts the given request once. If successful, resolves the
+ * PromiseResolver with the result. Otherwise, requeues the request for retry,
+ * or rejects the PromiseResolver if it ran out of attempts. Also processes a
+ * queued request, if any, at the end.
+ * @param {!QueueEntry_} request
+ * @private
+ */
+ startRequest_(request) {
+ this.numPendingRequests_++;
+ Assertions.assert(
+ request.numAttemptsLeft > 0, 'request.numAttemptsLeft > 0');
+ request.numAttemptsLeft--;
+
+ const cleanUpAndStartNextRequest = () => {
+ this.numPendingRequests_--;
+ this.startNextRequestFromQueue_();
+ };
+
+ this.sendOneAttempt_(request).then(
+ response => {
+ request.resolver.resolve(response);
+ cleanUpAndStartNextRequest();
+ },
+ e => {
+ if (request.numAttemptsLeft == 0) {
+ request.resolver.reject(e);
+ } else {
+ // Try it again later by re-adding the request to back of queue.
+
+ this.queuedRequests_.push(request);
+ }
+ cleanUpAndStartNextRequest();
+ });
+ }
+
+ /**
+ * Executes a XMLHttpRequest and returns a Promise that resolves with the
+ * response, or rejects if the request times out.
+ * @param {!QueueEntry_} request
+ * @return {!Promise<!XMLHttpRequest>} Resolves with the response. Rejects if
+ * the request timed out.
+ * @private
+ */
+ sendOneAttempt_(request) {
+ return new Promise((resolve, reject) => {
+ const xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = () => {
+ if (xhr.readyState == XMLHttpRequest.DONE) {
+ resolve(xhr);
+ }
+ };
+
+ xhr.timeout = request.timeoutMillis;
+ xhr.ontimeout = () => {
+ reject(new Error('Timed out'));
+ };
+
+ xhr.open(request.method, request.url, true);
+ if (request.headers == null) {
+ xhr.setRequestHeader(
+ 'Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
+ } else {
+ request.headers.forEach(
+ header => xhr.setRequestHeader(header[0], header[1]));
+ }
+ xhr.responseType = request.responseType;
+ xhr.send(request.body);
+ });
+ }
+}
+
+/** @private @record */
+const QueueEntry_ = class {};
+/** @type {!PromiseResolver<!XMLHttpRequest>} */
+QueueEntry_.prototype.resolver;
+/** @type {string} */
+QueueEntry_.prototype.url;
+/** @type {string} */
+QueueEntry_.prototype.method;
+/** @type {?Array<!Array<string>>} */
+QueueEntry_.prototype.headers;
+/** @type {string} */
+QueueEntry_.prototype.responseType;
+/** @type {string|undefined} */
+QueueEntry_.prototype.body;
+/** @type {number} */
+QueueEntry_.prototype.timeoutMillis;
+/** @type {number} */
+QueueEntry_.prototype.numAttemptsLeft;
+
+exports = XhrManager;
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/utils/xhr_manager_test.js b/chromium/chrome/browser/resources/media_router/extension/src/utils/xhr_manager_test.js
new file mode 100644
index 00000000000..44857e2a23d
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/utils/xhr_manager_test.js
@@ -0,0 +1,185 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.module('mr.XhrManagerTest');
+goog.setTestOnly('mr.XhrManagerTest');
+
+const XhrManager = goog.require('mr.XhrManager');
+
+describe('XhrManager Tests', function() {
+ const MAX_REQUESTS = 5;
+ const DEFAULT_TIMEOUT = 10;
+ const DEFAULT_ATTEMPTS = 3;
+ const SEND_URL = 'https://www.google.com';
+ const BODY = 'body';
+
+ let mockXhr;
+ let xhrManager;
+
+ beforeEach(() => {
+ mockXhr = jasmine.createSpyObj('Xhr', ['open', 'send', 'setRequestHeader']);
+ xhrManager =
+ new XhrManager(MAX_REQUESTS, DEFAULT_TIMEOUT, DEFAULT_ATTEMPTS);
+ });
+
+ it('Resolves on first try with defaults', done => {
+ spyOn(window, 'XMLHttpRequest').and.returnValue(mockXhr);
+ mockXhr.open.and.callFake((method, url, async) => {
+ expect(mockXhr.onreadystatechange).toBeDefined();
+ expect(mockXhr.timeout).toEqual(DEFAULT_TIMEOUT);
+ expect(mockXhr.ontimeout).toBeDefined();
+ expect(method).toEqual('POST');
+ expect(url).toEqual(SEND_URL);
+ expect(async).toBe(true);
+ });
+ const headersThatWereSet = [];
+ mockXhr.setRequestHeader.and.callFake((key, value) => {
+ headersThatWereSet.push([key, value]);
+ });
+ mockXhr.send.and.callFake(body => {
+ expect(mockXhr.open).toHaveBeenCalled();
+ expect(mockXhr.setRequestHeader).toHaveBeenCalled();
+ expect(headersThatWereSet).toEqual([
+ ['Content-Type', 'application/x-www-form-urlencoded;charset=utf-8']
+ ]);
+ expect(mockXhr.responseType).toBe('');
+ expect(body).toEqual(BODY);
+
+ setTimeout(() => {
+ mockXhr.readyState = XMLHttpRequest.DONE;
+ mockXhr.onreadystatechange();
+ }, 0);
+ });
+
+ xhrManager.send(SEND_URL, 'POST', BODY).then(response => {
+ expect(mockXhr.send).toHaveBeenCalled();
+ done();
+ });
+ });
+
+ it('Resolves on first try with overrides', done => {
+ const overrides = {
+ timeoutMillis: 1234,
+ headers: [['Hello', 'World'], ['Eat', 'Cheese']],
+ responseType: 'arraybuffer'
+ };
+
+ spyOn(window, 'XMLHttpRequest').and.returnValue(mockXhr);
+ mockXhr.open.and.callFake((method, url, async) => {
+ expect(mockXhr.onreadystatechange).toBeDefined();
+ expect(mockXhr.timeout).toEqual(overrides.timeoutMillis);
+ expect(mockXhr.ontimeout).toBeDefined();
+ expect(method).toEqual('GET');
+ expect(url).toEqual(SEND_URL);
+ expect(async).toBe(true);
+ });
+ const headersThatWereSet = [];
+ mockXhr.setRequestHeader.and.callFake((key, value) => {
+ headersThatWereSet.push([key, value]);
+ });
+ mockXhr.send.and.callFake(body => {
+ expect(mockXhr.open).toHaveBeenCalled();
+ expect(mockXhr.setRequestHeader).toHaveBeenCalled();
+ expect(headersThatWereSet).toEqual(overrides.headers);
+ expect(mockXhr.responseType).toBe(overrides.responseType);
+ expect(body).toEqual(BODY);
+
+ setTimeout(() => {
+ mockXhr.readyState = XMLHttpRequest.DONE;
+ mockXhr.onreadystatechange();
+ }, 0);
+ });
+
+ xhrManager.send(SEND_URL, 'GET', BODY, overrides).then(response => {
+ expect(mockXhr.send).toHaveBeenCalled();
+ done();
+ });
+ });
+
+ it('Resolves on retry', done => {
+ spyOn(window, 'XMLHttpRequest').and.returnValue(mockXhr);
+ let numAttempts = 0;
+ mockXhr.send.and.callFake(() => {
+ expect(mockXhr.timeout).toBe(DEFAULT_TIMEOUT);
+ ++numAttempts;
+ if (numAttempts <= 1) {
+ setTimeout(() => mockXhr.ontimeout(), DEFAULT_TIMEOUT);
+ } else {
+ setTimeout(() => {
+ mockXhr.readyState = XMLHttpRequest.DONE;
+ mockXhr.onreadystatechange();
+ }, 0);
+ }
+ });
+
+ xhrManager.send(SEND_URL, 'GET').then(() => {
+ expect(numAttempts).toBe(2);
+ done();
+ });
+ });
+
+ it('Queues requests', done => {
+ let xhrs = [];
+ spyOn(window, 'XMLHttpRequest').and.callFake(() => {
+ const mockXhr =
+ jasmine.createSpyObj('Xhr', ['open', 'send', 'setRequestHeader']);
+ mockXhr.send.and.callFake(() => {
+ expect(xhrs.length).toBeLessThan(MAX_REQUESTS);
+ xhrs.push(mockXhr);
+ });
+ return mockXhr;
+ });
+
+ let promises1 = [];
+ let promises2 = [];
+ for (let i = 0; i < MAX_REQUESTS; i++) {
+ promises1.push(xhrManager.send(SEND_URL, 'POST', BODY));
+ }
+ for (let i = 0; i < MAX_REQUESTS; i++) {
+ promises2.push(xhrManager.send(SEND_URL, 'POST', BODY));
+ }
+
+ // Resolve the first 5 requests.
+ expect(xhrs.length).toBe(MAX_REQUESTS);
+ while (xhrs.length > 0) {
+ let xhr = xhrs.shift();
+ setTimeout(() => {
+ xhr.readyState = XMLHttpRequest.DONE;
+ xhr.onreadystatechange();
+ }, 0);
+ }
+
+ Promise.all(promises1).then(() => {
+ // Resolve the next 5 requests.
+ expect(xhrs.length).toBe(MAX_REQUESTS);
+ while (xhrs.length > 0) {
+ let xhr = xhrs.shift();
+ setTimeout(() => {
+ xhr.readyState = XMLHttpRequest.DONE;
+ xhr.onreadystatechange();
+ }, 0);
+ }
+ Promise.all(promises2).then(done);
+ });
+ });
+
+ it('Rejects if all retries failed', done => {
+ spyOn(window, 'XMLHttpRequest').and.returnValue(mockXhr);
+ let numAttempts = 0;
+ mockXhr.send.and.callFake((path, init) => {
+ ++numAttempts;
+ setTimeout(() => mockXhr.ontimeout(), DEFAULT_TIMEOUT);
+ });
+
+ xhrManager.send(SEND_URL, 'GET')
+ .then(
+ _ => {
+ fail('send() unexpectedly succeeded.');
+ },
+ () => {
+ expect(numAttempts).toBe(DEFAULT_ATTEMPTS);
+ done();
+ });
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/webrtc/messages.js b/chromium/chrome/browser/resources/media_router/extension/src/webrtc/messages.js
new file mode 100644
index 00000000000..6f8e6d43b3c
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/webrtc/messages.js
@@ -0,0 +1,200 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.provide('mr.webrtc.AuthReadyMessageData');
+goog.provide('mr.webrtc.ChannelType');
+goog.provide('mr.webrtc.Message');
+goog.provide('mr.webrtc.MessageType');
+goog.provide('mr.webrtc.OfferMessageData');
+
+goog.require('mr.mirror.Settings');
+
+goog.scope(function() {
+
+
+/**
+ * The channel types supported by the cloud MRP.
+ * @enum {string}
+ */
+mr.webrtc.ChannelType = {
+ WEAVE: 'weave',
+ SLARTI: 'slarti',
+ MESI: 'mesi'
+};
+
+
+/**
+ * The types of messages used by the cloud MRP.
+ * @enum {string}
+ */
+mr.webrtc.MessageType = {
+ // TURN messages.
+ GET_TURN_CREDENTIALS: 'GET_TURN_CREDENTIALS', // Request for creds.
+ TURN_CREDENTIALS: 'TURN_CREDENTIALS', // Creds received.
+
+ // Signaling messages.
+ OFFER: 'OFFER', // Peer connection offer.
+ ANSWER: 'ANSWER', // Peer connection answer.
+ KNOCK_ANSWER: 'KNOCK_ANSWER', // Knocking peer connection answer.
+ STOP: 'STOP', // Stop the session.
+
+ // Event messages.
+ SESSION_START_SUCCESS: 'SESSION_START_SUCCESS', // Start success event.
+ SESSION_FAILURE: 'SESSION_FAILURE', // Start failure event.
+ SESSION_END: 'SESSION_END', // Session ended event.
+
+ WEB_RTC_STATS: '__webrtc_stats__', // WebRTC stats message.
+
+ // Hangout session control messages.
+ REFRESH_AUTH: 'REFRESH_AUTH', // Request to refresh the auth token.
+ // Message data for AUTH_READY messages will be an instance of
+ // mr.webwrtc.AuthReadyMessageData.
+ AUTH_READY: 'AUTH_READY', // Response that auth token was updated.
+
+ // Route details control messages.
+ MUTE: 'MUTE', // Request to mute audio.
+ LOCAL_PRESENT: 'LOCAL_PRESENT', // Request to disable conf mode.
+ ROUTE_STATUS_REQUEST: 'STATUS_REQUEST', // Request for route status update.
+ ROUTE_STATUS_RESPONSE:
+ 'STATUS_RESPONSE', // Response to route details status.
+
+ // Hangout issues.
+ HANGOUT_INVALID: 'HANGOUT_INVALID', // Hangout name could not be resolved.
+ HANGOUT_INACTIVE: 'HANGOUT_INACTIVE', // Not enough participants in Hangout.
+
+ // Application messages sent through Presentation API.
+ PRESENTATION_CONNECTION_MESSAGE: 'PRESENTATION_CONNECTION_MESSAGE',
+};
+
+
+/**
+ * Cloud message object used for internal communication.
+ */
+mr.webrtc.Message = class {
+ /**
+ * @param {mr.webrtc.MessageType} type
+ * @param {!Object|undefined=} opt_data
+ */
+ constructor(type, opt_data) {
+ /**
+ * @type {mr.webrtc.MessageType}
+ * @export
+ */
+ this.type = type;
+ /**
+ * @type {!Object|undefined}
+ * @export
+ */
+ this.data = opt_data;
+ }
+
+ /**
+ * Returns the message for the provided JSON string.
+ * @param {string} messageStr
+ * @return {!mr.webrtc.Message}
+ */
+ static fromString(messageStr) {
+ const messageJson = JSON.parse(messageStr);
+ if (!messageJson['type']) {
+ throw Error('Invalid message');
+ }
+ return new Message(
+ /** @type {mr.webrtc.MessageType} */ (messageJson['type']),
+ /** @type {!Object|undefined} */ (messageJson['data']));
+ }
+
+ /**
+ * Constructs an AUTH_READY message.
+ * @param {!mr.webrtc.AuthReadyMessageData} data
+ * @return {!mr.webrtc.Message}
+ */
+ static authReady(data) {
+ return new Message(mr.webrtc.MessageType.AUTH_READY, data);
+ }
+
+ /**
+ * Workaround for broken handling of ES6 classes in Jasmine.
+ * @return {string}
+ */
+ jasmineToString() {
+ return '[mr.webrtc.Message instance]';
+ }
+};
+
+const Message = mr.webrtc.Message;
+
+
+/**
+ * The data for an offer message. Setting the presentation url in the WebRTC
+ * offer message indicates to the receiver that the session is a presentation
+ * session.
+ */
+mr.webrtc.OfferMessageData = class {
+ /**
+ * @param {RTCSessionDescription} description
+ * @param {mr.mirror.Settings=} opt_settings
+ * @param {MediaConstraints=} opt_mediaConstraints
+ * @param {string=} opt_presentationUrl
+ * @param {string=} opt_presentationId
+ */
+ constructor(
+ description, opt_settings, opt_mediaConstraints, opt_presentationUrl,
+ opt_presentationId) {
+ /**
+ * @type {RTCSessionDescription}
+ * @export
+ */
+ this.description = description;
+
+ /**
+ * @type {?mr.mirror.Settings}
+ * @export
+ */
+ this.settings = opt_settings || null;
+
+ /**
+ * @type {?MediaConstraints}
+ * @export
+ */
+ this.mediaConstraints = opt_mediaConstraints || null;
+
+ /**
+ * @type {?string}
+ * @export
+ */
+ this.presentationUrl = opt_presentationUrl || null;
+
+ /**
+ * @type {?string}
+ * @export
+ */
+ this.presentationId = opt_presentationId || null;
+ }
+};
+
+
+/**
+ * Data associated with an AUTH_READY message.
+ *
+ * Fields:
+ *
+ * - isMeeting: True for Thor meetings, false for Hangouts.
+ *
+ * - hangoutId: The public ID of the Hangout/meeting.
+ *
+ * - resolvedId: The resolved (internal) ID of the Hangout/meeting. May be ''.
+ *
+ * - conferenceMode: If defined, used to override the setting of the
+ * isConferenceMode_ field of HangoutSession.
+ *
+ * @typedef {{
+ * isMeeting: boolean,
+ * hangoutId: string,
+ * resolvedId: string,
+ * conferenceMode: (boolean|undefined)
+ * }}
+ */
+mr.webrtc.AuthReadyMessageData;
+
+}); // goog.scope
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/webrtc/peer_connection.js b/chromium/chrome/browser/resources/media_router/extension/src/webrtc/peer_connection.js
new file mode 100644
index 00000000000..4517bc2c85c
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/webrtc/peer_connection.js
@@ -0,0 +1,570 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+goog.provide('mr.webrtc.PeerConnection');
+goog.provide('mr.webrtc.TurnCredential');
+
+goog.require('mr.Assertions');
+goog.require('mr.Logger');
+goog.require('mr.PromiseResolver');
+goog.require('mr.webrtc.PeerConnectionAnalytics');
+
+goog.scope(function() {
+
+
+/**
+ * Type of credentials returned by the TURN credential service. Can be used
+ * as-is in the 'iceConnection' property of the configuration passed to the
+ * webkitRTCPeerConnection() constructor.
+ *
+ * @typedef {{
+ * username: string,
+ * credential: string,
+ * url: string
+ * }}
+ */
+mr.webrtc.TurnCredential;
+
+
+/**
+ * Creates a new PeerConnection.
+ */
+mr.webrtc.PeerConnection = class {
+ /**
+ * @param {string} routeId The route ID for the route to open this
+ * PeerConnection. Used as the data channel ID.
+ * @param {!Array<!mr.webrtc.TurnCredential>} turnCreds
+ * TURN server credentials.
+ */
+ constructor(routeId, turnCreds) {
+ mr.Assertions.assert(
+ webkitRTCPeerConnection !== undefined,
+ 'webkitRTCPeerConnection is not available. Do you need to set flags?');
+
+ /**
+ * Logger instance.
+ * @private {mr.Logger}
+ */
+ this.logger_ = mr.Logger.getInstance('cv2.PeerConnection');
+
+ /**
+ * The media constraints to assign to the PeerConnection.
+ * @type {!MediaConstraints | undefined}
+ * @private
+ */
+ this.mediaConstraints_ = PeerConnection.MEDIA_CONSTRAINTS;
+
+ /**
+ * WebRTC PeerConnection wrapped by this class.
+ * @private {webkitRTCPeerConnection}
+ */
+ this.peerConnection_ = this.createPeerConnection_(turnCreds);
+
+ /**
+ * WebRTC data channel (if it should be enabled).
+ * @type {!RTCDataChannel}
+ * @private
+ */
+ this.dataChannel_ = this.createDataChannel_(routeId);
+
+
+
+ /**
+ * Whether we will try an ICE restart when we are in a disconnected state.
+ * @private {boolean}
+ */
+ this.enableIceRestart_ = true;
+
+
+
+ /**
+ * Resolves when the Web RTC session description is received.
+ * @private {!mr.PromiseResolver<RTCSessionDescription>}
+ */
+ this.sessionDescriptionResolver_ = new mr.PromiseResolver();
+
+ /**
+ * True if sessionDescriptionResolver_ has been resolved.
+ * @private {boolean}
+ */
+ this.sessionDescriptionResolved_ = false;
+
+ /**
+ * The number of ICE candidates that have been received so far.
+ * @private {number}
+ */
+ this.numIceCandidatesReceived_ = 0;
+
+ /**
+ * ID of the timer used to abort ICE candidate gathering.
+ * @private {?number}
+ */
+ this.iceCandidateGatheringTimerId_ = null;
+
+ /**
+ * The time when the offer is created.
+ * @private {number}
+ */
+ this.offerCreationTime_ = 0;
+
+ /**
+ * The time when the most recent ICE candidate was received.
+ * @private {number}
+ */
+ this.lastIceCandidateTime_ = 0;
+
+ /**
+ * True if the PeerConnection has been started.
+ * @private {boolean}
+ */
+ this.started_ = false;
+
+ /**
+ * Callback invoked when a PeerConnection has connection issues.
+ * @private {function()}
+ */
+ this.onConnectionStale_ = () => {};
+
+ /**
+ * Callback invoked when the offer description is ready.
+ * @private {function(RTCSessionDescription)}
+ */
+ this.onOfferDescription_ = () => {};
+
+ /**
+ * Callback invoked when the connection is successfully made.
+ * @private {function(mr.webrtc.PeerConnection.Event)}
+ */
+ this.onConnectionSuccess_ = () => {};
+
+ /**
+ * Callback invoked when the connection fails.
+ * @private {function(mr.webrtc.PeerConnection.Event)}
+ */
+ this.onConnectionFailure_ = () => {};
+
+ /**
+ * Callback invoked when the connection is closed.
+ * @type {function(mr.webrtc.PeerConnection.Event)}
+ * @private
+ */
+ this.onConnectionClosed_ = () => {};
+ }
+
+ /**
+ * @param {function()} staleFn The callback.
+ */
+ setOnConnectionStale(staleFn) {
+ this.onConnectionStale_ = staleFn;
+ }
+
+ /**
+ * @param {function(mr.webrtc.PeerConnection.Event)} successFn
+ */
+ setOnConnectionSuccess(successFn) {
+ this.onConnectionSuccess_ = successFn;
+ }
+
+ /**
+ * @param {function(mr.webrtc.PeerConnection.Event)} failureFn
+ */
+ setOnConnectionFailure(failureFn) {
+ this.onConnectionFailure_ = failureFn;
+ }
+
+ /**
+ * @param {function(mr.webrtc.PeerConnection.Event)} closedFn
+ */
+ setOnConnectionClosed(closedFn) {
+ this.onConnectionClosed_ = closedFn;
+ }
+
+ /**
+ * @param {function(RTCSessionDescription)} onOfferFn
+ */
+ setOnOfferDescriptionReady(onOfferFn) {
+ this.onOfferDescription_ = onOfferFn;
+ }
+
+ /**
+ * @param {function(string)} onMessageFn
+ */
+ setOnDataChannelMessage(onMessageFn) {
+ this.dataChannel_.onmessage = function(event) {
+ onMessageFn(event.data);
+ };
+ }
+
+ /**
+ * @param {boolean} shouldEnable Whether we should enable ICE restart.
+ */
+ enableIceRestart(shouldEnable) {
+ this.enableIceRestart_ = shouldEnable;
+ }
+
+ /**
+ * @return {boolean} true if the PeerConnection has been started.
+ */
+ isStarted() {
+ return this.started_;
+ }
+
+ /**
+ * Returns the configuration data for the WebRTC PeerConnection.
+ * @return {!RTCConfiguration} The configuration.
+ * @param {!Array<!mr.webrtc.TurnCredential>} turnCreds
+ * TURN server credentials.
+ * @private
+ * @suppress {invalidCasts} invalid cast - must be a subtype or supertype
+ * from: {}
+ * to : (!RTCConfiguration)
+ */
+ getPeerConnectionConfig_(turnCreds) {
+ const server = {};
+ server['url'] = 'stun:stun.l.google.com:19302';
+ const config = {};
+ config['iceServers'] = [server].concat(turnCreds);
+ return /** @type {!RTCConfiguration} */ (config);
+ }
+
+ /**
+ * Creates the WebRTC PeerConnection object.
+ * @return {webkitRTCPeerConnection} The new PC.
+ * @param {!Array<!mr.webrtc.TurnCredential>} turnCreds
+ * TURN server credentials.
+ * @private
+ */
+ createPeerConnection_(turnCreds) {
+ const config = this.getPeerConnectionConfig_(turnCreds);
+ const peerConnection = new webkitRTCPeerConnection(config);
+ peerConnection.onicecandidate = this.onIceCandidate_.bind(this);
+ peerConnection.onicegatheringstatechange =
+ this.onIceGatheringStateChange_.bind(this);
+ peerConnection.oniceconnectionstatechange =
+ this.onIceConnectionStateChange_.bind(this);
+ this.logger_.info(
+ () => 'Created webkitRTCPeerConnnection with config: ' +
+ JSON.stringify(config));
+ return peerConnection;
+ }
+
+ /**
+ * Creates a data channel. Must be called in the initiator before the SDP is
+ * created.
+ * @param {string} channelId
+ * @return {!RTCDataChannel}
+ * @private
+ */
+ createDataChannel_(channelId) {
+ const dataChannel =
+ this.peerConnection_.createDataChannel(channelId, {'reliable': false});
+ return dataChannel;
+ }
+
+ /**
+ * Sends the provided message via the data channel.
+ * @param {!Object|string} message The message to send.
+ */
+ sendDataChannelMessage(message) {
+ if (typeof message == 'string') {
+ this.dataChannel_.send(message);
+ } else {
+ this.dataChannel_.send(JSON.stringify(message));
+ }
+ }
+
+ /**
+ * Starts the PeerConnection with any added streams.
+ */
+ start() {
+ if (!this.started_) {
+ this.started_ = true;
+ // Caller initiates offer to peer.
+ this.createOffer_();
+ }
+ }
+
+ /**
+ * Stops this PeerConnection.
+ */
+ stop() {
+ this.logger_.info('Stopping peer connection...');
+ if (this.started_) {
+ this.started_ = false;
+ if (this.peerConnection_.signalingState != 'closed') {
+ this.peerConnection_.close();
+ }
+ }
+ this.peerConnection_ = null;
+ }
+
+ /**
+ * Adds a stream to the PeerConnection.
+ * @param {!MediaStream} stream The media stream to add.
+ */
+ addStream(stream) {
+ this.peerConnection_.addStream(stream);
+ }
+
+ /**
+ * Removes a stream from the PeerConnection.
+ * @param {!MediaStream} stream The media stream to remove.
+ */
+ removeStream(stream) {
+ if (this.started_) {
+ this.peerConnection_.removeStream(stream);
+ }
+ }
+
+ /**
+ * Initiates a call to the peer.
+ * @private
+ */
+ createOffer_() {
+ this.logger_.info('Sending offer to peer.');
+ this.offerCreationTime_ = Date.now();
+
+ this.peerConnection_.createOffer(
+ this.setLocalDescription_.bind(this), error => {
+ this.logger_.warning('Error creating offer.', error);
+ }, this.mediaConstraints_);
+ this.getSessionDescription().then(sessionDescription => {
+ this.onOfferDescription_(sessionDescription);
+ });
+ }
+
+ /**
+ * Sets the local description for the session.
+ * @param {!RTCSessionDescription} sessionDescription The offer.
+ * @private
+ */
+ setLocalDescription_(sessionDescription) {
+
+ this.logger_.info(
+ () =>
+ 'Setting local description: ' + JSON.stringify(sessionDescription));
+ this.peerConnection_.setLocalDescription(
+ sessionDescription,
+ () => {
+ this.logger_.info('Local description set successfully');
+ },
+ error => {
+ this.logger_.warning('Error setting local description.', error);
+ });
+ // Cloud connections only send messages when ICE gathering is complete.
+ // This is done in createOffer_ for cloud connections instead.
+ }
+
+ /**
+ * There's currently a WebRTC "bug" which means we can't JSON.stringify an
+ * RTCSessionDescription. See http://b/19817649. So for now, we'll just put it
+ * in our own object.
+
+ * @param {RTCSessionDescription} description
+ * @return {RTCSessionDescription}
+ * @private
+ */
+ formDescriptionMessage_(description) {
+ return /** @type {RTCSessionDescription} */ (
+ {'type': description.type, 'sdp': description.sdp});
+ }
+
+ /**
+ * Returns the session offer description (if this is the sender) or answer
+ * description (if this is the receiver).
+ * Note that this description contains all of the ICE candidates as well.
+ * @return {!Promise<RTCSessionDescription>}
+ */
+ getSessionDescription() {
+ return this.sessionDescriptionResolver_.promise;
+ }
+
+ /**
+ * Sets the remote description on the peer connection.
+ * @param {!RTCSessionDescription} sessionDescription
+ */
+ setRemoteDescription(sessionDescription) {
+ this.logger_.fine(() => '<===: ' + JSON.stringify(sessionDescription));
+ const description = new RTCSessionDescription(sessionDescription);
+ this.logger_.info(
+ () => 'Setting remote description: ' + JSON.stringify(description));
+ // We received an answer! Just set the description.
+ this.peerConnection_.setRemoteDescription(
+ description,
+ () => {
+ this.logger_.info('Remote description set successfully.');
+ },
+ error => {
+ this.logger_.warning('Error setting remote description.', error);
+ });
+ }
+
+ /**
+ * Resolves the session description once all ice candidates have been
+ * received.
+ * @param {RTCPeerConnectionIceEvent} event The ICE candidate event.
+ * @private
+ */
+ onIceCandidate_(event) {
+ if (event.candidate) {
+ this.numIceCandidatesReceived_++;
+ this.lastIceCandidateTime_ = Date.now();
+ if (this.numIceCandidatesReceived_ == 1) {
+ // This is the first ICE candidate. Set a timer to abort gathering
+ // candidates. This is needed because sometimes the end of ICE
+ // candidate
+ // gathering is not detected right away even though all candidates have
+ // been gathered.
+ mr.Assertions.assert(this.iceCandidateGatheringTimerId_ == null);
+ this.iceCandidateGatheringTimerId_ = setTimeout(() => {
+ this.logger_.info('ICE candidate gathering timed out.');
+ this.iceCandidateGatheringTimerId_ = null;
+ this.resolveSessionDescription_();
+ }, PeerConnection.ICE_CANDIDATE_GATHERING_TIMEOUT_MS_);
+ } else if (this.sessionDescriptionResolved_) {
+ // This branch runs when additional ICE candidates are reported after
+ // the timeout above fires.
+ this.logger_.warning(
+ 'Received ICE candidate after resolving session description.');
+ }
+ } else {
+ this.logger_.info('End of ICE candidates.');
+ mr.webrtc.PeerConnectionAnalytics
+ .recordIceCandidateGatheringReportedDuration(
+ Date.now() - this.offerCreationTime_);
+
+ // This is a no-op if the timout above has already fired.
+ this.resolveSessionDescription_();
+
+ // Record the true duration of candidate gathering based on the time the
+ // last candidate was reported. Don't record anything if no candidates
+ // were
+ // found, because the computed duration will be invalid.
+ if (this.numIceCandidatesReceived_ > 0) {
+ mr.webrtc.PeerConnectionAnalytics
+ .recordIceCandidateGatheringRealDuration(
+ this.lastIceCandidateTime_ - this.offerCreationTime_);
+ }
+ }
+ }
+
+ /**
+ * Called when ICE gathering state changes. Tracks when the candidates are
+ * complete.
+ * @private
+ */
+ onIceGatheringStateChange_() {
+ // This method never appears to be called.
+ const state = this.peerConnection_.iceGatheringState;
+ if (state == 'completed') {
+ this.resolveSessionDescription_();
+ }
+ }
+
+ /**
+ * Resolves the session description promise. Should be called after all ICE
+ * candidates have been received.
+ * @private
+ */
+ resolveSessionDescription_() {
+ clearTimeout(this.iceCandidateGatheringTimerId_);
+ this.iceCandidateGatheringTimerId_ = null;
+ if (!this.sessionDescriptionResolved_) {
+ this.logger_.info(
+ 'Resolving sesion description after gathering ' +
+ this.numIceCandidatesReceived_ + ' ICE candidates.');
+ this.sessionDescriptionResolver_.resolve(
+ this.formDescriptionMessage_(this.peerConnection_.localDescription));
+ this.sessionDescriptionResolved_ = true;
+ }
+ }
+
+ /**
+ * Handles ICE connection state changes. Tries to restart PeerConnection when
+ * we
+ * are in a disconnected state by creating a new offer with the IceRestart
+ * constraint.
+ * @param {Event} event
+ * @private
+ */
+ onIceConnectionStateChange_(event) {
+ if (!this.peerConnection_) return;
+
+ const state = this.peerConnection_.iceConnectionState;
+ this.logger_.info('New ICE connection state: ' + state + '.');
+ if (state == 'connected') {
+ this.onConnectionSuccess_(PeerConnection.Event.ICE_CONNECTED);
+ } else if (state == 'completed') {
+ this.onConnectionSuccess_(PeerConnection.Event.ICE_COMPLETED);
+ } else if (state == 'failed') {
+ this.logger_.warning(
+ () => 'Ice connection failed: ' + JSON.stringify(event));
+ this.onConnectionFailure_(PeerConnection.Event.ICE_FAILED);
+ } else if (state == 'closed') {
+ this.onConnectionClosed_(PeerConnection.Event.ICE_CLOSED);
+ } else if (state == 'disconnected') {
+ this.logger_.warning('Ice connection state is bad.');
+ if (this.enableIceRestart_ && this.isStarted()) {
+ this.logger_.info('Restarting ICE.');
+ this.peerConnection_.createOffer(
+ this.setLocalDescription_.bind(this), error => {
+ this.logger_.warning('Error creating new offer.', error);
+ }, PeerConnection.ICE_RESTART_MEDIA_CONSTRAINTS);
+ } else {
+ this.onConnectionStale_();
+ }
+ }
+ }
+};
+
+const PeerConnection = mr.webrtc.PeerConnection;
+
+
+/**
+ * Default media constraints for tab capture.
+ * @const {!MediaConstraints}
+ */
+PeerConnection.MEDIA_CONSTRAINTS = {
+ 'mandatory': {'OfferToReceiveAudio': true, 'OfferToReceiveVideo': true}
+};
+
+
+/**
+ * Default media constraints during ICE restarts.
+ * @const {!MediaConstraints}
+ */
+PeerConnection.ICE_RESTART_MEDIA_CONSTRAINTS = {
+ 'mandatory': {
+ 'IceRestart': true,
+ 'OfferToReceiveAudio': true,
+ 'OfferToReceiveVideo': true
+ }
+};
+
+
+/**
+ * PeerConnection event types.
+ * @enum {string}
+ */
+PeerConnection.Event = {
+ // events that have a corresponding on on<Event> callback.
+ ADD_STREAM: 'addstream',
+ REMOVE_STREAM: 'removestream',
+ ICE_CANDIDATE: 'icecandidate',
+ // events that do not have a corresponding on on<Event> callback.
+ ICE_CONNECTED: 'iceconnected',
+ ICE_COMPLETED: 'icecompleted',
+ ICE_FAILED: 'icefailed',
+ ICE_CLOSED: 'iceclosed'
+};
+
+
+/**
+ * The maximum time to wait, in ms, for all ICE candidates to be gathered.
+ * Timing starts when the first ICE candidate is seen.
+ * @private @const
+ */
+PeerConnection.ICE_CANDIDATE_GATHERING_TIMEOUT_MS_ = 5 * 1000;
+
+}); // goog.scope
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/webrtc/peer_connection_analytics.js b/chromium/chrome/browser/resources/media_router/extension/src/webrtc/peer_connection_analytics.js
new file mode 100644
index 00000000000..69b5e3fabd8
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/webrtc/peer_connection_analytics.js
@@ -0,0 +1,58 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Defines UMA analytics specific to peer connection.
+ */
+
+goog.provide('mr.webrtc.PeerConnectionAnalytics');
+
+goog.require('mr.Timing');
+
+
+/** @const {*} */
+mr.webrtc.PeerConnectionAnalytics = {};
+
+
+/**
+ * Histogram name for time taken to gather ICE candidates, from the start of
+ * candidate gethering to the time the last candidate is reported.
+ * @private @const {string}
+ */
+mr.webrtc.PeerConnectionAnalytics.ICE_CANDIDATE_GATHERING_REAL_DURATION_ =
+ 'MediaRouter.WebRtc.IceCandidateGathering.Duration.Real';
+
+
+/**
+ * Histogram name for time taken to gather ICE candidates, from the start of
+ * candidate gethering to the time the the end of collection is reported.
+ * @private @const {string}
+ */
+mr.webrtc.PeerConnectionAnalytics.ICE_CANDIDATE_GATHERING_REPORTED_DURATION_ =
+ 'MediaRouter.WebRtc.IceCandidateGathering.Duration.Reported';
+
+
+/**
+ * Records the real duration of ICE candidate gathering.
+ * @param {number} value
+ */
+mr.webrtc.PeerConnectionAnalytics.recordIceCandidateGatheringRealDuration =
+ function(value) {
+ mr.Timing.recordDuration(
+ mr.webrtc.PeerConnectionAnalytics.ICE_CANDIDATE_GATHERING_REAL_DURATION_,
+ value);
+};
+
+
+/**
+ * Records the reported duration of ICE candidate gathering.
+ * @param {number} value
+ */
+mr.webrtc.PeerConnectionAnalytics.recordIceCandidateGatheringReportedDuration =
+ function(value) {
+ mr.Timing.recordDuration(
+ mr.webrtc.PeerConnectionAnalytics
+ .ICE_CANDIDATE_GATHERING_REPORTED_DURATION_,
+ value);
+};
diff --git a/chromium/chrome/browser/resources/media_router/extension/src/webrtc/peer_connection_test.js b/chromium/chrome/browser/resources/media_router/extension/src/webrtc/peer_connection_test.js
new file mode 100644
index 00000000000..bd87b43971d
--- /dev/null
+++ b/chromium/chrome/browser/resources/media_router/extension/src/webrtc/peer_connection_test.js
@@ -0,0 +1,215 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Tests for peer_connection.
+ */
+goog.setTestOnly('peer_connection_test');
+
+goog.require('mr.webrtc.PeerConnection');
+
+describe('mr.webrtc.PeerConnection', function() {
+ let peerConnection;
+ let mockWebkitPeerConnection, mockDataChannel;
+ let mockOnConnectionSuccess, mockOnConnectionStale;
+ let mockOnConnectionClosed, mockOnConnectionFailure;
+ let mockOnDescriptionFn, mockOnDataChannelMessage;
+ let mockClock;
+
+ const DATA_CHANNEL_NAME = 'TEST_DATA_CHANNEL';
+
+ beforeEach(function() {
+ mockOnDescriptionFn = jasmine.createSpy('mockOnDescriptionFn');
+ mockOnConnectionSuccess = jasmine.createSpy('mockOnConnectionSuccess');
+ mockOnConnectionClosed = jasmine.createSpy('mockOnConnectionClosed');
+ mockOnConnectionFailure = jasmine.createSpy('mockOnConnectionFailure');
+ mockOnConnectionStale = jasmine.createSpy('mockOnConnectionStale');
+ mockOnDataChannelMessage = jasmine.createSpy('mockOnDataChannelMessage');
+ // There seems to no longer be a prototype exposed for
+ // webkitRTCPeerConnection as of cr43. Likely duplicate of b/19817649 (and
+ // related). So create a dumb Jasmine mock object with all methods we care
+ // about.
+ mockWebkitPeerConnection = jasmine.createSpyObj('peerConnection', [
+ 'close', 'createOffer', 'createAnswer', 'createDataChannel', 'addStream',
+ 'setLocalDescription', 'setRemoteDescription', 'addIceCandidate',
+ 'removeStream', 'oniceconnected', 'onicecompleted', 'onicefailed',
+ 'onicecandidate', 'oniceconnectionstatechange'
+ ]);
+ mockDataChannel =
+ jasmine.createSpyObj('dataChannel', ['onmessage', 'send']);
+ mockWebkitPeerConnection.createDataChannel.and.returnValue(mockDataChannel);
+ webkitRTCPeerConnection = function(config) {
+ return mockWebkitPeerConnection;
+ };
+ peerConnection = new mr.webrtc.PeerConnection(DATA_CHANNEL_NAME);
+ peerConnection.setOnConnectionSuccess(mockOnConnectionSuccess);
+ peerConnection.setOnConnectionClosed(mockOnConnectionClosed);
+ peerConnection.setOnConnectionFailure(mockOnConnectionFailure);
+ peerConnection.setOnConnectionStale(mockOnConnectionStale);
+ peerConnection.setOnOfferDescriptionReady(mockOnDescriptionFn);
+ });
+
+ it('constructor creates webkit peer connection with data channel, ' +
+ 'does not start',
+ function() {
+ expect(peerConnection.isStarted()).toEqual(false);
+ expect(peerConnection.peerConnection_).toEqual(mockWebkitPeerConnection);
+ expect(peerConnection.dataChannel_).toEqual(mockDataChannel);
+ expect(mockWebkitPeerConnection.createDataChannel)
+ .toHaveBeenCalledWith(DATA_CHANNEL_NAME, {'reliable': false});
+ });
+
+ it('data channel onmessage calls callback', function() {
+ peerConnection.setOnDataChannelMessage(mockOnDataChannelMessage);
+ const event = {'data': 'DATA!!!'};
+ mockDataChannel.onmessage(event);
+ expect(mockOnDataChannelMessage).toHaveBeenCalledWith(event.data);
+ });
+
+ it('sendDataChannelMessage sends message via data channel', function() {
+ // String message.
+ const stringMessage = 'String message!';
+ peerConnection.sendDataChannelMessage(stringMessage);
+ expect(mockDataChannel.send).toHaveBeenCalledWith(stringMessage);
+
+ // Object message.
+ const objMessage = {'obj': 'message'};
+ peerConnection.sendDataChannelMessage(objMessage);
+ expect(mockDataChannel.send)
+ .toHaveBeenCalledWith(JSON.stringify(objMessage));
+ });
+
+ it('start creates offer, sets local description and calls on description ' +
+ 'callback message when ready',
+ function(done) {
+ peerConnection.start();
+
+ expect(peerConnection.isStarted()).toEqual(true);
+ expect(mockWebkitPeerConnection.createOffer).toHaveBeenCalled();
+ const createOfferArgs =
+ mockWebkitPeerConnection.createOffer.calls.mostRecent().args;
+ expect(createOfferArgs[2])
+ .toEqual(mr.webrtc.PeerConnection.MEDIA_CONSTRAINTS);
+
+ // Now call the local description callback (first arg)
+ const description = {'sdp': 'SDP!', 'type': 'TYPE!'};
+ createOfferArgs[0](description);
+ const localDescriptionArgs =
+ mockWebkitPeerConnection.setLocalDescription.calls.mostRecent().args;
+ expect(localDescriptionArgs[0]).toEqual(description);
+
+ // Now trigger the message sending by triggering ICE complete.
+ const webkitLocalDescription = {
+ 'sdp': 'Local SDP with ICE candidates!',
+ 'type': 'Local Type!'
+ };
+ mockWebkitPeerConnection.localDescription = webkitLocalDescription;
+ peerConnection.onIceCandidate_({
+ 'candidate': null // empty candidate signifies that it's done.
+ });
+ mockOnDescriptionFn.and.callFake(arg => {
+ expect(arg).toEqual(webkitLocalDescription);
+ done();
+ });
+ });
+
+ it('stop closes the webkit peer connection', function() {
+ peerConnection.started_ = true;
+ peerConnection.stop();
+
+ expect(peerConnection.isStarted()).toEqual(false);
+ expect(mockWebkitPeerConnection.close).toHaveBeenCalled();
+ });
+
+ it('addStream adds the stream to the webkit peer connection', function() {
+ const stream = {'fake': 'media stream'};
+ peerConnection.addStream(stream);
+
+ expect(mockWebkitPeerConnection.addStream).toHaveBeenCalledWith(stream);
+ });
+
+ it('removeStream removes the stream from the webkit peer connection',
+ function() {
+ const stream = {'fake': 'media stream'};
+ peerConnection.started_ = true;
+ peerConnection.removeStream(stream);
+
+ expect(mockWebkitPeerConnection.removeStream)
+ .toHaveBeenCalledWith(stream);
+ });
+
+ it('setRemoteDescription sets remote description', function() {
+ const description = {'sdp': 'SDP!', 'type': 'TYPE!'};
+ const mockRtcSessionDescription = {'sdp': 'RTC SDP!', 'type': 'RTC TYPE!'};
+ spyOn(window, 'RTCSessionDescription')
+ .and.returnValue(mockRtcSessionDescription);
+
+ peerConnection.setRemoteDescription(description);
+
+ const args =
+ mockWebkitPeerConnection.setRemoteDescription.calls.mostRecent().args;
+ expect(args[0]).toEqual(mockRtcSessionDescription);
+ });
+
+ it('onIceGatheringStateChange_ resolves the session description', done => {
+ const description = {'sdp': 'SDP!', 'type': 'TYPE!'};
+ mockWebkitPeerConnection.iceGatheringState = 'completed';
+ mockWebkitPeerConnection.localDescription = description;
+ peerConnection.onIceGatheringStateChange_();
+
+ peerConnection.sessionDescriptionResolver_.promise.then(value => {
+ expect(value).toEqual(description);
+ done();
+ });
+ });
+
+ it('onIceConnectionStateChange_ calls success callback when connected',
+ function() {
+ mockWebkitPeerConnection.iceConnectionState = 'connected';
+ peerConnection.onIceConnectionStateChange_({});
+ expect(mockOnConnectionSuccess)
+ .toHaveBeenCalledWith(mr.webrtc.PeerConnection.Event.ICE_CONNECTED);
+
+ mockOnConnectionSuccess.calls.reset();
+ mockWebkitPeerConnection.iceConnectionState = 'completed';
+ peerConnection.onIceConnectionStateChange_({});
+ expect(mockOnConnectionSuccess)
+ .toHaveBeenCalledWith(mr.webrtc.PeerConnection.Event.ICE_COMPLETED);
+ });
+
+ it('onConnectionStateChange_ calls closed callback when connection closes',
+ function() {
+ mockWebkitPeerConnection.iceConnectionState = 'closed';
+ peerConnection.onIceConnectionStateChange_({});
+ expect(mockOnConnectionClosed)
+ .toHaveBeenCalledWith(mr.webrtc.PeerConnection.Event.ICE_CLOSED);
+ });
+
+ it('onConnectionStateChange_ calls failure callback when connection fails',
+ function() {
+ mockWebkitPeerConnection.iceConnectionState = 'failed';
+ peerConnection.onIceConnectionStateChange_({});
+ expect(mockOnConnectionFailure)
+ .toHaveBeenCalledWith(mr.webrtc.PeerConnection.Event.ICE_FAILED);
+ });
+
+ it('onIceConnectionStateChange_ tries to re-connect when disconnected',
+ function() {
+ peerConnection.started_ = true;
+ peerConnection.enableIceRestart_ = true;
+
+ mockWebkitPeerConnection.iceConnectionState = 'disconnected';
+ peerConnection.onIceConnectionStateChange_({});
+
+ const args =
+ mockWebkitPeerConnection.createOffer.calls.mostRecent().args;
+ expect(args[2]).toEqual(
+ mr.webrtc.PeerConnection.ICE_RESTART_MEDIA_CONSTRAINTS);
+
+ // Instead, try when enableIceRestart is false.
+ peerConnection.enableIceRestart_ = false;
+ peerConnection.onIceConnectionStateChange_({});
+ expect(mockOnConnectionStale).toHaveBeenCalled();
+ });
+});
diff --git a/chromium/chrome/browser/resources/media_router/media_router_ui_interface.js b/chromium/chrome/browser/resources/media_router/media_router_ui_interface.js
index 8cd1ca3406d..2c1bce1a3ad 100644
--- a/chromium/chrome/browser/resources/media_router/media_router_ui_interface.js
+++ b/chromium/chrome/browser/resources/media_router/media_router_ui_interface.js
@@ -16,6 +16,9 @@ cr.define('media_router.ui', function() {
// The route-controls element. Is null if the route details view isn't open.
var routeControls = null;
+ // The initial height for |container|.
+ var initialMaxHeight = 0;
+
/**
* Handles response of previous create route attempt.
*
@@ -64,6 +67,11 @@ cr.define('media_router.ui', function() {
function setElements(mediaRouterContainer, mediaRouterHeader) {
container = mediaRouterContainer;
header = mediaRouterHeader;
+
+ if (initialMaxHeight) {
+ container.updateMaxDialogHeight(initialMaxHeight);
+ initialMaxHeight = 0;
+ }
}
/**
@@ -183,7 +191,12 @@ cr.define('media_router.ui', function() {
* @param {number} height
*/
function updateMaxHeight(height) {
- container.updateMaxDialogHeight(height);
+ if (container) {
+ container.updateMaxDialogHeight(height);
+ } else {
+ // Update the max height once |container| gets set.
+ initialMaxHeight = height;
+ }
}
/**
diff --git a/chromium/chrome/browser/resources/net_internals/http_cache_view.html b/chromium/chrome/browser/resources/net_internals/http_cache_view.html
index e2c45132725..9af570e3229 100644
--- a/chromium/chrome/browser/resources/net_internals/http_cache_view.html
+++ b/chromium/chrome/browser/resources/net_internals/http_cache_view.html
@@ -1,8 +1,4 @@
<div id=http-cache-view-tab-content class=content-box>
- <div class="hide-when-not-capturing">
- <a href="chrome://view-http-cache" target=_blank>Explore cache entries</a>
- </div>
-
<h4>Statistics</h4>
<div id=http-cache-view-cache-stats>Nothing loaded yet.</div>
</div>
diff --git a/chromium/chrome/browser/resources/ntp4/new_tab.js b/chromium/chrome/browser/resources/ntp4/new_tab.js
index c6564c4f062..98066bfe562 100644
--- a/chromium/chrome/browser/resources/ntp4/new_tab.js
+++ b/chromium/chrome/browser/resources/ntp4/new_tab.js
@@ -284,7 +284,8 @@ cr.define('ntp', function() {
var headerContainer = $('login-status-header-container');
headerContainer.classList.toggle('login-status-icon', !!iconURL);
- headerContainer.style.backgroundImage = iconURL ? url(iconURL) : 'none';
+ headerContainer.style.backgroundImage =
+ iconURL ? getUrlForCss(iconURL) : 'none';
}
if (shouldShowLoginBubble) {
diff --git a/chromium/chrome/browser/resources/offline_pages/offline_internals.html b/chromium/chrome/browser/resources/offline_pages/offline_internals.html
index 013cfd09390..f7ab768f209 100644
--- a/chromium/chrome/browser/resources/offline_pages/offline_internals.html
+++ b/chromium/chrome/browser/resources/offline_pages/offline_internals.html
@@ -58,24 +58,22 @@
<table class="stored-pages-table">
<thead>
<tr>
- <th>&nbsp;</th>
+ <th>#</th>
+ <th></th>
<th>URL</th>
<th>Namespace</th>
<th>Size (Kb)</th>
- <th>Expired</th>
- <th>Request Origin</th>
</tr>
</thead>
<tbody id="stored-pages"> </tbody>
</table>
<template id="stored-pages-table-row">
<tr>
+ <td></td>
<td><input type="checkbox" name="stored"></td>
<td><a></a></td>
<td></td>
<td></td>
- <td></td>
- <td></td>
</tr>
</template>
<div id="page-actions-info" class="dump"></div>
diff --git a/chromium/chrome/browser/resources/offline_pages/offline_internals.js b/chromium/chrome/browser/resources/offline_pages/offline_internals.js
index d9034ccda18..f184c8c005b 100644
--- a/chromium/chrome/browser/resources/offline_pages/offline_internals.js
+++ b/chromium/chrome/browser/resources/offline_pages/offline_internals.js
@@ -35,18 +35,27 @@ cr.define('offlineInternals', function() {
var template = $('stored-pages-table-row');
var td = template.content.querySelectorAll('td');
- for (let page of pages) {
- var checkbox = td[0].querySelector('input');
+ for (let pageIndex = 0; pageIndex < pages.length; pageIndex++) {
+ var page = pages[pageIndex];
+ td[0].textContent = pageIndex;
+ var checkbox = td[1].querySelector('input');
checkbox.setAttribute('value', page.id);
- var link = td[1].querySelector('a');
+ var link = td[2].querySelector('a');
link.setAttribute('href', page.onlineUrl);
- link.textContent = page.onlineUrl;
+ var maxUrlCharsPerLine = 50;
+ if (page.onlineUrl.length > maxUrlCharsPerLine) {
+ link.textContent = '';
+ for (let i = 0; i < page.onlineUrl.length; i += maxUrlCharsPerLine) {
+ link.textContent += page.onlineUrl.slice(i, i + maxUrlCharsPerLine);
+ link.textContent += '\r\n';
+ }
+ } else {
+ link.textContent = page.onlineUrl;
+ }
- td[2].textContent = page.namespace;
- td[3].textContent = Math.round(page.size / 1024);
- td[4].textContent = page.isExpired;
- td[5].textContent = page.requestOrigin;
+ td[3].textContent = page.namespace;
+ td[4].textContent = Math.round(page.size / 1024);
var row = document.importNode(template.content, true);
storedPagesTable.appendChild(row);
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.html b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.html
index 7320212941e..174b498e017 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.html
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.html
@@ -8,8 +8,8 @@
<template>
<style>
#item {
- @apply(--layout-center);
- @apply(--layout-horizontal);
+ @apply --layout-center;
+ @apply --layout-horizontal;
cursor: pointer;
height: 30px;
position: relative;
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html b/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html
index 8ada7527043..a2c53b9ee6d 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-page-selector/viewer-page-selector.html
@@ -15,10 +15,9 @@
}
#pageselector {
- --container: { visibility: hidden; };
+ --container: { display: none; };
--paper-input-container-underline: var(--container);
--paper-input-container-underline-focus: var(--container);
- display: inline-block;
padding: 0;
width: 1ch;
}
@@ -26,6 +25,7 @@
#input {
-webkit-margin-start: -3px;
color: #fff;
+ height: 100%;
line-height: 18px;
padding: 3px;
text-align: end;
@@ -43,10 +43,19 @@
}
#pagelength-spacer {
- display: inline-block;
+ -webkit-margin-start: -2px;
+ padding-bottom: 1px;
text-align: start;
}
+ #pageselector,
+ #slash,
+ #pagelength-spacer {
+ display: inline-block;
+ margin-bottom: 2px;
+ vertical-align: middle;
+ }
+
#input,
#slash,
#pagelength {
@@ -54,7 +63,7 @@
}
</style>
<paper-input-container id="pageselector" no-label-float>
- <input id="input" is="iron-input" value="{{pageNo}}"
+ <input id="input" is="iron-input" value="{{pageNo}}" slot="input"
prevent-invalid-input allowed-pattern="\d" on-mouseup="select"
on-change="pageNoCommitted" aria-label$="{{strings.labelPageNumber}}">
</paper-input-container>
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html
index 1d5c1ad9bb5..af50fd9f2ab 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html
@@ -28,7 +28,7 @@
}
#title {
- @apply(--layout-flex-5);
+ @apply --layout-flex-5;
font-size: 0.87rem;
font-weight: 500;
overflow: hidden;
@@ -37,7 +37,7 @@
}
#pageselector-container {
- @apply(--layout-flex-1);
+ @apply --layout-flex-1;
text-align: center;
/* The container resizes according to the width of the toolbar. On small
* screens with large numbers of pages, overflow page numbers without
@@ -46,7 +46,7 @@
}
#buttons {
- @apply(--layout-flex-5);
+ @apply --layout-flex-5;
text-align: end;
user-select: none;
}
@@ -68,7 +68,7 @@
}
#toolbar {
- @apply(--shadow-elevation-2dp);
+ @apply --shadow-elevation-2dp;
background-color: rgb(50, 54, 57);
color: rgb(241, 241, 241);
display: flex;
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html
index 2e3e6182753..95d75885720 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html
@@ -26,7 +26,7 @@
}
#dropdown {
- @apply(--shadow-elevation-2dp);
+ @apply --shadow-elevation-2dp;
background-color: rgb(256, 256, 256);
border-radius: 4px;
color: var(--primary-text-color);
diff --git a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.html b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.html
index 83d3d8df7f0..28ed7680a35 100644
--- a/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.html
+++ b/chromium/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.html
@@ -21,7 +21,7 @@
}
paper-fab {
- @apply(--shadow-elevation-4dp);
+ @apply --shadow-elevation-4dp;
--paper-fab-keyboard-focus-background: var(--viewer-icon-ink-color);
--paper-fab-mini: {
height: 36px;
diff --git a/chromium/chrome/browser/resources/pdf/open_pdf_params_parser.js b/chromium/chrome/browser/resources/pdf/open_pdf_params_parser.js
index e573656c3fd..5903e403366 100644
--- a/chromium/chrome/browser/resources/pdf/open_pdf_params_parser.js
+++ b/chromium/chrome/browser/resources/pdf/open_pdf_params_parser.js
@@ -2,25 +2,28 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-var OpenPDFParamsParser;
-
(function() {
'use strict';
/**
- * Creates a new OpenPDFParamsParser. This parses the open pdf parameters
- * passed in the url to set initial viewport settings for opening the pdf.
- * @param {!Function} getNamedDestinationsFunction The function called to fetch
- * the page number for a named destination.
- * @constructor
+ * Parses the open pdf parameters passed in the url to set initial viewport
+ * settings for opening the pdf.
*/
-OpenPDFParamsParser = function(getNamedDestinationsFunction) {
- this.outstandingRequests_ = [];
- this.getNamedDestinationsFunction_ = getNamedDestinationsFunction;
-};
+window.OpenPDFParamsParser = class {
+ /**
+ * Constructor.
+ * @param {function(Object)} postMessageCallback
+ * Function called to fetch information for a named destination.
+ */
+ constructor(postMessageCallback) {
+ /** @private {!Array<!Object>} */
+ this.outstandingRequests_ = [];
+
+ /** @private {!function(Object)} */
+ this.postMessageCallback_ = postMessageCallback;
+ }
-OpenPDFParamsParser.prototype = {
/**
* @private
* Parse zoom parameter of open PDF parameters. The PDF should be opened at
@@ -28,14 +31,14 @@ OpenPDFParamsParser.prototype = {
* @param {string} paramValue zoom value.
* @return {Object} Map with zoom parameters (zoom and position).
*/
- parseZoomParam_: function(paramValue) {
- var paramValueSplit = paramValue.split(',');
+ parseZoomParam_(paramValue) {
+ const paramValueSplit = paramValue.split(',');
if (paramValueSplit.length != 1 && paramValueSplit.length != 3)
return {};
// User scale of 100 means zoom value of 100% i.e. zoom factor of 1.0.
- var zoomFactor = parseFloat(paramValueSplit[0]) / 100;
- if (isNaN(zoomFactor))
+ const zoomFactor = parseFloat(paramValueSplit[0]) / 100;
+ if (Number.isNaN(zoomFactor))
return {};
// Handle #zoom=scale.
@@ -44,12 +47,12 @@ OpenPDFParamsParser.prototype = {
}
// Handle #zoom=scale,left,top.
- var position = {
+ const position = {
x: parseFloat(paramValueSplit[1]),
y: parseFloat(paramValueSplit[2])
};
return {'position': position, 'zoom': zoomFactor};
- },
+ }
/**
* @private
@@ -58,14 +61,14 @@ OpenPDFParamsParser.prototype = {
* @param {string} paramValue view value.
* @return {Object} Map with view parameters (view and viewPosition).
*/
- parseViewParam_: function(paramValue) {
- var viewModeComponents = paramValue.toLowerCase().split(',');
+ parseViewParam_(paramValue) {
+ const viewModeComponents = paramValue.toLowerCase().split(',');
if (viewModeComponents.length < 1)
return {};
- var params = {};
- var viewMode = viewModeComponents[0];
- var acceptsPositionParam;
+ const params = {};
+ const viewMode = viewModeComponents[0];
+ let acceptsPositionParam;
if (viewMode === 'fit') {
params['view'] = FittingType.FIT_TO_PAGE;
acceptsPositionParam = false;
@@ -80,12 +83,12 @@ OpenPDFParamsParser.prototype = {
if (!acceptsPositionParam || viewModeComponents.length < 2)
return params;
- var position = parseFloat(viewModeComponents[1]);
- if (!isNaN(position))
+ const position = parseFloat(viewModeComponents[1]);
+ if (!Number.isNaN(position))
params['viewPosition'] = position;
return params;
- },
+ }
/**
* Parse the parameters encoded in the fragment of a URL into a dictionary.
@@ -93,14 +96,14 @@ OpenPDFParamsParser.prototype = {
* @param {string} url to parse
* @return {Object} Key-value pairs of URL parameters
*/
- parseUrlParams_: function(url) {
- var params = {};
+ parseUrlParams_(url) {
+ const params = {};
- var paramIndex = url.search('#');
+ const paramIndex = url.search('#');
if (paramIndex == -1)
return params;
- var paramTokens = url.substring(paramIndex + 1).split('&');
+ const paramTokens = url.substring(paramIndex + 1).split('&');
if ((paramTokens.length == 1) && (paramTokens[0].search('=') == -1)) {
// Handle the case of http://foo.com/bar#NAMEDDEST. This is not
// explicitly mentioned except by example in the Adobe
@@ -109,15 +112,15 @@ OpenPDFParamsParser.prototype = {
return params;
}
- for (var i = 0; i < paramTokens.length; ++i) {
- var keyValueSplit = paramTokens[i].split('=');
+ for (let paramToken of paramTokens) {
+ const keyValueSplit = paramToken.split('=');
if (keyValueSplit.length != 2)
continue;
params[keyValueSplit[0]] = keyValueSplit[1];
}
return params;
- },
+ }
/**
* Parse PDF url parameters used for controlling the state of UI. These need
@@ -126,15 +129,15 @@ OpenPDFParamsParser.prototype = {
* @param {string} url that needs to be parsed.
* @return {Object} parsed url parameters.
*/
- getUiUrlParams: function(url) {
- var params = this.parseUrlParams_(url);
- var uiParams = {toolbar: true};
+ getUiUrlParams(url) {
+ const params = this.parseUrlParams_(url);
+ const uiParams = {toolbar: true};
if ('toolbar' in params && params['toolbar'] == 0)
uiParams.toolbar = false;
return uiParams;
- },
+ }
/**
* Parse PDF url parameters. These parameters are mentioned in the url
@@ -144,16 +147,16 @@ OpenPDFParamsParser.prototype = {
* @param {string} url that needs to be parsed.
* @param {Function} callback function to be called with viewport info.
*/
- getViewportFromUrlParams: function(url, callback) {
- var params = {};
+ getViewportFromUrlParams(url, callback) {
+ const params = {};
params['url'] = url;
- var urlParams = this.parseUrlParams_(url);
+ const urlParams = this.parseUrlParams_(url);
if ('page' in urlParams) {
// |pageNumber| is 1-based, but goToPage() take a zero-based page number.
- var pageNumber = parseInt(urlParams['page'], 10);
- if (!isNaN(pageNumber) && pageNumber > 0)
+ const pageNumber = parseInt(urlParams['page'], 10);
+ if (!Number.isNaN(pageNumber) && pageNumber > 0)
params['page'] = pageNumber - 1;
}
@@ -165,11 +168,14 @@ OpenPDFParamsParser.prototype = {
if (params.page === undefined && 'nameddest' in urlParams) {
this.outstandingRequests_.push({callback: callback, params: params});
- this.getNamedDestinationsFunction_(urlParams['nameddest']);
+ this.postMessageCallback_({
+ type: 'getNamedDestination',
+ namedDestination: urlParams['nameddest']
+ });
} else {
callback(params);
}
- },
+ }
/**
* This is called when a named destination is received and the page number
@@ -177,12 +183,12 @@ OpenPDFParamsParser.prototype = {
* @param {number} pageNumber The page corresponding to the named destination
* requested.
*/
- onNamedDestinationReceived: function(pageNumber) {
- var outstandingRequest = this.outstandingRequests_.shift();
+ onNamedDestinationReceived(pageNumber) {
+ const outstandingRequest = this.outstandingRequests_.shift();
if (pageNumber != -1)
outstandingRequest.params.page = pageNumber;
outstandingRequest.callback(outstandingRequest.params);
- },
+ }
};
}());
diff --git a/chromium/chrome/browser/resources/pdf/pdf.js b/chromium/chrome/browser/resources/pdf/pdf.js
index 4a8cb93199e..029f5b231af 100644
--- a/chromium/chrome/browser/resources/pdf/pdf.js
+++ b/chromium/chrome/browser/resources/pdf/pdf.js
@@ -106,15 +106,20 @@ function PDFViewer(browserApi) {
this.isUserInitiatedEvent_ = true;
/**
- * @type {PDFMetrics}
+ * @type {!PDFMetrics}
*/
this.metrics =
(chrome.metricsPrivate ? new PDFMetricsImpl() : new PDFMetricsDummy());
this.metrics.onDocumentOpened();
+ /**
+ * @private {!PDFCoordsTransformer}
+ */
+ this.coordsTransformer_ =
+ new PDFCoordsTransformer(this.postMessage_.bind(this));
+
// Parse open pdf parameters.
- this.paramsParser_ =
- new OpenPDFParamsParser(this.getNamedDestination_.bind(this));
+ this.paramsParser_ = new OpenPDFParamsParser(this.postMessage_.bind(this));
var toolbarEnabled =
this.paramsParser_.getUiUrlParams(this.originalUrl_).toolbar &&
!this.isPrintPreview_;
@@ -224,9 +229,6 @@ function PDFViewer(browserApi) {
this.toolbar_.docTitle = getFilenameFromURL(this.originalUrl_);
}
- this.coordsTransformer_ =
- new PDFCoordsTransformer(this.plugin_.postMessage.bind(this.plugin_));
-
document.body.addEventListener('change-page', e => {
this.viewport_.goToPage(e.detail.page);
if (e.detail.origin == 'bookmark')
@@ -391,7 +393,7 @@ PDFViewer.prototype = {
return;
case 65: // 'a' key.
if (e.ctrlKey || e.metaKey) {
- this.plugin_.postMessage({type: 'selectAll'});
+ this.postMessage_({type: 'selectAll'});
// Since we do selection ourselves.
e.preventDefault();
}
@@ -451,7 +453,7 @@ PDFViewer.prototype = {
*/
rotateClockwise_: function() {
this.metrics.onRotation();
- this.plugin_.postMessage({type: 'rotateClockwise'});
+ this.postMessage_({type: 'rotateClockwise'});
},
/**
@@ -460,7 +462,7 @@ PDFViewer.prototype = {
*/
rotateCounterClockwise_: function() {
this.metrics.onRotation();
- this.plugin_.postMessage({type: 'rotateCounterclockwise'});
+ this.postMessage_({type: 'rotateCounterclockwise'});
},
/**
@@ -488,7 +490,7 @@ PDFViewer.prototype = {
* Notify the plugin to print.
*/
print_: function() {
- this.plugin_.postMessage({type: 'print'});
+ this.postMessage_({type: 'print'});
},
/**
@@ -496,17 +498,7 @@ PDFViewer.prototype = {
* Notify the plugin to save.
*/
save_: function() {
- this.plugin_.postMessage({type: 'save'});
- },
-
- /**
- * Fetches the page number corresponding to the given named destination from
- * the plugin.
- * @param {string} name The namedDestination to fetch page number from plugin.
- */
- getNamedDestination_: function(name) {
- this.plugin_.postMessage(
- {type: 'getNamedDestination', namedDestination: name});
+ this.postMessage_({type: 'save'});
},
/**
@@ -630,12 +622,22 @@ PDFViewer.prototype = {
* @param {Object} event a password-submitted event.
*/
onPasswordSubmitted_: function(event) {
- this.plugin_.postMessage(
+ this.postMessage_(
{type: 'getPasswordComplete', password: event.detail.password});
},
/**
* @private
+ * Post a message to the PPAPI plugin. Some messages will cause an async reply
+ * to be received through handlePluginMessage_().
+ * @param {Object} message Message to post.
+ */
+ postMessage_: function(message) {
+ this.plugin_.postMessage(message);
+ },
+
+ /**
+ * @private
* An event handler for handling message events received from the plugin.
* @param {MessageObject} message a message event.
*/
@@ -747,13 +749,13 @@ PDFViewer.prototype = {
* reacting to scroll events while zoom is taking place to avoid flickering.
*/
beforeZoom_: function() {
- this.plugin_.postMessage({type: 'stopScrolling'});
+ this.postMessage_({type: 'stopScrolling'});
if (this.viewport_.pinchPhase == Viewport.PinchPhase.PINCH_START) {
var position = this.viewport_.position;
var zoom = this.viewport_.zoom;
var pinchPhase = this.viewport_.pinchPhase;
- this.plugin_.postMessage({
+ this.postMessage_({
type: 'viewport',
userInitiated: true,
zoom: zoom,
@@ -776,7 +778,7 @@ PDFViewer.prototype = {
var pinchCenter = this.viewport_.pinchCenter || {x: 0, y: 0};
var pinchPhase = this.viewport_.pinchPhase;
- this.plugin_.postMessage({
+ this.postMessage_({
type: 'viewport',
userInitiated: this.isUserInitiatedEvent_,
zoom: zoom,
@@ -935,7 +937,7 @@ PDFViewer.prototype = {
case 'getSelectedText':
case 'print':
case 'selectAll':
- this.plugin_.postMessage(message.data);
+ this.postMessage_(message.data);
break;
}
},
@@ -952,7 +954,7 @@ PDFViewer.prototype = {
switch (message.data.type.toString()) {
case 'loadPreviewPage':
- this.plugin_.postMessage(message.data);
+ this.postMessage_(message.data);
return true;
case 'resetPrintPreviewMode':
this.loadState_ = LoadState.LOADING;
@@ -977,7 +979,7 @@ PDFViewer.prototype = {
this.pageIndicator_.pageLabels = message.data.pageNumbers;
- this.plugin_.postMessage({
+ this.postMessage_({
type: 'resetPrintPreviewMode',
url: message.data.url,
grayscale: message.data.grayscale,
diff --git a/chromium/chrome/browser/resources/plugin_metadata/plugins_linux.json b/chromium/chrome/browser/resources/plugin_metadata/plugins_linux.json
index 9d4b50b6b41..d00ba641dd8 100644
--- a/chromium/chrome/browser/resources/plugin_metadata/plugins_linux.json
+++ b/chromium/chrome/browser/resources/plugin_metadata/plugins_linux.json
@@ -1,5 +1,5 @@
{
- "x-version": 28,
+ "x-version": 29,
"google-talk": {
"mime_types": [
],
@@ -80,9 +80,9 @@
],
"versions": [
{
- "version": "28.0.0.161",
+ "version": "29.0.0.140",
"status": "up_to_date",
- "reference": "https://helpx.adobe.com/security/products/flash-player/apsb18-03.html"
+ "reference": "https://helpx.adobe.com/security/products/flash-player/apsb18-08.html"
}
],
"lang": "en-US",
diff --git a/chromium/chrome/browser/resources/plugin_metadata/plugins_mac.json b/chromium/chrome/browser/resources/plugin_metadata/plugins_mac.json
index 5a7f063708f..a4feba50406 100644
--- a/chromium/chrome/browser/resources/plugin_metadata/plugins_mac.json
+++ b/chromium/chrome/browser/resources/plugin_metadata/plugins_mac.json
@@ -1,5 +1,5 @@
{
- "x-version": 34,
+ "x-version": 35,
"google-talk": {
"mime_types": [
],
@@ -115,9 +115,9 @@
],
"versions": [
{
- "version": "28.0.0.161",
+ "version": "29.0.0.140",
"status": "requires_authorization",
- "reference": "https://helpx.adobe.com/security/products/flash-player/apsb18-03.html"
+ "reference": "https://helpx.adobe.com/security/products/flash-player/apsb18-08.html"
}
],
"lang": "en-US",
diff --git a/chromium/chrome/browser/resources/plugin_metadata/plugins_win.json b/chromium/chrome/browser/resources/plugin_metadata/plugins_win.json
index 4d356b256f0..45880e4aefa 100644
--- a/chromium/chrome/browser/resources/plugin_metadata/plugins_win.json
+++ b/chromium/chrome/browser/resources/plugin_metadata/plugins_win.json
@@ -1,5 +1,5 @@
{
- "x-version": 43,
+ "x-version": 44,
"google-talk": {
"mime_types": [
],
@@ -137,9 +137,9 @@
],
"versions": [
{
- "version": "28.0.0.161",
+ "version": "29.0.0.140",
"status": "requires_authorization",
- "reference": "https://helpx.adobe.com/security/products/flash-player/apsb18-03.html"
+ "reference": "https://helpx.adobe.com/security/products/flash-player/apsb18-08.html"
}
],
"lang": "en-US",
diff --git a/chromium/chrome/browser/resources/policy.css b/chromium/chrome/browser/resources/policy.css
index d9b74acf519..45df8882782 100644
--- a/chromium/chrome/browser/resources/policy.css
+++ b/chromium/chrome/browser/resources/policy.css
@@ -36,11 +36,6 @@ html[dir='rtl'] div.left-aligned-button {
float: right;
}
-div.chrome-for-work {
- -webkit-padding-start: 25px;
- display: inline-block;
-}
-
section.status-box-section {
clear: both;
} \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/policy.html b/chromium/chrome/browser/resources/policy.html
index e03b6758dd7..93a74fdf41d 100644
--- a/chromium/chrome/browser/resources/policy.html
+++ b/chromium/chrome/browser/resources/policy.html
@@ -36,10 +36,6 @@
<div class="left-aligned-button">
<button id="export-policies">$i18n{exportPoliciesJSON}</button>
</div>
- <div class="chrome-for-work">
- <a href="http://g.co/chromeent/learn" target="_blank">
- <span>$i18n{chromeForWork}</span></a>
- </div>
<div id="show-unset-container" class="show-unset-checkbox">
<label>
<input id="show-unset" type="checkbox">
diff --git a/chromium/chrome/browser/resources/predictors/resource_prefetch_predictor.html b/chromium/chrome/browser/resources/predictors/resource_prefetch_predictor.html
index ad8c3a7cae5..61edce46557 100644
--- a/chromium/chrome/browser/resources/predictors/resource_prefetch_predictor.html
+++ b/chromium/chrome/browser/resources/predictors/resource_prefetch_predictor.html
@@ -1,50 +1,4 @@
<div id='rpp_enabled'>
- <tabbox id="rpp_data">
- <tabs>
- <tab>URL Table Cache</tab>
- <tab>Host Table Cache</tab>
- <tab>Origin Table Cache</tab>
- </tabs>
- <tabpanels>
- <tabpanel>
- <table>
- <thead>
- <tr>
- <th>Main Frame Url</th>
- <th>Resource Url</th>
- <th>Resource Type</th>
- <th>Num Hits</th>
- <th>Num Misses</th>
- <th>Consec Misses</th>
- <th>Average Position</th>
- <th>Score</th>
- <th>Before FCP</th>
- </tr>
- </thead>
- <tbody id="rpp_url_body">
- </tbody>
- </table>
- </tabpanel>
- <tabpanel>
- <table>
- <thead>
- <tr>
- <th>Host</th>
- <th>Resource Url</th>
- <th>Resource Type</th>
- <th>Num Hits</th>
- <th>Num Misses</th>
- <th>Consec Misses</th>
- <th>Average Position</th>
- <th>Score</th>
- <th>Before FCP</th>
- </tr>
- </thead>
- <tbody id="rpp_host_body">
- </tbody>
- </table>
- </tabpanel>
- <tabpanel>
<table>
<thead>
<tr>
@@ -62,9 +16,6 @@
<tbody id="rpp_origin_body">
</tbody>
</table>
- </tabpanel>
- </tabpanels>
- </tabbox>
</div>
<div id='rpp_disabled'>
Resource prefetch prediction is disabled.
diff --git a/chromium/chrome/browser/resources/predictors/resource_prefetch_predictor.js b/chromium/chrome/browser/resources/predictors/resource_prefetch_predictor.js
index a00b7d58a20..66af67b4186 100644
--- a/chromium/chrome/browser/resources/predictors/resource_prefetch_predictor.js
+++ b/chromium/chrome/browser/resources/predictors/resource_prefetch_predictor.js
@@ -45,62 +45,13 @@ function updateResourcePrefetchPredictorDbView(database) {
$('rpp_enabled').style.display = 'block';
$('rpp_disabled').style.display = 'none';
- var hasUrlData = database.url_db && database.url_db.length > 0;
- var hasHostData = database.host_db && database.host_db.length > 0;
var hasOriginData = database.origin_db && database.origin_db.length > 0;
- if (hasUrlData)
- renderCacheData($('rpp_url_body'), database.url_db);
- if (hasHostData)
- renderCacheData($('rpp_host_body'), database.host_db);
if (hasOriginData)
renderOriginData($('rpp_origin_body'), database.origin_db);
}
/**
- * Renders cache data for URL or host based data.
- * @param {HTMLElement} body element of table to render into.
- * @param {Object} database to render.
- */
-function renderCacheData(body, database) {
- body.textContent = '';
- for (let main of database) {
- for (var j = 0; j < main.resources.length; ++j) {
- var resource = main.resources[j];
- var row = document.createElement('tr');
-
- if (j == 0) {
- var t = document.createElement('td');
- t.rowSpan = main.resources.length;
- t.textContent = truncateString(main.main_frame_url);
- row.appendChild(t);
- }
-
- row.className =
- resource.is_prefetchable ? 'action-prerender' : 'action-none';
-
- row.appendChild(document.createElement('td')).textContent =
- truncateString(resource.resource_url);
- row.appendChild(document.createElement('td')).textContent =
- resource.resource_type;
- row.appendChild(document.createElement('td')).textContent =
- resource.number_of_hits;
- row.appendChild(document.createElement('td')).textContent =
- resource.number_of_misses;
- row.appendChild(document.createElement('td')).textContent =
- resource.consecutive_misses;
- row.appendChild(document.createElement('td')).textContent =
- resource.position;
- row.appendChild(document.createElement('td')).textContent =
- resource.score;
- row.appendChild(document.createElement('td')).textContent =
- resource.before_first_contentful_paint;
- body.appendChild(row);
- }
- }
-}
-
-/**
* Renders the content of the predictor origin table.
* @param {HTMLElement} body element of table to render into.
* @param {Object} database to render.
diff --git a/chromium/chrome/browser/resources/print_preview/OWNERS b/chromium/chrome/browser/resources/print_preview/OWNERS
index ef10fbb92d5..e542f20827d 100644
--- a/chromium/chrome/browser/resources/print_preview/OWNERS
+++ b/chromium/chrome/browser/resources/print_preview/OWNERS
@@ -1,5 +1,3 @@
-dpapad@chromium.org
-gene@chromium.org
-vitalybuka@chromium.org
+file://printing/OWNERS
# COMPONENT: UI>Browser>PrintPreview
diff --git a/chromium/chrome/browser/resources/print_preview/cloud_print_interface.html b/chromium/chrome/browser/resources/print_preview/cloud_print_interface.html
new file mode 100644
index 00000000000..9997dacb339
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/cloud_print_interface.html
@@ -0,0 +1,9 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+<link rel="import" href="native_layer.html">
+<link rel="import" href="data/cloud_parsers.html">
+<link rel="import" href="data/destination.html">
+<link rel="import" href="data/document_info.html">
+<link rel="import" href="data/invitation.html">
+<link rel="import" href="data/user_info.html">
+
+<script src="cloud_print_interface.js"></script>
diff --git a/chromium/chrome/browser/resources/print_preview/data/cloud_parsers.html b/chromium/chrome/browser/resources/print_preview/data/cloud_parsers.html
new file mode 100644
index 00000000000..12f99ebed56
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/data/cloud_parsers.html
@@ -0,0 +1,5 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+<link rel="import" href="destination.html">
+<link rel="import" href="invitation.html">
+
+<script src="cloud_parsers.js"></script>
diff --git a/chromium/chrome/browser/resources/print_preview/data/destination.js b/chromium/chrome/browser/resources/print_preview/data/destination.js
index 46f97789af1..a3b1e5627d0 100644
--- a/chromium/chrome/browser/resources/print_preview/data/destination.js
+++ b/chromium/chrome/browser/resources/print_preview/data/destination.js
@@ -71,10 +71,40 @@ print_preview.DestinationCertificateStatus = {
};
/**
+ * @typedef {{
+ * display_name: (string),
+ * type: (string | undefined),
+ * value: (number | string | boolean),
+ * is_default: (boolean | undefined),
+ * }}
+ */
+print_preview.VendorCapabilitySelectOption;
+
+/**
+ * Specifies a custom vendor capability.
+ * @typedef {{
+ * id: (string),
+ * display_name: (string),
+ * localized_display_name: (string | undefined),
+ * type: (string),
+ * select_cap: ({
+ * option: (Array<!print_preview.VendorCapabilitySelectOption>|undefined),
+ * }|undefined),
+ * typed_value_cap: ({
+ * default: (number | string | boolean | undefined),
+ * }|undefined),
+ * range_cap: ({
+ * default: (number),
+ * }),
+ * }}
+ */
+print_preview.VendorCapability;
+
+/**
* Capabilities of a print destination represented in a CDD.
*
* @typedef {{
- * vendor_capability: !Array<{Object}>,
+ * vendor_capability: !Array<!print_preview.VendorCapability>,
* collate: ({default: (boolean|undefined)}|undefined),
* color: ({
* option: !Array<{
@@ -537,10 +567,18 @@ cr.define('print_preview', function() {
}
/**
+ * @return {boolean} Whether the destination is offline or has an invalid
+ * certificate.
+ */
+ get isOfflineOrInvalid() {
+ return this.isOffline || this.shouldShowInvalidCertificateError;
+ }
+
+ /**
* @return {string} Human readable status for a destination that is offline
* or has a bad certificate. */
get connectionStatusText() {
- if (!this.isOffline && !this.shouldShowInvalidCertificateError)
+ if (!this.isOfflineOrInvalid)
return '';
const offlineDurationMs = Date.now() - this.lastAccessTime_;
let statusMessageId;
@@ -781,7 +819,6 @@ cr.define('print_preview', function() {
LOCAL_2X: 'images/2x/printer.png',
MOBILE: 'images/mobile.png',
MOBILE_SHARED: 'images/mobile_shared.png',
- THIRD_PARTY: 'images/third_party.png',
PDF: 'images/pdf.png',
DOCS: 'images/google_doc.png',
ENTERPRISE: 'images/business.svg'
diff --git a/chromium/chrome/browser/resources/print_preview/data/destination_store.html b/chromium/chrome/browser/resources/print_preview/data/destination_store.html
index ff0c3f8f95c..67f7dbc63f8 100644
--- a/chromium/chrome/browser/resources/print_preview/data/destination_store.html
+++ b/chromium/chrome/browser/resources/print_preview/data/destination_store.html
@@ -1,7 +1,4 @@
<link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/html/event_tracker.html">
-<link rel="import" href="chrome://resources/html/webui_listener_tracker.html">
-<link rel="import" href="chrome://resources/html/cr/event_target.html">
<link rel="import" href="../metrics.html">
<link rel="import" href="../native_layer.html">
<link rel="import" href="destination.html">
diff --git a/chromium/chrome/browser/resources/print_preview/data/invitation.html b/chromium/chrome/browser/resources/print_preview/data/invitation.html
new file mode 100644
index 00000000000..0a72be7c1ea
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/data/invitation.html
@@ -0,0 +1,4 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+<link rel="import" href="destination.html">
+
+<script src="invitation.js"></script>
diff --git a/chromium/chrome/browser/resources/print_preview/images/2x/printer.png b/chromium/chrome/browser/resources/print_preview/images/2x/printer.png
index b704e02f841..6bd2a925be3 100644
--- a/chromium/chrome/browser/resources/print_preview/images/2x/printer.png
+++ b/chromium/chrome/browser/resources/print_preview/images/2x/printer.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/print_preview/images/2x/printer_shared.png b/chromium/chrome/browser/resources/print_preview/images/2x/printer_shared.png
index bbddfd04d2c..f27e672f7b9 100644
--- a/chromium/chrome/browser/resources/print_preview/images/2x/printer_shared.png
+++ b/chromium/chrome/browser/resources/print_preview/images/2x/printer_shared.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/print_preview/images/third_party.png b/chromium/chrome/browser/resources/print_preview/images/third_party.png
deleted file mode 100644
index d15552d390c..00000000000
--- a/chromium/chrome/browser/resources/print_preview/images/third_party.png
+++ /dev/null
Binary files differ
diff --git a/chromium/chrome/browser/resources/print_preview/new/advanced_options_settings.html b/chromium/chrome/browser/resources/print_preview/new/advanced_options_settings.html
index 554ba1337a4..5ecb91c14e2 100644
--- a/chromium/chrome/browser/resources/print_preview/new/advanced_options_settings.html
+++ b/chromium/chrome/browser/resources/print_preview/new/advanced_options_settings.html
@@ -1,7 +1,11 @@
<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.html">
+<link rel="import" href="../data/destination.html">
+<link rel="import" href="advanced_settings_dialog.html">
<link rel="import" href="button_css.html">
<link rel="import" href="print_preview_shared_css.html">
+<link rel="import" href="settings_behavior.html">
<link rel="import" href="settings_section.html">
<dom-module id="print-preview-advanced-options-settings">
@@ -15,9 +19,16 @@
<print-preview-settings-section>
<span slot="title">$i18n{advancedOptionsLabel}</span>
<div slot="controls">
- <button>$i18n{showAdvancedOptions}</button>
+ <button disabled$="[[disabled]]" on-click="onButtonClick_">
+ $i18n{showAdvancedOptions}
+ </button>
</div>
</print-preview-settings-section>
+ <template is="cr-lazy-render" id="advancedDialog">
+ <print-preview-advanced-dialog
+ settings="{{settings}}" destination="[[destination]]">
+ </print-preview-advanced-dialog>
+ </template>
</template>
<script src="advanced_options_settings.js"></script>
</dom-module>
diff --git a/chromium/chrome/browser/resources/print_preview/new/advanced_options_settings.js b/chromium/chrome/browser/resources/print_preview/new/advanced_options_settings.js
index 6df05eb6896..b47ab004d75 100644
--- a/chromium/chrome/browser/resources/print_preview/new/advanced_options_settings.js
+++ b/chromium/chrome/browser/resources/print_preview/new/advanced_options_settings.js
@@ -4,4 +4,23 @@
Polymer({
is: 'print-preview-advanced-options-settings',
+
+ behaviors: [SettingsBehavior],
+
+ properties: {
+ disabled: Boolean,
+
+ /** @type {!print_preview.Destination} */
+ destination: Object,
+ },
+
+ /** @private */
+ onButtonClick_: function() {
+ const dialog = this.$.advancedDialog.get();
+ // This async() call is a workaround to prevent a DCHECK - see
+ // https://crbug.com/804047.
+ this.async(() => {
+ dialog.show();
+ }, 1);
+ },
});
diff --git a/chromium/chrome/browser/resources/print_preview/new/advanced_settings_dialog.html b/chromium/chrome/browser/resources/print_preview/new/advanced_settings_dialog.html
new file mode 100644
index 00000000000..91e920b5383
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/new/advanced_settings_dialog.html
@@ -0,0 +1,47 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="../data/destination.html">
+<link rel="import" href="advanced_settings_item.html">
+<link rel="import" href="settings_behavior.html">
+<link rel="import" href="button_css.html">
+<link rel="import" href="print_preview_shared_css.html">
+<link rel="import" href="search_dialog_css.html">
+
+<dom-module id="print-preview-advanced-dialog">
+ <style include="print-preview-shared search-dialog button cr-hidden-style">
+ </style>
+ <template>
+ <dialog is="cr-dialog" id="dialog" on-close="onCloseOrCancel_">
+ <div slot="title">
+ <div>[[i18n('advancedSettingsDialogTitle', destination.displayName)]]
+ </div>
+ <print-preview-search-box id="searchBox"
+ label="$i18n{advancedSettingsSearchBoxPlaceholder}"
+ search-query="{{searchQuery_}}">
+ </print-preview-search-box>
+ </div>
+ <div slot="body">
+ <template is="dom-repeat"
+ items="[[destination.capabilities.printer.vendor_capability]]">
+ <print-preview-advanced-settings-item capability="[[item]]"
+ settings="[[settings]]">
+ </print-preview-advanced-settings-item>
+ </template>
+ <div class="no-settings-match-hint"
+ hidden$="[[!shouldShowHint_(hasMatching_)]]">
+ $i18n{noAdvancedSettingsMatchSearchHint}
+ </div>
+ </div>
+ <div slot="button-container">
+ <button on-click="onCancelButtonClick_">$i18n{cancel}</button>
+ <button on-click="onApplyButtonClick_">
+ $i18n{advancedSettingsDialogConfirm}
+ </button>
+ </div>
+ </dialog>
+ </template>
+ <script src="advanced_settings_dialog.js"></script>
+</dom-module>
diff --git a/chromium/chrome/browser/resources/print_preview/new/advanced_settings_dialog.js b/chromium/chrome/browser/resources/print_preview/new/advanced_settings_dialog.js
new file mode 100644
index 00000000000..989de5b0285
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/new/advanced_settings_dialog.js
@@ -0,0 +1,82 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+Polymer({
+ is: 'print-preview-advanced-dialog',
+
+ behaviors: [SettingsBehavior, I18nBehavior],
+
+ properties: {
+ /** @type {!print_preview.Destination} */
+ destination: Object,
+
+ /** @private {?RegExp} */
+ searchQuery_: {
+ type: Object,
+ value: null,
+ },
+
+ /** @private {boolean} */
+ hasMatching_: {
+ type: Boolean,
+ notify: true,
+ computed: 'computeHasMatching_(searchQuery_)',
+ },
+ },
+
+ /**
+ * @return {boolean} Whether there is a setting matching the query.
+ * @private
+ */
+ computeHasMatching_: function() {
+ const listItems = this.shadowRoot.querySelectorAll(
+ 'print-preview-advanced-settings-item');
+ let hasMatch = false;
+ listItems.forEach(item => {
+ const matches = item.hasMatch(this.searchQuery_);
+ item.hidden = !matches;
+ hasMatch = hasMatch || matches;
+ item.updateHighlighting(this.searchQuery_);
+ });
+ return hasMatch;
+ },
+
+ /**
+ * @return {boolean} Whether the no matching settings hint should be shown.
+ * @private
+ */
+ shouldShowHint_: function() {
+ return !!this.searchQuery_ && !this.hasMatching_;
+ },
+
+ /** @private */
+ onCloseOrCancel_: function() {
+ if (this.searchQuery_)
+ this.$.searchBox.setValue('');
+ },
+
+ /** @private */
+ onCancelButtonClick_: function() {
+ this.$.dialog.cancel();
+ },
+
+ /** @private */
+ onApplyButtonClick_: function() {
+ const settingsValues = {};
+ this.shadowRoot.querySelectorAll('print-preview-advanced-settings-item')
+ .forEach(item => {
+ settingsValues[item.capability.id] = item.getCurrentValue();
+ });
+ this.setSetting('vendorItems', settingsValues);
+ this.$.dialog.close();
+ },
+
+ show: function() {
+ this.$.dialog.showModal();
+ },
+
+ close: function() {
+ this.$.dialog.close();
+ },
+});
diff --git a/chromium/chrome/browser/resources/print_preview/new/advanced_settings_item.html b/chromium/chrome/browser/resources/print_preview/new/advanced_settings_item.html
new file mode 100644
index 00000000000..af531c738fa
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/new/advanced_settings_item.html
@@ -0,0 +1,68 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/search_highlight_style_css.html">
+<link rel="import" href="../print_preview_utils.html">
+<link rel="import" href="../data/destination.html">
+<link rel="import" href="highlight_utils.html">
+<link rel="import" href="print_preview_shared_css.html">
+<link rel="import" href="select_css.html">
+<link rel="import" href="settings_behavior.html">
+
+<dom-module id="print-preview-advanced-settings-item">
+ <style include="print-preview-shared select search-highlight-style">
+ :host {
+ display: flex;
+ position: relative;
+ }
+
+ :host > * {
+ overflow: hidden;
+ padding-bottom: 15px;
+ padding-top: 10px;
+ text-overflow: ellipsis;
+ vertical-align: middle;
+ white-space: nowrap;
+ }
+
+ :host .label {
+ -webkit-padding-end: 20px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ width: 250px;
+ }
+
+ :host .value {
+ width: 175px;
+ }
+
+ :host input {
+ height: 28px;
+ line-height: 24px;
+ width: 175px;
+ }
+ </style>
+ <template>
+ <label class="label searchable">[[getDisplayName_(capability)]]</label>
+ <div class="value">
+ <template is="dom-if" if="[[isCapabilityTypeSelect_(capability)]]"
+ restamp>
+ <div>
+ <select on-change="onUserInput_">
+ <template is="dom-repeat" items="[[capability.select_cap.option]]">
+ <option class="searchable" text="[[getDisplayName_(item)]]"
+ value="[[item.value]]"
+ selected="[[isOptionSelected_(item, currentValue_)]]">
+ </option>
+ </template>
+ </select>
+ </div>
+ </template>
+ <span hidden$="[[isCapabilityTypeSelect_(capability)]]">
+ <input type="text" on-input="onUserInput_"
+ placeholder="[[getCapabilityPlaceholder_(capability)]]">
+ </span>
+ </div>
+ </template>
+ <script src="advanced_settings_item.js"></script>
+</dom-module>
diff --git a/chromium/chrome/browser/resources/print_preview/new/advanced_settings_item.js b/chromium/chrome/browser/resources/print_preview/new/advanced_settings_item.js
new file mode 100644
index 00000000000..3de026d756b
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/new/advanced_settings_item.js
@@ -0,0 +1,154 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+Polymer({
+ is: 'print-preview-advanced-settings-item',
+
+ behaviors: [SettingsBehavior],
+
+ properties: {
+ /** @type {!print_preview.VendorCapability} */
+ capability: Object,
+
+ /** @private {(number | string | boolean)} */
+ currentValue_: {
+ type: Object,
+ value: null,
+ },
+ },
+
+ observers: [
+ 'updateFromSettings_(capability, settings.vendorItems.value)',
+ ],
+
+ /** @private {boolean} */
+ highlighted_: false,
+
+ /** @private */
+ updateFromSettings_: function() {
+ const settings = this.getSetting('vendorItems').value;
+
+ // The settings may not have a property with the id if they were populated
+ // from sticky settings from a different destination or if the
+ // destination's capabilities changed since the sticky settings were
+ // generated.
+ if (!settings.hasOwnProperty(this.capability.id))
+ return;
+
+ const value = settings[this.capability.id];
+ if (this.isCapabilityTypeSelect_()) {
+ // Ignore a value that can't be selected.
+ if (this.hasOptionWithValue_(value))
+ this.currentValue_ = value;
+ } else {
+ this.currentValue_ = value;
+ this.$$('input[type="text"]').value = this.currentValue_;
+ }
+ },
+
+ /**
+ * @param {!print_preview.VendorCapability |
+ * !print_preview.VendorCapabilitySelectOption} item
+ * @return {string} The display name for the setting.
+ * @private
+ */
+ getDisplayName_: function(item) {
+ let displayName = item.display_name;
+ if (!displayName && item.display_name_localized)
+ displayName = getStringForCurrentLocale(item.display_name_localized);
+ return displayName || '';
+ },
+
+ /**
+ * @return {boolean} Whether the capability represented by this item is
+ * of type select.
+ * @private
+ */
+ isCapabilityTypeSelect_: function() {
+ return this.capability.type == 'SELECT';
+ },
+
+ /**
+ * @param {!print_preview.VendorCapabilitySelectOption} option The option
+ * for a select capability.
+ * @return {boolean} Whether the option is selected.
+ * @private
+ */
+ isOptionSelected_: function(option) {
+ return (this.currentValue_ !== null &&
+ option.value === this.currentValue_) ||
+ (this.currentValue_ == null && !!option.is_default);
+ },
+
+ /**
+ * @return {string} The placeholder value for the capability's text input.
+ * @private
+ */
+ getCapabilityPlaceholder_: function() {
+ if (this.capability.type == 'TYPED_VALUE' &&
+ this.capability.typed_value_cap &&
+ this.capability.typed_value_cap.default != undefined) {
+ return this.capability.typed_value_cap.default.toString() || '';
+ }
+ if (this.capability.type == 'RANGE' && this.capability.range_cap &&
+ this.capability.range_cap.default != undefined)
+ return this.capability.range_cap.default.toString() || '';
+ return '';
+ },
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ hasOptionWithValue_: function(value) {
+ return !!this.capability.select_cap &&
+ !!this.capability.select_cap.option &&
+ this.capability.select_cap.option.some(option => option.value == value);
+ },
+
+ /**
+ * @param {?RegExp} query The current search query.
+ * @return {boolean} Whether the item has a match for the query.
+ */
+ hasMatch: function(query) {
+ if (!query || this.getDisplayName_(this.capability).match(query))
+ return true;
+
+ if (!this.isCapabilityTypeSelect_())
+ return false;
+
+ for (let option of
+ /** @type {!Array<!print_preview.VendorCapabilitySelectOption>} */ (
+ this.capability.select_cap.option)) {
+ if (this.getDisplayName_(option).match(query))
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * @param {!Event} e Event containing the new value.
+ * @private
+ */
+ onUserInput_: function(e) {
+ this.currentValue_ = e.target.value;
+ },
+
+ /**
+ * @return {(number | string | boolean)} The current value of the setting.
+ */
+ getCurrentValue: function() {
+ return this.currentValue_;
+ },
+
+ /**
+ * @param {?RegExp} query The current search query.
+ * @return {boolean} Whether the current query is a match for this item.
+ */
+ updateHighlighting: function(query) {
+ this.highlighted_ =
+ print_preview.updateHighlights(this, query, this.highlighted_);
+ return this.highlighted_ || !query;
+ },
+});
diff --git a/chromium/chrome/browser/resources/print_preview/new/app.html b/chromium/chrome/browser/resources/print_preview/new/app.html
index fd29781eb45..dbfe5590f85 100644
--- a/chromium/chrome/browser/resources/print_preview/new/app.html
+++ b/chromium/chrome/browser/resources/print_preview/new/app.html
@@ -2,6 +2,7 @@
<link rel="import" href="chrome://resources/html/event_tracker.html">
<link rel="import" href="chrome://resources/html/webui_listener_tracker.html">
+<link rel="import" href="../cloud_print_interface.html">
<link rel="import" href="../native_layer.html">
<link rel="import" href="../data/destination.html">
<link rel="import" href="../data/destination_store.html">
@@ -9,6 +10,7 @@
<link rel="import" href="../data/measurement_system.html">
<link rel="import" href="../data/user_info.html">
<link rel="import" href="settings_behavior.html">
+<link rel="import" href="state.html">
<link rel="import" href="model.html">
<link rel="import" href="header.html">
<link rel="import" href="preview_area.html">
@@ -48,64 +50,82 @@
overflow: auto;
}
- #preview-area {
+ #preview-area-container {
-webkit-border-start: 1px solid #dcdcdc;
align-items: center;
background-color: #e6e6e6;
flex: 1;
}
</style>
+ <print-preview-state id="state" state="{{state}}"></print-preview-state>
<print-preview-model id="model" settings="{{settings}}"
destination="{{destination_}}" document-info="{{documentInfo_}}"
recent-destinations="{{recentDestinations_}}"
on-save-sticky-settings="onSaveStickySettings_">
</print-preview-model>
- <div id="sidebar">
- <print-preview-header destination="[[destination_]]" state="{{state_}}"
- settings="[[settings]]"></print-preview-header>
+ <div id="sidebar" on-setting-valid-changed="onSettingValidChanged_">
+ <print-preview-header destination="[[destination_]]" state="[[state]]"
+ error-message="[[errorMessage_]]" settings="[[settings]]"
+ on-print-requested="onPrintRequested_"
+ on-cancel-requested="onCancelRequested_">
+ </print-preview-header>
<div id="settings-sections">
- <print-preview-destination-settings destination="[[destination_]]">
+ <print-preview-destination-settings id="destinationSettings"
+ destination="[[destination_]]"
+ destination-store="[[destinationStore_]]"
+ disabled="[[controlsDisabled_]]" state="[[state]]"
+ recent-destinations="[[recentDestinations_]]"
+ user-info="{{userInfo_}}">
</print-preview-destination-settings>
<print-preview-pages-settings settings="{{settings}}"
- document-info="[[documentInfo_]]"
+ document-info="[[documentInfo_]]" disabled="[[controlsDisabled_]]"
hidden$="[[!settings.pages.available]]">
</print-preview-pages-settings>
<print-preview-copies-settings settings="{{settings}}"
+ disabled="[[controlsDisabled_]]"
hidden$="[[!settings.copies.available]]">
</print-preview-copies-settings>
<print-preview-layout-settings settings="{{settings}}"
+ disabled="[[controlsDisabled_]]"
hidden$="[[!settings.layout.available]]">
</print-preview-layout-settings>
<print-preview-color-settings settings="{{settings}}"
+ disabled="[[controlsDisabled_]]"
hidden$="[[!settings.color.available]]">
</print-preview-color-settings>
<print-preview-media-size-settings settings="{{settings}}"
capability="[[destination_.capabilities.printer.media_size]]"
+ disabled="[[controlsDisabled_]]"
hidden$="[[!settings.mediaSize.available]]">
</print-preview-media-size-settings>
<print-preview-margins-settings settings="{{settings}}"
+ disabled="[[controlsDisabled_]]"
hidden$="[[!settings.margins.available]]">
</print-preview-margins-settings>
<print-preview-dpi-settings settings="{{settings}}"
capability="[[destination_.capabilities.printer.dpi]]"
+ disabled="[[controlsDisabled_]]"
hidden$="[[!settings.dpi.available]]">
</print-preview-dpi-settings>
<print-preview-scaling-settings settings="{{settings}}"
- document-info="[[documentInfo_]]"
+ document-info="[[documentInfo_]]" disabled="[[controlsDisabled_]]"
hidden$="[[!settings.scaling.available]]">
</print-preview-scaling-settings>
<print-preview-other-options-settings settings="{{settings}}"
+ disabled="[[controlsDisabled_]]"
hidden$="[[!settings.otherOptions.available]]">
</print-preview-other-options-settings>
<print-preview-advanced-options-settings settings="{{settings}}"
+ destination="[[destination_]]" disabled="[[controlsDisabled_]]"
hidden$="[[!settings.vendorItems.available]]">
</print-preview-advanced-options-settings>
</div>
</div>
- <div id="preview-area">
- <print-preview-preview-area settings="{{settings}}"
+ <div id="preview-area-container">
+ <print-preview-preview-area id="previewArea" settings="{{settings}}"
destination="[[destination_]]" document-info="{{documentInfo_}}"
- state="{{state_}}">
+ state="[[state]]" on-preview-failed="onPreviewFailed_"
+ on-preview-loaded="onPreviewLoaded_">
</print-preview-preview-area>
</div>
</template>
diff --git a/chromium/chrome/browser/resources/print_preview/new/app.js b/chromium/chrome/browser/resources/print_preview/new/app.js
index c45886a2ec6..0e4dcebab1d 100644
--- a/chromium/chrome/browser/resources/print_preview/new/app.js
+++ b/chromium/chrome/browser/resources/print_preview/new/app.js
@@ -17,14 +17,21 @@ Polymer({
notify: true,
},
- /** @private {print_preview.DocumentInfo} */
- documentInfo_: {
+ /** @private {print_preview.Destination} */
+ destination_: {
type: Object,
notify: true,
},
- /** @private {print_preview.Destination} */
- destination_: {
+ /** @private {?print_preview.DestinationStore} */
+ destinationStore_: {
+ type: Object,
+ notify: true,
+ value: null,
+ },
+
+ /** @private {print_preview.DocumentInfo} */
+ documentInfo_: {
type: Object,
notify: true,
},
@@ -35,40 +42,55 @@ Polymer({
notify: true,
},
- /** @private {!print_preview_new.State} */
- state_: {
+ /** @type {!print_preview_new.State} */
+ state: {
+ type: Number,
+ observer: 'onStateChanged_',
+ },
+
+ /** @private {?print_preview.UserInfo} */
+ userInfo_: {
type: Object,
notify: true,
- value: {
- previewLoading: false,
- previewFailed: false,
- cloudPrintError: '',
- privetExtensionError: '',
- invalidSettings: false,
- initialized: false,
- cancelled: false,
- },
+ value: null,
},
- },
- /** @private {?print_preview.NativeLayer} */
- nativeLayer_: null,
+ /** @private {string} */
+ errorMessage_: {
+ type: String,
+ notify: true,
+ value: '',
+ },
- /** @private {?print_preview.UserInfo} */
- userInfo_: null,
+ /** @private {boolean} */
+ controlsDisabled_: {
+ type: Boolean,
+ notify: true,
+ computed: 'computeControlsDisabled_(state)',
+ }
+ },
/** @private {?WebUIListenerTracker} */
listenerTracker_: null,
- /** @private {?print_preview.DestinationStore} */
- destinationStore_: null,
+ /** @type {!print_preview.MeasurementSystem} */
+ measurementSystem_: new print_preview.MeasurementSystem(
+ ',', '.', print_preview.MeasurementSystemUnitType.IMPERIAL),
+
+ /** @private {?print_preview.NativeLayer} */
+ nativeLayer_: null,
+
+ /** @private {?cloudprint.CloudPrintInterface} */
+ cloudPrintInterface_: null,
/** @private {!EventTracker} */
tracker_: new EventTracker(),
- /** @type {!print_preview.MeasurementSystem} */
- measurementSystem_: new print_preview.MeasurementSystem(
- ',', '.', print_preview.MeasurementSystemUnitType.IMPERIAL),
+ /** @private {boolean} */
+ cancelled_: false,
+
+ /** @private {boolean} */
+ isInAppKioskMode_: false,
/** @override */
attached: function() {
@@ -76,6 +98,8 @@ Polymer({
this.documentInfo_ = new print_preview.DocumentInfo();
this.userInfo_ = new print_preview.UserInfo();
this.listenerTracker_ = new WebUIListenerTracker();
+ this.listenerTracker_.add(
+ 'use-cloud-print', this.onCloudPrintEnable_.bind(this));
this.destinationStore_ = new print_preview.DestinationStore(
this.userInfo_, this.listenerTracker_);
this.tracker_.add(
@@ -98,6 +122,14 @@ Polymer({
},
/**
+ * @return {boolean} Whether the controls should be disabled.
+ * @private
+ */
+ computeControlsDisabled_: function() {
+ return this.state != print_preview_new.State.READY;
+ },
+
+ /**
* @param {!print_preview.NativeInitialSettings} settings
* @private
*/
@@ -109,7 +141,7 @@ Polymer({
this.notifyPath('documentInfo_.hasSelection');
this.notifyPath('documentInfo_.title');
this.notifyPath('documentInfo_.pageCount');
- this.$.model.updateFromStickySettings(settings.serializedAppStateStr);
+ this.$.model.setStickySettings(settings.serializedAppStateStr);
this.measurementSystem_.setSystem(
settings.thousandsDelimeter, settings.decimalDelimeter,
settings.unitType);
@@ -120,9 +152,42 @@ Polymer({
this.recentDestinations_);
},
+ /**
+ * Called when Google Cloud Print integration is enabled by the
+ * PrintPreviewHandler.
+ * Fetches the user's cloud printers.
+ * @param {string} cloudPrintUrl The URL to use for cloud print servers.
+ * @param {boolean} appKioskMode Whether to print automatically for kiosk
+ * mode.
+ * @private
+ */
+ onCloudPrintEnable_: function(cloudPrintUrl, appKioskMode) {
+ assert(!this.cloudPrintInterface_);
+ this.cloudPrintInterface_ = new cloudprint.CloudPrintInterface(
+ cloudPrintUrl, assert(this.nativeLayer_), assert(this.userInfo_),
+ appKioskMode);
+ this.tracker_.add(
+ assert(this.cloudPrintInterface_),
+ cloudprint.CloudPrintInterfaceEventType.SUBMIT_DONE,
+ this.close_.bind(this));
+ [cloudprint.CloudPrintInterfaceEventType.SEARCH_FAILED,
+ cloudprint.CloudPrintInterfaceEventType.SUBMIT_FAILED,
+ cloudprint.CloudPrintInterfaceEventType.PRINTER_FAILED,
+ ].forEach(eventType => {
+ this.tracker_.add(
+ assert(this.cloudPrintInterface_), eventType,
+ this.onCloudPrintError_.bind(this));
+ });
+
+ this.destinationStore_.setCloudPrintInterface(this.cloudPrintInterface_);
+ if (this.$.destinationSettings.isDialogOpen())
+ this.destinationStore_.startLoadCloudDestinations();
+ },
+
/** @private */
onDestinationSelect_: function() {
this.destination_ = this.destinationStore_.selectedDestination;
+ this.$.state.transitTo(print_preview_new.State.NOT_READY);
},
/** @private */
@@ -130,16 +195,10 @@ Polymer({
this.set(
'destination_.capabilities',
this.destinationStore_.selectedDestination.capabilities);
- if (!this.state_.initialized)
- this.set('state_.initialized', true);
- },
-
- /** @private */
- onPreviewCancelled_: function() {
- if (!this.state_.cancelled)
- return;
- this.detached();
- this.nativeLayer_.dialogClose(true);
+ if (this.state != print_preview_new.State.READY)
+ this.$.state.transitTo(print_preview_new.State.READY);
+ if (!this.$.model.initialized())
+ this.$.model.applyStickySettings();
},
/**
@@ -149,4 +208,128 @@ Polymer({
onSaveStickySettings_: function(e) {
this.nativeLayer_.saveAppState(/** @type {string} */ (e.detail));
},
+
+ /** @private */
+ onStateChanged_: function() {
+ if (this.state == print_preview_new.State.CLOSING) {
+ this.remove();
+ this.nativeLayer_.dialogClose(this.cancelled_);
+ } else if (this.state == print_preview_new.State.HIDDEN) {
+ this.nativeLayer_.hidePreview();
+ } else if (this.state == print_preview_new.State.PRINTING) {
+ const destination = assert(this.destinationStore_.selectedDestination);
+ const whenPrintDone =
+ this.nativeLayer_.print(this.$.model.createPrintTicket(destination));
+ if (destination.isLocal) {
+ const onError = destination.id ==
+ print_preview.Destination.GooglePromotedId.SAVE_AS_PDF ?
+ this.onFileSelectionCancel_.bind(this) :
+ this.onPrintFailed_.bind(this);
+ whenPrintDone.then(this.close_.bind(this), onError);
+ } else {
+ // Cloud print resolves when print data is returned to submit to cloud
+ // print, or if print ticket cannot be read, no PDF data is found, or
+ // PDF is oversized.
+ whenPrintDone.then(
+ this.onPrintToCloud_.bind(this), this.onPrintFailed_.bind(this));
+ }
+ }
+ },
+
+ /** @private */
+ onPreviewLoaded_: function() {
+ if (this.state == print_preview_new.State.HIDDEN)
+ this.$.state.transitTo(print_preview_new.State.PRINTING);
+ },
+
+ /** @private */
+ onPrintRequested_: function() {
+ this.$.state.transitTo(
+ this.$.previewArea.previewLoaded() ? print_preview_new.State.PRINTING :
+ print_preview_new.State.HIDDEN);
+ },
+
+ /** @private */
+ onCancelRequested_: function() {
+ this.cancelled_ = true;
+ this.$.state.transitTo(print_preview_new.State.CLOSING);
+ },
+
+ /**
+ * @param {!CustomEvent} e The event containing the new validity.
+ * @private
+ */
+ onSettingValidChanged_: function(e) {
+ this.$.state.transitTo(
+ /** @type {boolean} */ (e.detail) ?
+ print_preview_new.State.READY :
+ print_preview_new.State.INVALID_TICKET);
+ },
+
+ /** @private */
+ onFileSelectionCancel_: function() {
+ this.$.state.transitTo(print_preview_new.State.READY);
+ },
+
+ /**
+ * Called when the native layer has retrieved the data to print to Google
+ * Cloud Print.
+ * @param {string} data The body to send in the HTTP request.
+ * @private
+ */
+ onPrintToCloud_: function(data) {
+ assert(
+ this.cloudPrintInterface_ != null, 'Google Cloud Print is not enabled');
+ const destination = assert(this.destinationStore_.selectedDestination);
+ this.cloudPrintInterface_.submit(
+ destination, this.$.model.createCloudJobTicket(destination),
+ assert(this.documentInfo_), data);
+ },
+
+ /**
+ * Called when printing to a privet, cloud, or extension printer fails.
+ * @param {*} httpError The HTTP error code, or -1 or a string describing
+ * the error, if not an HTTP error.
+ * @private
+ */
+ onPrintFailed_: function(httpError) {
+ console.error('Printing failed with error code ' + httpError);
+ this.errorMessage_ = httpError.toString();
+ this.$.state.transitTo(print_preview_new.State.FATAL_ERROR);
+ },
+
+ /** @private */
+ onPreviewFailed_: function() {
+ this.$.state.transitTo(print_preview_new.State.FATAL_ERROR);
+ },
+
+ /**
+ * Called when there was an error communicating with Google Cloud print.
+ * Displays an error message in the print header.
+ * @param {!Event} event Contains the error message.
+ * @private
+ */
+ onCloudPrintError_: function(event) {
+ if (event.status == 0) {
+ return; // Ignore, the system does not have internet connectivity.
+ }
+ if (event.status == 403) {
+ if (!this.isInAppKioskMode_) {
+ this.$.destinationSettings.showCloudPrintPromo();
+ }
+ } else {
+ this.set('state_.cloudPrintError', event.message);
+ }
+ if (event.status == 200) {
+ console.error(
+ `Google Cloud Print Error: (${event.errorCode}) ${event.message}`);
+ } else {
+ console.error(`Google Cloud Print Error: HTTP status ${event.status}`);
+ }
+ },
+
+ /** @private */
+ close_: function() {
+ this.$.state.transitTo(print_preview_new.State.CLOSING);
+ },
});
diff --git a/chromium/chrome/browser/resources/print_preview/new/button_css.html b/chromium/chrome/browser/resources/print_preview/new/button_css.html
index d33555445a4..77481fa255a 100644
--- a/chromium/chrome/browser/resources/print_preview/new/button_css.html
+++ b/chromium/chrome/browser/resources/print_preview/new/button_css.html
@@ -13,13 +13,13 @@
<if expr="not is_ios">
button:enabled:hover {
background-image: linear-gradient(#f0f0f0, #f0f0f0 38%, #e0e0e0);
- @apply(--print-preview-hover);
+ @apply --print-preview-hover;
}
</if>
button:enabled:active {
background-image: linear-gradient(#e7e7e7, #e7e7e7 38%, #d7d7d7);
- @apply(--print-preview-active);
+ @apply --print-preview-active;
}
</style>
</template>
diff --git a/chromium/chrome/browser/resources/print_preview/new/checkbox_radio_css.html b/chromium/chrome/browser/resources/print_preview/new/checkbox_radio_css.html
index f2564079cbd..159ad3c3dbb 100644
--- a/chromium/chrome/browser/resources/print_preview/new/checkbox_radio_css.html
+++ b/chromium/chrome/browser/resources/print_preview/new/checkbox_radio_css.html
@@ -48,14 +48,14 @@
[type='radio']) {
background-image:
linear-gradient(#f0f0f0, #f0f0f0 38%, #e0e0e0);
- @apply(--print-preview-hover);
+ @apply --print-preview-hover;
}
</if>
input:enabled:active:-webkit-any([type='checkbox'],
[type='radio']) {
background-image: linear-gradient(#e7e7e7, #e7e7e7 38%, #d7d7d7);
- @apply(--print-preview-active);
+ @apply --print-preview-active;
}
input:disabled:-webkit-any([type='checkbox'],
diff --git a/chromium/chrome/browser/resources/print_preview/new/color_settings.html b/chromium/chrome/browser/resources/print_preview/new/color_settings.html
index b95459a0f05..88adf84c90c 100644
--- a/chromium/chrome/browser/resources/print_preview/new/color_settings.html
+++ b/chromium/chrome/browser/resources/print_preview/new/color_settings.html
@@ -11,7 +11,8 @@
<print-preview-settings-section>
<span id="color-label" slot="title">$i18n{optionColor}</span>
<div slot="controls">
- <select aria-labelledby="color-label" on-change="onChange_">
+ <select aria-labelledby="color-label" on-change="onChange_"
+ disabled$="[[disabled]]">
<option value="bw" selected>$i18n{optionBw}</option>
<option value="color">$i18n{optionColor}</option>
</select>
diff --git a/chromium/chrome/browser/resources/print_preview/new/color_settings.js b/chromium/chrome/browser/resources/print_preview/new/color_settings.js
index 075141f2a01..4c8631aa5ec 100644
--- a/chromium/chrome/browser/resources/print_preview/new/color_settings.js
+++ b/chromium/chrome/browser/resources/print_preview/new/color_settings.js
@@ -7,6 +7,10 @@ Polymer({
behaviors: [SettingsBehavior],
+ properties: {
+ disabled: Boolean,
+ },
+
observers: ['onColorSettingChange_(settings.color.value)'],
/**
diff --git a/chromium/chrome/browser/resources/print_preview/new/compiled_resources2.gyp b/chromium/chrome/browser/resources/print_preview/new/compiled_resources2.gyp
index 0441a3c1e62..e75ada55312 100644
--- a/chromium/chrome/browser/resources/print_preview/new/compiled_resources2.gyp
+++ b/chromium/chrome/browser/resources/print_preview/new/compiled_resources2.gyp
@@ -21,6 +21,7 @@
'preview_area',
'model',
'state',
+ '../compiled_resources2.gyp:cloud_print_interface',
'../compiled_resources2.gyp:native_layer',
'../data/compiled_resources2.gyp:destination',
'../data/compiled_resources2.gyp:destination_store',
@@ -39,6 +40,7 @@
'model',
'settings_behavior',
'state',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
@@ -47,6 +49,11 @@
'target_name': 'destination_settings',
'dependencies': [
'../data/compiled_resources2.gyp:destination',
+ '../data/compiled_resources2.gyp:destination_store',
+ '../data/compiled_resources2.gyp:user_info',
+ 'destination_dialog',
+ 'state',
+ '<(DEPTH)/ui/webui/resources/cr_elements/cr_lazy_render/compiled_resources2.gyp:cr_lazy_render',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
@@ -55,7 +62,6 @@
'dependencies': [
'settings_behavior',
'../data/compiled_resources2.gyp:document_info',
- '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
@@ -126,6 +132,9 @@
{
'target_name': 'advanced_options_settings',
'dependencies': [
+ '../data/compiled_resources2.gyp:destination',
+ 'advanced_settings_dialog',
+ 'settings_behavior',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
@@ -156,6 +165,7 @@
'target_name': 'preview_area',
'dependencies': [
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior',
'../../pdf/compiled_resources2.gyp:pdf_scripting_api',
'../compiled_resources2.gyp:native_layer',
@@ -166,12 +176,80 @@
'../data/compiled_resources2.gyp:size',
'../data/compiled_resources2.gyp:margins',
'../data/compiled_resources2.gyp:printable_area',
+ 'model',
'settings_behavior',
'state',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
{
+ 'target_name': 'destination_dialog',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/cr_elements/cr_dialog/compiled_resources2.gyp:cr_dialog',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
+ '../data/compiled_resources2.gyp:destination',
+ '../data/compiled_resources2.gyp:destination_store',
+ '../data/compiled_resources2.gyp:user_info',
+ 'destination_list',
+ 'print_preview_search_box',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'destination_list',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ '../compiled_resources2.gyp:native_layer',
+ '../data/compiled_resources2.gyp:destination',
+ 'destination_list_item',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'destination_list_item',
+ 'dependencies': [
+ 'highlight_utils',
+ '../data/compiled_resources2.gyp:destination',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'advanced_settings_dialog',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/cr_elements/cr_dialog/compiled_resources2.gyp:cr_dialog',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ '../data/compiled_resources2.gyp:destination',
+ 'advanced_settings_item',
+ 'print_preview_search_box',
+ 'settings_behavior',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'advanced_settings_item',
+ 'dependencies': [
+ 'highlight_utils',
+ '../compiled_resources2.gyp:print_preview_utils',
+ '../data/compiled_resources2.gyp:destination',
+ 'settings_behavior',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'print_preview_search_box',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/cr_elements/cr_search_field/compiled_resources2.gyp:cr_search_field_behavior',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'highlight_utils',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:search_highlight_utils',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
'target_name': 'model',
'dependencies': [
'settings_behavior',
diff --git a/chromium/chrome/browser/resources/print_preview/new/copies_settings.html b/chromium/chrome/browser/resources/print_preview/new/copies_settings.html
index f1168f6f5d8..f64b68121a0 100644
--- a/chromium/chrome/browser/resources/print_preview/new/copies_settings.html
+++ b/chromium/chrome/browser/resources/print_preview/new/copies_settings.html
@@ -11,12 +11,13 @@
</style>
<print-preview-number-settings-section max-value="999" min-value=1
default-value="1" input-label="$i18n{copiesLabel}"
- input-string="{{inputString_}}" input-valid="{{inputValid_}}"
- hint-message="$i18n{copiesInstruction}">
+ disabled="[[disabled]]" current-value="{{currentValue_}}"
+ input-valid="{{inputValid_}}" hint-message="$i18n{copiesInstruction}">
<div slot="opt-inside-content" class="checkbox" aria-live="polite"
- hidden$="[[collateHidden_(inputString_, inputValid_)]]">
+ hidden$="[[collateHidden_(currentValue_, inputValid_)]]">
<label>
<input id="collate" type="checkbox" on-change="onCollateChange_"
+ disabled$="[[getDisabled(state)]]"
aria-labelledby="copies-collate-label">
<span id="copies-collate-label">$i18n{optionCollate}</span>
</label>
diff --git a/chromium/chrome/browser/resources/print_preview/new/copies_settings.js b/chromium/chrome/browser/resources/print_preview/new/copies_settings.js
index 826c5de2217..1115ac4c99f 100644
--- a/chromium/chrome/browser/resources/print_preview/new/copies_settings.js
+++ b/chromium/chrome/browser/resources/print_preview/new/copies_settings.js
@@ -9,17 +9,19 @@ Polymer({
properties: {
/** @private {string} */
- inputString_: String,
+ currentValue_: String,
/** @private {boolean} */
inputValid_: Boolean,
+
+ disabled: Boolean,
},
/** @private {boolean} */
isInitialized_: false,
observers: [
- 'onInputChanged_(inputString_, inputValid_)',
+ 'onInputChanged_(currentValue_, inputValid_)',
'onInitialized_(settings.copies.value, settings.collate.value)'
],
@@ -32,7 +34,7 @@ Polymer({
return;
this.isInitialized_ = true;
const copies = this.getSetting('copies');
- this.inputString_ = /** @type {string} */ (copies.value.toString());
+ this.currentValue_ = /** @type {string} */ (copies.value.toString());
const collate = this.getSetting('collate');
this.$.collate.checked = /** @type {boolean} */ (collate.value);
},
@@ -44,7 +46,7 @@ Polymer({
*/
onInputChanged_: function() {
this.setSetting(
- 'copies', this.inputValid_ ? parseInt(this.inputString_, 10) : 1);
+ 'copies', this.inputValid_ ? parseInt(this.currentValue_, 10) : 1);
this.setSettingValid('copies', this.inputValid_);
},
@@ -53,7 +55,7 @@ Polymer({
* @private
*/
collateHidden_: function() {
- return !this.inputValid_ || parseInt(this.inputString_, 10) == 1;
+ return !this.inputValid_ || parseInt(this.currentValue_, 10) == 1;
},
/** @private */
diff --git a/chromium/chrome/browser/resources/print_preview/new/destination_dialog.html b/chromium/chrome/browser/resources/print_preview/new/destination_dialog.html
new file mode 100644
index 00000000000..540227ae57d
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/new/destination_dialog.html
@@ -0,0 +1,132 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
+<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/load_time_data.html">
+<link rel="import" href="../data/destination.html">
+<link rel="import" href="../data/destination_store.html">
+<link rel="import" href="button_css.html">
+<link rel="import" href="destination_list.html">
+<link rel="import" href="print_preview_search_box.html">
+<link rel="import" href="print_preview_shared_css.html">
+<link rel="import" href="search_dialog_css.html">
+<link rel="import" href="select_css.html">
+
+<dom-module id="print-preview-destination-dialog">
+ <template>
+ <style include="print-preview-shared button action-link select cr-hidden-style search-dialog">
+ :host #dialog {
+ width: 640px;
+ }
+
+ :host .user-info {
+ font-size: calc(13/15 * 1em);
+ margin-top: 14px;
+ }
+
+ :host .user-info .account-select-label {
+ -webkit-padding-end: 18px;
+ }
+
+ :host .user-info .account-select {
+ width: auto
+ }
+
+ :host #dialog .cloudprint-promo {
+ align-items: center;
+ background-color: #f5f5f5;
+ border-color: #e7e7e7;
+ border-top-style: solid;
+ border-width: 1px;
+ color: #888;
+ display: flex;
+ padding: 14px 17px;
+ }
+
+ :host .cloudprint-promo .promo-text {
+ flex: 1;
+ }
+
+ :host .cloudprint-promo .icon {
+ -webkit-margin-end: 12px;
+ display: block;
+ height: 24px;
+ width: 24px;
+ }
+
+ :host .cloudprint-promo .close-button {
+ -webkit-margin-start: 12px;
+ background-image: -webkit-image-set(
+ url(chrome://theme/IDR_CLOSE_DIALOG) 1x,
+ url(chrome://theme/IDR_CLOSE_DIALOG@2x) 2x);
+ background-repeat: no-repeat;
+ background-size: 14px;
+ height: 14px;
+ width: 14px;
+ }
+
+ :host .cloudprint-promo .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);
+ }
+
+ :host .cloudprint-promo .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);
+ }
+ </style>
+ <dialog is="cr-dialog" id="dialog" on-close="onCloseOrCancel_">
+ <div slot="title">
+ <div>$i18n{destinationSearchTitle}</div>
+ <div class="user-info" hidden$="[[!userInfo.loggedIn]]">
+ <label class="account-select-label" id="accountSelectLabel">
+ $i18n{accountSelectTitle}
+ </label>
+ <select class="account-select" aria-labelledby="accountSelectLabel"
+ on-change="onUserChange_">
+ <template is="dom-repeat" items="[[userInfo.users]]">
+ <option selected="[[isSelected_(item, userInfo.activeUser)]]"
+ value="[[item]]">
+ [[item]]
+ </option>
+ </template>
+ <option value="">$i18n{addAccountTitle}</option>
+ </select>
+ </div>
+ <print-preview-search-box id="searchBox"
+ label="$i18n{searchBoxPlaceholder}" search-query="{{searchQuery_}}">
+ </print-preview-search-box>
+ </div>
+ <div slot="body" scrollable>
+ <print-preview-destination-list
+ destinations="[[recentDestinationList_]]"
+ search-query="[[searchQuery_]]"
+ title="$i18n{recentDestinationsTitle}"
+ on-destination-selected="onDestinationSelected_">
+ </print-preview-destination-list>
+ <print-preview-destination-list destinations="[[destinations_]]"
+ has-action-link loading-destinations="[[loadingDestinations_]]"
+ search-query="[[searchQuery_]]"
+ title="$i18n{printDestinationsTitle}"
+ on-destination-selected="onDestinationSelected_">
+ </print-preview-destination-list>
+ </div>
+ <div slot="button-container">
+ <button class="cancel-button" on-click="onCancelButtonClick_">
+ $i18n{cancel}
+ </button>
+ </div>
+ <div class="cloudprint-promo" slot="footer"
+ hidden$="[[!showCloudPrintPromo]]">
+ <img src="../images/cloud.png" class="icon" alt="">
+ <div class="promo-text"></div>
+ <div class="close-button"></div>
+ </div>
+ </dialog>
+ </template>
+ <script src="destination_dialog.js"></script>
+</dom-module>
diff --git a/chromium/chrome/browser/resources/print_preview/new/destination_dialog.js b/chromium/chrome/browser/resources/print_preview/new/destination_dialog.js
new file mode 100644
index 00000000000..7e2162941c6
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/new/destination_dialog.js
@@ -0,0 +1,205 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+Polymer({
+ is: 'print-preview-destination-dialog',
+
+ behaviors: [I18nBehavior],
+
+ properties: {
+ /** @type {?print_preview.DestinationStore} */
+ destinationStore: {
+ type: Object,
+ observer: 'onDestinationStoreSet_',
+ },
+
+ /** @type {!print_preview.UserInfo} */
+ userInfo: {
+ type: Object,
+ notify: true,
+ },
+
+ /** @type {boolean} */
+ showCloudPrintPromo: {
+ type: Boolean,
+ notify: true,
+ },
+
+ /** @private {!Array<!print_preview.Destination>} */
+ destinations_: {
+ type: Array,
+ notify: true,
+ value: [],
+ },
+
+ /** @private {boolean} */
+ loadingDestinations_: {
+ type: Boolean,
+ value: false,
+ },
+
+ /** @type {!Array<!print_preview.RecentDestination>} */
+ recentDestinations: Array,
+
+ /** @private {!Array<!print_preview.Destination>} */
+ recentDestinationList_: {
+ type: Array,
+ notify: true,
+ computed: 'computeRecentDestinationList_(' +
+ 'destinationStore, recentDestinations, recentDestinations.*, ' +
+ 'userInfo, destinations_.*)',
+ },
+
+ /** @private {?RegExp} */
+ searchQuery_: {
+ type: Object,
+ value: null,
+ },
+ },
+
+ /** @private {!EventTracker} */
+ tracker_: new EventTracker(),
+
+ /** @override */
+ ready: function() {
+ this.$$('.promo-text').innerHTML =
+ this.i18nAdvanced('cloudPrintPromotion', {
+ substitutions: ['<a is="action-link" class="sign-in">', '</a>'],
+ attrs: {
+ 'is': (node, v) => v == 'action-link',
+ 'class': (node, v) => v == 'sign-in',
+ },
+ });
+ },
+
+ /** @override */
+ attached: function() {
+ this.tracker_.add(
+ assert(this.$$('.sign-in')), 'click', this.onSignInClick_.bind(this));
+ this.tracker_.add(
+ assert(this.$$('.cloudprint-promo > .close-button')), 'click',
+ this.onCloudPrintPromoDismissed_.bind(this));
+ },
+
+ /** @private */
+ onDestinationStoreSet_: function() {
+ assert(this.destinations_.length == 0);
+ const destinationStore = assert(this.destinationStore);
+ this.tracker_.add(
+ destinationStore,
+ print_preview.DestinationStore.EventType.DESTINATIONS_INSERTED,
+ this.updateDestinations_.bind(this));
+ this.tracker_.add(
+ destinationStore,
+ print_preview.DestinationStore.EventType.DESTINATION_SEARCH_DONE,
+ this.updateDestinations_.bind(this));
+ },
+
+ /** @private */
+ updateDestinations_: function() {
+ this.notifyPath('userInfo.users');
+ this.notifyPath('userInfo.activeUser');
+ this.notifyPath('userInfo.loggedIn');
+ if (this.userInfo.loggedIn)
+ this.showCloudPrintPromo = false;
+
+ this.destinations_ = this.userInfo ?
+ this.destinationStore.destinations(this.userInfo.activeUser) :
+ [];
+ this.loadingDestinations_ =
+ this.destinationStore.isPrintDestinationSearchInProgress;
+ },
+
+ /**
+ * @return {!Array<!print_preview.Destination>}
+ * @private
+ */
+ computeRecentDestinationList_: function() {
+ let recentDestinations = [];
+ const filterAccount = this.userInfo.activeUser;
+ this.recentDestinations.forEach((recentDestination) => {
+ const destination = this.destinationStore.getDestination(
+ recentDestination.origin, recentDestination.id,
+ recentDestination.account || '');
+ if (destination &&
+ (!destination.account || destination.account == filterAccount)) {
+ recentDestinations.push(destination);
+ }
+ });
+ return recentDestinations;
+ },
+
+ /** @private */
+ onCloseOrCancel_: function() {
+ if (this.searchQuery_)
+ this.$.searchBox.setValue('');
+ },
+
+ /** @private */
+ onCancelButtonClick_: function() {
+ this.$.dialog.cancel();
+ },
+
+ /**
+ * @param {!CustomEvent} e Event containing the selected destination.
+ * @private
+ */
+ onDestinationSelected_: function(e) {
+ this.destinationStore.selectDestination(
+ /** @type {!print_preview.Destination} */ (e.detail));
+ this.$.dialog.close();
+ },
+
+ show: function() {
+ this.loadingDestinations_ =
+ this.destinationStore.isPrintDestinationSearchInProgress;
+ this.$.dialog.showModal();
+ },
+
+ /** @return {boolean} Whether the dialog is open. */
+ isOpen: function() {
+ return this.$.dialog.hasAttribute('open');
+ },
+
+ /** @private */
+ isSelected_: function(account) {
+ return account == this.userInfo.activeUser;
+ },
+
+ /** @private */
+ onSignInClick_: function() {
+ print_preview.NativeLayer.getInstance().signIn(false).then(() => {
+ this.destinationStore.onDestinationsReload();
+ });
+ },
+
+ /** @private */
+ onCloudPrintPromoDismissed_: function() {
+ this.showCloudPrintPromo = false;
+ },
+
+ /** @private */
+ onUserChange_: function() {
+ const select = this.$$('select');
+ const account = select.value;
+ if (account) {
+ this.showCloudPrintPromo = false;
+ this.userInfo.activeUser = account;
+ this.notifyPath('userInfo.activeUser');
+ this.notifyPath('userInfo.loggedIn');
+ this.destinationStore.reloadUserCookieBasedDestinations();
+ } else {
+ print_preview.NativeLayer.getInstance().signIn(true).then(
+ this.destinationStore.onDestinationsReload.bind(
+ this.destinationStore));
+ const options = select.querySelectorAll('option');
+ for (let i = 0; i < options.length; i++) {
+ if (options[i].value == this.userInfo.activeUser) {
+ select.selectedIndex = i;
+ break;
+ }
+ }
+ }
+ },
+});
diff --git a/chromium/chrome/browser/resources/print_preview/new/destination_list.html b/chromium/chrome/browser/resources/print_preview/new/destination_list.html
new file mode 100644
index 00000000000..3a008a5df4d
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/new/destination_list.html
@@ -0,0 +1,102 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
+<link rel="import" href="chrome://resources/html/action_link_css.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="../native_layer.html">
+<link rel="import" href="../data/destination.html">
+<link rel="import" href="destination_list_item.html">
+<link rel="import" href="print_preview_shared_css.html">
+<link rel="import" href="throbber_css.html">
+
+<dom-module id="print-preview-destination-list">
+ <template>
+ <style include="print-preview-shared action-link cr-hidden-style throbber">
+ :host {
+ padding: 0 14px 18px;
+ user-select: none;
+ }
+
+ :host > header {
+ -webkit-padding-end: 19px;
+ -webkit-padding-start: 0;
+ background-color: transparent;
+ border-bottom: 1px solid #d2d2d2;
+ padding-bottom: 8px;
+ }
+
+ :host :-webkit-any(.title, .action-link, .total) {
+ -webkit-padding-end: 8px;
+ -webkit-padding-start: 4px;
+ display: inline;
+ vertical-align: middle;
+ }
+
+ :host .throbber-container {
+ -webkit-padding-end: 16px;
+ -webkit-padding-start: 8px;
+ display: inline-block;
+ position: relative;
+ vertical-align: middle;
+ }
+
+ :host .throbber {
+ vertical-align: middle;
+ }
+
+ :host .no-destinations-message {
+ -webkit-padding-start: 18px;
+ color: #999;
+ padding-bottom: 8px;
+ padding-top: 8px;
+ }
+
+ :host .list-item {
+ -webkit-padding-end: 2px;
+ -webkit-padding-start: 18px;
+ cursor: default;
+ display: flex;
+ padding-bottom: 3px;
+ padding-top: 3px;
+ }
+
+ :not(.moving).list-item {
+ transition: background-color 150ms;
+ }
+
+ .list-item:hover,
+ .list-item:focus {
+ background-color: rgb(228, 236, 247);
+ }
+
+ .list-item:focus {
+ outline: none;
+ }
+ </style>
+ <header>
+ <h4 class="title">[[title]]</h4>
+ <span class="total" hidden$="[[!showDestinationsTotal_]]">
+ [[i18n('destinationCount', matchingDestinationsCount_)]]
+ </span>
+ <a is="action-link" class="action-link" hidden$="[[!hasActionLink]]"
+ on-click="onActionLinkClick_">
+ $i18n{manage}
+ </a>
+ <div class="throbber-container" hidden$="[[!loadingDestinations]]">
+ <div class="throbber"></div>
+ </div>
+ </header>
+ <template is="dom-repeat" items="[[destinations]]" notify-dom-change
+ on-dom-change="updateIfNeeded_">
+ <print-preview-destination-list-item class="list-item"
+ search-query="[[searchQuery]]" destination="[[item]]"
+ on-click="onDestinationSelected_">
+ </print-preview-destination-list-item>
+ </template>
+ <div class="no-destinations-message" hidden$="[[hasDestinations_]]">
+ $i18n{noDestinationsMessage}
+ </div>
+ </template>
+ <script src="destination_list.js"></script>
+</dom-module>
+
diff --git a/chromium/chrome/browser/resources/print_preview/new/destination_list.js b/chromium/chrome/browser/resources/print_preview/new/destination_list.js
new file mode 100644
index 00000000000..b83f9309149
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/new/destination_list.js
@@ -0,0 +1,138 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+'use strict';
+
+Polymer({
+ is: 'print-preview-destination-list',
+
+ behaviors: [I18nBehavior],
+
+ properties: {
+ /** @type {Array<!print_preview.Destination>} */
+ destinations: {
+ type: Array,
+ observer: 'destinationsChanged_',
+ },
+
+ /** @type {boolean} */
+ hasActionLink: {
+ type: Boolean,
+ value: false,
+ },
+
+ /** @type {boolean} */
+ loadingDestinations: {
+ type: Boolean,
+ value: false,
+ },
+
+ /** @type {?RegExp} */
+ searchQuery: {
+ type: Object,
+ observer: 'update_',
+ },
+
+ /** @type {boolean} */
+ title: String,
+
+ /** @private {number} */
+ matchingDestinationsCount_: {
+ type: Number,
+ value: 0,
+ },
+
+ /** @private {boolean} */
+ hasDestinations_: {
+ type: Boolean,
+ computed: 'computeHasDestinations_(matchingDestinationsCount_)',
+ },
+
+ /** @private {boolean} */
+ showDestinationsTotal_: {
+ type: Boolean,
+ computed: 'computeShowDestinationsTotal_(matchingDestinationsCount_)',
+ },
+ },
+
+ /** @private {boolean} */
+ newDestinations_: false,
+
+ /**
+ * @param {!Array<!print_preview.Destination>} current
+ * @param {?Array<!print_preview.Destination>} previous
+ * @private
+ */
+ destinationsChanged_: function(current, previous) {
+ if (previous == undefined) {
+ this.matchingDestinationsCount_ = this.destinations.length;
+ } else {
+ this.newDestinations_ = true;
+ }
+ },
+
+ /** @private */
+ updateIfNeeded_: function() {
+ if (!this.newDestinations_)
+ return;
+ this.newDestinations_ = false;
+ this.update_();
+ },
+
+ /** @private */
+ update_: function() {
+ if (!this.destinations)
+ return;
+
+ const listItems =
+ this.shadowRoot.querySelectorAll('print-preview-destination-list-item');
+
+ let matchCount = 0;
+ listItems.forEach(item => {
+ item.hidden =
+ !!this.searchQuery && !item.destination.matches(this.searchQuery);
+ if (!item.hidden) {
+ matchCount++;
+ item.update();
+ }
+ });
+
+ this.matchingDestinationsCount_ =
+ !this.searchQuery ? listItems.length : matchCount;
+ },
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ computeHasDestinations_: function() {
+ return !this.destinations || this.matchingDestinationsCount_ > 0;
+ },
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ computeShowDestinationsTotal_: function() {
+ return this.matchingDestinationsCount_ > 4;
+ },
+
+ /** @private */
+ onActionLinkClick_: function() {
+ print_preview.NativeLayer.getInstance().managePrinters();
+ },
+
+ /**
+ * @param {!Event} e Event containing the destination that was selected.
+ * @private
+ */
+ onDestinationSelected_: function(e) {
+ this.fire(
+ 'destination-selected',
+ /** @type {PrintPreviewDestinationListItemElement} */
+ (e.target).destination);
+ },
+});
+})();
diff --git a/chromium/chrome/browser/resources/print_preview/new/destination_list_item.html b/chromium/chrome/browser/resources/print_preview/new/destination_list_item.html
new file mode 100644
index 00000000000..ad4503b6c68
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/new/destination_list_item.html
@@ -0,0 +1,136 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
+<link rel="import" href="../native_layer.html">
+<link rel="import" href="../data/destination.html">
+<link rel="import" href="highlight_utils.html">
+<link rel="import" href="print_preview_shared_css.html">
+
+<dom-module id="print-preview-destination-list-item">
+ <template>
+ <style include="print-preview-shared action-link cr-hidden-style">
+ :host .icon {
+ -webkit-margin-end: 8px;
+ display: inline-block;
+ flex: 0 0 auto;
+ height: 24px;
+ transition: opacity 150ms;
+ vertical-align: middle;
+ width: 24px;
+ }
+
+ :host .name,
+ :host .search-hint {
+ flex: 0 1 auto;
+ line-height: 24px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ vertical-align: middle;
+ white-space: nowrap;
+ }
+
+ :host .search-hint {
+ -webkit-margin-start: 1em;
+ color: #999;
+ font-size: 75%;
+ }
+
+ :host .connection-status,
+ :host .learn-more-link {
+ -webkit-margin-start: 1em;
+ flex: 0 0 auto;
+ font-size: 75%;
+ line-height: 24px;
+ vertical-align: middle;
+ }
+
+ :host .learn-more-link {
+ color: rgb(51, 103, 214);
+ }
+
+ :host .register-promo {
+ -webkit-margin-start: 1em;
+ flex: 0 0 auto;
+ }
+
+ :host .extension-controlled-indicator {
+ display: flex;
+ flex: 1;
+ justify-content: flex-end;
+ min-width: 150px;
+ }
+
+ :host .extension-name {
+ -webkit-margin-start: 1em;
+ color: #777;
+ line-height: 24px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ :host .extension-icon {
+ background-position: center;
+ background-repeat: no-repeat;
+ cursor: pointer;
+ flex: 0 0 auto;
+ height: 24px;
+ margin: 0 3px;
+ width: 24px;
+ }
+
+ :host .configuring-in-progress-text,
+ :host .configuring-failed-text {
+ -webkit-margin-start: 1em;
+ flex: 0 1 auto;
+ line-height: 24px;
+ vertical-align: middle;
+ }
+
+ :host .configuring-failed-text {
+ color: red;
+ font-style: italic;
+ }
+
+ :host([stale_]) :-webkit-any(.icon, .name, .connection-status) {
+ opacity: 0.4;
+ }
+ </style>
+ <img class="icon" src="[[destination.iconUrl]]"
+ srcset="[[destination.srcSet]]">
+ <span class="name searchable">[[destination.displayName]]</span>
+ <span class="search-hint searchable">[[searchHint_]]</span>
+ <span class="connection-status"
+ hidden$="[[!destination.isOfflineOrInvalid]]">
+ [[destination.connectionStatusText]]
+ </span>
+ <a is="action-link" class="learn-more-link"
+ hidden$="[[!destination.shouldShowInvalidCertificateError]]">
+ $i18n{learnMore}
+ </a>
+ <span class="register-promo" hidden$="[[!destination.isUnregistered]]">
+ <button class="register-promo-button">
+ $i18n{registerPromoButtonText}
+ </button>
+ </span>
+ <span class="extension-controlled-indicator"
+ hidden$="[[!destination.isExtension]]">
+ <span class="extension-name searchable">
+ [[destination.extensionName]]
+ </span>
+ <span class="extension-icon" role="button" tabindex="0"></span>
+ </span>
+<if expr="chromeos">
+ <span class="configuring-in-progress-text" hidden>
+ $i18n{configuringInProgressText}
+ <span class="configuring-text-jumping-dots">
+ <span>.</span><span>.</span><span>.</span>
+ </span>
+ </span>
+ <span class="configuring-failed-text" hidden>
+ $i18n{configuringFailedText}
+ </span>
+</if>
+ </template>
+ <script src="destination_list_item.js"></script>
+</dom-module>
diff --git a/chromium/chrome/browser/resources/print_preview/new/destination_list_item.js b/chromium/chrome/browser/resources/print_preview/new/destination_list_item.js
new file mode 100644
index 00000000000..7e790c0644e
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/new/destination_list_item.js
@@ -0,0 +1,58 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+Polymer({
+ is: 'print-preview-destination-list-item',
+
+ properties: {
+ /** @type {!print_preview.Destination} */
+ destination: Object,
+
+ /** @type {?RegExp} */
+ searchQuery: Object,
+
+ /** @private */
+ stale_: {
+ type: Boolean,
+ reflectToAttribute: true,
+ },
+
+ /** @private {string} */
+ searchHint_: String,
+ },
+
+ observers: [
+ 'onDestinationPropertiesChange_(' +
+ 'destination.displayName, destination.isOfflineOrInvalid)',
+ ],
+
+ /** @private {boolean} */
+ highlighted_: false,
+
+ /** @private */
+ onDestinationPropertiesChange_: function() {
+ this.title = this.destination.displayName;
+ this.stale_ = this.destination.isOfflineOrInvalid;
+ },
+
+ update: function() {
+ this.updateSearchHint_();
+ this.updateHighlighting_();
+ },
+
+ /** @private */
+ updateSearchHint_: function() {
+ this.searchHint_ = !this.searchQuery ?
+ '' :
+ this.destination.extraPropertiesToMatch
+ .filter(p => p.match(this.searchQuery))
+ .join(' ');
+ },
+
+ /** @private */
+ updateHighlighting_: function() {
+ this.highlighted_ = print_preview.updateHighlights(
+ this, this.searchQuery, this.highlighted_);
+ },
+});
diff --git a/chromium/chrome/browser/resources/print_preview/new/destination_settings.html b/chromium/chrome/browser/resources/print_preview/new/destination_settings.html
index 69f1f01c23f..b95e9e8be34 100644
--- a/chromium/chrome/browser/resources/print_preview/new/destination_settings.html
+++ b/chromium/chrome/browser/resources/print_preview/new/destination_settings.html
@@ -1,8 +1,13 @@
<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.html">
<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
-<link rel="import" href="button_css.html">
+<link rel="import" href="chrome://resources/html/event_tracker.html">
<link rel="import" href="../data/destination.html">
+<link rel="import" href="../data/destination_store.html">
+<link rel="import" href="../data/user_info.html">
+<link rel="import" href="button_css.html">
+<link rel="import" href="destination_dialog.html">
<link rel="import" href="print_preview_shared_css.html">
<link rel="import" href="throbber_css.html">
<link rel="import" href="settings_section.html">
@@ -69,15 +74,28 @@
<img class="destination-icon"
src="[[destination.iconUrl]]" alt="">
<div class="destination-info-wrapper">
- <div class="destination-name">[[destination.id]]</div>
+ <div class="destination-name">[[destination.displayName]]</div>
<div class="destination-location">[[destination.hint]]</div>
<div class="destination-connection-status">
[[destination.connectionStatusText]]</div>
</div>
</div>
- <button>$i18n{changeDestination}</button>
+ <button
+ disabled$="[[shouldDisableButton_(destinationStore, disabled,
+ state)]]"
+ on-click="onChangeButtonClick_">
+ $i18n{changeDestination}
+ </button>
</div>
</print-preview-settings-section>
+ <template is="cr-lazy-render" id="destinationDialog">
+ <print-preview-destination-dialog
+ destination-store="[[destinationStore]]"
+ recent-destinations="[[recentDestinations]]"
+ user-info="{{userInfo}}"
+ show-cloud-print-promo="{{showCloudPrintPromo_}}">
+ </print-preview-destination-dialog>
+ </template>
</template>
<script src="destination_settings.js"></script>
</dom-module>
diff --git a/chromium/chrome/browser/resources/print_preview/new/destination_settings.js b/chromium/chrome/browser/resources/print_preview/new/destination_settings.js
index 6fde6cc0895..e61f33c637c 100644
--- a/chromium/chrome/browser/resources/print_preview/new/destination_settings.js
+++ b/chromium/chrome/browser/resources/print_preview/new/destination_settings.js
@@ -9,19 +9,72 @@ Polymer({
/** @type {!print_preview.Destination} */
destination: Object,
+ /** @type {?print_preview.DestinationStore} */
+ destinationStore: Object,
+
+ /** @type {!Array<!print_preview.RecentDestination>} */
+ recentDestinations: Array,
+
+ /** @type {!print_preview.UserInfo} */
+ userInfo: {
+ type: Object,
+ notify: true,
+ },
+
+ disabled: Boolean,
+
+ /** @type {!print_preview_new.State} */
+ state: Number,
+
+ /** @private {boolean} */
+ showCloudPrintPromo_: {
+ type: Boolean,
+ value: false,
+ },
+
/** @private {boolean} */
- loadingDestination_: Boolean,
+ loadingDestination_: {
+ type: Boolean,
+ value: true,
+ },
},
- /** @override */
- ready: function() {
- this.loadingDestination_ = true;
- // Simulate transition from spinner to destination.
- setTimeout(this.doneLoading_.bind(this), 5000);
+ observers: ['onDestinationSet_(destination, destination.id)'],
+
+ /**
+ * @return {boolean} Whether the destination change button should be disabled.
+ * @private
+ */
+ shouldDisableButton_: function() {
+ return !this.destinationStore ||
+ (this.disabled &&
+ this.state != print_preview_new.State.INVALID_PRINTER);
},
/** @private */
- doneLoading_: function() {
- this.loadingDestination_ = false;
+ onDestinationSet_: function() {
+ if (this.destination && this.destination.id)
+ this.loadingDestination_ = false;
+ },
+
+ /** @private */
+ onChangeButtonClick_: function() {
+ this.destinationStore.startLoadAllDestinations();
+ const dialog = this.$.destinationDialog.get();
+ // This async() call is a workaround to prevent a DCHECK - see
+ // https://crbug.com/804047.
+ this.async(() => {
+ dialog.show();
+ }, 1);
+ },
+
+ showCloudPrintPromo: function() {
+ this.showCloudPrintPromo_ = true;
+ },
+
+ /** @return {boolean} Whether the destinations dialog is open. */
+ isDialogOpen: function() {
+ const destinationDialog = this.$$('print-preview-destination-dialog');
+ return destinationDialog && destinationDialog.isOpen();
},
});
diff --git a/chromium/chrome/browser/resources/print_preview/new/dpi_settings.html b/chromium/chrome/browser/resources/print_preview/new/dpi_settings.html
index 2e448060168..1714dc87c91 100644
--- a/chromium/chrome/browser/resources/print_preview/new/dpi_settings.html
+++ b/chromium/chrome/browser/resources/print_preview/new/dpi_settings.html
@@ -15,7 +15,7 @@
<div slot="controls">
<print-preview-settings-select aria-labelled-by="dpi-label"
capability="[[capabilityWithLabels_]]" setting-name="dpi"
- settings="{{settings}}">
+ settings="{{settings}}" disabled="[[disabled]]">
</print-preview-settings-select>
</div>
</print-preview-settings-section>
diff --git a/chromium/chrome/browser/resources/print_preview/new/dpi_settings.js b/chromium/chrome/browser/resources/print_preview/new/dpi_settings.js
index c5d1c8d9b11..31e95f6d35b 100644
--- a/chromium/chrome/browser/resources/print_preview/new/dpi_settings.js
+++ b/chromium/chrome/browser/resources/print_preview/new/dpi_settings.js
@@ -30,6 +30,8 @@ Polymer({
/** @type {{ option: Array<!print_preview_new.SelectOption> }} */
capability: Object,
+ disabled: Boolean,
+
/** @private {{ option: Array<!print_preview_new.SelectOption> }} */
capabilityWithLabels_: {
type: Object,
diff --git a/chromium/chrome/browser/resources/print_preview/new/header.html b/chromium/chrome/browser/resources/print_preview/new/header.html
index e16008da496..70468e7670d 100644
--- a/chromium/chrome/browser/resources/print_preview/new/header.html
+++ b/chromium/chrome/browser/resources/print_preview/new/header.html
@@ -1,9 +1,11 @@
<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="chrome://resources/html/cr.html">
<link rel="import" href="button_css.html">
<link rel="import" href="../data/destination.html">
<link rel="import" href="settings_behavior.html">
<link rel="import" href="print_preview_shared_css.html">
+<link rel="import" href="state.html">
<link rel="import" href="strings.html">
<dom-module id="print-preview-header">
@@ -67,16 +69,13 @@
}
</style>
<h1 class="title">$i18n{title}</h1>
- <span class="summary"
- aria-label="[[getSummaryLabel_(currentErrorOrState_, labelInfo_)]]"
- inner-h-t-m-l="[[getSummary_(currentErrorOrState_, labelInfo_)]]">
+ <span class="summary" aria-label$="[[summaryLabel_]]"
+ inner-h-t-m-l="[[summary_]]">
</span>
<div id="button-strip">
- <button class="cancel" on-tap="onCancelButtonTap_">
- $i18n{cancel}
- </button>
- <button class="print default" on-tap="onPrintButtonTap_"
- disabled$="[[printButtonDisabled_(currentErrorOrState_)]]">
+ <button class="cancel" on-click="onCancelClick_">$i18n{cancel}</button>
+ <button class="print default" on-click="onPrintClick_"
+ disabled$="[[!printButtonEnabled_]]">
[[getPrintButton_(destination.id)]]
</button>
</div>
diff --git a/chromium/chrome/browser/resources/print_preview/new/header.js b/chromium/chrome/browser/resources/print_preview/new/header.js
index 00ebc16df7f..b1bf7920e69 100644
--- a/chromium/chrome/browser/resources/print_preview/new/header.js
+++ b/chromium/chrome/browser/resources/print_preview/new/header.js
@@ -2,6 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+cr.exportPath('print_preview_new.Header');
+
+/**
+ * @typedef {{numPages: number,
+ * numSheets: number,
+ * pagesLabel: string,
+ * summaryLabel: string}}
+ */
+print_preview_new.Header.LabelInfo;
+
Polymer({
is: 'print-preview-header',
@@ -12,51 +22,43 @@ Polymer({
destination: Object,
/** @type {!print_preview_new.State} */
- state: {
- type: Object,
- notify: true,
- },
+ state: Number,
/** @private {boolean} */
- printInProgress_: {
+ printButtonEnabled_: {
type: Boolean,
- notify: true,
value: false,
},
- /**
- * @private {?string} Null value indicates that there is no error or
- * state to display in the summary.
- */
- currentErrorOrState_: {
+ /** @private {?string} */
+ summary_: {
type: String,
- computed: 'computeErrorOrStateString_(state.*, ' +
- 'settings.copies.valid, settings.scaling.valid, ' +
- 'settings.pages.valid, printInProgress_)'
+ notify: true,
+ value: null,
},
- /**
- * @private {{numPages: number,
- * numSheets: number,
- * pagesLabel: string,
- * summaryLabel: string}}
- */
- labelInfo_: {
- type: Object,
- computed: 'getLabelInfo_(currentErrorOrState_, destination.id, ' +
- 'settings.copies.value, settings.pages.value, ' +
- 'settings.duplex.value)'
+ /** @private {?string} */
+ summaryLabel_: {
+ type: String,
+ notify: true,
+ value: null,
},
+
+ errorMessage: String,
},
+ observers:
+ ['update_(settings.copies.value, settings.duplex.value, ' +
+ 'settings.pages.value, state)'],
+
/** @private */
- onPrintButtonTap_: function() {
- this.printInProgress_ = true;
+ onPrintClick_: function() {
+ this.fire('print-requested');
},
/** @private */
- onCancelButtonTap_: function() {
- this.set('state.cancelled', true);
+ onCancelClick_: function() {
+ this.fire('cancel-requested');
},
/**
@@ -81,34 +83,10 @@ Polymer({
},
/**
- * @return {?string}
+ * @return {!print_preview_new.Header.LabelInfo}
* @private
*/
- computeErrorOrStateString_: function() {
- if (this.state.cloudPrintError != '')
- return this.state.cloudPrintError;
- if (this.state.privetExtensionError != '')
- return this.state.privetExtensionError;
- if (this.state.invalidSettings || this.state.previewFailed ||
- this.state.previewLoading || !this.getSetting('copies').valid ||
- !this.getSetting('scaling').valid || !this.getSetting('pages').valid) {
- return '';
- }
- if (this.printInProgress_) {
- return loadTimeData.getString(
- this.isPdfOrDrive_() ? 'saving' : 'printing');
- }
- return null;
- },
-
- /**
- * @return {{numPages: number,
- * numSheets: number,
- * pagesLabel: string,
- * summaryLabel: string}}
- * @private
- */
- getLabelInfo_: function() {
+ computeLabelInfo_: function() {
const saveToPdfOrDrive = this.isPdfOrDrive_();
let numPages = this.getSetting('pages').value.length;
let numSheets = numPages;
@@ -139,23 +117,41 @@ Polymer({
};
},
- /**
- * @return {boolean}
- * @private
- */
- printButtonDisabled_: function() {
- return this.currentErrorOrState_ != null;
+ /** @private */
+ update_: function() {
+ switch (this.state) {
+ case (print_preview_new.State.PRINTING):
+ this.printButtonEnabled_ = false;
+ this.summary_ = loadTimeData.getString(
+ this.isPdfOrDrive_() ? 'saving' : 'printing');
+ this.summaryLabel_ = this.summary_;
+ break;
+ case (print_preview_new.State.READY):
+ this.printButtonEnabled_ = true;
+ const labelInfo = this.computeLabelInfo_();
+ this.summary_ = this.getSummary_(labelInfo);
+ this.summaryLabel_ = this.getSummaryLabel_(labelInfo);
+ break;
+ case (print_preview_new.State.FATAL_ERROR):
+ this.printButtonEnabled_ = false;
+ this.summary_ = this.errorMessage;
+ this.summaryLabel_ = this.errorMessage;
+ break;
+ default:
+ this.summary_ = null;
+ this.summaryLabel_ = null;
+ this.printButtonEnabled_ = false;
+ break;
+ }
},
/**
+ * @param {!print_preview_new.Header.LabelInfo} labelInfo
* @return {string}
* @private
*/
- getSummary_: function() {
- let html = this.currentErrorOrState_;
- if (html != null)
- return html;
- const labelInfo = this.labelInfo_;
+ getSummary_: function(labelInfo) {
+ let html = null;
if (labelInfo.numPages != labelInfo.numSheets) {
html = loadTimeData.getStringF(
'printPreviewSummaryFormatLong',
@@ -175,13 +171,11 @@ Polymer({
},
/**
+ * @param {!print_preview_new.Header.LabelInfo} labelInfo
* @return {string}
* @private
*/
- getSummaryLabel_: function() {
- if (this.currentErrorOrState_ != null)
- return this.currentErrorOrState_;
- const labelInfo = this.labelInfo_;
+ getSummaryLabel_: function(labelInfo) {
if (labelInfo.numPages != labelInfo.numSheets) {
return loadTimeData.getStringF(
'printPreviewSummaryFormatLong', labelInfo.numSheets.toLocaleString(),
diff --git a/chromium/chrome/browser/resources/print_preview/new/highlight_utils.html b/chromium/chrome/browser/resources/print_preview/new/highlight_utils.html
new file mode 100644
index 00000000000..ad6c80409c2
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/new/highlight_utils.html
@@ -0,0 +1,3 @@
+<link rel="import" href="chrome://resources/html/search_highlight_utils.html">
+
+<script src="highlight_utils.js"></script>
diff --git a/chromium/chrome/browser/resources/print_preview/new/highlight_utils.js b/chromium/chrome/browser/resources/print_preview/new/highlight_utils.js
new file mode 100644
index 00000000000..0a8df2f4780
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/new/highlight_utils.js
@@ -0,0 +1,60 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('print_preview', function() {
+ 'use strict';
+
+ /**
+ * @param {!HTMLElement} element The element to update. Element should have a
+ * shadow root.
+ * @param {?RegExp} query The current search query
+ * @param {boolean} wasHighlighted Whether the element was previously
+ * highlighted.
+ * @return {boolean} Whether the element is highlighted after the update.
+ */
+ function updateHighlights(element, query, wasHighlighted) {
+ if (wasHighlighted) {
+ cr.search_highlight_utils.findAndRemoveHighlights(element);
+ cr.search_highlight_utils.findAndRemoveBubbles(element);
+ }
+
+ if (!query)
+ return false;
+
+ let isHighlighted = false;
+ element.shadowRoot.querySelectorAll('.searchable').forEach(childElement => {
+ childElement.childNodes.forEach(node => {
+ if (node.nodeType != Node.TEXT_NODE)
+ return;
+
+ const textContent = node.nodeValue.trim();
+ if (textContent.length == 0)
+ return;
+
+ if (query.test(textContent)) {
+ isHighlighted = true;
+ // Don't highlight <select> nodes, yellow rectangles can't be
+ // displayed within an <option>.
+ if (node.parentNode.nodeName != 'OPTION') {
+ cr.search_highlight_utils.highlight(node, textContent.split(query));
+ } else {
+ const selectNode = node.parentNode.parentNode;
+ // The bubble should be parented by the select node's parent.
+ // Note: The bubble's ::after element, a yellow arrow, will not
+ // appear correctly in print preview without SPv175 enabled. See
+ // https://crbug.com/817058.
+ cr.search_highlight_utils.highlightControlWithBubble(
+ /** @type {!HTMLElement} */ (assert(selectNode.parentNode)),
+ textContent.match(query)[0]);
+ }
+ }
+ });
+ });
+ return isHighlighted;
+ }
+
+ return {
+ updateHighlights: updateHighlights,
+ };
+});
diff --git a/chromium/chrome/browser/resources/print_preview/new/layout_settings.html b/chromium/chrome/browser/resources/print_preview/new/layout_settings.html
index afbc18b2d85..0b0e7ae3098 100644
--- a/chromium/chrome/browser/resources/print_preview/new/layout_settings.html
+++ b/chromium/chrome/browser/resources/print_preview/new/layout_settings.html
@@ -11,7 +11,8 @@
<print-preview-settings-section>
<span id="layout-label" slot="title">$i18n{layoutLabel}</span>
<div slot="controls">
- <select aria-labelledby="layout-label" on-change="onChange_">
+ <select aria-labelledby="layout-label" on-change="onChange_"
+ disabled$="[[disabled]]">
<option value="portrait" selected>$i18n{optionPortrait}</option>
<option value="landscape">$i18n{optionLandscape}</option>
</select>
diff --git a/chromium/chrome/browser/resources/print_preview/new/layout_settings.js b/chromium/chrome/browser/resources/print_preview/new/layout_settings.js
index 77d431925c4..b84fbe2bd00 100644
--- a/chromium/chrome/browser/resources/print_preview/new/layout_settings.js
+++ b/chromium/chrome/browser/resources/print_preview/new/layout_settings.js
@@ -7,6 +7,10 @@ Polymer({
behaviors: [SettingsBehavior],
+ properties: {
+ disabled: Boolean,
+ },
+
observers: ['onLayoutSettingChange_(settings.layout.value)'],
/**
diff --git a/chromium/chrome/browser/resources/print_preview/new/margins_settings.html b/chromium/chrome/browser/resources/print_preview/new/margins_settings.html
index dbc9ac60d9c..7bf74d1b5a3 100644
--- a/chromium/chrome/browser/resources/print_preview/new/margins_settings.html
+++ b/chromium/chrome/browser/resources/print_preview/new/margins_settings.html
@@ -12,7 +12,8 @@
<print-preview-settings-section>
<span id="margins-label" slot="title">$i18n{marginsLabel}</span>
<div slot="controls">
- <select aria-labelledby="margins-label" on-change="onChange_">
+ <select aria-labelledby="margins-label" on-change="onChange_"
+ disabled$="[[disabled]]">
<!-- The order of these options must match the natural order of their
values, which come from
print_preview.ticket_items.MarginsTypeValue. -->
diff --git a/chromium/chrome/browser/resources/print_preview/new/margins_settings.js b/chromium/chrome/browser/resources/print_preview/new/margins_settings.js
index 65ec5a10e3e..c641b40c73b 100644
--- a/chromium/chrome/browser/resources/print_preview/new/margins_settings.js
+++ b/chromium/chrome/browser/resources/print_preview/new/margins_settings.js
@@ -7,6 +7,10 @@ Polymer({
behaviors: [SettingsBehavior],
+ properties: {
+ disabled: Boolean,
+ },
+
observers: ['onMarginsSettingChange_(settings.margins.value)'],
/**
diff --git a/chromium/chrome/browser/resources/print_preview/new/media_size_settings.html b/chromium/chrome/browser/resources/print_preview/new/media_size_settings.html
index 540f8f0972c..03223dfdce2 100644
--- a/chromium/chrome/browser/resources/print_preview/new/media_size_settings.html
+++ b/chromium/chrome/browser/resources/print_preview/new/media_size_settings.html
@@ -14,7 +14,7 @@
<div slot="controls">
<print-preview-settings-select aria-labelledby="media-size-label"
capability="[[capability]]" setting-name="mediaSize"
- settings="{{settings}}">
+ settings="{{settings}}" disabled="[[disabled]]">
</print-preview-settings-select>
</div>
</print-preview-settings-section>
diff --git a/chromium/chrome/browser/resources/print_preview/new/media_size_settings.js b/chromium/chrome/browser/resources/print_preview/new/media_size_settings.js
index 55dc7ebcb8b..1ff1bfb0715 100644
--- a/chromium/chrome/browser/resources/print_preview/new/media_size_settings.js
+++ b/chromium/chrome/browser/resources/print_preview/new/media_size_settings.js
@@ -9,6 +9,8 @@ Polymer({
properties: {
capability: Object,
+
+ disabled: Boolean,
},
observers:
diff --git a/chromium/chrome/browser/resources/print_preview/new/model.html b/chromium/chrome/browser/resources/print_preview/new/model.html
index 30924113655..3e3ea6386f2 100644
--- a/chromium/chrome/browser/resources/print_preview/new/model.html
+++ b/chromium/chrome/browser/resources/print_preview/new/model.html
@@ -1,6 +1,7 @@
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/html/cr.html">
+<link rel="import" href="settings_behavior.html">
<link rel="import" href="../data/destination.html">
<link rel="import" href="../data/document_info.html">
<link rel="import" href="../data/margins.html">
diff --git a/chromium/chrome/browser/resources/print_preview/new/model.js b/chromium/chrome/browser/resources/print_preview/new/model.js
index bf4fe074786..0b90df49409 100644
--- a/chromium/chrome/browser/resources/print_preview/new/model.js
+++ b/chromium/chrome/browser/resources/print_preview/new/model.js
@@ -33,6 +33,16 @@ cr.exportPath('print_preview_new');
*/
print_preview_new.SerializedSettings;
+/**
+ * Constant values matching printing::DuplexMode enum.
+ * @enum {number}
+ */
+print_preview_new.DuplexMode = {
+ SIMPLEX: 0,
+ LONG_EDGE: 1,
+ UNKNOWN_DUPLEX_MODE: -1
+};
+
(function() {
'use strict';
@@ -42,16 +52,28 @@ const NUM_DESTINATIONS = 3;
/**
* Sticky setting names. Alphabetical except for fitToPage, which must be set
* after scaling in updateFromStickySettings().
- * @type {Array<string>}
+ * @type {!Array<string>}
*/
const STICKY_SETTING_NAMES = [
- 'collate', 'color', 'cssBackground', 'dpi', 'duplex', 'headerFooter',
- 'layout', 'margins', 'mediaSize', 'scaling', 'fitToPage'
+ 'collate',
+ 'color',
+ 'cssBackground',
+ 'dpi',
+ 'duplex',
+ 'headerFooter',
+ 'layout',
+ 'margins',
+ 'mediaSize',
+ 'scaling',
+ 'fitToPage',
+ 'vendorItems',
];
Polymer({
is: 'print-preview-model',
+ behaviors: [SettingsBehavior],
+
properties: {
/**
* Object containing current settings of Print Preview, for use by Polymer
@@ -175,7 +197,7 @@ Polymer({
unavailableValue: {},
valid: true,
available: true,
- key: '',
+ key: 'vendorOptions',
},
// This does not represent a real setting value, and is used only to
// expose the availability of the other options settings section.
@@ -228,12 +250,19 @@ Polymer({
'settings.mediaSize.value, settings.margins.value, ' +
'settings.dpi.value, settings.fitToPage.value, ' +
'settings.scaling.value, settings.duplex.value, ' +
- 'settings.headerFooter.value, settings.cssBackground.value)',
+ 'settings.headerFooter.value, settings.cssBackground.value, ' +
+ 'settings.vendorItems.value)',
],
/** @private {boolean} */
initialized_: false,
+ /** @private {?print_preview_new.SerializedSettings} */
+ stickySettings_: null,
+
+ /** @private {?print_preview.Cdd} */
+ lastDestinationCapabilities_: null,
+
/**
* Updates the availability of the settings sections and values of dpi and
* media size settings.
@@ -244,6 +273,14 @@ Polymer({
this.destination.capabilities.printer :
null;
this.updateSettingsAvailability_(caps);
+
+ if (!caps)
+ return;
+
+ if (this.destination.capabilities == this.lastDestinationCapabilities_)
+ return;
+
+ this.lastDestinationCapabilities_ = this.destination.capabilities;
this.updateSettingsValues_(caps);
},
@@ -290,6 +327,8 @@ Polymer({
this.settings.selectionOnly.available ||
this.settings.headerFooter.available ||
this.settings.rasterize.available);
+ this.set(
+ 'settings.vendorItems.available', !!caps && !!caps.vendor_capability);
},
/**
@@ -318,25 +357,38 @@ Polymer({
*/
updateSettingsValues_: function(caps) {
if (this.settings.mediaSize.available) {
- for (const option of caps.media_size.option) {
- if (option.is_default) {
- this.set('settings.mediaSize.value', option);
- break;
- }
- }
+ const defaultOption = caps.media_size.option.find(o => !!o.is_default);
+ this.set('settings.mediaSize.value', defaultOption);
}
-
if (this.settings.dpi.available) {
- for (const option of caps.dpi.option) {
- if (option.is_default) {
- this.set('settings.dpi.value', option);
- break;
- }
- }
+ const defaultOption = caps.dpi.option.find(o => !!o.is_default);
+ this.set('settings.dpi.value', defaultOption);
} else if (
caps && caps.dpi && caps.dpi.option && caps.dpi.option.length > 0) {
this.set('settings.dpi.value', caps.dpi.option[0]);
}
+
+ if (this.settings.vendorItems.available) {
+ const vendorSettings = {};
+ for (const item of caps.vendor_capability) {
+ let defaultValue = null;
+ if (item.type == 'SELECT' && !!item.select_cap &&
+ !!item.select_cap.option) {
+ const defaultOption =
+ item.select_cap.option.find(o => !!o.is_default);
+ defaultValue = !!defaultOption ? defaultOption.value : null;
+ } else if (item.type == 'RANGE') {
+ if (!!item.range_cap)
+ defaultValue = item.range_cap.default || null;
+ } else if (item.type == 'TYPED_VALUE') {
+ if (!!item.typed_value_cap)
+ defaultValue = item.typed_value_cap.default || null;
+ }
+ if (defaultValue != null)
+ vendorSettings[item.id] = defaultValue;
+ }
+ this.set('settings.vendorItems.value', vendorSettings);
+ }
},
/** @private */
@@ -371,18 +423,20 @@ Polymer({
this.recentDestinations.splice(indexFound, 1);
// Add the most recent destination
- this.recentDestinations.splice(0, 0, newDestination);
- this.notifyPath('recentDestinations');
+ this.splice('recentDestinations', 0, 0, newDestination);
// Persist sticky settings.
this.stickySettingsChanged_();
},
/**
+ * Caches the sticky settings and sets up the recent destinations. Sticky
+ * settings will be applied when destinaton capabilities have been retrieved.
* @param {?string} savedSettingsStr The sticky settings from native layer
*/
- updateFromStickySettings: function(savedSettingsStr) {
- this.initialized_ = true;
+ setStickySettings: function(savedSettingsStr) {
+ assert(!this.stickySettings_ && this.recentDestinations.length == 0);
+
if (!savedSettingsStr)
return;
@@ -403,16 +457,26 @@ Polymer({
}
this.recentDestinations = recentDestinations;
- // Reset initialized, or stickySettingsChanged_ will get called for
- // every setting that gets set below.
- this.initialized_ = false;
- STICKY_SETTING_NAMES.forEach(settingName => {
- const setting = this.get(settingName, this.settings);
- const value = savedSettings[setting.key];
- if (value != undefined)
- this.set(`settings.${settingName}.value`, value);
- });
+ this.stickySettings_ = savedSettings;
+ },
+
+ applyStickySettings: function() {
+ if (this.stickySettings_) {
+ STICKY_SETTING_NAMES.forEach(settingName => {
+ const setting = this.get(settingName, this.settings);
+ const value = this.stickySettings_[setting.key];
+ if (value != undefined)
+ this.set(`settings.${settingName}.value`, value);
+ });
+ }
this.initialized_ = true;
+ this.stickySettings_ = null;
+ this.stickySettingsChanged_();
+ },
+
+ /** @return {boolean} Whether the model has been initialized. */
+ initialized: function() {
+ return this.initialized_;
},
/** @private */
@@ -431,5 +495,168 @@ Polymer({
});
this.fire('save-sticky-settings', JSON.stringify(serialization));
},
+
+ /**
+ * Creates a string that represents a print ticket.
+ * @param {!print_preview.Destination} destination Destination to print to.
+ * @return {string} Serialized print ticket.
+ */
+ createPrintTicket: function(destination) {
+ const dpi = /** @type {{horizontal_dpi: (number | undefined),
+ vertical_dpi: (number | undefined),
+ vendor_id: (number | undefined)}} */ (
+ this.getSettingValue('dpi'));
+
+ const ticket = {
+ mediaSize: this.getSettingValue('mediaSize'),
+ pageCount: this.getSettingValue('pages').length,
+ landscape: this.getSettingValue('layout'),
+ color: destination.getNativeColorModel(
+ /** @type {boolean} */ (this.getSettingValue('color'))),
+ headerFooterEnabled: false, // only used in print preview
+ marginsType: this.getSettingValue('margins'),
+ duplex: this.getSettingValue('duplex') ?
+ print_preview_new.DuplexMode.LONG_EDGE :
+ print_preview_new.DuplexMode.SIMPLEX,
+ copies: this.getSettingValue('copies'),
+ collate: this.getSettingValue('collate'),
+ shouldPrintBackgrounds: this.getSettingValue('cssBackground'),
+ shouldPrintSelectionOnly: false, // only used in print preview
+ previewModifiable: this.documentInfo.isModifiable,
+ printToPDF: destination.id ==
+ print_preview.Destination.GooglePromotedId.SAVE_AS_PDF,
+ printWithCloudPrint: !destination.isLocal,
+ printWithPrivet: destination.isPrivet,
+ printWithExtension: destination.isExtension,
+ rasterizePDF: this.getSettingValue('rasterize'),
+ scaleFactor: parseInt(this.getSettingValue('scaling'), 10),
+ dpiHorizontal: (dpi && 'horizontal_dpi' in dpi) ? dpi.horizontal_dpi : 0,
+ dpiVertical: (dpi && 'vertical_dpi' in dpi) ? dpi.vertical_dpi : 0,
+ deviceName: destination.id,
+ fitToPageEnabled: this.getSettingValue('fitToPage'),
+ pageWidth: this.documentInfo.pageSize.width,
+ pageHeight: this.documentInfo.pageSize.height,
+ showSystemDialog: false,
+ };
+
+ // Set 'cloudPrintID' only if the destination is not local.
+ if (!destination.isLocal)
+ ticket.cloudPrintID = destination.id;
+
+ if (this.getSettingValue('margins') ==
+ print_preview.ticket_items.MarginsTypeValue.CUSTOM) {
+ // TODO (rbpotter): Replace this with real values when custom margins are
+ // implemented.
+ ticket.marginsCustom = {
+ marginTop: 70,
+ marginRight: 70,
+ marginBottom: 70,
+ marginLeft: 70,
+ };
+ }
+
+ if (destination.isPrivet || destination.isExtension) {
+ // TODO (rbpotter): Get local and PDF printers to use the same ticket and
+ // send only this ticket instead of nesting it in a larger ticket.
+ ticket.ticket = this.createCloudJobTicket(destination);
+ ticket.capabilities = JSON.stringify(destination.capabilities);
+ }
+ return JSON.stringify(ticket);
+ },
+
+ /**
+ * Creates an object that represents a Google Cloud Print print ticket.
+ * @param {!print_preview.Destination} destination Destination to print to.
+ * @return {string} Google Cloud Print print ticket.
+ */
+ createCloudJobTicket: function(destination) {
+ assert(
+ !destination.isLocal || destination.isPrivet || destination.isExtension,
+ 'Trying to create a Google Cloud Print print ticket for a local ' +
+ ' non-privet and non-extension destination');
+ assert(
+ destination.capabilities,
+ 'Trying to create a Google Cloud Print print ticket for a ' +
+ 'destination with no print capabilities');
+
+ // Create CJT (Cloud Job Ticket)
+ const cjt = {version: '1.0', print: {}};
+ if (this.settings.collate.available)
+ cjt.print.collate = {collate: this.settings.collate.value};
+ if (this.settings.color.available) {
+ const selectedOption = destination.getSelectedColorOption(
+ /** @type {boolean} */ (this.settings.color.value));
+ if (!selectedOption) {
+ console.error('Could not find correct color option');
+ } else {
+ cjt.print.color = {type: selectedOption.type};
+ if (selectedOption.hasOwnProperty('vendor_id')) {
+ cjt.print.color.vendor_id = selectedOption.vendor_id;
+ }
+ }
+ } else {
+ // Always try setting the color in the print ticket, otherwise a
+ // reasonable reader of the ticket will have to do more work, or process
+ // the ticket sub-optimally, in order to safely handle the lack of a
+ // color ticket item.
+ const defaultOption = destination.defaultColorOption;
+ if (defaultOption) {
+ cjt.print.color = {type: defaultOption.type};
+ if (defaultOption.hasOwnProperty('vendor_id')) {
+ cjt.print.color.vendor_id = defaultOption.vendor_id;
+ }
+ }
+ }
+ if (this.settings.copies.available)
+ cjt.print.copies = {copies: this.settings.copies.value};
+ if (this.settings.duplex.available) {
+ cjt.print.duplex = {
+ type: this.settings.duplex.value ? 'LONG_EDGE' : 'NO_DUPLEX'
+ };
+ }
+ if (this.settings.mediaSize.available) {
+ const mediaValue = this.settings.mediaSize.value;
+ cjt.print.media_size = {
+ width_microns: mediaValue.width_microns,
+ height_microns: mediaValue.height_microns,
+ is_continuous_feed: mediaValue.is_continuous_feed,
+ vendor_id: mediaValue.vendor_id
+ };
+ }
+ if (!this.settings.layout.available) {
+ // In this case "orientation" option is hidden from user, so user can't
+ // adjust it for page content, see Landscape.isCapabilityAvailable().
+ // We can improve results if we set AUTO here.
+ const capability = destination.capabilities.printer ?
+ destination.capabilities.printer.page_orientation :
+ null;
+ if (capability && capability.option &&
+ capability.option.some(option => option.type == 'AUTO')) {
+ cjt.print.page_orientation = {type: 'AUTO'};
+ }
+ } else {
+ cjt.print.page_orientation = {
+ type: this.settings.layout ? 'LANDSCAPE' : 'PORTRAIT'
+ };
+ }
+ if (this.settings.dpi.available) {
+ const dpiValue = this.settings.dpi.value;
+ cjt.print.dpi = {
+ horizontal_dpi: dpiValue.horizontal_dpi,
+ vertical_dpi: dpiValue.vertical_dpi,
+ vendor_id: dpiValue.vendor_id
+ };
+ }
+ if (this.settings.vendorItems.available) {
+ const items = this.settings.vendorItems.value;
+ cjt.print.vendor_ticket_item = [];
+ for (const itemId in items) {
+ if (items.hasOwnProperty(itemId)) {
+ cjt.print.vendor_ticket_item.push({id: itemId, value: items[itemId]});
+ }
+ }
+ }
+ return JSON.stringify(cjt);
+ },
});
})();
diff --git a/chromium/chrome/browser/resources/print_preview/new/number_settings_section.html b/chromium/chrome/browser/resources/print_preview/new/number_settings_section.html
index dfea6be0f89..c56de1f3557 100644
--- a/chromium/chrome/browser/resources/print_preview/new/number_settings_section.html
+++ b/chromium/chrome/browser/resources/print_preview/new/number_settings_section.html
@@ -32,13 +32,16 @@
<div slot="controls">
<slot name="opt-outside-content"></slot>
<span class="input-wrapper">
- <input class="user-value" type="number" value="{{inputString::input}}"
- max="[[maxValue]]" min="[[minValue]]" on-blur="onBlur_"
- on-keydown="onKeydown_" aria-labelled-by="section-title">
+ <input class="user-value" type="number"
+ value="{{inputString_::input}}"
+ max="[[maxValue]]" min="[[minValue]]"
+ disabled$="[[getDisabled_(inputValid, disabled)]]"
+ on-blur="onBlur_" on-keydown="onKeydown_"
+ aria-labelled-by="section-title">
<slot name="opt-inside-content"></slot>
</span>
<span class="hint" aria-live="polite"
- hidden$="[[hintHidden_(inputString, inputValid)]]">
+ hidden$="[[hintHidden_(inputString_, inputValid)]]">
[[hintMessage]]
</span>
</div>
diff --git a/chromium/chrome/browser/resources/print_preview/new/number_settings_section.js b/chromium/chrome/browser/resources/print_preview/new/number_settings_section.js
index 4084b852b71..ed173077085 100644
--- a/chromium/chrome/browser/resources/print_preview/new/number_settings_section.js
+++ b/chromium/chrome/browser/resources/print_preview/new/number_settings_section.js
@@ -6,33 +6,46 @@ Polymer({
is: 'print-preview-number-settings-section',
properties: {
- /** @type {string} */
- inputString: {
+ /** @private {string} */
+ inputString_: {
type: String,
notify: true,
+ observer: 'onInputChanged_',
},
/** @type {boolean} */
inputValid: {
type: Boolean,
notify: true,
- computed: 'computeValid_(inputString)',
+ value: true,
},
/** @type {string} */
+ currentValue: {
+ type: String,
+ notify: true,
+ observer: 'onCurrentValueChanged_',
+ },
+
defaultValue: String,
- /** @type {number} */
maxValue: Number,
- /** @type {number} */
minValue: Number,
- /** @type {string} */
inputLabel: String,
- /** @type {string} */
hintMessage: String,
+
+ disabled: Boolean,
+ },
+
+ /**
+ * @return {boolean} Whether the input should be disabled.
+ * @private
+ */
+ getDisabled_: function() {
+ return this.disabled && this.inputValid;
},
/**
@@ -45,19 +58,31 @@ Polymer({
/** @private */
onBlur_: function() {
- if (this.inputString == '')
- this.set('inputString', this.defaultValue);
+ if (this.inputString_ == '')
+ this.set('inputString_', this.defaultValue);
+ },
+
+ /** @private */
+ onInputChanged_: function() {
+ this.inputValid = this.computeValid_();
+ if (this.inputValid)
+ this.currentValue = this.inputString_;
+ },
+
+ /** @private */
+ onCurrentValueChanged_: function() {
+ this.inputString_ = this.currentValue;
},
/**
- * @return {boolean} Whether input value represented by inputString is
+ * @return {boolean} Whether input value represented by inputString_ is
* valid.
* @private
*/
computeValid_: function() {
- // Make sure value updates first, in case inputString was updated by JS.
- this.$$('.user-value').value = this.inputString;
- return this.$$('.user-value').validity.valid && this.inputString != '';
+ // Make sure value updates first, in case inputString_ was updated by JS.
+ this.$$('.user-value').value = this.inputString_;
+ return this.$$('.user-value').validity.valid && this.inputString_ != '';
},
/**
@@ -65,6 +90,6 @@ Polymer({
* @private
*/
hintHidden_: function() {
- return this.inputValid || this.inputString == '';
+ return this.inputValid || this.inputString_ == '';
},
});
diff --git a/chromium/chrome/browser/resources/print_preview/new/other_options_settings.html b/chromium/chrome/browser/resources/print_preview/new/other_options_settings.html
index b816a3274e9..afd187b856d 100644
--- a/chromium/chrome/browser/resources/print_preview/new/other_options_settings.html
+++ b/chromium/chrome/browser/resources/print_preview/new/other_options_settings.html
@@ -19,12 +19,14 @@
<label aria-live="polite"
hidden$="[[!settings.headerFooter.available]]">
<input type="checkbox" id="header-footer"
+ disabled$="[[disabled]]"
on-change="onHeaderFooterChange_"
checked$="[[settings.headerFooter.value]]">
<span>$i18n{optionHeaderFooter}</span>
</label>
<label aria-live="polite" hidden$="[[!settings.duplex.available]]">
<input type="checkbox" id="duplex" on-change="onDuplexChange_"
+ disabled$="[[disabled]]"
checked$="[[settings.duplex.value]]">
<span>$i18n{optionTwoSided}</span>
</label>
@@ -32,18 +34,20 @@
hidden$="[[!settings.cssBackground.available]]">
<input type="checkbox" id="css-background"
on-change="onCssBackgroundChange_"
+ disabled$="[[disabled]]"
checked$="[[settings.cssBackground.value]]">
<span>$i18n{optionBackgroundColorsAndImages}</span>
</label>
<label aria-live="polite" hidden$="[[!settings.rasterize.available]]">
<input type="checkbox" id="rasterize"
- on-change="onRasterizeChange_"
+ disabled$="[[disabled]]" on-change="onRasterizeChange_"
checked$="[[settings.rasterize.value]]">
<span>$i18n{optionRasterize}</span>
</label>
<label aria-live="polite"
hidden$="[[!settings.selectionOnly.available]]">
<input type="checkbox" id="selection-only"
+ disabled$="[[disabled]]"
on-change="onSelectionOnlyChange_"
checked$="[[settings.selectionOnly.value]]">
<span>$i18n{optionSelectionOnly}</span>
diff --git a/chromium/chrome/browser/resources/print_preview/new/other_options_settings.js b/chromium/chrome/browser/resources/print_preview/new/other_options_settings.js
index 07e21f5de60..07d9f82eb58 100644
--- a/chromium/chrome/browser/resources/print_preview/new/other_options_settings.js
+++ b/chromium/chrome/browser/resources/print_preview/new/other_options_settings.js
@@ -7,6 +7,10 @@ Polymer({
behaviors: [SettingsBehavior],
+ properties: {
+ disabled: Boolean,
+ },
+
observers: [
'onHeaderFooterSettingChange_(settings.headerFooter.value)',
'onDuplexSettingChange_(settings.duplex.value)',
diff --git a/chromium/chrome/browser/resources/print_preview/new/pages_settings.html b/chromium/chrome/browser/resources/print_preview/new/pages_settings.html
index 72fc32d2160..2777b3b0973 100644
--- a/chromium/chrome/browser/resources/print_preview/new/pages_settings.html
+++ b/chromium/chrome/browser/resources/print_preview/new/pages_settings.html
@@ -1,6 +1,5 @@
<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/html/cr.html">
<link rel="import" href="checkbox_radio_css.html">
<link rel="import" href="../data/document_info.html">
<link rel="import" href="input_css.html">
@@ -30,16 +29,19 @@
<div slot="controls">
<div class="radio">
<label><input type="radio" name="pages" id="all-radio-button"
- checked="{{allSelected_::change}}">
+ checked="{{allSelected_::change}}"
+ disabled$="[[getDisabled_(disabled, settings.pages.valid)]]">
<span>$i18n{optionAllPages}</span>
</label>
<label class="custom-input-wrapper"
for="page-settings-custom-input" tabindex=-1>
<input type="radio" name="pages" id="custom-radio-button"
+ disabled$="[[getDisabled_(disabled, settings.pages.valid)]]"
on-click="onCustomRadioClick_">
<input class="user-value" type="text"
value="{{inputString_::input}}" id="page-settings-custom-input"
checked="{{customSelected_::change}}"
+ disabled$="[[getDisabled_(dsiabled, settings.pages.valid)]]"
pattern="([0-9]*(-)?[0-9]*(,)( )?)*([0-9]*(-)?[0-9]*(,)?( )?)?"
on-focus="onCustomInputFocus_" on-blur="onCustomInputBlur_"
placeholder="$i18n{examplePageRangeText}"
diff --git a/chromium/chrome/browser/resources/print_preview/new/pages_settings.js b/chromium/chrome/browser/resources/print_preview/new/pages_settings.js
index e230e2c3500..4b6e5276fc1 100644
--- a/chromium/chrome/browser/resources/print_preview/new/pages_settings.js
+++ b/chromium/chrome/browser/resources/print_preview/new/pages_settings.js
@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-cr.exportPath('print_preview_new');
+(function() {
+'use strict';
/** @enum {number} */
const PagesInputErrorState = {
@@ -44,6 +45,14 @@ Polymer({
value: false,
},
+ disabled: Boolean,
+
+ /** @private {number} */
+ errorState_: {
+ type: Number,
+ value: PagesInputErrorState.NO_ERROR,
+ },
+
/** @private {!Array<number>} */
pagesToPrint_: {
type: Array,
@@ -56,13 +65,6 @@ Polymer({
type: Array,
computed: 'computeRangesToPrint_(pagesToPrint_, allPagesArray_)',
},
-
- /** @private {!PagesInputErrorState} */
- errorState_: {
- type: Number,
- computed: 'computeErrorState_(documentInfo.pageCount, pagesToPrint_)',
- },
-
},
observers: [
@@ -71,6 +73,14 @@ Polymer({
],
/**
+ * @return {boolean} Whether the controls should be disabled.
+ * @private
+ */
+ getDisabled_: function() {
+ return this.getSetting('pages').valid && this.disabled;
+ },
+
+ /**
* @return {!Array<number>}
* @private
*/
@@ -88,10 +98,14 @@ Polymer({
* @private
*/
computePagesToPrint_: function() {
- if (this.allSelected_ || this.inputString_.trim() == '')
+ if (this.allSelected_ || this.inputString_.trim() == '') {
+ this.errorState_ = PagesInputErrorState.NO_ERROR;
return this.allPagesArray_;
- if (!this.$$('.user-value').validity.valid)
- return [];
+ }
+ if (!this.$$('.user-value').validity.valid) {
+ this.errorState_ = PagesInputErrorState.INVALID_SYNTAX;
+ return this.pagesToPrint_;
+ }
const pages = [];
const added = {};
@@ -103,11 +117,15 @@ Polymer({
continue;
const limits = range.split('-');
let min = parseInt(limits[0], 10);
- if (min < 1)
- return [];
+ if (min < 1) {
+ this.errorState_ = PagesInputErrorState.OUT_OF_BOUNDS;
+ return this.pagesToPrint_;
+ }
if (limits.length == 1) {
- if (min > maxPage)
- return [-1];
+ if (min > maxPage) {
+ this.errorState_ = PagesInputErrorState.OUT_OF_BOUNDS;
+ return this.pagesToPrint_;
+ }
if (!added.hasOwnProperty(min)) {
pages.push(min);
added[min] = true;
@@ -120,10 +138,14 @@ Polymer({
min = 1;
if (isNaN(max))
max = maxPage;
- if (min > max)
- return [];
- if (max > maxPage)
- return [-1];
+ if (min > max) {
+ this.errorState_ = PagesInputErrorState.INVALID_SYNTAX;
+ return this.pagesToPrint_;
+ }
+ if (max > maxPage) {
+ this.errorState_ = PagesInputErrorState.OUT_OF_BOUNDS;
+ return this.pagesToPrint_;
+ }
for (let i = min; i <= max; i++) {
if (!added.hasOwnProperty(i)) {
pages.push(i);
@@ -162,17 +184,17 @@ Polymer({
},
/**
- * @return {!PagesInputErrorState}
- * @private
+ * @return {boolean} Whether pages setting and pagesToPrint_ match.
*/
- computeErrorState_: function() {
- if (this.documentInfo.pageCount == 0) // page count not yet initialized
- return PagesInputErrorState.NO_ERROR;
- if (this.pagesToPrint_.length == 0)
- return PagesInputErrorState.INVALID_SYNTAX;
- if (this.pagesToPrint_[0] == -1)
- return PagesInputErrorState.OUT_OF_BOUNDS;
- return PagesInputErrorState.NO_ERROR;
+ settingMatches_: function() {
+ const setting = this.getSetting('pages').value;
+ if (setting.length != this.pagesToPrint_.length)
+ return false;
+ for (let index = 0; index < this.pagesToPrint_.length; index++) {
+ if (this.pagesToPrint_[index] != setting[index])
+ return false;
+ }
+ return true;
},
/**
@@ -188,8 +210,10 @@ Polymer({
}
this.$$('.user-value').classList.remove('invalid');
this.setSettingValid('pages', true);
- this.setSetting('pages', this.pagesToPrint_);
- this.setSetting('ranges', this.rangesToPrint_);
+ if (!this.settingMatches_()) {
+ this.setSetting('pages', this.pagesToPrint_);
+ this.setSetting('ranges', this.rangesToPrint_);
+ }
},
/** @private */
@@ -249,3 +273,4 @@ Polymer({
return this.errorState_ == PagesInputErrorState.NO_ERROR;
}
});
+})();
diff --git a/chromium/chrome/browser/resources/print_preview/new/preview_area.html b/chromium/chrome/browser/resources/print_preview/new/preview_area.html
index 716d3b5e6f3..a7a9d5cd05b 100644
--- a/chromium/chrome/browser/resources/print_preview/new/preview_area.html
+++ b/chromium/chrome/browser/resources/print_preview/new/preview_area.html
@@ -1,6 +1,7 @@
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/html/cr.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
<link rel="import" href="../native_layer.html">
<link rel="import" href="../print_preview_utils.html">
@@ -10,6 +11,8 @@
<link rel="import" href="../data/margins.html">
<link rel="import" href="../data/printable_area.html">
<link rel="import" href="../data/size.html">
+<link rel="import" href="model.html">
+<link rel="import" href="state.html">
<link rel="import" href="settings_behavior.html">
<dom-module id="print-preview-preview-area">
@@ -108,37 +111,20 @@
}
</style>
- <div class="preview-area-overlay-layer">
+ <div class$="preview-area-overlay-layer [[getInvisible_(previewState_)]]"
+ aria-hidden$="[[previewLoaded(previewState_)]]">
<div class="preview-area-messages">
-
- <div class="preview-area-loading-message preview-area-message"
- hidden$="[[!state.previewLoading]]">
- <span>$i18n{loading}</span>
- <span class="preview-area-loading-message-jumping-dots jumping-dots"
- ><span>.</span><span>.</span><span>.</span></span>
- </div>
-
- <div class="preview-area-custom-message preview-area-message" hidden>
- <div class="preview-area-custom-message-text"></div>
- <div class="preview-area-custom-action-area">
- <button class="preview-area-open-system-dialog-button">
- $i18n{launchNativeDialog}
- </button>
- <div
- class="preview-area-open-system-dialog-button-throbber throbber"
- hidden></div>
+ <div class="preview-area-message">
+ <div>
+ <span>[[currentMessage_(previewState_)]]</span>
+ <span class$="preview-area-loading-message-jumping-dots
+ [[getJumpingDots_(previewState_)]]"
+ hidden$="[[!isPreviewLoading_(previewState_)]]">
+ <span>.</span><span>.</span><span>.</span>
+ </span>
</div>
- </div>
-
- <div class="preview-area-preview-failed-message preview-area-message"
- hidden$="[[!state.previewFailed]]">
- $i18n{previewFailed}
- </div>
-
- <div class="preview-area-print-failed preview-area-message"
- hidden$="[[!state.invalidSettings]]">
- <div>$i18n{invalidPrinterSettings}</div>
- <div class="preview-area-print-failed-action-area">
+ <div class="preview-area-action-area"
+ hidden$="[[!displaySystemDialogButton_(previewState_)]]">
<button class="preview-area-open-system-dialog-button">
$i18n{launchNativeDialog}
</button>
@@ -147,7 +133,6 @@
hidden></div>
</div>
</div>
-
</div>
</div>
<div class="preview-area-plugin-wrapper">
diff --git a/chromium/chrome/browser/resources/print_preview/new/preview_area.js b/chromium/chrome/browser/resources/print_preview/new/preview_area.js
index 6a1b53b0d1a..9c456b76ccf 100644
--- a/chromium/chrome/browser/resources/print_preview/new/preview_area.js
+++ b/chromium/chrome/browser/resources/print_preview/new/preview_area.js
@@ -34,20 +34,23 @@ cr.exportPath('print_preview_new');
*/
print_preview_new.PDFPlugin;
-/**
- * Constant values matching printing::DuplexMode enum.
- * @enum {number}
- */
-print_preview_new.DuplexMode = {
- SIMPLEX: 0,
- LONG_EDGE: 1,
- UNKNOWN_DUPLEX_MODE: -1
+(function() {
+'use strict';
+
+/** @enum {string} */
+const PreviewAreaState_ = {
+ LOADING: 'loading',
+ DISPLAY_PREVIEW: 'display-preview',
+ OPEN_IN_PREVIEW: 'open-in-preview',
+ INVALID_SETTINGS: 'invalid-settings',
+ PREVIEW_FAILED: 'preview-failed',
};
Polymer({
is: 'print-preview-preview-area',
- behaviors: [WebUIListenerBehavior, SettingsBehavior],
+ behaviors: [WebUIListenerBehavior, SettingsBehavior, I18nBehavior],
+
properties: {
/** @type {print_preview.DocumentInfo} */
documentInfo: Object,
@@ -55,10 +58,17 @@ Polymer({
/** @type {print_preview.Destination} */
destination: Object,
- /** @type {print_preview_new.State} */
+ /** @type {!print_preview_new.State} */
state: {
- type: Object,
+ type: Number,
+ observer: 'onStateChanged_',
+ },
+
+ /** @private {string} */
+ previewState_: {
+ type: String,
notify: true,
+ value: PreviewAreaState_.LOADING,
},
},
@@ -68,9 +78,7 @@ Polymer({
'settings.layout.value, settings.margins.value, ' +
'settings.mediaSize.value, settings.ranges.value,' +
'settings.selectionOnly.value, settings.scaling.value, ' +
- 'destination.id, destination.capabilities, state.initialized)',
- 'onPreviewStateChanged_(state.previewLoading, state.invalidSettings, ' +
- 'state.previewFailed)',
+ 'settings.rasterize.value, destination.id, destination.capabilities)',
],
/** @private {print_preview.NativeLayer} */
@@ -79,13 +87,16 @@ Polymer({
/** @private {number} */
inFlightRequestId_: -1,
+ /** @private {boolean} */
+ requestPreviewWhenReady_: false,
+
/** @private {HTMLEmbedElement|print_preview_new.PDFPlugin} */
plugin_: null,
- /** @private {boolean} */
+ /** @private {boolean} Whether the plugin is loaded */
pluginLoaded_: false,
- /** @private {boolean} */
+ /** @private {boolean} Whether the document is ready */
documentReady_: false,
/** @override */
@@ -102,59 +113,119 @@ Polymer({
this.$$('.preview-area-compatibility-object-out-of-process');
const isOOPCompatible = oopCompatObj.postMessage;
oopCompatObj.parentElement.removeChild(oopCompatObj);
- if (!isOOPCompatible)
- this.set('state.previewFailed', true);
- else
- this.set('state.previewLoading', true);
+ if (!isOOPCompatible) {
+ this.previewState_ = PreviewAreaState_.PREVIEW_FAILED;
+ this.fire('preview-failed');
+ }
+ },
+
+ /** @return {boolean} Whether the preview is loaded. */
+ previewLoaded: function() {
+ return this.previewState_ == PreviewAreaState_.DISPLAY_PREVIEW;
},
/** @private */
onSettingsChanged_: function() {
- if (!this.state.initialized || !this.getSetting('scaling').valid ||
- !this.getSetting('pages').valid || !this.getSetting('copies').valid ||
- !this.destination || !this.destination.capabilities) {
+ if (this.state == print_preview_new.State.READY) {
+ this.startPreview_();
return;
}
+ this.requestPreviewWhenReady_ = true;
+ },
+
+ /**
+ * @return {string} 'invisible' if overlay is invisible, '' otherwise.
+ * @private
+ */
+ getInvisible_: function() {
+ return this.previewLoaded() ? 'invisible' : '';
+ },
+
+ /**
+ * @return {boolean} Whether the preview is currently loading.
+ * @private
+ */
+ isPreviewLoading_: function() {
+ return this.previewState_ == PreviewAreaState_.LOADING;
+ },
+
+ /**
+ * @return {string} 'jumping-dots' to enable animation, '' otherwise.
+ * @private
+ */
+ getJumpingDots_: function() {
+ return this.isPreviewLoading_() ? 'jumping-dots' : '';
+ },
+
+ /**
+ * @return {boolean} Whether the system dialog button should be shown.
+ * @private
+ */
+ displaySystemDialogButton_: function() {
+ return this.previewState_ == PreviewAreaState_.INVALID_SETTINGS ||
+ this.previewState_ == PreviewAreaState_.OPEN_IN_PREVIEW;
+ },
+
+ /**
+ * @return {string} The current preview area message to display.
+ * @private
+ */
+ currentMessage_: function() {
+ if (this.previewState_ == PreviewAreaState_.LOADING)
+ return this.i18n('loading');
+ if (this.previewState_ == PreviewAreaState_.OPEN_IN_PREVIEW)
+ return this.i18n('openPdfInPreview');
+ if (this.previewState_ == PreviewAreaState_.INVALID_SETTINGS)
+ return this.i18n('invalidSettings');
+ if (this.previewState_ == PreviewAreaState_.PREVIEW_FAILED)
+ return this.i18n('previewFailed');
+ return '';
+ },
+
+ /** @private */
+ startPreview_: function() {
+ this.previewState_ = PreviewAreaState_.LOADING;
this.documentReady_ = false;
- this.set('state.previewLoading', true);
this.getPreview_().then(
previewUid => {
if (!this.documentInfo.isModifiable)
this.onPreviewStart_(previewUid, -1);
this.documentReady_ = true;
- if (this.pluginLoaded_)
- this.set('state.previewLoading', false);
+ if (this.pluginLoaded_) {
+ this.previewState_ = PreviewAreaState_.DISPLAY_PREVIEW;
+ this.fire('preview-loaded');
+ }
},
type => {
if (/** @type{string} */ (type) == 'SETTINGS_INVALID')
- this.set('state.invalidSettings', true);
- else if (/** @type{string} */ (type) != 'CANCELLED')
- this.set('state.previewFailed', true);
- this.set('state.previewLoading', false);
+ this.previewState_ = PreviewAreaState_.INVALID_SETTINGS;
+ else if (/** @type{string} */ (type) != 'CANCELLED') {
+ this.previewState_ = PreviewAreaState_.PREVIEW_FAILED;
+ this.fire('preview-failed');
+ }
});
},
- /**
- * Set the visibility of the message overlay.
- * @param {boolean} visible Whether to make the overlay visible or not
- * @private
- */
- setOverlayVisible_: function(visible) {
- const overlayEl = this.$$('.preview-area-overlay-layer');
- overlayEl.classList.toggle('invisible', !visible);
- overlayEl.setAttribute('aria-hidden', !visible);
- },
-
/** @private */
- onPreviewStateChanged_: function() {
- // update the appearance here.
- const visible = this.state.previewLoading || this.state.previewFailed ||
- this.state.invalidSettings;
- this.setOverlayVisible_(visible);
-
- // Disable jumping animation to conserve cycles.
- const jumpingDotsEl = this.$$('.preview-area-loading-message-jumping-dots');
- jumpingDotsEl.classList.toggle('jumping-dots', this.state.previewLoading);
+ onStateChanged_: function() {
+ switch (this.state) {
+ case (print_preview_new.State.NOT_READY):
+ // Resetting the destination clears the invalid settings error.
+ this.previewState_ = PreviewAreaState_.LOADING;
+ break;
+ case (print_preview_new.State.READY):
+ // Request a new preview.
+ if (this.requestPreviewWhenReady_) {
+ this.startPreview_();
+ this.requestPreviewWhenReady_ = false;
+ }
+ break;
+ case (print_preview_new.State.INVALID_PRINTER):
+ this.previewState_ = PreviewAreaState_.INVALID_SETTINGS;
+ break;
+ default:
+ break;
+ }
},
/**
@@ -240,8 +311,10 @@ Polymer({
*/
onPluginLoad_: function() {
this.pluginLoaded_ = true;
- if (this.documentReady_)
- this.set('state.previewLoading', false);
+ if (this.documentReady_) {
+ this.previewState_ = PreviewAreaState_.DISPLAY_PREVIEW;
+ this.fire('preview-loaded');
+ }
},
/**
@@ -345,7 +418,7 @@ Polymer({
printWithCloudPrint: !this.destination.isLocal,
printWithPrivet: this.destination.isPrivet,
printWithExtension: this.destination.isExtension,
- rasterizePDF: false,
+ rasterizePDF: this.getSettingValue('rasterize'),
};
// Set 'cloudPrintID' only if the this.destination is not local.
@@ -372,3 +445,4 @@ Polymer({
return this.nativeLayer_.getPreview(JSON.stringify(ticket), pageCount);
},
});
+})();
diff --git a/chromium/chrome/browser/resources/print_preview/new/print_preview_search_box.html b/chromium/chrome/browser/resources/print_preview/new/print_preview_search_box.html
new file mode 100644
index 00000000000..50e421bb32b
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/new/print_preview_search_box.html
@@ -0,0 +1,41 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_search_field/cr_search_field_behavior.html">
+<link rel="import" href="print_preview_shared_css.html">
+
+<dom-module id="print-preview-search-box">
+ <template>
+ <style include="print-preview-shared">
+ :host {
+ display: flex;
+ position: relative;
+ user-select: none;
+ }
+
+ .search-box-icon {
+ display: inline-block;
+ height: 1em;
+ left: 8px;
+ position: absolute;
+ right: 8px;
+ top: 6px;
+ user-select: none;
+ width: 1em;
+ }
+
+ .search-box-input {
+ text-indent: 2em;
+ width: 100%;
+ }
+
+ .search-box-input::-webkit-search-cancel-button {
+ -webkit-appearance: none;
+ }
+ </style>
+ <img src="../images/search.png" class="search-box-icon" alt="">
+ <input type="search" id="searchInput" class="search-box-input"
+ on-search="onSearchTermSearch" on-input="onSearchTermInput"
+ incremental aria-label$="[[label]]" placeholder="[[label]]">
+ </template>
+ <script src="print_preview_search_box.js"></script>
+</dom-module>
diff --git a/chromium/chrome/browser/resources/print_preview/new/print_preview_search_box.js b/chromium/chrome/browser/resources/print_preview/new/print_preview_search_box.js
new file mode 100644
index 00000000000..c8890137541
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/new/print_preview_search_box.js
@@ -0,0 +1,42 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+'use strict';
+
+/** @type {!RegExp} */
+const SANITIZE_REGEX = /[-[\]{}()*+?.,\\^$|#\s]/g;
+
+Polymer({
+ is: 'print-preview-search-box',
+
+ behaviors: [CrSearchFieldBehavior],
+
+ properties: {
+ /** @type {?RegExp} */
+ searchQuery: {
+ type: Object,
+ notify: true,
+ },
+ },
+
+ listeners: {
+ 'search-changed': 'onSearchChanged_',
+ },
+
+ /** @return {!HTMLInputElement} */
+ getSearchInput: function() {
+ return this.$.searchInput;
+ },
+
+ /**
+ * @param {!CustomEvent} e Event containing the new search.
+ */
+ onSearchChanged_: function(e) {
+ const safeQuery = e.detail.trim().replace(SANITIZE_REGEX, '\\$&');
+ this.searchQuery =
+ safeQuery.length > 0 ? new RegExp(`(${safeQuery})`, 'i') : null;
+ },
+});
+})();
diff --git a/chromium/chrome/browser/resources/print_preview/new/scaling_settings.html b/chromium/chrome/browser/resources/print_preview/new/scaling_settings.html
index fbb0f21462f..6fc479afebf 100644
--- a/chromium/chrome/browser/resources/print_preview/new/scaling_settings.html
+++ b/chromium/chrome/browser/resources/print_preview/new/scaling_settings.html
@@ -12,12 +12,14 @@
</style>
<print-preview-number-settings-section max-value=200 min-value=10
default-value="100" input-label="$i18n{scalingLabel}"
- input-string="{{inputString_}}" input-valid="{{inputValid_}}"
- hint-message="$i18n{scalingInstruction}" class="multirow-controls">
+ disabled="[[disabled]]" current-value="{{currentValue_}}"
+ input-valid="{{inputValid_}}" hint-message="$i18n{scalingInstruction}"
+ class="multirow-controls">
<div slot="opt-outside-content" class="checkbox"
hidden$="[[!settings.fitToPage.available]]">
<label aria-live="polite">
<input type="checkbox" id="fit-to-page-checkbox"
+ disabled$="[[getDisabled_(disabled, inputValid_)]]"
on-change="onFitToPageChange_">
<span>$i18n{optionFitToPage}</span>
</label>
diff --git a/chromium/chrome/browser/resources/print_preview/new/scaling_settings.js b/chromium/chrome/browser/resources/print_preview/new/scaling_settings.js
index 8117771923e..4588514c6a8 100644
--- a/chromium/chrome/browser/resources/print_preview/new/scaling_settings.js
+++ b/chromium/chrome/browser/resources/print_preview/new/scaling_settings.js
@@ -12,10 +12,12 @@ Polymer({
documentInfo: Object,
/** @private {string} */
- inputString_: String,
+ currentValue_: String,
/** @private {boolean} */
inputValid_: Boolean,
+
+ disabled: Boolean,
},
/** @private {string} */
@@ -27,7 +29,7 @@ Polymer({
observers: [
'onFitToPageSettingChange_(settings.fitToPage.value, ' +
'settings.fitToPage.available, documentInfo.fitToPageScaling)',
- 'onInputChanged_(inputString_, inputValid_)',
+ 'onInputChanged_(currentValue_, inputValid_)',
'onScalingSettingChanged_(settings.scaling.value)',
],
@@ -39,13 +41,13 @@ Polymer({
this.$$('#fit-to-page-checkbox').checked = fitToPage.value;
if (!fitToPage.value) {
// Fit to page is no longer checked. Update the display.
- this.inputString_ = this.lastValidScaling_;
+ this.currentValue_ = this.lastValidScaling_;
} else if (fitToPage.value) {
// Set flag to number of expected calls to onInputChanged_. If scaling
- // is valid, 1 call will occur due to the change to |inputString_|. If
+ // is valid, 1 call will occur due to the change to |currentValue_|. If
// not, 2 calls will occur, since |inputValid_| will also change.
this.fitToPageFlag_ = this.inputValid_ ? 1 : 2;
- this.inputString_ = this.documentInfo.fitToPageScaling;
+ this.currentValue_ = this.documentInfo.fitToPageScaling;
}
},
@@ -57,7 +59,7 @@ Polymer({
// Update last valid scaling and ensure input string matches.
this.lastValidScaling_ =
/** @type {string} */ (this.getSetting('scaling').value);
- this.inputString_ = this.lastValidScaling_;
+ this.currentValue_ = this.lastValidScaling_;
},
/**
@@ -70,8 +72,9 @@ Polymer({
if (fitToPage && this.fitToPageFlag_ == 0) {
// User modified scaling while fit to page was checked. Uncheck fit to
// page.
- if (this.inputValid_)
- this.setSetting('scaling', this.inputString_);
+ const wasValid = this.getSetting('scaling').valid;
+ if (this.inputValid_ && wasValid)
+ this.setSetting('scaling', this.currentValue_);
else
this.setSettingValid('scaling', false);
this.$$('#fit-to-page-checkbox').checked = false;
@@ -83,9 +86,10 @@ Polymer({
} else {
// User modified scaling while fit to page was not checked or
// scaling setting was set.
+ const wasValid = this.getSetting('scaling').valid;
this.setSettingValid('scaling', this.inputValid_);
- if (this.inputValid_)
- this.setSetting('scaling', this.inputString_);
+ if (this.inputValid_ && wasValid)
+ this.setSetting('scaling', this.currentValue_);
}
},
@@ -93,4 +97,12 @@ Polymer({
onFitToPageChange_: function() {
this.setSetting('fitToPage', this.$$('#fit-to-page-checkbox').checked);
},
+
+ /**
+ * @return {boolean} Whether the input should be disabled.
+ * @private
+ */
+ getDisabled_: function() {
+ return this.disabled && this.inputValid_;
+ },
});
diff --git a/chromium/chrome/browser/resources/print_preview/new/search_dialog_css.html b/chromium/chrome/browser/resources/print_preview/new/search_dialog_css.html
new file mode 100644
index 00000000000..6ec25973375
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/new/search_dialog_css.html
@@ -0,0 +1,48 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="button_css.html">
+<link rel="import" href="print_preview_search_box.html">
+<link rel="import" href="print_preview_shared_css.html">
+
+<dom-module id="search-dialog">
+ <template>
+ <style include="print-preview-shared button">
+ #dialog::backdrop {
+ background-color: rgba(255, 255, 255, 0.75);
+ }
+
+ #dialog {
+ --cr-dialog-close-image: {
+ background-image: url(chrome://theme/IDR_CLOSE_DIALOG);
+ }
+ --cr-dialog-close-image-active: {
+ background-image: url(chrome://theme/IDR_CLOSE_DIALOG_P);
+ }
+ --cr-dialog-close-image-hover: {
+ background-image: url(chrome://theme/IDR_CLOSE_DIALOG_H);
+ }
+ --cr-icon-ripple-size: 0;
+ --cr-icon-size: 14px;
+ --cr-dialog-body: {
+ box-sizing: border-box;
+ padding-top: 0;
+ }
+ --cr-dialog-wrapper: {
+ max-height: calc(100vh - 40px);
+ }
+ box-shadow: 0 4px 23px 5px rgba(0, 0, 0, 0.2),
+ 0 2px 6px rgba(0, 0, 0, 0.15);
+ }
+
+ #searchBox {
+ font-size: calc(13/15 * 1em);
+ margin-top: 14px;
+ }
+
+ #body {
+ height: 100vh;
+ }
+ </style>
+ </template>
+</dom-module>
diff --git a/chromium/chrome/browser/resources/print_preview/new/select_css.html b/chromium/chrome/browser/resources/print_preview/new/select_css.html
index 5a316158da1..04838c08b04 100644
--- a/chromium/chrome/browser/resources/print_preview/new/select_css.html
+++ b/chromium/chrome/browser/resources/print_preview/new/select_css.html
@@ -21,14 +21,14 @@
select:enabled:hover {
background-image: url(chrome://resources/images/select.png),
linear-gradient(#f0f0f0, #f0f0f0 38%, #e0e0e0);
- @apply(--print-preview-hover);
+ @apply --print-preview-hover;
}
</if>
select:enabled:active {
background-image: url(chrome://resources/images/select.png),
linear-gradient(#e7e7e7, #e7e7e7 38%, #d7d7d7);
- @apply(--print-preview-active);
+ @apply --print-preview-active;
}
select:disabled {
diff --git a/chromium/chrome/browser/resources/print_preview/new/settings_behavior.js b/chromium/chrome/browser/resources/print_preview/new/settings_behavior.js
index aa4c95dc575..b28e5fcd8ae 100644
--- a/chromium/chrome/browser/resources/print_preview/new/settings_behavior.js
+++ b/chromium/chrome/browser/resources/print_preview/new/settings_behavior.js
@@ -91,6 +91,8 @@ const SettingsBehavior = {
// is no way for the user to change the value in this case.
if (!valid)
assert(setting.available, 'Setting is not available: ' + settingName);
+ if (valid != setting.valid)
+ this.fire('setting-valid-changed', valid);
this.set(`settings.${settingName}.valid`, valid);
}
};
diff --git a/chromium/chrome/browser/resources/print_preview/new/settings_select.html b/chromium/chrome/browser/resources/print_preview/new/settings_select.html
index 0525c00e2b5..9b867bec3b0 100644
--- a/chromium/chrome/browser/resources/print_preview/new/settings_select.html
+++ b/chromium/chrome/browser/resources/print_preview/new/settings_select.html
@@ -9,9 +9,10 @@
<template>
<style include="print-preview-shared select">
</style>
- <select on-change="onChange_">
+ <select on-change="onChange_" disabled$="[[disabled]]">
<template is="dom-repeat" items="[[capability.option]]">
- <option selected="[[item.is_default]]" value="[[getValue_(item)]]">
+ <option selected="[[isSelected_(item, selectedValue_)]]"
+ value="[[getValue_(item)]]">
[[getDisplayName_(item)]]
</option>
</template>
diff --git a/chromium/chrome/browser/resources/print_preview/new/settings_select.js b/chromium/chrome/browser/resources/print_preview/new/settings_select.js
index aba0680710a..4877620f573 100644
--- a/chromium/chrome/browser/resources/print_preview/new/settings_select.js
+++ b/chromium/chrome/browser/resources/print_preview/new/settings_select.js
@@ -23,13 +23,31 @@ Polymer({
/** @type {{ option: Array<!print_preview_new.SelectOption> }} */
capability: Object,
- /** @type {string} */
settingName: String,
+
+ disabled: Boolean,
+
+ /** @private {string} */
+ selectedValue_: {
+ type: String,
+ notify: true,
+ value: '',
+ },
+ },
+
+ /**
+ * @param {!print_preview_new.SelectOption} option Option to check.
+ * @return {boolean} Whether the option is selected.
+ * @private
+ */
+ isSelected_: function(option) {
+ return this.getValue_(option) == this.selectedValue_ ||
+ (!!option.is_default && this.selectedValue_ == '');
},
/** @param {string} value The value to select. */
selectValue: function(value) {
- this.$$('select').value = value;
+ this.selectedValue_ = value;
},
/**
diff --git a/chromium/chrome/browser/resources/print_preview/new/state.html b/chromium/chrome/browser/resources/print_preview/new/state.html
new file mode 100644
index 00000000000..12da96dc34d
--- /dev/null
+++ b/chromium/chrome/browser/resources/print_preview/new/state.html
@@ -0,0 +1,5 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/html/cr.html">
+
+<script src="state.js"></script>
diff --git a/chromium/chrome/browser/resources/print_preview/new/state.js b/chromium/chrome/browser/resources/print_preview/new/state.js
index 99cb889343e..26d1a636918 100644
--- a/chromium/chrome/browser/resources/print_preview/new/state.js
+++ b/chromium/chrome/browser/resources/print_preview/new/state.js
@@ -1,18 +1,68 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
cr.exportPath('print_preview_new');
-/**
- * @typedef {{
- * previewLoading: boolean,
- * previewFailed: boolean,
- * cloudPrintError: string,
- * privetExtensionError: string,
- * invalidSettings: boolean,
- * initialized: boolean,
- * cancelled: boolean,
- * }}
- */
-print_preview_new.State;
+/** @enum {number} */
+print_preview_new.State = {
+ NOT_READY: 0,
+ READY: 1,
+ HIDDEN: 2,
+ PRINTING: 3,
+ INVALID_TICKET: 4,
+ INVALID_PRINTER: 5,
+ FATAL_ERROR: 6,
+ CLOSING: 7,
+};
+
+Polymer({
+ is: 'print-preview-state',
+
+ properties: {
+ /** @type {print_preview_new.State} */
+ state: {
+ type: Number,
+ notify: true,
+ value: print_preview_new.State.NOT_READY,
+ },
+ },
+
+ /** @param {print_preview_new.State} newState The state to transition to. */
+ transitTo: function(newState) {
+ switch (newState) {
+ case (print_preview_new.State.NOT_READY):
+ assert(
+ this.state == print_preview_new.State.NOT_READY ||
+ this.state == print_preview_new.State.READY ||
+ this.state == print_preview_new.State.INVALID_PRINTER);
+ break;
+ case (print_preview_new.State.READY):
+ assert(
+ this.state == print_preview_new.State.INVALID_TICKET ||
+ this.state == print_preview_new.State.NOT_READY ||
+ this.state == print_preview_new.State.PRINTING);
+ break;
+ case (print_preview_new.State.HIDDEN):
+ assert(this.state == print_preview_new.State.READY);
+ break;
+ case (print_preview_new.State.PRINTING):
+ assert(
+ this.state == print_preview_new.State.READY ||
+ this.state == print_preview_new.State.HIDDEN);
+ break;
+ case (print_preview_new.State.INVALID_TICKET):
+ assert(this.state == print_preview_new.State.READY);
+ break;
+ case (print_preview_new.State.INVALID_PRINTER):
+ assert(
+ this.state == print_preview_new.State.NOT_READY ||
+ this.state == print_preview_new.State.READY);
+ break;
+ case (print_preview_new.State.CLOSING):
+ assert(this.state != print_preview_new.State.HIDDEN);
+ break;
+ }
+ this.state = newState;
+ },
+});
diff --git a/chromium/chrome/browser/resources/print_preview/preview_generator.js b/chromium/chrome/browser/resources/print_preview/preview_generator.js
index 4cfb3d90ede..832529cca5f 100644
--- a/chromium/chrome/browser/resources/print_preview/preview_generator.js
+++ b/chromium/chrome/browser/resources/print_preview/preview_generator.js
@@ -101,6 +101,12 @@ cr.define('print_preview', function() {
this.scalingValue_ = 100;
/**
+ * Whether the PDF should be rasterized for printing.
+ * @private {boolean}
+ */
+ this.rasterize_ = false;
+
+ /**
* Page ranges setting used used to generate the last preview.
* @private {Array<{from: number, to: number}>}
*/
@@ -176,6 +182,7 @@ cr.define('print_preview', function() {
this.printTicketStore_.headerFooter.getValue();
this.colorValue_ = this.printTicketStore_.color.getValue();
this.isFitToPageEnabled_ = this.printTicketStore_.fitToPage.getValue();
+ this.rasterize_ = this.printTicketStore_.rasterize.getValue();
this.scalingValue_ = this.printTicketStore_.scaling.getValueAsNumber();
this.pageRanges_ = this.printTicketStore_.pageRange.getPageRanges();
this.marginsType_ = this.printTicketStore_.marginsType.getValue();
@@ -240,7 +247,7 @@ cr.define('print_preview', function() {
printWithCloudPrint: !destination.isLocal,
printWithPrivet: destination.isPrivet,
printWithExtension: destination.isExtension,
- rasterizePDF: false,
+ rasterizePDF: printTicketStore.rasterize.getValue(),
shouldPrintBackgrounds: printTicketStore.cssBackground.getValue(),
shouldPrintSelectionOnly: printTicketStore.selectionOnly.getValue()
};
@@ -321,6 +328,7 @@ cr.define('print_preview', function() {
!ticketStore.color.isValueEqual(this.colorValue_) ||
!ticketStore.scaling.isValueEqual(this.scalingValue_) ||
!ticketStore.fitToPage.isValueEqual(this.isFitToPageEnabled_) ||
+ !ticketStore.rasterize.isValueEqual(this.rasterize_) ||
(!ticketStore.marginsType.isValueEqual(this.marginsType_) &&
!ticketStore.marginsType.isValueEqual(
print_preview.ticket_items.MarginsTypeValue.CUSTOM)) ||
diff --git a/chromium/chrome/browser/resources/print_preview/previewarea/preview_area.css b/chromium/chrome/browser/resources/print_preview/previewarea/preview_area.css
index a6ce9ed4fae..fd272fa1824 100644
--- a/chromium/chrome/browser/resources/print_preview/previewarea/preview_area.css
+++ b/chromium/chrome/browser/resources/print_preview/previewarea/preview_area.css
@@ -80,7 +80,6 @@
}
#preview-area .learn-more-link {
- -webkit-margin-start: 0.5em;
color: rgb(51, 103, 214);
}
diff --git a/chromium/chrome/browser/resources/print_preview/previewarea/preview_area.js b/chromium/chrome/browser/resources/print_preview/previewarea/preview_area.js
index d1cb9dedd2d..89ceb0d1654 100644
--- a/chromium/chrome/browser/resources/print_preview/previewarea/preview_area.js
+++ b/chromium/chrome/browser/resources/print_preview/previewarea/preview_area.js
@@ -81,7 +81,8 @@ cr.define('print_preview', function() {
this.printTicketStore_ = printTicketStore;
/**
- * Used to contruct the preview generator.
+ * Used to construct the preview generator and to open the GCP learn more
+ * help link.
* @type {!print_preview.NativeLayer}
* @private
*/
@@ -149,6 +150,13 @@ cr.define('print_preview', function() {
this.isDocumentReady_ = false;
/**
+ * Whether the current destination is valid.
+ * @type {boolean}
+ * @private
+ */
+ this.isDestinationValid_ = true;
+
+ /**
* Timeout object used to display a loading message if the preview is taking
* a long time to generate.
* @type {?number}
@@ -213,7 +221,8 @@ cr.define('print_preview', function() {
'preview-area-open-system-dialog-button-throbber',
OVERLAY: 'preview-area-overlay-layer',
MARGIN_CONTROL: 'margin-control',
- PREVIEW_AREA: 'preview-area-plugin-wrapper'
+ PREVIEW_AREA: 'preview-area-plugin-wrapper',
+ GCP_ERROR_LEARN_MORE_LINK: 'learn-more-link'
};
/**
@@ -321,6 +330,16 @@ cr.define('print_preview', function() {
this.showMessage_(print_preview.PreviewAreaMessageId_.CUSTOM, message);
},
+ /** @param {boolean} valid Whether the current destination is valid. */
+ setDestinationValid: function(valid) {
+ this.isDestinationValid_ = valid;
+ // If destination is valid and preview is ready, hide the error message.
+ if (valid && this.isPluginReloaded_ && this.isDocumentReady_) {
+ this.setOverlayVisible_(false);
+ this.dispatchPreviewGenerationDoneIfReady_();
+ }
+ },
+
/** @override */
enterDocument: function() {
print_preview.Component.prototype.enterDocument.call(this);
@@ -328,6 +347,9 @@ cr.define('print_preview', function() {
this.tracker.add(
assert(this.openSystemDialogButton_), 'click',
this.onOpenSystemDialogButtonClick_.bind(this));
+ this.tracker.add(
+ assert(this.gcpErrorLearnMoreLink_), 'click',
+ this.onGcpErrorLearnMoreClick_.bind(this));
const TicketStoreEvent = print_preview.PrintTicketStore.EventType;
[TicketStoreEvent.INITIALIZE, TicketStoreEvent.CAPABILITIES_CHANGE,
@@ -377,6 +399,7 @@ cr.define('print_preview', function() {
print_preview.Component.prototype.exitDocument.call(this);
this.overlayEl_ = null;
this.openSystemDialogButton_ = null;
+ this.gcpErrorLearnMoreLink_ = null;
},
/** @override */
@@ -386,6 +409,8 @@ cr.define('print_preview', function() {
PreviewArea.Classes_.OVERLAY)[0];
this.openSystemDialogButton_ = this.getElement().getElementsByClassName(
PreviewArea.Classes_.OPEN_SYSTEM_DIALOG_BUTTON)[0];
+ this.gcpErrorLearnMoreLink_ = this.getElement().getElementsByClassName(
+ PreviewArea.Classes_.GCP_ERROR_LEARN_MORE_LINK)[0];
},
/**
@@ -526,11 +551,22 @@ cr.define('print_preview', function() {
},
/**
+ * Called when the learn more link for a cloud destination with an invalid
+ * certificate is clicked. Calls nativeLayer to open a new tab with the help
+ * page.
+ * @private
+ */
+ onGcpErrorLearnMoreClick_: function() {
+ this.nativeLayer_.forceOpenNewTab(
+ loadTimeData.getString('gcpCertificateErrorLearnMoreURL'));
+ },
+
+ /**
* Called when the print ticket changes. Updates the preview.
* @private
*/
onTicketChange_: function() {
- if (!this.previewGenerator_)
+ if (!this.previewGenerator_ || !this.isDestinationValid_)
return;
const previewRequest = this.previewGenerator_.requestPreview();
if (previewRequest.id <= -1) {
diff --git a/chromium/chrome/browser/resources/print_preview/print_preview.js b/chromium/chrome/browser/resources/print_preview/print_preview.js
index 918f2ad7db4..ce0d488008a 100644
--- a/chromium/chrome/browser/resources/print_preview/print_preview.js
+++ b/chromium/chrome/browser/resources/print_preview/print_preview.js
@@ -1026,6 +1026,7 @@ cr.define('print_preview', function() {
this.uiState_ = PrintPreviewUiState_.ERROR;
this.isPreviewGenerationInProgress_ = false;
this.printHeader_.isPrintButtonEnabled = false;
+ this.previewArea_.setDestinationValid(false);
},
/**
@@ -1311,8 +1312,10 @@ cr.define('print_preview', function() {
}
// Reset if we had a bad settings fetch since the user selected a new
// printer.
- if (this.uiState_ == PrintPreviewUiState_.ERROR)
+ if (this.uiState_ == PrintPreviewUiState_.ERROR) {
this.uiState_ = PrintPreviewUiState_.READY;
+ this.previewArea_.setDestinationValid(true);
+ }
if (this.destinationStore_.selectedDestination &&
this.isInKioskAutoPrintMode_) {
this.onPrintButtonClick_();
diff --git a/chromium/chrome/browser/resources/print_preview/print_preview_new.html b/chromium/chrome/browser/resources/print_preview/print_preview_new.html
index 303438ada23..b7a82538e9b 100644
--- a/chromium/chrome/browser/resources/print_preview/print_preview_new.html
+++ b/chromium/chrome/browser/resources/print_preview/print_preview_new.html
@@ -1,11 +1,14 @@
<!doctype html>
<html dir="$i18n{textdirection}" lang="$i18n{language}">
<head>
-<meta charset="utf-8">
+ <meta charset="utf-8">
<title></title>
-</head>
-<body>
<style>
+ html {
+ /* Remove 300ms delay for 'click' event, when using touch interface. */
+ touch-action: manipulation;
+ }
+
html,
body {
height: 100%;
@@ -14,6 +17,8 @@
width: 100%;
}
</style>
+</head>
+<body>
<print-preview-app></print-preview-app>
<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
<link rel="import" href="new/app.html">
diff --git a/chromium/chrome/browser/resources/print_preview/print_preview_resources.grd b/chromium/chrome/browser/resources/print_preview/print_preview_resources.grd
index 943dce160ce..089ce3ff322 100644
--- a/chromium/chrome/browser/resources/print_preview/print_preview_resources.grd
+++ b/chromium/chrome/browser/resources/print_preview/print_preview_resources.grd
@@ -29,6 +29,12 @@
<structure name="IDR_PRINT_PREVIEW_NEW_MODEL_JS"
file="new/model.js"
type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_CLOUD_PRINT_INTERFACE_HTML"
+ file="cloud_print_interface.html"
+ type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_CLOUD_PRINT_INTERFACE_JS"
+ file="cloud_print_interface.js"
+ type="chrome_html" />
<structure name="IDR_PRINT_PREVIEW_NATIVE_LAYER_HTML"
file="native_layer.html"
type="chrome_html" />
@@ -53,12 +59,24 @@
<structure name="IDR_PRINT_PREVIEW_DATA_DESTINATION_STORE_JS"
file="data/destination_store.js"
type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_DATA_CLOUD_PARSERS_HTML"
+ file="data/cloud_parsers.html"
+ type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_DATA_CLOUD_PARSERS_JS"
+ file="data/cloud_parsers.js"
+ type="chrome_html" />
<structure name="IDR_PRINT_PREVIEW_DATA_LOCAL_PARSERS_HTML"
file="data/local_parsers.html"
type="chrome_html" />
<structure name="IDR_PRINT_PREVIEW_DATA_LOCAL_PARSERS_JS"
file="data/local_parsers.js"
type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_DATA_INVITATION_HTML"
+ file="data/invitation.html"
+ type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_DATA_INVITATION_JS"
+ file="data/invitation.js"
+ type="chrome_html" />
<structure name="IDR_PRINT_PREVIEW_DATA_MARGINS_HTML"
file="data/margins.html"
type="chrome_html" />
@@ -134,6 +152,12 @@
<structure name="IDR_PRINT_PREVIEW_NEW_SETTINGS_BEHAVIOR_JS"
file="new/settings_behavior.js"
type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_NEW_STATE_HTML"
+ file="new/state.html"
+ type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_NEW_STATE_JS"
+ file="new/state.js"
+ type="chrome_html" />
<structure name="IDR_PRINT_PREVIEW_NEW_SETTINGS_SECTION_HTML"
file="new/settings_section.html"
type="chrome_html" />
@@ -212,12 +236,57 @@
<structure name="IDR_PRINT_PREVIEW_NEW_ADVANCED_OPTIONS_SETTINGS_JS"
file="new/advanced_options_settings.js"
type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_NEW_ADVANCED_SETTINGS_DIALOG_HTML"
+ file="new/advanced_settings_dialog.html"
+ type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_NEW_ADVANCED_SETTINGS_DIALOG_JS"
+ file="new/advanced_settings_dialog.js"
+ type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_NEW_ADVANCED_SETTINGS_ITEM_HTML"
+ file="new/advanced_settings_item.html"
+ type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_NEW_ADVANCED_SETTINGS_ITEM_JS"
+ file="new/advanced_settings_item.js"
+ type="chrome_html" />
<structure name="IDR_PRINT_PREVIEW_NEW_NUMBER_SETTINGS_SECTION_HTML"
file="new/number_settings_section.html"
type="chrome_html" />
<structure name="IDR_PRINT_PREVIEW_NEW_NUMBER_SETTINGS_SECTION_JS"
file="new/number_settings_section.js"
type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_NEW_DESTINATION_DIALOG_HTML"
+ file="new/destination_dialog.html"
+ type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_NEW_DESTINATION_DIALOG_JS"
+ file="new/destination_dialog.js"
+ type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_NEW_DESTINATION_LIST_HTML"
+ file="new/destination_list.html"
+ type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_NEW_DESTINATION_LIST_JS"
+ file="new/destination_list.js"
+ type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_NEW_DESTINATION_LIST_ITEM_HTML"
+ file="new/destination_list_item.html"
+ type="chrome_html"
+ preprocess="true" />
+ <structure name="IDR_PRINT_PREVIEW_NEW_DESTINATION_LIST_ITEM_JS"
+ file="new/destination_list_item.js"
+ type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_NEW_PRINT_PREVIEW_SEARCH_BOX_HTML"
+ file="new/print_preview_search_box.html"
+ type="chrome_html"
+ flattenhtml="true"
+ allowexternalscript="true" />
+ <structure name="IDR_PRINT_PREVIEW_NEW_PRINT_PREVIEW_SEARCH_BOX_JS"
+ file="new/print_preview_search_box.js"
+ type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_NEW_HIGHLIGHT_UTILS_HTML"
+ file="new/highlight_utils.html"
+ type="chrome_html" />
+ <structure name="IDR_PRINT_PREVIEW_NEW_HIGHLIGHT_UTILS_JS"
+ file="new/highlight_utils.js"
+ type="chrome_html" />
<structure name="IDR_PRINT_PREVIEW_NEW_PRINT_PREVIEW_SHARED_CSS_HTML"
file="new/print_preview_shared_css.html"
type="chrome_html"
@@ -240,6 +309,9 @@
<structure name="IDR_PRINT_PREVIEW_NEW_THROBBER_CSS_HTML"
file="new/throbber_css.html"
type="chrome_html"/>
+ <structure name="IDR_PRINT_PREVIEW_NEW_SEARCH_DIALOG_CSS_HTML"
+ file="new/search_dialog_css.html"
+ type="chrome_html"/>
<structure name="IDR_PRINT_PREVIEW_NEW_STRINGS_HTML"
file="new/strings.html"
type="chrome_html" />
diff --git a/chromium/chrome/browser/resources/print_preview/print_preview_utils.js b/chromium/chrome/browser/resources/print_preview/print_preview_utils.js
index cd931980ba4..0220810fe82 100644
--- a/chromium/chrome/browser/resources/print_preview/print_preview_utils.js
+++ b/chromium/chrome/browser/resources/print_preview/print_preview_utils.js
@@ -114,7 +114,7 @@ function pageRangeTextToPageRanges(pageRangeText, opt_totalPageCount) {
opt_totalPageCount ? opt_totalPageCount : MAX_PAGE_NUMBER;
const regex = /^\s*([0-9]*)\s*-\s*([0-9]*)\s*$/;
- const parts = pageRangeText.split(/,/);
+ const parts = pageRangeText.split(/,|\u3001/);
const pageRanges = [];
for (let i = 0; i < parts.length; ++i) {
diff --git a/chromium/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs b/chromium/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs
index ff15d6fa61e..17fa25be71e 100644
--- a/chromium/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs
+++ b/chromium/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs
@@ -86,6 +86,12 @@ TEST_F('PrintPreviewUtilsUnitTest', 'PageRanges', function() {
pageRangeTextToPageRanges("-", null));
assertRangesEqual([[1, 1000000000]],
pageRangeTextToPageRanges("-", 0));
+
+ // https://crbug.com/806165
+ assertRangesEqual([1, 2, 3, 1, 56],
+ pageRangeTextToPageRanges("1\u30012\u30013\u30011\u300156", 100));
+ assertRangesEqual([1, 2, 3, 1, 56],
+ pageRangeTextToPageRanges("1,2,3\u30011\u300156", 100));
});
TEST_F('PrintPreviewUtilsUnitTest', 'InvalidPageRanges', function() {
@@ -101,6 +107,11 @@ TEST_F('PrintPreviewUtilsUnitTest', 'InvalidPageRanges', function() {
pageRangeTextToPageRanges("1,2,56-40", 100));
assertEquals(PageRangeStatus.LIMIT_ERROR,
pageRangeTextToPageRanges("101-110", 100));
+
+ assertEquals(PageRangeStatus.SYNTAX_ERROR,
+ pageRangeTextToPageRanges("1\u30012\u30010\u300156", 100));
+ assertEquals(PageRangeStatus.SYNTAX_ERROR,
+ pageRangeTextToPageRanges("-1,1,2\u3001\u300156", 100));
});
TEST_F('PrintPreviewUtilsUnitTest', 'PageRangeTextToPageList', function() {
diff --git a/chromium/chrome/browser/resources/print_preview/search/destination_list_item.js b/chromium/chrome/browser/resources/print_preview/search/destination_list_item.js
index c6ef0c7b846..e31d26c99c1 100644
--- a/chromium/chrome/browser/resources/print_preview/search/destination_list_item.js
+++ b/chromium/chrome/browser/resources/print_preview/search/destination_list_item.js
@@ -72,6 +72,9 @@ cr.define('print_preview', function() {
this.tracker.add(
this.getChildElement('.register-promo-button'), 'click',
this.onRegisterPromoClicked_.bind(this));
+ this.tracker.add(
+ this.getChildElement('.learn-more-link'), 'click',
+ this.onGcpErrorLearnMoreClick_.bind(this));
},
/** @return {!print_preview.Destination} */
@@ -199,15 +202,10 @@ cr.define('print_preview', function() {
// Initialize the element which renders the destination's connection
// status.
this.getElement().classList.toggle(
- 'stale',
- this.destination_.isOffline ||
- this.destination_.shouldShowInvalidCertificateError);
+ 'stale', this.destination_.isOfflineOrInvalid);
const connectionStatusEl = this.getChildElement('.connection-status');
connectionStatusEl.textContent = this.destination_.connectionStatusText;
- setIsVisible(
- connectionStatusEl,
- this.destination_.isOffline ||
- this.destination_.shouldShowInvalidCertificateError);
+ setIsVisible(connectionStatusEl, this.destination_.isOfflineOrInvalid);
setIsVisible(
this.getChildElement('.learn-more-link'),
this.destination_.shouldShowInvalidCertificateError);
@@ -324,6 +322,16 @@ cr.define('print_preview', function() {
},
/**
+ * Called when the learn more link for an unsupported cloud destination is
+ * clicked. Opens the help page via native layer.
+ * @private
+ */
+ onGcpErrorLearnMoreClick_: function() {
+ print_preview.NativeLayer.getInstance().forceOpenNewTab(
+ loadTimeData.getString('gcpCertificateErrorLearnMoreURL'));
+ },
+
+ /**
* Handles click and 'Enter' key down events for the extension icon element.
* It opens extensions page with the extension associated with the
* destination highlighted.
diff --git a/chromium/chrome/browser/resources/print_preview/search/destination_search.css b/chromium/chrome/browser/resources/print_preview/search/destination_search.css
index fd7fa54103b..8eddc19d078 100644
--- a/chromium/chrome/browser/resources/print_preview/search/destination_search.css
+++ b/chromium/chrome/browser/resources/print_preview/search/destination_search.css
@@ -89,6 +89,10 @@
-webkit-margin-start: 10px;
}
+#destination-search .invitation-cloud-print-information {
+ padding-top: 12px;
+}
+
#destination-search #invitation-process-throbber {
display: block;
}
diff --git a/chromium/chrome/browser/resources/print_preview/search/destination_search.html b/chromium/chrome/browser/resources/print_preview/search/destination_search.html
index 7d3c4c325e0..554855e835c 100644
--- a/chromium/chrome/browser/resources/print_preview/search/destination_search.html
+++ b/chromium/chrome/browser/resources/print_preview/search/destination_search.html
@@ -23,6 +23,9 @@
<button class="invitation-reject-button">$i18n{reject}</button>
<div id="invitation-process-throbber" class="throbber" hidden></div>
</div>
+ <div class="invitation-cloud-print-information">
+ $i18nRaw{registerPrinterInformationMessage}
+ </div>
</div>
<div class="cloudprint-promo gray-bottom-bar" hidden>
<img src="../images/cloud.png" class="icon" alt="">
diff --git a/chromium/chrome/browser/resources/print_preview/search/destination_search.js b/chromium/chrome/browser/resources/print_preview/search/destination_search.js
index 2262082c5eb..6235d6f3c12 100644
--- a/chromium/chrome/browser/resources/print_preview/search/destination_search.js
+++ b/chromium/chrome/browser/resources/print_preview/search/destination_search.js
@@ -791,7 +791,7 @@ cr.define('print_preview', function() {
*/
onWindowResize_: function() {
this.reflowLists_();
- }
+ },
};
// Export
diff --git a/chromium/chrome/browser/resources/print_preview/settings/advanced_settings/advanced_settings_item.js b/chromium/chrome/browser/resources/print_preview/settings/advanced_settings/advanced_settings_item.js
index b5ce64999e5..9c54b548ce9 100644
--- a/chromium/chrome/browser/resources/print_preview/settings/advanced_settings/advanced_settings_item.js
+++ b/chromium/chrome/browser/resources/print_preview/settings/advanced_settings/advanced_settings_item.js
@@ -1,24 +1,6 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-/**
- * Specifies a custom vendor capability.
- * @typedef {{
- * id: (string),
- * display_name: (string),
- * localized_display_name: (string | undefined),
- * type: (string),
- * select_cap: ({
- * option: (Array<{
- * display_name: (string),
- * type: (string | undefined),
- * value: (number | string | boolean),
- * is_default: (boolean | undefined)
- * }>|undefined)
- * }|undefined)
- * }}
- */
-print_preview.VendorCapability;
cr.define('print_preview', function() {
'use strict';
diff --git a/chromium/chrome/browser/resources/print_preview/settings/destination_settings.js b/chromium/chrome/browser/resources/print_preview/settings/destination_settings.js
index f8061808678..04b0362e2c4 100644
--- a/chromium/chrome/browser/resources/print_preview/settings/destination_settings.js
+++ b/chromium/chrome/browser/resources/print_preview/settings/destination_settings.js
@@ -139,15 +139,23 @@ cr.define('print_preview', function() {
locationEl.textContent = hint;
locationEl.title = hint;
- const connectionStatusText = destination.connectionStatusText;
+ const showDestinationInvalid =
+ (destination.hasInvalidCertificate &&
+ !loadTimeData.getBoolean('isEnterpriseManaged'));
+ let connectionStatusText = '';
+ if (showDestinationInvalid) {
+ connectionStatusText =
+ loadTimeData.getString('noLongerSupportedFragment');
+ } else {
+ connectionStatusText = destination.connectionStatusText;
+ }
const connectionStatusEl =
this.getChildElement('.destination-settings-connection-status');
connectionStatusEl.textContent = connectionStatusText;
connectionStatusEl.title = connectionStatusText;
- const hasConnectionError = destination.isOffline ||
- (destination.hasInvalidCertificate &&
- !loadTimeData.getBoolean('isEnterpriseManaged'));
+ const hasConnectionError =
+ destination.isOffline || showDestinationInvalid;
destinationSettingsBoxEl.classList.toggle(
print_preview.DestinationSettingsClasses_.STALE,
hasConnectionError);
diff --git a/chromium/chrome/browser/resources/safe_browsing/download_file_types.asciipb b/chromium/chrome/browser/resources/safe_browsing/download_file_types.asciipb
index 04a91c17835..8634478bccc 100644
--- a/chromium/chrome/browser/resources/safe_browsing/download_file_types.asciipb
+++ b/chromium/chrome/browser/resources/safe_browsing/download_file_types.asciipb
@@ -8,7 +8,7 @@
##
## Top level settings
##
-version_id: 15
+version_id: 16
sampled_ping_probability: 0.01
max_archived_binaries_to_report: 10
default_file_type {
@@ -797,7 +797,7 @@ file_types {
file_types {
# Opened by uTorrent and Transmission (can be a renamed .torrent)
# Added crbug.com/767502
- extension: "btbtskin"
+ extension: "btskin"
uma_value: 299
ping_setting: FULL_PING
}
diff --git a/chromium/chrome/browser/resources/settings/PRESUBMIT.py b/chromium/chrome/browser/resources/settings/PRESUBMIT.py
new file mode 100644
index 00000000000..dcc145b7f91
--- /dev/null
+++ b/chromium/chrome/browser/resources/settings/PRESUBMIT.py
@@ -0,0 +1,24 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+def _CheckChangeOnUploadOrCommit(input_api, output_api):
+ import sys
+ old_sys_path, cwd = sys.path[:], input_api.PresubmitLocalPath()
+ src_root = input_api.os_path.join(cwd, '..', '..', '..', '..')
+ try:
+ sys.path += [input_api.os_path.join(src_root, 'tools', 'web_dev_style')]
+ import web_dev_style.presubmit_support
+ finally:
+ sys.path = old_sys_path
+ return web_dev_style.presubmit_support.DisallowIncludes(input_api, output_api,
+ '<include> does not work in settings; use HTML imports instead')
+
+
+def CheckChangeOnUpload(input_api, output_api):
+ return _CheckChangeOnUploadOrCommit(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+ return _CheckChangeOnUploadOrCommit(input_api, output_api)
diff --git a/chromium/chrome/browser/resources/settings/a11y_page/a11y_page.html b/chromium/chrome/browser/resources/settings/a11y_page/a11y_page.html
index 8e9979679bd..3714da1774e 100644
--- a/chromium/chrome/browser/resources/settings/a11y_page/a11y_page.html
+++ b/chromium/chrome/browser/resources/settings/a11y_page/a11y_page.html
@@ -24,7 +24,7 @@
pref="{{prefs.settings.a11y.enable_menu}}">
</settings-toggle-button>
<div id="subpage-trigger" class="settings-box two-line"
- on-tap="onManageAccessibilityFeaturesTap_" actionable>
+ on-click="onManageAccessibilityFeaturesTap_" actionable>
<div class="start">
$i18n{manageAccessibilityFeatures}
<div class="secondary" id="themesSecondary">
diff --git a/chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html b/chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html
index 08720eb78c2..59420dcd63f 100644
--- a/chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html
+++ b/chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.html
@@ -16,11 +16,7 @@
-webkit-padding-start: var(--settings-box-row-padding);
}
- .list-item settings-dropdown-menu {
- -webkit-margin-start: 16px;
- }
-
- .sub-item > .start {
+ .sub-item {
-webkit-margin-start: var(--settings-indent-width);
}
@@ -50,7 +46,7 @@
</settings-toggle-button>
<iron-collapse opened="[[prefs.settings.accessibility.value]]">
<div class="settings-box"
- on-tap="onChromeVoxSettingsTap_" actionable>
+ on-click="onChromeVoxSettingsTap_" actionable>
<div class="start">$i18n{chromeVoxOptionsLabel}</div>
<button class="icon-external" is="paper-icon-button-light"
aria-label="$i18n{chromeVoxOptionsLabel}"></button>
@@ -63,7 +59,7 @@
</settings-toggle-button>
<iron-collapse opened="[[prefs.settings.a11y.select_to_speak.value]]">
<div class="settings-box"
- on-tap="onSelectToSpeakSettingsTap_" actionable>
+ on-click="onSelectToSpeakSettingsTap_" actionable>
<div class="start">$i18n{selectToSpeakOptionsLabel}</div>
<button class="icon-external" is="paper-icon-button-light"
aria-label="$i18n{selectToSpeakOptionsLabel}"></button>
@@ -77,9 +73,33 @@
</settings-toggle-button>
<settings-toggle-button class="continuation"
pref="{{prefs.settings.a11y.screen_magnifier}}"
- label="$i18n{screenMagnifierLabel}">
+ label="$i18n{screenMagnifierLabel}"
+ disabled="[[prefs.ash.docked_magnifier.enabled.value]]">
</settings-toggle-button>
- <div class="settings-box two-line" on-tap="onDisplayTap_" actionable>
+ <div class="settings-box continuation">
+ <div class="start sub-item">$i18n{screenMagnifierZoomLabel}</div>
+ <settings-dropdown-menu label="$i18n{screenMagnifierZoomLabel}"
+ pref="{{prefs.settings.a11y.screen_magnifier_scale}}"
+ menu-options="[[screenMagnifierZoomOptions_]]"
+ disabled="[[!prefs.settings.a11y.screen_magnifier.value]]">
+ </settings-dropdown-menu>
+ </div>
+ <template is="dom-if" if="[[dockedMagnifierFeatureEnabled_]]" restamp>
+ <settings-toggle-button class="continuation"
+ pref="{{prefs.ash.docked_magnifier.enabled}}"
+ label="$i18n{dockedMagnifierLabel}"
+ disabled="[[prefs.settings.a11y.screen_magnifier.value]]">
+ </settings-toggle-button>
+ <div class="settings-box continuation">
+ <div class="start sub-item">$i18n{dockedMagnifierZoomLabel}</div>
+ <settings-dropdown-menu label="$i18n{dockedMagnifierZoomLabel}"
+ pref="{{prefs.ash.docked_magnifier.scale}}"
+ menu-options="[[screenMagnifierZoomOptions_]]"
+ disabled="[[!prefs.ash.docked_magnifier.enabled.value]]">
+ </settings-dropdown-menu>
+ </div>
+ </template>
+ <div class="settings-box two-line" on-click="onDisplayTap_" actionable>
<div class="start">
$i18n{displaySettingsTitle}
<div class="secondary">$i18n{displaySettingsDescription}</div>
@@ -88,7 +108,7 @@
aria-label="$i18n{displaySettingsTitle}"
aria-describedby="displaySettingsSecondary"></button>
</div>
- <div class="settings-box two-line" on-tap="onAppearanceTap_" actionable>
+ <div class="settings-box two-line" on-click="onAppearanceTap_" actionable>
<div class="start">
$i18n{appearanceSettingsTitle}
<div class="secondary" id="appearanceSettingsSecondary">
@@ -123,13 +143,13 @@
label="$i18n{switchAccessLabel}">
<button is="paper-icon-button-light"
class="icon-settings" slot="more-actions"
- on-tap="onSwitchAccessSettingsTap_"
+ on-click="onSwitchAccessSettingsTap_"
hidden="[[!prefs.settings.a11y.switch_access.value]]"
aria-label="$i18n{selectToSpeakOptionsLabel}">
</button>
</settings-toggle-button>
</template>
- <div class="settings-box two-line" on-tap="onKeyboardTap_" actionable>
+ <div class="settings-box two-line" on-click="onKeyboardTap_" actionable>
<div class="start">
$i18n{keyboardSettingsTitle}
<div class="secondary" id="keyboardSettingsSecondary">
@@ -146,15 +166,13 @@
pref="{{prefs.settings.a11y.autoclick}}"
label="$i18n{clickOnStopLabel}">
</settings-toggle-button>
- <div class="settings-box block first">
- <div class="list-item sub-item">
- <div class="start">$i18n{delayBeforeClickLabel}</div>
- <settings-dropdown-menu label="$i18n{delayBeforeClickLabel}"
- pref="{{prefs.settings.a11y.autoclick_delay_ms}}"
- menu-options="[[autoClickDelayOptions_]]"
- disabled="[[!prefs.settings.a11y.autoclick.value]]">
- </settings-dropdown-menu>
- </div>
+ <div class="settings-box continuation">
+ <div class="start sub-item">$i18n{delayBeforeClickLabel}</div>
+ <settings-dropdown-menu label="$i18n{delayBeforeClickLabel}"
+ pref="{{prefs.settings.a11y.autoclick_delay_ms}}"
+ menu-options="[[autoClickDelayOptions_]]"
+ disabled="[[!prefs.settings.a11y.autoclick.value]]">
+ </settings-dropdown-menu>
</div>
<settings-toggle-button class="continuation"
pref="{{prefs.settings.touchpad.enable_tap_dragging}}"
@@ -164,23 +182,21 @@
pref="{{prefs.settings.a11y.large_cursor_enabled}}"
label="$i18n{largeMouseCursorLabel}">
</settings-toggle-button>
- <div class="settings-box block continuation"
+ <div class="settings-box continuation"
hidden$="[[!prefs.settings.a11y.large_cursor_enabled.value]]">
- <div class="list-item sub-item">
- <div class="start">$i18n{largeMouseCursorSizeLabel}</div>
- <settings-slider
- pref="{{prefs.settings.a11y.large_cursor_dip_size}}"
- min="25" max="64"
- label-min="$i18n{largeMouseCursorSizeDefaultLabel}"
- label-max="$i18n{largeMouseCursorSizeLargeLabel}">
- </settings-slider>
- </div>
+ <div class="start sub-item">$i18n{largeMouseCursorSizeLabel}</div>
+ <settings-slider
+ pref="{{prefs.settings.a11y.large_cursor_dip_size}}"
+ min="25" max="64"
+ label-min="$i18n{largeMouseCursorSizeDefaultLabel}"
+ label-max="$i18n{largeMouseCursorSizeLargeLabel}">
+ </settings-slider>
</div>
<settings-toggle-button class="continuation"
pref="{{prefs.settings.a11y.cursor_highlight}}"
label="$i18n{cursorHighlightLabel}">
</settings-toggle-button>
- <div class="settings-box two-line" on-tap="onMouseTap_" actionable>
+ <div class="settings-box two-line" on-click="onMouseTap_" actionable>
<div class="start">
$i18n{mouseSettingsTitle}
<div class="secondary" id="mouseSettingsSecondary">
diff --git a/chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.js b/chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.js
index 79e39c1dd0e..090a8a7f74a 100644
--- a/chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.js
+++ b/chromium/chrome/browser/resources/settings/a11y_page/manage_a11y_page.js
@@ -19,6 +19,28 @@ Polymer({
notify: true,
},
+ screenMagnifierZoomOptions_: {
+ readOnly: true,
+ type: Array,
+ value: function() {
+ // These values correspond to the i18n values in settings_strings.grdp.
+ // If these values get changed then those strings need to be changed as
+ // well.
+ return [
+ {value: 2, name: loadTimeData.getString('screenMagnifierZoom2x')},
+ {value: 4, name: loadTimeData.getString('screenMagnifierZoom4x')},
+ {value: 6, name: loadTimeData.getString('screenMagnifierZoom6x')},
+ {value: 8, name: loadTimeData.getString('screenMagnifierZoom8x')},
+ {value: 10, name: loadTimeData.getString('screenMagnifierZoom10x')},
+ {value: 12, name: loadTimeData.getString('screenMagnifierZoom12x')},
+ {value: 14, name: loadTimeData.getString('screenMagnifierZoom14x')},
+ {value: 16, name: loadTimeData.getString('screenMagnifierZoom16x')},
+ {value: 18, name: loadTimeData.getString('screenMagnifierZoom18x')},
+ {value: 20, name: loadTimeData.getString('screenMagnifierZoom20x')},
+ ];
+ },
+ },
+
autoClickDelayOptions_: {
readOnly: true,
type: Array,
@@ -56,6 +78,17 @@ Polymer({
},
},
+ /**
+ * Whether the docked magnifier flag is enabled.
+ * @private {boolean}
+ */
+ dockedMagnifierFeatureEnabled_: {
+ type: Boolean,
+ value: function() {
+ return loadTimeData.getBoolean('dockedMagnifierFeatureEnabled');
+ },
+ },
+
/** @private */
isGuest_: {
type: Boolean,
diff --git a/chromium/chrome/browser/resources/settings/about_page/about_page.html b/chromium/chrome/browser/resources/settings/about_page/about_page.html
index 4f2f097bbe7..9053116b11d 100644
--- a/chromium/chrome/browser/resources/settings/about_page/about_page.html
+++ b/chromium/chrome/browser/resources/settings/about_page/about_page.html
@@ -78,7 +78,7 @@
<if expr="_google_chrome and is_macosx">
#promoteUpdater[disabled] {
- @apply(--cr-secondary-text);
+ @apply --cr-secondary-text;
}
</if>
</style>
@@ -88,7 +88,7 @@
focus-config="[[focusConfig_]]">
<neon-animatable route-path="default">
<div class="settings-box two-line">
- <img id="product-logo" on-tap="onProductLogoTap_"
+ <img id="product-logo" on-click="onProductLogoTap_"
srcset="chrome://theme/current-channel-logo@1x 1x,
chrome://theme/current-channel-logo@2x 2x"
alt="$i18n{aboutProductLogoAlt}">
@@ -153,18 +153,18 @@
<div class="separator" hidden="[[!showButtonContainer_]]"></div>
<span id="buttonContainer" hidden="[[!showButtonContainer_]]">
<paper-button id="relaunch" class="secondary-button"
- hidden="[[!showRelaunch_]]" on-tap="onRelaunchTap_">
+ hidden="[[!showRelaunch_]]" on-click="onRelaunchTap_">
$i18n{aboutRelaunch}
</paper-button>
<if expr="chromeos">
<paper-button id="relaunchAndPowerwash" class="secondary-button"
hidden="[[!showRelaunchAndPowerwash_]]"
- on-tap="onRelaunchAndPowerwashTap_">
+ on-click="onRelaunchAndPowerwashTap_">
$i18n{aboutRelaunchAndPowerwash}
</paper-button>
<paper-button id="checkForUpdates" class="secondary-button"
hidden="[[!showCheckUpdates_]]"
- on-tap="onCheckUpdatesTap_">
+ on-click="onCheckUpdatesTap_">
$i18n{aboutCheckForUpdates}
</paper-button>
</if>
@@ -173,13 +173,13 @@
<if expr="chromeos">
<div id="aboutTPMFirmwareUpdate" class="settings-box two-line"
hidden$="[[!showTPMFirmwareUpdateLineItem_]]"
- on-tap="onTPMFirmwareUpdateTap_" actionable>
+ on-click="onTPMFirmwareUpdateTap_" actionable>
<div class="start">
<div>$i18n{aboutTPMFirmwareUpdateTitle}</div>
<div class="secondary">
$i18n{aboutTPMFirmwareUpdateDescription}
<a href="$i18n{aboutTPMFirmwareUpdateLearnMoreURL}"
- target="_blank" on-tap="onLearnMoreTap_">
+ target="_blank" on-click="onLearnMoreTap_">
$i18n{learnMore}
</a>
</div>
@@ -194,12 +194,12 @@
<div id="promoteUpdater" class="settings-box"
disabled$="[[promoteUpdaterStatus_.disabled]]"
actionable$="[[promoteUpdaterStatus_.actionable]]"
- on-tap="onPromoteUpdaterTap_">
+ on-click="onPromoteUpdaterTap_">
<div class="start">
[[promoteUpdaterStatus_.text]]
<a href="https://support.google.com/chrome/answer/95414"
target="_blank" id="updaterLearnMore"
- on-tap="onLearnMoreTap_">
+ on-click="onLearnMoreTap_">
$i18n{learnMore}
</a>
</div>
@@ -211,21 +211,22 @@
</div>
</template>
</if>
- <div id="help" class="settings-box" on-tap="onHelpTap_" actionable>
+ <div id="help" class="settings-box" on-click="onHelpTap_"
+ actionable>
<div class="start">$i18n{aboutGetHelpUsingChrome}</div>
<button class="icon-external" is="paper-icon-button-light"
aria-labelledby="help"></button>
</div>
<if expr="_google_chrome">
<div id="reportIssue" class="settings-box" actionable
- on-tap="onReportIssueTap_">
+ on-click="onReportIssueTap_">
<div class="start">$i18n{aboutReportAnIssue}</div>
<button class="subpage-arrow" is="paper-icon-button-light"
aria-labelledby="reportIssue"></button>
</div>
</if>
<if expr="chromeos">
- <div class="settings-box" on-tap="onDetailedBuildInfoTap_"
+ <div class="settings-box" on-click="onDetailedBuildInfoTap_"
actionable>
<div class="start">$i18n{aboutDetailedBuildInfo}</div>
<button id="detailed-build-info-trigger" class="subpage-arrow"
diff --git a/chromium/chrome/browser/resources/settings/about_page/channel_switcher_dialog.html b/chromium/chrome/browser/resources/settings/about_page/channel_switcher_dialog.html
index 9f58ef3661b..70cbc55efe9 100644
--- a/chromium/chrome/browser/resources/settings/about_page/channel_switcher_dialog.html
+++ b/chromium/chrome/browser/resources/settings/about_page/channel_switcher_dialog.html
@@ -53,15 +53,15 @@
</iron-selector>
</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onCancelTap_"
+ <paper-button class="cancel-button" on-click="onCancelTap_"
id="cancel">$i18n{cancel}</paper-button>
<paper-button id="changeChannel" class="action-button"
- on-tap="onChangeChannelTap_"
+ on-click="onChangeChannelTap_"
hidden="[[!shouldShowButtons_.changeChannel]]">
$i18n{aboutChangeChannel}
</paper-button>
<paper-button id="changeChannelAndPowerwash" class="action-button"
- on-tap="onChangeChannelAndPowerwashTap_"
+ on-click="onChangeChannelAndPowerwashTap_"
hidden="[[!shouldShowButtons_.changeChannelAndPowerwash]]">
$i18n{aboutChangeChannelAndPowerwash}
</paper-button>
diff --git a/chromium/chrome/browser/resources/settings/about_page/detailed_build_info.html b/chromium/chrome/browser/resources/settings/about_page/detailed_build_info.html
index a1ae3142cd9..08ab773cc7c 100644
--- a/chromium/chrome/browser/resources/settings/about_page/detailed_build_info.html
+++ b/chromium/chrome/browser/resources/settings/about_page/detailed_build_info.html
@@ -39,7 +39,7 @@
<div class="secondary">[[currentlyOnChannelText_]]</div>
</div>
<div class="separator"></div>
- <paper-button on-tap="onChangeChannelTap_"
+ <paper-button on-click="onChangeChannelTap_"
disabled="[[!canChangeChannel_]]">
$i18n{aboutChangeChannel}
</paper-button>
diff --git a/chromium/chrome/browser/resources/settings/about_page/update_warning_dialog.html b/chromium/chrome/browser/resources/settings/about_page/update_warning_dialog.html
index 3a336109f1f..e6dd4ab2540 100644
--- a/chromium/chrome/browser/resources/settings/about_page/update_warning_dialog.html
+++ b/chromium/chrome/browser/resources/settings/about_page/update_warning_dialog.html
@@ -16,9 +16,9 @@
</div>
<div slot="button-container">
<paper-button id="cancel" class="cancel-button"
- on-tap="onCancelTap_">$i18n{cancel}</paper-button>
+ on-click="onCancelTap_">$i18n{cancel}</paper-button>
<paper-button id="continue" class="action-button"
- on-tap="onContinueTap_">
+ on-click="onContinueTap_">
$i18n{aboutUpdateWarningContinue}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/android_apps_page/android_apps_page.html b/chromium/chrome/browser/resources/settings/android_apps_page/android_apps_page.html
index a59514198c0..db780b0d718 100644
--- a/chromium/chrome/browser/resources/settings/android_apps_page/android_apps_page.html
+++ b/chromium/chrome/browser/resources/settings/android_apps_page/android_apps_page.html
@@ -23,7 +23,7 @@
<template is="dom-if" if="[[havePlayStoreApp]]" restamp>
<div id="android-apps" class="settings-box two-line first"
actionable$="[[androidAppsInfo.playStoreEnabled]]"
- on-tap="onSubpageTap_">
+ on-click="onSubpageTap_">
<div class="start">
$i18n{androidAppsPageLabel}
<div class="secondary" id="secondaryText"
@@ -43,7 +43,7 @@
<div class="separator"></div>
<paper-button id="enable" class="secondary-button"
disabled="[[isEnforced_(prefs.arc.enabled)]]"
- on-tap="onEnableTap_"
+ on-click="onEnableTap_"
aria-label="$i18n{androidAppsPageTitle}"
aria-describedby="secondaryText">
$i18n{androidAppsEnable}
diff --git a/chromium/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.html b/chromium/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.html
index 58cc44902e0..771918fe76a 100644
--- a/chromium/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.html
+++ b/chromium/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.html
@@ -21,7 +21,7 @@
</template>
<template is="dom-if" if="[[allowRemove_(prefs.arc.enabled.*)]]">
- <div id="remove" class="settings-box" actionable on-tap="onRemoveTap_">
+ <div id="remove" class="settings-box" actionable on-click="onRemoveTap_">
<div class="start">$i18n{androidAppsRemove}</div>
<button class="subpage-arrow" is="paper-icon-button-light"
aria-label="$i18n{androidAppsRemove}">
@@ -37,11 +37,11 @@
<div slot="body" inner-h-t-m-l="[[dialogBody_]]"></div>
<div slot="button-container">
<paper-button class="cancel-button"
- on-tap="onConfirmDisableDialogCancel_">
+ on-click="onConfirmDisableDialogCancel_">
$i18n{cancel}
</paper-button>
<paper-button class="action-button"
- on-tap="onConfirmDisableDialogConfirm_">
+ on-click="onConfirmDisableDialogConfirm_">
$i18n{androidAppsDisableDialogRemove}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/android_apps_page/android_settings_element.html b/chromium/chrome/browser/resources/settings/android_apps_page/android_settings_element.html
index 17b6e861109..eb65859100f 100644
--- a/chromium/chrome/browser/resources/settings/android_apps_page/android_settings_element.html
+++ b/chromium/chrome/browser/resources/settings/android_apps_page/android_settings_element.html
@@ -12,7 +12,7 @@
<style include="settings-shared"></style>
<div id="manageApps" class="settings-box first"
on-keydown="onManageAndroidAppsKeydown_"
- on-tap="onManageAndroidAppsTap_" actionable>
+ on-click="onManageAndroidAppsTap_" actionable>
<div class="start">
<div>$i18n{androidAppsManageApps}</div>
</div>
diff --git a/chromium/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html b/chromium/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html
index 3b014cc73a3..d82f8743fac 100644
--- a/chromium/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html
+++ b/chromium/chrome/browser/resources/settings/appearance_page/appearance_fonts_page.html
@@ -121,7 +121,7 @@
</div>
<template is="dom-if" if="[[!isGuest_]]">
<div class="settings-box two-line" id="advancedButton"
- on-tap="openAdvancedExtension_" actionable>
+ on-click="openAdvancedExtension_" actionable>
<div class="start">
$i18n{advancedFontSettings}
<div class="secondary" id="advancedButtonSublabel">
diff --git a/chromium/chrome/browser/resources/settings/appearance_page/appearance_page.html b/chromium/chrome/browser/resources/settings/appearance_page/appearance_page.html
index 7ac5845998d..6667159ff4e 100644
--- a/chromium/chrome/browser/resources/settings/appearance_page/appearance_page.html
+++ b/chromium/chrome/browser/resources/settings/appearance_page/appearance_page.html
@@ -60,7 +60,7 @@
<button icon-class="icon-external" id="wallpaperButton"
is="cr-link-row"
hidden="[[!pageVisibility.setWallpaper]]"
- on-tap="openWallpaperManager_"
+ on-click="openWallpaperManager_"
label="$i18n{setWallpaper}" sub-label="$i18n{openWallpaperApp}"
disabled="[[isWallpaperPolicyControlled_]]">
<template is="dom-if" if="[[isWallpaperPolicyControlled_]]">
@@ -76,11 +76,11 @@
<button class="first" icon-class="icon-external" is="cr-link-row"
hidden="[[!pageVisibility.setTheme]]"
label="$i18n{themes}" sub-label="[[themeSublabel_]]"
- on-tap="openThemeUrl_"></button>
+ on-click="openThemeUrl_"></button>
<if expr="not is_linux or chromeos">
<template is="dom-if" if="[[prefs.extensions.theme.id.value]]">
<div class="separator"></div>
- <paper-button id="useDefault" on-tap="onUseDefaultTap_"
+ <paper-button id="useDefault" on-click="onUseDefaultTap_"
class="secondary-button">
$i18n{resetToDefaultTheme}
</paper-button>
@@ -94,14 +94,14 @@
<div class="separator"></div>
<template is="dom-if" if="[[showUseClassic_(
prefs.extensions.theme.id.value, useSystemTheme_)]]" restamp>
- <paper-button id="useDefault" on-tap="onUseDefaultTap_"
+ <paper-button id="useDefault" on-click="onUseDefaultTap_"
class="secondary-button">
$i18n{useClassicTheme}
</paper-button>
</template>
<template is="dom-if" if="[[showUseSystem_(
prefs.extensions.theme.id.value, useSystemTheme_)]]" restamp>
- <paper-button id="useSystem" on-tap="onUseSystemTap_"
+ <paper-button id="useSystem" on-click="onUseSystemTap_"
class="secondary-button">
$i18n{useSystemTheme}
</paper-button>
@@ -168,7 +168,7 @@
</div>
<button class="hr" is="cr-link-row"
icon-class="subpage-arrow" id="customize-fonts-subpage-trigger"
- label="$i18n{customizeFonts}" on-tap="onCustomizeFontsTap_">
+ label="$i18n{customizeFonts}" on-click="onCustomizeFontsTap_">
</button>
<div class="settings-box" hidden="[[!pageVisibility.pageZoom]]">
<div id="pageZoom" class="start">$i18n{pageZoom}</div>
diff --git a/chromium/chrome/browser/resources/settings/basic_page/basic_page.html b/chromium/chrome/browser/resources/settings/basic_page/basic_page.html
index 94029ec4769..80ab0b65c36 100644
--- a/chromium/chrome/browser/resources/settings/basic_page/basic_page.html
+++ b/chromium/chrome/browser/resources/settings/basic_page/basic_page.html
@@ -46,7 +46,7 @@
--paper-button: {
text-transform: none;
}
- @apply(--settings-actionable);
+ @apply --settings-actionable;
align-items: center;
display: flex;
margin-bottom: 3px;
@@ -56,7 +56,7 @@
}
#secondaryUserBanner {
- @apply(--shadow-elevation-2dp);
+ @apply --shadow-elevation-2dp;
align-items: center;
background-color: white;
border-radius: 2px;
diff --git a/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_device_list_item.html b/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_device_list_item.html
index b7134f661f3..c3ddc427f33 100644
--- a/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_device_list_item.html
+++ b/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_device_list_item.html
@@ -33,14 +33,15 @@
<span hidden$="[[!device.connecting]]">$i18n{bluetoothConnecting}</span>
<div hidden$="[[!device.paired]]">
<button is="paper-icon-button-light" class="icon-more-vert"
- on-tap="onMenuButtonTap_" tabindex$="[[tabindex]]"
+ on-click="onMenuButtonTap_" tabindex$="[[tabindex]]"
title="$i18n{moreActions}" on-keydown="ignoreEnterKey_">
</button>
<dialog id="dotsMenu" is="cr-action-menu">
- <button class="dropdown-item" on-tap="onConnectActionTap_">
+ <button slot="item" class="dropdown-item"
+ on-click="onConnectActionTap_">
[[getConnectActionText_(device.connected)]]
</button>
- <button class="dropdown-item" on-tap="onRemoveTap_">
+ <button slot="item" class="dropdown-item" on-click="onRemoveTap_">
$i18n{bluetoothRemove}
</button>
</dialog>
diff --git a/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html b/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html
index fb27688ce72..1e386deda42 100644
--- a/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html
+++ b/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_page.html
@@ -21,7 +21,7 @@
focus-config="[[focusConfig_]]">
<neon-animatable route-path="default">
<div id="bluetoothDevices"
- class="settings-box two-line" actionable on-tap="onTap_">
+ class="settings-box two-line" actionable on-click="onTap_">
<iron-icon icon="[[getIcon_(bluetoothToggleState_)]]"></iron-icon>
<div class="middle">
$i18n{bluetoothPageTitle}
@@ -36,7 +36,7 @@
</cr-policy-pref-indicator>
<template is="dom-if" if="[[bluetoothToggleState_]]">
<button class="subpage-arrow" is="paper-icon-button-light"
- on-tap="onSubpageArrowTap_"
+ on-click="onSubpageArrowTap_"
aria-label="$i18n{bluetoothPageTitle}"
aria-describedby="bluetoothSecondary">
</button>
@@ -46,7 +46,7 @@
checked="{{bluetoothToggleState_}}"
disabled$=
"[[!isToggleEnabled_(adapterState_, stateChangeInProgress_)]]"
- on-tap="stopTap_"
+ on-click="stopTap_"
aria-label="$i18n{bluetoothToggleA11yLabel}">
</paper-toggle-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html b/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html
index 6ca9d1b16fa..018a505ac33 100644
--- a/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html
+++ b/chromium/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html
@@ -15,7 +15,7 @@
<template>
<style include="settings-shared iron-flex">
.container {
- @apply(--settings-list-frame-padding);
+ @apply --settings-list-frame-padding;
display: flex;
flex-direction: column;
min-height: 10px;
@@ -27,7 +27,7 @@
}
paper-spinner-lite {
- @apply(--cr-icon-height-width);
+ @apply --cr-icon-height-width;
}
#onOff {
@@ -39,7 +39,7 @@
}
</style>
- <div class="settings-box first" actionable on-tap="onEnableTap_">
+ <div class="settings-box first" actionable on-click="onEnableTap_">
<div id="onOff" class="start" on$="[[bluetoothToggleState]]">
[[getOnOffString_(bluetoothToggleState,
'$i18nPolymer{deviceOn}', '$i18nPolymer{deviceOff}')]]
@@ -48,7 +48,7 @@
checked="{{bluetoothToggleState}}"
disabled$="[[!isToggleEnabled_(adapterState, stateChangeInProgress)]]"
aria-label="$i18n{bluetoothToggleA11yLabel}"
- on-tap="stopTap_">
+ on-click="stopTap_">
</paper-toggle-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/change_password_page/change_password_page.html b/chromium/chrome/browser/resources/settings/change_password_page/change_password_page.html
index deb22750820..ceaae386226 100644
--- a/chromium/chrome/browser/resources/settings/change_password_page/change_password_page.html
+++ b/chromium/chrome/browser/resources/settings/change_password_page/change_password_page.html
@@ -46,7 +46,7 @@
</div>
<div class="separator"></div>
<paper-button class="primary-button" id="changePassword"
- on-tap="changePassword_">
+ on-click="changePassword_">
$i18n{changePasswordPageButton}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html b/chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html
index 245dbdd0765..d0cb57647aa 100644
--- a/chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html
+++ b/chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.html
@@ -84,11 +84,11 @@
</iron-icon>
</div>
<div class="start">
- <div>[[title_]]</div>
+ <div role="status">[[title_]]</div>
<div hidden="[[!showExplanation_]]">
<span class="secondary">[[explanation_]]</span>
<a id="learn-more" href="$i18n{chromeCleanupLearnMoreUrl}"
- on-tap="learnMore_" target="_blank"
+ on-click="learnMore_" target="_blank"
hidden="[[!showLearnMore_]]">
$i18n{learnMore}
</a>
@@ -97,7 +97,7 @@
<template is="dom-if" if="[[showActionButton_]]">
<div class="separator"></div>
<paper-button id="action-button" class="primary-button"
- on-tap="proceed_">
+ on-click="proceed_">
[[actionButtonLabel_]]
</paper-button>
</template>
@@ -112,7 +112,7 @@
on-settings-boolean-control-change="changeLogsPermission_">
</settings-toggle-button>
<div id="show-items-button" class="settings-box" actionable
- on-tap="toggleExpandButton_" hidden="[[!showItemsToRemove_]]">
+ on-click="toggleExpandButton_" hidden="[[!showItemsToRemove_]]">
<div class="start">[[showItemsLinkLabel_]]</div>
<cr-expand-button expanded="{{itemsToRemoveSectionExpanded_}}"
alt="[[showItemsLinkLabel_]]">
diff --git a/chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.js b/chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.js
index ec8cc5aa7cc..7a31f0eef1d 100644
--- a/chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.js
+++ b/chromium/chrome/browser/resources/settings/chrome_cleanup_page/chrome_cleanup_page.js
@@ -767,14 +767,14 @@ Polymer({
},
DISMISS_CLEANUP_SUCCESS: {
- label: this.i18n('chromeCleanupDoneButtonLabel'),
+ label: this.i18n('done'),
doAction: this.dismiss_.bind(
this,
settings.ChromeCleanupDismissSource.CLEANUP_SUCCESS_DONE_BUTTON),
},
DISMISS_CLEANUP_FAILURE: {
- label: this.i18n('chromeCleanupDoneButtonLabel'),
+ label: this.i18n('done'),
doAction: this.dismiss_.bind(
this,
settings.ChromeCleanupDismissSource.CLEANUP_FAILURE_DONE_BUTTON),
diff --git a/chromium/chrome/browser/resources/settings/chrome_cleanup_page/items_to_remove_list.html b/chromium/chrome/browser/resources/settings/chrome_cleanup_page/items_to_remove_list.html
index 6f8b7eb1f77..21e3bb1cedc 100644
--- a/chromium/chrome/browser/resources/settings/chrome_cleanup_page/items_to_remove_list.html
+++ b/chromium/chrome/browser/resources/settings/chrome_cleanup_page/items_to_remove_list.html
@@ -20,18 +20,34 @@
color: var(--google-blue-500);
cursor: pointer;
}
+
+ #remaining-list {
+ margin-top: -13px;
+ }
</style>
<div id="title" class="secondary" hidden="[[!titleVisible]]">
[[title]]
</div>
- <ul id="list" class="secondary">
- <template is="dom-repeat" items="[[visibleItems_]]">
+ <ul class="secondary">
+ <template is="dom-repeat" items="[[initialItems_]]">
<li class="visible-item">[[item]]</li>
</template>
- <li id="more-items-link" hidden="[[expanded_]]" on-tap="expandList_">
+ <li id="more-items-link" hidden="[[expanded_]]" on-click="expandList_">
[[moreItemsLinkText_]]
</li>
</ul>
+ <!-- Remaining items are kept in a separate <ul> element so that screen
+ readers don't get confused when the list is expanded. If new items are
+ simply added to the first <ul> element, the first new item (which will
+ replace the "N more" link), will be skipped by the reader. As a
+ consequence, visual impaired users will only have a chance to inspect
+ that item if they move up on the list, which can't be considered an
+ expected action. -->
+ <ul id="remaining-list" hidden="[[!expanded_]]" class="secondary">
+ <template is="dom-repeat" items="[[remainingItems_]]">
+ <li class$="[[remainingItemsClass_(expanded_)]]">[[item]]</li>
+ </template>
+ </ul>
</template>
<script src="items_to_remove_list.js"></script>
</dom-module>
diff --git a/chromium/chrome/browser/resources/settings/chrome_cleanup_page/items_to_remove_list.js b/chromium/chrome/browser/resources/settings/chrome_cleanup_page/items_to_remove_list.js
index e628437c50e..8b9c803fb3b 100644
--- a/chromium/chrome/browser/resources/settings/chrome_cleanup_page/items_to_remove_list.js
+++ b/chromium/chrome/browser/resources/settings/chrome_cleanup_page/items_to_remove_list.js
@@ -71,11 +71,21 @@ Polymer({
},
/**
- * The list of items to actually present on the card. If |expanded_|, then
- * it's the same as |itemsToShow|.
+ * The items to be shown to the user the first time this component is
+ * rendered. If |initiallyExpanded| is true, then it includes all items
+ * from |itemsToShow|. Otherwise, it contains the first
+ * |CHROME_CLEANUP_DEFAULT_ITEMS_TO_SHOW| items.
* @private {?Array<string>}
*/
- visibleItems_: Array,
+ initialItems_: Array,
+
+ /**
+ * The remaining items to be presented that are not included in
+ * |initialItems_|. Items in this list are only shown to the user if
+ * |expanded_| is true.
+ * @private {?Array<string>}
+ */
+ remainingItems_: Array,
/**
* The text for the "show more" link available if not all files are visible
@@ -93,7 +103,6 @@ Polymer({
/** @private */
expandList_: function() {
this.expanded_ = true;
- this.visibleItems_ = this.itemsToShow;
this.moreItemsLinkText_ = '';
},
@@ -118,25 +127,34 @@ Polymer({
updateVisibleState_: function(itemsToShow, initiallyExpanded) {
// Start expanded if there are less than
// |settings.CHROME_CLEANUP_DEFAULT_ITEMS_TO_SHOW| items to show.
- this.expanded_ = this.initiallyExpanded ||
- this.itemsToShow.length <=
- settings.CHROME_CLEANUP_DEFAULT_ITEMS_TO_SHOW;
+ this.expanded_ = initiallyExpanded ||
+ itemsToShow.length <= settings.CHROME_CLEANUP_DEFAULT_ITEMS_TO_SHOW;
if (this.expanded_) {
- this.visibleItems_ = this.itemsToShow;
+ this.initialItems_ = itemsToShow;
+ this.remainingItems_ = [];
this.moreItemsLinkText_ = '';
return;
}
- this.visibleItems_ = this.itemsToShow.slice(
- 0, settings.CHROME_CLEANUP_DEFAULT_ITEMS_TO_SHOW - 1);
+ this.initialItems_ =
+ itemsToShow.slice(0, settings.CHROME_CLEANUP_DEFAULT_ITEMS_TO_SHOW - 1);
+ this.remainingItems_ =
+ itemsToShow.slice(settings.CHROME_CLEANUP_DEFAULT_ITEMS_TO_SHOW - 1);
const browserProxy = settings.ChromeCleanupProxyImpl.getInstance();
- browserProxy
- .getMoreItemsPluralString(
- this.itemsToShow.length - this.visibleItems_.length)
+ browserProxy.getMoreItemsPluralString(this.remainingItems_.length)
.then(linkText => {
this.moreItemsLinkText_ = linkText;
});
},
+
+ /**
+ * Returns the class for the <li> elements that correspond to the items hidden
+ * in the default view.
+ * @param {boolean} expanded
+ */
+ remainingItemsClass_: function(expanded) {
+ return expanded ? 'visible-item' : 'hidden-item';
+ },
});
diff --git a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
index ec3fad23b77..dbbc94a3556 100644
--- a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
+++ b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
@@ -2,8 +2,10 @@
<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-pages/iron-pages.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-tabs/paper-tabs.html">
<link rel="import" href="../i18n_setup.html">
<link rel="import" href="clear_browsing_data_browser_proxy.html">
<link rel="import" href="history_deletion_dialog.html">
@@ -12,12 +14,29 @@
<link rel="import" href="../controls/settings_dropdown_menu.html">
<link rel="import" href="../icons.html">
<link rel="import" href="../settings_shared_css.html">
+<link rel="import" href="../settings_vars_css.html">
-<!-- This file is forked as clear_browsing_data_dialog_tabs.html until the new
- CBD UI is launched. -->
<dom-module id="settings-clear-browsing-data-dialog">
<template>
<style include="settings-shared">
+ :host {
+ /* Fixed height to allow multiple tabs with different height.
+ * The last entry in the advanced tab should show half an entry.
+ * crbug.com/652027 */
+ --body-container-height: 322px;
+ }
+
+ #clearBrowsingDataDialog {
+ --cr-dialog-top-container-min-height: 42px;
+ --cr-dialog-title: {
+ padding-bottom: 8px;
+ };
+ --cr-dialog-body-container: {
+ border-top: 1px solid var(--paper-grey-300);
+ height: var(--body-container-height);
+ };
+ }
+
#clearBrowsingDataDialog:not(.fully-rendered) {
visibility: hidden;
}
@@ -26,6 +45,16 @@
color: var(--paper-grey-600);
}
+ #clearBrowsingDataDialog [slot=body] {
+ padding-top: 8px;
+ }
+
+ #importantSitesDialog {
+ --cr-dialog-body-container: {
+ height: var(--body-container-height);
+ };
+ }
+
.row {
align-items: center;
display: flex;
@@ -43,56 +72,36 @@
--settings-row-two-line-min-height: 48px;
--settings-checkbox-label: {
line-height: 1.25rem;
- };
- }
-
- #generalFooter {
- margin: 0;
- min-height: 18px;
- }
-
- #generalFooter iron-icon {
- height: 18px;
- padding: 1px;
- width: 18px;
- }
-
- #googleFooter {
- margin: 0 0 0.8em 0;
- min-height: 16px;
- }
-
- #googleFooter iron-icon {
- height: 16px;
- padding: 2px;
- width: 16px;
- }
-
- [slot=footer] iron-icon {
- margin: auto;
+ }
}
- .clear-browsing-data-footer {
- -webkit-padding-start: 4px;
- align-items: flex-start;
- display: flex;
- line-height: 1.538em; /* 20px/13px */
+ #basic-tab settings-checkbox + settings-checkbox {
+ --settings-checkbox-margin-top: 12px;
}
- .clear-browsing-data-footer .footer-text {
- -webkit-margin-start: 16px;
+ paper-tabs {
+ --paper-tabs-selection-bar-color: var(--google-blue-500);
+ --paper-tabs: {
+ font-size: 100%;
+ height: 40px;
+ }
}
- .clear-browsing-data-footer iron-icon {
- flex-shrink: 0;
+ paper-tab {
+ --paper-tab-content: {
+ color: var(--google-blue-700);
+ };
+ --paper-tab-content-unselected: {
+ opacity: 1;
+ color: var(--paper-grey-600);
+ };
}
- .clear-browsing-data-footer a {
- text-decoration: none;
+ .time-range-row {
+ margin-bottom: 12px;
}
- #clearFrom {
- -webkit-margin-start: 0.5em;
+ .time-range-select {
/* Adjust for md-select-underline and 1px additional bottom padding
* to keep md-select's text (without the underline) aligned with
* neighboring text that does not have an underline. */
@@ -103,115 +112,153 @@
font-size: calc(13 / 15 * 100%);
padding-top: 8px;
}
-
- /* Cap the height on smaller screens to avoid unfavorable clipping.
- * Replace the bottom margin with padding to avoid the gap between
- * the scrollbar and the bottom separator. */
- @media all and (max-height: 724px) {
- #clearBrowsingDataDialog {
- /* crbug.com/652027: Show four and a *half* items in the list. */
- --cr-dialog-body-container: {
- max-height: 280px;
- };
- }
- }
</style>
<dialog is="cr-dialog" id="clearBrowsingDataDialog"
on-close="onClearBrowsingDataDialogClose_"
- close-text="$i18n{close}" ignore-popstate>
- <div slot="title">$i18n{clearBrowsingData}</div>
+ close-text="$i18n{close}" ignore-popstate has-tabs>
+ <div slot="title">
+ <div>$i18n{clearBrowsingData}</div>
+ </div>
+ <div slot="header">
+ <paper-tabs noink on-selected-changed="recordTabChange_"
+ selected="{{prefs.browser.last_clear_browsing_data_tab.value}}">
+ <paper-tab>$i18n{basicPageTitle}</paper-tab>
+ <paper-tab>$i18n{advancedPageTitle}</paper-tab>
+ </paper-tabs>
+ </div>
<div slot="body">
- <div class="row">
- $i18n{clearFollowingItemsFrom}
- <settings-dropdown-menu id="clearFrom"
- label="$i18n{clearFollowingItemsFrom}"
- pref="{{prefs.browser.clear_data.time_period}}"
- menu-options="[[clearFromOptions_]]">
- </settings-dropdown-menu>
- </div>
- <!-- Note: whether these checkboxes are checked are ignored if deleting
- history is disabled (i.e. supervised users, policy), so it's OK to
- have a hidden checkbox that's also checked (as the C++ accounts for
- whether a user is allowed to delete history independently). -->
- <settings-checkbox id="browsingCheckbox" class="browsing-data-checkbox"
- pref="{{prefs.browser.clear_data.browsing_history}}"
- label="$i18n{clearBrowsingHistory}"
- sub-label="[[counters_.browsing_history]]"
- disabled="[[clearingInProgress_]]"
- hidden="[[isSupervised_]]">
- </settings-checkbox>
- <settings-checkbox id="downloadCheckbox" class="browsing-data-checkbox"
- pref="{{prefs.browser.clear_data.download_history}}"
- label="$i18n{clearDownloadHistory}"
- sub-label="[[counters_.download_history]]"
- disabled="[[clearingInProgress_]]"
- hidden="[[isSupervised_]]">
- </settings-checkbox>
- <settings-checkbox id="cacheCheckbox" class="browsing-data-checkbox"
- pref="{{prefs.browser.clear_data.cache}}"
- label="$i18n{clearCache}"
- sub-label="[[counters_.cache]]"
- disabled="[[clearingInProgress_]]">
- </settings-checkbox>
- <settings-checkbox id="cookiesCheckbox" class="browsing-data-checkbox"
- pref="{{prefs.browser.clear_data.cookies}}"
- label="$i18n{clearCookies}"
- sub-label="$i18n{clearCookiesCounter}"
- disabled="[[clearingInProgress_]]">
- </settings-checkbox>
- <settings-checkbox class="browsing-data-checkbox"
- pref="{{prefs.browser.clear_data.passwords}}"
- label="$i18n{clearPasswords}"
- sub-label="[[counters_.passwords]]"
- disabled="[[clearingInProgress_]]">
- </settings-checkbox>
- <settings-checkbox class="browsing-data-checkbox"
- pref="{{prefs.browser.clear_data.form_data}}"
- label="$i18n{clearFormData}"
- sub-label="[[counters_.form_data]]"
- disabled="[[clearingInProgress_]]">
- </settings-checkbox>
- <settings-checkbox class="browsing-data-checkbox"
- pref="{{prefs.browser.clear_data.hosted_apps_data}}"
- label="$i18n{clearHostedAppData}"
- sub-label="[[counters_.hosted_apps_data]]"
- disabled="[[clearingInProgress_]]">
- </settings-checkbox>
- <settings-checkbox class="browsing-data-checkbox"
- pref="{{prefs.browser.clear_data.media_licenses}}"
- label="$i18n{clearMediaLicenses}"
- sub-label="[[counters_.media_licenses]]"
- disabled="[[clearingInProgress_]]">
- </settings-checkbox>
+ <iron-pages id="tabs"
+ selected="[[prefs.browser.last_clear_browsing_data_tab.value]]">
+ <div id="basic-tab">
+ <div class="row time-range-row">
+ <span class="time-range-label">
+ $i18n{clearTimeRange}
+ </span>
+ <settings-dropdown-menu id="clearFromBasic"
+ class="time-range-select"
+ label="$i18n{clearTimeRange}"
+ pref="{{prefs.browser.clear_data.time_period_basic}}"
+ menu-options="[[clearFromOptions_]]">
+ </settings-dropdown-menu>
+ </div>
+ <!-- Note: whether these checkboxes are checked are ignored if
+ deleting history is disabled (i.e. supervised users, policy),
+ so it's OK to have a hidden checkbox that's also checked (as
+ the C++ accounts for whether a user is allowed to delete
+ history independently). -->
+ <settings-checkbox id="browsingCheckboxBasic"
+ pref="{{prefs.browser.clear_data.browsing_history_basic}}"
+ label="$i18n{clearBrowsingHistory}"
+ sub-label-html="[[browsingCheckboxLabel_(
+ isSignedIn_, isSyncingHistory_,
+ '$i18nPolymer{clearBrowsingHistorySummary}',
+ '$i18nPolymer{clearBrowsingHistorySummarySignedIn}',
+ '$i18nPolymer{clearBrowsingHistorySummarySynced}')]]"
+ disabled="[[clearingInProgress_]]"
+ hidden="[[isSupervised_]]">
+ </settings-checkbox>
+ <settings-checkbox id="cookiesCheckboxBasic"
+ class="cookies-checkbox"
+ pref="{{prefs.browser.clear_data.cookies_basic}}"
+ label="$i18n{clearCookies}"
+ sub-label="$i18n{clearCookiesSummary}"
+ disabled="[[clearingInProgress_]]">
+ </settings-checkbox>
+ <settings-checkbox id="cacheCheckboxBasic"
+ class="cache-checkbox"
+ pref="{{prefs.browser.clear_data.cache_basic}}"
+ label="$i18n{clearCache}"
+ sub-label="[[counters_.cache_basic]]"
+ disabled="[[clearingInProgress_]]">
+ </settings-checkbox>
+ </div>
+ <div id="advanced-tab">
+ <div class="row time-range-row">
+ <span class="time-range-label">
+ $i18n{clearTimeRange}
+ </span>
+ <settings-dropdown-menu id="clearFrom"
+ class="time-range-select"
+ label="$i18n{clearTimeRange}"
+ pref="{{prefs.browser.clear_data.time_period}}"
+ menu-options="[[clearFromOptions_]]">
+ </settings-dropdown-menu>
+ </div>
+ <settings-checkbox id="browsingCheckbox"
+ pref="{{prefs.browser.clear_data.browsing_history}}"
+ label="$i18n{clearBrowsingHistory}"
+ sub-label="[[counters_.browsing_history]]"
+ disabled="[[clearingInProgress_]]"
+ hidden="[[isSupervised_]]">
+ </settings-checkbox>
+ <settings-checkbox id="downloadCheckbox"
+ pref="{{prefs.browser.clear_data.download_history}}"
+ label="$i18n{clearDownloadHistory}"
+ sub-label="[[counters_.download_history]]"
+ disabled="[[clearingInProgress_]]"
+ hidden="[[isSupervised_]]">
+ </settings-checkbox>
+ <settings-checkbox id="cookiesCheckbox"
+ class="cookies-checkbox"
+ pref="{{prefs.browser.clear_data.cookies}}"
+ label="$i18n{clearCookies}"
+ sub-label="[[counters_.cookies]]"
+ disabled="[[clearingInProgress_]]">
+ </settings-checkbox>
+ <settings-checkbox id="cacheCheckbox"
+ class="cache-checkbox"
+ pref="{{prefs.browser.clear_data.cache}}"
+ label="$i18n{clearCache}"
+ sub-label="[[counters_.cache]]"
+ disabled="[[clearingInProgress_]]">
+ </settings-checkbox>
+ <settings-checkbox
+ pref="{{prefs.browser.clear_data.passwords}}"
+ label="$i18n{clearPasswords}"
+ sub-label="[[counters_.passwords]]"
+ disabled="[[clearingInProgress_]]">
+ </settings-checkbox>
+ <settings-checkbox
+ pref="{{prefs.browser.clear_data.form_data}}"
+ label="$i18n{clearFormData}"
+ sub-label="[[counters_.form_data]]"
+ disabled="[[clearingInProgress_]]">
+ </settings-checkbox>
+ <settings-checkbox
+ pref="{{prefs.browser.clear_data.site_settings}}"
+ label="[[siteSettingsLabel_(
+ '$i18nPolymer{siteSettings}',
+ '$i18nPolymer{contentSettings}')]]"
+ sub-label="[[counters_.site_settings]]"
+ disabled="[[clearingInProgress_]]">
+ </settings-checkbox>
+ <settings-checkbox
+ pref="{{prefs.browser.clear_data.hosted_apps_data}}"
+ label="$i18n{clearHostedAppData}"
+ sub-label="[[counters_.hosted_apps_data]]"
+ disabled="[[clearingInProgress_]]">
+ </settings-checkbox>
+ <settings-checkbox
+ pref="{{prefs.browser.clear_data.media_licenses}}"
+ label="$i18n{clearMediaLicenses}"
+ sub-label="[[counters_.media_licenses]]"
+ disabled="[[clearingInProgress_]]">
+ </settings-checkbox>
+ </div>
+ </iron-pages>
</div>
<div slot="button-container">
<paper-spinner-lite active="[[clearingInProgress_]]">
</paper-spinner-lite>
<paper-button class="cancel-button" disabled="[[clearingInProgress_]]"
- on-tap="onCancelTap_">$i18n{cancel}</paper-button>
+ on-click="onCancelTap_">$i18n{cancel}</paper-button>
<paper-button id="clearBrowsingDataConfirm"
class="action-button" disabled="[[clearingInProgress_]]"
- on-tap="onClearBrowsingDataTap_">
- $i18n{clearBrowsingData}
+ on-click="onClearBrowsingDataTap_">
+ $i18n{clearData}
</paper-button>
</div>
- <div slot="footer">
- <div id="googleFooter" class="clear-browsing-data-footer">
- <iron-icon icon="settings:googleg"></iron-icon>
- <div class="footer-text">$i18nRaw{otherFormsOfBrowsingHistory}</div>
- </div>
- <div id="generalFooter" class="clear-browsing-data-footer">
- <iron-icon icon="settings:info"></iron-icon>
- <div class="footer-text">
- <span id="syncedDataSentence">$i18n{clearsSyncedData}</span>
- <span>$i18n{warnAboutNonClearedData}</span>
- <a id="clear-browser-data-old-learn-more-link"
- href="$i18n{clearBrowsingDataLearnMoreUrl}"
- target="_blank">$i18n{learnMore}</a>
- </div>
- </div>
- </div>
</dialog>
<template is="dom-if" if="[[showImportantSitesDialog_]]">
@@ -220,13 +267,12 @@
<div slot="title">
$i18n{clearBrowsingData}
<div class="secondary">
- <template is="dom-if"
- if="[[!prefs.browser.clear_data.cache.value]]">
+ <span hidden$="[[showImportantSitesCacheSubtitle_]]">
$i18n{importantSitesSubtitleCookies}
- </template>
- <template is="dom-if" if="[[prefs.browser.clear_data.cache.value]]">
+ </span>
+ <span hidden$="[[!showImportantSitesCacheSubtitle_]]">
$i18n{importantSitesSubtitleCookiesAndCache}
- </template>
+ </span>
</div>
</div>
<div slot="body">
@@ -243,10 +289,10 @@
<paper-spinner-lite active="[[clearingInProgress_]]">
</paper-spinner-lite>
<paper-button class="cancel-button" disabled="[[clearingInProgress_]]"
- on-tap="onImportantSitesCancelTap_">$i18n{cancel}</paper-button>
+ on-click="onImportantSitesCancelTap_">$i18n{cancel}</paper-button>
<paper-button id="importantSitesConfirm"
class="action-button" disabled="[[clearingInProgress_]]"
- on-tap="onImportantSitesConfirmTap_">
+ on-click="onImportantSitesConfirmTap_">
$i18n{importantSitesConfirm}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.js b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.js
index 60ec1ccbf33..97beb3c7906 100644
--- a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.js
+++ b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.js
@@ -3,16 +3,13 @@
// found in the LICENSE file.
/**
- * @fileoverview 'settings-clear-browsing-data-dialog' allows the user to delete
- * browsing data that has been cached by Chromium.
- *
- * This file is forked as clear_browsing_data_dialog_tabs.js until the new
- * CBD UI is launched.
+ * @fileoverview 'settings-clear-browsing-data-dialog' allows the user to
+ * delete browsing data that has been cached by Chromium.
*/
Polymer({
is: 'settings-clear-browsing-data-dialog',
- behaviors: [WebUIListenerBehavior],
+ behaviors: [WebUIListenerBehavior, settings.RouteObserverBehavior],
properties: {
/**
@@ -45,11 +42,11 @@ Polymer({
readOnly: true,
type: Array,
value: [
- {value: 0, name: loadTimeData.getString('clearDataHour')},
- {value: 1, name: loadTimeData.getString('clearDataDay')},
- {value: 2, name: loadTimeData.getString('clearDataWeek')},
- {value: 3, name: loadTimeData.getString('clearData4Weeks')},
- {value: 4, name: loadTimeData.getString('clearDataEverything')},
+ {value: 0, name: loadTimeData.getString('clearPeriodHour')},
+ {value: 1, name: loadTimeData.getString('clearPeriod24Hours')},
+ {value: 2, name: loadTimeData.getString('clearPeriod7Days')},
+ {value: 3, name: loadTimeData.getString('clearPeriod4Weeks')},
+ {value: 4, name: loadTimeData.getString('clearPeriodEverything')},
],
},
@@ -73,6 +70,18 @@ Polymer({
value: false,
},
+ /** @private */
+ isSignedIn_: {
+ type: Boolean,
+ value: false,
+ },
+
+ /** @private */
+ isSyncingHistory_: {
+ type: Boolean,
+ value: false,
+ },
+
/** @private {!Array<ImportantSite>} */
importantSites_: {
type: Array,
@@ -90,7 +99,25 @@ Polymer({
},
/** @private */
- showImportantSitesDialog_: {type: Boolean, value: false},
+ showImportantSitesDialog_: {
+ type: Boolean,
+ value: false,
+ },
+
+ /** @private */
+ showImportantSitesCacheSubtitle_: {
+ type: Boolean,
+ value: false,
+ },
+
+ /**
+ * Time in ms, when the dialog was opened.
+ * @private
+ */
+ dialogOpenedTime_: {
+ type: Number,
+ value: 0,
+ }
},
/** @private {settings.ClearBrowsingDataBrowserProxy} */
@@ -98,7 +125,6 @@ Polymer({
/** @override */
ready: function() {
- this.$.clearFrom.menuOptions = this.clearFromOptions_;
this.addWebUIListener(
'update-sync-state', this.updateSyncState_.bind(this));
this.addWebUIListener(
@@ -109,6 +135,7 @@ Polymer({
attached: function() {
this.browserProxy_ =
settings.ClearBrowsingDataBrowserProxyImpl.getInstance();
+ this.dialogOpenedTime_ = Date.now();
this.browserProxy_.initialize().then(() => {
this.$.clearBrowsingDataDialog.showModal();
});
@@ -121,21 +148,67 @@ Polymer({
},
/**
- * Updates the footer to show only those sentences that are relevant to this
- * user.
+ * Record visits to the CBD dialog.
+ *
+ * settings.RouteObserverBehavior
+ * @param {!settings.Route} currentRoute
+ * @protected
+ */
+ currentRouteChanged: function(currentRoute) {
+ if (currentRoute == settings.routes.CLEAR_BROWSER_DATA) {
+ chrome.metricsPrivate.recordUserAction('ClearBrowsingData_DialogCreated');
+ this.dialogOpenedTime_ = Date.now();
+ }
+ },
+
+ /**
+ * Updates the history description to show the relevant information
+ * depending on sync and signin state.
+ *
* @param {boolean} signedIn Whether the user is signed in.
- * @param {boolean} syncing Whether the user is syncing data.
- * @param {boolean} otherFormsOfBrowsingHistory Whether the user has other
- * forms of browsing history in their account.
+ * @param {boolean} syncing Whether the user is syncing history.
* @private
*/
- updateSyncState_: function(signedIn, syncing, otherFormsOfBrowsingHistory) {
- this.$.googleFooter.hidden = !otherFormsOfBrowsingHistory;
- this.$.syncedDataSentence.hidden = !syncing;
+ updateSyncState_: function(signedIn, syncing) {
+ this.isSignedIn_ = signedIn;
+ this.isSyncingHistory_ = syncing;
this.$.clearBrowsingDataDialog.classList.add('fully-rendered');
},
/**
+ * Choose a summary checkbox label.
+ * @param {boolean} isSignedIn
+ * @param {boolean} isSyncingHistory
+ * @param {string} historySummary
+ * @param {string} historySummarySigned
+ * @param {string} historySummarySynced
+ * @return {string}
+ * @private
+ */
+ browsingCheckboxLabel_: function(
+ isSignedIn, isSyncingHistory, historySummary, historySummarySigned,
+ historySummarySynced) {
+ if (isSyncingHistory) {
+ return historySummarySynced;
+ } else if (isSignedIn) {
+ return historySummarySigned;
+ }
+ return historySummary;
+ },
+
+ /**
+ * Choose a content/site settings label.
+ * @param {string} siteSettings
+ * @param {string} contentSettings
+ * @return {string}
+ * @private
+ */
+ siteSettingsLabel_: function(siteSettings, contentSettings) {
+ return loadTimeData.getBoolean('enableSiteSettings') ? siteSettings :
+ contentSettings;
+ },
+
+ /**
* Updates the text of a browsing data counter corresponding to the given
* preference.
* @param {string} prefName Browsing data type deletion preference.
@@ -156,8 +229,10 @@ Polymer({
shouldShowImportantSites_: function() {
if (!this.importantSitesFlagEnabled_)
return false;
- if (!this.$.cookiesCheckbox.checked)
+ const tab = this.$.tabs.selectedItem;
+ if (!tab.querySelector('.cookies-checkbox').checked) {
return false;
+ }
const haveImportantSites = this.importantSites_.length > 0;
chrome.send(
@@ -172,12 +247,13 @@ Polymer({
*/
onClearBrowsingDataTap_: function() {
if (this.shouldShowImportantSites_()) {
+ const tab = this.$.tabs.selectedItem;
this.showImportantSitesDialog_ = true;
+ this.showImportantSitesCacheSubtitle_ =
+ tab.querySelector('.cache-checkbox').checked;
this.$.clearBrowsingDataDialog.close();
// Show important sites dialog after dom-if is applied.
- this.async(function() {
- this.$$('#importantSitesDialog').showModal();
- });
+ this.async(() => this.$$('#importantSitesDialog').showModal());
} else {
this.clearBrowsingData_();
}
@@ -200,21 +276,31 @@ Polymer({
*/
clearBrowsingData_: function() {
this.clearingInProgress_ = true;
+ const tab = this.$.tabs.selectedItem;
- const checkboxes = this.root.querySelectorAll('.browsing-data-checkbox');
+ const checkboxes = tab.querySelectorAll('settings-checkbox');
const dataTypes = [];
checkboxes.forEach((checkbox) => {
if (checkbox.checked)
dataTypes.push(checkbox.pref.key);
});
- const timePeriod = this.$.clearFrom.pref.value;
+ const timePeriod = tab.querySelector('.time-range-select').pref.value;
+
+ if (tab.id == 'basic-tab') {
+ chrome.metricsPrivate.recordUserAction('ClearBrowsingData_BasicTab');
+ } else {
+ chrome.metricsPrivate.recordUserAction('ClearBrowsingData_AdvancedTab');
+ }
this.browserProxy_
.clearBrowsingData(dataTypes, timePeriod, this.importantSites_)
.then(shouldShowNotice => {
this.clearingInProgress_ = false;
this.showHistoryDeletionDialog_ = shouldShowNotice;
+ chrome.metricsPrivate.recordMediumTime(
+ 'History.ClearBrowsingData.TimeSpentInDialog',
+ Date.now() - this.dialogOpenedTime_);
if (!shouldShowNotice)
this.closeDialogs_();
});
@@ -257,4 +343,20 @@ Polymer({
this.showHistoryDeletionDialog_ = false;
this.closeDialogs_();
},
+
+ /**
+ * Records an action when the user changes between the basic and advanced tab.
+ * @param {!Event} event
+ * @private
+ */
+ recordTabChange_: function(event) {
+ if (event.detail.value == 0) {
+ chrome.metricsPrivate.recordUserAction(
+ 'ClearBrowsingData_SwitchTo_BasicTab');
+ } else {
+ chrome.metricsPrivate.recordUserAction(
+ 'ClearBrowsingData_SwitchTo_AdvancedTab');
+ }
+ },
+
});
diff --git a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.html b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.html
deleted file mode 100644
index b9118df038f..00000000000
--- a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.html
+++ /dev/null
@@ -1,311 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-pages/iron-pages.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-tabs/paper-tabs.html">
-<link rel="import" href="../i18n_setup.html">
-<link rel="import" href="clear_browsing_data_browser_proxy.html">
-<link rel="import" href="history_deletion_dialog.html">
-<link rel="import" href="../controls/important_site_checkbox.html">
-<link rel="import" href="../controls/settings_checkbox.html">
-<link rel="import" href="../controls/settings_dropdown_menu.html">
-<link rel="import" href="../icons.html">
-<link rel="import" href="../settings_shared_css.html">
-<link rel="import" href="../settings_vars_css.html">
-
-<!-- This file is a fork of clear_browsing_data_dialog.html until the new CBD
- UI is launched. -->
-<dom-module id="settings-clear-browsing-data-dialog-tabs">
- <template>
- <style include="settings-shared">
- :host {
- /* Fixed height to allow multiple tabs with different height.
- * The last entry in the advanced tab should show half an entry.
- * crbug.com/652027 */
- --body-container-height: 322px;
- }
-
- #clearBrowsingDataDialog {
- --cr-dialog-top-container-min-height: 42px;
- --cr-dialog-title: {
- padding-bottom: 8px;
- };
- --cr-dialog-body-container: {
- border-top: 1px solid var(--paper-grey-300);
- height: var(--body-container-height);
- };
- }
-
- #clearBrowsingDataDialog:not(.fully-rendered) {
- visibility: hidden;
- }
-
- #clearBrowsingDataDialog [slot=footer] {
- color: var(--paper-grey-600);
- }
-
- #clearBrowsingDataDialog [slot=body] {
- padding-top: 8px;
- }
-
- #importantSitesDialog {
- --cr-dialog-body-container: {
- height: var(--body-container-height);
- };
- }
-
- .row {
- align-items: center;
- display: flex;
- min-height: 40px;
- }
-
- paper-spinner-lite {
- -webkit-margin-end: 16px;
- margin-bottom: auto;
- margin-top: auto;
- }
-
- settings-checkbox,
- important-site-checkbox {
- --settings-row-two-line-min-height: 48px;
- --settings-checkbox-label: {
- line-height: 1.25rem;
- }
- }
-
- #basic-tab settings-checkbox + settings-checkbox {
- --settings-checkbox-margin-top: 12px;
- }
-
- paper-tabs {
- --paper-tabs-selection-bar-color: var(--google-blue-500);
- --paper-tabs: {
- font-size: 100%;
- height: 40px;
- }
- }
-
- paper-tab {
- --paper-tab-content: {
- color: var(--google-blue-700);
- };
- --paper-tab-content-unselected: {
- opacity: 1;
- color: var(--paper-grey-600);
- };
- }
-
- .time-range-row {
- margin-bottom: 12px;
- }
-
- .time-range-select {
- /* Adjust for md-select-underline and 1px additional bottom padding
- * to keep md-select's text (without the underline) aligned with
- * neighboring text that does not have an underline. */
- margin-top: 3px;
- }
-
- [slot=title] .secondary {
- font-size: calc(13 / 15 * 100%);
- padding-top: 8px;
- }
- </style>
-
- <dialog is="cr-dialog" id="clearBrowsingDataDialog"
- on-close="onClearBrowsingDataDialogClose_"
- close-text="$i18n{close}" ignore-popstate has-tabs>
- <div slot="title">
- <div>$i18n{clearBrowsingData}</div>
- </div>
- <div slot="header">
- <paper-tabs noink on-selected-changed="recordTabChange_"
- selected="{{prefs.browser.last_clear_browsing_data_tab.value}}">
- <paper-tab>$i18n{basicPageTitle}</paper-tab>
- <paper-tab>$i18n{advancedPageTitle}</paper-tab>
- </paper-tabs>
- </div>
- <div slot="body">
- <iron-pages id="tabs"
- selected="[[prefs.browser.last_clear_browsing_data_tab.value]]">
- <div id="basic-tab">
- <div class="row time-range-row">
- <span class="time-range-label">
- $i18n{clearTimeRange}
- </span>
- <settings-dropdown-menu id="clearFromBasic"
- class="time-range-select"
- label="$i18n{clearTimeRange}"
- pref="{{prefs.browser.clear_data.time_period_basic}}"
- menu-options="[[clearFromOptions_]]">
- </settings-dropdown-menu>
- </div>
- <!-- Note: whether these checkboxes are checked are ignored if
- deleting history is disabled (i.e. supervised users, policy),
- so it's OK to have a hidden checkbox that's also checked (as
- the C++ accounts for whether a user is allowed to delete
- history independently). -->
- <settings-checkbox id="browsingCheckboxBasic"
- pref="{{prefs.browser.clear_data.browsing_history_basic}}"
- label="$i18n{clearBrowsingHistory}"
- sub-label-html="[[browsingCheckboxLabel_(
- isSignedIn_, isSyncingHistory_,
- '$i18nPolymer{clearBrowsingHistorySummary}',
- '$i18nPolymer{clearBrowsingHistorySummarySignedIn}',
- '$i18nPolymer{clearBrowsingHistorySummarySynced}')]]"
- disabled="[[clearingInProgress_]]"
- hidden="[[isSupervised_]]">
- </settings-checkbox>
- <settings-checkbox id="cookiesCheckboxBasic"
- class="cookies-checkbox"
- pref="{{prefs.browser.clear_data.cookies_basic}}"
- label="$i18n{clearCookies}"
- sub-label="$i18n{clearCookiesSummary}"
- disabled="[[clearingInProgress_]]">
- </settings-checkbox>
- <settings-checkbox id="cacheCheckboxBasic"
- class="cache-checkbox"
- pref="{{prefs.browser.clear_data.cache_basic}}"
- label="$i18n{clearCache}"
- sub-label="[[counters_.cache_basic]]"
- disabled="[[clearingInProgress_]]">
- </settings-checkbox>
- </div>
- <div id="advanced-tab">
- <div class="row time-range-row">
- <span class="time-range-label">
- $i18n{clearTimeRange}
- </span>
- <settings-dropdown-menu id="clearFrom"
- class="time-range-select"
- label="$i18n{clearTimeRange}"
- pref="{{prefs.browser.clear_data.time_period}}"
- menu-options="[[clearFromOptions_]]">
- </settings-dropdown-menu>
- </div>
- <settings-checkbox id="browsingCheckbox"
- pref="{{prefs.browser.clear_data.browsing_history}}"
- label="$i18n{clearBrowsingHistory}"
- sub-label="[[counters_.browsing_history]]"
- disabled="[[clearingInProgress_]]"
- hidden="[[isSupervised_]]">
- </settings-checkbox>
- <settings-checkbox id="downloadCheckbox"
- pref="{{prefs.browser.clear_data.download_history}}"
- label="$i18n{clearDownloadHistory}"
- sub-label="[[counters_.download_history]]"
- disabled="[[clearingInProgress_]]"
- hidden="[[isSupervised_]]">
- </settings-checkbox>
- <settings-checkbox id="cookiesCheckbox"
- class="cookies-checkbox"
- pref="{{prefs.browser.clear_data.cookies}}"
- label="$i18n{clearCookies}"
- sub-label="[[counters_.cookies]]"
- disabled="[[clearingInProgress_]]">
- </settings-checkbox>
- <settings-checkbox id="cacheCheckbox"
- class="cache-checkbox"
- pref="{{prefs.browser.clear_data.cache}}"
- label="$i18n{clearCache}"
- sub-label="[[counters_.cache]]"
- disabled="[[clearingInProgress_]]">
- </settings-checkbox>
- <settings-checkbox
- pref="{{prefs.browser.clear_data.passwords}}"
- label="$i18n{clearPasswords}"
- sub-label="[[counters_.passwords]]"
- disabled="[[clearingInProgress_]]">
- </settings-checkbox>
- <settings-checkbox
- pref="{{prefs.browser.clear_data.form_data}}"
- label="$i18n{clearFormData}"
- sub-label="[[counters_.form_data]]"
- disabled="[[clearingInProgress_]]">
- </settings-checkbox>
- <settings-checkbox
- pref="{{prefs.browser.clear_data.site_settings}}"
- label="[[siteSettingsLabel_(
- '$i18nPolymer{siteSettings}',
- '$i18nPolymer{contentSettings}')]]"
- sub-label="[[counters_.site_settings]]"
- disabled="[[clearingInProgress_]]">
- </settings-checkbox>
- <settings-checkbox
- pref="{{prefs.browser.clear_data.hosted_apps_data}}"
- label="$i18n{clearHostedAppData}"
- sub-label="[[counters_.hosted_apps_data]]"
- disabled="[[clearingInProgress_]]">
- </settings-checkbox>
- <settings-checkbox
- pref="{{prefs.browser.clear_data.media_licenses}}"
- label="$i18n{clearMediaLicenses}"
- sub-label="[[counters_.media_licenses]]"
- disabled="[[clearingInProgress_]]">
- </settings-checkbox>
- </div>
- </iron-pages>
- </div>
- <div slot="button-container">
- <paper-spinner-lite active="[[clearingInProgress_]]">
- </paper-spinner-lite>
- <paper-button class="cancel-button" disabled="[[clearingInProgress_]]"
- on-tap="onCancelTap_">$i18n{cancel}</paper-button>
- <paper-button id="clearBrowsingDataConfirm"
- class="action-button" disabled="[[clearingInProgress_]]"
- on-tap="onClearBrowsingDataTap_">
- $i18n{clearData}
- </paper-button>
- </div>
- </dialog>
-
- <template is="dom-if" if="[[showImportantSitesDialog_]]">
- <dialog is="cr-dialog" id="importantSitesDialog" close-text="$i18n{close}"
- show-scroll-borders ignore-popstate>
- <div slot="title">
- $i18n{clearBrowsingData}
- <div class="secondary">
- <span hidden$="[[showImportantSitesCacheSubtitle_]]">
- $i18n{importantSitesSubtitleCookies}
- </span>
- <span hidden$="[[!showImportantSitesCacheSubtitle_]]">
- $i18n{importantSitesSubtitleCookiesAndCache}
- </span>
- </div>
- </div>
- <div slot="body">
- <template is="dom-repeat" items="[[importantSites_]]">
- <div class="row">
- <important-site-checkbox
- site="[[item]]"
- disabled="[[clearingInProgress_]]">
- </important-site-checkbox>
- </div>
- </template>
- </div>
- <div slot="button-container">
- <paper-spinner-lite active="[[clearingInProgress_]]">
- </paper-spinner-lite>
- <paper-button class="cancel-button" disabled="[[clearingInProgress_]]"
- on-tap="onImportantSitesCancelTap_">$i18n{cancel}</paper-button>
- <paper-button id="importantSitesConfirm"
- class="action-button" disabled="[[clearingInProgress_]]"
- on-tap="onImportantSitesConfirmTap_">
- $i18n{importantSitesConfirm}
- </paper-button>
- </div>
- </dialog>
- </template>
-
- <template is="dom-if" if="[[showHistoryDeletionDialog_]]" restamp>
- <settings-history-deletion-dialog id="notice"
- on-close="onHistoryDeletionDialogClose_">
- </settings-history-deletion-dialog>
- </template>
- </template>
- <script src="clear_browsing_data_dialog_tabs.js"></script>
-</dom-module>
diff --git a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.js b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.js
deleted file mode 100644
index 27f055c5e4b..00000000000
--- a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.js
+++ /dev/null
@@ -1,367 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview 'settings-clear-browsing-data-dialog-tabs' allows the user to
- * delete browsing data that has been cached by Chromium.
- *
- * This file is a fork of clear_browsing_data_dialog.js until the new CBD UI is
- * launched.
- */
-Polymer({
- is: 'settings-clear-browsing-data-dialog-tabs',
-
- behaviors: [WebUIListenerBehavior, settings.RouteObserverBehavior],
-
- properties: {
- /**
- * Preferences state.
- */
- prefs: {
- type: Object,
- notify: true,
- },
-
- /**
- * Results of browsing data counters, keyed by the suffix of
- * the corresponding data type deletion preference, as reported
- * by the C++ side.
- * @private {!Object<string>}
- */
- counters_: {
- type: Object,
- // Will be filled as results are reported.
- value: function() {
- return {};
- }
- },
-
- /**
- * List of options for the dropdown menu.
- * @private {!DropdownMenuOptionList}
- */
- clearFromOptions_: {
- readOnly: true,
- type: Array,
- value: [
- {value: 0, name: loadTimeData.getString('clearPeriodHour')},
- {value: 1, name: loadTimeData.getString('clearPeriod24Hours')},
- {value: 2, name: loadTimeData.getString('clearPeriod7Days')},
- {value: 3, name: loadTimeData.getString('clearPeriod4Weeks')},
- {value: 4, name: loadTimeData.getString('clearPeriodEverything')},
- ],
- },
-
- /** @private */
- clearingInProgress_: {
- type: Boolean,
- value: false,
- },
-
- /** @private */
- isSupervised_: {
- type: Boolean,
- value: function() {
- return loadTimeData.getBoolean('isSupervised');
- },
- },
-
- /** @private */
- showHistoryDeletionDialog_: {
- type: Boolean,
- value: false,
- },
-
- /** @private */
- isSignedIn_: {
- type: Boolean,
- value: false,
- },
-
- /** @private */
- isSyncingHistory_: {
- type: Boolean,
- value: false,
- },
-
- /** @private {!Array<ImportantSite>} */
- importantSites_: {
- type: Array,
- value: function() {
- return [];
- }
- },
-
- /** @private */
- importantSitesFlagEnabled_: {
- type: Boolean,
- value: function() {
- return loadTimeData.getBoolean('importantSitesInCbd');
- },
- },
-
- /** @private */
- showImportantSitesDialog_: {
- type: Boolean,
- value: false,
- },
-
- /** @private */
- showImportantSitesCacheSubtitle_: {
- type: Boolean,
- value: false,
- },
-
- /**
- * Time in ms, when the dialog was opened.
- * @private
- */
- dialogOpenedTime_: {
- type: Number,
- value: 0,
- }
- },
-
- /** @private {settings.ClearBrowsingDataBrowserProxy} */
- browserProxy_: null,
-
- /** @override */
- ready: function() {
- this.addWebUIListener(
- 'update-sync-state', this.updateSyncState_.bind(this));
- this.addWebUIListener(
- 'update-counter-text', this.updateCounterText_.bind(this));
- },
-
- /** @override */
- attached: function() {
- this.browserProxy_ =
- settings.ClearBrowsingDataBrowserProxyImpl.getInstance();
- this.dialogOpenedTime_ = Date.now();
- this.browserProxy_.initialize().then(() => {
- this.$.clearBrowsingDataDialog.showModal();
- });
-
- if (this.importantSitesFlagEnabled_) {
- this.browserProxy_.getImportantSites().then(sites => {
- this.importantSites_ = sites;
- });
- }
- },
-
- /**
- * Record visits to the CBD dialog.
- *
- * settings.RouteObserverBehavior
- * @param {!settings.Route} currentRoute
- * @protected
- */
- currentRouteChanged: function(currentRoute) {
- if (currentRoute == settings.routes.CLEAR_BROWSER_DATA) {
- chrome.metricsPrivate.recordUserAction('ClearBrowsingData_DialogCreated');
- this.dialogOpenedTime_ = Date.now();
- }
- },
-
- /**
- * Updates the history description to show the relevant information
- * depending on sync and signin state.
- *
- * @param {boolean} signedIn Whether the user is signed in.
- * @param {boolean} syncing Whether the user is syncing history.
- * @param {boolean} otherFormsOfBrowsingHistory Whether the user has other
- * forms of browsing history in their account.
- * @private
- */
- updateSyncState_: function(signedIn, syncing, otherFormsOfBrowsingHistory) {
- this.isSignedIn_ = signedIn;
- this.isSyncingHistory_ = syncing;
- this.$.clearBrowsingDataDialog.classList.add('fully-rendered');
- },
-
- /**
- * Choose a summary checkbox label.
- * @param {boolean} isSignedIn
- * @param {boolean} isSyncingHistory
- * @param {string} historySummary
- * @param {string} historySummarySigned
- * @param {string} historySummarySynced
- * @return {string}
- * @private
- */
- browsingCheckboxLabel_: function(
- isSignedIn, isSyncingHistory, historySummary, historySummarySigned,
- historySummarySynced) {
- if (isSyncingHistory) {
- return historySummarySynced;
- } else if (isSignedIn) {
- return historySummarySigned;
- }
- return historySummary;
- },
-
- /**
- * Choose a content/site settings label.
- * @param {string} siteSettings
- * @param {string} contentSettings
- * @return {string}
- * @private
- */
- siteSettingsLabel_: function(siteSettings, contentSettings) {
- return loadTimeData.getBoolean('enableSiteSettings') ? siteSettings :
- contentSettings;
- },
-
- /**
- * Updates the text of a browsing data counter corresponding to the given
- * preference.
- * @param {string} prefName Browsing data type deletion preference.
- * @param {string} text The text with which to update the counter
- * @private
- */
- updateCounterText_: function(prefName, text) {
- // Data type deletion preferences are named "browser.clear_data.<datatype>".
- // Strip the common prefix, i.e. use only "<datatype>".
- const matches = prefName.match(/^browser\.clear_data\.(\w+)$/);
- this.set('counters_.' + assert(matches[1]), text);
- },
-
- /**
- * @return {boolean} Whether the ImportantSites dialog should be shown.
- * @private
- */
- shouldShowImportantSites_: function() {
- if (!this.importantSitesFlagEnabled_)
- return false;
- const tab = this.$.tabs.selectedItem;
- if (!tab.querySelector('.cookies-checkbox').checked) {
- return false;
- }
-
- const haveImportantSites = this.importantSites_.length > 0;
- chrome.send(
- 'metricsHandler:recordBooleanHistogram',
- ['History.ClearBrowsingData.ImportantDialogShown', haveImportantSites]);
- return haveImportantSites;
- },
-
- /**
- * Handles the tap on the Clear Data button.
- * @private
- */
- onClearBrowsingDataTap_: function() {
- if (this.shouldShowImportantSites_()) {
- const tab = this.$.tabs.selectedItem;
- this.showImportantSitesDialog_ = true;
- this.showImportantSitesCacheSubtitle_ =
- tab.querySelector('.cache-checkbox').checked;
- this.$.clearBrowsingDataDialog.close();
- // Show important sites dialog after dom-if is applied.
- this.async(() => this.$$('#importantSitesDialog').showModal());
- } else {
- this.clearBrowsingData_();
- }
- },
-
- /**
- * Handles closing of the clear browsing data dialog. Stops the close
- * event from propagating if another dialog is shown to prevent the
- * privacy-page from closing this dialog.
- * @private
- */
- onClearBrowsingDataDialogClose_: function(event) {
- if (this.showImportantSitesDialog_)
- event.stopPropagation();
- },
-
- /**
- * Clears browsing data and maybe shows a history notice.
- * @private
- */
- clearBrowsingData_: function() {
- this.clearingInProgress_ = true;
- const tab = this.$.tabs.selectedItem;
-
- checkboxes = tab.querySelectorAll('settings-checkbox');
- const dataTypes = [];
- checkboxes.forEach((checkbox) => {
- if (checkbox.checked)
- dataTypes.push(checkbox.pref.key);
- });
-
- const timePeriod = tab.querySelector('.time-range-select').pref.value;
-
- if (tab.id == 'basic-tab') {
- chrome.metricsPrivate.recordUserAction('ClearBrowsingData_BasicTab');
- } else {
- chrome.metricsPrivate.recordUserAction('ClearBrowsingData_AdvancedTab');
- }
-
- this.browserProxy_
- .clearBrowsingData(dataTypes, timePeriod, this.importantSites_)
- .then(shouldShowNotice => {
- this.clearingInProgress_ = false;
- this.showHistoryDeletionDialog_ = shouldShowNotice;
- chrome.metricsPrivate.recordMediumTime(
- 'History.ClearBrowsingData.TimeSpentInDialog',
- Date.now() - this.dialogOpenedTime_);
- if (!shouldShowNotice)
- this.closeDialogs_();
- });
- },
-
- /**
- * Closes the clear browsing data or important site dialog if they are open.
- * @private
- */
- closeDialogs_: function() {
- if (this.$.clearBrowsingDataDialog.open)
- this.$.clearBrowsingDataDialog.close();
- if (this.showImportantSitesDialog_)
- this.$$('#importantSitesDialog').close();
- },
-
- /** @private */
- onCancelTap_: function() {
- this.$.clearBrowsingDataDialog.cancel();
- },
-
- /**
- * Handles the tap confirm button in important sites.
- * @private
- */
- onImportantSitesConfirmTap_: function() {
- this.clearBrowsingData_();
- },
-
- /** @private */
- onImportantSitesCancelTap_: function() {
- /** @type {!CrDialogElement} */ (this.$$('#importantSitesDialog')).cancel();
- },
-
- /**
- * Handles the closing of the notice about other forms of browsing history.
- * @private
- */
- onHistoryDeletionDialogClose_: function() {
- this.showHistoryDeletionDialog_ = false;
- this.closeDialogs_();
- },
-
- /**
- * Records an action when the user changes between the basic and advanced tab.
- * @param {!Event} event
- * @private
- */
- recordTabChange_: function(event) {
- if (event.detail.value == 0) {
- chrome.metricsPrivate.recordUserAction(
- 'ClearBrowsingData_SwitchTo_BasicTab');
- } else {
- chrome.metricsPrivate.recordUserAction(
- 'ClearBrowsingData_SwitchTo_AdvancedTab');
- }
- },
-
-});
diff --git a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/compiled_resources2.gyp b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/compiled_resources2.gyp
index 518e48ef804..8660449e98a 100644
--- a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/compiled_resources2.gyp
+++ b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/compiled_resources2.gyp
@@ -14,6 +14,7 @@
{
'target_name': 'clear_browsing_data_dialog',
'dependencies': [
+ '<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-pages/compiled_resources2.gyp:iron-pages-extracted',
'<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-resizable-behavior/compiled_resources2.gyp:iron-resizable-behavior-extracted',
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
diff --git a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/history_deletion_dialog.html b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/history_deletion_dialog.html
index a17572fb569..52429899184 100644
--- a/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/history_deletion_dialog.html
+++ b/chromium/chrome/browser/resources/settings/clear_browsing_data_dialog/history_deletion_dialog.html
@@ -11,7 +11,7 @@
<div slot="title">$i18n{historyDeletionDialogTitle}</div>
<div slot="body">$i18nRaw{historyDeletionDialogBody}</div>
<div slot="button-container">
- <paper-button class="action-button" on-tap="onOkTap_">
+ <paper-button class="action-button" on-click="onOkTap_">
$i18n{historyDeletionDialogOK}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/compiled_resources2.gyp b/chromium/chrome/browser/resources/settings/compiled_resources2.gyp
index a0f14ce056a..f56f7ebe8c4 100644
--- a/chromium/chrome/browser/resources/settings/compiled_resources2.gyp
+++ b/chromium/chrome/browser/resources/settings/compiled_resources2.gyp
@@ -57,6 +57,7 @@
'target_name': 'search_settings',
'dependencies': [
'<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:search_highlight_utils',
],
'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
},
@@ -79,6 +80,7 @@
'default_browser_page/compiled_resources2.gyp:*',
'device_page/compiled_resources2.gyp:*',
'downloads_page/compiled_resources2.gyp:*',
+ 'incompatible_applications_page/compiled_resources2.gyp:*',
'internet_page/compiled_resources2.gyp:*',
'languages_page/compiled_resources2.gyp:*',
'on_startup_page/compiled_resources2.gyp:*',
diff --git a/chromium/chrome/browser/resources/settings/controls/controlled_button.html b/chromium/chrome/browser/resources/settings/controls/controlled_button.html
index e0134790c7d..a5cf488854f 100644
--- a/chromium/chrome/browser/resources/settings/controls/controlled_button.html
+++ b/chromium/chrome/browser/resources/settings/controls/controlled_button.html
@@ -49,7 +49,7 @@
<paper-button disabled="[[enforced_]]">[[label]]</paper-button>
<template is="dom-if" if="[[hasPrefPolicyIndicator(pref.*)]]" restamp>
- <cr-policy-pref-indicator pref="[[pref]]" on-tap="onIndicatorTap_"
+ <cr-policy-pref-indicator pref="[[pref]]" on-click="onIndicatorTap_"
icon-aria-label="[[label]]">
</cr-policy-pref-indicator>
</template>
diff --git a/chromium/chrome/browser/resources/settings/controls/controlled_button.js b/chromium/chrome/browser/resources/settings/controls/controlled_button.js
index 307475678a9..49d46860349 100644
--- a/chromium/chrome/browser/resources/settings/controls/controlled_button.js
+++ b/chromium/chrome/browser/resources/settings/controls/controlled_button.js
@@ -32,7 +32,7 @@ Polymer({
* @private
*/
onIndicatorTap_: function(e) {
- // Disallow <controlled-button on-tap="..."> when controlled.
+ // Disallow <controlled-button on-click="..."> when controlled.
e.preventDefault();
e.stopPropagation();
},
diff --git a/chromium/chrome/browser/resources/settings/controls/controlled_radio_button.html b/chromium/chrome/browser/resources/settings/controls/controlled_radio_button.html
index 10b1bca7fe2..d6fcdf26b06 100644
--- a/chromium/chrome/browser/resources/settings/controls/controlled_radio_button.html
+++ b/chromium/chrome/browser/resources/settings/controls/controlled_radio_button.html
@@ -12,7 +12,7 @@
:host {
--ink-to-circle: calc((var(--paper-radio-button-ink-size) -
var(--paper-radio-button-size)) / 2);
- @apply(--settings-actionable);
+ @apply --settings-actionable;
align-items: center;
display: flex;
outline: none;
@@ -24,7 +24,7 @@
}
#label {
- color: var(--paper-radio-button-label-color, --primary-text-color);
+ color: var(--paper-radio-button-label-color, var(--primary-text-color));
}
.circle,
@@ -53,11 +53,12 @@
.circle {
border: 2px solid var(--paper-radio-button-unchecked-color,
- --primary-text-color);
+ var(--primary-text-color));
}
:host([checked]) .circle {
- border-color: var(--paper-radio-button-checked-color, --primary-color);
+ border-color: var(--paper-radio-button-checked-color,
+ var(--primary-color));
}
.disc {
@@ -69,23 +70,23 @@
:host([checked]) .disc {
background-color: var(--paper-radio-button-checked-color,
- --primary-color);
+ var(--primary-color));
transform: scale(0.5);
}
paper-ripple {
color: var(--paper-radio-button-unchecked-ink-color,
- --primary-text-color);
+ var(--primary-text-color));
opacity: .6;
}
:host([checked]) paper-ripple {
color: var(--paper-radio-button-checked-ink-color,
- --primary-text-color);
+ var(--primary-text-color));
}
:host(:not([controlled_])) {
- @apply(--settings-actionable);
+ @apply --settings-actionable;
}
:host([controlled_]) {
@@ -100,12 +101,12 @@
:host([controlled_]) .circle {
border-color: var(--paper-radio-button-unchecked-color,
- --primary-text-color);
+ var(--primary-text-color));
}
:host([controlled_][checked]) .disc {
background-color: var(--paper-radio-button-unchecked-color,
- --primary-text-color);
+ var(--primary-text-color));
}
:host([controlled_]) #labelWrapper {
@@ -132,7 +133,7 @@
</div>
<template is="dom-if" if="[[showIndicator_(controlled_, name, pref.*)]]">
- <cr-policy-pref-indicator pref="[[pref]]" on-tap="onIndicatorTap_"
+ <cr-policy-pref-indicator pref="[[pref]]" on-click="onIndicatorTap_"
icon-aria-label="[[label]]">
</cr-policy-pref-indicator>
</template>
diff --git a/chromium/chrome/browser/resources/settings/controls/controlled_radio_button.js b/chromium/chrome/browser/resources/settings/controls/controlled_radio_button.js
index ca55d6b2bb1..53bd0505a3c 100644
--- a/chromium/chrome/browser/resources/settings/controls/controlled_radio_button.js
+++ b/chromium/chrome/browser/resources/settings/controls/controlled_radio_button.js
@@ -48,7 +48,6 @@ Polymer({
'blur': 'updatePressed_',
'down': 'updatePressed_',
'focus': 'updatePressed_',
- 'tap': 'onTap_',
'up': 'updatePressed_',
},
@@ -88,17 +87,11 @@ Polymer({
* @private
*/
onIndicatorTap_: function(e) {
- // Disallow <controlled-radio-button on-tap="..."> when controlled.
+ // Disallow <controlled-radio-button on-click="..."> when controlled.
e.preventDefault();
e.stopPropagation();
},
- /** @private */
- onTap_: function() {
- if (!this.controlled_)
- this.checked = true;
- },
-
/**
* @param {!Event} e
* @private
diff --git a/chromium/chrome/browser/resources/settings/controls/extension_controlled_indicator.html b/chromium/chrome/browser/resources/settings/controls/extension_controlled_indicator.html
index ff53538c98f..50e3313c33c 100644
--- a/chromium/chrome/browser/resources/settings/controls/extension_controlled_indicator.html
+++ b/chromium/chrome/browser/resources/settings/controls/extension_controlled_indicator.html
@@ -17,7 +17,7 @@
}
img {
- @apply(--cr-icon-height-width);
+ @apply --cr-icon-height-width;
-webkit-margin-end: 16px;
}
@@ -32,7 +32,7 @@
<img role="presentation" src="chrome://extension-icon/[[extensionId]]/40/1">
<span inner-h-t-m-l="[[getLabel_(extensionId, extensionName)]]"></span>
<template is="dom-if" if="[[extensionCanBeDisabled]]" restamp>
- <paper-button class="secondary-button" on-tap="onDisableTap_">
+ <paper-button class="secondary-button" on-click="onDisableTap_">
$i18n{disable}
</paper-button>
</template>
diff --git a/chromium/chrome/browser/resources/settings/controls/important_site_checkbox.html b/chromium/chrome/browser/resources/settings/controls/important_site_checkbox.html
index 0a0dd1b1937..e70da80b8f5 100644
--- a/chromium/chrome/browser/resources/settings/controls/important_site_checkbox.html
+++ b/chromium/chrome/browser/resources/settings/controls/important_site_checkbox.html
@@ -20,7 +20,7 @@
}
paper-checkbox:not([checked]) .secondary {
- @apply(--settings-secondary-unchecked);
+ @apply --settings-secondary-unchecked;
}
.middot {
@@ -28,7 +28,7 @@
}
.label {
- @apply(--settings-checkbox-label);
+ @apply --settings-checkbox-label;
}
</style>
<div id="outerRow">
diff --git a/chromium/chrome/browser/resources/settings/controls/settings_checkbox.html b/chromium/chrome/browser/resources/settings/controls/settings_checkbox.html
index b46ee716595..0563536cb09 100644
--- a/chromium/chrome/browser/resources/settings/controls/settings_checkbox.html
+++ b/chromium/chrome/browser/resources/settings/controls/settings_checkbox.html
@@ -30,7 +30,7 @@
}
paper-checkbox:not([checked]) .secondary {
- @apply(--settings-secondary-unchecked);
+ @apply --settings-secondary-unchecked;
}
cr-policy-pref-indicator {
@@ -38,7 +38,7 @@
}
.label {
- @apply(--settings-checkbox-label);
+ @apply --settings-checkbox-label;
}
</style>
<div id="outerRow" noSubLabel$="[[!hasSubLabel_(subLabel, subLabelHtml)]]">
diff --git a/chromium/chrome/browser/resources/settings/controls/settings_checkbox.js b/chromium/chrome/browser/resources/settings/controls/settings_checkbox.js
index 3c0e8ded868..c4482b15c39 100644
--- a/chromium/chrome/browser/resources/settings/controls/settings_checkbox.js
+++ b/chromium/chrome/browser/resources/settings/controls/settings_checkbox.js
@@ -33,7 +33,7 @@ Polymer({
subLabelHtmlChanged_: function() {
const links = this.root.querySelectorAll('.secondary.label a');
links.forEach((link) => {
- link.addEventListener('tap', this.stopPropagation);
+ link.addEventListener('click', this.stopPropagation);
});
},
diff --git a/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.html b/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.html
index 7cdb86fdff3..2277abc6f7e 100644
--- a/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.html
+++ b/chromium/chrome/browser/resources/settings/controls/settings_toggle_button.html
@@ -1,5 +1,6 @@
<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
<link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
@@ -10,7 +11,7 @@
<template>
<style include="settings-shared iron-flex">
:host {
- @apply(--cr-section);
+ @apply --cr-section;
}
:host(.first),
@@ -29,7 +30,7 @@
}
:host([elide-label]) .label {
- @apply(--settings-text-elide);
+ @apply --cr-text-elide;
}
#outerRow {
diff --git a/chromium/chrome/browser/resources/settings/date_time_page/date_time_page.html b/chromium/chrome/browser/resources/settings/date_time_page/date_time_page.html
index c064fabcbef..20b846cb2e7 100644
--- a/chromium/chrome/browser/resources/settings/date_time_page/date_time_page.html
+++ b/chromium/chrome/browser/resources/settings/date_time_page/date_time_page.html
@@ -45,7 +45,7 @@
if="[[prefs.cros.flags.fine_grained_time_zone_detection_enabled.value]]"
restamp>
<div id="timeZoneSettingsTrigger" class="settings-box first"
- on-tap="onTimeZoneSettings_" actionable>
+ on-click="onTimeZoneSettings_" actionable>
<div id="timeZoneButton" class="two-line">
$i18n{timeZoneButton}
<div class="secondary">
@@ -79,7 +79,7 @@
label="$i18n{use24HourClock}">
</settings-toggle-button>
<div class="settings-box" id="setDateTime" actionable
- on-tap="onSetDateTimeTap_" hidden$="[[!canSetDateTime_]]">
+ on-click="onSetDateTimeTap_" hidden$="[[!canSetDateTime_]]">
<div class="start">$i18n{setDateTime}</div>
<button class="subpage-arrow" is="paper-icon-button-light"
aria-label="$i18n{setDateTime}"></button>
diff --git a/chromium/chrome/browser/resources/settings/default_browser_page/default_browser_page.html b/chromium/chrome/browser/resources/settings/default_browser_page/default_browser_page.html
index ee383626bcc..c24783b0bd2 100644
--- a/chromium/chrome/browser/resources/settings/default_browser_page/default_browser_page.html
+++ b/chromium/chrome/browser/resources/settings/default_browser_page/default_browser_page.html
@@ -17,7 +17,7 @@
</div>
<div class="separator"></div>
<paper-button class="secondary-button"
- on-tap="onSetDefaultBrowserTap_">
+ on-click="onSetDefaultBrowserTap_">
$i18n{defaultBrowserMakeDefaultButton}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/device_page/device_page.html b/chromium/chrome/browser/resources/settings/device_page/device_page.html
index 56cc4fcfd76..cf90e9489fb 100644
--- a/chromium/chrome/browser/resources/settings/device_page/device_page.html
+++ b/chromium/chrome/browser/resources/settings/device_page/device_page.html
@@ -24,7 +24,7 @@
focus-config="[[focusConfig_]]">
<neon-animatable id="main" route-path="default">
<div id="pointersRow" class="settings-box first"
- on-tap="onPointersTap_" actionable>
+ on-click="onPointersTap_" actionable>
<div class="start">
[[getPointersTitle_(hasMouse_, hasTouchpad_)]]
</div>
@@ -32,34 +32,34 @@
aria-label$="[[getPointersTitle_(hasMouse_,
hasTouchpad_)]]"></button>
</div>
- <div id="keyboardRow" class="settings-box" on-tap="onKeyboardTap_"
+ <div id="keyboardRow" class="settings-box" on-click="onKeyboardTap_"
actionable>
<div class="start">$i18n{keyboardTitle}</div>
<button class="subpage-arrow" is="paper-icon-button-light"
aria-label="$i18n{keyboardTitle}"></button>
</div>
<template is="dom-if" if="[[hasStylus_]]">
- <div id="stylusRow" class="settings-box" on-tap="onStylusTap_"
+ <div id="stylusRow" class="settings-box" on-click="onStylusTap_"
actionable>
<div class="start">$i18n{stylusTitle}</div>
<button class="subpage-arrow" is="paper-icon-button-light"
aria-label="$i18n{stylusTitle}"></button>
</div>
</template>
- <div id="displayRow" class="settings-box" on-tap="onDisplayTap_"
+ <div id="displayRow" class="settings-box" on-click="onDisplayTap_"
actionable>
<div class="start">$i18n{displayTitle}</div>
<button class="subpage-arrow" is="paper-icon-button-light"
aria-label="$i18n{displayTitle}"></button>
</div>
- <div id="storageRow" class="settings-box" on-tap="onStorageTap_"
+ <div id="storageRow" class="settings-box" on-click="onStorageTap_"
actionable>
<div class="start">$i18n{storageTitle}</div>
<button class="subpage-arrow" is="paper-icon-button-light"
aria-label="$i18n{storageTitle}"></button>
</div>
<template is="dom-if" if="[[enablePowerSettings_]]">
- <div id="powerRow" class="settings-box" on-tap="onPowerTap_"
+ <div id="powerRow" class="settings-box" on-click="onPowerTap_"
actionable>
<div class="start">$i18n{powerTitle}</div>
<button class="subpage-arrow" is="paper-icon-button-light"
diff --git a/chromium/chrome/browser/resources/settings/device_page/device_page_browser_proxy.js b/chromium/chrome/browser/resources/settings/device_page/device_page_browser_proxy.js
index 0344e145e0b..ae2fb6d008c 100644
--- a/chromium/chrome/browser/resources/settings/device_page/device_page_browser_proxy.js
+++ b/chromium/chrome/browser/resources/settings/device_page/device_page_browser_proxy.js
@@ -185,7 +185,7 @@ cr.define('settings', function() {
/** override */
handleLinkEvent(e) {
- // Prevent the link from activating its parent element when tapped or
+ // Prevent the link from activating its parent element when clicked or
// when Enter is pressed.
if (e.type != 'keydown' || e.keyCode == 13)
e.stopPropagation();
diff --git a/chromium/chrome/browser/resources/settings/device_page/display.html b/chromium/chrome/browser/resources/settings/device_page/display.html
index 81cf27e5056..0eec56f0f97 100644
--- a/chromium/chrome/browser/resources/settings/device_page/display.html
+++ b/chromium/chrome/browser/resources/settings/device_page/display.html
@@ -82,7 +82,7 @@
restamp>
<div class="secondary self-start">
<paper-checkbox checked="[[isMirrored_(displays)]]"
- on-tap="onMirroredTap_"
+ on-click="onMirroredTap_"
aria-label="[[getDisplayMirrorText_(displays)]]">
<div class="text-area">[[getDisplayMirrorText_(displays)]]</div>
</paper-checkbox>
@@ -188,7 +188,7 @@
<button is="cr-link-row" icon-class="subpage-arrow" class="indented hr"
id="overscan" label="$i18n{displayOverscanPageTitle}"
- sub-label="$i18n{displayOverscanPageText}" on-tap="onOverscanTap_"
+ sub-label="$i18n{displayOverscanPageText}" on-click="onOverscanTap_"
hidden$="[[!showOverscanSetting_(selectedDisplay)]]">
</button>
@@ -198,7 +198,7 @@
</settings-display-overscan-dialog>
<div class="settings-box indented two-line"
- on-tap="onTouchCalibrationTap_"
+ on-click="onTouchCalibrationTap_"
hidden$="[[!showTouchCalibrationSetting_(selectedDisplay)]]"
actionable>
<div class="start">
diff --git a/chromium/chrome/browser/resources/settings/device_page/display.js b/chromium/chrome/browser/resources/settings/device_page/display.js
index c21864b443b..2fcb479af3b 100644
--- a/chromium/chrome/browser/resources/settings/device_page/display.js
+++ b/chromium/chrome/browser/resources/settings/device_page/display.js
@@ -699,25 +699,18 @@ Polymer({
// Blur the control so that when the transition animation completes and the
// UI is focused, the control does not receive focus. crbug.com/785070
event.target.blur();
- let id = '';
- /** @type {!chrome.system.display.DisplayProperties} */
- const properties = {};
- if (this.isMirrored_(this.displays)) {
- id = this.primaryDisplayId;
- properties.mirroringSourceId = '';
- } else {
- // Set the mirroringSourceId of the secondary (first non-primary) display.
- for (let i = 0; i < this.displays.length; ++i) {
- const display = this.displays[i];
- if (display.id != this.primaryDisplayId) {
- id = display.id;
- break;
- }
- }
- properties.mirroringSourceId = this.primaryDisplayId;
- }
- settings.display.systemDisplayApi.setDisplayProperties(
- id, properties, this.setPropertiesCallback_.bind(this));
+
+ /** @type {!chrome.system.display.MirrorModeInfo} */
+ let mirrorModeInfo = {
+ mode: this.isMirrored_(this.displays) ?
+ chrome.system.display.MirrorMode.OFF :
+ chrome.system.display.MirrorMode.NORMAL
+ };
+ settings.display.systemDisplayApi.setMirrorMode(mirrorModeInfo, () => {
+ let error = chrome.runtime.lastError;
+ if (error)
+ console.error('setMirrorMode Error: ' + error.message);
+ });
},
/** @private */
diff --git a/chromium/chrome/browser/resources/settings/device_page/display_layout.html b/chromium/chrome/browser/resources/settings/device_page/display_layout.html
index 289ebf9c8f2..4b138cb2b17 100644
--- a/chromium/chrome/browser/resources/settings/device_page/display_layout.html
+++ b/chromium/chrome/browser/resources/settings/device_page/display_layout.html
@@ -54,7 +54,7 @@
}
.display.elevate {
- @apply(--shadow-elevation-2dp);
+ @apply --shadow-elevation-2dp;
}
</style>
<div id="displayArea" on-iron-resize="calculateVisualScale_">
@@ -67,15 +67,11 @@
</template>
<template is="dom-repeat" items="[[displays]]">
<div id="_[[item.id]]" class="display elevate"
- draggable="[[dragEnabled]]" on-tap="onSelectDisplayTap_"
+ draggable="[[dragEnabled]]" on-click="onSelectDisplayTap_"
style$="[[getDivStyle_(item.id, item.bounds, visualScale)]]"
selected$="[[isSelected_(item, selectedDisplay)]]">
- <div hidden$="[[mirroring]]">
- [[item.name]]
- </div>
- <div hidden$="[[!mirroring]]">
- $i18n{displayMirrorDisplayName}
- </div>
+ [[getDisplayName_(mirroring, item.name,
+ '$i18nPolymer{displayMirrorDisplayName}')]]
</div>
</template>
</div>
diff --git a/chromium/chrome/browser/resources/settings/device_page/display_layout.js b/chromium/chrome/browser/resources/settings/device_page/display_layout.js
index 4b9f4192931..6cd70ca9581 100644
--- a/chromium/chrome/browser/resources/settings/device_page/display_layout.js
+++ b/chromium/chrome/browser/resources/settings/device_page/display_layout.js
@@ -191,6 +191,17 @@ Polymer({
},
/**
+ * @param {boolean} mirroring
+ * @param {string} displayName
+ * @param {string} mirroringName
+ * @return {string}
+ * @private
+ */
+ getDisplayName_: function(mirroring, displayName, mirroringName) {
+ return mirroring ? mirroringName : displayName;
+ },
+
+ /**
* @param {!chrome.system.display.DisplayUnitInfo} display
* @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay
* @return {boolean}
diff --git a/chromium/chrome/browser/resources/settings/device_page/display_overscan_dialog.html b/chromium/chrome/browser/resources/settings/device_page/display_overscan_dialog.html
index 08e263c7738..5c90de1c152 100644
--- a/chromium/chrome/browser/resources/settings/device_page/display_overscan_dialog.html
+++ b/chromium/chrome/browser/resources/settings/device_page/display_overscan_dialog.html
@@ -74,10 +74,10 @@
</div>
</div>
<div slot="button-container">
- <paper-button id="reset" class="cancel-button" on-tap="onResetTap_">
+ <paper-button id="reset" class="cancel-button" on-click="onResetTap_">
$i18n{displayOverscanReset}
</paper-button>
- <paper-button class="action-button" on-tap="onSaveTap_">
+ <paper-button class="action-button" on-click="onSaveTap_">
$i18n{ok}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/device_page/drive_cache_dialog.html b/chromium/chrome/browser/resources/settings/device_page/drive_cache_dialog.html
index 696cbad5190..d111fcf73a3 100644
--- a/chromium/chrome/browser/resources/settings/device_page/drive_cache_dialog.html
+++ b/chromium/chrome/browser/resources/settings/device_page/drive_cache_dialog.html
@@ -17,11 +17,11 @@
</div>
<div slot="button-container">
<paper-button id="cancelButton" class="cancel-button"
- on-tap="onCancelTap_">
+ on-click="onCancelTap_">
$i18n{cancel}
</paper-button>
<paper-button id="deleteButton" class="action-button"
- on-tap="onDeleteTap_">
+ on-click="onDeleteTap_">
$i18n{storageDeleteAllButtonTitle}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/device_page/keyboard.html b/chromium/chrome/browser/resources/settings/device_page/keyboard.html
index cabf3ca8e43..1ec10018922 100644
--- a/chromium/chrome/browser/resources/settings/device_page/keyboard.html
+++ b/chromium/chrome/browser/resources/settings/device_page/keyboard.html
@@ -104,12 +104,12 @@
</div>
</iron-collapse>
<div id="keyboardOverlay" class="settings-box"
- on-tap="onShowKeyboardShortcutsOverlayTap_" actionable>
+ on-click="onShowKeyboardShortcutsOverlayTap_" actionable>
<div class="start">$i18n{showKeyboardShortcutsOverlay}</div>
<button class="icon-external" is="paper-icon-button-light"
aria-label="$i18n{showKeyboardShortcutsOverlay}"></button>
</div>
- <div class="settings-box" on-tap="onShowLanguageInputTap_" actionable>
+ <div class="settings-box" on-click="onShowLanguageInputTap_" actionable>
<div class="start">$i18n{keyboardShowLanguageAndInput}</div>
<button class="subpage-arrow" is="paper-icon-button-light"
aria-label="$i18n{keyboardShowLanguageAndInput}"></button>
diff --git a/chromium/chrome/browser/resources/settings/device_page/pointers.html b/chromium/chrome/browser/resources/settings/device_page/pointers.html
index d93db637cf5..0fb133e2933 100644
--- a/chromium/chrome/browser/resources/settings/device_page/pointers.html
+++ b/chromium/chrome/browser/resources/settings/device_page/pointers.html
@@ -85,7 +85,7 @@
<paper-radio-button name="true">
$i18n{naturalScrollLabel}
<a href="$i18n{naturalScrollLearnMoreLink}" target="_blank"
- on-tap="onLearnMoreLinkActivated_"
+ on-click="onLearnMoreLinkActivated_"
on-keydown="onLearnMoreLinkActivated_">
$i18n{naturalScrollLearnMore}
</a>
diff --git a/chromium/chrome/browser/resources/settings/device_page/storage.html b/chromium/chrome/browser/resources/settings/device_page/storage.html
index a10225b2f15..fc8cc1df590 100644
--- a/chromium/chrome/browser/resources/settings/device_page/storage.html
+++ b/chromium/chrome/browser/resources/settings/device_page/storage.html
@@ -195,7 +195,7 @@
</div>
</div>
</div>
- <div class="settings-box two-line" on-tap="onDownloadsTap_" actionable>
+ <div class="settings-box two-line" on-click="onDownloadsTap_" actionable>
<div class="start">
$i18n{storageItemDownloads}
<div id="downloadsSize" class="secondary">
@@ -207,7 +207,7 @@
aria-describedby="downloadsSize"></button>
</div>
<template is="dom-if" if="[[driveEnabled_]]">
- <div class="settings-box two-line" on-tap="onDriveCacheTap_"
+ <div class="settings-box two-line" on-click="onDriveCacheTap_"
actionable$="[[hasDriveCache_]]" >
<div class="start">
$i18n{storageItemDriveCache}
@@ -221,7 +221,7 @@
</button>
</div>
</template>
- <div class="settings-box two-line" on-tap="onBrowsingDataTap_" actionable>
+ <div class="settings-box two-line" on-click="onBrowsingDataTap_" actionable>
<div class="start">
$i18n{storageItemBrowsingData}
<div id="browsingDataSize" class="secondary">
@@ -233,7 +233,7 @@
aria-describedby="browsingDataSize"></button>
</div>
<template is="dom-if" if="[[androidEnabled_]]">
- <div class="settings-box two-line" on-tap="onAndroidTap_" actionable>
+ <div class="settings-box two-line" on-click="onAndroidTap_" actionable>
<div class="start">
$i18n{storageItemAndroid}
<div id="androidSize" class="secondary">
@@ -246,7 +246,7 @@
</div>
</template>
<template is="dom-if" if="[[!isGuest_]]">
- <div class="settings-box two-line" on-tap="onOtherUsersTap_" actionable>
+ <div class="settings-box two-line" on-click="onOtherUsersTap_" actionable>
<div class="start">
$i18n{storageItemOtherUsers}
<div id="otherUsersSize" class="secondary">
diff --git a/chromium/chrome/browser/resources/settings/device_page/stylus.html b/chromium/chrome/browser/resources/settings/device_page/stylus.html
index 9aa157eceab..93252074b04 100644
--- a/chromium/chrome/browser/resources/settings/device_page/stylus.html
+++ b/chromium/chrome/browser/resources/settings/device_page/stylus.html
@@ -19,7 +19,7 @@
paper-spinner-lite {
margin-left: 12px;
- @apply(--cr-icon-height-width);
+ @apply --cr-icon-height-width;
}
cr-policy-indicator {
@@ -77,7 +77,7 @@
<!-- TODO(scottchen): Make a proper a[href].settings-box with
icon-external (see: https://crbug.com/684005)-->
- <div class="settings-box two-line" on-tap="onFindAppsTap_" actionable
+ <div class="settings-box two-line" on-click="onFindAppsTap_" actionable
hidden$="[[!prefs.arc.enabled.value]]">
<div class="start">
$i18n{stylusFindMoreAppsPrimary}
@@ -97,7 +97,7 @@
<div class="settings-box first">
<div id="lock-screen-toggle-label" class="start"
actionable$="[[!disallowedOnLockScreenByPolicy_(selectedApp_)]]"
- on-tap="toggleLockScreenSupport_">
+ on-click="toggleLockScreenSupport_">
$i18n{stylusNoteTakingAppEnabledOnLockScreen}
</div>
<template is="dom-if"
diff --git a/chromium/chrome/browser/resources/settings/downloads_page/downloads_page.html b/chromium/chrome/browser/resources/settings/downloads_page/downloads_page.html
index 2f213e62e6a..ea4a199cb0a 100644
--- a/chromium/chrome/browser/resources/settings/downloads_page/downloads_page.html
+++ b/chromium/chrome/browser/resources/settings/downloads_page/downloads_page.html
@@ -29,7 +29,7 @@
<div class="separator"></div>
<controlled-button class="secondary-button" id="changeDownloadsPath"
label="$i18n{changeDownloadLocation}"
- on-tap="selectDownloadLocation_"
+ on-click="selectDownloadLocation_"
pref="[[prefs.download.default_directory]]"
end-justified>
</controlled-button>
@@ -52,7 +52,7 @@
</div>
<div class="separator"></div>
<paper-button id="resetAutoOpenFileTypes" class="secondary-button"
- on-tap="onClearAutoOpenFileTypesTap_">
+ on-click="onClearAutoOpenFileTypesTap_">
$i18n{clear}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html b/chromium/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html
index b96f029582e..46081160c54 100644
--- a/chromium/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html
+++ b/chromium/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html
@@ -30,7 +30,7 @@
on-change="onGoogleAssistantContextEnableChange_">
</settings-toggle-button>
<div id="googleAssistantSettings" class="settings-box"
- on-tap="onGoogleAssistantSettingsTapped_" actionable>
+ on-click="onGoogleAssistantSettingsTapped_" actionable>
<div class="start">
$i18n{googleAssistantSettings}
</div>
diff --git a/chromium/chrome/browser/resources/settings/icons.html b/chromium/chrome/browser/resources/settings/icons.html
index c15dd7ba4a7..bf9a0008176 100644
--- a/chromium/chrome/browser/resources/settings/icons.html
+++ b/chromium/chrome/browser/resources/settings/icons.html
@@ -10,9 +10,7 @@ List icons here rather than importing large sets of (e.g. Polymer) icons.
<defs>
<!-- Ads icon in the Content Settings -->
<g id="ads">
- <path d="M19,3H5C3.89,3,3,3.9,3,5v14c0,1.1,0.89,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M11,15H9.5v-1.5h-2V15H6v-4.5V9h1.5h2H10h1V15z M18,14c0,0.55-0.45,1-1,1h-4V9h4c0.55,0,1,0.45,1,1V14z"></path>
- <rect x="7.5" y="10.5" width="2" height="1.5"></rect>
- <rect x="14.5" y="10.5" width="2" height="3"></rect>
+ <path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.89-2-2-2zm0 14H5V8h14v10z"></path>
</g>
<!-- Cookie SVG obtained from rolfe@ -->
@@ -104,6 +102,7 @@ List icons here rather than importing large sets of (e.g. Polymer) icons.
<g id="refresh"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"></path></g>
<g id="restore"><path d="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z"></path></g>
<g id="rotate-right"><path d="M15.55 5.55L11 1v3.07C7.06 4.56 4 7.92 4 12s3.05 7.44 7 7.93v-2.02c-2.84-.48-5-2.94-5-5.91s2.16-5.43 5-5.91V10l4.55-4.45zM19.93 11c-.17-1.39-.72-2.73-1.62-3.89l-1.42 1.42c.54.75.88 1.6 1.02 2.47h2.02zM13 17.9v2.02c1.39-.17 2.74-.71 3.9-1.61l-1.44-1.44c-.75.54-1.59.89-2.46 1.03zm3.89-2.42l1.42 1.41c.9-1.16 1.45-2.5 1.62-3.89h-2.02c-.14.87-.48 1.72-1.02 2.48z"></path></g>
+ <g id="sensors"><path d="M10 8.5c-0.8 0-1.5 0.7-1.5 1.5s0.7 1.5 1.5 1.5s1.5-0.7 1.5-1.5S10.8 8.5 10 8.5z M7.6 5.8 C6.2 6.7 5.2 8.2 5.2 10c0 1.8 1 3.4 2.4 4.2l0.8-1.4c-1-0.6-1.6-1.6-1.6-2.8c0-1.2 0.6-2.2 1.6-2.8L7.6 5.8z M14.8 10 c0-1.8-1-3.4-2.4-4.2l-0.8 1.4c0.9 0.6 1.6 1.6 1.6 2.8c0 1.2-0.6 2.2-1.6 2.8l0.8 1.4C13.8 13.4 14.8 11.8 14.8 10z M6 3 c-2.4 1.4-4 4-4 7c0 3 1.6 5.6 4 7l0.8-1.4c-1.9-1.1-3.2-3.2-3.2-5.6c0-2.4 1.3-4.5 3.2-5.6L6 3z M13.2 4.4 c1.9 1.1 3.2 3.2 3.2 5.6c0 2.4-1.3 4.5-3.2 5.6L14 17c2.4-1.4 4-4 4-7c0-3-1.6-5.6-4-7L13.2 4.4z"></path></g>
<g id="security"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z"></path></g>
<if expr="chromeos">
<g id="alert-device-out-of-range" fill="none" fill-rule="evenodd"><path d="M-1-1h20v20H-1z"></path><path fill="#C53929" fill-rule="nonzero" d="M8.167 11.5h1.666v1.667H8.167V11.5zm0-6.667h1.666v5H8.167v-5zM8.992.667C4.392.667.667 4.4.667 9s3.725 8.333 8.325 8.333c4.608 0 8.341-3.733 8.341-8.333S13.6.667 8.992.667zm.008 15A6.665 6.665 0 0 1 2.333 9 6.665 6.665 0 0 1 9 2.333 6.665 6.665 0 0 1 15.667 9 6.665 6.665 0 0 1 9 15.667z"></path></g>
diff --git a/chromium/chrome/browser/resources/settings/images/sync_banner.svg b/chromium/chrome/browser/resources/settings/images/sync_banner.svg
new file mode 100644
index 00000000000..d2ecb8603a7
--- /dev/null
+++ b/chromium/chrome/browser/resources/settings/images/sync_banner.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="155px" height="131px" viewBox="0 0 155 131" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+ <polygon fill="#3874D5" points="0 114.5 154.75 114.5 154.75 0 0 0"></polygon>
+ <g transform="translate(61.000000, 53.250000)">
+ <path d="M59.25,77 L19,77 C17.35,77 16,75.65 16,74 L16,3 C16,1.35 17.35,0 19,0 L59.25,0 C60.9,0 62.25,1.35 62.25,3 L62.25,74 C62.25,75.65 60.9,77 59.25,77" fill="#4285F4"></path>
+ <path d="M31.4473,61.5527 C31.4473,70.0837 24.5313,76.9997 16.0003,76.9997 C7.4683,76.9997 0.5523,70.0837 0.5523,61.5527 C0.5523,53.0207 7.4683,46.1047 16.0003,46.1047 C24.5313,46.1047 31.4473,53.0207 31.4473,61.5527" fill="#FABB05"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/settings/incompatible_applications_page/compiled_resources2.gyp b/chromium/chrome/browser/resources/settings/incompatible_applications_page/compiled_resources2.gyp
new file mode 100644
index 00000000000..c73f9ebe50a
--- /dev/null
+++ b/chromium/chrome/browser/resources/settings/incompatible_applications_page/compiled_resources2.gyp
@@ -0,0 +1,33 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+ 'targets': [
+ {
+ 'target_name': 'incompatible_applications_browser_proxy',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'incompatible_applications_page',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior',
+ 'incompatible_applications_browser_proxy',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ {
+ 'target_name': 'incompatible_application_item',
+ 'dependencies': [
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
+ 'incompatible_applications_browser_proxy',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
+ ],
+}
diff --git a/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_application_item.html b/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_application_item.html
new file mode 100644
index 00000000000..1f8027ae747
--- /dev/null
+++ b/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_application_item.html
@@ -0,0 +1,25 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/html/assert.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="../settings_shared_css.html">
+<link rel="import" href="incompatible_applications_browser_proxy.html">
+
+<dom-module id="incompatible-application-item">
+ <template>
+ <style include="settings-shared">
+ :host {
+ display: block;
+ }
+ </style>
+ <div class="list-item">
+ <div class="start">[[applicationName]]</div>
+ <div class="separator"></div>
+ <paper-button class="primary-button" on-click="onActionTap_">
+ [[getActionName_(actionType)]]
+ </paper-button>
+ </div>
+ </template>
+ <script src="incompatible_application_item.js"></script>
+</dom-module>
diff --git a/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_application_item.js b/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_application_item.js
new file mode 100644
index 00000000000..72dd7f1941e
--- /dev/null
+++ b/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_application_item.js
@@ -0,0 +1,103 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * 'incompatible-application-item' represents one item in a "list-box" of
+ * incompatible applications, as defined in
+ * chrome/browser/conflicts/problematic_programs_updater_win.h.
+ * This element contains a button that can be used to remove or update the
+ * incompatible application, depending on the value of the action-type property.
+ *
+ * Example usage:
+ *
+ * <div class="list-box">
+ * <incompatible-application-item
+ * application-name="Google Chrome"
+ * action-type="1"
+ * action-url="https://www.google.com/chrome/more-info">
+ * </incompatible-application-item>
+ * </div>
+ *
+ * or
+ *
+ * <div class="list-box">
+ * <template is="dom-repeat" items="[[applications]]" as="application">
+ * <incompatible-application-item
+ * application-name="[[application.name]]"
+ * action-type="[[application.actionType]]"
+ * action-url="[[application.actionUrl]]">
+ * </incompatible-application-item>
+ * </template>
+ * </div>
+ */
+
+Polymer({
+ is: 'incompatible-application-item',
+
+ behaviors: [I18nBehavior],
+
+ properties: {
+ /**
+ * The name of the application to be displayed. Also used for the UNINSTALL
+ * action, where the name is passed to the startProgramUninstallation()
+ * call.
+ */
+ applicationName: String,
+
+ /**
+ * The type of the action to be taken on this incompatible application. Must
+ * be one of BlacklistMessageType in
+ * chrome/browser/conflicts/proto/module_list.proto.
+ * @type {!settings.ActionTypes}
+ */
+ actionType: Number,
+
+ /**
+ * For the actions MORE_INFO and UPGRADE, this is the URL that must be
+ * opened when the action button is tapped.
+ */
+ actionUrl: String,
+ },
+
+ /** @private {settings.IncompatibleApplicationsBrowserProxy} */
+ browserProxy_: null,
+
+ /** @override */
+ created: function() {
+ this.browserProxy_ =
+ settings.IncompatibleApplicationsBrowserProxyImpl.getInstance();
+ },
+
+ /**
+ * Executes the action for this incompatible application, depending on
+ * actionType.
+ * @private
+ */
+ onActionTap_: function() {
+ if (this.actionType === settings.ActionTypes.UNINSTALL) {
+ this.browserProxy_.startProgramUninstallation(this.applicationName);
+ } else if (
+ this.actionType === settings.ActionTypes.MORE_INFO ||
+ this.actionType === settings.ActionTypes.UPGRADE) {
+ this.browserProxy_.openURL(this.actionUrl);
+ } else {
+ assertNotReached();
+ }
+ },
+
+ /**
+ * @return {string} The label that should be applied to the action button.
+ * @private
+ */
+ getActionName_: function(actionType) {
+ if (actionType === settings.ActionTypes.UNINSTALL)
+ return this.i18n('incompatibleApplicationsRemoveButton');
+ if (actionType === settings.ActionTypes.MORE_INFO)
+ return this.i18n('learnMore');
+ if (actionType === settings.ActionTypes.UPGRADE)
+ return this.i18n('incompatibleApplicationsUpdateButton');
+ assertNotReached();
+ },
+});
diff --git a/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_applications_browser_proxy.html b/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_applications_browser_proxy.html
new file mode 100644
index 00000000000..8d55a232daa
--- /dev/null
+++ b/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_applications_browser_proxy.html
@@ -0,0 +1,2 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+<script src="incompatible_applications_browser_proxy.js"></script>
diff --git a/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_applications_browser_proxy.js b/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_applications_browser_proxy.js
new file mode 100644
index 00000000000..7017e0a469d
--- /dev/null
+++ b/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_applications_browser_proxy.js
@@ -0,0 +1,122 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview A helper object used from the Incompatible Applications section
+ * to interact with the browser.
+ */
+
+cr.exportPath('settings');
+
+/**
+ * All possible actions to take on an incompatible application.
+ *
+ * Must be kept in sync with BlacklistMessageType in
+ * chrome/browser/conflicts/proto/module_list.proto
+ * @readonly
+ * @enum {number}
+ */
+settings.ActionTypes = {
+ UNINSTALL: 0,
+ MORE_INFO: 1,
+ UPGRADE: 2,
+};
+
+/**
+ * @typedef {{
+ * name: string,
+ * actionType: {settings.ActionTypes},
+ * actionUrl: string,
+ * }}
+ */
+settings.IncompatibleApplication;
+
+cr.define('settings', function() {
+ /** @interface */
+ class IncompatibleApplicationsBrowserProxy {
+ /**
+ * Get the list of incompatible applications.
+ * @return {!Promise<!Array<!settings.IncompatibleApplication>>}
+ */
+ requestIncompatibleApplicationsList() {}
+
+ /**
+ * Launches the Apps & Features page that allows uninstalling 'programName'.
+ * @param {string} programName
+ */
+ startProgramUninstallation(programName) {}
+
+ /**
+ * Opens the specified URL in a new tab.
+ * @param {!string} url
+ */
+ openURL(url) {}
+
+ /**
+ * Requests the plural string for the subtitle of the Incompatible
+ * Applications subpage.
+ * @param {number} numApplications
+ * @return {!Promise<string>}
+ */
+ getSubtitlePluralString(numApplications) {}
+
+ /**
+ * Requests the plural string for the subtitle of the Incompatible
+ * Applications subpage, when the user does not have administrator rights.
+ * @param {number} numApplications
+ * @return {!Promise<string>}
+ */
+ getSubtitleNoAdminRightsPluralString(numApplications) {}
+
+ /**
+ * Requests the plural string for the title of the list of Incompatible
+ * Applications.
+ * @param {number} numApplications
+ * @return {!Promise<string>}
+ */
+ getListTitlePluralString(numApplications) {}
+ }
+
+ /** @implements {settings.IncompatibleApplicationsBrowserProxy} */
+ class IncompatibleApplicationsBrowserProxyImpl {
+ /** @override */
+ requestIncompatibleApplicationsList() {
+ return cr.sendWithPromise('requestIncompatibleApplicationsList');
+ }
+
+ /** @override */
+ startProgramUninstallation(programName) {
+ chrome.send('startProgramUninstallation', [programName]);
+ }
+
+ /** @override */
+ openURL(url) {
+ window.open(url);
+ }
+
+ /** @override */
+ getSubtitlePluralString(numApplications) {
+ return cr.sendWithPromise('getSubtitlePluralString', numApplications);
+ }
+
+ /** @override */
+ getSubtitleNoAdminRightsPluralString(numApplications) {
+ return cr.sendWithPromise(
+ 'getSubtitleNoAdminRightsPluralString', numApplications);
+ }
+
+ /** @override */
+ getListTitlePluralString(numApplications) {
+ return cr.sendWithPromise('getListTitlePluralString', numApplications);
+ }
+ }
+
+ cr.addSingletonGetter(IncompatibleApplicationsBrowserProxyImpl);
+
+ return {
+ IncompatibleApplicationsBrowserProxy: IncompatibleApplicationsBrowserProxy,
+ IncompatibleApplicationsBrowserProxyImpl:
+ IncompatibleApplicationsBrowserProxyImpl,
+ };
+});
diff --git a/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_applications_page.html b/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_applications_page.html
new file mode 100644
index 00000000000..9fcccb14ef3
--- /dev/null
+++ b/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_applications_page.html
@@ -0,0 +1,59 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/html/assert.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
+<link rel="import" href="../settings_shared_css.html">
+<link rel="import" href="incompatible_application_item.html">
+<link rel="import" href="incompatible_applications_browser_proxy.html">
+
+<dom-module id="settings-incompatible-applications-page">
+ <template>
+ <style include="settings-shared">
+ #is-done-section > iron-icon {
+ --iron-icon-fill-color: var(--google-blue-500);
+ }
+ </style>
+
+ <div hidden$="[[!isDone_]]" id="is-done-section" class="settings-box first">
+ <iron-icon icon="settings:check-circle"></iron-icon>
+ <div class="middle no-min-width">
+ $i18n{incompatibleApplicationsDone}
+ </div>
+ </div>
+
+ <template is="dom-if" if="[[!isDone_]]">
+ <div class="settings-box first two-line">
+ <iron-icon icon="settings:security"></iron-icon>
+ <div class="middle no-min-width">
+ <div hidden$="[[!hasAdminRights_]]">
+ [[subtitleText_]] $i18nRaw{incompatibleApplicationsSubpageLearnHow}
+ </div>
+ <div hidden$="[[hasAdminRights_]]">
+ [[subtitleNoAdminRightsText_]]
+ </div>
+ </div>
+ </div>
+ <div class="settings-box continuation">
+ <div class="secondary">[[listTitleText_]]</div>
+ </div>
+ <div id="incompatible-applications-list" class="list-frame vertical-list">
+ <template is="dom-repeat" items="[[applications_]]" as="application">
+ <incompatible-application-item
+ hidden$="[[!hasAdminRights_]]"
+ class="incompatible-application"
+ application-name="[[application.name]]"
+ action-type="[[application.type]]"
+ action-url="[[application.url]]">
+ </incompatible-application-item>
+ <div hidden$="[[hasAdminRights_]]"
+ class="list-item incompatible-application">
+ [[application.name]]
+ </div>
+ </template>
+ </div>
+ </template>
+ </template>
+ <script src="incompatible_applications_page.js"></script>
+</dom-module>
diff --git a/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_applications_page.js b/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_applications_page.js
new file mode 100644
index 00000000000..27426d236a8
--- /dev/null
+++ b/chromium/chrome/browser/resources/settings/incompatible_applications_page/incompatible_applications_page.js
@@ -0,0 +1,138 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * 'settings-incompatible-applications-page' is the settings subpage containing
+ * the list of incompatible applications.
+ *
+ * Example:
+ *
+ * <iron-animated-pages>
+ * <settings-incompatible-applications-page">
+ * </settings-incompatible-applications-page>
+ * ... other pages ...
+ * </iron-animated-pages>
+ */
+
+Polymer({
+ is: 'settings-incompatible-applications-page',
+
+ behaviors: [I18nBehavior, WebUIListenerBehavior],
+
+ properties: {
+ /**
+ * Indicates if the current user has administrator rights.
+ * @private
+ */
+ hasAdminRights_: {
+ type: Boolean,
+ value: function() {
+ return loadTimeData.getBoolean('hasAdminRights');
+ },
+ },
+
+ /**
+ * The list of all the incompatible applications.
+ * @private {Array<settings.IncompatibleApplication>}
+ */
+ applications_: Array,
+
+ /**
+ * Determines if the user has finished with this page.
+ * @private
+ */
+ isDone_: {
+ type: Boolean,
+ computed: 'computeIsDone_(applications_.*)',
+ },
+
+ /**
+ * The text for the subtitle of the subpage.
+ * @private
+ */
+ subtitleText_: {
+ type: String,
+ value: '',
+ },
+
+ /**
+ * The text for the subtitle of the subpage, when the user does not have
+ * administrator rights.
+ * @private
+ */
+ subtitleNoAdminRightsText_: {
+ type: String,
+ value: '',
+ },
+
+ /**
+ * The text for the title of the list of incompatible applications.
+ * @private
+ */
+ listTitleText_: {
+ type: String,
+ value: '',
+ },
+ },
+
+ /** @override */
+ ready: function() {
+ this.addWebUIListener(
+ 'incompatible-application-removed',
+ this.onIncompatibleApplicationRemoved_.bind(this));
+
+ settings.IncompatibleApplicationsBrowserProxyImpl.getInstance()
+ .requestIncompatibleApplicationsList()
+ .then(list => {
+ this.applications_ = list;
+ this.updatePluralStrings_();
+ });
+ },
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ computeIsDone_: function() {
+ return this.applications_.length === 0;
+ },
+
+ /**
+ * Removes a single incompatible application from the |applications_| list.
+ * @private
+ */
+ onIncompatibleApplicationRemoved_: function(applicationName) {
+ // Find the index of the element.
+ let index = this.applications_.findIndex(function(application) {
+ return application.name == applicationName;
+ });
+
+ assert(index !== -1);
+
+ this.splice('applications_', index, 1);
+ },
+
+ /**
+ * Updates the texts of the Incompatible Applications subpage that depends on
+ * the length of |applications_|.
+ * @private
+ */
+ updatePluralStrings_: function() {
+ const browserProxy =
+ settings.IncompatibleApplicationsBrowserProxyImpl.getInstance();
+ const numApplications = this.applications_.length;
+ Promise
+ .all([
+ browserProxy.getSubtitlePluralString(numApplications),
+ browserProxy.getSubtitleNoAdminRightsPluralString(numApplications),
+ browserProxy.getListTitlePluralString(numApplications),
+ ])
+ .then(strings => {
+ this.subtitleText_ = strings[0];
+ this.subtitleNoAdminRightsText_ = strings[1];
+ this.listTitleText_ = strings[2];
+ });
+ },
+});
diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_config.html b/chromium/chrome/browser/resources/settings/internet_page/internet_config.html
index 4fa0a79d5e8..fce0c7e3d29 100644
--- a/chromium/chrome/browser/resources/settings/internet_page/internet_config.html
+++ b/chromium/chrome/browser/resources/settings/internet_page/internet_config.html
@@ -32,7 +32,7 @@
share-allow-enable="[[shareAllowEnable_]]"
share-default="[[shareDefault_]]"
error="{{error_}}"
- on-close="close">
+ on-close="onClose_">
</network-config>
</div>
@@ -40,17 +40,17 @@
<template is="dom-if" if="[[error_]]" restamp>
<div class="flex error">[[getError_(error_)]]</div>
</template>
- <paper-button class="cancel-button" on-tap="onCancelTap_">
+ <paper-button class="cancel-button" on-click="onCancelTap_">
$i18n{cancel}
</paper-button>
- <template is="dom-if" if="[[isConfigured_(networkProperties_, guid)]]">
- <paper-button class="action-button" on-tap="onSaveOrConnectTap_"
+ <template is="dom-if" if="[[!showConnect]]">
+ <paper-button class="action-button" on-click="onSaveTap_"
disabled="[[!enableSave_]]">
$i18n{save}
</paper-button>
</template>
- <template is="dom-if" if="[[!isConfigured_(networkProperties_, guid)]]">
- <paper-button class="action-button" on-tap="onSaveOrConnectTap_"
+ <template is="dom-if" if="[[showConnect]]">
+ <paper-button class="action-button" on-click="onConnectTap_"
disabled="[[!enableConnect_]]">
$i18n{networkButtonConnect}
</paper-button>
diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_config.js b/chromium/chrome/browser/resources/settings/internet_page/internet_config.js
index b6bda81171f..ba7c90de4ab 100644
--- a/chromium/chrome/browser/resources/settings/internet_page/internet_config.js
+++ b/chromium/chrome/browser/resources/settings/internet_page/internet_config.js
@@ -56,6 +56,12 @@ Polymer({
*/
name: String,
+ /**
+ * Set to true to show the 'connect' button instead of 'save'.
+ * @private
+ */
+ showConnect: Boolean,
+
/** @private */
enableConnect_: Boolean,
@@ -103,6 +109,16 @@ Polymer({
},
/**
+ * @param {!Event} event
+ * @private
+ */
+ onClose_: function(event) {
+ this.close();
+ this.fire('networks-changed');
+ event.stopPropagation();
+ },
+
+ /**
* @return {string}
* @private
*/
@@ -124,22 +140,18 @@ Polymer({
return this.i18n('networkErrorUnknown');
},
- /**
- * @return {boolean}
- * @private
- */
- isConfigured_: function() {
- const source = this.networkProperties_.Source;
- return !!this.guid && !!source && source != CrOnc.Source.NONE;
- },
-
/** @private */
onCancelTap_: function() {
this.close();
},
/** @private */
- onSaveOrConnectTap_: function() {
- this.$.networkConfig.saveOrConnect();
+ onSaveTap_: function() {
+ this.$.networkConfig.save();
+ },
+
+ /** @private */
+ onConnectTap_: function() {
+ this.$.networkConfig.connect();
},
});
diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.html b/chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.html
index e3afc520289..cbee01f9b6e 100644
--- a/chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.html
+++ b/chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.html
@@ -81,30 +81,30 @@
</template>
</div>
<template is="dom-if" if="[[!isSecondaryUser_]]">
- <paper-button on-tap="onForgetTap_"
+ <paper-button on-click="onForgetTap_"
hidden$="[[!showForget_(networkProperties)]]">
$i18n{networkButtonForget}
</paper-button>
- <paper-button on-tap="onViewAccountTap_"
+ <paper-button on-click="onViewAccountTap_"
hidden$="[[!showViewAccount_(networkProperties)]]">
$i18n{networkButtonViewAccount}
</paper-button>
- <paper-button on-tap="onActivateTap_"
+ <paper-button on-click="onActivateTap_"
hidden$="[[!showActivate_(networkProperties)]]">
$i18n{networkButtonActivate}
</paper-button>
- <paper-button on-tap="onConfigureTap_"
+ <paper-button on-click="onConfigureTap_"
hidden$="[[!showConfigure_(networkProperties, globalPolicy)]]">
$i18n{networkButtonConfigure}
</paper-button>
</template>
- <paper-button class="primary-button" on-tap="onConnectTap_"
+ <paper-button class="primary-button" on-click="onConnectTap_"
hidden$="[[!showConnect_(networkProperties, globalPolicy)]]"
disabled="[[!enableConnect_(networkProperties, defaultNetwork,
globalPolicy, networkPropertiesReceived_, outOfRange_)]]">
$i18n{networkButtonConnect}
</paper-button>
- <paper-button class="primary-button" on-tap="onDisconnectTap_"
+ <paper-button class="primary-button" on-click="onDisconnectTap_"
hidden$="[[!showDisconnect_(networkProperties)]]">
$i18n{networkButtonDisconnect}
</paper-button>
@@ -203,7 +203,7 @@
<template is="dom-if" if="[[showAdvanced_(networkProperties)]]">
<!-- Advanced toggle. -->
- <div class="settings-box" actionable on-tap="toggleAdvancedExpanded_">
+ <div class="settings-box" actionable on-click="toggleAdvancedExpanded_">
<div class="flex">$i18n{networkSectionAdvanced}</div>
<cr-expand-button expanded="{{advancedExpanded_}}"
alt="$i18n{networkSectionAdvancedA11yLabel}">
@@ -232,7 +232,7 @@
<template is="dom-if" if="[[hasNetworkSection_(networkProperties)]]">
<!-- Network toggle -->
- <div class="settings-box" actionable on-tap="toggleNetworkExpanded_">
+ <div class="settings-box" actionable on-click="toggleNetworkExpanded_">
<div class="start">$i18n{networkSectionNetwork}</div>
<cr-expand-button expanded="{{networkExpanded_}}"
alt="$i18n{networkSectionNetworkExpandA11yLabel}">
@@ -274,7 +274,7 @@
<template is="dom-if" if="[[hasProxySection_(networkProperties)]]">
<!-- Proxy toggle -->
- <div class="settings-box" actionable on-tap="toggleProxyExpanded_">
+ <div class="settings-box" actionable on-click="toggleProxyExpanded_">
<div class="start">$i18n{networkSectionProxy}</div>
<cr-expand-button expanded="{{proxyExpanded_}}"
alt="$i18n{networkSectionProxyExpandA11yLabel}">
diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.js b/chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.js
index b8c3480ddcd..c21b36319f5 100644
--- a/chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.js
+++ b/chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.js
@@ -630,7 +630,6 @@ Polymer({
this.showTetherDialog_();
return;
}
-
this.fire('network-connect', {networkProperties: this.networkProperties});
},
diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html b/chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html
index 6d2f1357614..4466b23e664 100644
--- a/chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html
+++ b/chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html
@@ -36,12 +36,12 @@
</cr-policy-indicator>
</template>
<button class="subpage-arrow" is="paper-icon-button-light"
- actionable on-tap="fireShowDetails_" tabindex$="[[tabindex]]"
+ actionable on-click="fireShowDetails_" tabindex$="[[tabindex]]"
aria-label$="[[item.Name]]">
</button>
<div class="separator"></div>
<button is="paper-icon-button-light" class="icon-more-vert"
- preferred tabindex$="[[tabindex]]" on-tap="onMenuButtonTap_"
+ preferred tabindex$="[[tabindex]]" on-click="onMenuButtonTap_"
title="$i18n{moreActions}">
</button>
</div>
@@ -63,12 +63,12 @@
</cr-policy-indicator>
</template>
<button class="subpage-arrow" is="paper-icon-button-light"
- actionable on-tap="fireShowDetails_" tabindex$="[[tabindex]]"
+ actionable on-click="fireShowDetails_" tabindex$="[[tabindex]]"
aria-label$="[[item.Name]]">
</button>
<div class="separator"></div>
<button is="paper-icon-button-light" class="icon-more-vert"
- tabindex$="[[tabindex]]" on-tap="onMenuButtonTap_"
+ tabindex$="[[tabindex]]" on-click="onMenuButtonTap_"
title="$i18n{moreActions}">
</button>
</div>
@@ -76,16 +76,16 @@
</div>
<dialog id="dotsMenu" is="cr-action-menu">
- <button class="dropdown-item" hidden="[[!showAddPreferred_]]"
- on-tap="onAddPreferredTap_">
+ <button slot="item" class="dropdown-item" hidden="[[!showAddPreferred_]]"
+ on-click="onAddPreferredTap_">
$i18n{knownNetworksMenuAddPreferred}
</button>
- <button class="dropdown-item" hidden="[[!showRemovePreferred_]]"
- on-tap="onRemovePreferredTap_">
+ <button slot="item" class="dropdown-item"
+ hidden="[[!showRemovePreferred_]]" on-click="onRemovePreferredTap_">
$i18n{knownNetworksMenuRemovePreferred}
</button>
- <button class="dropdown-item" disabled="[[!enableForget_]]"
- on-tap="onForgetTap_">
+ <button slot="item" class="dropdown-item" disabled="[[!enableForget_]]"
+ on-click="onForgetTap_">
$i18n{knownNetworksMenuForget}
</button>
</dialog>
diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_page.html b/chromium/chrome/browser/resources/settings/internet_page/internet_page.html
index cd6b2afbadb..0c7cf9c5c72 100644
--- a/chromium/chrome/browser/resources/settings/internet_page/internet_page.html
+++ b/chromium/chrome/browser/resources/settings/internet_page/internet_page.html
@@ -38,7 +38,7 @@
</network-summary>
<template is="dom-if" if="[[allowAddConnection_(globalPolicy_)]]">
<div actionable class="settings-box two-line"
- on-tap="onExpandAddConnectionsTap_">
+ on-click="onExpandAddConnectionsTap_">
<div class="start layout horizontal center">
<div>$i18n{internetAddConnection}</div>
</div>
@@ -50,7 +50,7 @@
<div class="list-frame vertical-list">
<template is="dom-if"
if="[[deviceIsEnabled_(deviceStates.WiFi)]]">
- <div actionable class="list-item" on-tap="onAddWiFiTap_">
+ <div actionable class="list-item" on-click="onAddWiFiTap_">
<div class="start">$i18n{internetAddWiFi}</div>
<button class$="[[getAddNetworkClass_('WiFi')]]"
is="paper-icon-button-light"
@@ -58,7 +58,7 @@
</button>
</div>
</template>
- <div actionable class="list-item" on-tap="onAddVPNTap_">
+ <div actionable class="list-item" on-click="onAddVPNTap_">
<div class="start">$i18n{internetAddVPN}</div>
<button class$="[[getAddNetworkClass_('VPN')]]"
is="paper-icon-button-light"
@@ -67,7 +67,7 @@
</div>
<template is="dom-repeat" items="[[thirdPartyVpnProviders_]]">
<div actionable class="list-item"
- on-tap="onAddThirdPartyVpnTap_" provider="[[item]]">
+ on-click="onAddThirdPartyVpnTap_" provider="[[item]]">
<div class="start">[[getAddThirdPartyVpnLabel_(item)]]</div>
<button class="icon-external" is="paper-icon-button-light"
aria-label$="[[getAddThirdPartyVpnLabel_(item)]]">
@@ -76,7 +76,7 @@
</template>
<template is="dom-if" if="[[arcVpnProviders_.length]]">
<div actionable class="list-item" id="addArcVpn"
- on-tap="onAddArcVpnTap_">
+ on-click="onAddArcVpnTap_">
<div class="start">$i18n{internetAddArcVPN}</div>
<button class="icon-external" is="paper-icon-button-light"
aria-label$="$i18n{internetAddArcVPN}">
diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_page.js b/chromium/chrome/browser/resources/settings/internet_page/internet_page.js
index 3b8126607c7..ed1eda816ef 100644
--- a/chromium/chrome/browser/resources/settings/internet_page/internet_page.js
+++ b/chromium/chrome/browser/resources/settings/internet_page/internet_page.js
@@ -272,27 +272,27 @@ Polymer({
*/
onShowConfig_: function(event) {
const properties = event.detail;
+ let configAndConnect = !properties.GUID; // New configuration
this.showConfig_(
- properties.Type, properties.GUID, CrOnc.getNetworkName(properties));
+ configAndConnect, properties.Type, properties.GUID,
+ CrOnc.getNetworkName(properties));
},
/**
+ * @param {boolean} configAndConnect
* @param {string} type
* @param {string=} guid
* @param {string=} name
* @private
*/
- showConfig_: function(type, guid, name) {
- if (!loadTimeData.getBoolean('networkSettingsConfig')) {
- chrome.send('configureNetwork', [guid]);
- return;
- }
+ showConfig_: function(configAndConnect, type, guid, name) {
const configDialog =
/** @type {!InternetConfigElement} */ (this.$.configDialog);
configDialog.type =
/** @type {chrome.networkingPrivate.NetworkType} */ (type);
configDialog.guid = guid || '';
configDialog.name = name || '';
+ configDialog.showConnect = configAndConnect;
configDialog.open();
},
@@ -369,7 +369,7 @@ Polymer({
},
/**
- * Event triggered when the 'Add connections' div is tapped.
+ * Event triggered when the 'Add connections' div is clicked.
* @param {!Event} event
* @private
*/
@@ -382,7 +382,7 @@ Polymer({
/** @private */
onAddWiFiTap_: function() {
if (loadTimeData.getBoolean('networkSettingsConfig'))
- this.showConfig_(CrOnc.Type.WI_FI);
+ this.showConfig_(true /* configAndConnect */, CrOnc.Type.WI_FI);
else
chrome.send('addNetwork', [CrOnc.Type.WI_FI]);
},
@@ -390,7 +390,7 @@ Polymer({
/** @private */
onAddVPNTap_: function() {
if (loadTimeData.getBoolean('networkSettingsConfig'))
- this.showConfig_(CrOnc.Type.VPN);
+ this.showConfig_(true /* configAndConnect */, CrOnc.Type.VPN);
else
chrome.send('addNetwork', [CrOnc.Type.VPN]);
},
@@ -604,7 +604,8 @@ Polymer({
}
if (properties.Connectable === false || properties.ErrorState) {
- this.showConfig_(properties.Type, properties.GUID, name);
+ this.showConfig_(
+ true /* configAndConnect */, properties.Type, properties.GUID, name);
return;
}
@@ -618,7 +619,9 @@ Polymer({
console.error(
'networkingPrivate.startConnect error: ' + message +
' For: ' + properties.GUID);
- this.showConfig_(properties.Type, properties.GUID, name);
+ this.showConfig_(
+ true /* configAndConnect */, properties.Type, properties.GUID,
+ name);
}
});
},
diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.html b/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.html
index 3119ac4ec1c..ad9e3d48712 100644
--- a/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.html
+++ b/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.html
@@ -65,12 +65,12 @@
}
#gmscore-notifications-device-string {
- @apply(--cr-secondary-text);
+ @apply --cr-secondary-text;
margin-top: 5px;
}
#gmscore-notifications-instructions {
- @apply(--cr-secondary-text);
+ @apply --cr-secondary-text;
-webkit-padding-start: 15px;
margin: 0;
}
@@ -86,19 +86,19 @@
<button is="paper-icon-button-light" id="addButton"
hidden$="[[!showAddButton_(deviceState, globalPolicy)]]"
aria-label="$i18n{internetAddWiFi}" class="icon-add-wifi"
- on-tap="onAddButtonTap_" tabindex$="[[tabindex]]">
+ on-click="onAddButtonTap_" tabindex$="[[tabindex]]">
</button>
<paper-toggle-button id="deviceEnabledButton"
aria-label$="[[getToggleA11yString_(deviceState)]]"
checked="[[deviceIsEnabled_(deviceState)]]"
disabled="[[!enableToggleIsEnabled_(deviceState)]]"
- on-tap="onDeviceEnabledTap_">
+ on-click="onDeviceEnabledTap_">
</paper-toggle-button>
</div>
</template>
<template is="dom-if" if="[[knownNetworksIsVisible_(deviceState)]]">
- <div actionable class="settings-box" on-tap="onKnownNetworksTap_">
+ <div actionable class="settings-box" on-click="onKnownNetworksTap_">
<div class="start">$i18n{knownNetworksButton}</div>
<button class="subpage-arrow" is="paper-icon-button-light"
aria-label="$i18n{knownNetworksButton}">
@@ -114,7 +114,7 @@
<div class="flex">$i18n{networkVpnBuiltin}</div>
<button is="paper-icon-button-light" class="icon-add-circle"
aria-label="$i18n{internetAddVPN}"
- on-tap="onAddButtonTap_" tabindex$="[[tabindex]]">
+ on-click="onAddButtonTap_" tabindex$="[[tabindex]]">
</button>
</div>
</template>
@@ -143,6 +143,7 @@
<li>$i18n{gmscoreNotificationsFirstStep}</li>
<li>$i18n{gmscoreNotificationsSecondStep}</li>
<li>$i18n{gmscoreNotificationsThirdStep}</li>
+ <li>$i18n{gmscoreNotificationsFourthStep}</li>
</ol>
</div>
</template>
@@ -165,7 +166,7 @@
<div class="flex">[[item.ProviderName]]</div>
<button is="paper-icon-button-light" class="icon-add-circle"
aria-label$="[[getAddThirdPartyVpnA11yString_(item)]]"
- on-tap="onAddThirdPartyVpnTap_" tabindex$="[[tabindex]]">
+ on-click="onAddThirdPartyVpnTap_" tabindex$="[[tabindex]]">
</button>
</div>
<cr-network-list show-buttons
@@ -185,7 +186,7 @@
<div class="flex">[[item.ProviderName]]</div>
<button is="paper-icon-button-light" class="icon-add-circle"
aria-label$="[[getAddArcVpnAllyString_(item)]]"
- on-tap="onAddArcVpnTap_" tabindex$="[[tabindex]]">
+ on-click="onAddArcVpnTap_" tabindex$="[[tabindex]]">
</button>
</div>
<cr-network-list show-buttons
@@ -204,7 +205,7 @@
<template is="dom-if"
if="[[tetherToggleIsVisible_(deviceState, tetherDeviceState)]]">
<div class="settings-box two-line" actionable
- on-tap="onTetherEnabledTap_">
+ on-click="onTetherEnabledTap_">
<div class="start">
$i18n{internetToggleTetherLabel}
<div id="tetherSecondary" class="secondary">
diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.js b/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.js
index 2d1c08ee297..c45566055aa 100644
--- a/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.js
+++ b/chromium/chrome/browser/resources/settings/internet_page/internet_subpage.js
@@ -442,7 +442,7 @@ Polymer({
},
/**
- * Event triggered when the known networks button is tapped.
+ * Event triggered when the known networks button is clicked.
* @private
*/
onKnownNetworksTap_: function() {
diff --git a/chromium/chrome/browser/resources/settings/internet_page/network_proxy_section.html b/chromium/chrome/browser/resources/settings/internet_page/network_proxy_section.html
index f5d88e89c41..7aa2e646067 100644
--- a/chromium/chrome/browser/resources/settings/internet_page/network_proxy_section.html
+++ b/chromium/chrome/browser/resources/settings/internet_page/network_proxy_section.html
@@ -90,11 +90,11 @@
</div>
<div slot="button-container">
<paper-button class="cancel-button"
- on-tap="onAllowSharedDialogCancel_">
+ on-click="onAllowSharedDialogCancel_">
$i18n{cancel}
</paper-button>
<paper-button class="action-button"
- on-tap="onAllowSharedDialogConfirm_">
+ on-click="onAllowSharedDialogConfirm_">
$i18n{confirm}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/internet_page/network_summary_item.html b/chromium/chrome/browser/resources/settings/internet_page/network_summary_item.html
index 812bf51fff9..2df92961938 100644
--- a/chromium/chrome/browser/resources/settings/internet_page/network_summary_item.html
+++ b/chromium/chrome/browser/resources/settings/internet_page/network_summary_item.html
@@ -34,7 +34,7 @@
font-weight: 400;
}
</style>
- <div actionable class="settings-box two-line" on-tap="onShowDetailsTap_">
+ <div actionable class="settings-box two-line" on-click="onShowDetailsTap_">
<div id="details" no-flex$="[[showSimInfo_(deviceState)]]">
<cr-network-icon network-state="[[activeNetworkState]]"
device-state="[[deviceState]]">
@@ -48,7 +48,7 @@
</div>
<template is="dom-if" if="[[showSimInfo_(deviceState)]]" restamp>
- <network-siminfo editable on-tap="doNothing_"
+ <network-siminfo editable on-click="doNothing_"
network-properties="[[getCellularState_(deviceState)]]"
networking-private="[[networkingPrivate]]">
</network-siminfo>
@@ -57,7 +57,7 @@
<template is="dom-if" if="[[showPolicyIndicator_(activeNetworkState)]]">
<cr-policy-indicator indicator-type="[[getIndicatorTypeForSource(
activeNetworkState.Source)]]"
- on-tap="doNothing_">
+ on-click="doNothing_">
</cr-policy-indicator>
</template>
@@ -75,7 +75,7 @@
aria-label$="[[getToggleA11yString_(deviceState)]]"
checked="[[deviceIsEnabled_(deviceState)]]"
disabled="[[!enableToggleIsEnabled_(deviceState)]]"
- on-tap="onDeviceEnabledTap_">
+ on-click="onDeviceEnabledTap_">
</paper-toggle-button>
</template>
</div>
diff --git a/chromium/chrome/browser/resources/settings/internet_page/network_summary_item.js b/chromium/chrome/browser/resources/settings/internet_page/network_summary_item.js
index 65c882d595d..e7a54cb245a 100644
--- a/chromium/chrome/browser/resources/settings/internet_page/network_summary_item.js
+++ b/chromium/chrome/browser/resources/settings/internet_page/network_summary_item.js
@@ -68,10 +68,9 @@ Polymer({
* @private
*/
getNetworkStateText_: function(activeNetworkState, deviceState) {
- const state = activeNetworkState.ConnectionState;
- const name = CrOnc.getNetworkName(activeNetworkState);
- if (state)
- return this.getConnectionStateText_(state, name);
+ const stateText = this.getConnectionStateText_(activeNetworkState);
+ if (stateText)
+ return stateText;
// No network state, use device state.
if (deviceState) {
// Type specific scanning or initialization states.
@@ -98,12 +97,15 @@ Polymer({
},
/**
- * @param {CrOnc.ConnectionState} state
- * @param {string} name
+ * @param {!CrOnc.NetworkStateProperties} networkState
* @return {string}
* @private
*/
- getConnectionStateText_: function(state, name) {
+ getConnectionStateText_: function(networkState) {
+ const state = networkState.ConnectionState;
+ if (!state)
+ return '';
+ const name = CrOnc.getNetworkName(networkState);
switch (state) {
case CrOnc.ConnectionState.CONNECTED:
return name;
@@ -112,6 +114,10 @@ Polymer({
return CrOncStrings.networkListItemConnectingTo.replace('$1', name);
return CrOncStrings.networkListItemConnecting;
case CrOnc.ConnectionState.NOT_CONNECTED:
+ if (networkState.Type == CrOnc.Type.CELLULAR && networkState.Cellular &&
+ networkState.Cellular.Scanning) {
+ return this.i18n('internetMobileSearching');
+ }
return CrOncStrings.networkListItemNotConnected;
}
assertNotReached();
diff --git a/chromium/chrome/browser/resources/settings/internet_page/tether_connection_dialog.html b/chromium/chrome/browser/resources/settings/internet_page/tether_connection_dialog.html
index 51e02e88fb0..c54c5c1186a 100644
--- a/chromium/chrome/browser/resources/settings/internet_page/tether_connection_dialog.html
+++ b/chromium/chrome/browser/resources/settings/internet_page/tether_connection_dialog.html
@@ -108,11 +108,11 @@
</ul>
</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onNotNowTap_">
+ <paper-button class="cancel-button" on-click="onNotNowTap_">
$i18n{tetherConnectionNotNowButton}
</paper-button>
<paper-button id="connectButton" class="action-button"
- on-tap="onConnectTap_" disabled="[[outOfRange]]">
+ on-click="onConnectTap_" disabled="[[outOfRange]]">
$i18n{tetherConnectionConnectButton}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/languages_page/add_languages_dialog.html b/chromium/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
index 6a6a1512872..a3f6edd6f54 100644
--- a/chromium/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
+++ b/chromium/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
@@ -59,10 +59,10 @@
</iron-list>
</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onCancelButtonTap_">
+ <paper-button class="cancel-button" on-click="onCancelButtonTap_">
$i18n{cancel}
</paper-button>
- <paper-button class="action-button" on-tap="onActionButtonTap_"
+ <paper-button class="action-button" on-click="onActionButtonTap_"
disabled="[[disableActionButton_]]">
$i18n{add}
</paper-button>
diff --git a/chromium/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html b/chromium/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html
index d5d0912c66d..ce5c803d292 100644
--- a/chromium/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html
+++ b/chromium/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html
@@ -42,7 +42,7 @@
'$i18nPolymer{addDictionaryWordDuplicateError}',
'$i18nPolymer{addDictionaryWordLengthError}')]]"></paper-input>
</div>
- <paper-button class="secondary-button" on-tap="onAddWordTap_"
+ <paper-button class="secondary-button" on-click="onAddWordTap_"
disabled="[[disableAddButton_(newWordValue_)]]" id="addWord">
$i18n{addDictionaryWordButton}
</paper-button>
@@ -58,7 +58,7 @@
<div class="list-item">
<div class="word text-elide">[[item]]</div>
<button is="paper-icon-button-light" class="icon-clear"
- on-tap="onRemoveWordTap_" tabindex$="[[tabIndex]]">
+ on-click="onRemoveWordTap_" tabindex$="[[tabIndex]]">
</button>
</div>
</template>
diff --git a/chromium/chrome/browser/resources/settings/languages_page/languages_page.html b/chromium/chrome/browser/resources/settings/languages_page/languages_page.html
index 608022db7bb..7b70201c2cb 100644
--- a/chromium/chrome/browser/resources/settings/languages_page/languages_page.html
+++ b/chromium/chrome/browser/resources/settings/languages_page/languages_page.html
@@ -84,7 +84,7 @@
focus-config="[[focusConfig_]]">
<neon-animatable route-path="default">
<div class$="settings-box first [[getLanguageListTwoLine_()]]"
- actionable on-tap="toggleExpandButton_">
+ actionable on-click="toggleExpandButton_">
<div class="start">
<div>$i18n{languagesListTitle}</div>
<if expr="chromeos or is_win">
@@ -127,20 +127,20 @@
<if expr="chromeos or is_win">
<template is="dom-if" if="[[isRestartRequired_(
item.language.code, languages.prospectiveUILanguage)]]">
- <paper-button on-tap="onRestartTap_">
+ <paper-button on-click="onRestartTap_">
$i18n{restart}
</paper-button>
</template>
</if>
<button is="paper-icon-button-light" title="$i18n{moreActions}"
- id="more-[[item.language.code]]" on-tap="onDotsTap_"
+ id="more-[[item.language.code]]" on-click="onDotsTap_"
class="icon-more-vert">
</button>
</div>
</template>
<div class="list-item">
<a is="action-link" class="list-button" id="addLanguages"
- on-tap="onAddLanguagesTap_">
+ on-click="onAddLanguagesTap_">
$i18n{addLanguages}
</a>
</div>
@@ -153,7 +153,7 @@
<if expr="chromeos">
<div id="manageInputMethodsSubpageTrigger"
class="settings-box two-line" actionable
- on-tap="toggleExpandButton_">
+ on-click="toggleExpandButton_">
<div class="start">
<div>$i18n{inputMethodsListTitle}</div>
<div class="secondary">
@@ -171,7 +171,7 @@
items="[[languages.inputMethods.enabled]]">
<div class$="list-item [[getInputMethodItemClass_(
item.id, languages.inputMethods.currentId)]]"
- on-tap="onInputMethodTap_" on-keypress="onInputMethodTap_"
+ on-click="onInputMethodTap_" on-keypress="onInputMethodTap_"
actionable tabindex="0">
<div class="start">
<div>[[item.displayName]]</div>
@@ -182,12 +182,13 @@
</div>
</div>
<button class="icon-external" is="paper-icon-button-light"
- on-tap="onInputMethodOptionsTap_"
+ on-click="onInputMethodOptionsTap_"
hidden="[[!item.hasOptionsPage]]">
</button>
</div>
</template>
- <div class="list-item" on-tap="onManageInputMethodsTap_" actionable>
+ <div class="list-item" on-click="onManageInputMethodsTap_"
+ actionable>
<div class="start" id="manageInputMethods">
$i18n{manageInputMethods}
</div>
@@ -207,7 +208,7 @@
class$="settings-box [[getSpellCheckListTwoLine_(
spellCheckSecondaryText_)]]"
actionable$="[[!spellCheckDisabled_]]"
- on-tap="toggleExpandButton_">
+ on-click="toggleExpandButton_">
<div class="start">
<div>$i18n{spellCheckListTitle}</div>
<div class="secondary">[[spellCheckSecondaryText_]]</div>
@@ -230,7 +231,7 @@
<template is="dom-repeat" items="[[spellCheckLanguages_]]">
<div class="list-item">
<template is="dom-if" if="[[!item.isManaged]]">
- <div class="start" on-tap="onSpellCheckChange_"
+ <div class="start" on-click="onSpellCheckChange_"
actionable$="[[item.language.supportsSpellcheck]]">
[[item.language.displayName]]
</div>
@@ -250,7 +251,7 @@
</template>
</div>
</template>
- <div class="list-item" on-tap="onEditDictionaryTap_" actionable>
+ <div class="list-item" on-click="onEditDictionaryTap_" actionable>
<div class="start" id="customSpelling">
$i18n{manageSpellCheck}
</div>
@@ -265,7 +266,8 @@
<dialog is="cr-action-menu"
class$="[[getMenuClass_(prefs.translate.enabled.value)]]">
<if expr="chromeos or is_win">
- <paper-checkbox id="uiLanguageItem" class="dropdown-item"
+ <paper-checkbox id="uiLanguageItem" slot="item"
+ class="dropdown-item"
checked="[[isProspectiveUILanguage_(
detailLanguage_.language.code,
languages.prospectiveUILanguage)]]"
@@ -275,7 +277,8 @@
$i18n{displayInThisLanguage}
</paper-checkbox>
</if>
- <paper-checkbox id="offerTranslations" class="dropdown-item"
+ <paper-checkbox id="offerTranslations" slot="item"
+ class="dropdown-item"
checked="[[detailLanguage_.translateEnabled]]"
on-change="onTranslateCheckboxChange_"
hidden="[[!prefs.translate.enabled.value]]"
@@ -283,26 +286,26 @@
detailLanguage_.language, languages.translateTarget)]]">
$i18n{offerToTranslateInThisLanguage}
</paper-checkbox>
- <hr>
- <button class="dropdown-item" role="menuitem"
- on-tap="onMoveToTopTap_"
+ <hr slot="item">
+ <button slot="item" class="dropdown-item" role="menuitem"
+ on-click="onMoveToTopTap_"
hidden="[[isNthLanguage_(
0, detailLanguage_, languages.enabled.*)]]">
$i18n{moveToTop}
</button>
- <button class="dropdown-item" role="menuitem"
- on-tap="onMoveUpTap_"
+ <button slot="item" class="dropdown-item" role="menuitem"
+ on-click="onMoveUpTap_"
hidden="[[!showMoveUp_(detailLanguage_, languages.enabled.*)]]">
$i18n{moveUp}
</button>
- <button class="dropdown-item" role="menuitem"
- on-tap="onMoveDownTap_"
+ <button slot="item" class="dropdown-item" role="menuitem"
+ on-click="onMoveDownTap_"
hidden="[[!showMoveDown_(
detailLanguage_, languages.enabled.*)]]">
$i18n{moveDown}
</button>
- <button class="dropdown-item" role="menuitem"
- on-tap="onRemoveLanguageTap_"
+ <button slot="item" class="dropdown-item" role="menuitem"
+ on-click="onRemoveLanguageTap_"
hidden="[[!detailLanguage_.removable]]">
$i18n{removeLanguage}
</button>
diff --git a/chromium/chrome/browser/resources/settings/languages_page/languages_page.js b/chromium/chrome/browser/resources/settings/languages_page/languages_page.js
index 43787f93abb..d9df072708b 100644
--- a/chromium/chrome/browser/resources/settings/languages_page/languages_page.js
+++ b/chromium/chrome/browser/resources/settings/languages_page/languages_page.js
@@ -116,11 +116,13 @@ Polymer({
},
},
+ // <if expr="not is_macosx">
observers: [
'updateSpellcheckLanguages_(languages.enabled.*, ' +
'languages.forcedSpellCheckLanguages.*)',
'updateSpellcheckEnabled_(prefs.browser.enable_spellchecking.*)',
],
+ // </if>
/**
* Stamps and opens the Add Languages dialog, registering a listener to
@@ -628,7 +630,7 @@ Polymer({
/**
* Closes the shared action menu after a short delay, so when a checkbox is
- * tapped it can be seen to change state before disappearing.
+ * clicked it can be seen to change state before disappearing.
* @private
*/
closeMenuSoon_: function() {
diff --git a/chromium/chrome/browser/resources/settings/on_startup_page/on_startup_page.html b/chromium/chrome/browser/resources/settings/on_startup_page/on_startup_page.html
index 40bfadfde9d..f4bdec1ade6 100644
--- a/chromium/chrome/browser/resources/settings/on_startup_page/on_startup_page.html
+++ b/chromium/chrome/browser/resources/settings/on_startup_page/on_startup_page.html
@@ -23,13 +23,11 @@
label="$i18n{onStartupOpenNewTab}"
no-extension-indicator>
</controlled-radio-button>
- <template is="dom-if" if="[[showIndicator_(
- ntpExtension_, prefs.session.restore_on_startup.value)]]">
+ <template is="dom-if" if="[[ntpExtension_]]">
<extension-controlled-indicator
extension-id="[[ntpExtension_.id]]"
extension-name="[[ntpExtension_.name]]"
- extension-can-be-disabled="[[ntpExtension_.canBeDisabled]]"
- on-extension-disable="getNtpExtension_">
+ extension-can-be-disabled="[[ntpExtension_.canBeDisabled]]">
</extension-controlled-indicator>
</template>
<controlled-radio-button name="[[prefValues_.CONTINUE]]"
diff --git a/chromium/chrome/browser/resources/settings/on_startup_page/on_startup_page.js b/chromium/chrome/browser/resources/settings/on_startup_page/on_startup_page.js
index 26484951dcf..aff7303d079 100644
--- a/chromium/chrome/browser/resources/settings/on_startup_page/on_startup_page.js
+++ b/chromium/chrome/browser/resources/settings/on_startup_page/on_startup_page.js
@@ -37,29 +37,13 @@ Polymer({
/** @override */
attached: function() {
- this.getNtpExtension_();
- this.addWebUIListener('update-ntp-extension', ntpExtension => {
+ const updateNtpExtension = ntpExtension => {
// Note that |ntpExtension| is empty if there is no NTP extension.
this.ntpExtension_ = ntpExtension;
- });
- },
-
- /** @private */
- getNtpExtension_: function() {
+ };
settings.OnStartupBrowserProxyImpl.getInstance().getNtpExtension().then(
- function(ntpExtension) {
- this.ntpExtension_ = ntpExtension;
- }.bind(this));
- },
-
- /**
- * @param {?NtpExtension} ntpExtension
- * @param {number} restoreOnStartup Value of prefs.session.restore_on_startup.
- * @return {boolean}
- * @private
- */
- showIndicator_: function(ntpExtension, restoreOnStartup) {
- return !!ntpExtension && restoreOnStartup == this.prefValues_.OPEN_NEW_TAB;
+ updateNtpExtension);
+ this.addWebUIListener('update-ntp-extension', updateNtpExtension);
},
/**
diff --git a/chromium/chrome/browser/resources/settings/on_startup_page/startup_url_dialog.html b/chromium/chrome/browser/resources/settings/on_startup_page/startup_url_dialog.html
index d2139335785..313bdf7aba0 100644
--- a/chromium/chrome/browser/resources/settings/on_startup_page/startup_url_dialog.html
+++ b/chromium/chrome/browser/resources/settings/on_startup_page/startup_url_dialog.html
@@ -21,10 +21,10 @@
</paper-input>
</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onCancelTap_"
+ <paper-button class="cancel-button" on-click="onCancelTap_"
id="cancel">$i18n{cancel}</paper-button>
<paper-button id="actionButton" class="action-button"
- on-tap="onActionButtonTap_">[[actionButtonText_]]</paper-button>
+ on-click="onActionButtonTap_">[[actionButtonText_]]</paper-button>
</div>
</dialog>
</template>
diff --git a/chromium/chrome/browser/resources/settings/on_startup_page/startup_url_entry.html b/chromium/chrome/browser/resources/settings/on_startup_page/startup_url_entry.html
index cf1a7a9cf81..99699c9a021 100644
--- a/chromium/chrome/browser/resources/settings/on_startup_page/startup_url_entry.html
+++ b/chromium/chrome/browser/resources/settings/on_startup_page/startup_url_entry.html
@@ -25,16 +25,17 @@
<div class="text-elide secondary">[[model.url]]</div>
</div>
<template is="dom-if" if="[[editable]]">
- <button is="paper-icon-button-light" id="dots" on-tap="onDotsTap_"
+ <button is="paper-icon-button-light" id="dots" on-click="onDotsTap_"
title="$i18n{moreActions}" focus-row-control focus-type="menu"
class="icon-more-vert">
</button>
<template is="cr-lazy-render" id="menu">
<dialog is="cr-action-menu">
- <button class="dropdown-item" on-tap="onEditTap_">
+ <button slot="item" class="dropdown-item" on-click="onEditTap_">
$i18n{edit}
</button>
- <button class="dropdown-item" id="remove" on-tap="onRemoveTap_">
+ <button slot="item" class="dropdown-item" id="remove"
+ on-click="onRemoveTap_">
$i18n{onStartupRemove}
</button>
</dialog>
diff --git a/chromium/chrome/browser/resources/settings/on_startup_page/startup_url_entry.js b/chromium/chrome/browser/resources/settings/on_startup_page/startup_url_entry.js
index 8c4460dc865..dd6ac50a045 100644
--- a/chromium/chrome/browser/resources/settings/on_startup_page/startup_url_entry.js
+++ b/chromium/chrome/browser/resources/settings/on_startup_page/startup_url_entry.js
@@ -12,7 +12,7 @@ cr.exportPath('settings');
/**
* The name of the event fired from this element when the "Edit" option is
- * tapped.
+ * clicked.
* @type {string}
*/
settings.EDIT_STARTUP_URL_EVENT = 'edit-startup-url';
diff --git a/chromium/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html b/chromium/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html
index 02b87b2093a..93cd7cd0c94 100644
--- a/chromium/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html
+++ b/chromium/chrome/browser/resources/settings/on_startup_page/startup_urls_page.html
@@ -18,7 +18,7 @@
<template>
<style include="settings-shared action-link iron-flex">
.list-frame {
- @apply(--settings-list-frame-padding);
+ @apply --settings-list-frame-padding;
}
.list-frame > div {
@@ -26,7 +26,7 @@
}
#outer {
- @apply(--settings-list-frame-padding);
+ @apply --settings-list-frame-padding;
max-height: 355px; /** Enough height to show six entries. */
}
@@ -53,13 +53,13 @@
<template is="dom-if" if="[[shouldAllowUrlsEdit_(
prefs.session.startup_urls.enforcement)]]" restamp>
<div class="list-item" id="addPage">
- <a is="action-link" class="list-button" on-tap="onAddPageTap_">
+ <a is="action-link" class="list-button" on-click="onAddPageTap_">
$i18n{onStartupAddNewPage}
</a>
</div>
<div class="list-item" id="useCurrentPages">
<a is="action-link" class="list-button"
- on-tap="onUseCurrentPagesTap_">
+ on-click="onUseCurrentPagesTap_">
$i18n{onStartupUseCurrent}
</a>
</div>
diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/address_edit_dialog.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/address_edit_dialog.html
index 37c189b5231..8d6515df3a8 100644
--- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/address_edit_dialog.html
+++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/address_edit_dialog.html
@@ -51,7 +51,7 @@
transform: scale(0.75);
transform-origin: left;
width: 133%;
- @apply(--paper-input-container-label-floating);
+ @apply --paper-input-container-label-floating;
}
:host-context([dir=rtl]) #select-label {
@@ -144,11 +144,11 @@
</div>
<div slot="button-container">
<paper-button id="cancelButton" class="cancel-button"
- on-tap="onCancelTap_">
+ on-click="onCancelTap_">
$i18n{cancel}
</paper-button>
<paper-button id="saveButton" class="action-button"
- disabled="[[!canSave_]]" on-tap="onSaveButtonTap_">
+ disabled="[[!canSave_]]" on-click="onSaveButtonTap_">
$i18n{save}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.html
index 4da90c016dc..e5193b1a7d1 100644
--- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.html
+++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/autofill_section.html
@@ -65,9 +65,9 @@
}
</style>
<settings-toggle-button id="autofillToggle"
- class="first primary-toggle"
+ class="first"
aria-label="$i18n{autofill}" no-extension-indicator
- label="[[getOnOffLabel_(prefs.autofill.enabled.value)]]"
+ label="$i18n{autofillFormsLabel}"
pref="{{prefs.autofill.enabled}}">
</settings-toggle-button>
<template is="dom-if" if="[[prefs.autofill.enabled.extensionId]]">
@@ -85,7 +85,7 @@
<h2 class="start">$i18n{addresses}</h2>
<paper-button id="addAddress"
class="secondary-button header-aligned-button"
- on-tap="onAddAddressTap_">
+ on-click="onAddAddressTap_">
$i18n{add}
</paper-button>
</div>
@@ -108,13 +108,13 @@
</div>
<template is="dom-if" if="[[item.metadata.isLocal]]">
<button is="paper-icon-button-light" id="addressMenu"
- class="icon-more-vert" on-tap="onAddressMenuTap_"
+ class="icon-more-vert" on-click="onAddressMenuTap_"
title="$i18n{moreActions}">
</button>
</template>
<template is="dom-if" if="[[!item.metadata.isLocal]]">
<button is="paper-icon-button-light" class="icon-external"
- on-tap="onRemoteEditAddressTap_" actionable></button>
+ on-click="onRemoteEditAddressTap_" actionable></button>
</template>
</div>
</template>
@@ -125,10 +125,10 @@
</div>
</div>
<dialog is="cr-action-menu" id="addressSharedMenu">
- <button id="menuEditAddress" class="dropdown-item"
- on-tap="onMenuEditAddressTap_">$i18n{edit}</button>
- <button id="menuRemoveAddress" class="dropdown-item"
- on-tap="onMenuRemoveAddressTap_">$i18n{removeAddress}</button>
+ <button id="menuEditAddress" slot="item" class="dropdown-item"
+ on-click="onMenuEditAddressTap_">$i18n{edit}</button>
+ <button id="menuRemoveAddress" slot="item" class="dropdown-item"
+ on-click="onMenuRemoveAddressTap_">$i18n{removeAddress}</button>
</dialog>
<template is="dom-if" if="[[showAddressDialog_]]" restamp>
<settings-address-edit-dialog address="[[activeAddress]]"
@@ -139,7 +139,7 @@
<h2 class="start">$i18n{creditCards}</h2>
<paper-button id="addCreditCard"
class="secondary-button header-aligned-button"
- on-tap="onAddCreditCardTap_"
+ on-click="onAddCreditCardTap_"
hidden$="[[isDisabled_(prefs.autofill.credit_card_enabled)]]">
$i18n{add}
</paper-button>
@@ -173,13 +173,13 @@
<template is="dom-if" if="[[showDots_(item.metadata)]]">
<button is="paper-icon-button-light" id="creditCardMenu"
class="icon-more-vert" title="$i18n{moreActions}"
- on-tap="onCreditCardMenuTap_">
+ on-click="onCreditCardMenuTap_">
</button>
</template>
<template is="dom-if" if="[[!showDots_(item.metadata)]]">
<button is="paper-icon-button-light" id="remoteCreditCardLink"
class="icon-external"
- on-tap="onRemoteEditCreditCardTap_" actionable></button>
+ on-click="onRemoteEditCreditCardTap_" actionable></button>
</template>
</div>
</div>
@@ -198,13 +198,13 @@
</div>
</div>
<dialog is="cr-action-menu" id="creditCardSharedMenu">
- <button id="menuEditCreditCard" class="dropdown-item"
- on-tap="onMenuEditCreditCardTap_">$i18n{edit}</button>
- <button id="menuRemoveCreditCard" class="dropdown-item"
+ <button id="menuEditCreditCard" slot="item" class="dropdown-item"
+ on-click="onMenuEditCreditCardTap_">$i18n{edit}</button>
+ <button id="menuRemoveCreditCard" slot="item" class="dropdown-item"
hidden$="[[!activeCreditCard.metadata.isLocal]]"
- on-tap="onMenuRemoveCreditCardTap_">$i18n{removeCreditCard}</button>
- <button id="menuClearCreditCard" class="dropdown-item"
- on-tap="onMenuClearCreditCardTap_"
+ on-click="onMenuRemoveCreditCardTap_">$i18n{removeCreditCard}</button>
+ <button id="menuClearCreditCard" slot="item" class="dropdown-item"
+ on-click="onMenuClearCreditCardTap_"
hidden$="[[!activeCreditCard.metadata.isCached]]">
$i18n{clearCreditCard}
</button>
diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/credit_card_edit_dialog.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/credit_card_edit_dialog.html
index 041413545b9..6465c600227 100644
--- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/credit_card_edit_dialog.html
+++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/credit_card_edit_dialog.html
@@ -89,9 +89,9 @@
</div>
<div slot="button-container">
<paper-button id="cancelButton" class="cancel-button"
- on-tap="onCancelButtonTap_">$i18n{cancel}</paper-button>
+ on-click="onCancelButtonTap_">$i18n{cancel}</paper-button>
<paper-button id="saveButton" class="action-button"
- on-tap="onSaveButtonTap_" disabled>$i18n{save}</paper-button>
+ on-click="onSaveButtonTap_" disabled>$i18n{save}</paper-button>
</div>
</dialog>
</template>
diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_edit_dialog.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_edit_dialog.html
index 0a0814697a6..ae402835da2 100644
--- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_edit_dialog.html
+++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_edit_dialog.html
@@ -22,7 +22,7 @@
};
--paper-input-container-label-focus: {
- color: var(--paper-input-container-color, --secondary-text-color);
+ color: var(--secondary-text-color);
};
}
@@ -61,15 +61,15 @@
<button is="paper-icon-button-light" id="showPasswordButton"
class$="[[getIconClass_(item.password)]]"
hidden$="[[item.entry.federationText]]"
- on-tap="onShowPasswordButtonTap_"
+ on-click="onShowPasswordButtonTap_"
title="[[showPasswordTitle_(item.password,
'$i18nPolymer{hidePassword}','$i18nPolymer{showPassword}')]]">
</button>
</div>
</div>
<div slot="button-container">
- <paper-button class="action-button" on-tap="onActionButtonTap_">
- $i18n{passwordsDone}
+ <paper-button class="action-button" on-click="onActionButtonTap_">
+ $i18n{done}
</paper-button>
</div>
</dialog>
diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_list_item.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_list_item.html
index 85dda982896..59f3d8a1e3c 100644
--- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_list_item.html
+++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/password_list_item.html
@@ -48,12 +48,12 @@
<template is="dom-if" if="[[!item.entry.federationText]]">
<input id="password" aria-label=$i18n{editPasswordPasswordLabel}
type="[[getPasswordInputType_(item.password)]]"
- on-tap="onReadonlyInputTap_" class="password-field" readonly
+ on-click="onReadonlyInputTap_" class="password-field" readonly
disabled$="[[!item.password]]"
value="[[getPassword_(item.password)]]">
<button is="paper-icon-button-light" id="showPasswordButton"
class$="[[getIconClass_(item.password)]]"
- on-tap="onShowPasswordButtonTap_"
+ on-click="onShowPasswordButtonTap_"
title="[[showPasswordTitle_(item.password,
'$i18nPolymer{hidePassword}','$i18nPolymer{showPassword}')]]"
focus-row-control focus-type="showPassword">
@@ -66,7 +66,7 @@
</template>
</div>
<button is="paper-icon-button-light" id="passwordMenu"
- class="icon-more-vert" on-tap="onPasswordMenuTap_"
+ class="icon-more-vert" on-click="onPasswordMenuTap_"
title="$i18n{moreActions}" focus-row-control
focus-type="passwordMenu">
</button>
diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.html
index 55dd3fd8ed6..a994bde577f 100644
--- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.html
+++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_and_forms_page.html
@@ -25,10 +25,10 @@
<neon-animatable route-path="default">
<button is="cr-link-row" icon-class="subpage-arrow"
id="autofillManagerButton" label="$i18n{autofill}"
- sub-label="$i18n{autofillDetail}" on-tap="onAutofillTap_">
+ sub-label="$i18n{autofillDetail}" on-click="onAutofillTap_">
</button>
<div class="settings-box two-line">
- <div class="start two-line" on-tap="onPasswordsTap_" actionable
+ <div class="start two-line" on-click="onPasswordsTap_" actionable
id="passwordManagerButton">
<div class="flex">
$i18n{passwords}
diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.html
index 9353ae7e5fd..2cfe136b936 100644
--- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.html
+++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.html
@@ -8,8 +8,15 @@
<dom-module id="passwords-export-dialog">
<template>
<style include="settings-shared iron-flex">
+ paper-progress {
+ width: 100%;
+ --paper-progress-active-color: var(--google-blue-500);
+ }
+ .action-button {
+ -webkit-margin-start: 8px;
+ }
</style>
- <dialog is="cr-dialog" id="dialog" close-text="$i18n{close}">
+ <dialog is="cr-dialog" id="dialog_start" close-text="$i18n{close}">
<div slot="title">$i18n{exportPasswordsTitle}</div>
<div slot="body">
<div class="layout horizontal center">
@@ -18,15 +25,51 @@
</div>
<div slot="button-container">
<paper-button class="secondary-button header-aligned-button"
- on-tap="onCancelButtonTap_">
+ on-click="onCancelButtonTap_" id="cancelButton">
$i18n{cancel}
</paper-button>
<paper-button class="action-button header-aligned-button"
- on-tap="onExportTap_" id="exportPasswordsButton">
+ on-click="onExportTap_" id="exportPasswordsButton">
$i18n{exportPasswords}
</paper-button>
</div>
</dialog>
+
+ <dialog is="cr-dialog" id="dialog_progress" no-cancel="true">
+ <div slot="title">$i18n{exportingPasswordsTitle}</div>
+ <div slot="body">
+ <paper-progress indeterminate class="blue"></paper-progress>
+ </div>
+ <div slot="button-container">
+ <paper-button id="cancel_progress_button"
+ class="secondary-button header-aligned-button"
+ on-click="onCancelProgressButtonTap_">
+ $i18n{cancel}
+ </paper-button>
+ </div>
+ </dialog>
+
+ <dialog is="cr-dialog" id="dialog_error" close-text="$i18n{close}">
+ <div slot="title">[[exportErrorMessage]]</div>
+ <div slot="body">
+ $i18n{exportPasswordsFailTips}
+ <ul>
+ <li>$i18n{exportPasswordsFailTipsEnoughSpace}</li>
+ <li>$i18n{exportPasswordsFailTipsAnotherFolder}</li>
+ </ul>
+ </div>
+ <div slot="button-container">
+ <paper-button class="secondary-button header-aligned-button"
+ on-click="onCancelButtonTap_" id="cancelErrorButton">
+ $i18n{cancel}
+ </paper-button>
+ <paper-button class="action-button header-aligned-button"
+ on-click="onExportTap_" id="tryAgainButton">
+ $i18n{exportPasswordsTryAgain}
+ </paper-button>
+ </div>
+ </dialog>
+
</template>
<script src="passwords_export_dialog.js"></script>
-</dom-module> \ No newline at end of file
+</dom-module>
diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.js b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.js
index da06563ff1c..737b3f504e4 100644
--- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.js
+++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_export_dialog.js
@@ -10,9 +10,43 @@
(function() {
'use strict';
+/**
+ * The states of the export passwords dialog.
+ * @enum {string}
+ */
+const States = {
+ START: 'START',
+ IN_PROGRESS: 'IN_PROGRESS',
+ ERROR: 'ERROR',
+};
+
+const ProgressStatus = chrome.passwordsPrivate.ExportProgressStatus;
+
+/**
+ * The amount of time (ms) between the start of the export and the moment we
+ * start showing the progress bar.
+ * @type {number}
+ */
+const progressBarDelayMs = 100;
+
+/**
+ * The minimum amount of time (ms) that the progress bar will be visible.
+ * @type {number}
+ */
+const progressBarBlockMs = 1000;
+
Polymer({
is: 'passwords-export-dialog',
+ behaviors: [I18nBehavior],
+
+ properties: {
+ /** The error that occurred while exporting. */
+ exportErrorMessage: String,
+ },
+
+ listeners: {'cancel': 'close'},
+
/**
* The interface for callbacks to the browser.
* Defined in passwords_section.js
@@ -21,16 +55,114 @@ Polymer({
*/
passwordManager_: null,
+ /** @private {function(!PasswordManager.PasswordExportProgress):void} */
+ onPasswordsFileExportProgressListener_: null,
+
+ /**
+ * The task that will display the progress bar, if the export doesn't finish
+ * quickly. This is null, unless the task is currently scheduled.
+ * @private {?number}
+ */
+ progressTaskToken_: null,
+
+ /**
+ * The task that will display the completion of the export, if any. We display
+ * the progress bar for at least |progressBarBlockMs|, therefore, if export
+ * finishes earlier, we cache the result in |delayedProgress_| and this task
+ * will consume it. This is null, unless the task is currently scheduled.
+ * @private {?number}
+ */
+ delayedCompletionToken_: null,
+
+ /**
+ * We display the progress bar for at least |progressBarBlockMs|. If progress
+ * is achieved earlier, we store the update here and consume it later.
+ * @private {?PasswordManager.PasswordExportProgress}
+ */
+ delayedProgress_: null,
+
/** @override */
attached: function() {
- this.$.dialog.showModal();
-
this.passwordManager_ = PasswordManagerImpl.getInstance();
+
+ this.switchToDialog_(States.START);
+
+ this.onPasswordsFileExportProgressListener_ =
+ this.onPasswordsFileExportProgress_.bind(this);
+
+ // If export started on a different tab and is still in progress, display a
+ // busy UI.
+ this.passwordManager_.requestExportProgressStatus(status => {
+ if (status == ProgressStatus.IN_PROGRESS)
+ this.switchToDialog_(States.IN_PROGRESS);
+ });
+
+ this.passwordManager_.addPasswordsFileExportProgressListener(
+ this.onPasswordsFileExportProgressListener_);
+ },
+
+ /**
+ * Handles an export progress event by changing the visible dialog or caching
+ * the event for later consumption.
+ * @param {!PasswordManager.PasswordExportProgress} progress
+ * @private
+ */
+ onPasswordsFileExportProgress_(progress) {
+ // If Chrome has already started displaying the progress bar
+ // (|progressTaskToken_ is null) and hasn't completed its minimum display
+ // time (|delayedCompletionToken_| is not null) progress should be cached
+ // for consumption when the blocking time ends.
+ const progressBlocked =
+ !this.progressTaskToken_ && !!this.delayedCompletionToken_;
+ if (!progressBlocked) {
+ clearTimeout(this.progressTaskToken_);
+ this.progressTaskToken_ = null;
+ this.processProgress_(progress);
+ } else {
+ this.delayedProgress_ = progress;
+ }
+ },
+
+ /**
+ * Displays the progress bar and suspends further UI updates for
+ * |progressBarBlockMs|.
+ * @private
+ */
+ progressTask_() {
+ this.progressTaskToken_ = null;
+ this.switchToDialog_(States.IN_PROGRESS);
+
+ this.delayedCompletionToken_ =
+ setTimeout(this.delayedCompletionTask_.bind(this), progressBarBlockMs);
+ },
+
+ /**
+ * Unblocks progress after showing the progress bar for |progressBarBlock|ms
+ * and processes any progress that was delayed.
+ * @private
+ */
+ delayedCompletionTask_() {
+ this.delayedCompletionToken_ = null;
+ if (this.delayedProgress_) {
+ this.processProgress_(this.delayedProgress_);
+ this.delayedProgress_ = null;
+ }
},
/** Closes the dialog. */
close: function() {
- this.$.dialog.close();
+ clearTimeout(this.progressTaskToken_);
+ clearTimeout(this.delayedCompletionToken_);
+ this.progressTaskToken_ = null;
+ this.delayedCompletionToken_ = null;
+ this.passwordManager_.removePasswordsFileExportProgressListener(
+ this.onPasswordsFileExportProgressListener_);
+ if (this.$.dialog_start.open)
+ this.$.dialog_start.close();
+ if (this.$.dialog_progress.open)
+ this.$.dialog_progress.close();
+ if (this.$.dialog_error.open)
+ this.$.dialog_error.close();
},
/**
@@ -38,7 +170,56 @@ Polymer({
* @private
*/
onExportTap_: function() {
- this.passwordManager_.exportPasswords();
+ this.passwordManager_.exportPasswords(() => {
+ if (chrome.runtime.lastError &&
+ chrome.runtime.lastError.message == 'in-progress') {
+ // Exporting was started by a different call to exportPasswords() and is
+ // is still in progress. This UI needs to be updated to the current
+ // status.
+ this.switchToDialog_(States.IN_PROGRESS);
+ }
+ });
+ },
+
+ /**
+ * Prepares and displays the appropriate view (with delay, if necessary).
+ * @param {!PasswordManager.PasswordExportProgress} progress
+ * @private
+ */
+ processProgress_(progress) {
+ if (progress.status == ProgressStatus.IN_PROGRESS) {
+ this.progressTaskToken_ =
+ setTimeout(this.progressTask_.bind(this), progressBarDelayMs);
+ return;
+ }
+ if (progress.status == ProgressStatus.SUCCEEDED) {
+ this.close();
+ return;
+ }
+ if (progress.status == ProgressStatus.FAILED_WRITE_FAILED) {
+ this.exportErrorMessage =
+ this.i18n('exportPasswordsFailTitle', progress.folderName);
+ this.switchToDialog_(States.ERROR);
+ return;
+ }
+ },
+
+ /**
+ * Opens the specified dialog and hides the others.
+ * @param {!States} state the dialog to open.
+ * @private
+ */
+ switchToDialog_(state) {
+ this.$.dialog_start.open = false;
+ this.$.dialog_error.open = false;
+ this.$.dialog_progress.open = false;
+
+ if (state == States.START)
+ this.$.dialog_start.showModal();
+ if (state == States.ERROR)
+ this.$.dialog_error.showModal();
+ if (state == States.IN_PROGRESS)
+ this.$.dialog_progress.showModal();
},
/**
@@ -48,5 +229,15 @@ Polymer({
onCancelButtonTap_: function() {
this.close();
},
+
+ /**
+ * Handler for tapping the 'cancel' button on the progress dialog. It should
+ * cancel the export and dismiss the dialog.
+ * @private
+ */
+ onCancelProgressButtonTap_: function() {
+ this.passwordManager_.cancelExportPasswords();
+ this.close();
+ },
});
})(); \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html
index 4b47e458f25..956780bb648 100644
--- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html
+++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.html
@@ -52,12 +52,16 @@
#undoToast {
z-index: 1;
- }
+ }
+
+ #exportImportMenuButton {
+ -webkit-margin-end: 0;
+ }
</style>
<settings-toggle-button id="passwordToggle"
- class="first primary-toggle"
+ class="first"
aria-label="$i18n{passwords}" no-extension-indicator
- label="[[getOnOffLabel_(prefs.credentials_enable_service.value)]]"
+ label="$i18n{passwordsSavePasswordsLabel}"
pref="{{prefs.credentials_enable_service}}">
</settings-toggle-button>
<template is="dom-if"
@@ -89,7 +93,7 @@
if="[[showImportOrExportPasswords_(
showExportPasswords_, showImportPasswords_)]]">
<button is="paper-icon-button-light" id="exportImportMenuButton"
- class="icon-more-vert" on-tap="onImportExportMenuTap_"
+ class="icon-more-vert" on-click="onImportExportMenuTap_"
title="$i18n{moreActions}" focus-type="exportImportMenuButton">
</button>
</template>
@@ -122,20 +126,20 @@
</div>
</div>
<dialog is="cr-action-menu" id="menu">
- <button id="menuEditPassword" class="dropdown-item"
- on-tap="onMenuEditPasswordTap_">$i18n{passwordViewDetails}</button>
- <button id="menuRemovePassword" class="dropdown-item"
- on-tap="onMenuRemovePasswordTap_">$i18n{removePassword}</button>
+ <button id="menuEditPassword" slot="item" class="dropdown-item"
+ on-click="onMenuEditPasswordTap_">$i18n{passwordViewDetails}</button>
+ <button id="menuRemovePassword" slot="item" class="dropdown-item"
+ on-click="onMenuRemovePasswordTap_">$i18n{removePassword}</button>
</dialog>
<dialog is="cr-action-menu" id="exportImportMenu">
- <template is="dom-if" if="[[showImportPasswords_]]">
- <button id="menuImportPassword" class="dropdown-item"
- on-tap="onImportTap_">$i18n{import}</button>
- </template>
- <template is="dom-if" if="[[showExportPasswords_]]">
- <button id="menuExportPassword" class="dropdown-item"
- on-tap="onExportTap_">$i18n{export}</button>
- </template>
+ <button id="menuImportPassword" slot="item" class="dropdown-item"
+ on-click="onImportTap_" hidden="[[!showImportPasswords_]]">
+ $i18n{import}
+ </button>
+ <button id="menuExportPassword" slot="item" class="dropdown-item"
+ on-click="onExportTap_" hidden="[[!showExportPasswords_]]">
+ $i18n{exportMenuItem}
+ </button>
</dialog>
<template is="dom-if" if="[[showPasswordsExportDialog_]]" restamp>
<passwords-export-dialog on-close="onPasswordsExportDialogClosed_">
@@ -148,7 +152,7 @@
</template>
<cr-toast id="undoToast" duration="[[toastDuration_]]">
<div id="undoLabel">$i18n{passwordDeleted}</div>
- <paper-button id="undoButton" on-tap="onUndoButtonTap_">
+ <paper-button id="undoButton" on-click="onUndoButtonTap_">
$i18n{undoRemovePassword}
</paper-button>
</cr-toast>
@@ -165,7 +169,7 @@
</a>
</div>
<button is="paper-icon-button-light" id="removeExceptionButton"
- class="icon-clear" on-tap="onRemoveExceptionButtonTap_"
+ class="icon-clear" on-click="onRemoveExceptionButtonTap_"
tabindex$="[[tabIndex]]"
title="$i18n{deletePasswordException}">
</button>
diff --git a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js
index d4f0f70ac06..33cb93e78a8 100644
--- a/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js
+++ b/chromium/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js
@@ -10,6 +10,8 @@
/**
* Interface for all callbacks to the password API.
+ * TODO(crbug.com/802352) Move the PasswordManager proxy to a separate
+ * location.
* @interface
*/
class PasswordManager {
@@ -84,8 +86,32 @@ class PasswordManager {
/**
* Triggers the dialogue for exporting passwords.
+ * @param {function():void} callback
*/
- exportPasswords() {}
+ exportPasswords(callback) {}
+
+ /**
+ * Cancels the ongoing export of passwords.
+ */
+ cancelExportPasswords(callback) {}
+
+ /**
+ * Queries the status of any ongoing export.
+ * @param {function(!PasswordManager.ExportProgressStatus):void} callback
+ */
+ requestExportProgressStatus(callback) {}
+
+ /**
+ * Add an observer to the export progress.
+ * @param {function(!PasswordManager.PasswordExportProgress):void} listener
+ */
+ addPasswordsFileExportProgressListener(listener) {}
+
+ /**
+ * Remove an observer from the export progress.
+ * @param {function(!PasswordManager.PasswordExportProgress):void} listener
+ */
+ removePasswordsFileExportProgressListener(listener) {}
}
/** @typedef {chrome.passwordsPrivate.PasswordUiEntry} */
@@ -103,6 +129,12 @@ PasswordManager.PlaintextPasswordEvent;
/** @typedef {{ entry: !PasswordManager.PasswordUiEntry, password: string }} */
PasswordManager.UiEntryWithPassword;
+/** @typedef {chrome.passwordsPrivate.PasswordExportProgress} */
+PasswordManager.PasswordExportProgress;
+
+/** @typedef {chrome.passwordsPrivate.ExportProgressStatus} */
+PasswordManager.ExportProgressStatus;
+
/**
* Implementation that accesses the private API.
* @implements {PasswordManager}
@@ -176,8 +208,29 @@ class PasswordManagerImpl {
}
/** @override */
- exportPasswords() {
- chrome.passwordsPrivate.exportPasswords();
+ exportPasswords(callback) {
+ chrome.passwordsPrivate.exportPasswords(callback);
+ }
+
+ /** @override */
+ cancelExportPasswords() {
+ chrome.passwordsPrivate.cancelExportPasswords();
+ }
+
+ /** @override */
+ requestExportProgressStatus(callback) {
+ chrome.passwordsPrivate.requestExportProgressStatus(callback);
+ }
+
+ /** @override */
+ addPasswordsFileExportProgressListener(listener) {
+ chrome.passwordsPrivate.onPasswordsFileExportProgress.addListener(listener);
+ }
+
+ /** @override */
+ removePasswordsFileExportProgressListener(listener) {
+ chrome.passwordsPrivate.onPasswordsFileExportProgress.removeListener(
+ listener);
}
}
@@ -251,10 +304,7 @@ Polymer({
/** @private */
showExportPasswords_: {
type: Boolean,
- value: function() {
- return loadTimeData.valueExists('showExportPasswords') &&
- loadTimeData.getBoolean('showExportPasswords');
- }
+ computed: 'showExportPasswordsAndReady_(savedPasswords)'
},
/** @private */
@@ -489,6 +539,7 @@ Polymer({
*/
onImportTap_: function() {
this.passwordManager_.importPasswords();
+ this.$.exportImportMenu.close();
},
/**
@@ -503,6 +554,8 @@ Polymer({
/** @private */
onPasswordsExportDialogClosed_: function() {
this.showPasswordsExportDialog_ = false;
+ cr.ui.focusWithoutInk(assert(this.activeDialogAnchor_));
+ this.activeDialogAnchor_ = null;
},
/**
@@ -538,6 +591,16 @@ Polymer({
/**
* @private
+ * @param {!Array<!PasswordManager.PasswordUiEntry>} savedPasswords
+ */
+ showExportPasswordsAndReady_: function(savedPasswords) {
+ return loadTimeData.valueExists('showExportPasswords') &&
+ loadTimeData.getBoolean('showExportPasswords') &&
+ savedPasswords.length > 0;
+ },
+
+ /**
+ * @private
* @param {boolean} showExportPasswords
* @param {boolean} showImportPasswords
* @return {boolean}
diff --git a/chromium/chrome/browser/resources/settings/people_page/change_picture.html b/chromium/chrome/browser/resources/settings/people_page/change_picture.html
index 87ec0b1917d..f14495a8af7 100644
--- a/chromium/chrome/browser/resources/settings/people_page/change_picture.html
+++ b/chromium/chrome/browser/resources/settings/people_page/change_picture.html
@@ -106,7 +106,7 @@
choose-file-label="$i18n{chooseFile}"
old-image-label="$i18n{oldPhoto}"
profile-image-label="$i18n{profilePhoto}"
- take-photo-label="$i18n{takePhoto}">
+ take-photo-label="$i18n{takePhoto}"
capture-video-label="$i18n{captureVideo}">
</cr-picture-list>
</div>
diff --git a/chromium/chrome/browser/resources/settings/people_page/change_picture.js b/chromium/chrome/browser/resources/settings/people_page/change_picture.js
index 48eaa44ed61..eeca32878b8 100644
--- a/chromium/chrome/browser/resources/settings/people_page/change_picture.js
+++ b/chromium/chrome/browser/resources/settings/people_page/change_picture.js
@@ -81,6 +81,9 @@ Polymer({
/** @private {?CrPictureListElement} */
pictureList_: null,
+ /** @private {boolean} */
+ oldImagePending_: false,
+
/** @override */
ready: function() {
this.browserProxy_ = settings.ChangePictureBrowserProxyImpl.getInstance();
@@ -144,6 +147,7 @@ Polymer({
* @private
*/
receiveOldImage_: function(imageInfo) {
+ this.oldImagePending_ = false;
this.pictureList_.setOldImageUrl(imageInfo.url, imageInfo.index);
},
@@ -216,6 +220,7 @@ Polymer({
* @private
*/
onPhotoTaken_: function(event) {
+ this.oldImagePending_ = true;
this.browserProxy_.photoTaken(event.detail.photoDataUrl);
this.pictureList_.setOldImageUrl(event.detail.photoDataUrl);
this.pictureList_.setFocus();
@@ -235,6 +240,9 @@ Polymer({
/** @private */
onDiscardImage_: function() {
+ // Prevent image from being discarded if old image is pending.
+ if (this.oldImagePending_)
+ return;
this.pictureList_.setOldImageUrl(CrPicture.kDefaultImageUrl);
// Revert to profile image as we don't know what last used default image is.
this.browserProxy_.selectProfileImage();
diff --git a/chromium/chrome/browser/resources/settings/people_page/change_picture_browser_proxy.js b/chromium/chrome/browser/resources/settings/people_page/change_picture_browser_proxy.js
index 7295e66f6a0..3cd333e51bd 100644
--- a/chromium/chrome/browser/resources/settings/people_page/change_picture_browser_proxy.js
+++ b/chromium/chrome/browser/resources/settings/people_page/change_picture_browser_proxy.js
@@ -51,8 +51,9 @@ cr.define('settings', function() {
selectProfileImage() {}
/**
- * Provides the taken photo as a data URL to the C++. No response is
- * expected.
+ * Provides the taken photo as a data URL to the C++ and sets the user
+ * image to the 'old' image. As a response, the C++ sends the
+ * 'old-image-changed' WebUIListener event.
* @param {string} photoDataUrl
*/
photoTaken(photoDataUrl) {}
diff --git a/chromium/chrome/browser/resources/settings/people_page/compiled_resources2.gyp b/chromium/chrome/browser/resources/settings/people_page/compiled_resources2.gyp
index 3159cd12ca1..5c15adeb340 100644
--- a/chromium/chrome/browser/resources/settings/people_page/compiled_resources2.gyp
+++ b/chromium/chrome/browser/resources/settings/people_page/compiled_resources2.gyp
@@ -255,5 +255,19 @@
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
+ {
+ 'target_name': 'sync_account_control',
+ 'dependencies': [
+ '../compiled_resources2.gyp:route',
+ '../prefs/compiled_resources2.gyp:prefs_behavior',
+ '<(DEPTH)/ui/webui/resources/cr_elements/cr_action_menu/compiled_resources2.gyp:cr_action_menu',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:icon',
+ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior',
+ 'profile_info_browser_proxy',
+ 'sync_browser_proxy',
+ ],
+ 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+ },
],
}
diff --git a/chromium/chrome/browser/resources/settings/people_page/easy_unlock_turn_off_dialog.html b/chromium/chrome/browser/resources/settings/people_page/easy_unlock_turn_off_dialog.html
index 211d825ee32..dd65510f60f 100644
--- a/chromium/chrome/browser/resources/settings/people_page/easy_unlock_turn_off_dialog.html
+++ b/chromium/chrome/browser/resources/settings/people_page/easy_unlock_turn_off_dialog.html
@@ -21,11 +21,12 @@
hidden="[[isButtonBarHidden_(status_)]]">
<paper-spinner-lite active="[[isSpinnerActive_(status_)]]">
</paper-spinner-lite>
- <paper-button class="cancel-button" on-tap="onCancelTap_"
+ <paper-button class="cancel-button" on-click="onCancelTap_"
hidden="[[isCancelButtonHidden_(status_)]]">
$i18n{cancel}
</paper-button>
- <paper-button id="turnOff" class="action-button" on-tap="onTurnOffTap_"
+ <paper-button id="turnOff" class="action-button"
+ on-click="onTurnOffTap_"
disabled="[[!isTurnOffButtonEnabled_(status_)]]">
[[getTurnOffButtonText_(status_)]]
</paper-button>
diff --git a/chromium/chrome/browser/resources/settings/people_page/fingerprint_list.html b/chromium/chrome/browser/resources/settings/people_page/fingerprint_list.html
index 6c62e3e44af..c98c91b9c8a 100644
--- a/chromium/chrome/browser/resources/settings/people_page/fingerprint_list.html
+++ b/chromium/chrome/browser/resources/settings/people_page/fingerprint_list.html
@@ -29,7 +29,7 @@
}
.body {
- @apply(--settings-list-frame-padding);
+ @apply --settings-list-frame-padding;
}
.list-item {
@@ -55,14 +55,14 @@
on-change="onFingerprintLabelChanged_">
</paper-input>
<button is="paper-icon-button-light" class="icon-delete-gray"
- on-tap="onFingerprintDeleteTapped_">
+ on-click="onFingerprintDeleteTapped_">
</button>
</div>
</template>
</iron-list>
<div class="continuation">
<paper-button id="addFingerprint" class="add-link action-button"
- on-tap="openAddFingerprintDialog_">
+ on-click="openAddFingerprintDialog_">
$i18n{lockScreenAddFingerprint}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/people_page/import_data_dialog.html b/chromium/chrome/browser/resources/settings/people_page/import_data_dialog.html
index 985a4aec02c..cb07d334450 100644
--- a/chromium/chrome/browser/resources/settings/people_page/import_data_dialog.html
+++ b/chromium/chrome/browser/resources/settings/people_page/import_data_dialog.html
@@ -104,7 +104,7 @@
importStatusEnum_.SUCCEEDED, importStatus_)]]"
disabled="[[hasImportStatus_(
importStatusEnum_.IN_PROGRESS, importStatus_)]]"
- on-tap="closeDialog_">
+ on-click="closeDialog_">
$i18n{cancel}
</paper-button>
<paper-button id="import" class="action-button"
@@ -112,14 +112,14 @@
importStatusEnum_.SUCCEEDED, importStatus_)]]"
disabled="[[shouldDisableImport_(
importStatus_, noImportDataTypeSelected_)]]"
- on-tap="onActionButtonTap_">
+ on-click="onActionButtonTap_">
[[getActionButtonText_(selected_)]]
</paper-button>
<paper-button id="done" class="action-button"
hidden$="[[!hasImportStatus_(
importStatusEnum_.SUCCEEDED, importStatus_)]]"
- on-tap="closeDialog_">$i18n{done}</paper-button>
+ on-click="closeDialog_">$i18n{done}</paper-button>
</div>
</dialog>
</template>
diff --git a/chromium/chrome/browser/resources/settings/people_page/lock_screen.html b/chromium/chrome/browser/resources/settings/people_page/lock_screen.html
index cdaa2505cae..d12222f1f36 100644
--- a/chromium/chrome/browser/resources/settings/people_page/lock_screen.html
+++ b/chromium/chrome/browser/resources/settings/people_page/lock_screen.html
@@ -59,7 +59,7 @@
}
#easyUnlockSettingsCollapsible {
- @apply(--settings-list-frame-padding);
+ @apply --settings-list-frame-padding;
}
.no-padding {
@@ -111,7 +111,7 @@
<div id="pinPasswordSecondaryActionDiv"
class="secondary-action">
<paper-button id="setupPinButton" class="secondary-button"
- on-tap="onConfigurePin_">
+ on-click="onConfigurePin_">
[[getSetupPinText_(hasPin)]]
</paper-button>
</div>
@@ -140,7 +140,7 @@
<div class="separator"></div>
<div class="secondary-action">
<paper-button class="secondary-button"
- on-tap="onEditFingerprints_"
+ on-click="onEditFingerprints_"
aria-label="$i18n{lockScreenEditFingerprints}"
aria-descibedby="lockScreenEditFingerprintsSecondary">
$i18n{lockScreenSetupFingerprintButton}
@@ -167,13 +167,13 @@
<div class="separator"></div>
<template is="dom-if" if="[[!easyUnlockEnabled_]]">
<paper-button id="easyUnlockSetup" class="secondary-button"
- on-tap="onEasyUnlockSetupTap_">
+ on-click="onEasyUnlockSetupTap_">
$i18n{easyUnlockSetupButton}
</paper-button>
</template>
<template is="dom-if" if="[[easyUnlockEnabled_]]">
<paper-button id="easyUnlockTurnOff" class="secondary-button"
- on-tap="onEasyUnlockTurnOffTap_">
+ on-click="onEasyUnlockTurnOffTap_">
$i18n{easyUnlockTurnOffButton}
</paper-button>
</template>
diff --git a/chromium/chrome/browser/resources/settings/people_page/password_prompt_dialog.html b/chromium/chrome/browser/resources/settings/people_page/password_prompt_dialog.html
index 9a7dac7a58c..68056695bfe 100644
--- a/chromium/chrome/browser/resources/settings/people_page/password_prompt_dialog.html
+++ b/chromium/chrome/browser/resources/settings/people_page/password_prompt_dialog.html
@@ -34,11 +34,11 @@
</paper-input>
</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onCancelTap_">
+ <paper-button class="cancel-button" on-click="onCancelTap_">
$i18n{cancel}
</paper-button>
- <paper-button class="action-button" on-tap="submitPassword_"
+ <paper-button class="action-button" on-click="submitPassword_"
disabled$="[[!enableConfirm_(password_, passwordInvalid_)]]">
$i18n{confirm}
</paper-button>
diff --git a/chromium/chrome/browser/resources/settings/people_page/people_page.html b/chromium/chrome/browser/resources/settings/people_page/people_page.html
index 4ab0dfcd81f..d7b6822e5ec 100644
--- a/chromium/chrome/browser/resources/settings/people_page/people_page.html
+++ b/chromium/chrome/browser/resources/settings/people_page/people_page.html
@@ -33,6 +33,7 @@
<link rel="import" href="users_page.html">
</if>
<if expr="not chromeos">
+<link rel="import" href="sync_account_control.html">
<link rel="import" href="import_data_dialog.html">
<link rel="import" href="manage_profile.html">
</if>
@@ -46,9 +47,7 @@
}
#profile-icon {
- background-position: center;
- background-repeat: no-repeat;
- background-size: cover;
+ background: center / cover no-repeat;
border-radius: 20px;
flex-shrink: 0;
height: 40px;
@@ -102,66 +101,80 @@
<settings-animated-pages id="pages" section="people"
focus-config="[[focusConfig_]]">
<neon-animatable route-path="default">
- <div id="picture-subpage-trigger" class="settings-box first two-line">
- <template is="dom-if" if="[[syncStatus]]">
- <div id="profile-icon" on-tap="onPictureTap_" actionable
- style="background-image: [[getIconImageSet_(profileIconUrl_)]]">
- </div>
<if expr="not chromeos">
- <div class="middle two-line no-min-width"
- on-tap="onProfileNameTap_" actionable>
-</if>
-<if expr="chromeos">
- <div class="middle two-line no-min-width" on-tap="onPictureTap_"
- actionable>
+ <template is="dom-if" if="[[shouldShowSyncAccountControl_(diceEnabled_,
+ syncStatus.syncSystemEnabled, syncStatus.signinAllowed)]]">
+ <settings-sync-account-control
+ promo-label="$i18n{peopleSignInPrompt}"
+ promo-secondary-label="$i18n{peopleSignInPromptSecondary}">
+ </settings-sync-account-control>
+ </template>
+ <template is="dom-if" if="[[!diceEnabled_]]">
</if>
- <div class="flex text-elide">
- <span id="profile-name">[[profileName_]]</span>
- <div class="secondary" hidden="[[!syncStatus.signedIn]]">
- [[syncStatus.signedInUsername]]
- </div>
+ <div id="picture-subpage-trigger" class="settings-box first two-line">
+ <template is="dom-if" if="[[syncStatus]]">
+ <div id="profile-icon" on-click="onProfileTap_" actionable
+ style="background-image: [[getIconImageSet_(
+ profileIconUrl_)]]">
</div>
+ <div class="middle two-line no-min-width" on-click="onProfileTap_"
+ actionable>
+ <div class="flex text-elide">
+ <span id="profile-name">[[profileName_]]</span>
+ <div class="secondary" hidden="[[!syncStatus.signedIn]]">
+ [[syncStatus.signedInUsername]]
+ </div>
+ </div>
<if expr="not chromeos">
- <button class="subpage-arrow" is="paper-icon-button-light"
- aria-label="$i18n{editPerson}"
- aria-describedby="profile-name"></button>
+ <button class="subpage-arrow" is="paper-icon-button-light"
+ aria-label="$i18n{editPerson}"
+ aria-describedby="profile-name"></button>
</if>
<if expr="chromeos">
- <button class="subpage-arrow" is="paper-icon-button-light"
- aria-label="$i18n{changePictureTitle}"
- aria-describedby="profile-name"></button>
+ <button class="subpage-arrow" is="paper-icon-button-light"
+ aria-label="$i18n{changePictureTitle}"
+ aria-describedby="profile-name"></button>
</if>
- </div>
+ </div>
<if expr="not chromeos">
- <template is="dom-if" if="[[showSignin_(syncStatus)]]">
- <div class="separator"></div>
- <paper-button class="primary-button" on-tap="onSigninTap_"
- disabled="[[syncStatus.setupInProgress]]">
- $i18n{syncSignin}
- </paper-button>
- </template>
- <template is="dom-if" if="[[syncStatus.signedIn]]">
- <div class="separator"></div>
- <paper-button id="disconnectButton" class="secondary-button"
- on-tap="onDisconnectTap_"
- disabled="[[syncStatus.setupInProgress]]">
- $i18n{syncDisconnect}
- </paper-button>
+ <template is="dom-if" if="[[showSignin_(syncStatus)]]">
+ <div class="separator"></div>
+ <paper-button class="primary-button" on-click="onSigninTap_"
+ disabled="[[syncStatus.setupInProgress]]">
+ $i18n{syncSignin}
+ </paper-button>
+ </template>
+ <template is="dom-if" if="[[syncStatus.signedIn]]">
+ <div class="separator"></div>
+ <paper-button id="disconnectButton" class="secondary-button"
+ on-click="onDisconnectTap_"
+ disabled="[[syncStatus.setupInProgress]]">
+ $i18n{syncDisconnect}
+ </paper-button>
+ </template>
+</if>
</template>
+ </div>
+<if expr="not chromeos">
+ </template> <!-- if="[[!diceEnabled_]]" -->
</if>
- </template>
- </div>
<template is="dom-if" if="[[!syncStatus.signedIn]]">
- <div class="settings-box two-line"
- hidden="[[!syncStatus.signinAllowed]]">
- <div class="start">
- $i18n{syncOverview}
- <a target="_blank" href="$i18n{syncLearnMoreUrl}">
- $i18n{learnMore}
- </a>
+<if expr="not chromeos">
+ <template is="dom-if" if="[[!diceEnabled_]]">
+</if>
+ <div class="settings-box two-line" id="sync-overview"
+ hidden="[[!syncStatus.signinAllowed]]">
+ <div class="start">
+ $i18n{syncOverview}
+ <a target="_blank" href="$i18n{syncLearnMoreUrl}">
+ $i18n{learnMore}
+ </a>
+ </div>
</div>
- </div>
+<if expr="not chromeos">
+ </template> <!-- if="[[!diceEnabled_]]" -->
+</if>
<div class="settings-box" hidden="[[syncStatus.signinAllowed]]">
$i18n{syncDisabledByAdministrator}
</div>
@@ -182,7 +195,7 @@
<template is="dom-if"
if="[[isAdvancedSyncSettingsVisible_(syncStatus)]]">
- <div class="settings-box two-line" on-tap="onSyncTap_"
+ <div class="settings-box two-line" on-click="onSyncTap_"
id="sync-status" actionable$="[[isSyncStatusActionable_(
syncStatus)]]">
<div class="icon-container">
@@ -202,9 +215,20 @@
</div>
</template>
+<if expr="not chromeos">
+ <template is="dom-if" if="[[diceEnabled_]]">
+ <div class="settings-box" id="edit-profile" on-click="onProfileTap_"
+ actionable>
+ <div class="start">$i18n{profileNameAndPicture}</div>
+ <button class="subpage-arrow" is="paper-icon-button-light"
+ aria-label="$i18n{editPerson}"></button>
+ </div>
+ </template>
+</if>
+
<if expr="chromeos">
<div id="lock-screen-subpage-trigger" class="settings-box two-line"
- actionable on-tap="onConfigureLockTap_">
+ actionable on-click="onConfigureLockTap_">
<div class="start">
$i18n{lockScreenTitle}
<div class="secondary" id="lockScreenSecondary">
@@ -219,14 +243,14 @@
</if>
<div id="manage-other-people-subpage-trigger"
- class="settings-box" on-tap="onManageOtherPeople_" actionable>
+ class="settings-box" on-click="onManageOtherPeople_" actionable>
<div class="start">$i18n{manageOtherPeople}</div>
<button class="subpage-arrow" is="paper-icon-button-light"
aria-label="$i18n{manageOtherPeople}"></button>
</div>
<if expr="not chromeos">
- <div class="settings-box" on-tap="onImportDataTap_" actionable>
+ <div class="settings-box" on-click="onImportDataTap_" actionable>
<div class="start">$i18n{importTitle}</div>
<button id="importDataDialogTrigger" class="subpage-arrow"
is="paper-icon-button-light" aria-label="$i18n{importTitle}">
@@ -234,16 +258,6 @@
</div>
</if>
- <template is="dom-if" if="[[profileManagesSupervisedUsers_]]">
- <a id="manageSupervisedUsersContainer"
- class="settings-box inherit-color no-outline" tabindex="-1"
- target="_blank" href="$i18n{supervisedUsersUrl}">
- <div class="start">$i18n{manageSupervisedUsers}</div>
- <button class="icon-external" is="paper-icon-button-light"
- actionable aria-label="$i18n{manageSupervisedUsers}">
- </button>
- </a>
- </template>
</neon-animatable>
<template is="dom-if" route-path="/syncSetup"
no-search$="[[!isAdvancedSyncSettingsVisible_(syncStatus)]]">
@@ -274,10 +288,7 @@
<settings-subpage
associated-control="[[$$('#manage-other-people-subpage-trigger')]]"
page-title="$i18n{manageOtherPeople}">
- <settings-users-page
- prefs="{{prefs}}"
- profile-manages-supervised-users=
- "[[profileManagesSupervisedUsers_]]">
+ <settings-users-page prefs="{{prefs}}">
</settings-users-page>
</settings-subpage>
</template>
@@ -313,16 +324,16 @@
</div>
</div>
<div slot="button-container">
- <paper-button on-tap="onDisconnectCancel_" class="cancel-button">
+ <paper-button on-click="onDisconnectCancel_" class="cancel-button">
$i18n{cancel}
</paper-button>
<paper-button id="disconnectConfirm" class="action-button"
- hidden="[[syncStatus.domain]]" on-tap="onDisconnectConfirm_">
+ hidden="[[syncStatus.domain]]" on-click="onDisconnectConfirm_">
$i18n{syncDisconnect}
</paper-button>
<paper-button id="disconnectManagedProfileConfirm"
class="action-button" hidden="[[!syncStatus.domain]]"
- on-tap="onDisconnectConfirm_">
+ on-click="onDisconnectConfirm_">
$i18n{syncDisconnectConfirm}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/people_page/people_page.js b/chromium/chrome/browser/resources/settings/people_page/people_page.js
index 60555a4e8a4..d1be1d2c850 100644
--- a/chromium/chrome/browser/resources/settings/people_page/people_page.js
+++ b/chromium/chrome/browser/resources/settings/people_page/people_page.js
@@ -25,6 +25,22 @@ Polymer({
notify: true,
},
+ // <if expr="not chromeos">
+ /**
+ * This flag is used to conditionally show a set of new sign-in UIs to the
+ * profiles that have been migrated to be consistent with the web sign-ins.
+ * TODO(scottchen): In the future when all profiles are completely migrated,
+ * this should be removed, and UIs hidden behind it should become default.
+ * @private
+ */
+ diceEnabled_: {
+ type: Boolean,
+ value: function() {
+ return loadTimeData.getBoolean('diceEnabled');
+ },
+ },
+ // </if>
+
/**
* The current sync status, supplied by SyncBrowserProxy.
* @type {?settings.SyncStatus}
@@ -33,33 +49,33 @@ Polymer({
/**
* The currently selected profile icon URL. May be a data URL.
+ * @private
*/
profileIconUrl_: String,
/**
* The current profile name.
+ * @private
*/
profileName_: String,
/**
- * True if the current profile manages supervised users.
- */
- profileManagesSupervisedUsers_: Boolean,
-
- /**
* The profile deletion warning. The message indicates the number of
* profile stats that will be deleted if a non-zero count for the profile
* stats is returned from the browser.
+ * @private
*/
deleteProfileWarning_: String,
/**
* True if the profile deletion warning is visible.
+ * @private
*/
deleteProfileWarningVisible_: Boolean,
/**
* True if the checkbox to delete the profile has been checked.
+ * @private
*/
deleteProfile_: Boolean,
@@ -134,12 +150,6 @@ Polymer({
this.addWebUIListener(
'profile-info-changed', this.handleProfileInfo_.bind(this));
- profileInfoProxy.getProfileManagesSupervisedUsers().then(
- this.handleProfileManagesSupervisedUsers_.bind(this));
- this.addWebUIListener(
- 'profile-manages-supervised-users-changed',
- this.handleProfileManagesSupervisedUsers_.bind(this));
-
this.addWebUIListener(
'profile-stats-count-ready', this.handleProfileStatsCount_.bind(this));
@@ -209,15 +219,6 @@ Polymer({
},
/**
- * Handler for when the profile starts or stops managing supervised users.
- * @private
- * @param {boolean} managesSupervisedUsers
- */
- handleProfileManagesSupervisedUsers_: function(managesSupervisedUsers) {
- this.profileManagesSupervisedUsers_ = managesSupervisedUsers;
- },
-
- /**
* Handler for when the profile stats count is pushed from the browser.
* @param {number} count
* @private
@@ -251,7 +252,7 @@ Polymer({
},
/** @private */
- onPictureTap_: function() {
+ onProfileTap_: function() {
// <if expr="chromeos">
settings.navigateTo(settings.routes.CHANGE_PICTURE);
// </if>
@@ -260,13 +261,6 @@ Polymer({
// </if>
},
- // <if expr="not chromeos">
- /** @private */
- onProfileNameTap_: function() {
- settings.navigateTo(settings.routes.MANAGE_PROFILE);
- },
- // </if>
-
/** @private */
onSigninTap_: function() {
this.syncBrowserProxy_.startSignIn();
@@ -275,7 +269,16 @@ Polymer({
/** @private */
onDisconnectClosed_: function() {
this.showDisconnectDialog_ = false;
+ // <if expr="not chromeos">
+ if (!this.diceEnabled_) {
+ // If DICE-enabled, this button won't exist here.
+ cr.ui.focusWithoutInk(assert(this.$$('#disconnectButton')));
+ }
+ // </if>
+
+ // <if expr="chromeos">
cr.ui.focusWithoutInk(assert(this.$$('#disconnectButton')));
+ // </if>
if (settings.getCurrentRoute() == settings.routes.SIGN_OUT)
settings.navigateToPreviousRoute();
@@ -391,6 +394,15 @@ Polymer({
settings.navigateToPreviousRoute();
cr.ui.focusWithoutInk(assert(this.$.importDataDialogTrigger));
},
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ shouldShowSyncAccountControl_: function() {
+ return !!this.diceEnabled_ && !!this.syncStatus.syncSystemEnabled &&
+ !!this.syncStatus.signinAllowed;
+ },
// </if>
/**
diff --git a/chromium/chrome/browser/resources/settings/people_page/pin_keyboard.html b/chromium/chrome/browser/resources/settings/people_page/pin_keyboard.html
deleted file mode 100644
index 65056df0516..00000000000
--- a/chromium/chrome/browser/resources/settings/people_page/pin_keyboard.html
+++ /dev/null
@@ -1 +0,0 @@
-<include src="../../chromeos/quick_unlock/pin_keyboard.html">
diff --git a/chromium/chrome/browser/resources/settings/people_page/pin_keyboard.js b/chromium/chrome/browser/resources/settings/people_page/pin_keyboard.js
deleted file mode 100644
index 4cb8d021731..00000000000
--- a/chromium/chrome/browser/resources/settings/people_page/pin_keyboard.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// <include src="../../chromeos/quick_unlock/pin_keyboard.js">
diff --git a/chromium/chrome/browser/resources/settings/people_page/profile_info_browser_proxy.js b/chromium/chrome/browser/resources/settings/people_page/profile_info_browser_proxy.js
index 31f4824fd3b..65808a60de6 100644
--- a/chromium/chrome/browser/resources/settings/people_page/profile_info_browser_proxy.js
+++ b/chromium/chrome/browser/resources/settings/people_page/profile_info_browser_proxy.js
@@ -32,12 +32,6 @@ cr.define('settings', function() {
* 'profile-stats-count-ready' WebUI listener event.
*/
getProfileStatsCount() {}
-
- /**
- * Returns a Promise that's true if the profile manages supervised users.
- * @return {!Promise<boolean>}
- */
- getProfileManagesSupervisedUsers() {}
}
/**
@@ -53,11 +47,6 @@ cr.define('settings', function() {
getProfileStatsCount() {
chrome.send('getProfileStatsCount');
}
-
- /** @override */
- getProfileManagesSupervisedUsers() {
- return cr.sendWithPromise('getProfileManagesSupervisedUsers');
- }
}
cr.addSingletonGetter(ProfileInfoBrowserProxyImpl);
diff --git a/chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.html b/chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.html
index c39f06d3012..7e005111b04 100644
--- a/chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.html
+++ b/chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.html
@@ -72,13 +72,13 @@
</div>
</div>
<div slot="button-container">
- <paper-button id="addAnotherButton" on-tap="onAddAnotherFingerprint_"
+ <paper-button id="addAnotherButton" on-click="onAddAnotherFingerprint_"
hidden$="[[hideAddAnother_(step_)]]">
$i18n{configureFingerprintAddAnotherButton}
</paper-button>
<paper-button id="closeButton"
- class$="[[getCloseButtonClass_(step_)]]" on-tap="onClose_">
+ class$="[[getCloseButtonClass_(step_)]]" on-click="onClose_">
[[getCloseButtonText_(step_)]]
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.js b/chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.js
index 7922b7808ec..4b9ca548d58 100644
--- a/chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.js
+++ b/chromium/chrome/browser/resources/settings/people_page/setup_fingerprint_dialog.js
@@ -259,7 +259,7 @@ Polymer({
*/
getCloseButtonText_: function(step) {
if (step == settings.FingerprintSetupStep.READY)
- return this.i18n('configureFingerprintDoneButton');
+ return this.i18n('done');
return this.i18n('cancel');
},
diff --git a/chromium/chrome/browser/resources/settings/people_page/setup_pin_dialog.html b/chromium/chrome/browser/resources/settings/people_page/setup_pin_dialog.html
index d7a74e2a102..7cffa7ec8cb 100644
--- a/chromium/chrome/browser/resources/settings/people_page/setup_pin_dialog.html
+++ b/chromium/chrome/browser/resources/settings/people_page/setup_pin_dialog.html
@@ -1,3 +1,4 @@
+<link rel="import" href="chrome://resources/cr_components/chromeos/quick_unlock/pin_keyboard.html">
<link rel="import" href="chrome://resources/cr_elements/icons.html">
<link rel="import" href="chrome://resources/html/polymer.html">
@@ -6,7 +7,6 @@
<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
<link rel="import" href="../i18n_setup.html">
<link rel="import" href="lock_screen_constants.html">
-<link rel="import" href="pin_keyboard.html">
<link rel="import" href="../settings_shared_css.html">
<dom-module id="settings-setup-pin-dialog">
@@ -28,6 +28,13 @@
--iron-icon-fill-color: var(--paper-grey-700);
}
+ pin-keyboard {
+ --pin-keyboard-digit-button: {
+ font-size: 18px;
+ padding: 15px 21px;
+ };
+ }
+
#pinKeyboardDiv {
justify-content: center;
}
@@ -62,11 +69,11 @@
</div>
</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onCancelTap_">
+ <paper-button class="cancel-button" on-click="onCancelTap_">
$i18n{cancel}
</paper-button>
- <paper-button class="action-button" on-tap="onPinSubmit_"
+ <paper-button class="action-button" on-click="onPinSubmit_"
disabled$="[[!enableSubmit_]]">
<span>[[getContinueMessage_(isConfirmStep_)]]</span>
</paper-button>
diff --git a/chromium/chrome/browser/resources/settings/people_page/sync_account_control.html b/chromium/chrome/browser/resources/settings/people_page/sync_account_control.html
new file mode 100644
index 00000000000..e59e5f06258
--- /dev/null
+++ b/chromium/chrome/browser/resources/settings/people_page/sync_account_control.html
@@ -0,0 +1,200 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html">
+<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/notification-icons.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="profile_info_browser_proxy.html">
+<link rel="import" href="sync_browser_proxy.html">
+<link rel="import" href="../i18n_setup.html">
+<link rel="import" href="../route.html">
+<link rel="import" href="../settings_shared_css.html">
+
+<dom-module id="settings-sync-account-control">
+ <template>
+ <style include="settings-shared">
+ :host {
+ --sync-icon-size: 16px;
+ --sync-icon-border-size: 2px;
+ --shown-avatar-size: 40px;
+ }
+
+ setting-box.middle {
+ /* Per spec, middle text is indented 20px in this section. */
+ -webkit-margin-start: 20px;
+ }
+
+ .account-icon {
+ border-radius: 20px;
+ flex-shrink: 0;
+ height: var(--shown-avatar-size);
+ width: var(--shown-avatar-size);
+ }
+
+ .account-icon.small {
+ height: 20px;
+ width: 20px;
+ }
+
+ #menu .dropdown-item {
+ padding: 12px;
+ }
+
+ #menu .dropdown-item span {
+ -webkit-margin-start: 8px;
+ }
+
+ .flex {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ }
+
+ #avatar-container {
+ position: relative;
+ }
+
+ #sync-icon-container {
+ align-items: center;
+ background: var(--google-blue-500);
+ border: var(--sync-icon-border-size) solid white;
+ border-radius: 50%;
+ display: flex;
+ height: var(--sync-icon-size);
+ position: absolute;
+ right: -6px;
+ top: calc(var(--shown-avatar-size) - var(--sync-icon-size) -
+ var(--sync-icon-border-size));
+ width: var(--sync-icon-size);
+ }
+
+ :host-context([dir='rtl']) #sync-icon-container {
+ left: -6px;
+ right: initial;
+ }
+
+ #sync-icon-container[syncing] {
+ background: green;
+ }
+
+ #sync-icon-container iron-icon {
+ fill: white;
+ height: 12px;
+ margin: auto;
+ width: 12px;
+ }
+
+ #sign-in {
+ min-width: 100px;
+ }
+
+ #banner {
+ background-color: var(--google-blue-500);
+ display: none;
+ }
+
+ #banner img {
+ -webkit-margin-start: 380px;
+ height: 100px;
+ margin-bottom: -12px;
+ margin-top: 32px;
+ }
+
+ :host([showing-promo]) #banner {
+ display: flex;
+ }
+
+ :host([showing-promo]) #promo-headers {
+ line-height: 1.625rem;
+ padding-bottom: 10px;
+ padding-top: 10px;
+ }
+
+ :host([showing-promo]) #promo-headers #promo-title {
+ font-size: 1.1rem;
+ }
+
+ :host([showing-promo]) #promo-headers .secondary {
+ font-size: 0.9rem;
+ }
+
+ :host([showing-promo]) #promo-headers .separator {
+ display: none;
+ }
+ </style>
+ <div class="settings-box" id="banner">
+ <img src="../images/sync_banner.svg" alt="">
+ </div>
+ <div class="settings-box first two-line" id="promo-headers"
+ hidden="[[syncStatus.signedIn]]">
+ <div class="start">
+ <div id="promo-title">[[promoLabel]]</div>
+ <div class="secondary">
+ [[promoSecondaryLabel]]
+ </div>
+ </div>
+ <div class="separator" hidden="[[shouldShowAvatarRow_]]"></div>
+ <paper-button class="action-button" on-click="onSigninTap_"
+ disabled="[[syncStatus.setupInProgress]]" id="sign-in"
+ hidden="[[shouldShowAvatarRow_]]">
+ $i18n{peopleSignIn}
+ </paper-button>
+ </div>
+ <template is="dom-if" if="[[shouldShowAvatarRow_]]">
+ <div class="settings-box first two-line" id="avatar-row">
+ <div id="avatar-container">
+ <img class="account-icon" alt=""
+ src="[[getAccountImageSrc_(shownAccount_.avatarImage)]]">
+ <div id="sync-icon-container" syncing$="[[syncStatus.signedIn]]">
+ <iron-icon icon="notification:sync"></iron-icon>
+ </div>
+ </div>
+ <div class="middle two-line no-min-width">
+ <div class="flex text-elide" id="user-info">
+ <span>
+ [[getNameDisplay_('$i18nPolymer{syncedToName}',
+ shownAccount_.fullName, syncStatus.signedIn)]]
+ </span>
+ <div class="secondary">[[shownAccount_.email]]</div>
+ </div>
+ </div>
+ <button is="paper-icon-button-light" id="dropdown-arrow"
+ on-click="onMenuButtonTap_" title="$i18n{useAnotherAccount}"
+ class="icon-arrow-dropdown" hidden="[[syncStatus.signedIn]]">
+ </button>
+ <div class="separator" hidden="[[syncStatus.signedIn]]"></div>
+ <paper-button class="action-button" on-click="onSyncButtonTap_"
+ hidden="[[syncStatus.signedIn]]"
+ disabled="[[syncStatus.setupInProgress]]">
+ [[getSubstituteLabel_(
+ '$i18nPolymer{syncAsName}', shownAccount_.givenName)]]
+ </paper-button>
+ <paper-button class="secondary-button" on-click="onTurnOffButtonTap_"
+ hidden="[[!syncStatus.signedIn]]"
+ disabled="[[syncStatus.setupInProgress]]">
+ $i18n{turnOffSync}
+ </paper-button>
+ </div>
+ <template is="dom-if" if="[[!syncStatus.signedIn]]" restamp>
+ <dialog is="cr-action-menu" id="menu" auto-reposition>
+ <template is="dom-repeat" items="[[storedAccounts_]]">
+ <button class="dropdown-item" on-click="onAccountTap_" slot="item">
+ <img class="account-icon small" alt=""
+ src="[[getAccountImageSrc_(item.avatarImage)]]">
+ <span>[[item.email]]</span>
+ </button>
+ </template>
+ <button class="dropdown-item" on-click="onSigninTap_" slot="item"
+ disabled="[[syncStatus.setupInProgress]]" id="sign-in-item">
+ <img class="account-icon small" alt=""
+ src="chrome://theme/IDR_PROFILE_AVATAR_PLACEHOLDER_LARGE">
+ <span>$i18n{useAnotherAccount}</span>
+ </button>
+ </dialog>
+ </template>
+ </template>
+ </template>
+ <script src="sync_account_control.js"></script>
+</dom-module>
diff --git a/chromium/chrome/browser/resources/settings/people_page/sync_account_control.js b/chromium/chrome/browser/resources/settings/people_page/sync_account_control.js
new file mode 100644
index 00000000000..8b079fe22ac
--- /dev/null
+++ b/chromium/chrome/browser/resources/settings/people_page/sync_account_control.js
@@ -0,0 +1,222 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+/**
+ * @fileoverview
+ * 'settings-sync-account-section' is the settings page containing sign-in
+ * settings.
+ */
+cr.exportPath('settings');
+
+/** @const {number} */
+settings.MAX_SIGNIN_PROMO_IMPRESSION = 10;
+
+Polymer({
+ is: 'settings-sync-account-control',
+ behaviors: [WebUIListenerBehavior],
+ properties: {
+ /**
+ * The current sync status, supplied by SyncBrowserProxy.
+ * @type {!settings.SyncStatus}
+ */
+ syncStatus: Object,
+
+ /**
+ * Proxy variable for syncStatus.signedIn to shield observer from being
+ * triggered multiple times whenever syncStatus changes.
+ * @private {boolean}
+ */
+ signedIn_: {
+ type: Boolean,
+ computed: 'computeSignedIn_(syncStatus.signedIn)',
+ observer: 'onSignedInChanged_',
+ },
+
+ /** @private {!Array<!settings.StoredAccount>} */
+ storedAccounts_: Object,
+
+ /** @private {?settings.StoredAccount} */
+ shownAccount_: Object,
+
+ showingPromo: {
+ type: Boolean,
+ value: false,
+ reflectToAttribute: true,
+ },
+
+ promoLabel: String,
+
+ promoSecondaryLabel: String,
+
+ /** @private {boolean} */
+ shouldShowAvatarRow_: {
+ type: Boolean,
+ value: false,
+ computed: 'computeShouldShowAvatarRow_(storedAccounts_, syncStatus,' +
+ 'storedAccounts_.length, syncStatus.signedIn)',
+ observer: 'onShouldShowAvatarRowChange_',
+ }
+ },
+
+ observers: [
+ 'onShownAccountShouldChange_(storedAccounts_, syncStatus)',
+ ],
+
+ /** @private {?settings.SyncBrowserProxy} */
+ syncBrowserProxy_: null,
+
+ /** @override */
+ attached: function() {
+ this.syncBrowserProxy_ = settings.SyncBrowserProxyImpl.getInstance();
+ this.syncBrowserProxy_.getSyncStatus().then(
+ this.handleSyncStatus_.bind(this));
+ this.syncBrowserProxy_.getStoredAccounts().then(
+ this.handleStoredAccounts_.bind(this));
+ this.addWebUIListener(
+ 'sync-status-changed', this.handleSyncStatus_.bind(this));
+ this.addWebUIListener(
+ 'stored-accounts-updated', this.handleStoredAccounts_.bind(this));
+ },
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ computeSignedIn_: function() {
+ return !!this.syncStatus.signedIn;
+ },
+
+ /** @private */
+ onSignedInChanged_: function() {
+ if (!this.showingPromo && !this.syncStatus.signedIn &&
+ this.syncBrowserProxy_.getPromoImpressionCount() <
+ settings.MAX_SIGNIN_PROMO_IMPRESSION) {
+ this.showingPromo = true;
+ this.syncBrowserProxy_.incrementPromoImpressionCount();
+ } else {
+ // Turn off the promo if the user is signed in.
+ this.showingPromo = false;
+ }
+ },
+
+ /**
+ * @param {string} label
+ * @param {string} name
+ * @return {string}
+ * @private
+ */
+ getSubstituteLabel_: function(label, name) {
+ return loadTimeData.substituteString(label, name);
+ },
+
+ /**
+ * @param {string} label
+ * @param {string} name
+ * @return {string}
+ * @private
+ */
+ getNameDisplay_: function(label, name) {
+ return this.syncStatus.signedIn ?
+ loadTimeData.substituteString(label, name) :
+ name;
+ },
+
+ /**
+ * @param {?string} image
+ * @return {string}
+ * @private
+ */
+ getAccountImageSrc_: function(image) {
+ // image can be undefined if the account has not set an avatar photo.
+ return image || 'chrome://theme/IDR_PROFILE_AVATAR_PLACEHOLDER_LARGE';
+ },
+
+ /**
+ * @param {!Array<!settings.StoredAccount>} accounts
+ * @private
+ */
+ handleStoredAccounts_: function(accounts) {
+ this.storedAccounts_ = accounts;
+ },
+
+ /**
+ * Handler for when the sync state is pushed from the browser.
+ * @param {!settings.SyncStatus} syncStatus
+ * @private
+ */
+ handleSyncStatus_: function(syncStatus) {
+ this.syncStatus = syncStatus;
+ },
+
+ /**
+ * @return {boolean}
+ * @private
+ */
+ computeShouldShowAvatarRow_: function() {
+ return this.syncStatus.signedIn || this.storedAccounts_.length > 0;
+ },
+
+ /** @private */
+ onSigninTap_: function() {
+ this.syncBrowserProxy_.startSignIn();
+
+ // Need to close here since one menu item also triggers this function.
+ if (this.$$('#menu')) {
+ /** @type {!CrActionMenuElement} */ (this.$$('#menu')).close();
+ }
+ },
+
+ /** @private */
+ onSyncButtonTap_: function() {
+ assert(this.shownAccount_);
+ this.syncBrowserProxy_.startSyncingWithEmail(this.shownAccount_.email);
+ },
+
+ /** @private */
+ onTurnOffButtonTap_: function() {
+ /* This will route to people_page's disconnect dialog. */
+ settings.navigateTo(settings.routes.SIGN_OUT);
+ },
+
+ /** @private */
+ onMenuButtonTap_: function() {
+ const actionMenu =
+ /** @type {!CrActionMenuElement} */ (this.$$('#menu'));
+ actionMenu.showAt(assert(this.$$('#dropdown-arrow')));
+ },
+
+ /** @private */
+ onShouldShowAvatarRowChange_: function() {
+ // Close dropdown when avatar-row hides, so if it appears again, the menu
+ // won't be open by default.
+ const actionMenu = this.$$('#menu');
+ if (!this.shouldShowAvatarRow_ && actionMenu && actionMenu.open)
+ actionMenu.close();
+ },
+
+ /**
+ * @param {!{model:
+ * !{item: !settings.StoredAccount},
+ * }} e
+ * @private
+ */
+ onAccountTap_: function(e) {
+ this.shownAccount_ = e.model.item;
+ /** @type {!CrActionMenuElement} */ (this.$$('#menu')).close();
+ },
+
+ /** @private */
+ onShownAccountShouldChange_: function() {
+ if (this.syncStatus.signedIn) {
+ for (let i = 0; i < this.storedAccounts_.length; i++) {
+ if (this.storedAccounts_[i].email == this.syncStatus.signedInUsername) {
+ this.shownAccount_ = this.storedAccounts_[i];
+ return;
+ }
+ }
+ } else {
+ this.shownAccount_ =
+ this.storedAccounts_ ? this.storedAccounts_[0] : null;
+ }
+ }
+}); \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/settings/people_page/sync_browser_proxy.js b/chromium/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
index 05f345f3edf..ecb2a43ed3d 100644
--- a/chromium/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
+++ b/chromium/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
@@ -10,12 +10,20 @@
cr.exportPath('settings');
/**
+ * @typedef {{fullName: (string|undefined),
+ * givenName: (string|undefined),
+ * email: string,
+ * avatarImage: (string|undefined)}}
+ * @see chrome/browser/ui/webui/settings/people_handler.cc
+ */
+settings.StoredAccount;
+
+/**
* @typedef {{childUser: (boolean|undefined),
* domain: (string|undefined),
* hasError: (boolean|undefined),
* hasUnrecoverableError: (boolean|undefined),
* managed: (boolean|undefined),
- * setupCompleted: (boolean|undefined),
* setupInProgress: (boolean|undefined),
* signedIn: (boolean|undefined),
* signedInUsername: (string|undefined),
@@ -104,6 +112,12 @@ settings.PageStatus = {
};
cr.define('settings', function() {
+ /**
+ * Key to be used with localStorage.
+ * @type {string}
+ */
+ const PROMO_IMPRESSION_COUNT_KEY = 'signin-promo-count';
+
/** @interface */
class SyncBrowserProxy {
// <if expr="not chromeos">
@@ -124,6 +138,16 @@ cr.define('settings', function() {
*/
manageOtherPeople() {}
+ /**
+ * @return {number} the number of times the sync account promo was shown.
+ */
+ getPromoImpressionCount() {}
+
+ /**
+ * Increment the number of times the sync account promo was shown.
+ */
+ incrementPromoImpressionCount() {}
+
// </if>
// <if expr="chromeos">
@@ -141,6 +165,12 @@ cr.define('settings', function() {
getSyncStatus() {}
/**
+ * Gets a list of stored accounts.
+ * @return {!Promise<!Array<!settings.StoredAccount>>}
+ */
+ getStoredAccounts() {}
+
+ /**
* Function to invoke when the sync page has been navigated to. This
* registers the UI as the "active" sync UI so that if the user tries to
* open another sync UI, this one will be shown instead.
@@ -168,6 +198,12 @@ cr.define('settings', function() {
setSyncEncryption(syncPrefs) {}
/**
+ * Start syncing with an account, specified by its email.
+ * @param {string} email
+ */
+ startSyncingWithEmail(email) {}
+
+ /**
* Opens the Google Activity Controls url in a new tab.
*/
openActivityControlsUrl() {}
@@ -193,13 +229,26 @@ cr.define('settings', function() {
chrome.send('SyncSetupManageOtherPeople');
}
+ /** @override */
+ getPromoImpressionCount() {
+ return parseInt(
+ window.localStorage.getItem(PROMO_IMPRESSION_COUNT_KEY), 10) ||
+ 0;
+ }
+
+ /** @override */
+ incrementPromoImpressionCount() {
+ window.localStorage.setItem(
+ PROMO_IMPRESSION_COUNT_KEY,
+ (this.getPromoImpressionCount() + 1).toString());
+ }
+
// </if>
// <if expr="chromeos">
/** @override */
attemptUserExit() {
return chrome.send('AttemptUserExit');
}
-
// </if>
/** @override */
@@ -208,6 +257,11 @@ cr.define('settings', function() {
}
/** @override */
+ getStoredAccounts() {
+ return cr.sendWithPromise('SyncSetupGetStoredAccounts');
+ }
+
+ /** @override */
didNavigateToSyncPage() {
chrome.send('SyncSetupShowSetupUI');
}
@@ -230,6 +284,11 @@ cr.define('settings', function() {
}
/** @override */
+ startSyncingWithEmail(email) {
+ chrome.send('SyncSetupStartSyncingWithEmail', [email]);
+ }
+
+ /** @override */
openActivityControlsUrl() {
chrome.metricsPrivate.recordUserAction(
'Signin_AccountSettings_GoogleActivityControlsClicked');
diff --git a/chromium/chrome/browser/resources/settings/people_page/sync_page.html b/chromium/chrome/browser/resources/settings/people_page/sync_page.html
index bea84fd3f43..5f9b6a925e1 100644
--- a/chromium/chrome/browser/resources/settings/people_page/sync_page.html
+++ b/chromium/chrome/browser/resources/settings/people_page/sync_page.html
@@ -88,7 +88,7 @@
on-keypress="onSubmitExistingPassphraseTap_">
</paper-input>
<paper-button id="submitExistingPassphrase"
- on-tap="onSubmitExistingPassphraseTap_" class="action-button"
+ on-click="onSubmitExistingPassphraseTap_" class="action-button"
disabled="[[!existingPassphrase_]]">
$i18n{submitPassphraseButton}
</paper-button>
@@ -251,7 +251,7 @@
<a class="settings-box two-line inherit-color no-outline" tabindex="-1"
target="_blank" href="$i18n{activityControlsUrl}"
- on-tap="onActivityControlsTap_">
+ on-click="onActivityControlsTap_">
<div class="start">
$i18n{personalizeGoogleServicesTitle}
<div class="secondary" id="activityControlsSecondary">
@@ -294,7 +294,7 @@
<span>[[syncPrefs.fullEncryptionBody]]</span>
</template>
<template is="dom-if" if="[[!syncPrefs.fullEncryptionBody]]">
- <span on-tap="onLearnMoreTap_">
+ <span on-click="onLearnMoreTap_">
$i18nRaw{encryptWithSyncPassphraseLabel}
</span>
</template>
@@ -323,7 +323,7 @@
error-message="$i18n{mismatchedPassphraseError}">
</paper-input>
<paper-button id="saveNewPassphrase"
- on-tap="onSaveNewPassphraseTap_" class="action-button"
+ on-click="onSaveNewPassphraseTap_" class="action-button"
disabled="[[!isSaveNewPassphraseEnabled_(passphrase_,
confirmation_)]]">
$i18n{save}
diff --git a/chromium/chrome/browser/resources/settings/people_page/user_list.html b/chromium/chrome/browser/resources/settings/people_page/user_list.html
index 0873990c000..4ca878eef14 100644
--- a/chromium/chrome/browser/resources/settings/people_page/user_list.html
+++ b/chromium/chrome/browser/resources/settings/people_page/user_list.html
@@ -46,7 +46,7 @@
</template>
</div>
<button is="paper-icon-button-light" class="icon-clear"
- on-tap="removeUser_"
+ on-click="removeUser_"
hidden="[[shouldHideCloseButton_(disabled, item.isOwner)]]">
</button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/people_page/users_add_user_dialog.html b/chromium/chrome/browser/resources/settings/people_page/users_add_user_dialog.html
index 99850568bff..fe8cd9c784b 100644
--- a/chromium/chrome/browser/resources/settings/people_page/users_add_user_dialog.html
+++ b/chromium/chrome/browser/resources/settings/people_page/users_add_user_dialog.html
@@ -25,10 +25,10 @@
</paper-input>
</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onCancelTap_">
+ <paper-button class="cancel-button" on-click="onCancelTap_">
$i18n{cancel}
</paper-button>
- <paper-button on-tap="addUser_" class="action-button"
+ <paper-button on-click="addUser_" class="action-button"
disabled$="[[!isValid_]]">
$i18n{add}
</paper-button>
diff --git a/chromium/chrome/browser/resources/settings/people_page/users_page.html b/chromium/chrome/browser/resources/settings/people_page/users_page.html
index ac720c8628e..bd58da4cdd0 100644
--- a/chromium/chrome/browser/resources/settings/people_page/users_page.html
+++ b/chromium/chrome/browser/resources/settings/people_page/users_page.html
@@ -39,13 +39,6 @@
label="$i18n{guestBrowsingLabel}"
disabled="[[isEditingDisabled_(isOwner_, isWhitelistManaged_)]]">
</settings-toggle-button>
- <template is="dom-if" if="[[profileManagesSupervisedUsers]]">
- <settings-toggle-button class="continuation"
- pref="{{prefs.cros.accounts.supervisedUsersEnabled}}"
- label="$i18n{supervisedUsersLabel}"
- disabled="[[isEditingDisabled_(isOwner_, isWhitelistManaged_)]]">
- </settings-toggle-button>
- </template>
<settings-toggle-button class="continuation"
pref="{{prefs.cros.accounts.showUserNamesOnSignIn}}"
label="$i18n{showOnSigninLabel}"
@@ -66,7 +59,7 @@
<div id="add-user-button" class="list-item"
hidden="[[isEditingUsersDisabled_(isOwner_, isWhitelistManaged_,
prefs.cros.accounts.allowGuest.value)]]">
- <a is="action-link" class="list-button" on-tap="openAddUserDialog_">
+ <a is="action-link" class="list-button" on-click="openAddUserDialog_">
$i18n{addUsers}
</a>
</div>
diff --git a/chromium/chrome/browser/resources/settings/people_page/users_page.js b/chromium/chrome/browser/resources/settings/people_page/users_page.js
index 762fb6763f8..2d14833a0dc 100644
--- a/chromium/chrome/browser/resources/settings/people_page/users_page.js
+++ b/chromium/chrome/browser/resources/settings/people_page/users_page.js
@@ -19,15 +19,6 @@ Polymer({
notify: true,
},
- /**
- * True if the current profile manages supervised users.
- * Set in people-page.
- */
- profileManagesSupervisedUsers: {
- type: Boolean,
- value: false,
- },
-
/** @private */
isOwner_: {
type: Boolean,
diff --git a/chromium/chrome/browser/resources/settings/prefs/pref_util.js b/chromium/chrome/browser/resources/settings/prefs/pref_util.js
index f9ce20040b3..fd5a45a3ce8 100644
--- a/chromium/chrome/browser/resources/settings/prefs/pref_util.js
+++ b/chromium/chrome/browser/resources/settings/prefs/pref_util.js
@@ -18,7 +18,7 @@ cr.define('Settings.PrefUtil', function() {
case chrome.settingsPrivate.PrefType.BOOLEAN:
return value == 'true';
case chrome.settingsPrivate.PrefType.NUMBER:
- const n = parseInt(value, 10);
+ const n = parseFloat(value);
if (isNaN(n)) {
console.error(
'Argument to stringToPrefValue for number pref ' +
diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
index b9cdf9e4628..d8436bfe128 100644
--- a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
+++ b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
@@ -51,18 +51,18 @@
<div slot="dialog-buttons">
<div> <!-- Left group -->
<paper-button id="manuallyAddPrinterButton" class="secondary-button"
- on-tap="switchToManualAddDialog_">
+ on-click="switchToManualAddDialog_">
$i18n{manuallyAddPrinterButtonText}
</paper-button>
</div>
<div> <!-- Right group -->
<paper-button class="cancel-button secondary-button"
- on-tap="onCancelTap_">
+ on-click="onCancelTap_">
$i18n{cancel}
</paper-button>
<paper-button class="action-button" id="addPrinterButton"
disabled="[[!canAddPrinter_(selectedPrinter)]]"
- on-tap="switchToConfiguringDialog_">
+ on-click="switchToConfiguringDialog_">
$i18n{addPrinterButtonText}
</paper-button>
</div>
@@ -122,8 +122,7 @@
<div class="label">$i18n{printerAddress}</div>
<div class="secondary">
<paper-input no-label-float id="printerAddressInput"
- value="{{newPrinter.printerAddress}}"
- on-input="onAddressChanged_">
+ value="{{newPrinter.printerAddress}}">
</paper-input>
</div>
</div>
@@ -159,32 +158,21 @@
</div>
</div>
</div>
- <div class="search-printer-box" id="searchInProgress" hidden>
- <paper-spinner-lite active></paper-spinner-lite>
- <span class="spinner-comment">$i18n{searchingPrinter}</span>
- </div>
- <div class="search-printer-box printer-not-found"
- id="searchNotFound" hidden>
- <span>$i18n{printerNotFound}</span>
- </div>
- <div class="search-printer-box printer-found" id="searchFound" hidden>
- <span>$i18n{printerFound}</span>
- </div>
</div>
<div slot="dialog-buttons">
<div> <!-- Left group -->
<paper-button class="secondary-button"
- on-tap="switchToDiscoveryDialog_">
+ on-click="switchToDiscoveryDialog_">
$i18n{discoverPrintersButtonText}
</paper-button>
</div>
<div> <!-- Right group -->
<paper-button class="cancel-button secondary-button"
- on-tap="onCancelTap_">
+ on-click="onCancelTap_">
$i18n{cancel}
</paper-button>
<paper-button id="addPrinterButton" class="action-button"
- on-tap="addPressed_"
+ on-click="addPressed_"
disabled="[[!canAddPrinter_(newPrinter.printerName,
newPrinter.printerAddress)]]">
$i18n{addPrinterButtonText}
@@ -233,9 +221,11 @@
<div class="label">$i18n{selectDriver}</div>
<div class="secondary">
<paper-input class="browse-file-input" no-label-float readonly
- value="[[getBaseName_(activePrinter.printerPPDPath)]]">
- <paper-button class="browse-button" suffix
- on-tap="onBrowseFile_">
+ value="[[getBaseName_(activePrinter.printerPPDPath)]]"
+ error-message="$i18n{selectDriverErrorMessage}"
+ invalid="[[invalidPPD]]">
+ <paper-button class="browse-button" slot="suffix"
+ on-click="onBrowseFile_">
$i18n{selectDriverButtonText}
</paper-button>
</paper-input>
@@ -248,14 +238,14 @@
</div>
<div slot="dialog-buttons">
<paper-button class="cancel-button secondary-button"
- on-tap="onCancelTap_">
+ on-click="onCancelTap_">
$i18n{cancel}
</paper-button>
<paper-button class="action-button" id="addPrinterButton"
disabled="[[!canAddPrinter_(activePrinter.ppdManufacturer,
activePrinter.ppdModel,
activePrinter.printerPPDPath)]]"
- on-tap="switchToConfiguringDialog_">
+ on-click="switchToConfiguringDialog_">
$i18n{addPrinterButtonText}
</paper-button>
</div>
@@ -279,7 +269,7 @@
</div>
<div slot="dialog-buttons">
<paper-button class="cancel-button secondary-button"
- on-tap="onCancelConfiguringTap_">
+ on-click="onCancelConfiguringTap_">
$i18n{cancel}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
index bd6a143ae4d..52ff88a5f5c 100644
--- a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
+++ b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
@@ -184,13 +184,6 @@ Polymer({
this.fire('open-configuring-printer-dialog');
},
- /** @private */
- onAddressChanged_: function() {
- // TODO(xdai): Check if the printer address exists and then show the
- // corresponding message after the API is ready.
- // The format of address is: ip-address-or-hostname:port-number.
- },
-
/**
* @param {!Event} event
* @private
diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html
index 797f6fe0f76..3928af6bfcd 100644
--- a/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html
+++ b/chromium/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog_util.html
@@ -23,8 +23,8 @@
selected="{{selectedPrinter}}">
</array-selector>
<template is="dom-repeat" items="[[printers]]">
- <button class="list-item" on-tap="onSelect_">
- [[item.printerName]] [[item.printerModel]]
+ <button class="list-item" on-click="onSelect_">
+ [[item.printerName]]
</button>
</template>
</div>
@@ -39,7 +39,11 @@
width: 270px;
}
- iron-dropdown .dropdown-content {
+ iron-dropdown {
+ height: 270px;
+ }
+
+ iron-dropdown [slot='dropdown-content'] {
background-color: white;
box-shadow: 0 2px 6px var(--paper-grey-500);
min-width: 128px;
@@ -56,22 +60,23 @@
background-size: 24px;
}
</style>
- <paper-input-container no-label-float on-tap="onTap_">
+ <paper-input-container no-label-float on-click="onTap_">
<input is="iron-input" type="search" bind-value="{{selectedItem}}"
- on-search="onInputValueChanged_" on-change="onChange_" incremental>
- <button is="paper-icon-button-light" class="icon-search" suffix
- id="searchIcon" hidden>
+ on-search="onInputValueChanged_" on-change="onChange_" incremental
+ slot="input">
+ <button is="paper-icon-button-light" class="icon-search"
+ id="searchIcon" hidden slot="suffix">
</button>
- <button is="paper-icon-button-light" class="icon-arrow-dropdown" suffix
- id="dropdownIcon">
+ <button is="paper-icon-button-light" class="icon-arrow-dropdown"
+ id="dropdownIcon" slot="suffix">
</button>
</paper-input-container>
<iron-dropdown horizontal-align="left" vertical-align="top"
vertical-offset="35">
- <div class="dropdown-content">
+ <div slot="dropdown-content">
<template is="dom-repeat" items="[[items]]"
filter="[[filterItems_(searchTerm_)]]">
- <button class="list-item" on-tap="onSelect_">[[item]]</button>
+ <button class="list-item" on-click="onSelect_">[[item]]</button>
</template>
</div>
</iron-dropdown>
diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html b/chromium/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html
index 63ebb00216d..8f0c517f723 100644
--- a/chromium/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html
+++ b/chromium/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html
@@ -100,8 +100,8 @@
<div class="secondary">
<paper-input class="browse-file-input" no-label-float readonly
value="[[getBaseName_(activePrinter.printerPPDPath)]]">
- <paper-button class="browse-button" suffix
- on-tap="onBrowseFile_">
+ <paper-button class="browse-button" slot="suffix"
+ on-click="onBrowseFile_">
$i18n{selectDriverButtonText}
</paper-button>
</paper-input>
@@ -111,10 +111,10 @@
</div>
<div slot="dialog-buttons">
<paper-button class="cancel-button secondary-button"
- on-tap="onCancelTap_">
+ on-click="onCancelTap_">
$i18n{cancel}
</paper-button>
- <paper-button class="action-button" on-tap="onSaveTap_">
+ <paper-button class="action-button" on-click="onSaveTap_">
$i18n{editPrinterButtonText}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html b/chromium/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html
index 029074eb08d..026ed96e938 100644
--- a/chromium/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html
+++ b/chromium/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html
@@ -89,7 +89,7 @@
padding: 0 24px;
text-align: start;
width: 100%;
- @apply(--settings-actionable);
+ @apply --settings-actionable;
}
.list-item:focus {
diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_printers.html b/chromium/chrome/browser/resources/settings/printing_page/cups_printers.html
index 3df538af0ea..8fe645217ad 100644
--- a/chromium/chrome/browser/resources/settings/printing_page/cups_printers.html
+++ b/chromium/chrome/browser/resources/settings/printing_page/cups_printers.html
@@ -46,6 +46,14 @@
width: 350px;
}
+ #noSearchResultsMessage {
+ color: var(--md-loading-message-color);
+ font-size: 16px;
+ font-weight: 500;
+ margin-top: 80px;
+ text-align: center;
+ }
+
#addPrinterErrorMessage {
display: flex;
justify-content: space-around;
@@ -68,7 +76,7 @@
</div>
</div>
<paper-button class="primary-button" id="addPrinter"
- on-tap="onAddPrinterTap_" disabled="[[!canAddPrinter_]]">
+ on-click="onAddPrinterTap_" disabled="[[!canAddPrinter_]]">
$i18n{addCupsPrinter}
</paper-button>
</div>
@@ -88,6 +96,11 @@
search-term="[[searchTerm]]">
</settings-cups-printers-list>
+ <div id="noSearchResultsMessage"
+ hidden="[[!showNoSearchResultsMessage_(searchTerm)]]">
+ $i18n{noSearchResults}
+ </div>
+
<div id="message">
<div class="center" id="addPrinterDoneMessage" hidden>
$i18n{printerAddedSuccessfulMessage}
diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_printers.js b/chromium/chrome/browser/resources/settings/printing_page/cups_printers.js
index f5c6e55c690..055f4a807c5 100644
--- a/chromium/chrome/browser/resources/settings/printing_page/cups_printers.js
+++ b/chromium/chrome/browser/resources/settings/printing_page/cups_printers.js
@@ -186,4 +186,18 @@ Polymer({
});
},
+ /**
+ * @param {string} searchTerm
+ * @return {boolean} If the 'no-search-results-found' string should be shown.
+ * @private
+ */
+ showNoSearchResultsMessage_: function(searchTerm) {
+ if (!searchTerm || !this.printers.length)
+ return false;
+ searchTerm = searchTerm.toLowerCase();
+ return !this.printers.some(printer => {
+ return printer.printerName.toLowerCase().includes(searchTerm);
+ });
+ },
+
});
diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_printers_list.html b/chromium/chrome/browser/resources/settings/printing_page/cups_printers_list.html
index 8c4a1d51ac0..353742c55e2 100644
--- a/chromium/chrome/browser/resources/settings/printing_page/cups_printers_list.html
+++ b/chromium/chrome/browser/resources/settings/printing_page/cups_printers_list.html
@@ -15,10 +15,10 @@
</style>
<dialog is="cr-action-menu">
- <button class="dropdown-item" on-tap="onEditTap_">
+ <button slot="item" class="dropdown-item" on-click="onEditTap_">
$i18n{editPrinter}
</button>
- <button class="dropdown-item" on-tap="onRemoveTap_">
+ <button slot="item" class="dropdown-item" on-click="onRemoveTap_">
$i18n{removePrinter}
</button>
</dialog>
@@ -29,7 +29,7 @@
<div class="printer-name text-elide">[[item.printerName]]</div>
<!--TODO(xdai): Add icon for enterprise CUPS printer. -->
<button is="paper-icon-button-light" class="icon-more-vert"
- on-tap="onOpenActionMenuTap_" title="$i18n{moreActions}">
+ on-click="onOpenActionMenuTap_" title="$i18n{moreActions}">
</button>
</div>
</template>
diff --git a/chromium/chrome/browser/resources/settings/printing_page/cups_set_manufacturer_model_behavior.js b/chromium/chrome/browser/resources/settings/printing_page/cups_set_manufacturer_model_behavior.js
index 84508310b53..43f805bf3f8 100644
--- a/chromium/chrome/browser/resources/settings/printing_page/cups_set_manufacturer_model_behavior.js
+++ b/chromium/chrome/browser/resources/settings/printing_page/cups_set_manufacturer_model_behavior.js
@@ -16,15 +16,16 @@ const SetManufacturerModelBehavior = {
notify: true,
},
- /** @type {?Array<string>} */
- manufacturerList: {
- type: Array,
+ invalidPPD: {
+ type: Boolean,
+ value: false,
},
/** @type {?Array<string>} */
- modelList: {
- type: Array,
- },
+ manufacturerList: Array,
+
+ /** @type {?Array<string>} */
+ modelList: Array,
},
observers: [
@@ -86,11 +87,12 @@ const SetManufacturerModelBehavior = {
},
/**
- * @param {string} path
+ * @param {string} path The full path to the selected PPD file
* @private
*/
printerPPDPathChanged_: function(path) {
this.set('activePrinter.printerPPDPath', path);
+ this.invalidPPD = !path;
},
/**
diff --git a/chromium/chrome/browser/resources/settings/printing_page/printing_page.html b/chromium/chrome/browser/resources/settings/printing_page/printing_page.html
index 90e18fbb2e5..6513b2f9f1a 100644
--- a/chromium/chrome/browser/resources/settings/printing_page/printing_page.html
+++ b/chromium/chrome/browser/resources/settings/printing_page/printing_page.html
@@ -22,7 +22,7 @@
<neon-animatable route-path="default">
<if expr="chromeos">
<div id="cupsPrinters" class="settings-box first"
- on-tap="onTapCupsPrinters_" actionable>
+ on-click="onTapCupsPrinters_" actionable>
<div class="start">$i18n{cupsPrintersTitle}</div>
<button class="subpage-arrow" is="paper-icon-button-light"
aria-label="$i18n{cupsPrintersTitle}"></button>
@@ -30,14 +30,14 @@
</if>
<if expr="not chromeos">
<div class="settings-box first"
- on-tap="onTapLocalPrinters_" actionable>
+ on-click="onTapLocalPrinters_" actionable>
<div class="start">$i18n{localPrintersTitle}</div>
<button class="subpage-arrow" is="paper-icon-button-light"
aria-label="$i18n{localPrintersTitle}"></button>
</div>
</if>
<div id="cloudPrinters" class="settings-box"
- on-tap="onTapCloudPrinters_" actionable>
+ on-click="onTapCloudPrinters_" actionable>
<div class="start">$i18n{cloudPrintersTitle}</div>
<button class="subpage-arrow" is="paper-icon-button-light"
aria-label="$i18n{cloudPrintersTitle}"></button>
diff --git a/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.html
index 1ba4c86e60b..d0bd4cfddfd 100644
--- a/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.html
+++ b/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -9,7 +9,6 @@
<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
<link rel="import" href="../clear_browsing_data_dialog/clear_browsing_data_dialog.html">
-<link rel="import" href="../clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.html">
<link rel="import" href="../controls/settings_toggle_button.html">
<link rel="import" href="../lifetime_browser_proxy.html">
<link rel="import" href="../route.html">
@@ -40,16 +39,9 @@
<style include="settings-shared">
</style>
<template is="dom-if" if="[[showClearBrowsingDataDialog_]]" restamp>
- <template is="dom-if" if="[[!tabsInCbd_]]" restamp>
- <settings-clear-browsing-data-dialog prefs="{{prefs}}"
- on-close="onDialogClosed_">
- </settings-clear-browsing-data-dialog>
- </template>
- <template is="dom-if" if="[[tabsInCbd_]]" restamp>
- <settings-clear-browsing-data-dialog-tabs prefs="{{prefs}}"
- on-close="onDialogClosed_">
- </settings-clear-browsing-data-dialog-tabs>
- </template>
+ <settings-clear-browsing-data-dialog prefs="{{prefs}}"
+ on-close="onDialogClosed_">
+ </settings-clear-browsing-data-dialog>
</template>
<template id="doNotTrackDialogIf" is="dom-if"
if="[[showDoNotTrackDialog_]]" notify-dom-change>
@@ -60,11 +52,11 @@
<div slot="body">$i18nRaw{doNotTrackDialogMessage}</div>
<div slot="button-container">
<paper-button class="cancel-button"
- on-tap="onDoNotTrackDialogCancel_">
+ on-click="onDoNotTrackDialogCancel_">
$i18n{cancel}
</paper-button>
<paper-button class="action-button"
- on-tap="onDoNotTrackDialogConfirm_">
+ on-click="onDoNotTrackDialogConfirm_">
$i18n{confirm}
</paper-button>
</div>
@@ -141,7 +133,7 @@
</if>
<if expr="use_nss_certs or is_win or is_macosx">
<div id="manageCertificates" class="settings-box two-line"
- actionable on-tap="onManageCertificatesTap_">
+ actionable on-click="onManageCertificatesTap_">
<div class="start">
$i18n{manageCertificates}
<div class="secondary" id="manageCertificatesSecondary">
@@ -162,7 +154,7 @@
</if>
<div id="site-settings-subpage-trigger"
class="settings-box two-line" actionable
- on-tap="onSiteSettingsTap_">
+ on-click="onSiteSettingsTap_">
<div class="start">
[[siteSettingsPageTitle_()]]
<div class="secondary" id="siteSettingsSecondary">
@@ -174,7 +166,7 @@
aria-describedby="siteSettingsSecondary"></button>
</div>
<div class="settings-box two-line" id="clearBrowsingData"
- on-tap="onClearBrowsingDataTap_" actionable>
+ on-click="onClearBrowsingDataTap_" actionable>
<div class="start">
$i18n{clearBrowsingData}
<div class="secondary" id="clearBrowsingDataSecondary">
@@ -265,7 +257,7 @@
label="$i18n{thirdPartyCookie}"
sub-label="$i18n{thirdPartyCookieSublabel}">
</settings-toggle-button>
- <div class="settings-box" actionable on-tap="onSiteDataTap_">
+ <div class="settings-box" actionable on-click="onSiteDataTap_">
<div class="start" id="cookiesLink">
$i18n{siteSettingsCookieLink}
</div>
@@ -375,6 +367,21 @@
</category-setting-exceptions>
</settings-subpage>
</template>
+ <template is="dom-if" if="[[enableSensorsContentSetting_]]" no-search>
+ <template is="dom-if" route-path="/content/sensors" no-search>
+ <settings-subpage page-title="$i18n{siteSettingsSensors}">
+ <category-default-setting
+ toggle-off-label="$i18n{siteSettingsSensorsBlock}"
+ toggle-on-label="$i18n{siteSettingsSensorsAllow}"
+ category="{{ContentSettingsTypes.SENSORS}}">
+ </category-default-setting>
+ <category-setting-exceptions
+ category="{{ContentSettingsTypes.SENSORS}}" read-only-list
+ block-header="$i18n{siteSettingsBlock}">
+ </category-setting-exceptions>
+ </settings-subpage>
+ </template>
+ </template>
<template is="dom-if" route-path="/content/notifications" no-search>
<settings-subpage page-title="$i18n{siteSettingsCategoryNotifications}">
<category-default-setting
@@ -479,7 +486,7 @@
<template is="dom-if" route-path="/cookies/detail" no-search>
<settings-subpage page-title="[[pageTitle]]">
<paper-button slot="subpage-title-extra" class="secondary-button"
- on-tap="onRemoveAllCookiesFromSite_">
+ on-click="onRemoveAllCookiesFromSite_">
$i18n{siteSettingsCookieRemoveAll}
</paper-button>
<site-data-details-subpage page-title="{{pageTitle}}">
diff --git a/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.js b/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.js
index e55ac46515f..abb969d0d3b 100644
--- a/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.js
+++ b/chromium/chrome/browser/resources/settings/privacy_page/privacy_page.js
@@ -80,14 +80,6 @@ Polymer({
showClearBrowsingDataDialog_: Boolean,
/** @private */
- tabsInCbd_: {
- type: Boolean,
- value: function() {
- return loadTimeData.getBoolean('tabsInCbd');
- }
- },
-
- /** @private */
showDoNotTrackDialog_: {
type: Boolean,
value: false,
@@ -128,6 +120,15 @@ Polymer({
}
},
+ /** @private */
+ enableSensorsContentSetting_: {
+ type: Boolean,
+ readOnly: true,
+ value: function() {
+ return loadTimeData.getBoolean('enableSensorsContentSetting');
+ }
+ },
+
/** @private {!Map<string, string>} */
focusConfig_: {
type: Object,
@@ -282,7 +283,7 @@ Polymer({
/** @private */
onDialogClosed_: function() {
- settings.navigateToPreviousRoute();
+ settings.navigateTo(settings.routes.CLEAR_BROWSER_DATA.parent);
cr.ui.focusWithoutInk(assert(this.$.clearBrowsingDataTrigger));
},
@@ -337,16 +338,21 @@ Polymer({
// </if>
/**
- * @param {boolean} enabled Whether reporting is enabled or not.
+ * @param {!SberPrefState} sberPrefState SBER enabled and managed state.
* @private
*/
- setSafeBrowsingExtendedReporting_: function(enabled) {
+ setSafeBrowsingExtendedReporting_: function(sberPrefState) {
// Ignore the next change because it will happen when we set the pref.
- this.safeBrowsingExtendedReportingPref_ = {
+ const pref = {
key: '',
type: chrome.settingsPrivate.PrefType.BOOLEAN,
- value: enabled,
+ value: sberPrefState.enabled,
};
+ if (sberPrefState.managed) {
+ pref.enforcement = chrome.settingsPrivate.Enforcement.ENFORCED;
+ pref.controlledBy = chrome.settingsPrivate.ControlledBy.USER_POLICY;
+ }
+ this.safeBrowsingExtendedReportingPref_ = pref;
},
/**
diff --git a/chromium/chrome/browser/resources/settings/privacy_page/privacy_page_browser_proxy.js b/chromium/chrome/browser/resources/settings/privacy_page/privacy_page_browser_proxy.js
index e5037cfef21..63790dfadb2 100644
--- a/chromium/chrome/browser/resources/settings/privacy_page/privacy_page_browser_proxy.js
+++ b/chromium/chrome/browser/resources/settings/privacy_page/privacy_page_browser_proxy.js
@@ -7,6 +7,9 @@
/** @typedef {{enabled: boolean, managed: boolean}} */
let MetricsReporting;
+/** @typedef {{enabled: boolean, managed: boolean}} */
+let SberPrefState;
+
cr.define('settings', function() {
/** @interface */
class PrivacyPageBrowserProxy {
@@ -25,7 +28,7 @@ cr.define('settings', function() {
// </if>
- /** @return {!Promise<boolean>} */
+ /** @return {!Promise<!SberPrefState>} */
getSafeBrowsingExtendedReporting() {}
/** @param {boolean} enabled */
diff --git a/chromium/chrome/browser/resources/settings/reset_page/powerwash_dialog.html b/chromium/chrome/browser/resources/settings/reset_page/powerwash_dialog.html
index 1459a9afe0e..98a82fd5991 100644
--- a/chromium/chrome/browser/resources/settings/reset_page/powerwash_dialog.html
+++ b/chromium/chrome/browser/resources/settings/reset_page/powerwash_dialog.html
@@ -22,10 +22,10 @@
</span>
</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onCancelTap_"
+ <paper-button class="cancel-button" on-click="onCancelTap_"
id="cancel">$i18n{cancel}</paper-button>
<paper-button class="action-button" id="powerwash"
- on-tap="onRestartTap_">$i18n{powerwashDialogButton}</paper-button>
+ on-click="onRestartTap_">$i18n{powerwashDialogButton}</paper-button>
</div>
</dialog>
</template>
diff --git a/chromium/chrome/browser/resources/settings/reset_page/reset_page.html b/chromium/chrome/browser/resources/settings/reset_page/reset_page.html
index 82b9c6cf022..d45c9976996 100644
--- a/chromium/chrome/browser/resources/settings/reset_page/reset_page.html
+++ b/chromium/chrome/browser/resources/settings/reset_page/reset_page.html
@@ -17,6 +17,7 @@
<if expr="_google_chrome and is_win">
<link rel="import" href="../chrome_cleanup_page/chrome_cleanup_page.html">
+<link rel="import" href="../incompatible_applications_page/incompatible_applications_page.html">
</if>
<dom-module id="settings-reset-page">
@@ -25,7 +26,7 @@
<settings-animated-pages id="reset-pages" section="reset">
<neon-animatable route-path="default">
<div class="settings-box first two-line" id="resetProfile"
- on-tap="onShowResetProfileDialog_" actionable>
+ on-click="onShowResetProfileDialog_" actionable>
<div class="start">
$i18n{resetTrigger}
<div class="secondary" id="resetProfileSecondary">
@@ -44,7 +45,7 @@
</template>
<if expr="chromeos">
<div class="settings-box two-line" id="powerwash" actionable
- on-tap="onShowPowerwashDialog_" hidden="[[!allowPowerwash_]]">
+ on-click="onShowPowerwashDialog_" hidden="[[!allowPowerwash_]]">
<div class="start">
$i18n{powerwashTitle}
<div class="secondary" id="powerwashSecondary">
@@ -62,20 +63,28 @@
</if>
<if expr="_google_chrome and is_win">
<template is="dom-if" if="[[userInitiatedCleanupsEnabled_]]" restamp>
- <div class="settings-box two-line" id="chromeCleanupSubpageTrigger"
- on-tap="onChromeCleanupTap_" actionable>
- <div class="start">
- $i18n{resetCleanupComputerTrigger}
- <div class="secondary" id="chromeCleanupSecondary">
- $i18n{resetCleanupComputerTriggerDescription}
- </div>
- </div>
+ <div class="settings-box" id="chromeCleanupSubpageTrigger"
+ on-click="onChromeCleanupTap_" actionable>
+ <div class="start">$i18n{resetCleanupComputerTrigger}</div>
<button id="chromeCleanupArrow" is="paper-icon-button-light"
class="subpage-arrow"
aria-label="$i18n{resetCleanupComputerTrigger}"
aria-describedby="chromeCleanupSecondary"></button>
</div>
</template>
+ <template is="dom-if" if="[[showIncompatibleApplications_]]" restamp>
+ <div class="settings-box"
+ id="incompatibleApplicationsSubpageTrigger"
+ on-click="onIncompatibleApplicationsTap_" actionable>
+ <div class="start">
+ $i18n{incompatibleApplicationsResetCardTitle}
+ </div>
+ <button is="paper-icon-button-light"
+ class="subpage-arrow"
+ aria-label="$i18n{incompatibleApplicationsResetCardTitle}"
+ aria-describedby="incompatibleApplicationsSecondary"></button>
+ </div>
+ </template>
</if>
</neon-animatable>
<if expr="_google_chrome and is_win">
@@ -89,6 +98,16 @@
</settings-subpage>
</template>
</template>
+ <template is="dom-if" if="[[showIncompatibleApplications_]]">
+ <template is="dom-if" route-path="/incompatibleApplications">
+ <settings-subpage id="incompatibleApplicationsSubpage"
+ associated-control="[[$$('#incompatibleApplicationsSubpageTrigger')]]"
+ page-title="$i18n{incompatibleApplicationsResetCardTitle}">
+ <settings-incompatible-applications-page>
+ </settings-incompatible-applications-page>
+ </settings-subpage>
+ </template>
+ </template>
</if>
</settings-animated-pages>
</template>
diff --git a/chromium/chrome/browser/resources/settings/reset_page/reset_page.js b/chromium/chrome/browser/resources/settings/reset_page/reset_page.js
index fb5c2229223..10082a0b8f8 100644
--- a/chromium/chrome/browser/resources/settings/reset_page/reset_page.js
+++ b/chromium/chrome/browser/resources/settings/reset_page/reset_page.js
@@ -40,6 +40,14 @@ Polymer({
return loadTimeData.getBoolean('userInitiatedCleanupsEnabled');
},
},
+
+ /** @private */
+ showIncompatibleApplications_: {
+ type: Boolean,
+ value: function() {
+ return loadTimeData.getBoolean('showIncompatibleApplications');
+ },
+ },
// </if>
},
@@ -87,9 +95,15 @@ Polymer({
// </if>
// <if expr="_google_chrome and is_win">
+ /** @private */
onChromeCleanupTap_: function() {
settings.navigateTo(settings.routes.CHROME_CLEANUP);
},
+
+ /** @private */
+ onIncompatibleApplicationsTap_: function() {
+ settings.navigateTo(settings.routes.INCOMPATIBLE_APPLICATIONS);
+ },
// </if>
});
diff --git a/chromium/chrome/browser/resources/settings/reset_page/reset_profile_banner.html b/chromium/chrome/browser/resources/settings/reset_page/reset_profile_banner.html
index 3f49fa4eb03..aed186e6fab 100644
--- a/chromium/chrome/browser/resources/settings/reset_page/reset_profile_banner.html
+++ b/chromium/chrome/browser/resources/settings/reset_page/reset_profile_banner.html
@@ -19,10 +19,10 @@
</span>
</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onOkTap_" id="ok">
+ <paper-button class="cancel-button" on-click="onOkTap_" id="ok">
$i18n{ok}
</paper-button>
- <paper-button class="action-button" on-tap="onResetTap_" id="reset">
+ <paper-button class="action-button" on-click="onResetTap_" id="reset">
$i18n{resetProfileBannerButton}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/reset_page/reset_profile_dialog.html b/chromium/chrome/browser/resources/settings/reset_page/reset_profile_dialog.html
index ef12529acb3..be6154e2f30 100644
--- a/chromium/chrome/browser/resources/settings/reset_page/reset_profile_dialog.html
+++ b/chromium/chrome/browser/resources/settings/reset_page/reset_profile_dialog.html
@@ -35,11 +35,11 @@
<div slot="button-container">
<paper-spinner-lite id="resetSpinner" active="[[clearingInProgress_]]">
</paper-spinner-lite>
- <paper-button class="cancel-button" on-tap="onCancelTap_"
+ <paper-button class="cancel-button" on-click="onCancelTap_"
id="cancel" disabled="[[clearingInProgress_]]">
$i18n{cancel}
</paper-button>
- <paper-button class="action-button" on-tap="onResetTap_"
+ <paper-button class="action-button" on-click="onResetTap_"
id="reset" disabled="[[clearingInProgress_]]">
$i18n{resetPageCommit}
</paper-button>
diff --git a/chromium/chrome/browser/resources/settings/reset_page/reset_profile_dialog.js b/chromium/chrome/browser/resources/settings/reset_page/reset_profile_dialog.js
index 683e2692f9e..22066b37961 100644
--- a/chromium/chrome/browser/resources/settings/reset_page/reset_profile_dialog.js
+++ b/chromium/chrome/browser/resources/settings/reset_page/reset_profile_dialog.js
@@ -77,7 +77,7 @@ Polymer({
});
this.$$('paper-checkbox a')
- .addEventListener('tap', this.onShowReportedSettingsTap_.bind(this));
+ .addEventListener('click', this.onShowReportedSettingsTap_.bind(this));
// Prevent toggling of the checkbox when hitting the "Enter" key on the
// link.
this.$$('paper-checkbox a').addEventListener('keydown', function(e) {
diff --git a/chromium/chrome/browser/resources/settings/route.js b/chromium/chrome/browser/resources/settings/route.js
index de01fd22a9c..68183214b91 100644
--- a/chromium/chrome/browser/resources/settings/route.js
+++ b/chromium/chrome/browser/resources/settings/route.js
@@ -36,6 +36,7 @@
* FONTS: (undefined|!settings.Route),
* GOOGLE_ASSISTANT: (undefined|!settings.Route),
* IMPORT_DATA: (undefined|!settings.Route),
+ * INCOMPATIBLE_APPLICATIONS: (undefined|!settings.Route),
* INPUT_METHODS: (undefined|!settings.Route),
* INTERNET: (undefined|!settings.Route),
* INTERNET_NETWORKS: (undefined|!settings.Route),
@@ -73,6 +74,7 @@
* SITE_SETTINGS_HANDLERS: (undefined|!settings.Route),
* SITE_SETTINGS_IMAGES: (undefined|!settings.Route),
* SITE_SETTINGS_JAVASCRIPT: (undefined|!settings.Route),
+ * SITE_SETTINGS_SENSORS: (undefined|!settings.Route),
* SITE_SETTINGS_SOUND: (undefined|!settings.Route),
* SITE_SETTINGS_LOCATION: (undefined|!settings.Route),
* SITE_SETTINGS_MICROPHONE: (undefined|!settings.Route),
@@ -317,6 +319,7 @@ cr.define('settings', function() {
r.SITE_SETTINGS_IMAGES = r.SITE_SETTINGS.createChild('images');
r.SITE_SETTINGS_JAVASCRIPT = r.SITE_SETTINGS.createChild('javascript');
r.SITE_SETTINGS_SOUND = r.SITE_SETTINGS.createChild('sound');
+ r.SITE_SETTINGS_SENSORS = r.SITE_SETTINGS.createChild('sensors');
r.SITE_SETTINGS_LOCATION = r.SITE_SETTINGS.createChild('location');
r.SITE_SETTINGS_MICROPHONE = r.SITE_SETTINGS.createChild('microphone');
r.SITE_SETTINGS_NOTIFICATIONS =
@@ -388,6 +391,10 @@ cr.define('settings', function() {
if (loadTimeData.getBoolean('userInitiatedCleanupsEnabled')) {
r.CHROME_CLEANUP = r.RESET.createChild('/cleanup');
}
+ if (loadTimeData.getBoolean('showIncompatibleApplications')) {
+ r.INCOMPATIBLE_APPLICATIONS =
+ r.RESET.createChild('/incompatibleApplications');
+ }
// </if>
}
}
diff --git a/chromium/chrome/browser/resources/settings/search_engines_page/omnibox_extension_entry.html b/chromium/chrome/browser/resources/settings/search_engines_page/omnibox_extension_entry.html
index aa5677369c5..89475dacb36 100644
--- a/chromium/chrome/browser/resources/settings/search_engines_page/omnibox_extension_entry.html
+++ b/chromium/chrome/browser/resources/settings/search_engines_page/omnibox_extension_entry.html
@@ -35,14 +35,16 @@
</div>
<div class="keyword-column">[[engine.keyword]]</div>
<button is="paper-icon-button-light" class="icon-more-vert"
- on-tap="onDotsTap_" title="$i18n{moreActions}" focus-row-control
+ on-click="onDotsTap_" title="$i18n{moreActions}" focus-row-control
focus-type="menu">
</button>
<dialog is="cr-action-menu">
- <button class="dropdown-item" on-tap="onManageTap_" id="manage">
+ <button slot="item" class="dropdown-item" on-click="onManageTap_"
+ id="manage">
$i18n{searchEnginesManageExtension}
</button>
- <button class="dropdown-item" on-tap="onDisableTap_" id="disable">
+ <button slot="item" class="dropdown-item" on-click="onDisableTap_"
+ id="disable">
$i18n{disable}
</button>
</dialog>
diff --git a/chromium/chrome/browser/resources/settings/search_engines_page/search_engine_dialog.html b/chromium/chrome/browser/resources/settings/search_engines_page/search_engine_dialog.html
index a2788c608ec..a8e0591c515 100644
--- a/chromium/chrome/browser/resources/settings/search_engines_page/search_engine_dialog.html
+++ b/chromium/chrome/browser/resources/settings/search_engines_page/search_engine_dialog.html
@@ -44,10 +44,10 @@
</paper-input>
</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="cancel_" id="cancel">
+ <paper-button class="cancel-button" on-click="cancel_" id="cancel">
$i18n{cancel}</paper-button>
<paper-button id="actionButton" class="action-button"
- on-tap="onActionButtonTap_">
+ on-click="onActionButtonTap_">
[[actionButtonText_]]
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html b/chromium/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html
index f4b23582e1c..5f0f410817a 100644
--- a/chromium/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html
+++ b/chromium/chrome/browser/resources/settings/search_engines_page/search_engine_entry.html
@@ -54,19 +54,19 @@
<div id="keyword-column"><div>[[engine.keyword]]</div></div>
<div id="url-column" class="text-elide">[[engine.url]]</div>
<button is="paper-icon-button-light" class="icon-more-vert"
- on-tap="onDotsTap_" title="$i18n{moreActions}" focus-row-control
+ on-click="onDotsTap_" title="$i18n{moreActions}" focus-row-control
focus-type="cr-menu-button">
</button>
<dialog is="cr-action-menu">
- <button class="dropdown-item" on-tap="onMakeDefaultTap_"
+ <button slot="item" class="dropdown-item" on-click="onMakeDefaultTap_"
hidden$="[[!engine.canBeDefault]]" id="makeDefault">
$i18n{searchEnginesMakeDefault}
</button>
- <button class="dropdown-item" on-tap="onEditTap_"
+ <button slot="item" class="dropdown-item" on-click="onEditTap_"
hidden$="[[!engine.canBeEdited]]" id="edit">
$i18n{edit}
</button>
- <button class="dropdown-item" on-tap="onDeleteTap_"
+ <button slot="item" class="dropdown-item" on-click="onDeleteTap_"
hidden$="[[!engine.canBeRemoved]]" id="delete">
$i18n{searchEnginesRemoveFromList}
</button>
diff --git a/chromium/chrome/browser/resources/settings/search_engines_page/search_engines_list.html b/chromium/chrome/browser/resources/settings/search_engines_page/search_engines_list.html
index 507bf4875ad..1e6af9d9737 100644
--- a/chromium/chrome/browser/resources/settings/search_engines_page/search_engines_list.html
+++ b/chromium/chrome/browser/resources/settings/search_engines_page/search_engines_list.html
@@ -23,7 +23,7 @@
}
#outer {
- @apply(--settings-list-frame-padding);
+ @apply --settings-list-frame-padding;
}
settings-search-engine-entry {
diff --git a/chromium/chrome/browser/resources/settings/search_engines_page/search_engines_page.html b/chromium/chrome/browser/resources/settings/search_engines_page/search_engines_page.html
index eb06d3cd345..44638d4ee43 100644
--- a/chromium/chrome/browser/resources/settings/search_engines_page/search_engines_page.html
+++ b/chromium/chrome/browser/resources/settings/search_engines_page/search_engines_page.html
@@ -19,7 +19,7 @@
.extension-engines,
#noOtherEngines,
.no-search-results {
- @apply(--settings-list-frame-padding);
+ @apply --settings-list-frame-padding;
}
settings-omnibox-extension-entry {
@@ -43,7 +43,7 @@
<div class="settings-box first">
<h2 class="start">$i18n{searchEnginesOther}</h2>
<paper-button class="secondary-button header-aligned-button"
- on-tap="onAddSearchEngineTap_" id="addSearchEngine">
+ on-click="onAddSearchEngineTap_" id="addSearchEngine">
$i18n{add}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/search_page/search_page.html b/chromium/chrome/browser/resources/settings/search_page/search_page.html
index 1e9f88f1e7c..d32e97c489c 100644
--- a/chromium/chrome/browser/resources/settings/search_page/search_page.html
+++ b/chromium/chrome/browser/resources/settings/search_page/search_page.html
@@ -84,7 +84,7 @@
<!-- Manage search engines -->
<div id="engines-subpage-trigger" class="settings-box"
- on-tap="onManageSearchEnginesTap_" actionable>
+ on-click="onManageSearchEnginesTap_" actionable>
<div class="start">
$i18n{searchEnginesManage}
</div>
@@ -96,7 +96,7 @@
<!-- Google Assistant -->
<template is="dom-if" if="[[voiceInteractionFeatureEnabled_]]">
<div id="assistant-subpage-trigger" class="settings-box two-line"
- on-tap="onGoogleAssistantTap_" actionable>
+ on-click="onGoogleAssistantTap_" actionable>
<div class="start">
$i18n{searchGoogleAssistant}
<div class="secondary">
@@ -111,7 +111,7 @@
<template is="dom-if" if="[[!assistantOn_]]">
<div class="separator"></div>
<paper-button id="enable" class="secondary-button"
- on-tap="onAssistantTurnOnTap_"
+ on-click="onAssistantTurnOnTap_"
aria-label="$i18n{searchPageTitle}"
aria-describedby="secondaryText">
$i18n{assistantTurnOn}
diff --git a/chromium/chrome/browser/resources/settings/search_settings.js b/chromium/chrome/browser/resources/settings/search_settings.js
index 684aed657e9..71971fa8246 100644
--- a/chromium/chrome/browser/resources/settings/search_settings.js
+++ b/chromium/chrome/browser/resources/settings/search_settings.js
@@ -17,18 +17,6 @@ cr.exportPath('settings');
settings.SearchResult;
cr.define('settings', function() {
- /** @type {string} */
- const WRAPPER_CSS_CLASS = 'search-highlight-wrapper';
-
- /** @type {string} */
- const ORIGINAL_CONTENT_CSS_CLASS = 'search-highlight-original-content';
-
- /** @type {string} */
- const HIT_CSS_CLASS = 'search-highlight-hit';
-
- /** @type {string} */
- const SEARCH_BUBBLE_CSS_CLASS = 'search-bubble';
-
/**
* A CSS attribute indicating that a node should be ignored during searching.
* @type {string}
@@ -65,59 +53,8 @@ cr.define('settings', function() {
* @private
*/
function findAndRemoveHighlights_(node) {
- const wrappers = node.querySelectorAll('* /deep/ .' + WRAPPER_CSS_CLASS);
-
- for (let i = 0; i < wrappers.length; i++) {
- const wrapper = wrappers[i];
- const originalNode =
- wrapper.querySelector('.' + ORIGINAL_CONTENT_CSS_CLASS);
- wrapper.parentElement.replaceChild(originalNode.firstChild, wrapper);
- }
-
- const searchBubbles =
- node.querySelectorAll('* /deep/ .' + SEARCH_BUBBLE_CSS_CLASS);
- for (let j = 0; j < searchBubbles.length; j++)
- searchBubbles[j].remove();
- }
-
- /**
- * Applies the highlight UI (yellow rectangle) around all matches in |node|.
- * @param {!Node} node The text node to be highlighted. |node| ends up
- * being removed from the DOM tree.
- * @param {!Array<string>} tokens The string tokens after splitting on the
- * relevant regExp. Even indices hold text that doesn't need highlighting,
- * odd indices hold the text to be highlighted. For example:
- * const r = new RegExp('(foo)', 'i');
- * 'barfoobar foo bar'.split(r) => ['bar', 'foo', 'bar ', 'foo', ' bar']
- * @private
- */
- function highlight_(node, tokens) {
- const wrapper = document.createElement('span');
- wrapper.classList.add(WRAPPER_CSS_CLASS);
- // Use existing node as placeholder to determine where to insert the
- // replacement content.
- node.parentNode.replaceChild(wrapper, node);
-
- // Keep the existing node around for when the highlights are removed. The
- // existing text node might be involved in data-binding and therefore should
- // not be discarded.
- const span = document.createElement('span');
- span.classList.add(ORIGINAL_CONTENT_CSS_CLASS);
- span.style.display = 'none';
- span.appendChild(node);
- wrapper.appendChild(span);
-
- for (let i = 0; i < tokens.length; ++i) {
- if (i % 2 == 0) {
- wrapper.appendChild(document.createTextNode(tokens[i]));
- } else {
- const hitSpan = document.createElement('span');
- hitSpan.classList.add(HIT_CSS_CLASS);
- hitSpan.style.backgroundColor = '#ffeb3b'; // --var(--paper-yellow-500)
- hitSpan.textContent = tokens[i];
- wrapper.appendChild(hitSpan);
- }
- }
+ cr.search_highlight_utils.findAndRemoveHighlights(node);
+ cr.search_highlight_utils.findAndRemoveBubbles(node);
}
/**
@@ -163,8 +100,10 @@ cr.define('settings', function() {
// displayed within an <option>.
// TODO(dpapad): highlight <select> controls with a search bubble
// instead.
- if (node.parentNode.nodeName != 'OPTION')
- highlight_(node, textContent.split(request.regExp));
+ if (node.parentNode.nodeName != 'OPTION') {
+ cr.search_highlight_utils.highlight(
+ node, textContent.split(request.regExp));
+ }
}
// Returning early since TEXT_NODE nodes never have children.
return;
@@ -189,44 +128,6 @@ cr.define('settings', function() {
}
/**
- * Highlights the HTML control that triggers a subpage, by displaying a search
- * bubble.
- * @param {!HTMLElement} element The element to be highlighted.
- * @param {string} rawQuery The search query.
- * @private
- */
- function highlightAssociatedControl_(element, rawQuery) {
- let searchBubble = element.querySelector('.' + SEARCH_BUBBLE_CSS_CLASS);
- // If the associated control has already been highlighted due to another
- // match on the same subpage, there is no need to do anything.
- if (searchBubble)
- return;
-
- searchBubble = document.createElement('div');
- searchBubble.classList.add(SEARCH_BUBBLE_CSS_CLASS);
- const innards = document.createElement('div');
- innards.classList.add('search-bubble-innards', 'text-elide');
- innards.textContent = rawQuery;
- searchBubble.appendChild(innards);
- element.appendChild(searchBubble);
-
- // Dynamically position the bubble at the edge the associated control
- // element.
- const updatePosition = function() {
- searchBubble.style.top = element.offsetTop +
- (innards.classList.contains('above') ? -searchBubble.offsetHeight :
- element.offsetHeight) +
- 'px';
- };
- updatePosition();
-
- searchBubble.addEventListener('mouseover', function() {
- innards.classList.toggle('above');
- updatePosition();
- });
- }
-
- /**
* Finds and makes visible the <settings-section> parent of |node|.
* @param {!Node} node
* @param {string} rawQuery
@@ -253,8 +154,10 @@ cr.define('settings', function() {
// Need to add the search bubble after the parent SETTINGS-SECTION has
// become visible, otherwise |offsetWidth| returns zero.
- if (associatedControl)
- highlightAssociatedControl_(associatedControl, rawQuery);
+ if (associatedControl) {
+ cr.search_highlight_utils.highlightControlWithBubble(
+ associatedControl, rawQuery);
+ }
}
/** @abstract */
diff --git a/chromium/chrome/browser/resources/settings/settings.html b/chromium/chrome/browser/resources/settings/settings.html
index 8732184b614..2f8a74473c8 100644
--- a/chromium/chrome/browser/resources/settings/settings.html
+++ b/chromium/chrome/browser/resources/settings/settings.html
@@ -10,6 +10,8 @@
html {
background-color: #f1f1f1;
overflow: hidden;
+ /* Remove 300ms delay for 'click' event, when using touch interface. */
+ touch-action: manipulation;
}
.loading {
@@ -20,6 +22,7 @@
</head>
<body>
<settings-ui></settings-ui>
+ <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="settings_ui/settings_ui.html">
diff --git a/chromium/chrome/browser/resources/settings/settings_main/settings_main.html b/chromium/chrome/browser/resources/settings/settings_main/settings_main.html
index fcbd63786ef..31e3040c21e 100644
--- a/chromium/chrome/browser/resources/settings/settings_main/settings_main.html
+++ b/chromium/chrome/browser/resources/settings/settings_main/settings_main.html
@@ -2,6 +2,7 @@
<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
<link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/html/search_highlight_utils.html">
<link rel="import" href="chrome://resources/html/promise_resolver.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-announcer/iron-a11y-announcer.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
diff --git a/chromium/chrome/browser/resources/settings/settings_page/settings_animated_pages.html b/chromium/chrome/browser/resources/settings/settings_page/settings_animated_pages.html
index 5b3e82131ca..655b3faa8a0 100644
--- a/chromium/chrome/browser/resources/settings/settings_page/settings_animated_pages.html
+++ b/chromium/chrome/browser/resources/settings/settings_page/settings_animated_pages.html
@@ -11,6 +11,7 @@
<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animatable.html">
<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animated-pages.html">
<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/neon-animation-runner-behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/neon-animation/web-animations.html">
<link rel="import" href="../animation/fade_animations.html">
<link rel="import" href="../route.html">
diff --git a/chromium/chrome/browser/resources/settings/settings_page/settings_section.html b/chromium/chrome/browser/resources/settings/settings_page/settings_section.html
index 644879a6d93..3daf5135314 100644
--- a/chromium/chrome/browser/resources/settings/settings_page/settings_section.html
+++ b/chromium/chrome/browser/resources/settings/settings_page/settings_section.html
@@ -20,13 +20,13 @@
}
#header .title {
- @apply(--cr-section-text);
+ @apply --cr-section-text;
margin-bottom: 0;
margin-top: var(--settings-page-vertical-margin);
}
#card {
- @apply(--shadow-elevation-2dp);
+ @apply --shadow-elevation-2dp;
background-color: white;
border-radius: 2px;
flex: 1;
@@ -39,7 +39,7 @@
:host(.expanding) #card,
:host(.collapsing) #card,
:host(.expanded) #card {
- @apply(--shadow-elevation-4dp);
+ @apply --shadow-elevation-4dp;
overflow: hidden;
/* A stacking context constrains sliding sub-pages to the card. */
z-index: 0;
diff --git a/chromium/chrome/browser/resources/settings/settings_page/settings_subpage.html b/chromium/chrome/browser/resources/settings/settings_page/settings_subpage.html
index 12b26d259d6..1021acdcbe8 100644
--- a/chromium/chrome/browser/resources/settings/settings_page/settings_subpage.html
+++ b/chromium/chrome/browser/resources/settings/settings_page/settings_subpage.html
@@ -26,7 +26,7 @@
}
#learnMore {
- @apply(--cr-paper-icon-button-margin);
+ @apply --cr-paper-icon-button-margin;
align-items: center;
display: flex;
height: var(--cr-icon-ripple-size);
@@ -42,12 +42,12 @@
}
paper-spinner-lite {
- @apply(--cr-icon-height-width);
+ @apply --cr-icon-height-width;
}
h1 {
flex: 1; /* Push other items to the end. */
- @apply(--cr-title-text);
+ @apply --cr-title-text;
}
settings-subpage-search {
@@ -56,7 +56,7 @@
}
</style>
<div class="settings-box first" id="headerLine">
- <button is="paper-icon-button-light" on-tap="onTapBack_"
+ <button is="paper-icon-button-light" on-click="onTapBack_"
aria-label="$i18n{back}" class="icon-arrow-back">
</button>
<h1>[[pageTitle]]</h1>
diff --git a/chromium/chrome/browser/resources/settings/settings_page/settings_subpage_search.html b/chromium/chrome/browser/resources/settings/settings_page/settings_subpage_search.html
index 4f4245f7fec..da6272b5834 100644
--- a/chromium/chrome/browser/resources/settings/settings_page/settings_subpage_search.html
+++ b/chromium/chrome/browser/resources/settings/settings_page/settings_subpage_search.html
@@ -71,10 +71,10 @@
<paper-input-container no-label-float>
<input id="searchInput" type="search" on-search="onSearchTermSearch"
on-input="onSearchTermInput" aria-label$="[[label]]" incremental
- autofocus$="[[autofocus]]" placeholder="[[label]]">
- <button suffix is="paper-icon-button-light" id="clearSearch"
- class="icon-cancel" on-tap="onTapClear_" title="[[clearLabel]]"
- hidden$="[[!hasSearchText]]">
+ autofocus$="[[autofocus]]" placeholder="[[label]]" slot="input">
+ <button is="paper-icon-button-light" id="clearSearch"
+ class="icon-cancel" on-click="onTapClear_" title="[[clearLabel]]"
+ hidden$="[[!hasSearchText]]" slot="suffix">
</button>
</paper-input-container>
</template>
diff --git a/chromium/chrome/browser/resources/settings/settings_resources.grd b/chromium/chrome/browser/resources/settings/settings_resources.grd
index 34e3036a3cc..e4122b701b2 100644
--- a/chromium/chrome/browser/resources/settings/settings_resources.grd
+++ b/chromium/chrome/browser/resources/settings/settings_resources.grd
@@ -325,6 +325,26 @@
file="chrome_cleanup_page/items_to_remove_list.js"
type="chrome_html"/>
</if>
+ <if expr="is_win and _google_chrome">
+ <structure name="IDR_SETTINGS_INCOMPATIBLE_APPLICATIONS_PAGE_HTML"
+ file="incompatible_applications_page/incompatible_applications_page.html"
+ type="chrome_html" />
+ <structure name="IDR_SETTINGS_INCOMPATIBLE_APPLICATIONS_PAGE_JS"
+ file="incompatible_applications_page/incompatible_applications_page.js"
+ type="chrome_html" />
+ <structure name="IDR_SETTINGS_INCOMPATIBLE_APPLICATIONS_BROWSER_PROXY_HTML"
+ file="incompatible_applications_page/incompatible_applications_browser_proxy.html"
+ type="chrome_html" />
+ <structure name="IDR_SETTINGS_INCOMPATIBLE_APPLICATIONS_BROWSER_PROXY_JS"
+ file="incompatible_applications_page/incompatible_applications_browser_proxy.js"
+ type="chrome_html" />
+ <structure name="IDR_SETTINGS_INCOMPATIBLE_APPLICATIONS_INCOMPATIBLE_APPLICATION_ITEM_HTML"
+ file="incompatible_applications_page/incompatible_application_item.html"
+ type="chrome_html" />
+ <structure name="IDR_SETTINGS_INCOMPATIBLE_APPLICATIONS_INCOMPATIBLE_APPLICATION_ITEM_JS"
+ file="incompatible_applications_page/incompatible_application_item.js"
+ type="chrome_html" />
+ </if>
<structure name="IDR_SETTINGS_CLEAR_BROWSING_DATA_BROWSER_PROXY_HTML"
file="clear_browsing_data_dialog/clear_browsing_data_browser_proxy.html"
type="chrome_html" />
@@ -337,12 +357,6 @@
<structure name="IDR_SETTINGS_CLEAR_BROWSING_DATA_DIALOG_JS"
file="clear_browsing_data_dialog/clear_browsing_data_dialog.js"
type="chrome_html" />
- <structure name="IDR_SETTINGS_CLEAR_BROWSING_DATA_DIALOG_TABS_HTML"
- file="clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.html"
- type="chrome_html" />
- <structure name="IDR_SETTINGS_CLEAR_BROWSING_DATA_DIALOG_TABS_JS"
- file="clear_browsing_data_dialog/clear_browsing_data_dialog_tabs.js"
- type="chrome_html" />
<structure name="IDR_SETTINGS_HISTORY_DELETION_DIALOG_HTML"
file="clear_browsing_data_dialog/history_deletion_dialog.html"
type="chrome_html" />
@@ -707,6 +721,14 @@
preprocess="true"
allowexternalscript="true" />
<if expr="not chromeos">
+ <structure name="IDR_SETTINGS_PEOPLE_PAGE_SYNC_ACCOUNT_CONTROL_HTML"
+ file="people_page/sync_account_control.html"
+ type="chrome_html"
+ flattenhtml="true"
+ allowexternalscript="true" />
+ <structure name="IDR_SETTINGS_PEOPLE_PAGE_SYNC_ACCOUNT_CONTROL_JS"
+ file="people_page/sync_account_control.js"
+ type="chrome_html" />
<structure name="IDR_SETTINGS_PEOPLE_PAGE_IMPORT_DATA_DIALOG_HTML"
file="people_page/import_data_dialog.html"
type="chrome_html" />
@@ -1258,11 +1280,6 @@
type="chrome_html"
preprocess="true"
allowexternalscript="true" />
- <structure name="IDR_SETTINGS_PEOPLE_PIN_KEYBOARD_HTML"
- file="people_page/pin_keyboard.html"
- type="chrome_html"
- preprocess="true"
- allowexternalscript="true"/>
<structure name="IDR_SETTINGS_PEOPLE_LOCK_SCREEN_JS"
file="people_page/lock_screen.js"
type="chrome_html" />
@@ -1319,10 +1336,6 @@
<structure name="IDR_SETTINGS_PEOPLE_FINGERPRINT_BROWSER_PROXY_HTML"
file="people_page/fingerprint_browser_proxy.html"
type="chrome_html" />
- <structure name="IDR_SETTINGS_KEYBOARD_PIN_JS"
- file="people_page/pin_keyboard.js"
- type="chrome_html"
- preprocess="true" />
<structure name="IDR_SETTINGS_USERS_PAGE_ADD_USER_DIALOG_JS"
file="people_page/users_add_user_dialog.js"
type="chrome_html" />
diff --git a/chromium/chrome/browser/resources/settings/settings_shared_css.html b/chromium/chrome/browser/resources/settings/settings_shared_css.html
index 36f43725cd7..2c89c7a5a36 100644
--- a/chromium/chrome/browser/resources/settings/settings_shared_css.html
+++ b/chromium/chrome/browser/resources/settings/settings_shared_css.html
@@ -2,6 +2,7 @@
<link rel="import" href="chrome://resources/cr_elements/paper_checkbox_style_css.html">
<link rel="import" href="chrome://resources/cr_elements/paper_input_style_css.html">
<link rel="import" href="chrome://resources/cr_elements/paper_toggle_style_css.html">
+<link rel="import" href="chrome://resources/cr_elements/search_highlight_style_css.html">
<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
<link rel="import" href="settings_icons_css.html">
@@ -11,7 +12,7 @@
<!-- Common styles for Material Design settings. -->
<dom-module id="settings-shared">
<template>
- <style include="settings-icons paper-button-style paper-checkbox-style paper-input-style paper-toggle-style cr-shared-style">
+ <style include="settings-icons paper-button-style paper-checkbox-style paper-input-style paper-toggle-style cr-shared-style search-highlight-style">
/* Prevent action-links from being selected to avoid accidental
* selection when trying to click it. */
a[is=action-link] {
@@ -89,8 +90,9 @@
-webkit-margin-start: 16px;
}
- /* Adjust the margin between the separator and the first button. */
- .separator + paper-button {
+ /* Adjust the margin between the separator and the first button. Exclude
+ * .action-button since it has a background thus is visually different. */
+ .separator + paper-button:not(.action-button) {
-webkit-margin-start: calc(var(--cr-button-edge-spacing) * -1);
}
@@ -105,7 +107,7 @@
}
paper-toggle-button {
- @apply(--settings-actionable);
+ @apply --settings-actionable;
height: var(--settings-row-min-height);
user-select: none; /* Prevents text selection while dragging. */
width: 36px;
@@ -154,7 +156,7 @@
/* See also: .no-min-width below. */
.text-elide {
- @apply(--settings-text-elide);
+ @apply --cr-text-elide;
}
/* By default, flexbox children have min-width calculated to be the width
@@ -174,7 +176,7 @@
* outside of a settings-box. A list-frame is likely to follow a
* settings box. */
.list-frame {
- @apply(--settings-list-frame-padding);
+ @apply --settings-list-frame-padding;
align-items: center;
display: block;
}
@@ -230,7 +232,7 @@
/* A settings-box is a horizontal row of text or controls within a
* setting section (page or subpage). */
.settings-box {
- @apply(--cr-section);
+ @apply --cr-section;
}
.settings-box.two-line {
@@ -273,7 +275,7 @@
/* The lower line of text in a two-line row. */
.secondary {
- @apply(--cr-secondary-text);
+ @apply --cr-secondary-text;
}
/* The |:empty| CSS selector only works when there is no whitespace.
@@ -358,44 +360,6 @@
width: 16px;
}
- .search-bubble {
- /* RGB value matches var(--paper-yellow-500). */
- --search-bubble-color: rgba(255, 235, 59, 0.9);
- position: absolute;
- z-index: 1;
- }
-
- .search-bubble-innards {
- align-items: center;
- background-color: var(--search-bubble-color);
- border-radius: 2px;
- max-width: 100px;
- min-width: 64px;
- padding: 4px 10px;
- text-align: center;
- }
-
- /* Provides the arrow which points at the anchor element. */
- .search-bubble-innards::after {
- background-color: var(--search-bubble-color);
- content: '';
- height: 10px;
- left: calc(50% - 5px);
- position: absolute;
- top: -5px;
- transform: rotate(-45deg);
- width: 10px;
- z-index: -1;
- }
-
- /* Turns the arrow direction downwards, when the bubble is placed above
- * the anchor element */
- .search-bubble-innards.above::after {
- bottom: -5px;
- top: auto;
- transform: rotate(-135deg);
- }
-
.column-header {
color: var(--paper-grey-600);
font-weight: 500;
diff --git a/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.html b/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.html
index 7aca114c1af..9031db844ab 100644
--- a/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.html
+++ b/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.html
@@ -24,7 +24,7 @@
<template>
<style include="settings-shared">
:host {
- @apply(--layout-fit);
+ @apply --layout-fit;
color: var(--primary-text-color);
display: flex;
flex-direction: column;
@@ -38,7 +38,7 @@
}
cr-toolbar {
- @apply(--layout-center);
+ @apply --layout-center;
--iron-icon-fill-color: white;
background-color: var(--google-blue-700);
color: white;
diff --git a/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.js b/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.js
index 097280993a4..4b8c62678ec 100644
--- a/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.js
+++ b/chromium/chrome/browser/resources/settings/settings_ui/settings_ui.js
@@ -118,6 +118,8 @@ Polymer({
loadTimeData.getString('networkListItemConnectingTo'),
networkListItemInitializing:
loadTimeData.getString('networkListItemInitializing'),
+ networkListItemScanning:
+ loadTimeData.getString('networkListItemScanning'),
networkListItemNotConnected:
loadTimeData.getString('networkListItemNotConnected'),
networkListItemNoNetwork:
diff --git a/chromium/chrome/browser/resources/settings/settings_vars_css.html b/chromium/chrome/browser/resources/settings/settings_vars_css.html
index 595e689d955..5e9062ead38 100644
--- a/chromium/chrome/browser/resources/settings/settings_vars_css.html
+++ b/chromium/chrome/browser/resources/settings/settings_vars_css.html
@@ -37,12 +37,6 @@
--settings-row-three-line-min-height:
var(--cr-section-three-line-min-height);
- --settings-text-elide: {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- };
-
--settings-separator-height: var(--cr-separator-height);
--settings-separator-line: var(--cr-separator-line);
diff --git a/chromium/chrome/browser/resources/settings/site_settings/add_site_dialog.html b/chromium/chrome/browser/resources/settings/site_settings/add_site_dialog.html
index 94157bee5f4..3aad9d10c78 100644
--- a/chromium/chrome/browser/resources/settings/site_settings/add_site_dialog.html
+++ b/chromium/chrome/browser/resources/settings/site_settings/add_site_dialog.html
@@ -35,10 +35,10 @@
</paper-checkbox>
</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onCancelTap_">
+ <paper-button class="cancel-button" on-click="onCancelTap_">
$i18n{cancel}
</paper-button>
- <paper-button class="action-button" id="add" on-tap="onSubmit_"
+ <paper-button class="action-button" id="add" on-click="onSubmit_"
disabled>
$i18n{add}
</paper-button>
diff --git a/chromium/chrome/browser/resources/settings/site_settings/all_sites.html b/chromium/chrome/browser/resources/settings/site_settings/all_sites.html
index 47fa36b5184..2891cc77aa8 100644
--- a/chromium/chrome/browser/resources/settings/site_settings/all_sites.html
+++ b/chromium/chrome/browser/resources/settings/site_settings/all_sites.html
@@ -18,7 +18,7 @@
<div class="list-frame menu-content vertical-list" id="listContainer">
<template is="dom-repeat" items="[[sites]]">
<div class="list-item">
- <div class="layout horizontal center flex" on-tap="onOriginTap_"
+ <div class="layout horizontal center flex" on-click="onOriginTap_"
actionable>
<div class="favicon-image"
style$="[[computeSiteIcon(item.origin)]]">
diff --git a/chromium/chrome/browser/resources/settings/site_settings/category_default_setting.js b/chromium/chrome/browser/resources/settings/site_settings/category_default_setting.js
index 220b76dad6f..9933ccaaabe 100644
--- a/chromium/chrome/browser/resources/settings/site_settings/category_default_setting.js
+++ b/chromium/chrome/browser/resources/settings/site_settings/category_default_setting.js
@@ -97,6 +97,7 @@ Polymer({
case settings.ContentSettingsTypes.IMAGES:
case settings.ContentSettingsTypes.JAVASCRIPT:
case settings.ContentSettingsTypes.SOUND:
+ case settings.ContentSettingsTypes.SENSORS:
case settings.ContentSettingsTypes.POPUPS:
case settings.ContentSettingsTypes.PROTOCOL_HANDLERS:
diff --git a/chromium/chrome/browser/resources/settings/site_settings/constants.js b/chromium/chrome/browser/resources/settings/site_settings/constants.js
index c6a690d271f..f878c560619 100644
--- a/chromium/chrome/browser/resources/settings/site_settings/constants.js
+++ b/chromium/chrome/browser/resources/settings/site_settings/constants.js
@@ -30,9 +30,10 @@ settings.ContentSettingsTypes = {
MIDI_DEVICES: 'midi-sysex',
USB_DEVICES: 'usb-chooser-data',
ZOOM_LEVELS: 'zoom-levels',
- PROTECTED_CONTENT: 'protectedContent',
+ PROTECTED_CONTENT: 'protected-content',
ADS: 'ads',
CLIPBOARD: 'clipboard',
+ SENSORS: 'sensors',
};
/**
diff --git a/chromium/chrome/browser/resources/settings/site_settings/edit_exception_dialog.html b/chromium/chrome/browser/resources/settings/site_settings/edit_exception_dialog.html
index f6ab56fa94a..25da836f905 100644
--- a/chromium/chrome/browser/resources/settings/site_settings/edit_exception_dialog.html
+++ b/chromium/chrome/browser/resources/settings/site_settings/edit_exception_dialog.html
@@ -19,10 +19,10 @@
</paper-input>
</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onCancelTap_"
+ <paper-button class="cancel-button" on-click="onCancelTap_"
id="cancel">$i18n{cancel}</paper-button>
<paper-button id="actionButton" class="action-button"
- on-tap="onActionButtonTap_" disabled="[[invalid_]]">
+ on-click="onActionButtonTap_" disabled="[[invalid_]]">
$i18n{edit}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.html b/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.html
index a37356e6dda..b734faea7df 100644
--- a/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.html
+++ b/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.html
@@ -44,34 +44,55 @@
<div class="middle" >
<div class="protocol-host">[[item.host]]</div>
<div class="secondary protocol-default"
- hidden$="[[!isDefault_(index, protocol.default_handler)]]">
+ hidden$="[[!item.is_default]]">
$i18n{handlerIsDefault}
</div>
</div>
- <button is="paper-icon-button-light" on-tap="showMenu_"
+ <button is="paper-icon-button-light" on-click="showMenu_"
class="icon-more-vert" title="$i18n{moreActions}">
</button>
</div>
-
</template>
</div>
</template>
<dialog is="cr-action-menu">
- <button class="dropdown-item" on-tap="onDefaultTap_" id="defaultButton"
- hidden$="[[isModelDefault_(actionMenuModel_)]]">
+ <button slot="item" class="dropdown-item" on-click="onDefaultClick_"
+ id="defaultButton" hidden$="[[actionMenuModel_.is_default]]">
$i18n{handlerSetDefault}
</button>
- <button class="dropdown-item" on-tap="onRemoveTap_" id="removeButton">
+ <button slot="item" class="dropdown-item" on-click="onRemoveClick_"
+ id="removeButton">
$i18n{handlerRemove}
</button>
</dialog>
+ <template is="dom-if" if="[[ignoredProtocols.length]]">
+ <div class="column-header">$i18n{siteSettingsBlocked}</div>
+ <div class="list-frame menu-content vertical-list">
+ <template is="dom-repeat" items="[[ignoredProtocols]]">
+ <div class="list-item">
+ <div class="favicon-image" style$="[[computeSiteIcon(item.host)]]">
+ </div>
+ <div class="middle" >
+ <div class="protocol-host">[[item.host]]</div>
+ <div class="secondary protocol-protocol">[[item.protocol]]</div>
+ </div>
+
+ <button is="paper-icon-button-light" on-click="onRemoveIgnored_"
+ class="icon-clear" title="$i18n{moreActions}"
+ id="removeIgnoredButton">
+ </button>
+ </div>
+ </template>
+ </div>
+ </template>
+
<if expr="chromeos">
<template is="dom-if" if="[[settingsAppAvailable_]]">
<div class="settings-box first"
- on-tap="onManageAndroidAppsTap_" actionable>
+ on-click="onManageAndroidAppsClick_" actionable>
<div class="start">
<div>$i18n{androidAppsManageAppLinks}</div>
</div>
diff --git a/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.js b/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.js
index 6ad35cec77e..17667e90278 100644
--- a/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.js
+++ b/chromium/chrome/browser/resources/settings/site_settings/protocol_handlers.js
@@ -19,16 +19,14 @@ const MenuActions = {
/**
* @typedef {{host: string,
+ * is_default: boolean,
* protocol: string,
* spec: string}}
*/
let HandlerEntry;
/**
- * @typedef {{default_handler: number,
- * handlers: !Array<!HandlerEntry>,
- * has_policy_recommendations: boolean,
- * is_default_handler_set_by_user: boolean,
+ * @typedef {{handlers: !Array<!HandlerEntry>,
* protocol: string}}
*/
let ProtocolEntry;
@@ -52,7 +50,7 @@ Polymer({
/**
* The targetted object for menu operations.
- * @private {?Object}
+ * @private {?HandlerEntry}
*/
actionMenuModel_: Object,
@@ -60,6 +58,12 @@ Polymer({
toggleOffLabel: String,
toggleOnLabel: String,
+ /**
+ * Array of ignored (blocked) protocols.
+ * @type {!Array<!HandlerEntry>}
+ */
+ ignoredProtocols: Array,
+
// <if expr="chromeos">
/** @private */
settingsAppAvailable_: {
@@ -114,17 +118,6 @@ Polymer({
},
/**
- * Returns whether the given index matches the default handler.
- * @param {number} index The index to evaluate.
- * @param {number} defaultHandler The default handler index.
- * @return {boolean} Whether the item is default.
- * @private
- */
- isDefault_: function(index, defaultHandler) {
- return defaultHandler == index;
- },
-
- /**
* Updates the main toggle to set it enabled/disabled.
* @param {boolean} enabled The state to set.
* @private
@@ -144,13 +137,21 @@ Polymer({
/**
* Updates the list of ignored protocol handlers.
- * @param {!Array<!ProtocolEntry>} args The new (ignored) protocol handler
- * list.
+ * @param {!Array<!HandlerEntry>} ignoredProtocols The new (ignored) protocol
+ * handler list.
* @private
*/
- setIgnoredProtocolHandlers_: function(args) {
- // TODO(finnur): Figure this out. Have yet to be able to trigger the C++
- // side to send this.
+ setIgnoredProtocolHandlers_: function(ignoredProtocols) {
+ this.ignoredProtocols = ignoredProtocols;
+ },
+
+ /**
+ * Closes action menu and resets action menu model
+ * @private
+ */
+ closeActionMenu_: function() {
+ this.$$('dialog[is=cr-action-menu]').close();
+ this.actionMenuModel_ = null;
},
/**
@@ -165,45 +166,38 @@ Polymer({
* The handler for when "Set Default" is selected in the action menu.
* @private
*/
- onDefaultTap_: function() {
- const item = this.actionMenuModel_.item;
-
- this.$$('dialog[is=cr-action-menu]').close();
- this.actionMenuModel_ = null;
+ onDefaultClick_: function() {
+ const item = this.actionMenuModel_;
this.browserProxy.setProtocolDefault(item.protocol, item.spec);
+ this.closeActionMenu_();
},
/**
* The handler for when "Remove" is selected in the action menu.
* @private
*/
- onRemoveTap_: function() {
- const item = this.actionMenuModel_.item;
-
- this.$$('dialog[is=cr-action-menu]').close();
- this.actionMenuModel_ = null;
+ onRemoveClick_: function() {
+ const item = this.actionMenuModel_;
this.browserProxy.removeProtocolHandler(item.protocol, item.spec);
+ this.closeActionMenu_();
},
/**
- * Checks whether or not the selected actionMenuModel is the default handler
- * for its protocol.
- * @return {boolean} if actionMenuModel_ is default handler of its protocol.
+ * Handler for removing handlers that were blocked
+ * @private
*/
- isModelDefault_: function() {
- return !!this.actionMenuModel_ &&
- (this.actionMenuModel_.index ==
- this.actionMenuModel_.protocol.default_handler);
+ onRemoveIgnored_: function(event) {
+ const item = event.model.item;
+ this.browserProxy.removeProtocolHandler(item.protocol, item.spec);
},
/**
* A handler to show the action menu next to the clicked menu button.
- * @param {!{model: !{protocol: HandlerEntry, item: ProtocolEntry,
- * index: number}}} event
+ * @param {!{model: !{item: HandlerEntry}}} event
* @private
*/
showMenu_: function(event) {
- this.actionMenuModel_ = event.model;
+ this.actionMenuModel_ = event.model.item;
/** @type {!CrActionMenuElement} */ (this.$$('dialog[is=cr-action-menu]'))
.showAt(
/** @type {!Element} */ (
@@ -215,7 +209,7 @@ Polymer({
* Opens an activity to handle App links (preferred apps).
* @private
*/
- onManageAndroidAppsTap_: function() {
+ onManageAndroidAppsClick_: function() {
this.browserProxy.showAndroidManageAppLinks();
},
// </if>
diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_data.html b/chromium/chrome/browser/resources/settings/site_settings/site_data.html
index c50d94123c3..a92719b376e 100644
--- a/chromium/chrome/browser/resources/settings/site_settings/site_data.html
+++ b/chromium/chrome/browser/resources/settings/site_settings/site_data.html
@@ -25,7 +25,7 @@
}
paper-spinner-lite {
- @apply(--cr-icon-height-width);
+ @apply --cr-icon-height-width;
opacity: 0;
transition-delay: 1s;
}
@@ -46,7 +46,7 @@
<paper-spinner-lite active="[[isLoading_]]"></paper-spinner-lite>
<paper-button class="secondary-button"
disabled$="[[isLoading_]]" id="removeShowingSites"
- on-tap="onRemoveShowingSitesTap_" hidden$="[[!sites.length]]">
+ on-click="onRemoveShowingSitesTap_" hidden$="[[!sites.length]]">
[[computeRemoveLabel_(filter)]]
</paper-button>
</div>
@@ -54,7 +54,7 @@
scroll-target="[[subpageScrollTarget]]">
<template>
<div class="settings-box two-line site-item" first$="[[!index]]"
- on-tap="onSiteTap_" actionable>
+ on-click="onSiteTap_" actionable>
<div class="favicon-image"
style$="background-image: [[favicon_(item.site)]]">
</div>
@@ -67,7 +67,7 @@
<div class="separator"></div>
<button is="paper-icon-button-light" class="icon-delete-gray"
title$="[[i18n('siteSettingsCookieRemoveSite', item.site)]]"
- on-tap="onRemoveSiteTap_">
+ on-click="onRemoveSiteTap_">
</button>
</div>
</template>
@@ -81,10 +81,10 @@
</div>
<div slot="body">$i18n{siteSettingsCookieRemoveMultipleConfirmation}</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onCloseDialog_">
+ <paper-button class="cancel-button" on-click="onCloseDialog_">
$i18n{cancel}
</paper-button>
- <paper-button class="action-button" on-tap="onConfirmDelete_">
+ <paper-button class="action-button" on-click="onConfirmDelete_">
$i18n{siteSettingsCookiesClearAll}
</paper-button>
</div>
diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_data_details_subpage.html b/chromium/chrome/browser/resources/settings/site_settings/site_data_details_subpage.html
index 958e28f8cb5..aedab0ae6d3 100644
--- a/chromium/chrome/browser/resources/settings/site_settings/site_data_details_subpage.html
+++ b/chromium/chrome/browser/resources/settings/site_settings/site_data_details_subpage.html
@@ -29,7 +29,7 @@
</cr-expand-button>
<div class="separator"></div>
<button is="paper-icon-button-light" data-id-path$="[[item.idPath]]"
- class="icon-clear" on-tap="onRemove_">
+ class="icon-clear" on-click="onRemove_">
</button>
</div>
<iron-collapse class="list-frame vertical-list"
diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_details.html b/chromium/chrome/browser/resources/settings/site_settings/site_details.html
index 9a10a9ebf6b..b7f1908390e 100644
--- a/chromium/chrome/browser/resources/settings/site_settings/site_details.html
+++ b/chromium/chrome/browser/resources/settings/site_settings/site_details.html
@@ -44,10 +44,10 @@
$i18n{siteSettingsSiteResetConfirmation}
</div>
<div slot="button-container">
- <paper-button class="cancel-button" on-tap="onCloseDialog_">
+ <paper-button class="cancel-button" on-click="onCloseDialog_">
$i18n{cancel}
</paper-button>
- <paper-button class="action-button" on-tap="onClearAndReset_">
+ <paper-button class="action-button" on-click="onClearAndReset_">
$i18n{siteSettingsSiteResetAll}
</paper-button>
</div>
@@ -66,7 +66,8 @@
<div class="list-item" id="storage" hidden$="[[!storedData_]]">
<div class="start">[[storedData_]]</div>
<button is="paper-icon-button-light" class="icon-delete-gray"
- on-tap="onConfirmClearStorage_" alt="$i18n{siteSettingsDelete}">
+ on-click="onConfirmClearStorage_"
+ aria-label="$i18n{siteSettingsDelete}">
</button>
</div>
</div>
@@ -89,6 +90,12 @@
icon="settings:mic" id="mic"
label="$i18n{siteSettingsMic}">
</site-details-permission>
+ <site-details-permission
+ category="{{ContentSettingsTypes.SENSORS}}"
+ icon="settings:sensors" id="sensors"
+ label="$i18n{siteSettingsSensors}"
+ hidden$="[[!enableSensorsContentSetting_]]">
+ </site-details-permission>
<site-details-permission category="{{ContentSettingsTypes.NOTIFICATIONS}}"
icon="settings:notifications" id="notifications"
label="$i18n{siteSettingsNotifications}">
@@ -132,12 +139,6 @@
id="midiDevices" label="$i18n{siteSettingsMidiDevices}">
</site-details-permission>
<site-details-permission
- category="{{ContentSettingsTypes.CLIPBOARD}}"
- icon="settings:clipboard" id="clipboard"
- label="$i18n{siteSettingsClipboard}"
- hidden$="[[!enableClipboardContentSetting_]]">
- </site-details-permission>
- <site-details-permission
category="{{ContentSettingsTypes.UNSANDBOXED_PLUGINS}}"
icon="cr:extension" id="unsandboxedPlugins"
label="$i18n{siteSettingsUnsandboxedPlugins}">
@@ -149,10 +150,16 @@
label="$i18n{siteSettingsProtectedContentIdentifiers}">
</site-details-permission>
</if>
+ <site-details-permission
+ category="{{ContentSettingsTypes.CLIPBOARD}}"
+ icon="settings:clipboard" id="clipboard"
+ label="$i18n{siteSettingsClipboard}"
+ hidden$="[[!enableClipboardContentSetting_]]">
+ </site-details-permission>
</div>
<div id="clearAndReset" class="settings-box"
- on-tap="onConfirmClearSettings_" actionable>
+ on-click="onConfirmClearSettings_" actionable>
<div class="start">
$i18n{siteSettingsReset}
</div>
diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_details.js b/chromium/chrome/browser/resources/settings/site_settings/site_details.js
index 6d4f9c7c7db..73bd087ea10 100644
--- a/chromium/chrome/browser/resources/settings/site_settings/site_details.js
+++ b/chromium/chrome/browser/resources/settings/site_settings/site_details.js
@@ -74,6 +74,15 @@ Polymer({
},
},
+ /** @private */
+ enableSensorsContentSetting_: {
+ type: Boolean,
+ readOnly: true,
+ value: function() {
+ return loadTimeData.getBoolean('enableSensorsContentSetting');
+ },
+ },
+
/**
* The type of storage for the origin.
* @private
@@ -236,6 +245,8 @@ Polymer({
onClearAndReset_: function() {
this.browserProxy.setOriginPermissions(
this.origin, this.getCategoryList_(), settings.ContentSetting.DEFAULT);
+ if (this.getCategoryList_().includes(settings.ContentSettingsTypes.PLUGINS))
+ this.browserProxy.clearFlashPref(this.origin);
if (this.storedData_ != '')
this.onClearStorage_();
diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_list.html b/chromium/chrome/browser/resources/settings/site_settings/site_list.html
index 4f0141e4d32..9090d7df259 100644
--- a/chromium/chrome/browser/resources/settings/site_settings/site_list.html
+++ b/chromium/chrome/browser/resources/settings/site_settings/site_list.html
@@ -30,29 +30,31 @@
<h2 class="start">[[categoryHeader]]</h2>
<paper-button id="addSite"
class="secondary-button header-aligned-button"
- hidden="[[readOnlyList]]" on-tap="onAddSiteTap_">
+ hidden="[[readOnlyList]]" on-click="onAddSiteTap_">
$i18n{add}
</paper-button>
</div>
<dialog is="cr-action-menu">
- <button class="dropdown-item" id="allow"
- on-tap="onAllowTap_" hidden$="[[!showAllowAction_]]">
+ <button slot="item" class="dropdown-item" id="allow"
+ on-click="onAllowTap_" hidden$="[[!showAllowAction_]]">
$i18n{siteSettingsActionAllow}
</button>
- <button class="dropdown-item" id="block"
- on-tap="onBlockTap_" hidden$="[[!showBlockAction_]]">
+ <button slot="item" class="dropdown-item" id="block"
+ on-click="onBlockTap_" hidden$="[[!showBlockAction_]]">
$i18n{siteSettingsActionBlock}
</button>
- <button class="dropdown-item" id="sessionOnly"
- on-tap="onSessionOnlyTap_"
+ <button slot="item" class="dropdown-item" id="sessionOnly"
+ on-click="onSessionOnlyTap_"
hidden$="[[!showSessionOnlyActionForSite_(actionMenuSite_)]]">
$i18n{siteSettingsActionSessionOnly}
</button>
- <button class="dropdown-item" id="edit" on-tap="onEditTap_">
+ <button slot="item" class="dropdown-item" id="edit"
+ on-click="onEditTap_">
$i18n{edit}
</button>
- <button class="dropdown-item" id="reset" on-tap="onResetTap_">
+ <button slot="item" class="dropdown-item" id="reset"
+ on-click="onResetTap_">
$i18n{siteSettingsActionReset}
</button>
</dialog>
@@ -64,7 +66,7 @@
<template is="dom-repeat" items="[[sites]]">
<div class="list-item">
<div class="settings-row"
- actionable$="[[enableSiteSettings_]]" on-tap="onOriginTap_">
+ actionable$="[[enableSiteSettings_]]" on-click="onOriginTap_">
<div class="favicon-image"
style$="[[computeSiteIcon(item.origin)]]">
</div>
@@ -76,7 +78,7 @@
id="siteDescription">[[computeSiteDescription_(item)]]</div>
</div>
<template is="dom-if" if="[[enableSiteSettings_]]">
- <div on-tap="onOriginTap_" actionable>
+ <div on-click="onOriginTap_" actionable>
<button class="subpage-arrow" is="paper-icon-button-light"
aria-label$="[[item.displayName]]"
aria-describedby="siteDescription"></button>
@@ -90,12 +92,12 @@
</cr-policy-pref-indicator>
</template>
<button is="paper-icon-button-light" id="resetSite"
- class="icon-delete-gray" on-tap="onResetButtonTap_"
+ class="icon-delete-gray" on-click="onResetButtonTap_"
hidden="[[shouldHideResetButton_(item, readOnlyList)]]"
- alt="$i18n{siteSettingsActionReset}">
+ aria-label="$i18n{siteSettingsActionReset}">
</button>
<button is="paper-icon-button-light" id="actionMenuButton"
- class="icon-more-vert" on-tap="onShowActionMenuTap_"
+ class="icon-more-vert" on-click="onShowActionMenuTap_"
hidden="[[shouldHideActionMenu_(item, readOnlyList)]]"
title="$i18n{moreActions}">
</button>
diff --git a/chromium/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js b/chromium/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
index 81a019ec3c4..7974cfdf2a1 100644
--- a/chromium/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
+++ b/chromium/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
@@ -137,6 +137,13 @@ cr.define('settings', function() {
setOriginPermissions(origin, contentTypes, blanketSetting) {}
/**
+ * Clears the flag that's set when the user has changed the Flash permission
+ * for this particular origin.
+ * @param {string} origin The origin to clear the Flash preference for.
+ */
+ clearFlashPref(origin) {}
+
+ /**
* Resets the category permission for a given origin (expressed as primary
* and secondary patterns). Only use this if intending to remove an
* exception - use setOriginPermissions() for origin-scoped settings.
@@ -307,6 +314,11 @@ cr.define('settings', function() {
}
/** @override */
+ clearFlashPref(origin) {
+ chrome.send('clearFlashPref', [origin]);
+ }
+
+ /** @override */
resetCategoryPermissionForPattern(
primaryPattern, secondaryPattern, contentType, incognito) {
chrome.send(
@@ -362,12 +374,12 @@ cr.define('settings', function() {
/** @override */
setProtocolDefault(protocol, url) {
- chrome.send('setDefault', [[protocol, url]]);
+ chrome.send('setDefault', [protocol, url]);
}
/** @override */
removeProtocolHandler(protocol, url) {
- chrome.send('removeHandler', [[protocol, url]]);
+ chrome.send('removeHandler', [protocol, url]);
}
/** @override */
diff --git a/chromium/chrome/browser/resources/settings/site_settings/usb_devices.html b/chromium/chrome/browser/resources/settings/site_settings/usb_devices.html
index 0d49492c635..9b4e5e046d2 100644
--- a/chromium/chrome/browser/resources/settings/site_settings/usb_devices.html
+++ b/chromium/chrome/browser/resources/settings/site_settings/usb_devices.html
@@ -33,7 +33,7 @@
style$="[[computeSiteIcon(item.origin)]]"></div>
<div class="middle">[[item.origin]]</div>
- <button is="paper-icon-button-light" on-tap="showMenu_"
+ <button is="paper-icon-button-light" on-click="showMenu_"
class="icon-more-vert" title="$i18n{moreActions}">
</button>
</div>
@@ -41,7 +41,8 @@
</template>
<dialog is="cr-action-menu">
- <button id="removeButton" class="dropdown-item" on-tap="onRemoveTap_">
+ <button id="removeButton" slot="item" class="dropdown-item"
+ on-click="onRemoveTap_">
$i18n{handlerRemove}
</button>
</dialog>
diff --git a/chromium/chrome/browser/resources/settings/site_settings/zoom_levels.html b/chromium/chrome/browser/resources/settings/site_settings/zoom_levels.html
index e651cd9ad88..facdc237e3e 100644
--- a/chromium/chrome/browser/resources/settings/site_settings/zoom_levels.html
+++ b/chromium/chrome/browser/resources/settings/site_settings/zoom_levels.html
@@ -36,7 +36,7 @@
<div class="zoom-label">[[item.zoom]]</div>
<div>
<button is="paper-icon-button-light" class="icon-clear"
- on-tap="removeZoomLevel_"
+ on-click="removeZoomLevel_"
title="$i18n{siteSettingsRemoveZoomLevel}"></button>
</div>
</div>
diff --git a/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.html b/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
index c523b2f8536..37210d136c7 100644
--- a/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
+++ b/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
@@ -18,7 +18,7 @@
</style>
<template is="dom-if" if="[[enableSiteSettings_]]">
<div class="settings-box first" category$="[[ALL_SITES]]"
- data-route="SITE_SETTINGS_ALL" on-tap="onTapNavigate_" actionable>
+ data-route="SITE_SETTINGS_ALL" on-click="onTapNavigate_" actionable>
<iron-icon icon="settings:list"></iron-icon>
<div class="middle">$i18n{siteSettingsCategoryAllSites}</div>
<button class="subpage-arrow" is="paper-icon-button-light"
@@ -29,7 +29,7 @@
</template>
<div id="cookies" class="settings-box two-line first"
category$="[[ContentSettingsTypes.COOKIES]]"
- data-route="SITE_SETTINGS_COOKIES" on-tap="onTapNavigate_" actionable>
+ data-route="SITE_SETTINGS_COOKIES" on-click="onTapNavigate_" actionable>
<iron-icon icon="settings:cookie"></iron-icon>
<div class="middle">
$i18n{siteSettingsCookies}
@@ -47,7 +47,8 @@
</div>
<div id="location" class="settings-box two-line"
category$="[[ContentSettingsTypes.GEOLOCATION]]"
- data-route="SITE_SETTINGS_LOCATION" on-tap="onTapNavigate_" actionable>
+ data-route="SITE_SETTINGS_LOCATION" on-click="onTapNavigate_"
+ actionable>
<iron-icon icon="settings:location-on"></iron-icon>
<div class="middle">
$i18n{siteSettingsLocation}
@@ -65,7 +66,7 @@
<div id="camera" class="settings-box two-line"
category$="[[ContentSettingsTypes.CAMERA]]"
data-route="SITE_SETTINGS_CAMERA"
- on-tap="onTapNavigate_" actionable>
+ on-click="onTapNavigate_" actionable>
<iron-icon icon="settings:videocam"></iron-icon>
<div class="middle">
$i18n{siteSettingsCamera}
@@ -82,7 +83,7 @@
</div>
<div id="microphone" class="settings-box two-line"
category$="[[ContentSettingsTypes.MIC]]"
- data-route="SITE_SETTINGS_MICROPHONE" on-tap="onTapNavigate_"
+ data-route="SITE_SETTINGS_MICROPHONE" on-click="onTapNavigate_"
actionable>
<iron-icon icon="settings:mic"></iron-icon>
<div class="middle">
@@ -98,9 +99,29 @@
aria-label="$i18n{siteSettingsMic}"
aria-describedby="micSecondary"></button>
</div>
+ <template is="dom-if" if="[[enableSensorsContentSetting_]]">
+ <div id="sensors" class="settings-box two-line"
+ category$="[[ContentSettingsTypes.SENSORS]]"
+ data-route="SITE_SETTINGS_SENSORS" on-click="onTapNavigate_"
+ actionable>
+ <iron-icon icon="settings:sensors"></iron-icon>
+ <div class="middle">
+ $i18n{siteSettingsSensors}
+ <div class="secondary" id="sensorsSecondary">
+ [[defaultSettingLabel_(
+ default_.sensors,
+ '$i18nPolymer{siteSettingsSensorsAllow}',
+ '$i18nPolymer{siteSettingsSensorsBlock}')]]
+ </div>
+ </div>
+ <button class="subpage-arrow" is="paper-icon-button-light"
+ aria-label="$i18n{siteSettingsSensors}"
+ aria-describedby="sensorsSecondary"></button>
+ </div>
+ </template>
<div id="notifications" class="settings-box two-line"
category$="[[ContentSettingsTypes.NOTIFICATIONS]]"
- data-route="SITE_SETTINGS_NOTIFICATIONS" on-tap="onTapNavigate_"
+ data-route="SITE_SETTINGS_NOTIFICATIONS" on-click="onTapNavigate_"
actionable>
<iron-icon icon="settings:notifications"></iron-icon>
<div class="middle">
@@ -118,7 +139,7 @@
</div>
<div id="javascript" class="settings-box two-line"
category$="[[ContentSettingsTypes.JAVASCRIPT]]"
- data-route="SITE_SETTINGS_JAVASCRIPT" on-tap="onTapNavigate_"
+ data-route="SITE_SETTINGS_JAVASCRIPT" on-click="onTapNavigate_"
actionable>
<iron-icon icon="settings:code"></iron-icon>
<div class="middle">
@@ -136,7 +157,7 @@
</div>
<div id="flash" class="settings-box two-line"
category$="[[ContentSettingsTypes.PLUGINS]]"
- data-route="SITE_SETTINGS_FLASH" on-tap="onTapNavigate_" actionable>
+ data-route="SITE_SETTINGS_FLASH" on-click="onTapNavigate_" actionable>
<iron-icon icon="cr:extension"></iron-icon>
<div class="middle">
$i18n{siteSettingsFlash}
@@ -153,7 +174,7 @@
</div>
<div id="images" class="settings-box two-line"
category$="[[ContentSettingsTypes.IMAGES]]"
- data-route="SITE_SETTINGS_IMAGES" on-tap="onTapNavigate_" actionable>
+ data-route="SITE_SETTINGS_IMAGES" on-click="onTapNavigate_" actionable>
<iron-icon icon="settings:photo"></iron-icon>
<div class="middle">
$i18n{siteSettingsImages}
@@ -170,7 +191,7 @@
</div>
<div id="popups" category$="[[ContentSettingsTypes.POPUPS]]"
class="settings-box two-line" data-route="SITE_SETTINGS_POPUPS"
- on-tap="onTapNavigate_" actionable>
+ on-click="onTapNavigate_" actionable>
<iron-icon icon="cr:open-in-new"></iron-icon>
<div class="middle">
$i18n{siteSettingsPopups}
@@ -188,7 +209,7 @@
<template is="dom-if" if="[[enableSafeBrowsingSubresourceFilter_]]">
<div id="ads" class="settings-box two-line"
category$="[[ContentSettingsTypes.ADS]]"
- data-route="SITE_SETTINGS_ADS" on-tap="onTapNavigate_"
+ data-route="SITE_SETTINGS_ADS" on-click="onTapNavigate_"
actionable>
<iron-icon icon="settings:ads"></iron-icon>
<div class="middle">
@@ -207,7 +228,7 @@
</template>
<div id="background-sync" class="settings-box two-line"
category$="[[ContentSettingsTypes.BACKGROUND_SYNC]]"
- data-route="SITE_SETTINGS_BACKGROUND_SYNC" on-tap="onTapNavigate_"
+ data-route="SITE_SETTINGS_BACKGROUND_SYNC" on-click="onTapNavigate_"
actionable>
<iron-icon icon="settings:sync"></iron-icon>
<div class="middle">
@@ -226,7 +247,7 @@
<template is="dom-if" if="[[enableSoundContentSetting_]]">
<div id="sound" class="settings-box two-line"
category$="[[ContentSettingsTypes.SOUND]]"
- data-route="SITE_SETTINGS_SOUND" on-tap="onTapNavigate_"
+ data-route="SITE_SETTINGS_SOUND" on-click="onTapNavigate_"
actionable>
<iron-icon icon="settings:volume-up"></iron-icon>
<div class="middle">
@@ -246,7 +267,7 @@
<div id="automatic-downloads" class="settings-box two-line"
category$="[[ContentSettingsTypes.AUTOMATIC_DOWNLOADS]]"
data-route="SITE_SETTINGS_AUTOMATIC_DOWNLOADS"
- on-tap="onTapNavigate_" actionable>
+ on-click="onTapNavigate_" actionable>
<iron-icon icon="cr:file-download"></iron-icon>
<div class="middle">
$i18n{siteSettingsAutomaticDownloads}
@@ -264,7 +285,7 @@
<div id="unsandboxed-plugins" class="settings-box two-line"
category$="[[ContentSettingsTypes.UNSANDBOXED_PLUGINS]]"
data-route="SITE_SETTINGS_UNSANDBOXED_PLUGINS"
- on-tap="onTapNavigate_" actionable>
+ on-click="onTapNavigate_" actionable>
<iron-icon icon="cr:extension"></iron-icon>
<div class="middle">
$i18n{siteSettingsUnsandboxedPlugins}
@@ -283,7 +304,7 @@
<div id="protocol-handlers" class="settings-box two-line"
category$="[[ContentSettingsTypes.PROTOCOL_HANDLERS]]"
data-route="SITE_SETTINGS_HANDLERS"
- on-tap="onTapNavigate_" actionable>
+ on-click="onTapNavigate_" actionable>
<iron-icon icon="settings:protocol-handler"></iron-icon>
<div class="middle">
$i18n{siteSettingsHandlers}
@@ -302,7 +323,7 @@
<div id="midi-devices" class="settings-box two-line"
category$="[[ContentSettingsTypes.MIDI_DEVICES]]"
data-route="SITE_SETTINGS_MIDI_DEVICES"
- on-tap="onTapNavigate_" actionable>
+ on-click="onTapNavigate_" actionable>
<iron-icon icon="settings:midi"></iron-icon>
<div class="middle">
$i18n{siteSettingsMidiDevices}
@@ -317,29 +338,9 @@
aria-label="$i18n{siteSettingsMidiDevices}"
aria-describedby="midiDevicesSecondary"></button>
</div>
- <template is="dom-if" if="[[enableClipboardContentSetting_]]">
- <div id="clipboard" class="settings-box two-line"
- category$="[[ContentSettingsTypes.CLIPBOARD]]"
- data-route="SITE_SETTINGS_CLIPBOARD" on-tap="onTapNavigate_"
- actionable>
- <iron-icon icon="settings:clipboard"></iron-icon>
- <div class="middle">
- $i18n{siteSettingsClipboard}
- <div class="secondary" id="clipboardSecondary">
- [[defaultSettingLabel_(
- default_.clipboard,
- '$i18nPolymer{siteSettingsAskBeforeAccessing}',
- '$i18nPolymer{siteSettingsBlocked}')]]
- </div>
- </div>
- <button class="subpage-arrow" is="paper-icon-button-light"
- aria-label="$i18n{siteSettingsClipboard}"
- aria-describedby="clipboardSecondary"></button>
- </div>
- </template>
<div id="zoom-levels" class="settings-box"
category$="[[ContentSettingsTypes.ZOOM_LEVELS]]"
- data-route="SITE_SETTINGS_ZOOM_LEVELS" on-tap="onTapNavigate_"
+ data-route="SITE_SETTINGS_ZOOM_LEVELS" on-click="onTapNavigate_"
actionable>
<iron-icon icon="settings:zoom-in"></iron-icon>
<div class="middle">$i18n{siteSettingsZoomLevels}</div>
@@ -348,7 +349,7 @@
</div>
<div id="usb-devices" class="settings-box"
category$="[[ContentSettingsTypes.USB_DEVICES]]"
- data-route="SITE_SETTINGS_USB_DEVICES" on-tap="onTapNavigate_"
+ data-route="SITE_SETTINGS_USB_DEVICES" on-click="onTapNavigate_"
actionable>
<iron-icon icon="settings:usb"></iron-icon>
<div class="middle">$i18n{siteSettingsUsbDevices}</div>
@@ -356,7 +357,7 @@
aria-label="$i18n{siteSettingsUsbDevices}"></button>
</div>
<div id="pdf-documents" class="settings-box"
- data-route="SITE_SETTINGS_PDF_DOCUMENTS" on-tap="onTapNavigate_"
+ data-route="SITE_SETTINGS_PDF_DOCUMENTS" on-click="onTapNavigate_"
actionable>
<iron-icon icon="settings:pdf"></iron-icon>
<div class="middle">$i18n{siteSettingsPdfDocuments}</div>
@@ -364,13 +365,33 @@
aria-label="$i18n{siteSettingsPdfDocuments}"></button>
</div>
<div id="protected-content" class="settings-box"
- data-route="SITE_SETTINGS_PROTECTED_CONTENT" on-tap="onTapNavigate_"
+ data-route="SITE_SETTINGS_PROTECTED_CONTENT" on-click="onTapNavigate_"
actionable>
<iron-icon icon="settings:security"></iron-icon>
<div class="middle">$i18n{siteSettingsProtectedContent}</div>
<button class="subpage-arrow" is="paper-icon-button-light"
aria-label="$i18n{siteSettingsProtectedContent}"></button>
</div>
+ <template is="dom-if" if="[[enableClipboardContentSetting_]]">
+ <div id="clipboard" class="settings-box two-line"
+ category$="[[ContentSettingsTypes.CLIPBOARD]]"
+ data-route="SITE_SETTINGS_CLIPBOARD" on-click="onTapNavigate_"
+ actionable>
+ <iron-icon icon="settings:clipboard"></iron-icon>
+ <div class="middle">
+ $i18n{siteSettingsClipboard}
+ <div class="secondary" id="clipboardSecondary">
+ [[defaultSettingLabel_(
+ default_.clipboard,
+ '$i18nPolymer{siteSettingsAskBeforeAccessing}',
+ '$i18nPolymer{siteSettingsBlocked}')]]
+ </div>
+ </div>
+ <button class="subpage-arrow" is="paper-icon-button-light"
+ aria-label="$i18n{siteSettingsClipboard}"
+ aria-describedby="clipboardSecondary"></button>
+ </div>
+ </template>
</template>
<script src="site_settings_page.js"></script>
</dom-module>
diff --git a/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.js b/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
index 0b1ce9dc1de..5a3b98d12af 100644
--- a/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
+++ b/chromium/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
@@ -67,6 +67,15 @@ Polymer({
}
},
+ /** @private */
+ enableSensorsContentSetting_: {
+ type: Boolean,
+ readOnly: true,
+ value: function() {
+ return loadTimeData.getBoolean('enableSensorsContentSetting');
+ }
+ },
+
/** @type {!Map<string, string>} */
focusConfig: {
type: Object,
@@ -105,6 +114,7 @@ Polymer({
[R.SITE_SETTINGS_PDF_DOCUMENTS, 'pdf-documents'],
[R.SITE_SETTINGS_PROTECTED_CONTENT, 'protected-content'],
[R.SITE_SETTINGS_CLIPBOARD, 'clipboard'],
+ [R.SITE_SETTINGS_SENSORS, 'sensors'],
].forEach(pair => {
const route = pair[0];
const id = pair[1];
diff --git a/chromium/chrome/browser/resources/settings/system_page/system_page.html b/chromium/chrome/browser/resources/settings/system_page/system_page.html
index 77bc47816f3..fb4ed5031ee 100644
--- a/chromium/chrome/browser/resources/settings/system_page/system_page.html
+++ b/chromium/chrome/browser/resources/settings/system_page/system_page.html
@@ -29,7 +29,7 @@
</paper-button>
</template>
</settings-toggle-button>
- <div id="proxy" class="settings-box" on-tap="onProxyTap_"
+ <div id="proxy" class="settings-box" on-click="onProxyTap_"
actionable$="[[!isProxyEnforcedByPolicy_]]">
<div class="start">$i18n{proxySettingsLabel}</div>
<button is="paper-icon-button-light" class="icon-external"
diff --git a/chromium/chrome/browser/resources/signin/dice_sync_confirmation/images/ic_google.png b/chromium/chrome/browser/resources/signin/dice_sync_confirmation/images/ic_google.png
index 3c38ca006b5..f7602a57b40 100644
--- a/chromium/chrome/browser/resources/signin/dice_sync_confirmation/images/ic_google.png
+++ b/chromium/chrome/browser/resources/signin/dice_sync_confirmation/images/ic_google.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/signin/dice_sync_confirmation/images/ic_google_2x.png b/chromium/chrome/browser/resources/signin/dice_sync_confirmation/images/ic_google_2x.png
index 3634aa48a93..76d098584c9 100644
--- a/chromium/chrome/browser/resources/signin/dice_sync_confirmation/images/ic_google_2x.png
+++ b/chromium/chrome/browser/resources/signin/dice_sync_confirmation/images/ic_google_2x.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.html b/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.html
index 41dac3c5e3b..644c00a0c8f 100644
--- a/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.html
+++ b/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.html
@@ -81,9 +81,9 @@
}
#personalize-logo {
- fill: var(--google-blue-700);
/* Need the following rules to adjust for white spacing in the svg. */
-webkit-margin-end: 14px;
+ fill: var(--google-blue-700);
height: 18px;
width: 18px;
}
@@ -107,37 +107,52 @@
url(./images/ic_google_2x.png) 2x);
}
</style>
+
+ <!--
+ Use the 'consent-description' attribute to annotate all the UI elements
+ that are part of the text the user reads before consenting to the Sync
+ data collection . Similarly, use 'consent-confirmation' on UI elements on
+ which user clicks to indicate consent.
+ -->
+
<div id="illustration-container"></div>
- <h1 id="heading">$i18n{syncConfirmationTitle}</h1>
+ <h1 id="heading" consent-description>$i18n{syncConfirmationTitle}</h1>
<div class="message-container">
<!-- Container needed to contain the icon in a green circle. -->
<div id="sync-logo-container" class="logo">
<iron-icon icon="notification:sync" class="logo">
</iron-icon>
</div>
- <div>$i18n{syncConfirmationChromeSyncBody}</div>
+ <div consent-description>$i18n{syncConfirmationChromeSyncBody}</div>
</div>
<div class="message-container">
<iron-icon icon="image:assistant" id="personalize-logo" class="logo">
</iron-icon>
- <div>$i18n{syncConfirmationPersonalizeServicesBody}</div>
+ <div consent-description>
+ $i18n{syncConfirmationPersonalizeServicesBody}
+ </div>
</div>
<div class="message-container">
<div id="googleg-logo" class="logo"></div>
- <div>$i18n{syncConfirmationGoogleServicesBody}</div>
+ <div consent-description>$i18n{syncConfirmationGoogleServicesBody}</div>
</div>
<div class="footer">
<div class="message-container">
<iron-icon icon="icons:settings" class="logo"></iron-icon>
- <div>$i18nRaw{syncConfirmationSyncSettingsLinkBody}</div>
+ <div consent-description consent-confirmation>
+ $i18nRaw{syncConfirmationSyncSettingsLinkBody}
+ </div>
</div>
<div class="message-container">
<div class="logo"><!-- Spacer to line up with other texts --></div>
- <div>$i18n{syncConfirmationSyncSettingsDescription}</div>
+ <div consent-description>
+ $i18n{syncConfirmationSyncSettingsDescription}
+ </div>
</div>
</div>
<div class="action-container">
- <paper-button class="primary-action" id="confirmButton" on-tap="onConfirm_">
+ <paper-button class="primary-action" id="confirmButton"
+ on-tap="onConfirm_" consent-confirmation>
$i18n{syncConfirmationConfirmLabel}
</paper-button>
<paper-button class="secondary-action" id="undoButton" on-tap="onUndo_">
diff --git a/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.js b/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.js
index bb45993114c..fe19b8f3dd4 100644
--- a/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.js
+++ b/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.js
@@ -33,8 +33,9 @@ Polymer({
},
/** @private */
- onConfirm_: function() {
- this.syncConfirmationBrowserProxy_.confirm();
+ onConfirm_: function(e) {
+ this.syncConfirmationBrowserProxy_.confirm(
+ this.getConsentDescription_(), this.getConsentConfirmation_(e.path));
},
/** @private */
@@ -43,15 +44,41 @@ Polymer({
},
/** @private */
- onGoToSettings_: function() {
- this.syncConfirmationBrowserProxy_.goToSettings();
+ onGoToSettings_: function(e) {
+ this.syncConfirmationBrowserProxy_.goToSettings(
+ this.getConsentDescription_(), this.getConsentConfirmation_(e.path));
},
/** @private */
onKeyDown_: function(e) {
if (e.key == 'Enter' && !/^(A|PAPER-BUTTON)$/.test(e.path[0].tagName)) {
- this.onConfirm_();
+ this.onConfirm_(e);
e.preventDefault();
}
},
+
+ /**
+ * @param {!Array<!HTMLElement>} path Path of the click event. Must contain
+ * a consent confirmation element.
+ * @return {string} The text of the consent confirmation element.
+ * @private
+ */
+ getConsentConfirmation_: function(path) {
+ for (var element of path) {
+ if (element.hasAttribute('consent-confirmation'))
+ return element.innerHTML.trim();
+ }
+ assertNotReached('No consent confirmation element found.');
+ return '';
+ },
+
+ /** @return {!Array<string>} Text of the consent description elements. */
+ getConsentDescription_: function() {
+ var consentDescription =
+ Array.from(this.shadowRoot.querySelectorAll('[consent-description]'))
+ .filter(element => element.clientWidth * element.clientHeight > 0)
+ .map(element => element.innerHTML.trim());
+ assert(consentDescription);
+ return consentDescription;
+ }
});
diff --git a/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_browser_proxy.js b/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_browser_proxy.js
index 304c3024888..f6d3b1096be 100644
--- a/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_browser_proxy.js
+++ b/chromium/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_browser_proxy.js
@@ -11,9 +11,27 @@ cr.define('sync.confirmation', function() {
/** @interface */
class SyncConfirmationBrowserProxy {
- confirm() {}
+ /**
+ * Called when the user confirms the Sync Confirmation dialog.
+ * @param {!Array<string>} description Strings that the user was presented
+ * with in the UI.
+ * @param {string} confirmation Text of the element that the user
+ * clicked on.
+ */
+ confirm(description, confirmation) {}
+
+ /** Called when the user undoes the Sync confirmation. */
undo() {}
- goToSettings() {}
+
+ /**
+ * Called when the user clicks on the Settings link in
+ * the Sync Confirmation dialog.
+ * @param {!Array<string>} description Strings that the user was presented
+ * with in the UI.
+ * @param {string} confirmation Text of the element that the user
+ * clicked on.
+ */
+ goToSettings(description, confirmation) {}
/** @param {!Array<number>} height */
initializedWithSize(height) {}
@@ -22,8 +40,8 @@ cr.define('sync.confirmation', function() {
/** @implements {sync.confirmation.SyncConfirmationBrowserProxy} */
class SyncConfirmationBrowserProxyImpl {
/** @override */
- confirm() {
- chrome.send('confirm');
+ confirm(description, confirmation) {
+ chrome.send('confirm', [description, confirmation]);
}
/** @override */
@@ -32,8 +50,8 @@ cr.define('sync.confirmation', function() {
}
/** @override */
- goToSettings() {
- chrome.send('goToSettings');
+ goToSettings(description, confirmation) {
+ chrome.send('goToSettings', [description, confirmation]);
}
/** @override */
diff --git a/chromium/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html b/chromium/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html
index 99a1e1122d7..74acb08a2dd 100644
--- a/chromium/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html
+++ b/chromium/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html
@@ -18,8 +18,16 @@
</style>
</head>
<body>
+ <!--
+ Use the 'consent-description' attribute to annotate all the UI elements
+ that are part of the text the user reads before consenting to the Sync
+ data collection . Similarly, use 'consent-confirmation' on UI elements on
+ which user clicks to indicate consent.
+ -->
<div class="container">
- <div class="top-title-bar">$i18n{syncConfirmationTitle}</div>
+ <div class="top-title-bar" consent-description>
+ $i18n{syncConfirmationTitle}
+ </div>
<div class="details" id="syncConfirmationDetails">
<div id="picture-container">
<div id="illustration">
@@ -55,8 +63,12 @@
-->
<div id="chrome-logo" class="logo"></div>
<div>
- <div class="title">$i18n{syncConfirmationChromeSyncTitle}</div>
- <div class="body text">$i18n{syncConfirmationChromeSyncBody}</div>
+ <div class="title" consent-description>
+ $i18n{syncConfirmationChromeSyncTitle}
+ </div>
+ <div class="body text" consent-description>
+ $i18n{syncConfirmationChromeSyncBody}
+ </div>
</div>
</div>
<div class="message-container">
@@ -66,23 +78,28 @@
-->
<div id="googleg-logo" class="logo"></div>
<div>
- <div class="title">
+ <div class="title" consent-description>
$i18n{syncConfirmationPersonalizeServicesTitle}
</div>
- <div class="body text">
+ <div class="body text" consent-description>
$i18n{syncConfirmationPersonalizeServicesBody}
</div>
</div>
</div>
<div class="message-container">
- <div class="body">$i18nRaw{syncConfirmationSyncSettingsLinkBody}</div>
+ <div class="body" consent-description consent-confirmation>
+ $i18nRaw{syncConfirmationSyncSettingsLinkBody}
+ </div>
</div>
</div>
<div class="details" id="syncDisabledDetails">
- <div class="body text">$i18n{syncDisabledConfirmationDetails}</div>
+ <div class="body text" consent-description>
+ $i18n{syncDisabledConfirmationDetails}
+ </div>
</div>
<div class="action-container">
- <paper-button class="primary-action" id="confirmButton">
+ <paper-button class="primary-action" id="confirmButton"
+ consent-confirmation>
$i18n{syncConfirmationConfirmLabel}
</paper-button>
<paper-button class="secondary-action" id="undoButton">
diff --git a/chromium/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.js b/chromium/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.js
index 17eb3bfa71c..72d29205331 100644
--- a/chromium/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.js
+++ b/chromium/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.js
@@ -5,8 +5,35 @@
cr.define('sync.confirmation', function() {
'use strict';
+ /**
+ * @param {!Array<!HTMLElement>} path Path of the click event. Must contain
+ * a consent confirmation element.
+ * @return {string} The text of the consent confirmation element.
+ * @private
+ */
+ function getConsentConfirmation(path) {
+ var consentConfirmation;
+ for (var element of path) {
+ if (element.hasAttribute('consent-confirmation'))
+ return element.innerHTML.trim();
+ }
+ assertNotReached('No consent confirmation element found.');
+ return '';
+ }
+
+ /** @return {!Array<string>} Text of the consent description elements. */
+ function getConsentDescription() {
+ var consentDescription =
+ Array.from(document.querySelectorAll('[consent-description]'))
+ .filter(element => element.clientWidth * element.clientHeight > 0)
+ .map(element => element.innerHTML.trim());
+ assert(consentDescription);
+ return consentDescription;
+ }
+
function onConfirm(e) {
- chrome.send('confirm');
+ chrome.send(
+ 'confirm', [getConsentDescription(), getConsentConfirmation(e.path)]);
}
function onUndo(e) {
@@ -14,7 +41,9 @@ cr.define('sync.confirmation', function() {
}
function onGoToSettings(e) {
- chrome.send('goToSettings');
+ chrome.send(
+ 'goToSettings',
+ [getConsentDescription(), getConsentConfirmation(e.path)]);
}
function initialize() {
diff --git a/chromium/chrome/browser/resources/snippets_internals.html b/chromium/chrome/browser/resources/snippets_internals.html
index 27ee59e3a95..6b3cb663e44 100644
--- a/chromium/chrome/browser/resources/snippets_internals.html
+++ b/chromium/chrome/browser/resources/snippets_internals.html
@@ -90,11 +90,10 @@ found in the LICENSE file.
</div>
<div id="snippets">
- <h2>NTPSnippetsService</h2>
+ <h2>ContentSuggestionsService</h2>
<div class="forms">
<div>
- <button id="submit-download" type="button">Add snippets</button>
- <span id="remote-status" class="detail"></span>
+ <button id="submit-download" type="button">Reload suggestions</button>
</div>
<div>
<button id="debug-log-dump" type="button">Dump the debug log</button>
@@ -107,27 +106,36 @@ found in the LICENSE file.
</div>
</div>
- <div id="last-json" class="hidden">
- <h2>Last JSON from Server</h2>
- <a id="last-json-button">Show the last JSON &gt;&gt;</a>
- <div id="last-json-container" class="hidden">
- <div id="last-json-text"></div>
- <button id="last-json-dump" type="button">Dump the last JSON</button>
- </div>
- </div>
-
<div id="remote-content-suggestions">
<h2>Remote content suggestions</h2>
+ <table class="section-details">
+ <tr>
+ <td class="name">Last Fetch Status
+ <td id="remote-status" class="value">
+ <tr>
+ <td class="name">Last Fetch Type
+ <td id="remote-authenticated" class="value">
+ <tr>
+ <td class="name">Last Background Fetch Time:
+ <td id="last-background-fetch-time-label" class="value">
+ </table>
<div>
- <span>Last Background Fetch Time: </span>
- <span id="last-background-fetch-time-label"></span>
+ <button id="background-fetch-button" type="button">
+ Fetch remote suggestions in the background in 2 seconds
+ </button>
+ </div>
+ <div>
+ <button id="push-dummy-suggestion-10-seconds-button" type="button">
+ Push dummy suggestion in 10 seconds
+ </button>
+ </div>
+ <div>
+ <button id="last-json-button" type="button">Show the last JSON</button>
+ </div>
+ <div id="last-json-container" class="hidden">
+ <div id="last-json-text"></div>
+ <button id="last-json-dump" type="button">Dump the last JSON</button>
</div>
- <button id="background-fetch-button" type="button">
- Fetch remote suggestions in the background in 2 seconds
- </button>
- <button id="push-dummy-suggestion-10-seconds-button" type="button">
- Push dummy suggestion in 10 seconds
- </button>
</div>
<div id="notifications">
diff --git a/chromium/chrome/browser/resources/vr/assets/PRESUBMIT.py b/chromium/chrome/browser/resources/vr/assets/PRESUBMIT.py
index 5d740312570..c290f6d7b3d 100644
--- a/chromium/chrome/browser/resources/vr/assets/PRESUBMIT.py
+++ b/chromium/chrome/browser/resources/vr/assets/PRESUBMIT.py
@@ -12,10 +12,11 @@ def IsNewer(old_version, new_version):
old_version.minor < new_version.minor)))
-def CheckVersion(input_api, output_api):
+def CheckVersionAndAssetParity(input_api, output_api):
"""Checks that
- the version was upraded if assets files were changed,
- - the version was not downgraded.
+ - the version was not downgraded,
+ - both the google_chrome and the chromium assets have the same files.
"""
sys.path.append(input_api.PresubmitLocalPath())
import parse_version
@@ -24,9 +25,21 @@ def CheckVersion(input_api, output_api):
new_version = None
changed_assets = False
changed_version = False
+ changed_asset_files = {'google_chrome': [], 'chromium': []}
for file in input_api.AffectedFiles():
basename = input_api.os_path.basename(file.LocalPath())
extension = input_api.os_path.splitext(basename)[1][1:].strip().lower()
+ basename_without_extension = input_api.os_path.splitext(basename)[
+ 0].strip().lower()
+ if extension == 'sha1':
+ basename_without_extension = input_api.os_path.splitext(
+ basename_without_extension)[0]
+ dirname = input_api.os_path.basename(
+ input_api.os_path.dirname(file.LocalPath()))
+ action = file.Action()
+ if (dirname in changed_asset_files and extension in {'sha1', 'png'} and
+ action in {'A', 'D'}):
+ changed_asset_files[dirname].append((action, basename_without_extension))
if (extension == 'sha1' or basename == 'vr_assets_component_files.json'):
changed_assets = True
if (basename == 'VERSION'):
@@ -38,6 +51,15 @@ def CheckVersion(input_api, output_api):
input_api.os_path.dirname(input_api.AffectedFiles()[0].LocalPath()),
'VERSION')
+ if changed_asset_files['google_chrome'] != changed_asset_files['chromium']:
+ return [
+ output_api.PresubmitError(
+ 'Must have same asset files for %s in \'%s\'.' %
+ (changed_asset_files.keys(),
+ input_api.os_path.dirname(
+ input_api.AffectedFiles()[0].LocalPath())))
+ ]
+
if changed_version and (not old_version or not new_version):
return [
output_api.PresubmitError(
@@ -61,8 +83,8 @@ def CheckVersion(input_api, output_api):
def CheckChangeOnUpload(input_api, output_api):
- return CheckVersion(input_api, output_api)
+ return CheckVersionAndAssetParity(input_api, output_api)
def CheckChangeOnCommit(input_api, output_api):
- return CheckVersion(input_api, output_api)
+ return CheckVersionAndAssetParity(input_api, output_api)
diff --git a/chromium/chrome/browser/resources/vr/assets/VERSION b/chromium/chrome/browser/resources/vr/assets/VERSION
index 1dea3031bc3..75fb8d39112 100644
--- a/chromium/chrome/browser/resources/vr/assets/VERSION
+++ b/chromium/chrome/browser/resources/vr/assets/VERSION
@@ -1,2 +1,2 @@
MAJOR=1
-MINOR=2 \ No newline at end of file
+MINOR=3 \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/vr/assets/chromium/background.png b/chromium/chrome/browser/resources/vr/assets/chromium/background.png
new file mode 100644
index 00000000000..0fb3bfc35fc
--- /dev/null
+++ b/chromium/chrome/browser/resources/vr/assets/chromium/background.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/vr/assets/chromium/fullscreen_gradient.png b/chromium/chrome/browser/resources/vr/assets/chromium/fullscreen_gradient.png
new file mode 100644
index 00000000000..04c5d88e646
--- /dev/null
+++ b/chromium/chrome/browser/resources/vr/assets/chromium/fullscreen_gradient.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/vr/assets/chromium/incognito_gradient.png b/chromium/chrome/browser/resources/vr/assets/chromium/incognito_gradient.png
new file mode 100644
index 00000000000..4fb17499405
--- /dev/null
+++ b/chromium/chrome/browser/resources/vr/assets/chromium/incognito_gradient.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/vr/assets/chromium/normal_gradient.png b/chromium/chrome/browser/resources/vr/assets/chromium/normal_gradient.png
new file mode 100644
index 00000000000..0fb3bfc35fc
--- /dev/null
+++ b/chromium/chrome/browser/resources/vr/assets/chromium/normal_gradient.png
Binary files differ
diff --git a/chromium/chrome/browser/resources/vr/assets/fullscreen_gradient.png.sha1 b/chromium/chrome/browser/resources/vr/assets/fullscreen_gradient.png.sha1
deleted file mode 100644
index 8286e261102..00000000000
--- a/chromium/chrome/browser/resources/vr/assets/fullscreen_gradient.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-4945728b62a01aa712b5b0bacbf10d5a1a95bd80 \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/vr/assets/background.png.sha1 b/chromium/chrome/browser/resources/vr/assets/google_chrome/background.png.sha1
index d3427cb5c7b..d3427cb5c7b 100644
--- a/chromium/chrome/browser/resources/vr/assets/background.png.sha1
+++ b/chromium/chrome/browser/resources/vr/assets/google_chrome/background.png.sha1
diff --git a/chromium/chrome/browser/resources/vr/assets/google_chrome/fullscreen_gradient.png.sha1 b/chromium/chrome/browser/resources/vr/assets/google_chrome/fullscreen_gradient.png.sha1
new file mode 100644
index 00000000000..201ca21adc8
--- /dev/null
+++ b/chromium/chrome/browser/resources/vr/assets/google_chrome/fullscreen_gradient.png.sha1
@@ -0,0 +1 @@
+be61cbcdc981d5592455d3b7a14b97e14d3acac4 \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/vr/assets/google_chrome/incognito_gradient.png.sha1 b/chromium/chrome/browser/resources/vr/assets/google_chrome/incognito_gradient.png.sha1
new file mode 100644
index 00000000000..1282cf18759
--- /dev/null
+++ b/chromium/chrome/browser/resources/vr/assets/google_chrome/incognito_gradient.png.sha1
@@ -0,0 +1 @@
+6b0e506b19b79ec1be4963a9bd92ab8b92ccca1c \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/vr/assets/google_chrome/normal_gradient.png.sha1 b/chromium/chrome/browser/resources/vr/assets/google_chrome/normal_gradient.png.sha1
new file mode 100644
index 00000000000..30a79ad3c93
--- /dev/null
+++ b/chromium/chrome/browser/resources/vr/assets/google_chrome/normal_gradient.png.sha1
@@ -0,0 +1 @@
+bbe1145ae7778d7d50b02c54fd1bf4b62bbc04db \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/vr/assets/incognito_gradient.png.sha1 b/chromium/chrome/browser/resources/vr/assets/incognito_gradient.png.sha1
deleted file mode 100644
index 88f59906b07..00000000000
--- a/chromium/chrome/browser/resources/vr/assets/incognito_gradient.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a80f18117d1e8820404d50cdf49ba6bf6c3c69f0 \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/vr/assets/normal_gradient.png.sha1 b/chromium/chrome/browser/resources/vr/assets/normal_gradient.png.sha1
deleted file mode 100644
index 425681fc101..00000000000
--- a/chromium/chrome/browser/resources/vr/assets/normal_gradient.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-9888994181e2fa6cf6b510c02f21e183ad8efc46 \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/vr/assets/push_assets_component.py b/chromium/chrome/browser/resources/vr/assets/push_assets_component.py
index b259a6da904..b47f275ad0d 100755
--- a/chromium/chrome/browser/resources/vr/assets/push_assets_component.py
+++ b/chromium/chrome/browser/resources/vr/assets/push_assets_component.py
@@ -42,8 +42,8 @@ def main():
assets_dir = os.path.dirname(os.path.abspath(__file__))
files = []
- with open(
- os.path.join(assets_dir, 'vr_assets_component_files.json')) as json_file:
+ with open(os.path.join(assets_dir,
+ 'vr_assets_component_files.json')) as json_file:
files = json.load(json_file)
version = None
@@ -61,16 +61,20 @@ def main():
zip_path = os.path.join(zip_dir, 'vr-assets.zip')
os.makedirs(zip_dir)
+ zip_files = []
with zipfile.ZipFile(zip_path, 'w') as zip:
for file in files:
file_path = os.path.join(assets_dir, file)
zip.write(file_path, os.path.basename(file_path), zipfile.ZIP_DEFLATED)
+ for info in zip.infolist():
+ zip_files.append(info.filename)
# Upload component.
command = ['gsutil', 'cp', '-nR', '.', DEST_BUCKET]
PrintInfo('Going to run the following command', [' '.join(command)])
PrintInfo('In directory', [temp_dir])
PrintInfo('Which pushes the following file', [zip_path])
+ PrintInfo('Which contains the files', zip_files)
if raw_input('\nAre you sure (y/N) ').lower() != 'y':
print 'aborting'
diff --git a/chromium/chrome/browser/resources/vr/assets/vr_assets_component_files.json b/chromium/chrome/browser/resources/vr/assets/vr_assets_component_files.json
index 7601c295253..f58922a30a3 100644
--- a/chromium/chrome/browser/resources/vr/assets/vr_assets_component_files.json
+++ b/chromium/chrome/browser/resources/vr/assets/vr_assets_component_files.json
@@ -1,6 +1,6 @@
[
- "background.png",
- "fullscreen_gradient.png",
- "incognito_gradient.png",
- "normal_gradient.png"
+ "google_chrome/background.png",
+ "google_chrome/fullscreen_gradient.png",
+ "google_chrome/incognito_gradient.png",
+ "google_chrome/normal_gradient.png"
] \ No newline at end of file
diff --git a/chromium/chrome/browser/resources/vr_shell/OWNERS b/chromium/chrome/browser/resources/vr_shell/OWNERS
deleted file mode 100644
index 0fa23cfe0db..00000000000
--- a/chromium/chrome/browser/resources/vr_shell/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-file://chrome/browser/android/vr_shell/OWNERS
-
-# COMPONENT: UI>Browser>VR
diff --git a/chromium/chrome/browser/resources/vr_shell/ddcontroller.glb b/chromium/chrome/browser/resources/vr_shell/ddcontroller.glb
deleted file mode 100644
index fa28d6b1962..00000000000
--- a/chromium/chrome/browser/resources/vr_shell/ddcontroller.glb
+++ /dev/null
Binary files differ
diff --git a/chromium/chrome/browser/resources/vr_shell/tex/ddcontroller_app.png b/chromium/chrome/browser/resources/vr_shell/tex/ddcontroller_app.png
deleted file mode 100644
index 77594782ad3..00000000000
--- a/chromium/chrome/browser/resources/vr_shell/tex/ddcontroller_app.png
+++ /dev/null
Binary files differ
diff --git a/chromium/chrome/browser/resources/vr_shell/tex/ddcontroller_idle.png b/chromium/chrome/browser/resources/vr_shell/tex/ddcontroller_idle.png
deleted file mode 100644
index 5928f21896c..00000000000
--- a/chromium/chrome/browser/resources/vr_shell/tex/ddcontroller_idle.png
+++ /dev/null
Binary files differ
diff --git a/chromium/chrome/browser/resources/vr_shell/tex/ddcontroller_system.png b/chromium/chrome/browser/resources/vr_shell/tex/ddcontroller_system.png
deleted file mode 100644
index d4bae158510..00000000000
--- a/chromium/chrome/browser/resources/vr_shell/tex/ddcontroller_system.png
+++ /dev/null
Binary files differ
diff --git a/chromium/chrome/browser/resources/vr_shell/tex/ddcontroller_touchpad.png b/chromium/chrome/browser/resources/vr_shell/tex/ddcontroller_touchpad.png
deleted file mode 100644
index 7be802ee886..00000000000
--- a/chromium/chrome/browser/resources/vr_shell/tex/ddcontroller_touchpad.png
+++ /dev/null
Binary files differ
diff --git a/chromium/chrome/browser/resources/vr_shell_resources.grd b/chromium/chrome/browser/resources/vr_shell_resources.grd
deleted file mode 100644
index 06de759f2f6..00000000000
--- a/chromium/chrome/browser/resources/vr_shell_resources.grd
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
- <outputs>
- <output filename="grit/vr_shell_resources.h" type="rc_header">
- <emit emit_type='prepend'></emit>
- </output>
- <output filename="vr_shell_resources.pak" type="data_package" />
- </outputs>
- <release seq="1">
- <includes>
- <!-- TODO(vollick): add conditionals for test-specific generic assets (see crbug.com/743687) -->
- <include name="IDR_VR_SHELL_DDCONTROLLER_MODEL" file="vr_shell\ddcontroller.glb" type="BINDATA" />
- <include name="IDR_VR_SHELL_DDCONTROLLER_IDLE_TEXTURE" file="vr_shell\tex\ddcontroller_idle.png" type="BINDATA" />
- <include name="IDR_VR_SHELL_DDCONTROLLER_APP_PATCH" file="vr_shell\tex\ddcontroller_app.png" type="BINDATA" />
- <include name="IDR_VR_SHELL_DDCONTROLLER_TOUCHPAD_PATCH" file="vr_shell\tex\ddcontroller_touchpad.png" type="BINDATA" />
- <include name="IDR_VR_SHELL_DDCONTROLLER_SYSTEM_PATCH" file="vr_shell\tex\ddcontroller_system.png" type="BINDATA" />
- </includes>
- </release>
-</grit>
diff --git a/chromium/chrome/browser/safe_browsing/BUILD.gn b/chromium/chrome/browser/safe_browsing/BUILD.gn
index d870ac227ab..98c6062015f 100644
--- a/chromium/chrome/browser/safe_browsing/BUILD.gn
+++ b/chromium/chrome/browser/safe_browsing/BUILD.gn
@@ -105,6 +105,8 @@ static_library("safe_browsing") {
"test_safe_browsing_blocking_page_quiet.h",
"test_safe_browsing_service.cc",
"test_safe_browsing_service.h",
+ "trigger_creator.cc",
+ "trigger_creator.h",
"ui_manager.cc",
"ui_manager.h",
]
@@ -115,9 +117,12 @@ static_library("safe_browsing") {
"//components/safe_browsing:safe_browsing",
"//components/safe_browsing/browser:browser",
"//components/safe_browsing/common:common",
+ "//components/safe_browsing/common:safe_browsing_prefs",
"//components/safe_browsing/db:metadata_proto",
"//components/safe_browsing/db:whitelist_checker_client",
"//components/safe_browsing/password_protection",
+ "//components/safe_browsing/triggers:ad_sampler_trigger",
+ "//components/safe_browsing/triggers:trigger_throttler",
"//components/safe_browsing/triggers:triggers",
]
if (safe_browsing_mode == 1) {
@@ -227,18 +232,13 @@ static_library("safe_browsing") {
"services_delegate_impl.h",
"signature_evaluator_mac.h",
"signature_evaluator_mac.mm",
- "trigger_creator.cc",
- "trigger_creator.h",
]
deps += [
"//chrome/services/file_util/public/cpp",
"//components/content_settings/core/browser:browser",
+ "//components/language/core/common:common",
"//components/prefs:prefs",
- "//components/safe_browsing/common:safe_browsing_prefs",
"//components/safe_browsing/db:db",
- "//components/safe_browsing/triggers:ad_sampler_trigger",
- "//components/safe_browsing/triggers:trigger_throttler",
- "//components/safe_browsing/triggers:triggers",
"//components/security_interstitials/content:security_interstitial_page",
"//content/public/browser:browser",
"//net:net",
diff --git a/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl.cc b/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl.cc
index 0aafa6af519..23bb639702c 100644
--- a/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl.cc
+++ b/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl.cc
@@ -18,7 +18,7 @@
SpellCheckHostChromeImpl::SpellCheckHostChromeImpl(
const service_manager::Identity& renderer_identity)
- : renderer_identity_(renderer_identity) {}
+ : renderer_identity_(renderer_identity), weak_factory_(this) {}
SpellCheckHostChromeImpl::~SpellCheckHostChromeImpl() = default;
@@ -27,7 +27,7 @@ void SpellCheckHostChromeImpl::Create(
spellcheck::mojom::SpellCheckHostRequest request,
const service_manager::BindSourceInfo& source_info) {
mojo::MakeStrongBinding(
- base::MakeUnique<SpellCheckHostChromeImpl>(source_info.identity),
+ std::make_unique<SpellCheckHostChromeImpl>(source_info.identity),
std::move(request));
}
@@ -61,6 +61,7 @@ void SpellCheckHostChromeImpl::NotifyChecked(const base::string16& word,
spellcheck->GetMetrics()->RecordCheckedWordStats(word, misspelled);
}
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
void SpellCheckHostChromeImpl::CallSpellingService(
const base::string16& text,
CallSpellingServiceCallback callback) {
@@ -72,7 +73,6 @@ void SpellCheckHostChromeImpl::CallSpellingService(
return;
}
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
// Checks the user profile and sends a JSON-RPC request to the Spelling
// service if a user enables the "Ask Google for suggestions" option. When
// a response is received (including an error) from the remote Spelling
@@ -83,13 +83,9 @@ void SpellCheckHostChromeImpl::CallSpellingService(
client_.RequestTextCheck(
context, SpellingServiceClient::SPELLCHECK, text,
base::BindOnce(&SpellCheckHostChromeImpl::CallSpellingServiceDone,
- base::Unretained(this), base::Passed(&callback)));
-#else
- std::move(callback).Run(false, std::vector<SpellCheckResult>());
-#endif
+ weak_factory_.GetWeakPtr(), std::move(callback)));
}
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
void SpellCheckHostChromeImpl::CallSpellingServiceDone(
CallSpellingServiceCallback callback,
bool success,
diff --git a/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl.h b/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl.h
index 1e712fcdbb3..e0d5c7c9393 100644
--- a/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl.h
+++ b/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl.h
@@ -5,22 +5,18 @@
#ifndef CHROME_BROWSER_SPELLCHECKER_SPELL_CHECK_HOST_CHROME_IMPL_H_
#define CHROME_BROWSER_SPELLCHECKER_SPELL_CHECK_HOST_CHROME_IMPL_H_
-#include "base/macros.h"
+#include "build/build_config.h"
+#include "components/spellcheck/browser/spell_check_host_impl.h"
#include "components/spellcheck/browser/spelling_service_client.h"
-#include "components/spellcheck/common/spellcheck.mojom.h"
-#include "components/spellcheck/spellcheck_build_features.h"
#include "services/service_manager/public/cpp/bind_source_info.h"
-#if !BUILDFLAG(ENABLE_SPELLCHECK)
-#error "Spellcheck should be enabled."
-#endif
-
class SpellcheckCustomDictionary;
class SpellcheckService;
struct SpellCheckResult;
-class SpellCheckHostChromeImpl : public spellcheck::mojom::SpellCheckHost {
+// Implementation of SpellCheckHost involving Chrome-only features.
+class SpellCheckHostChromeImpl : public SpellCheckHostImpl {
public:
explicit SpellCheckHostChromeImpl(
const service_manager::Identity& renderer_identity);
@@ -31,14 +27,16 @@ class SpellCheckHostChromeImpl : public spellcheck::mojom::SpellCheckHost {
private:
friend class TestSpellCheckHostChromeImpl;
+ friend class SpellCheckHostChromeImplMacTest;
- // spellcheck::mojom::SpellCheckHost:
+ // SpellCheckHostImpl:
void RequestDictionary() override;
void NotifyChecked(const base::string16& word, bool misspelled) override;
+
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
void CallSpellingService(const base::string16& text,
CallSpellingServiceCallback callback) override;
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
// Invoked when the remote Spelling service has finished checking the
// text of a CallSpellingService request.
void CallSpellingServiceDone(
@@ -55,6 +53,28 @@ class SpellCheckHostChromeImpl : public spellcheck::mojom::SpellCheckHost {
const std::vector<SpellCheckResult>& service_results);
#endif
+#if defined(OS_MACOSX)
+ // Non-Mac (i.e., Android) implementations of the following APIs are in the
+ // base class SpellCheckHostImpl.
+ void CheckSpelling(const base::string16& word,
+ int route_id,
+ CheckSpellingCallback callback) override;
+ void FillSuggestionList(const base::string16& word,
+ FillSuggestionListCallback callback) override;
+ void RequestTextCheck(const base::string16& text,
+ int route_id,
+ RequestTextCheckCallback callback) override;
+
+ // Exposed to tests only.
+ static void CombineResultsForTesting(
+ std::vector<SpellCheckResult>* remote_results,
+ const std::vector<SpellCheckResult>& local_results);
+
+ int ToDocumentTag(int route_id);
+ void RetireDocumentTag(int route_id);
+ std::map<int, int> tag_map_;
+#endif // defined(OS_MACOSX)
+
// Returns the SpellcheckService of our |render_process_id_|. The return
// is null if the render process is being shut down.
virtual SpellcheckService* GetSpellcheckService() const;
@@ -65,6 +85,8 @@ class SpellCheckHostChromeImpl : public spellcheck::mojom::SpellCheckHost {
// A JSON-RPC client that calls the remote Spelling service.
SpellingServiceClient client_;
+ base::WeakPtrFactory<SpellCheckHostChromeImpl> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(SpellCheckHostChromeImpl);
};
diff --git a/chromium/chrome/browser/spellchecker/spellcheck_message_filter_platform_mac.cc b/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl_mac.cc
index 5666e75af54..316a6b45352 100644
--- a/chromium/chrome/browser/spellchecker/spellcheck_message_filter_platform_mac.cc
+++ b/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl_mac.cc
@@ -2,48 +2,70 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/spellcheck/browser/spellcheck_message_filter_platform.h"
-
-#include <algorithm>
-#include <functional>
+#include "chrome/browser/spellchecker/spell_check_host_chrome_impl.h"
#include "base/barrier_closure.h"
#include "base/bind.h"
#include "chrome/browser/spellchecker/spellcheck_factory.h"
#include "chrome/browser/spellchecker/spellcheck_service.h"
#include "components/spellcheck/browser/spellcheck_platform.h"
-#include "components/spellcheck/browser/spelling_service_client.h"
-#include "components/spellcheck/common/spellcheck_messages.h"
-#include "components/spellcheck/common/spellcheck_result.h"
#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
+#include "services/service_manager/public/cpp/identity.h"
using content::BrowserThread;
using content::BrowserContext;
namespace {
-bool CompareLocation(const SpellCheckResult& r1,
- const SpellCheckResult& r2) {
+bool CompareLocation(const SpellCheckResult& r1, const SpellCheckResult& r2) {
return r1.location < r2.location;
}
+// Adjusts remote_results by examining local_results. Any result that's both
+// local and remote stays type SPELLING, all others are flagged GRAMMAR.
+// (This is needed to force gray underline for remote-only results.)
+void CombineResults(std::vector<SpellCheckResult>* remote_results,
+ const std::vector<SpellCheckResult>& local_results) {
+ std::vector<SpellCheckResult>::const_iterator local_iter(
+ local_results.begin());
+ std::vector<SpellCheckResult>::iterator remote_iter;
+
+ for (remote_iter = remote_results->begin();
+ remote_iter != remote_results->end(); ++remote_iter) {
+ // Discard all local results occurring before remote result.
+ while (local_iter != local_results.end() &&
+ local_iter->location < remote_iter->location) {
+ local_iter++;
+ }
+
+ // Unless local and remote result coincide, result is GRAMMAR.
+ remote_iter->decoration = SpellCheckResult::GRAMMAR;
+ if (local_iter != local_results.end() &&
+ local_iter->location == remote_iter->location &&
+ local_iter->length == remote_iter->length) {
+ remote_iter->decoration = SpellCheckResult::SPELLING;
+ }
+ }
+}
+
} // namespace
class SpellingRequest {
public:
- SpellingRequest(SpellingServiceClient* client,
- content::BrowserMessageFilter* destination,
- int render_process_id);
+ using RequestTextCheckCallback =
+ spellcheck::mojom::SpellCheckHost::RequestTextCheckCallback;
- void RequestCheck(const base::string16& text,
- int route_id,
- int identifier,
- int document_tag);
+ SpellingRequest(SpellingServiceClient* client,
+ const base::string16& text,
+ const service_manager::Identity& renderer_identity,
+ int document_tag,
+ RequestTextCheckCallback callback);
private:
// Request server-side checking for |text_|.
- void RequestRemoteCheck();
+ void RequestRemoteCheck(SpellingServiceClient* client);
// Request a check for |text_| from local spell checker.
void RequestLocalCheck();
@@ -66,57 +88,42 @@ class SpellingRequest {
base::RepeatingClosure completion_barrier_;
bool remote_success_;
- SpellingServiceClient* client_; // Owned by |destination|.
- content::BrowserMessageFilter* destination_; // ref-counted.
- int render_process_id_;
-
base::string16 text_;
- int route_id_;
- int identifier_;
+ const service_manager::Identity renderer_identity_;
int document_tag_;
+ RequestTextCheckCallback callback_;
};
-SpellingRequest::SpellingRequest(SpellingServiceClient* client,
- content::BrowserMessageFilter* destination,
- int render_process_id)
+SpellingRequest::SpellingRequest(
+ SpellingServiceClient* client,
+ const base::string16& text,
+ const service_manager::Identity& renderer_identity,
+ int document_tag,
+ RequestTextCheckCallback callback)
: remote_success_(false),
- client_(client),
- destination_(destination),
- render_process_id_(render_process_id),
- route_id_(-1),
- identifier_(-1),
- document_tag_(-1) {
- destination_->AddRef();
-}
-
-void SpellingRequest::RequestCheck(const base::string16& text,
- int route_id,
- int identifier,
- int document_tag) {
- DCHECK(!text.empty());
+ text_(text),
+ renderer_identity_(renderer_identity),
+ document_tag_(document_tag),
+ callback_(std::move(callback)) {
+ DCHECK(!text_.empty());
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- text_ = text;
- route_id_ = route_id;
- identifier_ = identifier;
- document_tag_ = document_tag;
-
// Send the remote query out. The barrier owns |this|, ensuring it is deleted
// after completion.
completion_barrier_ = BarrierClosure(
2, base::BindOnce(&SpellingRequest::OnCheckCompleted, base::Owned(this)));
- RequestRemoteCheck();
+ RequestRemoteCheck(client);
RequestLocalCheck();
}
-void SpellingRequest::RequestRemoteCheck() {
+void SpellingRequest::RequestRemoteCheck(SpellingServiceClient* client) {
BrowserContext* context = NULL;
content::RenderProcessHost* host =
- content::RenderProcessHost::FromID(render_process_id_);
+ content::RenderProcessHost::FromRendererIdentity(renderer_identity_);
if (host)
context = host->GetBrowserContext();
- client_->RequestTextCheck(
+ client->RequestTextCheck(
context, SpellingServiceClient::SPELLCHECK, text_,
base::BindOnce(&SpellingRequest::OnRemoteCheckCompleted,
base::Unretained(this)));
@@ -131,22 +138,18 @@ void SpellingRequest::RequestLocalCheck() {
void SpellingRequest::OnCheckCompleted() {
// Final completion can happen on any thread - don't DCHECK thread.
- const std::vector<SpellCheckResult>* check_results = &local_results_;
+ std::vector<SpellCheckResult>* check_results = &local_results_;
if (remote_success_) {
std::sort(remote_results_.begin(), remote_results_.end(), CompareLocation);
std::sort(local_results_.begin(), local_results_.end(), CompareLocation);
- SpellCheckMessageFilterPlatform::CombineResults(&remote_results_,
- local_results_);
+ CombineResults(&remote_results_, local_results_);
check_results = &remote_results_;
}
- destination_->Send(
- new SpellCheckMsg_RespondTextCheck(
- route_id_,
- identifier_,
- text_,
- *check_results));
- destination_->Release();
+ // |callback_| must be run on UI thread.
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI, FROM_HERE,
+ base::BindOnce(std::move(callback_), std::move(*check_results)));
// Object is self-managed - at this point, its life span is over.
// No need to delete, since the OnCheckCompleted callback owns |this|.
@@ -169,81 +172,33 @@ void SpellingRequest::OnLocalCheckCompleted(
completion_barrier_.Run();
}
-
-SpellCheckMessageFilterPlatform::SpellCheckMessageFilterPlatform(
- int render_process_id)
- : BrowserMessageFilter(SpellCheckMsgStart),
- render_process_id_(render_process_id),
- client_(new SpellingServiceClient) {
-}
-
-void SpellCheckMessageFilterPlatform::OverrideThreadForMessage(
- const IPC::Message& message, BrowserThread::ID* thread) {
- if (message.type() == SpellCheckHostMsg_RequestTextCheck::ID)
- *thread = BrowserThread::UI;
-}
-
-bool SpellCheckMessageFilterPlatform::OnMessageReceived(
- const IPC::Message& message) {
- bool handled = true;
- IPC_BEGIN_MESSAGE_MAP(SpellCheckMessageFilterPlatform, message)
- IPC_MESSAGE_HANDLER(SpellCheckHostMsg_CheckSpelling,
- OnCheckSpelling)
- IPC_MESSAGE_HANDLER(SpellCheckHostMsg_FillSuggestionList,
- OnFillSuggestionList)
- IPC_MESSAGE_HANDLER(SpellCheckHostMsg_RequestTextCheck,
- OnRequestTextCheck)
- IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP()
- return handled;
-}
-
// static
-void SpellCheckMessageFilterPlatform::CombineResults(
+void SpellCheckHostChromeImpl::CombineResultsForTesting(
std::vector<SpellCheckResult>* remote_results,
const std::vector<SpellCheckResult>& local_results) {
- std::vector<SpellCheckResult>::const_iterator local_iter(
- local_results.begin());
- std::vector<SpellCheckResult>::iterator remote_iter;
-
- for (remote_iter = remote_results->begin();
- remote_iter != remote_results->end();
- ++remote_iter) {
- // Discard all local results occurring before remote result.
- while (local_iter != local_results.end() &&
- local_iter->location < remote_iter->location) {
- local_iter++;
- }
-
- // Unless local and remote result coincide, result is GRAMMAR.
- remote_iter->decoration = SpellCheckResult::GRAMMAR;
- if (local_iter != local_results.end() &&
- local_iter->location == remote_iter->location &&
- local_iter->length == remote_iter->length) {
- remote_iter->decoration = SpellCheckResult::SPELLING;
- }
- }
+ CombineResults(remote_results, local_results);
}
-SpellCheckMessageFilterPlatform::~SpellCheckMessageFilterPlatform() {}
-
-void SpellCheckMessageFilterPlatform::OnCheckSpelling(
- const base::string16& word,
- int route_id,
- bool* correct) {
- *correct = spellcheck_platform::CheckSpelling(word, ToDocumentTag(route_id));
+void SpellCheckHostChromeImpl::CheckSpelling(const base::string16& word,
+ int route_id,
+ CheckSpellingCallback callback) {
+ bool correct =
+ spellcheck_platform::CheckSpelling(word, ToDocumentTag(route_id));
+ std::move(callback).Run(correct);
}
-void SpellCheckMessageFilterPlatform::OnFillSuggestionList(
+void SpellCheckHostChromeImpl::FillSuggestionList(
const base::string16& word,
- std::vector<base::string16>* suggestions) {
- spellcheck_platform::FillSuggestionList(word, suggestions);
+ FillSuggestionListCallback callback) {
+ std::vector<base::string16> suggestions;
+ spellcheck_platform::FillSuggestionList(word, &suggestions);
+ std::move(callback).Run(suggestions);
}
-void SpellCheckMessageFilterPlatform::OnRequestTextCheck(
+void SpellCheckHostChromeImpl::RequestTextCheck(
+ const base::string16& text,
int route_id,
- int identifier,
- const base::string16& text) {
+ RequestTextCheckCallback callback) {
DCHECK(!text.empty());
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -251,18 +206,14 @@ void SpellCheckMessageFilterPlatform::OnRequestTextCheck(
// language code for text breaking to the renderer. (Text breaking is required
// for the context menu to show spelling suggestions.) Initialization must
// happen on UI thread.
- content::RenderProcessHost* host =
- content::RenderProcessHost::FromID(render_process_id_);
- if (host)
- SpellcheckServiceFactory::GetForRenderer(host->GetChildIdentity());
+ GetSpellcheckService();
// SpellingRequest self-destructs.
- SpellingRequest* request =
- new SpellingRequest(client_.get(), this, render_process_id_);
- request->RequestCheck(text, route_id, identifier, ToDocumentTag(route_id));
+ new SpellingRequest(&client_, text, renderer_identity_,
+ ToDocumentTag(route_id), std::move(callback));
}
-int SpellCheckMessageFilterPlatform::ToDocumentTag(int route_id) {
+int SpellCheckHostChromeImpl::ToDocumentTag(int route_id) {
if (!tag_map_.count(route_id))
tag_map_[route_id] = spellcheck_platform::GetDocumentTag();
return tag_map_[route_id];
@@ -271,7 +222,7 @@ int SpellCheckMessageFilterPlatform::ToDocumentTag(int route_id) {
// TODO(groby): We are currently not notified of retired tags. We need
// to track destruction of RenderViewHosts on the browser process side
// to update our mappings when a document goes away.
-void SpellCheckMessageFilterPlatform::RetireDocumentTag(int route_id) {
+void SpellCheckHostChromeImpl::RetireDocumentTag(int route_id) {
spellcheck_platform::CloseDocumentWithTag(ToDocumentTag(route_id));
tag_map_.erase(route_id);
}
diff --git a/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl_mac_browsertest.cc b/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl_mac_browsertest.cc
new file mode 100644
index 00000000000..03d2bab0daf
--- /dev/null
+++ b/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl_mac_browsertest.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/spellchecker/spell_check_host_chrome_impl.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/test/mock_render_process_host.h"
+
+class SpellCheckHostChromeImplMacBrowserTest : public InProcessBrowserTest {
+ public:
+ void SetUpOnMainThread() override {
+ content::BrowserContext* context = browser()->profile();
+ renderer_.reset(new content::MockRenderProcessHost(context));
+
+ service_manager::BindSourceInfo source_info;
+ source_info.identity = renderer_->GetChildIdentity();
+ SpellCheckHostChromeImpl::Create(mojo::MakeRequest(&spell_check_host_),
+ source_info);
+ }
+
+ void TearDownOnMainThread() override { renderer_.reset(); }
+
+ void LogResult(const std::vector<SpellCheckResult>& result) {
+ received_result_ = true;
+ result_ = result;
+ if (quit_)
+ std::move(quit_).Run();
+ }
+
+ void RunUntilResultReceived() {
+ if (received_result_)
+ return;
+ base::RunLoop run_loop;
+ quit_ = run_loop.QuitClosure();
+ run_loop.Run();
+ }
+
+ protected:
+ std::unique_ptr<content::MockRenderProcessHost> renderer_;
+ spellcheck::mojom::SpellCheckHostPtr spell_check_host_;
+
+ bool received_result_ = false;
+ std::vector<SpellCheckResult> result_;
+ base::OnceClosure quit_;
+};
+
+// Uses browsertest to setup chrome threads.
+IN_PROC_BROWSER_TEST_F(SpellCheckHostChromeImplMacBrowserTest,
+ SpellCheckReturnMessage) {
+ spell_check_host_->RequestTextCheck(
+ base::UTF8ToUTF16("zz."), 123,
+ base::BindOnce(&SpellCheckHostChromeImplMacBrowserTest::LogResult,
+ base::Unretained(this)));
+ RunUntilResultReceived();
+
+ ASSERT_EQ(1U, result_.size());
+ EXPECT_EQ(result_[0].location, 0);
+ EXPECT_EQ(result_[0].length, 2);
+ EXPECT_EQ(result_[0].decoration, SpellCheckResult::SPELLING);
+}
diff --git a/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl_mac_unittest.cc b/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl_mac_unittest.cc
new file mode 100644
index 00000000000..2eecc346490
--- /dev/null
+++ b/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl_mac_unittest.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/spellchecker/spell_check_host_chrome_impl.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class SpellCheckHostChromeImplMacTest : public ::testing::Test {
+ protected:
+ void CombineResults(
+ std::vector<SpellCheckResult>* remote_results,
+ const std::vector<SpellCheckResult>& local_results) const {
+ SpellCheckHostChromeImpl::CombineResultsForTesting(remote_results,
+ local_results);
+ }
+};
+
+TEST_F(SpellCheckHostChromeImplMacTest, CombineResults) {
+ std::vector<SpellCheckResult> local_results;
+ std::vector<SpellCheckResult> remote_results;
+ base::string16 remote_suggestion = base::ASCIIToUTF16("remote");
+ base::string16 local_suggestion = base::ASCIIToUTF16("local");
+
+ // Remote-only result - must be flagged as GRAMMAR after combine
+ remote_results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, 0, 5));
+
+ // Local-only result - must be discarded after combine
+ local_results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, 10, 5));
+
+ // local & remote result - must be flagged SPELLING, uses remote suggestion.
+ SpellCheckResult result(SpellCheckResult::SPELLING, 20, 5, local_suggestion);
+ local_results.push_back(result);
+ result.replacements[0] = remote_suggestion;
+ remote_results.push_back(result);
+
+ CombineResults(&remote_results, local_results);
+
+ ASSERT_EQ(2U, remote_results.size());
+ EXPECT_EQ(SpellCheckResult::GRAMMAR, remote_results[0].decoration);
+ EXPECT_EQ(0, remote_results[0].location);
+ EXPECT_EQ(SpellCheckResult::SPELLING, remote_results[1].decoration);
+ EXPECT_EQ(20, remote_results[1].location);
+ EXPECT_EQ(remote_suggestion, remote_results[1].replacements[0]);
+}
diff --git a/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl_unittest.cc b/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl_unittest.cc
index e991fddc5e1..7e978c228ee 100644
--- a/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl_unittest.cc
+++ b/chromium/chrome/browser/spellchecker/spell_check_host_chrome_impl_unittest.cc
@@ -22,7 +22,7 @@
class TestSpellCheckHostChromeImpl {
public:
TestSpellCheckHostChromeImpl()
- : spellcheck_(base::MakeUnique<SpellcheckService>(&testing_profile_)) {}
+ : spellcheck_(std::make_unique<SpellcheckService>(&testing_profile_)) {}
SpellcheckCustomDictionary& GetCustomDictionary() const {
EXPECT_NE(nullptr, spellcheck_.get());
diff --git a/chromium/chrome/browser/spellchecker/spell_check_panel_host_impl.cc b/chromium/chrome/browser/spellchecker/spell_check_panel_host_impl.cc
index 591b90567eb..558e744efa2 100644
--- a/chromium/chrome/browser/spellchecker/spell_check_panel_host_impl.cc
+++ b/chromium/chrome/browser/spellchecker/spell_check_panel_host_impl.cc
@@ -17,7 +17,7 @@ SpellCheckPanelHostImpl::~SpellCheckPanelHostImpl() = default;
// static
void SpellCheckPanelHostImpl::Create(
spellcheck::mojom::SpellCheckPanelHostRequest request) {
- mojo::MakeStrongBinding(base::MakeUnique<SpellCheckPanelHostImpl>(),
+ mojo::MakeStrongBinding(std::make_unique<SpellCheckPanelHostImpl>(),
std::move(request));
}
diff --git a/chromium/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc b/chromium/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc
index 24a9f621704..5ad42fc6183 100644
--- a/chromium/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc
+++ b/chromium/chrome/browser/spellchecker/spellcheck_custom_dictionary.cc
@@ -87,7 +87,7 @@ ChecksumStatus LoadFile(const base::FilePath& file_path,
bool IsValidWord(const std::string& word) {
std::string tmp;
return !word.empty() &&
- word.size() <= spellcheck::MAX_CUSTOM_DICTIONARY_WORD_BYTES &&
+ word.size() <= spellcheck::kMaxCustomDictionaryWordBytes &&
base::IsStringUTF8(word) &&
base::TRIM_NONE ==
base::TrimWhitespaceASCII(word, base::TRIM_ALL, &tmp);
@@ -340,7 +340,7 @@ syncer::SyncDataList SpellcheckCustomDictionary::GetAllSyncData(
syncer::SyncDataList data;
size_t i = 0;
for (const auto& word : words_) {
- if (i++ >= spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS)
+ if (i++ >= spellcheck::kMaxSyncableDictionaryWords)
break;
sync_pb::EntitySpecifics specifics;
specifics.mutable_dictionary()->set_word(word);
@@ -435,7 +435,7 @@ void SpellcheckCustomDictionary::OnLoaded(
// Save cleaned up data only after startup.
fix_invalid_file_.Reset(
base::BindOnce(&SpellcheckCustomDictionary::FixInvalidFile,
- weak_ptr_factory_.GetWeakPtr(), base::Passed(&result)));
+ weak_ptr_factory_.GetWeakPtr(), std::move(result)));
BrowserThread::PostAfterStartupTask(
FROM_HERE, BrowserThread::GetTaskRunnerForThread(BrowserThread::UI),
fix_invalid_file_.callback());
@@ -458,7 +458,7 @@ void SpellcheckCustomDictionary::FixInvalidFile(
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&SavePassedWordsToDictionaryFileReliably,
- custom_dictionary_path_, base::Passed(&load_file_result)));
+ custom_dictionary_path_, std::move(load_file_result)));
}
void SpellcheckCustomDictionary::Save(
@@ -468,8 +468,7 @@ void SpellcheckCustomDictionary::Save(
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&SpellcheckCustomDictionary::UpdateDictionaryFile,
- base::Passed(&dictionary_change),
- custom_dictionary_path_));
+ std::move(dictionary_change), custom_dictionary_path_));
}
syncer::SyncError SpellcheckCustomDictionary::Sync(
@@ -483,7 +482,7 @@ syncer::SyncError SpellcheckCustomDictionary::Sync(
int server_size = static_cast<int>(words_.size()) -
static_cast<int>(dictionary_change.to_add().size());
int max_upload_size =
- std::max(0, static_cast<int>(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS) -
+ std::max(0, static_cast<int>(spellcheck::kMaxSyncableDictionaryWords) -
server_size);
int upload_size = std::min(
static_cast<int>(dictionary_change.to_add().size()),
@@ -518,7 +517,7 @@ syncer::SyncError SpellcheckCustomDictionary::Sync(
// Turn off syncing of this dictionary if the server already has the maximum
// number of words.
- if (words_.size() > spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS)
+ if (words_.size() > spellcheck::kMaxSyncableDictionaryWords)
StopSyncing(syncer::DICTIONARY);
return error;
diff --git a/chromium/chrome/browser/spellchecker/spellcheck_custom_dictionary_unittest.cc b/chromium/chrome/browser/spellchecker/spellcheck_custom_dictionary_unittest.cc
index c488d189d6d..b60ebfd25a6 100644
--- a/chromium/chrome/browser/spellchecker/spellcheck_custom_dictionary_unittest.cc
+++ b/chromium/chrome/browser/spellchecker/spellcheck_custom_dictionary_unittest.cc
@@ -58,7 +58,7 @@ syncer::SyncDataList GetAllSyncDataNoLimit(
static std::unique_ptr<KeyedService> BuildSpellcheckService(
content::BrowserContext* profile) {
- return base::MakeUnique<SpellcheckService>(static_cast<Profile*>(profile));
+ return std::make_unique<SpellcheckService>(static_cast<Profile*>(profile));
}
class SpellcheckCustomDictionaryTest : public testing::Test {
@@ -325,31 +325,31 @@ TEST_F(SpellcheckCustomDictionaryTest, GetAllSyncDataHasLimit) {
&profile_)->GetCustomDictionary();
SpellcheckCustomDictionary::Change change;
- for (size_t i = 0; i < spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS - 1; i++) {
+ for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords - 1; i++) {
change.AddWord("foo" + base::NumberToString(i));
}
Apply(*dictionary, change);
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS - 1,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords - 1,
dictionary->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS - 1,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords - 1,
dictionary->GetAllSyncData(syncer::DICTIONARY).size());
dictionary->AddWord("baz");
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
dictionary->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
dictionary->GetAllSyncData(syncer::DICTIONARY).size());
dictionary->AddWord("bar");
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS + 1,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
dictionary->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
dictionary->GetAllSyncData(syncer::DICTIONARY).size());
dictionary->AddWord("snafoo");
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS + 2,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 2,
dictionary->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
dictionary->GetAllSyncData(syncer::DICTIONARY).size());
}
@@ -438,13 +438,13 @@ TEST_F(SpellcheckCustomDictionaryTest, MergeDataAndStartSyncing) {
spellcheck_service2->GetCustomDictionary();
SpellcheckCustomDictionary::Change change;
- for (size_t i = 0; i < spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS / 2; ++i) {
+ for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords / 2; ++i) {
change.AddWord("foo" + base::NumberToString(i));
}
Apply(*custom_dictionary, change);
SpellcheckCustomDictionary::Change change2;
- for (size_t i = 0; i < spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS / 2; ++i) {
+ for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords / 2; ++i) {
change2.AddWord("bar" + base::NumberToString(i));
}
Apply(*custom_dictionary2, change2);
@@ -535,7 +535,7 @@ TEST_F(SpellcheckCustomDictionaryTest, DictionaryTooBigBeforeSyncing) {
spellcheck_service2->GetCustomDictionary();
SpellcheckCustomDictionary::Change change;
- for (size_t i = 0; i < spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS + 1; ++i) {
+ for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords + 1; ++i) {
change.AddWord("foo" + base::NumberToString(i));
}
Apply(*custom_dictionary, change);
@@ -555,14 +555,14 @@ TEST_F(SpellcheckCustomDictionaryTest, DictionaryTooBigBeforeSyncing) {
EXPECT_EQ(0, error_counter);
EXPECT_FALSE(custom_dictionary->IsSyncing());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS + 1,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
custom_dictionary->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary2->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary->GetAllSyncData(syncer::DICTIONARY).size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary2->GetAllSyncData(syncer::DICTIONARY).size());
}
@@ -581,7 +581,7 @@ TEST_F(SpellcheckCustomDictionaryTest, DictionaryTooBigAndServerFull) {
SpellcheckCustomDictionary::Change change;
SpellcheckCustomDictionary::Change change2;
- for (size_t i = 0; i < spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS; ++i) {
+ for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords; ++i) {
change.AddWord("foo" + base::NumberToString(i));
change2.AddWord("bar" + base::NumberToString(i));
}
@@ -589,9 +589,9 @@ TEST_F(SpellcheckCustomDictionaryTest, DictionaryTooBigAndServerFull) {
Apply(*custom_dictionary, change);
Apply(*custom_dictionary2, change2);
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS + 1,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
custom_dictionary->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary2->GetWords().size());
int error_counter = 0;
@@ -609,14 +609,14 @@ TEST_F(SpellcheckCustomDictionaryTest, DictionaryTooBigAndServerFull) {
EXPECT_EQ(0, error_counter);
EXPECT_FALSE(custom_dictionary->IsSyncing());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS * 2 + 1,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords * 2 + 1,
custom_dictionary->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary2->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary->GetAllSyncData(syncer::DICTIONARY).size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary2->GetAllSyncData(syncer::DICTIONARY).size());
}
@@ -635,16 +635,16 @@ TEST_F(SpellcheckCustomDictionaryTest, ServerTooBig) {
SpellcheckCustomDictionary::Change change;
SpellcheckCustomDictionary::Change change2;
- for (size_t i = 0; i < spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS + 1; ++i) {
+ for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords + 1; ++i) {
change.AddWord("foo" + base::NumberToString(i));
change2.AddWord("bar" + base::NumberToString(i));
}
Apply(*custom_dictionary, change);
Apply(*custom_dictionary2, change2);
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS + 1,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
custom_dictionary->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS + 1,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
custom_dictionary2->GetWords().size());
int error_counter = 0;
@@ -662,14 +662,14 @@ TEST_F(SpellcheckCustomDictionaryTest, ServerTooBig) {
EXPECT_EQ(0, error_counter);
EXPECT_FALSE(custom_dictionary->IsSyncing());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS * 2 + 2,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords * 2 + 2,
custom_dictionary->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS + 1,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
custom_dictionary2->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary->GetAllSyncData(syncer::DICTIONARY).size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary2->GetAllSyncData(syncer::DICTIONARY).size());
}
@@ -687,7 +687,7 @@ TEST_F(SpellcheckCustomDictionaryTest, DictionaryTooBigToStartSyncing) {
spellcheck_service2->GetCustomDictionary();
SpellcheckCustomDictionary::Change change;
- for (size_t i = 0; i < spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS - 1; ++i) {
+ for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords - 1; ++i) {
change.AddWord("foo" + base::NumberToString(i));
}
Apply(*custom_dictionary, change);
@@ -710,14 +710,14 @@ TEST_F(SpellcheckCustomDictionaryTest, DictionaryTooBigToStartSyncing) {
EXPECT_EQ(0, error_counter);
EXPECT_FALSE(custom_dictionary->IsSyncing());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS + 1,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
custom_dictionary->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary2->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary->GetAllSyncData(syncer::DICTIONARY).size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary2->GetAllSyncData(syncer::DICTIONARY).size());
}
@@ -735,7 +735,7 @@ TEST_F(SpellcheckCustomDictionaryTest, DictionaryTooBigToContiueSyncing) {
spellcheck_service2->GetCustomDictionary();
SpellcheckCustomDictionary::Change change;
- for (size_t i = 0; i < spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS - 1; ++i) {
+ for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords - 1; ++i) {
change.AddWord("foo" + base::NumberToString(i));
}
Apply(*custom_dictionary, change);
@@ -763,14 +763,14 @@ TEST_F(SpellcheckCustomDictionaryTest, DictionaryTooBigToContiueSyncing) {
EXPECT_EQ(0, error_counter);
EXPECT_FALSE(custom_dictionary->IsSyncing());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS + 1,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
custom_dictionary->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary2->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary->GetAllSyncData(syncer::DICTIONARY).size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary2->GetAllSyncData(syncer::DICTIONARY).size());
}
@@ -849,21 +849,21 @@ TEST_F(SpellcheckCustomDictionaryTest, LoadAfterSyncStartTooBigToSync) {
std::unique_ptr<std::set<std::string>> custom_words(
new std::set<std::string>);
- for (size_t i = 0; i < spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS; ++i) {
+ for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords; ++i) {
custom_words->insert(custom_words->end(), "foo" + base::NumberToString(i));
}
OnLoaded(*custom_dictionary, std::move(custom_words));
EXPECT_EQ(0, error_counter);
EXPECT_FALSE(custom_dictionary->IsSyncing());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS + 1,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
custom_dictionary->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary2->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary->GetAllSyncData(syncer::DICTIONARY).size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary2->GetAllSyncData(syncer::DICTIONARY).size());
}
@@ -881,7 +881,7 @@ TEST_F(SpellcheckCustomDictionaryTest, LoadDuplicatesAfterSync) {
spellcheck_service2->GetCustomDictionary();
SpellcheckCustomDictionary::Change change;
- for (size_t i = 0; i < spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS / 2; ++i) {
+ for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords / 2; ++i) {
change.AddWord("foo" + base::NumberToString(i));
}
Apply(*custom_dictionary, change);
@@ -902,18 +902,18 @@ TEST_F(SpellcheckCustomDictionaryTest, LoadDuplicatesAfterSync) {
EXPECT_TRUE(custom_dictionary->IsSyncing());
OnLoaded(*custom_dictionary,
- base::MakeUnique<std::set<std::string>>(change.to_add()));
+ std::make_unique<std::set<std::string>>(change.to_add()));
EXPECT_EQ(0, error_counter);
EXPECT_TRUE(custom_dictionary->IsSyncing());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS / 2,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords / 2,
custom_dictionary->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS / 2,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords / 2,
custom_dictionary2->GetWords().size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS / 2,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords / 2,
custom_dictionary->GetAllSyncData(syncer::DICTIONARY).size());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS / 2,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords / 2,
custom_dictionary2->GetAllSyncData(syncer::DICTIONARY).size());
}
@@ -1054,7 +1054,7 @@ TEST_F(SpellcheckCustomDictionaryTest, DictionarySyncLimit) {
spellcheck_service->GetCustomDictionary();
SpellcheckCustomDictionary::Change change;
- for (size_t i = 0; i < spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS; ++i) {
+ for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords; ++i) {
change.AddWord("foo" + base::NumberToString(i));
}
Apply(*custom_dictionary, change);
@@ -1074,12 +1074,12 @@ TEST_F(SpellcheckCustomDictionaryTest, DictionarySyncLimit) {
.IsSet());
EXPECT_EQ(0, error_counter);
EXPECT_TRUE(custom_dictionary->IsSyncing());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
custom_dictionary->GetWords().size());
}
// The sync server now has the maximum number of words.
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
server_custom_dictionary->GetWords().size());
// Associate the sync server with a client that also has the maximum number of
@@ -1099,7 +1099,7 @@ TEST_F(SpellcheckCustomDictionaryTest, DictionarySyncLimit) {
// Add the maximum number of words to the client. These words are all
// different from those on the server.
SpellcheckCustomDictionary::Change change;
- for (size_t i = 0; i < spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS; ++i) {
+ for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords; ++i) {
change.AddWord("bar" + base::NumberToString(i));
}
Apply(*client_custom_dictionary, change);
@@ -1120,13 +1120,13 @@ TEST_F(SpellcheckCustomDictionaryTest, DictionarySyncLimit) {
.IsSet());
EXPECT_EQ(0, error_counter);
EXPECT_FALSE(client_custom_dictionary->IsSyncing());
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS * 2,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords * 2,
client_custom_dictionary->GetWords().size());
}
// The sync server should not receive more words, because it has the maximum
// number of words already.
- EXPECT_EQ(spellcheck::MAX_SYNCABLE_DICTIONARY_WORDS,
+ EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
server_custom_dictionary->GetWords().size());
}
diff --git a/chromium/chrome/browser/spellchecker/spellcheck_factory.cc b/chromium/chrome/browser/spellchecker/spellcheck_factory.cc
index f02ff11f850..20054d12bc5 100644
--- a/chromium/chrome/browser/spellchecker/spellcheck_factory.cc
+++ b/chromium/chrome/browser/spellchecker/spellcheck_factory.cc
@@ -69,9 +69,9 @@ KeyedService* SpellcheckServiceFactory::BuildServiceInstanceFor(
void SpellcheckServiceFactory::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* user_prefs) {
user_prefs->RegisterListPref(spellcheck::prefs::kSpellCheckDictionaries,
- base::MakeUnique<base::ListValue>());
+ std::make_unique<base::ListValue>());
user_prefs->RegisterListPref(spellcheck::prefs::kSpellCheckForcedDictionaries,
- base::MakeUnique<base::ListValue>());
+ std::make_unique<base::ListValue>());
// Continue registering kSpellCheckDictionary for preference migration.
// TODO(estade): remove: crbug.com/751275
user_prefs->RegisterStringPref(
diff --git a/chromium/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc b/chromium/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc
index 3a10b007355..cca9f376dfc 100644
--- a/chromium/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc
+++ b/chromium/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc
@@ -106,9 +106,12 @@ SpellcheckHunspellDictionary::SpellcheckHunspellDictionary(
language_(language),
use_browser_spellchecker_(false),
request_context_getter_(request_context_getter),
+#if !defined(OS_ANDROID)
spellcheck_service_(spellcheck_service),
+#endif
download_status_(DOWNLOAD_NONE),
- weak_ptr_factory_(this) {}
+ weak_ptr_factory_(this) {
+}
SpellcheckHunspellDictionary::~SpellcheckHunspellDictionary() {
if (dictionary_file_.file.IsValid()) {
@@ -227,7 +230,7 @@ void SpellcheckHunspellDictionary::OnURLFetchComplete(
base::PostTaskAndReplyWithResult(
task_runner_.get(), FROM_HERE,
- base::BindOnce(&SaveDictionaryData, base::Passed(&data),
+ base::BindOnce(&SaveDictionaryData, std::move(data),
dictionary_file_.path),
base::BindOnce(&SpellcheckHunspellDictionary::SaveDictionaryDataComplete,
weak_ptr_factory_.GetWeakPtr()));
@@ -287,18 +290,19 @@ void SpellcheckHunspellDictionary::DownloadDictionary(GURL url) {
request_context_getter_ = NULL;
}
-// The default_dictionary_file can either come from the standard list of
-// hunspell dictionaries (determined in InitializeDictionaryLocation), or it
-// can be passed in via an extension. In either case, the file is checked for
-// existence so that it's not re-downloaded.
-// For systemwide installations on Windows, the default directory may not
-// have permissions for download. In that case, the alternate directory for
-// download is chrome::DIR_USER_DATA.
-//
+#if !defined(OS_ANDROID)
// static
SpellcheckHunspellDictionary::DictionaryFile
SpellcheckHunspellDictionary::OpenDictionaryFile(const base::FilePath& path) {
base::AssertBlockingAllowed();
+
+ // The default_dictionary_file can either come from the standard list of
+ // hunspell dictionaries (determined in InitializeDictionaryLocation), or it
+ // can be passed in via an extension. In either case, the file is checked for
+ // existence so that it's not re-downloaded.
+ // For systemwide installations on Windows, the default directory may not
+ // have permissions for download. In that case, the alternate directory for
+ // download is chrome::DIR_USER_DATA.
DictionaryFile dictionary;
#if defined(OS_WIN)
@@ -313,13 +317,12 @@ SpellcheckHunspellDictionary::OpenDictionaryFile(const base::FilePath& path) {
dictionary.path = path;
#else
dictionary.path = path;
-#endif
+#endif // defined(OS_WIN)
// Read the dictionary file and scan its data to check for corruption. The
// scoping closes the memory-mapped file before it is opened or deleted.
bool bdict_is_valid = false;
-#if !defined(OS_ANDROID)
{
base::MemoryMappedFile map;
bdict_is_valid =
@@ -328,7 +331,6 @@ SpellcheckHunspellDictionary::OpenDictionaryFile(const base::FilePath& path) {
hunspell::BDict::Verify(reinterpret_cast<const char*>(map.data()),
map.length());
}
-#endif
if (bdict_is_valid) {
dictionary.file.Initialize(dictionary.path,
@@ -340,15 +342,15 @@ SpellcheckHunspellDictionary::OpenDictionaryFile(const base::FilePath& path) {
return dictionary;
}
-// The default place where the spellcheck dictionary resides is
-// chrome::DIR_APP_DICTIONARIES.
-//
// static
SpellcheckHunspellDictionary::DictionaryFile
SpellcheckHunspellDictionary::InitializeDictionaryLocation(
const std::string& language) {
base::AssertBlockingAllowed();
+ // The default place where the spellcheck dictionary resides is
+ // chrome::DIR_APP_DICTIONARIES.
+ //
// Initialize the BDICT path. Initialization should be on the blocking
// sequence because it checks if there is a "Dictionaries" directory and
// create it.
@@ -384,6 +386,7 @@ void SpellcheckHunspellDictionary::InitializeDictionaryLocationComplete(
InformListenersOfInitialization();
}
+#endif // !defined(OS_ANDROID)
void SpellcheckHunspellDictionary::SaveDictionaryDataComplete(
bool dictionary_saved) {
diff --git a/chromium/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h b/chromium/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h
index a9a193e0e00..020284e76b7 100644
--- a/chromium/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h
+++ b/chromium/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h
@@ -15,6 +15,7 @@
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/sequenced_task_runner.h"
+#include "build/build_config.h"
#include "components/spellcheck/browser/spellcheck_dictionary.h"
#include "net/url_request/url_fetcher_delegate.h"
@@ -122,6 +123,7 @@ class SpellcheckHunspellDictionary
// Attempt to download the dictionary.
void DownloadDictionary(GURL url);
+#if !defined(OS_ANDROID)
// Figures out the location for the dictionary, verifies its contents, and
// opens it.
static DictionaryFile OpenDictionaryFile(const base::FilePath& path);
@@ -133,6 +135,7 @@ class SpellcheckHunspellDictionary
// The reply point for PostTaskAndReplyWithResult, called after the dictionary
// file has been initialized.
void InitializeDictionaryLocationComplete(DictionaryFile file);
+#endif
// The reply point for PostTaskAndReplyWithResult, called after the dictionary
// file has been saved.
@@ -145,10 +148,10 @@ class SpellcheckHunspellDictionary
void InformListenersOfDownloadFailure();
// Task runner where the file operations takes place.
- scoped_refptr<base::SequencedTaskRunner> task_runner_;
+ scoped_refptr<base::SequencedTaskRunner> const task_runner_;
// The language of the dictionary file.
- std::string language_;
+ const std::string language_;
// Whether to use the platform spellchecker instead of Hunspell.
bool use_browser_spellchecker_;
@@ -160,7 +163,9 @@ class SpellcheckHunspellDictionary
// Used for downloading the dictionary file.
std::unique_ptr<net::URLFetcher> fetcher_;
- SpellcheckService* spellcheck_service_;
+#if !defined(OS_ANDROID)
+ SpellcheckService* const spellcheck_service_;
+#endif
// Observers of Hunspell dictionary events.
base::ObserverList<Observer> observers_;
diff --git a/chromium/chrome/browser/spellchecker/spellcheck_language_policy_handler.cc b/chromium/chrome/browser/spellchecker/spellcheck_language_policy_handler.cc
index 5e7d5d2ac98..187eb3b1eb9 100644
--- a/chromium/chrome/browser/spellchecker/spellcheck_language_policy_handler.cc
+++ b/chromium/chrome/browser/spellchecker/spellcheck_language_policy_handler.cc
@@ -24,9 +24,7 @@ bool SpellcheckLanguagePolicyHandler::CheckPolicySettings(
const policy::PolicyMap& policies,
policy::PolicyErrorMap* errors) {
const base::Value* value = nullptr;
- if (!CheckAndGetValue(policies, errors, &value))
- return false;
- return true;
+ return CheckAndGetValue(policies, errors, &value);
}
void SpellcheckLanguagePolicyHandler::ApplyPolicySettings(
@@ -35,10 +33,8 @@ void SpellcheckLanguagePolicyHandler::ApplyPolicySettings(
// Ignore this policy if the SpellcheckEnabled policy disables spellcheck.
const base::Value* spellcheck_enabled_value =
policies.GetValue(policy::key::kSpellcheckEnabled);
- if (spellcheck_enabled_value &&
- spellcheck_enabled_value->GetBool() == false) {
+ if (spellcheck_enabled_value && spellcheck_enabled_value->GetBool() == false)
return;
- }
const base::Value* value = policies.GetValue(policy_name());
if (!value)
@@ -47,12 +43,11 @@ void SpellcheckLanguagePolicyHandler::ApplyPolicySettings(
const base::Value::ListStorage& languages = value->GetList();
std::unique_ptr<base::ListValue> forced_language_list =
- base::MakeUnique<base::ListValue>();
+ std::make_unique<base::ListValue>();
for (const base::Value& language : languages) {
std::string current_language =
spellcheck::GetCorrespondingSpellCheckLanguage(
- base::TrimWhitespaceASCII(language.GetString(), base::TRIM_ALL)
- .as_string());
+ base::TrimWhitespaceASCII(language.GetString(), base::TRIM_ALL));
if (!current_language.empty()) {
forced_language_list->GetList().push_back(base::Value(current_language));
} else {
@@ -61,7 +56,7 @@ void SpellcheckLanguagePolicyHandler::ApplyPolicySettings(
}
prefs->SetValue(spellcheck::prefs::kSpellCheckEnable,
- base::MakeUnique<base::Value>(true));
+ std::make_unique<base::Value>(true));
prefs->SetValue(spellcheck::prefs::kSpellCheckForcedDictionaries,
std::move(forced_language_list));
}
diff --git a/chromium/chrome/browser/spellchecker/spellcheck_message_filter_platform_mac_browsertest.cc b/chromium/chrome/browser/spellchecker/spellcheck_message_filter_platform_mac_browsertest.cc
deleted file mode 100644
index 6db6c0684c6..00000000000
--- a/chromium/chrome/browser/spellchecker/spellcheck_message_filter_platform_mac_browsertest.cc
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/spellcheck/browser/spellcheck_message_filter_platform.h"
-
-#include <memory>
-#include <tuple>
-#include <vector>
-
-#include "base/command_line.h"
-#include "base/memory/ptr_util.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/stl_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "components/spellcheck/common/spellcheck_messages.h"
-#include "components/spellcheck/common/spellcheck_result.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-// Fake filter for testing, which stores sent messages and
-// allows verification by the test case.
-class TestingSpellCheckMessageFilter : public SpellCheckMessageFilterPlatform {
- public:
- explicit TestingSpellCheckMessageFilter(base::OnceClosure&& quit_closure)
- : SpellCheckMessageFilterPlatform(0),
- quit_closure_(std::move(quit_closure)) {}
-
- bool Send(IPC::Message* message) override {
- sent_messages_.push_back(base::WrapUnique(message));
- main_thread_task_runner_->PostTask(FROM_HERE, std::move(quit_closure_));
- return true;
- }
-
- std::vector<std::unique_ptr<IPC::Message>> sent_messages_;
- const scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_ =
- base::ThreadTaskRunnerHandle::Get();
- base::OnceClosure quit_closure_;
-
- private:
- ~TestingSpellCheckMessageFilter() override {}
-};
-
-typedef InProcessBrowserTest SpellCheckMessageFilterPlatformMacBrowserTest;
-
-// Uses browsertest to setup chrome threads.
-IN_PROC_BROWSER_TEST_F(SpellCheckMessageFilterPlatformMacBrowserTest,
- SpellCheckReturnMessage) {
- base::RunLoop run_loop;
- scoped_refptr<TestingSpellCheckMessageFilter> target(
- new TestingSpellCheckMessageFilter(run_loop.QuitWhenIdleClosure()));
-
- SpellCheckHostMsg_RequestTextCheck to_be_received(123, 456,
- base::UTF8ToUTF16("zz."));
- target->OnMessageReceived(to_be_received);
-
- run_loop.Run();
- ASSERT_EQ(1U, target->sent_messages_.size());
-
- SpellCheckMsg_RespondTextCheck::Param params;
- bool ok = SpellCheckMsg_RespondTextCheck::Read(
- target->sent_messages_[0].get(), &params);
- EXPECT_TRUE(ok);
-
- std::vector<SpellCheckResult> sent_results = std::get<2>(params);
- ASSERT_EQ(1U, sent_results.size());
- EXPECT_EQ(sent_results[0].location, 0);
- EXPECT_EQ(sent_results[0].length, 2);
- EXPECT_EQ(sent_results[0].decoration, SpellCheckResult::SPELLING);
-}
diff --git a/chromium/chrome/browser/spellchecker/spellcheck_message_filter_platform_mac_unittest.cc b/chromium/chrome/browser/spellchecker/spellcheck_message_filter_platform_mac_unittest.cc
deleted file mode 100644
index 1ad7b2b13a0..00000000000
--- a/chromium/chrome/browser/spellchecker/spellcheck_message_filter_platform_mac_unittest.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/spellcheck/browser/spellcheck_message_filter_platform.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "base/macros.h"
-#include "base/strings/utf_string_conversions.h"
-#include "components/spellcheck/common/spellcheck_messages.h"
-#include "components/spellcheck/common/spellcheck_result.h"
-#include "content/public/browser/browser_thread.h"
-#include "ipc/ipc_message.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-TEST(SpellcheckMessageFilterPlatformMacTest, CombineResults) {
- std::vector<SpellCheckResult> local_results;
- std::vector<SpellCheckResult> remote_results;
- base::string16 remote_suggestion = base::ASCIIToUTF16("remote");
- base::string16 local_suggestion = base::ASCIIToUTF16("local");
-
- // Remote-only result - must be flagged as GRAMMAR after combine
- remote_results.push_back(
- SpellCheckResult(SpellCheckResult::SPELLING, 0, 5));
-
- // Local-only result - must be discarded after combine
- local_results.push_back(
- SpellCheckResult(SpellCheckResult::SPELLING, 10, 5));
-
- // local & remote result - must be flagged SPELLING, uses remote suggestion.
- SpellCheckResult result(SpellCheckResult::SPELLING, 20, 5, local_suggestion);
- local_results.push_back(result);
- result.replacements[0] = remote_suggestion;
- remote_results.push_back(result);
-
- SpellCheckMessageFilterPlatform::CombineResults(&remote_results,
- local_results);
-
- ASSERT_EQ(2U, remote_results.size());
- EXPECT_EQ(SpellCheckResult::GRAMMAR, remote_results[0].decoration);
- EXPECT_EQ(0, remote_results[0].location);
- EXPECT_EQ(SpellCheckResult::SPELLING, remote_results[1].decoration);
- EXPECT_EQ(20, remote_results[1].location);
- EXPECT_EQ(remote_suggestion, remote_results[1].replacements[0]);
-}
-
-TEST(SpellCheckMessageFilterPlatformMacTest, TestOverrideThread) {
- static const uint32_t kSpellcheckMessages[] = {
- SpellCheckHostMsg_RequestTextCheck::ID,
- };
- scoped_refptr<SpellCheckMessageFilterPlatform> filter(
- new SpellCheckMessageFilterPlatform(0));
- content::BrowserThread::ID thread;
- IPC::Message message;
- for (size_t i = 0; i < arraysize(kSpellcheckMessages); ++i) {
- message.SetHeaderValues(
- 0, kSpellcheckMessages[i], IPC::Message::PRIORITY_NORMAL);
- thread = content::BrowserThread::IO;
- filter->OverrideThreadForMessage(message, &thread);
- EXPECT_EQ(content::BrowserThread::UI, thread);
- }
-}
-
-} // namespace
diff --git a/chromium/chrome/browser/spellchecker/spellcheck_service.cc b/chromium/chrome/browser/spellchecker/spellcheck_service.cc
index 2b1399033b6..f83bd7d56d1 100644
--- a/chromium/chrome/browser/spellchecker/spellcheck_service.cc
+++ b/chromium/chrome/browser/spellchecker/spellcheck_service.cc
@@ -13,8 +13,10 @@
#include "base/synchronization/waitable_event.h"
#include "base/values.h"
#include "build/build_config.h"
+#include "chrome/browser/chrome_service.h"
#include "chrome/browser/spellchecker/spellcheck_factory.h"
#include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h"
+#include "chrome/common/constants.mojom.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_member.h"
#include "components/prefs/pref_service.h"
@@ -168,7 +170,7 @@ bool SpellcheckService::SignalStatusEvent(
}
void SpellcheckService::StartRecordingMetrics(bool spellcheck_enabled) {
- metrics_.reset(new SpellCheckHostMetrics());
+ metrics_ = std::make_unique<SpellCheckHostMetrics>();
metrics_->RecordEnabledStats(spellcheck_enabled);
OnUseSpellingServiceChanged();
}
@@ -202,8 +204,10 @@ void SpellcheckService::InitForRenderer(
}
spellcheck::mojom::SpellCheckerPtr spellchecker;
- content::BindInterface(
- content::RenderProcessHost::FromRendererIdentity(renderer_identity),
+ ChromeService::GetInstance()->connector()->BindInterface(
+ service_manager::Identity(chrome::mojom::kRendererServiceName,
+ renderer_identity.user_id(),
+ renderer_identity.instance()),
&spellchecker);
spellchecker->Initialize(std::move(dictionaries), custom_words, enable);
}
@@ -236,7 +240,7 @@ void SpellcheckService::LoadHunspellDictionaries() {
for (const auto& dictionary : dictionaries) {
hunspell_dictionaries_.push_back(
- base::MakeUnique<SpellcheckHunspellDictionary>(
+ std::make_unique<SpellcheckHunspellDictionary>(
dictionary,
content::BrowserContext::GetDefaultStoragePartition(context_)
->GetURLRequestContext(),
@@ -287,8 +291,14 @@ void SpellcheckService::OnCustomDictionaryChanged(
const std::vector<std::string> deletions(change.to_remove().begin(),
change.to_remove().end());
while (!process_hosts.IsAtEnd()) {
+ service_manager::Identity renderer_identity =
+ process_hosts.GetCurrentValue()->GetChildIdentity();
spellcheck::mojom::SpellCheckerPtr spellchecker;
- content::BindInterface(process_hosts.GetCurrentValue(), &spellchecker);
+ ChromeService::GetInstance()->connector()->BindInterface(
+ service_manager::Identity(chrome::mojom::kRendererServiceName,
+ renderer_identity.user_id(),
+ renderer_identity.instance()),
+ &spellchecker);
spellchecker->CustomDictionaryChanged(additions, deletions);
process_hosts.Advance();
}
diff --git a/chromium/chrome/browser/spellchecker/spellcheck_service_browsertest.cc b/chromium/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
index 854b69cccdb..6f310786d47 100644
--- a/chromium/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
+++ b/chromium/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
@@ -18,12 +18,14 @@
#include "base/test/histogram_tester.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
+#include "chrome/browser/chrome_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/spellchecker/spell_check_host_chrome_impl.h"
#include "chrome/browser/spellchecker/spellcheck_factory.h"
#include "chrome/browser/spellchecker/spellcheck_service.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_paths.h"
+#include "chrome/common/constants.mojom.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/prefs/pref_service.h"
@@ -74,13 +76,19 @@ class SpellcheckServiceBrowserTest : public InProcessBrowserTest,
base::SPLIT_WANT_NONEMPTY));
prefs_->Set(spellcheck::prefs::kSpellCheckDictionaries, dictionaries_value);
+ service_manager::Identity renderer_identity = renderer_->GetChildIdentity();
SpellcheckService* spellcheck =
- SpellcheckServiceFactory::GetForRenderer(renderer_->GetChildIdentity());
+ SpellcheckServiceFactory::GetForRenderer(renderer_identity);
ASSERT_NE(nullptr, spellcheck);
- // Override |renderer_| requests for the spellcheck::mojom::SpellChecker
+ // Override requests for the spellcheck::mojom::SpellChecker
// interface so we can test the SpellChecker request flow.
- renderer_->OverrideBinderForTesting(
+ service_manager::Connector::TestApi test_api(
+ ChromeService::GetInstance()->connector());
+ test_api.OverrideBinderForTesting(
+ service_manager::Identity(chrome::mojom::kRendererServiceName,
+ renderer_identity.user_id(),
+ renderer_identity.instance()),
spellcheck::mojom::SpellChecker::Name_,
base::BindRepeating(&SpellcheckServiceBrowserTest::Bind,
base::Unretained(this)));
diff --git a/chromium/chrome/browser/ui/BUILD.gn b/chromium/chrome/browser/ui/BUILD.gn
index 1a43eba4852..e89ff1f97be 100644
--- a/chromium/chrome/browser/ui/BUILD.gn
+++ b/chromium/chrome/browser/ui/BUILD.gn
@@ -8,7 +8,7 @@ import("//build/config/ui.gni")
import("//build/split_static_library.gni")
import("//chrome/common/features.gni")
import("//components/nacl/features.gni")
-import("//components/offline_pages/features/features.gni")
+import("//components/offline_pages/buildflags/features.gni")
import("//components/signin/features.gni")
import("//extensions/features/features.gni")
import("//media/media_options.gni")
@@ -38,6 +38,579 @@ split_static_library("ui") {
split_count = 1
}
+ # The cocoa browser (ie, primary ui) sources. These are in a separate
+ # variable temporarily to ease the work on https://crbug.com/804950 and
+ # https://crbug.com/802257.
+ # TODO(ellyjones): Remove this variable once 804950 and 802257 are fixed.
+ if (is_mac) {
+ cocoa_browser_sources = [
+ "cocoa/animatable_image.h",
+ "cocoa/animatable_image.mm",
+ "cocoa/animatable_view.h",
+ "cocoa/animatable_view.mm",
+ "cocoa/app_menu/app_menu_button_cell.h",
+ "cocoa/app_menu/app_menu_button_cell.mm",
+ "cocoa/app_menu/app_menu_controller.h",
+ "cocoa/app_menu/app_menu_controller.mm",
+ "cocoa/app_menu/menu_tracked_button.h",
+ "cocoa/app_menu/menu_tracked_button.mm",
+ "cocoa/app_menu/menu_tracked_root_view.h",
+ "cocoa/app_menu/menu_tracked_root_view.mm",
+ "cocoa/app_menu/recent_tabs_menu_model_delegate.h",
+ "cocoa/app_menu/recent_tabs_menu_model_delegate.mm",
+ "cocoa/apps/chrome_app_window_client_views_cocoa.mm",
+ "cocoa/apps/native_app_window_cocoa.h",
+ "cocoa/apps/native_app_window_cocoa.mm",
+ "cocoa/autofill/autofill_bubble_controller.h",
+ "cocoa/autofill/autofill_bubble_controller.mm",
+ "cocoa/autofill/autofill_dialog_constants.h",
+ "cocoa/autofill/autofill_popup_base_view_cocoa.h",
+ "cocoa/autofill/autofill_popup_base_view_cocoa.mm",
+ "cocoa/autofill/autofill_popup_view_bridge.h",
+ "cocoa/autofill/autofill_popup_view_bridge.mm",
+ "cocoa/autofill/autofill_popup_view_cocoa.h",
+ "cocoa/autofill/autofill_popup_view_cocoa.mm",
+ "cocoa/autofill/autofill_tooltip_controller.h",
+ "cocoa/autofill/autofill_tooltip_controller.mm",
+ "cocoa/autofill/credit_card_autofill_touch_bar_controller.h",
+ "cocoa/autofill/credit_card_autofill_touch_bar_controller.mm",
+ "cocoa/autofill/password_generation_popup_view_bridge.h",
+ "cocoa/autofill/password_generation_popup_view_bridge.mm",
+ "cocoa/autofill/password_generation_popup_view_cocoa.h",
+ "cocoa/autofill/password_generation_popup_view_cocoa.mm",
+ "cocoa/autofill/save_card_bubble_view_views.h",
+ "cocoa/autofill/save_card_bubble_view_views.mm",
+ "cocoa/background_gradient_view.h",
+ "cocoa/background_gradient_view.mm",
+ "cocoa/base_bubble_controller.h",
+ "cocoa/base_bubble_controller.mm",
+ "cocoa/bookmarks/bookmark_all_tabs_controller.h",
+ "cocoa/bookmarks/bookmark_all_tabs_controller.mm",
+ "cocoa/bookmarks/bookmark_bar_bridge.h",
+ "cocoa/bookmarks/bookmark_bar_bridge.mm",
+ "cocoa/bookmarks/bookmark_bar_constants.h",
+ "cocoa/bookmarks/bookmark_bar_controller.h",
+ "cocoa/bookmarks/bookmark_bar_controller.mm",
+ "cocoa/bookmarks/bookmark_bar_folder_button_cell.h",
+ "cocoa/bookmarks/bookmark_bar_folder_button_cell.mm",
+ "cocoa/bookmarks/bookmark_bar_folder_controller.h",
+ "cocoa/bookmarks/bookmark_bar_folder_controller.mm",
+ "cocoa/bookmarks/bookmark_bar_folder_hover_state.h",
+ "cocoa/bookmarks/bookmark_bar_folder_hover_state.mm",
+ "cocoa/bookmarks/bookmark_bar_folder_view.h",
+ "cocoa/bookmarks/bookmark_bar_folder_view.mm",
+ "cocoa/bookmarks/bookmark_bar_folder_window.h",
+ "cocoa/bookmarks/bookmark_bar_folder_window.mm",
+ "cocoa/bookmarks/bookmark_bar_state.h",
+ "cocoa/bookmarks/bookmark_bar_toolbar_view.h",
+ "cocoa/bookmarks/bookmark_bar_toolbar_view.mm",
+ "cocoa/bookmarks/bookmark_bar_view_cocoa.h",
+ "cocoa/bookmarks/bookmark_bar_view_cocoa.mm",
+ "cocoa/bookmarks/bookmark_bubble_controller.h",
+ "cocoa/bookmarks/bookmark_bubble_controller.mm",
+ "cocoa/bookmarks/bookmark_bubble_observer_cocoa.h",
+ "cocoa/bookmarks/bookmark_bubble_observer_cocoa.mm",
+ "cocoa/bookmarks/bookmark_button.h",
+ "cocoa/bookmarks/bookmark_button.mm",
+ "cocoa/bookmarks/bookmark_button_cell.h",
+ "cocoa/bookmarks/bookmark_button_cell.mm",
+ "cocoa/bookmarks/bookmark_context_menu_cocoa_controller.h",
+ "cocoa/bookmarks/bookmark_context_menu_cocoa_controller.mm",
+ "cocoa/bookmarks/bookmark_drag_drop_cocoa.mm",
+ "cocoa/bookmarks/bookmark_editor_base_controller.h",
+ "cocoa/bookmarks/bookmark_editor_base_controller.mm",
+ "cocoa/bookmarks/bookmark_editor_controller.h",
+ "cocoa/bookmarks/bookmark_editor_controller.mm",
+ "cocoa/bookmarks/bookmark_folder_target.h",
+ "cocoa/bookmarks/bookmark_folder_target.mm",
+ "cocoa/bookmarks/bookmark_model_observer_for_cocoa.h",
+ "cocoa/bookmarks/bookmark_model_observer_for_cocoa.mm",
+ "cocoa/bookmarks/bookmark_name_folder_controller.h",
+ "cocoa/bookmarks/bookmark_name_folder_controller.mm",
+ "cocoa/bookmarks/bookmark_tree_browser_cell.h",
+ "cocoa/bookmarks/bookmark_tree_browser_cell.mm",
+ "cocoa/browser/exclusive_access_controller_views.h",
+ "cocoa/browser/exclusive_access_controller_views.mm",
+ "cocoa/browser/zoom_bubble_controller.h",
+ "cocoa/browser/zoom_bubble_controller.mm",
+ "cocoa/browser_dialogs_views_mac.cc",
+ "cocoa/browser_dialogs_views_mac.h",
+ "cocoa/browser_window_cocoa.h",
+ "cocoa/browser_window_cocoa.mm",
+ "cocoa/browser_window_cocoa_views_mac.mm",
+ "cocoa/browser_window_controller.h",
+ "cocoa/browser_window_controller.mm",
+ "cocoa/browser_window_controller_private.h",
+ "cocoa/browser_window_controller_private.mm",
+ "cocoa/browser_window_factory_cocoa.mm",
+ "cocoa/browser_window_fullscreen_transition.h",
+ "cocoa/browser_window_fullscreen_transition.mm",
+ "cocoa/browser_window_layout.h",
+ "cocoa/browser_window_layout.mm",
+ "cocoa/browser_window_touch_bar.h",
+ "cocoa/browser_window_touch_bar.mm",
+ "cocoa/browser_window_utils.h",
+ "cocoa/browser_window_utils.mm",
+ "cocoa/bubble_anchor_helper.h",
+ "cocoa/bubble_anchor_helper.mm",
+ "cocoa/bubble_anchor_helper_views.h",
+ "cocoa/bubble_anchor_helper_views.mm",
+ "cocoa/bubble_anchor_util_views_mac.mm",
+ "cocoa/bubble_combobox.h",
+ "cocoa/bubble_combobox.mm",
+ "cocoa/bubble_sync_promo_controller.h",
+ "cocoa/bubble_sync_promo_controller.mm",
+ "cocoa/bubble_view.h",
+ "cocoa/bubble_view.mm",
+ "cocoa/certificate_viewer_mac_cocoa.h",
+ "cocoa/certificate_viewer_mac_cocoa.mm",
+ "cocoa/chrome_browser_window.h",
+ "cocoa/chrome_browser_window.mm",
+ "cocoa/chrome_event_processing_window.h",
+ "cocoa/chrome_event_processing_window.mm",
+ "cocoa/clickhold_button_cell.h",
+ "cocoa/clickhold_button_cell.mm",
+ "cocoa/constrained_web_dialog_delegate_mac.mm",
+ "cocoa/constrained_window/constrained_window_alert.h",
+ "cocoa/constrained_window/constrained_window_alert.mm",
+ "cocoa/constrained_window/constrained_window_button.h",
+ "cocoa/constrained_window/constrained_window_button.mm",
+ "cocoa/constrained_window/constrained_window_control_utils.h",
+ "cocoa/constrained_window/constrained_window_control_utils.mm",
+ "cocoa/constrained_window/constrained_window_custom_sheet.h",
+ "cocoa/constrained_window/constrained_window_custom_sheet.mm",
+ "cocoa/constrained_window/constrained_window_custom_window.h",
+ "cocoa/constrained_window/constrained_window_custom_window.mm",
+ "cocoa/constrained_window/constrained_window_mac.h",
+ "cocoa/constrained_window/constrained_window_mac.mm",
+ "cocoa/constrained_window/constrained_window_sheet.h",
+ "cocoa/constrained_window/constrained_window_sheet_controller.h",
+ "cocoa/constrained_window/constrained_window_sheet_controller.mm",
+ "cocoa/constrained_window/constrained_window_sheet_info.h",
+ "cocoa/constrained_window/constrained_window_sheet_info.mm",
+ "cocoa/constrained_window/constrained_window_web_dialog_sheet.h",
+ "cocoa/constrained_window/constrained_window_web_dialog_sheet.mm",
+ "cocoa/content_settings/blocked_plugin_bubble_controller.h",
+ "cocoa/content_settings/blocked_plugin_bubble_controller.mm",
+ "cocoa/content_settings/collected_cookies_mac.h",
+ "cocoa/content_settings/collected_cookies_mac.mm",
+ "cocoa/content_settings/content_setting_bubble_cocoa.h",
+ "cocoa/content_settings/content_setting_bubble_cocoa.mm",
+ "cocoa/content_settings/cookie_details.h",
+ "cocoa/content_settings/cookie_details.mm",
+ "cocoa/content_settings/cookie_details_view_controller.h",
+ "cocoa/content_settings/cookie_details_view_controller.mm",
+ "cocoa/content_settings/cookie_tree_node.h",
+ "cocoa/content_settings/cookie_tree_node.mm",
+ "cocoa/content_settings/cookies_tree_controller_bridge.h",
+ "cocoa/content_settings/cookies_tree_controller_bridge.mm",
+ "cocoa/create_native_web_modal_manager_cocoa.mm",
+ "cocoa/dev_tools_controller.h",
+ "cocoa/dev_tools_controller.mm",
+ "cocoa/device_chooser_content_view_cocoa.h",
+ "cocoa/device_chooser_content_view_cocoa.mm",
+ "cocoa/dialog_text_field_editor.h",
+ "cocoa/dialog_text_field_editor.mm",
+ "cocoa/download/background_theme.h",
+ "cocoa/download/background_theme.mm",
+ "cocoa/download/download_danger_prompt_impl.cc",
+ "cocoa/download/download_item_button.h",
+ "cocoa/download/download_item_button.mm",
+ "cocoa/download/download_item_cell.h",
+ "cocoa/download/download_item_cell.mm",
+ "cocoa/download/download_item_controller.h",
+ "cocoa/download/download_item_controller.mm",
+ "cocoa/download/download_item_mac.h",
+ "cocoa/download/download_item_mac.mm",
+ "cocoa/download/download_item_view_protocol.h",
+ "cocoa/download/download_shelf_context_menu_controller.h",
+ "cocoa/download/download_shelf_context_menu_controller.mm",
+ "cocoa/download/download_shelf_controller.h",
+ "cocoa/download/download_shelf_controller.mm",
+ "cocoa/download/download_shelf_mac.h",
+ "cocoa/download/download_shelf_mac.mm",
+ "cocoa/download/download_shelf_view_cocoa.h",
+ "cocoa/download/download_shelf_view_cocoa.mm",
+ "cocoa/download/download_show_all_button.h",
+ "cocoa/download/download_show_all_button.mm",
+ "cocoa/download/download_show_all_cell.h",
+ "cocoa/download/download_show_all_cell.mm",
+ "cocoa/download/download_started_animation_mac.mm",
+ "cocoa/download/md_download_item_progress_indicator.h",
+ "cocoa/download/md_download_item_progress_indicator.mm",
+ "cocoa/download/md_download_item_view.h",
+ "cocoa/download/md_download_item_view.mm",
+ "cocoa/download/md_download_item_view_testing.h",
+ "cocoa/drag_util.h",
+ "cocoa/drag_util.mm",
+ "cocoa/draggable_button.h",
+ "cocoa/draggable_button.mm",
+ "cocoa/draggable_button_mixin.h",
+ "cocoa/draggable_button_mixin.mm",
+ "cocoa/extensions/browser_action_button.h",
+ "cocoa/extensions/browser_action_button.mm",
+ "cocoa/extensions/browser_actions_container_view.h",
+ "cocoa/extensions/browser_actions_container_view.mm",
+ "cocoa/extensions/browser_actions_controller.h",
+ "cocoa/extensions/browser_actions_controller.mm",
+ "cocoa/extensions/chooser_dialog_cocoa.h",
+ "cocoa/extensions/chooser_dialog_cocoa.mm",
+ "cocoa/extensions/chooser_dialog_cocoa_controller.h",
+ "cocoa/extensions/chooser_dialog_cocoa_controller.mm",
+ "cocoa/extensions/extension_action_platform_delegate_cocoa.h",
+ "cocoa/extensions/extension_action_platform_delegate_cocoa.mm",
+ "cocoa/extensions/extension_install_dialog_controller.h",
+ "cocoa/extensions/extension_install_dialog_controller.mm",
+ "cocoa/extensions/extension_install_view_controller.h",
+ "cocoa/extensions/extension_install_view_controller.mm",
+ "cocoa/extensions/extension_installed_bubble_controller.h",
+ "cocoa/extensions/extension_installed_bubble_controller.mm",
+ "cocoa/extensions/extension_keybinding_registry_cocoa.h",
+ "cocoa/extensions/extension_keybinding_registry_cocoa.mm",
+ "cocoa/extensions/extension_popup_controller.h",
+ "cocoa/extensions/extension_popup_controller.mm",
+ "cocoa/extensions/extension_popup_views_mac.h",
+ "cocoa/extensions/extension_popup_views_mac.mm",
+ "cocoa/extensions/extension_uninstall_dialog_cocoa.mm",
+ "cocoa/extensions/extension_view_mac.h",
+ "cocoa/extensions/extension_view_mac.mm",
+ "cocoa/extensions/media_galleries_dialog_cocoa.h",
+ "cocoa/extensions/media_galleries_dialog_cocoa.mm",
+ "cocoa/extensions/media_gallery_list_entry_view.h",
+ "cocoa/extensions/media_gallery_list_entry_view.mm",
+ "cocoa/extensions/toolbar_actions_bar_bubble_mac.h",
+ "cocoa/extensions/toolbar_actions_bar_bubble_mac.mm",
+ "cocoa/extensions/toolbar_actions_bar_bubble_views_presenter.h",
+ "cocoa/extensions/toolbar_actions_bar_bubble_views_presenter.mm",
+ "cocoa/extensions/windowed_install_dialog_controller.h",
+ "cocoa/extensions/windowed_install_dialog_controller.mm",
+ "cocoa/external_protocol_dialog.h",
+ "cocoa/external_protocol_dialog_cocoa.mm",
+ "cocoa/external_protocol_dialog_views_mac.mm",
+ "cocoa/fast_resize_view.h",
+ "cocoa/fast_resize_view.mm",
+ "cocoa/find_bar/find_bar_bridge.h",
+ "cocoa/find_bar/find_bar_bridge.mm",
+ "cocoa/find_bar/find_bar_cocoa_controller.h",
+ "cocoa/find_bar/find_bar_cocoa_controller.mm",
+ "cocoa/find_bar/find_bar_text_field.h",
+ "cocoa/find_bar/find_bar_text_field.mm",
+ "cocoa/find_bar/find_bar_text_field_cell.h",
+ "cocoa/find_bar/find_bar_text_field_cell.mm",
+ "cocoa/find_bar/find_bar_view_cocoa.h",
+ "cocoa/find_bar/find_bar_view_cocoa.mm",
+ "cocoa/floating_bar_backing_view.h",
+ "cocoa/floating_bar_backing_view.mm",
+ "cocoa/framed_browser_window.h",
+ "cocoa/framed_browser_window.mm",
+ "cocoa/fullscreen/fullscreen_menubar_tracker.h",
+ "cocoa/fullscreen/fullscreen_menubar_tracker.mm",
+ "cocoa/fullscreen/fullscreen_toolbar_animation_controller.h",
+ "cocoa/fullscreen/fullscreen_toolbar_animation_controller.mm",
+ "cocoa/fullscreen/fullscreen_toolbar_controller.h",
+ "cocoa/fullscreen/fullscreen_toolbar_controller.mm",
+ "cocoa/fullscreen/fullscreen_toolbar_mouse_tracker.h",
+ "cocoa/fullscreen/fullscreen_toolbar_mouse_tracker.mm",
+ "cocoa/fullscreen/fullscreen_toolbar_visibility_lock_controller.h",
+ "cocoa/fullscreen/fullscreen_toolbar_visibility_lock_controller.mm",
+ "cocoa/fullscreen/immersive_fullscreen_controller.h",
+ "cocoa/fullscreen/immersive_fullscreen_controller.mm",
+ "cocoa/fullscreen_placeholder_view.h",
+ "cocoa/fullscreen_placeholder_view.mm",
+ "cocoa/fullscreen_window.h",
+ "cocoa/fullscreen_window.mm",
+ "cocoa/global_error_bubble_controller.h",
+ "cocoa/global_error_bubble_controller.mm",
+ "cocoa/global_error_bubble_controller_views.mm",
+ "cocoa/gradient_button_cell.h",
+ "cocoa/gradient_button_cell.mm",
+ "cocoa/harmony_button.h",
+ "cocoa/harmony_button.mm",
+ "cocoa/has_weak_browser_pointer.h",
+ "cocoa/hover_close_button.h",
+ "cocoa/hover_close_button.mm",
+ "cocoa/hung_renderer_controller.h",
+ "cocoa/hung_renderer_controller.mm",
+ "cocoa/image_button_cell.h",
+ "cocoa/image_button_cell.mm",
+ "cocoa/importer/import_lock_dialog_cocoa.mm",
+ "cocoa/info_bubble_view.h",
+ "cocoa/info_bubble_view.mm",
+ "cocoa/info_bubble_window.h",
+ "cocoa/info_bubble_window.mm",
+ "cocoa/infobars/after_translate_infobar_controller.h",
+ "cocoa/infobars/after_translate_infobar_controller.mm",
+ "cocoa/infobars/alternate_nav_infobar_controller.h",
+ "cocoa/infobars/alternate_nav_infobar_controller.mm",
+ "cocoa/infobars/before_translate_infobar_controller.h",
+ "cocoa/infobars/before_translate_infobar_controller.mm",
+ "cocoa/infobars/confirm_infobar_controller.h",
+ "cocoa/infobars/confirm_infobar_controller.mm",
+ "cocoa/infobars/infobar_cocoa.h",
+ "cocoa/infobars/infobar_cocoa.mm",
+ "cocoa/infobars/infobar_container_cocoa.h",
+ "cocoa/infobars/infobar_container_cocoa.mm",
+ "cocoa/infobars/infobar_container_controller.h",
+ "cocoa/infobars/infobar_container_controller.mm",
+ "cocoa/infobars/infobar_controller.h",
+ "cocoa/infobars/infobar_controller.mm",
+ "cocoa/infobars/infobar_gradient_view.h",
+ "cocoa/infobars/infobar_gradient_view.mm",
+ "cocoa/infobars/infobar_utilities.h",
+ "cocoa/infobars/infobar_utilities.mm",
+ "cocoa/infobars/translate_infobar_base.h",
+ "cocoa/infobars/translate_infobar_base.mm",
+ "cocoa/infobars/translate_message_infobar_controller.h",
+ "cocoa/infobars/translate_message_infobar_controller.mm",
+ "cocoa/javascript_app_modal_dialog_cocoa.h",
+ "cocoa/javascript_app_modal_dialog_cocoa.mm",
+ "cocoa/location_bar/autocomplete_text_field.h",
+ "cocoa/location_bar/autocomplete_text_field.mm",
+ "cocoa/location_bar/autocomplete_text_field_cell.h",
+ "cocoa/location_bar/autocomplete_text_field_cell.mm",
+ "cocoa/location_bar/autocomplete_text_field_editor.h",
+ "cocoa/location_bar/autocomplete_text_field_editor.mm",
+ "cocoa/location_bar/bubble_decoration.h",
+ "cocoa/location_bar/bubble_decoration.mm",
+ "cocoa/location_bar/content_setting_decoration.h",
+ "cocoa/location_bar/content_setting_decoration.mm",
+ "cocoa/location_bar/image_decoration.h",
+ "cocoa/location_bar/image_decoration.mm",
+ "cocoa/location_bar/keyword_hint_decoration.h",
+ "cocoa/location_bar/keyword_hint_decoration.mm",
+ "cocoa/location_bar/location_bar_decoration.h",
+ "cocoa/location_bar/location_bar_decoration.mm",
+ "cocoa/location_bar/location_bar_view_mac.h",
+ "cocoa/location_bar/location_bar_view_mac.mm",
+ "cocoa/location_bar/manage_passwords_decoration.h",
+ "cocoa/location_bar/manage_passwords_decoration.mm",
+ "cocoa/location_bar/page_info_bubble_decoration.h",
+ "cocoa/location_bar/page_info_bubble_decoration.mm",
+ "cocoa/location_bar/save_credit_card_decoration.h",
+ "cocoa/location_bar/save_credit_card_decoration.mm",
+ "cocoa/location_bar/selected_keyword_decoration.h",
+ "cocoa/location_bar/selected_keyword_decoration.mm",
+ "cocoa/location_bar/star_decoration.h",
+ "cocoa/location_bar/star_decoration.mm",
+ "cocoa/location_bar/translate_decoration.h",
+ "cocoa/location_bar/translate_decoration.mm",
+ "cocoa/location_bar/zoom_decoration.h",
+ "cocoa/location_bar/zoom_decoration.mm",
+ "cocoa/login_handler_cocoa.h",
+ "cocoa/login_handler_cocoa.mm",
+ "cocoa/main_menu_item.h",
+ "cocoa/menu_button.h",
+ "cocoa/menu_button.mm",
+ "cocoa/multi_key_equivalent_button.h",
+ "cocoa/multi_key_equivalent_button.mm",
+ "cocoa/new_tab_button.h",
+ "cocoa/new_tab_button_cocoa.mm",
+ "cocoa/nsview_additions.h",
+ "cocoa/nsview_additions.mm",
+ "cocoa/omnibox/omnibox_popup_cell.h",
+ "cocoa/omnibox/omnibox_popup_cell.mm",
+ "cocoa/omnibox/omnibox_popup_matrix.h",
+ "cocoa/omnibox/omnibox_popup_matrix.mm",
+ "cocoa/omnibox/omnibox_popup_separator_view.h",
+ "cocoa/omnibox/omnibox_popup_separator_view.mm",
+ "cocoa/omnibox/omnibox_popup_view_mac.h",
+ "cocoa/omnibox/omnibox_popup_view_mac.mm",
+ "cocoa/omnibox/omnibox_view_mac.h",
+ "cocoa/omnibox/omnibox_view_mac.mm",
+ "cocoa/omnibox_decoration_bubble_controller.h",
+ "cocoa/omnibox_decoration_bubble_controller.mm",
+ "cocoa/one_click_signin_dialog_controller.h",
+ "cocoa/one_click_signin_dialog_controller.mm",
+ "cocoa/one_click_signin_view_controller.h",
+ "cocoa/one_click_signin_view_controller.mm",
+ "cocoa/page_info/page_info_bubble_controller.h",
+ "cocoa/page_info/page_info_bubble_controller.mm",
+ "cocoa/page_info/page_info_utils_cocoa.h",
+ "cocoa/page_info/page_info_utils_cocoa.mm",
+ "cocoa/page_info/permission_selector_button.h",
+ "cocoa/page_info/permission_selector_button.mm",
+ "cocoa/page_info/split_block_button.h",
+ "cocoa/page_info/split_block_button.mm",
+ "cocoa/password_reuse_warning_dialog_cocoa.h",
+ "cocoa/password_reuse_warning_dialog_cocoa.mm",
+ "cocoa/password_reuse_warning_view_controller.h",
+ "cocoa/password_reuse_warning_view_controller.mm",
+ "cocoa/passwords/account_avatar_fetcher_manager.h",
+ "cocoa/passwords/account_avatar_fetcher_manager.mm",
+ "cocoa/passwords/account_chooser_view_controller.h",
+ "cocoa/passwords/account_chooser_view_controller.mm",
+ "cocoa/passwords/auto_signin_view_controller.h",
+ "cocoa/passwords/auto_signin_view_controller.mm",
+ "cocoa/passwords/autosignin_prompt_view_controller.h",
+ "cocoa/passwords/autosignin_prompt_view_controller.mm",
+ "cocoa/passwords/base_passwords_content_view_controller.h",
+ "cocoa/passwords/base_passwords_content_view_controller.mm",
+ "cocoa/passwords/confirmation_password_saved_view_controller.h",
+ "cocoa/passwords/confirmation_password_saved_view_controller.mm",
+ "cocoa/passwords/credential_item_button.h",
+ "cocoa/passwords/credential_item_button.mm",
+ "cocoa/passwords/credentials_selection_view_cocoa.h",
+ "cocoa/passwords/credentials_selection_view_cocoa.mm",
+ "cocoa/passwords/manage_passwords_view_controller.h",
+ "cocoa/passwords/manage_passwords_view_controller.mm",
+ "cocoa/passwords/password_item_views.h",
+ "cocoa/passwords/password_prompt_bridge_interface.h",
+ "cocoa/passwords/password_prompt_view_bridge.h",
+ "cocoa/passwords/password_prompt_view_bridge.mm",
+ "cocoa/passwords/password_prompt_views_mac.mm",
+ "cocoa/passwords/passwords_bubble_cocoa.h",
+ "cocoa/passwords/passwords_bubble_cocoa.mm",
+ "cocoa/passwords/passwords_bubble_controller.h",
+ "cocoa/passwords/passwords_bubble_controller.mm",
+ "cocoa/passwords/passwords_bubble_utils.h",
+ "cocoa/passwords/passwords_bubble_utils.mm",
+ "cocoa/passwords/passwords_list_view_controller.h",
+ "cocoa/passwords/passwords_list_view_controller.mm",
+ "cocoa/passwords/pending_password_view_controller.h",
+ "cocoa/passwords/pending_password_view_controller.mm",
+ "cocoa/passwords/save_pending_password_view_controller.h",
+ "cocoa/passwords/save_pending_password_view_controller.mm",
+ "cocoa/passwords/signin_promo_view_controller.h",
+ "cocoa/passwords/signin_promo_view_controller.mm",
+ "cocoa/passwords/update_pending_password_view_controller.h",
+ "cocoa/passwords/update_pending_password_view_controller.mm",
+ "cocoa/permission_bubble/chooser_bubble_ui_cocoa.h",
+ "cocoa/permission_bubble/chooser_bubble_ui_cocoa.mm",
+ "cocoa/permission_bubble/chooser_bubble_ui_views_mac.mm",
+ "cocoa/permission_bubble/permission_prompt_impl_views_mac.mm",
+ "cocoa/profiles/avatar_base_controller.h",
+ "cocoa/profiles/avatar_base_controller.mm",
+ "cocoa/profiles/avatar_button.h",
+ "cocoa/profiles/avatar_button_cocoa.mm",
+ "cocoa/profiles/avatar_button_controller.h",
+ "cocoa/profiles/avatar_button_controller.mm",
+ "cocoa/profiles/avatar_icon_controller.h",
+ "cocoa/profiles/avatar_icon_controller.mm",
+ "cocoa/profiles/profile_chooser_bridge_views.h",
+ "cocoa/profiles/profile_chooser_bridge_views.mm",
+ "cocoa/profiles/profile_chooser_controller.h",
+ "cocoa/profiles/profile_chooser_controller.mm",
+ "cocoa/profiles/profile_signin_confirmation_dialog_cocoa.h",
+ "cocoa/profiles/profile_signin_confirmation_dialog_cocoa.mm",
+ "cocoa/profiles/profile_signin_confirmation_view_controller.h",
+ "cocoa/profiles/profile_signin_confirmation_view_controller.mm",
+ "cocoa/profiles/signin_view_controller_delegate_mac.h",
+ "cocoa/profiles/signin_view_controller_delegate_mac.mm",
+ "cocoa/profiles/user_manager_mac.h",
+ "cocoa/profiles/user_manager_mac.mm",
+ "cocoa/rect_path_utils.h",
+ "cocoa/rect_path_utils.mm",
+ "cocoa/restart_browser.h",
+ "cocoa/restart_browser.mm",
+ "cocoa/screen_capture_notification_ui_cocoa.h",
+ "cocoa/screen_capture_notification_ui_cocoa.mm",
+ "cocoa/separate_fullscreen_window.h",
+ "cocoa/separate_fullscreen_window.mm",
+ "cocoa/session_crashed_bubble.mm",
+ "cocoa/simple_message_box_bridge_views.mm",
+ "cocoa/simple_message_box_cocoa.h",
+ "cocoa/simple_message_box_cocoa.mm",
+ "cocoa/single_web_contents_dialog_manager_cocoa.h",
+ "cocoa/single_web_contents_dialog_manager_cocoa.mm",
+ "cocoa/spinner_view.h",
+ "cocoa/spinner_view.mm",
+ "cocoa/ssl_client_certificate_selector_cocoa.h",
+ "cocoa/ssl_client_certificate_selector_cocoa.mm",
+ "cocoa/status_bubble_mac.h",
+ "cocoa/status_bubble_mac.mm",
+ "cocoa/styled_text_field.h",
+ "cocoa/styled_text_field.mm",
+ "cocoa/styled_text_field_cell.h",
+ "cocoa/styled_text_field_cell.mm",
+ "cocoa/subresource_filter/subresource_filter_bubble_controller.h",
+ "cocoa/subresource_filter/subresource_filter_bubble_controller.mm",
+ "cocoa/tab_contents/favicon_util_mac.h",
+ "cocoa/tab_contents/favicon_util_mac.mm",
+ "cocoa/tab_contents/overlayable_contents_controller.h",
+ "cocoa/tab_contents/overlayable_contents_controller.mm",
+ "cocoa/tab_contents/sad_tab_mac.mm",
+ "cocoa/tab_contents/sad_tab_view_cocoa.h",
+ "cocoa/tab_contents/sad_tab_view_cocoa.mm",
+ "cocoa/tab_contents/tab_contents_controller.h",
+ "cocoa/tab_contents/tab_contents_controller.mm",
+ "cocoa/tab_dialogs_cocoa.h",
+ "cocoa/tab_dialogs_cocoa.mm",
+ "cocoa/tab_dialogs_views_mac.h",
+ "cocoa/tab_dialogs_views_mac.mm",
+ "cocoa/tab_modal_confirm_dialog_mac.h",
+ "cocoa/tab_modal_confirm_dialog_mac.mm",
+ "cocoa/tabbed_browser_window.h",
+ "cocoa/tabbed_browser_window.mm",
+ "cocoa/tabs/alert_indicator_button_cocoa.h",
+ "cocoa/tabs/alert_indicator_button_cocoa.mm",
+ "cocoa/tabs/tab_controller.h",
+ "cocoa/tabs/tab_controller.mm",
+ "cocoa/tabs/tab_controller_target.h",
+ "cocoa/tabs/tab_favicon_view.h",
+ "cocoa/tabs/tab_favicon_view.mm",
+ "cocoa/tabs/tab_spinner_view.h",
+ "cocoa/tabs/tab_spinner_view.mm",
+ "cocoa/tabs/tab_strip_background_view.h",
+ "cocoa/tabs/tab_strip_background_view.mm",
+ "cocoa/tabs/tab_strip_controller.h",
+ "cocoa/tabs/tab_strip_controller.mm",
+ "cocoa/tabs/tab_strip_drag_controller.h",
+ "cocoa/tabs/tab_strip_drag_controller.mm",
+ "cocoa/tabs/tab_strip_model_observer_bridge.h",
+ "cocoa/tabs/tab_strip_model_observer_bridge.mm",
+ "cocoa/tabs/tab_strip_view.h",
+ "cocoa/tabs/tab_strip_view.mm",
+ "cocoa/tabs/tab_view.h",
+ "cocoa/tabs/tab_view.mm",
+ "cocoa/tabs/tab_window_controller.h",
+ "cocoa/tabs/tab_window_controller.mm",
+ "cocoa/themed_window.h",
+ "cocoa/themed_window.mm",
+ "cocoa/toolbar/app_toolbar_button.h",
+ "cocoa/toolbar/app_toolbar_button.mm",
+ "cocoa/toolbar/app_toolbar_button_cell.h",
+ "cocoa/toolbar/app_toolbar_button_cell.mm",
+ "cocoa/toolbar/back_forward_menu_controller.h",
+ "cocoa/toolbar/back_forward_menu_controller.mm",
+ "cocoa/toolbar/media_router_action_platform_delegate_cocoa.h",
+ "cocoa/toolbar/media_router_action_platform_delegate_cocoa.mm",
+ "cocoa/toolbar/reload_button_cocoa.h",
+ "cocoa/toolbar/reload_button_cocoa.mm",
+ "cocoa/toolbar/toolbar_button_cocoa.h",
+ "cocoa/toolbar/toolbar_button_cocoa.mm",
+ "cocoa/toolbar/toolbar_controller.h",
+ "cocoa/toolbar/toolbar_controller.mm",
+ "cocoa/toolbar/toolbar_view_cocoa.h",
+ "cocoa/toolbar/toolbar_view_cocoa.mm",
+ "cocoa/translate/translate_bubble_bridge_views.h",
+ "cocoa/translate/translate_bubble_bridge_views.mm",
+ "cocoa/translate/translate_bubble_controller.h",
+ "cocoa/translate/translate_bubble_controller.mm",
+ "cocoa/url_drop_target.h",
+ "cocoa/url_drop_target.mm",
+ "cocoa/vertical_gradient_view.h",
+ "cocoa/vertical_gradient_view.mm",
+ "cocoa/view_id_util.h",
+ "cocoa/view_id_util.mm",
+ "cocoa/view_resizer.h",
+ "cocoa/web_contents_modal_dialog_manager_views_mac.h",
+ "cocoa/web_contents_modal_dialog_manager_views_mac.mm",
+ "cocoa/web_textfield_touch_bar_controller.h",
+ "cocoa/web_textfield_touch_bar_controller.mm",
+
+ # TODO(estade): this class should be deleted in favor of a combobox model.
+ # See crbug.com/590850
+ "content_settings/content_setting_media_menu_model.cc",
+ "content_settings/content_setting_media_menu_model.h",
+ "javascript_dialogs/javascript_dialog_cocoa.h",
+ "javascript_dialogs/javascript_dialog_cocoa.mm",
+ "javascript_dialogs/javascript_dialog_mac.cc",
+ "proximity_auth/proximity_auth_error_bubble_stub.cc",
+ "startup/session_crashed_infobar_delegate.cc",
+ "startup/session_crashed_infobar_delegate.h",
+ ]
+ }
+
sources = [
"accelerator_utils.h",
"app_list/app_list_util.cc",
@@ -49,6 +622,8 @@ split_static_library("ui") {
"autofill/autofill_popup_controller.h",
"autofill/autofill_popup_controller_impl.cc",
"autofill/autofill_popup_controller_impl.h",
+ "autofill/autofill_popup_controller_impl_mac.h",
+ "autofill/autofill_popup_controller_impl_mac.mm",
"autofill/autofill_popup_layout_model.cc",
"autofill/autofill_popup_layout_model.h",
"autofill/autofill_popup_view.h",
@@ -342,7 +917,7 @@ split_static_library("ui") {
deps = [
"//base",
"//base:i18n",
- "//base/allocator:features",
+ "//base/allocator:buildflags",
"//cc/paint",
"//chrome:extra_resources",
"//chrome:resources",
@@ -375,6 +950,7 @@ split_static_library("ui") {
"//components/browsing_data/core",
"//components/bubble:bubble",
"//components/certificate_reporting:cert_logger_proto",
+ "//components/consent_auditor/",
"//components/content_settings/core/browser",
"//components/crx_file",
"//components/data_reduction_proxy/core/browser",
@@ -387,7 +963,7 @@ split_static_library("ui") {
"//components/encrypted_messages:encrypted_message_proto",
"//components/favicon/content",
"//components/favicon/core",
- "//components/feature_engagement:features",
+ "//components/feature_engagement:buildflags",
"//components/feedback",
"//components/flags_ui",
"//components/gcm_driver",
@@ -399,11 +975,12 @@ split_static_library("ui") {
"//components/invalidation/impl",
"//components/keyed_service/content",
"//components/keyed_service/core",
+ "//components/language/core/common",
"//components/navigation_metrics",
"//components/net_log",
"//components/ntp_snippets",
"//components/ntp_tiles",
- "//components/offline_pages/features:features",
+ "//components/offline_pages/buildflags",
"//components/omnibox/browser",
"//components/onc",
"//components/password_manager/content/browser",
@@ -442,7 +1019,6 @@ split_static_library("ui") {
"//components/strings",
"//components/subresource_filter/content/browser",
"//components/subresource_filter/core/browser:browser",
- "//components/suggestions/proto",
"//components/supervised_user_error_page",
"//components/sync",
"//components/sync_preferences",
@@ -481,9 +1057,9 @@ split_static_library("ui") {
"//skia",
"//storage/browser",
"//storage/common",
- "//third_party/WebKit/common:blink_common",
"//third_party/WebKit/public:features",
"//third_party/WebKit/public:resources",
+ "//third_party/WebKit/public/common",
"//third_party/adobe/flash:flapper_version_h",
"//third_party/brotli:dec",
"//third_party/cacheinvalidation",
@@ -588,6 +1164,8 @@ split_static_library("ui") {
"android/infobars/infobar_android.h",
"android/infobars/infobar_container_android.cc",
"android/infobars/infobar_container_android.h",
+ "android/infobars/installable_ambient_badge_infobar.cc",
+ "android/infobars/installable_ambient_badge_infobar.h",
"android/infobars/instant_apps_infobar.cc",
"android/infobars/instant_apps_infobar.h",
"android/infobars/near_oom_infobar.cc",
@@ -670,7 +1248,7 @@ split_static_library("ui") {
"//crypto:platform",
"//device/usb/mojo",
"//device/usb/public/cpp",
- "//device/usb/public/interfaces",
+ "//device/usb/public/mojom",
"//device/vr/features",
"//ui/android",
]
@@ -784,8 +1362,6 @@ split_static_library("ui") {
"fast_unload_controller.h",
"find_bar/find_bar_controller.cc",
"find_bar/find_bar_controller.h",
- "first_run_bubble_presenter.cc",
- "first_run_bubble_presenter.h",
"global_error/global_error.cc",
"global_error/global_error.h",
"global_error/global_error_bubble_view_base.h",
@@ -793,6 +1369,8 @@ split_static_library("ui") {
"global_error/global_error_service.h",
"global_error/global_error_service_factory.cc",
"global_error/global_error_service_factory.h",
+ "hung_renderer/hung_renderer_core.cc",
+ "hung_renderer/hung_renderer_core.h",
"infobar_container_delegate.cc",
"infobar_container_delegate.h",
"javascript_dialogs/javascript_dialog_views.cc",
@@ -805,6 +1383,7 @@ split_static_library("ui") {
"media_router/presentation_receiver_window_controller.cc",
"media_router/presentation_receiver_window_controller.h",
"media_router/presentation_receiver_window_delegate.h",
+ "media_router/presentation_receiver_window_factory_mac.cc",
"native_window_tracker.h",
"omnibox/alternate_nav_infobar_delegate.cc",
"omnibox/alternate_nav_infobar_delegate.h",
@@ -855,10 +1434,8 @@ split_static_library("ui") {
"scoped_tabbed_browser_displayer.h",
"search/instant_controller.cc",
"search/instant_controller.h",
- "search/new_tab_page_interceptor_service.cc",
- "search/new_tab_page_interceptor_service.h",
- "search/new_tab_page_interceptor_service_factory.cc",
- "search/new_tab_page_interceptor_service_factory.h",
+ "search/new_tab_page_navigation_throttle.cc",
+ "search/new_tab_page_navigation_throttle.h",
"search/ntp_user_data_logger.cc",
"search/ntp_user_data_logger.h",
"search/search_ipc_router.cc",
@@ -907,9 +1484,6 @@ split_static_library("ui") {
"tabs/tab_change_type.h",
"tabs/tab_menu_model.cc",
"tabs/tab_menu_model.h",
- "tabs/tab_metrics_logger.h",
- "tabs/tab_metrics_logger_impl.cc",
- "tabs/tab_metrics_logger_impl.h",
"tabs/tab_network_state.cc",
"tabs/tab_network_state.h",
"tabs/tab_strip_model.cc",
@@ -966,8 +1540,6 @@ split_static_library("ui") {
"unload_controller.h",
"unload_controller_web_contents_delegate.cc",
"unload_controller_web_contents_delegate.h",
- "user_manager.cc",
- "user_manager.h",
"webui/app_launcher_login_handler.cc",
"webui/app_launcher_login_handler.h",
"webui/bookmarks_ui.cc",
@@ -1033,6 +1605,7 @@ split_static_library("ui") {
"webui/media_router/media_sink_with_cast_modes.h",
"webui/media_router/query_result_manager.cc",
"webui/media_router/query_result_manager.h",
+ "webui/media_router/web_contents_display_observer.h",
"webui/ntp/app_icon_webui_handler.cc",
"webui/ntp/app_icon_webui_handler.h",
"webui/ntp/app_launcher_handler.cc",
@@ -1055,8 +1628,6 @@ split_static_library("ui") {
"webui/policy_tool_ui.h",
"webui/policy_tool_ui_handler.cc",
"webui/policy_tool_ui_handler.h",
- "webui/profile_helper.cc",
- "webui/profile_helper.h",
"webui/profile_info_watcher.cc",
"webui/profile_info_watcher.h",
"webui/set_as_default_browser_ui_win.cc",
@@ -1153,8 +1724,8 @@ split_static_library("ui") {
"//chrome/browser:theme_properties",
"//chrome/browser/media/router",
"//chrome/browser/profile_resetter:profile_reset_report_proto",
- "//chrome/browser/ui/tabs:tab_metrics_event_proto",
- "//chrome/common:features",
+ "//chrome/browser/resource_coordinator:tab_metrics_event_proto",
+ "//chrome/common:buildflags",
"//chrome/common:search_mojom",
"//components/feedback/proto",
"//components/keep_alive_registry",
@@ -1166,8 +1737,9 @@ split_static_library("ui") {
"//components/web_modal",
"//components/zoom",
"//device/bluetooth",
- "//mash/public/interfaces",
- "//services/device/public/interfaces",
+ "//mash/public/mojom",
+ "//services/device/public/cpp:device_features",
+ "//services/device/public/mojom",
"//services/metrics/public/cpp:metrics_cpp",
"//third_party/libaddressinput",
"//third_party/libaddressinput:strings",
@@ -1222,6 +1794,7 @@ split_static_library("ui") {
"ash/chrome_new_window_client.h",
"ash/chrome_screenshot_grabber.cc",
"ash/chrome_screenshot_grabber.h",
+ "ash/chrome_screenshot_grabber_test_observer.h",
"ash/chrome_shell_content_state.cc",
"ash/chrome_shell_content_state.h",
"ash/chrome_shell_content_state_chromeos.cc",
@@ -1231,6 +1804,8 @@ split_static_library("ui") {
"ash/ime_controller_client.h",
"ash/keyboard_ui_service.cc",
"ash/keyboard_ui_service.h",
+ "ash/ksv/keyboard_shortcut_viewer_util.cc",
+ "ash/ksv/keyboard_shortcut_viewer_util.h",
"ash/launcher/app_shortcut_launcher_item_controller.cc",
"ash/launcher/app_shortcut_launcher_item_controller.h",
"ash/launcher/app_window_launcher_controller.cc",
@@ -1337,8 +1912,6 @@ split_static_library("ui") {
"views/frame/browser_frame_header_ash.h",
"views/frame/browser_non_client_frame_view_ash.cc",
"views/frame/browser_non_client_frame_view_ash.h",
- "views/frame/hosted_app_frame_header_ash.cc",
- "views/frame/hosted_app_frame_header_ash.h",
"views/frame/immersive_context_mus.cc",
"views/frame/immersive_context_mus.h",
"views/frame/immersive_handler_factory_mus.cc",
@@ -1538,13 +2111,16 @@ split_static_library("ui") {
deps += [
"//ash",
"//ash:ash_with_content",
+ "//ash/components/shortcut_viewer:ksv",
"//ash/public/cpp",
+ "//ash/public/cpp/vector_icons",
"//ash/resources/vector_icons",
"//ash/strings",
"//chrome/browser/chromeos",
"//chromeos:cryptohome_proto",
"//chromeos/components/tether",
"//components/arc",
+ "//components/consent_auditor:consent_auditor",
"//components/cryptauth",
"//components/drive:drive_chromeos",
"//components/exo",
@@ -1553,7 +2129,8 @@ split_static_library("ui") {
"//components/session_manager/core",
"//components/user_manager",
"//services/data_decoder/public/cpp",
- "//services/device/public/interfaces",
+ "//services/device/public/cpp:device_features",
+ "//services/device/public/mojom",
"//services/ui/public/cpp",
"//services/ui/public/interfaces",
"//ui/app_list/presenter",
@@ -1612,17 +2189,22 @@ split_static_library("ui") {
"sync/one_click_signin_sync_observer.h",
"sync/one_click_signin_sync_starter.cc",
"sync/one_click_signin_sync_starter.h",
+ "user_manager.cc",
+ "user_manager.h",
"views/close_bubble_on_tab_activation_helper.cc",
"views/close_bubble_on_tab_activation_helper.h",
"views/external_protocol_dialog.cc",
"views/external_protocol_dialog.h",
- "views/profiles/avatar_button_style.h",
"views/profiles/badged_profile_photo.cc",
"views/profiles/badged_profile_photo.h",
+ "views/profiles/dice_accounts_menu.cc",
+ "views/profiles/dice_accounts_menu.h",
"views/profiles/profile_chooser_view.cc",
"views/profiles/profile_chooser_view.h",
"webui/app_launcher_page_ui.cc",
"webui/app_launcher_page_ui.h",
+ "webui/profile_helper.cc",
+ "webui/profile_helper.h",
"webui/settings/settings_default_browser_handler.cc",
"webui/settings/settings_default_browser_handler.h",
"webui/settings/system_handler.cc",
@@ -1665,20 +2247,19 @@ split_static_library("ui") {
if (!is_mac || mac_views_browser) {
sources += [
- "views/frame/avatar_button_manager.cc",
- "views/frame/avatar_button_manager.h",
"views/profiles/avatar_button.cc",
"views/profiles/avatar_button.h",
+ "views/profiles/user_manager_view.cc",
+ "views/profiles/user_manager_view.h",
]
- deps += [ "//ui/views:features" ]
}
if (enable_dice_support) {
sources += [
"webui/signin/dice_turn_sync_on_helper.cc",
"webui/signin/dice_turn_sync_on_helper.h",
- "webui/signin/signin_dice_internals_handler.cc",
- "webui/signin/signin_dice_internals_handler.h",
+ "webui/signin/dice_turn_sync_on_helper_delegate_impl.cc",
+ "webui/signin/dice_turn_sync_on_helper_delegate_impl.h",
]
}
}
@@ -1758,6 +2339,7 @@ split_static_library("ui") {
"cocoa/bookmarks/bookmark_menu_cocoa_controller.mm",
"cocoa/browser_window_command_handler.h",
"cocoa/browser_window_command_handler.mm",
+ "cocoa/browser_window_views_mac.h",
"cocoa/chrome_command_dispatcher_delegate.h",
"cocoa/chrome_command_dispatcher_delegate.mm",
"cocoa/chrome_style.cc",
@@ -1848,6 +2430,9 @@ split_static_library("ui") {
"webui/settings_utils_mac.mm",
"window_sizer/window_sizer_mac.mm",
]
+
+ sources += cocoa_browser_sources
+
deps += [
"//chrome/app/nibs:localizer_table",
"//chrome/browser/apps/app_shim",
@@ -1872,6 +2457,8 @@ split_static_library("ui") {
"views/dropdown_bar_host_mac.mm",
"views/frame/browser_frame_mac.h",
"views/frame/browser_frame_mac.mm",
+ "views/frame/browser_native_widget_window_mac.h",
+ "views/frame/browser_native_widget_window_mac.mm",
"views/frame/browser_non_client_frame_view_factory_mac.mm",
"views/frame/browser_non_client_frame_view_mac.h",
"views/frame/browser_non_client_frame_view_mac.mm",
@@ -1882,586 +2469,49 @@ split_static_library("ui") {
"views/tabs/window_finder_mac.mm",
]
deps += [ "//extensions/components/native_app_window" ]
- } else {
- sources += [
- "cocoa/animatable_image.h",
- "cocoa/animatable_image.mm",
- "cocoa/animatable_view.h",
- "cocoa/animatable_view.mm",
- "cocoa/app_menu/app_menu_button_cell.h",
- "cocoa/app_menu/app_menu_button_cell.mm",
- "cocoa/app_menu/app_menu_controller.h",
- "cocoa/app_menu/app_menu_controller.mm",
- "cocoa/app_menu/menu_tracked_button.h",
- "cocoa/app_menu/menu_tracked_button.mm",
- "cocoa/app_menu/menu_tracked_root_view.h",
- "cocoa/app_menu/menu_tracked_root_view.mm",
- "cocoa/app_menu/recent_tabs_menu_model_delegate.h",
- "cocoa/app_menu/recent_tabs_menu_model_delegate.mm",
- "cocoa/apps/chrome_app_window_client_views_cocoa.mm",
- "cocoa/apps/native_app_window_cocoa.h",
- "cocoa/apps/native_app_window_cocoa.mm",
- "cocoa/autofill/autofill_bubble_controller.h",
- "cocoa/autofill/autofill_bubble_controller.mm",
- "cocoa/autofill/autofill_dialog_constants.h",
- "cocoa/autofill/autofill_input_field.h",
- "cocoa/autofill/autofill_layout.h",
- "cocoa/autofill/autofill_pop_up_button.h",
- "cocoa/autofill/autofill_pop_up_button.mm",
- "cocoa/autofill/autofill_popup_base_view_cocoa.h",
- "cocoa/autofill/autofill_popup_base_view_cocoa.mm",
- "cocoa/autofill/autofill_popup_view_bridge.h",
- "cocoa/autofill/autofill_popup_view_bridge.mm",
- "cocoa/autofill/autofill_popup_view_cocoa.h",
- "cocoa/autofill/autofill_popup_view_cocoa.mm",
- "cocoa/autofill/autofill_textfield.h",
- "cocoa/autofill/autofill_textfield.mm",
- "cocoa/autofill/autofill_tooltip_controller.h",
- "cocoa/autofill/autofill_tooltip_controller.mm",
- "cocoa/autofill/card_unmask_prompt_view_bridge.h",
- "cocoa/autofill/card_unmask_prompt_view_bridge.mm",
- "cocoa/autofill/card_unmask_prompt_view_views.mm",
- "cocoa/autofill/layout_view.h",
- "cocoa/autofill/layout_view.mm",
- "cocoa/autofill/password_generation_popup_view_bridge.h",
- "cocoa/autofill/password_generation_popup_view_bridge.mm",
- "cocoa/autofill/password_generation_popup_view_cocoa.h",
- "cocoa/autofill/password_generation_popup_view_cocoa.mm",
- "cocoa/autofill/save_card_bubble_view_bridge.h",
- "cocoa/autofill/save_card_bubble_view_bridge.mm",
- "cocoa/autofill/save_card_bubble_view_views.h",
- "cocoa/autofill/save_card_bubble_view_views.mm",
- "cocoa/autofill/simple_grid_layout.h",
- "cocoa/autofill/simple_grid_layout.mm",
- "cocoa/background_gradient_view.h",
- "cocoa/background_gradient_view.mm",
- "cocoa/base_bubble_controller.h",
- "cocoa/base_bubble_controller.mm",
+
+ # Truly cocoa-browser-specific sources. These are secondary UI pieces that
+ # are obsolete before mac_views_browser will ever ship, so they aren't
+ # linked in at all.
+ sources -= [
"cocoa/bookmarks/bookmark_all_tabs_controller.h",
"cocoa/bookmarks/bookmark_all_tabs_controller.mm",
- "cocoa/bookmarks/bookmark_bar_bridge.h",
- "cocoa/bookmarks/bookmark_bar_bridge.mm",
- "cocoa/bookmarks/bookmark_bar_constants.h",
- "cocoa/bookmarks/bookmark_bar_controller.h",
- "cocoa/bookmarks/bookmark_bar_controller.mm",
- "cocoa/bookmarks/bookmark_bar_folder_button_cell.h",
- "cocoa/bookmarks/bookmark_bar_folder_button_cell.mm",
- "cocoa/bookmarks/bookmark_bar_folder_controller.h",
- "cocoa/bookmarks/bookmark_bar_folder_controller.mm",
- "cocoa/bookmarks/bookmark_bar_folder_hover_state.h",
- "cocoa/bookmarks/bookmark_bar_folder_hover_state.mm",
- "cocoa/bookmarks/bookmark_bar_folder_view.h",
- "cocoa/bookmarks/bookmark_bar_folder_view.mm",
- "cocoa/bookmarks/bookmark_bar_folder_window.h",
- "cocoa/bookmarks/bookmark_bar_folder_window.mm",
- "cocoa/bookmarks/bookmark_bar_state.h",
- "cocoa/bookmarks/bookmark_bar_toolbar_view.h",
- "cocoa/bookmarks/bookmark_bar_toolbar_view.mm",
- "cocoa/bookmarks/bookmark_bar_view_cocoa.h",
- "cocoa/bookmarks/bookmark_bar_view_cocoa.mm",
- "cocoa/bookmarks/bookmark_bubble_controller.h",
- "cocoa/bookmarks/bookmark_bubble_controller.mm",
- "cocoa/bookmarks/bookmark_bubble_observer_cocoa.h",
- "cocoa/bookmarks/bookmark_bubble_observer_cocoa.mm",
- "cocoa/bookmarks/bookmark_button.h",
- "cocoa/bookmarks/bookmark_button.mm",
- "cocoa/bookmarks/bookmark_button_cell.h",
- "cocoa/bookmarks/bookmark_button_cell.mm",
- "cocoa/bookmarks/bookmark_context_menu_cocoa_controller.h",
- "cocoa/bookmarks/bookmark_context_menu_cocoa_controller.mm",
- "cocoa/bookmarks/bookmark_drag_drop_cocoa.mm",
"cocoa/bookmarks/bookmark_editor_base_controller.h",
"cocoa/bookmarks/bookmark_editor_base_controller.mm",
"cocoa/bookmarks/bookmark_editor_controller.h",
"cocoa/bookmarks/bookmark_editor_controller.mm",
- "cocoa/bookmarks/bookmark_folder_target.h",
- "cocoa/bookmarks/bookmark_folder_target.mm",
- "cocoa/bookmarks/bookmark_model_observer_for_cocoa.h",
- "cocoa/bookmarks/bookmark_model_observer_for_cocoa.mm",
- "cocoa/bookmarks/bookmark_name_folder_controller.h",
- "cocoa/bookmarks/bookmark_name_folder_controller.mm",
- "cocoa/bookmarks/bookmark_tree_browser_cell.h",
- "cocoa/bookmarks/bookmark_tree_browser_cell.mm",
- "cocoa/browser/exclusive_access_controller_views.h",
- "cocoa/browser/exclusive_access_controller_views.mm",
- "cocoa/browser/zoom_bubble_controller.h",
- "cocoa/browser/zoom_bubble_controller.mm",
- "cocoa/browser_dialogs_views_mac.cc",
- "cocoa/browser_dialogs_views_mac.h",
- "cocoa/browser_window_cocoa.h",
- "cocoa/browser_window_cocoa.mm",
- "cocoa/browser_window_controller.h",
- "cocoa/browser_window_controller.mm",
- "cocoa/browser_window_controller_private.h",
- "cocoa/browser_window_controller_private.mm",
- "cocoa/browser_window_factory_cocoa.mm",
- "cocoa/browser_window_fullscreen_transition.h",
- "cocoa/browser_window_fullscreen_transition.mm",
- "cocoa/browser_window_layout.h",
- "cocoa/browser_window_layout.mm",
- "cocoa/browser_window_touch_bar.h",
- "cocoa/browser_window_touch_bar.mm",
- "cocoa/browser_window_utils.h",
- "cocoa/browser_window_utils.mm",
- "cocoa/bubble_anchor_helper.h",
- "cocoa/bubble_anchor_helper.mm",
- "cocoa/bubble_anchor_helper_views.h",
- "cocoa/bubble_anchor_helper_views.mm",
- "cocoa/bubble_anchor_util_views_mac.mm",
- "cocoa/bubble_combobox.h",
- "cocoa/bubble_combobox.mm",
- "cocoa/bubble_sync_promo_controller.h",
- "cocoa/bubble_sync_promo_controller.mm",
- "cocoa/bubble_view.h",
- "cocoa/bubble_view.mm",
- "cocoa/certificate_viewer_mac_cocoa.h",
- "cocoa/certificate_viewer_mac_cocoa.mm",
- "cocoa/chrome_browser_window.h",
- "cocoa/chrome_browser_window.mm",
- "cocoa/chrome_event_processing_window.h",
- "cocoa/chrome_event_processing_window.mm",
- "cocoa/clickhold_button_cell.h",
- "cocoa/clickhold_button_cell.mm",
- "cocoa/confirm_bubble_cocoa.h",
- "cocoa/confirm_bubble_cocoa.mm",
- "cocoa/confirm_bubble_controller.h",
- "cocoa/confirm_bubble_controller.mm",
- "cocoa/confirm_bubble_views_mac.mm",
"cocoa/constrained_web_dialog_delegate_mac.mm",
- "cocoa/constrained_window/constrained_window_alert.h",
- "cocoa/constrained_window/constrained_window_alert.mm",
- "cocoa/constrained_window/constrained_window_button.h",
- "cocoa/constrained_window/constrained_window_button.mm",
- "cocoa/constrained_window/constrained_window_control_utils.h",
- "cocoa/constrained_window/constrained_window_control_utils.mm",
- "cocoa/constrained_window/constrained_window_custom_sheet.h",
- "cocoa/constrained_window/constrained_window_custom_sheet.mm",
- "cocoa/constrained_window/constrained_window_custom_window.h",
- "cocoa/constrained_window/constrained_window_custom_window.mm",
- "cocoa/constrained_window/constrained_window_mac.h",
- "cocoa/constrained_window/constrained_window_mac.mm",
- "cocoa/constrained_window/constrained_window_sheet.h",
- "cocoa/constrained_window/constrained_window_sheet_controller.h",
- "cocoa/constrained_window/constrained_window_sheet_controller.mm",
- "cocoa/constrained_window/constrained_window_sheet_info.h",
- "cocoa/constrained_window/constrained_window_sheet_info.mm",
- "cocoa/constrained_window/constrained_window_web_dialog_sheet.h",
- "cocoa/constrained_window/constrained_window_web_dialog_sheet.mm",
- "cocoa/content_settings/blocked_plugin_bubble_controller.h",
- "cocoa/content_settings/blocked_plugin_bubble_controller.mm",
- "cocoa/content_settings/collected_cookies_mac.h",
- "cocoa/content_settings/collected_cookies_mac.mm",
- "cocoa/content_settings/content_setting_bubble_cocoa.h",
- "cocoa/content_settings/content_setting_bubble_cocoa.mm",
- "cocoa/content_settings/cookie_details.h",
- "cocoa/content_settings/cookie_details.mm",
- "cocoa/content_settings/cookie_details_view_controller.h",
- "cocoa/content_settings/cookie_details_view_controller.mm",
- "cocoa/content_settings/cookie_tree_node.h",
- "cocoa/content_settings/cookie_tree_node.mm",
- "cocoa/content_settings/cookies_tree_controller_bridge.h",
- "cocoa/content_settings/cookies_tree_controller_bridge.mm",
- "cocoa/create_native_web_modal_manager_cocoa.mm",
- "cocoa/dev_tools_controller.h",
- "cocoa/dev_tools_controller.mm",
- "cocoa/device_chooser_content_view_cocoa.h",
- "cocoa/device_chooser_content_view_cocoa.mm",
- "cocoa/dialog_text_field_editor.h",
- "cocoa/dialog_text_field_editor.mm",
- "cocoa/download/background_theme.h",
- "cocoa/download/background_theme.mm",
"cocoa/download/download_danger_prompt_impl.cc",
- "cocoa/download/download_item_button.h",
- "cocoa/download/download_item_button.mm",
- "cocoa/download/download_item_cell.h",
- "cocoa/download/download_item_cell.mm",
- "cocoa/download/download_item_controller.h",
- "cocoa/download/download_item_controller.mm",
- "cocoa/download/download_item_mac.h",
- "cocoa/download/download_item_mac.mm",
- "cocoa/download/download_item_view_protocol.h",
- "cocoa/download/download_shelf_context_menu_controller.h",
- "cocoa/download/download_shelf_context_menu_controller.mm",
- "cocoa/download/download_shelf_controller.h",
- "cocoa/download/download_shelf_controller.mm",
- "cocoa/download/download_shelf_mac.h",
- "cocoa/download/download_shelf_mac.mm",
- "cocoa/download/download_shelf_view_cocoa.h",
- "cocoa/download/download_shelf_view_cocoa.mm",
- "cocoa/download/download_show_all_button.h",
- "cocoa/download/download_show_all_button.mm",
- "cocoa/download/download_show_all_cell.h",
- "cocoa/download/download_show_all_cell.mm",
- "cocoa/download/download_started_animation_mac.mm",
- "cocoa/download/md_download_item_progress_indicator.h",
- "cocoa/download/md_download_item_progress_indicator.mm",
- "cocoa/download/md_download_item_view.h",
- "cocoa/download/md_download_item_view.mm",
- "cocoa/download/md_download_item_view_testing.h",
- "cocoa/drag_util.h",
- "cocoa/drag_util.mm",
- "cocoa/draggable_button.h",
- "cocoa/draggable_button.mm",
- "cocoa/draggable_button_mixin.h",
- "cocoa/draggable_button_mixin.mm",
- "cocoa/extensions/browser_action_button.h",
- "cocoa/extensions/browser_action_button.mm",
- "cocoa/extensions/browser_actions_container_view.h",
- "cocoa/extensions/browser_actions_container_view.mm",
- "cocoa/extensions/browser_actions_controller.h",
- "cocoa/extensions/browser_actions_controller.mm",
"cocoa/extensions/chooser_dialog_cocoa.h",
"cocoa/extensions/chooser_dialog_cocoa.mm",
"cocoa/extensions/chooser_dialog_cocoa_controller.h",
"cocoa/extensions/chooser_dialog_cocoa_controller.mm",
- "cocoa/extensions/extension_action_platform_delegate_cocoa.h",
- "cocoa/extensions/extension_action_platform_delegate_cocoa.mm",
- "cocoa/extensions/extension_install_dialog_controller.h",
- "cocoa/extensions/extension_install_dialog_controller.mm",
- "cocoa/extensions/extension_install_view_controller.h",
- "cocoa/extensions/extension_install_view_controller.mm",
"cocoa/extensions/extension_installed_bubble_controller.h",
"cocoa/extensions/extension_installed_bubble_controller.mm",
- "cocoa/extensions/extension_keybinding_registry_cocoa.h",
- "cocoa/extensions/extension_keybinding_registry_cocoa.mm",
- "cocoa/extensions/extension_popup_controller.h",
- "cocoa/extensions/extension_popup_controller.mm",
"cocoa/extensions/extension_uninstall_dialog_cocoa.mm",
- "cocoa/extensions/extension_view_mac.h",
- "cocoa/extensions/extension_view_mac.mm",
- "cocoa/extensions/media_galleries_dialog_cocoa.h",
- "cocoa/extensions/media_galleries_dialog_cocoa.mm",
- "cocoa/extensions/media_gallery_list_entry_view.h",
- "cocoa/extensions/media_gallery_list_entry_view.mm",
- "cocoa/extensions/toolbar_actions_bar_bubble_mac.h",
- "cocoa/extensions/toolbar_actions_bar_bubble_mac.mm",
- "cocoa/extensions/toolbar_actions_bar_bubble_views_presenter.h",
- "cocoa/extensions/toolbar_actions_bar_bubble_views_presenter.mm",
- "cocoa/extensions/windowed_install_dialog_controller.h",
- "cocoa/extensions/windowed_install_dialog_controller.mm",
- "cocoa/external_protocol_dialog.h",
- "cocoa/external_protocol_dialog_cocoa.mm",
"cocoa/external_protocol_dialog_views_mac.mm",
- "cocoa/fast_resize_view.h",
- "cocoa/fast_resize_view.mm",
- "cocoa/find_bar/find_bar_bridge.h",
- "cocoa/find_bar/find_bar_bridge.mm",
- "cocoa/find_bar/find_bar_cocoa_controller.h",
- "cocoa/find_bar/find_bar_cocoa_controller.mm",
- "cocoa/find_bar/find_bar_text_field.h",
- "cocoa/find_bar/find_bar_text_field.mm",
- "cocoa/find_bar/find_bar_text_field_cell.h",
- "cocoa/find_bar/find_bar_text_field_cell.mm",
- "cocoa/find_bar/find_bar_view_cocoa.h",
- "cocoa/find_bar/find_bar_view_cocoa.mm",
- "cocoa/first_run_bubble_controller.h",
- "cocoa/first_run_bubble_controller.mm",
- "cocoa/floating_bar_backing_view.h",
- "cocoa/floating_bar_backing_view.mm",
- "cocoa/framed_browser_window.h",
- "cocoa/framed_browser_window.mm",
- "cocoa/fullscreen/fullscreen_menubar_tracker.h",
- "cocoa/fullscreen/fullscreen_menubar_tracker.mm",
- "cocoa/fullscreen/fullscreen_toolbar_animation_controller.h",
- "cocoa/fullscreen/fullscreen_toolbar_animation_controller.mm",
- "cocoa/fullscreen/fullscreen_toolbar_controller.h",
- "cocoa/fullscreen/fullscreen_toolbar_controller.mm",
- "cocoa/fullscreen/fullscreen_toolbar_mouse_tracker.h",
- "cocoa/fullscreen/fullscreen_toolbar_mouse_tracker.mm",
- "cocoa/fullscreen/fullscreen_toolbar_visibility_lock_controller.h",
- "cocoa/fullscreen/fullscreen_toolbar_visibility_lock_controller.mm",
- "cocoa/fullscreen/immersive_fullscreen_controller.h",
- "cocoa/fullscreen/immersive_fullscreen_controller.mm",
- "cocoa/fullscreen_placeholder_view.h",
- "cocoa/fullscreen_placeholder_view.mm",
- "cocoa/fullscreen_window.h",
- "cocoa/fullscreen_window.mm",
"cocoa/global_error_bubble_controller.h",
"cocoa/global_error_bubble_controller.mm",
- "cocoa/global_error_bubble_controller_views.mm",
- "cocoa/gradient_button_cell.h",
- "cocoa/gradient_button_cell.mm",
- "cocoa/harmony_button.h",
- "cocoa/harmony_button.mm",
- "cocoa/has_weak_browser_pointer.h",
- "cocoa/hover_close_button.h",
- "cocoa/hover_close_button.mm",
- "cocoa/hung_renderer_controller.h",
- "cocoa/hung_renderer_controller.mm",
- "cocoa/image_button_cell.h",
- "cocoa/image_button_cell.mm",
"cocoa/importer/import_lock_dialog_cocoa.mm",
- "cocoa/info_bubble_view.h",
- "cocoa/info_bubble_view.mm",
- "cocoa/info_bubble_window.h",
- "cocoa/info_bubble_window.mm",
- "cocoa/infobars/after_translate_infobar_controller.h",
- "cocoa/infobars/after_translate_infobar_controller.mm",
- "cocoa/infobars/alternate_nav_infobar_controller.h",
- "cocoa/infobars/alternate_nav_infobar_controller.mm",
- "cocoa/infobars/before_translate_infobar_controller.h",
- "cocoa/infobars/before_translate_infobar_controller.mm",
- "cocoa/infobars/confirm_infobar_controller.h",
- "cocoa/infobars/confirm_infobar_controller.mm",
- "cocoa/infobars/infobar_cocoa.h",
- "cocoa/infobars/infobar_cocoa.mm",
- "cocoa/infobars/infobar_container_cocoa.h",
- "cocoa/infobars/infobar_container_cocoa.mm",
- "cocoa/infobars/infobar_container_controller.h",
- "cocoa/infobars/infobar_container_controller.mm",
- "cocoa/infobars/infobar_controller.h",
- "cocoa/infobars/infobar_controller.mm",
- "cocoa/infobars/infobar_gradient_view.h",
- "cocoa/infobars/infobar_gradient_view.mm",
- "cocoa/infobars/infobar_utilities.h",
- "cocoa/infobars/infobar_utilities.mm",
- "cocoa/infobars/translate_infobar_base.h",
- "cocoa/infobars/translate_infobar_base.mm",
- "cocoa/infobars/translate_message_infobar_controller.h",
- "cocoa/infobars/translate_message_infobar_controller.mm",
- "cocoa/javascript_app_modal_dialog_cocoa.h",
- "cocoa/javascript_app_modal_dialog_cocoa.mm",
- "cocoa/location_bar/autocomplete_text_field.h",
- "cocoa/location_bar/autocomplete_text_field.mm",
- "cocoa/location_bar/autocomplete_text_field_cell.h",
- "cocoa/location_bar/autocomplete_text_field_cell.mm",
- "cocoa/location_bar/autocomplete_text_field_editor.h",
- "cocoa/location_bar/autocomplete_text_field_editor.mm",
- "cocoa/location_bar/bubble_decoration.h",
- "cocoa/location_bar/bubble_decoration.mm",
- "cocoa/location_bar/content_setting_decoration.h",
- "cocoa/location_bar/content_setting_decoration.mm",
- "cocoa/location_bar/image_decoration.h",
- "cocoa/location_bar/image_decoration.mm",
- "cocoa/location_bar/keyword_hint_decoration.h",
- "cocoa/location_bar/keyword_hint_decoration.mm",
- "cocoa/location_bar/location_bar_decoration.h",
- "cocoa/location_bar/location_bar_decoration.mm",
- "cocoa/location_bar/location_bar_view_mac.h",
- "cocoa/location_bar/location_bar_view_mac.mm",
- "cocoa/location_bar/manage_passwords_decoration.h",
- "cocoa/location_bar/manage_passwords_decoration.mm",
- "cocoa/location_bar/page_info_bubble_decoration.h",
- "cocoa/location_bar/page_info_bubble_decoration.mm",
- "cocoa/location_bar/save_credit_card_decoration.h",
- "cocoa/location_bar/save_credit_card_decoration.mm",
- "cocoa/location_bar/selected_keyword_decoration.h",
- "cocoa/location_bar/selected_keyword_decoration.mm",
- "cocoa/location_bar/star_decoration.h",
- "cocoa/location_bar/star_decoration.mm",
- "cocoa/location_bar/translate_decoration.h",
- "cocoa/location_bar/translate_decoration.mm",
- "cocoa/location_bar/zoom_decoration.h",
- "cocoa/location_bar/zoom_decoration.mm",
"cocoa/login_handler_cocoa.h",
"cocoa/login_handler_cocoa.mm",
- "cocoa/main_menu_item.h",
- "cocoa/menu_button.h",
- "cocoa/menu_button.mm",
- "cocoa/multi_key_equivalent_button.h",
- "cocoa/multi_key_equivalent_button.mm",
- "cocoa/new_tab_button.h",
- "cocoa/new_tab_button.mm",
- "cocoa/nsview_additions.h",
- "cocoa/nsview_additions.mm",
- "cocoa/omnibox/omnibox_popup_cell.h",
- "cocoa/omnibox/omnibox_popup_cell.mm",
- "cocoa/omnibox/omnibox_popup_matrix.h",
- "cocoa/omnibox/omnibox_popup_matrix.mm",
- "cocoa/omnibox/omnibox_popup_separator_view.h",
- "cocoa/omnibox/omnibox_popup_separator_view.mm",
- "cocoa/omnibox/omnibox_popup_view_mac.h",
- "cocoa/omnibox/omnibox_popup_view_mac.mm",
- "cocoa/omnibox/omnibox_view_mac.h",
- "cocoa/omnibox/omnibox_view_mac.mm",
- "cocoa/omnibox_decoration_bubble_controller.h",
- "cocoa/omnibox_decoration_bubble_controller.mm",
- "cocoa/one_click_signin_dialog_controller.h",
- "cocoa/one_click_signin_dialog_controller.mm",
- "cocoa/one_click_signin_view_controller.h",
- "cocoa/one_click_signin_view_controller.mm",
"cocoa/page_info/page_info_bubble_controller.h",
"cocoa/page_info/page_info_bubble_controller.mm",
- "cocoa/page_info/page_info_utils_cocoa.h",
- "cocoa/page_info/page_info_utils_cocoa.mm",
- "cocoa/page_info/permission_selector_button.h",
- "cocoa/page_info/permission_selector_button.mm",
- "cocoa/page_info/split_block_button.h",
- "cocoa/page_info/split_block_button.mm",
"cocoa/password_reuse_warning_dialog_cocoa.h",
"cocoa/password_reuse_warning_dialog_cocoa.mm",
"cocoa/password_reuse_warning_view_controller.h",
"cocoa/password_reuse_warning_view_controller.mm",
- "cocoa/passwords/account_avatar_fetcher_manager.h",
- "cocoa/passwords/account_avatar_fetcher_manager.mm",
- "cocoa/passwords/account_chooser_view_controller.h",
- "cocoa/passwords/account_chooser_view_controller.mm",
- "cocoa/passwords/auto_signin_view_controller.h",
- "cocoa/passwords/auto_signin_view_controller.mm",
- "cocoa/passwords/autosignin_prompt_view_controller.h",
- "cocoa/passwords/autosignin_prompt_view_controller.mm",
- "cocoa/passwords/base_passwords_content_view_controller.h",
- "cocoa/passwords/base_passwords_content_view_controller.mm",
- "cocoa/passwords/confirmation_password_saved_view_controller.h",
- "cocoa/passwords/confirmation_password_saved_view_controller.mm",
- "cocoa/passwords/credential_item_button.h",
- "cocoa/passwords/credential_item_button.mm",
- "cocoa/passwords/credentials_selection_view_cocoa.h",
- "cocoa/passwords/credentials_selection_view_cocoa.mm",
- "cocoa/passwords/manage_passwords_view_controller.h",
- "cocoa/passwords/manage_passwords_view_controller.mm",
- "cocoa/passwords/password_item_views.h",
- "cocoa/passwords/password_prompt_bridge_interface.h",
- "cocoa/passwords/password_prompt_view_bridge.h",
- "cocoa/passwords/password_prompt_view_bridge.mm",
- "cocoa/passwords/passwords_bubble_cocoa.h",
- "cocoa/passwords/passwords_bubble_cocoa.mm",
- "cocoa/passwords/passwords_bubble_controller.h",
- "cocoa/passwords/passwords_bubble_controller.mm",
- "cocoa/passwords/passwords_bubble_utils.h",
- "cocoa/passwords/passwords_bubble_utils.mm",
- "cocoa/passwords/passwords_list_view_controller.h",
- "cocoa/passwords/passwords_list_view_controller.mm",
- "cocoa/passwords/pending_password_view_controller.h",
- "cocoa/passwords/pending_password_view_controller.mm",
- "cocoa/passwords/save_pending_password_view_controller.h",
- "cocoa/passwords/save_pending_password_view_controller.mm",
- "cocoa/passwords/signin_promo_view_controller.h",
- "cocoa/passwords/signin_promo_view_controller.mm",
- "cocoa/passwords/update_pending_password_view_controller.h",
- "cocoa/passwords/update_pending_password_view_controller.mm",
- "cocoa/permission_bubble/chooser_bubble_ui_cocoa.h",
- "cocoa/permission_bubble/chooser_bubble_ui_cocoa.mm",
+ "cocoa/passwords/password_prompt_views_mac.mm",
"cocoa/permission_bubble/chooser_bubble_ui_views_mac.mm",
- "cocoa/permission_bubble/permission_prompt_impl_views_mac.mm",
- "cocoa/profiles/avatar_base_controller.h",
- "cocoa/profiles/avatar_base_controller.mm",
- "cocoa/profiles/avatar_button.h",
- "cocoa/profiles/avatar_button.mm",
- "cocoa/profiles/avatar_button_controller.h",
- "cocoa/profiles/avatar_button_controller.mm",
- "cocoa/profiles/avatar_icon_controller.h",
- "cocoa/profiles/avatar_icon_controller.mm",
- "cocoa/profiles/profile_chooser_bridge_views.h",
- "cocoa/profiles/profile_chooser_bridge_views.mm",
- "cocoa/profiles/profile_chooser_controller.h",
- "cocoa/profiles/profile_chooser_controller.mm",
- "cocoa/profiles/profile_signin_confirmation_dialog_cocoa.h",
- "cocoa/profiles/profile_signin_confirmation_dialog_cocoa.mm",
- "cocoa/profiles/profile_signin_confirmation_view_controller.h",
- "cocoa/profiles/profile_signin_confirmation_view_controller.mm",
- "cocoa/profiles/signin_view_controller_delegate_mac.h",
- "cocoa/profiles/signin_view_controller_delegate_mac.mm",
- "cocoa/profiles/user_manager_mac.h",
- "cocoa/profiles/user_manager_mac.mm",
- "cocoa/rect_path_utils.h",
- "cocoa/rect_path_utils.mm",
- "cocoa/restart_browser.h",
- "cocoa/restart_browser.mm",
- "cocoa/screen_capture_notification_ui_cocoa.h",
- "cocoa/screen_capture_notification_ui_cocoa.mm",
- "cocoa/separate_fullscreen_window.h",
- "cocoa/separate_fullscreen_window.mm",
"cocoa/session_crashed_bubble.mm",
"cocoa/simple_message_box_bridge_views.mm",
"cocoa/simple_message_box_cocoa.h",
"cocoa/simple_message_box_cocoa.mm",
- "cocoa/single_web_contents_dialog_manager_cocoa.h",
- "cocoa/single_web_contents_dialog_manager_cocoa.mm",
- "cocoa/spinner_view.h",
- "cocoa/spinner_view.mm",
- "cocoa/sprite_view.h",
- "cocoa/sprite_view.mm",
- "cocoa/ssl_client_certificate_selector_cocoa.h",
- "cocoa/ssl_client_certificate_selector_cocoa.mm",
- "cocoa/status_bubble_mac.h",
- "cocoa/status_bubble_mac.mm",
- "cocoa/styled_text_field.h",
- "cocoa/styled_text_field.mm",
- "cocoa/styled_text_field_cell.h",
- "cocoa/styled_text_field_cell.mm",
- "cocoa/subresource_filter/subresource_filter_bubble_controller.h",
- "cocoa/subresource_filter/subresource_filter_bubble_controller.mm",
- "cocoa/tab_contents/favicon_util_mac.h",
- "cocoa/tab_contents/favicon_util_mac.mm",
- "cocoa/tab_contents/overlayable_contents_controller.h",
- "cocoa/tab_contents/overlayable_contents_controller.mm",
- "cocoa/tab_contents/sad_tab_mac.mm",
- "cocoa/tab_contents/sad_tab_view_cocoa.h",
- "cocoa/tab_contents/sad_tab_view_cocoa.mm",
- "cocoa/tab_contents/tab_contents_controller.h",
- "cocoa/tab_contents/tab_contents_controller.mm",
"cocoa/tab_dialogs_cocoa.h",
"cocoa/tab_dialogs_cocoa.mm",
- "cocoa/tab_dialogs_views_mac.h",
- "cocoa/tab_dialogs_views_mac.mm",
- "cocoa/tab_modal_confirm_dialog_mac.h",
- "cocoa/tab_modal_confirm_dialog_mac.mm",
- "cocoa/tabbed_browser_window.h",
- "cocoa/tabbed_browser_window.mm",
- "cocoa/tabs/alert_indicator_button_cocoa.h",
- "cocoa/tabs/alert_indicator_button_cocoa.mm",
- "cocoa/tabs/tab_controller.h",
- "cocoa/tabs/tab_controller.mm",
- "cocoa/tabs/tab_controller_target.h",
- "cocoa/tabs/tab_strip_background_view.h",
- "cocoa/tabs/tab_strip_background_view.mm",
- "cocoa/tabs/tab_strip_controller.h",
- "cocoa/tabs/tab_strip_controller.mm",
- "cocoa/tabs/tab_strip_drag_controller.h",
- "cocoa/tabs/tab_strip_drag_controller.mm",
- "cocoa/tabs/tab_strip_model_observer_bridge.h",
- "cocoa/tabs/tab_strip_model_observer_bridge.mm",
- "cocoa/tabs/tab_strip_view.h",
- "cocoa/tabs/tab_strip_view.mm",
- "cocoa/tabs/tab_view.h",
- "cocoa/tabs/tab_view.mm",
- "cocoa/tabs/tab_window_controller.h",
- "cocoa/tabs/tab_window_controller.mm",
- "cocoa/themed_window.h",
- "cocoa/themed_window.mm",
- "cocoa/toolbar/app_toolbar_button.h",
- "cocoa/toolbar/app_toolbar_button.mm",
- "cocoa/toolbar/app_toolbar_button_cell.h",
- "cocoa/toolbar/app_toolbar_button_cell.mm",
- "cocoa/toolbar/back_forward_menu_controller.h",
- "cocoa/toolbar/back_forward_menu_controller.mm",
- "cocoa/toolbar/media_router_action_platform_delegate_cocoa.h",
- "cocoa/toolbar/media_router_action_platform_delegate_cocoa.mm",
- "cocoa/toolbar/reload_button_cocoa.h",
- "cocoa/toolbar/reload_button_cocoa.mm",
- "cocoa/toolbar/toolbar_button_cocoa.h",
- "cocoa/toolbar/toolbar_button_cocoa.mm",
- "cocoa/toolbar/toolbar_controller.h",
- "cocoa/toolbar/toolbar_controller.mm",
- "cocoa/toolbar/toolbar_view_cocoa.h",
- "cocoa/toolbar/toolbar_view_cocoa.mm",
- "cocoa/translate/translate_bubble_bridge_views.h",
- "cocoa/translate/translate_bubble_bridge_views.mm",
- "cocoa/translate/translate_bubble_controller.h",
- "cocoa/translate/translate_bubble_controller.mm",
- "cocoa/url_drop_target.h",
- "cocoa/url_drop_target.mm",
- "cocoa/vertical_gradient_view.h",
- "cocoa/vertical_gradient_view.mm",
- "cocoa/view_id_util.h",
- "cocoa/view_id_util.mm",
- "cocoa/view_resizer.h",
- "cocoa/web_contents_modal_dialog_manager_views_mac.h",
- "cocoa/web_contents_modal_dialog_manager_views_mac.mm",
- "cocoa/web_textfield_touch_bar_controller.h",
- "cocoa/web_textfield_touch_bar_controller.mm",
-
- # TODO(estade): this class should be deleted in favor of a combobox model.
- # See crbug.com/590850
- "content_settings/content_setting_media_menu_model.cc",
- "content_settings/content_setting_media_menu_model.h",
"javascript_dialogs/javascript_dialog_cocoa.h",
"javascript_dialogs/javascript_dialog_cocoa.mm",
"javascript_dialogs/javascript_dialog_mac.cc",
- "proximity_auth/proximity_auth_error_bubble_stub.cc",
- "startup/session_crashed_infobar_delegate.cc",
- "startup/session_crashed_infobar_delegate.h",
]
}
} else { # non-Mac.
@@ -2567,6 +2617,8 @@ split_static_library("ui") {
sources += [
"webui/help/version_updater_win.cc",
"webui/help/version_updater_win.h",
+ "webui/settings/incompatible_applications_handler_win.cc",
+ "webui/settings/incompatible_applications_handler_win.h",
]
deps += [ "//google_update" ]
} else {
@@ -2678,6 +2730,8 @@ split_static_library("ui") {
# This test header is included because it contains forward declarations
# needed for "friend" statements for use in tests.
"translate/translate_bubble_test_utils.h",
+ "views/accessibility/non_accessible_image_view.cc",
+ "views/accessibility/non_accessible_image_view.h",
"views/apps/app_info_dialog/app_info_dialog_container.cc",
"views/apps/app_info_dialog/app_info_dialog_container.h",
"views/apps/app_info_dialog/app_info_dialog_views.cc",
@@ -2744,8 +2798,8 @@ split_static_library("ui") {
"views/extensions/pwa_confirmation_view.h",
"views/extensions/web_app_info_image_source.cc",
"views/extensions/web_app_info_image_source.h",
- "views/first_run_bubble.cc",
- "views/first_run_bubble.h",
+ "views/folder_upload_confirmation_view.cc",
+ "views/folder_upload_confirmation_view.h",
"views/fullscreen_control/fullscreen_control_host.cc",
"views/fullscreen_control/fullscreen_control_host.h",
"views/fullscreen_control/fullscreen_control_popup.cc",
@@ -2782,8 +2836,6 @@ split_static_library("ui") {
"views/page_info/chosen_object_view.cc",
"views/page_info/chosen_object_view.h",
"views/page_info/chosen_object_view_observer.h",
- "views/page_info/non_accessible_image_view.cc",
- "views/page_info/non_accessible_image_view.h",
"views/page_info/page_info_bubble_view.cc",
"views/page_info/page_info_bubble_view.h",
"views/page_info/page_info_bubble_view_base.cc",
@@ -2799,22 +2851,20 @@ split_static_library("ui") {
"views/passwords/credentials_item_view.h",
"views/passwords/credentials_selection_view.cc",
"views/passwords/credentials_selection_view.h",
- "views/passwords/manage_password_auto_sign_in_view.cc",
- "views/passwords/manage_password_auto_sign_in_view.h",
- "views/passwords/manage_password_items_view.cc",
- "views/passwords/manage_password_items_view.h",
- "views/passwords/manage_password_pending_view.cc",
- "views/passwords/manage_password_pending_view.h",
- "views/passwords/manage_password_save_confirmation_view.cc",
- "views/passwords/manage_password_save_confirmation_view.h",
- "views/passwords/manage_password_sign_in_promo_view.cc",
- "views/passwords/manage_password_sign_in_promo_view.h",
- "views/passwords/manage_password_update_pending_view.cc",
- "views/passwords/manage_password_update_pending_view.h",
- "views/passwords/manage_passwords_bubble_delegate_view_base.cc",
- "views/passwords/manage_passwords_bubble_delegate_view_base.h",
- "views/passwords/manage_passwords_bubble_view.cc",
- "views/passwords/manage_passwords_bubble_view.h",
+ "views/passwords/password_auto_sign_in_view.cc",
+ "views/passwords/password_auto_sign_in_view.h",
+ "views/passwords/password_bubble_view_base.cc",
+ "views/passwords/password_bubble_view_base.h",
+ "views/passwords/password_items_view.cc",
+ "views/passwords/password_items_view.h",
+ "views/passwords/password_pending_view.cc",
+ "views/passwords/password_pending_view.h",
+ "views/passwords/password_save_confirmation_view.cc",
+ "views/passwords/password_save_confirmation_view.h",
+ "views/passwords/password_sign_in_promo_view.cc",
+ "views/passwords/password_sign_in_promo_view.h",
+ "views/passwords/password_update_pending_view.cc",
+ "views/passwords/password_update_pending_view.h",
"views/payments/contact_info_editor_view_controller.cc",
"views/payments/contact_info_editor_view_controller.h",
"views/payments/credit_card_editor_view_controller.cc",
@@ -2923,6 +2973,15 @@ split_static_library("ui") {
deps += [ "//ui/views/mus" ]
}
+ if (enable_dice_support) {
+ sources += [
+ "views/sync/dice_bubble_sync_promo_view.cc",
+ "views/sync/dice_bubble_sync_promo_view.h",
+ "views/sync/dice_signin_button_view.cc",
+ "views/sync/dice_signin_button_view.h",
+ ]
+ }
+
if (!is_mac || mac_views_browser) {
sources += [
"javascript_dialogs/javascript_dialog.cc",
@@ -2934,7 +2993,6 @@ split_static_library("ui") {
"views/autofill/autofill_popup_view_native_views.h",
"views/autofill/autofill_popup_view_views.cc",
"views/autofill/autofill_popup_view_views.h",
- "views/autofill/card_unmask_prompt_views_shim.cc",
"views/autofill/password_generation_popup_view_views.cc",
"views/autofill/password_generation_popup_view_views.h",
"views/autofill/save_card_icon_view.cc",
@@ -2985,6 +3043,8 @@ split_static_library("ui") {
"views/find_bar_host.h",
"views/find_bar_view.cc",
"views/find_bar_view.h",
+ "views/frame/avatar_button_manager.cc",
+ "views/frame/avatar_button_manager.h",
"views/frame/browser_frame.cc",
"views/frame/browser_frame.h",
"views/frame/browser_non_client_frame_view.cc",
@@ -2993,6 +3053,7 @@ split_static_library("ui") {
"views/frame/browser_root_view.h",
"views/frame/browser_view.cc",
"views/frame/browser_view.h",
+ "views/frame/browser_view_button_provider.h",
"views/frame/browser_view_layout.cc",
"views/frame/browser_view_layout.h",
"views/frame/browser_view_layout_delegate.h",
@@ -3065,20 +3126,25 @@ split_static_library("ui") {
"views/media_router/presentation_receiver_window_frame.h",
"views/media_router/presentation_receiver_window_view.cc",
"views/media_router/presentation_receiver_window_view.h",
+ "views/media_router/web_contents_display_observer_view.cc",
+ "views/media_router/web_contents_display_observer_view.h",
"views/omnibox/omnibox_popup_contents_view.cc",
"views/omnibox/omnibox_popup_contents_view.h",
"views/omnibox/omnibox_result_view.cc",
"views/omnibox/omnibox_result_view.h",
+ "views/omnibox/omnibox_tab_switch_button.cc",
+ "views/omnibox/omnibox_tab_switch_button.h",
"views/omnibox/omnibox_view_views.cc",
"views/omnibox/omnibox_view_views.h",
+ "views/omnibox/rounded_omnibox_results_frame.cc",
+ "views/omnibox/rounded_omnibox_results_frame.h",
"views/passwords/manage_passwords_icon_views.cc",
"views/passwords/manage_passwords_icon_views.h",
"views/permission_bubble/chooser_bubble_ui_views.cc",
"views/permission_bubble/permission_prompt_impl_views.cc",
+ "views/profiles/avatar_button_style.h",
"views/profiles/profile_indicator_icon.cc",
"views/profiles/profile_indicator_icon.h",
- "views/profiles/user_manager_view.cc",
- "views/profiles/user_manager_view.h",
"views/proximity_auth/proximity_auth_error_bubble_view.cc",
"views/proximity_auth/proximity_auth_error_bubble_view.h",
"views/sad_tab_view.cc",
@@ -3150,8 +3216,24 @@ split_static_library("ui") {
"views/translate/translate_icon_view.h",
"views/webshare/webshare_target_picker_view.cc",
"views/webshare/webshare_target_picker_view.h",
+ "views_mode_controller.cc",
+ "views_mode_controller.h",
]
+ deps += [ "//ui/views:features" ]
+
+ if (is_mac) {
+ # This Mac-specific file is being removed in this case because it's only
+ # needed when Views isn't used on Mac.
+ sources -=
+ [ "media_router/presentation_receiver_window_factory_mac.cc" ]
+
+ # This source file is compiled out on Mac because there is a
+ # Mac-specific implementation of it that works on mac_views_browser
+ # builds as well:
+ sources -= [ "views/permission_bubble/permission_prompt_impl_views.cc" ]
+ }
+
if (!is_chromeos) {
sources += [
"views/frame/opaque_browser_frame_view.cc",
@@ -3233,6 +3315,23 @@ split_static_library("ui") {
]
}
}
+
+ if (!is_chromeos) {
+ sources += [
+ "views/relaunch_notification/relaunch_notification_controller.cc",
+ "views/relaunch_notification/relaunch_notification_controller.h",
+ "views/relaunch_notification/relaunch_recommended_bubble_view.cc",
+ "views/relaunch_notification/relaunch_recommended_bubble_view.h",
+ "views/relaunch_notification/relaunch_required_dialog_view.cc",
+ "views/relaunch_notification/relaunch_required_dialog_view.h",
+ ]
+ if (is_mac) {
+ sources += [
+ "views/relaunch_notification/get_app_menu_anchor_point.h",
+ "views/relaunch_notification/get_app_menu_anchor_point.mm",
+ ]
+ }
+ }
}
if (use_aura) {
@@ -3316,11 +3415,14 @@ split_static_library("ui") {
"app_list/app_context_menu.cc",
"app_list/app_context_menu.h",
"app_list/app_context_menu_delegate.h",
+ "app_list/app_list_client_impl.cc",
+ "app_list/app_list_client_impl.h",
"app_list/app_list_controller_delegate.cc",
"app_list/app_list_controller_delegate.h",
"app_list/app_list_model_builder.cc",
"app_list/app_list_model_builder.h",
"app_list/app_list_model_updater.h",
+ "app_list/app_list_model_updater_delegate.h",
"app_list/app_list_service.cc",
"app_list/app_list_service.h",
"app_list/app_list_service_impl.cc",
@@ -3335,7 +3437,6 @@ split_static_library("ui") {
"app_list/chrome_app_list_item.h",
"app_list/chrome_app_list_model_updater.cc",
"app_list/chrome_app_list_model_updater.h",
- "app_list/chrome_app_list_model_updater_delegate.h",
"app_list/extension_app_context_menu.cc",
"app_list/extension_app_context_menu.h",
"app_list/extension_app_item.cc",
@@ -3393,27 +3494,12 @@ split_static_library("ui") {
"app_list/search/search_util.h",
"app_list/search/search_webstore_result.cc",
"app_list/search/search_webstore_result.h",
- "app_list/search/suggestions/suggestions_search_provider.cc",
- "app_list/search/suggestions/suggestions_search_provider.h",
- "app_list/search/suggestions/url_suggestion_result.cc",
- "app_list/search/suggestions/url_suggestion_result.h",
"app_list/search/webstore/webstore_installer.cc",
"app_list/search/webstore/webstore_installer.h",
"app_list/search/webstore/webstore_provider.cc",
"app_list/search/webstore/webstore_provider.h",
"app_list/search/webstore/webstore_result.cc",
"app_list/search/webstore/webstore_result.h",
- "app_list/speech_auth_helper.cc",
- "app_list/speech_auth_helper.h",
- "app_list/start_page_observer.h",
- "app_list/start_page_service.cc",
- "app_list/start_page_service.h",
- "app_list/start_page_service_factory.cc",
- "app_list/start_page_service_factory.h",
- "webui/app_list/start_page_handler.cc",
- "webui/app_list/start_page_handler.h",
- "webui/app_list/start_page_ui.cc",
- "webui/app_list/start_page_ui.h",
]
deps += [
"//ui/app_list",
@@ -3454,10 +3540,22 @@ split_static_library("ui") {
"app_list/arc/arc_pai_starter.h",
"app_list/arc/arc_playstore_app_context_menu.cc",
"app_list/arc/arc_playstore_app_context_menu.h",
+ "app_list/arc/arc_usb_host_permission_manager.cc",
+ "app_list/arc/arc_usb_host_permission_manager.h",
+ "app_list/arc/arc_usb_host_permission_manager_factory.cc",
+ "app_list/arc/arc_usb_host_permission_manager_factory.h",
"app_list/arc/arc_vpn_provider_manager.cc",
"app_list/arc/arc_vpn_provider_manager.h",
"app_list/arc/arc_vpn_provider_manager_factory.cc",
"app_list/arc/arc_vpn_provider_manager_factory.h",
+ "app_list/crostini/crostini_app_item.cc",
+ "app_list/crostini/crostini_app_item.h",
+ "app_list/crostini/crostini_app_model_builder.cc",
+ "app_list/crostini/crostini_app_model_builder.h",
+ "app_list/crostini/crostini_installer_view.cc",
+ "app_list/crostini/crostini_installer_view.h",
+ "app_list/crostini/crostini_util.cc",
+ "app_list/crostini/crostini_util.h",
"app_list/search/arc_app_result.cc",
"app_list/search/arc_app_result.h",
"ash/launcher/arc_app_deferred_launcher_controller.cc",
@@ -3542,6 +3640,10 @@ split_static_library("ui") {
"extensions/installation_error_infobar_delegate.h",
"extensions/settings_api_bubble_helpers.cc",
"extensions/settings_api_bubble_helpers.h",
+ "views/extensions/extension_popup.cc",
+ "views/extensions/extension_popup.h",
+ "views/extensions/extension_view_views.cc",
+ "views/extensions/extension_view_views.h",
"webui/extensions/extension_basic_info.cc",
"webui/extensions/extension_basic_info.h",
"webui/extensions/extension_icon_source.cc",
@@ -3561,10 +3663,6 @@ split_static_library("ui") {
"views/extensions/extension_dialog.h",
"views/extensions/extension_dialog_observer.cc",
"views/extensions/extension_dialog_observer.h",
- "views/extensions/extension_popup.cc",
- "views/extensions/extension_popup.h",
- "views/extensions/extension_view_views.cc",
- "views/extensions/extension_view_views.h",
"views/extensions/media_galleries_dialog_views.cc",
"views/extensions/media_galleries_dialog_views.h",
"views/extensions/media_gallery_checkbox_view.cc",
@@ -3658,6 +3756,9 @@ split_static_library("ui") {
"webui/local_discovery/local_discovery_ui_handler.cc",
"webui/local_discovery/local_discovery_ui_handler.h",
]
+ if (enable_print_preview && !is_chromeos) {
+ deps += [ "//chrome/common:service_process_mojom" ]
+ }
}
if (enable_webrtc) {
@@ -3665,6 +3766,7 @@ split_static_library("ui") {
"webui/media/webrtc_logs_ui.cc",
"webui/media/webrtc_logs_ui.h",
]
+ deps += [ "//components/webrtc_logging/browser" ]
}
if (safe_browsing_mode == 1) {
@@ -3756,7 +3858,9 @@ static_library("test_support") {
]
if (toolkit_views) {
+ deps += [ "//ui/views:test_support" ]
sources += [
+ "extensions/browser_action_test_util.h",
"views/payments/test_chrome_payment_request_delegate.cc",
"views/payments/test_chrome_payment_request_delegate.h",
]
@@ -3770,7 +3874,7 @@ static_library("test_support") {
}
} else {
sources += [
- "cocoa/extensions/browser_action_test_util_mac.mm",
+ "cocoa/extensions/browser_action_test_util_views_mac.mm",
"cocoa/find_bar/find_bar_host_unittest_util_cocoa.mm",
]
}
@@ -3794,6 +3898,7 @@ static_library("test_support") {
]
deps += [
"//chrome/test:test_support_ui",
+ "//components/signin/core/browser",
"//components/zoom",
]
}
diff --git a/chromium/chrome/browser/ui/libgtkui/BUILD.gn b/chromium/chrome/browser/ui/libgtkui/BUILD.gn
index 505d87c67be..cc7663cbe6c 100644
--- a/chromium/chrome/browser/ui/libgtkui/BUILD.gn
+++ b/chromium/chrome/browser/ui/libgtkui/BUILD.gn
@@ -90,7 +90,7 @@ template("libgtkui") {
"//base:i18n",
"//base/third_party/dynamic_annotations",
"//cc/paint",
- "//chrome/common:features",
+ "//chrome/common:buildflags",
"//chrome:extra_resources",
"//chrome:resources",
"//chrome:strings",
diff --git a/chromium/chrome/browser/ui/webui/OWNERS b/chromium/chrome/browser/ui/webui/OWNERS
index 4dbbf3fcf06..211ccfc88f3 100644
--- a/chromium/chrome/browser/ui/webui/OWNERS
+++ b/chromium/chrome/browser/ui/webui/OWNERS
@@ -10,6 +10,8 @@ per-file inspect_ui*=pfeldman@chromium.org
per-file md_history_ui*=calamity@chromium.org
+per-file memory_internals_ui*=erikchen@chromium.org
+
per-file net_export_ui.*=file://net/OWNERS
per-file ntp_tiles_internals_ui.*=file://components/ntp_tiles/OWNERS
diff --git a/chromium/chrome/browser/ui/webui/about_ui.cc b/chromium/chrome/browser/ui/webui/about_ui.cc
index 8c97750329e..808d6e76497 100644
--- a/chromium/chrome/browser/ui/webui/about_ui.cc
+++ b/chromium/chrome/browser/ui/webui/about_ui.cc
@@ -51,6 +51,7 @@
#include "components/about_ui/credit_utils.h"
#include "components/grit/components_resources.h"
#include "components/strings/grit/components_locale_settings.h"
+#include "components/strings/grit/components_strings.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
@@ -325,6 +326,25 @@ std::string ChromeURLs() {
return html;
}
+std::string HelpCenterContent() {
+ std::string html;
+ AppendHeader(&html, 0, l10n_util::GetStringUTF8(IDS_CONNECTION_HELP_TITLE));
+ html +=
+ "<meta name=\"viewport\" content=\"initial-scale=1, minimum-scale=1, "
+ "width=device-width\">\n";
+ webui::AppendWebUiCssTextDefaults(&html);
+ html += "<style>";
+ html += l10n_util::GetStringUTF8(IDR_SECURITY_INTERSTITIAL_COMMON_CSS);
+ html += l10n_util::GetStringUTF8(IDR_SECURITY_INTERSTITIAL_CORE_CSS);
+ html += "</style>";
+ AppendBody(&html);
+ html += "<div class=\"interstitial-wrapper\">\n";
+ html += l10n_util::GetStringUTF8(IDS_CONNECTION_HELP_HTML);
+ html += "</div>\n";
+ AppendFooter(&html);
+ return html;
+}
+
// AboutDnsHandler bounces the request back to the IO thread to collect
// the DNS information.
class AboutDnsHandler : public base::RefCountedThreadSafe<AboutDnsHandler> {
@@ -425,6 +445,8 @@ void AboutUIHTMLSource::StartDataRequest(
// Add your data source here, in alphabetical order.
if (source_name_ == chrome::kChromeUIChromeURLsHost) {
response = ChromeURLs();
+ } else if (source_name_ == chrome::kChromeUIConnectionHelpHost) {
+ response = HelpCenterContent();
} else if (source_name_ == chrome::kChromeUICreditsHost) {
int idr = IDR_ABOUT_UI_CREDITS_HTML;
if (path == kCreditsJsPath)
diff --git a/chromium/chrome/browser/ui/webui/app_list/OWNERS b/chromium/chrome/browser/ui/webui/app_list/OWNERS
deleted file mode 100644
index ed54f09c40c..00000000000
--- a/chromium/chrome/browser/ui/webui/app_list/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-calamity@chromium.org
-khmel@chromium.org
diff --git a/chromium/chrome/browser/ui/webui/app_list/start_page_browsertest.js b/chromium/chrome/browser/ui/webui/app_list/start_page_browsertest.js
deleted file mode 100644
index 84869c4f540..00000000000
--- a/chromium/chrome/browser/ui/webui/app_list/start_page_browsertest.js
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * TestFixture for kiosk app settings WebUI testing.
- * @extends {testing.Test}
- * @constructor
- */
-function AppListStartPageWebUITest() {}
-
-/**
- * Mock of audioContext.
- * @constructor
- */
-function mockAudioContext() {
- this.sampleRate = 44100; /* some dummy number */
-}
-
-mockAudioContext.prototype = {
- createMediaStreamSource: function(stream) {
- return {connect: function(audioProc) {},
- disconnect: function() {}};
- },
- createScriptProcessor: function(bufSize, in_channels, out_channels) {
- return {connect: function(destination) {},
- disconnect: function() {}};
- }
-};
-
-AppListStartPageWebUITest.prototype = {
- __proto__: testing.Test.prototype,
-
- /**
- * Browser to app launcher start page.
- */
- browsePreload: 'chrome://app-list/',
-
- /**
- * Placeholder for mock speech recognizer.
- */
- speechRecognizer: null,
-
- /**
- * Sends the speech recognition result.
- *
- * @param {string} result The testing result.
- * @param {boolean} isFinal Whether the result is final or not.
- */
- sendSpeechResult: function(result, isFinal) {
- var speechEvent = new Event('test');
- // Each result contains a list of alternatives and 'isFinal' flag.
- var speechResult = [{transcript: result}];
- speechResult.isFinal = isFinal;
- speechEvent.results = [speechResult];
- this.speechRecognizer.onresult(speechEvent);
- },
-
- /**
- * Registers the webkitSpeechRecognition mock for test.
- * @private
- */
- registerMockSpeechRecognition_: function() {
- var owner = this;
- function mockSpeechRecognition() {
- this.inSpeech_ = false;
- owner.speechRecognizer = this;
- }
-
- mockSpeechRecognition.prototype = {
- start: function() {
- this.onstart();
- },
-
- abort: function() {
- if (this.inSpeech_)
- this.onspeechend();
- this.onerror(new Error());
- this.onend();
- }
- },
-
- window.webkitSpeechRecognition = mockSpeechRecognition;
- },
-
- /**
- * Mock of webkitGetUserMedia for start page.
- *
- * @private
- * @param {object} constraint The constraint parameter.
- * @param {Function} success The success callback.
- * @param {Function} error The error callback.
- */
- mockGetUserMedia_: function(constraint, success, error) {
- function getAudioTracks() {
- }
- assertTrue(constraint.audio);
- assertNotEquals(null, error, 'error callback must not be null');
- var audioTracks = [];
- for (var i = 0; i < 2; ++i) {
- audioTracks.push(this.audioTrackMocks[i].proxy());
- }
- success({getAudioTracks: function() { return audioTracks; }});
- },
-
- /** @override */
- preLoad: function() {
- this.makeAndRegisterMockHandler(['initialize',
- 'launchApp',
- 'setSpeechRecognitionState',
- 'speechResult']);
- this.mockHandler.stubs().initialize();
- this.mockHandler.stubs().launchApp(ANYTHING);
-
- this.registerMockSpeechRecognition_();
- window.AudioContext = mockAudioContext;
- navigator.webkitGetUserMedia = this.mockGetUserMedia_.bind(this);
- this.audioTrackMocks = [mock(MediaStreamTrack), mock(MediaStreamTrack)];
- }
-};
-
-TEST_F('AppListStartPageWebUITest', 'Basic', function() {
- assertEquals(this.browsePreload, document.location.href);
-});
-
-TEST_F('AppListStartPageWebUITest', 'LoadDoodle', function() {
- var doodleData = {
- 'ddljson': {
- 'transparent_large_image': {
- 'url': 'doodle.png'
- },
- 'alt_text': 'Doodle alt text',
- 'target_url': '/target.html'
- }
- };
-
- assertEquals(null, $('doodle'));
-
- // Load the doodle with a target url and alt text.
- appList.startPage.onAppListDoodleUpdated(doodleData,
- 'http://example.com/x/');
- assertNotEquals(null, $('doodle'));
- assertEquals('http://example.com/x/doodle.png', $('doodle_image').src);
- assertEquals(doodleData.ddljson.alt_text, $('doodle_image').title);
- assertEquals('http://example.com/target.html', $('doodle_link').href);
-
- // Reload the doodle without a target url and alt text.
- doodleData.ddljson.alt_text = undefined;
- doodleData.ddljson.target_url = undefined;
- appList.startPage.onAppListDoodleUpdated(doodleData,
- 'http://example.com/x/');
- assertNotEquals(null, $('doodle'));
- assertEquals('http://example.com/x/doodle.png', $('doodle_image').src);
- assertEquals('', $('doodle_image').title);
- assertEquals(null, $('doodle_link'));
-
-
- appList.startPage.onAppListDoodleUpdated({},
- 'http://example.com/');
- assertEquals(null, $('doodle'));
-});
diff --git a/chromium/chrome/browser/ui/webui/app_list/start_page_handler.cc b/chromium/chrome/browser/ui/webui/app_list/start_page_handler.cc
deleted file mode 100644
index d8fd5b02f2b..00000000000
--- a/chromium/chrome/browser/ui/webui/app_list/start_page_handler.cc
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/app_list/start_page_handler.h"
-
-#include <memory>
-#include <string>
-
-#include "base/bind.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/values.h"
-#include "base/version.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
-#include "chrome/browser/ui/app_list/app_list_service.h"
-#include "chrome/browser/ui/app_list/start_page_service.h"
-#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
-#include "chrome/common/pref_names.h"
-#include "components/update_client/update_query_params.h"
-#include "content/public/browser/web_ui.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/browser/extension_system.h"
-#include "extensions/common/constants.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/extension_icon_set.h"
-#include "ui/app_list/app_list_switches.h"
-#include "ui/events/event_constants.h"
-
-namespace app_list {
-
-namespace {
-
-const char kAppListDoodleActionHistogram[] = "Apps.AppListDoodleAction";
-
-// Interactions a user has with the app list doodle. This enum must not have its
-// order altered as it is used in histograms.
-enum DoodleAction {
- DOODLE_SHOWN = 0,
- DOODLE_CLICKED,
- // Add values here.
-
- DOODLE_ACTION_LAST,
-};
-
-} // namespace
-
-StartPageHandler::StartPageHandler() {
-}
-
-StartPageHandler::~StartPageHandler() {
-}
-
-void StartPageHandler::RegisterMessages() {
- web_ui()->RegisterMessageCallback(
- "appListShown", base::Bind(&StartPageHandler::HandleAppListShown,
- base::Unretained(this)));
- web_ui()->RegisterMessageCallback(
- "doodleClicked", base::Bind(&StartPageHandler::HandleDoodleClicked,
- base::Unretained(this)));
- web_ui()->RegisterMessageCallback(
- "initialize",
- base::Bind(&StartPageHandler::HandleInitialize, base::Unretained(this)));
- web_ui()->RegisterMessageCallback(
- "launchApp",
- base::Bind(&StartPageHandler::HandleLaunchApp, base::Unretained(this)));
-}
-
-void StartPageHandler::HandleAppListShown(const base::ListValue* args) {
- bool doodle_shown = false;
- if (args->GetBoolean(0, &doodle_shown) && doodle_shown) {
- UMA_HISTOGRAM_ENUMERATION(kAppListDoodleActionHistogram, DOODLE_SHOWN,
- DOODLE_ACTION_LAST);
- }
-}
-
-void StartPageHandler::HandleDoodleClicked(const base::ListValue* args) {
- UMA_HISTOGRAM_ENUMERATION(kAppListDoodleActionHistogram, DOODLE_CLICKED,
- DOODLE_ACTION_LAST);
-}
-
-void StartPageHandler::HandleInitialize(const base::ListValue* args) {
- Profile* profile = Profile::FromWebUI(web_ui());
- StartPageService* service = StartPageService::Get(profile);
- if (!service)
- return;
-
- service->WebUILoaded();
-}
-
-void StartPageHandler::HandleLaunchApp(const base::ListValue* args) {
- std::string app_id;
- CHECK(args->GetString(0, &app_id));
-
- Profile* profile = Profile::FromWebUI(web_ui());
- const extensions::Extension* app =
- extensions::ExtensionRegistry::Get(profile)
- ->GetExtensionById(app_id, extensions::ExtensionRegistry::EVERYTHING);
- if (!app) {
- NOTREACHED();
- return;
- }
-
- AppListControllerDelegate* controller =
- AppListService::Get()->GetControllerDelegate();
- controller->ActivateApp(profile,
- app,
- AppListControllerDelegate::LAUNCH_FROM_APP_LIST,
- ui::EF_NONE);
-}
-
-} // namespace app_list
diff --git a/chromium/chrome/browser/ui/webui/app_list/start_page_handler.h b/chromium/chrome/browser/ui/webui/app_list/start_page_handler.h
deleted file mode 100644
index 516413a3270..00000000000
--- a/chromium/chrome/browser/ui/webui/app_list/start_page_handler.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_APP_LIST_START_PAGE_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_APP_LIST_START_PAGE_HANDLER_H_
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "components/prefs/pref_change_registrar.h"
-#include "content/public/browser/web_ui_message_handler.h"
-
-namespace base {
-class ListValue;
-}
-
-namespace app_list {
-
-// Handler for the app launcher start page.
-class StartPageHandler : public content::WebUIMessageHandler {
- public:
- StartPageHandler();
- ~StartPageHandler() override;
-
- private:
- // content::WebUIMessageHandler overrides:
- void RegisterMessages() override;
-
- // JS callbacks.
- void HandleAppListShown(const base::ListValue* args);
- void HandleDoodleClicked(const base::ListValue* args);
- void HandleInitialize(const base::ListValue* args);
- void HandleLaunchApp(const base::ListValue* args);
-
- PrefChangeRegistrar pref_change_registrar_;
-
- DISALLOW_COPY_AND_ASSIGN(StartPageHandler);
-};
-
-} // namespace app_list
-
-#endif // CHROME_BROWSER_UI_WEBUI_APP_LIST_START_PAGE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/app_list/start_page_ui.cc b/chromium/chrome/browser/ui/webui/app_list/start_page_ui.cc
deleted file mode 100644
index a5160c2ab5f..00000000000
--- a/chromium/chrome/browser/ui/webui/app_list/start_page_ui.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/app_list/start_page_ui.h"
-
-#include <memory>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/memory/ref_counted_memory.h"
-#include "base/sys_info.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/app_list/start_page_handler.h"
-#include "chrome/common/extensions/extension_constants.h"
-#include "chrome/common/url_constants.h"
-#include "chrome/grit/browser_resources.h"
-#include "content/public/browser/browser_thread.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.h"
-
-namespace app_list {
-
-StartPageUI::StartPageUI(content::WebUI* web_ui)
- : content::WebUIController(web_ui) {
- web_ui->AddMessageHandler(std::make_unique<StartPageHandler>());
- InitDataSource();
-}
-
-StartPageUI::~StartPageUI() {}
-
-void StartPageUI::InitDataSource() {
- std::unique_ptr<content::WebUIDataSource> source(
- content::WebUIDataSource::Create(chrome::kChromeUIAppListStartPageHost));
-
- source->SetJsonPath("strings.js");
-
- source->AddResourcePath("start_page.css", IDR_APP_LIST_START_PAGE_CSS);
- source->AddResourcePath("start_page.js", IDR_APP_LIST_START_PAGE_JS);
- source->SetDefaultResource(IDR_APP_LIST_START_PAGE_HTML);
-
- content::WebUIDataSource::Add(Profile::FromWebUI(web_ui()), source.release());
-}
-
-} // namespace app_list
diff --git a/chromium/chrome/browser/ui/webui/app_list/start_page_ui.h b/chromium/chrome/browser/ui/webui/app_list/start_page_ui.h
deleted file mode 100644
index 04089144dd3..00000000000
--- a/chromium/chrome/browser/ui/webui/app_list/start_page_ui.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_APP_LIST_START_PAGE_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_APP_LIST_START_PAGE_UI_H_
-
-#include "base/macros.h"
-#include "content/public/browser/web_ui_controller.h"
-
-namespace app_list {
-
-// StartPageUI for the app launcher start page.
-class StartPageUI : public content::WebUIController {
- public:
- explicit StartPageUI(content::WebUI* web_ui);
- ~StartPageUI() override;
-
- private:
- // Initializes the data source used for this webui.
- void InitDataSource();
-
- DISALLOW_COPY_AND_ASSIGN(StartPageUI);
-};
-
-} // namespace app_list
-
-#endif // CHROME_BROWSER_UI_WEBUI_APP_LIST_START_PAGE_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/bluetooth_internals/BUILD.gn b/chromium/chrome/browser/ui/webui/bluetooth_internals/BUILD.gn
index 7aa5cd60035..f3e7c4bd926 100644
--- a/chromium/chrome/browser/ui/webui/bluetooth_internals/BUILD.gn
+++ b/chromium/chrome/browser/ui/webui/bluetooth_internals/BUILD.gn
@@ -30,6 +30,6 @@ mojom("mojo_bindings") {
]
deps = [
- "//device/bluetooth/public/interfaces:deprecated_experimental_interfaces",
+ "//device/bluetooth/public/mojom:deprecated_experimental_interfaces",
]
}
diff --git a/chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals.mojom b/chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals.mojom
index c1730c3500c..e5b8e22777b 100644
--- a/chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals.mojom
+++ b/chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals.mojom
@@ -4,7 +4,7 @@
module mojom;
-import "device/bluetooth/public/interfaces/adapter.mojom";
+import "device/bluetooth/public/mojom/adapter.mojom";
interface BluetoothInternalsHandler {
// Gets an Adapter interface. Returns null if Bluetooth is not supported.
diff --git a/chromium/chrome/browser/ui/webui/browsing_history_handler.cc b/chromium/chrome/browser/ui/webui/browsing_history_handler.cc
index 7666d2d1077..da08cc49277 100644
--- a/chromium/chrome/browser/ui/webui/browsing_history_handler.cc
+++ b/chromium/chrome/browser/ui/webui/browsing_history_handler.cc
@@ -27,8 +27,8 @@
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/webui/favicon_source.h"
+#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_features.h"
-#include "chrome/common/features.h"
#include "chrome/common/pref_names.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/browser/bookmark_utils.h"
@@ -230,7 +230,8 @@ std::unique_ptr<base::DictionaryValue> HistoryEntryToValue(
} // namespace
BrowsingHistoryHandler::BrowsingHistoryHandler()
- : clock_(new base::DefaultClock()), browsing_history_service_(nullptr) {}
+ : clock_(base::DefaultClock::GetInstance()),
+ browsing_history_service_(nullptr) {}
BrowsingHistoryHandler::~BrowsingHistoryHandler() {}
@@ -242,6 +243,9 @@ void BrowsingHistoryHandler::RegisterMessages() {
ProfileSyncServiceFactory::GetSyncServiceForBrowserContext(profile);
browsing_history_service_ = std::make_unique<BrowsingHistoryService>(
this, local_history, sync_service);
+ // Make sure BrowsingDataRemoverDelegate is initialized and listening
+ // for history deletions.
+ profile->GetBrowsingDataRemoverDelegate();
// Create our favicon data source.
content::URLDataSource::Add(profile, new FaviconSource(profile));
@@ -363,9 +367,8 @@ void BrowsingHistoryHandler::OnQueryComplete(
// Convert the result vector into a ListValue.
base::ListValue results_value;
for (const BrowsingHistoryService::HistoryEntry& entry : results) {
- std::unique_ptr<base::Value> value(
- HistoryEntryToValue(entry, bookmark_model, supervised_user_service,
- sync_service, clock_.get()));
+ std::unique_ptr<base::Value> value(HistoryEntryToValue(
+ entry, bookmark_model, supervised_user_service, sync_service, clock_));
results_value.Append(std::move(value));
}
diff --git a/chromium/chrome/browser/ui/webui/browsing_history_handler.h b/chromium/chrome/browser/ui/webui/browsing_history_handler.h
index 0a300973273..eef6585d166 100644
--- a/chromium/chrome/browser/ui/webui/browsing_history_handler.h
+++ b/chromium/chrome/browser/ui/webui/browsing_history_handler.h
@@ -59,10 +59,9 @@ class BrowsingHistoryHandler : public content::WebUIMessageHandler,
// ProfileBasedBrowsingHistoryDriver implementation.
Profile* GetProfile() override;
- // For tests.
- void set_clock(std::unique_ptr<base::Clock> clock) {
- clock_ = std::move(clock);
- }
+ // For tests. This does not take the ownership of the clock. |clock| must
+ // outlive the BrowsingHistoryHandler instance.
+ void set_clock(base::Clock* clock) { clock_ = clock; }
private:
FRIEND_TEST_ALL_PREFIXES(BrowsingHistoryHandlerTest,
@@ -70,7 +69,7 @@ class BrowsingHistoryHandler : public content::WebUIMessageHandler,
FRIEND_TEST_ALL_PREFIXES(BrowsingHistoryHandlerTest, MdTruncatesTitles);
// The clock used to vend times.
- std::unique_ptr<base::Clock> clock_;
+ base::Clock* clock_;
std::unique_ptr<history::BrowsingHistoryService> browsing_history_service_;
diff --git a/chromium/chrome/browser/ui/webui/browsing_history_handler_unittest.cc b/chromium/chrome/browser/ui/webui/browsing_history_handler_unittest.cc
index ae9c92fa2ec..fb5013eaff2 100644
--- a/chromium/chrome/browser/ui/webui/browsing_history_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/browsing_history_handler_unittest.cc
@@ -57,8 +57,6 @@ base::Time PretendNow() {
return out_time;
}
-void IgnoreBoolAndDoNothing(bool ignored_argument) {}
-
class TestSyncService : public browser_sync::TestProfileSyncService {
public:
explicit TestSyncService(Profile* profile)
@@ -84,17 +82,16 @@ class TestSyncService : public browser_sync::TestProfileSyncService {
class BrowsingHistoryHandlerWithWebUIForTesting
: public BrowsingHistoryHandler {
public:
- explicit BrowsingHistoryHandlerWithWebUIForTesting(content::WebUI* web_ui)
- : test_clock_(new base::SimpleTestClock()) {
- set_clock(base::WrapUnique(test_clock_));
+ explicit BrowsingHistoryHandlerWithWebUIForTesting(content::WebUI* web_ui) {
+ set_clock(&test_clock_);
set_web_ui(web_ui);
- test_clock_->SetNow(PretendNow());
+ test_clock_.SetNow(PretendNow());
}
- base::SimpleTestClock* test_clock() { return test_clock_; }
+ base::SimpleTestClock* test_clock() { return &test_clock_; }
private:
- base::SimpleTestClock* test_clock_;
+ base::SimpleTestClock test_clock_;
};
} // namespace
@@ -167,7 +164,7 @@ class BrowsingHistoryHandlerTest : public ::testing::Test {
// Tests that BrowsingHistoryHandler is informed about WebHistoryService
// deletions.
TEST_F(BrowsingHistoryHandlerTest, ObservingWebHistoryDeletions) {
- base::Callback<void(bool)> callback = base::Bind(&IgnoreBoolAndDoNothing);
+ base::Callback<void(bool)> callback = base::DoNothing();
// BrowsingHistoryHandler is informed about WebHistoryService history
// deletions.
diff --git a/chromium/chrome/browser/ui/webui/certificate_manager_localized_strings_provider.cc b/chromium/chrome/browser/ui/webui/certificate_manager_localized_strings_provider.cc
index 88a3a34ecf0..301701382d3 100644
--- a/chromium/chrome/browser/ui/webui/certificate_manager_localized_strings_provider.cc
+++ b/chromium/chrome/browser/ui/webui/certificate_manager_localized_strings_provider.cc
@@ -39,7 +39,6 @@ void AddLocalizedStrings(content::WebUIDataSource* html_source) {
IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_AND_BIND},
{"certificateManagerExport", IDS_SETTINGS_CERTIFICATE_MANAGER_EXPORT},
{"certificateManagerDelete", IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE},
- {"certificateManagerDone", IDS_SETTINGS_CERTIFICATE_MANAGER_DONE},
{"certificateManagerUntrusted",
IDS_SETTINGS_CERTIFICATE_MANAGER_UNTRUSTED},
// CA trust edit dialog.
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 1edbf61e3a7..8b87a09ec96 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
@@ -9,6 +9,7 @@
#include <vector>
#include "base/bind.h"
+#include "base/feature_list.h"
#include "base/location.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
@@ -62,8 +63,8 @@
#include "chrome/browser/ui/webui/usb_internals/usb_internals_ui.h"
#include "chrome/browser/ui/webui/user_actions/user_actions_ui.h"
#include "chrome/browser/ui/webui/version_ui.h"
+#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_features.h"
-#include "chrome/common/features.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "components/dom_distiller/core/dom_distiller_constants.h"
@@ -75,7 +76,7 @@
#include "components/favicon_base/favicon_util.h"
#include "components/favicon_base/select_favicon_frames.h"
#include "components/history/core/browser/history_types.h"
-#include "components/nacl/common/features.h"
+#include "components/nacl/common/buildflags.h"
#include "components/prefs/pref_service.h"
#include "components/safe_browsing/web_ui/constants.h"
#include "components/safe_browsing/web_ui/safe_browsing_ui.h"
@@ -192,13 +193,10 @@
#include "chrome/browser/ui/webui/local_discovery/local_discovery_ui.h"
#endif
-#if BUILDFLAG(ENABLE_APP_LIST)
-#include "chrome/browser/ui/webui/app_list/start_page_ui.h"
-#endif
-
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "chrome/browser/extensions/extension_web_ui.h"
#include "chrome/browser/ui/webui/extensions/extensions_ui.h"
+#include "chrome/common/chrome_features.h"
#include "chrome/common/extensions/extension_constants.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
@@ -279,6 +277,10 @@ WebUIController* NewWebUI<WelcomeWin10UI>(WebUI* web_ui, const GURL& url) {
#endif // defined(OS_WIN)
bool IsAboutUI(const GURL& url) {
+ if (base::FeatureList::IsEnabled(features::kBundledConnectionHelpFeature) &&
+ url.host_piece() == chrome::kChromeUIConnectionHelpHost) {
+ return true;
+ }
return (url.host_piece() == chrome::kChromeUIChromeURLsHost ||
url.host_piece() == chrome::kChromeUICreditsHost ||
url.host_piece() == chrome::kChromeUIDNSHost
@@ -328,10 +330,7 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
return &NewWebUI<chromeos::DeviceLogUI>;
if (url.host_piece() == chrome::kChromeUIDomainReliabilityInternalsHost)
return &NewWebUI<DomainReliabilityInternalsUI>;
- // TODO(dtrainor): Remove the OffTheRecord check once crbug.com/766363 is
- // fixed.
- if (url.host_piece() == chrome::kChromeUIDownloadInternalsHost &&
- !profile->IsOffTheRecord())
+ if (url.host_piece() == chrome::kChromeUIDownloadInternalsHost)
return &NewWebUI<DownloadInternalsUI>;
if (url.host_piece() == chrome::kChromeUIFlagsHost)
return &NewWebUI<FlagsUI>;
@@ -359,11 +358,11 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
return &NewWebUI<PasswordManagerInternalsUI>;
if (url.host_piece() == chrome::kChromeUIPredictorsHost)
return &NewWebUI<PredictorsUI>;
- if (url.host() == chrome::kChromeUIQuotaInternalsHost)
+ if (url.host_piece() == chrome::kChromeUIQuotaInternalsHost)
return &NewWebUI<QuotaInternalsUI>;
- if (url.host() == safe_browsing::kChromeUISafeBrowsingHost)
+ if (url.host_piece() == safe_browsing::kChromeUISafeBrowsingHost)
return &NewWebUI<safe_browsing::SafeBrowsingUI>;
- if (url.host() == chrome::kChromeUISignInInternalsHost)
+ if (url.host_piece() == chrome::kChromeUISignInInternalsHost)
return &NewWebUI<SignInInternalsUI>;
if (url.host_piece() == chrome::kChromeUISuggestionsHost)
return &NewWebUI<suggestions::SuggestionsUI>;
@@ -546,10 +545,6 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
}
#endif
-#if BUILDFLAG(ENABLE_APP_LIST)
- if (url.host_piece() == chrome::kChromeUIAppListStartPageHost)
- return &NewWebUI<app_list::StartPageUI>;
-#endif
#if BUILDFLAG(ENABLE_EXTENSIONS)
if (url.host_piece() == chrome::kChromeUIExtensionsFrameHost)
return &NewWebUI<extensions::ExtensionsUI>;
@@ -672,7 +667,7 @@ void ChromeWebUIControllerFactory::GetFaviconForURL(
// All extensions but the bookmark manager get their favicon from the icons
// part of the manifest.
if (url.SchemeIs(extensions::kExtensionScheme) &&
- url.host() != extension_misc::kBookmarkManagerId) {
+ url.host_piece() != extension_misc::kBookmarkManagerId) {
ExtensionWebUI::GetFaviconForURL(profile, url, callback);
return;
}
diff --git a/chromium/chrome/browser/ui/webui/chromeos/DEPS b/chromium/chrome/browser/ui/webui/chromeos/DEPS
index 20c74c9134c..2bc7e71f953 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/DEPS
+++ b/chromium/chrome/browser/ui/webui/chromeos/DEPS
@@ -2,7 +2,7 @@ include_rules = [
"+components/login",
"+components/user_manager",
"+media/audio/sounds",
- "+services/device/public/interfaces",
+ "+services/device/public/mojom",
]
specific_include_rules = {
diff --git a/chromium/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
index 675d6a2ca46..5ee6b3b64bf 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
@@ -733,7 +733,7 @@ void DriveInternalsWebUIHandler::UpdateCacheContentsSection(
debug_info_collector->IterateFileCache(
base::Bind(&DriveInternalsWebUIHandler::UpdateCacheEntry,
weak_ptr_factory_.GetWeakPtr()),
- base::Bind(&base::DoNothing));
+ base::DoNothing());
}
void DriveInternalsWebUIHandler::UpdateEventLogSection() {
diff --git a/chromium/chrome/browser/ui/webui/chromeos/image_source.cc b/chromium/chrome/browser/ui/webui/chromeos/image_source.cc
index 514536325e9..67ea4411f91 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/image_source.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/image_source.cc
@@ -17,7 +17,6 @@
#include "base/single_thread_task_runner.h"
#include "base/task_scheduler/post_task.h"
#include "base/task_scheduler/task_scheduler.h"
-#include "base/threading/sequenced_worker_pool.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/chromeos/login/users/avatar/user_image_loader.h"
#include "chrome/common/url_constants.h"
diff --git a/chromium/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc
index 39421136d31..d9eb8871542 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc
@@ -8,7 +8,7 @@
#include <memory>
-#include "ash/public/cpp/ash_switches.h"
+#include "ash/public/cpp/ash_features.h"
#include "ash/shell.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -25,6 +25,7 @@
#include "chrome/grit/generated_resources.h"
#include "chromeos/chromeos_switches.h"
#include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
#include "content/public/browser/page_navigator.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
@@ -76,7 +77,7 @@ struct I18nContentToMessage {
IDS_KEYBOARD_OVERLAY_SYSTEM_MENU_KEY_LABEL},
{"keyboardOverlayLauncherKeyLabel",
IDS_KEYBOARD_OVERLAY_LAUNCHER_KEY_LABEL},
- {"keyboardOverlayLearnMore", IDS_KEYBOARD_OVERLAY_LEARN_MORE},
+ {"keyboardOverlayLearnMore", IDS_LEARN_MORE},
{"keyboardOverlayTitle", IDS_KEYBOARD_OVERLAY_TITLE},
{"keyboardOverlayEscKeyLabel", IDS_KEYBOARD_OVERLAY_ESC_KEY_LABEL},
{"keyboardOverlayBackKeyLabel", IDS_KEYBOARD_OVERLAY_BACK_KEY_LABEL},
@@ -212,14 +213,8 @@ struct I18nContentToMessage {
{"keyboardOverlayMirrorMonitors", IDS_KEYBOARD_OVERLAY_MIRROR_MONITORS},
// TODO(warx): keyboard overlay name for move window between displays
// shortcuts need to be updated when new keyboard shortcuts helper is there.
- {"keyboardOverlayMoveWindowToAboveDisplay",
- IDS_KEYBOARD_OVERLAY_MOVE_WINDOW_TO_ABOVE_DISPLAY},
- {"keyboardOverlayMoveWindowToBelowDisplay",
- IDS_KEYBOARD_OVERLAY_MOVE_WINDOW_TO_BELOW_DISPLAY},
- {"keyboardOverlayMoveWindowToLeftDisplay",
- IDS_KEYBOARD_OVERLAY_MOVE_WINDOW_TO_LEFT_DISPLAY},
- {"keyboardOverlayMoveWindowToRightDisplay",
- IDS_KEYBOARD_OVERLAY_MOVE_WINDOW_TO_RIGHT_DISPLAY},
+ {"keyboardOverlayMoveActiveWindowBetweenDisplays",
+ IDS_KEYBOARD_OVERLAY_MOVE_ACTIVE_WINDOW_BETWEEN_DISPLAYS},
{"keyboardOverlayNewIncognitoWindow",
IDS_KEYBOARD_OVERLAY_NEW_INCOGNITO_WINDOW},
{"keyboardOverlayNewTab", IDS_KEYBOARD_OVERLAY_NEW_TAB},
@@ -342,7 +337,7 @@ content::WebUIDataSource* CreateKeyboardOverlayUIHTMLSource(Profile* profile) {
source->AddBoolean("voiceInteractionEnabled",
chromeos::switches::IsVoiceInteractionEnabled());
source->AddBoolean("displayMoveWindowAccelsEnabled",
- ash::switches::IsDisplayMoveWindowAccelsEnabled());
+ ash::features::IsDisplayMoveWindowAccelsEnabled());
source->AddBoolean("keyboardOverlayUsesLayout2",
ui::DeviceUsesKeyboardLayout2());
ash::Shell* shell = ash::Shell::Get();
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/active_directory_password_change_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/active_directory_password_change_screen_handler.cc
index 6e1171adf37..684dfbfeecf 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/active_directory_password_change_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/active_directory_password_change_screen_handler.cc
@@ -8,6 +8,7 @@
#include "chrome/browser/chromeos/login/oobe_screen.h"
#include "chrome/browser/chromeos/login/screens/core_oobe_view.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/login/auth/authpolicy_login_helper.h"
@@ -77,9 +78,7 @@ void ActiveDirectoryPasswordChangeScreenHandler::HandleCancel() {
}
void ActiveDirectoryPasswordChangeScreenHandler::ShowScreen(
- const std::string& username,
- SigninScreenHandlerDelegate* delegate) {
- delegate_ = delegate;
+ const std::string& username) {
base::DictionaryValue data;
data.SetString(kUsernameKey, username);
ShowScreenWithData(OobeScreen::SCREEN_ACTIVE_DIRECTORY_PASSWORD_CHANGE,
@@ -105,16 +104,16 @@ void ActiveDirectoryPasswordChangeScreenHandler::OnAuthFinished(
!account_info.account_id().empty());
const AccountId account_id = user_manager::known_user::GetAccountId(
username, account_info.account_id(), AccountType::ACTIVE_DIRECTORY);
- DCHECK(delegate_);
- delegate_->SetDisplayAndGivenName(account_info.display_name(),
- account_info.given_name());
+ DCHECK(LoginDisplayHost::default_host());
+ LoginDisplayHost::default_host()->SetDisplayAndGivenName(
+ account_info.display_name(), account_info.given_name());
UserContext user_context(account_id);
user_context.SetKey(key);
user_context.SetAuthFlow(UserContext::AUTH_FLOW_ACTIVE_DIRECTORY);
user_context.SetIsUsingOAuth(false);
user_context.SetUserType(
user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY);
- delegate_->CompleteLogin(user_context);
+ LoginDisplayHost::default_host()->CompleteLogin(user_context);
break;
}
case authpolicy::ERROR_BAD_PASSWORD:
@@ -132,7 +131,7 @@ void ActiveDirectoryPasswordChangeScreenHandler::OnAuthFinished(
break;
default:
NOTREACHED() << "Unhandled error: " << error;
- ShowScreen(username, delegate_);
+ ShowScreen(username);
core_oobe_view_->ShowSignInError(
0, l10n_util::GetStringUTF8(IDS_AD_AUTH_UNKNOWN_ERROR), std::string(),
HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT);
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/active_directory_password_change_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/active_directory_password_change_screen_handler.h
index 83b651b80f6..e53f21e9054 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/active_directory_password_change_screen_handler.h
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/active_directory_password_change_screen_handler.h
@@ -19,7 +19,6 @@ namespace chromeos {
class CoreOobeView;
class Key;
-class SigninScreenHandlerDelegate;
// A class that handles WebUI hooks in Active Directory password change screen.
class ActiveDirectoryPasswordChangeScreenHandler : public BaseScreenHandler {
@@ -42,10 +41,8 @@ class ActiveDirectoryPasswordChangeScreenHandler : public BaseScreenHandler {
const std::string& new_password);
void HandleCancel();
- // Shows the password change screen for |username|. Uses |delegate| to
- // compelete authentication process.
- void ShowScreen(const std::string& username,
- SigninScreenHandlerDelegate* delegate);
+ // Shows the password change screen for |username|.
+ void ShowScreen(const std::string& username);
private:
// Shows the screen with the error message corresponding to |error|.
@@ -63,9 +60,6 @@ class ActiveDirectoryPasswordChangeScreenHandler : public BaseScreenHandler {
// password on the Active Directory server.
std::unique_ptr<AuthPolicyLoginHelper> authpolicy_login_helper_;
- // Non-owned. Used to complete authentication process.
- SigninScreenHandlerDelegate* delegate_ = nullptr;
-
// Non-owned. Used to display signin error.
CoreOobeView* core_oobe_view_ = nullptr;
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc
index 9930d3a2e99..42a867d7dfc 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc
@@ -4,11 +4,13 @@
#include "chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h"
-#include "base/command_line.h"
#include "base/i18n/timezone.h"
+#include "chrome/browser/chromeos/arc/arc_support_host.h"
+#include "chrome/browser/chromeos/arc/arc_util.h"
#include "chrome/browser/chromeos/arc/optin/arc_optin_preference_handler.h"
#include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_view_observer.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/consent_auditor/consent_auditor_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/grit/generated_resources.h"
@@ -17,6 +19,7 @@
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h"
#include "components/arc/arc_prefs.h"
+#include "components/consent_auditor/consent_auditor.h"
#include "components/login/localized_values_builder.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/web_contents.h"
@@ -72,11 +75,6 @@ void ArcTermsOfServiceScreenHandler::OnCurrentScreenChanged(
if (new_screen != OobeScreen::SCREEN_GAIA_SIGNIN)
return;
- const base::CommandLine* command_line =
- base::CommandLine::ForCurrentProcess();
- if (!command_line->HasSwitch(chromeos::switches::kEnableArcOOBEOptIn))
- return;
-
MaybeLoadPlayStoreToS(false);
StartNetworkAndTimeZoneObserving();
}
@@ -102,17 +100,28 @@ void ArcTermsOfServiceScreenHandler::DeclareLocalizedValues(
builder->Add("arcTermsOfServiceRetryButton", IDS_ARC_OOBE_TERMS_BUTTON_RETRY);
builder->Add("arcTermsOfServiceAcceptButton",
IDS_ARC_OOBE_TERMS_BUTTON_ACCEPT);
+ builder->Add("arcTermsOfServiceNextButton",
+ IDS_ARC_OPT_IN_DIALOG_BUTTON_NEXT);
builder->Add("arcPolicyLink", IDS_ARC_OPT_IN_PRIVACY_POLICY_LINK);
builder->Add("arcTextBackupRestore", IDS_ARC_OPT_IN_DIALOG_BACKUP_RESTORE);
builder->Add("arcTextLocationService", IDS_ARC_OPT_IN_LOCATION_SETTING);
+ builder->Add("arcTextPaiService", IDS_ARC_OPT_IN_PAI);
+ builder->Add("arcTextGoogleServiceConfirmation",
+ IDS_ARC_OPT_IN_GOOGLE_SERVICE_CONFIRMATION);
builder->Add("arcLearnMoreStatistics", IDS_ARC_OPT_IN_LEARN_MORE_STATISTICS);
builder->Add("arcLearnMoreLocationService",
IDS_ARC_OPT_IN_LEARN_MORE_LOCATION_SERVICES);
builder->Add("arcLearnMoreBackupAndRestore",
IDS_ARC_OPT_IN_LEARN_MORE_BACKUP_AND_RESTORE);
+ builder->Add("arcLearnMorePaiService", IDS_ARC_OPT_IN_LEARN_MORE_PAI_SERVICE);
builder->Add("arcOverlayClose", IDS_ARC_OOBE_TERMS_POPUP_HELP_CLOSE_BUTTON);
}
+void ArcTermsOfServiceScreenHandler::SendArcManagedStatus(Profile* profile) {
+ CallJS("setArcManaged",
+ arc::IsArcPlayStoreEnabledPreferenceManagedForProfile(profile));
+}
+
void ArcTermsOfServiceScreenHandler::OnMetricsModeChanged(bool enabled,
bool managed) {
const Profile* const profile = ProfileManager::GetActiveUserProfile();
@@ -130,23 +139,29 @@ void ArcTermsOfServiceScreenHandler::OnMetricsModeChanged(bool enabled,
// managed flag.
const bool owner_profile = !owner.is_valid() || user->GetAccountId() == owner;
- if (owner_profile && !managed) {
+ if (owner_profile && !managed && !enabled) {
CallJS("setMetricsMode", base::string16(), false);
} else {
- int message_id = enabled ?
- IDS_ARC_OOBE_TERMS_DIALOG_METRICS_MANAGED_ENABLED :
- IDS_ARC_OOBE_TERMS_DIALOG_METRICS_MANAGED_DISABLED;
+ int message_id;
+ if (owner_profile && !managed) {
+ message_id = IDS_ARC_OOBE_TERMS_DIALOG_METRICS_ENABLED;
+ } else {
+ message_id = enabled ? IDS_ARC_OOBE_TERMS_DIALOG_METRICS_MANAGED_ENABLED
+ : IDS_ARC_OOBE_TERMS_DIALOG_METRICS_MANAGED_DISABLED;
+ }
CallJS("setMetricsMode", l10n_util::GetStringUTF16(message_id), true);
}
}
void ArcTermsOfServiceScreenHandler::OnBackupAndRestoreModeChanged(
bool enabled, bool managed) {
+ backup_restore_managed_ = managed;
CallJS("setBackupAndRestoreMode", enabled, managed);
}
void ArcTermsOfServiceScreenHandler::OnLocationServicesModeChanged(
bool enabled, bool managed) {
+ location_services_managed_ = managed;
CallJS("setLocationServicesMode", enabled, managed);
}
@@ -210,6 +225,7 @@ void ArcTermsOfServiceScreenHandler::DoShow() {
ShowScreen(kScreenId);
+ SendArcManagedStatus(profile);
MaybeLoadPlayStoreToS(true);
StartNetworkAndTimeZoneObserving();
@@ -235,12 +251,40 @@ void ArcTermsOfServiceScreenHandler::HandleSkip() {
void ArcTermsOfServiceScreenHandler::HandleAccept(
bool enable_backup_restore,
- bool enable_location_services) {
+ bool enable_location_services,
+ const std::string& tos_content) {
if (!NeedDispatchEventOnAction())
return;
-
pref_handler_->EnableBackupRestore(enable_backup_restore);
pref_handler_->EnableLocationService(enable_location_services);
+
+ consent_auditor::ConsentAuditor* consent_auditor =
+ ConsentAuditorFactory::GetForProfile(
+ ProfileManager::GetPrimaryUserProfile());
+
+ // Record acceptance of Play ToS.
+ consent_auditor->RecordGaiaConsent(
+ consent_auditor::Feature::PLAY_STORE,
+ ArcSupportHost::ComputePlayToSConsentIds(tos_content),
+ IDS_ARC_OOBE_TERMS_BUTTON_ACCEPT, consent_auditor::ConsentStatus::GIVEN);
+
+ // If the user - not policy - chose Backup and Restore, record consent.
+ if (enable_backup_restore && !backup_restore_managed_) {
+ consent_auditor->RecordGaiaConsent(
+ consent_auditor::Feature::BACKUP_AND_RESTORE,
+ {IDS_ARC_OPT_IN_DIALOG_BACKUP_RESTORE},
+ IDS_ARC_OOBE_TERMS_BUTTON_ACCEPT,
+ consent_auditor::ConsentStatus::GIVEN);
+ }
+
+ // If the user - not policy - chose Location Services, record consent.
+ if (enable_location_services && !location_services_managed_) {
+ consent_auditor->RecordGaiaConsent(
+ consent_auditor::Feature::GOOGLE_LOCATION_SERVICE,
+ {IDS_ARC_OPT_IN_LOCATION_SETTING}, IDS_ARC_OOBE_TERMS_BUTTON_ACCEPT,
+ consent_auditor::ConsentStatus::GIVEN);
+ }
+
for (auto& observer : observer_list_)
observer.OnAccept();
}
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h
index b626fb1fc7b..bac38856df3 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h
@@ -17,6 +17,8 @@
#include "chromeos/network/network_state_handler_observer.h"
#include "chromeos/settings/timezone_settings.h"
+class Profile;
+
namespace arc {
class ArcOptInPreferenceHandler;
}
@@ -66,13 +68,17 @@ class ArcTermsOfServiceScreenHandler
void DoShow();
void HandleSkip();
void HandleAccept(bool enable_backup_restore,
- bool enable_location_services);
+ bool enable_location_services,
+ const std::string& tos_content);
// Loads Play Store ToS content in case default network exists. If
// |ignore_network_state| is set then network state is not checked.
void MaybeLoadPlayStoreToS(bool ignore_network_state);
void StartNetworkAndTimeZoneObserving();
+ // Sends if Arc enable status is manged to screen.
+ void SendArcManagedStatus(Profile* profile);
+
bool NeedDispatchEventOnAction();
// arc::ArcOptInPreferenceHandlerObserver:
@@ -91,6 +97,10 @@ class ArcTermsOfServiceScreenHandler
// To filter out duplicate notifications from html.
bool action_taken_ = false;
+ // To track if optional features are managed preferences.
+ bool backup_restore_managed_ = false;
+ bool location_services_managed_ = false;
+
std::unique_ptr<arc::ArcOptInPreferenceHandler> pref_handler_;
DISALLOW_COPY_AND_ASSIGN(ArcTermsOfServiceScreenHandler);
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
index df9f8651a17..1164b909f83 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
@@ -37,6 +37,7 @@
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/chromeos_constants.h"
+#include "chromeos/chromeos_switches.h"
#include "components/login/base_screen_handler_utils.h"
#include "components/login/localized_values_builder.h"
#include "components/prefs/pref_service.h"
@@ -194,6 +195,7 @@ void CoreOobeHandler::RegisterMessages() {
&CoreOobeHandler::HandleSetOobeBootstrappingSlave);
AddRawCallback("getPrimaryDisplayNameForTesting",
&CoreOobeHandler::HandleGetPrimaryDisplayNameForTesting);
+ AddCallback("setupDemoMode", &CoreOobeHandler::HandleSetupDemoMode);
}
void CoreOobeHandler::ShowSignInError(
@@ -211,34 +213,7 @@ void CoreOobeHandler::ShowTpmError() {
}
void CoreOobeHandler::ShowDeviceResetScreen() {
- // Powerwash is generally not available on enterprise devices. First, check
- // the common case of a correctly enrolled device.
- if (g_browser_process->platform_part()
- ->browser_policy_connector_chromeos()
- ->IsEnterpriseManaged()) {
- // Powerwash not allowed, except if allowed by the admin specifically for
- // the purpose of installing a TPM firmware update.
- tpm_firmware_update::ShouldOfferUpdateViaPowerwash(
- base::Bind([](bool offer_update) {
- if (offer_update) {
- // Force the TPM firmware update option to be enabled.
- g_browser_process->local_state()->SetBoolean(
- prefs::kFactoryResetTPMFirmwareUpdateRequested, true);
- LaunchResetScreen();
- }
- }));
- return;
- }
-
- // Devices that are still in OOBE may be subject to forced re-enrollment (FRE)
- // and thus pending for enterprise management. These should not be allowed to
- // powerwash either. Note that taking consumer device ownership has the side
- // effect of dropping the FRE requirement if it was previously in effect.
- const AutoEnrollmentController::FRERequirement requirement =
- AutoEnrollmentController::GetFRERequirement();
- if (requirement != AutoEnrollmentController::EXPLICITLY_REQUIRED) {
- LaunchResetScreen();
- }
+ LaunchResetScreen();
}
void CoreOobeHandler::ShowEnableDebuggingScreen() {
@@ -402,7 +377,35 @@ void CoreOobeHandler::HandleSkipToUpdateForTesting() {
}
void CoreOobeHandler::HandleToggleResetScreen() {
- ShowDeviceResetScreen();
+ // Powerwash is generally not available on enterprise devices. First, check
+ // the common case of a correctly enrolled device.
+ if (g_browser_process->platform_part()
+ ->browser_policy_connector_chromeos()
+ ->IsEnterpriseManaged()) {
+ // Powerwash is only available if allowed by the admin specifically for the
+ // purpose of installing a TPM firmware update.
+ tpm_firmware_update::ShouldOfferUpdateViaPowerwash(
+ base::BindOnce([](bool offer_update) {
+ if (offer_update) {
+ // Force the TPM firmware update option to be enabled.
+ g_browser_process->local_state()->SetBoolean(
+ prefs::kFactoryResetTPMFirmwareUpdateRequested, true);
+ LaunchResetScreen();
+ }
+ }),
+ base::TimeDelta());
+ return;
+ }
+
+ // Devices that are still in OOBE may be subject to forced re-enrollment (FRE)
+ // and thus pending for enterprise management. These should not be allowed to
+ // powerwash either. Note that taking consumer device ownership has the side
+ // effect of dropping the FRE requirement if it was previously in effect.
+ const AutoEnrollmentController::FRERequirement requirement =
+ AutoEnrollmentController::GetFRERequirement();
+ if (requirement != AutoEnrollmentController::EXPLICITLY_REQUIRED) {
+ LaunchResetScreen();
+ }
}
void CoreOobeHandler::HandleEnableDebuggingScreen() {
@@ -575,6 +578,15 @@ void CoreOobeHandler::HandleGetPrimaryDisplayNameForTesting(
ResolveJavascriptCallback(*callback_id, base::Value(display_name));
}
+void CoreOobeHandler::HandleSetupDemoMode() {
+ const bool is_demo_mode_enabled =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kEnableDemoMode);
+ if (is_demo_mode_enabled) {
+ NOTIMPLEMENTED();
+ }
+}
+
void CoreOobeHandler::InitDemoModeDetection() {
demo_mode_detector_.InitDetection();
}
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
index 2579735ea1f..795c016df47 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
@@ -130,6 +130,7 @@ class CoreOobeHandler : public BaseWebUIHandler,
void HandleHeaderBarVisible();
void HandleSetOobeBootstrappingSlave();
void HandleGetPrimaryDisplayNameForTesting(const base::ListValue* args);
+ void HandleSetupDemoMode();
// When keyboard_utils.js arrow key down event is reached, raise it
// to tab/shift-tab event.
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc
index 07a8fc05c1f..3f3a9c1e27a 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc
@@ -39,8 +39,8 @@
#include "content/public/browser/browser_thread.h"
#include "content/public/common/service_manager_connection.h"
#include "mojo/public/cpp/bindings/interface_request.h"
-#include "services/device/public/interfaces/constants.mojom.h"
-#include "services/device/public/interfaces/wake_lock_provider.mojom.h"
+#include "services/device/public/mojom/constants.mojom.h"
+#include "services/device/public/mojom/wake_lock_provider.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
#include "ui/base/text/bytes_formatting.h"
@@ -255,7 +255,7 @@ namespace chromeos {
EncryptionMigrationScreenHandler::EncryptionMigrationScreenHandler()
: BaseScreenHandler(kScreenId),
- tick_clock_(std::make_unique<base::DefaultTickClock>()),
+ tick_clock_(base::DefaultTickClock::GetInstance()),
weak_ptr_factory_(this) {
set_call_js_prefix(kJsScreenPath);
free_disk_space_fetcher_ = base::Bind(&base::SysInfo::AmountOfFreeDiskSpace,
@@ -386,8 +386,8 @@ void EncryptionMigrationScreenHandler::SetFreeDiskSpaceFetcherForTesting(
}
void EncryptionMigrationScreenHandler::SetTickClockForTesting(
- std::unique_ptr<base::TickClock> tick_clock) {
- tick_clock_ = std::move(tick_clock);
+ base::TickClock* tick_clock) {
+ tick_clock_ = tick_clock;
}
void EncryptionMigrationScreenHandler::RegisterMessages() {
@@ -581,11 +581,13 @@ void EncryptionMigrationScreenHandler::StartMigration() {
void EncryptionMigrationScreenHandler::OnMountExistingVault(
base::Optional<cryptohome::BaseReply> reply) {
- if (cryptohome::BaseReplyToMountError(reply) !=
- cryptohome::MOUNT_ERROR_NONE) {
+ cryptohome::MountError return_code =
+ cryptohome::MountExReplyToMountError(reply);
+ if (return_code != cryptohome::MOUNT_ERROR_NONE) {
RecordMigrationResultMountFailure(IsResumingIncompleteMigration(),
IsArcKiosk());
UpdateUIState(UIState::MIGRATION_FAILED);
+ LOG(ERROR) << "Mount existing vault failed. Error: " << return_code;
return;
}
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.h
index e117c206705..48ce6f487b4 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.h
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.h
@@ -19,7 +19,7 @@
#include "chromeos/dbus/cryptohome_client.h"
#include "chromeos/dbus/power_manager_client.h"
#include "chromeos/login/auth/user_context.h"
-#include "services/device/public/interfaces/wake_lock.mojom.h"
+#include "services/device/public/mojom/wake_lock.mojom.h"
#include "third_party/cros_system_api/dbus/cryptohome/dbus-constants.h"
namespace base {
@@ -64,7 +64,9 @@ class EncryptionMigrationScreenHandler : public EncryptionMigrationScreenView,
FreeDiskSpaceFetcher free_disk_space_fetcher);
// Testing only: Sets the tick clock used to measure elapsed time during
// migration.
- void SetTickClockForTesting(std::unique_ptr<base::TickClock> tick_clock);
+ // This doesn't toke the ownership of the clock. |tick_clock| must outlive the
+ // EncryptionMigrationScreenHandler instance.
+ void SetTickClockForTesting(base::TickClock* tick_clock);
virtual device::mojom::WakeLock* GetWakeLock();
@@ -180,7 +182,7 @@ class EncryptionMigrationScreenHandler : public EncryptionMigrationScreenView,
std::unique_ptr<LoginFeedback> login_feedback_;
// Used to measure elapsed time during migration.
- std::unique_ptr<base::TickClock> tick_clock_;
+ base::TickClock* tick_clock_;
FreeDiskSpaceFetcher free_disk_space_fetcher_;
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler_unittest.cc b/chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler_unittest.cc
index bc669e2c0ca..1e60a10482e 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler_unittest.cc
@@ -69,9 +69,7 @@ class TestEncryptionMigrationScreenHandler
SetFreeDiskSpaceFetcherForTesting(base::BindRepeating(
&TestEncryptionMigrationScreenHandler::FreeDiskSpaceFetcher,
base::Unretained(this)));
- auto tick_clock = std::make_unique<base::SimpleTestTickClock>();
- testing_tick_clock_ = tick_clock.get();
- SetTickClockForTesting(std::move(tick_clock));
+ SetTickClockForTesting(&testing_tick_clock_);
}
// Sets the testing WebUI.
@@ -87,7 +85,7 @@ class TestEncryptionMigrationScreenHandler
// Returns the SimpleTestTickClock used to simulate time elapsed during
// migration.
base::SimpleTestTickClock* testing_tick_clock() {
- return testing_tick_clock_;
+ return &testing_tick_clock_;
}
FakeWakeLock* fake_wake_lock() { return &fake_wake_lock_; }
@@ -102,9 +100,8 @@ class TestEncryptionMigrationScreenHandler
FakeWakeLock fake_wake_lock_;
- // Non-owned pointer. Tick clock used to simulate time elapsed during
- // migration. This is actually owned by the base class.
- base::SimpleTestTickClock* testing_tick_clock_;
+ // Tick clock used to simulate time elapsed during migration.
+ base::SimpleTestTickClock testing_tick_clock_;
int64_t free_disk_space_;
};
@@ -235,6 +232,9 @@ TEST_F(EncryptionMigrationScreenHandlerTest, MinimalMigration) {
EXPECT_TRUE(fake_cryptohome_client_->minimal_migration());
EXPECT_EQ(cryptohome::Identification(user_context_.GetAccountId()),
fake_cryptohome_client_->get_id_for_disk_migrated_to_dircrypto());
+ EXPECT_EQ(
+ user_context_.GetKey()->GetSecret(),
+ fake_cryptohome_client_->get_secret_for_last_mount_authentication());
}
// Tests handling of a resumed minimal migration run. This should behave the
@@ -256,6 +256,9 @@ TEST_F(EncryptionMigrationScreenHandlerTest, ResumeMinimalMigration) {
EXPECT_TRUE(fake_cryptohome_client_->minimal_migration());
EXPECT_EQ(cryptohome::Identification(user_context_.GetAccountId()),
fake_cryptohome_client_->get_id_for_disk_migrated_to_dircrypto());
+ EXPECT_EQ(
+ user_context_.GetKey()->GetSecret(),
+ fake_cryptohome_client_->get_secret_for_last_mount_authentication());
}
// Tests handling of a minimal migration run that takes a long time to finish.
@@ -279,6 +282,9 @@ TEST_F(EncryptionMigrationScreenHandlerTest, MinimalMigrationSlow) {
EXPECT_TRUE(fake_cryptohome_client_->minimal_migration());
EXPECT_EQ(cryptohome::Identification(user_context_.GetAccountId()),
fake_cryptohome_client_->get_id_for_disk_migrated_to_dircrypto());
+ EXPECT_EQ(
+ user_context_.GetKey()->GetSecret(),
+ fake_cryptohome_client_->get_secret_for_last_mount_authentication());
}
// Tests handling of a minimal migration run that fails.
@@ -304,6 +310,9 @@ TEST_F(EncryptionMigrationScreenHandlerTest, MinimalMigrationFails) {
EXPECT_TRUE(fake_cryptohome_client_->minimal_migration());
EXPECT_EQ(cryptohome::Identification(user_context_.GetAccountId()),
fake_cryptohome_client_->get_id_for_disk_migrated_to_dircrypto());
+ EXPECT_EQ(
+ user_context_.GetKey()->GetSecret(),
+ fake_cryptohome_client_->get_secret_for_last_mount_authentication());
}
} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
index 654ddbb4409..8ecb85b0cf4 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
@@ -112,6 +112,37 @@ std::string GetEnterpriseDisplayDomain() {
return connector->GetEnterpriseDisplayDomain();
}
+constexpr struct {
+ int title_id;
+ int subtitle_id;
+ authpolicy::KerberosEncryptionTypes encryption_types;
+} kEncryptionTypes[] = {
+ {IDS_AD_ENCRYPTION_STRONG_TITLE, IDS_AD_ENCRYPTION_STRONG_SUBTITLE,
+ authpolicy::KerberosEncryptionTypes::ENC_TYPES_STRONG},
+ {IDS_AD_ENCRYPTION_ALL_TITLE, IDS_AD_ENCRYPTION_ALL_SUBTITLE,
+ authpolicy::KerberosEncryptionTypes::ENC_TYPES_ALL},
+ {IDS_AD_ENCRYPTION_LEGACY_TITLE, IDS_AD_ENCRYPTION_LEGACY_SUBTITLE,
+ authpolicy::KerberosEncryptionTypes::ENC_TYPES_LEGACY}};
+
+std::unique_ptr<base::ListValue> GetEncryptionTypesList() {
+ const authpolicy::KerberosEncryptionTypes default_types =
+ authpolicy::KerberosEncryptionTypes::ENC_TYPES_STRONG;
+ auto encryption_list = std::make_unique<base::ListValue>();
+ for (const auto& enc_types : kEncryptionTypes) {
+ auto enc_option = std::make_unique<base::DictionaryValue>();
+ enc_option->SetKey(
+ "title", base::Value(l10n_util::GetStringUTF16(enc_types.title_id)));
+ enc_option->SetKey(
+ "subtitle",
+ base::Value(l10n_util::GetStringUTF16(enc_types.subtitle_id)));
+ enc_option->SetKey("value", base::Value(enc_types.encryption_types));
+ enc_option->SetKey(
+ "selected", base::Value(default_types == enc_types.encryption_types));
+ encryption_list->Append(std::move(enc_option));
+ }
+ return encryption_list;
+}
+
} // namespace
// EnrollmentScreenHandler, public ------------------------------
@@ -438,6 +469,12 @@ void EnrollmentScreenHandler::DeclareLocalizedValues(
IDS_ENTERPRISE_ENROLLMENT_KIOSK_LICENSE_TYPE);
builder->Add("licenseCountTemplate",
IDS_ENTERPRISE_ENROLLMENT_LICENSES_REMAINING_TEMPLATE);
+ builder->Add("selectEncryption", IDS_AD_ENCRYPTION_SELECTION_SELECT);
+}
+
+void EnrollmentScreenHandler::GetAdditionalParameters(
+ base::DictionaryValue* parameters) {
+ parameters->Set("encryptionTypesList", GetEncryptionTypesList());
}
bool EnrollmentScreenHandler::IsOnEnrollmentScreen() const {
@@ -574,13 +611,14 @@ void EnrollmentScreenHandler::HandleCompleteLogin(
void EnrollmentScreenHandler::HandleAdCompleteLogin(
const std::string& machine_name,
const std::string& distinguished_name,
+ int encryption_types,
const std::string& user_name,
const std::string& password) {
observe_network_failure_ = false;
DCHECK(controller_);
DCHECK(authpolicy_login_helper_);
authpolicy_login_helper_->JoinAdDomain(
- machine_name, distinguished_name, user_name, password,
+ machine_name, distinguished_name, encryption_types, user_name, password,
base::BindOnce(&EnrollmentScreenHandler::HandleAdDomainJoin,
weak_ptr_factory_.GetWeakPtr(), machine_name, user_name));
}
@@ -639,6 +677,9 @@ void EnrollmentScreenHandler::HandleAdDomainJoin(
case authpolicy::ERROR_SETTING_OU_FAILED:
ShowError(IDS_AD_OU_SETTING_FAILED, true);
return;
+ case authpolicy::ERROR_KDC_DOES_NOT_SUPPORT_ENCRYPTION_TYPE:
+ ShowError(IDS_AD_NOT_SUPPORTED_ENCRYPTION, true);
+ return;
#if !defined(ARCH_CPU_X86_64)
// Currently, the Active Directory integration is only supported on x86_64
// systems. (see https://crbug.com/676602)
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h
index e97c73e7e4d..93bfb3f7289 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h
@@ -72,6 +72,7 @@ class EnrollmentScreenHandler
void Initialize() override;
void DeclareLocalizedValues(
::login::LocalizedValuesBuilder* builder) override;
+ void GetAdditionalParameters(base::DictionaryValue* parameters) override;
// Implements NetworkStateInformer::NetworkStateInformerObserver
void UpdateState(NetworkError::ErrorReason reason) override;
@@ -84,6 +85,7 @@ class EnrollmentScreenHandler
const std::string& auth_code);
void HandleAdCompleteLogin(const std::string& machine_name,
const std::string& distinguished_name,
+ int encryption_types,
const std::string& user_name,
const std::string& password);
void HandleRetry();
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index e5e0153399f..b54c7d66bc8 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -21,16 +21,20 @@
#include "chrome/browser/browser_shutdown.h"
#include "chrome/browser/chromeos/language_preferences.h"
#include "chrome/browser/chromeos/login/lock_screen_utils.h"
+#include "chrome/browser/chromeos/login/reauth_stats.h"
#include "chrome/browser/chromeos/login/screens/network_error.h"
#include "chrome/browser/chromeos/login/signin_partition_manager.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host_webui.h"
#include "chrome/browser/chromeos/login/ui/user_adding_screen.h"
#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
#include "chrome/browser/chromeos/net/network_portal_detector_impl.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
-#include "chrome/browser/chromeos/policy/untrusted_authority_certs_cache.h"
+#include "chrome/browser/chromeos/policy/temp_certs_cache_nss.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/io_thread.h"
+#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/chromeos/login/active_directory_password_change_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h"
@@ -280,7 +284,9 @@ void GaiaScreenHandler::MaybePreloadAuthExtension() {
if (!network_portal_detector_) {
NetworkPortalDetectorImpl* detector = new NetworkPortalDetectorImpl(
- g_browser_process->system_request_context(), false);
+ g_browser_process->system_network_context_manager()
+ ->GetURLLoaderFactory(),
+ false);
detector->set_portal_test_url(GURL(kRestrictiveProxyURL));
network_portal_detector_.reset(detector);
network_portal_detector_->AddObserver(this);
@@ -345,6 +351,11 @@ void GaiaScreenHandler::LoadGaiaWithPartitionAndVersionAndConsent(
UpdateAuthParams(&params, IsRestrictiveProxy());
+ int user_count = LoginDisplayHost::default_host()
+ ? LoginDisplayHost::default_host()->GetUsers().size()
+ : 0;
+ params.SetInteger("userCount", user_count);
+
GaiaScreenMode screen_mode = GetGaiaScreenMode(context.email,
context.use_offline);
params.SetInteger("screenMode", screen_mode);
@@ -450,7 +461,7 @@ void GaiaScreenHandler::DeclareLocalizedValues(
builder->Add("whitelistErrorEnterprise",
IDS_ENTERPRISE_LOGIN_ERROR_WHITELIST);
builder->Add("tryAgainButton", IDS_WHITELIST_ERROR_TRY_AGAIN_BUTTON);
- builder->Add("learnMoreButton", IDS_WHITELIST_ERROR_LEARN_MORE_BUTTON);
+ builder->Add("learnMoreButton", IDS_LEARN_MORE);
builder->Add("gaiaLoading", IDS_LOGIN_GAIA_LOADING_MESSAGE);
// Strings used by the SAML fatal error dialog.
@@ -529,6 +540,11 @@ void GaiaScreenHandler::RegisterMessages() {
&GaiaScreenHandler::HandleCompleteAdAuthentication);
AddCallback("cancelAdAuthentication",
&GaiaScreenHandler::HandleCancelActiveDirectoryAuth);
+ AddRawCallback("showAddUser", &GaiaScreenHandler::HandleShowAddUser);
+ AddCallback("updateGaiaDialogSize",
+ &GaiaScreenHandler::HandleUpdateGaiaDialogSize);
+ AddCallback("updateGaiaDialogVisibility",
+ &GaiaScreenHandler::HandleUpdateGaiaDialogVisibility);
// Allow UMA metrics collection from JS.
web_ui()->AddMessageHandler(std::make_unique<MetricsHandler>());
@@ -553,9 +569,12 @@ void GaiaScreenHandler::OnPortalDetectionCompleted(
}
void GaiaScreenHandler::HandleIdentifierEntered(const std::string& user_email) {
- if (!Delegate()->IsUserWhitelisted(user_manager::known_user::GetAccountId(
- user_email, std::string() /* id */, AccountType::UNKNOWN)))
+ if (LoginDisplayHost::default_host() &&
+ !LoginDisplayHost::default_host()->IsUserWhitelisted(
+ user_manager::known_user::GetAccountId(
+ user_email, std::string() /* id */, AccountType::UNKNOWN))) {
ShowWhitelistCheckFailedError();
+ }
}
void GaiaScreenHandler::HandleAuthExtensionLoaded() {
@@ -626,24 +645,24 @@ void GaiaScreenHandler::DoAdAuth(
switch (error) {
case authpolicy::ERROR_NONE: {
DCHECK(account_info.has_account_id() &&
- !account_info.account_id().empty());
+ !account_info.account_id().empty() &&
+ LoginDisplayHost::default_host());
const AccountId account_id(GetAccountId(
username, account_info.account_id(), AccountType::ACTIVE_DIRECTORY));
- Delegate()->SetDisplayAndGivenName(account_info.display_name(),
- account_info.given_name());
+ LoginDisplayHost::default_host()->SetDisplayAndGivenName(
+ account_info.display_name(), account_info.given_name());
UserContext user_context(account_id);
user_context.SetKey(key);
user_context.SetAuthFlow(UserContext::AUTH_FLOW_ACTIVE_DIRECTORY);
user_context.SetIsUsingOAuth(false);
user_context.SetUserType(
user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY);
- Delegate()->CompleteLogin(user_context);
+ LoginDisplayHost::default_host()->CompleteLogin(user_context);
break;
}
case authpolicy::ERROR_PASSWORD_EXPIRED:
DCHECK(active_directory_password_change_screen_handler_);
- active_directory_password_change_screen_handler_->ShowScreen(username,
- Delegate());
+ active_directory_password_change_screen_handler_->ShowScreen(username);
break;
case authpolicy::ERROR_PARSE_UPN_FAILED:
case authpolicy::ERROR_BAD_USER_NAME:
@@ -667,7 +686,9 @@ void GaiaScreenHandler::DoAdAuth(
void GaiaScreenHandler::HandleCompleteAdAuthentication(
const std::string& username,
const std::string& password) {
- Delegate()->SetDisplayEmail(username);
+ if (LoginDisplayHost::default_host())
+ LoginDisplayHost::default_host()->SetDisplayEmail(username);
+
set_populated_email(username);
DCHECK(authpolicy_login_helper_);
authpolicy_login_helper_->AuthenticateUser(
@@ -688,22 +709,29 @@ void GaiaScreenHandler::HandleCompleteAuthentication(
const std::string& auth_code,
bool using_saml,
const std::string& gaps_cookie) {
- if (!Delegate())
+ if (!LoginDisplayHost::default_host())
return;
DCHECK(!email.empty());
DCHECK(!gaia_id.empty());
const std::string sanitized_email = gaia::SanitizeEmail(email);
- Delegate()->SetDisplayEmail(sanitized_email);
+ LoginDisplayHost::default_host()->SetDisplayEmail(sanitized_email);
UserContext user_context(GetAccountId(email, gaia_id, AccountType::GOOGLE));
user_context.SetKey(Key(password));
+ // Only save the password for enterprise users. See https://crbug.com/386606.
+ const bool is_enterprise_managed = g_browser_process->platform_part()
+ ->browser_policy_connector_chromeos()
+ ->IsEnterpriseManaged();
+ if (is_enterprise_managed) {
+ user_context.SetPasswordKey(Key(password));
+ }
user_context.SetAuthCode(auth_code);
user_context.SetAuthFlow(using_saml
? UserContext::AUTH_FLOW_GAIA_WITH_SAML
: UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML);
user_context.SetGAPSCookie(gaps_cookie);
- Delegate()->CompleteLogin(user_context);
+ LoginDisplayHost::default_host()->CompleteLogin(user_context);
}
void GaiaScreenHandler::HandleCompleteLogin(const std::string& gaia_id,
@@ -748,15 +776,48 @@ void GaiaScreenHandler::HandleGaiaUIReady() {
if (test_expects_complete_login_)
SubmitLoginFormForTest();
- if (Delegate())
- Delegate()->OnGaiaScreenReady();
+
+ if (LoginDisplayHost::default_host())
+ LoginDisplayHost::default_host()->OnGaiaScreenReady();
+}
+
+void GaiaScreenHandler::HandleUpdateGaiaDialogSize(int width, int height) {
+ if (LoginDisplayHost::default_host())
+ LoginDisplayHost::default_host()->UpdateGaiaDialogSize(width, height);
+}
+
+void GaiaScreenHandler::HandleUpdateGaiaDialogVisibility(bool visible) {
+ if (LoginDisplayHost::default_host())
+ LoginDisplayHost::default_host()->UpdateGaiaDialogVisibility(visible);
+}
+
+void GaiaScreenHandler::HandleShowAddUser(const base::ListValue* args) {
+ // TODO(xiaoyinh): Add trace event for gaia webui in views login screen.
+ TRACE_EVENT_ASYNC_STEP_INTO0("ui", "ShowLoginWebUI",
+ LoginDisplayHostWebUI::kShowLoginWebUIid,
+ "ShowAddUser");
+
+ std::string email;
+ // |args| can be null if it's OOBE.
+ if (args)
+ args->GetString(0, &email);
+ set_populated_email(email);
+ if (!email.empty())
+ SendReauthReason(AccountId::FromUserEmail(email));
+ OnShowAddUser();
+}
+
+void GaiaScreenHandler::OnShowAddUser() {
+ signin_screen_handler_->is_account_picker_showing_first_time_ = false;
+ lock_screen_utils::EnforcePolicyInputMethods(std::string());
+ ShowGaiaAsync();
}
void GaiaScreenHandler::DoCompleteLogin(const std::string& gaia_id,
const std::string& typed_email,
const std::string& password,
bool using_saml) {
- if (!Delegate())
+ if (!LoginDisplayHost::default_host())
return;
if (using_saml && !using_saml_api_)
@@ -765,14 +826,14 @@ void GaiaScreenHandler::DoCompleteLogin(const std::string& gaia_id,
DCHECK(!typed_email.empty());
DCHECK(!gaia_id.empty());
const std::string sanitized_email = gaia::SanitizeEmail(typed_email);
- Delegate()->SetDisplayEmail(sanitized_email);
+ LoginDisplayHost::default_host()->SetDisplayEmail(sanitized_email);
UserContext user_context(
GetAccountId(typed_email, gaia_id, AccountType::GOOGLE));
user_context.SetKey(Key(password));
user_context.SetAuthFlow(using_saml
? UserContext::AUTH_FLOW_GAIA_WITH_SAML
: UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML);
- Delegate()->CompleteLogin(user_context);
+ LoginDisplayHost::default_host()->CompleteLogin(user_context);
if (test_expects_complete_login_) {
VLOG(2) << "Complete test login for " << typed_email
@@ -837,7 +898,7 @@ void GaiaScreenHandler::ShowSigninScreenForTest(const std::string& username,
if (frame_state() == GaiaScreenHandler::FRAME_STATE_LOADED) {
SubmitLoginFormForTest();
} else if (frame_state() != GaiaScreenHandler::FRAME_STATE_LOADING) {
- signin_screen_handler_->OnShowAddUser();
+ OnShowAddUser();
}
}
@@ -886,10 +947,9 @@ void GaiaScreenHandler::CancelShowGaiaAsync() {
}
void GaiaScreenHandler::ShowGaiaScreenIfReady() {
- if (!dns_cleared_ ||
- !cookies_cleared_ ||
+ if (!dns_cleared_ || !cookies_cleared_ ||
!show_when_dns_and_cookies_cleared_ ||
- !Delegate()) {
+ !LoginDisplayHost::default_host()) {
return;
}
@@ -903,10 +963,11 @@ void GaiaScreenHandler::ShowGaiaScreenIfReady() {
// Note that LoadAuthExtension clears |populated_email_|.
if (populated_email_.empty()) {
- Delegate()->LoadSigninWallpaper();
+ LoginDisplayHost::default_host()->LoadSigninWallpaper();
} else {
- Delegate()->LoadWallpaper(user_manager::known_user::GetAccountId(
- populated_email_, std::string() /* id */, AccountType::UNKNOWN));
+ LoginDisplayHost::default_host()->LoadWallpaper(
+ user_manager::known_user::GetAccountId(
+ populated_email_, std::string() /* id */, AccountType::UNKNOWN));
}
input_method::InputMethodManager* imm =
@@ -953,8 +1014,8 @@ void GaiaScreenHandler::ShowGaiaScreenIfReady() {
// When the WebUI is destroyed, |untrusted_authority_certs_cache_| will go
// out of scope and the certificates will not be held in memory anymore.
untrusted_authority_certs_cache_ =
- std::make_unique<policy::UntrustedAuthorityCertsCache>(
- policy::UntrustedAuthorityCertsCache::
+ std::make_unique<policy::TempCertsCacheNSS>(
+ policy::TempCertsCacheNSS::
GetUntrustedAuthoritiesFromDeviceOncPolicy());
}
@@ -1027,10 +1088,6 @@ void GaiaScreenHandler::UpdateState(NetworkError::ErrorReason reason) {
signin_screen_handler_->UpdateState(reason);
}
-SigninScreenHandlerDelegate* GaiaScreenHandler::Delegate() {
- return signin_screen_handler_->delegate_;
-}
-
bool GaiaScreenHandler::IsRestrictiveProxy() const {
return !disable_restrictive_proxy_check_for_test_ &&
!IsOnline(captive_portal_status_);
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
index 749c2de039e..f4c4c5150bc 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
@@ -21,7 +21,7 @@
class AccountId;
namespace policy {
-class UntrustedAuthorityCertsCache;
+class TempCertsCacheNSS;
}
namespace chromeos {
@@ -29,7 +29,6 @@ namespace chromeos {
class ActiveDirectoryPasswordChangeScreenHandler;
class Key;
class SigninScreenHandler;
-class SigninScreenHandlerDelegate;
// A class that handles WebUI hooks in Gaia screen.
class GaiaScreenHandler : public BaseScreenHandler,
@@ -53,9 +52,10 @@ class GaiaScreenHandler : public BaseScreenHandler,
// GaiaView:
void MaybePreloadAuthExtension() override;
void DisableRestrictiveProxyCheckForTest() override;
+ void ShowGaiaAsync() override;
private:
- // TODO (antrim@): remove this dependency.
+ // TODO (xiaoyinh): remove this dependency.
friend class SigninScreenHandler;
struct GaiaContext;
@@ -127,6 +127,10 @@ class GaiaScreenHandler : public BaseScreenHandler,
void HandleIdentifierEntered(const std::string& account_identifier);
void HandleAuthExtensionLoaded();
+ void HandleUpdateGaiaDialogSize(int width, int height);
+ void HandleUpdateGaiaDialogVisibility(bool visible);
+ void HandleShowAddUser(const base::ListValue* args);
+ void OnShowAddUser();
// Really handles the complete login message.
void DoCompleteLogin(const std::string& gaia_id,
@@ -163,13 +167,6 @@ class GaiaScreenHandler : public BaseScreenHandler,
// principals API was used during SAML login.
void SetSAMLPrincipalsAPIUsed(bool api_used);
- // Show the sign-in screen. Depending on internal state, the screen will
- // either be shown immediately or after an asynchronous clean-up process that
- // cleans DNS cache and cookies. In the latter case, the request to show the
- // screen can be canceled by calling CancelShowGaiaAsync() while the clean-up
- // is in progress.
- void ShowGaiaAsync();
-
// Cancels the request to show the sign-in screen while the asynchronous
// clean-up process that precedes the screen showing is in progress.
void CancelShowGaiaAsync();
@@ -194,8 +191,6 @@ class GaiaScreenHandler : public BaseScreenHandler,
// Are we on a restrictive proxy?
bool IsRestrictiveProxy() const;
- SigninScreenHandlerDelegate* Delegate();
-
// Returns temporary unused device Id.
std::string GetTemporaryDeviceId();
@@ -283,8 +278,7 @@ class GaiaScreenHandler : public BaseScreenHandler,
// Makes untrusted authority certificates from device policy available for
// client certificate discovery.
- std::unique_ptr<policy::UntrustedAuthorityCertsCache>
- untrusted_authority_certs_cache_;
+ std::unique_ptr<policy::TempCertsCacheNSS> untrusted_authority_certs_cache_;
base::WeakPtrFactory<GaiaScreenHandler> weak_factory_;
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.cc
index 9b1091eeacb..11d88571163 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.cc
@@ -103,8 +103,19 @@ void NetworkDropdownHandler::HandleLaunchAddWiFiNetworkDialog() {
void NetworkDropdownHandler::HandleShowNetworkDetails(
const base::ListValue* args) {
- std::string guid;
- args->GetString(0, &guid);
+ std::string type, guid;
+ args->GetString(0, &type);
+ args->GetString(1, &guid);
+ if (type == ::onc::network_type::kCellular) {
+ // Make sure Cellular is enabled.
+ NetworkStateHandler* handler =
+ NetworkHandler::Get()->network_state_handler();
+ if (handler->GetTechnologyState(NetworkTypePattern::Cellular()) !=
+ NetworkStateHandler::TECHNOLOGY_ENABLED) {
+ handler->SetTechnologyEnabled(NetworkTypePattern::Cellular(), true,
+ network_handler::ErrorCallback());
+ }
+ }
InternetDetailDialog::ShowDialog(guid);
}
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
index 8e783732f12..8225c73c1ea 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
@@ -147,8 +147,6 @@ void NetworkScreenHandler::DeclareLocalizedValues(
builder->Add("networkScreenGreeting", IDS_WELCOME_SCREEN_GREETING);
builder->Add("networkScreenTitle", IDS_WELCOME_SCREEN_TITLE);
- builder->Add("networkScreenAccessibleTitle",
- IDS_NETWORK_SCREEN_ACCESSIBLE_TITLE);
builder->Add("selectLanguage", IDS_LANGUAGE_SELECTION_SELECT);
builder->Add("selectKeyboard", IDS_KEYBOARD_SELECTION_SELECT);
builder->Add("selectNetwork", IDS_NETWORK_SELECTION_SELECT);
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/network_state_informer.cc b/chromium/chrome/browser/ui/webui/chromeos/login/network_state_informer.cc
index e235b926c97..5bf72ab80fd 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/network_state_informer.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/network_state_informer.cc
@@ -17,7 +17,7 @@
#include "chromeos/network/proxy/ui_proxy_config_service.h"
#include "components/proxy_config/proxy_config_dictionary.h"
#include "components/proxy_config/proxy_prefs.h"
-#include "net/proxy/proxy_config.h"
+#include "net/proxy_resolution/proxy_config.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace chromeos {
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc
index 93b5b1f5a49..62fe9bd2e68 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc
@@ -23,8 +23,7 @@ namespace chromeos {
namespace {
bool TouchSupportAvailable(const display::Display& display) {
- return display.touch_support() ==
- display::Display::TouchSupport::TOUCH_SUPPORT_AVAILABLE;
+ return display.touch_support() == display::Display::TouchSupport::AVAILABLE;
}
// TODO(felixe): More context at crbug.com/738885
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_unittest.cc b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_unittest.cc
index 55379f6367d..e63d5176a4f 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_unittest.cc
@@ -97,7 +97,7 @@ TEST_F(OobeDisplayChooserTest, DontSwitchFromTouch) {
display::ManagedDisplayInfo::CreateFromSpecWithID("0+0-3000x2000", 1));
display_info.push_back(
display::ManagedDisplayInfo::CreateFromSpecWithID("3000+0-800x600", 2));
- display_info[0].set_touch_support(display::Display::TOUCH_SUPPORT_AVAILABLE);
+ display_info[0].set_touch_support(display::Display::TouchSupport::AVAILABLE);
display_manager()->OnNativeDisplaysChanged(display_info);
base::RunLoop().RunUntilIdle();
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
index b55575d7d73..87a5e5e2571 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
@@ -96,7 +96,8 @@ const char* kKnownDisplayTypes[] = {OobeUI::kOobeDisplay,
OobeUI::kLockDisplay,
OobeUI::kUserAddingDisplay,
OobeUI::kAppLaunchSplashDisplay,
- OobeUI::kArcKioskSplashDisplay};
+ OobeUI::kArcKioskSplashDisplay,
+ OobeUI::kGaiaSigninDisplay};
const char kStringsJSPath[] = "strings.js";
const char kLockJSPath[] = "lock.js";
@@ -108,10 +109,6 @@ const char kCustomElementsJSPath[] = "custom_elements.js";
const char kCustomElementsUserPodHTMLPath[] = "custom_elements_user_pod.html";
// Paths for deferred resource loading.
-const char kCustomElementsPinKeyboardHTMLPath[] =
- "custom_elements/pin_keyboard.html";
-const char kCustomElementsPinKeyboardJSPath[] =
- "custom_elements/pin_keyboard.js";
const char kEnrollmentHTMLPath[] = "enrollment.html";
const char kEnrollmentCSSPath[] = "enrollment.css";
const char kEnrollmentJSPath[] = "enrollment.js";
@@ -143,42 +140,23 @@ content::WebUIDataSource* CreateOobeUIDataSource(
IDR_CUSTOM_ELEMENTS_OOBE_HTML);
source->AddResourcePath(kCustomElementsJSPath, IDR_CUSTOM_ELEMENTS_OOBE_JS);
} else if (display_type == OobeUI::kLockDisplay) {
- if (command_line->HasSwitch(chromeos::switches::kShowNonMdLogin)) {
- source->SetDefaultResource(IDR_LOCK_HTML);
- source->AddResourcePath(kLockJSPath, IDR_LOCK_JS);
- source->AddResourcePath(kCustomElementsPinKeyboardHTMLPath,
- IDR_CUSTOM_ELEMENTS_PIN_KEYBOARD_HTML);
- source->AddResourcePath(kCustomElementsPinKeyboardJSPath,
- IDR_CUSTOM_ELEMENTS_PIN_KEYBOARD_JS);
- } else {
- source->SetDefaultResource(IDR_MD_LOCK_HTML);
- source->AddResourcePath(kLockJSPath, IDR_MD_LOCK_JS);
- source->AddResourcePath(kCustomElementsPinKeyboardHTMLPath,
- IDR_MD_CUSTOM_ELEMENTS_PIN_KEYBOARD_HTML);
- source->AddResourcePath(kCustomElementsPinKeyboardJSPath,
- IDR_MD_CUSTOM_ELEMENTS_PIN_KEYBOARD_JS);
- }
+ // TODO(crbug.com/810170): Remove the resource files associated with
+ // kShowNonMdLogin switch (IDR_LOCK_HTML/JS and IDR_LOGIN_HTML/JS and the
+ // files those use).
+ source->SetDefaultResource(IDR_MD_LOCK_HTML);
+ source->AddResourcePath(kLockJSPath, IDR_MD_LOCK_JS);
source->AddResourcePath(kCustomElementsHTMLPath,
IDR_CUSTOM_ELEMENTS_LOCK_HTML);
source->AddResourcePath(kCustomElementsJSPath, IDR_CUSTOM_ELEMENTS_LOCK_JS);
source->AddResourcePath(kCustomElementsUserPodHTMLPath,
IDR_CUSTOM_ELEMENTS_USER_POD_HTML);
} else {
- if (command_line->HasSwitch(chromeos::switches::kShowNonMdLogin)) {
- source->SetDefaultResource(IDR_LOGIN_HTML);
- source->AddResourcePath(kLoginJSPath, IDR_LOGIN_JS);
- } else {
- source->SetDefaultResource(IDR_MD_LOGIN_HTML);
- source->AddResourcePath(kLoginJSPath, IDR_MD_LOGIN_JS);
- }
+ source->SetDefaultResource(IDR_MD_LOGIN_HTML);
+ source->AddResourcePath(kLoginJSPath, IDR_MD_LOGIN_JS);
source->AddResourcePath(kCustomElementsHTMLPath,
IDR_CUSTOM_ELEMENTS_LOGIN_HTML);
source->AddResourcePath(kCustomElementsJSPath,
IDR_CUSTOM_ELEMENTS_LOGIN_JS);
- source->AddResourcePath(kCustomElementsPinKeyboardHTMLPath,
- IDR_CUSTOM_ELEMENTS_PIN_KEYBOARD_HTML);
- source->AddResourcePath(kCustomElementsPinKeyboardJSPath,
- IDR_CUSTOM_ELEMENTS_PIN_KEYBOARD_JS);
source->AddResourcePath(kCustomElementsUserPodHTMLPath,
IDR_CUSTOM_ELEMENTS_USER_POD_HTML);
}
@@ -245,6 +223,7 @@ const char OobeUI::kLockDisplay[] = "lock";
const char OobeUI::kUserAddingDisplay[] = "user-adding";
const char OobeUI::kAppLaunchSplashDisplay[] = "app-launch-splash";
const char OobeUI::kArcKioskSplashDisplay[] = "arc-kiosk-splash";
+const char OobeUI::kGaiaSigninDisplay[] = "gaia-signin";
OobeUI::OobeUI(content::WebUI* web_ui, const GURL& url)
: WebUIController(web_ui) {
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.h b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
index 0f97d1acbf9..4a1eb7d2094 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
@@ -79,6 +79,7 @@ class OobeUI : public content::WebUIController,
static const char kUserAddingDisplay[];
static const char kAppLaunchSplashDisplay[];
static const char kArcKioskSplashDisplay[];
+ static const char kGaiaSigninDisplay[];
class Observer {
public:
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index 07b957a72b2..aebb3b11c28 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -56,7 +56,6 @@
#include "chrome/browser/chromeos/login/ui/login_display_webui.h"
#include "chrome/browser/chromeos/login/ui/login_feedback.h"
#include "chrome/browser/chromeos/login/users/multi_profile_user_controller.h"
-#include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/policy/device_local_account.h"
@@ -92,6 +91,7 @@
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/proximity_auth/screenlock_bridge.h"
+#include "components/strings/grit/components_strings.h"
#include "components/user_manager/known_user.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
@@ -418,7 +418,7 @@ void SigninScreenHandler::DeclareLocalizedValues(
IDS_LOGIN_PUBLIC_ACCOUNT_ENTER_ACCESSIBLE_NAME);
builder->Add("publicAccountMonitoringWarning",
IDS_LOGIN_PUBLIC_ACCOUNT_MONITORING_WARNING);
- builder->Add("publicAccountLearnMore", IDS_LOGIN_PUBLIC_ACCOUNT_LEARN_MORE);
+ builder->Add("publicAccountLearnMore", IDS_LEARN_MORE);
builder->Add("publicAccountMonitoringInfo",
IDS_LOGIN_PUBLIC_ACCOUNT_MONITORING_INFO);
builder->Add("publicAccountMonitoringInfoItem1",
@@ -494,7 +494,6 @@ void SigninScreenHandler::RegisterMessages() {
&SigninScreenHandler::HandleLaunchPublicSession);
AddRawCallback("offlineLogin", &SigninScreenHandler::HandleOfflineLogin);
AddCallback("rebootSystem", &SigninScreenHandler::HandleRebootSystem);
- AddRawCallback("showAddUser", &SigninScreenHandler::HandleShowAddUser);
AddCallback("shutdownSystem", &SigninScreenHandler::HandleShutdownSystem);
AddCallback("removeUser", &SigninScreenHandler::HandleRemoveUser);
AddCallback("toggleEnrollmentScreen",
@@ -622,7 +621,7 @@ void SigninScreenHandler::ShowImpl() {
if (oobe_ui_) {
// Shows new user sign-in for OOBE.
- OnShowAddUser();
+ gaia_screen_handler_->OnShowAddUser();
} else {
// Populates account picker. Animation is turned off for now until we
// figure out how to make it fast enough. This will call LoadUsers.
@@ -987,7 +986,7 @@ void SigninScreenHandler::OnUserRemoved(const AccountId& account_id,
bool last_user_removed) {
CallJS("login.AccountPickerScreen.removeUser", account_id);
if (last_user_removed)
- OnShowAddUser();
+ gaia_screen_handler_->OnShowAddUser();
}
void SigninScreenHandler::OnUserImageChanged(const user_manager::User& user) {
@@ -1018,7 +1017,7 @@ void SigninScreenHandler::OnPreferencesChanged() {
!delegate_->IsShowUsers()) {
// We are at the account picker screen and the POD setting has changed
// to be disabled. We need to show the add user page.
- HandleShowAddUser(nullptr);
+ gaia_screen_handler_->HandleShowAddUser(nullptr);
return;
}
@@ -1182,6 +1181,13 @@ void SigninScreenHandler::HandleAuthenticateUser(const AccountId& account_id,
UserContext user_context(account_id);
user_context.SetKey(Key(password));
+ // Only save the password for enterprise users. See https://crbug.com/386606.
+ const bool is_enterprise_managed = g_browser_process->platform_part()
+ ->browser_policy_connector_chromeos()
+ ->IsEnterpriseManaged();
+ if (is_enterprise_managed) {
+ user_context.SetPasswordKey(Key(password));
+ }
user_context.SetIsUsingPin(authenticated_by_pin);
const user_manager::User* user =
user_manager::UserManager::Get()->FindUser(account_id);
@@ -1269,20 +1275,6 @@ void SigninScreenHandler::HandleRemoveUser(const AccountId& account_id) {
UpdateAddButtonStatus();
}
-void SigninScreenHandler::HandleShowAddUser(const base::ListValue* args) {
- TRACE_EVENT_ASYNC_STEP_INTO0("ui", "ShowLoginWebUI",
- LoginDisplayHostWebUI::kShowLoginWebUIid,
- "ShowAddUser");
- std::string email;
- // |args| can be null if it's OOBE.
- if (args)
- args->GetString(0, &email);
- gaia_screen_handler_->set_populated_email(email);
- if (!email.empty())
- SendReauthReason(AccountId::FromUserEmail(email));
- OnShowAddUser();
-}
-
void SigninScreenHandler::HandleToggleEnrollmentScreen() {
if (delegate_)
delegate_->ShowEnterpriseEnrollmentScreen();
@@ -1433,7 +1425,7 @@ void SigninScreenHandler::HandleLoginUIStateChanged(const std::string& source,
// On slow devices, the wallpaper animation is not shown initially, so we
// must explicitly load the wallpaper. This is also the case for the
// account-picker and gaia-signin UI states.
- delegate_->LoadSigninWallpaper();
+ LoginDisplayHost::default_host()->LoadSigninWallpaper();
HandleToggleKioskAutolaunchScreen();
return;
}
@@ -1477,8 +1469,8 @@ void SigninScreenHandler::HandleFocusPod(const AccountId& account_id,
lock_screen_utils::SetUserInputMethod(account_id.GetUserEmail(),
ime_state_.get());
lock_screen_utils::SetKeyboardSettings(account_id);
- if (delegate_ && load_wallpaper)
- delegate_->LoadWallpaper(account_id);
+ if (LoginDisplayHost::default_host() && load_wallpaper)
+ LoginDisplayHost::default_host()->LoadWallpaper(account_id);
bool use_24hour_clock = false;
if (user_manager::known_user::GetBooleanPref(
@@ -1650,12 +1642,6 @@ bool SigninScreenHandler::IsGuestSigninAllowed() const {
return allow_guest;
}
-void SigninScreenHandler::OnShowAddUser() {
- is_account_picker_showing_first_time_ = false;
- lock_screen_utils::EnforcePolicyInputMethods(std::string());
- gaia_screen_handler_->ShowGaiaAsync();
-}
-
net::Error SigninScreenHandler::FrameError() const {
return gaia_screen_handler_->frame_error();
}
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
index d63b0652fe9..b2a51bc7ad8 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
@@ -143,18 +143,10 @@ class SigninScreenHandlerDelegate {
// Signs out if the screen is currently locked.
virtual void Signout() = 0;
- // --------------- Account creation methods.
- // Confirms sign up by provided credentials in |user_context|.
- // Used for new user login via GAIA extension.
- virtual void CompleteLogin(const UserContext& user_context) = 0;
-
// --------------- Shared with login display methods.
// Notify the delegate when the sign-in UI is finished loading.
virtual void OnSigninScreenReady() = 0;
- // Notify the delegate when the GAIA UI is finished loading.
- virtual void OnGaiaScreenReady() = 0;
-
// Shows Enterprise Enrollment screen.
virtual void ShowEnterpriseEnrollmentScreen() = 0;
@@ -173,26 +165,10 @@ class SigninScreenHandlerDelegate {
// Show update required screen.
virtual void ShowUpdateRequiredScreen() = 0;
- // Sets the displayed email for the next login attempt. If it succeeds,
- // user's displayed email value will be updated to |email|.
- virtual void SetDisplayEmail(const std::string& email) = 0;
-
- // Sets the displayed name and given name for the next login attempt. If it
- // succeeds, user's displayed name and give name values will be updated to
- // |display_name| and |given_name|.
- virtual void SetDisplayAndGivenName(const std::string& display_name,
- const std::string& given_name) = 0;
-
// --------------- Rest of the methods.
// Cancels user adding.
virtual void CancelUserAdding() = 0;
- // Load wallpaper for given |account_id|.
- virtual void LoadWallpaper(const AccountId& account_id) = 0;
-
- // Loads the default sign-in wallpaper.
- virtual void LoadSigninWallpaper() = 0;
-
// Attempts to remove given user.
virtual void RemoveUser(const AccountId& account_id) = 0;
@@ -224,9 +200,6 @@ class SigninScreenHandlerDelegate {
// Runs an OAuth token validation check for user.
virtual void CheckUserStatus(const AccountId& account_id) = 0;
- // Returns true if user is allowed to log in by domain policy.
- virtual bool IsUserWhitelisted(const AccountId& account_id) = 0;
-
protected:
virtual ~SigninScreenHandlerDelegate() {}
};
@@ -398,7 +371,6 @@ class SigninScreenHandler
void HandleShutdownSystem();
void HandleRebootSystem();
void HandleRemoveUser(const AccountId& account_id);
- void HandleShowAddUser(const base::ListValue* args);
void HandleToggleEnrollmentScreen();
void HandleToggleEnrollmentAd();
void HandleToggleEnableDebuggingScreen();
@@ -468,9 +440,6 @@ class SigninScreenHandler
bool ShouldLoadGaia() const;
- // Shows signin.
- void OnShowAddUser();
-
net::Error FrameError() const;
// input_method::ImeKeyboard::Observer implementation:
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/signin_userlist_unittest.cc b/chromium/chrome/browser/ui/webui/chromeos/login/signin_userlist_unittest.cc
index f80653a91ac..54e29d5cec6 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/signin_userlist_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/signin_userlist_unittest.cc
@@ -12,7 +12,6 @@
#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/chromeos/login/users/multi_profile_user_controller.h"
#include "chrome/browser/chromeos/login/users/multi_profile_user_controller_delegate.h"
-#include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/chromeos/settings/device_settings_service.h"
#include "chrome/browser/ui/ash/test_wallpaper_controller.h"
@@ -68,7 +67,6 @@ class SigninPrepareUserListTest : public ash::AshTestBase,
fake_user_manager_->set_owner_id(AccountId::FromUserEmail(kOwner));
- chromeos::WallpaperManager::Initialize();
chromeos::DeviceSettingsService::Initialize();
chromeos::CrosSettings::Initialize();
wallpaper_controller_client_ =
@@ -78,7 +76,6 @@ class SigninPrepareUserListTest : public ash::AshTestBase,
}
void TearDown() override {
- chromeos::WallpaperManager::Shutdown();
controller_.reset();
profile_manager_.reset();
wallpaper_controller_client_.reset();
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc
index f6838cd88dd..1936b235f4c 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc
@@ -15,8 +15,8 @@
#include "chrome/browser/chromeos/login/supervised/supervised_user_creation_flow.h"
#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
#include "chrome/browser/chromeos/login/users/supervised_user_manager.h"
-#include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/ui/ash/wallpaper_controller_client.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/grit/generated_resources.h"
@@ -300,7 +300,7 @@ void SupervisedUserCreationScreenHandler::HandleManagerSelected(
const AccountId& manager_id) {
if (!delegate_)
return;
- WallpaperManager::Get()->ShowUserWallpaper(manager_id);
+ WallpaperControllerClient::Get()->ShowUserWallpaper(manager_id);
}
void SupervisedUserCreationScreenHandler::HandleImportUserSelected(
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/sync_consent_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/sync_consent_screen_handler.cc
index 825e79595a6..4d1b2f4a302 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/sync_consent_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/sync_consent_screen_handler.cc
@@ -35,31 +35,10 @@ void SyncConsentScreenHandler::DeclareLocalizedValues(
builder->Add(
"syncConsentScreenPersonalizeGoogleServicesDescription",
IDS_LOGIN_SYNC_CONSENT_SCREEN_PERSONALIZE_GOOGLE_SERVICES_DESCRIPTION);
- builder->Add("syncConsentScreenSettingsLink",
- IDS_LOGIN_SYNC_CONSENT_SCREEN_SETTINGS_LINK);
- builder->Add("syncConsentSettingsDialogTitle",
- IDS_LOGIN_SYNC_CONSENT_SETTINGS_TITLE);
- builder->Add("syncConsentSettingsDialogSubTitle",
- IDS_LOGIN_SYNC_CONSENT_SETTINGS_SUBTITLE);
- builder->Add("syncConsentSyncAllOptionTitle",
- IDS_LOGIN_SYNC_CONSENT_SYNC_ALL_OPTION);
- builder->Add("syncConsentSyncAllOptionOn",
- IDS_LOGIN_SYNC_CONSENT_SYNC_ALL_OPTION_ON);
- builder->Add("syncConsentSyncAllOptionOff",
- IDS_LOGIN_SYNC_CONSENT_SYNC_ALL_OPTION_OFF);
- builder->Add("syncConsentSettingsStatusSyncAllOn",
- IDS_LOGIN_SYNC_CONSENT_STATUS_SYNC_ALL_ON);
- builder->Add("syncConsentSettingsStatusSyncAllOff",
- IDS_LOGIN_SYNC_CONSENT_STATUS_SYNC_ALL_OFF);
- builder->Add("syncConsentSettingsSaveAndContinue",
- IDS_LOGIN_SYNC_CONSENT_SAVE_AND_CONTINUE);
-}
-
-void SyncConsentScreenHandler::RegisterMessages() {
- BaseScreenHandler::RegisterMessages();
-
- AddCallback("syncEverythingChanged",
- &SyncConsentScreenHandler::HandleSyncEverythingChanged);
+ builder->Add("syncConsentReviewSyncOptionsText",
+ IDS_LOGIN_SYNC_CONSENT_SCREEN_REVIEW_SYNC_OPTIONS_LATER);
+ builder->Add("syncConsentAcceptAndContinue",
+ IDS_LOGIN_SYNC_CONSENT_SCREEN_ACCEPT_AND_CONTINUE);
}
void SyncConsentScreenHandler::Bind(SyncConsentScreen* screen) {
@@ -75,14 +54,4 @@ void SyncConsentScreenHandler::Hide() {}
void SyncConsentScreenHandler::Initialize() {}
-void SyncConsentScreenHandler::HandleSyncEverythingChanged(
- bool sync_everything) {
- screen_->SetSyncAllValue(sync_everything);
-}
-
-void SyncConsentScreenHandler::OnUserPrefKnown(bool sync_everything,
- bool is_managed) {
- CallJS("onUserSyncPrefsKnown", sync_everything, is_managed);
-}
-
} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/sync_consent_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/sync_consent_screen_handler.h
index 993601791f1..6e8242bf95e 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/sync_consent_screen_handler.h
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/sync_consent_screen_handler.h
@@ -24,21 +24,15 @@ class SyncConsentScreenHandler : public BaseScreenHandler,
void DeclareLocalizedValues(
::login::LocalizedValuesBuilder* builder) override;
- // WebUIMessageHandler:
- void RegisterMessages() override;
-
// SyncConsentScreenView:
void Bind(SyncConsentScreen* screen) override;
void Show() override;
void Hide() override;
- void OnUserPrefKnown(bool sync_everything, bool is_managed) override;
private:
// BaseScreenHandler:
void Initialize() override;
- // WebUI message handlers:
- void HandleSyncEverythingChanged(bool sync_everything);
SyncConsentScreen* screen_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(SyncConsentScreenHandler);
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc
index e927f9b8cdc..a1afa85efa7 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc
@@ -21,6 +21,7 @@
#include "chrome/common/pref_names.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
+#include "components/language/core/common/locale_util.h"
#include "components/login/localized_values_builder.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user.h"
@@ -81,12 +82,13 @@ void TermsOfServiceScreenHandler::Show() {
return;
}
- const std::string locale =
+ std::string locale =
ProfileHelper::Get()
->GetProfileByUserUnsafe(
user_manager::UserManager::Get()->GetActiveUser())
->GetPrefs()
->GetString(prefs::kApplicationLocale);
+ language::ConvertToActualUILocale(&locale);
if (locale.empty() || locale == g_browser_process->GetApplicationLocale()) {
// If the user has not chosen a UI locale yet or the chosen locale matches
diff --git a/chromium/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.cc b/chromium/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.cc
index 3bca92cad9a..fc02a996fb7 100644
--- a/chromium/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.cc
+++ b/chromium/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.cc
@@ -32,6 +32,7 @@ struct {
{"networkListItemConnecting", IDS_STATUSBAR_NETWORK_DEVICE_CONNECTING},
{"networkListItemConnectingTo", IDS_NETWORK_LIST_CONNECTING_TO},
{"networkListItemInitializing", IDS_NETWORK_LIST_INITIALIZING},
+ {"networkListItemScanning", IDS_SETTINGS_INTERNET_MOBILE_SEARCH},
{"networkListItemNotConnected", IDS_NETWORK_LIST_NOT_CONNECTED},
{"networkListItemNoNetwork", IDS_NETWORK_LIST_NO_NETWORK},
{"vpnNameTemplate", IDS_NETWORK_LIST_THIRD_PARTY_VPN_NAME_TEMPLATE},
@@ -294,6 +295,9 @@ void AddErrorLocalizedStrings(content::WebUIDataSource* html_source) {
{"Error.PolicyControlled", IDS_NETWORK_ERROR_POLICY_CONTROLLED},
{"networkErrorNoUserCertificate", IDS_NETWORK_ERROR_NO_USER_CERT},
{"networkErrorUnknown", IDS_NETWORK_ERROR_UNKNOWN},
+ // TODO(stevenjb): Move this id to settings_strings.grdp:
+ {"networkErrorNotHardwareBacked",
+ IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_REQUIRE_HARDWARE_BACKED},
};
for (const auto& entry : localized_strings)
html_source->AddLocalizedString(entry.name, entry.id);
diff --git a/chromium/chrome/browser/ui/webui/components_ui.cc b/chromium/chrome/browser/ui/webui/components_ui.cc
index 1d24e81c5a6..f58e2500837 100644
--- a/chromium/chrome/browser/ui/webui/components_ui.cc
+++ b/chromium/chrome/browser/ui/webui/components_ui.cc
@@ -214,6 +214,8 @@ base::string16 ComponentsUI::ComponentEventToString(Events event) {
return l10n_util::GetStringUTF16(IDS_COMPONENTS_EVT_STATUS_UPDATED);
case Events::COMPONENT_NOT_UPDATED:
return l10n_util::GetStringUTF16(IDS_COMPONENTS_EVT_STATUS_NOTUPDATED);
+ case Events::COMPONENT_UPDATE_ERROR:
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_EVT_STATUS_UPDATE_ERROR);
case Events::COMPONENT_UPDATE_DOWNLOADING:
return l10n_util::GetStringUTF16(IDS_COMPONENTS_EVT_STATUS_DOWNLOADING);
}
@@ -245,7 +247,7 @@ base::string16 ComponentsUI::ServiceStatusToString(
case update_client::ComponentState::kUpToDate:
return l10n_util::GetStringUTF16(IDS_COMPONENTS_SVC_STATUS_UPTODATE);
case update_client::ComponentState::kUpdateError:
- return l10n_util::GetStringUTF16(IDS_COMPONENTS_SVC_STATUS_NOUPDATE);
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_SVC_STATUS_UPDATE_ERROR);
case update_client::ComponentState::kUninstalled: // Fall through.
case update_client::ComponentState::kRun:
case update_client::ComponentState::kLastStatus:
diff --git a/chromium/chrome/browser/ui/webui/devtools_ui.cc b/chromium/chrome/browser/ui/webui/devtools_ui.cc
index 37c210a2fdc..4bae28351f1 100644
--- a/chromium/chrome/browser/ui/webui/devtools_ui.cc
+++ b/chromium/chrome/browser/ui/webui/devtools_ui.cc
@@ -4,17 +4,21 @@
#include "chrome/browser/ui/webui/devtools_ui.h"
+#include <list>
+#include <utility>
+
#include "base/command_line.h"
#include "base/macros.h"
#include "base/memory/ref_counted_memory.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/devtools/url_constants.h"
-#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/url_constants.h"
+#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/devtools_frontend_host.h"
+#include "content/public/browser/storage_partition.h"
#include "content/public/browser/url_data_source.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
@@ -22,9 +26,8 @@
#include "net/base/filename_util.h"
#include "net/base/load_flags.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/simple_url_loader.h"
#include "third_party/WebKit/public/public_features.h"
using content::BrowserThread;
@@ -37,7 +40,11 @@ std::string PathWithoutParams(const std::string& path) {
.path().substr(1);
}
-const char kHttpNotFound[] = "HTTP/1.1 404 Not Found\n\n";
+scoped_refptr<base::RefCountedMemory> CreateNotFoundResponse() {
+ const char kHttpNotFound[] = "HTTP/1.1 404 Not Found\n\n";
+ return base::MakeRefCounted<base::RefCountedStaticMemory>(
+ kHttpNotFound, strlen(kHttpNotFound));
+}
// DevToolsDataSource ---------------------------------------------------------
@@ -73,12 +80,13 @@ std::string GetMimeTypeForPath(const std::string& path) {
// 2. /remote/: remote DevTools frontend is served from App Engine.
// 3. /custom/: custom DevTools frontend is served from the server as specified
// by the --custom-devtools-frontend flag.
-class DevToolsDataSource : public content::URLDataSource,
- public net::URLFetcherDelegate {
+class DevToolsDataSource : public content::URLDataSource {
public:
using GotDataCallback = content::URLDataSource::GotDataCallback;
- explicit DevToolsDataSource(net::URLRequestContextGetter* request_context);
+ explicit DevToolsDataSource(
+ scoped_refptr<content::SharedURLLoaderFactory> url_loader_factory)
+ : url_loader_factory_(std::move(url_loader_factory)) {}
// content::URLDataSource implementation.
std::string GetSource() const override;
@@ -89,14 +97,18 @@ class DevToolsDataSource : public content::URLDataSource,
const GotDataCallback& callback) override;
private:
+ struct PendingRequest;
+
+ ~DevToolsDataSource() override = default;
+
// content::URLDataSource overrides.
std::string GetMimeType(const std::string& path) const override;
bool ShouldAddContentSecurityPolicy() const override;
bool ShouldDenyXFrameOptions() const override;
bool ShouldServeMimeTypeAsContentTypeHeader() const override;
- // net::URLFetcherDelegate overrides.
- void OnURLFetchComplete(const net::URLFetcher* source) override;
+ void OnLoadComplete(std::list<PendingRequest>::iterator request_iter,
+ std::unique_ptr<std::string> response_body);
// Serves bundled DevTools frontend from ResourceBundle.
void StartBundledDataRequest(const std::string& path,
@@ -110,28 +122,33 @@ class DevToolsDataSource : public content::URLDataSource,
void StartCustomDataRequest(const GURL& url,
const GotDataCallback& callback);
- ~DevToolsDataSource() override;
+ void StartNetworkRequest(
+ const GURL& url,
+ const net::NetworkTrafficAnnotationTag& traffic_annotation,
+ int load_flags,
+ const GotDataCallback& callback);
- scoped_refptr<net::URLRequestContextGetter> request_context_;
+ struct PendingRequest {
+ PendingRequest() = default;
+ PendingRequest(PendingRequest&& other) = default;
+ PendingRequest& operator=(PendingRequest&& other) = default;
- using PendingRequestsMap = std::map<const net::URLFetcher*, GotDataCallback>;
- PendingRequestsMap pending_;
+ ~PendingRequest() {
+ if (callback)
+ callback.Run(CreateNotFoundResponse());
+ }
- DISALLOW_COPY_AND_ASSIGN(DevToolsDataSource);
-};
+ GotDataCallback callback;
+ std::unique_ptr<network::SimpleURLLoader> loader;
-DevToolsDataSource::DevToolsDataSource(
- net::URLRequestContextGetter* request_context)
- : request_context_(request_context) {
-}
+ DISALLOW_COPY_AND_ASSIGN(PendingRequest);
+ };
-DevToolsDataSource::~DevToolsDataSource() {
- for (const auto& pair : pending_) {
- delete pair.first;
- pair.second.Run(
- new base::RefCountedStaticMemory(kHttpNotFound, strlen(kHttpNotFound)));
- }
-}
+ scoped_refptr<content::SharedURLLoaderFactory> url_loader_factory_;
+ std::list<PendingRequest> pending_requests_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsDataSource);
+};
std::string DevToolsDataSource::GetSource() const {
return chrome::kChromeUIDevToolsHost;
@@ -171,8 +188,7 @@ void DevToolsDataSource::StartDataRequest(
StartRemoteDataRequest(url, callback);
} else {
DLOG(ERROR) << "Refusing to load invalid remote front-end URL";
- callback.Run(new base::RefCountedStaticMemory(kHttpNotFound,
- strlen(kHttpNotFound)));
+ callback.Run(CreateNotFoundResponse());
}
return;
}
@@ -261,20 +277,15 @@ void DevToolsDataSource::StartRemoteDataRequest(
}
}
})");
- net::URLFetcher* fetcher = net::URLFetcher::Create(url, net::URLFetcher::GET,
- this, traffic_annotation)
- .release();
- pending_[fetcher] = callback;
- fetcher->SetRequestContext(request_context_.get());
- fetcher->Start();
+
+ StartNetworkRequest(url, traffic_annotation, net::LOAD_NORMAL, callback);
}
void DevToolsDataSource::StartCustomDataRequest(
const GURL& url,
const content::URLDataSource::GotDataCallback& callback) {
if (!url.is_valid()) {
- callback.Run(
- new base::RefCountedStaticMemory(kHttpNotFound, strlen(kHttpNotFound)));
+ callback.Run(CreateNotFoundResponse());
return;
}
net::NetworkTrafficAnnotationTag traffic_annotation =
@@ -305,24 +316,38 @@ void DevToolsDataSource::StartCustomDataRequest(
}
}
})");
- net::URLFetcher* fetcher = net::URLFetcher::Create(url, net::URLFetcher::GET,
- this, traffic_annotation)
- .release();
- pending_[fetcher] = callback;
- fetcher->SetRequestContext(request_context_.get());
- fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE);
- fetcher->Start();
+
+ StartNetworkRequest(url, traffic_annotation, net::LOAD_DISABLE_CACHE,
+ callback);
}
-void DevToolsDataSource::OnURLFetchComplete(const net::URLFetcher* source) {
- DCHECK(source);
- PendingRequestsMap::iterator it = pending_.find(source);
- DCHECK(it != pending_.end());
- std::string response;
- source->GetResponseAsString(&response);
- delete source;
- it->second.Run(base::RefCountedString::TakeString(&response));
- pending_.erase(it);
+void DevToolsDataSource::StartNetworkRequest(
+ const GURL& url,
+ const net::NetworkTrafficAnnotationTag& traffic_annotation,
+ int load_flags,
+ const GotDataCallback& callback) {
+ auto request = std::make_unique<network::ResourceRequest>();
+ request->url = url;
+ request->load_flags = load_flags;
+
+ auto request_iter = pending_requests_.emplace(pending_requests_.begin());
+ request_iter->callback = callback;
+ request_iter->loader =
+ network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
+ request_iter->loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ url_loader_factory_.get(),
+ base::BindOnce(&DevToolsDataSource::OnLoadComplete,
+ base::Unretained(this), request_iter));
+}
+
+void DevToolsDataSource::OnLoadComplete(
+ std::list<PendingRequest>::iterator request_iter,
+ std::unique_ptr<std::string> response_body) {
+ std::move(request_iter->callback)
+ .Run(response_body
+ ? base::RefCountedString::TakeString(response_body.get())
+ : CreateNotFoundResponse());
+ pending_requests_.erase(request_iter);
}
} // namespace
@@ -377,11 +402,11 @@ bool DevToolsUI::IsFrontendResourceURL(const GURL& url) {
DevToolsUI::DevToolsUI(content::WebUI* web_ui)
: WebUIController(web_ui), bindings_(web_ui->GetWebContents()) {
web_ui->SetBindings(0);
- Profile* profile = Profile::FromWebUI(web_ui);
- content::URLDataSource::Add(
- profile,
- new DevToolsDataSource(profile->GetRequestContext()));
+ auto factory = content::BrowserContext::GetDefaultStoragePartition(
+ web_ui->GetWebContents()->GetBrowserContext())
+ ->GetURLLoaderFactoryForBrowserProcess();
+ content::URLDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
+ new DevToolsDataSource(std::move(factory)));
}
-DevToolsUI::~DevToolsUI() {
-}
+DevToolsUI::~DevToolsUI() = default;
diff --git a/chromium/chrome/browser/ui/webui/discards/discards_ui.cc b/chromium/chrome/browser/ui/webui/discards/discards_ui.cc
index d2377955f7c..2d53a02fc19 100644
--- a/chromium/chrome/browser/ui/webui/discards/discards_ui.cc
+++ b/chromium/chrome/browser/ui/webui/discards/discards_ui.cc
@@ -4,7 +4,9 @@
#include "chrome/browser/ui/webui/discards/discards_ui.h"
-#include "base/memory/ptr_util.h"
+#include <utility>
+#include <vector>
+
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
@@ -23,15 +25,11 @@
namespace {
-namespace {
-
resource_coordinator::DiscardReason GetDiscardReason(bool urgent) {
return urgent ? resource_coordinator::DiscardReason::kUrgent
: resource_coordinator::DiscardReason::kProactive;
}
-} // namespace
-
class DiscardsDetailsProviderImpl : public mojom::DiscardsDetailsProvider {
public:
// This instance is deleted when the supplied pipe is destroyed.
@@ -129,5 +127,6 @@ DiscardsUI::DiscardsUI(content::WebUI* web_ui)
DiscardsUI::~DiscardsUI() {}
void DiscardsUI::BindUIHandler(mojom::DiscardsDetailsProviderRequest request) {
- ui_handler_.reset(new DiscardsDetailsProviderImpl(std::move(request)));
+ ui_handler_ =
+ std::make_unique<DiscardsDetailsProviderImpl>(std::move(request));
}
diff --git a/chromium/chrome/browser/ui/webui/discards/discards_ui.h b/chromium/chrome/browser/ui/webui/discards/discards_ui.h
index 442fd982277..212c6646059 100644
--- a/chromium/chrome/browser/ui/webui/discards/discards_ui.h
+++ b/chromium/chrome/browser/ui/webui/discards/discards_ui.h
@@ -5,6 +5,8 @@
#ifndef CHROME_BROWSER_UI_WEBUI_DISCARDS_DISCARDS_UI_H_
#define CHROME_BROWSER_UI_WEBUI_DISCARDS_DISCARDS_UI_H_
+#include <memory>
+
#include "base/macros.h"
#include "chrome/browser/ui/webui/discards/discards.mojom.h"
#include "chrome/browser/ui/webui/mojo_web_ui_controller.h"
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 4330fe7e091..9988db2e568 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
@@ -5,11 +5,14 @@
#include "chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.h"
#include "base/bind.h"
+#include "base/guid.h"
#include "base/values.h"
#include "chrome/browser/download/download_service_factory.h"
#include "chrome/browser/profiles/profile.h"
-#include "components/download/public/download_service.h"
+#include "components/download/public/background_service/download_params.h"
+#include "components/download/public/background_service/download_service.h"
#include "content/public/browser/web_ui.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
namespace download_internals {
@@ -24,12 +27,19 @@ DownloadInternalsUIMessageHandler::~DownloadInternalsUIMessageHandler() {
void DownloadInternalsUIMessageHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback(
"getServiceStatus",
- base::Bind(&DownloadInternalsUIMessageHandler::HandleGetServiceStatus,
- weak_ptr_factory_.GetWeakPtr()));
+ base::BindRepeating(
+ &DownloadInternalsUIMessageHandler::HandleGetServiceStatus,
+ weak_ptr_factory_.GetWeakPtr()));
web_ui()->RegisterMessageCallback(
"getServiceDownloads",
- base::Bind(&DownloadInternalsUIMessageHandler::HandleGetServiceDownloads,
- weak_ptr_factory_.GetWeakPtr()));
+ base::BindRepeating(
+ &DownloadInternalsUIMessageHandler::HandleGetServiceDownloads,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "startDownload",
+ base::BindRepeating(
+ &DownloadInternalsUIMessageHandler::HandleStartDownload,
+ weak_ptr_factory_.GetWeakPtr()));
Profile* profile = Profile::FromWebUI(web_ui());
download_service_ = DownloadServiceFactory::GetForBrowserContext(profile);
@@ -96,4 +106,45 @@ void DownloadInternalsUIMessageHandler::HandleGetServiceDownloads(
*callback_id, download_service_->GetLogger()->GetServiceDownloads());
}
+void DownloadInternalsUIMessageHandler::HandleStartDownload(
+ const base::ListValue* args) {
+ CHECK_GT(args->GetList().size(), 1u) << "Missing argument download URL.";
+ GURL url = GURL(args->GetList()[1].GetString());
+ if (!url.is_valid()) {
+ LOG(WARNING) << "Can't parse download URL, try to enter a valid URL.";
+ return;
+ }
+
+ download::DownloadParams params;
+ params.guid = base::GenerateGUID();
+ params.client = download::DownloadClient::DEBUGGING;
+ params.request_params.method = "GET";
+ params.request_params.url = url;
+
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("download_internals_webui_source", R"(
+ semantics {
+ sender: "Download Internals Page"
+ description:
+ "Starts a download with background download service in WebUI."
+ trigger:
+ "User clicks on the download button in "
+ "chrome://download-internals."
+ data: "None"
+ destination: WEBSITE
+ }
+ policy {
+ cookies_allowed: YES
+ cookies_store: "user"
+ setting: "This feature cannot be disabled by settings."
+ policy_exception_justification: "Not implemented."
+ })");
+
+ params.traffic_annotation =
+ net::MutableNetworkTrafficAnnotationTag(traffic_annotation);
+
+ DCHECK(download_service_);
+ download_service_->StartDownload(params);
+}
+
} // namespace download_internals
diff --git a/chromium/chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.h b/chromium/chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.h
index cd8b9641cba..ac0a3c645b6 100644
--- a/chromium/chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.h
+++ b/chromium/chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.h
@@ -7,7 +7,7 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
-#include "components/download/public/logger.h"
+#include "components/download/public/background_service/logger.h"
#include "content/public/browser/web_ui_message_handler.h"
namespace download {
@@ -39,6 +39,9 @@ class DownloadInternalsUIMessageHandler : public content::WebUIMessageHandler,
void HandleGetServiceStatus(const base::ListValue* args);
void HandleGetServiceDownloads(const base::ListValue* args);
+ // Starts a background download.
+ void HandleStartDownload(const base::ListValue* args);
+
download::DownloadService* download_service_;
base::WeakPtrFactory<DownloadInternalsUIMessageHandler> weak_ptr_factory_;
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 aa84e9ea54c..0b5991d6885 100644
--- a/chromium/chrome/browser/ui/webui/engagement/site_engagement_ui.cc
+++ b/chromium/chrome/browser/ui/webui/engagement/site_engagement_ui.cc
@@ -84,7 +84,7 @@ SiteEngagementUI::SiteEngagementUI(content::WebUI* web_ui)
source->AddResourcePath(
"chrome/browser/engagement/site_engagement_details.mojom.js",
IDR_SITE_ENGAGEMENT_MOJO_JS);
- source->AddResourcePath("url/mojo/url.mojom.js", IDR_URL_MOJO_JS);
+ source->AddResourcePath("url/mojom/url.mojom.js", IDR_URL_MOJO_JS);
source->SetDefaultResource(IDR_SITE_ENGAGEMENT_HTML);
source->UseGzip();
content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), source.release());
diff --git a/chromium/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_browsertest.js b/chromium/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_browsertest.js
index 993adb1a55a..85ec336b42c 100644
--- a/chromium/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_browsertest.js
+++ b/chromium/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_browsertest.js
@@ -3,6 +3,7 @@
// found in the LICENSE file.
GEN('#include "base/command_line.h"');
+GEN('#include "chrome/common/chrome_features.h"');
/**
* TestFixture for kiosk app settings WebUI testing.
@@ -24,6 +25,9 @@ KioskAppSettingsWebUITest.prototype = {
switchName: 'enable-consumer-kiosk',
}],
+ /** @override */
+ featureList: ['', 'features::kMaterialDesignExtensions'],
+
/**
* Mock settings data.
* @private
diff --git a/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js b/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js
index d0c34c854d1..7d4bef5721f 100644
--- a/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js
+++ b/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js
@@ -6,6 +6,7 @@
GEN('#include "chrome/browser/ui/webui/extensions/' +
'extension_settings_browsertest.h"');
+GEN('#include "chrome/common/chrome_features.h"');
// The id of the extension from |InstallGoodExtension|.
var GOOD_EXTENSION_ID = 'ldnnhddmnhbkjipkidpdiheffobcpfmf';
@@ -50,6 +51,9 @@ ExtensionSettingsWebUITest.prototype = {
typedefCppFixture: 'ExtensionSettingsUIBrowserTest',
/** @override */
+ featureList: ['', 'features::kMaterialDesignExtensions'],
+
+ /** @override */
setUp: function() {
testing.Test.prototype.setUp.call(this);
testing.Test.disableAnimationsAndTransitions();
diff --git a/chromium/chrome/browser/ui/webui/extensions/extension_settings_handler.cc b/chromium/chrome/browser/ui/webui/extensions/extension_settings_handler.cc
index b3c34254a83..8e9efd24aa9 100644
--- a/chromium/chrome/browser/ui/webui/extensions/extension_settings_handler.cc
+++ b/chromium/chrome/browser/ui/webui/extensions/extension_settings_handler.cc
@@ -13,7 +13,7 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/apps/app_info_dialog.h"
#include "chrome/browser/ui/browser.h"
-#include "chrome/common/features.h"
+#include "chrome/common/buildflags.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/chromium_strings.h"
diff --git a/chromium/chrome/browser/ui/webui/extensions/extensions_ui.cc b/chromium/chrome/browser/ui/webui/extensions/extensions_ui.cc
index a55a100e8a3..207eb0edb4a 100644
--- a/chromium/chrome/browser/ui/webui/extensions/extensions_ui.cc
+++ b/chromium/chrome/browser/ui/webui/extensions/extensions_ui.cc
@@ -203,6 +203,10 @@ content::WebUIDataSource* CreateMdExtensionsSource(bool in_dev_mode) {
IDS_MD_EXTENSIONS_DEPENDENT_ENTRY);
source->AddLocalizedString("itemDetails", IDS_MD_EXTENSIONS_ITEM_DETAILS);
source->AddLocalizedString("itemErrors", IDS_MD_EXTENSIONS_ITEM_ERRORS);
+ source->AddLocalizedString("accessibilityErrorLine",
+ IDS_MD_EXTENSIONS_ACCESSIBILITY_ERROR_LINE);
+ source->AddLocalizedString("accessibilityErrorMultiLine",
+ IDS_MD_EXTENSIONS_ACCESSIBILITY_ERROR_MULTI_LINE);
source->AddLocalizedString("appIcon", IDS_MD_EXTENSIONS_APP_ICON);
source->AddLocalizedString("extensionIcon", IDS_MD_EXTENSIONS_EXTENSION_ICON);
source->AddLocalizedString("extensionA11yAssociation",
@@ -253,7 +257,7 @@ content::WebUIDataSource* CreateMdExtensionsSource(bool in_dev_mode) {
l10n_util::GetStringFUTF16(
IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE,
l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE)));
- source->AddLocalizedString("learnMore", IDS_MD_EXTENSIONS_LEARN_MORE);
+ source->AddLocalizedString("learnMore", IDS_LEARN_MORE);
source->AddLocalizedString(
"loadErrorCouldNotLoadManifest",
IDS_MD_EXTENSIONS_LOAD_ERROR_COULD_NOT_LOAD_MANIFEST);
@@ -319,6 +323,8 @@ content::WebUIDataSource* CreateMdExtensionsSource(bool in_dev_mode) {
IDS_MD_EXTENSIONS_TOOLBAR_UPDATE_NOW_TOOLTIP);
source->AddLocalizedString("toolbarUpdateDone",
IDS_MD_EXTENSIONS_TOOLBAR_UPDATE_DONE);
+ source->AddLocalizedString("toolbarUpdatingToast",
+ IDS_MD_EXTENSIONS_TOOLBAR_UPDATING_TOAST);
source->AddLocalizedString(
"updateRequiredByPolicy",
IDS_MD_EXTENSIONS_DISABLED_UPDATE_REQUIRED_BY_POLICY);
diff --git a/chromium/chrome/browser/ui/webui/extensions/install_extension_handler.cc b/chromium/chrome/browser/ui/webui/extensions/install_extension_handler.cc
index 5395f3270da..953306e2712 100644
--- a/chromium/chrome/browser/ui/webui/extensions/install_extension_handler.cc
+++ b/chromium/chrome/browser/ui/webui/extensions/install_extension_handler.cc
@@ -9,11 +9,11 @@
#include "base/bind.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/extensions/chrome_zipfile_installer.h"
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/extension_install_prompt.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/unpacked_installer.h"
-#include "chrome/browser/extensions/zipfile_installer.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/web_contents.h"
@@ -99,7 +99,9 @@ void InstallExtensionHandler::HandleInstallMessage(
web_ui()->GetWebContents()->GetBrowserContext());
if (file_display_name_.MatchesExtension(FILE_PATH_LITERAL(".zip"))) {
- ZipFileInstaller::Create(ExtensionSystem::Get(profile)->extension_service())
+ ZipFileInstaller::Create(
+ MakeRegisterInExtensionServiceCallback(
+ ExtensionSystem::Get(profile)->extension_service()))
->LoadFromZipFile(file_to_install_);
} else {
std::unique_ptr<ExtensionInstallPrompt> prompt(
diff --git a/chromium/chrome/browser/ui/webui/flags_ui.cc b/chromium/chrome/browser/ui/webui/flags_ui.cc
index 63701c68fb3..cf719ba468f 100644
--- a/chromium/chrome/browser/ui/webui/flags_ui.cc
+++ b/chromium/chrome/browser/ui/webui/flags_ui.cc
@@ -46,10 +46,8 @@
#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/chromeos/settings/owner_flags_storage.h"
-#include "chromeos/cryptohome/cryptohome_parameters.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/dbus/session_manager_client.h"
#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/signin/core/account_id/account_id.h"
#include "components/user_manager/user_manager.h"
#endif
@@ -223,19 +221,20 @@ void FlagsDOMHandler::HandleRestartBrowser(const base::ListValue* args) {
// Apply additional switches from policy that should not be dropped when
// applying flags..
- chromeos::UserSessionManager::MaybeAppendPolicySwitches(&user_flags);
+ chromeos::UserSessionManager::MaybeAppendPolicySwitches(
+ Profile::FromWebUI(web_ui())->GetPrefs(), &user_flags);
base::CommandLine::StringVector flags;
// argv[0] is the program name |base::CommandLine::NO_PROGRAM|.
flags.assign(user_flags.argv().begin() + 1, user_flags.argv().end());
VLOG(1) << "Restarting to apply per-session flags...";
- chromeos::DBusThreadManager::Get()
- ->GetSessionManagerClient()
- ->SetFlagsForUser(
- cryptohome::Identification(user_manager::UserManager::Get()
- ->GetActiveUser()
- ->GetAccountId()),
- flags);
+ AccountId account_id =
+ user_manager::UserManager::Get()->GetActiveUser()->GetAccountId();
+ chromeos::UserSessionManager::GetInstance()->SetSwitchesForUser(
+ account_id,
+ chromeos::UserSessionManager::CommandLineSwitchesType::
+ kPolicyAndFlagsAndKioskControl,
+ flags);
#endif
chrome::AttemptRestart();
}
diff --git a/chromium/chrome/browser/ui/webui/foreign_session_handler.cc b/chromium/chrome/browser/ui/webui/foreign_session_handler.cc
index 425ffff9c0a..cbae4e87be5 100644
--- a/chromium/chrome/browser/ui/webui/foreign_session_handler.cc
+++ b/chromium/chrome/browser/ui/webui/foreign_session_handler.cc
@@ -45,6 +45,31 @@ namespace {
// Maximum number of sessions we're going to display on the NTP
const size_t kMaxSessionsToShow = 10;
+// Converts the DeviceType enum value to a string. This is used
+// in the NTP handler for foreign sessions for matching session
+// types to an icon style.
+std::string DeviceTypeToString(sync_pb::SyncEnums::DeviceType device_type) {
+ switch (device_type) {
+ case sync_pb::SyncEnums::TYPE_UNSET:
+ break;
+ case sync_pb::SyncEnums::TYPE_WIN:
+ return "win";
+ case sync_pb::SyncEnums::TYPE_MAC:
+ return "macosx";
+ case sync_pb::SyncEnums::TYPE_LINUX:
+ return "linux";
+ case sync_pb::SyncEnums::TYPE_CROS:
+ return "chromeos";
+ case sync_pb::SyncEnums::TYPE_OTHER:
+ return "other";
+ case sync_pb::SyncEnums::TYPE_PHONE:
+ return "phone";
+ case sync_pb::SyncEnums::TYPE_TABLET:
+ return "tablet";
+ }
+ return std::string();
+}
+
// Helper method to create JSON compatible objects from Session objects.
std::unique_ptr<base::DictionaryValue> SessionTabToValue(
const ::sessions::SessionTab& tab) {
@@ -284,7 +309,8 @@ void ForeignSessionHandler::HandleGetForeignSessions(
// remove any keys here.
session_data->SetString("tag", session_tag);
session_data->SetString("name", session->session_name);
- session_data->SetString("deviceType", session->DeviceTypeAsString());
+ session_data->SetString("deviceType",
+ DeviceTypeToString(session->device_type));
session_data->SetString("modifiedTime",
FormatSessionTime(session->modified_time));
session_data->SetDouble("timestamp", session->modified_time.ToJsTime());
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 9a61c4f79d7..815a49eb16d 100644
--- a/chromium/chrome/browser/ui/webui/help/version_updater_chromeos.cc
+++ b/chromium/chrome/browser/ui/webui/help/version_updater_chromeos.cc
@@ -304,7 +304,7 @@ void VersionUpdaterCros::UpdateStatusChanged(
break;
case UpdateEngineClient::UPDATE_STATUS_DOWNLOADING:
progress = static_cast<int>(round(status.download_progress * 100));
- // Fall through.
+ FALLTHROUGH;
case UpdateEngineClient::UPDATE_STATUS_UPDATE_AVAILABLE:
my_status = UPDATING;
break;
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 c7f0393ab93..243d5624542 100644
--- a/chromium/chrome/browser/ui/webui/help/version_updater_mac.mm
+++ b/chromium/chrome/browser/ui/webui/help/version_updater_mac.mm
@@ -191,7 +191,7 @@ void VersionUpdaterMac::UpdateStatus(NSDictionary* dictionary) {
case kAutoupdateRegisterFailed:
enable_promote_button = false;
- // Fall through.
+ FALLTHROUGH;
case kAutoupdateCheckFailed:
case kAutoupdateInstallFailed:
case kAutoupdatePromoteFailed:
diff --git a/chromium/chrome/browser/ui/webui/inspect_ui.cc b/chromium/chrome/browser/ui/webui/inspect_ui.cc
index 597ffcfa35e..ab19c4d204f 100644
--- a/chromium/chrome/browser/ui/webui/inspect_ui.cc
+++ b/chromium/chrome/browser/ui/webui/inspect_ui.cc
@@ -40,6 +40,7 @@ namespace {
const char kInitUICommand[] = "init-ui";
const char kInspectCommand[] = "inspect";
+const char kInspectFallbackCommand[] = "inspect-fallback";
const char kInspectAdditionalCommand[] = "inspect-additional";
const char kActivateCommand[] = "activate";
const char kCloseCommand[] = "close";
@@ -90,6 +91,7 @@ class InspectMessageHandler : public WebUIMessageHandler {
void HandleInitUICommand(const base::ListValue* args);
void HandleInspectCommand(const base::ListValue* args);
+ void HandleInspectFallbackCommand(const base::ListValue* args);
void HandleInspectAdditionalCommand(const base::ListValue* args);
void HandleActivateCommand(const base::ListValue* args);
void HandleCloseCommand(const base::ListValue* args);
@@ -115,6 +117,10 @@ void InspectMessageHandler::RegisterMessages() {
base::Bind(&InspectMessageHandler::HandleInspectCommand,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
+ kInspectFallbackCommand,
+ base::BindRepeating(&InspectMessageHandler::HandleInspectFallbackCommand,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
kInspectAdditionalCommand,
base::Bind(&InspectMessageHandler::HandleInspectAdditionalCommand,
base::Unretained(this)));
@@ -177,6 +183,14 @@ void InspectMessageHandler::HandleInspectCommand(const base::ListValue* args) {
inspect_ui_->Inspect(source, id);
}
+void InspectMessageHandler::HandleInspectFallbackCommand(
+ const base::ListValue* args) {
+ std::string source;
+ std::string id;
+ if (ParseStringArgs(args, &source, &id))
+ inspect_ui_->InspectFallback(source, id);
+}
+
void InspectMessageHandler::HandleInspectAdditionalCommand(
const base::ListValue* args) {
std::string url;
@@ -358,6 +372,16 @@ void InspectUI::Inspect(const std::string& source_id,
}
}
+void InspectUI::InspectFallback(const std::string& source_id,
+ const std::string& target_id) {
+ scoped_refptr<DevToolsAgentHost> target = FindTarget(source_id, target_id);
+ if (target) {
+ Profile* profile = Profile::FromBrowserContext(
+ web_ui()->GetWebContents()->GetBrowserContext());
+ DevToolsWindow::OpenDevToolsWindowWithBundledFrontend(target, profile);
+ }
+}
+
void InspectUI::Activate(const std::string& source_id,
const std::string& target_id) {
scoped_refptr<DevToolsAgentHost> target = FindTarget(source_id, target_id);
diff --git a/chromium/chrome/browser/ui/webui/inspect_ui.h b/chromium/chrome/browser/ui/webui/inspect_ui.h
index a0ccc386dbf..4838d34b1d0 100644
--- a/chromium/chrome/browser/ui/webui/inspect_ui.h
+++ b/chromium/chrome/browser/ui/webui/inspect_ui.h
@@ -38,6 +38,8 @@ class InspectUI : public content::WebUIController,
void InitUI();
void Inspect(const std::string& source_id, const std::string& target_id);
+ void InspectFallback(const std::string& source_id,
+ const std::string& target_id);
void Activate(const std::string& source_id, const std::string& target_id);
void Close(const std::string& source_id, const std::string& target_id);
void Reload(const std::string& source_id, const std::string& target_id);
diff --git a/chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.cc b/chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
index 3ce482ef532..572f66c8fae 100644
--- a/chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
+++ b/chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
@@ -20,7 +20,7 @@
#include "chrome/browser/ssl/mitm_software_blocking_page.h"
#include "chrome/browser/ssl/ssl_blocking_page.h"
#include "chrome/browser/supervised_user/supervised_user_interstitial.h"
-#include "chrome/common/features.h"
+#include "chrome/common/buildflags.h"
#include "chrome/common/url_constants.h"
#include "components/grit/components_resources.h"
#include "components/safe_browsing/db/database_manager.h"
@@ -421,7 +421,7 @@ std::string InterstitialHTMLSource::GetSource() const {
std::string InterstitialHTMLSource::GetContentSecurityPolicyScriptSrc() const {
// 'unsafe-inline' is added to script-src.
- return "script-src chrome://resources 'self' 'unsafe-eval' 'unsafe-inline';";
+ return "script-src chrome://resources 'self' 'unsafe-inline';";
}
std::string InterstitialHTMLSource::GetContentSecurityPolicyStyleSrc() const {
diff --git a/chromium/chrome/browser/ui/webui/interventions_internals/BUILD.gn b/chromium/chrome/browser/ui/webui/interventions_internals/BUILD.gn
index a2da942ee62..424c3a9cb0b 100644
--- a/chromium/chrome/browser/ui/webui/interventions_internals/BUILD.gn
+++ b/chromium/chrome/browser/ui/webui/interventions_internals/BUILD.gn
@@ -10,6 +10,6 @@ mojom("mojo_bindings") {
]
public_deps = [
- "//url/mojo:url_mojom_gurl",
+ "//url/mojom:url_mojom_gurl",
]
}
diff --git a/chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals.mojom b/chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals.mojom
index 0e401283453..0b39e904745 100644
--- a/chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals.mojom
+++ b/chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals.mojom
@@ -4,7 +4,7 @@
module mojom;
-import "url/mojo/url.mojom";
+import "url/mojom/url.mojom";
struct PreviewsStatus {
// The human readable description of the status that will be displayed on
diff --git a/chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler.cc b/chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler.cc
index e31861f342c..0a4eb3c584d 100644
--- a/chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler.cc
+++ b/chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler.cc
@@ -23,18 +23,19 @@ namespace {
// HTML DOM ID used in the JavaScript code. The IDs are generated here so that
// the DOM would have sensible name instead of autogenerated IDs.
-const char kAmpRedirectionPreviewsHtmlId[] = "amp-preview-status";
+const char kPreviewsAllowedHtmlId[] = "previews-allowed-status";
const char kClientLoFiPreviewsHtmlId[] = "client-lofi-preview-status";
const char kNoScriptPreviewsHtmlId[] = "noscript-preview-status";
const char kOfflinePreviewsHtmlId[] = "offline-preview-status";
// Descriptions for previews.
-const char kAmpRedirectionDescription[] = "AMP Previews";
+const char kPreviewsAllowedDescription[] = "Previews Allowed";
const char kClientLoFiDescription[] = "Client LoFi Previews";
const char kNoScriptDescription[] = "NoScript Previews";
const char kOfflineDesciption[] = "Offline Previews";
// Flag feature name.
+const char kPreviewsAllowedFeatureName[] = "Previews";
const char kNoScriptFeatureName[] = "NoScriptPreviews";
#if defined(OS_ANDROID)
const char kOfflinePageFeatureName[] = "OfflinePreviews";
@@ -42,6 +43,7 @@ const char kOfflinePageFeatureName[] = "OfflinePreviews";
// HTML DOM ID used in the JavaScript code. The IDs are generated here so that
// the DOM would have sensible name instead of autogenerated IDs.
+const char kPreviewsAllowedFlagHtmlId[] = "previews-flag";
const char kEctFlagHtmlId[] = "ect-flag";
const char kNoScriptFlagHtmlId[] = "noscript-flag";
const char kOfflinePageFlagHtmlId[] = "offline-page-flag";
@@ -49,6 +51,7 @@ const char kIgnorePreviewsBlacklistFlagHtmlId[] = "ignore-previews-blacklist";
// Links to flags in chrome://flags.
// TODO(thanhdle): Refactor into vector of structs. crbug.com/787010.
+const char kPreviewsAllowedFlagLink[] = "chrome://flags/#allow-previews";
const char kEctFlagLink[] = "chrome://flags/#force-effective-connection-type";
const char kNoScriptFlagLink[] = "chrome://flags/#enable-noscript-previews";
const char kOfflinePageFlagLink[] = "chrome://flags/#enable-offline-previews";
@@ -186,11 +189,11 @@ void InterventionsInternalsPageHandler::GetPreviewsEnabled(
GetPreviewsEnabledCallback callback) {
std::vector<mojom::PreviewsStatusPtr> statuses;
- auto amp_status = mojom::PreviewsStatus::New();
- amp_status->description = kAmpRedirectionDescription;
- amp_status->enabled = previews::params::IsAMPRedirectionPreviewEnabled();
- amp_status->htmlId = kAmpRedirectionPreviewsHtmlId;
- statuses.push_back(std::move(amp_status));
+ auto previews_allowed_status = mojom::PreviewsStatus::New();
+ previews_allowed_status->description = kPreviewsAllowedDescription;
+ previews_allowed_status->enabled = previews::params::ArePreviewsAllowed();
+ previews_allowed_status->htmlId = kPreviewsAllowedHtmlId;
+ statuses.push_back(std::move(previews_allowed_status));
auto client_lofi_status = mojom::PreviewsStatus::New();
client_lofi_status->description = kClientLoFiDescription;
@@ -217,6 +220,15 @@ void InterventionsInternalsPageHandler::GetPreviewsFlagsDetails(
GetPreviewsFlagsDetailsCallback callback) {
std::vector<mojom::PreviewsFlagPtr> flags;
+ auto previews_allowed_status = mojom::PreviewsFlag::New();
+ previews_allowed_status->description =
+ flag_descriptions::kPreviewsAllowedName;
+ previews_allowed_status->link = kPreviewsAllowedFlagLink;
+ previews_allowed_status->value =
+ GetFeatureFlagStatus(kPreviewsAllowedFeatureName);
+ previews_allowed_status->htmlId = kPreviewsAllowedFlagHtmlId;
+ flags.push_back(std::move(previews_allowed_status));
+
auto ect_status = mojom::PreviewsFlag::New();
ect_status->description =
flag_descriptions::kForceEffectiveConnectionTypeName;
diff --git a/chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler_unittest.cc b/chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler_unittest.cc
index 41e8772b096..d9572b773e3 100644
--- a/chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals_page_handler_unittest.cc
@@ -44,13 +44,13 @@
namespace {
// The HTML DOM ID used in Javascript.
-constexpr char kAMPRedirectionPreviewsHtmlId[] = "amp-preview-status";
+constexpr char kPreviewsAllowedHtmlId[] = "previews-allowed-status";
constexpr char kClientLoFiPreviewsHtmlId[] = "client-lofi-preview-status";
constexpr char kNoScriptPreviewsHtmlId[] = "noscript-preview-status";
constexpr char kOfflinePreviewsHtmlId[] = "offline-preview-status";
// Descriptions for previews.
-constexpr char kAmpRedirectionDescription[] = "AMP Previews";
+constexpr char kPreviewsAllowedDescription[] = "Previews Allowed";
constexpr char kClientLoFiDescription[] = "Client LoFi Previews";
constexpr char kNoScriptDescription[] = "NoScript Previews";
constexpr char kOfflineDesciption[] = "Offline Previews";
@@ -318,30 +318,28 @@ TEST_F(InterventionsInternalsPageHandlerTest, GetPreviewsEnabledCount) {
EXPECT_EQ(expected, passed_in_modes.size());
}
-TEST_F(InterventionsInternalsPageHandlerTest, AMPRedirectionDisabled) {
- // Init with kAMPRedirection disabled.
- scoped_feature_list_->InitWithFeatures({},
- {previews::features::kAMPRedirection});
+TEST_F(InterventionsInternalsPageHandlerTest, PreviewsAllowedDisabled) {
+ // Init with kPreviews disabled.
+ scoped_feature_list_->InitWithFeatures({}, {previews::features::kPreviews});
page_handler_->GetPreviewsEnabled(
base::BindOnce(&MockGetPreviewsEnabledCallback));
- auto amp_redirection = passed_in_modes.find(kAMPRedirectionPreviewsHtmlId);
- ASSERT_NE(passed_in_modes.end(), amp_redirection);
- EXPECT_EQ(kAmpRedirectionDescription, amp_redirection->second->description);
- EXPECT_FALSE(amp_redirection->second->enabled);
+ auto previews_allowed = passed_in_modes.find(kPreviewsAllowedHtmlId);
+ ASSERT_NE(passed_in_modes.end(), previews_allowed);
+ EXPECT_EQ(kPreviewsAllowedDescription, previews_allowed->second->description);
+ EXPECT_FALSE(previews_allowed->second->enabled);
}
-TEST_F(InterventionsInternalsPageHandlerTest, AMPRedirectionEnabled) {
- // Init with kAMPRedirection enabled.
- scoped_feature_list_->InitWithFeatures({previews::features::kAMPRedirection},
- {});
+TEST_F(InterventionsInternalsPageHandlerTest, PreviewsAllowedEnabled) {
+ // Init with kPreviews enabled.
+ scoped_feature_list_->InitWithFeatures({previews::features::kPreviews}, {});
page_handler_->GetPreviewsEnabled(
base::BindOnce(&MockGetPreviewsEnabledCallback));
- auto amp_redirection = passed_in_modes.find(kAMPRedirectionPreviewsHtmlId);
- ASSERT_NE(passed_in_modes.end(), amp_redirection);
- EXPECT_EQ(kAmpRedirectionDescription, amp_redirection->second->description);
- EXPECT_TRUE(amp_redirection->second->enabled);
+ auto previews_allowed = passed_in_modes.find(kPreviewsAllowedHtmlId);
+ ASSERT_NE(passed_in_modes.end(), previews_allowed);
+ EXPECT_EQ(kPreviewsAllowedDescription, previews_allowed->second->description);
+ EXPECT_TRUE(previews_allowed->second->enabled);
}
TEST_F(InterventionsInternalsPageHandlerTest, ClientLoFiDisabled) {
@@ -425,7 +423,7 @@ TEST_F(InterventionsInternalsPageHandlerTest, GetFlagsCount) {
page_handler_->GetPreviewsFlagsDetails(
base::BindOnce(&MockGetPreviewsFlagsCallback));
- constexpr size_t expected = 4;
+ constexpr size_t expected = 5;
EXPECT_EQ(expected, passed_in_flags.size());
}
diff --git a/chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals_ui.cc b/chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals_ui.cc
index 0e1930eda5a..e0b8fc7be54 100644
--- a/chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals_ui.cc
+++ b/chromium/chrome/browser/ui/webui/interventions_internals/interventions_internals_ui.cc
@@ -28,7 +28,7 @@ content::WebUIDataSource* GetSource() {
"chrome/browser/ui/webui/interventions_internals/"
"interventions_internals.mojom.js",
IDR_INTERVENTIONS_INTERNALS_MOJO_INDEX_JS);
- source->AddResourcePath("url/mojo/url.mojom.js", IDR_URL_MOJO_JS);
+ source->AddResourcePath("url/mojom/url.mojom.js", IDR_URL_MOJO_JS);
source->SetDefaultResource(IDR_INTERVENTIONS_INTERNALS_INDEX_HTML);
source->UseGzip(std::vector<std::string>());
return source;
diff --git a/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui.cc b/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui.cc
index 44c818a989c..2c1cb6e3567 100644
--- a/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui.cc
+++ b/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui.cc
@@ -36,10 +36,8 @@ content::WebUIDataSource* CreateLocalDiscoveryHTMLSource() {
IDS_LOCAL_DISCOVERY_SERVICE_REGISTER);
source->AddLocalizedString("manageDevice", IDS_LOCAL_DISCOVERY_MANAGE_DEVICE);
- source->AddLocalizedString("registerPrinterConfirmMessage",
- IDS_LOCAL_DISCOVERY_REGISTER_PRINTER_CONFIRMATION);
- source->AddLocalizedString("registerDeviceConfirmMessage",
- IDS_LOCAL_DISCOVERY_REGISTER_DEVICE_CONFIRMATION);
+ source->AddLocalizedString("registerPrinterInformationMessage",
+ IDS_CLOUD_PRINT_REGISTER_PRINTER_INFORMATION);
source->AddLocalizedString("registerUser",
IDS_LOCAL_DISCOVERY_REGISTER_USER);
source->AddLocalizedString("confirmRegistration",
@@ -77,6 +75,7 @@ content::WebUIDataSource* CreateLocalDiscoveryHTMLSource() {
source->AddLocalizedString("printersOnNetworkMultiple",
IDS_LOCAL_DISCOVERY_PRINTERS_ON_NETWORK_MULTIPLE);
source->AddLocalizedString("cancel", IDS_CANCEL);
+ source->AddLocalizedString("confirm", IDS_CONFIRM);
source->AddLocalizedString("ok", IDS_OK);
source->AddLocalizedString("loading", IDS_LOCAL_DISCOVERY_LOADING);
source->AddLocalizedString("addPrinters", IDS_LOCAL_DISCOVERY_ADD_PRINTERS);
diff --git a/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc
index fc499de4454..3d74af7b946 100644
--- a/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc
@@ -18,6 +18,8 @@
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "chrome/browser/local_discovery/test_service_discovery_client.h"
+#include "chrome/browser/media/router/providers/cast/dual_media_sink_service.h"
+#include "chrome/browser/media/router/test/noop_dual_media_sink_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
@@ -355,6 +357,16 @@ class LocalDiscoveryUITest : public WebUIBrowserTest {
~LocalDiscoveryUITest() override {
}
+ void SetUp() override {
+ // We need to stub out DualMediaSinkService here, because the profile setup
+ // instantiates DualMediaSinkService, which in turn sets
+ // |g_service_discovery_client| with a real instance. This causes
+ // a DCHECK during TestServiceDiscoveryClient construction.
+ media_router::DualMediaSinkService::SetInstanceForTest(
+ new media_router::NoopDualMediaSinkService());
+ WebUIBrowserTest::SetUp();
+ }
+
void SetUpOnMainThread() override {
WebUIBrowserTest::SetUpOnMainThread();
diff --git a/chromium/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc b/chromium/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc
index e3ab4ba1125..63fcf51691f 100644
--- a/chromium/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc
+++ b/chromium/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc
@@ -54,6 +54,8 @@ content::WebUIDataSource* CreateMdBookmarksUIHTMLSource(Profile* profile) {
AddLocalizedString(source, "editDialogUrlInput",
IDS_BOOKMARK_MANAGER_URL_INPUT_PLACE_HOLDER);
AddLocalizedString(source, "emptyList", IDS_MD_BOOKMARK_MANAGER_EMPTY_LIST);
+ AddLocalizedString(source, "emptyUnmodifiableList",
+ IDS_MD_BOOKMARK_MANAGER_EMPTY_UNMODIFIABLE_LIST);
AddLocalizedString(source, "folderLabel",
IDS_MD_BOOKMARK_MANAGER_FOLDER_LABEL);
AddLocalizedString(source, "itemsSelected",
diff --git a/chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker.cc b/chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker.cc
index 2400a23bdfa..63dbceb45fa 100644
--- a/chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker.cc
+++ b/chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker.cc
@@ -21,8 +21,9 @@
#include "chrome/browser/extensions/api/downloads/downloads_api.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
+#include "components/download/public/common/download_item.h"
#include "content/public/browser/browser_context.h"
-#include "content/public/browser/download_item.h"
+#include "content/public/browser/download_item_utils.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/web_ui.h"
#include "extensions/browser/extension_system.h"
@@ -31,7 +32,7 @@
#include "ui/base/l10n/time_format.h"
using content::BrowserContext;
-using content::DownloadItem;
+using download::DownloadItem;
using content::DownloadManager;
using DownloadVector = DownloadManager::DownloadVector;
@@ -42,24 +43,24 @@ namespace {
// CreateDownloadItemValue(). Only return strings for DANGEROUS_FILE,
// DANGEROUS_URL, DANGEROUS_CONTENT, and UNCOMMON_CONTENT because the
// |danger_type| value is only defined if the value of |state| is |DANGEROUS|.
-const char* GetDangerTypeString(content::DownloadDangerType danger_type) {
+const char* GetDangerTypeString(download::DownloadDangerType danger_type) {
switch (danger_type) {
- case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
+ case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
return "DANGEROUS_FILE";
- case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
+ case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
return "DANGEROUS_URL";
- case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
+ case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
return "DANGEROUS_CONTENT";
- case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
+ case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
return "UNCOMMON_CONTENT";
- case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
+ case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
return "DANGEROUS_HOST";
- case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
+ case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
return "POTENTIALLY_UNWANTED";
- case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
- case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
- case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
- case content::DOWNLOAD_DANGER_TYPE_MAX:
+ case download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
+ case download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
+ case download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
+ case download::DOWNLOAD_DANGER_TYPE_MAX:
break;
}
// Don't return a danger type string if it is NOT_DANGEROUS,
@@ -185,7 +186,7 @@ DownloadsListTracker::DownloadsListTracker(
std::unique_ptr<base::DictionaryValue>
DownloadsListTracker::CreateDownloadItemValue(
- content::DownloadItem* download_item) const {
+ download::DownloadItem* download_item) const {
// TODO(asanka): Move towards using download_model here for getting status and
// progress. The difference currently only matters to Drive downloads and
// those don't show up on the downloads page, but should.
@@ -225,9 +226,12 @@ DownloadsListTracker::CreateDownloadItemValue(
// language. This won't work if the extension was uninstalled, so the name
// might be the wrong language.
bool include_disabled = true;
- const extensions::Extension* extension = extensions::ExtensionSystem::Get(
- Profile::FromBrowserContext(download_item->GetBrowserContext()))->
- extension_service()->GetExtensionById(by_ext->id(), include_disabled);
+ const extensions::Extension* extension =
+ extensions::ExtensionSystem::Get(
+ Profile::FromBrowserContext(
+ content::DownloadItemUtils::GetBrowserContext(download_item)))
+ ->extension_service()
+ ->GetExtensionById(by_ext->id(), include_disabled);
if (extension)
by_ext_name = extension->name();
}
@@ -256,7 +260,7 @@ DownloadsListTracker::CreateDownloadItemValue(
const char* state = nullptr;
switch (download_item->GetState()) {
- case content::DownloadItem::IN_PROGRESS: {
+ case download::DownloadItem::IN_PROGRESS: {
if (download_item->IsDangerous()) {
state = "DANGEROUS";
danger_type = GetDangerTypeString(download_item->GetDangerType());
@@ -270,7 +274,7 @@ DownloadsListTracker::CreateDownloadItemValue(
break;
}
- case content::DownloadItem::INTERRUPTED:
+ case download::DownloadItem::INTERRUPTED:
state = "INTERRUPTED";
progress_status_text = download_model.GetTabProgressStatusText();
@@ -282,23 +286,24 @@ DownloadsListTracker::CreateDownloadItemValue(
// GetStatusText() as a temporary measure until the layout is fixed to
// accommodate the longer string. http://crbug.com/609255
last_reason_text = download_model.GetStatusText();
- if (content::DOWNLOAD_INTERRUPT_REASON_CRASH ==
- download_item->GetLastReason() && !download_item->CanResume()) {
+ if (download::DOWNLOAD_INTERRUPT_REASON_CRASH ==
+ download_item->GetLastReason() &&
+ !download_item->CanResume()) {
retry = true;
}
break;
- case content::DownloadItem::CANCELLED:
+ case download::DownloadItem::CANCELLED:
state = "CANCELLED";
retry = true;
break;
- case content::DownloadItem::COMPLETE:
+ case download::DownloadItem::COMPLETE:
DCHECK(!download_item->IsDangerous());
state = "COMPLETE";
break;
- case content::DownloadItem::MAX_DOWNLOAD_STATE:
+ case download::DownloadItem::MAX_DOWNLOAD_STATE:
NOTREACHED();
}
@@ -343,8 +348,9 @@ bool DownloadsListTracker::ShouldShow(const DownloadItem& item) const {
DownloadQuery::MatchesQuery(search_terms_, item);
}
-bool DownloadsListTracker::StartTimeComparator::operator() (
- const content::DownloadItem* a, const content::DownloadItem* b) const {
+bool DownloadsListTracker::StartTimeComparator::operator()(
+ const download::DownloadItem* a,
+ const download::DownloadItem* b) const {
return a->GetStartTime() > b->GetStartTime();
}
diff --git a/chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker.h b/chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker.h
index b92245acd3a..438bbabc529 100644
--- a/chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker.h
+++ b/chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker.h
@@ -16,7 +16,7 @@
#include "base/time/time.h"
#include "base/values.h"
#include "components/download/content/public/all_download_item_notifier.h"
-#include "content/public/browser/download_item.h"
+#include "components/download/public/common/download_item.h"
namespace base {
class DictionaryValue;
@@ -58,35 +58,35 @@ class DownloadsListTracker
// AllDownloadItemNotifier::Observer:
void OnDownloadCreated(content::DownloadManager* manager,
- content::DownloadItem* download_item) override;
+ download::DownloadItem* download_item) override;
void OnDownloadUpdated(content::DownloadManager* manager,
- content::DownloadItem* download_item) override;
+ download::DownloadItem* download_item) override;
void OnDownloadRemoved(content::DownloadManager* manager,
- content::DownloadItem* download_item) override;
+ download::DownloadItem* download_item) override;
protected:
// Testing constructor.
DownloadsListTracker(content::DownloadManager* download_manager,
content::WebUI* web_ui,
- base::Callback<bool(const content::DownloadItem&)>);
+ base::Callback<bool(const download::DownloadItem&)>);
// Creates a dictionary value that's sent to the page as JSON.
virtual std::unique_ptr<base::DictionaryValue> CreateDownloadItemValue(
- content::DownloadItem* item) const;
+ download::DownloadItem* item) const;
// Exposed for testing.
- bool IsIncognito(const content::DownloadItem& item) const;
+ bool IsIncognito(const download::DownloadItem& item) const;
- const content::DownloadItem* GetItemForTesting(size_t index) const;
+ const download::DownloadItem* GetItemForTesting(size_t index) const;
void SetChunkSizeForTesting(size_t chunk_size);
private:
struct StartTimeComparator {
- bool operator() (const content::DownloadItem* a,
- const content::DownloadItem* b) const;
+ bool operator()(const download::DownloadItem* a,
+ const download::DownloadItem* b) const;
};
- using SortedSet = std::set<content::DownloadItem*, StartTimeComparator>;
+ using SortedSet = std::set<download::DownloadItem*, StartTimeComparator>;
// Called by both constructors to initialize common state.
void Init();
@@ -95,7 +95,7 @@ class DownloadsListTracker
void RebuildSortedItems();
// Whether |item| should show on the current page.
- bool ShouldShow(const content::DownloadItem& item) const;
+ bool ShouldShow(const download::DownloadItem& item) const;
// Returns the index of |item| in |sorted_items_|.
size_t GetIndex(const SortedSet::iterator& item) const;
@@ -118,7 +118,7 @@ class DownloadsListTracker
// Callback used to determine if an item should show on the page. Set to
// |ShouldShow()| in default constructor, passed in while testing.
- base::Callback<bool(const content::DownloadItem&)> should_show_;
+ base::Callback<bool(const download::DownloadItem&)> should_show_;
// When this is true, all changes to downloads that affect the page are sent
// via JavaScript.
diff --git a/chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker_unittest.cc b/chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker_unittest.cc
index a2bad3f2d52..8300e101409 100644
--- a/chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker_unittest.cc
@@ -22,7 +22,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-using content::DownloadItem;
+using download::DownloadItem;
using content::MockDownloadItem;
using DownloadVector = std::vector<DownloadItem*>;
using testing::_;
@@ -82,7 +82,7 @@ class TestDownloadsListTracker : public DownloadsListTracker {
protected:
std::unique_ptr<base::DictionaryValue> CreateDownloadItemValue(
- content::DownloadItem* item) const override {
+ download::DownloadItem* item) const override {
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
CHECK_LE(item->GetId(), static_cast<uint64_t>(INT_MAX));
dict->SetInteger("id", item->GetId());
diff --git a/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.cc b/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.cc
index 7cb3c72c564..4138366f5a8 100644
--- a/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.cc
+++ b/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.cc
@@ -32,11 +32,11 @@
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
+#include "components/download/public/common/download_danger_type.h"
+#include "components/download/public/common/download_item.h"
#include "components/prefs/pref_service.h"
#include "components/safe_browsing/common/safe_browsing_prefs.h"
#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/download_danger_type.h"
-#include "content/public/browser/download_item.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/url_data_source.h"
#include "content/public/browser/web_contents.h"
@@ -163,14 +163,14 @@ void MdDownloadsDOMHandler::HandleGetDownloads(const base::ListValue* args) {
void MdDownloadsDOMHandler::HandleOpenFile(const base::ListValue* args) {
CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FILE);
- content::DownloadItem* file = GetDownloadByValue(args);
+ download::DownloadItem* file = GetDownloadByValue(args);
if (file)
file->OpenDownload();
}
void MdDownloadsDOMHandler::HandleDrag(const base::ListValue* args) {
CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_DRAG);
- content::DownloadItem* file = GetDownloadByValue(args);
+ download::DownloadItem* file = GetDownloadByValue(args);
if (!file)
return;
@@ -179,7 +179,7 @@ void MdDownloadsDOMHandler::HandleDrag(const base::ListValue* args) {
if (!web_contents)
return;
- if (file->GetState() != content::DownloadItem::COMPLETE)
+ if (file->GetState() != download::DownloadItem::COMPLETE)
return;
gfx::Image* icon = g_browser_process->icon_manager()->LookupIconFromFilepath(
@@ -195,18 +195,17 @@ void MdDownloadsDOMHandler::HandleDrag(const base::ListValue* args) {
void MdDownloadsDOMHandler::HandleSaveDangerous(const base::ListValue* args) {
CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS);
- content::DownloadItem* file = GetDownloadByValue(args);
+ download::DownloadItem* file = GetDownloadByValue(args);
SaveDownload(file);
}
-void MdDownloadsDOMHandler::SaveDownload(
- content::DownloadItem* download) {
+void MdDownloadsDOMHandler::SaveDownload(download::DownloadItem* download) {
if (!download)
return;
// If danger type is NOT DANGEROUS_FILE, chrome shows users a download danger
// prompt.
if (download->GetDangerType() !=
- content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE) {
+ download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE) {
ShowDangerPrompt(download);
} else {
// If danger type is DANGEROUS_FILE, chrome proceeds to keep this download
@@ -233,21 +232,21 @@ void MdDownloadsDOMHandler::HandleDiscardDangerous(
void MdDownloadsDOMHandler::HandleShow(const base::ListValue* args) {
CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SHOW);
- content::DownloadItem* file = GetDownloadByValue(args);
+ download::DownloadItem* file = GetDownloadByValue(args);
if (file)
file->ShowDownloadInShell();
}
void MdDownloadsDOMHandler::HandlePause(const base::ListValue* args) {
CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_PAUSE);
- content::DownloadItem* file = GetDownloadByValue(args);
+ download::DownloadItem* file = GetDownloadByValue(args);
if (file)
file->Pause();
}
void MdDownloadsDOMHandler::HandleResume(const base::ListValue* args) {
CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_RESUME);
- content::DownloadItem* file = GetDownloadByValue(args);
+ download::DownloadItem* file = GetDownloadByValue(args);
if (file)
file->Resume();
}
@@ -275,7 +274,7 @@ void MdDownloadsDOMHandler::HandleUndo(const base::ListValue* args) {
}
for (auto id : last_removed_ids) {
- content::DownloadItem* download = GetDownloadById(id);
+ download::DownloadItem* download = GetDownloadById(id);
if (!download)
continue;
@@ -294,7 +293,7 @@ void MdDownloadsDOMHandler::HandleUndo(const base::ListValue* args) {
void MdDownloadsDOMHandler::HandleCancel(const base::ListValue* args) {
CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CANCEL);
- content::DownloadItem* file = GetDownloadByValue(args);
+ download::DownloadItem* file = GetDownloadByValue(args);
if (file)
file->Cancel(true);
}
@@ -332,7 +331,7 @@ void MdDownloadsDOMHandler::RemoveDownloads(const DownloadVector& to_remove) {
DownloadItemModel item_model(download);
if (!item_model.ShouldShowInShelf() ||
- download->GetState() == content::DownloadItem::IN_PROGRESS) {
+ download->GetState() == download::DownloadItem::IN_PROGRESS) {
continue;
}
@@ -375,7 +374,7 @@ void MdDownloadsDOMHandler::FinalizeRemovals() {
removals_.pop_back();
for (const auto id : remove) {
- content::DownloadItem* download = GetDownloadById(id);
+ download::DownloadItem* download = GetDownloadById(id);
if (download)
download->Remove();
}
@@ -383,7 +382,7 @@ void MdDownloadsDOMHandler::FinalizeRemovals() {
}
void MdDownloadsDOMHandler::ShowDangerPrompt(
- content::DownloadItem* dangerous_item) {
+ download::DownloadItem* dangerous_item) {
DownloadDangerPrompt* danger_prompt = DownloadDangerPrompt::Create(
dangerous_item,
GetWebUIWebContents(),
@@ -398,7 +397,7 @@ void MdDownloadsDOMHandler::DangerPromptDone(
int download_id, DownloadDangerPrompt::Action action) {
if (action != DownloadDangerPrompt::ACCEPT)
return;
- content::DownloadItem* item = NULL;
+ download::DownloadItem* item = NULL;
if (GetMainNotifierManager())
item = GetMainNotifierManager()->GetDownload(download_id);
if (!item && GetOriginalNotifierManager())
@@ -416,7 +415,7 @@ bool MdDownloadsDOMHandler::IsDeletingHistoryAllowed() {
GetPrefs()->GetBoolean(prefs::kAllowDeletingBrowserHistory);
}
-content::DownloadItem* MdDownloadsDOMHandler::GetDownloadByValue(
+download::DownloadItem* MdDownloadsDOMHandler::GetDownloadByValue(
const base::ListValue* args) {
std::string download_id;
if (!args->GetString(0, &download_id)) {
@@ -433,8 +432,8 @@ content::DownloadItem* MdDownloadsDOMHandler::GetDownloadByValue(
return GetDownloadById(static_cast<uint32_t>(id));
}
-content::DownloadItem* MdDownloadsDOMHandler::GetDownloadById(uint32_t id) {
- content::DownloadItem* item = NULL;
+download::DownloadItem* MdDownloadsDOMHandler::GetDownloadById(uint32_t id) {
+ download::DownloadItem* item = NULL;
if (GetMainNotifierManager())
item = GetMainNotifierManager()->GetDownload(id);
if (!item && GetOriginalNotifierManager())
@@ -454,7 +453,7 @@ void MdDownloadsDOMHandler::CheckForRemovedFiles() {
}
void MdDownloadsDOMHandler::RemoveDownloadInArgs(const base::ListValue* args) {
- content::DownloadItem* file = GetDownloadByValue(args);
+ download::DownloadItem* file = GetDownloadByValue(args);
if (!file)
return;
diff --git a/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.h b/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.h
index 162aced0f12..fd401f52edb 100644
--- a/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.h
+++ b/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.h
@@ -22,12 +22,15 @@ class ListValue;
}
namespace content {
-class DownloadItem;
class DownloadManager;
class WebContents;
class WebUI;
}
+namespace download {
+class DownloadItem;
+}
+
class Profile;
// The handler for Javascript messages related to the "downloads" view,
@@ -98,14 +101,14 @@ class MdDownloadsDOMHandler : public content::WebContentsObserver,
// Actually remove downloads with an ID in |removals_|. This cannot be undone.
void FinalizeRemovals();
- using DownloadVector = std::vector<content::DownloadItem*>;
+ using DownloadVector = std::vector<download::DownloadItem*>;
// Remove all downloads in |to_remove|. Safe downloads can be revived,
// dangerous ones are immediately removed. Protected for testing.
void RemoveDownloads(const DownloadVector& to_remove);
// Helper function to handle save download event.
- void SaveDownload(content::DownloadItem* download);
+ void SaveDownload(download::DownloadItem* download);
private:
using IdSet = std::set<uint32_t>;
@@ -124,7 +127,7 @@ class MdDownloadsDOMHandler : public content::WebContentsObserver,
// user accepts the dangerous download. The native prompt will observe
// |dangerous| until either the dialog is dismissed or |dangerous| is no
// longer an in-progress dangerous download.
- virtual void ShowDangerPrompt(content::DownloadItem* dangerous);
+ virtual void ShowDangerPrompt(download::DownloadItem* dangerous);
// Conveys danger acceptance from the DownloadDangerPrompt to the
// DownloadItem.
@@ -136,10 +139,10 @@ class MdDownloadsDOMHandler : public content::WebContentsObserver,
bool IsDeletingHistoryAllowed();
// Returns the download that is referred to in a given value.
- content::DownloadItem* GetDownloadByValue(const base::ListValue* args);
+ download::DownloadItem* GetDownloadByValue(const base::ListValue* args);
// Returns the download with |id| or NULL if it doesn't exist.
- content::DownloadItem* GetDownloadById(uint32_t id);
+ download::DownloadItem* GetDownloadById(uint32_t id);
// Removes the download specified by an ID from JavaScript in |args|.
void RemoveDownloadInArgs(const base::ListValue* args);
diff --git a/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler_unittest.cc b/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler_unittest.cc
index 947dfe8a049..5b018373bd6 100644
--- a/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler_unittest.cc
@@ -32,7 +32,7 @@ class TestMdDownloadsDOMHandler : public MdDownloadsDOMHandler {
int danger_prompt_count() { return danger_prompt_count_; }
private:
- void ShowDangerPrompt(content::DownloadItem* dangerous) override {
+ void ShowDangerPrompt(download::DownloadItem* dangerous) override {
danger_prompt_count_++;
}
@@ -89,14 +89,14 @@ TEST_F(MdDownloadsDOMHandlerTest, HandleGetDownloads) {
}
TEST_F(MdDownloadsDOMHandlerTest, ClearAll) {
- std::vector<content::DownloadItem*> downloads;
+ std::vector<download::DownloadItem*> downloads;
// Safe, in-progress items should be passed over.
testing::StrictMock<content::MockDownloadItem> in_progress;
EXPECT_CALL(in_progress, IsDangerous()).WillOnce(testing::Return(false));
EXPECT_CALL(in_progress, IsTransient()).WillOnce(testing::Return(false));
- EXPECT_CALL(in_progress, GetState()).WillOnce(
- testing::Return(content::DownloadItem::IN_PROGRESS));
+ EXPECT_CALL(in_progress, GetState())
+ .WillOnce(testing::Return(download::DownloadItem::IN_PROGRESS));
downloads.push_back(&in_progress);
// Dangerous items should be removed (regardless of state).
@@ -109,8 +109,8 @@ TEST_F(MdDownloadsDOMHandlerTest, ClearAll) {
testing::StrictMock<content::MockDownloadItem> completed;
EXPECT_CALL(completed, IsDangerous()).WillOnce(testing::Return(false));
EXPECT_CALL(completed, IsTransient()).WillRepeatedly(testing::Return(false));
- EXPECT_CALL(completed, GetState()).WillOnce(
- testing::Return(content::DownloadItem::COMPLETE));
+ EXPECT_CALL(completed, GetState())
+ .WillOnce(testing::Return(download::DownloadItem::COMPLETE));
EXPECT_CALL(completed, GetId()).WillOnce(testing::Return(1));
EXPECT_CALL(completed, UpdateObservers());
downloads.push_back(&completed);
@@ -135,7 +135,7 @@ TEST_F(MdDownloadsDOMHandlerTest, HandleSaveDownload) {
testing::StrictMock<content::MockDownloadItem> dangerous_file_type;
EXPECT_CALL(dangerous_file_type, GetDangerType())
.WillRepeatedly(
- testing::Return(content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE));
+ testing::Return(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE));
EXPECT_CALL(dangerous_file_type, GetId())
.WillOnce(testing::Return(uint32_t()));
TestMdDownloadsDOMHandler handler(manager(), web_ui());
@@ -148,7 +148,7 @@ TEST_F(MdDownloadsDOMHandlerTest, HandleSaveDownload) {
testing::StrictMock<content::MockDownloadItem> malicious_download;
EXPECT_CALL(malicious_download, GetDangerType())
.WillRepeatedly(
- testing::Return(content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL));
+ testing::Return(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL));
handler.SaveDownload(&malicious_download);
EXPECT_EQ(1, handler.danger_prompt_count());
}
diff --git a/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_ui.cc b/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_ui.cc
index 00665110a4a..e3938ed4ed6 100644
--- a/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_ui.cc
+++ b/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_ui.cc
@@ -16,8 +16,8 @@
#include "chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.h"
#include "chrome/browser/ui/webui/metrics_handler.h"
#include "chrome/browser/ui/webui/theme_source.h"
+#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_switches.h"
-#include "chrome/common/features.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/browser_resources.h"
diff --git a/chromium/chrome/browser/ui/webui/media/media_engagement_ui.cc b/chromium/chrome/browser/ui/webui/media/media_engagement_ui.cc
index 75a02a671c8..9bf372c9b25 100644
--- a/chromium/chrome/browser/ui/webui/media/media_engagement_ui.cc
+++ b/chromium/chrome/browser/ui/webui/media/media_engagement_ui.cc
@@ -73,7 +73,7 @@ MediaEngagementUI::MediaEngagementUI(content::WebUI* web_ui)
source->AddResourcePath(
"chrome/browser/media/media_engagement_score_details.mojom.js",
IDR_MEDIA_ENGAGEMENT_MOJO_JS);
- source->AddResourcePath("url/mojo/url.mojom.js", IDR_URL_MOJO_JS);
+ source->AddResourcePath("url/mojom/url.mojom.js", IDR_URL_MOJO_JS);
source->SetDefaultResource(IDR_MEDIA_ENGAGEMENT_HTML);
source->UseGzip();
content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), source.release());
diff --git a/chromium/chrome/browser/ui/webui/media/webrtc_logs_ui.cc b/chromium/chrome/browser/ui/webui/media/webrtc_logs_ui.cc
index a461fb4d442..782ea7d2fe4 100644
--- a/chromium/chrome/browser/ui/webui/media/webrtc_logs_ui.cc
+++ b/chromium/chrome/browser/ui/webui/media/webrtc_logs_ui.cc
@@ -19,7 +19,6 @@
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
-#include "chrome/browser/media/webrtc/webrtc_log_list.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/browser_resources.h"
@@ -27,6 +26,7 @@
#include "components/prefs/pref_service.h"
#include "components/upload_list/upload_list.h"
#include "components/version_info/version_info.h"
+#include "components/webrtc_logging/browser/log_list.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
@@ -110,11 +110,12 @@ class WebRtcLogsDOMHandler : public WebUIMessageHandler {
};
WebRtcLogsDOMHandler::WebRtcLogsDOMHandler(Profile* profile)
- : log_dir_(WebRtcLogList::GetWebRtcLogDirectoryForBrowserContextPath(
- profile->GetPath())),
+ : log_dir_(
+ webrtc_logging::LogList::GetWebRtcLogDirectoryForBrowserContextPath(
+ profile->GetPath())),
list_available_(false),
js_request_pending_(false) {
- upload_list_ = WebRtcLogList::CreateWebRtcLogList(profile);
+ upload_list_ = webrtc_logging::LogList::CreateWebRtcLogList(profile);
}
WebRtcLogsDOMHandler::~WebRtcLogsDOMHandler() {
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_file_dialog.cc b/chromium/chrome/browser/ui/webui/media_router/media_router_file_dialog.cc
index 7cd940c4877..2506cbba5b2 100644
--- a/chromium/chrome/browser/ui/webui/media_router/media_router_file_dialog.cc
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_file_dialog.cc
@@ -252,6 +252,7 @@ IssueInfo MediaRouterFileDialog::CreateIssue(
// Create issue shouldn't be called with FILE_OK, but to ensure things
// compile, fall through sets |issue_title| to the generic error.
NOTREACHED();
+ FALLTHROUGH;
case MediaRouterFileDialog::UNKNOWN_FAILURE:
issue_title = l10n_util::GetStringUTF8(
IDS_MEDIA_ROUTER_ISSUE_FILE_CAST_GENERIC_ERROR);
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.cc b/chromium/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.cc
index f709b8725f6..fc3afe5587f 100644
--- a/chromium/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.cc
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.h"
#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_ui_data_source.h"
namespace {
@@ -15,7 +16,7 @@ const char kLocalizedStringsFile[] = "strings.js";
void AddMediaRouterStrings(content::WebUIDataSource* html_source) {
html_source->AddLocalizedString("mediaRouterTitle", IDS_MEDIA_ROUTER_TITLE);
- html_source->AddLocalizedString("learnMoreText", IDS_MEDIA_ROUTER_LEARN_MORE);
+ html_source->AddLocalizedString("learnMoreText", IDS_LEARN_MORE);
html_source->AddLocalizedString("backButtonTitle",
IDS_MEDIA_ROUTER_BACK_BUTTON_TITLE);
html_source->AddLocalizedString("closeButtonTitle",
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_ui.cc b/chromium/chrome/browser/ui/webui/media_router/media_router_ui.cc
index 63b144ef051..4e10bf5fd64 100644
--- a/chromium/chrome/browser/ui/webui/media_router/media_router_ui.cc
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_ui.cc
@@ -55,12 +55,17 @@
#include "extensions/browser/extension_registry.h"
#include "extensions/common/constants.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
-#include "third_party/WebKit/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/WebKit/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/icu/source/i18n/unicode/coll.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/web_dialogs/web_dialog_delegate.h"
#include "url/origin.h"
+#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
+#include "chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider.h"
+#include "ui/display/display.h"
+#endif
+
namespace media_router {
namespace {
@@ -111,7 +116,7 @@ base::TimeDelta GetRouteRequestTimeout(MediaCastMode cast_mode) {
// used by the Media Router to find such a matching route if it exists.
MediaSource GetSourceForRouteObserver(const std::vector<MediaSource>& sources) {
auto source_it =
- std::find_if(sources.begin(), sources.end(), CanConnectToMediaSource);
+ std::find_if(sources.begin(), sources.end(), IsCastPresentationUrl);
return source_it != sources.end() ? *source_it : MediaSource("");
}
@@ -338,11 +343,6 @@ MediaRouterUI::MediaRouterUI(content::WebUI* web_ui)
std::unique_ptr<content::WebUIDataSource> html_source(
content::WebUIDataSource::Create(chrome::kChromeUIMediaRouterHost));
- content::WebContents* wc = web_ui->GetWebContents();
- DCHECK(wc);
- content::BrowserContext* context = wc->GetBrowserContext();
- router_ = MediaRouterFactory::GetApiForBrowserContext(context);
-
AddLocalizedStrings(html_source.get());
AddMediaRouterUIResources(html_source.get());
// Ownership of |html_source| is transferred to the BrowserContext.
@@ -397,9 +397,10 @@ void MediaRouterUI::InitWithDefaultMediaSource(
OnDefaultPresentationChanged(delegate->GetDefaultPresentationRequest());
} else {
// Register for MediaRoute updates without a media source.
- routes_observer_.reset(new UIMediaRoutesObserver(
+ routes_observer_ = std::make_unique<UIMediaRoutesObserver>(
router_, MediaSource::Id(),
- base::Bind(&MediaRouterUI::OnRoutesUpdated, base::Unretained(this))));
+ base::BindRepeating(&MediaRouterUI::OnRoutesUpdated,
+ base::Unretained(this)));
}
}
@@ -423,11 +424,13 @@ void MediaRouterUI::InitWithStartPresentationContext(
void MediaRouterUI::InitCommon(content::WebContents* initiator) {
DCHECK(initiator);
- DCHECK(router_);
TRACE_EVENT_NESTABLE_ASYNC_INSTANT1("media_router", "UI", initiator,
"MediaRouterUI::InitCommon", this);
+ router_ = GetMediaRouter();
+ DCHECK(router_);
+
// Presentation requests from content must show the origin requesting
// presentation: crbug.com/704964
if (start_presentation_context_)
@@ -446,7 +449,7 @@ void MediaRouterUI::InitCommon(content::WebContents* initiator) {
collator_.reset();
}
- query_result_manager_.reset(new QueryResultManager(router_));
+ query_result_manager_ = std::make_unique<QueryResultManager>(router_);
query_result_manager_->AddObserver(this);
// Use a placeholder URL as origin for mirroring.
@@ -476,6 +479,11 @@ void MediaRouterUI::InitCommon(content::WebContents* initiator) {
// Get the current list of media routes, so that the WebUI will have routes
// information at initialization.
OnRoutesUpdated(router_->GetCurrentRoutes(), std::vector<MediaRoute::Id>());
+#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
+ display_observer_ = WebContentsDisplayObserver::Create(
+ initiator_,
+ base::BindRepeating(&MediaRouterUI::UpdateSinks, base::Unretained(this)));
+#endif
}
void MediaRouterUI::InitForTest(
@@ -484,7 +492,6 @@ void MediaRouterUI::InitForTest(
MediaRouterWebUIMessageHandler* handler,
std::unique_ptr<StartPresentationContext> context,
std::unique_ptr<MediaRouterFileDialog> file_dialog) {
- router_ = router;
handler_ = handler;
start_presentation_context_ = std::move(context);
InitForTest(std::move(file_dialog));
@@ -517,9 +524,10 @@ void MediaRouterUI::OnDefaultPresentationChanged(
// observer API for this case.
const MediaSource source_for_route_observer =
GetSourceForRouteObserver(sources);
- routes_observer_.reset(new UIMediaRoutesObserver(
+ routes_observer_ = std::make_unique<UIMediaRoutesObserver>(
router_, source_for_route_observer.id(),
- base::Bind(&MediaRouterUI::OnRoutesUpdated, base::Unretained(this))));
+ base::BindRepeating(&MediaRouterUI::OnRoutesUpdated,
+ base::Unretained(this)));
UpdateCastModes();
}
@@ -534,9 +542,10 @@ void MediaRouterUI::OnDefaultPresentationRemoved() {
forced_cast_mode_ = base::nullopt;
// Register for MediaRoute updates without a media source.
- routes_observer_.reset(new UIMediaRoutesObserver(
+ routes_observer_ = std::make_unique<UIMediaRoutesObserver>(
router_, MediaSource::Id(),
- base::Bind(&MediaRouterUI::OnRoutesUpdated, base::Unretained(this))));
+ base::BindRepeating(&MediaRouterUI::OnRoutesUpdated,
+ base::Unretained(this)));
UpdateCastModes();
}
@@ -580,8 +589,16 @@ void MediaRouterUI::UIInitialized() {
TRACE_EVENT_NESTABLE_ASYNC_END0("media_router", "UI", initiator_);
ui_initialized_ = true;
+ // Workaround for MediaRouterElementsBrowserTest, in which MediaRouterUI is
+ // created without calling one of the |Init*()| methods.
+ // TODO(imcheng): We should be able to instantiate |issue_observer_| during
+ // InitCommon by storing an initial Issue in this class.
+ if (!router_)
+ router_ = GetMediaRouter();
+
// Register for Issue updates.
- issues_observer_.reset(new UIIssuesObserver(GetIssueManager(), this));
+ issues_observer_ =
+ std::make_unique<UIIssuesObserver>(GetIssueManager(), this);
issues_observer_->Init();
}
@@ -792,9 +809,10 @@ void MediaRouterUI::SearchSinksAndCreateRoute(
// The CreateRoute() part of the function is accomplished in the callback
// OnSearchSinkResponseReceived().
- router_->SearchSinks(sink_id, source_id, search_criteria, domain,
- base::Bind(&MediaRouterUI::OnSearchSinkResponseReceived,
- weak_factory_.GetWeakPtr(), cast_mode));
+ router_->SearchSinks(
+ sink_id, source_id, search_criteria, domain,
+ base::BindRepeating(&MediaRouterUI::OnSearchSinkResponseReceived,
+ weak_factory_.GetWeakPtr(), cast_mode));
}
bool MediaRouterUI::UserSelectedTabMirroringForCurrentOrigin() const {
@@ -842,7 +860,7 @@ void MediaRouterUI::OnResultsUpdated(
});
if (ui_initialized_)
- handler_->UpdateSinks(sinks_);
+ UpdateSinks();
}
void MediaRouterUI::SetIssue(const Issue& issue) {
@@ -994,6 +1012,30 @@ GURL MediaRouterUI::GetFrameURL() const {
: GURL();
}
+std::vector<MediaSinkWithCastModes> MediaRouterUI::GetEnabledSinks() const {
+#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
+ if (!display_observer_)
+ return sinks_;
+
+ // Filter out the wired display sink for the display that the dialog is on.
+ // This is not the best place to do this because MRUI should not perform a
+ // provider-specific behavior, but we currently do not have a way to
+ // communicate dialog-specific information to/from the
+ // WiredDisplayMediaRouteProvider.
+ std::vector<MediaSinkWithCastModes> enabled_sinks;
+ const std::string display_sink_id =
+ WiredDisplayMediaRouteProvider::GetSinkIdForDisplay(
+ display_observer_->GetCurrentDisplay());
+ for (const MediaSinkWithCastModes& sink : sinks_) {
+ if (sink.sink.id() != display_sink_id)
+ enabled_sinks.push_back(sink);
+ }
+ return enabled_sinks;
+#else
+ return sinks_;
+#endif
+}
+
std::string MediaRouterUI::GetPresentationRequestSourceName() const {
GURL gurl = GetFrameURL();
return gurl.SchemeIs(extensions::kExtensionScheme)
@@ -1082,4 +1124,12 @@ IssueManager* MediaRouterUI::GetIssueManager() {
return router_->GetIssueManager();
}
+void MediaRouterUI::UpdateSinks() {
+ handler_->UpdateSinks(GetEnabledSinks());
+}
+
+MediaRouter* MediaRouterUI::GetMediaRouter() {
+ return MediaRouterFactory::GetApiForBrowserContext(
+ web_ui()->GetWebContents()->GetBrowserContext());
+}
} // namespace media_router
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_ui.h b/chromium/chrome/browser/ui/webui/media_router/media_router_ui.h
index 018dafa34d8..e8abac52f0a 100644
--- a/chromium/chrome/browser/ui/webui/media_router/media_router_ui.h
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_ui.h
@@ -8,12 +8,15 @@
#include <memory>
#include <set>
#include <string>
+#include <unordered_map>
+#include <utility>
#include <vector>
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
+#include "build/build_config.h"
#include "chrome/browser/media/router/media_router_dialog_controller.h"
#include "chrome/browser/media/router/mojo/media_route_controller.h"
#include "chrome/browser/media/router/presentation/presentation_service_delegate_impl.h"
@@ -26,8 +29,13 @@
#include "chrome/common/media_router/media_source.h"
#include "content/public/browser/web_ui_data_source.h"
#include "third_party/icu/source/common/unicode/uversion.h"
+#include "ui/base/ui_features.h"
#include "url/gurl.h"
+#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
+#include "chrome/browser/ui/webui/media_router/web_contents_display_observer.h"
+#endif
+
namespace content {
struct PresentationRequest;
class WebContents;
@@ -145,13 +153,15 @@ class MediaRouterUI
// mode is MediaCastMode::DESKTOP_MIRROR.
virtual void RecordCastModeSelection(MediaCastMode cast_mode);
+ // Returns a subset of |sinks_| that should be listed in the dialog.
+ std::vector<MediaSinkWithCastModes> GetEnabledSinks() const;
+
// Returns the hostname of the PresentationRequest's parent frame URL.
std::string GetPresentationRequestSourceName() const;
std::string GetTruncatedPresentationRequestSourceName() const;
bool HasPendingRouteRequest() const {
return current_route_request_id_ != -1;
}
- const std::vector<MediaSinkWithCastModes>& sinks() const { return sinks_; }
const std::vector<MediaRoute>& routes() const { return routes_; }
const std::vector<MediaRoute::Id>& joinable_route_ids() const {
return joinable_route_ids_;
@@ -193,6 +203,13 @@ class MediaRouterUI
void InitForTest(std::unique_ptr<MediaRouterFileDialog> file_dialog);
+#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
+ void set_display_observer_for_test(
+ std::unique_ptr<WebContentsDisplayObserver> display_observer) {
+ display_observer_ = std::move(display_observer);
+ }
+#endif
+
private:
friend class MediaRouterUITest;
@@ -216,6 +233,8 @@ class MediaRouterUI
FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, SendMediaCommands);
FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, SendMediaStatusUpdate);
FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, SendInitialMediaStatusUpdate);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest,
+ UpdateSinksWhenDialogMovesToAnotherDisplay);
class UIIssuesObserver;
class WebContentsFullscreenOnLoadedObserver;
@@ -223,8 +242,8 @@ class MediaRouterUI
class UIMediaRoutesObserver : public MediaRoutesObserver {
public:
using RoutesUpdatedCallback =
- base::Callback<void(const std::vector<MediaRoute>&,
- const std::vector<MediaRoute::Id>&)>;
+ base::RepeatingCallback<void(const std::vector<MediaRoute>&,
+ const std::vector<MediaRoute::Id>&)>;
UIMediaRoutesObserver(MediaRouter* router,
const MediaSource::Id& source_id,
const RoutesUpdatedCallback& callback);
@@ -381,6 +400,12 @@ class MediaRouterUI
// Returns the IssueManager associated with |router_|.
IssueManager* GetIssueManager();
+ // Sends the current list of enabled sinks to |handler_|.
+ void UpdateSinks();
+
+ // Overridden by tests.
+ virtual MediaRouter* GetMediaRouter();
+
// Owned by the |web_ui| passed in the ctor, and guaranteed to be deleted
// only after it has deleted |this|.
MediaRouterWebUIMessageHandler* handler_ = nullptr;
@@ -449,6 +474,11 @@ class MediaRouterUI
// If set, a cast mode that is required to be shown first.
base::Optional<MediaCastMode> forced_cast_mode_;
+#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
+ // Keeps track of which display the dialog WebContents is on.
+ std::unique_ptr<WebContentsDisplayObserver> display_observer_;
+#endif
+
// NOTE: Weak pointers must be invalidated before all other member variables.
// Therefore |weak_factory_| must be placed at the end.
base::WeakPtrFactory<MediaRouterUI> weak_factory_;
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_ui_service_factory_unittest.cc b/chromium/chrome/browser/ui/webui/media_router/media_router_ui_service_factory_unittest.cc
index c3d0cbdb82e..3ee9f6d215e 100644
--- a/chromium/chrome/browser/ui/webui/media_router/media_router_ui_service_factory_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_ui_service_factory_unittest.cc
@@ -4,6 +4,8 @@
#include <memory>
+#include "chrome/browser/media/router/media_router_factory.h"
+#include "chrome/browser/media/router/test/mock_media_router.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/browser/ui/toolbar/toolbar_actions_model_factory.h"
@@ -26,6 +28,8 @@ class MediaRouterUIServiceFactoryUnitTest : public testing::Test {
// requires ToolbarActionsModel.
builder.AddTestingFactory(ToolbarActionsModelFactory::GetInstance(),
BuildFakeToolBarActionsModel);
+ builder.AddTestingFactory(MediaRouterFactory::GetInstance(),
+ MockMediaRouter::Create);
profile_ = builder.Build();
}
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc b/chromium/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
index 2aa46df6beb..6a6e17efd68 100644
--- a/chromium/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
#include "chrome/browser/media/router/media_router_factory.h"
#include "chrome/browser/media/router/test/media_router_mojo_test.h"
#include "chrome/browser/media/router/test/mock_media_router.h"
@@ -31,6 +32,12 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
+#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
+#include "chrome/browser/media/router/providers/wired_display/wired_display_media_route_provider.h"
+#include "chrome/browser/ui/webui/media_router/web_contents_display_observer.h"
+#include "ui/display/display.h"
+#endif
+
using content::WebContents;
using testing::_;
using testing::AnyNumber;
@@ -53,6 +60,8 @@ class MockMediaRouterWebUIMessageHandler
: MediaRouterWebUIMessageHandler(media_router_ui) {}
~MockMediaRouterWebUIMessageHandler() override {}
+ MOCK_METHOD1(UpdateSinks,
+ void(const std::vector<MediaSinkWithCastModes>& sinks));
MOCK_METHOD1(UpdateIssue, void(const Issue& issue));
MOCK_METHOD1(UpdateMediaRouteStatus, void(const MediaStatus& status));
MOCK_METHOD3(UpdateCastModes,
@@ -71,6 +80,24 @@ class MockMediaRouterFileDialog : public MediaRouterFileDialog {
MOCK_METHOD1(OpenFileDialog, void(Browser* browser));
};
+#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
+class TestWebContentsDisplayObserver : public WebContentsDisplayObserver {
+ public:
+ explicit TestWebContentsDisplayObserver(const display::Display& display)
+ : display_(display) {}
+ ~TestWebContentsDisplayObserver() override {}
+
+ const display::Display& GetCurrentDisplay() const override {
+ return display_;
+ }
+
+ void set_display(const display::Display& display) { display_ = display; }
+
+ private:
+ display::Display display_;
+};
+#endif // !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
+
class PresentationRequestCallbacks {
public:
PresentationRequestCallbacks() {}
@@ -90,6 +117,19 @@ class PresentationRequestCallbacks {
content::PresentationError expected_error_;
};
+class TestMediaRouterUI : public MediaRouterUI {
+ public:
+ TestMediaRouterUI(content::WebUI* web_ui, MediaRouter* router)
+ : MediaRouterUI(web_ui), router_(router) {}
+ ~TestMediaRouterUI() override = default;
+
+ MediaRouter* GetMediaRouter() override { return router_; }
+
+ private:
+ MediaRouter* router_;
+ DISALLOW_COPY_AND_ASSIGN(TestMediaRouterUI);
+};
+
class MediaRouterUITest : public ChromeRenderViewHostTestHarness {
public:
MediaRouterUITest()
@@ -135,7 +175,8 @@ class MediaRouterUITest : public ChromeRenderViewHostTestHarness {
web_ui_contents_.reset(
WebContents::Create(WebContents::CreateParams(profile)));
web_ui_.set_web_contents(web_ui_contents_.get());
- media_router_ui_ = std::make_unique<MediaRouterUI>(&web_ui_);
+ media_router_ui_ =
+ std::make_unique<TestMediaRouterUI>(&web_ui_, &mock_router_);
message_handler_ = std::make_unique<MockMediaRouterWebUIMessageHandler>(
media_router_ui_.get());
@@ -186,7 +227,7 @@ class MediaRouterUITest : public ChromeRenderViewHostTestHarness {
content::TestWebUI web_ui_;
std::unique_ptr<WebContents> web_ui_contents_;
std::unique_ptr<StartPresentationContext> start_presentation_context_;
- std::unique_ptr<MediaRouterUI> media_router_ui_;
+ std::unique_ptr<TestMediaRouterUI> media_router_ui_;
std::unique_ptr<MockMediaRouterWebUIMessageHandler> message_handler_;
MockMediaRouterFileDialog* mock_file_dialog_ = nullptr;
std::vector<MediaSinksObserver*> media_sinks_observers_;
@@ -760,7 +801,8 @@ TEST_F(MediaRouterUITest, SetsForcedCastModeWithPresentationURLs) {
web_ui_contents_.reset(
WebContents::Create(WebContents::CreateParams(profile())));
web_ui_.set_web_contents(web_ui_contents_.get());
- media_router_ui_ = std::make_unique<MediaRouterUI>(&web_ui_);
+ media_router_ui_ =
+ std::make_unique<TestMediaRouterUI>(&web_ui_, &mock_router_);
message_handler_ = std::make_unique<MockMediaRouterWebUIMessageHandler>(
media_router_ui_.get());
message_handler_->SetWebUIForTest(&web_ui_);
@@ -770,28 +812,83 @@ TEST_F(MediaRouterUITest, SetsForcedCastModeWithPresentationURLs) {
return true;
}));
EXPECT_CALL(mock_router_, RegisterMediaRoutesObserver(_)).Times(AnyNumber());
- // For some reason we push two sets of cast modes to the dialog, even when
- // initializing the dialog with a presentation request. The WebUI can handle
- // the forced mode that is not in the initial cast mode set, but is this a
- // bug?
- CastModeSet expected_modes({MediaCastMode::TAB_MIRROR,
- MediaCastMode::DESKTOP_MIRROR,
- MediaCastMode::LOCAL_FILE});
- EXPECT_CALL(*message_handler_,
- UpdateCastModes(
- expected_modes, "",
- base::Optional<MediaCastMode>(MediaCastMode::PRESENTATION)));
- expected_modes.insert(MediaCastMode::PRESENTATION);
- EXPECT_CALL(*message_handler_,
- UpdateCastModes(
- expected_modes, "google.com",
- base::Optional<MediaCastMode>(MediaCastMode::PRESENTATION)));
- media_router_ui_->UIInitialized();
+
+ CastModeSet expected_modes(
+ {MediaCastMode::TAB_MIRROR, MediaCastMode::DESKTOP_MIRROR,
+ MediaCastMode::LOCAL_FILE, MediaCastMode::PRESENTATION});
media_router_ui_->InitForTest(
&mock_router_, web_contents(), message_handler_.get(),
std::move(start_presentation_context_), nullptr);
+ EXPECT_EQ(expected_modes, media_router_ui_->cast_modes());
+ EXPECT_EQ(base::Optional<MediaCastMode>(MediaCastMode::PRESENTATION),
+ media_router_ui_->forced_cast_mode());
+ EXPECT_EQ("google.com", media_router_ui_->GetPresentationRequestSourceName());
+
// |media_router_ui_| takes ownership of |request_callbacks|.
media_router_ui_.reset();
}
+#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
+// A wired display sink should not be on the sinks list when the dialog is on
+// that display, to prevent showing a fullscreen presentation window over the
+// controlling window.
+TEST_F(MediaRouterUITest, UpdateSinksWhenDialogMovesToAnotherDisplay) {
+ const display::Display display1(1000001);
+ const display::Display display2(1000002);
+ const std::string display_sink_id1 =
+ WiredDisplayMediaRouteProvider::GetSinkIdForDisplay(display1);
+ const std::string display_sink_id2 =
+ WiredDisplayMediaRouteProvider::GetSinkIdForDisplay(display2);
+
+ CreateMediaRouterUI(profile());
+
+ auto display_observer_unique =
+ std::make_unique<TestWebContentsDisplayObserver>(display1);
+ TestWebContentsDisplayObserver* display_observer =
+ display_observer_unique.get();
+ media_router_ui_->set_display_observer_for_test(
+ std::move(display_observer_unique));
+
+ std::vector<MediaSinkWithCastModes> sinks;
+ MediaSinkWithCastModes display_sink1(
+ MediaSink(display_sink_id1, "sink", SinkIconType::GENERIC));
+ sinks.push_back(display_sink1);
+ MediaSinkWithCastModes display_sink2(
+ MediaSink(display_sink_id2, "sink", SinkIconType::GENERIC));
+ sinks.push_back(display_sink2);
+ MediaSinkWithCastModes sink3(MediaSink("id3", "sink", SinkIconType::GENERIC));
+ sinks.push_back(sink3);
+ media_router_ui_->OnResultsUpdated(sinks);
+
+ // Initially |display_sink1| should not be on the sinks list because we are on
+ // |display1|.
+ EXPECT_CALL(*message_handler_, UpdateSinks(_))
+ .WillOnce(Invoke([&display_sink_id1](
+ const std::vector<MediaSinkWithCastModes>& sinks) {
+ EXPECT_EQ(2u, sinks.size());
+ EXPECT_TRUE(std::find_if(sinks.begin(), sinks.end(),
+ [&display_sink_id1](
+ const MediaSinkWithCastModes& sink) {
+ return sink.sink.id() == display_sink_id1;
+ }) == sinks.end());
+ }));
+ media_router_ui_->UpdateSinks();
+
+ // Change the display to |display2|. Now |display_sink2| should be removed
+ // from the list of sinks.
+ EXPECT_CALL(*message_handler_, UpdateSinks(_))
+ .WillOnce(Invoke([&display_sink_id2](
+ const std::vector<MediaSinkWithCastModes>& sinks) {
+ EXPECT_EQ(2u, sinks.size());
+ EXPECT_TRUE(std::find_if(sinks.begin(), sinks.end(),
+ [&display_sink_id2](
+ const MediaSinkWithCastModes& sink) {
+ return sink.sink.id() == display_sink_id2;
+ }) == sinks.end());
+ }));
+ display_observer->set_display(display2);
+ media_router_ui_->UpdateSinks();
+}
+#endif // !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
+
} // namespace media_router
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_web_ui_test.cc b/chromium/chrome/browser/ui/webui/media_router/media_router_web_ui_test.cc
index 11ec23b4515..0b0a38ed222 100644
--- a/chromium/chrome/browser/ui/webui/media_router/media_router_web_ui_test.cc
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_web_ui_test.cc
@@ -4,6 +4,8 @@
#include "chrome/browser/ui/webui/media_router/media_router_web_ui_test.h"
+#include "chrome/browser/media/router/media_router_factory.h"
+#include "chrome/browser/media/router/test/mock_media_router.h"
#include "chrome/browser/ui/toolbar/mock_media_router_action_controller.h"
#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/browser/ui/toolbar/toolbar_actions_model_factory.h"
@@ -45,14 +47,18 @@ MediaRouterWebUITest::MediaRouterWebUITest(bool require_mock_ui_service)
MediaRouterWebUITest::~MediaRouterWebUITest() {}
TestingProfile::TestingFactories MediaRouterWebUITest::GetTestingFactories() {
+ TestingProfile::TestingFactories factories = {
+ {media_router::MediaRouterFactory::GetInstance(),
+ &media_router::MockMediaRouter::Create}};
if (require_mock_ui_service_) {
- return {
- {media_router::MediaRouterUIServiceFactory::GetInstance(),
- BuildMockMediaRouterUIService},
- {ToolbarActionsModelFactory::GetInstance(), BuildToolbarActionsModel}};
+ factories.emplace_back(
+ media_router::MediaRouterUIServiceFactory::GetInstance(),
+ BuildMockMediaRouterUIService);
+ factories.emplace_back(ToolbarActionsModelFactory::GetInstance(),
+ BuildToolbarActionsModel);
}
- return BrowserWithTestWindowTest::GetTestingFactories();
+ return factories;
}
BrowserWindow* MediaRouterWebUITest::CreateBrowserWindow() {
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc b/chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc
index e528bf82973..96be58cdb9a 100644
--- a/chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc
@@ -4,7 +4,9 @@
#include "chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h"
+#include <algorithm>
#include <memory>
+#include <set>
#include <string>
#include <utility>
@@ -506,7 +508,8 @@ void MediaRouterWebUIMessageHandler::OnRequestInitialData(
base::StringPrintf(kHelpPageUrlPrefix, 3249268));
std::unique_ptr<base::DictionaryValue> sinks_and_identity(
- SinksAndIdentityToValue(media_router_ui_->sinks(), GetAccountInfo()));
+ SinksAndIdentityToValue(media_router_ui_->GetEnabledSinks(),
+ GetAccountInfo()));
initial_data.Set("sinksAndIdentity", std::move(sinks_and_identity));
std::unique_ptr<base::ListValue> routes(RoutesToValue(
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h b/chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h
index 43e0f44b13f..8ebaa248630 100644
--- a/chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h
@@ -5,6 +5,8 @@
#ifndef CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_WEBUI_MESSAGE_HANDLER_H_
#define CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_WEBUI_MESSAGE_HANDLER_H_
+#include <memory>
+#include <string>
#include <unordered_map>
#include <vector>
@@ -41,7 +43,7 @@ class MediaRouterWebUIMessageHandler : public content::WebUIMessageHandler {
~MediaRouterWebUIMessageHandler() override;
// Methods to update the status displayed by the dialog.
- void UpdateSinks(const std::vector<MediaSinkWithCastModes>& sinks);
+ virtual void UpdateSinks(const std::vector<MediaSinkWithCastModes>& sinks);
void UpdateRoutes(const std::vector<MediaRoute>& routes,
const std::vector<MediaRoute::Id>& joinable_route_ids,
const std::unordered_map<MediaRoute::Id, MediaCastMode>&
diff --git a/chromium/chrome/browser/ui/webui/media_router/web_contents_display_observer.h b/chromium/chrome/browser/ui/webui/media_router/web_contents_display_observer.h
new file mode 100644
index 00000000000..40d2d9411cb
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/web_contents_display_observer.h
@@ -0,0 +1,38 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_WEB_CONTENTS_DISPLAY_OBSERVER_H_
+#define CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_WEB_CONTENTS_DISPLAY_OBSERVER_H_
+
+#include <memory>
+
+#include "base/callback.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace display {
+class Display;
+}
+
+namespace media_router {
+
+// Keeps track of the display that a WebContents is on.
+class WebContentsDisplayObserver {
+ public:
+ // |web_contents|: WebContents to observe.
+ // |callback|: Gets called whenever |web_contents| moves to another display.
+ static std::unique_ptr<WebContentsDisplayObserver> Create(
+ content::WebContents* web_contents,
+ base::RepeatingClosure callback);
+
+ virtual ~WebContentsDisplayObserver() = default;
+
+ virtual const display::Display& GetCurrentDisplay() const = 0;
+};
+
+} // namespace media_router
+
+#endif // CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_WEB_CONTENTS_DISPLAY_OBSERVER_H_
diff --git a/chromium/chrome/browser/ui/webui/memory_internals_ui.cc b/chromium/chrome/browser/ui/webui/memory_internals_ui.cc
index ebf83424203..c38eda39caa 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/features.h"
+#include "base/allocator/buildflags.h"
#include "base/bind.h"
#include "base/memory/weak_ptr.h"
#include "base/path_service.h"
@@ -61,11 +61,6 @@ std::string GetMessageString() {
case ProfilingProcessHost::Mode::kGpu:
return std::string("Memory logging is enabled for just the gpu process.");
- case ProfilingProcessHost::Mode::kManual:
- return std::string(
- "Memory logging must be manually enabled for each process via "
- "chrome://memory-internals.");
-
case ProfilingProcessHost::Mode::kMinimal:
return std::string(
"Memory logging is enabled for the browser and GPU processes.");
@@ -76,14 +71,11 @@ std::string GetMessageString() {
"processes. This UI is disabled.");
case ProfilingProcessHost::Mode::kNone:
+ case ProfilingProcessHost::Mode::kManual:
default:
- return base::StringPrintf(
- "Memory logging is not enabled. Start with --%s=%s"
- " to log all processes, or --%s=%s to log only the browser and GPU "
- "processes. "
- "Other options available in chrome://flags",
- switches::kMemlog, switches::kMemlogModeAll, switches::kMemlog,
- switches::kMemlogModeMinimal);
+ return std::string(
+ "Memory logging must be manually enabled for each process via "
+ "chrome://memory-internals.");
}
#elif defined(ADDRESS_SANITIZER) || defined(SYZYASAN)
return "Memory logging is not available in this build because a memory "
diff --git a/chromium/chrome/browser/ui/webui/nacl_ui.cc b/chromium/chrome/browser/ui/webui/nacl_ui.cc
index 80e72f55980..d687697c457 100644
--- a/chromium/chrome/browser/ui/webui/nacl_ui.cc
+++ b/chromium/chrome/browser/ui/webui/nacl_ui.cc
@@ -24,7 +24,6 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task_scheduler/post_task.h"
-#include "base/threading/sequenced_worker_pool.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/plugins/plugin_prefs.h"
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 158173bac8e..302df0f9a2c 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
@@ -72,7 +72,7 @@
#include "net/log/net_log_capture_mode.h"
#include "net/log/net_log_entry.h"
#include "net/log/net_log_util.h"
-#include "net/proxy/proxy_service.h"
+#include "net/proxy_resolution/proxy_service.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
@@ -84,6 +84,7 @@
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/debug_daemon_client.h"
#include "chromeos/network/onc/onc_certificate_importer_impl.h"
+#include "chromeos/network/onc/onc_parsed_certificates.h"
#include "chromeos/network/onc/onc_utils.h"
#endif
@@ -683,7 +684,7 @@ void NetInternalsMessageHandler::IOThreadImpl::OnGetNetInfo(
void NetInternalsMessageHandler::IOThreadImpl::OnReloadProxySettings(
const base::ListValue* list) {
DCHECK(!list);
- GetMainContext()->proxy_service()->ForceReloadProxyConfig();
+ GetMainContext()->proxy_resolution_service()->ForceReloadProxyConfig();
// Cause the renderer to be notified of the new values.
SendNetInfo(net::NET_INFO_PROXY_SETTINGS);
@@ -692,7 +693,7 @@ void NetInternalsMessageHandler::IOThreadImpl::OnReloadProxySettings(
void NetInternalsMessageHandler::IOThreadImpl::OnClearBadProxies(
const base::ListValue* list) {
DCHECK(!list);
- GetMainContext()->proxy_service()->ClearBadProxiesCache();
+ GetMainContext()->proxy_resolution_service()->ClearBadProxiesCache();
// Cause the renderer to be notified of the new values.
SendNetInfo(net::NET_INFO_BAD_PROXIES);
@@ -1020,11 +1021,10 @@ void NetInternalsMessageHandler::ImportONCFileToNSSDB(
chromeos::onc::CertificateImporterImpl cert_importer(
BrowserThread::GetTaskRunnerForThread(BrowserThread::IO), nssdb);
cert_importer.ImportCertificates(
- certificates,
+ std::make_unique<chromeos::onc::OncParsedCertificates>(certificates),
onc_source,
base::Bind(&NetInternalsMessageHandler::OnCertificatesImported,
- AsWeakPtr(),
- error));
+ AsWeakPtr(), error));
}
void NetInternalsMessageHandler::OnCertificatesImported(
@@ -1133,9 +1133,9 @@ void NetInternalsMessageHandler::IOThreadImpl::OnSetCaptureMode(
// can be called from ANY THREAD.
void NetInternalsMessageHandler::IOThreadImpl::OnAddEntry(
const net::NetLogEntry& entry) {
- BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
- base::BindOnce(&IOThreadImpl::AddEntryToQueue, this,
- base::Passed(entry.ToValue())));
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&IOThreadImpl::AddEntryToQueue, this, entry.ToValue()));
}
// Note that this can be called from ANY THREAD.
@@ -1153,7 +1153,7 @@ void NetInternalsMessageHandler::IOThreadImpl::SendJavascriptCommand(
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::BindOnce(&IOThreadImpl::SendJavascriptCommand,
- this, command, base::Passed(&arg)));
+ this, command, std::move(arg)));
}
void NetInternalsMessageHandler::IOThreadImpl::AddEntryToQueue(
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 708f400dd42..604df9e0b0c 100644
--- a/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
+++ b/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
@@ -33,6 +33,7 @@
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/extensions/app_launch_params.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/browser/ui/extensions/extension_enable_flow.h"
@@ -40,11 +41,11 @@
#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"
+#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/extensions/extension_metrics.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
-#include "chrome/common/features.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/common/web_application_info.h"
@@ -298,7 +299,7 @@ void AppLauncherHandler::Observe(int type,
crx_installer->profile())) {
return;
}
- // Fall through.
+ FALLTHROUGH;
}
case extensions::NOTIFICATION_EXTENSION_LOAD_ERROR: {
attempted_bookmark_app_install_ = false;
@@ -608,6 +609,13 @@ void AppLauncherHandler::HandleShowAppInfo(const base::ListValue* args) {
if (!extension)
return;
+ if (extension->is_hosted_app() && extension->from_bookmark()) {
+ chrome::ShowSiteSettings(
+ chrome::FindBrowserWithWebContents(web_ui()->GetWebContents()),
+ extensions::AppLaunchInfo::GetFullLaunchURL(extension));
+ return;
+ }
+
UMA_HISTOGRAM_ENUMERATION("Apps.AppInfoDialog.Launches",
AppInfoLaunchSource::FROM_APPS_PAGE,
AppInfoLaunchSource::NUM_LAUNCH_SOURCES);
diff --git a/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc b/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
index 0ce8d87bb43..36cdfcfe8e4 100644
--- a/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
+++ b/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
@@ -24,7 +24,7 @@
#include "chrome/browser/ui/bookmarks/bookmark_bar_constants.h"
#include "chrome/browser/ui/webui/app_launcher_login_handler.h"
#include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
-#include "chrome/common/features.h"
+#include "chrome/common/buildflags.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/browser_resources.h"
@@ -292,7 +292,7 @@ void NTPResourceCache::CreateNewTabGuestHTML() {
int guest_tab_ids = IDR_GUEST_TAB_HTML;
int guest_tab_description_ids = IDS_NEW_TAB_GUEST_SESSION_DESCRIPTION;
int guest_tab_heading_ids = IDS_NEW_TAB_GUEST_SESSION_HEADING;
- int guest_tab_link_ids = IDS_NEW_TAB_GUEST_SESSION_LEARN_MORE_LINK;
+ int guest_tab_link_ids = IDS_LEARN_MORE;
#if defined(OS_CHROMEOS)
guest_tab_ids = IDR_GUEST_SESSION_TAB_HTML;
@@ -351,6 +351,16 @@ void NTPResourceCache::CreateNewTabGuestHTML() {
new_tab_guest_html_ = base::RefCountedString::TakeString(&full_html);
}
+// TODO(alancutter): Consider moving this utility function up somewhere where it
+// can be shared with md_bookmarks_ui.cc.
+// Ampersands are used by menus to determine which characters to use as shortcut
+// keys. This functionality is not implemented for NTP.
+static base::string16 GetLocalizedString(int message_id) {
+ base::string16 result = l10n_util::GetStringUTF16(message_id);
+ result.erase(std::remove(result.begin(), result.end(), '&'), result.end());
+ return result;
+}
+
void NTPResourceCache::CreateNewTabHTML() {
// TODO(estade): these strings should be defined in their relevant handlers
// (in GetLocalizedValues) and should have more legible names.
@@ -361,57 +371,63 @@ void NTPResourceCache::CreateNewTabHTML() {
load_time_data.SetString(
"bookmarkbarattached",
prefs->GetBoolean(bookmarks::prefs::kShowBookmarkBar) ? "true" : "false");
- load_time_data.SetString("title",
- l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
+ load_time_data.SetString("title", GetLocalizedString(IDS_NEW_TAB_TITLE));
load_time_data.SetString("webStoreTitle",
- l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE));
- load_time_data.SetString("webStoreTitleShort",
- l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE_SHORT));
+ GetLocalizedString(IDS_EXTENSION_WEB_STORE_TITLE));
+ load_time_data.SetString(
+ "webStoreTitleShort",
+ GetLocalizedString(IDS_EXTENSION_WEB_STORE_TITLE_SHORT));
load_time_data.SetString("attributionintro",
- l10n_util::GetStringUTF16(IDS_NEW_TAB_ATTRIBUTION_INTRO));
+ GetLocalizedString(IDS_NEW_TAB_ATTRIBUTION_INTRO));
load_time_data.SetString("appuninstall",
- l10n_util::GetStringUTF16(IDS_EXTENSIONS_UNINSTALL));
+ GetLocalizedString(IDS_EXTENSIONS_UNINSTALL));
load_time_data.SetString("appoptions",
- l10n_util::GetStringUTF16(IDS_NEW_TAB_APP_OPTIONS));
+ GetLocalizedString(IDS_NEW_TAB_APP_OPTIONS));
load_time_data.SetString("appdetails",
- l10n_util::GetStringUTF16(IDS_NEW_TAB_APP_DETAILS));
+ GetLocalizedString(IDS_NEW_TAB_APP_DETAILS));
load_time_data.SetString("appinfodialog",
- l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_SHOW_INFO));
+ GetLocalizedString(IDS_APP_CONTEXT_MENU_SHOW_INFO));
load_time_data.SetString("appcreateshortcut",
- l10n_util::GetStringUTF16(IDS_NEW_TAB_APP_CREATE_SHORTCUT));
+ GetLocalizedString(IDS_NEW_TAB_APP_CREATE_SHORTCUT));
load_time_data.SetString("appDefaultPageName",
- l10n_util::GetStringUTF16(IDS_APP_DEFAULT_PAGE_NAME));
- load_time_data.SetString("applaunchtypepinned",
- l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_PINNED));
- load_time_data.SetString("applaunchtyperegular",
- l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_REGULAR));
- load_time_data.SetString("applaunchtypewindow",
- l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_WINDOW));
- load_time_data.SetString("applaunchtypefullscreen",
- l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_FULLSCREEN));
- load_time_data.SetString("syncpromotext",
- l10n_util::GetStringUTF16(IDS_SYNC_START_SYNC_BUTTON_LABEL));
+ GetLocalizedString(IDS_APP_DEFAULT_PAGE_NAME));
+ load_time_data.SetString(
+ "applaunchtypepinned",
+ GetLocalizedString(IDS_APP_CONTEXT_MENU_OPEN_PINNED));
+ load_time_data.SetString(
+ "applaunchtyperegular",
+ GetLocalizedString(IDS_APP_CONTEXT_MENU_OPEN_REGULAR));
+ load_time_data.SetString(
+ "applaunchtypewindow",
+ GetLocalizedString(IDS_APP_CONTEXT_MENU_OPEN_WINDOW));
+ load_time_data.SetString(
+ "applaunchtypefullscreen",
+ GetLocalizedString(IDS_APP_CONTEXT_MENU_OPEN_FULLSCREEN));
+ load_time_data.SetString(
+ "syncpromotext", GetLocalizedString(IDS_SYNC_START_SYNC_BUTTON_LABEL));
load_time_data.SetString("syncLinkText",
- l10n_util::GetStringUTF16(IDS_SYNC_ADVANCED_OPTIONS));
+ GetLocalizedString(IDS_SYNC_ADVANCED_OPTIONS));
load_time_data.SetBoolean("shouldShowSyncLogin",
AppLauncherLoginHandler::ShouldShow(profile_));
- load_time_data.SetString("learnMore",
- l10n_util::GetStringUTF16(IDS_LEARN_MORE));
+ load_time_data.SetString("learnMore", GetLocalizedString(IDS_LEARN_MORE));
const std::string& app_locale = g_browser_process->GetApplicationLocale();
load_time_data.SetString(
"webStoreLink", google_util::AppendGoogleLocaleParam(
extension_urls::GetWebstoreLaunchURL(), app_locale)
.spec());
- load_time_data.SetString("appInstallHintText",
- l10n_util::GetStringUTF16(IDS_NEW_TAB_APP_INSTALL_HINT_LABEL));
- load_time_data.SetString("learn_more",
- l10n_util::GetStringUTF16(IDS_LEARN_MORE));
- load_time_data.SetString("tile_grid_screenreader_accessible_description",
- l10n_util::GetStringUTF16(IDS_NEW_TAB_TILE_GRID_ACCESSIBLE_DESCRIPTION));
- load_time_data.SetString("page_switcher_change_title",
- l10n_util::GetStringUTF16(IDS_NEW_TAB_PAGE_SWITCHER_CHANGE_TITLE));
- load_time_data.SetString("page_switcher_same_title",
- l10n_util::GetStringUTF16(IDS_NEW_TAB_PAGE_SWITCHER_SAME_TITLE));
+ load_time_data.SetString(
+ "appInstallHintText",
+ GetLocalizedString(IDS_NEW_TAB_APP_INSTALL_HINT_LABEL));
+ load_time_data.SetString("learn_more", GetLocalizedString(IDS_LEARN_MORE));
+ load_time_data.SetString(
+ "tile_grid_screenreader_accessible_description",
+ GetLocalizedString(IDS_NEW_TAB_TILE_GRID_ACCESSIBLE_DESCRIPTION));
+ load_time_data.SetString(
+ "page_switcher_change_title",
+ GetLocalizedString(IDS_NEW_TAB_PAGE_SWITCHER_CHANGE_TITLE));
+ load_time_data.SetString(
+ "page_switcher_same_title",
+ GetLocalizedString(IDS_NEW_TAB_PAGE_SWITCHER_SAME_TITLE));
// On Mac OS X 10.7+, horizontal scrolling can be treated as a back or
// forward gesture. Pass through a flag that indicates whether or not that
// feature is enabled.
diff --git a/chromium/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc b/chromium/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc
index 47f2794447b..5513e2c6e93 100644
--- a/chromium/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc
+++ b/chromium/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc
@@ -18,7 +18,6 @@
#include "components/grit/components_resources.h"
#include "components/history/core/browser/top_sites.h"
#include "components/image_fetcher/core/image_fetcher_impl.h"
-#include "components/ntp_tiles/field_trial.h"
#include "components/ntp_tiles/icon_cacher.h"
#include "components/ntp_tiles/most_visited_sites.h"
#include "components/ntp_tiles/webui/ntp_tiles_internals_message_handler.h"
diff --git a/chromium/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc b/chromium/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
index e8c51f8d221..8053b7ab8bd 100644
--- a/chromium/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
+++ b/chromium/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
@@ -157,7 +157,6 @@ void OfflineInternalsUIMessageHandler::HandleStoredPagesCallback(
std::string callback_id,
const offline_pages::MultipleOfflinePageItemResult& pages) {
base::ListValue results;
-
for (const auto& page : pages) {
auto offline_page = std::make_unique<base::DictionaryValue>();
offline_page->SetString("onlineUrl", page.url.spec());
@@ -172,6 +171,13 @@ void OfflineInternalsUIMessageHandler::HandleStoredPagesCallback(
offline_page->SetString("requestOrigin", page.request_origin);
results.Append(std::move(offline_page));
}
+ // Sort by creation order.
+ std::sort(results.GetList().begin(), results.GetList().end(),
+ [](auto& a, auto& b) {
+ return a.FindKey({"creationTime"})->GetDouble() <
+ b.FindKey({"creationTime"})->GetDouble();
+ });
+
ResolveJavascriptCallback(base::Value(callback_id), results);
}
@@ -317,13 +323,26 @@ void OfflineInternalsUIMessageHandler::HandleGeneratePageBundle(
for (auto& page_url : page_urls) {
// Creates a dummy prefetch URL with a bogus ID, and using the URL as the
// page title.
- prefetch_urls.push_back(offline_pages::PrefetchURL(
- "dummy id", GURL(page_url), base::UTF8ToUTF16(page_url)));
+ GURL url(page_url);
+ if (url.is_valid()) {
+ prefetch_urls.push_back(offline_pages::PrefetchURL(
+ "offline-internals", url, base::UTF8ToUTF16(page_url)));
+ }
}
prefetch_service_->GetPrefetchDispatcher()->AddCandidatePrefetchURLs(
offline_pages::kSuggestedArticlesNamespace, prefetch_urls);
- std::string message("Added candidate URLs.\n");
+ // Note: Static casts are needed here so that both Windows and Android can
+ // compile these printf formats.
+ std::string message =
+ base::StringPrintf("Added %zu candidate URLs.", prefetch_urls.size());
+ if (prefetch_urls.size() < page_urls.size()) {
+ size_t invalid_urls_count = page_urls.size() - prefetch_urls.size();
+ message.append(
+ base::StringPrintf(" Ignored %zu invalid URLs.", invalid_urls_count));
+ }
+ message.append("\n");
+
// Construct a JSON array containing all the URLs. To guard against malicious
// URLs that might contain special characters, we create a ListValue and then
// serialize it into JSON, instead of doing direct string manipulation.
diff --git a/chromium/chrome/browser/ui/webui/policy_ui.cc b/chromium/chrome/browser/ui/webui/policy_ui.cc
index f6a5f67d606..0c2f31ea748 100644
--- a/chromium/chrome/browser/ui/webui/policy_ui.cc
+++ b/chromium/chrome/browser/ui/webui/policy_ui.cc
@@ -24,7 +24,6 @@ content::WebUIDataSource* CreatePolicyUIHtmlSource() {
IDS_POLICY_FILTER_PLACEHOLDER);
source->AddLocalizedString("reloadPolicies", IDS_POLICY_RELOAD_POLICIES);
source->AddLocalizedString("exportPoliciesJSON", IDS_EXPORT_POLICIES_JSON);
- source->AddLocalizedString("chromeForWork", IDS_POLICY_CHROME_FOR_WORK);
source->AddLocalizedString("status", IDS_POLICY_STATUS);
source->AddLocalizedString("statusDevice", IDS_POLICY_STATUS_DEVICE);
source->AddLocalizedString("statusUser", IDS_POLICY_STATUS_USER);
diff --git a/chromium/chrome/browser/ui/webui/predictors/predictors_handler.cc b/chromium/chrome/browser/ui/webui/predictors/predictors_handler.cc
index 832038b8a75..7a338593fa9 100644
--- a/chromium/chrome/browser/ui/webui/predictors/predictors_handler.cc
+++ b/chromium/chrome/browser/ui/webui/predictors/predictors_handler.cc
@@ -23,27 +23,6 @@ using predictors::AutocompleteActionPredictor;
using predictors::ResourcePrefetchPredictor;
using predictors::ResourcePrefetchPredictorTables;
-namespace {
-
-using predictors::ResourceData;
-
-std::string ConvertResourceType(ResourceData::ResourceType type) {
- switch (type) {
- case ResourceData::RESOURCE_TYPE_IMAGE:
- return "Image";
- case ResourceData::RESOURCE_TYPE_STYLESHEET:
- return "Stylesheet";
- case ResourceData::RESOURCE_TYPE_SCRIPT:
- return "Script";
- case ResourceData::RESOURCE_TYPE_FONT_RESOURCE:
- return "Font";
- default:
- return "Unknown";
- }
-}
-
-} // namespace
-
PredictorsHandler::PredictorsHandler(Profile* profile) {
autocomplete_action_predictor_ =
predictors::AutocompleteActionPredictorFactory::GetForProfile(profile);
@@ -103,22 +82,10 @@ void PredictorsHandler::RequestResourcePrefetchPredictorDb(
ResourcePrefetchPredictor::INITIALIZED;
if (initialized) {
- // URL table cache.
- auto db = std::make_unique<base::ListValue>();
- AddPrefetchDataMapToListValue(
- *resource_prefetch_predictor->url_resource_data_->data_cache_,
- db.get());
- dict.Set("url_db", std::move(db));
-
- // Host table cache.
- db = std::make_unique<base::ListValue>();
- AddPrefetchDataMapToListValue(
- *resource_prefetch_predictor->host_resource_data_->data_cache_,
- db.get());
- dict.Set("host_db", std::move(db));
+ // TODO(alexilin): Add redirects table.
// Origin table cache.
- db = std::make_unique<base::ListValue>();
+ auto db = std::make_unique<base::ListValue>();
AddOriginDataMapToListValue(
*resource_prefetch_predictor->origin_data_->data_cache_, db.get());
dict.Set("origin_db", std::move(db));
@@ -129,38 +96,6 @@ void PredictorsHandler::RequestResourcePrefetchPredictorDb(
dict);
}
-void PredictorsHandler::AddPrefetchDataMapToListValue(
- const std::map<std::string, predictors::PrefetchData>& data_map,
- base::ListValue* db) const {
- for (const auto& p : data_map) {
- auto main = std::make_unique<base::DictionaryValue>();
- main->SetString("main_frame_url", p.first);
- auto resources = std::make_unique<base::ListValue>();
- for (const predictors::ResourceData& r : p.second.resources()) {
- auto resource = std::make_unique<base::DictionaryValue>();
- resource->SetString("resource_url", r.resource_url());
- resource->SetString("resource_type",
- ConvertResourceType(r.resource_type()));
- resource->SetInteger("number_of_hits", r.number_of_hits());
- resource->SetInteger("number_of_misses", r.number_of_misses());
- resource->SetInteger("consecutive_misses", r.consecutive_misses());
- resource->SetDouble("position", r.average_position());
- resource->SetDouble(
- "score", ResourcePrefetchPredictorTables::ComputeResourceScore(r));
- resource->SetBoolean("before_first_contentful_paint",
- r.before_first_contentful_paint());
- auto* resource_prefetch_predictor =
- loading_predictor_->resource_prefetch_predictor();
- resource->SetBoolean(
- "is_prefetchable",
- resource_prefetch_predictor->IsResourcePrefetchable(r));
- resources->Append(std::move(resource));
- }
- main->Set("resources", std::move(resources));
- db->Append(std::move(main));
- }
-}
-
void PredictorsHandler::AddOriginDataMapToListValue(
const std::map<std::string, predictors::OriginData>& data_map,
base::ListValue* db) const {
diff --git a/chromium/chrome/browser/ui/webui/predictors/predictors_handler.h b/chromium/chrome/browser/ui/webui/predictors/predictors_handler.h
index e9a4e086b00..61d5d9ab730 100644
--- a/chromium/chrome/browser/ui/webui/predictors/predictors_handler.h
+++ b/chromium/chrome/browser/ui/webui/predictors/predictors_handler.h
@@ -40,9 +40,6 @@ class PredictorsHandler : public content::WebUIMessageHandler {
void RequestResourcePrefetchPredictorDb(const base::ListValue* args);
// Helpers for RequestResourcePrefetchPredictorDb.
- void AddPrefetchDataMapToListValue(
- const std::map<std::string, predictors::PrefetchData>& data_map,
- base::ListValue* db) const;
void AddOriginDataMapToListValue(
const std::map<std::string, predictors::OriginData>& data_map,
base::ListValue* db) const;
diff --git a/chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc b/chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
index fb98de43578..2f453449b5b 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
@@ -801,10 +801,12 @@ TEST_F(ExtensionPrinterHandlerTest, Print_Pwg) {
EXPECT_FALSE(pwg_raster_converter_->bitmap_settings().reverse_page_order);
EXPECT_TRUE(pwg_raster_converter_->bitmap_settings().use_color);
- EXPECT_EQ(printing::kDefaultPdfDpi,
+ EXPECT_EQ(gfx::Size(printing::kDefaultPdfDpi, printing::kDefaultPdfDpi),
pwg_raster_converter_->conversion_settings().dpi);
EXPECT_TRUE(pwg_raster_converter_->conversion_settings().autorotate);
- EXPECT_EQ("0,0 208x416", // vertically_oriented_size * dpi / points_per_inch
+ // size = vertically_oriented_size * vertical_dpi / points_per_inch x
+ // horizontally_oriented_size * horizontal_dpi / points_per_inch
+ EXPECT_EQ("0,0 208x416",
pwg_raster_converter_->conversion_settings().area.ToString());
const PrinterProviderPrintJob* print_job = fake_api->GetNextPendingPrintJob();
@@ -855,10 +857,12 @@ TEST_F(ExtensionPrinterHandlerTest, Print_Pwg_NonDefaultSettings) {
EXPECT_TRUE(pwg_raster_converter_->bitmap_settings().reverse_page_order);
EXPECT_TRUE(pwg_raster_converter_->bitmap_settings().use_color);
- EXPECT_EQ(200, // max(vertical_dpi, horizontal_dpi)
+ EXPECT_EQ(gfx::Size(200, 100),
pwg_raster_converter_->conversion_settings().dpi);
EXPECT_TRUE(pwg_raster_converter_->conversion_settings().autorotate);
- EXPECT_EQ("0,0 138x277", // vertically_oriented_size * dpi / points_per_inch
+ // size = vertically_oriented_size * vertical_dpi / points_per_inch x
+ // horizontally_oriented_size * horizontal_dpi / points_per_inch
+ EXPECT_EQ("0,0 138x138",
pwg_raster_converter_->conversion_settings().area.ToString());
const PrinterProviderPrintJob* print_job = fake_api->GetNextPendingPrintJob();
diff --git a/chromium/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc b/chromium/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc
index 7615db3985a..b17d6ba0c5a 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc
+++ b/chromium/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.h"
#include <memory>
+#include <utility>
#include <vector>
#include "base/bind_helpers.h"
@@ -78,8 +79,6 @@ LocalPrinterHandlerChromeos::LocalPrinterHandlerChromeos(
printers_manager_(CupsPrintersManager::Create(profile)),
printer_configurer_(chromeos::PrinterConfigurer::Create(profile)),
weak_factory_(this) {
- printers_manager_->Start();
-
// Construct the CupsPrintJobManager to listen for printing events.
chromeos::CupsPrintJobManagerFactory::GetForBrowserContext(profile);
}
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 4da653cb56b..52fbb52e895 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
@@ -134,9 +134,13 @@ base::FilePath GetUniquePath(const base::FilePath& path) {
return unique_path;
}
-void CreateDirectoryIfNeeded(const base::FilePath& path) {
- if (!base::DirectoryExists(path))
- base::CreateDirectory(path);
+base::FilePath SelectSaveDirectory(const base::FilePath& path,
+ const base::FilePath& default_path) {
+ if (base::DirectoryExists(path))
+ return path;
+ if (!base::DirectoryExists(default_path))
+ base::CreateDirectory(default_path);
+ return default_path;
}
} // namespace
@@ -330,19 +334,21 @@ void PdfPrinterHandler::SelectFile(const base::FilePath& default_filename,
return;
}
- // If the directory is empty there is no reason to create it.
+ // If the directory is empty there is no reason to create it or use the
+ // default location.
if (path.empty()) {
- OnDirectoryCreated(default_filename);
+ OnDirectorySelected(default_filename, path);
return;
}
- // Create the directory to save in if it does not exist.
- base::PostTaskWithTraitsAndReply(
+ // Get default download directory. This will be used as a fallback if the
+ // save directory does not exist.
+ base::FilePath default_path = download_prefs->DownloadPath();
+ base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
- base::Bind(&CreateDirectoryIfNeeded, path),
- base::Bind(&PdfPrinterHandler::OnDirectoryCreated,
- weak_ptr_factory_.GetWeakPtr(),
- path.Append(default_filename)));
+ base::BindOnce(&SelectSaveDirectory, path, default_path),
+ base::BindOnce(&PdfPrinterHandler::OnDirectorySelected,
+ weak_ptr_factory_.GetWeakPtr(), default_filename));
}
void PdfPrinterHandler::PostPrintToPdfTask() {
@@ -358,7 +364,10 @@ void PdfPrinterHandler::OnGotUniqueFileName(const base::FilePath& path) {
FileSelected(path, 0, nullptr);
}
-void PdfPrinterHandler::OnDirectoryCreated(const base::FilePath& path) {
+void PdfPrinterHandler::OnDirectorySelected(const base::FilePath& filename,
+ const base::FilePath& directory) {
+ base::FilePath path = directory.Append(filename);
+
// Prompts the user to select the file.
ui::SelectFileDialog::FileTypeInfo file_type_info;
file_type_info.extensions.resize(1);
diff --git a/chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler.h b/chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler.h
index 5fcc74cb63d..641fb89cc83 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler.h
+++ b/chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler.h
@@ -90,7 +90,11 @@ class PdfPrinterHandler : public PrinterHandler,
private:
void PostPrintToPdfTask();
void OnGotUniqueFileName(const base::FilePath& path);
- void OnDirectoryCreated(const base::FilePath& path);
+
+ // Prompts the user to save the file. The dialog will default to saving
+ // the file with name |filename| in |directory|.
+ void OnDirectorySelected(const base::FilePath& filename,
+ const base::FilePath& directory);
Profile* const profile_;
printing::StickySettings* const sticky_settings_;
diff --git a/chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler_win_unittest.cc b/chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler_win_unittest.cc
index 9246f32c1a3..f46f4498638 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler_win_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/print_preview/pdf_printer_handler_win_unittest.cc
@@ -26,8 +26,6 @@ class FakePdfPrinterHandler;
bool GetOpenFileNameImpl(OPENFILENAME* ofn);
bool GetSaveFileNameImpl(FakePdfPrinterHandler* handler, OPENFILENAME* ofn);
-void EmptyPrintCallback(const base::Value& error) {}
-
class FakePdfPrinterHandler : public PdfPrinterHandler {
public:
FakePdfPrinterHandler(Profile* profile,
@@ -51,8 +49,7 @@ class FakePdfPrinterHandler : public PdfPrinterHandler {
}
void StartPrintToPdf(const base::string16& job_title) {
- StartPrint("", "", job_title, "", gfx::Size(), nullptr,
- base::Bind(&EmptyPrintCallback));
+ StartPrint("", "", job_title, "", gfx::Size(), nullptr, base::DoNothing());
run_loop_.Run();
}
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 2913dbfdc78..9bce25e7094 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
@@ -46,17 +46,17 @@
#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
#include "chrome/browser/ui/webui/print_preview/printer_handler.h"
#include "chrome/browser/ui/webui/print_preview/sticky_settings.h"
+#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_switches.h"
-#include "chrome/common/cloud_print/cloud_print_cdd_conversion.h"
#include "chrome/common/cloud_print/cloud_print_constants.h"
#include "chrome/common/crash_keys.h"
-#include "chrome/common/features.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/webui_url_constants.h"
#include "components/cloud_devices/common/cloud_device_description.h"
#include "components/cloud_devices/common/cloud_devices_urls.h"
#include "components/cloud_devices/common/printer_description.h"
#include "components/prefs/pref_service.h"
+#include "components/printing/common/cloud_print_cdd_conversion.h"
#include "components/printing/common/print_messages.h"
#include "components/signin/core/browser/gaia_cookie_manager_service.h"
#include "components/signin/core/browser/profile_management_switches.h"
@@ -891,7 +891,7 @@ void PrintPreviewHandler::GetNumberFormatAndMeasurementSystem(
// Getting the number formatting based on the locale and writing to
// dictionary.
base::string16 number_format = base::FormatDouble(123456.78, 2);
- settings->SetString(kDecimalDelimeter, number_format.substr(6, 1));
+ settings->SetString(kDecimalDelimeter, number_format.substr(7, 1));
settings->SetString(kThousandsDelimeter, number_format.substr(3, 1));
settings->SetInteger(kUnitType, system);
}
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 894f5e9adca..8efb84708d7 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
@@ -15,7 +15,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
-#include "chrome/common/features.h"
+#include "chrome/common/buildflags.h"
#include "components/signin/core/browser/gaia_cookie_manager_service.h"
#include "content/public/browser/web_ui_message_handler.h"
#include "printing/backend/print_backend.h"
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 382461c39d9..bc72b510408 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
@@ -6,6 +6,7 @@
#include "base/base64.h"
#include "base/containers/flat_set.h"
+#include "base/i18n/rtl.h"
#include "base/json/json_writer.h"
#include "base/memory/ref_counted_memory.h"
#include "base/strings/string16.h"
@@ -339,6 +340,10 @@ class PrintPreviewHandlerTest : public testing::Test {
}
void Initialize() {
+ // Set locale since the delimeters we check in VerifyInitialSettings()
+ // depend on it.
+ base::i18n::SetICUDefaultLocale("en");
+
// Sending this message will enable javascript, so it must always be called
// before any other messages are sent.
base::Value args(base::Value::Type::LIST);
@@ -376,7 +381,8 @@ class PrintPreviewHandlerTest : public testing::Test {
// print_preview.NativeInitialSettings type in
// chrome/browser/resources/print_preview/native_layer.js. Checks that
// |default_printer_name| is the printer name returned and that
- // |initiator_title| is the initiator title returned. Assumes
+ // |initiator_title| is the initiator title returned and validates that
+ // delimeters are correct for "en" locale (set in Initialize()). Assumes
// "test-callback-id-0" was used as the callback id.
void ValidateInitialSettings(const content::TestWebUI::CallData& data,
const std::string& default_printer_name,
@@ -387,10 +393,16 @@ class PrintPreviewHandlerTest : public testing::Test {
base::Value::Type::BOOLEAN));
ASSERT_TRUE(settings->FindKeyOfType("isInAppKioskMode",
base::Value::Type::BOOLEAN));
- ASSERT_TRUE(settings->FindKeyOfType("thousandsDelimeter",
- base::Value::Type::STRING));
- ASSERT_TRUE(
- settings->FindKeyOfType("decimalDelimeter", base::Value::Type::STRING));
+
+ const base::Value* thousandsDelimeter = settings->FindKeyOfType(
+ "thousandsDelimeter", base::Value::Type::STRING);
+ ASSERT_TRUE(thousandsDelimeter);
+ EXPECT_EQ(",", thousandsDelimeter->GetString());
+ const base::Value* decimalDelimeter =
+ settings->FindKeyOfType("decimalDelimeter", base::Value::Type::STRING);
+ ASSERT_TRUE(decimalDelimeter);
+ EXPECT_EQ(".", decimalDelimeter->GetString());
+
ASSERT_TRUE(
settings->FindKeyOfType("unitType", base::Value::Type::INTEGER));
ASSERT_TRUE(settings->FindKeyOfType("previewModifiable",
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 003106131a5..8c0162c47a4 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
@@ -241,6 +241,8 @@ void AddPrintPreviewStrings(content::WebUIDataSource* source) {
source->AddString(
"noDestsPromoLearnMoreUrl",
chrome::kCloudPrintNoDestinationsLearnMoreURL);
+ source->AddString("gcpCertificateErrorLearnMoreURL",
+ chrome::kCloudPrintCertificateErrorLearnMoreURL);
source->AddLocalizedString("pageRangeLimitInstruction",
IDS_PRINT_PREVIEW_PAGE_RANGE_LIMIT_INSTRUCTION);
source->AddLocalizedString(
@@ -313,6 +315,8 @@ void AddPrintPreviewStrings(content::WebUIDataSource* source) {
source->AddLocalizedString("offlineForWeek",
IDS_PRINT_PREVIEW_OFFLINE_FOR_WEEK);
source->AddLocalizedString("offline", IDS_PRINT_PREVIEW_OFFLINE);
+ source->AddLocalizedString("noLongerSupportedFragment",
+ IDS_PRINT_PREVIEW_NO_LONGER_SUPPORTED_FRAGMENT);
source->AddLocalizedString("noLongerSupported",
IDS_PRINT_PREVIEW_NO_LONGER_SUPPORTED);
source->AddLocalizedString("couldNotPrint",
@@ -347,6 +351,8 @@ void AddPrintPreviewStrings(content::WebUIDataSource* source) {
"groupPrinterSharingInviteText", IDS_PRINT_PREVIEW_GROUP_INVITE_TEXT);
source->AddLocalizedString(
"printerSharingInviteText", IDS_PRINT_PREVIEW_INVITE_TEXT);
+ source->AddLocalizedString("registerPrinterInformationMessage",
+ IDS_CLOUD_PRINT_REGISTER_PRINTER_INFORMATION);
source->AddLocalizedString("moreOptionsLabel", IDS_MORE_OPTIONS_LABEL);
source->AddLocalizedString("lessOptionsLabel", IDS_LESS_OPTIONS_LABEL);
#if defined(OS_CHROMEOS)
@@ -379,8 +385,6 @@ void AddPrintPreviewImages(content::WebUIDataSource* source) {
IDR_PRINT_PREVIEW_IMAGES_2X_PRINTER_SHARED);
source->AddResourcePath("images/business.svg",
IDR_PRINT_PREVIEW_IMAGES_ENTERPRISE_PRINTER);
- source->AddResourcePath("images/third_party.png",
- IDR_PRINT_PREVIEW_IMAGES_THIRD_PARTY);
source->AddResourcePath("images/google_doc.png",
IDR_PRINT_PREVIEW_IMAGES_GOOGLE_DOC);
source->AddResourcePath("images/pdf.png", IDR_PRINT_PREVIEW_IMAGES_PDF);
diff --git a/chromium/chrome/browser/ui/webui/print_preview/printer_capabilities.cc b/chromium/chrome/browser/ui/webui/print_preview/printer_capabilities.cc
index ffef1099d74..918401ab811 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/printer_capabilities.cc
+++ b/chromium/chrome/browser/ui/webui/print_preview/printer_capabilities.cc
@@ -21,8 +21,8 @@
#include "chrome/browser/printing/print_view_manager.h"
#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
#include "chrome/browser/ui/webui/print_preview/printer_handler.h"
-#include "chrome/common/cloud_print/cloud_print_cdd_conversion.h"
-#include "chrome/common/crash_keys.h"
+#include "components/crash/core/common/crash_keys.h"
+#include "components/printing/common/cloud_print_cdd_conversion.h"
#include "content/public/browser/render_frame_host.h"
#include "printing/backend/print_backend.h"
#include "printing/backend/print_backend_consts.h"
@@ -32,7 +32,7 @@
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
-#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
#endif
diff --git a/chromium/chrome/browser/ui/webui/print_preview/printer_handler.cc b/chromium/chrome/browser/ui/webui/print_preview/printer_handler.cc
index bf297f51a01..af0d09391cb 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/printer_handler.cc
+++ b/chromium/chrome/browser/ui/webui/print_preview/printer_handler.cc
@@ -7,7 +7,7 @@
#include "build/buildflag.h"
#include "chrome/browser/ui/webui/print_preview/extension_printer_handler.h"
#include "chrome/browser/ui/webui/print_preview/pdf_printer_handler.h"
-#include "chrome/common/features.h"
+#include "chrome/common/buildflags.h"
#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
#include "chrome/browser/ui/webui/print_preview/privet_printer_handler.h"
diff --git a/chromium/chrome/browser/ui/webui/print_preview/printer_handler.h b/chromium/chrome/browser/ui/webui/print_preview/printer_handler.h
index 6231bafd927..29fc3194f90 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/printer_handler.h
+++ b/chromium/chrome/browser/ui/webui/print_preview/printer_handler.h
@@ -11,7 +11,7 @@
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/strings/string16.h"
-#include "chrome/common/features.h"
+#include "chrome/common/buildflags.h"
namespace base {
class DictionaryValue;
diff --git a/chromium/chrome/browser/ui/webui/print_preview/privet_printer_handler.cc b/chromium/chrome/browser/ui/webui/print_preview/privet_printer_handler.cc
index eedc4c4573b..6df2047be61 100644
--- a/chromium/chrome/browser/ui/webui/print_preview/privet_printer_handler.cc
+++ b/chromium/chrome/browser/ui/webui/print_preview/privet_printer_handler.cc
@@ -100,15 +100,19 @@ void PrivetPrinterHandler::LocalPrinterChanged(
bool has_local_printing,
const cloud_print::DeviceDescription& description) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- if (has_local_printing ||
- command_line->HasSwitch(switches::kEnablePrintPreviewRegisterPromos)) {
- auto printer_info = std::make_unique<base::DictionaryValue>();
- FillPrinterDescription(name, description, has_local_printing,
- printer_info.get());
- base::ListValue printers;
- printers.Set(0, std::move(printer_info));
- added_printers_callback_.Run(printers);
+ if (!added_printers_callback_ ||
+ (!has_local_printing &&
+ !command_line->HasSwitch(switches::kEnablePrintPreviewRegisterPromos))) {
+ // If Print Preview is not expecting this printer (callback reset or no
+ // registration promos and not a local printer), return early.
+ return;
}
+ auto printer_info = std::make_unique<base::DictionaryValue>();
+ FillPrinterDescription(name, description, has_local_printing,
+ printer_info.get());
+ base::ListValue printers;
+ printers.Set(0, std::move(printer_info));
+ added_printers_callback_.Run(printers);
}
void PrivetPrinterHandler::LocalPrinterRemoved(const std::string& name) {}
diff --git a/chromium/chrome/browser/ui/webui/profile_helper.cc b/chromium/chrome/browser/ui/webui/profile_helper.cc
index 7bf6fd82b9e..e0e10904671 100644
--- a/chromium/chrome/browser/ui/webui/profile_helper.cc
+++ b/chromium/chrome/browser/ui/webui/profile_helper.cc
@@ -83,10 +83,7 @@ void OpenNewWindowForProfile(Profile* profile) {
}
void DeleteProfileAtPath(base::FilePath file_path,
- content::WebUI* web_ui,
ProfileMetrics::ProfileDelete deletion_source) {
- DCHECK(web_ui);
-
if (!profiles::IsMultipleProfilesEnabled())
return;
g_browser_process->profile_manager()->MaybeScheduleProfileForDeletion(
diff --git a/chromium/chrome/browser/ui/webui/profile_helper.h b/chromium/chrome/browser/ui/webui/profile_helper.h
index 1b0f930765d..3c82a62112e 100644
--- a/chromium/chrome/browser/ui/webui/profile_helper.h
+++ b/chromium/chrome/browser/ui/webui/profile_helper.h
@@ -9,17 +9,12 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_metrics.h"
-namespace content {
-class WebUI;
-}
-
namespace webui {
void OpenNewWindowForProfile(Profile* profile);
// Deletes the profile at the given |file_path|.
void DeleteProfileAtPath(base::FilePath file_path,
- content::WebUI* web_ui,
ProfileMetrics::ProfileDelete deletion_source);
} // namespace webui
diff --git a/chromium/chrome/browser/ui/webui/profile_helper_browsertest.cc b/chromium/chrome/browser/ui/webui/profile_helper_browsertest.cc
index 05f532d44bd..37f4e959945 100644
--- a/chromium/chrome/browser/ui/webui/profile_helper_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/profile_helper_browsertest.cc
@@ -128,7 +128,7 @@ IN_PROC_BROWSER_TEST_F(ProfileHelperTest, DeleteSoleProfile) {
content::NotificationService::AllSources());
content::WindowedNotificationObserver close_observer(
chrome::NOTIFICATION_BROWSER_CLOSED, content::Source<Browser>(browser()));
- webui::DeleteProfileAtPath(original_browser->profile()->GetPath(), &web_ui,
+ webui::DeleteProfileAtPath(original_browser->profile()->GetPath(),
ProfileMetrics::DELETE_PROFILE_SETTINGS);
open_observer.Wait();
close_observer.Wait();
@@ -158,7 +158,7 @@ IN_PROC_BROWSER_TEST_F(ProfileHelperTest, DeleteActiveProfile) {
content::NotificationService::AllSources());
content::WindowedNotificationObserver close_observer(
chrome::NOTIFICATION_BROWSER_CLOSED, content::Source<Browser>(browser()));
- webui::DeleteProfileAtPath(original_browser->profile()->GetPath(), &web_ui,
+ webui::DeleteProfileAtPath(original_browser->profile()->GetPath(),
ProfileMetrics::DELETE_PROFILE_SETTINGS);
open_observer.Wait();
close_observer.Wait();
@@ -184,7 +184,7 @@ IN_PROC_BROWSER_TEST_F(ProfileHelperTest, DeleteInactiveProfile) {
content::BrowsingDataRemoverCompletionInhibitor inhibitor(
content::BrowserContext::GetBrowsingDataRemover(additional_profile));
- webui::DeleteProfileAtPath(additional_profile->GetPath(), &web_ui,
+ webui::DeleteProfileAtPath(additional_profile->GetPath(),
ProfileMetrics::DELETE_PROFILE_SETTINGS);
inhibitor.BlockUntilNearCompletion();
inhibitor.ContinueToCompletion();
diff --git a/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.h b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.h
index 0c1fc6e5d71..26f5bfdffcd 100644
--- a/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.h
+++ b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.h
@@ -18,7 +18,7 @@
#include "base/sequenced_task_runner_helpers.h"
#include "content/public/browser/browser_thread.h"
#include "storage/browser/quota/quota_manager.h"
-#include "third_party/WebKit/common/quota/quota_types.mojom.h"
+#include "third_party/WebKit/public/mojom/quota/quota_types.mojom.h"
namespace quota_internals {
diff --git a/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_types.h b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_types.h
index c9b5c7a34cd..b6a1295afbb 100644
--- a/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_types.h
+++ b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_types.h
@@ -12,7 +12,7 @@
#include <string>
#include "base/time/time.h"
-#include "third_party/WebKit/common/quota/quota_types.mojom.h"
+#include "third_party/WebKit/public/mojom/quota/quota_types.mojom.h"
#include "url/gurl.h"
namespace base {
diff --git a/chromium/chrome/browser/ui/webui/sandbox_internals_ui.cc b/chromium/chrome/browser/ui/webui/sandbox_internals_ui.cc
index b73d1a50a04..66b53a7a944 100644
--- a/chromium/chrome/browser/ui/webui/sandbox_internals_ui.cc
+++ b/chromium/chrome/browser/ui/webui/sandbox_internals_ui.cc
@@ -6,8 +6,8 @@
#include <string>
+#include "build/build_config.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/render_messages.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/browser_resources.h"
#include "content/public/browser/render_frame_host.h"
@@ -15,6 +15,11 @@
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
+#if defined(OS_ANDROID)
+#include "chrome/common/sandbox_status_extension_android.mojom.h"
+#include "third_party/WebKit/public/common/associated_interfaces/associated_interface_provider.h"
+#endif
+
#if defined(OS_LINUX)
#include "content/public/browser/zygote_host_linux.h"
#include "services/service_manager/sandbox/sandbox.h"
@@ -75,8 +80,10 @@ SandboxInternalsUI::SandboxInternalsUI(content::WebUI* web_ui)
void SandboxInternalsUI::RenderFrameCreated(
content::RenderFrameHost* render_frame_host) {
#if defined(OS_ANDROID)
- render_frame_host->Send(new ChromeViewMsg_AddSandboxStatusExtension(
- render_frame_host->GetRoutingID()));
+ chrome::mojom::SandboxStatusExtensionAssociatedPtr sandbox_status;
+ render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
+ &sandbox_status);
+ sandbox_status->AddSandboxStatusExtension();
#endif
}
diff --git a/chromium/chrome/browser/ui/webui/settings/about_handler.cc b/chromium/chrome/browser/ui/webui/settings/about_handler.cc
index 11da4972a25..0e22bfc93e3 100644
--- a/chromium/chrome/browser/ui/webui/settings/about_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/about_handler.cc
@@ -602,7 +602,8 @@ void AboutHandler::HandleRefreshTPMFirmwareUpdateStatus(
const base::ListValue* args) {
chromeos::tpm_firmware_update::ShouldOfferUpdateViaPowerwash(
base::Bind(&AboutHandler::RefreshTPMFirmwareUpdateStatus,
- weak_factory_.GetWeakPtr()));
+ weak_factory_.GetWeakPtr()),
+ base::TimeDelta());
}
void AboutHandler::RefreshTPMFirmwareUpdateStatus(bool update_available) {
diff --git a/chromium/chrome/browser/ui/webui/settings/appearance_handler.cc b/chromium/chrome/browser/ui/webui/settings/appearance_handler.cc
index 128c7f1efd1..3e9bb1a4d96 100644
--- a/chromium/chrome/browser/ui/webui/settings/appearance_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/appearance_handler.cc
@@ -13,17 +13,13 @@
#include "content/public/browser/web_ui.h"
#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
#include "chrome/browser/ui/ash/wallpaper_controller_client.h"
-#include "chromeos/login/login_state.h"
-#include "components/user_manager/user_manager.h"
#endif
namespace settings {
AppearanceHandler::AppearanceHandler(content::WebUI* webui)
- : profile_(Profile::FromWebUI(webui)) {
-}
+ : profile_(Profile::FromWebUI(webui)), weak_ptr_factory_(this) {}
AppearanceHandler::~AppearanceHandler() {}
@@ -75,49 +71,28 @@ void AppearanceHandler::HandleUseSystemTheme(const base::ListValue* args) {
#if defined(OS_CHROMEOS)
void AppearanceHandler::IsWallpaperSettingVisible(const base::ListValue* args) {
CHECK_EQ(args->GetSize(), 1U);
- const base::Value* callback_id;
- CHECK(args->Get(0, &callback_id));
- AllowJavascript();
-
- bool is_wallpaper_visible = false;
- const chromeos::LoginState* login_state = chromeos::LoginState::Get();
- const chromeos::LoginState::LoggedInUserType user_type =
- login_state->GetLoggedInUserType();
- const user_manager::User* user =
- user_manager::UserManager::Get()->GetActiveUser();
-
- // Only login, whitelist types and active users are allowed to change
- // their wallpaper. Then show the wallpaper setting row for them.
- if (login_state->IsUserLoggedIn() && user &&
- (user_type == chromeos::LoginState::LOGGED_IN_USER_REGULAR ||
- user_type == chromeos::LoginState::LOGGED_IN_USER_OWNER ||
- user_type == chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT ||
- user_type == chromeos::LoginState::LOGGED_IN_USER_SUPERVISED)) {
- is_wallpaper_visible = true;
- }
-
- ResolveJavascriptCallback(*callback_id, base::Value(is_wallpaper_visible));
+ WallpaperControllerClient::Get()->ShouldShowWallpaperSetting(
+ base::Bind(&AppearanceHandler::ResolveCallback,
+ weak_ptr_factory_.GetWeakPtr(), args->GetList()[0].Clone()));
}
void AppearanceHandler::IsWallpaperPolicyControlled(
const base::ListValue* args) {
CHECK_EQ(args->GetSize(), 1U);
- const base::Value* callback_id;
- CHECK(args->Get(0, &callback_id));
- AllowJavascript();
-
- ResolveJavascriptCallback(
- *callback_id,
- base::Value(chromeos::WallpaperManager::Get()->IsPolicyControlled(
- user_manager::UserManager::Get()->GetActiveUser()->GetAccountId())));
+ WallpaperControllerClient::Get()->IsActiveUserWallpaperControlledByPolicy(
+ base::Bind(&AppearanceHandler::ResolveCallback,
+ weak_ptr_factory_.GetWeakPtr(), args->GetList()[0].Clone()));
}
void AppearanceHandler::HandleOpenWallpaperManager(
- const base::ListValue* /*args*/) {
- if (!chromeos::WallpaperManager::Get()->IsPolicyControlled(
- user_manager::UserManager::Get()->GetActiveUser()->GetAccountId())) {
- WallpaperControllerClient::Get()->OpenWallpaperPicker();
- }
+ const base::ListValue* args) {
+ WallpaperControllerClient::Get()->OpenWallpaperPickerIfAllowed();
+}
+
+void AppearanceHandler::ResolveCallback(const base::Value& callback_id,
+ bool result) {
+ AllowJavascript();
+ ResolveJavascriptCallback(callback_id, base::Value(result));
}
#endif
diff --git a/chromium/chrome/browser/ui/webui/settings/appearance_handler.h b/chromium/chrome/browser/ui/webui/settings/appearance_handler.h
index d31af5aa186..3acb6d567f4 100644
--- a/chromium/chrome/browser/ui/webui/settings/appearance_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/appearance_handler.h
@@ -6,6 +6,7 @@
#define CHROME_BROWSER_UI_WEBUI_SETTINGS_APPEARANCE_HANDLER_H_
#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
namespace base {
@@ -41,7 +42,7 @@ class AppearanceHandler : public SettingsPageUIHandler {
#endif
#if defined(OS_CHROMEOS)
- // Whether should show the wallpaper setting row.
+ // Whether the wallpaper setting should be shown.
void IsWallpaperSettingVisible(const base::ListValue* args);
// Whether the wallpaper is policy controlled.
@@ -49,10 +50,15 @@ class AppearanceHandler : public SettingsPageUIHandler {
// Open the wallpaper manager app.
void HandleOpenWallpaperManager(const base::ListValue* args);
+
+ // Helper function to resolve the Javascript callback.
+ void ResolveCallback(const base::Value& callback_id, bool result);
#endif
Profile* profile_; // Weak pointer.
+ base::WeakPtrFactory<AppearanceHandler> weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(AppearanceHandler);
};
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 d8fce5d42bb..551123b58ae 100644
--- a/chromium/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc
@@ -75,7 +75,7 @@ void BrowserLifetimeHandler::HandleFactoryReset(
true);
prefs->CommitPendingWrite();
chrome::AttemptRelaunch();
- }));
+ }), base::TimeDelta());
return;
}
diff --git a/chromium/chrome/browser/ui/webui/settings/change_password_handler.cc b/chromium/chrome/browser/ui/webui/settings/change_password_handler.cc
index 1590e25bf22..a077bd75bd2 100644
--- a/chromium/chrome/browser/ui/webui/settings/change_password_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/change_password_handler.cc
@@ -60,7 +60,8 @@ void ChangePasswordHandler::HandleChangePassword(const base::ListValue* args) {
void ChangePasswordHandler::UpdateChangePasswordCardVisibility() {
FireWebUIListener(
"change-password-visibility",
- base::Value(safe_browsing::ChromePasswordProtectionService::
+ base::Value(service_->IsWarningEnabled() &&
+ safe_browsing::ChromePasswordProtectionService::
ShouldShowChangePasswordSettingUI(profile_)));
}
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
index 6d0cf18456f..a535d490d14 100644
--- a/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
@@ -8,12 +8,14 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
+#include "base/containers/flat_map.h"
#include "base/files/file_util.h"
#include "base/json/json_string_value_serializer.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
+#include "base/task_scheduler/post_task.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
@@ -32,7 +34,11 @@
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/debug_daemon_client.h"
#include "chromeos/printing/ppd_cache.h"
+#include "chromeos/printing/ppd_line_reader.h"
#include "chromeos/printing/ppd_provider.h"
+#include "chromeos/printing/printer_configuration.h"
+#include "chromeos/printing/printing_constants.h"
+#include "chromeos/printing/uri_components.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_ui.h"
@@ -40,33 +46,17 @@
#include "net/base/filename_util.h"
#include "net/url_request/url_request_context_getter.h"
#include "printing/backend/print_backend.h"
-#include "url/third_party/mozilla/url_parse.h"
namespace chromeos {
namespace settings {
namespace {
-constexpr char kIppScheme[] = "ipp";
-constexpr char kIppsScheme[] = "ipps";
-
-constexpr int kIppPort = 631;
-// IPPS commonly uses the HTTPS port despite the spec saying it should use the
-// IPP port.
-constexpr int kIppsPort = 443;
-
// These values are written to logs. New enum values can be added, but existing
// enums must never be renumbered or deleted and reused.
enum PpdSourceForHistogram { kUser = 0, kScs = 1, kPpdSourceMax };
-// A parsed representation of a printer uri.
-struct PrinterUri {
- bool encrypted = false;
- std::string scheme;
- std::string host;
- int port = url::SpecialPort::PORT_INVALID;
- std::string path;
-};
+constexpr int kPpdMaxLineLength = 255;
void RecordPpdSource(const PpdSourceForHistogram& source) {
UMA_HISTOGRAM_ENUMERATION("Printing.CUPS.PpdSource", source, kPpdSourceMax);
@@ -82,43 +72,6 @@ void RecordIppQuerySuccess(bool success) {
UMA_HISTOGRAM_BOOLEAN("Printing.CUPS.IppAttributesSuccess", success);
}
-// Parses |printer_uri| into its components and written into |uri|. Returns
-// true if the uri was parsed successfully, returns false otherwise. No changes
-// are made to |uri| if this function returns false.
-bool ParseUri(const std::string& printer_uri, PrinterUri* uri) {
- DCHECK(uri);
- const char* uri_ptr = printer_uri.c_str();
- url::Parsed parsed;
- url::ParseStandardURL(uri_ptr, printer_uri.length(), &parsed);
- if (!parsed.scheme.is_valid() || !parsed.host.is_valid() ||
- !parsed.path.is_valid()) {
- return false;
- }
- base::StringPiece scheme(&uri_ptr[parsed.scheme.begin], parsed.scheme.len);
- base::StringPiece host(&uri_ptr[parsed.host.begin], parsed.host.len);
- base::StringPiece path(&uri_ptr[parsed.path.begin], parsed.path.len);
-
- bool encrypted = scheme != kIppScheme;
- int port = ParsePort(uri_ptr, parsed.port);
- // Port not specified.
- if (port == url::SpecialPort::PORT_UNSPECIFIED ||
- port == url::SpecialPort::PORT_INVALID) {
- if (scheme == kIppScheme) {
- port = kIppPort;
- } else if (scheme == kIppsScheme) {
- port = kIppsPort;
- }
- }
-
- uri->encrypted = encrypted;
- uri->scheme = scheme.as_string();
- uri->host = host.as_string();
- uri->port = port;
- uri->path = path.as_string();
-
- return true;
-}
-
// Returns true if |printer_uri| is an IPP uri.
bool IsIppUri(base::StringPiece printer_uri) {
base::StringPiece::size_type separator_location =
@@ -136,15 +89,17 @@ bool IsIppUri(base::StringPiece printer_uri) {
// error to attempt this with a non-IPP printer.
void QueryAutoconf(const std::string& printer_uri,
const PrinterInfoCallback& callback) {
- PrinterUri uri;
+ auto optional = ParseUri(printer_uri);
// Behavior for querying a non-IPP uri is undefined and disallowed.
- if (!IsIppUri(printer_uri) || !ParseUri(printer_uri, &uri)) {
+ if (!IsIppUri(printer_uri) || !optional.has_value()) {
LOG(WARNING) << "Printer uri is invalid: " << printer_uri;
callback.Run(false, "", "", "", false);
return;
}
- QueryIppPrinter(uri.host, uri.port, uri.path, uri.encrypted, callback);
+ UriComponents uri = optional.value();
+ QueryIppPrinter(uri.host(), uri.port(), uri.path(), uri.encrypted(),
+ callback);
}
// Create an empty CupsPrinterInfo dictionary value. It should be consistent
@@ -193,8 +148,8 @@ std::unique_ptr<base::DictionaryValue> GetPrinterInfo(const Printer& printer) {
printer_info->SetString("printerModel", printer.model());
printer_info->SetString("printerMakeAndModel", printer.make_and_model());
- PrinterUri uri;
- if (!ParseUri(printer.uri(), &uri)) {
+ auto optional = printer.GetUriComponents();
+ if (!optional.has_value()) {
// Uri is invalid so we set default values.
LOG(WARNING) << "Could not parse uri. Defaulting values";
printer_info->SetString("printerAddress", "");
@@ -204,7 +159,9 @@ std::unique_ptr<base::DictionaryValue> GetPrinterInfo(const Printer& printer) {
return printer_info;
}
- if (base::ToLowerASCII(uri.scheme) == "usb") {
+ UriComponents uri = optional.value();
+
+ if (base::ToLowerASCII(uri.scheme()) == "usb") {
// USB has URI path (and, maybe, query) components that aren't really
// associated with a queue -- the mapping between printing semantics and URI
// semantics breaks down a bit here. From the user's point of view, the
@@ -213,12 +170,12 @@ std::unique_ptr<base::DictionaryValue> GetPrinterInfo(const Printer& printer) {
printer.uri().substr(strlen("usb://")));
} else {
printer_info->SetString("printerAddress",
- PrinterAddress(uri.host, uri.port));
- if (!uri.path.empty()) {
- printer_info->SetString("printerQueue", uri.path.substr(1));
+ PrinterAddress(uri.host(), uri.port()));
+ if (!uri.path().empty()) {
+ printer_info->SetString("printerQueue", uri.path().substr(1));
}
}
- printer_info->SetString("printerProtocol", base::ToLowerASCII(uri.scheme));
+ printer_info->SetString("printerProtocol", base::ToLowerASCII(uri.scheme()));
return printer_info;
}
@@ -284,6 +241,16 @@ std::unique_ptr<chromeos::Printer> DictToPrinter(
return printer;
}
+std::string ReadFileToStringWithMaxSize(const base::FilePath& path,
+ int max_size) {
+ std::string contents;
+ // This call can fail, but it doesn't matter for our purposes. If it fails,
+ // we simply return an empty string for the contents, and it will be rejected
+ // as an invalid PPD.
+ base::ReadFileToStringWithMaxSize(path, &contents, max_size);
+ return contents;
+}
+
} // namespace
CupsPrintersHandler::CupsPrintersHandler(content::WebUI* webui)
@@ -348,7 +315,6 @@ void CupsPrintersHandler::RegisterMessages() {
void CupsPrintersHandler::OnJavascriptAllowed() {
printers_manager_->AddObserver(this);
- printers_manager_->Start();
}
void CupsPrintersHandler::OnJavascriptDisallowed() {
@@ -412,9 +378,8 @@ void CupsPrintersHandler::HandleRemoveCupsPrinter(const base::ListValue* args) {
printers_manager_->RemoveConfiguredPrinter(printer_id);
DebugDaemonClient* client = DBusThreadManager::Get()->GetDebugDaemonClient();
- client->CupsRemovePrinter(printer_name,
- base::Bind(&OnRemovedPrinter, protocol),
- base::Bind(&base::DoNothing));
+ client->CupsRemovePrinter(
+ printer_name, base::Bind(&OnRemovedPrinter, protocol), base::DoNothing());
}
void CupsPrintersHandler::HandleGetPrinterInfo(const base::ListValue* args) {
@@ -532,9 +497,17 @@ void CupsPrintersHandler::HandleAddCupsPrinter(const base::ListValue* args) {
CHECK(args->GetDictionary(0, &printer_dict));
std::unique_ptr<Printer> printer = DictToPrinter(*printer_dict);
- PrinterUri uri;
- if (!printer || !ParseUri(printer->uri(), &uri)) {
- LOG(ERROR) << "Failed to parse printer";
+ if (!printer) {
+ LOG(ERROR) << "Failed to parse printer URI";
+ OnAddPrinterError(PrinterSetupResult::kFatalError);
+ return;
+ }
+
+ auto optional = printer->GetUriComponents();
+ if (!optional.has_value()) {
+ // If the returned optional does not contain a value then it means that the
+ // printer's uri was not able to be parsed successfully.
+ LOG(ERROR) << "Failed to parse printer URI";
OnAddPrinterError(PrinterSetupResult::kFatalError);
return;
}
@@ -761,8 +734,26 @@ void CupsPrintersHandler::FileSelected(const base::FilePath& path,
int index,
void* params) {
DCHECK(!webui_callback_id_.empty());
+
+ // Load the beggining contents of the file located at |path| and callback into
+ // VerifyPpdContents() in order to determine whether the file appears to be a
+ // PPD file. The task's priority is USER_BLOCKING because the this task
+ // updates the UI as a result of a direct user action.
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
+ base::BindOnce(&ReadFileToStringWithMaxSize, path, kPpdMaxLineLength),
+ base::BindOnce(&CupsPrintersHandler::VerifyPpdContents,
+ weak_factory_.GetWeakPtr(), path));
+}
+
+void CupsPrintersHandler::VerifyPpdContents(const base::FilePath& path,
+ const std::string& contents) {
+ std::string result = "";
+ if (PpdLineReader::ContainsMagicNumber(contents, kPpdMaxLineLength))
+ result = path.value();
+
ResolveJavascriptCallback(base::Value(webui_callback_id_),
- base::Value(path.value()));
+ base::Value(result));
webui_callback_id_.clear();
}
@@ -837,11 +828,17 @@ void CupsPrintersHandler::HandleAddDiscoveredPrinter(
CHECK(args->GetString(0, &printer_id));
std::unique_ptr<Printer> printer = printers_manager_->GetPrinter(printer_id);
- PrinterUri uri;
- if (printer == nullptr || !ParseUri(printer->uri(), &uri)) {
+ if (printer == nullptr) {
// Printer disappeared, so we don't have information about it anymore and
- // can't really do much. Or the printer uri was not parsed successfully.
- // Fail the add.
+ // can't really do much. Fail the add.
+ FireWebUIListener("on-add-cups-printer", base::Value(false),
+ base::Value(printer_id));
+ return;
+ }
+
+ auto optional = printer->GetUriComponents();
+ if (!optional.has_value()) {
+ // The printer uri was not parsed successfully. Fail the add.
FireWebUIListener("on-add-cups-printer", base::Value(false),
base::Value(printer_id));
return;
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
index f3541827eed..0f9452af13e 100644
--- a/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
@@ -145,6 +145,13 @@ class CupsPrintersHandler : public ::settings::SettingsPageUIHandler,
int index,
void* params) override;
+ // Used by FileSelected() in order to verify whether the beginning contents of
+ // the selected file contain the magic number present in all PPD files. |path|
+ // is used for display in the UI as this function calls back into javascript
+ // with |path| as the result.
+ void VerifyPpdContents(const base::FilePath& path,
+ const std::string& contents);
+
Profile* profile_;
// Discovery support. discovery_active_ tracks whether or not the UI
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc
index f4176c34c2d..4ff2b1c8713 100644
--- a/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc
@@ -29,7 +29,9 @@
#include "chrome/browser/platform_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/grit/generated_resources.h"
-#include "chromeos/cryptohome/homedir_methods.h"
+#include "chromeos/cryptohome/cryptohome_util.h"
+#include "chromeos/dbus/cryptohome_client.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
#include "components/arc/arc_util.h"
#include "components/browsing_data/content/conditional_cache_counting_helper.h"
#include "components/drive/chromeos/file_system_interface.h"
@@ -300,10 +302,10 @@ void StorageHandler::UpdateOtherUsersSize() {
if (user->is_active())
continue;
other_users_.push_back(user);
- cryptohome::HomedirMethods::GetInstance()->GetAccountDiskUsage(
+ DBusThreadManager::Get()->GetCryptohomeClient()->GetAccountDiskUsage(
cryptohome::Identification(user->GetAccountId()),
- base::Bind(&StorageHandler::OnGetOtherUserSize,
- weak_ptr_factory_.GetWeakPtr()));
+ base::BindOnce(&StorageHandler::OnGetOtherUserSize,
+ weak_ptr_factory_.GetWeakPtr()));
}
// We should show "0 B" if there is no other user.
if (other_users_.empty()) {
@@ -313,8 +315,9 @@ void StorageHandler::UpdateOtherUsersSize() {
}
}
-void StorageHandler::OnGetOtherUserSize(bool success, int64_t size) {
- user_sizes_.push_back(success ? size : -1);
+void StorageHandler::OnGetOtherUserSize(
+ base::Optional<cryptohome::BaseReply> reply) {
+ user_sizes_.push_back(cryptohome::AccountDiskUsageReplyToUsageSize(reply));
if (user_sizes_.size() == other_users_.size()) {
base::string16 size_string;
// If all the requests succeed, shows the total bytes in the UI.
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h
index 9da1d5d437b..48f3d5b5875 100644
--- a/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h
@@ -12,8 +12,10 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
#include "chrome/browser/browsing_data/site_data_size_collector.h"
#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "chromeos/dbus/cryptohome/rpc.pb.h"
#include "components/arc/storage_manager/arc_storage_manager.h"
#include "components/user_manager/user.h"
@@ -76,7 +78,7 @@ class StorageHandler : public ::settings::SettingsPageUIHandler {
void UpdateOtherUsersSize();
// Callback to save the fetched user sizes and update the UI.
- void OnGetOtherUserSize(bool success, int64_t size);
+ void OnGetOtherUserSize(base::Optional<cryptohome::BaseReply> reply);
// Requests updating the space size used by Android apps and cache.
void UpdateAndroidSize();
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc
index 45d050cc78c..a97d004c949 100644
--- a/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc
@@ -19,7 +19,7 @@
#include "components/prefs/pref_service.h"
#include "components/session_manager/core/session_manager.h"
#include "content/public/common/service_manager_connection.h"
-#include "services/device/public/interfaces/constants.mojom.h"
+#include "services/device/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#include "ui/base/l10n/l10n_util.h"
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.h
index 5c296d69fe7..f63ea22d163 100644
--- a/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.h
@@ -10,7 +10,7 @@
#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
#include "components/session_manager/core/session_manager_observer.h"
#include "mojo/public/cpp/bindings/binding.h"
-#include "services/device/public/interfaces/fingerprint.mojom.h"
+#include "services/device/public/mojom/fingerprint.mojom.h"
class Profile;
diff --git a/chromium/chrome/browser/ui/webui/settings/downloads_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/downloads_handler_unittest.cc
index c20ca4db582..7e7628fb021 100644
--- a/chromium/chrome/browser/ui/webui/settings/downloads_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/downloads_handler_unittest.cc
@@ -6,6 +6,8 @@
#include "base/memory/ptr_util.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
+#include "chrome/browser/download/download_core_service_factory.h"
+#include "chrome/browser/download/download_core_service_impl.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
@@ -22,15 +24,17 @@ class DownloadsHandlerTest : public testing::Test {
public:
DownloadsHandlerTest()
: download_manager_(new content::MockDownloadManager()),
- chrome_download_manager_delegate_(&profile_),
handler_(&profile_) {
content::BrowserContext::SetDownloadManagerForTesting(
&profile_, base::WrapUnique(download_manager_));
- EXPECT_EQ(download_manager_,
- content::BrowserContext::GetDownloadManager(&profile_));
-
- EXPECT_CALL(*download_manager_, GetDelegate())
- .WillRepeatedly(testing::Return(&chrome_download_manager_delegate_));
+ std::unique_ptr<ChromeDownloadManagerDelegate> delegate =
+ std::make_unique<ChromeDownloadManagerDelegate>(&profile_);
+ chrome_download_manager_delegate_ = delegate.get();
+ service_ = DownloadCoreServiceFactory::GetForBrowserContext(&profile_);
+ service_->SetDownloadManagerDelegateForTesting(std::move(delegate));
+
+ EXPECT_CALL(*download_manager_, GetBrowserContext())
+ .WillRepeatedly(testing::Return(&profile_));
EXPECT_CALL(*download_manager_, Shutdown());
handler_.set_web_ui(&test_web_ui_);
@@ -49,7 +53,7 @@ class DownloadsHandlerTest : public testing::Test {
}
void TearDown() override {
- chrome_download_manager_delegate_.Shutdown();
+ service_->SetDownloadManagerDelegateForTesting(nullptr);
testing::Test::TearDown();
}
@@ -74,8 +78,9 @@ class DownloadsHandlerTest : public testing::Test {
content::TestWebUI test_web_ui_;
TestingProfile profile_;
+ DownloadCoreService* service_;
content::MockDownloadManager* download_manager_; // Owned by |profile_|.
- ChromeDownloadManagerDelegate chrome_download_manager_delegate_;
+ ChromeDownloadManagerDelegate* chrome_download_manager_delegate_;
DownloadsHandler handler_;
};
diff --git a/chromium/chrome/browser/ui/webui/settings/incompatible_applications_handler_win.cc b/chromium/chrome/browser/ui/webui/settings/incompatible_applications_handler_win.cc
new file mode 100644
index 00000000000..af1646f42db
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/incompatible_applications_handler_win.cc
@@ -0,0 +1,168 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/settings/incompatible_applications_handler_win.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "base/win/registry.h"
+#include "chrome/browser/conflicts/problematic_programs_updater_win.h"
+#include "chrome/browser/conflicts/registry_key_watcher_win.h"
+#include "chrome/browser/conflicts/uninstall_application_win.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace settings {
+
+IncompatibleApplicationsHandler::IncompatibleApplicationsHandler() = default;
+
+IncompatibleApplicationsHandler::~IncompatibleApplicationsHandler() = default;
+
+void IncompatibleApplicationsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "requestIncompatibleApplicationsList",
+ base::BindRepeating(&IncompatibleApplicationsHandler::
+ HandleRequestIncompatibleApplicationsList,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "startProgramUninstallation",
+ base::BindRepeating(
+ &IncompatibleApplicationsHandler::HandleStartProgramUninstallation,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getSubtitlePluralString",
+ base::BindRepeating(
+ &IncompatibleApplicationsHandler::HandleGetSubtitlePluralString,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getSubtitleNoAdminRightsPluralString",
+ base::BindRepeating(&IncompatibleApplicationsHandler::
+ HandleGetSubtitleNoAdminRightsPluralString,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getListTitlePluralString",
+ base::BindRepeating(
+ &IncompatibleApplicationsHandler::HandleGetListTitlePluralString,
+ base::Unretained(this)));
+}
+
+void IncompatibleApplicationsHandler::OnJavascriptAllowed() {}
+
+void IncompatibleApplicationsHandler::OnJavascriptDisallowed() {
+ registry_key_watchers_.clear();
+}
+
+void IncompatibleApplicationsHandler::HandleRequestIncompatibleApplicationsList(
+ const base::ListValue* args) {
+ CHECK_EQ(1u, args->GetList().size());
+
+ AllowJavascript();
+
+ // Reset the registry watchers, to correctly handle repeated calls to
+ // requestIncompatibleApplicationsList().
+ registry_key_watchers_.clear();
+
+ std::vector<ProblematicProgramsUpdater::ProblematicProgram>
+ problematic_programs = ProblematicProgramsUpdater::GetCachedPrograms();
+
+ base::Value application_list(base::Value::Type::LIST);
+ application_list.GetList().reserve(problematic_programs.size());
+
+ for (const auto& program : problematic_programs) {
+ // Set up a registry watcher for each problem application.
+ // Since this instance owns the watcher, it is safe to use
+ // base::Unretained() because the callback won't be invoked when the watcher
+ // gets deleted.
+ auto registry_key_watcher = RegistryKeyWatcher::Create(
+ program.info.registry_root, program.info.registry_key_path.c_str(),
+ program.info.registry_wow64_access,
+ base::BindOnce(&IncompatibleApplicationsHandler::OnApplicationRemoved,
+ base::Unretained(this), program.info));
+
+ // Only keep the watcher if it was successfully initialized. A failure here
+ // is unlikely, but the worst that can happen is that the |program| will not
+ // get removed from the list automatically in the Incompatible Applications
+ // subpage.
+ if (registry_key_watcher) {
+ registry_key_watchers_.insert(
+ {program.info, std::move(registry_key_watcher)});
+ }
+
+ // Also add the application to the list that is passed to the javascript.
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetKey("name", base::Value(program.info.name));
+ dict.SetKey("type", base::Value(program.blacklist_action->message_type()));
+ dict.SetKey("url", base::Value(program.blacklist_action->message_url()));
+ application_list.GetList().push_back(std::move(dict));
+ }
+
+ UMA_HISTOGRAM_COUNTS_100("IncompatibleApplicationsPage.NumApplications",
+ problematic_programs.size());
+
+ const base::Value& callback_id = args->GetList().front();
+ ResolveJavascriptCallback(callback_id, application_list);
+}
+
+void IncompatibleApplicationsHandler::HandleStartProgramUninstallation(
+ const base::ListValue* args) {
+ CHECK_EQ(1u, args->GetList().size());
+ base::RecordAction(base::UserMetricsAction(
+ "IncompatibleApplicationsPage.UninstallationStarted"));
+
+ // Open the Apps & Settings page with the program name highlighted.
+ uninstall_application::LaunchUninstallFlow(
+ base::UTF8ToUTF16(args->GetList()[0].GetString()));
+}
+
+void IncompatibleApplicationsHandler::HandleGetSubtitlePluralString(
+ const base::ListValue* args) {
+ GetPluralString(IDS_SETTINGS_INCOMPATIBLE_APPLICATIONS_SUBPAGE_SUBTITLE,
+ args);
+}
+
+void IncompatibleApplicationsHandler::
+ HandleGetSubtitleNoAdminRightsPluralString(const base::ListValue* args) {
+ GetPluralString(
+ IDS_SETTINGS_INCOMPATIBLE_APPLICATIONS_SUBPAGE_SUBTITLE_NO_ADMIN_RIGHTS,
+ args);
+}
+
+void IncompatibleApplicationsHandler::HandleGetListTitlePluralString(
+ const base::ListValue* args) {
+ GetPluralString(IDS_SETTINGS_INCOMPATIBLE_APPLICATIONS_LIST_TITLE, args);
+}
+
+void IncompatibleApplicationsHandler::GetPluralString(
+ int id,
+ const base::ListValue* args) {
+ CHECK_EQ(2U, args->GetList().size());
+
+ const base::Value& callback_id = args->GetList()[0];
+ int num_applications = args->GetList()[1].GetInt();
+ DCHECK_GT(0, num_applications);
+
+ ResolveJavascriptCallback(
+ callback_id,
+ base::Value(l10n_util::GetPluralStringFUTF16(id, num_applications)));
+}
+
+void IncompatibleApplicationsHandler::OnApplicationRemoved(
+ const InstalledPrograms::ProgramInfo& program) {
+ base::RecordAction(base::UserMetricsAction(
+ "IncompatibleApplicationsPage.ApplicationRemoved"));
+
+ registry_key_watchers_.erase(program);
+ FireWebUIListener("incompatible-application-removed",
+ base::Value(program.name));
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/incompatible_applications_handler_win.h b/chromium/chrome/browser/ui/webui/settings/incompatible_applications_handler_win.h
new file mode 100644
index 00000000000..b7518f69a45
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/incompatible_applications_handler_win.h
@@ -0,0 +1,58 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_INCOMPATIBLE_APPLICATIONS_HANDLER_WIN_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_INCOMPATIBLE_APPLICATIONS_HANDLER_WIN_H_
+
+#include <map>
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/conflicts/installed_programs_win.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+class RegistryKeyWatcher;
+
+namespace base {
+class ListValue;
+}
+
+namespace settings {
+
+// Incompatible Applications settings page UI handler.
+class IncompatibleApplicationsHandler : public SettingsPageUIHandler {
+ public:
+ IncompatibleApplicationsHandler();
+ ~IncompatibleApplicationsHandler() override;
+
+ // SettingsPageUIHandler:
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ private:
+ // Sends the list of incompatible applications to the caller via a promise.
+ void HandleRequestIncompatibleApplicationsList(const base::ListValue* args);
+
+ // Initiates the uninstallation of the program passed using |args|.
+ void HandleStartProgramUninstallation(const base::ListValue* args);
+
+ void HandleGetSubtitlePluralString(const base::ListValue* args);
+ void HandleGetSubtitleNoAdminRightsPluralString(const base::ListValue* args);
+ void HandleGetListTitlePluralString(const base::ListValue* args);
+ void GetPluralString(int id, const base::ListValue* args);
+
+ // Callback for the registry key watchers.
+ void OnApplicationRemoved(const InstalledPrograms::ProgramInfo& program);
+
+ // Container for the watchers.
+ std::map<InstalledPrograms::ProgramInfo, std::unique_ptr<RegistryKeyWatcher>>
+ registry_key_watchers_;
+
+ DISALLOW_COPY_AND_ASSIGN(IncompatibleApplicationsHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_INCOMPATIBLE_APPLICATIONS_HANDLER_WIN_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chromium/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 906dbf0f698..1c76ec475ac 100644
--- a/chromium/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chromium/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -11,6 +11,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
+#include "build/buildflag.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/plugins/plugin_utils.h"
@@ -28,12 +29,15 @@
#include "components/google/core/browser/google_util.h"
#include "components/password_manager/core/browser/password_manager_constants.h"
#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/signin/core/browser/signin_features.h"
#include "components/strings/grit/components_strings.h"
#include "components/subresource_filter/core/browser/subresource_filter_features.h"
#include "content/public/browser/web_ui_data_source.h"
+#include "services/device/public/cpp/device_features.h"
#include "ui/base/l10n/l10n_util.h"
#if defined(OS_CHROMEOS)
+#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/ash_switches.h"
#include "ash/public/interfaces/voice_interaction_controller.mojom.h"
#include "ash/strings/grit/ash_strings.h"
@@ -43,6 +47,7 @@
#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/signin/account_consistency_mode_manager.h"
#include "chrome/browser/ui/webui/chromeos/bluetooth_dialog_localized_strings_provider.h"
#include "chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.h"
#include "chromeos/chromeos_switches.h"
@@ -54,6 +59,7 @@
#include "ui/display/manager/chromeos/touch_device_manager.h"
#else
#include "chrome/browser/ui/webui/settings/system_handler.h"
+#include "components/signin/core/browser/profile_management_switches.h"
#endif
#if defined(OS_WIN)
@@ -176,6 +182,19 @@ void AddA11yStrings(content::WebUIDataSource* html_source) {
{"chromeVoxLabel", IDS_SETTINGS_CHROMEVOX_LABEL},
{"chromeVoxOptionsLabel", IDS_SETTINGS_CHROMEVOX_OPTIONS_LABEL},
{"screenMagnifierLabel", IDS_SETTINGS_SCREEN_MAGNIFIER_LABEL},
+ {"screenMagnifierZoomLabel", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_LABEL},
+ {"dockedMagnifierLabel", IDS_SETTINGS_DOCKED_MAGNIFIER_LABEL},
+ {"dockedMagnifierZoomLabel", IDS_SETTINGS_DOCKED_MAGNIFIER_ZOOM_LABEL},
+ {"screenMagnifierZoom2x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_2_X},
+ {"screenMagnifierZoom4x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_4_X},
+ {"screenMagnifierZoom6x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_6_X},
+ {"screenMagnifierZoom8x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_8_X},
+ {"screenMagnifierZoom10x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_10_X},
+ {"screenMagnifierZoom12x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_12_X},
+ {"screenMagnifierZoom14x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_14_X},
+ {"screenMagnifierZoom16x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_16_X},
+ {"screenMagnifierZoom18x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_18_X},
+ {"screenMagnifierZoom20x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_20_X},
{"tapDraggingLabel", IDS_SETTINGS_TAP_DRAGGING_LABEL},
{"clickOnStopLabel", IDS_SETTINGS_CLICK_ON_STOP_LABEL},
{"delayBeforeClickLabel", IDS_SETTINGS_DELAY_BEFORE_CLICK_LABEL},
@@ -231,13 +250,17 @@ void AddA11yStrings(content::WebUIDataSource* html_source) {
arraysize(localized_strings));
#if defined(OS_CHROMEOS)
- html_source->AddString("a11yLearnMoreUrl",
- chrome::kChromeAccessibilityHelpURL);
+ html_source->AddString(
+ "a11yLearnMoreUrl",
+ GetHelpUrlWithBoard(chrome::kChromeAccessibilityHelpURL));
html_source->AddBoolean(
"showExperimentalA11yFeatures",
base::CommandLine::ForCurrentProcess()->HasSwitch(
chromeos::switches::kEnableExperimentalAccessibilityFeatures));
+
+ html_source->AddBoolean("dockedMagnifierFeatureEnabled",
+ ash::features::IsDockedMagnifierEnabled());
#endif
}
@@ -448,22 +471,24 @@ void AddChangePasswordStrings(content::WebUIDataSource* html_source) {
#endif
}
-void AddClearBrowsingDataStrings(content::WebUIDataSource* html_source) {
+void AddClearBrowsingDataStrings(content::WebUIDataSource* html_source,
+ Profile* profile) {
int clear_cookies_summary_msg_id =
IDS_SETTINGS_CLEAR_COOKIES_AND_SITE_DATA_SUMMARY_BASIC;
#if defined(OS_CHROMEOS)
- // Mirror account reconciliation behavior is turned on for child accounts on
- // Chrome OS.
- if (user_manager::UserManager::Get()->GetPrimaryUser()->GetType() ==
- user_manager::USER_TYPE_CHILD) {
+ if (AccountConsistencyModeManager::IsMirrorEnabledForProfile(profile)) {
+ clear_cookies_summary_msg_id =
+ IDS_SETTINGS_CLEAR_COOKIES_AND_SITE_DATA_SUMMARY_BASIC_WITH_EXCEPTION;
+ }
+#else // !defined(OS_CHROMEOS)
+ if (signin::IsDiceEnabledForProfile(profile->GetPrefs())) {
clear_cookies_summary_msg_id =
- IDS_SETTINGS_CLEAR_COOKIES_AND_SITE_DATA_MIRROR_SUMMARY_BASIC;
+ IDS_SETTINGS_CLEAR_COOKIES_AND_SITE_DATA_SUMMARY_BASIC_WITH_EXCEPTION;
}
#endif
LocalizedString localized_strings[] = {
- {"clearFollowingItemsFrom", IDS_SETTINGS_CLEAR_FOLLOWING_ITEMS_FROM},
{"clearTimeRange", IDS_SETTINGS_CLEAR_PERIOD_TITLE},
{"clearBrowsingHistory", IDS_SETTINGS_CLEAR_BROWSING_HISTORY},
{"clearBrowsingHistorySummary",
@@ -478,19 +503,11 @@ void AddClearBrowsingDataStrings(content::WebUIDataSource* html_source) {
{"clearFormData", IDS_SETTINGS_CLEAR_FORM_DATA},
{"clearHostedAppData", IDS_SETTINGS_CLEAR_HOSTED_APP_DATA},
{"clearMediaLicenses", IDS_SETTINGS_CLEAR_MEDIA_LICENSES},
- {"clearDataHour", IDS_SETTINGS_CLEAR_DATA_HOUR},
- {"clearDataDay", IDS_SETTINGS_CLEAR_DATA_DAY},
- {"clearDataWeek", IDS_SETTINGS_CLEAR_DATA_WEEK},
- {"clearData4Weeks", IDS_SETTINGS_CLEAR_DATA_4WEEKS},
- {"clearDataEverything", IDS_SETTINGS_CLEAR_DATA_EVERYTHING},
{"clearPeriodHour", IDS_SETTINGS_CLEAR_PERIOD_HOUR},
{"clearPeriod24Hours", IDS_SETTINGS_CLEAR_PERIOD_24_HOURS},
{"clearPeriod7Days", IDS_SETTINGS_CLEAR_PERIOD_7_DAYS},
{"clearPeriod4Weeks", IDS_SETTINGS_CLEAR_PERIOD_FOUR_WEEKS},
{"clearPeriodEverything", IDS_SETTINGS_CLEAR_PERIOD_EVERYTHING},
- {"warnAboutNonClearedData", IDS_SETTINGS_CLEAR_DATA_SOME_STUFF_REMAINS},
- {"clearsSyncedData", IDS_SETTINGS_CLEAR_DATA_CLEARS_SYNCED_DATA},
- {"clearBrowsingDataLearnMoreUrl", IDS_SETTINGS_CLEAR_DATA_LEARN_MORE_URL},
{"historyDeletionDialogTitle",
IDS_CLEAR_BROWSING_DATA_HISTORY_NOTICE_TITLE},
{"historyDeletionDialogOK", IDS_CLEAR_BROWSING_DATA_HISTORY_NOTICE_OK},
@@ -513,12 +530,6 @@ void AddClearBrowsingDataStrings(content::WebUIDataSource* html_source) {
IDS_SETTINGS_CLEAR_BROWSING_HISTORY_SUMMARY_SYNCED,
base::ASCIIToUTF16(chrome::kMyActivityUrlInClearBrowsingData)));
html_source->AddString(
- "otherFormsOfBrowsingHistory",
- l10n_util::GetStringFUTF16(
- IDS_CLEAR_BROWSING_DATA_HISTORY_FOOTER,
- l10n_util::GetStringUTF16(
- IDS_SETTINGS_CLEAR_DATA_WEB_HISTORY_URL_IN_FOOTER)));
- html_source->AddString(
"historyDeletionDialogBody",
l10n_util::GetStringFUTF16(
IDS_CLEAR_BROWSING_DATA_HISTORY_NOTICE,
@@ -552,7 +563,7 @@ void AddDeviceStrings(content::WebUIDataSource* html_source) {
{"scrollLabel", IDS_SETTINGS_SCROLL_LABEL},
{"traditionalScrollLabel", IDS_SETTINGS_TRADITIONAL_SCROLL_LABEL},
{"naturalScrollLabel", IDS_SETTINGS_NATURAL_SCROLL_LABEL},
- {"naturalScrollLearnMore", IDS_SETTINGS_NATURAL_SCROLL_LEARN_MORE},
+ {"naturalScrollLearnMore", IDS_LEARN_MORE},
};
AddLocalizedStringsBulk(html_source, device_strings,
arraysize(device_strings));
@@ -785,8 +796,6 @@ void AddChromeCleanupStrings(content::WebUIDataSource* html_source) {
IDS_SETTINGS_RESET_CLEANUP_DETAILS_FILES_AND_PROGRAMS},
{"chromeCleanupDetailsRegistryEntries",
IDS_SETTINGS_RESET_CLEANUP_DETAILS_REGISTRY_ENTRIES},
- {"chromeCleanupDoneButtonLabel",
- IDS_SETTINGS_RESET_CLEANUP_DONE_BUTTON_LABEL},
{"chromeCleanupExplanationCleanupError",
IDS_SETTINGS_RESET_CLEANUP_EXPLANATION_CLEANUP_ERROR},
{"chromeCleanupExplanationFindAndRemove",
@@ -858,6 +867,33 @@ void AddChromeCleanupStrings(content::WebUIDataSource* html_source) {
html_source->AddString("chromeCleanupDetailsExplanation",
cleanup_details_explanation);
}
+
+void AddIncompatibleApplicationsStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"incompatibleApplicationsResetCardTitle",
+ IDS_SETTINGS_INCOMPATIBLE_APPLICATIONS_RESET_CARD_TITLE},
+ {"incompatibleApplicationsSubpageSubtitle",
+ IDS_SETTINGS_INCOMPATIBLE_APPLICATIONS_SUBPAGE_SUBTITLE},
+ {"incompatibleApplicationsSubpageSubtitleNoAdminRights",
+ IDS_SETTINGS_INCOMPATIBLE_APPLICATIONS_SUBPAGE_SUBTITLE_NO_ADMIN_RIGHTS},
+ {"incompatibleApplicationsListTitle",
+ IDS_SETTINGS_INCOMPATIBLE_APPLICATIONS_LIST_TITLE},
+ {"incompatibleApplicationsRemoveButton",
+ IDS_SETTINGS_INCOMPATIBLE_APPLICATIONS_REMOVE_BUTTON},
+ {"incompatibleApplicationsUpdateButton",
+ IDS_SETTINGS_INCOMPATIBLE_APPLICATIONS_UPDATE_BUTTON},
+ {"incompatibleApplicationsDone",
+ IDS_SETTINGS_INCOMPATIBLE_APPLICATIONS_DONE},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+ // TODO(pmonette): Add the help URL when available.
+ base::string16 learn_how_text = l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_INCOMPATIBLE_APPLICATIONS_SUBPAGE_LEARN_HOW,
+ base::ASCIIToUTF16("chrome://placeholder"));
+ html_source->AddString("incompatibleApplicationsSubpageLearnHow",
+ learn_how_text);
+}
#endif // defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
void AddResetStrings(content::WebUIDataSource* html_source) {
@@ -892,8 +928,6 @@ void AddResetStrings(content::WebUIDataSource* html_source) {
#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
{"resetCleanupComputerTrigger",
IDS_SETTINGS_RESET_CLEAN_UP_COMPUTER_TRIGGER},
- {"resetCleanupComputerTriggerDescription",
- IDS_SETTINGS_RESET_CLEAN_UP_COMPUTER_TRIGGER_DESCRIPTION},
#endif
};
AddLocalizedStringsBulk(html_source, localized_strings,
@@ -1078,8 +1112,7 @@ void AddInternetStrings(content::WebUIDataSource* html_source) {
{"networkConnectNotAllowed", IDS_SETTINGS_INTERNET_CONNECT_NOT_ALLOWED},
{"networkIPAddress", IDS_SETTINGS_INTERNET_NETWORK_IP_ADDRESS},
{"networkIPConfigAuto", IDS_SETTINGS_INTERNET_NETWORK_IP_CONFIG_AUTO},
- {"networkNameserversLearnMore",
- IDS_SETTINGS_INTERNET_NETWORK_NAMESERVERS_LEARN_MORE},
+ {"networkNameserversLearnMore", IDS_LEARN_MORE},
{"networkPrefer", IDS_SETTINGS_INTERNET_NETWORK_PREFER},
{"networkPrimaryUserControlled",
IDS_SETTINGS_INTERNET_NETWORK_PRIMARY_USER_CONTROLLED},
@@ -1112,6 +1145,8 @@ void AddInternetStrings(content::WebUIDataSource* html_source) {
IDS_SETTINGS_INTERNET_GMSCORE_NOTIFICATIONS_SECOND_STEP},
{"gmscoreNotificationsThirdStep",
IDS_SETTINGS_INTERNET_GMSCORE_NOTIFICATIONS_THIRD_STEP},
+ {"gmscoreNotificationsFourthStep",
+ IDS_SETTINGS_INTERNET_GMSCORE_NOTIFICATIONS_FOURTH_STEP},
{"tetherConnectionDialogTitle",
IDS_SETTINGS_INTERNET_TETHER_CONNECTION_DIALOG_TITLE},
{"tetherConnectionAvailableDeviceTitle",
@@ -1276,6 +1311,7 @@ void AddPasswordsAndFormsStrings(content::WebUIDataSource* html_source) {
{"autofill", IDS_SETTINGS_AUTOFILL},
{"googlePayments", IDS_SETTINGS_GOOGLE_PAYMENTS},
{"googlePaymentsCached", IDS_SETTINGS_GOOGLE_PAYMENTS_CACHED},
+ {"autofillFormsLabel", IDS_SETTINGS_AUTOFILL_TOGGLE_LABEL},
{"addresses", IDS_SETTINGS_AUTOFILL_ADDRESSES_HEADING},
{"addAddressTitle", IDS_SETTINGS_AUTOFILL_ADDRESSES_ADD_TITLE},
{"editAddressTitle", IDS_SETTINGS_AUTOFILL_ADDRESSES_EDIT_TITLE},
@@ -1297,6 +1333,8 @@ void AddPasswordsAndFormsStrings(content::WebUIDataSource* html_source) {
{"addCreditCardTitle", IDS_SETTINGS_ADD_CREDIT_CARD_TITLE},
{"autofillDetail", IDS_SETTINGS_AUTOFILL_DETAIL},
{"passwords", IDS_SETTINGS_PASSWORDS},
+ {"passwordsSavePasswordsLabel",
+ IDS_SETTINGS_PASSWORDS_SAVE_PASSWORDS_TOGGLE_LABEL},
{"passwordsAutosigninLabel",
IDS_SETTINGS_PASSWORDS_AUTOSIGNIN_CHECKBOX_LABEL},
{"passwordsAutosigninDescription",
@@ -1305,7 +1343,6 @@ void AddPasswordsAndFormsStrings(content::WebUIDataSource* html_source) {
{"savedPasswordsHeading", IDS_SETTINGS_PASSWORDS_SAVED_HEADING},
{"passwordExceptionsHeading", IDS_SETTINGS_PASSWORDS_EXCEPTIONS_HEADING},
{"deletePasswordException", IDS_SETTINGS_PASSWORDS_DELETE_EXCEPTION},
- {"passwordsDone", IDS_SETTINGS_PASSWORD_DONE},
{"removePassword", IDS_SETTINGS_PASSWORD_REMOVE},
{"searchPasswords", IDS_SETTINGS_PASSWORD_SEARCH},
{"showPassword", IDS_SETTINGS_PASSWORD_SHOW},
@@ -1321,12 +1358,22 @@ void AddPasswordsAndFormsStrings(content::WebUIDataSource* html_source) {
{"noPasswordsFound", IDS_SETTINGS_PASSWORDS_NONE},
{"noExceptionsFound", IDS_SETTINGS_PASSWORDS_EXCEPTIONS_NONE},
{"import", IDS_PASSWORD_MANAGER_IMPORT_BUTTON},
- {"export", IDS_PASSWORD_MANAGER_EXPORT_BUTTON},
+ {"exportMenuItem", IDS_SETTINGS_PASSWORDS_EXPORT_MENU_ITEM},
{"undoRemovePassword", IDS_SETTINGS_PASSWORD_UNDO},
{"passwordDeleted", IDS_SETTINGS_PASSWORD_DELETED_PASSWORD},
{"exportPasswordsTitle", IDS_SETTINGS_PASSWORDS_EXPORT_TITLE},
{"exportPasswordsDescription", IDS_SETTINGS_PASSWORDS_EXPORT_DESCRIPTION},
- {"exportPasswords", IDS_SETTINGS_PASSWORDS_EXPORT}};
+ {"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}};
html_source->AddString(
"managePasswordsLabel",
@@ -1345,11 +1392,10 @@ void AddPasswordsAndFormsStrings(content::WebUIDataSource* html_source) {
arraysize(localized_strings));
}
-void AddPeopleStrings(content::WebUIDataSource* html_source) {
+void AddPeopleStrings(content::WebUIDataSource* html_source, Profile* profile) {
LocalizedString localized_strings[] = {
{"peoplePageTitle", IDS_SETTINGS_PEOPLE},
{"manageOtherPeople", IDS_SETTINGS_PEOPLE_MANAGE_OTHER_PEOPLE},
- {"manageSupervisedUsers", IDS_SETTINGS_PEOPLE_MANAGE_SUPERVISED_USERS},
#if defined(OS_CHROMEOS)
{"configureFingerprintTitle", IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_TITLE},
{"configureFingerprintInstructionLocateScannerStep",
@@ -1372,8 +1418,6 @@ void AddPeopleStrings(content::WebUIDataSource* html_source) {
IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_FINGER_TOO_FAST},
{"configureFingerprintImmobile",
IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_FINGER_IMMOBILE},
- {"configureFingerprintDoneButton",
- IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_DONE_BUTTON},
{"configureFingerprintAddAnotherButton",
IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_ADD_ANOTHER_BUTTON},
{"configurePinChoosePinTitle",
@@ -1448,8 +1492,19 @@ void AddPeopleStrings(content::WebUIDataSource* html_source) {
#else // !defined(OS_CHROMEOS)
{"domainManagedProfile", IDS_SETTINGS_PEOPLE_DOMAIN_MANAGED_PROFILE},
{"editPerson", IDS_SETTINGS_EDIT_PERSON},
+ {"profileNameAndPicture", IDS_SETTINGS_PROFILE_NAME_AND_PICTURE},
{"showShortcutLabel", IDS_SETTINGS_PROFILE_SHORTCUT_TOGGLE_LABEL},
#endif // defined(OS_CHROMEOS)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+ {"peopleSignIn", IDS_SETTINGS_PEOPLE_SIGN_IN},
+ {"peopleSignInPrompt", IDS_SETTINGS_PEOPLE_SIGN_IN_PROMPT},
+ {"peopleSignInPromptSecondary",
+ IDS_SETTINGS_PEOPLE_SIGN_IN_PROMPT_SECONDARY},
+ {"useAnotherAccount", IDS_SETTINGS_PEOPLE_SYNC_ANOTHER_ACCOUNT},
+ {"syncAsName", IDS_SETTINGS_PEOPLE_SYNC_AS_NAME},
+ {"syncedToName", IDS_SETTINGS_PEOPLE_SYNCED_AS_NAME},
+ {"turnOffSync", IDS_SETTINGS_PEOPLE_SYNC_TURN_OFF},
+#endif
{"syncOverview", IDS_SETTINGS_SYNC_OVERVIEW},
{"syncDisabledByAdministrator",
IDS_SETTINGS_SYNC_DISABLED_BY_ADMINISTRATOR},
@@ -1563,6 +1618,20 @@ void AddPeopleStrings(content::WebUIDataSource* html_source) {
IDS_SETTINGS_SYNC_DISCONNECT_MANAGED_PROFILE_EXPLANATION,
base::ASCIIToUTF16("$1"),
base::ASCIIToUTF16(sync_dashboard_url)));
+
+ // The syncDisconnect text differs depending on Dice-enabledness.
+ if (signin::IsDiceEnabledForProfile(profile->GetPrefs())) {
+ LocalizedString sync_disconnect_strings[] = {
+ {"syncDisconnect", IDS_SETTINGS_TURN_OFF_SYNC_DIALOG_CONFIRM},
+ {"syncDisconnectTitle", IDS_SETTINGS_TURN_OFF_SYNC_DIALOG_TITLE},
+ {"syncDisconnectDeleteProfile",
+ IDS_SETTINGS_TURN_OFF_SYNC_DIALOG_CHECKBOX},
+ {"syncDisconnectConfirm",
+ IDS_SETTINGS_TURN_OFF_SYNC_DIALOG_MANAGED_CONFIRM},
+ };
+ AddLocalizedStringsBulk(html_source, sync_disconnect_strings,
+ arraysize(sync_disconnect_strings));
+ }
#endif
html_source->AddString("syncErrorHelpUrl", chrome::kSyncErrorsHelpURL);
@@ -1595,6 +1664,7 @@ void AddPrintingStrings(content::WebUIDataSource* html_source) {
{"editPrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_EDIT},
{"removePrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_REMOVE},
{"searchLabel", IDS_SETTINGS_PRINTING_CUPS_SEARCH_LABEL},
+ {"noSearchResults", IDS_SEARCH_NO_RESULTS},
{"printerDetailsTitle", IDS_SETTINGS_PRINTING_CUPS_PRINTER_DETAILS_TITLE},
{"printerName", IDS_SETTINGS_PRINTING_CUPS_PRINTER_DETAILS_NAME},
{"printerModel", IDS_SETTINGS_PRINTING_CUPS_PRINTER_DETAILS_MODEL},
@@ -1626,13 +1696,12 @@ void AddPrintingStrings(content::WebUIDataSource* html_source) {
{"printerProtocolUsb", IDS_SETTINGS_PRINTING_CUPS_PRINTER_PROTOCOL_USB},
{"printerConfiguringMessage",
IDS_SETTINGS_PRINTING_CUPS_PRINTER_CONFIGURING_MESSAGE},
- {"searchingPrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTER_SEARCHING_PRINTER},
- {"printerNotFound", IDS_SETTINGS_PRINTING_CUPS_PRINTER_NOT_FOUND_PRINTER},
- {"printerFound", IDS_SETTINGS_PRINTING_CUPS_PRINTER_FOUND_PRINTER},
{"printerManufacturer", IDS_SETTINGS_PRINTING_CUPS_PRINTER_MANUFACTURER},
{"selectDriver", IDS_SETTINGS_PRINTING_CUPS_PRINTER_SELECT_DRIVER},
{"selectDriverButtonText",
IDS_SETTINGS_PRINTING_CUPS_PRINTER_BUTTON_SELECT_DRIVER},
+ {"selectDriverErrorMessage",
+ IDS_SETTINGS_PRINTING_CUPS_PRINTER_INVALID_DRIVER},
{"printerAddedSuccessfulMessage",
IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADDED_PRINTER_DONE_MESSAGE},
{"noPrinterNearbyMessage",
@@ -1711,9 +1780,6 @@ void AddPrivacyStrings(content::WebUIDataSource* html_source,
AddLocalizedStringsBulk(html_source, localized_strings,
arraysize(localized_strings));
- html_source->AddBoolean("tabsInCbd",
- base::FeatureList::IsEnabled(features::kTabsInCbd));
-
html_source->AddBoolean(
"importantSitesInCbd",
base::FeatureList::IsEnabled(features::kImportantSitesInCbd));
@@ -1946,6 +2012,9 @@ void AddSiteSettingsStrings(content::WebUIDataSource* html_source,
{"siteSettingsSoundAllowRecommended",
IDS_SETTINGS_SITE_SETTINGS_SOUND_ALLOW_RECOMMENDED},
{"siteSettingsSoundBlock", IDS_SETTINGS_SITE_SETTINGS_SOUND_BLOCK},
+ {"siteSettingsSensors", IDS_SETTINGS_SITE_SETTINGS_SENSORS},
+ {"siteSettingsSensorsAllow", IDS_SETTINGS_SITE_SETTINGS_SENSORS_ALLOW},
+ {"siteSettingsSensorsBlock", IDS_SETTINGS_SITE_SETTINGS_SENSORS_BLOCK},
{"siteSettingsFlash", IDS_SETTINGS_SITE_SETTINGS_FLASH},
{"siteSettingsPdfDocuments", IDS_SETTINGS_SITE_SETTINGS_PDF_DOCUMENTS},
{"siteSettingsPdfDownloadPdfs",
@@ -2101,7 +2170,7 @@ void AddSiteSettingsStrings(content::WebUIDataSource* html_source,
{"handlerSetDefault", IDS_SETTINGS_SITE_SETTINGS_HANDLER_SET_DEFAULT},
{"handlerRemove", IDS_SETTINGS_SITE_SETTINGS_REMOVE},
{"adobeFlashStorage", IDS_SETTINGS_SITE_SETTINGS_ADOBE_FLASH_SETTINGS},
- {"learnMore", IDS_SETTINGS_SITE_SETTINGS_LEARN_MORE},
+ {"learnMore", IDS_LEARN_MORE},
{"incognitoSite", IDS_SETTINGS_SITE_SETTINGS_INCOGNITO},
{"incognitoSiteOnly", IDS_SETTINGS_SITE_SETTINGS_INCOGNITO_ONLY},
{"embeddedIncognitoSite", IDS_SETTINGS_SITE_SETTINGS_INCOGNITO_EMBEDDED},
@@ -2130,6 +2199,10 @@ void AddSiteSettingsStrings(content::WebUIDataSource* html_source,
"enableClipboardContentSetting",
base::FeatureList::IsEnabled(features::kClipboardContentSetting));
+ html_source->AddBoolean(
+ "enableSensorsContentSetting",
+ base::FeatureList::IsEnabled(features::kGenericSensorExtraClasses));
+
if (PluginUtils::ShouldPreferHtmlOverPlugins(
HostContentSettingsMapFactory::GetForProfile(profile))) {
LocalizedString flash_strings[] = {
@@ -2157,7 +2230,6 @@ void AddUsersStrings(content::WebUIDataSource* html_source) {
{"usersModifiedByOwnerLabel", IDS_SETTINGS_USERS_MODIFIED_BY_OWNER_LABEL},
{"guestBrowsingLabel", IDS_SETTINGS_USERS_GUEST_BROWSING_LABEL},
{"settingsManagedLabel", IDS_SETTINGS_USERS_MANAGED_LABEL},
- {"supervisedUsersLabel", IDS_SETTINGS_USERS_SUPERVISED_USERS_LABEL},
{"showOnSigninLabel", IDS_SETTINGS_USERS_SHOW_ON_SIGNIN_LABEL},
{"restrictSigninLabel", IDS_SETTINGS_USERS_RESTRICT_SIGNIN_LABEL},
{"deviceOwnerLabel", IDS_SETTINGS_USERS_DEVICE_OWNER_LABEL},
@@ -2245,16 +2317,17 @@ void AddLocalizedStrings(content::WebUIDataSource* html_source,
#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
AddChromeCleanupStrings(html_source);
+ AddIncompatibleApplicationsStrings(html_source);
#endif // defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
AddChangePasswordStrings(html_source);
- AddClearBrowsingDataStrings(html_source);
+ AddClearBrowsingDataStrings(html_source, profile);
AddCommonStrings(html_source, profile);
AddDownloadsStrings(html_source);
AddLanguagesStrings(html_source);
AddOnStartupStrings(html_source);
AddPasswordsAndFormsStrings(html_source);
- AddPeopleStrings(html_source);
+ AddPeopleStrings(html_source, profile);
AddPrintingStrings(html_source);
AddPrivacyStrings(html_source, profile);
AddResetStrings(html_source);
diff --git a/chromium/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chromium/chrome/browser/ui/webui/settings/md_settings_ui.cc
index 30779acd938..c3d1a1d2812 100644
--- a/chromium/chrome/browser/ui/webui/settings/md_settings_ui.cc
+++ b/chromium/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -54,6 +54,9 @@
#include "chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.h"
#include "chrome/browser/ui/webui/settings/chrome_cleanup_handler.h"
#if defined(GOOGLE_CHROME_BUILD)
+#include "chrome/browser/conflicts/problematic_programs_updater_win.h"
+#include "chrome/browser/conflicts/token_util_win.h"
+#include "chrome/browser/ui/webui/settings/incompatible_applications_handler_win.h"
#include "chrome/grit/chrome_unscaled_resources.h"
#endif
#endif // defined(OS_WIN)
@@ -88,6 +91,7 @@
#include "chrome/browser/ui/webui/settings/settings_default_browser_handler.h"
#include "chrome/browser/ui/webui/settings/settings_manage_profile_handler.h"
#include "chrome/browser/ui/webui/settings/system_handler.h"
+#include "components/signin/core/browser/profile_management_switches.h"
#endif // defined(OS_CHROMEOS)
#if defined(USE_NSS_CERTS)
@@ -222,9 +226,21 @@ MdSettingsUI::MdSettingsUI(content::WebUI* web_ui)
// should never change while Chrome is open.
html_source->AddBoolean("userInitiatedCleanupsEnabled",
userInitiatedCleanupsEnabled);
-
#endif // defined(OS_WIN)
+#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
+ bool has_incompatible_applications =
+ ProblematicProgramsUpdater::IsIncompatibleApplicationsWarningEnabled() &&
+ ProblematicProgramsUpdater::HasCachedPrograms();
+ html_source->AddBoolean("showIncompatibleApplications",
+ has_incompatible_applications);
+ html_source->AddBoolean("hasAdminRights", HasAdminRights());
+
+ if (has_incompatible_applications)
+ AddSettingsPageUIHandler(
+ std::make_unique<IncompatibleApplicationsHandler>());
+#endif // OS_WIN && defined(GOOGLE_CHROME_BUILD)
+
bool password_protection_available = false;
#if defined(SAFE_BROWSING_DB_LOCAL)
safe_browsing::ChromePasswordProtectionService* password_protection =
@@ -281,7 +297,10 @@ MdSettingsUI::MdSettingsUI(content::WebUI* web_ui)
AddSettingsPageUIHandler(std::make_unique<chromeos::settings::PowerHandler>(
profile->GetPrefs()));
}
-#endif
+#else // !defined(OS_CHROMEOS)
+ html_source->AddBoolean("diceEnabled",
+ signin::IsDiceEnabledForProfile(profile->GetPrefs()));
+#endif // defined(OS_CHROMEOS)
html_source->AddBoolean("showExportPasswords",
base::FeatureList::IsEnabled(
diff --git a/chromium/chrome/browser/ui/webui/settings/metrics_reporting_handler.cc b/chromium/chrome/browser/ui/webui/settings/metrics_reporting_handler.cc
index 60d70778592..def21ea78e0 100644
--- a/chromium/chrome/browser/ui/webui/settings/metrics_reporting_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/metrics_reporting_handler.cc
@@ -63,7 +63,8 @@ std::unique_ptr<base::DictionaryValue>
MetricsReportingHandler::CreateMetricsReportingDict() {
std::unique_ptr<base::DictionaryValue> dict(
std::make_unique<base::DictionaryValue>());
- dict->SetBoolean("enabled",
+ dict->SetBoolean(
+ "enabled",
ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled());
dict->SetBoolean("managed", IsMetricsReportingPolicyManaged());
return dict;
diff --git a/chromium/chrome/browser/ui/webui/settings/people_handler.cc b/chromium/chrome/browser/ui/webui/settings/people_handler.cc
index 170db11ae9c..8c8cddf5897 100644
--- a/chromium/chrome/browser/ui/webui/settings/people_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/people_handler.cc
@@ -31,8 +31,6 @@
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/singleton_tabs.h"
-#include "chrome/browser/ui/user_manager.h"
-#include "chrome/browser/ui/webui/profile_helper.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/chrome_switches.h"
@@ -62,8 +60,17 @@
#if defined(OS_CHROMEOS)
#include "components/signin/core/browser/signin_manager_base.h"
#else
+#include "chrome/browser/ui/user_manager.h"
+#include "chrome/browser/ui/webui/profile_helper.h"
#include "components/signin/core/browser/signin_manager.h"
#endif
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+#include "chrome/browser/profiles/profile_avatar_icon_util.h"
+#include "chrome/browser/signin/account_tracker_service_factory.h"
+#include "components/signin/core/browser/account_tracker_service.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/image/image.h"
+#endif
using browser_sync::ProfileSyncService;
using content::WebContents;
@@ -186,7 +193,14 @@ PeopleHandler::PeopleHandler(Profile* profile)
: profile_(profile),
configuring_sync_(false),
signin_observer_(this),
- sync_service_observer_(this) {}
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+ sync_service_observer_(this),
+ account_tracker_observer_(this) {
+}
+#else
+ sync_service_observer_(this) {
+}
+#endif
PeopleHandler::~PeopleHandler() {
// Early exit if running unit tests (no actual WebUI is attached).
@@ -231,6 +245,16 @@ void PeopleHandler::RegisterMessages() {
"SyncSetupStartSignIn",
base::Bind(&PeopleHandler::HandleStartSignin, base::Unretained(this)));
#endif
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+ web_ui()->RegisterMessageCallback(
+ "SyncSetupGetStoredAccounts",
+ base::BindRepeating(&PeopleHandler::HandleGetStoredAccounts,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "SyncSetupStartSyncingWithEmail",
+ base::BindRepeating(&PeopleHandler::HandleStartSyncingWithEmail,
+ base::Unretained(this)));
+#endif
}
void PeopleHandler::OnJavascriptAllowed() {
@@ -249,12 +273,22 @@ void PeopleHandler::OnJavascriptAllowed() {
ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_));
if (sync_service)
sync_service_observer_.Add(sync_service);
+
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+ AccountTrackerService* account_tracker(
+ AccountTrackerServiceFactory::GetForProfile(profile_));
+ if (account_tracker)
+ account_tracker_observer_.Add(account_tracker);
+#endif
}
void PeopleHandler::OnJavascriptDisallowed() {
profile_pref_registrar_.RemoveAll();
signin_observer_.RemoveAll();
sync_service_observer_.RemoveAll();
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+ account_tracker_observer_.RemoveAll();
+#endif
}
#if !defined(OS_CHROMEOS)
@@ -407,6 +441,68 @@ void PeopleHandler::HandleSetDatatypes(const base::ListValue* args) {
ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_CHOOSE);
}
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+void PeopleHandler::HandleGetStoredAccounts(const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+
+ ResolveJavascriptCallback(*callback_id, *GetStoredAccountsList());
+}
+
+void PeopleHandler::OnAccountUpdated(const AccountInfo& info) {
+ FireWebUIListener("stored-accounts-updated", *GetStoredAccountsList());
+}
+
+void PeopleHandler::OnAccountRemoved(const AccountInfo& info) {
+ FireWebUIListener("stored-accounts-updated", *GetStoredAccountsList());
+}
+
+std::unique_ptr<base::ListValue> PeopleHandler::GetStoredAccountsList() {
+ std::vector<AccountInfo> accounts =
+ signin_ui_util::GetAccountsForDicePromos(profile_);
+
+ AccountTrackerService* account_tracker =
+ AccountTrackerServiceFactory::GetForProfile(profile_);
+ std::unique_ptr<base::ListValue> accounts_list(new base::ListValue);
+ accounts_list->Reserve(accounts.size());
+
+ for (auto const& account : accounts) {
+ accounts_list->GetList().push_back(
+ base::Value(base::Value::Type::DICTIONARY));
+ base::Value& acc = accounts_list->GetList().back();
+ acc.SetKey("email", base::Value(account.email));
+ acc.SetKey("fullName", base::Value(account.full_name));
+ acc.SetKey("givenName", base::Value(account.given_name));
+ const gfx::Image& account_image =
+ account_tracker->GetAccountImage(account.account_id);
+ if (!account_image.IsEmpty()) {
+ acc.SetKey(
+ "avatarImage",
+ base::Value(webui::GetBitmapDataUrl(account_image.AsBitmap())));
+ }
+ }
+
+ return accounts_list;
+}
+
+void PeopleHandler::HandleStartSyncingWithEmail(const base::ListValue* args) {
+ const base::Value* email;
+ CHECK(args->Get(0, &email));
+
+ Browser* browser =
+ chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
+
+ AccountTrackerService* account_tracker =
+ AccountTrackerServiceFactory::GetForProfile(profile_);
+ AccountInfo account =
+ account_tracker->FindAccountInfoByEmail(email->GetString());
+
+ signin_ui_util::EnableSync(
+ browser, account, signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS);
+}
+#endif
+
void PeopleHandler::HandleSetEncryption(const base::ListValue* args) {
DCHECK(!sync_startup_tracker_);
@@ -545,7 +641,6 @@ void PeopleHandler::HandleStopSyncing(const base::ListValue* args) {
if (delete_profile) {
webui::DeleteProfileAtPath(profile_->GetPath(),
- web_ui(),
ProfileMetrics::DELETE_PROFILE_SETTINGS);
}
}
@@ -562,8 +657,10 @@ void PeopleHandler::HandleGetSyncStatus(const base::ListValue* args) {
}
void PeopleHandler::HandleManageOtherPeople(const base::ListValue* /* args */) {
+#if !defined(OS_CHROMEOS)
UserManager::Show(base::FilePath(),
profiles::USER_MANAGER_SELECT_PROFILE_NO_ACTION);
+#endif // !defined(OS_CHROMEOS)
}
void PeopleHandler::CloseSyncSetup() {
@@ -762,8 +859,6 @@ PeopleHandler::GetSyncStatusDictionary() {
ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_);
sync_status->SetBoolean("signinAllowed", signin->IsSigninAllowed());
sync_status->SetBoolean("syncSystemEnabled", (service != nullptr));
- sync_status->SetBoolean("setupCompleted",
- service && service->IsFirstSetupComplete());
sync_status->SetBoolean(
"setupInProgress",
service && !service->IsManaged() && service->IsFirstSetupInProgress());
@@ -786,7 +881,6 @@ PeopleHandler::GetSyncStatusDictionary() {
signin_ui_util::GetAuthenticatedUsername(signin));
sync_status->SetBoolean("hasUnrecoverableError",
service && service->HasUnrecoverableError());
-
return sync_status;
}
diff --git a/chromium/chrome/browser/ui/webui/settings/people_handler.h b/chromium/chrome/browser/ui/webui/settings/people_handler.h
index be8ab4ea3a4..0ea976fa175 100644
--- a/chromium/chrome/browser/ui/webui/settings/people_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/people_handler.h
@@ -14,13 +14,19 @@
#include "base/strings/utf_string_conversions.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
+#include "build/buildflag.h"
#include "chrome/browser/sync/sync_startup_tracker.h"
#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
#include "chrome/browser/ui/webui/signin/login_ui_service.h"
#include "components/prefs/pref_change_registrar.h"
+#include "components/signin/core/browser/signin_features.h"
#include "components/signin/core/browser/signin_manager_base.h"
#include "components/sync/driver/sync_service_observer.h"
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+#include "components/signin/core/browser/account_tracker_service.h"
+#endif
+
class LoginUIService;
class SigninManagerBase;
@@ -45,6 +51,9 @@ namespace settings {
class PeopleHandler : public SettingsPageUIHandler,
public SigninManagerBase::Observer,
public SyncStartupTracker::Observer,
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+ public AccountTrackerService::Observer,
+#endif
public LoginUIService::LoginUI,
public syncer::SyncServiceObserver {
public:
@@ -122,6 +131,12 @@ class PeopleHandler : public SettingsPageUIHandler,
// syncer::SyncServiceObserver implementation.
void OnStateChanged(syncer::SyncService* sync) override;
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+ // AccountTrackerService::Observer implementation.
+ void OnAccountUpdated(const AccountInfo& info) override;
+ void OnAccountRemoved(const AccountInfo& info) override;
+#endif
+
// Returns a newly created dictionary with a number of properties that
// correspond to the status of sync.
std::unique_ptr<base::DictionaryValue> GetSyncStatusDictionary();
@@ -155,6 +170,12 @@ class PeopleHandler : public SettingsPageUIHandler,
signin_metrics::AccessPoint access_point);
#endif
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+ void HandleGetStoredAccounts(const base::ListValue* args);
+ void HandleStartSyncingWithEmail(const base::ListValue* args);
+ std::unique_ptr<base::ListValue> GetStoredAccountsList();
+#endif
+
// Displays spinner-only UI indicating that something is going on in the
// background.
// TODO(kochi): better to show some message that the user can understand what
@@ -211,6 +232,11 @@ class PeopleHandler : public SettingsPageUIHandler,
ScopedObserver<browser_sync::ProfileSyncService, PeopleHandler>
sync_service_observer_;
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+ ScopedObserver<AccountTrackerService, PeopleHandler>
+ account_tracker_observer_;
+#endif
+
DISALLOW_COPY_AND_ASSIGN(PeopleHandler);
};
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 ec5442d0d1b..13312118858 100644
--- a/chromium/chrome/browser/ui/webui/settings/people_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -47,6 +47,7 @@
#include "ui/base/layout.h"
using ::testing::_;
+using ::testing::Invoke;
using ::testing::Mock;
using ::testing::Return;
using ::testing::ReturnRef;
@@ -197,8 +198,7 @@ class PeopleHandlerTest : public ChromeRenderViewHostTestHarness {
error_ = GoogleServiceAuthError::AuthErrorNone();
// Sign in the user.
- mock_signin_ = static_cast<SigninManagerBase*>(
- SigninManagerFactory::GetForProfile(profile()));
+ mock_signin_ = SigninManagerFactory::GetForProfile(profile());
std::string username = GetTestUser();
if (!username.empty())
mock_signin_->SetAuthenticatedAccountInfo(username, username);
@@ -213,6 +213,10 @@ class PeopleHandlerTest : public ChromeRenderViewHostTestHarness {
Return(base::Time()));
ON_CALL(*mock_pss_, GetRegisteredDataTypes())
.WillByDefault(Return(syncer::ModelTypeSet()));
+ ON_CALL(*mock_pss_, GetSetupInProgressHandle())
+ .WillByDefault(
+ Invoke(mock_pss_,
+ &ProfileSyncServiceMock::GetSetupInProgressHandleConcrete));
mock_pss_->Initialize();
@@ -332,7 +336,7 @@ TEST_F(PeopleHandlerFirstSigninTest, DisplayBasicLogin) {
EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false));
EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
// Ensure that the user is not signed in before calling |HandleStartSignin()|.
- SigninManager* manager = static_cast<SigninManager*>(mock_signin_);
+ SigninManager* manager = SigninManager::FromSigninManagerBase(mock_signin_);
manager->SignOut(signin_metrics::SIGNOUT_TEST,
signin_metrics::SignoutDelete::IGNORE_METRIC);
base::ListValue list_args;
diff --git a/chromium/chrome/browser/ui/webui/settings/profile_info_handler.cc b/chromium/chrome/browser/ui/webui/settings/profile_info_handler.cc
index 6261f7f9c28..299d6fe9a8d 100644
--- a/chromium/chrome/browser/ui/webui/settings/profile_info_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/profile_info_handler.cc
@@ -9,7 +9,7 @@
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
-#include "chrome/browser/ui/user_manager.h"
+#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/pref_names.h"
#include "ui/base/webui/web_ui_util.h"
@@ -32,9 +32,6 @@ namespace settings {
// static
const char ProfileInfoHandler::kProfileInfoChangedEventName[] =
"profile-info-changed";
-const char
- ProfileInfoHandler::kProfileManagesSupervisedUsersChangedEventName[] =
- "profile-manages-supervised-users-changed";
const char ProfileInfoHandler::kProfileStatsCountReadyEventName[] =
"profile-stats-count-ready";
@@ -63,23 +60,12 @@ void ProfileInfoHandler::RegisterMessages() {
base::Bind(&ProfileInfoHandler::HandleGetProfileStats,
base::Unretained(this)));
#endif
- web_ui()->RegisterMessageCallback(
- "getProfileManagesSupervisedUsers",
- base::Bind(&ProfileInfoHandler::HandleGetProfileManagesSupervisedUsers,
- base::Unretained(this)));
}
void ProfileInfoHandler::OnJavascriptAllowed() {
profile_observer_.Add(
&g_browser_process->profile_manager()->GetProfileAttributesStorage());
- PrefService* prefs = profile_->GetPrefs();
- profile_pref_registrar_.Init(prefs);
- profile_pref_registrar_.Add(
- prefs::kSupervisedUsers,
- base::Bind(&ProfileInfoHandler::PushProfileManagesSupervisedUsersStatus,
- base::Unretained(this)));
-
#if defined(OS_CHROMEOS)
user_manager_observer_.Add(user_manager::UserManager::Get());
#endif
@@ -91,8 +77,6 @@ void ProfileInfoHandler::OnJavascriptDisallowed() {
profile_observer_.Remove(
&g_browser_process->profile_manager()->GetProfileAttributesStorage());
- profile_pref_registrar_.RemoveAll();
-
#if defined(OS_CHROMEOS)
user_manager_observer_.Remove(user_manager::UserManager::Get());
#endif
@@ -147,29 +131,10 @@ void ProfileInfoHandler::PushProfileStatsCount(
}
#endif
-void ProfileInfoHandler::HandleGetProfileManagesSupervisedUsers(
- const base::ListValue* args) {
- AllowJavascript();
-
- CHECK_EQ(1U, args->GetSize());
- const base::Value* callback_id;
- CHECK(args->Get(0, &callback_id));
-
- ResolveJavascriptCallback(*callback_id,
- base::Value(IsProfileManagingSupervisedUsers()));
-}
-
void ProfileInfoHandler::PushProfileInfo() {
FireWebUIListener(kProfileInfoChangedEventName, *GetAccountNameAndIcon());
}
-void ProfileInfoHandler::PushProfileManagesSupervisedUsersStatus() {
- CallJavascriptFunction(
- "cr.webUIListenerCallback",
- base::Value(kProfileManagesSupervisedUsersChangedEventName),
- base::Value(IsProfileManagingSupervisedUsers()));
-}
-
std::unique_ptr<base::DictionaryValue>
ProfileInfoHandler::GetAccountNameAndIcon() const {
std::string name;
@@ -208,8 +173,4 @@ ProfileInfoHandler::GetAccountNameAndIcon() const {
return response;
}
-bool ProfileInfoHandler::IsProfileManagingSupervisedUsers() const {
- return !profile_->GetPrefs()->GetDictionary(prefs::kSupervisedUsers)->empty();
-}
-
} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/profile_info_handler.h b/chromium/chrome/browser/ui/webui/settings/profile_info_handler.h
index 5d8386beace..0d7d986709c 100644
--- a/chromium/chrome/browser/ui/webui/settings/profile_info_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/profile_info_handler.h
@@ -32,7 +32,6 @@ class ProfileInfoHandler : public SettingsPageUIHandler,
public ProfileAttributesStorage::Observer {
public:
static const char kProfileInfoChangedEventName[];
- static const char kProfileManagesSupervisedUsersChangedEventName[];
static const char kProfileStatsCountReadyEventName[];
explicit ProfileInfoHandler(Profile* profile);
@@ -56,14 +55,9 @@ class ProfileInfoHandler : public SettingsPageUIHandler,
private:
FRIEND_TEST_ALL_PREFIXES(ProfileInfoHandlerTest, GetProfileInfo);
FRIEND_TEST_ALL_PREFIXES(ProfileInfoHandlerTest, PushProfileInfo);
- FRIEND_TEST_ALL_PREFIXES(ProfileInfoHandlerTest,
- GetProfileManagesSupervisedUsers);
- FRIEND_TEST_ALL_PREFIXES(ProfileInfoHandlerTest,
- PushProfileManagesSupervisedUsers);
// Callbacks from the page.
void HandleGetProfileInfo(const base::ListValue* args);
- void HandleGetProfileManagesSupervisedUsers(const base::ListValue* args);
void PushProfileInfo();
#if !defined(OS_CHROMEOS)
@@ -74,12 +68,6 @@ class ProfileInfoHandler : public SettingsPageUIHandler,
void PushProfileStatsCount(profiles::ProfileCategoryStats stats);
#endif
- // Pushes whether the current profile manages supervised users to JavaScript.
- void PushProfileManagesSupervisedUsersStatus();
-
- // Returns true if this profile manages supervised users.
- bool IsProfileManagingSupervisedUsers() const;
-
std::unique_ptr<base::DictionaryValue> GetAccountNameAndIcon() const;
// Weak pointer.
@@ -93,9 +81,6 @@ class ProfileInfoHandler : public SettingsPageUIHandler,
ScopedObserver<ProfileAttributesStorage, ProfileInfoHandler>
profile_observer_;
- // Used to listen for changes in the list of managed supervised users.
- PrefChangeRegistrar profile_pref_registrar_;
-
// Used to cancel callbacks when JavaScript becomes disallowed.
base::WeakPtrFactory<ProfileInfoHandler> callback_weak_ptr_factory_;
diff --git a/chromium/chrome/browser/ui/webui/settings/profile_info_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/profile_info_handler_unittest.cc
index 734d9b70fe2..a5229444221 100644
--- a/chromium/chrome/browser/ui/webui/settings/profile_info_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/settings/profile_info_handler_unittest.cc
@@ -151,53 +151,4 @@ TEST_F(ProfileInfoHandlerTest, PushProfileInfo) {
VerifyProfileInfo(data.arg2());
}
-TEST_F(ProfileInfoHandlerTest, GetProfileManagesSupervisedUsers) {
- base::ListValue list_args;
- list_args.AppendString("get-profile-manages-supervised-users-callback-id");
- handler()->HandleGetProfileManagesSupervisedUsers(&list_args);
-
- EXPECT_EQ(1U, web_ui()->call_data().size());
-
- const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
- EXPECT_EQ("cr.webUIResponse", data.function_name());
-
- std::string callback_id;
- ASSERT_TRUE(data.arg1()->GetAsString(&callback_id));
- EXPECT_EQ("get-profile-manages-supervised-users-callback-id", callback_id);
-
- bool success = false;
- ASSERT_TRUE(data.arg2()->GetAsBoolean(&success));
- EXPECT_TRUE(success);
-
- bool has_supervised_users = false;
- ASSERT_TRUE(data.arg3()->GetAsBoolean(&has_supervised_users));
- EXPECT_FALSE(has_supervised_users);
-}
-
-TEST_F(ProfileInfoHandlerTest, PushProfileManagesSupervisedUsers) {
- handler()->AllowJavascript();
-
- // The handler is notified of the change after |update| is destroyed.
- std::unique_ptr<DictionaryPrefUpdate> update(
- new DictionaryPrefUpdate(profile()->GetPrefs(), prefs::kSupervisedUsers));
- base::DictionaryValue* dict = update->Get();
- dict->SetWithoutPathExpansion("supervised-user-id",
- std::make_unique<base::DictionaryValue>());
- update.reset();
-
- EXPECT_EQ(1U, web_ui()->call_data().size());
-
- const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
- EXPECT_EQ("cr.webUIListenerCallback", data.function_name());
-
- std::string event_id;
- ASSERT_TRUE(data.arg1()->GetAsString(&event_id));
- EXPECT_EQ(ProfileInfoHandler::kProfileManagesSupervisedUsersChangedEventName,
- event_id);
-
- bool has_supervised_users = false;
- ASSERT_TRUE(data.arg2()->GetAsBoolean(&has_supervised_users));
- EXPECT_TRUE(has_supervised_users);
-}
-
} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/protocol_handlers_handler.cc b/chromium/chrome/browser/ui/webui/settings/protocol_handlers_handler.cc
index 062d170c0c7..3b1453e6170 100644
--- a/chromium/chrome/browser/ui/webui/settings/protocol_handlers_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/protocol_handlers_handler.cc
@@ -47,9 +47,6 @@ void ProtocolHandlersHandler::RegisterMessages() {
base::Bind(
&ProtocolHandlersHandler::HandleObserveProtocolHandlersEnabledState,
base::Unretained(this)));
- web_ui()->RegisterMessageCallback("clearDefault",
- base::Bind(&ProtocolHandlersHandler::HandleClearDefault,
- base::Unretained(this)));
web_ui()->RegisterMessageCallback("removeHandler",
base::Bind(&ProtocolHandlersHandler::HandleRemoveHandler,
base::Unretained(this)));
@@ -59,9 +56,6 @@ void ProtocolHandlersHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback("setDefault",
base::Bind(&ProtocolHandlersHandler::HandleSetDefault,
base::Unretained(this)));
- web_ui()->RegisterMessageCallback("removeIgnoredHandler",
- base::Bind(&ProtocolHandlersHandler::HandleRemoveIgnoredHandler,
- base::Unretained(this)));
}
ProtocolHandlerRegistry* ProtocolHandlersHandler::GetProtocolHandlerRegistry() {
@@ -70,6 +64,7 @@ ProtocolHandlerRegistry* ProtocolHandlersHandler::GetProtocolHandlerRegistry() {
}
static void GetHandlersAsListValue(
+ const ProtocolHandlerRegistry& registry,
const ProtocolHandlerRegistry::ProtocolHandlerList& handlers,
base::ListValue* handler_list) {
ProtocolHandlerRegistry::ProtocolHandlerList::const_iterator handler;
@@ -79,6 +74,7 @@ static void GetHandlersAsListValue(
handler_value->SetString("protocol", handler->protocol());
handler_value->SetString("spec", handler->url().spec());
handler_value->SetString("host", handler->url().host());
+ handler_value->SetBoolean("is_default", registry.IsDefault(*handler));
handler_list->Append(std::move(handler_value));
}
}
@@ -88,16 +84,9 @@ void ProtocolHandlersHandler::GetHandlersForProtocol(
base::DictionaryValue* handlers_value) {
ProtocolHandlerRegistry* registry = GetProtocolHandlerRegistry();
handlers_value->SetString("protocol", protocol);
- handlers_value->SetInteger("default_handler",
- registry->GetHandlerIndex(protocol));
- handlers_value->SetBoolean(
- "is_default_handler_set_by_user",
- registry->IsRegisteredByUser(registry->GetHandlerFor(protocol)));
- handlers_value->SetBoolean("has_policy_recommendations",
- registry->HasPolicyRegisteredHandler(protocol));
auto handlers_list = std::make_unique<base::ListValue>();
- GetHandlersAsListValue(registry->GetHandlersFor(protocol),
+ GetHandlersAsListValue(*registry, registry->GetHandlersFor(protocol),
handlers_list.get());
handlers_value->Set("handlers", std::move(handlers_list));
}
@@ -106,7 +95,7 @@ void ProtocolHandlersHandler::GetIgnoredHandlers(base::ListValue* handlers) {
ProtocolHandlerRegistry* registry = GetProtocolHandlerRegistry();
ProtocolHandlerRegistry::ProtocolHandlerList ignored_handlers =
registry->GetIgnoredHandlers();
- return GetHandlersAsListValue(ignored_handlers, handlers);
+ return GetHandlersAsListValue(*registry, ignored_handlers, handlers);
}
void ProtocolHandlersHandler::UpdateHandlerList() {
@@ -148,13 +137,8 @@ void ProtocolHandlersHandler::SendHandlersEnabledValue() {
}
void ProtocolHandlersHandler::HandleRemoveHandler(const base::ListValue* args) {
- const base::ListValue* list;
- if (!args->GetList(0, &list)) {
- NOTREACHED();
- return;
- }
-
- ProtocolHandler handler(ParseHandlerFromArgs(list));
+ ProtocolHandler handler(ParseHandlerFromArgs(args));
+ CHECK(!handler.IsEmpty());
GetProtocolHandlerRegistry()->RemoveHandler(handler);
// No need to call UpdateHandlerList() - we should receive a notification
@@ -162,18 +146,6 @@ void ProtocolHandlersHandler::HandleRemoveHandler(const base::ListValue* args) {
// then.
}
-void ProtocolHandlersHandler::HandleRemoveIgnoredHandler(
- const base::ListValue* args) {
- const base::ListValue* list;
- if (!args->GetList(0, &list)) {
- NOTREACHED();
- return;
- }
-
- ProtocolHandler handler(ParseHandlerFromArgs(list));
- GetProtocolHandlerRegistry()->RemoveIgnoredHandler(handler);
-}
-
void ProtocolHandlersHandler::HandleSetHandlersEnabled(
const base::ListValue* args) {
bool enabled = true;
@@ -184,18 +156,8 @@ void ProtocolHandlersHandler::HandleSetHandlersEnabled(
GetProtocolHandlerRegistry()->Disable();
}
-void ProtocolHandlersHandler::HandleClearDefault(const base::ListValue* args) {
- const base::Value* value;
- CHECK(args->Get(0, &value));
- std::string protocol_to_clear;
- CHECK(value->GetAsString(&protocol_to_clear));
- GetProtocolHandlerRegistry()->ClearDefault(protocol_to_clear);
-}
-
void ProtocolHandlersHandler::HandleSetDefault(const base::ListValue* args) {
- const base::ListValue* list;
- CHECK(args->GetList(0, &list));
- const ProtocolHandler& handler(ParseHandlerFromArgs(list));
+ const ProtocolHandler& handler(ParseHandlerFromArgs(args));
CHECK(!handler.IsEmpty());
GetProtocolHandlerRegistry()->OnAcceptRegisterProtocolHandler(handler);
}
diff --git a/chromium/chrome/browser/ui/webui/settings/protocol_handlers_handler.h b/chromium/chrome/browser/ui/webui/settings/protocol_handlers_handler.h
index 269f27f6061..cf5094ad895 100644
--- a/chromium/chrome/browser/ui/webui/settings/protocol_handlers_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/protocol_handlers_handler.h
@@ -63,12 +63,8 @@ class ProtocolHandlersHandler : public SettingsPageUIHandler,
// Called when the user sets a new default handler for a protocol.
void HandleSetDefault(const base::ListValue* args);
- // Called when the user clears the default handler for a protocol.
- // |args| is the string name of the protocol to clear.
- void HandleClearDefault(const base::ListValue* args);
-
// Parses a ProtocolHandler out of the arguments passed back from the view.
- // |args| is a list of [protocol, url, title].
+ // |args| is a list of [protocol, url].
ProtocolHandler ParseHandlerFromArgs(const base::ListValue* args) const;
// Returns a JSON object describing the set of protocol handlers for the
@@ -83,13 +79,9 @@ class ProtocolHandlersHandler : public SettingsPageUIHandler,
void UpdateHandlerList();
// Remove a handler.
- // |args| is a list of [protocol, url, title].
+ // |args| is a list of [protocol, url].
void HandleRemoveHandler(const base::ListValue* args);
- // Remove an ignored handler.
- // |args| is a list of [protocol, url, title].
- void HandleRemoveIgnoredHandler(const base::ListValue* args);
-
ProtocolHandlerRegistry* GetProtocolHandlerRegistry();
content::NotificationRegistrar notification_registrar_;
diff --git a/chromium/chrome/browser/ui/webui/settings/safe_browsing_handler.cc b/chromium/chrome/browser/ui/webui/settings/safe_browsing_handler.cc
index e42d983ce75..f017287acdf 100644
--- a/chromium/chrome/browser/ui/webui/settings/safe_browsing_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/safe_browsing_handler.cc
@@ -47,8 +47,17 @@ void SafeBrowsingHandler::HandleGetSafeBrowsingExtendedReporting(
AllowJavascript();
const base::Value* callback_id;
CHECK(args->Get(0, &callback_id));
- base::Value is_enabled(safe_browsing::IsExtendedReportingEnabled(*prefs_));
- ResolveJavascriptCallback(*callback_id, is_enabled);
+
+ base::DictionaryValue dict;
+ dict.SetBoolean("enabled",
+ safe_browsing::IsExtendedReportingEnabled(*prefs_));
+ // TODO(crbug.com/813107): SBEROIA policy is being deprecated, revisit this
+ // after it is removed.
+ dict.SetBoolean("managed",
+ !safe_browsing::IsExtendedReportingOptInAllowed(*prefs_) ||
+ safe_browsing::IsExtendedReportingPolicyManaged(*prefs_));
+
+ ResolveJavascriptCallback(*callback_id, dict);
}
void SafeBrowsingHandler::HandleSetSafeBrowsingExtendedReportingEnabled(
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc b/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
index 49cd2fb9f4e..b4c04a907d9 100644
--- a/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
@@ -27,7 +27,7 @@
#include "chrome/common/pref_names.h"
#include "components/browsing_data/core/history_notice_utils.h"
#include "components/browsing_data/core/pref_names.h"
-#include "components/feature_engagement/features.h"
+#include "components/feature_engagement/buildflags.h"
#include "components/prefs/pref_member.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/signin_manager.h"
@@ -50,26 +50,23 @@ namespace {
const int kMaxTimesHistoryNoticeShown = 1;
// TODO(msramek): Get the list of deletion preferences from the JS side.
-const char* kCounterPrefs[] = {
+const char* kCounterPrefsAdvanced[] = {
browsing_data::prefs::kDeleteBrowsingHistory,
browsing_data::prefs::kDeleteCache,
+ browsing_data::prefs::kDeleteCookies,
browsing_data::prefs::kDeleteDownloadHistory,
browsing_data::prefs::kDeleteFormData,
browsing_data::prefs::kDeleteHostedAppsData,
browsing_data::prefs::kDeleteMediaLicenses,
browsing_data::prefs::kDeletePasswords,
+ browsing_data::prefs::kDeleteSiteSettings,
};
-// Additional counters for the tabbed ui.
+// Additional counters for the basic tab of CBD.
const char* kCounterPrefsBasic[] = {
browsing_data::prefs::kDeleteCacheBasic,
};
-const char* kCounterPrefsAdvanced[] = {
- browsing_data::prefs::kDeleteCookies,
- browsing_data::prefs::kDeleteSiteSettings,
-};
-
const char kRegisterableDomainField[] = "registerableDomain";
const char kReasonBitField[] = "reasonBitfield";
const char kExampleOriginField[] = "exampleOrigin";
@@ -87,13 +84,8 @@ ClearBrowsingDataHandler::ClearBrowsingDataHandler(content::WebUI* webui)
: profile_(Profile::FromWebUI(webui)),
sync_service_(ProfileSyncServiceFactory::GetForProfile(profile_)),
sync_service_observer_(this),
- show_history_footer_(false),
show_history_deletion_dialog_(false),
- weak_ptr_factory_(this) {
- if (base::FeatureList::IsEnabled(features::kTabsInCbd)) {
- browsing_data::MigratePreferencesToBasic(profile_->GetPrefs());
- }
-}
+ weak_ptr_factory_(this) {}
ClearBrowsingDataHandler::~ClearBrowsingDataHandler() {
}
@@ -120,32 +112,25 @@ void ClearBrowsingDataHandler::OnJavascriptAllowed() {
sync_service_observer_.Add(sync_service_);
DCHECK(counters_.empty());
- for (const std::string& pref : kCounterPrefs) {
+ for (const std::string& pref : kCounterPrefsBasic) {
AddCounter(BrowsingDataCounterFactory::GetForProfileAndPref(profile_, pref),
- browsing_data::ClearBrowsingDataTab::ADVANCED);
+ browsing_data::ClearBrowsingDataTab::BASIC);
}
- if (base::FeatureList::IsEnabled(features::kTabsInCbd)) {
- for (const std::string& pref : kCounterPrefsBasic) {
- AddCounter(
- BrowsingDataCounterFactory::GetForProfileAndPref(profile_, pref),
- browsing_data::ClearBrowsingDataTab::BASIC);
- }
- for (const std::string& pref : kCounterPrefsAdvanced) {
- AddCounter(
- BrowsingDataCounterFactory::GetForProfileAndPref(profile_, pref),
- browsing_data::ClearBrowsingDataTab::ADVANCED);
- }
- PrefService* prefs = profile_->GetPrefs();
- period_ = std::make_unique<IntegerPrefMember>();
- period_->Init(browsing_data::prefs::kDeleteTimePeriod, prefs,
- base::Bind(&ClearBrowsingDataHandler::HandleTimePeriodChanged,
- base::Unretained(this)));
- periodBasic_ = std::make_unique<IntegerPrefMember>();
- periodBasic_->Init(
- browsing_data::prefs::kDeleteTimePeriodBasic, prefs,
- base::Bind(&ClearBrowsingDataHandler::HandleTimePeriodChanged,
- base::Unretained(this)));
+ for (const std::string& pref : kCounterPrefsAdvanced) {
+ AddCounter(BrowsingDataCounterFactory::GetForProfileAndPref(profile_, pref),
+ browsing_data::ClearBrowsingDataTab::ADVANCED);
}
+ PrefService* prefs = profile_->GetPrefs();
+ period_ = std::make_unique<IntegerPrefMember>();
+ period_->Init(
+ browsing_data::prefs::kDeleteTimePeriod, prefs,
+ base::BindRepeating(&ClearBrowsingDataHandler::HandleTimePeriodChanged,
+ base::Unretained(this)));
+ periodBasic_ = std::make_unique<IntegerPrefMember>();
+ periodBasic_->Init(
+ browsing_data::prefs::kDeleteTimePeriodBasic, prefs,
+ base::BindRepeating(&ClearBrowsingDataHandler::HandleTimePeriodChanged,
+ base::Unretained(this)));
}
void ClearBrowsingDataHandler::OnJavascriptDisallowed() {
@@ -239,8 +224,10 @@ void ClearBrowsingDataHandler::HandleClearBrowsingData(
case BrowsingDataType::BOOKMARKS:
// Only implemented on Android.
NOTREACHED();
+ break;
case BrowsingDataType::NUM_TYPES:
NOTREACHED();
+ break;
}
}
@@ -455,25 +442,15 @@ void ClearBrowsingDataHandler::OnStateChanged(syncer::SyncService* sync) {
void ClearBrowsingDataHandler::UpdateSyncState() {
auto* signin_manager = SigninManagerFactory::GetForProfile(profile_);
- // TODO(dullweber): Remove "show_history_footer" attribute when the new UI
- // is launched as it doesn't have this footer anymore. Instead the
- // myactivity.google.com link is shown when a user is signed in or syncing.
CallJavascriptFunction(
"cr.webUIListenerCallback", base::Value("update-sync-state"),
base::Value(signin_manager && signin_manager->IsAuthenticated()),
base::Value(sync_service_ && sync_service_->IsSyncActive() &&
sync_service_->GetActiveDataTypes().Has(
- syncer::HISTORY_DELETE_DIRECTIVES)),
- base::Value(show_history_footer_));
+ syncer::HISTORY_DELETE_DIRECTIVES)));
}
void ClearBrowsingDataHandler::RefreshHistoryNotice() {
- browsing_data::ShouldShowNoticeAboutOtherFormsOfBrowsingHistory(
- sync_service_,
- WebHistoryServiceFactory::GetForProfile(profile_),
- base::Bind(&ClearBrowsingDataHandler::UpdateHistoryNotice,
- weak_ptr_factory_.GetWeakPtr()));
-
// If the dialog with history notice has been shown less than
// |kMaxTimesHistoryNoticeShown| times, we might have to show it when the
// user deletes history. Find out if the conditions are met.
@@ -490,15 +467,6 @@ void ClearBrowsingDataHandler::RefreshHistoryNotice() {
}
}
-void ClearBrowsingDataHandler::UpdateHistoryNotice(bool show) {
- show_history_footer_ = show;
- UpdateSyncState();
-
- UMA_HISTOGRAM_BOOLEAN(
- "History.ClearBrowsingData.HistoryNoticeShownInFooterWhenUpdated",
- show_history_footer_);
-}
-
void ClearBrowsingDataHandler::UpdateHistoryDeletionDialog(bool show) {
// This is used by OnClearingTaskFinished (when the deletion finishes).
show_history_deletion_dialog_ = show;
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h b/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h
index 1cbd4ea698a..f135b0172af 100644
--- a/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h
@@ -80,10 +80,6 @@ class ClearBrowsingDataHandler : public SettingsPageUIHandler,
// in user's account.
void RefreshHistoryNotice();
- // Called as an asynchronous response to |RefreshHistoryNotice()|. Shows or
- // hides the footer about other forms of history stored in user's account.
- void UpdateHistoryNotice(bool show);
-
// Called as an asynchronous response to |RefreshHistoryNotice()|. Enables or
// disables the dialog about other forms of history stored in user's account
// that is shown when the history deletion is finished.
@@ -111,11 +107,6 @@ class ClearBrowsingDataHandler : public SettingsPageUIHandler,
ScopedObserver<browser_sync::ProfileSyncService, syncer::SyncServiceObserver>
sync_service_observer_;
- // Whether the sentence about other forms of history stored in user's account
- // should be displayed in the footer. This value is retrieved asynchronously,
- // so we cache it here.
- bool show_history_footer_;
-
// Whether we should show a dialog informing the user about other forms of
// history stored in their account after the history deletion is finished.
bool show_history_deletion_dialog_;
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 0a4b4442f20..c413d8569ab 100644
--- a/chromium/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chromium/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -9,6 +9,7 @@
#include <string>
#include <utility>
+#include "base/barrier_closure.h"
#include "base/bind.h"
#include "base/i18n/number_formatting.h"
#include "base/macros.h"
@@ -45,7 +46,7 @@
#include "extensions/common/permissions/api_permission.h"
#include "extensions/common/permissions/permissions_data.h"
#include "storage/browser/quota/quota_manager.h"
-#include "third_party/WebKit/common/quota/quota_types.mojom.h"
+#include "third_party/WebKit/public/mojom/quota/quota_types.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/text/bytes_formatting.h"
@@ -92,7 +93,7 @@ void AddExceptionsGrantedByHostedApps(content::BrowserContext* context,
!(*extension)->permissions_data()->HasAPIPermission(permission))
continue;
- extensions::URLPatternSet web_extent = (*extension)->web_extent();
+ const extensions::URLPatternSet& web_extent = (*extension)->web_extent();
// Add patterns from web extent.
for (extensions::URLPatternSet::const_iterator pattern = web_extent.begin();
pattern != web_extent.end(); ++pattern) {
@@ -159,6 +160,9 @@ void SiteSettingsHandler::RegisterMessages() {
base::Bind(&SiteSettingsHandler::HandleSetOriginPermissions,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
+ "clearFlashPref", base::Bind(&SiteSettingsHandler::HandleClearFlashPref,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
"resetCategoryPermissionForPattern",
base::Bind(&SiteSettingsHandler::HandleResetCategoryPermissionForPattern,
base::Unretained(this)));
@@ -248,14 +252,18 @@ void SiteSettingsHandler::OnGetUsageInfo(
}
}
-void SiteSettingsHandler::OnUsageInfoCleared(
- blink::mojom::QuotaStatusCode code) {
+void SiteSettingsHandler::OnStorageCleared(base::OnceClosure callback,
+ blink::mojom::QuotaStatusCode code) {
if (code == blink::mojom::QuotaStatusCode::kOk) {
- CallJavascriptFunction("settings.WebsiteUsagePrivateApi.onUsageCleared",
- base::Value(clearing_origin_));
+ std::move(callback).Run();
}
}
+void SiteSettingsHandler::OnUsageCleared() {
+ CallJavascriptFunction("settings.WebsiteUsagePrivateApi.onUsageCleared",
+ base::Value(clearing_origin_));
+}
+
#if defined(OS_CHROMEOS)
void SiteSettingsHandler::OnPrefEnableDrmChanged() {
CallJavascriptFunction("cr.webUIListenerCallback",
@@ -355,19 +363,25 @@ void SiteSettingsHandler::HandleClearUsage(
if (url.is_valid()) {
clearing_origin_ = origin;
+ // Call OnUsageCleared when StorageInfoFetcher::ClearStorage and
+ // BrowsingDataLocalStorageHelper::DeleteOrigin are done.
+ base::RepeatingClosure barrier = base::BarrierClosure(
+ 2, base::BindOnce(&SiteSettingsHandler::OnUsageCleared,
+ base::Unretained(this)));
+
// Start by clearing the storage data asynchronously.
scoped_refptr<StorageInfoFetcher> storage_info_fetcher
= new StorageInfoFetcher(profile_);
storage_info_fetcher->ClearStorage(
url.host(),
static_cast<blink::mojom::StorageType>(static_cast<int>(storage_type)),
- base::Bind(&SiteSettingsHandler::OnUsageInfoCleared,
- base::Unretained(this)));
+ base::BindRepeating(&SiteSettingsHandler::OnStorageCleared,
+ base::Unretained(this), barrier));
// Also clear the *local* storage data.
scoped_refptr<BrowsingDataLocalStorageHelper> local_storage_helper =
new BrowsingDataLocalStorageHelper(profile_);
- local_storage_helper->DeleteOrigin(url);
+ local_storage_helper->DeleteOrigin(url, barrier);
}
}
@@ -621,6 +635,19 @@ void SiteSettingsHandler::HandleSetOriginPermissions(
}
}
+void SiteSettingsHandler::HandleClearFlashPref(const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ std::string origin_string;
+ CHECK(args->GetString(0, &origin_string));
+
+ HostContentSettingsMap* map =
+ HostContentSettingsMapFactory::GetForProfile(profile_);
+ const GURL origin(origin_string);
+ map->SetWebsiteSettingDefaultScope(origin, origin,
+ CONTENT_SETTINGS_TYPE_PLUGINS_DATA,
+ std::string(), nullptr);
+}
+
void SiteSettingsHandler::HandleResetCategoryPermissionForPattern(
const base::ListValue* args) {
CHECK_EQ(4U, args->GetSize());
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 63fa495c22e..01be8c4849d 100644
--- a/chromium/chrome/browser/ui/webui/settings/site_settings_handler.h
+++ b/chromium/chrome/browser/ui/webui/settings/site_settings_handler.h
@@ -16,7 +16,8 @@
#include "content/public/browser/host_zoom_map.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
-#include "third_party/WebKit/common/quota/quota_types.mojom.h"
+#include "ppapi/features/features.h"
+#include "third_party/WebKit/public/mojom/quota/quota_types.mojom.h"
class HostContentSettingsMap;
class Profile;
@@ -46,7 +47,9 @@ class SiteSettingsHandler : public SettingsPageUIHandler,
// Usage info.
void OnGetUsageInfo(const storage::UsageInfoEntries& entries);
- void OnUsageInfoCleared(blink::mojom::QuotaStatusCode code);
+ void OnStorageCleared(base::OnceClosure callback,
+ blink::mojom::QuotaStatusCode code);
+ void OnUsageCleared();
#if defined(OS_CHROMEOS)
// Alert the Javascript that the |kEnableDRM| pref has changed.
@@ -70,6 +73,10 @@ class SiteSettingsHandler : public SettingsPageUIHandler,
private:
friend class SiteSettingsHandlerTest;
friend class SiteSettingsHandlerInfobarTest;
+#if BUILDFLAG(ENABLE_PLUGINS)
+ FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest,
+ ChangingFlashSettingForSiteIsRemembered);
+#endif
FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, DefaultSettingSource);
FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ExceptionHelpers);
FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ExtensionDisplayName);
@@ -110,6 +117,10 @@ class SiteSettingsHandler : public SettingsPageUIHandler,
void HandleGetOriginPermissions(const base::ListValue* args);
void HandleSetOriginPermissions(const base::ListValue* args);
+ // Clears the Flash data setting used to remember if the user has changed the
+ // Flash permission for an origin.
+ void HandleClearFlashPref(const base::ListValue* args);
+
// Handles setting and resetting an origin permission.
void HandleResetCategoryPermissionForPattern(const base::ListValue* args);
void HandleSetCategoryPermissionForPattern(const base::ListValue* args);
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 434947cf2a6..7e40e5e5081 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
@@ -31,6 +31,7 @@
#include "content/public/test/test_web_ui.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/extension_builder.h"
+#include "ppapi/features/features.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_CHROMEOS)
@@ -38,6 +39,10 @@
#include "components/user_manager/scoped_user_manager.h"
#endif
+#if BUILDFLAG(ENABLE_PLUGINS)
+#include "chrome/browser/plugins/chrome_plugin_service_filter.h"
+#endif
+
namespace {
constexpr char kCallbackId[] = "test-callback-id";
@@ -45,7 +50,41 @@ constexpr char kSetting[] = "setting";
constexpr char kSource[] = "source";
constexpr char kExtensionName[] = "Test Extension";
-}
+#if BUILDFLAG(ENABLE_PLUGINS)
+// Waits until a change is observed in content settings.
+class FlashContentSettingsChangeWaiter : public content_settings::Observer {
+ public:
+ explicit FlashContentSettingsChangeWaiter(Profile* profile)
+ : profile_(profile) {
+ HostContentSettingsMapFactory::GetForProfile(profile)->AddObserver(this);
+ }
+ ~FlashContentSettingsChangeWaiter() override {
+ HostContentSettingsMapFactory::GetForProfile(profile_)->RemoveObserver(
+ this);
+ }
+
+ // content_settings::Observer:
+ void OnContentSettingChanged(const ContentSettingsPattern& primary_pattern,
+ const ContentSettingsPattern& secondary_pattern,
+ ContentSettingsType content_type,
+ std::string resource_identifier) override {
+ if (content_type == CONTENT_SETTINGS_TYPE_PLUGINS)
+ Proceed();
+ }
+
+ void Wait() { run_loop_.Run(); }
+
+ private:
+ void Proceed() { run_loop_.Quit(); }
+
+ Profile* profile_;
+ base::RunLoop run_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(FlashContentSettingsChangeWaiter);
+};
+#endif
+
+} // namespace
namespace settings {
@@ -90,6 +129,8 @@ class SiteSettingsHandlerTest : public testing::Test {
CONTENT_SETTINGS_TYPE_NOTIFICATIONS)),
kCookies(site_settings::ContentSettingsTypeToGroupName(
CONTENT_SETTINGS_TYPE_COOKIES)),
+ kFlash(site_settings::ContentSettingsTypeToGroupName(
+ CONTENT_SETTINGS_TYPE_PLUGINS)),
handler_(&profile_) {
#if defined(OS_CHROMEOS)
user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>(
@@ -277,6 +318,7 @@ class SiteSettingsHandlerTest : public testing::Test {
// Content setting group name for the relevant ContentSettingsType.
const std::string kNotifications;
const std::string kCookies;
+ const std::string kFlash;
private:
content::TestBrowserThreadBundle thread_bundle_;
@@ -476,6 +518,56 @@ TEST_F(SiteSettingsHandlerTest, GetAndSetOriginPermissions) {
site_settings::SiteSettingSource::kDefault, 4U);
}
+#if BUILDFLAG(ENABLE_PLUGINS)
+TEST_F(SiteSettingsHandlerTest, ChangingFlashSettingForSiteIsRemembered) {
+ ChromePluginServiceFilter::GetInstance()->RegisterResourceContext(
+ profile(), profile()->GetResourceContext());
+ FlashContentSettingsChangeWaiter waiter(profile());
+
+ const std::string origin_with_port("https://www.example.com:443");
+ // The display name won't show the port if it's default for that scheme.
+ const std::string origin("https://www.example.com");
+ base::ListValue get_args;
+ get_args.AppendString(kCallbackId);
+ get_args.AppendString(origin_with_port);
+ const GURL url(origin_with_port);
+
+ HostContentSettingsMap* map =
+ HostContentSettingsMapFactory::GetForProfile(profile());
+ // Make sure the site being tested doesn't already have this marker set.
+ EXPECT_EQ(nullptr,
+ map->GetWebsiteSetting(url, url, CONTENT_SETTINGS_TYPE_PLUGINS_DATA,
+ std::string(), nullptr));
+
+ // Change the Flash setting.
+ base::ListValue set_args;
+ set_args.AppendString(origin_with_port);
+ {
+ auto category_list = std::make_unique<base::ListValue>();
+ category_list->AppendString(kFlash);
+ set_args.Append(std::move(category_list));
+ }
+ set_args.AppendString(
+ content_settings::ContentSettingToString(CONTENT_SETTING_BLOCK));
+ handler()->HandleSetOriginPermissions(&set_args);
+ EXPECT_EQ(1U, web_ui()->call_data().size());
+ waiter.Wait();
+
+ // Check that this site has now been marked for displaying Flash always, then
+ // clear it and check this works.
+ EXPECT_NE(nullptr,
+ map->GetWebsiteSetting(url, url, CONTENT_SETTINGS_TYPE_PLUGINS_DATA,
+ std::string(), nullptr));
+ base::ListValue clear_args;
+ clear_args.AppendString(origin_with_port);
+ handler()->HandleSetOriginPermissions(&set_args);
+ handler()->HandleClearFlashPref(&clear_args);
+ EXPECT_EQ(nullptr,
+ map->GetWebsiteSetting(url, url, CONTENT_SETTINGS_TYPE_PLUGINS_DATA,
+ std::string(), nullptr));
+}
+#endif
+
TEST_F(SiteSettingsHandlerTest, GetAndSetForInvalidURLs) {
const std::string origin("arbitrary string");
EXPECT_FALSE(GURL(origin).is_valid());
diff --git a/chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.cc b/chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.cc
index a1962a59807..707c0a05630 100644
--- a/chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.cc
+++ b/chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.cc
@@ -5,37 +5,28 @@
#include "chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h"
#include "base/bind.h"
+#include "base/location.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/policy/cloud/user_policy_signin_service.h"
#include "chrome/browser/policy/cloud/user_policy_signin_service_factory.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/profiles/profile_window.h"
#include "chrome/browser/signin/account_tracker_service_factory.h"
#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/browser_tabstrip.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/startup/startup_types.h"
-#include "chrome/browser/ui/tab_dialogs.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"
+#include "chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_delegate_impl.h"
#include "chrome/browser/ui/webui/signin/signin_utils_desktop.h"
-#include "chrome/common/url_constants.h"
#include "components/browser_sync/profile_sync_service.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/account_info.h"
#include "components/signin/core/browser/account_tracker_service.h"
@@ -48,117 +39,32 @@
namespace {
-// UMA histogram for tracking what users do when presented with the signin
-// screen.
-// Hence,
-// (a) existing enumerated constants should never be deleted or reordered, and
-// (b) new constants should only be appended at the end of the enumeration.
-//
-// Keep this in sync with SigninChoice in histograms.xml.
-enum SigninChoice {
- SIGNIN_CHOICE_CANCEL = 0,
- SIGNIN_CHOICE_CONTINUE = 1,
- SIGNIN_CHOICE_NEW_PROFILE = 2,
- // SIGNIN_CHOICE_SIZE should always be last - this is a count of the number
- // of items in this enum.
- SIGNIN_CHOICE_SIZE,
-};
-
-void SetUserChoiceHistogram(SigninChoice choice) {
- UMA_HISTOGRAM_ENUMERATION("Enterprise.UserSigninChoice", choice,
- SIGNIN_CHOICE_SIZE);
-}
-
AccountInfo GetAccountInfo(Profile* profile, const std::string& account_id) {
return AccountTrackerServiceFactory::GetForProfile(profile)->GetAccountInfo(
account_id);
}
-// If the |browser| argument is non-null, returns the pointer directly.
-// Otherwise creates a new browser for the given profile on the given desktop,
-// adds an empty tab and makes sure the browser is visible.
-Browser* EnsureBrowser(Browser* browser, Profile* profile) {
- if (!browser) {
- // The user just created a new profile or has closed the browser that
- // we used previously. Grab the most recently active browser or else
- // create a new one.
- browser = chrome::FindLastActiveWithProfile(profile);
- if (!browser) {
- browser = new Browser(Browser::CreateParams(profile, true));
- chrome::AddTabAt(browser, GURL(), -1, true);
- }
- browser->window()->Show();
- }
- return browser;
-}
-
-void StartNewSigninInNewProfile(Profile* new_profile,
- const std::string& username) {
- profiles::FindOrCreateNewWindowForProfile(
- new_profile, chrome::startup::IS_PROCESS_STARTUP,
- chrome::startup::IS_FIRST_RUN, false);
- Browser* browser = chrome::FindTabbedBrowser(new_profile, false);
- browser->signin_view_controller()->ShowDiceSigninTab(
- profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN, browser,
- signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE, username);
-}
-
} // namespace
-DiceTurnSyncOnHelper::SigninDialogDelegate::SigninDialogDelegate(
- base::WeakPtr<DiceTurnSyncOnHelper> sync_starter)
- : sync_starter_(sync_starter) {}
-
-DiceTurnSyncOnHelper::SigninDialogDelegate::~SigninDialogDelegate() {}
-
-void DiceTurnSyncOnHelper::SigninDialogDelegate::OnCancelSignin() {
- SetUserChoiceHistogram(SIGNIN_CHOICE_CANCEL);
- base::RecordAction(
- base::UserMetricsAction("Signin_EnterpriseAccountPrompt_Cancel"));
-
- if (sync_starter_)
- sync_starter_->AbortAndDelete();
-}
-
-void DiceTurnSyncOnHelper::SigninDialogDelegate::OnContinueSignin() {
- SetUserChoiceHistogram(SIGNIN_CHOICE_CONTINUE);
- base::RecordAction(
- base::UserMetricsAction("Signin_EnterpriseAccountPrompt_ImportData"));
-
- if (sync_starter_)
- sync_starter_->LoadPolicyWithCachedCredentials();
-}
-
-void DiceTurnSyncOnHelper::SigninDialogDelegate::OnSigninWithNewProfile() {
- SetUserChoiceHistogram(SIGNIN_CHOICE_NEW_PROFILE);
- base::RecordAction(
- base::UserMetricsAction("Signin_EnterpriseAccountPrompt_DontImportData"));
-
- if (sync_starter_)
- sync_starter_->CreateNewSignedInProfile();
-}
-
DiceTurnSyncOnHelper::DiceTurnSyncOnHelper(
Profile* profile,
- Browser* browser,
signin_metrics::AccessPoint signin_access_point,
signin_metrics::Reason signin_reason,
const std::string& account_id,
- SigninAbortedMode signin_aborted_mode)
- : profile_(profile),
- browser_(browser),
+ SigninAbortedMode signin_aborted_mode,
+ std::unique_ptr<Delegate> delegate)
+ : delegate_(std::move(delegate)),
+ profile_(profile),
signin_manager_(SigninManagerFactory::GetForProfile(profile)),
token_service_(ProfileOAuth2TokenServiceFactory::GetForProfile(profile)),
signin_access_point_(signin_access_point),
signin_reason_(signin_reason),
signin_aborted_mode_(signin_aborted_mode),
account_info_(GetAccountInfo(profile, account_id)),
- scoped_browser_list_observer_(this),
- scoped_login_ui_service_observer_(this),
weak_pointer_factory_(this) {
+ DCHECK(delegate_);
DCHECK(signin::IsDicePrepareMigrationEnabled());
DCHECK(profile_);
- DCHECK(browser_);
DCHECK(!account_info_.gaia.empty());
DCHECK(!account_info_.email.empty());
// Should not start syncing if the profile is already authenticated
@@ -168,7 +74,10 @@ DiceTurnSyncOnHelper::DiceTurnSyncOnHelper(
DCHECK(!signin_util::IsForceSigninEnabled());
if (HasCanOfferSigninError()) {
- AbortAndDelete();
+ // Do not self-destruct synchronously in the constructor.
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&DiceTurnSyncOnHelper::AbortAndDelete,
+ base::Unretained(this)));
return;
}
@@ -181,22 +90,33 @@ DiceTurnSyncOnHelper::DiceTurnSyncOnHelper(
// last authenticated account of the current profile, then Chrome will show a
// confirmation dialog before starting sync.
// TODO(skym): Warn for high risk upgrade scenario (https://crbug.com/572754).
- content::WebContents* web_contents =
- browser_->tab_strip_model()->GetActiveWebContents();
std::string last_email =
profile_->GetPrefs()->GetString(prefs::kGoogleServicesLastUsername);
- SigninEmailConfirmationDialog::AskForConfirmation(
- web_contents, profile_, last_email, account_info_.email,
- base::Bind(&DiceTurnSyncOnHelper::ConfirmEmailAction,
- weak_pointer_factory_.GetWeakPtr()));
+ delegate_->ShowMergeSyncDataConfirmation(
+ last_email, account_info_.email,
+ base::BindOnce(&DiceTurnSyncOnHelper::OnMergeAccountConfirmation,
+ weak_pointer_factory_.GetWeakPtr()));
}
+DiceTurnSyncOnHelper::DiceTurnSyncOnHelper(
+ Profile* profile,
+ Browser* browser,
+ signin_metrics::AccessPoint signin_access_point,
+ signin_metrics::Reason signin_reason,
+ const std::string& account_id,
+ SigninAbortedMode signin_aborted_mode)
+ : DiceTurnSyncOnHelper(
+ profile,
+ signin_access_point,
+ signin_reason,
+ account_id,
+ signin_aborted_mode,
+ std::make_unique<DiceTurnSyncOnHelperDelegateImpl>(browser)) {}
+
DiceTurnSyncOnHelper::~DiceTurnSyncOnHelper() {
- DCHECK(!scoped_login_ui_service_observer_.IsObservingSources());
}
bool DiceTurnSyncOnHelper::HasCanOfferSigninError() {
- DCHECK(browser_);
std::string error_msg;
bool can_offer =
CanOfferSignin(profile_, CAN_OFFER_SIGNIN_FOR_ALL_ACCOUNTS,
@@ -205,36 +125,62 @@ bool DiceTurnSyncOnHelper::HasCanOfferSigninError() {
return false;
// Display the error message
- LoginUIServiceFactory::GetForProfile(profile_)->DisplayLoginResult(
- browser_, base::UTF8ToUTF16(error_msg),
- base::UTF8ToUTF16(account_info_.email));
+ delegate_->ShowLoginError(account_info_.email, error_msg);
return true;
}
-void DiceTurnSyncOnHelper::ConfirmEmailAction(
- SigninEmailConfirmationDialog::Action action) {
- switch (action) {
- case SigninEmailConfirmationDialog::CREATE_NEW_USER:
+void DiceTurnSyncOnHelper::OnMergeAccountConfirmation(SigninChoice choice) {
+ switch (choice) {
+ case SIGNIN_CHOICE_NEW_PROFILE:
base::RecordAction(
base::UserMetricsAction("Signin_ImportDataPrompt_DontImport"));
TurnSyncOnWithProfileMode(ProfileMode::NEW_PROFILE);
break;
- case SigninEmailConfirmationDialog::START_SYNC:
+ case SIGNIN_CHOICE_CONTINUE:
base::RecordAction(
base::UserMetricsAction("Signin_ImportDataPrompt_ImportData"));
TurnSyncOnWithProfileMode(ProfileMode::CURRENT_PROFILE);
break;
- case SigninEmailConfirmationDialog::CLOSE:
+ case SIGNIN_CHOICE_CANCEL:
base::RecordAction(
base::UserMetricsAction("Signin_ImportDataPrompt_Cancel"));
AbortAndDelete();
break;
+ case SIGNIN_CHOICE_SIZE:
+ NOTREACHED();
+ AbortAndDelete();
+ break;
}
}
-void DiceTurnSyncOnHelper::TurnSyncOnWithProfileMode(ProfileMode profile_mode) {
- scoped_browser_list_observer_.Add(BrowserList::GetInstance());
+void DiceTurnSyncOnHelper::OnEnterpriseAccountConfirmation(
+ SigninChoice choice) {
+ UMA_HISTOGRAM_ENUMERATION("Enterprise.UserSigninChoice", choice,
+ DiceTurnSyncOnHelper::SIGNIN_CHOICE_SIZE);
+ switch (choice) {
+ case SIGNIN_CHOICE_CANCEL:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_EnterpriseAccountPrompt_Cancel"));
+ AbortAndDelete();
+ break;
+ case SIGNIN_CHOICE_CONTINUE:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_EnterpriseAccountPrompt_ImportData"));
+ LoadPolicyWithCachedCredentials();
+ break;
+ case SIGNIN_CHOICE_NEW_PROFILE:
+ base::RecordAction(base::UserMetricsAction(
+ "Signin_EnterpriseAccountPrompt_DontImportData"));
+ CreateNewSignedInProfile();
+ break;
+ case SIGNIN_CHOICE_SIZE:
+ NOTREACHED();
+ AbortAndDelete();
+ break;
+ }
+}
+void DiceTurnSyncOnHelper::TurnSyncOnWithProfileMode(ProfileMode profile_mode) {
// Make sure the syncing is requested, otherwise the SigninManager
// will not be able to complete successfully.
syncer::SyncPrefs sync_prefs(profile_->GetPrefs());
@@ -282,20 +228,10 @@ void DiceTurnSyncOnHelper::OnRegisteredForPolicy(const std::string& dm_token,
client_id_ = client_id;
// Allow user to create a new profile before continuing with sign-in.
- browser_ = EnsureBrowser(browser_, profile_);
- content::WebContents* web_contents =
- browser_->tab_strip_model()->GetActiveWebContents();
- if (!web_contents) {
- AbortAndDelete();
- return;
- }
-
- base::RecordAction(
- base::UserMetricsAction("Signin_Show_EnterpriseAccountPrompt"));
- TabDialogs::FromWebContents(web_contents)
- ->ShowProfileSigninConfirmation(browser_, profile_, account_info_.email,
- std::make_unique<SigninDialogDelegate>(
- weak_pointer_factory_.GetWeakPtr()));
+ delegate_->ShowEnterpriseAccountConfirmation(
+ account_info_.email,
+ base::BindOnce(&DiceTurnSyncOnHelper::OnEnterpriseAccountConfirmation,
+ weak_pointer_factory_.GetWeakPtr()));
}
void DiceTurnSyncOnHelper::LoadPolicyWithCachedCredentials() {
@@ -349,7 +285,7 @@ void DiceTurnSyncOnHelper::CompleteInitForNewProfile(
break;
case Profile::CREATE_STATUS_INITIALIZED:
// The user needs to sign in to the new profile in order to enable sync.
- StartNewSigninInNewProfile(new_profile, account_info_.email);
+ delegate_->ShowSigninPageInNewProfile(new_profile, account_info_.email);
AbortAndDelete();
break;
case Profile::CREATE_STATUS_REMOTE_FAIL:
@@ -383,9 +319,14 @@ void DiceTurnSyncOnHelper::SigninAndShowSyncConfirmationUI() {
// progress.
// TODO(https://crbug.com/811211): Remove this handle.
sync_blocker_ = sync_service->GetSetupInProgressHandle();
- if (SyncStartupTracker::GetSyncServiceState(profile_) ==
- SyncStartupTracker::SYNC_STARTUP_PENDING) {
- // Wait until sync is initialized so that the confirmation UI can be
+ bool is_enterprise_user =
+ !policy::BrowserPolicyConnector::IsNonEnterpriseUser(
+ account_info_.email);
+ if (is_enterprise_user &&
+ SyncStartupTracker::GetSyncServiceState(profile_) ==
+ SyncStartupTracker::SYNC_STARTUP_PENDING) {
+ // For enterprise users it is important to wait until sync is initialized
+ // so that the confirmation UI can be
// aware of startup errors. This is needed to make sure that the sync
// confirmation dialog is shown only after the sync service had a chance
// to check whether sync was disabled by admin.
@@ -411,19 +352,16 @@ void DiceTurnSyncOnHelper::SyncStartupFailed() {
}
void DiceTurnSyncOnHelper::ShowSyncConfirmationUI() {
- scoped_login_ui_service_observer_.Add(
- LoginUIServiceFactory::GetForProfile(profile_));
- browser_ = EnsureBrowser(browser_, profile_);
- browser_->signin_view_controller()->ShowModalSyncConfirmationDialog(browser_);
+ delegate_->ShowSyncConfirmation(
+ base::BindOnce(&DiceTurnSyncOnHelper::FinishSyncSetupAndDelete,
+ weak_pointer_factory_.GetWeakPtr()));
}
-void DiceTurnSyncOnHelper::OnSyncConfirmationUIClosed(
+void DiceTurnSyncOnHelper::FinishSyncSetupAndDelete(
LoginUIService::SyncConfirmationUIClosedResult result) {
- scoped_login_ui_service_observer_.RemoveAll();
switch (result) {
case LoginUIService::CONFIGURE_SYNC_FIRST:
- browser_ = EnsureBrowser(browser_, profile_);
- chrome::ShowSettingsSubPage(browser_, chrome::kSyncSetupSubPage);
+ delegate_->ShowSyncSettings();
break;
case LoginUIService::SYNC_WITH_DEFAULT_SETTINGS: {
browser_sync::ProfileSyncService* sync_service = GetProfileSyncService();
@@ -441,11 +379,6 @@ void DiceTurnSyncOnHelper::OnSyncConfirmationUIClosed(
delete this;
}
-void DiceTurnSyncOnHelper::OnBrowserRemoved(Browser* browser) {
- if (browser == browser_)
- browser_ = nullptr;
-}
-
void DiceTurnSyncOnHelper::AbortAndDelete() {
if (signin_aborted_mode_ == SigninAbortedMode::REMOVE_ACCOUNT) {
// Revoke the token, and the AccountReconcilor and/or the Gaia server will
diff --git a/chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h b/chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h
index 85ef34e80f9..6641331e0fc 100644
--- a/chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h
+++ b/chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h
@@ -5,21 +5,19 @@
#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_DICE_TURN_SYNC_ON_HELPER_H_
#define CHROME_BROWSER_UI_WEBUI_SIGNIN_DICE_TURN_SYNC_ON_HELPER_H_
+#include <memory>
#include <string>
+#include "base/callback_forward.h"
+#include "base/macros.h"
#include "base/memory/weak_ptr.h"
-#include "base/scoped_observer.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/sync_startup_tracker.h"
-#include "chrome/browser/ui/browser_list_observer.h"
-#include "chrome/browser/ui/sync/profile_signin_confirmation_helper.h"
#include "chrome/browser/ui/webui/signin/login_ui_service.h"
-#include "chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h"
#include "components/signin/core/browser/account_info.h"
#include "components/signin/core/browser/signin_metrics.h"
class Browser;
-class BrowserList;
class ProfileOAuth2TokenService;
class SigninManager;
@@ -33,9 +31,7 @@ class SyncSetupInProgressHandle;
// Handles details of signing the user in with SigninManager and turning on
// sync for an account that is already present in the token service.
-class DiceTurnSyncOnHelper : public BrowserListObserver,
- public LoginUIService::Observer,
- public SyncStartupTracker::Observer {
+class DiceTurnSyncOnHelper : public SyncStartupTracker::Observer {
public:
// Behavior when the signin is aborted (by an error or cancelled by the user).
enum class SigninAbortedMode {
@@ -45,9 +41,67 @@ class DiceTurnSyncOnHelper : public BrowserListObserver,
KEEP_ACCOUNT
};
+ // User choice when signing in.
+ // Used for UMA histograms, Hence, constants should never be deleted or
+ // reordered, and new constants should only be appended at the end.
+ // Keep this in sync with SigninChoice in histograms.xml.
+ enum SigninChoice {
+ SIGNIN_CHOICE_CANCEL = 0, // Signin is cancelled.
+ SIGNIN_CHOICE_CONTINUE = 1, // Signin continues in the current profile.
+ SIGNIN_CHOICE_NEW_PROFILE = 2, // Signin continues in a new profile.
+ // SIGNIN_CHOICE_SIZE should always be last.
+ SIGNIN_CHOICE_SIZE,
+ };
+
+ using SigninChoiceCallback = base::OnceCallback<void(SigninChoice)>;
+
+ // Delegate implementing the UI prompts.
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Shows a login error to the user.
+ virtual void ShowLoginError(const std::string& email,
+ const std::string& error_message) = 0;
+
+ // Shows a confirmation dialog when the user was previously signed in with a
+ // different account in the same profile. |callback| must be called.
+ virtual void ShowMergeSyncDataConfirmation(
+ const std::string& previous_email,
+ const std::string& new_email,
+ SigninChoiceCallback callback) = 0;
+
+ // Shows a confirmation dialog when the user is signing in a managed
+ // account. |callback| must be called.
+ virtual void ShowEnterpriseAccountConfirmation(
+ const std::string& email,
+ SigninChoiceCallback callback) = 0;
+
+ // Shows a sync confirmation screen offering to open the Sync settings.
+ // |callback| must be called.
+ virtual void ShowSyncConfirmation(
+ base::OnceCallback<void(LoginUIService::SyncConfirmationUIClosedResult)>
+ callback) = 0;
+
+ // Opens the Sync settings page.
+ virtual void ShowSyncSettings() = 0;
+
+ // Opens the signin page in a new profile.
+ virtual void ShowSigninPageInNewProfile(Profile* new_profile,
+ const std::string& username) = 0;
+ };
+
// Create a helper that turns sync on for an account that is already present
// in the token service.
DiceTurnSyncOnHelper(Profile* profile,
+ signin_metrics::AccessPoint signin_access_point,
+ signin_metrics::Reason signin_reason,
+ const std::string& account_id,
+ SigninAbortedMode signin_aborted_mode,
+ std::unique_ptr<Delegate> delegate);
+
+ // Convenience constructor using the default delegate.
+ DiceTurnSyncOnHelper(Profile* profile,
Browser* browser,
signin_metrics::AccessPoint signin_access_point,
signin_metrics::Reason signin_reason,
@@ -70,21 +124,6 @@ class DiceTurnSyncOnHelper : public BrowserListObserver,
NEW_PROFILE
};
- // User input handler for the signin confirmation dialog.
- class SigninDialogDelegate : public ui::ProfileSigninConfirmationDelegate {
- public:
- explicit SigninDialogDelegate(
- base::WeakPtr<DiceTurnSyncOnHelper> sync_starter);
- ~SigninDialogDelegate() override;
- void OnCancelSignin() override;
- void OnContinueSignin() override;
- void OnSigninWithNewProfile() override;
-
- private:
- base::WeakPtr<DiceTurnSyncOnHelper> sync_starter_;
- };
- friend class SigninDialogDelegate;
-
// DiceTurnSyncOnHelper deletes itself.
~DiceTurnSyncOnHelper() override;
@@ -92,8 +131,11 @@ class DiceTurnSyncOnHelper : public BrowserListObserver,
// and false otherwise.
bool HasCanOfferSigninError();
- // Callback used with ConfirmEmailDialogDelegate.
- void ConfirmEmailAction(SigninEmailConfirmationDialog::Action action);
+ // Used as callback for ShowMergeSyncDataConfirmation().
+ void OnMergeAccountConfirmation(SigninChoice choice);
+
+ // Used as callback for ShowEnterpriseAccountConfirmation().
+ void OnEnterpriseAccountConfirmation(SigninChoice choice);
// Turns sync on with the current profile or a new profile.
void TurnSyncOnWithProfileMode(ProfileMode profile_mode);
@@ -132,18 +174,16 @@ class DiceTurnSyncOnHelper : public BrowserListObserver,
// confirmation dialog will be updated accordingly.
void ShowSyncConfirmationUI();
- // LoginUIService::Observer override. Deletes this object.
- void OnSyncConfirmationUIClosed(
- LoginUIService::SyncConfirmationUIClosedResult result) override;
-
- // BrowserListObserver override.
- void OnBrowserRemoved(Browser* browser) override;
+ // Handles the user input from the sync confirmation UI and deletes this
+ // object.
+ void FinishSyncSetupAndDelete(
+ LoginUIService::SyncConfirmationUIClosedResult result);
// Aborts the flow and deletes this object.
void AbortAndDelete();
+ std::unique_ptr<Delegate> delegate_;
Profile* profile_;
- Browser* browser_;
SigninManager* signin_manager_;
ProfileOAuth2TokenService* token_service_;
const signin_metrics::AccessPoint signin_access_point_;
@@ -163,10 +203,6 @@ class DiceTurnSyncOnHelper : public BrowserListObserver,
std::string dm_token_;
std::string client_id_;
- ScopedObserver<BrowserList, BrowserListObserver>
- scoped_browser_list_observer_;
- ScopedObserver<LoginUIService, LoginUIService::Observer>
- scoped_login_ui_service_observer_;
std::unique_ptr<SyncStartupTracker> sync_startup_tracker_;
base::WeakPtrFactory<DiceTurnSyncOnHelper> weak_pointer_factory_;
diff --git a/chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_delegate_impl.cc b/chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_delegate_impl.cc
new file mode 100644
index 00000000000..c89424c5a6c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_delegate_impl.cc
@@ -0,0 +1,183 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_delegate_impl.h"
+
+#include "base/logging.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/startup/startup_types.h"
+#include "chrome/browser/ui/tab_dialogs.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.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/common/url_constants.h"
+
+namespace {
+
+// If the |browser| argument is non-null, returns the pointer directly.
+// Otherwise creates a new browser for the profile, adds an empty tab and makes
+// sure the browser is visible.
+Browser* EnsureBrowser(Browser* browser, Profile* profile) {
+ if (!browser) {
+ // The user just created a new profile or has closed the browser that
+ // we used previously. Grab the most recently active browser or else
+ // create a new one.
+ browser = chrome::FindLastActiveWithProfile(profile);
+ if (!browser) {
+ browser = new Browser(Browser::CreateParams(profile, true));
+ chrome::AddTabAt(browser, GURL(), -1, true);
+ }
+ browser->window()->Show();
+ }
+ return browser;
+}
+
+// Converts SigninEmailConfirmationDialog::Action to
+// DiceTurnSyncOnHelper::SigninChoice and invokes |callback| on it.
+void OnEmailConfirmation(DiceTurnSyncOnHelper::SigninChoiceCallback callback,
+ SigninEmailConfirmationDialog::Action action) {
+ DCHECK(callback) << "This function should be called only once.";
+ switch (action) {
+ case SigninEmailConfirmationDialog::START_SYNC:
+ std::move(callback).Run(DiceTurnSyncOnHelper::SIGNIN_CHOICE_CONTINUE);
+ return;
+ case SigninEmailConfirmationDialog::CREATE_NEW_USER:
+ std::move(callback).Run(DiceTurnSyncOnHelper::SIGNIN_CHOICE_NEW_PROFILE);
+ return;
+ case SigninEmailConfirmationDialog::CLOSE:
+ std::move(callback).Run(DiceTurnSyncOnHelper::SIGNIN_CHOICE_CANCEL);
+ return;
+ }
+ NOTREACHED();
+}
+
+} // namespace
+
+DiceTurnSyncOnHelperDelegateImpl::SigninDialogDelegate::SigninDialogDelegate(
+ DiceTurnSyncOnHelper::SigninChoiceCallback callback)
+ : callback_(std::move(callback)) {
+ DCHECK(callback_);
+}
+
+DiceTurnSyncOnHelperDelegateImpl::SigninDialogDelegate::
+ ~SigninDialogDelegate() = default;
+
+void DiceTurnSyncOnHelperDelegateImpl::SigninDialogDelegate::OnCancelSignin() {
+ DCHECK(callback_);
+ std::move(callback_).Run(DiceTurnSyncOnHelper::SIGNIN_CHOICE_CANCEL);
+}
+
+void DiceTurnSyncOnHelperDelegateImpl::SigninDialogDelegate::
+ OnContinueSignin() {
+ DCHECK(callback_);
+ std::move(callback_).Run(DiceTurnSyncOnHelper::SIGNIN_CHOICE_CONTINUE);
+}
+
+void DiceTurnSyncOnHelperDelegateImpl::SigninDialogDelegate::
+ OnSigninWithNewProfile() {
+ DCHECK(callback_);
+ std::move(callback_).Run(DiceTurnSyncOnHelper::SIGNIN_CHOICE_NEW_PROFILE);
+}
+
+DiceTurnSyncOnHelperDelegateImpl::DiceTurnSyncOnHelperDelegateImpl(
+ Browser* browser)
+ : browser_(browser),
+ profile_(browser_->profile()),
+ scoped_browser_list_observer_(this),
+ scoped_login_ui_service_observer_(this) {
+ DCHECK(browser);
+ DCHECK(profile_);
+ scoped_browser_list_observer_.Add(BrowserList::GetInstance());
+}
+
+DiceTurnSyncOnHelperDelegateImpl::~DiceTurnSyncOnHelperDelegateImpl() {}
+
+void DiceTurnSyncOnHelperDelegateImpl::ShowLoginError(
+ const std::string& email,
+ const std::string& error_message) {
+ LoginUIServiceFactory::GetForProfile(profile_)->DisplayLoginResult(
+ browser_, base::UTF8ToUTF16(error_message), base::UTF8ToUTF16(email));
+}
+
+void DiceTurnSyncOnHelperDelegateImpl::ShowEnterpriseAccountConfirmation(
+ const std::string& email,
+ DiceTurnSyncOnHelper::SigninChoiceCallback callback) {
+ DCHECK(callback);
+ browser_ = EnsureBrowser(browser_, profile_);
+ content::WebContents* web_contents =
+ browser_->tab_strip_model()->GetActiveWebContents();
+ if (!web_contents) {
+ std::move(callback).Run(DiceTurnSyncOnHelper::SIGNIN_CHOICE_CANCEL);
+ return;
+ }
+
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Show_EnterpriseAccountPrompt"));
+ TabDialogs::FromWebContents(web_contents)
+ ->ShowProfileSigninConfirmation(
+ browser_, profile_, email,
+ std::make_unique<SigninDialogDelegate>(std::move(callback)));
+}
+
+void DiceTurnSyncOnHelperDelegateImpl::ShowSyncConfirmation(
+ base::OnceCallback<void(LoginUIService::SyncConfirmationUIClosedResult)>
+ callback) {
+ DCHECK(callback);
+ sync_confirmation_callback_ = std::move(callback);
+ scoped_login_ui_service_observer_.Add(
+ LoginUIServiceFactory::GetForProfile(profile_));
+ browser_ = EnsureBrowser(browser_, profile_);
+ browser_->signin_view_controller()->ShowModalSyncConfirmationDialog(browser_);
+}
+
+void DiceTurnSyncOnHelperDelegateImpl::ShowMergeSyncDataConfirmation(
+ const std::string& previous_email,
+ const std::string& new_email,
+ DiceTurnSyncOnHelper::SigninChoiceCallback callback) {
+ DCHECK(callback);
+ content::WebContents* web_contents =
+ browser_->tab_strip_model()->GetActiveWebContents();
+ // TODO(droger): Replace Bind with BindOnce once the
+ // SigninEmailConfirmationDialog supports it.
+ SigninEmailConfirmationDialog::AskForConfirmation(
+ web_contents, profile_, previous_email, new_email,
+ base::Bind(&OnEmailConfirmation, base::Passed(std::move(callback))));
+}
+
+void DiceTurnSyncOnHelperDelegateImpl::ShowSyncSettings() {
+ browser_ = EnsureBrowser(browser_, profile_);
+ chrome::ShowSettingsSubPage(browser_, chrome::kSyncSetupSubPage);
+}
+
+void DiceTurnSyncOnHelperDelegateImpl::ShowSigninPageInNewProfile(
+ Profile* new_profile,
+ const std::string& username) {
+ profiles::FindOrCreateNewWindowForProfile(
+ new_profile, chrome::startup::IS_PROCESS_STARTUP,
+ chrome::startup::IS_FIRST_RUN, false);
+ Browser* browser = chrome::FindTabbedBrowser(new_profile, false);
+ browser->signin_view_controller()->ShowDiceSigninTab(
+ profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN, browser,
+ signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE, username);
+}
+
+void DiceTurnSyncOnHelperDelegateImpl::OnSyncConfirmationUIClosed(
+ LoginUIService::SyncConfirmationUIClosedResult result) {
+ DCHECK(sync_confirmation_callback_);
+ std::move(sync_confirmation_callback_).Run(result);
+}
+
+void DiceTurnSyncOnHelperDelegateImpl::OnBrowserRemoved(Browser* browser) {
+ if (browser == browser_)
+ browser_ = nullptr;
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_delegate_impl.h b/chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_delegate_impl.h
new file mode 100644
index 00000000000..58f4fb8bf79
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_delegate_impl.h
@@ -0,0 +1,81 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_DICE_TURN_SYNC_ON_HELPER_DELEGATE_IMPL_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_DICE_TURN_SYNC_ON_HELPER_DELEGATE_IMPL_H_
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/ui/browser_list_observer.h"
+#include "chrome/browser/ui/sync/profile_signin_confirmation_helper.h"
+#include "chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h"
+#include "chrome/browser/ui/webui/signin/login_ui_service.h"
+
+class Browser;
+class BrowserList;
+class Profile;
+
+// Default implementation for DiceTurnSyncOnHelper::Delegate.
+class DiceTurnSyncOnHelperDelegateImpl : public DiceTurnSyncOnHelper::Delegate,
+ public BrowserListObserver,
+ public LoginUIService::Observer {
+ public:
+ explicit DiceTurnSyncOnHelperDelegateImpl(Browser* browser);
+ ~DiceTurnSyncOnHelperDelegateImpl() override;
+
+ private:
+ // User input handler for the signin confirmation dialog.
+ class SigninDialogDelegate : public ui::ProfileSigninConfirmationDelegate {
+ public:
+ explicit SigninDialogDelegate(
+ DiceTurnSyncOnHelper::SigninChoiceCallback callback);
+ ~SigninDialogDelegate() override;
+ void OnCancelSignin() override;
+ void OnContinueSignin() override;
+ void OnSigninWithNewProfile() override;
+
+ private:
+ DiceTurnSyncOnHelper::SigninChoiceCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(SigninDialogDelegate);
+ };
+
+ // DiceTurnSyncOnHelper::Delegate:
+ void ShowLoginError(const std::string& email,
+ const std::string& error_message) override;
+ void ShowMergeSyncDataConfirmation(
+ const std::string& previous_email,
+ const std::string& new_email,
+ DiceTurnSyncOnHelper::SigninChoiceCallback callback) override;
+ void ShowEnterpriseAccountConfirmation(
+ const std::string& email,
+ DiceTurnSyncOnHelper::SigninChoiceCallback callback) override;
+ void ShowSyncConfirmation(
+ base::OnceCallback<void(LoginUIService::SyncConfirmationUIClosedResult)>
+ callback) override;
+ void ShowSyncSettings() override;
+ void ShowSigninPageInNewProfile(Profile* new_profile,
+ const std::string& username) override;
+
+ // LoginUIService::Observer:
+ void OnSyncConfirmationUIClosed(
+ LoginUIService::SyncConfirmationUIClosedResult result) override;
+
+ // BrowserListObserver:
+ void OnBrowserRemoved(Browser* browser) override;
+
+ Browser* browser_;
+ Profile* profile_;
+ base::OnceCallback<void(LoginUIService::SyncConfirmationUIClosedResult)>
+ sync_confirmation_callback_;
+ ScopedObserver<BrowserList, BrowserListObserver>
+ scoped_browser_list_observer_;
+ ScopedObserver<LoginUIService, LoginUIService::Observer>
+ scoped_login_ui_service_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(DiceTurnSyncOnHelperDelegateImpl);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_DICE_TURN_SYNC_ON_HELPER_DELEGATE_IMPL_H_
diff --git a/chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_unittest.cc b/chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_unittest.cc
new file mode 100644
index 00000000000..ec518a4c526
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_unittest.cc
@@ -0,0 +1,715 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/policy/cloud/user_policy_signin_service.h"
+#include "chrome/browser/policy/cloud/user_policy_signin_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/signin/account_tracker_service_factory.h"
+#include "chrome/browser/signin/chrome_signin_client_factory.h"
+#include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h"
+#include "chrome/browser/signin/fake_signin_manager_builder.h"
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/test_signin_client_builder.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/sync/profile_sync_test_util.h"
+#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/browser_sync/profile_sync_service_mock.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "components/signin/core/browser/scoped_account_consistency.h"
+#include "components/signin/core/browser/signin_metrics.h"
+#include "components/signin/core/browser/signin_pref_names.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AtLeast;
+using ::testing::Return;
+
+class DiceTurnSyncOnHelperTest;
+
+namespace {
+
+const char kEmail[] = "foo@gmail.com";
+const char kGaiaID[] = "foo_gaia_id";
+const char kPreviousEmail[] = "notme@bar.com";
+const char kEnterpriseEmail[] = "enterprise@managed.com";
+const char kEnterpriseGaiaID[] = "enterprise_gaia_id";
+
+const signin_metrics::AccessPoint kAccessPoint =
+ signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER;
+const signin_metrics::Reason kSigninReason =
+ signin_metrics::Reason::REASON_REAUTHENTICATION;
+
+// Dummy delegate forwarding all the calls the test fixture.
+// Owned by the DiceTurnOnSyncHelper.
+class TestDiceTurnSyncOnHelperDelegate : public DiceTurnSyncOnHelper::Delegate {
+ public:
+ explicit TestDiceTurnSyncOnHelperDelegate(
+ DiceTurnSyncOnHelperTest* test_fixture);
+ ~TestDiceTurnSyncOnHelperDelegate() override;
+
+ private:
+ // DiceTurnSyncOnHelper::Delegate:
+ void ShowLoginError(const std::string& email,
+ const std::string& error_message) override;
+ void ShowMergeSyncDataConfirmation(
+ const std::string& previous_email,
+ const std::string& new_email,
+ DiceTurnSyncOnHelper::SigninChoiceCallback callback) override;
+ void ShowEnterpriseAccountConfirmation(
+ const std::string& email,
+ DiceTurnSyncOnHelper::SigninChoiceCallback callback) override;
+ void ShowSyncConfirmation(
+ base::OnceCallback<void(LoginUIService::SyncConfirmationUIClosedResult)>
+ callback) override;
+ void ShowSyncSettings() override;
+ void ShowSigninPageInNewProfile(Profile* new_profile,
+ const std::string& username) override;
+
+ DiceTurnSyncOnHelperTest* test_fixture_;
+};
+
+// Simple ProfileManager creating testing profiles.
+class UnittestProfileManager : public ProfileManagerWithoutInit {
+ public:
+ explicit UnittestProfileManager(const base::FilePath& user_data_dir)
+ : ProfileManagerWithoutInit(user_data_dir) {}
+
+ protected:
+ Profile* CreateProfileHelper(const base::FilePath& file_path) override {
+ if (!base::PathExists(file_path) && !base::CreateDirectory(file_path))
+ return nullptr;
+ return new TestingProfile(file_path, nullptr);
+ }
+
+ Profile* CreateProfileAsyncHelper(const base::FilePath& path,
+ Delegate* delegate) override {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(base::IgnoreResult(&base::CreateDirectory), path));
+ return new TestingProfile(path, this);
+ }
+};
+
+// Fake user policy signin service immediately invoking the callbacks.
+class FakeUserPolicySigninService : public policy::UserPolicySigninService {
+ public:
+ // Static method to use with BrowserContextKeyedServiceFactory.
+ static std::unique_ptr<KeyedService> Build(content::BrowserContext* context) {
+ Profile* profile = Profile::FromBrowserContext(context);
+ return std::make_unique<FakeUserPolicySigninService>(
+ profile, SigninManagerFactory::GetForProfile(profile),
+ ProfileOAuth2TokenServiceFactory::GetForProfile(profile));
+ }
+
+ FakeUserPolicySigninService(Profile* profile,
+ SigninManager* signin_manager,
+ ProfileOAuth2TokenService* oauth2_token_service)
+ : UserPolicySigninService(profile,
+ nullptr,
+ nullptr,
+ nullptr,
+ signin_manager,
+ nullptr,
+ oauth2_token_service) {}
+
+ void set_dm_token(const std::string& dm_token) { dm_token_ = dm_token; }
+ void set_client_id(const std::string& client_id) { client_id_ = client_id; }
+ void set_account(const std::string& account_id, const std::string& email) {
+ account_id_ = account_id;
+ email_ = email;
+ }
+
+ // policy::UserPolicySigninService:
+ void RegisterForPolicyWithAccountId(
+ const std::string& username,
+ const std::string& account_id,
+ const PolicyRegistrationCallback& callback) override {
+ EXPECT_EQ(email_, username);
+ EXPECT_EQ(account_id_, account_id);
+ callback.Run(dm_token_, client_id_);
+ }
+
+ // policy::UserPolicySigninServiceBase:
+ void FetchPolicyForSignedInUser(
+ const std::string& username,
+ const std::string& dm_token,
+ const std::string& client_id,
+ scoped_refptr<net::URLRequestContextGetter> profile_request_context,
+ const PolicyFetchCallback& callback) override {
+ callback.Run(true);
+ }
+
+ private:
+ std::string dm_token_;
+ std::string client_id_;
+ std::string account_id_;
+ std::string email_;
+};
+
+} // namespace
+
+class DiceTurnSyncOnHelperTest : public testing::Test {
+ public:
+ DiceTurnSyncOnHelperTest()
+ : local_state_(TestingBrowserProcess::GetGlobal()) {
+ EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
+ TestingBrowserProcess::GetGlobal()->SetProfileManager(
+ new UnittestProfileManager(temp_dir_.GetPath()));
+
+ TestingProfile::Builder profile_builder;
+ profile_builder.AddTestingFactory(
+ ProfileOAuth2TokenServiceFactory::GetInstance(),
+ BuildFakeProfileOAuth2TokenService);
+ profile_builder.AddTestingFactory(SigninManagerFactory::GetInstance(),
+ BuildFakeSigninManagerBase);
+ profile_builder.AddTestingFactory(ChromeSigninClientFactory::GetInstance(),
+ signin::BuildTestSigninClient);
+ profile_builder.AddTestingFactory(ProfileSyncServiceFactory::GetInstance(),
+ &BuildMockProfileSyncService);
+ profile_builder.AddTestingFactory(
+ policy::UserPolicySigninServiceFactory::GetInstance(),
+ &FakeUserPolicySigninService::Build);
+ profile_ = profile_builder.Build();
+ account_tracker_service_ =
+ AccountTrackerServiceFactory::GetForProfile(profile());
+ account_id_ = account_tracker_service_->SeedAccountInfo(kGaiaID, kEmail);
+ user_policy_signin_service_ = static_cast<FakeUserPolicySigninService*>(
+ policy::UserPolicySigninServiceFactory::GetForProfile(profile()));
+ user_policy_signin_service_->set_account(account_id_, kEmail);
+ token_service_ = ProfileOAuth2TokenServiceFactory::GetForProfile(profile());
+ token_service_->UpdateCredentials(account_id_, "refresh_token");
+ signin_manager_ = SigninManagerFactory::GetForProfile(profile());
+ EXPECT_TRUE(token_service_->RefreshTokenIsAvailable(account_id_));
+ }
+
+ ~DiceTurnSyncOnHelperTest() override {
+ DCHECK(delegate_destroyed_);
+ // Destroy extra profiles.
+ TestingBrowserProcess::GetGlobal()->SetProfileManager(nullptr);
+ base::RunLoop().RunUntilIdle();
+ }
+
+ // Basic accessors.
+ Profile* profile() { return profile_.get(); }
+ ProfileOAuth2TokenService* token_service() { return token_service_; }
+ SigninManager* signin_manager() { return signin_manager_; }
+ const std::string& account_id() { return account_id_; }
+ FakeUserPolicySigninService* user_policy_signin_service() {
+ return user_policy_signin_service_;
+ }
+
+ // Gets the ProfileSyncServiceMock.
+ browser_sync::ProfileSyncServiceMock* GetProfileSyncServiceMock() {
+ return static_cast<browser_sync::ProfileSyncServiceMock*>(
+ ProfileSyncServiceFactory::GetForProfile(profile()));
+ }
+
+ DiceTurnSyncOnHelper* CreateDiceTurnOnSyncHelper(
+ DiceTurnSyncOnHelper::SigninAbortedMode mode) {
+ return new DiceTurnSyncOnHelper(
+ profile(), kAccessPoint, kSigninReason, account_id_, mode,
+ std::make_unique<TestDiceTurnSyncOnHelperDelegate>(this));
+ }
+
+ void UseEnterpriseAccount() {
+ account_id_ = account_tracker_service_->SeedAccountInfo(kEnterpriseGaiaID,
+ kEnterpriseEmail);
+ user_policy_signin_service_->set_account(account_id_, kEnterpriseEmail);
+ token_service_->UpdateCredentials(account_id_, "enterprise_refresh_token");
+ }
+
+ void SetExpectationsForSyncStartupCompleted() {
+ browser_sync::ProfileSyncServiceMock* sync_service_mock =
+ GetProfileSyncServiceMock();
+ EXPECT_CALL(*sync_service_mock, GetSetupInProgressHandle()).Times(1);
+ EXPECT_CALL(*sync_service_mock, CanSyncStart())
+ .Times(AtLeast(0))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*sync_service_mock, IsEngineInitialized())
+ .Times(AtLeast(0))
+ .WillRepeatedly(Return(true));
+ }
+
+ void SetExpectationsForSyncStartupPending() {
+ browser_sync::ProfileSyncServiceMock* sync_service_mock =
+ GetProfileSyncServiceMock();
+ EXPECT_CALL(*sync_service_mock, GetSetupInProgressHandle()).Times(1);
+ EXPECT_CALL(*sync_service_mock, CanSyncStart())
+ .Times(AtLeast(0))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*sync_service_mock, IsEngineInitialized())
+ .Times(AtLeast(0))
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*sync_service_mock, waiting_for_auth())
+ .Times(AtLeast(0))
+ .WillRepeatedly(Return(true));
+ }
+
+ void CheckDelegateCalls() {
+ EXPECT_EQ(expected_login_error_email_, login_error_email_);
+ EXPECT_EQ(expected_login_error_message_, login_error_message_);
+ EXPECT_EQ(expected_merge_data_previous_email_, merge_data_previous_email_);
+ EXPECT_EQ(expected_merge_data_new_email_, merge_data_new_email_);
+ EXPECT_EQ(expected_enterprise_confirmation_email_,
+ enterprise_confirmation_email_);
+ EXPECT_EQ(expected_new_profile_username_, new_profile_username_);
+ EXPECT_EQ(expected_sync_confirmation_shown_, sync_confirmation_shown_);
+ EXPECT_EQ(expected_sync_settings_shown_, sync_settings_shown_);
+ }
+
+ // Functions called by the DiceTurnSyncOnHelper::Delegate:
+ void OnShowLoginError(const std::string& email,
+ const std::string& error_message) {
+ EXPECT_FALSE(sync_confirmation_shown_);
+ EXPECT_FALSE(email.empty());
+ EXPECT_TRUE(login_error_email_.empty())
+ << "Login error should be shown only once.";
+ login_error_email_ = email;
+ login_error_message_ = error_message; // May be empty.
+ }
+
+ void OnShowMergeSyncDataConfirmation(
+ const std::string& previous_email,
+ const std::string& new_email,
+ DiceTurnSyncOnHelper::SigninChoiceCallback callback) {
+ EXPECT_FALSE(sync_confirmation_shown_);
+ EXPECT_FALSE(previous_email.empty());
+ EXPECT_FALSE(new_email.empty());
+ EXPECT_TRUE(merge_data_previous_email_.empty())
+ << "Merge data confirmation should be shown only once";
+ EXPECT_TRUE(merge_data_new_email_.empty())
+ << "Merge data confirmation should be shown only once";
+ merge_data_previous_email_ = previous_email;
+ merge_data_new_email_ = new_email;
+ std::move(callback).Run(merge_data_choice_);
+ }
+
+ void OnShowEnterpriseAccountConfirmation(
+ const std::string& email,
+ DiceTurnSyncOnHelper::SigninChoiceCallback callback) {
+ EXPECT_FALSE(sync_confirmation_shown_);
+ EXPECT_FALSE(email.empty());
+ EXPECT_TRUE(enterprise_confirmation_email_.empty())
+ << "Enterprise confirmation should be shown only once.";
+ enterprise_confirmation_email_ = email;
+ std::move(callback).Run(enterprise_choice_);
+ }
+
+ void OnShowSyncConfirmation(
+ base::OnceCallback<void(LoginUIService::SyncConfirmationUIClosedResult)>
+ callback) {
+ EXPECT_FALSE(sync_confirmation_shown_)
+ << "Sync confirmation should be shown only once.";
+ sync_confirmation_shown_ = true;
+ std::move(callback).Run(sync_confirmation_result_);
+ }
+
+ void OnShowSyncSettings() {
+ EXPECT_TRUE(sync_confirmation_shown_)
+ << "Must show sync confirmation first";
+ EXPECT_FALSE(sync_settings_shown_);
+ sync_settings_shown_ = true;
+ }
+
+ void OnShowSigninPageInNewProfile(Profile* new_profile,
+ const std::string& username) {
+ EXPECT_TRUE(new_profile);
+ EXPECT_NE(profile(), new_profile)
+ << "new_profile should not be the existing profile";
+ EXPECT_FALSE(username.empty());
+ EXPECT_TRUE(new_profile_username_.empty())
+ << "Signin page should be shown only once";
+ new_profile_username_ = username;
+ }
+
+ void OnDelegateDestroyed() { delegate_destroyed_ = true; }
+
+ protected:
+ // Delegate behavior.
+ DiceTurnSyncOnHelper::SigninChoice merge_data_choice_ =
+ DiceTurnSyncOnHelper::SIGNIN_CHOICE_CANCEL;
+ DiceTurnSyncOnHelper::SigninChoice enterprise_choice_ =
+ DiceTurnSyncOnHelper::SIGNIN_CHOICE_CANCEL;
+ LoginUIService::SyncConfirmationUIClosedResult sync_confirmation_result_ =
+ LoginUIService::SyncConfirmationUIClosedResult::ABORT_SIGNIN;
+
+ // Expected delegate calls.
+ std::string expected_login_error_email_;
+ std::string expected_login_error_message_;
+ std::string expected_enterprise_confirmation_email_;
+ std::string expected_merge_data_previous_email_;
+ std::string expected_merge_data_new_email_;
+ std::string expected_new_profile_username_;
+ bool expected_sync_confirmation_shown_ = false;
+ bool expected_sync_settings_shown_ = false;
+
+ private:
+ signin::ScopedAccountConsistencyDicePrepareMigration scoped_dice;
+ content::TestBrowserThreadBundle thread_bundle_;
+ base::ScopedTempDir temp_dir_;
+ ScopedTestingLocalState local_state_;
+ std::string account_id_;
+ std::unique_ptr<TestingProfile> profile_;
+ AccountTrackerService* account_tracker_service_ = nullptr;
+ ProfileOAuth2TokenService* token_service_ = nullptr;
+ SigninManager* signin_manager_ = nullptr;
+ FakeUserPolicySigninService* user_policy_signin_service_ = nullptr;
+
+ // State of the delegate calls.
+ bool delegate_destroyed_ = false;
+ std::string login_error_email_;
+ std::string login_error_message_;
+ std::string enterprise_confirmation_email_;
+ std::string merge_data_previous_email_;
+ std::string merge_data_new_email_;
+ std::string new_profile_username_;
+ bool sync_confirmation_shown_ = false;
+ bool sync_settings_shown_ = false;
+};
+
+// TestDiceTurnSyncOnHelperDelegate implementation.
+
+TestDiceTurnSyncOnHelperDelegate::TestDiceTurnSyncOnHelperDelegate(
+ DiceTurnSyncOnHelperTest* test_fixture)
+ : test_fixture_(test_fixture) {}
+
+TestDiceTurnSyncOnHelperDelegate::~TestDiceTurnSyncOnHelperDelegate() {
+ test_fixture_->OnDelegateDestroyed();
+}
+
+void TestDiceTurnSyncOnHelperDelegate::ShowLoginError(
+ const std::string& email,
+ const std::string& error_message) {
+ test_fixture_->OnShowLoginError(email, error_message);
+}
+
+void TestDiceTurnSyncOnHelperDelegate::ShowMergeSyncDataConfirmation(
+ const std::string& previous_email,
+ const std::string& new_email,
+ DiceTurnSyncOnHelper::SigninChoiceCallback callback) {
+ test_fixture_->OnShowMergeSyncDataConfirmation(previous_email, new_email,
+ std::move(callback));
+}
+
+void TestDiceTurnSyncOnHelperDelegate::ShowEnterpriseAccountConfirmation(
+ const std::string& email,
+ DiceTurnSyncOnHelper::SigninChoiceCallback callback) {
+ test_fixture_->OnShowEnterpriseAccountConfirmation(email,
+ std::move(callback));
+}
+
+void TestDiceTurnSyncOnHelperDelegate::ShowSyncConfirmation(
+ base::OnceCallback<void(LoginUIService::SyncConfirmationUIClosedResult)>
+ callback) {
+ test_fixture_->OnShowSyncConfirmation(std::move(callback));
+}
+
+void TestDiceTurnSyncOnHelperDelegate::ShowSyncSettings() {
+ test_fixture_->OnShowSyncSettings();
+}
+
+void TestDiceTurnSyncOnHelperDelegate::ShowSigninPageInNewProfile(
+ Profile* new_profile,
+ const std::string& username) {
+ test_fixture_->OnShowSigninPageInNewProfile(new_profile, username);
+}
+
+// Tests that the login error is displayed and that the account is kept.
+TEST_F(DiceTurnSyncOnHelperTest, CanOfferSigninErrorKeepAccount) {
+ // Set expectations.
+ expected_login_error_email_ = kEmail;
+ // Configure the test.
+ profile()->GetPrefs()->SetBoolean(prefs::kSigninAllowed, false);
+ // Signin flow.
+ CreateDiceTurnOnSyncHelper(
+ DiceTurnSyncOnHelper::SigninAbortedMode::KEEP_ACCOUNT);
+ base::RunLoop().RunUntilIdle();
+ // Check expectations.
+ EXPECT_FALSE(signin_manager()->IsAuthenticated());
+ EXPECT_TRUE(token_service()->RefreshTokenIsAvailable(account_id()));
+ CheckDelegateCalls();
+}
+
+// Tests that the login error is displayed and that the account is removed.
+TEST_F(DiceTurnSyncOnHelperTest, CanOfferSigninErrorRemoveAccount) {
+ // Set expectations.
+ expected_login_error_email_ = kEmail;
+ // Configure the test.
+ profile()->GetPrefs()->SetBoolean(prefs::kSigninAllowed, false);
+ // Signin flow.
+ CreateDiceTurnOnSyncHelper(
+ DiceTurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT);
+ base::RunLoop().RunUntilIdle();
+ // Check expectations.
+ EXPECT_FALSE(signin_manager()->IsAuthenticated());
+ EXPECT_FALSE(token_service()->RefreshTokenIsAvailable(account_id()));
+ CheckDelegateCalls();
+}
+
+// Aborts the flow after the cross account dialog.
+TEST_F(DiceTurnSyncOnHelperTest, CrossAccountAbort) {
+ // Set expectations.
+ expected_merge_data_previous_email_ = kPreviousEmail;
+ expected_merge_data_new_email_ = kEmail;
+ // Configure the test.
+ profile()->GetPrefs()->SetString(prefs::kGoogleServicesLastUsername,
+ kPreviousEmail);
+ // Signin flow.
+ CreateDiceTurnOnSyncHelper(
+ DiceTurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT);
+ // Check expectations.
+ EXPECT_FALSE(signin_manager()->IsAuthenticated());
+ EXPECT_FALSE(token_service()->RefreshTokenIsAvailable(account_id()));
+ CheckDelegateCalls();
+}
+
+// Merge data after the cross account dialog.
+TEST_F(DiceTurnSyncOnHelperTest, CrossAccountContinue) {
+ // Set expectations.
+ expected_merge_data_previous_email_ = kPreviousEmail;
+ expected_merge_data_new_email_ = kEmail;
+ expected_sync_confirmation_shown_ = true;
+ // Configure the test.
+ merge_data_choice_ = DiceTurnSyncOnHelper::SIGNIN_CHOICE_CONTINUE;
+ profile()->GetPrefs()->SetString(prefs::kGoogleServicesLastUsername,
+ kPreviousEmail);
+ // Signin flow.
+ CreateDiceTurnOnSyncHelper(
+ DiceTurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT);
+ // Check expectations.
+ EXPECT_FALSE(signin_manager()->IsAuthenticated());
+ EXPECT_FALSE(token_service()->RefreshTokenIsAvailable(account_id()));
+ CheckDelegateCalls();
+}
+
+// Create a new profile after the cross account dialog and show the signin page.
+TEST_F(DiceTurnSyncOnHelperTest, CrossAccountNewProfile) {
+ // Set expectations.
+ expected_merge_data_previous_email_ = kPreviousEmail;
+ expected_merge_data_new_email_ = kEmail;
+ expected_new_profile_username_ = kEmail;
+ // Configure the test.
+ merge_data_choice_ = DiceTurnSyncOnHelper::SIGNIN_CHOICE_NEW_PROFILE;
+ profile()->GetPrefs()->SetString(prefs::kGoogleServicesLastUsername,
+ kPreviousEmail);
+ // Signin flow.
+ CreateDiceTurnOnSyncHelper(
+ DiceTurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT);
+ // Check expectations.
+ base::RunLoop().RunUntilIdle(); // Profile creation is asynchronous.
+ EXPECT_FALSE(signin_manager()->IsAuthenticated());
+ EXPECT_FALSE(token_service()->RefreshTokenIsAvailable(account_id()));
+ CheckDelegateCalls();
+}
+
+// Abort after the enterprise confirmation prompt.
+TEST_F(DiceTurnSyncOnHelperTest, EnterpriseConfirmationAbort) {
+ // Set expectations.
+ expected_enterprise_confirmation_email_ = kEmail;
+ // Configure the test.
+ user_policy_signin_service()->set_dm_token("foo");
+ user_policy_signin_service()->set_client_id("bar");
+ // Signin flow.
+ CreateDiceTurnOnSyncHelper(
+ DiceTurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT);
+ // Check expectations.
+ EXPECT_FALSE(signin_manager()->IsAuthenticated());
+ EXPECT_FALSE(token_service()->RefreshTokenIsAvailable(account_id()));
+ CheckDelegateCalls();
+}
+
+// Continue after the enterprise confirmation prompt.
+TEST_F(DiceTurnSyncOnHelperTest, EnterpriseConfirmationContinue) {
+ // Set expectations.
+ expected_enterprise_confirmation_email_ = kEmail;
+ expected_sync_confirmation_shown_ = true;
+ // Configure the test.
+ user_policy_signin_service()->set_dm_token("foo");
+ user_policy_signin_service()->set_client_id("bar");
+ enterprise_choice_ = DiceTurnSyncOnHelper::SIGNIN_CHOICE_CONTINUE;
+ // Signin flow.
+ CreateDiceTurnOnSyncHelper(
+ DiceTurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT);
+ // Check expectations.
+ EXPECT_FALSE(signin_manager()->IsAuthenticated());
+ EXPECT_FALSE(token_service()->RefreshTokenIsAvailable(account_id()));
+ CheckDelegateCalls();
+}
+
+// Continue with a new profile after the enterprise confirmation prompt.
+TEST_F(DiceTurnSyncOnHelperTest, EnterpriseConfirmationNewProfile) {
+ // Set expectations.
+ expected_enterprise_confirmation_email_ = kEmail;
+ expected_new_profile_username_ = kEmail;
+ // Configure the test.
+ user_policy_signin_service()->set_dm_token("foo");
+ user_policy_signin_service()->set_client_id("bar");
+ enterprise_choice_ = DiceTurnSyncOnHelper::SIGNIN_CHOICE_NEW_PROFILE;
+ // Signin flow.
+ CreateDiceTurnOnSyncHelper(
+ DiceTurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT);
+ // Check expectations.
+ base::RunLoop().RunUntilIdle(); // Profile creation is asynchronous.
+ EXPECT_FALSE(signin_manager()->IsAuthenticated());
+ EXPECT_FALSE(token_service()->RefreshTokenIsAvailable(account_id()));
+ CheckDelegateCalls();
+}
+
+// Tests that the sync confirmation is shown and the user can abort.
+TEST_F(DiceTurnSyncOnHelperTest, UndoSync) {
+ // Set expectations.
+ expected_sync_confirmation_shown_ = true;
+ SetExpectationsForSyncStartupCompleted();
+ EXPECT_CALL(*GetProfileSyncServiceMock(), SetFirstSetupComplete()).Times(0);
+
+ // Signin flow.
+ EXPECT_FALSE(signin_manager()->IsAuthenticated());
+ CreateDiceTurnOnSyncHelper(
+ DiceTurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT);
+ // Check expectations.
+ EXPECT_FALSE(signin_manager()->IsAuthenticated());
+ EXPECT_FALSE(token_service()->RefreshTokenIsAvailable(account_id()));
+ CheckDelegateCalls();
+}
+
+// Tests that the sync settings page is shown.
+TEST_F(DiceTurnSyncOnHelperTest, ConfigureSync) {
+ // Set expectations.
+ expected_sync_confirmation_shown_ = true;
+ expected_sync_settings_shown_ = true;
+ SetExpectationsForSyncStartupCompleted();
+ EXPECT_CALL(*GetProfileSyncServiceMock(), SetFirstSetupComplete()).Times(0);
+
+ // Configure the test.
+ sync_confirmation_result_ =
+ LoginUIService::SyncConfirmationUIClosedResult::CONFIGURE_SYNC_FIRST;
+ // Signin flow.
+ EXPECT_FALSE(signin_manager()->IsAuthenticated());
+ CreateDiceTurnOnSyncHelper(
+ DiceTurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT);
+ // Check expectations.
+ EXPECT_TRUE(signin_manager()->IsAuthenticated());
+ EXPECT_TRUE(token_service()->RefreshTokenIsAvailable(account_id()));
+ CheckDelegateCalls();
+}
+
+// Tests that the user is signed in and Sync configuration is complete.
+TEST_F(DiceTurnSyncOnHelperTest, StartSync) {
+ // Set expectations.
+ expected_sync_confirmation_shown_ = true;
+ SetExpectationsForSyncStartupCompleted();
+ EXPECT_CALL(*GetProfileSyncServiceMock(), SetFirstSetupComplete()).Times(1);
+ // Configure the test.
+ sync_confirmation_result_ = LoginUIService::SyncConfirmationUIClosedResult::
+ SYNC_WITH_DEFAULT_SETTINGS;
+ // Signin flow.
+ EXPECT_FALSE(signin_manager()->IsAuthenticated());
+ CreateDiceTurnOnSyncHelper(
+ DiceTurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT);
+ // Check expectations.
+ EXPECT_TRUE(token_service()->RefreshTokenIsAvailable(account_id()));
+ EXPECT_EQ(account_id(), signin_manager()->GetAuthenticatedAccountId());
+ CheckDelegateCalls();
+}
+
+// Tests that the user is signed in and Sync configuration is complete.
+// Regression test for http://crbug.com/812546
+TEST_F(DiceTurnSyncOnHelperTest, ShowSyncDialogForEndConsumerAccount) {
+ // Set expectations.
+ expected_sync_confirmation_shown_ = true;
+ sync_confirmation_result_ = LoginUIService::SyncConfirmationUIClosedResult::
+ SYNC_WITH_DEFAULT_SETTINGS;
+ SetExpectationsForSyncStartupCompleted();
+ EXPECT_CALL(*GetProfileSyncServiceMock(), SetFirstSetupComplete()).Times(1);
+
+ // Signin flow.
+ EXPECT_FALSE(signin_manager()->IsAuthenticated());
+ CreateDiceTurnOnSyncHelper(
+ DiceTurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT);
+
+ // Check expectations.
+ EXPECT_TRUE(token_service()->RefreshTokenIsAvailable(account_id()));
+ EXPECT_EQ(account_id(), signin_manager()->GetAuthenticatedAccountId());
+ CheckDelegateCalls();
+}
+
+// For enterprise user, tests that the user is signed in only after Sync engine
+// starts.
+// Regression test for http://crbug.com/812546
+TEST_F(DiceTurnSyncOnHelperTest,
+ ShowSyncDialogBlockedUntilSyncStartupCompletedForEnterpriseAccount) {
+ // Reset the account info to be an enterprise account.
+ UseEnterpriseAccount();
+
+ // Set expectations.
+ expected_sync_confirmation_shown_ = false;
+ SetExpectationsForSyncStartupPending();
+
+ // Signin flow.
+ EXPECT_FALSE(signin_manager()->IsAuthenticated());
+ DiceTurnSyncOnHelper* dice_sync_starter = CreateDiceTurnOnSyncHelper(
+ DiceTurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT);
+
+ // Check that the account was set in the sign-in manager, but the sync
+ // confirmation dialog was not yet shown.
+ EXPECT_TRUE(token_service()->RefreshTokenIsAvailable(account_id()));
+ EXPECT_EQ(account_id(), signin_manager()->GetAuthenticatedAccountId());
+ CheckDelegateCalls();
+
+ // Simulate that sync startup has completed.
+ expected_sync_confirmation_shown_ = true;
+ EXPECT_CALL(*GetProfileSyncServiceMock(), SetFirstSetupComplete()).Times(1);
+ sync_confirmation_result_ = LoginUIService::SyncConfirmationUIClosedResult::
+ SYNC_WITH_DEFAULT_SETTINGS;
+ dice_sync_starter->SyncStartupCompleted();
+ CheckDelegateCalls();
+}
+
+// For enterprise user, tests that the user is signed in only after Sync engine
+// fails to start.
+// Regression test for http://crbug.com/812546
+TEST_F(DiceTurnSyncOnHelperTest,
+ ShowSyncDialogBlockedUntilSyncStartupFailedForEnterpriseAccount) {
+ // Reset the account info to be an enterprise account.
+ UseEnterpriseAccount();
+
+ // Set expectations.
+ expected_sync_confirmation_shown_ = false;
+ SetExpectationsForSyncStartupPending();
+
+ // Signin flow.
+ EXPECT_FALSE(signin_manager()->IsAuthenticated());
+ DiceTurnSyncOnHelper* dice_sync_starter = CreateDiceTurnOnSyncHelper(
+ DiceTurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT);
+
+ // Check that the primary account was added to the token service and in the
+ // sign-in manager.
+ EXPECT_TRUE(token_service()->RefreshTokenIsAvailable(account_id()));
+ EXPECT_EQ(account_id(), signin_manager()->GetAuthenticatedAccountId());
+ CheckDelegateCalls();
+
+ // Simulate that sync startup has failed.
+ expected_sync_confirmation_shown_ = true;
+ EXPECT_CALL(*GetProfileSyncServiceMock(), SetFirstSetupComplete()).Times(1);
+ sync_confirmation_result_ = LoginUIService::SyncConfirmationUIClosedResult::
+ SYNC_WITH_DEFAULT_SETTINGS;
+ dice_sync_starter->SyncStartupFailed();
+ CheckDelegateCalls();
+}
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 b0bd02847f1..b98fde04295 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
@@ -239,8 +239,8 @@ void InlineSigninHelper::OnClientOAuthSuccessAndBrowserOpened(
PasswordStoreFactory::GetForProfile(profile_,
ServiceAccessType::EXPLICIT_ACCESS);
if (password_store) {
- password_store->SaveSyncPasswordHash(base::UTF8ToUTF16(password_));
- password_manager::metrics_util::LogSyncPasswordHashChange(
+ password_store->SaveSyncPasswordHash(
+ base::UTF8ToUTF16(password_),
password_manager::metrics_util::SyncPasswordHashChange::
SAVED_ON_CHROME_SIGNIN);
}
diff --git a/chromium/chrome/browser/ui/webui/signin/inline_login_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/signin/inline_login_ui_browsertest.cc
index 7e4a5f2f307..4911e8662fe 100644
--- a/chromium/chrome/browser/ui/webui/signin/inline_login_ui_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/signin/inline_login_ui_browsertest.cc
@@ -354,8 +354,9 @@ IN_PROC_BROWSER_TEST_F(InlineLoginUIBrowserTest, OneProcessLimit) {
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(FILE_PATH_LITERAL("title1.html")));
GURL test_url_2 = ui_test_utils::GetTestUrl(
- base::FilePath(base::FilePath::kCurrentDirectory),
- base::FilePath(FILE_PATH_LITERAL("data:text/html,Hello world!")));
+ base::FilePath(base::FilePath::kCurrentDirectory)
+ .Append(FILE_PATH_LITERAL("frame_tree")),
+ base::FilePath(FILE_PATH_LITERAL("simple.htm")));
// Even when the process limit is set to one, the signin process should
// still be given its own process and storage partition.
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 1e21d92418c..4ef22822f58 100644
--- a/chromium/chrome/browser/ui/webui/signin/login_ui_service.cc
+++ b/chromium/chrome/browser/ui/webui/signin/login_ui_service.cc
@@ -11,12 +11,15 @@
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
-#include "chrome/browser/ui/user_manager.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#include "chrome/common/url_constants.h"
#include "components/signin/core/browser/profile_management_switches.h"
#include "components/signin/core/browser/signin_header_helper.h"
+#if !defined(OS_CHROMEOS)
+#include "chrome/browser/ui/user_manager.h"
+#endif // !defined(OS_CHROMEOS)
+
LoginUIService::LoginUIService(Profile* profile)
#if !defined(OS_CHROMEOS)
: profile_(profile)
diff --git a/chromium/chrome/browser/ui/webui/signin/md_user_manager_ui.cc b/chromium/chrome/browser/ui/webui/signin/md_user_manager_ui.cc
index 31cb8022e77..4ae11a15f61 100644
--- a/chromium/chrome/browser/ui/webui/signin/md_user_manager_ui.cc
+++ b/chromium/chrome/browser/ui/webui/signin/md_user_manager_ui.cc
@@ -17,8 +17,8 @@
#include "chrome/browser/ui/webui/signin/signin_utils.h"
#include "chrome/browser/ui/webui/signin/user_manager_screen_handler.h"
#include "chrome/browser/ui/webui/theme_source.h"
+#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_features.h"
-#include "chrome/common/features.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/browser_resources.h"
#include "content/public/browser/web_ui.h"
diff --git a/chromium/chrome/browser/ui/webui/signin/md_user_manager_ui.h b/chromium/chrome/browser/ui/webui/signin/md_user_manager_ui.h
index 0aa98246525..63bf9ceed78 100644
--- a/chromium/chrome/browser/ui/webui/signin/md_user_manager_ui.h
+++ b/chromium/chrome/browser/ui/webui/signin/md_user_manager_ui.h
@@ -6,7 +6,7 @@
#define CHROME_BROWSER_UI_WEBUI_SIGNIN_MD_USER_MANAGER_UI_H_
#include "base/macros.h"
-#include "chrome/common/features.h"
+#include "chrome/common/buildflags.h"
#include "content/public/browser/web_ui_controller.h"
class SigninCreateProfileHandler;
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc b/chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc
index 44d69c0bfd9..2ce9d8cbab1 100644
--- a/chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc
+++ b/chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc
@@ -33,7 +33,7 @@
#include "chrome/browser/ui/user_manager.h"
#include "chrome/browser/ui/webui/profile_helper.h"
#include "chrome/browser/ui/webui/signin/signin_utils.h"
-#include "chrome/common/features.h"
+#include "chrome/common/buildflags.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/chromium_strings.h"
@@ -447,7 +447,6 @@ void SigninCreateProfileHandler::ShowProfileCreationError(
// The ProfileManager calls us back with a NULL profile in some cases.
if (profile) {
webui::DeleteProfileAtPath(profile->GetPath(),
- web_ui(),
ProfileMetrics::DELETE_PROFILE_SETTINGS);
}
profile_creation_type_ = NO_CREATION_IN_PROGRESS;
@@ -717,7 +716,6 @@ void SigninCreateProfileHandler::CancelProfileRegistration(
// RegisterAndInitSync() won't be called, so the cleanup must be done here.
profile_path_being_created_.clear();
webui::DeleteProfileAtPath(new_profile->GetPath(),
- web_ui(),
ProfileMetrics::DELETE_PROFILE_SETTINGS);
}
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler.h b/chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler.h
index 87bf6e44a84..dd6b4f00db4 100644
--- a/chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler.h
+++ b/chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler.h
@@ -13,7 +13,7 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_window.h"
-#include "chrome/common/features.h"
+#include "chrome/common/buildflags.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/web_ui_message_handler.h"
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc b/chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc
index f9337a1e0ff..94c07cbe51c 100644
--- a/chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc
@@ -15,14 +15,13 @@
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/ui/webui/signin/signin_utils.h"
-#include "chrome/common/features.h"
+#include "chrome/common/buildflags.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.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_manager.h"
#include "components/signin/core/browser/fake_auth_status_provider.h"
-#include "components/sync/model/attachments/attachment_service_proxy_for_test.h"
#include "components/sync/model/fake_sync_change_processor.h"
#include "components/sync/model/sync_data.h"
#include "components/sync/model/sync_error_factory_mock.h"
@@ -73,12 +72,7 @@ syncer::SyncData CreateSyncData(const std::string& id,
specifics.mutable_managed_user()->set_acknowledged(true);
specifics.mutable_managed_user()->set_chrome_avatar(chrome_avatar);
- return syncer::SyncData::CreateRemoteData(
- 1,
- specifics,
- base::Time(),
- syncer::AttachmentIdList(),
- syncer::AttachmentServiceProxyForTest::Create());
+ return syncer::SyncData::CreateRemoteData(1, specifics, base::Time());
}
#endif
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_dice_internals_handler.cc b/chromium/chrome/browser/ui/webui/signin/signin_dice_internals_handler.cc
deleted file mode 100644
index e6699179018..00000000000
--- a/chromium/chrome/browser/ui/webui/signin/signin_dice_internals_handler.cc
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/signin/signin_dice_internals_handler.h"
-
-#include "base/values.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/account_tracker_service_factory.h"
-#include "chrome/browser/signin/dice_tab_helper.h"
-#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
-#include "chrome/browser/ui/singleton_tabs.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h"
-#include "google_apis/gaia/gaia_urls.h"
-
-SigninDiceInternalsHandler::SigninDiceInternalsHandler(Profile* profile)
- : profile_(profile) {
- DCHECK(profile_);
- DCHECK(!profile_->IsOffTheRecord());
-}
-
-SigninDiceInternalsHandler::~SigninDiceInternalsHandler() {}
-
-void SigninDiceInternalsHandler::RegisterMessages() {
- web_ui()->RegisterMessageCallback(
- "enableSync", base::Bind(&SigninDiceInternalsHandler::HandleEnableSync,
- base::Unretained(this)));
-
- web_ui()->RegisterMessageCallback(
- "disableSync", base::Bind(&SigninDiceInternalsHandler::HandleDisableSync,
- base::Unretained(this)));
-}
-
-void SigninDiceInternalsHandler::HandleEnableSync(const base::ListValue* args) {
- if (SigninManagerFactory::GetForProfile(profile_)->IsAuthenticated()) {
- VLOG(1) << "[Dice] Cannot enable sync as profile is already authenticated";
- return;
- }
-
- Browser* browser = chrome::FindLastActiveWithProfile(profile_);
- DCHECK(browser);
-
- AccountTrackerService* tracker =
- AccountTrackerServiceFactory::GetForProfile(profile_);
- ProfileOAuth2TokenService* token_service =
- ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
- std::vector<std::string> account_ids = token_service->GetAccounts();
- if (account_ids.empty()) {
- browser->window()->ShowAvatarBubbleFromAvatarButton(
- BrowserWindow::AVATAR_BUBBLE_MODE_SIGNIN,
- signin::ManageAccountsParams(),
- signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN, false);
- return;
- }
-
- std::string account_id = account_ids[0];
- std::string email = tracker->GetAccountInfo(account_id).email;
- VLOG(1) << "[Dice] Start syncing with account " << email;
-
- // DiceTurnSyncOnHelper is suicidal (it will kill itself once it finishes
- // enabling sync).
- new DiceTurnSyncOnHelper(
- profile_, browser, signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN,
- signin_metrics::Reason::REASON_UNKNOWN_REASON, account_id,
- DiceTurnSyncOnHelper::SigninAbortedMode::KEEP_ACCOUNT);
-}
-
-void SigninDiceInternalsHandler::HandleDisableSync(
- const base::ListValue* args) {
- SigninManager* signin_manager = SigninManagerFactory::GetForProfile(profile_);
- if (!signin_manager->IsAuthenticated()) {
- VLOG(2) << "[Dice] Cannot disable sync as profile is not authenticated";
- return;
- }
-
- VLOG(2) << "[Dice] Sign out.";
- signin_manager->SignOut(signin_metrics::USER_TUNED_OFF_SYNC_FROM_DICE_UI,
- signin_metrics::SignoutDelete::IGNORE_METRIC);
-}
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_dice_internals_handler.h b/chromium/chrome/browser/ui/webui/signin/signin_dice_internals_handler.h
deleted file mode 100644
index 84513efaa6a..00000000000
--- a/chromium/chrome/browser/ui/webui/signin/signin_dice_internals_handler.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_DICE_INTERNALS_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_DICE_INTERNALS_HANDLER_H_
-
-#include "base/macros.h"
-#include "content/public/browser/web_ui_message_handler.h"
-
-namespace base {
-class ListValue;
-}
-class Profile;
-
-class SigninDiceInternalsHandler : public content::WebUIMessageHandler {
- public:
- explicit SigninDiceInternalsHandler(Profile* profile);
- ~SigninDiceInternalsHandler() override;
-
- // content::WebUIMessageHandler:
- void RegisterMessages() override;
-
- private:
- // Handler for enable sync event.
- void HandleEnableSync(const base::ListValue* args);
-
- // Handler for disable sync event.
- void HandleDisableSync(const base::ListValue* args);
-
- // Start a Gaia web-sign in and once done automatically start syncing with the
- // account that was signed in.
- void StartWebGaiaSigninAndStartSyncWhenDone();
-
- Profile* profile_;
-
- DISALLOW_COPY_AND_ASSIGN(SigninDiceInternalsHandler);
-};
-
-#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_DICE_INTERNALS_HANDLER_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 e127aefb512..7b1e9041935 100644
--- a/chromium/chrome/browser/ui/webui/signin/signin_error_ui.cc
+++ b/chromium/chrome/browser/ui/webui/signin/signin_error_ui.cc
@@ -11,6 +11,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/signin/signin_ui_util.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/user_manager.h"
#include "chrome/browser/ui/webui/signin/login_ui_service.h"
@@ -19,6 +20,9 @@
#include "chrome/common/url_constants.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/signin_pref_names.h"
+#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"
@@ -96,8 +100,21 @@ void SigninErrorUI::Initialize(Browser* browser, bool is_system_profile) {
if (is_profile_blocked) {
source->AddLocalizedString("profileBlockedMessage",
IDS_OLD_PROFILES_DISABLED_MESSAGE);
- source->AddLocalizedString("profileBlockedAddPersonSuggestion",
- IDS_OLD_PROFILES_DISABLED_ADD_PERSON_SUGGESTION);
+ std::string allowed_domain = signin_ui_util::GetAllowedDomain(
+ g_browser_process->local_state()->GetString(
+ prefs::kGoogleServicesUsernamePattern));
+ if (allowed_domain.empty()) {
+ source->AddLocalizedString(
+ "profileBlockedAddPersonSuggestion",
+ IDS_OLD_PROFILES_DISABLED_ADD_PERSON_SUGGESTION);
+ } else {
+ source->AddString(
+ "profileBlockedAddPersonSuggestion",
+ l10n_util::GetStringFUTF16(
+ IDS_OLD_PROFILES_DISABLED_ADD_PERSON_SUGGESTION_WITH_DOMAIN,
+ base::ASCIIToUTF16(allowed_domain)));
+ }
+
source->AddLocalizedString("profileBlockedRemoveProfileSuggestion",
IDS_OLD_PROFILES_DISABLED_REMOVED_OLD_PROFILE);
} else if (!is_system_profile &&
@@ -133,8 +150,7 @@ void SigninErrorUI::Initialize(Browser* browser, bool is_system_profile) {
source->AddString("signinErrorSwitchLabel",
l10n_util::GetStringFUTF16(
IDS_SIGNIN_ERROR_SWITCH_BUTTON_LABEL, existing_name));
- source->AddLocalizedString("signinErrorLearnMore",
- IDS_SIGNIN_ERROR_LEARN_MORE_LINK);
+ source->AddLocalizedString("signinErrorLearnMore", IDS_LEARN_MORE);
source->AddLocalizedString("signinErrorCloseLabel",
IDS_SIGNIN_ERROR_CLOSE_BUTTON_LABEL);
source->AddLocalizedString("signinErrorOkLabel",
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_supervised_user_import_handler_unittest.cc b/chromium/chrome/browser/ui/webui/signin/signin_supervised_user_import_handler_unittest.cc
index 13bd890f5b2..cc615490d3c 100644
--- a/chromium/chrome/browser/ui/webui/signin/signin_supervised_user_import_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/signin/signin_supervised_user_import_handler_unittest.cc
@@ -17,8 +17,6 @@
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/signin/core/browser/fake_auth_status_provider.h"
-#include "components/sync/model/attachments/attachment_id.h"
-#include "components/sync/model/attachments/attachment_service_proxy_for_test.h"
#include "components/sync/model/fake_sync_change_processor.h"
#include "components/sync/model/sync_error_factory_mock.h"
#include "components/sync/protocol/sync.pb.h"
@@ -48,12 +46,7 @@ syncer::SyncData CreateSyncData(const std::string& id,
specifics.mutable_managed_user()->set_acknowledged(true);
specifics.mutable_managed_user()->set_chrome_avatar(chrome_avatar);
- return syncer::SyncData::CreateRemoteData(
- 1,
- specifics,
- base::Time(),
- syncer::AttachmentIdList(),
- syncer::AttachmentServiceProxyForTest::Create());
+ return syncer::SyncData::CreateRemoteData(1, specifics, base::Time());
}
} // namespace
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 170211b6417..e2ed758f0ee 100644
--- a/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
+++ b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
@@ -8,6 +8,8 @@
#include "base/bind.h"
#include "base/metrics/user_metrics.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/consent_auditor/consent_auditor_factory.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/signin/account_tracker_service_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
@@ -16,6 +18,8 @@
#include "chrome/browser/ui/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"
+#include "components/consent_auditor/consent_auditor.h"
#include "components/signin/core/browser/account_tracker_service.h"
#include "components/signin/core/browser/avatar_icon_util.h"
#include "components/signin/core/browser/signin_manager.h"
@@ -25,10 +29,13 @@
const int kProfileImageSize = 128;
-SyncConfirmationHandler::SyncConfirmationHandler(Browser* browser)
+SyncConfirmationHandler::SyncConfirmationHandler(
+ Browser* browser,
+ const std::unordered_map<std::string, int>& string_to_grd_id_map)
: profile_(browser->profile()),
browser_(browser),
- did_user_explicitly_interact(false) {
+ did_user_explicitly_interact(false),
+ string_to_grd_id_map_(string_to_grd_id_map) {
DCHECK(profile_);
DCHECK(browser_);
BrowserList::AddObserver(this);
@@ -67,11 +74,13 @@ void SyncConfirmationHandler::RegisterMessages() {
void SyncConfirmationHandler::HandleConfirm(const base::ListValue* args) {
did_user_explicitly_interact = true;
+ RecordConsent(args);
CloseModalSigninWindow(LoginUIService::SYNC_WITH_DEFAULT_SETTINGS);
}
void SyncConfirmationHandler::HandleGoToSettings(const base::ListValue* args) {
did_user_explicitly_interact = true;
+ RecordConsent(args);
CloseModalSigninWindow(LoginUIService::CONFIGURE_SYNC_FIRST);
}
@@ -80,6 +89,35 @@ void SyncConfirmationHandler::HandleUndo(const base::ListValue* args) {
CloseModalSigninWindow(LoginUIService::ABORT_SIGNIN);
}
+void SyncConfirmationHandler::RecordConsent(const base::ListValue* args) {
+ CHECK_EQ(2U, args->GetSize());
+ const std::vector<base::Value>& consent_description =
+ args->GetList()[0].GetList();
+ const std::string& consent_confirmation = args->GetList()[1].GetString();
+
+ std::vector<int> consent_text_ids;
+
+ // The strings returned by the WebUI are not free-form, they must belong into
+ // a pre-determined set of strings (stored in |string_to_grd_id_map_|). As
+ // this has privacy and legal implications, CHECK the integrity of the strings
+ // received from the renderer process before recording the consent.
+ for (const base::Value& text : consent_description) {
+ auto iter = string_to_grd_id_map_.find(text.GetString());
+ CHECK(iter != string_to_grd_id_map_.end()) << "Unexpected string:\n"
+ << text.GetString();
+ consent_text_ids.push_back(iter->second);
+ }
+
+ auto iter = string_to_grd_id_map_.find(consent_confirmation);
+ CHECK(iter != string_to_grd_id_map_.end()) << "Unexpected string:\n"
+ << consent_confirmation;
+ int consent_confirmation_id = iter->second;
+
+ ConsentAuditorFactory::GetForProfile(profile_)->RecordGaiaConsent(
+ consent_auditor::Feature::CHROME_SYNC, consent_text_ids,
+ consent_confirmation_id, consent_auditor::ConsentStatus::GIVEN);
+}
+
void SyncConfirmationHandler::SetUserImageURL(const std::string& picture_url) {
std::string picture_url_to_load;
GURL picture_gurl(picture_url);
diff --git a/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.h b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.h
index 6fded10e455..8cab0b742a7 100644
--- a/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.h
+++ b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.h
@@ -6,6 +6,7 @@
#define CHROME_BROWSER_UI_WEBUI_SIGNIN_SYNC_CONFIRMATION_HANDLER_H_
#include <string>
+#include <unordered_map>
#include "base/macros.h"
#include "chrome/browser/ui/browser_list_observer.h"
@@ -21,7 +22,12 @@ class SyncConfirmationHandler : public content::WebUIMessageHandler,
public AccountTrackerService::Observer,
public BrowserListObserver {
public:
- explicit SyncConfirmationHandler(Browser* browser);
+ // Creates a SyncConfirmationHandler for the |browser|. All strings in the
+ // corresponding Web UI should be represented in |string_to_grd_id_map| and
+ // mapped to their GRD IDs.
+ explicit SyncConfirmationHandler(
+ Browser* browser,
+ const std::unordered_map<std::string, int>& string_to_grd_id_map);
~SyncConfirmationHandler() override;
// content::WebUIMessageHandler:
@@ -55,6 +61,16 @@ class SyncConfirmationHandler : public content::WebUIMessageHandler,
// a single integer value for the height the native view should resize to.
virtual void HandleInitializedWithSize(const base::ListValue* args);
+ // Records the user's consent to sync. Called from |HandleConfirm| and
+ // |HandleGoToSettings|, and expects two parameters to be passed through
+ // these methods from the WebUI:
+ // 1. List of strings (names of the string resources constituting the consent
+ // description as per WebUIDataSource)
+ // 2. Strings (name of the string resource of the consent confirmation)
+ // This message is sent when the user interacts with the dialog in a positive
+ // manner, i.e. clicks on the confirmation button or the settings link.
+ virtual void RecordConsent(const base::ListValue* args);
+
// Sets the profile picture shown in the dialog to the image at |url|.
virtual void SetUserImageURL(const std::string& url);
@@ -73,6 +89,10 @@ class SyncConfirmationHandler : public content::WebUIMessageHandler,
// Records whether the user clicked on Undo, Ok, or Settings.
bool did_user_explicitly_interact;
+ // Mapping between strings displayed in the UI corresponding to this handler
+ // and their respective GRD IDs.
+ std::unordered_map<std::string, int> string_to_grd_id_map_;
+
DISALLOW_COPY_AND_ASSIGN(SyncConfirmationHandler);
};
diff --git a/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
index a5caccc0dd3..28843bfe753 100644
--- a/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
+++ b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
@@ -5,9 +5,12 @@
#include "chrome/browser/ui/webui/signin/sync_confirmation_handler.h"
#include <memory>
+#include <unordered_map>
+#include <vector>
#include "base/test/user_action_tester.h"
#include "base/values.h"
+#include "chrome/browser/consent_auditor/consent_auditor_factory.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/signin/account_fetcher_service_factory.h"
#include "chrome/browser/signin/account_tracker_service_factory.h"
@@ -15,6 +18,7 @@
#include "chrome/browser/signin/fake_signin_manager_builder.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/sync/user_event_service_factory.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/signin/sync_confirmation_ui.h"
@@ -22,6 +26,7 @@
#include "chrome/test/base/dialog_test_browser_window.h"
#include "chrome/test/base/testing_profile.h"
#include "components/browser_sync/profile_sync_service.h"
+#include "components/consent_auditor/consent_auditor.h"
#include "components/signin/core/browser/account_fetcher_service.h"
#include "components/signin/core/browser/avatar_icon_util.h"
#include "components/signin/core/browser/fake_account_fetcher_service.h"
@@ -35,10 +40,17 @@ const int kExpectedProfileImageSize = 128;
// really matter in unit tests.
const double kDefaultDialogHeight = 350.0;
+const std::string kGaiaID = "gaia";
+const std::string kUsername = "foo@example.com";
+const std::string kPassword = "password";
+
class TestingSyncConfirmationHandler : public SyncConfirmationHandler {
public:
- TestingSyncConfirmationHandler(Browser* browser, content::WebUI* web_ui)
- : SyncConfirmationHandler(browser) {
+ TestingSyncConfirmationHandler(
+ Browser* browser,
+ content::WebUI* web_ui,
+ std::unordered_map<std::string, int> string_to_grd_id_map)
+ : SyncConfirmationHandler(browser, string_to_grd_id_map) {
set_web_ui(web_ui);
}
@@ -46,6 +58,7 @@ class TestingSyncConfirmationHandler : public SyncConfirmationHandler {
using SyncConfirmationHandler::HandleUndo;
using SyncConfirmationHandler::HandleInitializedWithSize;
using SyncConfirmationHandler::HandleGoToSettings;
+ using SyncConfirmationHandler::RecordConsent;
using SyncConfirmationHandler::SetUserImageURL;
private:
@@ -88,8 +101,47 @@ class TestingOneClickSigninSyncStarter : public OneClickSigninSyncStarter {
DISALLOW_COPY_AND_ASSIGN(TestingOneClickSigninSyncStarter);
};
+// TODO(msramek): Extract this into "consent_auditor_test_utils" for reusability
+// and to remove unnecessary dependencies from this test.
+class FakeConsentAuditor : public consent_auditor::ConsentAuditor {
+ public:
+ static std::unique_ptr<KeyedService> Build(content::BrowserContext* context) {
+ return std::make_unique<FakeConsentAuditor>(
+ Profile::FromBrowserContext(context));
+ }
+
+ FakeConsentAuditor(Profile* profile)
+ : ConsentAuditor(
+ profile->GetPrefs(),
+ browser_sync::UserEventServiceFactory::GetForProfile(profile),
+ std::string(),
+ std::string()) {}
+ ~FakeConsentAuditor() override {}
+
+ void RecordGaiaConsent(consent_auditor::Feature feature,
+ const std::vector<int>& description_grd_ids,
+ int confirmation_grd_id,
+ consent_auditor::ConsentStatus status) override {
+ recorded_ids_ = description_grd_ids;
+ recorded_ids_.push_back(confirmation_grd_id);
+ }
+
+ const std::vector<int>& recorded_ids() { return recorded_ids_; }
+
+ private:
+ std::vector<int> recorded_ids_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeConsentAuditor);
+};
+
class SyncConfirmationHandlerTest : public BrowserWithTestWindowTest {
public:
+ static const std::string kConsentText1;
+ static const std::string kConsentText2;
+ static const std::string kConsentText3;
+ static const std::string kConsentText4;
+ static const std::string kConsentText5;
+
SyncConfirmationHandlerTest()
: did_user_explicitly_interact(false), web_ui_(new content::TestWebUI) {}
void SetUp() override {
@@ -98,8 +150,8 @@ class SyncConfirmationHandlerTest : public BrowserWithTestWindowTest {
web_ui()->set_web_contents(
browser()->tab_strip_model()->GetActiveWebContents());
- auto handler =
- std::make_unique<TestingSyncConfirmationHandler>(browser(), web_ui());
+ auto handler = std::make_unique<TestingSyncConfirmationHandler>(
+ browser(), web_ui(), GetStringToGrdIdMap());
handler_ = handler.get();
sync_confirmation_ui_.reset(new SyncConfirmationUI(web_ui()));
web_ui()->AddMessageHandler(std::move(handler));
@@ -154,6 +206,11 @@ class SyncConfirmationHandlerTest : public BrowserWithTestWindowTest {
return &user_action_tester_;
}
+ FakeConsentAuditor* consent_auditor() {
+ return static_cast<FakeConsentAuditor*>(
+ ConsentAuditorFactory::GetForProfile(profile()));
+ }
+
// BrowserWithTestWindowTest
BrowserWindow* CreateBrowserWindow() override {
return new DialogTestBrowserWindow;
@@ -162,7 +219,19 @@ class SyncConfirmationHandlerTest : public BrowserWithTestWindowTest {
TestingProfile::TestingFactories GetTestingFactories() override {
return {{AccountFetcherServiceFactory::GetInstance(),
FakeAccountFetcherServiceBuilder::BuildForTests},
- {SigninManagerFactory::GetInstance(), BuildFakeSigninManagerBase}};
+ {SigninManagerFactory::GetInstance(), BuildFakeSigninManagerBase},
+ {ConsentAuditorFactory::GetInstance(), FakeConsentAuditor::Build}};
+ }
+
+ const std::unordered_map<std::string, int>& GetStringToGrdIdMap() {
+ if (string_to_grd_id_map_.empty()) {
+ string_to_grd_id_map_[kConsentText1] = 1;
+ string_to_grd_id_map_[kConsentText2] = 2;
+ string_to_grd_id_map_[kConsentText3] = 3;
+ string_to_grd_id_map_[kConsentText4] = 4;
+ string_to_grd_id_map_[kConsentText5] = 5;
+ }
+ return string_to_grd_id_map_;
}
protected:
@@ -173,10 +242,17 @@ class SyncConfirmationHandlerTest : public BrowserWithTestWindowTest {
std::unique_ptr<SyncConfirmationUI> sync_confirmation_ui_;
TestingSyncConfirmationHandler* handler_; // Not owned.
base::UserActionTester user_action_tester_;
+ std::unordered_map<std::string, int> string_to_grd_id_map_;
DISALLOW_COPY_AND_ASSIGN(SyncConfirmationHandlerTest);
};
+const std::string SyncConfirmationHandlerTest::kConsentText1 = "consentText1";
+const std::string SyncConfirmationHandlerTest::kConsentText2 = "consentText2";
+const std::string SyncConfirmationHandlerTest::kConsentText3 = "consentText3";
+const std::string SyncConfirmationHandlerTest::kConsentText4 = "consentText4";
+const std::string SyncConfirmationHandlerTest::kConsentText5 = "consentText5";
+
TEST_F(SyncConfirmationHandlerTest, TestSetImageIfPrimaryAccountReady) {
account_fetcher_service()->FakeUserInfoFetchSuccess(
"gaia",
@@ -310,10 +386,27 @@ TEST_F(SyncConfirmationHandlerTest, TestHandleUndo) {
}
TEST_F(SyncConfirmationHandlerTest, TestHandleConfirm) {
+ // The consent description consists of strings 1, 2, and 4.
+ base::ListValue consent_description;
+ consent_description.GetList().push_back(
+ base::Value(SyncConfirmationHandlerTest::kConsentText1));
+ consent_description.GetList().push_back(
+ base::Value(SyncConfirmationHandlerTest::kConsentText2));
+ consent_description.GetList().push_back(
+ base::Value(SyncConfirmationHandlerTest::kConsentText4));
+
+ // The consent confirmation contains string 5.
+ base::Value consent_confirmation(SyncConfirmationHandlerTest::kConsentText5);
+
+ // These are passed as parameters to HandleConfirm().
+ base::ListValue args;
+ args.GetList().push_back(std::move(consent_description));
+ args.GetList().push_back(std::move(consent_confirmation));
+
EXPECT_FALSE(sync()->IsFirstSetupComplete());
EXPECT_TRUE(sync()->IsFirstSetupInProgress());
- handler()->HandleConfirm(nullptr);
+ handler()->HandleConfirm(&args);
did_user_explicitly_interact = true;
EXPECT_FALSE(sync()->IsFirstSetupInProgress());
@@ -325,13 +418,34 @@ TEST_F(SyncConfirmationHandlerTest, TestHandleConfirm) {
"Signin_Signin_WithDefaultSyncSettings"));
EXPECT_EQ(0, user_action_tester()->GetActionCount(
"Signin_Signin_WithAdvancedSyncSettings"));
+
+ // The corresponding string IDs get recorded.
+ std::vector<int> expected_ids = {1, 2, 4, 5};
+ EXPECT_EQ(expected_ids, consent_auditor()->recorded_ids());
}
TEST_F(SyncConfirmationHandlerTest, TestHandleConfirmWithAdvancedSyncSettings) {
+ // The consent description consists of strings 2, 3, and 5.
+ base::ListValue consent_description;
+ consent_description.GetList().push_back(
+ base::Value(SyncConfirmationHandlerTest::kConsentText2));
+ consent_description.GetList().push_back(
+ base::Value(SyncConfirmationHandlerTest::kConsentText3));
+ consent_description.GetList().push_back(
+ base::Value(SyncConfirmationHandlerTest::kConsentText5));
+
+ // The consent confirmation contains string 2.
+ base::Value consent_confirmation(SyncConfirmationHandlerTest::kConsentText2);
+
+ // These are passed as parameters to HandleGoToSettings().
+ base::ListValue args;
+ args.GetList().push_back(std::move(consent_description));
+ args.GetList().push_back(std::move(consent_confirmation));
+
EXPECT_FALSE(sync()->IsFirstSetupComplete());
EXPECT_TRUE(sync()->IsFirstSetupInProgress());
- handler()->HandleGoToSettings(nullptr);
+ handler()->HandleGoToSettings(&args);
did_user_explicitly_interact = true;
EXPECT_FALSE(sync()->IsFirstSetupInProgress());
@@ -343,4 +457,8 @@ TEST_F(SyncConfirmationHandlerTest, TestHandleConfirmWithAdvancedSyncSettings) {
"Signin_Signin_WithDefaultSyncSettings"));
EXPECT_EQ(1, user_action_tester()->GetActionCount(
"Signin_Signin_WithAdvancedSyncSettings"));
+
+ // The corresponding string IDs get recorded.
+ std::vector<int> expected_ids = {2, 3, 5, 2};
+ EXPECT_EQ(expected_ids, consent_auditor()->recorded_ids());
}
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 446569de14a..119d9bb48e7 100644
--- a/chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
+++ b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
@@ -4,32 +4,37 @@
#include "chrome/browser/ui/webui/signin/sync_confirmation_ui.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/account_consistency_mode_manager.h"
+#include "chrome/browser/signin/unified_consent_helper.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/ui/webui/signin/sync_confirmation_handler.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/grit/generated_resources.h"
-#include "components/signin/core/browser/profile_management_switches.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/base/webui/web_ui_util.h"
SyncConfirmationUI::SyncConfirmationUI(content::WebUI* web_ui)
: SigninWebDialogUI(web_ui) {
Profile* profile = Profile::FromWebUI(web_ui);
bool is_sync_allowed = profile->IsSyncAllowed();
- bool is_dice_enabled = signin::IsDiceEnabledForProfile(profile->GetPrefs());
+ bool is_unified_consent_enabled = IsUnifiedConsentEnabled(profile);
content::WebUIDataSource* source =
content::WebUIDataSource::Create(chrome::kChromeUISyncConfirmationHost);
source->SetJsonPath("strings.js");
-
source->AddResourcePath("signin_shared_css.html", IDR_SIGNIN_SHARED_CSS_HTML);
- int title_ids, confirm_button_ids, undo_button_ids;
- if (is_dice_enabled && is_sync_allowed) {
+ int title_ids = -1;
+ int confirm_button_ids = -1;
+ int undo_button_ids = -1;
+ if (is_unified_consent_enabled && is_sync_allowed) {
source->SetDefaultResource(IDR_DICE_SYNC_CONFIRMATION_HTML);
source->AddResourcePath("sync_confirmation_browser_proxy.html",
IDR_DICE_SYNC_CONFIRMATION_BROWSER_PROXY_HTML);
@@ -42,21 +47,18 @@ SyncConfirmationUI::SyncConfirmationUI(content::WebUI* web_ui)
source->AddResourcePath("sync_confirmation.js",
IDR_DICE_SYNC_CONFIRMATION_JS);
- source->AddLocalizedString("syncConfirmationChromeSyncBody",
- IDS_SYNC_CONFIRMATION_DICE_CHROME_SYNC_MESSAGE);
- source->AddLocalizedString(
- "syncConfirmationPersonalizeServicesBody",
- IDS_SYNC_CONFIRMATION_DICE_PERSONALIZE_SERVICES_BODY);
- source->AddLocalizedString("syncConfirmationGoogleServicesBody",
- IDS_SYNC_CONFIRMATION_DICE_GOOGLE_SERVICES_BODY);
- source->AddLocalizedString(
- "syncConfirmationSyncSettingsLinkBody",
- IDS_SYNC_CONFIRMATION_DICE_SYNC_SETTINGS_LINK_BODY);
- source->AddLocalizedString(
- "syncConfirmationSyncSettingsDescription",
- IDS_SYNC_CONFIRMATION_DICE_SYNC_SETTINGS_DESCRIPTION);
-
- title_ids = IDS_SYNC_CONFIRMATION_DICE_TITLE;
+ AddStringResource(source, "syncConfirmationChromeSyncBody",
+ IDS_SYNC_CONFIRMATION_DICE_CHROME_SYNC_MESSAGE);
+ AddStringResource(source, "syncConfirmationPersonalizeServicesBody",
+ IDS_SYNC_CONFIRMATION_DICE_PERSONALIZE_SERVICES_BODY);
+ AddStringResource(source, "syncConfirmationGoogleServicesBody",
+ IDS_SYNC_CONFIRMATION_DICE_GOOGLE_SERVICES_BODY);
+ AddStringResource(source, "syncConfirmationSyncSettingsLinkBody",
+ IDS_SYNC_CONFIRMATION_DICE_SYNC_SETTINGS_LINK_BODY);
+ AddStringResource(source, "syncConfirmationSyncSettingsDescription",
+ IDS_SYNC_CONFIRMATION_DICE_SYNC_SETTINGS_DESCRIPTION);
+
+ title_ids = IDS_SYNC_CONFIRMATION_UNITY_TITLE;
confirm_button_ids = IDS_SYNC_CONFIRMATION_DICE_CONFIRM_BUTTON_LABEL;
undo_button_ids = IDS_SYNC_CONFIRMATION_DICE_UNDO_BUTTON_LABEL;
} else {
@@ -66,21 +68,22 @@ SyncConfirmationUI::SyncConfirmationUI(content::WebUI* web_ui)
source->AddBoolean("isSyncAllowed", is_sync_allowed);
- source->AddLocalizedString("syncConfirmationChromeSyncTitle",
- IDS_SYNC_CONFIRMATION_CHROME_SYNC_TITLE);
- source->AddLocalizedString("syncConfirmationChromeSyncBody",
- IDS_SYNC_CONFIRMATION_CHROME_SYNC_MESSAGE);
- source->AddLocalizedString(
- "syncConfirmationPersonalizeServicesTitle",
- IDS_SYNC_CONFIRMATION_PERSONALIZE_SERVICES_TITLE);
- source->AddLocalizedString("syncConfirmationPersonalizeServicesBody",
- IDS_SYNC_CONFIRMATION_PERSONALIZE_SERVICES_BODY);
- source->AddLocalizedString("syncConfirmationSyncSettingsLinkBody",
- IDS_SYNC_CONFIRMATION_SYNC_SETTINGS_LINK_BODY);
- source->AddLocalizedString("syncDisabledConfirmationDetails",
- IDS_SYNC_DISABLED_CONFIRMATION_DETAILS);
-
- title_ids = IDS_SYNC_CONFIRMATION_TITLE;
+ AddStringResource(source, "syncConfirmationChromeSyncTitle",
+ IDS_SYNC_CONFIRMATION_CHROME_SYNC_TITLE);
+ AddStringResource(source, "syncConfirmationChromeSyncBody",
+ IDS_SYNC_CONFIRMATION_CHROME_SYNC_MESSAGE);
+ AddStringResource(source, "syncConfirmationPersonalizeServicesTitle",
+ IDS_SYNC_CONFIRMATION_PERSONALIZE_SERVICES_TITLE);
+ AddStringResource(source, "syncConfirmationPersonalizeServicesBody",
+ IDS_SYNC_CONFIRMATION_PERSONALIZE_SERVICES_BODY);
+ AddStringResource(source, "syncConfirmationSyncSettingsLinkBody",
+ IDS_SYNC_CONFIRMATION_SYNC_SETTINGS_LINK_BODY);
+ AddStringResource(source, "syncDisabledConfirmationDetails",
+ IDS_SYNC_DISABLED_CONFIRMATION_DETAILS);
+
+ title_ids = AccountConsistencyModeManager::IsDiceEnabledForProfile(profile)
+ ? IDS_SYNC_CONFIRMATION_DICE_TITLE
+ : IDS_SYNC_CONFIRMATION_TITLE;
confirm_button_ids = IDS_SYNC_CONFIRMATION_CONFIRM_BUTTON_LABEL;
undo_button_ids = IDS_SYNC_CONFIRMATION_UNDO_BUTTON_LABEL;
if (!is_sync_allowed) {
@@ -90,10 +93,13 @@ SyncConfirmationUI::SyncConfirmationUI(content::WebUI* web_ui)
}
}
- source->AddLocalizedString("syncConfirmationTitle", title_ids);
- source->AddLocalizedString("syncConfirmationConfirmLabel",
- confirm_button_ids);
- source->AddLocalizedString("syncConfirmationUndoLabel", undo_button_ids);
+ DCHECK_GE(title_ids, 0);
+ DCHECK_GE(confirm_button_ids, 0);
+ DCHECK_GE(undo_button_ids, 0);
+
+ AddStringResource(source, "syncConfirmationTitle", title_ids);
+ AddStringResource(source, "syncConfirmationConfirmLabel", confirm_button_ids);
+ AddStringResource(source, "syncConfirmationUndoLabel", undo_button_ids);
base::DictionaryValue strings;
webui::SetLoadTimeDataDefaults(
@@ -103,7 +109,27 @@ SyncConfirmationUI::SyncConfirmationUI(content::WebUI* web_ui)
content::WebUIDataSource::Add(profile, source);
}
+SyncConfirmationUI::~SyncConfirmationUI() {}
+
void SyncConfirmationUI::InitializeMessageHandlerWithBrowser(Browser* browser) {
- web_ui()->AddMessageHandler(
- std::make_unique<SyncConfirmationHandler>(browser));
+ web_ui()->AddMessageHandler(std::make_unique<SyncConfirmationHandler>(
+ browser, js_localized_string_to_ids_map_));
+}
+
+void SyncConfirmationUI::AddStringResource(content::WebUIDataSource* source,
+ const std::string& name,
+ int ids) {
+ source->AddLocalizedString(name, ids);
+
+ // When the strings are passed to the HTML, the Unicode NBSP symbol (\u00A0)
+ // will be automatically replaced with "&nbsp;". This change must be mirrored
+ // in the string-to-ids map. Note that "\u00A0" is actually two characters,
+ // so we must use base::ReplaceSubstrings* rather than base::ReplaceChars.
+ // TODO(msramek): Find a more elegant solution.
+ std::string sanitized_string =
+ base::UTF16ToUTF8(l10n_util::GetStringUTF16(ids));
+ base::ReplaceSubstringsAfterOffset(&sanitized_string, 0, "\u00A0" /* NBSP */,
+ "&nbsp;");
+
+ js_localized_string_to_ids_map_[sanitized_string] = ids;
}
diff --git a/chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.h b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.h
index 7d5b5431041..0763d791a89 100644
--- a/chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.h
+++ b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.h
@@ -6,10 +6,16 @@
#define CHROME_BROWSER_UI_WEBUI_SIGNIN_SYNC_CONFIRMATION_UI_H_
#include <memory>
+#include <string>
+#include <unordered_map>
#include "base/macros.h"
#include "chrome/browser/ui/webui/signin/signin_web_dialog_ui.h"
+namespace content {
+class WebUIDataSource;
+}
+
namespace ui {
class WebUI;
}
@@ -21,11 +27,22 @@ class WebUI;
class SyncConfirmationUI : public SigninWebDialogUI {
public:
explicit SyncConfirmationUI(content::WebUI* web_ui);
- ~SyncConfirmationUI() override {}
+ ~SyncConfirmationUI() override;
// SigninWebDialogUI:
void InitializeMessageHandlerWithBrowser(Browser* browser) override;
+ private:
+ // Adds a string resource with the given GRD |ids| to the WebUI data |source|
+ // named as |name|. Also stores a reverse mapping from the localized version
+ // of the string to the |ids| in order to later pass it to
+ // SyncConfirmationHandler.
+ void AddStringResource(content::WebUIDataSource* source,
+ const std::string& name,
+ int ids);
+
+ std::unordered_map<std::string, int> js_localized_string_to_ids_map_;
+
DISALLOW_COPY_AND_ASSIGN(SyncConfirmationUI);
};
diff --git a/chromium/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc b/chromium/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
index dff00446d93..8642979e27a 100644
--- a/chromium/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
+++ b/chromium/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
@@ -51,7 +51,6 @@
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "components/prefs/pref_service.h"
-#include "components/proximity_auth/screenlock_bridge.h"
#include "components/signin/core/account_id/account_id.h"
#include "components/signin/core/browser/profile_management_switches.h"
#include "components/strings/grit/components_strings.h"
@@ -92,7 +91,6 @@ const char kJsApiUserManagerAuthLaunchUser[] = "authenticatedLaunchUser";
const char kJsApiUserManagerLaunchGuest[] = "launchGuest";
const char kJsApiUserManagerLaunchUser[] = "launchUser";
const char kJsApiUserManagerRemoveUser[] = "removeUser";
-const char kJsApiUserManagerAttemptUnlock[] = "attemptUnlock";
const char kJsApiUserManagerLogRemoveUserWarningShown[] =
"logRemoveUserWarningShown";
const char kJsApiUserManagerRemoveUserWarningLoadStats[] =
@@ -102,9 +100,6 @@ const char kJsApiUserManagerAreAllProfilesLocked[] =
const size_t kAvatarIconSize = 180;
const int kMaxOAuthRetries = 3;
-void HandleAndDoNothing(const base::ListValue* args) {
-}
-
std::string GetAvatarImage(const ProfileAttributesEntry* entry) {
bool is_gaia_picture = entry->IsUsingGAIAPicture() &&
entry->GetGAIAPicture() != nullptr;
@@ -123,17 +118,6 @@ std::string GetAvatarImage(const ProfileAttributesEntry* entry) {
return webui::GetBitmapDataUrl(resized_image.AsBitmap());
}
-extensions::ScreenlockPrivateEventRouter* GetScreenlockRouter(
- const std::string& email) {
- base::FilePath path =
- profiles::GetPathOfProfileWithEmail(g_browser_process->profile_manager(),
- email);
- Profile* profile = g_browser_process->profile_manager()
- ->GetProfileByPath(path);
- return extensions::ScreenlockPrivateEventRouter::GetFactoryInstance()->Get(
- profile);
-}
-
bool IsGuestModeEnabled() {
PrefService* service = g_browser_process->local_state();
DCHECK(service);
@@ -537,7 +521,6 @@ void UserManagerScreenHandler::HandleRemoveUser(const base::ListValue* args) {
// The callback is run if the only profile has been deleted, and a new
// profile has been created to replace it.
webui::DeleteProfileAtPath(profile_path,
- web_ui(),
ProfileMetrics::DELETE_PROFILE_USER_MANAGER);
}
@@ -598,14 +581,6 @@ void UserManagerScreenHandler::HandleLaunchUser(const base::ListValue* args) {
ProfileMetrics::SWITCH_PROFILE_MANAGER);
}
-void UserManagerScreenHandler::HandleAttemptUnlock(
- const base::ListValue* args) {
- std::string email;
- CHECK(args->GetString(0, &email));
- GetScreenlockRouter(email)
- ->OnAuthAttempted(GetAuthType(AccountId::FromUserEmail(email)), "");
-}
-
void UserManagerScreenHandler::HandleHardlockUserPod(
const base::ListValue* args) {
std::string email;
@@ -712,9 +687,6 @@ void UserManagerScreenHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback(kJsApiUserManagerRemoveUser,
base::Bind(&UserManagerScreenHandler::HandleRemoveUser,
base::Unretained(this)));
- web_ui()->RegisterMessageCallback(kJsApiUserManagerAttemptUnlock,
- base::Bind(&UserManagerScreenHandler::HandleAttemptUnlock,
- base::Unretained(this)));
web_ui()->RegisterMessageCallback(kJsApiUserManagerLogRemoveUserWarningShown,
base::Bind(&HandleLogRemoveUserWarningShown));
web_ui()->RegisterMessageCallback(kJsApiUserManagerRemoveUserWarningLoadStats,
@@ -725,21 +697,18 @@ void UserManagerScreenHandler::RegisterMessages() {
base::Bind(&UserManagerScreenHandler::HandleAreAllProfilesLocked,
base::Unretained(this)));
- const content::WebUI::MessageCallback& kDoNothingCallback =
- base::Bind(&HandleAndDoNothing);
-
// Unused callbacks from screen_account_picker.js
- web_ui()->RegisterMessageCallback("accountPickerReady", kDoNothingCallback);
- web_ui()->RegisterMessageCallback("loginUIStateChanged", kDoNothingCallback);
- web_ui()->RegisterMessageCallback("hideCaptivePortal", kDoNothingCallback);
- web_ui()->RegisterMessageCallback("getTabletModeState", kDoNothingCallback);
+ web_ui()->RegisterMessageCallback("accountPickerReady", base::DoNothing());
+ web_ui()->RegisterMessageCallback("loginUIStateChanged", base::DoNothing());
+ web_ui()->RegisterMessageCallback("hideCaptivePortal", base::DoNothing());
+ web_ui()->RegisterMessageCallback("getTabletModeState", base::DoNothing());
// Unused callbacks from display_manager.js
- web_ui()->RegisterMessageCallback("showAddUser", kDoNothingCallback);
- web_ui()->RegisterMessageCallback("updateCurrentScreen", kDoNothingCallback);
- web_ui()->RegisterMessageCallback("loginVisible", kDoNothingCallback);
+ web_ui()->RegisterMessageCallback("showAddUser", base::DoNothing());
+ web_ui()->RegisterMessageCallback("updateCurrentScreen", base::DoNothing());
+ web_ui()->RegisterMessageCallback("loginVisible", base::DoNothing());
// Unused callbacks from user_pod_row.js
- web_ui()->RegisterMessageCallback("focusPod", kDoNothingCallback);
- web_ui()->RegisterMessageCallback("noPodFocused", kDoNothingCallback);
+ web_ui()->RegisterMessageCallback("focusPod", base::DoNothing());
+ web_ui()->RegisterMessageCallback("noPodFocused", base::DoNothing());
}
void UserManagerScreenHandler::GetLocalizedValues(
diff --git a/chromium/chrome/browser/ui/webui/signin_internals_ui.cc b/chromium/chrome/browser/ui/webui/signin_internals_ui.cc
index e377c6f7767..b94b04d84b6 100644
--- a/chromium/chrome/browser/ui/webui/signin_internals_ui.cc
+++ b/chromium/chrome/browser/ui/webui/signin_internals_ui.cc
@@ -12,13 +12,10 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/about_signin_internals_factory.h"
#include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
-#include "chrome/browser/ui/webui/signin/signin_dice_internals_handler.h"
#include "chrome/common/url_constants.h"
#include "components/grit/components_resources.h"
#include "components/signin/core/browser/about_signin_internals.h"
#include "components/signin/core/browser/gaia_cookie_manager_service.h"
-#include "components/signin/core/browser/profile_management_switches.h"
-#include "components/signin/core/browser/signin_features.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
@@ -46,12 +43,6 @@ SignInInternalsUI::SignInInternalsUI(content::WebUI* web_ui)
AboutSigninInternalsFactory::GetForProfile(profile);
if (about_signin_internals)
about_signin_internals->AddSigninObserver(this);
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
- if (signin::IsDiceEnabledForProfile(profile->GetPrefs())) {
- web_ui->AddMessageHandler(
- std::make_unique<SigninDiceInternalsHandler>(profile));
- }
-#endif
}
}
diff --git a/chromium/chrome/browser/ui/webui/site_settings_helper.cc b/chromium/chrome/browser/ui/webui/site_settings_helper.cc
index 4f4d73d167d..cf47f689458 100644
--- a/chromium/chrome/browser/ui/webui/site_settings_helper.cc
+++ b/chromium/chrome/browser/ui/webui/site_settings_helper.cc
@@ -69,11 +69,13 @@ const ContentSettingsTypeNameEntry kContentSettingsTypeGroupNames[] = {
{CONTENT_SETTINGS_TYPE_PPAPI_BROKER, "ppapi-broker"},
{CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, "multiple-automatic-downloads"},
{CONTENT_SETTINGS_TYPE_MIDI_SYSEX, "midi-sysex"},
- {CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER, "protectedContent"},
+ {CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER, "protected-content"},
{CONTENT_SETTINGS_TYPE_BACKGROUND_SYNC, "background-sync"},
{CONTENT_SETTINGS_TYPE_ADS, "ads"},
{CONTENT_SETTINGS_TYPE_SOUND, "sound"},
{CONTENT_SETTINGS_TYPE_CLIPBOARD_READ, "clipboard"},
+ {CONTENT_SETTINGS_TYPE_SENSORS, "sensors"},
+ {CONTENT_SETTINGS_TYPE_PAYMENT_HANDLER, "payment-handler"},
// Add new content settings here if a corresponding Javascript string
// representation for it is not required. Note some exceptions, such as
@@ -96,9 +98,9 @@ const ContentSettingsTypeNameEntry kContentSettingsTypeGroupNames[] = {
{CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, nullptr},
{CONTENT_SETTINGS_TYPE_MEDIA_ENGAGEMENT, nullptr},
{CONTENT_SETTINGS_TYPE_CLIENT_HINTS, nullptr},
- {CONTENT_SETTINGS_TYPE_SENSORS, nullptr},
{CONTENT_SETTINGS_TYPE_ACCESSIBILITY_EVENTS, nullptr},
{CONTENT_SETTINGS_TYPE_CLIPBOARD_WRITE, nullptr},
+ {CONTENT_SETTINGS_TYPE_PLUGINS_DATA, nullptr},
};
static_assert(arraysize(kContentSettingsTypeGroupNames) ==
// ContentSettingsType starts at -1, so add 1 here.
diff --git a/chromium/chrome/browser/ui/webui/snippets_internals_message_handler.cc b/chromium/chrome/browser/ui/webui/snippets_internals_message_handler.cc
index 206412cb5dd..52527a8d360 100644
--- a/chromium/chrome/browser/ui/webui/snippets_internals_message_handler.cc
+++ b/chromium/chrome/browser/ui/webui/snippets_internals_message_handler.cc
@@ -21,9 +21,9 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
+#include "base/time/time_to_iso8601.h"
#include "base/values.h"
#include "chrome/browser/android/chrome_feature_list.h"
#include "chrome/browser/android/ntp/android_content_suggestions_notifier.h"
@@ -162,15 +162,6 @@ std::set<variations::VariationID> SnippetsExperiments() {
return result;
}
-std::string TimeToJSONTimeString(const base::Time time) {
- base::Time::Exploded exploded;
- time.UTCExplode(&exploded);
- return base::StringPrintf(
- "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", exploded.year, exploded.month,
- exploded.day_of_month, exploded.hour, exploded.minute, exploded.second,
- exploded.millisecond);
-}
-
ntp_snippets::BreakingNewsListener* GetBreakingNewsListener(
ntp_snippets::ContentSuggestionsService* service) {
DCHECK(service);
@@ -331,6 +322,7 @@ void SnippetsInternalsMessageHandler::HandleDownload(
DCHECK_EQ(0u, args->GetSize());
SendString("remote-status", std::string());
+ SendString("remote-authenticated", std::string());
if (!remote_suggestions_provider_) {
return;
@@ -611,6 +603,12 @@ void SnippetsInternalsMessageHandler::SendContentSuggestions() {
->GetLastStatusForDebugging();
if (!status.empty()) {
SendString("remote-status", "Finished: " + status);
+ SendString(
+ "remote-authenticated",
+ remote_suggestions_provider_->suggestions_fetcher_for_debugging()
+ ->WasLastFetchAuthenticatedForDebugging()
+ ? "Authenticated"
+ : "Non-authenticated");
}
}
@@ -664,8 +662,8 @@ void SnippetsInternalsMessageHandler::PushDummySuggestion() {
const base::Time now = base::Time::Now();
json = base::StringPrintf(
json.c_str(), base::UTF16ToUTF8(base::TimeFormatTimeOfDay(now)).c_str(),
- TimeToJSONTimeString(now).c_str(),
- TimeToJSONTimeString(now + base::TimeDelta::FromMinutes(60)).c_str());
+ base::TimeToISO8601(now).c_str(),
+ base::TimeToISO8601(now + base::TimeDelta::FromMinutes(60)).c_str());
gcm::IncomingMessage message;
message.data["payload"] = json;
diff --git a/chromium/chrome/browser/ui/webui/theme_source.cc b/chromium/chrome/browser/ui/webui/theme_source.cc
index 1aa3453da8a..f3681500ad5 100644
--- a/chromium/chrome/browser/ui/webui/theme_source.cc
+++ b/chromium/chrome/browser/ui/webui/theme_source.cc
@@ -117,6 +117,7 @@ void ThemeSource::StartDataRequest(
case version_info::Channel::BETA:
case version_info::Channel::STABLE:
NOTREACHED();
+ FALLTHROUGH;
#endif
case version_info::Channel::UNKNOWN:
resource_id = IDR_PRODUCT_LOGO_32;
diff --git a/chromium/chrome/browser/ui/webui/usb_internals/BUILD.gn b/chromium/chrome/browser/ui/webui/usb_internals/BUILD.gn
index 4d0059e3ae2..1ed96b376e7 100644
--- a/chromium/chrome/browser/ui/webui/usb_internals/BUILD.gn
+++ b/chromium/chrome/browser/ui/webui/usb_internals/BUILD.gn
@@ -10,7 +10,7 @@ mojom("mojo_bindings") {
]
public_deps = [
- "//url/mojo:url_mojom_gurl",
- "//url/mojo:url_mojom_origin",
+ "//url/mojom:url_mojom_gurl",
+ "//url/mojom:url_mojom_origin",
]
}
diff --git a/chromium/chrome/browser/ui/webui/usb_internals/usb_internals.mojom b/chromium/chrome/browser/ui/webui/usb_internals/usb_internals.mojom
index 408c09dfdc6..37ec5de0c3a 100644
--- a/chromium/chrome/browser/ui/webui/usb_internals/usb_internals.mojom
+++ b/chromium/chrome/browser/ui/webui/usb_internals/usb_internals.mojom
@@ -4,8 +4,8 @@
module mojom;
-import "url/mojo/origin.mojom";
-import "url/mojo/url.mojom";
+import "url/mojom/origin.mojom";
+import "url/mojom/url.mojom";
struct TestDeviceInfo {
string guid;
diff --git a/chromium/chrome/browser/ui/webui/usb_internals/usb_internals_ui.cc b/chromium/chrome/browser/ui/webui/usb_internals/usb_internals_ui.cc
index 0de8441ea74..36785e19586 100644
--- a/chromium/chrome/browser/ui/webui/usb_internals/usb_internals_ui.cc
+++ b/chromium/chrome/browser/ui/webui/usb_internals/usb_internals_ui.cc
@@ -20,8 +20,8 @@ UsbInternalsUI::UsbInternalsUI(content::WebUI* web_ui)
source->AddResourcePath(
"chrome/browser/ui/webui/usb_internals/usb_internals.mojom.js",
IDR_USB_INTERNALS_MOJO_JS);
- source->AddResourcePath("url/mojo/origin.mojom.js", IDR_ORIGIN_MOJO_JS);
- source->AddResourcePath("url/mojo/url.mojom.js", IDR_URL_MOJO_JS);
+ source->AddResourcePath("url/mojom/origin.mojom.js", IDR_ORIGIN_MOJO_JS);
+ source->AddResourcePath("url/mojom/url.mojom.js", IDR_URL_MOJO_JS);
source->SetDefaultResource(IDR_USB_INTERNALS_HTML);
source->UseGzip();
diff --git a/chromium/chrome/browser/ui/webui/version_handler.cc b/chromium/chrome/browser/ui/webui/version_handler.cc
index ebfc7d09590..9bd498db4c0 100644
--- a/chromium/chrome/browser/ui/webui/version_handler.cc
+++ b/chromium/chrome/browser/ui/webui/version_handler.cc
@@ -23,6 +23,7 @@
#include "components/version_ui/version_ui_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/plugin_service.h"
+#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/common/content_constants.h"
#include "ppapi/features/features.h"
@@ -53,9 +54,7 @@ void GetFilePaths(const base::FilePath& profile_path,
} // namespace
-VersionHandler::VersionHandler()
- : weak_ptr_factory_(this) {
-}
+VersionHandler::VersionHandler() : weak_ptr_factory_(this) {}
VersionHandler::~VersionHandler() {
}
@@ -68,6 +67,7 @@ void VersionHandler::RegisterMessages() {
}
void VersionHandler::HandleRequestVersionInfo(const base::ListValue* args) {
+ AllowJavascript();
#if BUILDFLAG(ENABLE_PLUGINS)
// The Flash version information is needed in the response, so make sure
// the plugins are loaded.
@@ -90,8 +90,14 @@ void VersionHandler::HandleRequestVersionInfo(const base::ListValue* args) {
base::Owned(exec_path_buffer), base::Owned(profile_path_buffer)));
// Respond with the variations info immediately.
- web_ui()->CallJavascriptFunctionUnsafe(version_ui::kReturnVariationInfo,
- *version_ui::GetVariationsList());
+ CallJavascriptFunction(version_ui::kReturnVariationInfo,
+ *version_ui::GetVariationsList());
+ GURL current_url = web_ui()->GetWebContents()->GetVisibleURL();
+ if (current_url.query().find(version_ui::kVariationsShowCmdQuery) !=
+ std::string::npos) {
+ CallJavascriptFunction(version_ui::kReturnVariationCmd,
+ version_ui::GetVariationsCommandLineAsValue());
+ }
}
void VersionHandler::OnGotFilePaths(base::string16* executable_path_data,
@@ -100,8 +106,7 @@ void VersionHandler::OnGotFilePaths(base::string16* executable_path_data,
base::Value exec_path(*executable_path_data);
base::Value profile_path(*profile_path_data);
- web_ui()->CallJavascriptFunctionUnsafe(version_ui::kReturnFilePaths,
- exec_path, profile_path);
+ CallJavascriptFunction(version_ui::kReturnFilePaths, exec_path, profile_path);
}
#if BUILDFLAG(ENABLE_PLUGINS)
@@ -128,6 +133,6 @@ void VersionHandler::OnGotPlugins(
base::Value arg(flash_version_and_path);
- web_ui()->CallJavascriptFunctionUnsafe(version_ui::kReturnFlashVersion, arg);
+ CallJavascriptFunction(version_ui::kReturnFlashVersion, arg);
}
#endif // BUILDFLAG(ENABLE_PLUGINS)
diff --git a/chromium/chrome/browser/ui/webui/version_ui.cc b/chromium/chrome/browser/ui/webui/version_ui.cc
index 456e94e4f58..4d83440102f 100644
--- a/chromium/chrome/browser/ui/webui/version_ui.cc
+++ b/chromium/chrome/browser/ui/webui/version_ui.cc
@@ -90,7 +90,8 @@ WebUIDataSource* CreateVersionUIDataSource() {
html_source->AddString(version_ui::kProfilePath, std::string());
html_source->AddLocalizedString(version_ui::kVariationsName,
IDS_VERSION_UI_VARIATIONS);
-
+ html_source->AddLocalizedString(version_ui::kVariationsCmdName,
+ IDS_VERSION_UI_VARIATIONS_CMD);
#if defined(OS_CHROMEOS)
html_source->AddLocalizedString(version_ui::kARC, IDS_ARC_LABEL);
html_source->AddLocalizedString(version_ui::kPlatform, IDS_PLATFORM_LABEL);
diff --git a/chromium/chrome/browser/ui/webui/web_ui_test_handler.cc b/chromium/chrome/browser/ui/webui/web_ui_test_handler.cc
index 9f070d576ed..8ae8cd38394 100644
--- a/chromium/chrome/browser/ui/webui/web_ui_test_handler.cc
+++ b/chromium/chrome/browser/ui/webui/web_ui_test_handler.cc
@@ -21,7 +21,7 @@
#include "content/public/browser/web_ui.h"
#include "content/public/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/WebKit/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/WebKit/public/common/associated_interfaces/associated_interface_provider.h"
using content::RenderViewHost;
diff --git a/chromium/chrome/browser/ui/webui/webui_browsertest.cc b/chromium/chrome/browser/ui/webui/webui_browsertest.cc
index 70289d0a452..95e722ad1f2 100644
--- a/chromium/chrome/browser/ui/webui/webui_browsertest.cc
+++ b/chromium/chrome/browser/ui/webui/webui_browsertest.cc
@@ -3,14 +3,18 @@
// found in the LICENSE file.
#include "base/command_line.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
+#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/url_constants.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/child_process_security_policy.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
@@ -18,6 +22,7 @@
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
+#include "ui/base/l10n/l10n_util.h"
namespace {
@@ -64,7 +69,7 @@ IN_PROC_BROWSER_TEST_F(WebUIImplBrowserTest, ForceSwapOnDifferenteWebUITypes) {
web_contents->GetMainFrame()->GetProcess()->GetID()));
}
-IN_PROC_BROWSER_TEST_F(WebUIImplBrowserTest, InPageNavigationsAndReload) {
+IN_PROC_BROWSER_TEST_F(WebUIImplBrowserTest, SameDocumentNavigationsAndReload) {
ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUITermsURL));
content::WebUIMessageHandler* test_handler = new TestWebUIMessageHandler;
@@ -93,3 +98,18 @@ IN_PROC_BROWSER_TEST_F(WebUIImplBrowserTest, InPageNavigationsAndReload) {
// Verify that after a reload, the test handler has been disallowed.
EXPECT_FALSE(test_handler->IsJavascriptAllowed());
}
+
+// Tests that navigating to chrome://connection-help displays the proper help
+// page.
+IN_PROC_BROWSER_TEST_F(WebUIImplBrowserTest, ConnectionHelpUI) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(features::kBundledConnectionHelpFeature);
+ ui_test_utils::NavigateToURL(browser(),
+ GURL(chrome::kChromeUIConnectionHelpURL));
+ content::WaitForLoadStop(
+ browser()->tab_strip_model()->GetActiveWebContents());
+ base::string16 tab_title;
+ ui_test_utils::GetCurrentTabTitle(browser(), &tab_title);
+ EXPECT_EQ(base::UTF16ToUTF8(tab_title),
+ l10n_util::GetStringUTF8(IDS_CONNECTION_HELP_TITLE));
+}
diff --git a/chromium/chrome/browser/ui/webui/welcome_handler.cc b/chromium/chrome/browser/ui/webui/welcome_handler.cc
index c764d29a70e..d989be56940 100644
--- a/chromium/chrome/browser/ui/webui/welcome_handler.cc
+++ b/chromium/chrome/browser/ui/webui/welcome_handler.cc
@@ -23,12 +23,18 @@ WelcomeHandler::WelcomeHandler(content::WebUI* web_ui)
login_ui_service_(LoginUIServiceFactory::GetForProfile(profile_)),
result_(WelcomeResult::DEFAULT) {
login_ui_service_->AddObserver(this);
- base::RecordAction(
- base::UserMetricsAction("Signin_Impression_FromStartPage"));
}
WelcomeHandler::~WelcomeHandler() {
login_ui_service_->RemoveObserver(this);
+
+ // We log that an impression occurred at destruct-time. This can't be done at
+ // construct-time on some platforms because this page is shown immediately
+ // after a new installation of Chrome and loads while the user is deciding
+ // whether or not to opt in to logging.
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Impression_FromStartPage"));
+
UMA_HISTOGRAM_ENUMERATION("Welcome.SignInPromptResult", result_,
WelcomeResult::WELCOME_RESULT_MAX);
}
diff --git a/chromium/chrome/browser/vr/BUILD.gn b/chromium/chrome/browser/vr/BUILD.gn
index b3e3d513722..ed0b63c8375 100644
--- a/chromium/chrome/browser/vr/BUILD.gn
+++ b/chromium/chrome/browser/vr/BUILD.gn
@@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//build/buildflag_header.gni")
+import("//chrome/browser/vr/features.gni")
import("//chrome/common/features.gni")
import("//device/vr/features/features.gni")
import("//testing/test.gni")
@@ -13,10 +15,15 @@ if (is_android) {
assert(enable_vr)
+buildflag_header("vr_build_features") {
+ header = "vr_features.h"
+ flags = [ "USE_VR_ASSETS_COMPONENT=$use_vr_assets_component" ]
+}
+
static_library("vr_common") {
sources = [
- "animation_player.cc",
- "animation_player.h",
+ "animation.cc",
+ "animation.h",
"assets_component_update_status.h",
"assets_load_status.h",
"assets_loader.cc",
@@ -24,17 +31,12 @@ static_library("vr_common") {
"browser_ui_interface.h",
"content_input_delegate.cc",
"content_input_delegate.h",
- "controller_mesh.h",
"cpu_surface_provider.cc",
"cpu_surface_provider.h",
"databinding/binding.h",
"databinding/binding_base.h",
"databinding/vector_binding.h",
"databinding/vector_element_binding.h",
- "elements/audio_permission_prompt.cc",
- "elements/audio_permission_prompt.h",
- "elements/audio_permission_prompt_texture.cc",
- "elements/audio_permission_prompt_texture.h",
"elements/button.cc",
"elements/button.h",
"elements/content_element.cc",
@@ -68,6 +70,12 @@ static_library("vr_common") {
"elements/linear_layout.h",
"elements/omnibox_formatting.cc",
"elements/omnibox_formatting.h",
+ "elements/omnibox_text_field.cc",
+ "elements/omnibox_text_field.h",
+ "elements/prompt.cc",
+ "elements/prompt.h",
+ "elements/prompt_texture.cc",
+ "elements/prompt_texture.h",
"elements/rect.cc",
"elements/rect.h",
"elements/render_text_wrapper.cc",
@@ -122,10 +130,6 @@ static_library("vr_common") {
"fps_meter.h",
"ganesh_surface_provider.cc",
"ganesh_surface_provider.h",
- "gltf_asset.cc",
- "gltf_asset.h",
- "gltf_parser.cc",
- "gltf_parser.h",
"keyboard_delegate.h",
"keyboard_ui_interface.h",
"macros.h",
@@ -135,16 +139,18 @@ static_library("vr_common") {
"model/assets.cc",
"model/assets.h",
"model/camera_model.h",
+ "model/capturing_state_model.h",
"model/color_scheme.cc",
"model/color_scheme.h",
+ "model/controller_model.cc",
"model/controller_model.h",
"model/modal_prompt_type.cc",
"model/modal_prompt_type.h",
"model/model.cc",
"model/model.h",
+ "model/native_ui_model.h",
"model/omnibox_suggestions.cc",
"model/omnibox_suggestions.h",
- "model/permissions_model.h",
"model/reticle_model.h",
"model/speech_recognition_model.h",
"model/text_input_info.cc",
@@ -181,6 +187,8 @@ static_library("vr_common") {
"speech_recognizer.h",
"target_property.cc",
"target_property.h",
+ "text_edit_action.cc",
+ "text_edit_action.h",
"text_input_delegate.cc",
"text_input_delegate.h",
"toolbar_helper.cc",
@@ -201,23 +209,17 @@ static_library("vr_common") {
"ui_scene_creator.cc",
"ui_scene_creator.h",
"ui_unsupported_mode.h",
+ "vr_features.h",
"vr_gl_util.cc",
"vr_gl_util.h",
- "web_contents_event_forwarder.cc",
- "web_contents_event_forwarder.h",
]
public_deps = [
"//ui/gl",
]
- if (enable_gvr_services) {
- sources += [ "controller_mesh.cc" ]
-
- public_deps += [ "//chrome/browser/resources:vr_shell_resources" ]
- }
-
deps = [
+ ":vr_build_features",
"//base",
"//cc/animation",
"//cc/paint",
@@ -232,8 +234,8 @@ static_library("vr_common") {
"//components/vector_icons",
"//content/public/browser",
"//content/public/common",
- "//device/vr:mojo_bindings",
- "//device/vr:vr",
+ "//device/vr",
+ "//device/vr/public/mojom",
"//net",
"//skia",
"//ui/base",
@@ -244,19 +246,22 @@ static_library("vr_common") {
test("vr_common_unittests") {
sources = [
- "animation_player_unittest.cc",
+ "animation_unittest.cc",
"databinding/binding_unittest.cc",
"databinding/vector_binding_unittest.cc",
"elements/button_unittest.cc",
+ "elements/content_element_unittest.cc",
"elements/disc_button_unittest.cc",
"elements/exit_prompt_unittest.cc",
"elements/linear_layout_unittest.cc",
"elements/omnibox_formatting_unittest.cc",
+ "elements/omnibox_text_field_unittest.cc",
"elements/rect_unittest.cc",
"elements/repositioner_unittest.cc",
"elements/scaled_depth_adjuster_unittest.cc",
"elements/shadow_unittest.cc",
"elements/spinner_unittest.cc",
+ "elements/text_input_unittest.cc",
"elements/text_unittest.cc",
"elements/throbber_unittest.cc",
"elements/transient_element_unittest.cc",
@@ -266,7 +271,7 @@ test("vr_common_unittests") {
"elements/vector_icon_unittest.cc",
"elements/viewport_aware_root_unittest.cc",
"fps_meter_unittest.cc",
- "gltf_parser_unittest.cc",
+ "model/text_input_info_unittest.cc",
"pose_util_unittest.cc",
"service/vr_device_manager_unittest.cc",
"sliding_average_unittest.cc",
@@ -278,7 +283,6 @@ test("vr_common_unittests") {
"test/run_all_unittests.cc",
"test/ui_test.cc",
"test/ui_test.h",
- "text_input_unittest.cc",
"ui_input_manager_unittest.cc",
"ui_scene_unittest.cc",
"ui_unittest.cc",
@@ -355,20 +359,18 @@ source_set("vr_test_support") {
"test/mock_browser_ui_interface.h",
"test/mock_content_input_delegate.cc",
"test/mock_content_input_delegate.h",
+ "test/mock_keyboard_delegate.cc",
+ "test/mock_keyboard_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",
"test/vr_test_suite.h",
]
- if (!enable_gvr_services) {
- # For testing, we add back controller_mesh.cc on platforms where it isn't
- # included by vr_common.
- sources += [ "controller_mesh.cc" ]
- }
-
public_deps = [
":vr_common",
":vr_test_pak",
@@ -380,7 +382,7 @@ source_set("vr_test_support") {
"//mojo/edk/system",
"//skia",
"//testing/gtest",
- "//ui/accessibility:ax_gen",
+ "//ui/accessibility:ax_enums_mojo",
"//ui/gfx:test_support",
# TODO(mthiesse, crbug.com/769373): Remove dependency on device/vr:fakes.
@@ -391,7 +393,6 @@ source_set("vr_test_support") {
]
data = [
- "test/data/",
"$root_out_dir/vr_test.pak",
]
}
@@ -415,7 +416,6 @@ source_set("vr_gl_test_support") {
repack("vr_test_pak") {
sources = [
"$root_gen_dir/chrome/generated_resources_en-US.pak",
- "$root_gen_dir/chrome/vr_shell_resources.pak",
"$root_gen_dir/components/components_resources.pak",
"$root_gen_dir/components/strings/components_strings_en-US.pak",
]
@@ -424,7 +424,6 @@ repack("vr_test_pak") {
deps = [
"//chrome/app:generated_resources",
- "//chrome/browser/resources:vr_shell_resources",
"//components/resources:components_resources",
"//components/strings",
]
diff --git a/chromium/chrome/browser/vr/testapp/BUILD.gn b/chromium/chrome/browser/vr/testapp/BUILD.gn
index 0aedbbcac45..8a28aac17cc 100644
--- a/chromium/chrome/browser/vr/testapp/BUILD.gn
+++ b/chromium/chrome/browser/vr/testapp/BUILD.gn
@@ -48,7 +48,7 @@ process_version("assets_component_version_header") {
grit("vr_testapp_resources") {
source = "vr_testapp_resources.grd"
- defines = [ "background_image_available=$is_chrome_branded" ]
+ defines = [ "is_chrome_branded=$is_chrome_branded" ]
outputs = [
"grit/vr_testapp_resources.h",
"vr_testapp_resources.pak",
diff --git a/chromium/chrome/browser/vr/testapp/vr_testapp_resources.grd b/chromium/chrome/browser/vr/testapp/vr_testapp_resources.grd
index 0a01cb0fed4..29935f29ee4 100644
--- a/chromium/chrome/browser/vr/testapp/vr_testapp_resources.grd
+++ b/chromium/chrome/browser/vr/testapp/vr_testapp_resources.grd
@@ -8,12 +8,20 @@
</outputs>
<release seq="1">
<includes>
- <if expr="background_image_available">
- <include name="IDR_VR_BACKGROUND_IMAGE" file="../../resources/vr/assets/background.png" type="BINDATA" />
- <include name="IDR_VR_NORMAL_GRADIENT_IMAGE" file="../../resources/vr/assets/normal_gradient.png" type="BINDATA" />
- <include name="IDR_VR_INCOGNITO_GRADIENT_IMAGE" file="../../resources/vr/assets/incognito_gradient.png" type="BINDATA" />
- <include name="IDR_VR_FULLSCREEN_GRADIENT_IMAGE" file="../../resources/vr/assets/fullscreen_gradient.png" type="BINDATA" />
- </if>
+ <if expr="is_chrome_branded">
+ <then>
+ <include name="IDR_VR_BACKGROUND_IMAGE" file="../../resources/vr/assets/google_chrome/background.png" type="BINDATA" />
+ <include name="IDR_VR_NORMAL_GRADIENT_IMAGE" file="../../resources/vr/assets/google_chrome/normal_gradient.png" type="BINDATA" />
+ <include name="IDR_VR_INCOGNITO_GRADIENT_IMAGE" file="../../resources/vr/assets/google_chrome/incognito_gradient.png" type="BINDATA" />
+ <include name="IDR_VR_FULLSCREEN_GRADIENT_IMAGE" file="../../resources/vr/assets/google_chrome/fullscreen_gradient.png" type="BINDATA" />
+ </then>
+ <else>
+ <include name="IDR_VR_BACKGROUND_IMAGE" file="../../resources/vr/assets/chromium/background.png" type="BINDATA" />
+ <include name="IDR_VR_NORMAL_GRADIENT_IMAGE" file="../../resources/vr/assets/chromium/normal_gradient.png" type="BINDATA" />
+ <include name="IDR_VR_INCOGNITO_GRADIENT_IMAGE" file="../../resources/vr/assets/chromium/incognito_gradient.png" type="BINDATA" />
+ <include name="IDR_VR_FULLSCREEN_GRADIENT_IMAGE" file="../../resources/vr/assets/chromium/fullscreen_gradient.png" type="BINDATA" />
+ </else>
+ </if>
</includes>
</release>
</grit>
diff --git a/chromium/chrome/browser/vr/vector_icons/BUILD.gn b/chromium/chrome/browser/vr/vector_icons/BUILD.gn
index 55685ab4890..5414141a212 100644
--- a/chromium/chrome/browser/vr/vector_icons/BUILD.gn
+++ b/chromium/chrome/browser/vr/vector_icons/BUILD.gn
@@ -8,8 +8,11 @@ aggregate_vector_icons("vr_vector_icons") {
icon_directory = "."
icons = [
- "sad_tab.icon",
"file_download_done.icon",
+ "reposition.icon",
+ "sad_tab.icon",
+ "daydream_controller_home_button.icon",
+ "daydream_controller_app_button.icon",
]
}