summaryrefslogtreecommitdiffstats
path: root/chromium/chrome
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2017-09-12 10:27:29 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-09-12 08:34:13 +0000
commit53d399fe6415a96ea6986ec0d402a9c07da72453 (patch)
treece7dbd5d170326a7d1c5f69f5dcd841c178e0dc0 /chromium/chrome
parenta3ee7849e3b0ad3d5f9595fa1cfd694c22dcee2a (diff)
BASELINE: Update Chromium to 60.0.3112.116 and Ninja to 1.8.2
Also adds a few devtools and webui files needed for new features. Change-Id: I431976cc9f4c209d062a925ab6a5d63ec61abcfe Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/chrome')
-rw-r--r--chromium/chrome/VERSION2
-rw-r--r--chromium/chrome/browser/devtools/OWNERS7
-rw-r--r--chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc494
-rw-r--r--chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h89
-rw-r--r--chromium/chrome/browser/devtools/device/adb/adb_client_socket.cc304
-rw-r--r--chromium/chrome/browser/devtools/device/adb/adb_client_socket.h63
-rw-r--r--chromium/chrome/browser/devtools/device/adb/adb_client_socket_browsertest.cc159
-rw-r--r--chromium/chrome/browser/devtools/device/adb/adb_device_provider.cc69
-rw-r--r--chromium/chrome/browser/devtools/device/adb/adb_device_provider.h25
-rw-r--r--chromium/chrome/browser/devtools/device/adb/mock_adb_server.cc625
-rw-r--r--chromium/chrome/browser/devtools/device/adb/mock_adb_server.h52
-rw-r--r--chromium/chrome/browser/devtools/device/android_device_info_query.cc370
-rw-r--r--chromium/chrome/browser/devtools/device/android_device_manager.cc569
-rw-r--r--chromium/chrome/browser/devtools/device/android_device_manager.h249
-rw-r--r--chromium/chrome/browser/devtools/device/android_web_socket.cc226
-rw-r--r--chromium/chrome/browser/devtools/device/cast_device_provider.cc206
-rw-r--r--chromium/chrome/browser/devtools/device/cast_device_provider.h63
-rw-r--r--chromium/chrome/browser/devtools/device/cast_device_provider_unittest.cc109
-rw-r--r--chromium/chrome/browser/devtools/device/devtools_android_bridge.cc406
-rw-r--r--chromium/chrome/browser/devtools/device/devtools_android_bridge.h193
-rw-r--r--chromium/chrome/browser/devtools/device/devtools_android_bridge_browsertest.cc144
-rw-r--r--chromium/chrome/browser/devtools/device/devtools_device_discovery.cc626
-rw-r--r--chromium/chrome/browser/devtools/device/devtools_device_discovery.h145
-rw-r--r--chromium/chrome/browser/devtools/device/port_forwarding_browsertest.cc190
-rw-r--r--chromium/chrome/browser/devtools/device/port_forwarding_controller.cc500
-rw-r--r--chromium/chrome/browser/devtools/device/port_forwarding_controller.h53
-rw-r--r--chromium/chrome/browser/devtools/device/tcp_device_provider.cc140
-rw-r--r--chromium/chrome/browser/devtools/device/tcp_device_provider.h46
-rw-r--r--chromium/chrome/browser/devtools/device/usb/android_rsa.cc280
-rw-r--r--chromium/chrome/browser/devtools/device/usb/android_rsa.h24
-rw-r--r--chromium/chrome/browser/devtools/device/usb/android_usb_browsertest.cc790
-rw-r--r--chromium/chrome/browser/devtools/device/usb/android_usb_device.cc692
-rw-r--r--chromium/chrome/browser/devtools/device/usb/android_usb_device.h171
-rw-r--r--chromium/chrome/browser/devtools/device/usb/android_usb_socket.cc262
-rw-r--r--chromium/chrome/browser/devtools/device/usb/android_usb_socket.h85
-rw-r--r--chromium/chrome/browser/devtools/device/usb/usb_device_provider.cc154
-rw-r--r--chromium/chrome/browser/devtools/device/usb/usb_device_provider.h46
-rw-r--r--chromium/chrome/browser/devtools/devtools_auto_opener.cc24
-rw-r--r--chromium/chrome/browser/devtools/devtools_auto_opener.h28
-rw-r--r--chromium/chrome/browser/devtools/devtools_contents_resizing_strategy.cc53
-rw-r--r--chromium/chrome/browser/devtools/devtools_contents_resizing_strategy.h48
-rw-r--r--chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc221
-rw-r--r--chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.h110
-rw-r--r--chromium/chrome/browser/devtools/devtools_eye_dropper.cc249
-rw-r--r--chromium/chrome/browser/devtools/devtools_eye_dropper.h55
-rw-r--r--chromium/chrome/browser/devtools/devtools_file_helper.cc465
-rw-r--r--chromium/chrome/browser/devtools/devtools_file_helper.h154
-rw-r--r--chromium/chrome/browser/devtools/devtools_file_system_indexer.cc459
-rw-r--r--chromium/chrome/browser/devtools/devtools_file_system_indexer.h108
-rw-r--r--chromium/chrome/browser/devtools/devtools_file_watcher.cc205
-rw-r--r--chromium/chrome/browser/devtools/devtools_file_watcher.h40
-rw-r--r--chromium/chrome/browser/devtools/devtools_network_conditions.cc40
-rw-r--r--chromium/chrome/browser/devtools/devtools_network_conditions.h41
-rw-r--r--chromium/chrome/browser/devtools/devtools_network_controller.cc71
-rw-r--r--chromium/chrome/browser/devtools/devtools_network_controller.h44
-rw-r--r--chromium/chrome/browser/devtools/devtools_network_controller_handle.cc55
-rw-r--r--chromium/chrome/browser/devtools/devtools_network_controller_handle.h42
-rw-r--r--chromium/chrome/browser/devtools/devtools_network_controller_unittest.cc329
-rw-r--r--chromium/chrome/browser/devtools/devtools_network_interceptor.cc290
-rw-r--r--chromium/chrome/browser/devtools/devtools_network_interceptor.h103
-rw-r--r--chromium/chrome/browser/devtools/devtools_network_protocol_handler.cc116
-rw-r--r--chromium/chrome/browser/devtools/devtools_network_protocol_handler.h49
-rw-r--r--chromium/chrome/browser/devtools/devtools_network_transaction.cc303
-rw-r--r--chromium/chrome/browser/devtools/devtools_network_transaction.h133
-rw-r--r--chromium/chrome/browser/devtools/devtools_network_transaction_factory.cc51
-rw-r--r--chromium/chrome/browser/devtools/devtools_network_transaction_factory.h43
-rw-r--r--chromium/chrome/browser/devtools/devtools_network_upload_data_stream.cc92
-rw-r--r--chromium/chrome/browser/devtools/devtools_network_upload_data_stream.h51
-rw-r--r--chromium/chrome/browser/devtools/devtools_protocol.cc149
-rw-r--r--chromium/chrome/browser/devtools/devtools_protocol.h53
-rwxr-xr-xchromium/chrome/browser/devtools/devtools_protocol_constants_generator.py202
-rw-r--r--chromium/chrome/browser/devtools/devtools_sanity_browsertest.cc2082
-rw-r--r--chromium/chrome/browser/devtools/devtools_sanity_interactive_browsertest.cc211
-rw-r--r--chromium/chrome/browser/devtools/devtools_targets_ui.cc490
-rw-r--r--chromium/chrome/browser/devtools/devtools_targets_ui.h82
-rw-r--r--chromium/chrome/browser/devtools/devtools_toggle_action.cc74
-rw-r--r--chromium/chrome/browser/devtools/devtools_toggle_action.h65
-rw-r--r--chromium/chrome/browser/devtools/devtools_ui_bindings.cc1428
-rw-r--r--chromium/chrome/browser/devtools/devtools_ui_bindings.h249
-rw-r--r--chromium/chrome/browser/devtools/devtools_ui_bindings_unittest.cc102
-rw-r--r--chromium/chrome/browser/devtools/devtools_window.cc1447
-rw-r--r--chromium/chrome/browser/devtools/devtools_window.h383
-rw-r--r--chromium/chrome/browser/devtools/devtools_window_testing.cc207
-rw-r--r--chromium/chrome/browser/devtools/devtools_window_testing.h88
-rw-r--r--chromium/chrome/browser/devtools/frontend/devtools_discovery_page.html197
-rw-r--r--chromium/chrome/browser/devtools/global_confirm_info_bar.cc224
-rw-r--r--chromium/chrome/browser/devtools/global_confirm_info_bar.h63
-rw-r--r--chromium/chrome/browser/devtools/remote_debugging_server.cc125
-rw-r--r--chromium/chrome/browser/devtools/remote_debugging_server.h28
-rw-r--r--chromium/chrome/browser/devtools/serialize_host_descriptions.cc106
-rw-r--r--chromium/chrome/browser/devtools/serialize_host_descriptions.h29
-rw-r--r--chromium/chrome/browser/devtools/serialize_host_descriptions_unittest.cc138
-rw-r--r--chromium/chrome/browser/devtools/url_constants.cc10
-rw-r--r--chromium/chrome/browser/devtools/url_constants.h12
-rw-r--r--chromium/chrome/browser/resources/settings/internet_page/internet_detail_page.js3
-rw-r--r--chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.html19
-rw-r--r--chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js13
-rw-r--r--chromium/chrome/browser/ui/webui/DEPS24
-rw-r--r--chromium/chrome/browser/ui/webui/OWNERS28
-rw-r--r--chromium/chrome/browser/ui/webui/PRESUBMIT.py31
-rw-r--r--chromium/chrome/browser/ui/webui/about_ui.cc797
-rw-r--r--chromium/chrome/browser/ui/webui/about_ui.h69
-rw-r--r--chromium/chrome/browser/ui/webui/app_launcher_login_handler.cc265
-rw-r--r--chromium/chrome/browser/ui/webui/app_launcher_login_handler.h78
-rw-r--r--chromium/chrome/browser/ui/webui/app_launcher_page_ui.cc151
-rw-r--r--chromium/chrome/browser/ui/webui/app_launcher_page_ui.h65
-rw-r--r--chromium/chrome/browser/ui/webui/app_list/OWNERS3
-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.cc49
-rw-r--r--chromium/chrome/browser/ui/webui/app_list/start_page_ui.h28
-rw-r--r--chromium/chrome/browser/ui/webui/bidi_checker_web_ui_test.cc726
-rw-r--r--chromium/chrome/browser/ui/webui/bidi_checker_web_ui_test.h59
-rw-r--r--chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.cc71
-rw-r--r--chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/bookmarks_ui.cc66
-rw-r--r--chromium/chrome/browser/ui/webui/bookmarks_ui.h52
-rw-r--r--chromium/chrome/browser/ui/webui/bookmarks_ui_browsertest.cc117
-rw-r--r--chromium/chrome/browser/ui/webui/browsing_history_handler.cc549
-rw-r--r--chromium/chrome/browser/ui/webui/browsing_history_handler.h98
-rw-r--r--chromium/chrome/browser/ui/webui/browsing_history_handler_unittest.cc334
-rw-r--r--chromium/chrome/browser/ui/webui/cast/OWNERS1
-rw-r--r--chromium/chrome/browser/ui/webui/cast/cast_ui.cc41
-rw-r--r--chromium/chrome/browser/ui/webui/cast/cast_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/certificate_viewer_ui.cc89
-rw-r--r--chromium/chrome/browser/ui/webui/certificate_viewer_ui.h32
-rw-r--r--chromium/chrome/browser/ui/webui/certificate_viewer_webui.cc500
-rw-r--r--chromium/chrome/browser/ui/webui/certificate_viewer_webui.h140
-rw-r--r--chromium/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc121
-rw-r--r--chromium/chrome/browser/ui/webui/chrome_web_contents_handler.cc104
-rw-r--r--chromium/chrome/browser/ui/webui/chrome_web_contents_handler.h34
-rw-r--r--chromium/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc828
-rw-r--r--chromium/chrome/browser/ui/webui/chrome_web_ui_controller_factory.h61
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/DEPS11
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/OWNERS13
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/bluetooth_pairing_ui.cc62
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/bluetooth_pairing_ui.h38
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/bluetooth_pairing_ui_browsertest-inl.h69
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/bluetooth_pairing_ui_browsertest.js33
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_browsertest.js30
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.cc150
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.h43
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/choose_mobile_network_ui.cc284
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/choose_mobile_network_ui.h24
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/cryptohome_ui.cc37
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/cryptohome_ui.h25
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.cc84
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.h58
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc906
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/drive_internals_ui.h25
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc642
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.h141
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/emulator/device_emulator_ui.cc67
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/emulator/device_emulator_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/first_run/OWNERS1
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_actor.cc71
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_actor.h115
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_handler.cc134
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_handler.h60
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_ui.cc113
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_ui.h33
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/image_source.cc125
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/image_source.h58
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc436
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.h19
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui_browsertest.cc176
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/OWNERS2
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.cc285
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.h82
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/arc_kiosk_splash_screen_handler.cc122
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/arc_kiosk_splash_screen_handler.h54
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc186
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h80
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.cc63
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.h47
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/base_screen_handler.cc14
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/base_screen_handler.h32
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/base_webui_handler.cc137
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/base_webui_handler.h274
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/controller_pairing_screen_handler.cc175
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/controller_pairing_screen_handler.h47
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc520
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h172
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/device_disabled_screen_handler.cc80
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/device_disabled_screen_handler.h47
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.cc291
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.h91
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc566
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.h141
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc679
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h169
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc131
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/error_screen_handler.h62
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc244
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h74
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc983
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h292
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.cc122
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.h64
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/host_pairing_screen_handler.cc256
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/host_pairing_screen_handler.h56
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.cc213
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.h76
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.cc160
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.h61
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.cc143
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.h63
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/l10n_util.cc601
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/l10n_util.h109
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/l10n_util_test_util.cc40
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/l10n_util_test_util.h41
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/l10n_util_unittest.cc230
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/native_window_delegate.h24
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown.cc215
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown.h87
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.cc123
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.h67
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc296
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/network_screen_handler.h71
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/network_state_informer.cc215
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/network_state_informer.h109
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc78
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.h32
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_browsertest.cc69
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_unittest.cc121
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc646
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.h259
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc120
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h50
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/screenlock_icon_provider.cc33
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/screenlock_icon_provider.h46
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/screenlock_icon_source.cc71
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/screenlock_icon_source.h44
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc1716
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h565
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/signin_userlist_unittest.cc133
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc477
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.h141
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc213
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.h92
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc89
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/update_screen_handler.h44
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.cc105
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.h68
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc199
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h84
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc76
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.h51
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/mobile_setup_dialog.cc167
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/mobile_setup_dialog.h22
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc652
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/mobile_setup_ui.h29
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.cc36
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.h22
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/network_ui.cc231
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/network_ui.h30
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/power_ui.cc248
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/power_ui.h24
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/proxy_settings_ui.cc140
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/proxy_settings_ui.h41
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/set_time_ui.cc144
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/set_time_ui.h25
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/set_time_ui_browsertest.js37
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/sim_unlock_ui.cc776
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/sim_unlock_ui.h27
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/slow_trace_ui.cc86
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/slow_trace_ui.h58
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/slow_ui.cc137
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/slow_ui.h26
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/ui_account_tweaks.cc54
-rw-r--r--chromium/chrome/browser/ui/webui/chromeos/ui_account_tweaks.h38
-rw-r--r--chromium/chrome/browser/ui/webui/cleanup_tool/cleanup_action_handler.cc139
-rw-r--r--chromium/chrome/browser/ui/webui/cleanup_tool/cleanup_action_handler.h44
-rw-r--r--chromium/chrome/browser/ui/webui/cleanup_tool/cleanup_tool_ui.cc46
-rw-r--r--chromium/chrome/browser/ui/webui/cleanup_tool/cleanup_tool_ui.h23
-rw-r--r--chromium/chrome/browser/ui/webui/components_ui.cc263
-rw-r--r--chromium/chrome/browser/ui/webui/components_ui.h45
-rw-r--r--chromium/chrome/browser/ui/webui/conflicts_ui.cc189
-rw-r--r--chromium/chrome/browser/ui/webui/conflicts_ui.h33
-rw-r--r--chromium/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.cc133
-rw-r--r--chromium/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.h81
-rw-r--r--chromium/chrome/browser/ui/webui/constrained_web_dialog_ui.cc125
-rw-r--r--chromium/chrome/browser/ui/webui/constrained_web_dialog_ui.h119
-rw-r--r--chromium/chrome/browser/ui/webui/constrained_web_dialog_ui_browsertest.cc278
-rw-r--r--chromium/chrome/browser/ui/webui/cookies_tree_model_util.cc401
-rw-r--r--chromium/chrome/browser/ui/webui/cookies_tree_model_util.h77
-rw-r--r--chromium/chrome/browser/ui/webui/crashes_ui.cc246
-rw-r--r--chromium/chrome/browser/ui/webui/crashes_ui.h27
-rw-r--r--chromium/chrome/browser/ui/webui/device_log_ui.cc93
-rw-r--r--chromium/chrome/browser/ui/webui/device_log_ui.h24
-rw-r--r--chromium/chrome/browser/ui/webui/devtools_ui.cc364
-rw-r--r--chromium/chrome/browser/ui/webui/devtools_ui.h25
-rw-r--r--chromium/chrome/browser/ui/webui/domain_reliability_internals_ui.cc66
-rw-r--r--chromium/chrome/browser/ui/webui/domain_reliability_internals_ui.h31
-rw-r--r--chromium/chrome/browser/ui/webui/engagement/site_engagement_ui.cc100
-rw-r--r--chromium/chrome/browser/ui/webui/engagement/site_engagement_ui.h30
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/OWNERS5
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_browsertest.js265
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.cc379
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.h81
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extension_basic_info.cc60
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extension_basic_info.h24
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extension_icon_source.cc344
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extension_icon_source.h163
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extension_loader_handler.cc225
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extension_loader_handler.h120
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc89
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.h68
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js527
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extension_settings_handler.cc324
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extension_settings_handler.h57
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extensions_ui.cc393
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/extensions_ui.h32
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/install_extension_handler.cc137
-rw-r--r--chromium/chrome/browser/ui/webui/extensions/install_extension_handler.h63
-rw-r--r--chromium/chrome/browser/ui/webui/fallback_icon_source.cc99
-rw-r--r--chromium/chrome/browser/ui/webui/fallback_icon_source.h91
-rw-r--r--chromium/chrome/browser/ui/webui/favicon_source.cc202
-rw-r--r--chromium/chrome/browser/ui/webui/favicon_source.h117
-rw-r--r--chromium/chrome/browser/ui/webui/fileicon_source.cc168
-rw-r--r--chromium/chrome/browser/ui/webui/fileicon_source.h70
-rw-r--r--chromium/chrome/browser/ui/webui/fileicon_source_unittest.cc121
-rw-r--r--chromium/chrome/browser/ui/webui/flags_ui.cc345
-rw-r--r--chromium/chrome/browser/ui/webui/flags_ui.h36
-rw-r--r--chromium/chrome/browser/ui/webui/flash_ui.cc401
-rw-r--r--chromium/chrome/browser/ui/webui/flash_ui.h28
-rw-r--r--chromium/chrome/browser/ui/webui/foreign_session_handler.cc436
-rw-r--r--chromium/chrome/browser/ui/webui/foreign_session_handler.h97
-rw-r--r--chromium/chrome/browser/ui/webui/gcm_internals_ui.cc180
-rw-r--r--chromium/chrome/browser/ui/webui/gcm_internals_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/help/help_browsertest.js147
-rw-r--r--chromium/chrome/browser/ui/webui/help/help_handler.cc766
-rw-r--r--chromium/chrome/browser/ui/webui/help/help_handler.h139
-rw-r--r--chromium/chrome/browser/ui/webui/help/help_ui.cc46
-rw-r--r--chromium/chrome/browser/ui/webui/help/help_ui.h20
-rw-r--r--chromium/chrome/browser/ui/webui/help/help_utils_chromeos.cc74
-rw-r--r--chromium/chrome/browser/ui/webui/help/help_utils_chromeos.h28
-rw-r--r--chromium/chrome/browser/ui/webui/help/version_updater.h106
-rw-r--r--chromium/chrome/browser/ui/webui/help/version_updater_basic.cc21
-rw-r--r--chromium/chrome/browser/ui/webui/help/version_updater_basic.h29
-rw-r--r--chromium/chrome/browser/ui/webui/help/version_updater_chromeos.cc317
-rw-r--r--chromium/chrome/browser/ui/webui/help/version_updater_chromeos.h81
-rw-r--r--chromium/chrome/browser/ui/webui/help/version_updater_chromeos_unittest.cc155
-rw-r--r--chromium/chrome/browser/ui/webui/help/version_updater_mac.h59
-rw-r--r--chromium/chrome/browser/ui/webui/help/version_updater_mac.mm281
-rw-r--r--chromium/chrome/browser/ui/webui/help/version_updater_win.cc150
-rw-r--r--chromium/chrome/browser/ui/webui/help/version_updater_win.h59
-rw-r--r--chromium/chrome/browser/ui/webui/history_login_handler.cc62
-rw-r--r--chromium/chrome/browser/ui/webui/history_login_handler.h44
-rw-r--r--chromium/chrome/browser/ui/webui/identity_internals_ui.cc331
-rw-r--r--chromium/chrome/browser/ui/webui/identity_internals_ui.h22
-rw-r--r--chromium/chrome/browser/ui/webui/identity_internals_ui_browsertest.cc60
-rw-r--r--chromium/chrome/browser/ui/webui/identity_internals_ui_browsertest.h33
-rw-r--r--chromium/chrome/browser/ui/webui/identity_internals_ui_browsertest.js260
-rw-r--r--chromium/chrome/browser/ui/webui/inspect_ui.cc635
-rw-r--r--chromium/chrome/browser/ui/webui/inspect_ui.h109
-rw-r--r--chromium/chrome/browser/ui/webui/inspect_ui_browsertest.cc90
-rw-r--r--chromium/chrome/browser/ui/webui/instant_ui.cc190
-rw-r--r--chromium/chrome/browser/ui/webui/instant_ui.h28
-rw-r--r--chromium/chrome/browser/ui/webui/interstitials/OWNERS1
-rw-r--r--chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.cc491
-rw-r--r--chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.h25
-rw-r--r--chromium/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc116
-rw-r--r--chromium/chrome/browser/ui/webui/invalidations_message_handler.cc128
-rw-r--r--chromium/chrome/browser/ui/webui/invalidations_message_handler.h65
-rw-r--r--chromium/chrome/browser/ui/webui/invalidations_ui.cc37
-rw-r--r--chromium/chrome/browser/ui/webui/invalidations_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/large_icon_source.cc130
-rw-r--r--chromium/chrome/browser/ui/webui/large_icon_source.h76
-rw-r--r--chromium/chrome/browser/ui/webui/local_discovery/OWNERS4
-rw-r--r--chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui.cc152
-rw-r--r--chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc590
-rw-r--r--chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc619
-rw-r--r--chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h210
-rw-r--r--chromium/chrome/browser/ui/webui/local_state/local_state_ui.cc125
-rw-r--r--chromium/chrome/browser/ui/webui/local_state/local_state_ui.h38
-rw-r--r--chromium/chrome/browser/ui/webui/local_state/local_state_ui_unittest.cc37
-rw-r--r--chromium/chrome/browser/ui/webui/log_web_ui_url.cc44
-rw-r--r--chromium/chrome/browser/ui/webui/log_web_ui_url.h22
-rw-r--r--chromium/chrome/browser/ui/webui/log_web_ui_url_browsertest.cc121
-rw-r--r--chromium/chrome/browser/ui/webui/log_web_ui_url_unittest.cc37
-rw-r--r--chromium/chrome/browser/ui/webui/md_bookmarks/OWNERS5
-rw-r--r--chromium/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc152
-rw-r--r--chromium/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/md_downloads/OWNERS4
-rw-r--r--chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker.cc423
-rw-r--r--chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker.h140
-rw-r--r--chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker_unittest.cc369
-rw-r--r--chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.cc462
-rw-r--r--chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.h163
-rw-r--r--chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler_unittest.cc154
-rw-r--r--chromium/chrome/browser/ui/webui/md_downloads/md_downloads_ui.cc168
-rw-r--r--chromium/chrome/browser/ui/webui/md_downloads/md_downloads_ui.h27
-rw-r--r--chromium/chrome/browser/ui/webui/md_feedback/OWNERS1
-rw-r--r--chromium/chrome/browser/ui/webui/md_feedback/md_feedback_dialog_controller.cc84
-rw-r--r--chromium/chrome/browser/ui/webui/md_feedback/md_feedback_dialog_controller.h31
-rw-r--r--chromium/chrome/browser/ui/webui/md_feedback/md_feedback_ui.cc80
-rw-r--r--chromium/chrome/browser/ui/webui/md_feedback/md_feedback_ui.h35
-rw-r--r--chromium/chrome/browser/ui/webui/md_feedback/md_feedback_webui_message_handler.cc67
-rw-r--r--chromium/chrome/browser/ui/webui/md_feedback/md_feedback_webui_message_handler.h32
-rw-r--r--chromium/chrome/browser/ui/webui/md_history_ui.cc245
-rw-r--r--chromium/chrome/browser/ui/webui/md_history_ui.h40
-rw-r--r--chromium/chrome/browser/ui/webui/media/webrtc_logs_ui.cc225
-rw-r--r--chromium/chrome/browser/ui/webui/media/webrtc_logs_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/OWNERS8
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/README4
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/cast_modes_with_media_sources.cc49
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/cast_modes_with_media_sources.h49
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/cast_modes_with_media_sources_unittest.cc78
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_cast_mode.cc44
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_cast_mode.h43
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_cast_mode_unittest.cc32
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl.cc342
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl.h92
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl_browsertest.cc140
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl_unittest.cc274
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.cc83
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.h21
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_resources_provider.cc91
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_resources_provider.h19
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_ui.cc838
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_ui.h411
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc745
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_web_ui_test.cc62
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_web_ui_test.h30
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc1057
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h168
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler_unittest.cc687
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_sink_with_cast_modes.cc23
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/media_sink_with_cast_modes.h32
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/query_result_manager.cc239
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/query_result_manager.h181
-rw-r--r--chromium/chrome/browser/ui/webui/media_router/query_result_manager_unittest.cc408
-rw-r--r--chromium/chrome/browser/ui/webui/metrics_handler.cc143
-rw-r--r--chromium/chrome/browser/ui/webui/metrics_handler.h64
-rw-r--r--chromium/chrome/browser/ui/webui/mojo_web_ui_controller.cc23
-rw-r--r--chromium/chrome/browser/ui/webui/mojo_web_ui_controller.h71
-rw-r--r--chromium/chrome/browser/ui/webui/mojo_web_ui_handler.h16
-rw-r--r--chromium/chrome/browser/ui/webui/nacl_ui.cc397
-rw-r--r--chromium/chrome/browser/ui/webui/nacl_ui.h20
-rw-r--r--chromium/chrome/browser/ui/webui/net_export_ui.cc416
-rw-r--r--chromium/chrome/browser/ui/webui/net_export_ui.h20
-rw-r--r--chromium/chrome/browser/ui/webui/net_internals/DEPS4
-rw-r--r--chromium/chrome/browser/ui/webui/net_internals/OWNERS4
-rw-r--r--chromium/chrome/browser/ui/webui/net_internals/net_internals_ui.cc1059
-rw-r--r--chromium/chrome/browser/ui/webui/net_internals/net_internals_ui.h19
-rw-r--r--chromium/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc389
-rw-r--r--chromium/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.h43
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/OWNERS5
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.cc892
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.h237
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.cc41
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.h36
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/core_app_launcher_handler.cc70
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/core_app_launcher_handler.h49
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/new_tab_ui.cc214
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/new_tab_ui.h86
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/new_tab_ui_browsertest.cc97
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc604
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache.h90
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.cc43
-rw-r--r--chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.h36
-rw-r--r--chromium/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc131
-rw-r--r--chromium/chrome/browser/ui/webui/ntp_tiles_internals_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/offline/offline_internals_ui.cc43
-rw-r--r--chromium/chrome/browser/ui/webui/offline/offline_internals_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc386
-rw-r--r--chromium/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h113
-rw-r--r--chromium/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc215
-rw-r--r--chromium/chrome/browser/ui/webui/omnibox/omnibox_page_handler.h82
-rw-r--r--chromium/chrome/browser/ui/webui/omnibox/omnibox_ui.cc37
-rw-r--r--chromium/chrome/browser/ui/webui/omnibox/omnibox_ui.h30
-rw-r--r--chromium/chrome/browser/ui/webui/options/DEPS4
-rw-r--r--chromium/chrome/browser/ui/webui/options/OWNERS8
-rw-r--r--chromium/chrome/browser/ui/webui/options/autofill_options_browsertest.js174
-rw-r--r--chromium/chrome/browser/ui/webui/options/autofill_options_handler.cc507
-rw-r--r--chromium/chrome/browser/ui/webui/options/autofill_options_handler.h114
-rw-r--r--chromium/chrome/browser/ui/webui/options/autofill_options_handler_unittest.cc62
-rw-r--r--chromium/chrome/browser/ui/webui/options/automatic_settings_reset_handler.cc71
-rw-r--r--chromium/chrome/browser/ui/webui/options/automatic_settings_reset_handler.h34
-rw-r--r--chromium/chrome/browser/ui/webui/options/browser_options_browsertest.js131
-rw-r--r--chromium/chrome/browser/ui/webui/options/browser_options_handler.cc2356
-rw-r--r--chromium/chrome/browser/ui/webui/options/browser_options_handler.h465
-rw-r--r--chromium/chrome/browser/ui/webui/options/certificate_manager_browsertest.cc108
-rw-r--r--chromium/chrome/browser/ui/webui/options/certificate_manager_browsertest.js405
-rw-r--r--chromium/chrome/browser/ui/webui/options/certificate_manager_handler.cc1239
-rw-r--r--chromium/chrome/browser/ui/webui/options/certificate_manager_handler.h194
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/DEPS3
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/OWNERS6
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/accounts_options_browsertest.cc166
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/accounts_options_browsertest.js42
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/accounts_options_handler.cc182
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/accounts_options_handler.h42
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/bluetooth_options_browsertest.js440
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/bluetooth_options_handler.cc104
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/bluetooth_options_handler.h40
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc478
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.h145
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.cc427
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.h81
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler.cc279
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler.h86
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler_unittest.cc92
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/date_time_options_browsertest.js38
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/date_time_options_handler.cc77
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/date_time_options_handler.h47
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc581
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/display_options_handler.h63
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/display_overscan_handler.cc211
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/display_overscan_handler.h62
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/guest_mode_options_browsertest.cc28
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/guest_mode_options_browsertest.h27
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/guest_mode_options_browsertest.js80
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/guest_mode_options_ui_browsertest.cc38
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc253
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/internet_options_handler.h60
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/internet_options_handler_strings.cc207
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/internet_options_handler_strings.h22
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/keyboard_handler.cc191
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/keyboard_handler.h47
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.cc152
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.h69
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/pointer_handler.cc87
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/pointer_handler.h47
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/power_handler.cc174
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/power_handler.h54
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/power_overlay_browsertest.js175
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/proxy_handler.cc77
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/proxy_handler.h33
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/shared_options_browsertest.cc467
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/stats_options_handler.cc46
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/stats_options_handler.h38
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/storage_manager_handler.cc450
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/storage_manager_handler.h130
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/user_image_source.cc114
-rw-r--r--chromium/chrome/browser/ui/webui/options/chromeos/user_image_source.h55
-rw-r--r--chromium/chrome/browser/ui/webui/options/clear_browser_data_browsertest.cc115
-rw-r--r--chromium/chrome/browser/ui/webui/options/clear_browser_data_handler.cc441
-rw-r--r--chromium/chrome/browser/ui/webui/options/clear_browser_data_handler.h108
-rw-r--r--chromium/chrome/browser/ui/webui/options/content_options_browsertest.js27
-rw-r--r--chromium/chrome/browser/ui/webui/options/content_settings_exception_area_browsertest.cc24
-rw-r--r--chromium/chrome/browser/ui/webui/options/content_settings_exception_area_browsertest.js109
-rw-r--r--chromium/chrome/browser/ui/webui/options/content_settings_handler.cc1494
-rw-r--r--chromium/chrome/browser/ui/webui/options/content_settings_handler.h285
-rw-r--r--chromium/chrome/browser/ui/webui/options/cookies_view_browsertest.js60
-rw-r--r--chromium/chrome/browser/ui/webui/options/cookies_view_handler.cc297
-rw-r--r--chromium/chrome/browser/ui/webui/options/cookies_view_handler.h81
-rw-r--r--chromium/chrome/browser/ui/webui/options/core_options_handler.cc665
-rw-r--r--chromium/chrome/browser/ui/webui/options/core_options_handler.h191
-rw-r--r--chromium/chrome/browser/ui/webui/options/create_profile_handler.cc483
-rw-r--r--chromium/chrome/browser/ui/webui/options/create_profile_handler.h171
-rw-r--r--chromium/chrome/browser/ui/webui/options/easy_unlock_handler.cc110
-rw-r--r--chromium/chrome/browser/ui/webui/options/easy_unlock_handler.h43
-rw-r--r--chromium/chrome/browser/ui/webui/options/edit_dictionary_browsertest.js166
-rw-r--r--chromium/chrome/browser/ui/webui/options/font_settings_browsertest.js66
-rw-r--r--chromium/chrome/browser/ui/webui/options/font_settings_handler.cc277
-rw-r--r--chromium/chrome/browser/ui/webui/options/font_settings_handler.h91
-rw-r--r--chromium/chrome/browser/ui/webui/options/font_settings_utils.cc25
-rw-r--r--chromium/chrome/browser/ui/webui/options/font_settings_utils.h41
-rw-r--r--chromium/chrome/browser/ui/webui/options/font_settings_utils_linux.cc14
-rw-r--r--chromium/chrome/browser/ui/webui/options/font_settings_utils_mac.mm42
-rw-r--r--chromium/chrome/browser/ui/webui/options/font_settings_utils_unittest.cc30
-rw-r--r--chromium/chrome/browser/ui/webui/options/font_settings_utils_win.cc27
-rw-r--r--chromium/chrome/browser/ui/webui/options/geolocation_options_handler.cc40
-rw-r--r--chromium/chrome/browser/ui/webui/options/geolocation_options_handler.h31
-rw-r--r--chromium/chrome/browser/ui/webui/options/handler_options_handler.cc224
-rw-r--r--chromium/chrome/browser/ui/webui/options/handler_options_handler.h90
-rw-r--r--chromium/chrome/browser/ui/webui/options/help_overlay_handler.cc34
-rw-r--r--chromium/chrome/browser/ui/webui/options/help_overlay_handler.h32
-rw-r--r--chromium/chrome/browser/ui/webui/options/home_page_overlay_handler.cc71
-rw-r--r--chromium/chrome/browser/ui/webui/options/home_page_overlay_handler.h48
-rw-r--r--chromium/chrome/browser/ui/webui/options/import_data_handler.cc272
-rw-r--r--chromium/chrome/browser/ui/webui/options/import_data_handler.h75
-rw-r--r--chromium/chrome/browser/ui/webui/options/language_dictionary_interactive_uitest.cc255
-rw-r--r--chromium/chrome/browser/ui/webui/options/language_dictionary_overlay_handler.cc134
-rw-r--r--chromium/chrome/browser/ui/webui/options/language_dictionary_overlay_handler.h61
-rw-r--r--chromium/chrome/browser/ui/webui/options/language_options_browsertest.js56
-rw-r--r--chromium/chrome/browser/ui/webui/options/language_options_dictionary_download_browsertest.js129
-rw-r--r--chromium/chrome/browser/ui/webui/options/language_options_handler.cc131
-rw-r--r--chromium/chrome/browser/ui/webui/options/language_options_handler.h52
-rw-r--r--chromium/chrome/browser/ui/webui/options/language_options_handler_common.cc302
-rw-r--r--chromium/chrome/browser/ui/webui/options/language_options_handler_common.h96
-rw-r--r--chromium/chrome/browser/ui/webui/options/language_options_handler_unittest.cc28
-rw-r--r--chromium/chrome/browser/ui/webui/options/language_options_interactive_uitest.cc166
-rw-r--r--chromium/chrome/browser/ui/webui/options/manage_profile_browsertest.js672
-rw-r--r--chromium/chrome/browser/ui/webui/options/manage_profile_handler.cc547
-rw-r--r--chromium/chrome/browser/ui/webui/options/manage_profile_handler.h144
-rw-r--r--chromium/chrome/browser/ui/webui/options/media_devices_selection_handler.cc181
-rw-r--r--chromium/chrome/browser/ui/webui/options/media_devices_selection_handler.h59
-rw-r--r--chromium/chrome/browser/ui/webui/options/multilanguage_options_browsertest.cc48
-rw-r--r--chromium/chrome/browser/ui/webui/options/multilanguage_options_browsertest.h29
-rw-r--r--chromium/chrome/browser/ui/webui/options/multilanguage_options_webui_browsertest.js212
-rw-r--r--chromium/chrome/browser/ui/webui/options/options_browsertest.cc70
-rw-r--r--chromium/chrome/browser/ui/webui/options/options_browsertest.h45
-rw-r--r--chromium/chrome/browser/ui/webui/options/options_browsertest.js969
-rw-r--r--chromium/chrome/browser/ui/webui/options/options_browsertest_base.js86
-rw-r--r--chromium/chrome/browser/ui/webui/options/options_ui.cc661
-rw-r--r--chromium/chrome/browser/ui/webui/options/options_ui.h164
-rw-r--r--chromium/chrome/browser/ui/webui/options/options_ui_browsertest.cc344
-rw-r--r--chromium/chrome/browser/ui/webui/options/options_ui_browsertest.h57
-rw-r--r--chromium/chrome/browser/ui/webui/options/password_manager_browsertest.js26
-rw-r--r--chromium/chrome/browser/ui/webui/options/password_manager_handler.cc402
-rw-r--r--chromium/chrome/browser/ui/webui/options/password_manager_handler.h138
-rw-r--r--chromium/chrome/browser/ui/webui/options/password_manager_handler_unittest.cc244
-rw-r--r--chromium/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils.cc130
-rw-r--r--chromium/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils.h62
-rw-r--r--chromium/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils_unittest.cc137
-rw-r--r--chromium/chrome/browser/ui/webui/options/preferences_browsertest.cc1106
-rw-r--r--chromium/chrome/browser/ui/webui/options/preferences_browsertest.h196
-rw-r--r--chromium/chrome/browser/ui/webui/options/profile_settings_reset_browsertest.js38
-rw-r--r--chromium/chrome/browser/ui/webui/options/reset_profile_settings_handler.cc269
-rw-r--r--chromium/chrome/browser/ui/webui/options/reset_profile_settings_handler.h91
-rw-r--r--chromium/chrome/browser/ui/webui/options/search_engine_manager_browsertest.js26
-rw-r--r--chromium/chrome/browser/ui/webui/options/search_engine_manager_handler.cc329
-rw-r--r--chromium/chrome/browser/ui/webui/options/search_engine_manager_handler.h90
-rw-r--r--chromium/chrome/browser/ui/webui/options/settings_format_browsertest.js176
-rw-r--r--chromium/chrome/browser/ui/webui/options/startup_page_list_browsertest.js151
-rw-r--r--chromium/chrome/browser/ui/webui/options/startup_pages_handler.cc271
-rw-r--r--chromium/chrome/browser/ui/webui/options/startup_pages_handler.h95
-rw-r--r--chromium/chrome/browser/ui/webui/options/supervised_user_create_confirm_handler.cc168
-rw-r--r--chromium/chrome/browser/ui/webui/options/supervised_user_create_confirm_handler.h49
-rw-r--r--chromium/chrome/browser/ui/webui/options/supervised_user_import_handler.cc304
-rw-r--r--chromium/chrome/browser/ui/webui/options/supervised_user_import_handler.h118
-rw-r--r--chromium/chrome/browser/ui/webui/options/supervised_user_learn_more_handler.cc42
-rw-r--r--chromium/chrome/browser/ui/webui/options/supervised_user_learn_more_handler.h33
-rw-r--r--chromium/chrome/browser/ui/webui/options/sync_setup_handler.cc956
-rw-r--r--chromium/chrome/browser/ui/webui/options/sync_setup_handler.h175
-rw-r--r--chromium/chrome/browser/ui/webui/options/sync_setup_handler_unittest.cc951
-rw-r--r--chromium/chrome/browser/ui/webui/password_manager_internals/OWNERS4
-rw-r--r--chromium/chrome/browser/ui/webui/password_manager_internals/password_manager_internals_ui.cc130
-rw-r--r--chromium/chrome/browser/ui/webui/password_manager_internals/password_manager_internals_ui.h41
-rw-r--r--chromium/chrome/browser/ui/webui/password_manager_internals/password_manager_internals_ui_browsertest.cc178
-rw-r--r--chromium/chrome/browser/ui/webui/physical_web/physical_web_ui.cc92
-rw-r--r--chromium/chrome/browser/ui/webui/physical_web/physical_web_ui.h20
-rw-r--r--chromium/chrome/browser/ui/webui/policy_indicator_localized_strings_provider.cc31
-rw-r--r--chromium/chrome/browser/ui/webui/policy_indicator_localized_strings_provider.h21
-rw-r--r--chromium/chrome/browser/ui/webui/policy_material_design_ui.cc102
-rw-r--r--chromium/chrome/browser/ui/webui/policy_material_design_ui.h25
-rw-r--r--chromium/chrome/browser/ui/webui/policy_ui.cc63
-rw-r--r--chromium/chrome/browser/ui/webui/policy_ui.h27
-rw-r--r--chromium/chrome/browser/ui/webui/policy_ui_browsertest.cc335
-rw-r--r--chromium/chrome/browser/ui/webui/policy_ui_handler.cc824
-rw-r--r--chromium/chrome/browser/ui/webui/policy_ui_handler.h121
-rw-r--r--chromium/chrome/browser/ui/webui/popular_sites_internals_ui.cc96
-rw-r--r--chromium/chrome/browser/ui/webui/popular_sites_internals_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/predictors/OWNERS1
-rw-r--r--chromium/chrome/browser/ui/webui/predictors/predictors_handler.cc149
-rw-r--r--chromium/chrome/browser/ui/webui/predictors/predictors_handler.h53
-rw-r--r--chromium/chrome/browser/ui/webui/predictors/predictors_ui.cc33
-rw-r--r--chromium/chrome/browser/ui/webui/predictors/predictors_ui.h19
-rw-r--r--chromium/chrome/browser/ui/webui/prefs_internals_browsertest.cc50
-rw-r--r--chromium/chrome/browser/ui/webui/prefs_internals_source.cc42
-rw-r--r--chromium/chrome/browser/ui/webui/prefs_internals_source.h33
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/OWNERS1
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc370
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler.h127
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc1018
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_handler.cc1794
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_handler.h410
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_win_unittest.cc177
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_ui.cc697
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_ui.h193
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_ui_browsertest.cc149
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/print_preview_ui_unittest.cc236
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/printer_backend_proxy.cc112
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/printer_backend_proxy.h57
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc201
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/printer_capabilities.cc111
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/printer_capabilities.h36
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/printer_capabilities_unittest.cc126
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/printer_handler.cc14
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/printer_handler.h91
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/sticky_settings.cc53
-rw-r--r--chromium/chrome/browser/ui/webui/print_preview/sticky_settings.h44
-rw-r--r--chromium/chrome/browser/ui/webui/profile_helper.cc103
-rw-r--r--chromium/chrome/browser/ui/webui/profile_helper.h29
-rw-r--r--chromium/chrome/browser/ui/webui/profile_helper_browsertest.cc196
-rw-r--r--chromium/chrome/browser/ui/webui/profile_info_watcher.cc60
-rw-r--r--chromium/chrome/browser/ui/webui/profile_info_watcher.h50
-rw-r--r--chromium/chrome/browser/ui/webui/profiler_ui.cc186
-rw-r--r--chromium/chrome/browser/ui/webui/profiler_ui.h36
-rw-r--r--chromium/chrome/browser/ui/webui/quota_internals/OWNERS2
-rw-r--r--chromium/chrome/browser/ui/webui/quota_internals/quota_internals_handler.cc91
-rw-r--r--chromium/chrome/browser/ui/webui/quota_internals/quota_internals_handler.h57
-rw-r--r--chromium/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.cc226
-rw-r--r--chromium/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.h91
-rw-r--r--chromium/chrome/browser/ui/webui/quota_internals/quota_internals_types.cc108
-rw-r--r--chromium/chrome/browser/ui/webui/quota_internals/quota_internals_types.h108
-rw-r--r--chromium/chrome/browser/ui/webui/quota_internals/quota_internals_ui.cc44
-rw-r--r--chromium/chrome/browser/ui/webui/quota_internals/quota_internals_ui.h20
-rw-r--r--chromium/chrome/browser/ui/webui/sandbox_internals_ui.cc83
-rw-r--r--chromium/chrome/browser/ui/webui/sandbox_internals_ui.h31
-rw-r--r--chromium/chrome/browser/ui/webui/set_as_default_browser_ui_browsertest_win.cc60
-rw-r--r--chromium/chrome/browser/ui/webui/set_as_default_browser_ui_win.cc371
-rw-r--r--chromium/chrome/browser/ui/webui/set_as_default_browser_ui_win.h34
-rw-r--r--chromium/chrome/browser/ui/webui/settings/OWNERS3
-rw-r--r--chromium/chrome/browser/ui/webui/settings/about_handler.cc695
-rw-r--r--chromium/chrome/browser/ui/webui/settings/about_handler.h168
-rw-r--r--chromium/chrome/browser/ui/webui/settings/appearance_handler.cc69
-rw-r--r--chromium/chrome/browser/ui/webui/settings/appearance_handler.h55
-rw-r--r--chromium/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc80
-rw-r--r--chromium/chrome/browser/ui/webui/settings/browser_lifetime_handler.h40
-rw-r--r--chromium/chrome/browser/ui/webui/settings/certificates_handler.cc1145
-rw-r--r--chromium/chrome/browser/ui/webui/settings/certificates_handler.h203
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.cc67
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h51
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/android_apps_handler.cc103
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/android_apps_handler.h66
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc409
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h139
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc447
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h118
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc182
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/date_time_handler.h78
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc82
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h67
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.cc51
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.h51
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler.cc138
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler.h59
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc370
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h127
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.cc143
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.h63
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/easy_unlock_settings_handler.cc160
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/easy_unlock_settings_handler.h67
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/easy_unlock_settings_handler_unittest.cc264
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc327
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.h90
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc132
-rw-r--r--chromium/chrome/browser/ui/webui/settings/chromeos/internet_handler.h41
-rw-r--r--chromium/chrome/browser/ui/webui/settings/downloads_handler.cc106
-rw-r--r--chromium/chrome/browser/ui/webui/settings/downloads_handler.h65
-rw-r--r--chromium/chrome/browser/ui/webui/settings/downloads_handler_unittest.cc89
-rw-r--r--chromium/chrome/browser/ui/webui/settings/extension_control_handler.cc38
-rw-r--r--chromium/chrome/browser/ui/webui/settings/extension_control_handler.h37
-rw-r--r--chromium/chrome/browser/ui/webui/settings/font_handler.cc149
-rw-r--r--chromium/chrome/browser/ui/webui/settings/font_handler.h85
-rw-r--r--chromium/chrome/browser/ui/webui/settings/languages_handler.cc88
-rw-r--r--chromium/chrome/browser/ui/webui/settings/languages_handler.h51
-rw-r--r--chromium/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc2130
-rw-r--r--chromium/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.h24
-rw-r--r--chromium/chrome/browser/ui/webui/settings/md_settings_ui.cc265
-rw-r--r--chromium/chrome/browser/ui/webui/settings/md_settings_ui.h54
-rw-r--r--chromium/chrome/browser/ui/webui/settings/md_settings_ui_browsertest.cc67
-rw-r--r--chromium/chrome/browser/ui/webui/settings/metrics_reporting_handler.cc106
-rw-r--r--chromium/chrome/browser/ui/webui/settings/metrics_reporting_handler.h73
-rw-r--r--chromium/chrome/browser/ui/webui/settings/metrics_reporting_handler_unittest.cc128
-rw-r--r--chromium/chrome/browser/ui/webui/settings/native_certificates_handler.cc32
-rw-r--r--chromium/chrome/browser/ui/webui/settings/native_certificates_handler.h33
-rw-r--r--chromium/chrome/browser/ui/webui/settings/on_startup_handler.cc73
-rw-r--r--chromium/chrome/browser/ui/webui/settings/on_startup_handler.h40
-rw-r--r--chromium/chrome/browser/ui/webui/settings/people_handler.cc913
-rw-r--r--chromium/chrome/browser/ui/webui/settings/people_handler.h209
-rw-r--r--chromium/chrome/browser/ui/webui/settings/people_handler_unittest.cc924
-rw-r--r--chromium/chrome/browser/ui/webui/settings/profile_info_handler.cc225
-rw-r--r--chromium/chrome/browser/ui/webui/settings/profile_info_handler.h107
-rw-r--r--chromium/chrome/browser/ui/webui/settings/profile_info_handler_unittest.cc203
-rw-r--r--chromium/chrome/browser/ui/webui/settings/protocol_handlers_handler.cc227
-rw-r--r--chromium/chrome/browser/ui/webui/settings/protocol_handlers_handler.h102
-rw-r--r--chromium/chrome/browser/ui/webui/settings/reset_settings_handler.cc310
-rw-r--r--chromium/chrome/browser/ui/webui/settings/reset_settings_handler.h120
-rw-r--r--chromium/chrome/browser/ui/webui/settings/reset_settings_handler_unittest.cc107
-rw-r--r--chromium/chrome/browser/ui/webui/settings/safe_browsing_handler.cc70
-rw-r--r--chromium/chrome/browser/ui/webui/settings/safe_browsing_handler.h52
-rw-r--r--chromium/chrome/browser/ui/webui/settings/search_engines_handler.cc560
-rw-r--r--chromium/chrome/browser/ui/webui/settings/search_engines_handler.h137
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc418
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h122
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc285
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_cookies_view_handler.h92
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_default_browser_handler.cc107
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_default_browser_handler.h65
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_import_data_handler.cc255
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_import_data_handler.h75
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc257
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler.h93
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler_unittest.cc140
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_media_devices_selection_handler.cc187
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_media_devices_selection_handler.h68
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_page_ui_handler.cc15
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_page_ui_handler.h30
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_startup_pages_handler.cc201
-rw-r--r--chromium/chrome/browser/ui/webui/settings/settings_startup_pages_handler.h77
-rw-r--r--chromium/chrome/browser/ui/webui/settings/site_settings_handler.cc758
-rw-r--r--chromium/chrome/browser/ui/webui/settings/site_settings_handler.h136
-rw-r--r--chromium/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc418
-rw-r--r--chromium/chrome/browser/ui/webui/settings/system_handler.cc40
-rw-r--r--chromium/chrome/browser/ui/webui/settings/system_handler.h43
-rw-r--r--chromium/chrome/browser/ui/webui/settings_utils.cc29
-rw-r--r--chromium/chrome/browser/ui/webui/settings_utils.h40
-rw-r--r--chromium/chrome/browser/ui/webui/settings_utils_linux.cc145
-rw-r--r--chromium/chrome/browser/ui/webui/settings_utils_mac.mm44
-rw-r--r--chromium/chrome/browser/ui/webui/settings_utils_unittest.cc28
-rw-r--r--chromium/chrome/browser/ui/webui/settings_utils_win.cc115
-rw-r--r--chromium/chrome/browser/ui/webui/signin/OWNERS7
-rw-r--r--chromium/chrome/browser/ui/webui/signin/inline_login_handler.cc292
-rw-r--r--chromium/chrome/browser/ui/webui/signin/inline_login_handler.h77
-rw-r--r--chromium/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc884
-rw-r--r--chromium/chrome/browser/ui/webui/signin/inline_login_handler_impl.h217
-rw-r--r--chromium/chrome/browser/ui/webui/signin/inline_login_ui.cc80
-rw-r--r--chromium/chrome/browser/ui/webui/signin/inline_login_ui.h27
-rw-r--r--chromium/chrome/browser/ui/webui/signin/inline_login_ui_browsertest.cc932
-rw-r--r--chromium/chrome/browser/ui/webui/signin/login_ui_service.cc97
-rw-r--r--chromium/chrome/browser/ui/webui/signin/login_ui_service.h112
-rw-r--r--chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.cc45
-rw-r--r--chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.h46
-rw-r--r--chromium/chrome/browser/ui/webui/signin/login_ui_service_unittest.cc49
-rw-r--r--chromium/chrome/browser/ui/webui/signin/login_ui_test_utils.cc293
-rw-r--r--chromium/chrome/browser/ui/webui/signin/login_ui_test_utils.h70
-rw-r--r--chromium/chrome/browser/ui/webui/signin/md_user_manager_ui.cc134
-rw-r--r--chromium/chrome/browser/ui/webui/signin/md_user_manager_ui.h47
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc817
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler.h265
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc659
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.cc204
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h99
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_ui.cc65
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_ui.h23
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_error_handler.cc116
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_error_handler.h93
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_error_handler_unittest.cc179
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_error_ui.cc134
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_error_ui.h32
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_supervised_user_import_handler.cc317
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_supervised_user_import_handler.h101
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_supervised_user_import_handler_unittest.cc262
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_utils.cc90
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_utils.h46
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_web_dialog_ui.cc8
-rw-r--r--chromium/chrome/browser/ui/webui/signin/signin_web_dialog_ui.h23
-rw-r--r--chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc153
-rw-r--r--chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.h74
-rw-r--r--chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc355
-rw-r--r--chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc70
-rw-r--r--chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.h32
-rw-r--r--chromium/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc1065
-rw-r--r--chromium/chrome/browser/ui/webui/signin/user_manager_screen_handler.h145
-rw-r--r--chromium/chrome/browser/ui/webui/signin/user_manager_ui_browsertest.cc88
-rw-r--r--chromium/chrome/browser/ui/webui/signin_internals_ui.cc115
-rw-r--r--chromium/chrome/browser/ui/webui/signin_internals_ui.h36
-rw-r--r--chromium/chrome/browser/ui/webui/site_settings_helper.cc463
-rw-r--r--chromium/chrome/browser/ui/webui/site_settings_helper.h141
-rw-r--r--chromium/chrome/browser/ui/webui/site_settings_helper_unittest.cc115
-rw-r--r--chromium/chrome/browser/ui/webui/snippets_internals_message_handler.cc520
-rw-r--r--chromium/chrome/browser/ui/webui/snippets_internals_message_handler.h102
-rw-r--r--chromium/chrome/browser/ui/webui/snippets_internals_ui.cc41
-rw-r--r--chromium/chrome/browser/ui/webui/snippets_internals_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/supervised_user_internals_message_handler.cc288
-rw-r--r--chromium/chrome/browser/ui/webui/supervised_user_internals_message_handler.h73
-rw-r--r--chromium/chrome/browser/ui/webui/supervised_user_internals_ui.cc42
-rw-r--r--chromium/chrome/browser/ui/webui/supervised_user_internals_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/sync_file_system_internals/DEPS3
-rw-r--r--chromium/chrome/browser/ui/webui/sync_file_system_internals/OWNERS7
-rw-r--r--chromium/chrome/browser/ui/webui/sync_file_system_internals/dump_database_handler.cc44
-rw-r--r--chromium/chrome/browser/ui/webui/sync_file_system_internals/dump_database_handler.h35
-rw-r--r--chromium/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.cc117
-rw-r--r--chromium/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.h47
-rw-r--r--chromium/chrome/browser/ui/webui/sync_file_system_internals/file_metadata_handler.cc84
-rw-r--r--chromium/chrome/browser/ui/webui/sync_file_system_internals/file_metadata_handler.h47
-rw-r--r--chromium/chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_handler.cc194
-rw-r--r--chromium/chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_handler.h64
-rw-r--r--chromium/chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_ui.cc61
-rw-r--r--chromium/chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_ui.h20
-rw-r--r--chromium/chrome/browser/ui/webui/sync_internals_browsertest.js407
-rw-r--r--chromium/chrome/browser/ui/webui/sync_internals_message_handler.cc323
-rw-r--r--chromium/chrome/browser/ui/webui/sync_internals_message_handler.h147
-rw-r--r--chromium/chrome/browser/ui/webui/sync_internals_message_handler_unittest.cc313
-rw-r--r--chromium/chrome/browser/ui/webui/sync_internals_ui.cc60
-rw-r--r--chromium/chrome/browser/ui/webui/sync_internals_ui.h22
-rw-r--r--chromium/chrome/browser/ui/webui/sync_internals_ui_unittest.cc222
-rw-r--r--chromium/chrome/browser/ui/webui/sync_setup_browsertest.js139
-rw-r--r--chromium/chrome/browser/ui/webui/system_info_ui.cc201
-rw-r--r--chromium/chrome/browser/ui/webui/system_info_ui.h19
-rw-r--r--chromium/chrome/browser/ui/webui/task_scheduler_internals/OWNERS1
-rw-r--r--chromium/chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui.cc117
-rw-r--r--chromium/chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui_browsertest.cc41
-rw-r--r--chromium/chrome/browser/ui/webui/test_files_request_filter.cc48
-rw-r--r--chromium/chrome/browser/ui/webui/test_files_request_filter.h19
-rw-r--r--chromium/chrome/browser/ui/webui/theme_handler.cc58
-rw-r--r--chromium/chrome/browser/ui/webui/theme_handler.h41
-rw-r--r--chromium/chrome/browser/ui/webui/theme_source.cc229
-rw-r--r--chromium/chrome/browser/ui/webui/theme_source.h56
-rw-r--r--chromium/chrome/browser/ui/webui/theme_source_unittest.cc99
-rw-r--r--chromium/chrome/browser/ui/webui/translate_internals/OWNERS1
-rw-r--r--chromium/chrome/browser/ui/webui/translate_internals/translate_internals_handler.cc252
-rw-r--r--chromium/chrome/browser/ui/webui/translate_internals/translate_internals_handler.h100
-rw-r--r--chromium/chrome/browser/ui/webui/translate_internals/translate_internals_ui.cc88
-rw-r--r--chromium/chrome/browser/ui/webui/translate_internals/translate_internals_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/uber/uber_ui.cc187
-rw-r--r--chromium/chrome/browser/ui/webui/uber/uber_ui.h66
-rw-r--r--chromium/chrome/browser/ui/webui/uber/uber_ui_browsertest.cc86
-rw-r--r--chromium/chrome/browser/ui/webui/usb_internals/OWNERS2
-rw-r--r--chromium/chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.cc111
-rw-r--r--chromium/chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.h37
-rw-r--r--chromium/chrome/browser/ui/webui/usb_internals/usb_internals_ui.cc37
-rw-r--r--chromium/chrome/browser/ui/webui/usb_internals/usb_internals_ui.h31
-rw-r--r--chromium/chrome/browser/ui/webui/user_actions/user_actions_ui.cc31
-rw-r--r--chromium/chrome/browser/ui/webui/user_actions/user_actions_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/user_actions/user_actions_ui_handler.cc29
-rw-r--r--chromium/chrome/browser/ui/webui/user_actions/user_actions_ui_handler.h32
-rw-r--r--chromium/chrome/browser/ui/webui/version_handler.cc131
-rw-r--r--chromium/chrome/browser/ui/webui/version_handler.h46
-rw-r--r--chromium/chrome/browser/ui/webui/version_handler_chromeos.cc52
-rw-r--r--chromium/chrome/browser/ui/webui/version_handler_chromeos.h36
-rw-r--r--chromium/chrome/browser/ui/webui/version_ui.cc200
-rw-r--r--chromium/chrome/browser/ui/webui/version_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/voice_search_ui.cc446
-rw-r--r--chromium/chrome/browser/ui/webui/voice_search_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/web_dialog_web_contents_delegate_unittest.cc118
-rw-r--r--chromium/chrome/browser/ui/webui/web_ui_test_handler.cc117
-rw-r--r--chromium/chrome/browser/ui/webui/web_ui_test_handler.h75
-rw-r--r--chromium/chrome/browser/ui/webui/webapks_handler.cc72
-rw-r--r--chromium/chrome/browser/ui/webui/webapks_handler.h43
-rw-r--r--chromium/chrome/browser/ui/webui/webapks_ui.cc43
-rw-r--r--chromium/chrome/browser/ui/webui/webapks_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/webui_browsertest.cc94
-rw-r--r--chromium/chrome/browser/ui/webui/webui_webview_browsertest.cc371
-rw-r--r--chromium/chrome/browser/ui/webui/welcome_handler.cc97
-rw-r--r--chromium/chrome/browser/ui/webui/welcome_handler.h60
-rw-r--r--chromium/chrome/browser/ui/webui/welcome_ui.cc73
-rw-r--r--chromium/chrome/browser/ui/webui/welcome_ui.h21
-rw-r--r--chromium/chrome/browser/ui/webui/welcome_win10_handler.cc176
-rw-r--r--chromium/chrome/browser/ui/webui/welcome_win10_handler.h69
-rw-r--r--chromium/chrome/browser/ui/webui/welcome_win10_ui.cc138
-rw-r--r--chromium/chrome/browser/ui/webui/welcome_win10_ui.h21
929 files changed, 159593 insertions, 6 deletions
diff --git a/chromium/chrome/VERSION b/chromium/chrome/VERSION
index bd6813af23a..393f8b9b522 100644
--- a/chromium/chrome/VERSION
+++ b/chromium/chrome/VERSION
@@ -1,4 +1,4 @@
MAJOR=60
MINOR=0
BUILD=3112
-PATCH=96
+PATCH=116
diff --git a/chromium/chrome/browser/devtools/OWNERS b/chromium/chrome/browser/devtools/OWNERS
new file mode 100644
index 00000000000..8d426acaa95
--- /dev/null
+++ b/chromium/chrome/browser/devtools/OWNERS
@@ -0,0 +1,7 @@
+dgozman@chromium.org
+pfeldman@chromium.org
+
+per-file devtools_embedder_message_dispatcher.*=set noparent
+per-file devtools_embedder_message_dispatcher.*=file://ipc/SECURITY_OWNERS
+
+# COMPONENT: Platform>DevTools
diff --git a/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc b/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
new file mode 100644
index 00000000000..75b04a7bff9
--- /dev/null
+++ b/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
@@ -0,0 +1,494 @@
+// 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/devtools/chrome_devtools_manager_delegate.h"
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "chrome/browser/devtools/device/android_device_manager.h"
+#include "chrome/browser/devtools/device/tcp_device_provider.h"
+#include "chrome/browser/devtools/devtools_network_protocol_handler.h"
+#include "chrome/browser/devtools/devtools_protocol_constants.h"
+#include "chrome/browser/devtools/devtools_window.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser_navigator.h"
+#include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
+#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/grit/browser_resources.h"
+#include "components/guest_view/browser/guest_view_base.h"
+#include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/browser/extension_host.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/process_manager.h"
+#include "ui/base/resource/resource_bundle.h"
+
+using content::DevToolsAgentHost;
+
+char ChromeDevToolsManagerDelegate::kTypeApp[] = "app";
+char ChromeDevToolsManagerDelegate::kTypeBackgroundPage[] = "background_page";
+
+namespace {
+
+char kLocationsParam[] = "locations";
+char kHostParam[] = "host";
+char kPortParam[] = "port";
+
+BrowserWindow* GetBrowserWindow(int window_id) {
+ for (auto* b : *BrowserList::GetInstance()) {
+ if (b->session_id().id() == window_id)
+ return b->window();
+ }
+ return nullptr;
+}
+
+// Get the bounds and state of the browser window. The bounds is for the
+// restored window when the window is minimized. Otherwise, it is for the actual
+// window.
+std::unique_ptr<base::DictionaryValue> GetBounds(BrowserWindow* window) {
+ gfx::Rect bounds;
+ if (window->IsMinimized())
+ bounds = window->GetRestoredBounds();
+ else
+ bounds = window->GetBounds();
+
+ auto bounds_object = base::MakeUnique<base::DictionaryValue>();
+
+ bounds_object->SetInteger("left", bounds.x());
+ bounds_object->SetInteger("top", bounds.y());
+ bounds_object->SetInteger("width", bounds.width());
+ bounds_object->SetInteger("height", bounds.height());
+
+ std::string window_state = "normal";
+ if (window->IsMinimized())
+ window_state = "minimized";
+ if (window->IsMaximized())
+ window_state = "maximized";
+ if (window->IsFullscreen())
+ window_state = "fullscreen";
+ bounds_object->SetString("windowState", window_state);
+
+ return bounds_object;
+}
+
+bool GetExtensionInfo(content::WebContents* wc,
+ std::string* name,
+ std::string* type) {
+ Profile* profile = Profile::FromBrowserContext(wc->GetBrowserContext());
+ if (!profile)
+ return false;
+ const extensions::Extension* extension =
+ extensions::ProcessManager::Get(profile)->GetExtensionForWebContents(wc);
+ if (!extension)
+ return false;
+ extensions::ExtensionHost* extension_host =
+ extensions::ProcessManager::Get(profile)->GetBackgroundHostForExtension(
+ extension->id());
+ if (extension_host && extension_host->host_contents() == wc) {
+ *name = extension->name();
+ *type = ChromeDevToolsManagerDelegate::kTypeBackgroundPage;
+ return true;
+ } else if (extension->is_hosted_app() ||
+ extension->is_legacy_packaged_app() ||
+ extension->is_platform_app()) {
+ *name = extension->name();
+ *type = ChromeDevToolsManagerDelegate::kTypeApp;
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+// static
+std::unique_ptr<base::DictionaryValue>
+ChromeDevToolsManagerDelegate::GetWindowForTarget(
+ int id,
+ base::DictionaryValue* params) {
+ std::string target_id;
+ if (!params->GetString("targetId", &target_id))
+ return DevToolsProtocol::CreateInvalidParamsResponse(id, "targetId");
+
+ Browser* browser = nullptr;
+ scoped_refptr<DevToolsAgentHost> host =
+ DevToolsAgentHost::GetForId(target_id);
+ if (!host)
+ return DevToolsProtocol::CreateErrorResponse(id, "No target with given id");
+ content::WebContents* web_contents = host->GetWebContents();
+ if (!web_contents) {
+ return DevToolsProtocol::CreateErrorResponse(
+ id, "No web contents in the target");
+ }
+ for (auto* b : *BrowserList::GetInstance()) {
+ int tab_index = b->tab_strip_model()->GetIndexOfWebContents(web_contents);
+ if (tab_index != TabStripModel::kNoTab)
+ browser = b;
+ }
+ if (!browser) {
+ return DevToolsProtocol::CreateErrorResponse(id,
+ "Browser window not found");
+ }
+
+ auto result = base::MakeUnique<base::DictionaryValue>();
+ result->SetInteger("windowId", browser->session_id().id());
+ result->Set("bounds", GetBounds(browser->window()));
+ return DevToolsProtocol::CreateSuccessResponse(id, std::move(result));
+}
+
+// static
+std::unique_ptr<base::DictionaryValue>
+ChromeDevToolsManagerDelegate::GetWindowBounds(int id,
+ base::DictionaryValue* params) {
+ int window_id;
+ if (!params->GetInteger("windowId", &window_id))
+ return DevToolsProtocol::CreateInvalidParamsResponse(id, "windowId");
+ BrowserWindow* window = GetBrowserWindow(window_id);
+ if (!window) {
+ return DevToolsProtocol::CreateErrorResponse(id,
+ "Browser window not found");
+ }
+
+ auto result = base::MakeUnique<base::DictionaryValue>();
+ result->Set("bounds", GetBounds(window));
+ return DevToolsProtocol::CreateSuccessResponse(id, std::move(result));
+}
+
+// static
+std::unique_ptr<base::DictionaryValue>
+ChromeDevToolsManagerDelegate::SetWindowBounds(int id,
+ base::DictionaryValue* params) {
+ int window_id;
+ if (!params->GetInteger("windowId", &window_id))
+ return DevToolsProtocol::CreateInvalidParamsResponse(id, "windowId");
+ BrowserWindow* window = GetBrowserWindow(window_id);
+ if (!window) {
+ return DevToolsProtocol::CreateErrorResponse(id,
+ "Browser window not found");
+ }
+
+ const base::Value* value = nullptr;
+ const base::DictionaryValue* bounds_dict = nullptr;
+ if (!params->Get("bounds", &value) || !value->GetAsDictionary(&bounds_dict))
+ return DevToolsProtocol::CreateInvalidParamsResponse(id, "bounds");
+
+ std::string window_state;
+ if (!bounds_dict->GetString("windowState", &window_state))
+ window_state = "normal";
+ else if (window_state != "normal" && window_state != "minimized" &&
+ window_state != "maximized" && window_state != "fullscreen")
+ return DevToolsProtocol::CreateInvalidParamsResponse(id, "windowState");
+
+ // Compute updated bounds when window state is normal.
+ bool set_bounds = false;
+ gfx::Rect bounds = window->GetBounds();
+ int left, top, width, height;
+ if (bounds_dict->GetInteger("left", &left)) {
+ bounds.set_x(left);
+ set_bounds = true;
+ }
+ if (bounds_dict->GetInteger("top", &top)) {
+ bounds.set_y(top);
+ set_bounds = true;
+ }
+ if (bounds_dict->GetInteger("width", &width)) {
+ if (width < 0)
+ return DevToolsProtocol::CreateInvalidParamsResponse(id, "width");
+ bounds.set_width(width);
+ set_bounds = true;
+ }
+ if (bounds_dict->GetInteger("height", &height)) {
+ if (height < 0)
+ return DevToolsProtocol::CreateInvalidParamsResponse(id, "height");
+ bounds.set_height(height);
+ set_bounds = true;
+ }
+
+ if (set_bounds && window_state != "normal") {
+ return DevToolsProtocol::CreateErrorResponse(
+ id,
+ "The 'minimized', 'maximized' and 'fullscreen' states cannot be "
+ "combined with 'left', 'top', 'width' or 'height'");
+ }
+
+ if (set_bounds && (window->IsMinimized() || window->IsMaximized() ||
+ window->IsFullscreen())) {
+ return DevToolsProtocol::CreateErrorResponse(
+ id,
+ "To resize minimized/maximized/fullscreen window, restore it to normal "
+ "state first.");
+ }
+
+ if (window_state == "fullscreen") {
+ if (window->IsMinimized()) {
+ return DevToolsProtocol::CreateErrorResponse(id,
+ "To make minimized window "
+ "fullscreen, restore it to "
+ "normal state first.");
+ }
+ window->GetExclusiveAccessContext()->EnterFullscreen(
+ GURL(), EXCLUSIVE_ACCESS_BUBBLE_TYPE_NONE);
+ }
+
+ if (window_state == "maximized") {
+ if (window->IsMinimized() || window->IsFullscreen()) {
+ return DevToolsProtocol::CreateErrorResponse(
+ id,
+ "To maximize a minimized or fullscreen window, restore it to normal "
+ "state first.");
+ }
+ window->Maximize();
+ }
+
+ if (window_state == "minimized") {
+ if (window->IsFullscreen()) {
+ return DevToolsProtocol::CreateErrorResponse(
+ id,
+ "To minimize a fullscreen window, restore it to normal "
+ "state first.");
+ }
+ window->Minimize();
+ }
+
+ if (window_state == "normal") {
+ if (window->IsFullscreen()) {
+ window->GetExclusiveAccessContext()->ExitFullscreen();
+ } else if (window->IsMinimized()) {
+ window->Show();
+ } else if (window->IsMaximized()) {
+ window->Restore();
+ } else if (set_bounds) {
+ window->SetBounds(bounds);
+ }
+ }
+
+ return DevToolsProtocol::CreateSuccessResponse(id, nullptr);
+}
+
+std::unique_ptr<base::DictionaryValue>
+ChromeDevToolsManagerDelegate::HandleBrowserCommand(
+ int id,
+ std::string method,
+ base::DictionaryValue* params) {
+ if (method == chrome::devtools::Browser::getWindowForTarget::kName)
+ return GetWindowForTarget(id, params);
+ if (method == chrome::devtools::Browser::getWindowBounds::kName)
+ return GetWindowBounds(id, params);
+ if (method == chrome::devtools::Browser::setWindowBounds::kName)
+ return SetWindowBounds(id, params);
+ return nullptr;
+}
+
+class ChromeDevToolsManagerDelegate::HostData {
+ public:
+ HostData() {}
+ ~HostData() {}
+
+ RemoteLocations& remote_locations() { return remote_locations_; }
+
+ void set_remote_locations(RemoteLocations& locations) {
+ remote_locations_.swap(locations);
+ }
+
+ private:
+ RemoteLocations remote_locations_;
+};
+
+ChromeDevToolsManagerDelegate::ChromeDevToolsManagerDelegate()
+ : network_protocol_handler_(new DevToolsNetworkProtocolHandler()) {
+ content::DevToolsAgentHost::AddObserver(this);
+}
+
+ChromeDevToolsManagerDelegate::~ChromeDevToolsManagerDelegate() {
+ content::DevToolsAgentHost::RemoveObserver(this);
+}
+
+void ChromeDevToolsManagerDelegate::Inspect(
+ content::DevToolsAgentHost* agent_host) {
+ DevToolsWindow::OpenDevToolsWindow(agent_host, nullptr);
+}
+
+base::DictionaryValue* ChromeDevToolsManagerDelegate::HandleCommand(
+ DevToolsAgentHost* agent_host,
+ base::DictionaryValue* command_dict) {
+
+ int id = 0;
+ std::string method;
+ base::DictionaryValue* params = nullptr;
+ if (!DevToolsProtocol::ParseCommand(command_dict, &id, &method, &params))
+ return nullptr;
+
+ if (agent_host->GetType() == DevToolsAgentHost::kTypeBrowser &&
+ method.find("Browser.") == 0)
+ return HandleBrowserCommand(id, method, params).release();
+
+ if (method == chrome::devtools::Target::setRemoteLocations::kName)
+ return SetRemoteLocations(agent_host, id, params).release();
+
+ return network_protocol_handler_->HandleCommand(agent_host, command_dict);
+}
+
+std::string ChromeDevToolsManagerDelegate::GetTargetType(
+ content::WebContents* web_contents) {
+ for (TabContentsIterator it; !it.done(); it.Next()) {
+ if (*it == web_contents)
+ return DevToolsAgentHost::kTypePage;
+ }
+
+ std::string extension_name;
+ std::string extension_type;
+ if (!GetExtensionInfo(web_contents, &extension_name, &extension_type))
+ return DevToolsAgentHost::kTypeOther;
+ return extension_type;
+}
+
+std::string ChromeDevToolsManagerDelegate::GetTargetTitle(
+ content::WebContents* web_contents) {
+ std::string extension_name;
+ std::string extension_type;
+ if (!GetExtensionInfo(web_contents, &extension_name, &extension_type))
+ return std::string();
+ return extension_name;
+}
+
+scoped_refptr<DevToolsAgentHost>
+ChromeDevToolsManagerDelegate::CreateNewTarget(const GURL& url) {
+ chrome::NavigateParams params(ProfileManager::GetLastUsedProfile(),
+ url, ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
+ params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
+ chrome::Navigate(&params);
+ if (!params.target_contents)
+ return nullptr;
+ return DevToolsAgentHost::GetOrCreateFor(params.target_contents);
+}
+
+std::string ChromeDevToolsManagerDelegate::GetDiscoveryPageHTML() {
+ return ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_DEVTOOLS_DISCOVERY_PAGE_HTML).as_string();
+}
+
+std::string ChromeDevToolsManagerDelegate::GetFrontendResource(
+ const std::string& path) {
+ return content::DevToolsFrontendHost::GetFrontendResource(path).as_string();
+}
+
+void ChromeDevToolsManagerDelegate::DevToolsAgentHostAttached(
+ content::DevToolsAgentHost* agent_host) {
+ network_protocol_handler_->DevToolsAgentStateChanged(agent_host, true);
+
+ DCHECK(host_data_.find(agent_host) == host_data_.end());
+ host_data_[agent_host].reset(new ChromeDevToolsManagerDelegate::HostData());
+}
+
+void ChromeDevToolsManagerDelegate::DevToolsAgentHostDetached(
+ content::DevToolsAgentHost* agent_host) {
+ network_protocol_handler_->DevToolsAgentStateChanged(agent_host, false);
+ // This class is created lazily, so it may not know about some attached hosts.
+ if (host_data_.find(agent_host) != host_data_.end()) {
+ host_data_.erase(agent_host);
+ UpdateDeviceDiscovery();
+ }
+}
+
+void ChromeDevToolsManagerDelegate::DevicesAvailable(
+ const DevToolsDeviceDiscovery::CompleteDevices& devices) {
+ DevToolsAgentHost::List remote_targets;
+ for (const auto& complete : devices) {
+ for (const auto& browser : complete.second->browsers()) {
+ for (const auto& page : browser->pages())
+ remote_targets.push_back(page->CreateTarget());
+ }
+ }
+ remote_agent_hosts_.swap(remote_targets);
+}
+
+void ChromeDevToolsManagerDelegate::UpdateDeviceDiscovery() {
+ RemoteLocations remote_locations;
+ for (const auto& pair : host_data_) {
+ RemoteLocations& locations = pair.second->remote_locations();
+ remote_locations.insert(locations.begin(), locations.end());
+ }
+
+ bool equals = remote_locations.size() == remote_locations_.size();
+ if (equals) {
+ RemoteLocations::iterator it1 = remote_locations.begin();
+ RemoteLocations::iterator it2 = remote_locations_.begin();
+ while (it1 != remote_locations.end()) {
+ DCHECK(it2 != remote_locations_.end());
+ if (!(*it1).Equals(*it2))
+ equals = false;
+ ++it1;
+ ++it2;
+ }
+ DCHECK(it2 == remote_locations_.end());
+ }
+
+ if (equals)
+ return;
+
+ if (remote_locations.empty()) {
+ device_discovery_.reset();
+ remote_agent_hosts_.clear();
+ } else {
+ if (!device_manager_)
+ device_manager_ = AndroidDeviceManager::Create();
+
+ AndroidDeviceManager::DeviceProviders providers;
+ providers.push_back(new TCPDeviceProvider(remote_locations));
+ device_manager_->SetDeviceProviders(providers);
+
+ device_discovery_.reset(new DevToolsDeviceDiscovery(device_manager_.get(),
+ base::Bind(&ChromeDevToolsManagerDelegate::DevicesAvailable,
+ base::Unretained(this))));
+ }
+ remote_locations_.swap(remote_locations);
+}
+
+std::unique_ptr<base::DictionaryValue>
+ChromeDevToolsManagerDelegate::SetRemoteLocations(
+ content::DevToolsAgentHost* agent_host,
+ int command_id,
+ base::DictionaryValue* params) {
+ // Could have been created late.
+ if (host_data_.find(agent_host) == host_data_.end())
+ DevToolsAgentHostAttached(agent_host);
+
+ std::set<net::HostPortPair> tcp_locations;
+ base::ListValue* locations;
+ if (!params->GetList(kLocationsParam, &locations))
+ return DevToolsProtocol::CreateInvalidParamsResponse(command_id,
+ kLocationsParam);
+ for (const auto& item : *locations) {
+ if (!item.IsType(base::Value::Type::DICTIONARY)) {
+ return DevToolsProtocol::CreateInvalidParamsResponse(command_id,
+ kLocationsParam);
+ }
+ const base::DictionaryValue* dictionary =
+ static_cast<const base::DictionaryValue*>(&item);
+ std::string host;
+ if (!dictionary->GetStringWithoutPathExpansion(kHostParam, &host)) {
+ return DevToolsProtocol::CreateInvalidParamsResponse(command_id,
+ kLocationsParam);
+ }
+ int port = 0;
+ if (!dictionary->GetIntegerWithoutPathExpansion(kPortParam, &port)) {
+ return DevToolsProtocol::CreateInvalidParamsResponse(command_id,
+ kLocationsParam);
+ }
+ tcp_locations.insert(net::HostPortPair(host, port));
+ }
+
+ host_data_[agent_host]->set_remote_locations(tcp_locations);
+ UpdateDeviceDiscovery();
+
+ return DevToolsProtocol::CreateSuccessResponse(command_id, nullptr);
+}
diff --git a/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h b/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h
new file mode 100644
index 00000000000..3042a2216a0
--- /dev/null
+++ b/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h
@@ -0,0 +1,89 @@
+// 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_DEVTOOLS_CHROME_DEVTOOLS_MANAGER_DELEGATE_H_
+#define CHROME_BROWSER_DEVTOOLS_CHROME_DEVTOOLS_MANAGER_DELEGATE_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/devtools/device/devtools_device_discovery.h"
+#include "content/public/browser/devtools_agent_host_observer.h"
+#include "content/public/browser/devtools_manager_delegate.h"
+#include "net/base/host_port_pair.h"
+
+class DevToolsNetworkProtocolHandler;
+
+class ChromeDevToolsManagerDelegate :
+ public content::DevToolsManagerDelegate,
+ public content::DevToolsAgentHostObserver {
+ public:
+ static char kTypeApp[];
+ static char kTypeBackgroundPage[];
+
+ ChromeDevToolsManagerDelegate();
+ ~ChromeDevToolsManagerDelegate() override;
+
+ private:
+ class HostData;
+ friend class DevToolsManagerDelegateTest;
+ using RemoteLocations = std::set<net::HostPortPair>;
+
+ // content::DevToolsManagerDelegate implementation.
+ void Inspect(content::DevToolsAgentHost* agent_host) override;
+ base::DictionaryValue* HandleCommand(
+ content::DevToolsAgentHost* agent_host,
+ base::DictionaryValue* command_dict) override;
+ std::string GetTargetType(content::WebContents* web_contents) override;
+ std::string GetTargetTitle(content::WebContents* web_contents) override;
+ scoped_refptr<content::DevToolsAgentHost> CreateNewTarget(
+ const GURL& url) override;
+ std::string GetDiscoveryPageHTML() override;
+ std::string GetFrontendResource(const std::string& path) override;
+
+ // content::DevToolsAgentHostObserver overrides.
+ void DevToolsAgentHostAttached(
+ content::DevToolsAgentHost* agent_host) override;
+ void DevToolsAgentHostDetached(
+ content::DevToolsAgentHost* agent_host) override;
+
+ void UpdateDeviceDiscovery();
+ void DevicesAvailable(
+ const DevToolsDeviceDiscovery::CompleteDevices& devices);
+
+ std::unique_ptr<base::DictionaryValue> SetRemoteLocations(
+ content::DevToolsAgentHost* agent_host,
+ int command_id,
+ base::DictionaryValue* params);
+
+ std::unique_ptr<base::DictionaryValue> HandleBrowserCommand(
+ int id,
+ std::string method,
+ base::DictionaryValue* params);
+ static std::unique_ptr<base::DictionaryValue> GetWindowForTarget(
+ int id,
+ base::DictionaryValue* params);
+ static std::unique_ptr<base::DictionaryValue> GetWindowBounds(
+ int id,
+ base::DictionaryValue* params);
+ static std::unique_ptr<base::DictionaryValue> SetWindowBounds(
+ int id,
+ base::DictionaryValue* params);
+
+ std::unique_ptr<DevToolsNetworkProtocolHandler> network_protocol_handler_;
+ std::map<content::DevToolsAgentHost*, std::unique_ptr<HostData>> host_data_;
+
+ std::unique_ptr<AndroidDeviceManager> device_manager_;
+ std::unique_ptr<DevToolsDeviceDiscovery> device_discovery_;
+ content::DevToolsAgentHost::List remote_agent_hosts_;
+ RemoteLocations remote_locations_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeDevToolsManagerDelegate);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_CHROME_DEVTOOLS_MANAGER_DELEGATE_H_
diff --git a/chromium/chrome/browser/devtools/device/adb/adb_client_socket.cc b/chromium/chrome/browser/devtools/device/adb/adb_client_socket.cc
new file mode 100644
index 00000000000..3ba639762a9
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/adb/adb_client_socket.cc
@@ -0,0 +1,304 @@
+// 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/devtools/device/adb/adb_client_socket.h"
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ptr_util.h"
+#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 "net/base/ip_address.h"
+#include "net/base/net_errors.h"
+#include "net/log/net_log_source.h"
+#include "net/socket/tcp_client_socket.h"
+
+namespace {
+
+const int kBufferSize = 16 * 1024;
+const char kOkayResponse[] = "OKAY";
+const char kHostTransportCommand[] = "host:transport:%s";
+const char kLocalhost[] = "127.0.0.1";
+
+std::string EncodeMessage(const std::string& message) {
+ static const char kHexChars[] = "0123456789ABCDEF";
+
+ size_t length = message.length();
+ std::string result(4, '\0');
+ char b = reinterpret_cast<const char*>(&length)[1];
+ result[0] = kHexChars[(b >> 4) & 0xf];
+ result[1] = kHexChars[b & 0xf];
+ b = reinterpret_cast<const char*>(&length)[0];
+ result[2] = kHexChars[(b >> 4) & 0xf];
+ result[3] = kHexChars[b & 0xf];
+ return result + message;
+}
+
+class AdbTransportSocket : public AdbClientSocket {
+ public:
+ AdbTransportSocket(int port,
+ const std::string& serial,
+ const std::string& socket_name,
+ const SocketCallback& callback)
+ : AdbClientSocket(port),
+ serial_(serial),
+ socket_name_(socket_name),
+ callback_(callback) {
+ Connect(base::Bind(&AdbTransportSocket::OnConnected,
+ base::Unretained(this)));
+ }
+
+ private:
+ ~AdbTransportSocket() {}
+
+ void OnConnected(int result) {
+ if (!CheckNetResultOrDie(result))
+ return;
+ SendCommand(base::StringPrintf(kHostTransportCommand, serial_.c_str()),
+ true, base::Bind(&AdbTransportSocket::SendLocalAbstract,
+ base::Unretained(this)));
+ }
+
+ void SendLocalAbstract(int result, const std::string& response) {
+ if (!CheckNetResultOrDie(result))
+ return;
+ SendCommand(socket_name_, true,
+ base::Bind(&AdbTransportSocket::OnSocketAvailable,
+ base::Unretained(this)));
+ }
+
+ void OnSocketAvailable(int result, const std::string& response) {
+ if (!CheckNetResultOrDie(result))
+ return;
+ callback_.Run(net::OK, std::move(socket_));
+ delete this;
+ }
+
+ bool CheckNetResultOrDie(int result) {
+ if (result >= 0)
+ return true;
+ callback_.Run(result, base::WrapUnique<net::StreamSocket>(NULL));
+ delete this;
+ return false;
+ }
+
+ std::string serial_;
+ std::string socket_name_;
+ SocketCallback callback_;
+};
+
+class AdbQuerySocket : AdbClientSocket {
+ public:
+ AdbQuerySocket(int port,
+ const std::string& query,
+ const CommandCallback& callback)
+ : AdbClientSocket(port),
+ current_query_(0),
+ callback_(callback) {
+ queries_ = base::SplitString(query, "|", base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY);
+ if (queries_.empty()) {
+ CheckNetResultOrDie(net::ERR_INVALID_ARGUMENT);
+ return;
+ }
+ Connect(base::Bind(&AdbQuerySocket::SendNextQuery,
+ base::Unretained(this)));
+ }
+
+ private:
+ ~AdbQuerySocket() {
+ }
+
+ void SendNextQuery(int result) {
+ if (!CheckNetResultOrDie(result))
+ return;
+ std::string query = queries_[current_query_];
+ if (query.length() > 0xFFFF) {
+ CheckNetResultOrDie(net::ERR_MSG_TOO_BIG);
+ return;
+ }
+ bool is_void = current_query_ < queries_.size() - 1;
+ SendCommand(query, is_void,
+ base::Bind(&AdbQuerySocket::OnResponse, base::Unretained(this)));
+ }
+
+ void OnResponse(int result, const std::string& response) {
+ if (++current_query_ < queries_.size()) {
+ SendNextQuery(net::OK);
+ } else {
+ callback_.Run(result, response);
+ delete this;
+ }
+ }
+
+ bool CheckNetResultOrDie(int result) {
+ if (result >= 0)
+ return true;
+ callback_.Run(result, std::string());
+ delete this;
+ return false;
+ }
+
+ std::vector<std::string> queries_;
+ size_t current_query_;
+ CommandCallback callback_;
+};
+
+} // namespace
+
+// static
+void AdbClientSocket::AdbQuery(int port,
+ const std::string& query,
+ const CommandCallback& callback) {
+ new AdbQuerySocket(port, query, callback);
+}
+
+// static
+void AdbClientSocket::TransportQuery(int port,
+ const std::string& serial,
+ const std::string& socket_name,
+ const SocketCallback& callback) {
+ new AdbTransportSocket(port, serial, socket_name, callback);
+}
+
+AdbClientSocket::AdbClientSocket(int port)
+ : host_(kLocalhost), port_(port) {
+}
+
+AdbClientSocket::~AdbClientSocket() {
+}
+
+void AdbClientSocket::Connect(const net::CompletionCallback& callback) {
+ net::IPAddress ip_address;
+ if (!ip_address.AssignFromIPLiteral(host_)) {
+ callback.Run(net::ERR_FAILED);
+ return;
+ }
+
+ net::AddressList address_list =
+ net::AddressList::CreateFromIPAddress(ip_address, port_);
+ socket_.reset(new net::TCPClientSocket(address_list, NULL, NULL,
+ net::NetLogSource()));
+ int result = socket_->Connect(callback);
+ if (result != net::ERR_IO_PENDING)
+ callback.Run(result);
+}
+
+void AdbClientSocket::SendCommand(const std::string& command,
+ bool is_void,
+ const CommandCallback& callback) {
+ scoped_refptr<net::StringIOBuffer> request_buffer =
+ new net::StringIOBuffer(EncodeMessage(command));
+ int result = socket_->Write(request_buffer.get(),
+ request_buffer->size(),
+ base::Bind(&AdbClientSocket::ReadResponse,
+ base::Unretained(this),
+ callback,
+ is_void));
+ if (result != net::ERR_IO_PENDING)
+ ReadResponse(callback, is_void, result);
+}
+
+void AdbClientSocket::ReadResponse(const CommandCallback& callback,
+ bool is_void,
+ int result) {
+ if (result < 0) {
+ callback.Run(result, "IO error");
+ return;
+ }
+ scoped_refptr<net::IOBuffer> response_buffer =
+ new net::IOBuffer(kBufferSize);
+ result = socket_->Read(response_buffer.get(),
+ kBufferSize,
+ base::Bind(&AdbClientSocket::OnResponseHeader,
+ base::Unretained(this),
+ callback,
+ is_void,
+ response_buffer));
+ if (result != net::ERR_IO_PENDING)
+ OnResponseHeader(callback, is_void, response_buffer, result);
+}
+
+void AdbClientSocket::OnResponseHeader(
+ const CommandCallback& callback,
+ bool is_void,
+ scoped_refptr<net::IOBuffer> response_buffer,
+ int result) {
+ if (result <= 0) {
+ callback.Run(result == 0 ? net::ERR_CONNECTION_CLOSED : result,
+ "IO error");
+ return;
+ }
+
+ std::string data = std::string(response_buffer->data(), result);
+ if (result < 4) {
+ callback.Run(net::ERR_FAILED, "Response is too short: " + data);
+ return;
+ }
+
+ std::string status = data.substr(0, 4);
+ if (status != kOkayResponse) {
+ callback.Run(net::ERR_FAILED, data);
+ return;
+ }
+
+ // Trim OKAY.
+ data = data.substr(4);
+ if (!is_void)
+ OnResponseData(callback, data, response_buffer, -1, 0);
+ else
+ callback.Run(net::OK, data);
+}
+
+void AdbClientSocket::OnResponseData(
+ const CommandCallback& callback,
+ const std::string& response,
+ scoped_refptr<net::IOBuffer> response_buffer,
+ int bytes_left,
+ int result) {
+ if (result < 0) {
+ callback.Run(result, "IO error");
+ return;
+ }
+
+ std::string new_response = response +
+ std::string(response_buffer->data(), result);
+
+ if (bytes_left == -1) {
+ // First read the response header.
+ int payload_length = 0;
+ if (new_response.length() >= 4 &&
+ base::HexStringToInt(new_response.substr(0, 4), &payload_length)) {
+ new_response = new_response.substr(4);
+ bytes_left = payload_length - new_response.size();
+ }
+ } else {
+ bytes_left -= result;
+ }
+
+ if (bytes_left == 0) {
+ callback.Run(net::OK, new_response);
+ return;
+ }
+
+ // Read tail
+ result = socket_->Read(response_buffer.get(),
+ kBufferSize,
+ base::Bind(&AdbClientSocket::OnResponseData,
+ base::Unretained(this),
+ callback,
+ new_response,
+ response_buffer,
+ bytes_left));
+ if (result > 0)
+ OnResponseData(callback, new_response, response_buffer, bytes_left, result);
+ else if (result != net::ERR_IO_PENDING)
+ callback.Run(net::OK, new_response);
+}
diff --git a/chromium/chrome/browser/devtools/device/adb/adb_client_socket.h b/chromium/chrome/browser/devtools/device/adb/adb_client_socket.h
new file mode 100644
index 00000000000..f7578e71957
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/adb/adb_client_socket.h
@@ -0,0 +1,63 @@
+// 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_DEVTOOLS_DEVICE_ADB_ADB_CLIENT_SOCKET_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVICE_ADB_ADB_CLIENT_SOCKET_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "net/base/io_buffer.h"
+#include "net/socket/stream_socket.h"
+
+class AdbClientSocket {
+ public:
+ typedef base::Callback<void(int, const std::string&)> CommandCallback;
+ typedef base::Callback<void(int result, std::unique_ptr<net::StreamSocket>)>
+ SocketCallback;
+
+ static void AdbQuery(int port,
+ const std::string& query,
+ const CommandCallback& callback);
+
+
+ static void TransportQuery(int port,
+ const std::string& serial,
+ const std::string& socket_name,
+ const SocketCallback& callback);
+
+ protected:
+ explicit AdbClientSocket(int port);
+ ~AdbClientSocket();
+
+ void Connect(const net::CompletionCallback& callback);
+
+ void SendCommand(const std::string& command,
+ bool is_void,
+ const CommandCallback& callback);
+
+ std::unique_ptr<net::StreamSocket> socket_;
+
+ private:
+ void ReadResponse(const CommandCallback& callback, bool is_void, int result);
+
+ void OnResponseHeader(const CommandCallback& callback,
+ bool is_void,
+ scoped_refptr<net::IOBuffer> response_buffer,
+ int result);
+
+ void OnResponseData(const CommandCallback& callback,
+ const std::string& response,
+ scoped_refptr<net::IOBuffer> response_buffer,
+ int bytes_left,
+ int result);
+
+ std::string host_;
+ int port_;
+
+ DISALLOW_COPY_AND_ASSIGN(AdbClientSocket);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVICE_ADB_ADB_CLIENT_SOCKET_H_
diff --git a/chromium/chrome/browser/devtools/device/adb/adb_client_socket_browsertest.cc b/chromium/chrome/browser/devtools/device/adb/adb_client_socket_browsertest.cc
new file mode 100644
index 00000000000..3da5dc89d18
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/adb/adb_client_socket_browsertest.cc
@@ -0,0 +1,159 @@
+// 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/message_loop/message_loop.h"
+#include "chrome/browser/devtools/device/adb/adb_device_provider.h"
+#include "chrome/browser/devtools/device/adb/mock_adb_server.h"
+#include "chrome/browser/devtools/device/devtools_android_bridge.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/test/test_utils.h"
+
+using content::BrowserThread;
+
+static scoped_refptr<DevToolsAndroidBridge::RemoteBrowser>
+FindBrowserByDisplayName(DevToolsAndroidBridge::RemoteBrowsers browsers,
+ const std::string& name) {
+ for (DevToolsAndroidBridge::RemoteBrowsers::iterator it = browsers.begin();
+ it != browsers.end(); ++it)
+ if ((*it)->display_name() == name)
+ return *it;
+ return NULL;
+}
+
+class AdbClientSocketTest : public InProcessBrowserTest,
+ public DevToolsAndroidBridge::DeviceListListener {
+
+ public:
+ void StartTest() {
+ Profile* profile = browser()->profile();
+ android_bridge_ = DevToolsAndroidBridge::Factory::GetForProfile(profile);
+ AndroidDeviceManager::DeviceProviders device_providers;
+ device_providers.push_back(new AdbDeviceProvider());
+ android_bridge_->set_device_providers_for_test(device_providers);
+ android_bridge_->AddDeviceListListener(this);
+ content::RunMessageLoop();
+ }
+
+ void DeviceListChanged(
+ const DevToolsAndroidBridge::RemoteDevices& devices) override {
+ devices_ = devices;
+ android_bridge_->RemoveDeviceListListener(this);
+ base::MessageLoop::current()->QuitWhenIdle();
+ }
+
+ void CheckDevices() {
+ ASSERT_EQ(2U, devices_.size());
+
+ scoped_refptr<DevToolsAndroidBridge::RemoteDevice> connected =
+ devices_[0]->is_connected() ? devices_[0] : devices_[1];
+
+ scoped_refptr<DevToolsAndroidBridge::RemoteDevice> not_connected =
+ devices_[0]->is_connected() ? devices_[1] : devices_[0];
+
+ ASSERT_TRUE(connected->is_connected());
+ ASSERT_FALSE(not_connected->is_connected());
+
+ ASSERT_EQ(720, connected->screen_size().width());
+ ASSERT_EQ(1184, connected->screen_size().height());
+
+ ASSERT_EQ("01498B321301A00A", connected->serial());
+ ASSERT_EQ("Nexus 6", connected->model());
+
+ ASSERT_EQ("01498B2B0D01300E", not_connected->serial());
+ ASSERT_EQ("Offline", not_connected->model());
+
+ const DevToolsAndroidBridge::RemoteBrowsers& browsers =
+ connected->browsers();
+ ASSERT_EQ(4U, browsers.size());
+
+ scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> chrome =
+ FindBrowserByDisplayName(browsers, "Chrome");
+ ASSERT_TRUE(chrome.get());
+
+ scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> chrome_beta =
+ FindBrowserByDisplayName(browsers, "Chrome Beta");
+ ASSERT_TRUE(chrome_beta.get());
+
+ scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> chromium =
+ FindBrowserByDisplayName(browsers, "Chromium");
+ ASSERT_FALSE(chromium.get());
+
+ scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> webview =
+ FindBrowserByDisplayName(browsers, "WebView in com.sample.feed");
+ ASSERT_TRUE(webview.get());
+
+ scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> noprocess =
+ FindBrowserByDisplayName(browsers, "Noprocess");
+ ASSERT_TRUE(noprocess.get());
+
+ ASSERT_EQ("32.0.1679.0", chrome->version());
+ ASSERT_EQ("31.0.1599.0", chrome_beta->version());
+ ASSERT_EQ("4.0", webview->version());
+
+ ASSERT_EQ("Test User", chrome->user());
+ ASSERT_EQ("Test User : 2", chrome_beta->user());
+ ASSERT_EQ("Test User", webview->user());
+
+ DevToolsAndroidBridge::RemotePages chrome_pages =
+ chrome->pages();
+ DevToolsAndroidBridge::RemotePages chrome_beta_pages =
+ chrome_beta->pages();
+ DevToolsAndroidBridge::RemotePages webview_pages =
+ webview->pages();
+
+ ASSERT_EQ(1U, chrome_pages.size());
+ ASSERT_EQ(1U, chrome_beta_pages.size());
+ ASSERT_EQ(2U, webview_pages.size());
+
+ scoped_refptr<content::DevToolsAgentHost> chrome_target(
+ chrome_pages[0]->CreateTarget());
+ scoped_refptr<content::DevToolsAgentHost> chrome_beta_target(
+ chrome_beta_pages[0]->CreateTarget());
+ scoped_refptr<content::DevToolsAgentHost> webview_target_0(
+ webview_pages[0]->CreateTarget());
+ scoped_refptr<content::DevToolsAgentHost> webview_target_1(
+ webview_pages[1]->CreateTarget());
+
+ // Check that we have non-empty description for webview pages.
+ ASSERT_EQ(0U, chrome_target->GetDescription().size());
+ ASSERT_EQ(0U, chrome_beta_target->GetDescription().size());
+ ASSERT_NE(0U, webview_target_0->GetDescription().size());
+ ASSERT_NE(0U, webview_target_1->GetDescription().size());
+
+ ASSERT_EQ(GURL("http://www.chromium.org/"),
+ chrome_target->GetURL());
+ ASSERT_EQ("The Chromium Projects",
+ chrome_target->GetTitle());
+ }
+
+ private:
+ DevToolsAndroidBridge* android_bridge_;
+ DevToolsAndroidBridge::RemoteDevices devices_;
+};
+
+// Flaky due to failure to bind a hardcoded port. crbug.com/566057
+IN_PROC_BROWSER_TEST_F(AdbClientSocketTest, DISABLED_TestFlushWithoutSize) {
+ StartMockAdbServer(FlushWithoutSize);
+ StartTest();
+ CheckDevices();
+ StopMockAdbServer();
+}
+
+// Flaky due to failure to bind a hardcoded port. crbug.com/566057
+IN_PROC_BROWSER_TEST_F(AdbClientSocketTest, DISABLED_TestFlushWithSize) {
+ StartMockAdbServer(FlushWithSize);
+ StartTest();
+ CheckDevices();
+ StopMockAdbServer();
+}
+
+// Flaky due to failure to bind a hardcoded port. crbug.com/566057
+IN_PROC_BROWSER_TEST_F(AdbClientSocketTest, DISABLED_TestFlushWithData) {
+ StartMockAdbServer(FlushWithData);
+ StartTest();
+ CheckDevices();
+ StopMockAdbServer();
+}
diff --git a/chromium/chrome/browser/devtools/device/adb/adb_device_provider.cc b/chromium/chrome/browser/devtools/device/adb/adb_device_provider.cc
new file mode 100644
index 00000000000..fda0496597c
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/adb/adb_device_provider.cc
@@ -0,0 +1,69 @@
+// 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/devtools/device/adb/adb_device_provider.h"
+
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/devtools/device/adb/adb_client_socket.h"
+
+namespace {
+
+const char kHostDevicesCommand[] = "host:devices";
+const char kHostTransportCommand[] = "host:transport:%s|%s";
+const char kLocalAbstractCommand[] = "localabstract:%s";
+
+const int kAdbPort = 5037;
+
+static void RunCommand(const std::string& serial,
+ const std::string& command,
+ const AdbDeviceProvider::CommandCallback& callback) {
+ std::string query = base::StringPrintf(
+ kHostTransportCommand, serial.c_str(), command.c_str());
+ AdbClientSocket::AdbQuery(kAdbPort, query, callback);
+}
+
+static void ReceivedAdbDevices(
+ const AdbDeviceProvider::SerialsCallback& callback,
+ int result_code,
+ const std::string& response) {
+ std::vector<std::string> result;
+ if (result_code < 0) {
+ callback.Run(result);
+ return;
+ }
+ for (const base::StringPiece& line :
+ base::SplitStringPiece(response, "\n", base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY)) {
+ std::vector<base::StringPiece> tokens =
+ base::SplitStringPiece(line, "\t ", base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY);
+ result.push_back(tokens[0].as_string());
+ }
+ callback.Run(result);
+}
+
+} // namespace
+
+void AdbDeviceProvider::QueryDevices(const SerialsCallback& callback) {
+ AdbClientSocket::AdbQuery(
+ kAdbPort, kHostDevicesCommand, base::Bind(&ReceivedAdbDevices, callback));
+}
+
+void AdbDeviceProvider::QueryDeviceInfo(const std::string& serial,
+ const DeviceInfoCallback& callback) {
+ AndroidDeviceManager::QueryDeviceInfo(base::Bind(&RunCommand, serial),
+ callback);
+}
+
+void AdbDeviceProvider::OpenSocket(const std::string& serial,
+ const std::string& socket_name,
+ const SocketCallback& callback) {
+ std::string request =
+ base::StringPrintf(kLocalAbstractCommand, socket_name.c_str());
+ AdbClientSocket::TransportQuery(kAdbPort, serial, request, callback);
+}
+
+AdbDeviceProvider::~AdbDeviceProvider() {
+}
diff --git a/chromium/chrome/browser/devtools/device/adb/adb_device_provider.h b/chromium/chrome/browser/devtools/device/adb/adb_device_provider.h
new file mode 100644
index 00000000000..56b7fb96b72
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/adb/adb_device_provider.h
@@ -0,0 +1,25 @@
+// 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_DEVTOOLS_DEVICE_ADB_ADB_DEVICE_PROVIDER_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVICE_ADB_ADB_DEVICE_PROVIDER_H_
+
+#include "chrome/browser/devtools/device/android_device_manager.h"
+
+class AdbDeviceProvider : public AndroidDeviceManager::DeviceProvider {
+ public:
+ void QueryDevices(const SerialsCallback& callback) override;
+
+ void QueryDeviceInfo(const std::string& serial,
+ const DeviceInfoCallback& callback) override;
+
+ void OpenSocket(const std::string& serial,
+ const std::string& socket_name,
+ const SocketCallback& callback) override;
+
+ private:
+ ~AdbDeviceProvider() override;
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVICE_ADB_ADB_DEVICE_PROVIDER_H_
diff --git a/chromium/chrome/browser/devtools/device/adb/mock_adb_server.cc b/chromium/chrome/browser/devtools/device/adb/mock_adb_server.cc
new file mode 100644
index 00000000000..88658a38902
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/adb/mock_adb_server.cc
@@ -0,0 +1,625 @@
+// 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/devtools/device/adb/mock_adb_server.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#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/threading/non_thread_safe.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/test_utils.h"
+#include "net/base/io_buffer.h"
+#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/log/net_log_source.h"
+#include "net/socket/stream_socket.h"
+#include "net/socket/tcp_server_socket.h"
+
+using content::BrowserThread;
+
+namespace {
+
+const char kHostTransportPrefix[] = "host:transport:";
+const char kLocalAbstractPrefix[] = "localabstract:";
+
+const char kShellPrefix[] = "shell:";
+const char kOpenedUnixSocketsCommand[] = "cat /proc/net/unix";
+const char kDeviceModelCommand[] = "getprop ro.product.model";
+const char kDumpsysCommand[] = "dumpsys window policy";
+const char kListProcessesCommand[] = "ps";
+const char kListUsersCommand[] = "dumpsys user";
+const char kEchoCommandPrefix[] = "echo ";
+
+const char kSerialOnline[] = "01498B321301A00A";
+const char kSerialOffline[] = "01498B2B0D01300E";
+const char kDeviceModel[] = "Nexus 6";
+
+const char kJsonVersionPath[] = "/json/version";
+const char kJsonPath[] = "/json";
+const char kJsonListPath[] = "/json/list";
+
+const char kHttpRequestTerminator[] = "\r\n\r\n";
+
+const char kHttpResponse[] =
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Length:%d\r\n"
+ "Content-Type:application/json; charset=UTF-8\r\n\r\n%s";
+
+const char kSampleOpenedUnixSockets[] =
+ "Num RefCount Protocol Flags Type St Inode Path\n"
+ "00000000: 00000004 00000000"
+ " 00000000 0002 01 3328 /dev/socket/wpa_wlan0\n"
+ "00000000: 00000002 00000000"
+ " 00010000 0001 01 5394 /dev/socket/vold\n"
+ "00000000: 00000002 00000000"
+ " 00010000 0001 01 11810 @webview_devtools_remote_2425\n"
+ "00000000: 00000002 00000000"
+ " 00010000 0001 01 20893 @chrome_devtools_remote\n"
+ "00000000: 00000002 00000000"
+ " 00010000 0001 01 20894 @chrome_devtools_remote_1002\n"
+ "00000000: 00000002 00000000"
+ " 00010000 0001 01 20895 @noprocess_devtools_remote\n";
+
+const char kSampleListProcesses[] =
+ "USER PID PPID VSIZE RSS WCHAN PC NAME\n"
+ "root 1 0 688 508 ffffffff 00000000 S /init\r\n"
+ "u0_a75 2425 123 933736 193024 ffffffff 00000000 S com.sample.feed\r\n"
+ "nfc 741 123 706448 26316 ffffffff 00000000 S com.android.nfc\r\n"
+ "u0_a76 1001 124 111111 222222 ffffffff 00000000 S com.android.chrome\r\n"
+ "u10_a77 1002 125 111111 222222 ffffffff 00000000 S com.chrome.beta\r\n"
+ "u0_a78 1003 126 111111 222222 ffffffff 00000000 S com.noprocess.app\r\n";
+
+const char kSampleDumpsys[] =
+ "WINDOW MANAGER POLICY STATE (dumpsys window policy)\r\n"
+ " mSafeMode=false mSystemReady=true mSystemBooted=true\r\n"
+ " mStable=(0,50)-(720,1184)\r\n" // Only mStable parameter is parsed
+ " mForceStatusBar=false mForceStatusBarFromKeyguard=false\r\n";
+
+const char kSampleListUsers[] =
+ "Users:\r\n"
+ " UserInfo{0:Test User:13} serialNo=0\r\n"
+ " Created: <unknown>\r\n"
+ " Last logged in: +17m18s871ms ago\r\n"
+ " UserInfo{10:Test User : 2:10} serialNo=10\r\n"
+ " Created: +3d4h35m1s139ms ago\r\n"
+ " Last logged in: +17m26s287ms ago\r\n";
+
+char kSampleChromeVersion[] = "{\n"
+ " \"Browser\": \"Chrome/32.0.1679.0\",\n"
+ " \"Protocol-Version\": \"1.0\",\n"
+ " \"User-Agent\": \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
+ "(KHTML, like Gecko) Chrome/32.0.1679.0 Safari/537.36\",\n"
+ " \"WebKit-Version\": \"537.36 (@160162)\"\n"
+ "}";
+
+char kSampleChromeBetaVersion[] = "{\n"
+ " \"Browser\": \"Chrome/31.0.1599.0\",\n"
+ " \"Protocol-Version\": \"1.0\",\n"
+ " \"User-Agent\": \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
+ "(KHTML, like Gecko) Chrome/32.0.1679.0 Safari/537.36\",\n"
+ " \"WebKit-Version\": \"537.36 (@160162)\"\n"
+ "}";
+
+char kSampleWebViewVersion[] = "{\n"
+ " \"Browser\": \"Version/4.0\",\n"
+ " \"Protocol-Version\": \"1.0\",\n"
+ " \"User-Agent\": \"Mozilla/5.0 (Linux; Android 4.3; Build/KRS74B) "
+ "AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Safari/537.36\",\n"
+ " \"WebKit-Version\": \"537.36 (@157588)\"\n"
+ "}";
+
+char kSampleChromePages[] = "[ {\n"
+ " \"description\": \"\",\n"
+ " \"devtoolsFrontendUrl\": \"/devtools/devtools.html?"
+ "ws=/devtools/page/0\",\n"
+ " \"id\": \"0\",\n"
+ " \"title\": \"The Chromium Projects\",\n"
+ " \"type\": \"page\",\n"
+ " \"url\": \"http://www.chromium.org/\",\n"
+ " \"webSocketDebuggerUrl\": \""
+ "ws:///devtools/page/0\"\n"
+ "} ]";
+
+char kSampleChromeBetaPages[] = "[ {\n"
+ " \"description\": \"\",\n"
+ " \"devtoolsFrontendUrl\": \"/devtools/devtools.html?"
+ "ws=/devtools/page/0\",\n"
+ " \"id\": \"0\",\n"
+ " \"title\": \"The Chromium Projects\",\n"
+ " \"type\": \"page\",\n"
+ " \"url\": \"http://www.chromium.org/\",\n"
+ " \"webSocketDebuggerUrl\": \""
+ "ws:///devtools/page/0\"\n"
+ "} ]";
+
+char kSampleWebViewPages[] = "[ {\n"
+ " \"description\": \"{\\\"attached\\\":false,\\\"empty\\\":false,"
+ "\\\"height\\\":1173,\\\"screenX\\\":0,\\\"screenY\\\":0,"
+ "\\\"visible\\\":true,\\\"width\\\":800}\",\n"
+ " \"devtoolsFrontendUrl\": \"http://chrome-devtools-frontend.appspot.com/"
+ "serve_rev/@157588/devtools.html?ws="
+ "/devtools/page/3E962D4D-B676-182D-3BE8-FAE7CE224DE7\",\n"
+ " \"faviconUrl\": \"http://chromium.org/favicon.ico\",\n"
+ " \"id\": \"3E962D4D-B676-182D-3BE8-FAE7CE224DE7\",\n"
+ " \"title\": \"Blink - The Chromium Projects\",\n"
+ " \"type\": \"page\",\n"
+ " \"url\": \"http://www.chromium.org/blink\",\n"
+ " \"webSocketDebuggerUrl\": \"ws:///devtools/"
+ "page/3E962D4D-B676-182D-3BE8-FAE7CE224DE7\"\n"
+ "}, {\n"
+ " \"description\": \"{\\\"attached\\\":true,\\\"empty\\\":true,"
+ "\\\"screenX\\\":0,\\\"screenY\\\":33,\\\"visible\\\":false}\",\n"
+ " \"devtoolsFrontendUrl\": \"http://chrome-devtools-frontend.appspot.com/"
+ "serve_rev/@157588/devtools.html?ws="
+ "/devtools/page/44681551-ADFD-2411-076B-3AB14C1C60E2\",\n"
+ " \"faviconUrl\": \"\",\n"
+ " \"id\": \"44681551-ADFD-2411-076B-3AB14C1C60E2\",\n"
+ " \"title\": \"More Activity\",\n"
+ " \"type\": \"page\",\n"
+ " \"url\": \"about:blank\",\n"
+ " \"webSocketDebuggerUrl\": \"ws:///devtools/page/"
+ "44681551-ADFD-2411-076B-3AB14C1C60E2\"\n"
+ "}]";
+
+static const int kBufferSize = 16*1024;
+static const uint16_t kAdbPort = 5037;
+
+static const int kAdbMessageHeaderSize = 4;
+
+class SimpleHttpServer : base::NonThreadSafe {
+ public:
+ class Parser {
+ public:
+ virtual int Consume(const char* data, int size) = 0;
+ virtual ~Parser() {}
+ };
+
+ using SendCallback = base::Callback<void(const std::string&)>;
+ using ParserFactory = base::Callback<Parser*(const SendCallback&)>;
+
+ SimpleHttpServer(const ParserFactory& factory, net::IPEndPoint endpoint);
+ virtual ~SimpleHttpServer();
+
+ private:
+ class Connection : base::NonThreadSafe {
+ public:
+ Connection(net::StreamSocket* socket, const ParserFactory& factory);
+ virtual ~Connection();
+
+ private:
+ void Send(const std::string& message);
+ void ReadData();
+ void OnDataRead(int count);
+ void WriteData();
+ void OnDataWritten(int count);
+
+ std::unique_ptr<net::StreamSocket> socket_;
+ std::unique_ptr<Parser> parser_;
+ scoped_refptr<net::GrowableIOBuffer> input_buffer_;
+ scoped_refptr<net::GrowableIOBuffer> output_buffer_;
+ int bytes_to_write_;
+ bool read_closed_;
+ base::WeakPtrFactory<Connection> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(Connection);
+ };
+
+ void OnConnect();
+ void OnAccepted(int result);
+
+ ParserFactory factory_;
+ std::unique_ptr<net::TCPServerSocket> socket_;
+ std::unique_ptr<net::StreamSocket> client_socket_;
+ base::WeakPtrFactory<SimpleHttpServer> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SimpleHttpServer);
+};
+
+SimpleHttpServer::SimpleHttpServer(const ParserFactory& factory,
+ net::IPEndPoint endpoint)
+ : factory_(factory),
+ socket_(new net::TCPServerSocket(nullptr, net::NetLogSource())),
+ weak_factory_(this) {
+ socket_->Listen(endpoint, 5);
+ OnConnect();
+}
+
+SimpleHttpServer::~SimpleHttpServer() {
+}
+
+SimpleHttpServer::Connection::Connection(net::StreamSocket* socket,
+ const ParserFactory& factory)
+ : socket_(socket),
+ parser_(factory.Run(base::Bind(&Connection::Send,
+ base::Unretained(this)))),
+ input_buffer_(new net::GrowableIOBuffer()),
+ output_buffer_(new net::GrowableIOBuffer()),
+ bytes_to_write_(0),
+ read_closed_(false),
+ weak_factory_(this) {
+ input_buffer_->SetCapacity(kBufferSize);
+ ReadData();
+}
+
+SimpleHttpServer::Connection::~Connection() {
+}
+
+void SimpleHttpServer::Connection::Send(const std::string& message) {
+ CHECK(CalledOnValidThread());
+ const char* data = message.c_str();
+ int size = message.size();
+
+ if ((output_buffer_->offset() + bytes_to_write_ + size) >
+ output_buffer_->capacity()) {
+ // If not enough space without relocation
+ if (output_buffer_->capacity() < (bytes_to_write_ + size)) {
+ // If even buffer is not enough
+ int new_size = std::max(output_buffer_->capacity() * 2, size * 2);
+ output_buffer_->SetCapacity(new_size);
+ }
+ memmove(output_buffer_->StartOfBuffer(),
+ output_buffer_->data(),
+ bytes_to_write_);
+ output_buffer_->set_offset(0);
+ }
+
+ memcpy(output_buffer_->data() + bytes_to_write_, data, size);
+ bytes_to_write_ += size;
+
+ if (bytes_to_write_ == size)
+ // If write loop wasn't yet started, then start it
+ WriteData();
+}
+
+void SimpleHttpServer::Connection::ReadData() {
+ CHECK(CalledOnValidThread());
+
+ if (input_buffer_->RemainingCapacity() == 0)
+ input_buffer_->SetCapacity(input_buffer_->capacity() * 2);
+
+ int read_result = socket_->Read(
+ input_buffer_.get(),
+ input_buffer_->RemainingCapacity(),
+ base::Bind(&Connection::OnDataRead, base::Unretained(this)));
+
+ if (read_result != net::ERR_IO_PENDING)
+ OnDataRead(read_result);
+}
+
+void SimpleHttpServer::Connection::OnDataRead(int count) {
+ CHECK(CalledOnValidThread());
+ if (count <= 0) {
+ if (bytes_to_write_ == 0)
+ delete this;
+ else
+ read_closed_ = true;
+ return;
+ }
+ input_buffer_->set_offset(input_buffer_->offset() + count);
+ int bytes_processed;
+
+ do {
+ char* data = input_buffer_->StartOfBuffer();
+ int data_size = input_buffer_->offset();
+ bytes_processed = parser_->Consume(data, data_size);
+
+ if (bytes_processed) {
+ memmove(data, data + bytes_processed, data_size - bytes_processed);
+ input_buffer_->set_offset(data_size - bytes_processed);
+ }
+ } while (bytes_processed);
+ // Posting to avoid deep recursion in case of synchronous IO
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&Connection::ReadData, weak_factory_.GetWeakPtr()));
+}
+
+void SimpleHttpServer::Connection::WriteData() {
+ CHECK(CalledOnValidThread());
+ CHECK_GE(output_buffer_->capacity(),
+ 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)));
+
+ if (write_result != net::ERR_IO_PENDING)
+ OnDataWritten(write_result);
+}
+
+void SimpleHttpServer::Connection::OnDataWritten(int count) {
+ CHECK(CalledOnValidThread());
+ if (count < 0) {
+ delete this;
+ return;
+ }
+ CHECK_GT(count, 0);
+ CHECK_GE(output_buffer_->capacity(),
+ output_buffer_->offset() + bytes_to_write_) << "Overflow";
+
+ bytes_to_write_ -= count;
+ output_buffer_->set_offset(output_buffer_->offset() + count);
+
+ if (bytes_to_write_ != 0)
+ // Posting to avoid deep recursion in case of synchronous IO
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&Connection::WriteData, weak_factory_.GetWeakPtr()));
+ else if (read_closed_)
+ delete this;
+}
+
+void SimpleHttpServer::OnConnect() {
+ CHECK(CalledOnValidThread());
+
+ int accept_result = socket_->Accept(&client_socket_,
+ base::Bind(&SimpleHttpServer::OnAccepted, base::Unretained(this)));
+
+ if (accept_result != net::ERR_IO_PENDING)
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&SimpleHttpServer::OnAccepted,
+ weak_factory_.GetWeakPtr(), accept_result));
+}
+
+void SimpleHttpServer::OnAccepted(int result) {
+ CHECK(CalledOnValidThread());
+ ASSERT_EQ(result, 0); // Fails if the socket is already in use.
+ new Connection(client_socket_.release(), factory_);
+ OnConnect();
+}
+
+class AdbParser : public SimpleHttpServer::Parser,
+ public base::NonThreadSafe,
+ public MockAndroidConnection::Delegate {
+ public:
+ static Parser* Create(FlushMode flush_mode,
+ const SimpleHttpServer::SendCallback& callback) {
+ return new AdbParser(flush_mode, callback);
+ }
+
+ ~AdbParser() override {}
+ private:
+ explicit AdbParser(FlushMode flush_mode,
+ const SimpleHttpServer::SendCallback& callback)
+ : flush_mode_(flush_mode),
+ callback_(callback) {
+ }
+
+ int Consume(const char* data, int size) override {
+ CHECK(CalledOnValidThread());
+ if (mock_connection_) {
+ mock_connection_->Receive(std::string(data, size));
+ return size;
+ }
+ if (size >= kAdbMessageHeaderSize) {
+ std::string message_header(data, kAdbMessageHeaderSize);
+ int message_size;
+
+ EXPECT_TRUE(base::HexStringToInt(message_header, &message_size));
+
+ if (size >= message_size + kAdbMessageHeaderSize) {
+ std::string message_body(data + kAdbMessageHeaderSize, message_size);
+ ProcessCommand(message_body);
+ return kAdbMessageHeaderSize + message_size;
+ }
+ }
+ return 0;
+ }
+
+ void ProcessCommand(const std::string& command) {
+ CHECK(CalledOnValidThread());
+ if (command == "host:devices") {
+ SendSuccess(base::StringPrintf("%s\tdevice\n%s\toffline",
+ kSerialOnline,
+ kSerialOffline));
+ } else if (base::StartsWith(command, kHostTransportPrefix,
+ base::CompareCase::SENSITIVE)) {
+ serial_ = command.substr(sizeof(kHostTransportPrefix) - 1);
+ SendSuccess(std::string());
+ } else if (serial_ != kSerialOnline) {
+ Send("FAIL", "device offline (x)");
+ } else {
+ mock_connection_ =
+ base::MakeUnique<MockAndroidConnection>(this, serial_, command);
+ }
+ }
+
+ void SendSuccess(const std::string& response) override {
+ Send("OKAY", response);
+ }
+
+ void SendRaw(const std::string& data) override {
+ callback_.Run(data);
+ }
+
+ void Send(const std::string& status, const std::string& response) {
+ CHECK(CalledOnValidThread());
+ CHECK_EQ(4U, status.size());
+ std::string buffer = status;
+ if (flush_mode_ == FlushWithoutSize) {
+ callback_.Run(buffer);
+ buffer = std::string();
+ }
+
+ int size = response.size();
+ if (size > 0) {
+ static const char kHexChars[] = "0123456789ABCDEF";
+ for (int i = 3; i >= 0; i--)
+ buffer += kHexChars[ (size >> 4*i) & 0x0f ];
+ if (flush_mode_ == FlushWithSize) {
+ callback_.Run(buffer);
+ buffer = std::string();
+ }
+ buffer += response;
+ callback_.Run(buffer);
+ } else if (flush_mode_ != FlushWithoutSize) {
+ callback_.Run(buffer);
+ }
+ }
+
+ FlushMode flush_mode_;
+ SimpleHttpServer::SendCallback callback_;
+ std::string serial_;
+ std::unique_ptr<MockAndroidConnection> mock_connection_;
+};
+
+static SimpleHttpServer* mock_adb_server_ = NULL;
+
+void StartMockAdbServerOnIOThread(FlushMode flush_mode) {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ CHECK(mock_adb_server_ == NULL);
+ net::IPEndPoint endpoint(net::IPAddress(127, 0, 0, 1), kAdbPort);
+ mock_adb_server_ = new SimpleHttpServer(
+ base::Bind(&AdbParser::Create, flush_mode), endpoint);
+}
+
+void StopMockAdbServerOnIOThread() {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ CHECK(mock_adb_server_ != NULL);
+ delete mock_adb_server_;
+ mock_adb_server_ = NULL;
+}
+
+} // namespace
+
+MockAndroidConnection::MockAndroidConnection(
+ Delegate* delegate,
+ const std::string& serial,
+ const std::string& command)
+ : delegate_(delegate),
+ serial_(serial) {
+ ProcessCommand(command);
+}
+
+MockAndroidConnection::~MockAndroidConnection() {
+}
+
+void MockAndroidConnection::Receive(const std::string& data) {
+ request_ += data;
+ size_t request_end_pos = data.find(kHttpRequestTerminator);
+ if (request_end_pos == std::string::npos)
+ return;
+
+ std::string request(request_.substr(0, request_end_pos));
+ std::vector<std::string> tokens =
+ base::SplitString(request, " ", base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY);
+ CHECK_EQ(3U, tokens.size());
+ CHECK_EQ("GET", tokens[0]);
+ CHECK_EQ("HTTP/1.1", tokens[2]);
+
+ std::string path(tokens[1]);
+ if (path == kJsonPath)
+ path = kJsonListPath;
+
+ if (socket_name_ == "chrome_devtools_remote") {
+ if (path == kJsonVersionPath)
+ SendHTTPResponse(kSampleChromeVersion);
+ else if (path == kJsonListPath)
+ SendHTTPResponse(kSampleChromePages);
+ else
+ NOTREACHED() << "Unknown command " << request;
+ } else if (socket_name_ == "chrome_devtools_remote_1002") {
+ if (path == kJsonVersionPath)
+ SendHTTPResponse(kSampleChromeBetaVersion);
+ else if (path == kJsonListPath)
+ SendHTTPResponse(kSampleChromeBetaPages);
+ else
+ NOTREACHED() << "Unknown command " << request;
+ } else if (base::StartsWith(socket_name_, "noprocess_devtools_remote",
+ base::CompareCase::SENSITIVE)) {
+ if (path == kJsonVersionPath)
+ SendHTTPResponse("{}");
+ else if (path == kJsonListPath)
+ SendHTTPResponse("[]");
+ else
+ NOTREACHED() << "Unknown command " << request;
+ } else if (socket_name_ == "webview_devtools_remote_2425") {
+ if (path == kJsonVersionPath)
+ SendHTTPResponse(kSampleWebViewVersion);
+ else if (path == kJsonListPath)
+ SendHTTPResponse(kSampleWebViewPages);
+ else
+ NOTREACHED() << "Unknown command " << request;
+ } else {
+ NOTREACHED() << "Unknown socket " << socket_name_;
+ }
+}
+
+void MockAndroidConnection::ProcessCommand(const std::string& command) {
+ if (base::StartsWith(command, kLocalAbstractPrefix,
+ base::CompareCase::SENSITIVE)) {
+ socket_name_ = command.substr(sizeof(kLocalAbstractPrefix) - 1);
+ delegate_->SendSuccess(std::string());
+ return;
+ }
+
+ if (base::StartsWith(command, kShellPrefix, base::CompareCase::SENSITIVE)) {
+ std::string result;
+ for (const auto& line :
+ base::SplitString(command.substr(sizeof(kShellPrefix) - 1), "\n",
+ base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
+ if (line == kDeviceModelCommand) {
+ result += kDeviceModel;
+ result += "\r\n";
+ } else if (line == kOpenedUnixSocketsCommand) {
+ result += kSampleOpenedUnixSockets;
+ } else if (line == kDumpsysCommand) {
+ result += kSampleDumpsys;
+ } else if (line == kListProcessesCommand) {
+ result += kSampleListProcesses;
+ } else if (line == kListUsersCommand) {
+ result += kSampleListUsers;
+ } else if (base::StartsWith(line, kEchoCommandPrefix,
+ base::CompareCase::SENSITIVE)) {
+ result += line.substr(sizeof(kEchoCommandPrefix) - 1);
+ result += "\r\n";
+ } else {
+ NOTREACHED() << "Unknown shell command - " << command;
+ }
+ }
+ delegate_->SendSuccess(result);
+ } else {
+ NOTREACHED() << "Unknown command - " << command;
+ }
+ delegate_->Close();
+}
+
+void MockAndroidConnection::SendHTTPResponse(const std::string& body) {
+ std::string response_data(base::StringPrintf(kHttpResponse,
+ static_cast<int>(body.size()),
+ body.c_str()));
+ delegate_->SendRaw(response_data);
+}
+
+void StartMockAdbServer(FlushMode flush_mode) {
+ BrowserThread::PostTaskAndReply(
+ BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&StartMockAdbServerOnIOThread, flush_mode),
+ base::MessageLoop::QuitWhenIdleClosure());
+ content::RunMessageLoop();
+}
+
+void StopMockAdbServer() {
+ BrowserThread::PostTaskAndReply(BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&StopMockAdbServerOnIOThread),
+ base::MessageLoop::QuitWhenIdleClosure());
+ content::RunMessageLoop();
+}
+
diff --git a/chromium/chrome/browser/devtools/device/adb/mock_adb_server.h b/chromium/chrome/browser/devtools/device/adb/mock_adb_server.h
new file mode 100644
index 00000000000..d18c5a27b26
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/adb/mock_adb_server.h
@@ -0,0 +1,52 @@
+// 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_DEVTOOLS_DEVICE_ADB_MOCK_ADB_SERVER_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVICE_ADB_MOCK_ADB_SERVER_H_
+
+#include <string>
+
+#include "base/callback.h"
+
+// Single instance mock ADB server for use in browser tests. Runs on IO thread.
+
+// These methods can be called from any thread.
+enum FlushMode {
+ FlushWithoutSize,
+ FlushWithSize,
+ FlushWithData
+};
+
+void StartMockAdbServer(FlushMode flush_mode);
+void StopMockAdbServer();
+
+// Part of mock server independent of transport.
+class MockAndroidConnection {
+ public:
+ class Delegate {
+ public:
+ virtual void SendSuccess(const std::string& message) {}
+ virtual void SendRaw(const std::string& data) {}
+ virtual void Close() {}
+ virtual ~Delegate() {}
+ };
+ using Callback = base::Callback<void(const std::string&)>;
+ MockAndroidConnection(Delegate* delegate,
+ const std::string& serial,
+ const std::string& command);
+ virtual ~MockAndroidConnection();
+
+ void Receive(const std::string& data);
+
+ private:
+ void ProcessCommand(const std::string& command);
+ void SendHTTPResponse(const std::string& body);
+
+ Delegate* delegate_;
+ std::string serial_;
+ std::string socket_name_;
+ std::string request_;
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVICE_ADB_MOCK_ADB_SERVER_H_
diff --git a/chromium/chrome/browser/devtools/device/android_device_info_query.cc b/chromium/chrome/browser/devtools/device/android_device_info_query.cc
new file mode 100644
index 00000000000..e16000b47e9
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/android_device_info_query.cc
@@ -0,0 +1,370 @@
+// 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 <stddef.h>
+
+#include "base/macros.h"
+#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 "chrome/browser/devtools/device/android_device_manager.h"
+
+namespace {
+
+#define SEPARATOR "======== output separator ========"
+
+const char kAllCommands[] = "shell:"
+ "getprop ro.product.model\n"
+ "echo " SEPARATOR "\n"
+ "dumpsys window policy\n"
+ "echo " SEPARATOR "\n"
+ "ps\n"
+ "echo " SEPARATOR "\n"
+ "cat /proc/net/unix\n"
+ "echo " SEPARATOR "\n"
+ "dumpsys user\n";
+
+const char kSeparator[] = SEPARATOR;
+
+#undef SEPARATOR
+
+const char kScreenSizePrefix[] = "mStable=";
+const char kUserInfoPrefix[] = "UserInfo{";
+
+const char kDevToolsSocketSuffix[] = "_devtools_remote";
+
+const char kChromeDefaultName[] = "Chrome";
+const char kChromeDefaultSocket[] = "chrome_devtools_remote";
+
+const char kWebViewSocketPrefix[] = "webview_devtools_remote";
+const char kWebViewNameTemplate[] = "WebView in %s";
+
+struct BrowserDescriptor {
+ const char* package;
+ const char* socket;
+ const char* display_name;
+};
+
+const BrowserDescriptor kBrowserDescriptors[] = {
+ {
+ "com.google.android.apps.chrome",
+ kChromeDefaultSocket,
+ "Chromium"
+ },
+ {
+ "com.chrome.canary",
+ kChromeDefaultSocket,
+ "Chrome Canary"
+ },
+ {
+ "com.chrome.dev",
+ kChromeDefaultSocket,
+ "Chrome Dev"
+ },
+ {
+ "com.chrome.beta",
+ kChromeDefaultSocket,
+ "Chrome Beta"
+ },
+ {
+ "com.android.chrome",
+ kChromeDefaultSocket,
+ kChromeDefaultName
+ },
+ {
+ "com.chrome.work",
+ kChromeDefaultSocket,
+ "Work Chrome"
+ },
+ {
+ "org.chromium.android_webview.shell",
+ "webview_devtools_remote",
+ "WebView Test Shell"
+ },
+ {
+ "org.chromium.content_shell_apk",
+ "content_shell_devtools_remote",
+ "Content Shell"
+ },
+ {
+ "org.chromium.chrome",
+ kChromeDefaultSocket,
+ "Chromium"
+ },
+};
+
+const BrowserDescriptor* FindBrowserDescriptor(const std::string& package) {
+ size_t count = arraysize(kBrowserDescriptors);
+ for (size_t i = 0; i < count; i++) {
+ if (kBrowserDescriptors[i].package == package)
+ return &kBrowserDescriptors[i];
+ }
+ return nullptr;
+}
+
+bool BrowserCompare(const AndroidDeviceManager::BrowserInfo& a,
+ const AndroidDeviceManager::BrowserInfo& b) {
+ size_t count = arraysize(kBrowserDescriptors);
+ for (size_t i = 0; i < count; i++) {
+ bool isA = kBrowserDescriptors[i].display_name == a.display_name;
+ bool isB = kBrowserDescriptors[i].display_name == b.display_name;
+ if (isA != isB)
+ return isA;
+ if (isA && isB)
+ break;
+ }
+ return a.socket_name < b.socket_name;
+}
+
+using StringMap = std::map<std::string, std::string>;
+
+void MapProcessesToPackages(const std::string& response,
+ StringMap* pid_to_package,
+ StringMap* pid_to_user) {
+ // Parse 'ps' output which on Android looks like this:
+ //
+ // USER PID PPID VSIZE RSS WCHAN PC ? NAME
+ //
+ for (const base::StringPiece& line :
+ base::SplitStringPiece(response, "\n", base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY)) {
+ std::vector<std::string> fields =
+ base::SplitString(line, " \r", base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY);
+ if (fields.size() < 9)
+ continue;
+ std::string pid = fields[1];
+ (*pid_to_package)[pid] = fields[8];
+ (*pid_to_user)[pid] = fields[0];
+ }
+}
+
+StringMap MapSocketsToProcesses(const std::string& response) {
+ // Parse 'cat /proc/net/unix' output which on Android looks like this:
+ //
+ // Num RefCount Protocol Flags Type St Inode Path
+ // 00000000: 00000002 00000000 00010000 0001 01 331813 /dev/socket/zygote
+ // 00000000: 00000002 00000000 00010000 0001 01 358606 @xxx_devtools_remote
+ // 00000000: 00000002 00000000 00010000 0001 01 347300 @yyy_devtools_remote
+ //
+ // We need to find records with paths starting from '@' (abstract socket)
+ // and containing the channel pattern ("_devtools_remote").
+ StringMap socket_to_pid;
+ for (const base::StringPiece& line :
+ base::SplitStringPiece(response, "\n", base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY)) {
+ std::vector<std::string> fields =
+ base::SplitString(line, " \r", base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY);
+ if (fields.size() < 8)
+ continue;
+ if (fields[3] != "00010000" || fields[5] != "01")
+ continue;
+ std::string path_field = fields[7];
+ if (path_field.size() < 1 || path_field[0] != '@')
+ continue;
+ size_t socket_name_pos = path_field.find(kDevToolsSocketSuffix);
+ if (socket_name_pos == std::string::npos)
+ continue;
+
+ std::string socket = path_field.substr(1);
+
+ std::string pid;
+ size_t socket_name_end = socket_name_pos + strlen(kDevToolsSocketSuffix);
+ if (socket_name_end < path_field.size() &&
+ path_field[socket_name_end] == '_') {
+ pid = path_field.substr(socket_name_end + 1);
+ }
+ socket_to_pid[socket] = pid;
+ }
+ return socket_to_pid;
+}
+
+gfx::Size ParseScreenSize(base::StringPiece str) {
+ std::vector<base::StringPiece> pairs =
+ base::SplitStringPiece(str, "-", base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY);
+ if (pairs.size() != 2)
+ return gfx::Size();
+
+ int width;
+ int height;
+ std::vector<base::StringPiece> numbers =
+ base::SplitStringPiece(pairs[1].substr(1, pairs[1].size() - 2), ",",
+ base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ if (numbers.size() != 2 ||
+ !base::StringToInt(numbers[0], &width) ||
+ !base::StringToInt(numbers[1], &height))
+ return gfx::Size();
+
+ return gfx::Size(width, height);
+}
+
+gfx::Size ParseWindowPolicyResponse(const std::string& response) {
+ for (const base::StringPiece& line :
+ base::SplitStringPiece(response, "\r", base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY)) {
+ size_t pos = line.find(kScreenSizePrefix);
+ if (pos != base::StringPiece::npos) {
+ return ParseScreenSize(
+ line.substr(pos + strlen(kScreenSizePrefix)));
+ }
+ }
+ return gfx::Size();
+}
+
+StringMap MapIdsToUsers(const std::string& response) {
+ // Parse 'dumpsys user' output which looks like this:
+ // Users:
+ // UserInfo{0:Test User:13} serialNo=0
+ // Created: <unknown>
+ // Last logged in: +17m18s871ms ago
+ // UserInfo{10:User with : (colon):10} serialNo=10
+ // Created: +3d4h35m1s139ms ago
+ // Last logged in: +17m26s287ms ago
+ StringMap id_to_username;
+ for (const base::StringPiece& line :
+ base::SplitStringPiece(response, "\r", base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY)) {
+ size_t pos = line.find(kUserInfoPrefix);
+ if (pos != std::string::npos) {
+ base::StringPiece fields = line.substr(pos + strlen(kUserInfoPrefix));
+ size_t first_pos = fields.find_first_of(":");
+ size_t last_pos = fields.find_last_of(":");
+ if (first_pos != std::string::npos && last_pos != std::string::npos) {
+ std::string id = fields.substr(0, first_pos).as_string();
+ std::string name = fields.substr(first_pos + 1,
+ last_pos - first_pos - 1).as_string();
+ id_to_username[id] = name;
+ }
+ }
+ }
+ return id_to_username;
+}
+
+std::string GetUserName(const std::string& unix_user,
+ const StringMap id_to_username) {
+ // Parse username as returned by ps which looks like 'u0_a31'
+ // where '0' is user id and '31' is app id.
+ if (!unix_user.empty() && unix_user[0] == 'u') {
+ size_t pos = unix_user.find('_');
+ if (pos != std::string::npos) {
+ StringMap::const_iterator it =
+ id_to_username.find(unix_user.substr(1, pos - 1));
+ if (it != id_to_username.end())
+ return it->second;
+ }
+ }
+ return std::string();
+}
+
+AndroidDeviceManager::BrowserInfo::Type
+GetBrowserType(const std::string& socket) {
+ if (base::StartsWith(socket, kChromeDefaultSocket,
+ base::CompareCase::SENSITIVE)) {
+ return AndroidDeviceManager::BrowserInfo::kTypeChrome;
+ }
+
+ if (base::StartsWith(socket, kWebViewSocketPrefix,
+ base::CompareCase::SENSITIVE)) {
+ return AndroidDeviceManager::BrowserInfo::kTypeWebView;
+ }
+
+ return AndroidDeviceManager::BrowserInfo::kTypeOther;
+}
+
+void ReceivedResponse(const AndroidDeviceManager::DeviceInfoCallback& callback,
+ int result,
+ const std::string& response) {
+ AndroidDeviceManager::DeviceInfo device_info;
+ if (result < 0) {
+ callback.Run(device_info);
+ return;
+ }
+ std::vector<std::string> outputs = base::SplitStringUsingSubstr(
+ response, kSeparator, base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+ if (outputs.size() != 5) {
+ callback.Run(device_info);
+ return;
+ }
+ device_info.connected = true;
+ device_info.model = outputs[0];
+ device_info.screen_size = ParseWindowPolicyResponse(outputs[1]);
+ StringMap pid_to_package;
+ StringMap pid_to_user;
+ MapProcessesToPackages(outputs[2], &pid_to_package, &pid_to_user);
+ StringMap socket_to_pid = MapSocketsToProcesses(outputs[3]);
+ StringMap id_to_username = MapIdsToUsers(outputs[4]);
+ std::set<std::string> used_pids;
+ for (const auto& pair : socket_to_pid)
+ used_pids.insert(pair.second);
+
+ for (const auto& pair : pid_to_package) {
+ std::string pid = pair.first;
+ std::string package = pair.second;
+ if (used_pids.find(pid) == used_pids.end()) {
+ const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
+ if (descriptor)
+ socket_to_pid[descriptor->socket] = pid;
+ }
+ }
+
+ for (const auto& pair : socket_to_pid) {
+ std::string socket = pair.first;
+ std::string pid = pair.second;
+ std::string package;
+ StringMap::iterator pit = pid_to_package.find(pid);
+ if (pit != pid_to_package.end())
+ package = pit->second;
+
+ AndroidDeviceManager::BrowserInfo browser_info;
+ browser_info.socket_name = socket;
+ browser_info.type = GetBrowserType(socket);
+ browser_info.display_name =
+ AndroidDeviceManager::GetBrowserName(socket, package);
+
+ StringMap::iterator uit = pid_to_user.find(pid);
+ if (uit != pid_to_user.end())
+ browser_info.user = GetUserName(uit->second, id_to_username);
+
+ device_info.browser_info.push_back(browser_info);
+ }
+ std::sort(device_info.browser_info.begin(),
+ device_info.browser_info.end(),
+ &BrowserCompare);
+ callback.Run(device_info);
+}
+
+} // namespace
+
+// static
+std::string AndroidDeviceManager::GetBrowserName(const std::string& socket,
+ const std::string& package) {
+ if (package.empty()) {
+ // Derive a fallback display name from the socket name.
+ std::string name = socket.substr(0, socket.find(kDevToolsSocketSuffix));
+ name[0] = base::ToUpperASCII(name[0]);
+ return name;
+ }
+
+ const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
+ if (descriptor)
+ return descriptor->display_name;
+
+ if (GetBrowserType(socket) ==
+ AndroidDeviceManager::BrowserInfo::kTypeWebView)
+ return base::StringPrintf(kWebViewNameTemplate, package.c_str());
+
+ return package;
+}
+
+// static
+void AndroidDeviceManager::QueryDeviceInfo(
+ const RunCommandCallback& command_callback,
+ const DeviceInfoCallback& callback) {
+ command_callback.Run(
+ kAllCommands,
+ base::Bind(&ReceivedResponse, callback));
+}
diff --git a/chromium/chrome/browser/devtools/device/android_device_manager.cc b/chromium/chrome/browser/devtools/device/android_device_manager.cc
new file mode 100644
index 00000000000..86901ff0007
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/android_device_manager.cc
@@ -0,0 +1,569 @@
+// 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/devtools/device/android_device_manager.h"
+
+#include <stddef.h>
+#include <string.h>
+
+#include <utility>
+
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/socket/stream_socket.h"
+
+using content::BrowserThread;
+
+namespace {
+
+const char kDevToolsAdbBridgeThreadName[] = "Chrome_DevToolsADBThread";
+
+const int kBufferSize = 16 * 1024;
+
+static const char kModelOffline[] = "Offline";
+
+static const char kHttpGetRequest[] = "GET %s HTTP/1.1\r\n\r\n";
+
+static const char kWebSocketUpgradeRequest[] = "GET %s HTTP/1.1\r\n"
+ "Upgrade: WebSocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
+ "Sec-WebSocket-Version: 13\r\n"
+ "%s"
+ "\r\n";
+
+static void PostDeviceInfoCallback(
+ scoped_refptr<base::SingleThreadTaskRunner> response_task_runner,
+ const AndroidDeviceManager::DeviceInfoCallback& callback,
+ const AndroidDeviceManager::DeviceInfo& device_info) {
+ response_task_runner->PostTask(FROM_HERE,
+ base::BindOnce(callback, device_info));
+}
+
+static void PostCommandCallback(
+ scoped_refptr<base::SingleThreadTaskRunner> response_task_runner,
+ const AndroidDeviceManager::CommandCallback& callback,
+ int result,
+ const std::string& response) {
+ response_task_runner->PostTask(FROM_HERE,
+ base::BindOnce(callback, result, response));
+}
+
+static void PostHttpUpgradeCallback(
+ scoped_refptr<base::SingleThreadTaskRunner> response_task_runner,
+ const AndroidDeviceManager::HttpUpgradeCallback& callback,
+ int result,
+ const std::string& extensions,
+ const std::string& body_head,
+ std::unique_ptr<net::StreamSocket> socket) {
+ response_task_runner->PostTask(
+ FROM_HERE, base::BindOnce(callback, result, extensions, body_head,
+ base::Passed(&socket)));
+}
+
+class HttpRequest {
+ public:
+ typedef AndroidDeviceManager::CommandCallback CommandCallback;
+ typedef AndroidDeviceManager::HttpUpgradeCallback HttpUpgradeCallback;
+
+ static void CommandRequest(const std::string& request,
+ const CommandCallback& callback,
+ int result,
+ std::unique_ptr<net::StreamSocket> socket) {
+ if (result != net::OK) {
+ callback.Run(result, std::string());
+ return;
+ }
+ new HttpRequest(std::move(socket), request, callback);
+ }
+
+ static void HttpUpgradeRequest(const std::string& request,
+ const HttpUpgradeCallback& callback,
+ int result,
+ std::unique_ptr<net::StreamSocket> socket) {
+ if (result != net::OK) {
+ callback.Run(result, std::string(), std::string(),
+ base::WrapUnique<net::StreamSocket>(nullptr));
+ return;
+ }
+ new HttpRequest(std::move(socket), request, callback);
+ }
+
+ private:
+ HttpRequest(std::unique_ptr<net::StreamSocket> socket,
+ const std::string& request,
+ const CommandCallback& callback)
+ : socket_(std::move(socket)),
+ command_callback_(callback),
+ expected_total_size_(0),
+ header_size_(std::string::npos) {
+ SendRequest(request);
+ }
+
+ HttpRequest(std::unique_ptr<net::StreamSocket> socket,
+ const std::string& request,
+ const HttpUpgradeCallback& callback)
+ : socket_(std::move(socket)),
+ http_upgrade_callback_(callback),
+ expected_total_size_(0),
+ header_size_(std::string::npos) {
+ SendRequest(request);
+ }
+
+ ~HttpRequest() {
+ }
+
+ void DoSendRequest(int result) {
+ while (result != net::ERR_IO_PENDING) {
+ if (!CheckNetResultOrDie(result))
+ return;
+
+ if (result > 0)
+ request_->DidConsume(result);
+
+ if (request_->BytesRemaining() == 0) {
+ request_ = nullptr;
+ ReadResponse(net::OK);
+ return;
+ }
+
+ result = socket_->Write(
+ request_.get(),
+ request_->BytesRemaining(),
+ base::Bind(&HttpRequest::DoSendRequest, base::Unretained(this)));
+ }
+ }
+
+ void SendRequest(const std::string& request) {
+ scoped_refptr<net::IOBuffer> base_buffer =
+ new net::IOBuffer(request.size());
+ memcpy(base_buffer->data(), request.data(), request.size());
+ request_ = new net::DrainableIOBuffer(base_buffer.get(), request.size());
+
+ DoSendRequest(net::OK);
+ }
+
+ void ReadResponse(int result) {
+ if (!CheckNetResultOrDie(result))
+ return;
+
+ response_buffer_ = new net::IOBuffer(kBufferSize);
+
+ result = socket_->Read(
+ response_buffer_.get(),
+ kBufferSize,
+ base::Bind(&HttpRequest::OnResponseData, base::Unretained(this)));
+ if (result != net::ERR_IO_PENDING)
+ OnResponseData(result);
+ }
+
+ void OnResponseData(int result) {
+ do {
+ if (!CheckNetResultOrDie(result))
+ return;
+ if (result == 0) {
+ CheckNetResultOrDie(net::ERR_CONNECTION_CLOSED);
+ return;
+ }
+
+ response_.append(response_buffer_->data(), result);
+
+ if (header_size_ == std::string::npos) {
+ header_size_ = response_.find("\r\n\r\n");
+
+ if (header_size_ != std::string::npos) {
+ header_size_ += 4;
+
+ int expected_body_size = 0;
+
+ // TODO(kaznacheev): Use net::HttpResponseHeader to parse the header.
+ std::string content_length = ExtractHeader("Content-Length:");
+ if (!content_length.empty()) {
+ if (!base::StringToInt(content_length, &expected_body_size)) {
+ CheckNetResultOrDie(net::ERR_FAILED);
+ return;
+ }
+ }
+
+ expected_total_size_ = header_size_ + expected_body_size;
+ }
+ }
+
+ // WebSocket handshake doesn't contain the Content-Length header. For this
+ // case, |expected_total_size_| is set to the size of the header (opening
+ // handshake).
+ //
+ // Some (part of) WebSocket frames can be already received into
+ // |response_|.
+ if (header_size_ != std::string::npos &&
+ response_.length() >= expected_total_size_) {
+ const std::string& body = response_.substr(header_size_);
+
+ if (!command_callback_.is_null()) {
+ command_callback_.Run(net::OK, body);
+ } else {
+ http_upgrade_callback_.Run(net::OK,
+ ExtractHeader("Sec-WebSocket-Extensions:"),
+ body, std::move(socket_));
+ }
+
+ delete this;
+ return;
+ }
+
+ result = socket_->Read(
+ response_buffer_.get(), kBufferSize,
+ base::Bind(&HttpRequest::OnResponseData, base::Unretained(this)));
+ } while (result != net::ERR_IO_PENDING);
+ }
+
+ std::string ExtractHeader(const std::string& header) {
+ size_t start_pos = response_.find(header);
+ if (start_pos == std::string::npos)
+ return std::string();
+
+ size_t endline_pos = response_.find("\n", start_pos);
+ if (endline_pos == std::string::npos)
+ return std::string();
+
+ std::string value = response_.substr(
+ start_pos + header.length(), endline_pos - start_pos - header.length());
+ base::TrimWhitespaceASCII(value, base::TRIM_ALL, &value);
+ return value;
+ }
+
+ bool CheckNetResultOrDie(int result) {
+ if (result >= 0)
+ return true;
+ if (!command_callback_.is_null()) {
+ command_callback_.Run(result, std::string());
+ } else {
+ http_upgrade_callback_.Run(result, std::string(), std::string(),
+ base::WrapUnique<net::StreamSocket>(nullptr));
+ }
+ delete this;
+ return false;
+ }
+
+ std::unique_ptr<net::StreamSocket> socket_;
+ scoped_refptr<net::DrainableIOBuffer> request_;
+ std::string response_;
+ CommandCallback command_callback_;
+ HttpUpgradeCallback http_upgrade_callback_;
+
+ scoped_refptr<net::IOBuffer> response_buffer_;
+
+ // Initially set to 0. Once the end of the header is seen:
+ // - If the Content-Length header is included, set to the sum of the header
+ // size (including the last two CRLFs) and the value of
+ // the header.
+ // - Otherwise, this variable is set to the size of the header (including the
+ // last two CRLFs).
+ size_t expected_total_size_;
+ // Initially set to std::string::npos. Once the end of the header is seen,
+ // set to the size of the header part in |response_| including the two CRLFs
+ // at the end.
+ size_t header_size_;
+};
+
+class DevicesRequest : public base::RefCountedThreadSafe<DevicesRequest> {
+ public:
+ typedef AndroidDeviceManager::DeviceInfo DeviceInfo;
+ typedef AndroidDeviceManager::DeviceProvider DeviceProvider;
+ typedef AndroidDeviceManager::DeviceProviders DeviceProviders;
+ typedef AndroidDeviceManager::DeviceDescriptors DeviceDescriptors;
+ typedef base::Callback<void(std::unique_ptr<DeviceDescriptors>)>
+ DescriptorsCallback;
+
+ static void Start(
+ scoped_refptr<base::SingleThreadTaskRunner> device_task_runner,
+ const DeviceProviders& providers,
+ const DescriptorsCallback& callback) {
+ // Don't keep counted reference on calling thread;
+ DevicesRequest* request = new DevicesRequest(callback);
+ // Avoid destruction while sending requests
+ request->AddRef();
+ for (DeviceProviders::const_iterator it = providers.begin();
+ it != providers.end(); ++it) {
+ device_task_runner->PostTask(
+ FROM_HERE, base::BindOnce(&DeviceProvider::QueryDevices, *it,
+ base::Bind(&DevicesRequest::ProcessSerials,
+ request, *it)));
+ }
+ device_task_runner->ReleaseSoon(FROM_HERE, request);
+ }
+
+ private:
+ explicit DevicesRequest(const DescriptorsCallback& callback)
+ : response_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ callback_(callback),
+ descriptors_(new DeviceDescriptors()) {}
+
+ friend class base::RefCountedThreadSafe<DevicesRequest>;
+ ~DevicesRequest() {
+ response_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(callback_, base::Passed(&descriptors_)));
+ }
+
+ typedef std::vector<std::string> Serials;
+
+ void ProcessSerials(scoped_refptr<DeviceProvider> provider,
+ const Serials& serials) {
+ for (Serials::const_iterator it = serials.begin(); it != serials.end();
+ ++it) {
+ descriptors_->resize(descriptors_->size() + 1);
+ descriptors_->back().provider = provider;
+ descriptors_->back().serial = *it;
+ }
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> response_task_runner_;
+ DescriptorsCallback callback_;
+ std::unique_ptr<DeviceDescriptors> descriptors_;
+};
+
+void ReleaseDeviceAndProvider(
+ AndroidDeviceManager::DeviceProvider* provider,
+ const std::string& serial) {
+ provider->ReleaseDevice(serial);
+ provider->Release();
+}
+
+} // namespace
+
+AndroidDeviceManager::BrowserInfo::BrowserInfo()
+ : type(kTypeOther) {
+}
+
+AndroidDeviceManager::BrowserInfo::BrowserInfo(const BrowserInfo& other) =
+ default;
+
+AndroidDeviceManager::DeviceInfo::DeviceInfo()
+ : model(kModelOffline), connected(false) {
+}
+
+AndroidDeviceManager::DeviceInfo::DeviceInfo(const DeviceInfo& other) = default;
+
+AndroidDeviceManager::DeviceInfo::~DeviceInfo() {
+}
+
+AndroidDeviceManager::DeviceDescriptor::DeviceDescriptor() {
+}
+
+AndroidDeviceManager::DeviceDescriptor::DeviceDescriptor(
+ const DeviceDescriptor& other) = default;
+
+AndroidDeviceManager::DeviceDescriptor::~DeviceDescriptor() {
+}
+
+void AndroidDeviceManager::DeviceProvider::SendJsonRequest(
+ const std::string& serial,
+ const std::string& socket_name,
+ const std::string& request,
+ const CommandCallback& callback) {
+ OpenSocket(serial,
+ socket_name,
+ base::Bind(&HttpRequest::CommandRequest,
+ base::StringPrintf(kHttpGetRequest, request.c_str()),
+ callback));
+}
+
+void AndroidDeviceManager::DeviceProvider::HttpUpgrade(
+ const std::string& serial,
+ const std::string& socket_name,
+ const std::string& path,
+ const std::string& extensions,
+ const HttpUpgradeCallback& callback) {
+ std::string extensions_with_new_line =
+ extensions.empty() ? std::string() : extensions + "\r\n";
+ OpenSocket(
+ serial,
+ socket_name,
+ base::Bind(&HttpRequest::HttpUpgradeRequest,
+ base::StringPrintf(kWebSocketUpgradeRequest,
+ path.c_str(),
+ extensions_with_new_line.c_str()),
+ callback));
+}
+
+void AndroidDeviceManager::DeviceProvider::ReleaseDevice(
+ const std::string& serial) {
+}
+
+AndroidDeviceManager::DeviceProvider::DeviceProvider() {
+}
+
+AndroidDeviceManager::DeviceProvider::~DeviceProvider() {
+}
+
+void AndroidDeviceManager::Device::QueryDeviceInfo(
+ const DeviceInfoCallback& callback) {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &DeviceProvider::QueryDeviceInfo, provider_, serial_,
+ base::Bind(&PostDeviceInfoCallback,
+ base::ThreadTaskRunnerHandle::Get(), callback)));
+}
+
+void AndroidDeviceManager::Device::OpenSocket(const std::string& socket_name,
+ const SocketCallback& callback) {
+ task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&DeviceProvider::OpenSocket, provider_, serial_,
+ socket_name, callback));
+}
+
+void AndroidDeviceManager::Device::SendJsonRequest(
+ const std::string& socket_name,
+ const std::string& request,
+ const CommandCallback& callback) {
+ task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&DeviceProvider::SendJsonRequest, provider_,
+ serial_, socket_name, request,
+ base::Bind(&PostCommandCallback,
+ base::ThreadTaskRunnerHandle::Get(),
+ callback)));
+}
+
+void AndroidDeviceManager::Device::HttpUpgrade(
+ const std::string& socket_name,
+ const std::string& path,
+ const std::string& extensions,
+ const HttpUpgradeCallback& callback) {
+ task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&DeviceProvider::HttpUpgrade, provider_,
+ serial_, socket_name, path, extensions,
+ base::Bind(&PostHttpUpgradeCallback,
+ base::ThreadTaskRunnerHandle::Get(),
+ callback)));
+}
+
+AndroidDeviceManager::Device::Device(
+ scoped_refptr<base::SingleThreadTaskRunner> device_task_runner,
+ scoped_refptr<DeviceProvider> provider,
+ const std::string& serial)
+ : task_runner_(device_task_runner),
+ provider_(provider),
+ serial_(serial),
+ weak_factory_(this) {
+}
+
+AndroidDeviceManager::Device::~Device() {
+ provider_->AddRef();
+ DeviceProvider* raw_ptr = provider_.get();
+ provider_ = nullptr;
+ task_runner_->PostTask(FROM_HERE,
+ base::BindOnce(&ReleaseDeviceAndProvider,
+ base::Unretained(raw_ptr), serial_));
+}
+
+AndroidDeviceManager::HandlerThread*
+AndroidDeviceManager::HandlerThread::instance_ = nullptr;
+
+// static
+scoped_refptr<AndroidDeviceManager::HandlerThread>
+AndroidDeviceManager::HandlerThread::GetInstance() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (!instance_)
+ new HandlerThread();
+ return instance_;
+}
+
+AndroidDeviceManager::HandlerThread::HandlerThread() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ instance_ = this;
+ thread_ = new base::Thread(kDevToolsAdbBridgeThreadName);
+ base::Thread::Options options;
+ options.message_loop_type = base::MessageLoop::TYPE_IO;
+ if (!thread_->StartWithOptions(options)) {
+ delete thread_;
+ thread_ = nullptr;
+ }
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+AndroidDeviceManager::HandlerThread::message_loop() {
+ return thread_ ? thread_->task_runner() : nullptr;
+}
+
+// static
+void AndroidDeviceManager::HandlerThread::StopThread(
+ base::Thread* thread) {
+ thread->Stop();
+ delete thread;
+}
+
+AndroidDeviceManager::HandlerThread::~HandlerThread() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ instance_ = nullptr;
+ if (!thread_)
+ return;
+ // Shut down thread on FILE thread to join into IO.
+ content::BrowserThread::PostTask(
+ content::BrowserThread::FILE, FROM_HERE,
+ base::BindOnce(&HandlerThread::StopThread, thread_));
+}
+
+// static
+std::unique_ptr<AndroidDeviceManager> AndroidDeviceManager::Create() {
+ return base::WrapUnique(new AndroidDeviceManager());
+}
+
+void AndroidDeviceManager::SetDeviceProviders(
+ const DeviceProviders& providers) {
+ for (DeviceProviders::iterator it = providers_.begin();
+ it != providers_.end(); ++it) {
+ (*it)->AddRef();
+ DeviceProvider* raw_ptr = it->get();
+ *it = nullptr;
+ handler_thread_->message_loop()->ReleaseSoon(FROM_HERE, raw_ptr);
+ }
+ providers_ = providers;
+}
+
+void AndroidDeviceManager::QueryDevices(const DevicesCallback& callback) {
+ DevicesRequest::Start(handler_thread_->message_loop(), providers_,
+ base::Bind(&AndroidDeviceManager::UpdateDevices,
+ weak_factory_.GetWeakPtr(), callback));
+}
+
+AndroidDeviceManager::AndroidDeviceManager()
+ : handler_thread_(HandlerThread::GetInstance()),
+ weak_factory_(this) {
+}
+
+AndroidDeviceManager::~AndroidDeviceManager() {
+ SetDeviceProviders(DeviceProviders());
+}
+
+void AndroidDeviceManager::UpdateDevices(
+ const DevicesCallback& callback,
+ std::unique_ptr<DeviceDescriptors> descriptors) {
+ Devices response;
+ DeviceWeakMap new_devices;
+ for (DeviceDescriptors::const_iterator it = descriptors->begin();
+ it != descriptors->end();
+ ++it) {
+ DeviceWeakMap::iterator found = devices_.find(it->serial);
+ scoped_refptr<Device> device;
+ if (found == devices_.end() || !found->second ||
+ found->second->provider_.get() != it->provider.get()) {
+ device =
+ new Device(handler_thread_->message_loop(), it->provider, it->serial);
+ } else {
+ device = found->second.get();
+ }
+ response.push_back(device);
+ new_devices[it->serial] = device->weak_factory_.GetWeakPtr();
+ }
+ devices_.swap(new_devices);
+ callback.Run(response);
+}
diff --git a/chromium/chrome/browser/devtools/device/android_device_manager.h b/chromium/chrome/browser/devtools/device/android_device_manager.h
new file mode 100644
index 00000000000..0838c1d788b
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/android_device_manager.h
@@ -0,0 +1,249 @@
+// 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_DEVTOOLS_DEVICE_ANDROID_DEVICE_MANAGER_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVICE_ANDROID_DEVICE_MANAGER_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/non_thread_safe.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/browser_thread.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace net {
+class StreamSocket;
+}
+
+class AndroidDeviceManager : public base::NonThreadSafe {
+ public:
+ using CommandCallback =
+ base::Callback<void(int, const std::string&)>;
+ using SocketCallback =
+ base::Callback<void(int result, std::unique_ptr<net::StreamSocket>)>;
+ // |body_head| should contain the body (WebSocket frame data) part that has
+ // been read during processing the header (WebSocket handshake).
+ using HttpUpgradeCallback =
+ base::Callback<void(int result,
+ const std::string& extensions,
+ const std::string& body_head,
+ std::unique_ptr<net::StreamSocket>)>;
+ using SerialsCallback =
+ base::Callback<void(const std::vector<std::string>&)>;
+
+ struct BrowserInfo {
+ BrowserInfo();
+ BrowserInfo(const BrowserInfo& other);
+
+ enum Type {
+ kTypeChrome,
+ kTypeWebView,
+ kTypeOther
+ };
+
+ std::string socket_name;
+ std::string display_name;
+ std::string user;
+ Type type;
+ };
+
+ struct DeviceInfo {
+ DeviceInfo();
+ DeviceInfo(const DeviceInfo& other);
+ ~DeviceInfo();
+
+ std::string model;
+ bool connected;
+ gfx::Size screen_size;
+ std::vector<BrowserInfo> browser_info;
+ };
+
+ typedef base::Callback<void(const DeviceInfo&)> DeviceInfoCallback;
+ class Device;
+
+ class AndroidWebSocket {
+ public:
+ class Delegate {
+ public:
+ virtual void OnSocketOpened() = 0;
+ virtual void OnFrameRead(const std::string& message) = 0;
+ virtual void OnSocketClosed() = 0;
+
+ protected:
+ virtual ~Delegate() {}
+ };
+
+ ~AndroidWebSocket();
+
+ void SendFrame(const std::string& message);
+
+ private:
+ friend class Device;
+ class WebSocketImpl;
+
+ AndroidWebSocket(
+ scoped_refptr<Device> device,
+ const std::string& socket_name,
+ const std::string& path,
+ AndroidWebSocket::Delegate* delegate);
+ void Connected(int result,
+ const std::string& extensions,
+ const std::string& body_head,
+ std::unique_ptr<net::StreamSocket> socket);
+ void OnFrameRead(const std::string& message);
+ void OnSocketClosed();
+
+ scoped_refptr<Device> device_;
+ WebSocketImpl* socket_impl_;
+ Delegate* delegate_;
+ base::WeakPtrFactory<AndroidWebSocket> weak_factory_;
+ DISALLOW_COPY_AND_ASSIGN(AndroidWebSocket);
+ };
+
+ class DeviceProvider;
+
+ class Device : public base::RefCountedThreadSafe<Device>,
+ public base::NonThreadSafe {
+ public:
+ void QueryDeviceInfo(const DeviceInfoCallback& callback);
+
+ void OpenSocket(const std::string& socket_name,
+ const SocketCallback& callback);
+
+ void SendJsonRequest(const std::string& socket_name,
+ const std::string& request,
+ const CommandCallback& callback);
+
+ void HttpUpgrade(const std::string& socket_name,
+ const std::string& path,
+ const std::string& extensions,
+ const HttpUpgradeCallback& callback);
+ AndroidWebSocket* CreateWebSocket(
+ const std::string& socket_name,
+ const std::string& path,
+ AndroidWebSocket::Delegate* delegate);
+
+ std::string serial() { return serial_; }
+
+ private:
+ friend class base::RefCountedThreadSafe<Device>;
+ friend class AndroidDeviceManager;
+ friend class AndroidWebSocket;
+
+ Device(scoped_refptr<base::SingleThreadTaskRunner> device_task_runner,
+ scoped_refptr<DeviceProvider> provider,
+ const std::string& serial);
+
+ virtual ~Device();
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ scoped_refptr<DeviceProvider> provider_;
+ std::string serial_;
+ base::WeakPtrFactory<Device> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(Device);
+ };
+
+ typedef std::vector<scoped_refptr<Device> > Devices;
+ typedef base::Callback<void(const Devices&)> DevicesCallback;
+
+ class DeviceProvider : public base::RefCountedThreadSafe<DeviceProvider> {
+ public:
+ typedef AndroidDeviceManager::SerialsCallback SerialsCallback;
+ typedef AndroidDeviceManager::DeviceInfoCallback DeviceInfoCallback;
+ typedef AndroidDeviceManager::SocketCallback SocketCallback;
+ typedef AndroidDeviceManager::CommandCallback CommandCallback;
+
+ virtual void QueryDevices(const SerialsCallback& callback) = 0;
+
+ virtual void QueryDeviceInfo(const std::string& serial,
+ const DeviceInfoCallback& callback) = 0;
+
+ virtual void OpenSocket(const std::string& serial,
+ const std::string& socket_name,
+ const SocketCallback& callback) = 0;
+
+ virtual void SendJsonRequest(const std::string& serial,
+ const std::string& socket_name,
+ const std::string& request,
+ const CommandCallback& callback);
+
+ virtual void HttpUpgrade(const std::string& serial,
+ const std::string& socket_name,
+ const std::string& path,
+ const std::string& extensions,
+ const HttpUpgradeCallback& callback);
+
+ virtual void ReleaseDevice(const std::string& serial);
+
+ protected:
+ friend class base::RefCountedThreadSafe<DeviceProvider>;
+ DeviceProvider();
+ virtual ~DeviceProvider();
+ };
+
+ typedef std::vector<scoped_refptr<DeviceProvider> > DeviceProviders;
+
+ virtual ~AndroidDeviceManager();
+
+ static std::unique_ptr<AndroidDeviceManager> Create();
+
+ void SetDeviceProviders(const DeviceProviders& providers);
+
+ void QueryDevices(const DevicesCallback& callback);
+
+ static std::string GetBrowserName(const std::string& socket,
+ const std::string& package);
+ using RunCommandCallback =
+ base::Callback<void(const std::string&, const CommandCallback&)>;
+
+ static void QueryDeviceInfo(const RunCommandCallback& command_callback,
+ const DeviceInfoCallback& callback);
+
+ struct DeviceDescriptor {
+ DeviceDescriptor();
+ DeviceDescriptor(const DeviceDescriptor& other);
+ ~DeviceDescriptor();
+
+ scoped_refptr<DeviceProvider> provider;
+ std::string serial;
+ };
+
+ typedef std::vector<DeviceDescriptor> DeviceDescriptors;
+
+ private:
+ class HandlerThread : public base::RefCountedThreadSafe<HandlerThread> {
+ public:
+ static scoped_refptr<HandlerThread> GetInstance();
+ scoped_refptr<base::SingleThreadTaskRunner> message_loop();
+
+ private:
+ friend class base::RefCountedThreadSafe<HandlerThread>;
+ static HandlerThread* instance_;
+ static void StopThread(base::Thread* thread);
+
+ HandlerThread();
+ virtual ~HandlerThread();
+ base::Thread* thread_;
+ };
+
+ AndroidDeviceManager();
+
+ void UpdateDevices(const DevicesCallback& callback,
+ std::unique_ptr<DeviceDescriptors> descriptors);
+
+ typedef std::map<std::string, base::WeakPtr<Device> > DeviceWeakMap;
+
+ scoped_refptr<HandlerThread> handler_thread_;
+ DeviceProviders providers_;
+ DeviceWeakMap devices_;
+
+ base::WeakPtrFactory<AndroidDeviceManager> weak_factory_;
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVICE_ANDROID_DEVICE_MANAGER_H_
diff --git a/chromium/chrome/browser/devtools/device/android_web_socket.cc b/chromium/chrome/browser/devtools/device/android_web_socket.cc
new file mode 100644
index 00000000000..8a57aa364a6
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/android_web_socket.cc
@@ -0,0 +1,226 @@
+// 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 <utility>
+
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/rand_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/devtools/device/android_device_manager.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/server/web_socket_encoder.h"
+#include "net/socket/stream_socket.h"
+
+using content::BrowserThread;
+using net::WebSocket;
+
+namespace {
+
+const int kBufferSize = 16 * 1024;
+const char kCloseResponse[] = "\x88\x80\x2D\x0E\x1E\xFA";
+
+} // namespace
+
+class AndroidDeviceManager::AndroidWebSocket::WebSocketImpl {
+ public:
+ WebSocketImpl(
+ scoped_refptr<base::SingleThreadTaskRunner> response_task_runner,
+ base::WeakPtr<AndroidWebSocket> weak_socket,
+ const std::string& extensions,
+ const std::string& body_head,
+ std::unique_ptr<net::StreamSocket> socket)
+ : response_task_runner_(response_task_runner),
+ weak_socket_(weak_socket),
+ socket_(std::move(socket)),
+ encoder_(net::WebSocketEncoder::CreateClient(extensions)),
+ response_buffer_(body_head) {
+ thread_checker_.DetachFromThread();
+ }
+
+ void StartListening() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(socket_);
+
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
+
+ if (response_buffer_.size() > 0)
+ ProcessResponseBuffer(buffer);
+ else
+ Read(buffer);
+ }
+
+ void SendFrame(const std::string& message) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!socket_)
+ return;
+ int mask = base::RandInt(0, 0x7FFFFFFF);
+ std::string encoded_frame;
+ encoder_->EncodeFrame(message, mask, &encoded_frame);
+ SendData(encoded_frame);
+ }
+
+ private:
+ void Read(scoped_refptr<net::IOBuffer> io_buffer) {
+ int result = socket_->Read(
+ io_buffer.get(),
+ kBufferSize,
+ base::Bind(&WebSocketImpl::OnBytesRead,
+ base::Unretained(this), io_buffer));
+ if (result != net::ERR_IO_PENDING)
+ OnBytesRead(io_buffer, result);
+ }
+
+ void OnBytesRead(scoped_refptr<net::IOBuffer> io_buffer, int result) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (result <= 0) {
+ Disconnect();
+ return;
+ }
+ response_buffer_.append(io_buffer->data(), result);
+
+ ProcessResponseBuffer(io_buffer);
+ }
+
+ void ProcessResponseBuffer(scoped_refptr<net::IOBuffer> io_buffer) {
+ int bytes_consumed;
+ std::string output;
+ WebSocket::ParseResult parse_result = encoder_->DecodeFrame(
+ response_buffer_, &bytes_consumed, &output);
+
+ while (parse_result == WebSocket::FRAME_OK) {
+ response_buffer_ = response_buffer_.substr(bytes_consumed);
+ response_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&AndroidWebSocket::OnFrameRead, weak_socket_, output));
+ parse_result = encoder_->DecodeFrame(
+ response_buffer_, &bytes_consumed, &output);
+ }
+ if (parse_result == WebSocket::FRAME_CLOSE)
+ SendData(kCloseResponse);
+
+ if (parse_result == WebSocket::FRAME_ERROR) {
+ Disconnect();
+ return;
+ }
+ Read(io_buffer);
+ }
+
+ void SendData(const std::string& data) {
+ request_buffer_ += data;
+ if (request_buffer_.length() == data.length())
+ SendPendingRequests(0);
+ }
+
+ void SendPendingRequests(int result) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (result < 0) {
+ Disconnect();
+ return;
+ }
+ request_buffer_ = request_buffer_.substr(result);
+ if (request_buffer_.empty())
+ return;
+
+ scoped_refptr<net::StringIOBuffer> buffer =
+ new net::StringIOBuffer(request_buffer_);
+ result = socket_->Write(buffer.get(), buffer->size(),
+ base::Bind(&WebSocketImpl::SendPendingRequests,
+ base::Unretained(this)));
+ if (result != net::ERR_IO_PENDING)
+ SendPendingRequests(result);
+ }
+
+ void Disconnect() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ socket_.reset();
+ response_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&AndroidWebSocket::OnSocketClosed, weak_socket_));
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> response_task_runner_;
+ base::WeakPtr<AndroidWebSocket> weak_socket_;
+ std::unique_ptr<net::StreamSocket> socket_;
+ std::unique_ptr<net::WebSocketEncoder> encoder_;
+ std::string response_buffer_;
+ std::string request_buffer_;
+ base::ThreadChecker thread_checker_;
+ DISALLOW_COPY_AND_ASSIGN(WebSocketImpl);
+};
+
+AndroidDeviceManager::AndroidWebSocket::AndroidWebSocket(
+ scoped_refptr<Device> device,
+ const std::string& socket_name,
+ const std::string& path,
+ Delegate* delegate)
+ : device_(device),
+ socket_impl_(nullptr),
+ delegate_(delegate),
+ weak_factory_(this) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(delegate_);
+ DCHECK(device_);
+ device_->HttpUpgrade(
+ socket_name, path, net::WebSocketEncoder::kClientExtensions,
+ base::Bind(&AndroidWebSocket::Connected, weak_factory_.GetWeakPtr()));
+}
+
+AndroidDeviceManager::AndroidWebSocket::~AndroidWebSocket() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (socket_impl_)
+ device_->task_runner_->DeleteSoon(FROM_HERE, socket_impl_);
+}
+
+void AndroidDeviceManager::AndroidWebSocket::SendFrame(
+ const std::string& message) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(socket_impl_);
+ DCHECK(device_);
+ device_->task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&WebSocketImpl::SendFrame,
+ base::Unretained(socket_impl_), message));
+}
+
+void AndroidDeviceManager::AndroidWebSocket::Connected(
+ int result,
+ const std::string& extensions,
+ const std::string& body_head,
+ std::unique_ptr<net::StreamSocket> socket) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (result != net::OK || !socket.get()) {
+ OnSocketClosed();
+ return;
+ }
+ socket_impl_ = new WebSocketImpl(base::ThreadTaskRunnerHandle::Get(),
+ weak_factory_.GetWeakPtr(), extensions,
+ body_head, std::move(socket));
+ device_->task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&WebSocketImpl::StartListening,
+ base::Unretained(socket_impl_)));
+ delegate_->OnSocketOpened();
+}
+
+void AndroidDeviceManager::AndroidWebSocket::OnFrameRead(
+ const std::string& message) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ delegate_->OnFrameRead(message);
+}
+
+void AndroidDeviceManager::AndroidWebSocket::OnSocketClosed() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ delegate_->OnSocketClosed();
+}
+
+AndroidDeviceManager::AndroidWebSocket*
+AndroidDeviceManager::Device::CreateWebSocket(
+ const std::string& socket_name,
+ const std::string& path,
+ AndroidWebSocket::Delegate* delegate) {
+ return new AndroidWebSocket(this, socket_name, path, delegate);
+}
diff --git a/chromium/chrome/browser/devtools/device/cast_device_provider.cc b/chromium/chrome/browser/devtools/device/cast_device_provider.cc
new file mode 100644
index 00000000000..8ee6714b2c5
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/cast_device_provider.cc
@@ -0,0 +1,206 @@
+// 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.
+
+#include "chrome/browser/devtools/device/cast_device_provider.h"
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/local_discovery/service_discovery_shared_client.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/ip_address.h"
+
+using local_discovery::ServiceDescription;
+using local_discovery::ServiceDiscoveryDeviceLister;
+using local_discovery::ServiceDiscoverySharedClient;
+
+namespace {
+
+const int kCastInspectPort = 9222;
+const char kCastServiceType[] = "_googlecast._tcp.local";
+const char kUnknownCastDevice[] = "Unknown Cast Device";
+
+using ServiceTxtRecordMap = std::map<std::string, std::string>;
+
+// Parses TXT record strings into a map. TXT key-value strings are assumed to
+// follow the form "$key(=$value)?", where $key must contain at least one
+// character, and $value may be empty.
+std::unique_ptr<ServiceTxtRecordMap> ParseServiceTxtRecord(
+ const std::vector<std::string>& record) {
+ std::unique_ptr<ServiceTxtRecordMap> record_map(new ServiceTxtRecordMap());
+ for (const auto& key_value_str : record) {
+ if (key_value_str.empty())
+ continue;
+
+ size_t index = key_value_str.find("=", 0);
+ if (index == std::string::npos) {
+ // Some strings may only define a key (no '=' in the key/value string).
+ // The chosen behavior is to assume the value is the empty string.
+ record_map->insert(std::make_pair(key_value_str, ""));
+ } else {
+ std::string key = key_value_str.substr(0, index);
+ std::string value = key_value_str.substr(index + 1);
+ record_map->insert(std::make_pair(key, value));
+ }
+ }
+ return record_map;
+}
+
+AndroidDeviceManager::DeviceInfo ServiceDescriptionToDeviceInfo(
+ const ServiceDescription& service_description) {
+ std::unique_ptr<ServiceTxtRecordMap> record_map =
+ ParseServiceTxtRecord(service_description.metadata);
+
+ AndroidDeviceManager::DeviceInfo device_info;
+ device_info.connected = true;
+ const auto it = record_map->find("md");
+ if (it != record_map->end() && !it->second.empty())
+ device_info.model = it->second;
+ else
+ device_info.model = kUnknownCastDevice;
+
+ AndroidDeviceManager::BrowserInfo browser_info;
+ browser_info.socket_name = base::IntToString(kCastInspectPort);
+ browser_info.display_name =
+ base::SplitString(service_description.service_name, ".",
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)[0];
+
+ browser_info.type = AndroidDeviceManager::BrowserInfo::kTypeChrome;
+ device_info.browser_info.push_back(browser_info);
+ return device_info;
+}
+
+} // namespace
+
+// The purpose of this class is to route lister delegate signals from
+// ServiceDiscoveryDeviceLister (on the UI thread) to CastDeviceProvider (on the
+// DevTools ADB thread). Cancellable callbacks are necessary since
+// CastDeviceProvider and ServiceDiscoveryDeviceLister are destroyed on
+// different threads in undefined order.
+class CastDeviceProvider::DeviceListerDelegate
+ : public ServiceDiscoveryDeviceLister::Delegate,
+ public base::SupportsWeakPtr<DeviceListerDelegate> {
+ public:
+ DeviceListerDelegate(base::WeakPtr<CastDeviceProvider> provider,
+ scoped_refptr<base::SingleThreadTaskRunner> runner)
+ : provider_(provider), runner_(runner) {}
+
+ virtual ~DeviceListerDelegate() {}
+
+ void StartDiscovery() {
+ // This must be called on the UI thread; ServiceDiscoverySharedClient and
+ // ServiceDiscoveryDeviceLister are thread protected.
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ if (device_lister_)
+ return;
+ service_discovery_client_ = ServiceDiscoverySharedClient::GetInstance();
+ device_lister_.reset(new ServiceDiscoveryDeviceLister(
+ this, service_discovery_client_.get(), kCastServiceType));
+ device_lister_->Start();
+ device_lister_->DiscoverNewDevices(true);
+ }
+
+ // ServiceDiscoveryDeviceLister::Delegate implementation:
+ void OnDeviceChanged(bool added,
+ const ServiceDescription& service_description) override {
+ runner_->PostTask(FROM_HERE,
+ base::BindOnce(&CastDeviceProvider::OnDeviceChanged,
+ provider_, added, service_description));
+ }
+
+ void OnDeviceRemoved(const std::string& service_name) override {
+ runner_->PostTask(FROM_HERE,
+ base::BindOnce(&CastDeviceProvider::OnDeviceRemoved,
+ provider_, service_name));
+ }
+
+ void OnDeviceCacheFlushed() override {
+ runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&CastDeviceProvider::OnDeviceCacheFlushed, provider_));
+ }
+
+ private:
+ // The device provider to notify of device changes.
+ base::WeakPtr<CastDeviceProvider> provider_;
+ // Runner for the thread the WeakPtr was created on (this is where device
+ // messages will be posted).
+ scoped_refptr<base::SingleThreadTaskRunner> runner_;
+ scoped_refptr<ServiceDiscoverySharedClient> service_discovery_client_;
+ std::unique_ptr<ServiceDiscoveryDeviceLister> device_lister_;
+};
+
+CastDeviceProvider::CastDeviceProvider() : weak_factory_(this) {}
+
+CastDeviceProvider::~CastDeviceProvider() {}
+
+void CastDeviceProvider::QueryDevices(const SerialsCallback& callback) {
+ if (!lister_delegate_) {
+ lister_delegate_.reset(new DeviceListerDelegate(
+ weak_factory_.GetWeakPtr(), base::ThreadTaskRunnerHandle::Get()));
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI, FROM_HERE,
+ base::BindOnce(&DeviceListerDelegate::StartDiscovery,
+ lister_delegate_->AsWeakPtr()));
+ }
+ std::set<net::HostPortPair> targets;
+ for (const auto& device_entry : device_info_map_)
+ targets.insert(net::HostPortPair(device_entry.first, kCastInspectPort));
+ tcp_provider_ = new TCPDeviceProvider(targets);
+ tcp_provider_->QueryDevices(callback);
+}
+
+void CastDeviceProvider::QueryDeviceInfo(const std::string& serial,
+ const DeviceInfoCallback& callback) {
+ auto it_device = device_info_map_.find(serial);
+ if (it_device == device_info_map_.end())
+ return;
+ callback.Run(it_device->second);
+}
+
+void CastDeviceProvider::OpenSocket(const std::string& serial,
+ const std::string& socket_name,
+ const SocketCallback& callback) {
+ tcp_provider_->OpenSocket(serial, socket_name, callback);
+}
+
+void CastDeviceProvider::OnDeviceChanged(
+ bool added,
+ const ServiceDescription& service_description) {
+ VLOG(1) << "Device " << (added ? "added: " : "changed: ")
+ << service_description.service_name;
+ if (service_description.service_type() != kCastServiceType)
+ return;
+ const net::IPAddress& ip_address = service_description.ip_address;
+ if (!ip_address.IsValid()) {
+ // An invalid IP address is not queryable.
+ return;
+ }
+ const std::string& name = service_description.service_name;
+ std::string host = ip_address.ToString();
+ service_hostname_map_[name] = host;
+ device_info_map_[host] = ServiceDescriptionToDeviceInfo(service_description);
+}
+
+void CastDeviceProvider::OnDeviceRemoved(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())
+ return;
+ const std::string& hostname = it->second;
+ device_info_map_.erase(hostname);
+ service_hostname_map_.erase(it);
+}
+
+void CastDeviceProvider::OnDeviceCacheFlushed() {
+ 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
new file mode 100644
index 00000000000..f0462cabaa8
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/cast_device_provider.h
@@ -0,0 +1,63 @@
+// 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.
+
+#ifndef CHROME_BROWSER_DEVTOOLS_DEVICE_CAST_DEVICE_PROVIDER_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVICE_CAST_DEVICE_PROVIDER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/devtools/device/android_device_manager.h"
+#include "chrome/browser/devtools/device/tcp_device_provider.h"
+#include "chrome/browser/local_discovery/service_discovery_device_lister.h"
+#include "content/public/browser/browser_thread.h"
+
+// Supplies Cast device information for the purposes of remote debugging Cast
+// applications over ADB.
+class CastDeviceProvider
+ : public AndroidDeviceManager::DeviceProvider,
+ public local_discovery::ServiceDiscoveryDeviceLister::Delegate {
+ public:
+ CastDeviceProvider();
+
+ // DeviceProvider implementation:
+ void QueryDevices(const SerialsCallback& callback) override;
+ void QueryDeviceInfo(const std::string& serial,
+ const DeviceInfoCallback& callback) override;
+ void OpenSocket(const std::string& serial,
+ const std::string& socket_name,
+ const SocketCallback& callback) override;
+
+ // ServiceDiscoveryDeviceLister::Delegate implementation:
+ void OnDeviceChanged(
+ bool added,
+ const local_discovery::ServiceDescription& service_description) override;
+ void OnDeviceRemoved(const std::string& service_name) override;
+ void OnDeviceCacheFlushed() override;
+
+ private:
+ class DeviceListerDelegate;
+
+ ~CastDeviceProvider() override;
+
+ scoped_refptr<TCPDeviceProvider> tcp_provider_;
+ std::unique_ptr<DeviceListerDelegate,
+ content::BrowserThread::DeleteOnUIThread>
+ lister_delegate_;
+
+ // Keyed on the hostname (IP address).
+ std::map<std::string, AndroidDeviceManager::DeviceInfo> device_info_map_;
+ // Maps a service name to the hostname (IP address).
+ std::map<std::string, std::string> service_hostname_map_;
+
+ base::WeakPtrFactory<CastDeviceProvider> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CastDeviceProvider);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVICE_CAST_DEVICE_PROVIDER_H_
diff --git a/chromium/chrome/browser/devtools/device/cast_device_provider_unittest.cc b/chromium/chrome/browser/devtools/device/cast_device_provider_unittest.cc
new file mode 100644
index 00000000000..936d7e56099
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/cast_device_provider_unittest.cc
@@ -0,0 +1,109 @@
+// 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.
+
+#include "chrome/browser/devtools/device/cast_device_provider.h"
+
+#include "chrome/browser/devtools/device/android_device_manager.h"
+#include "net/base/host_port_pair.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using local_discovery::ServiceDescription;
+using DeviceInfo = AndroidDeviceManager::DeviceInfo;
+using BrowserInfo = AndroidDeviceManager::BrowserInfo;
+
+namespace {
+
+void CompareDeviceInfo(bool* was_run,
+ const DeviceInfo& expected,
+ const DeviceInfo& actual) {
+ EXPECT_EQ(expected.model, actual.model);
+ EXPECT_EQ(expected.connected, actual.connected);
+
+ const BrowserInfo& exp_br_info = expected.browser_info[0];
+ const BrowserInfo& act_br_info = actual.browser_info[0];
+ EXPECT_EQ(exp_br_info.socket_name, act_br_info.socket_name);
+ EXPECT_EQ(exp_br_info.display_name, act_br_info.display_name);
+ EXPECT_EQ(exp_br_info.type, act_br_info.type);
+
+ *was_run = true;
+}
+
+void DummyCallback(bool* was_run, const DeviceInfo& device_info) {
+ *was_run = true;
+}
+
+} // namespace
+
+TEST(CastDeviceProviderTest, ServiceDiscovery) {
+ scoped_refptr<CastDeviceProvider> device_provider_ = new CastDeviceProvider();
+
+ // Create a cast service.
+ const std::string cast_service_display_name = "FakeCast1337";
+ const std::string cast_service_type = "_googlecast._tcp.local";
+ const std::string cast_service_model = "Fake Cast Device";
+
+ ServiceDescription cast_service;
+ cast_service.service_name =
+ cast_service_display_name + "." + cast_service_type;
+ cast_service.address = net::HostPortPair("192.168.1.101", 8009);
+ cast_service.metadata.push_back("id=0123456789abcdef0123456789abcdef");
+ cast_service.metadata.push_back("ve=00");
+ 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);
+
+ BrowserInfo exp_browser_info;
+ exp_browser_info.socket_name = "9222";
+ exp_browser_info.display_name = cast_service_display_name;
+ exp_browser_info.type = BrowserInfo::kTypeChrome;
+
+ DeviceInfo expected;
+ expected.model = cast_service_model; // From metadata::md
+ expected.connected = true;
+ expected.browser_info.push_back(exp_browser_info);
+
+ bool was_run = false;
+ // Callback should be run, and the queried service should match the expected.
+ device_provider_->QueryDeviceInfo(
+ cast_service.address.host(),
+ base::Bind(&CompareDeviceInfo, &was_run, expected));
+ ASSERT_TRUE(was_run);
+ was_run = false;
+
+ // Create a non-cast service.
+ const std::string other_service_display_name = "OtherDevice";
+ const std::string other_service_type = "_other._tcp.local";
+ const std::string other_service_model = "Some Other Device";
+
+ ServiceDescription other_service;
+ other_service.service_name =
+ other_service_display_name + "." + other_service_type;
+ other_service.address = net::HostPortPair("10.64.1.101", 1234);
+ other_service.metadata.push_back("id=0123456789abcdef0123456789abcdef");
+ other_service.metadata.push_back("ve=00");
+ other_service.metadata.push_back("md=" + other_service_model);
+ ASSERT_TRUE(other_service.ip_address.AssignFromIPLiteral("10.64.1.101"));
+
+ // Callback should not be run, since this service is not yet discovered.
+ device_provider_->QueryDeviceInfo(other_service.address.host(),
+ base::Bind(&DummyCallback, &was_run));
+ ASSERT_FALSE(was_run);
+
+ device_provider_->OnDeviceChanged(true, other_service);
+
+ // Callback should not be run, since non-cast services are not discovered by
+ // this device provider.
+ device_provider_->QueryDeviceInfo(other_service.address.host(),
+ base::Bind(&DummyCallback, &was_run));
+ ASSERT_FALSE(was_run);
+
+ // Remove the cast service.
+ device_provider_->OnDeviceRemoved(cast_service.service_name);
+
+ // Callback should not be run, since the cast service has been removed.
+ device_provider_->QueryDeviceInfo(cast_service.address.host(),
+ base::Bind(&DummyCallback, &was_run));
+ ASSERT_FALSE(was_run);
+}
diff --git a/chromium/chrome/browser/devtools/device/devtools_android_bridge.cc b/chromium/chrome/browser/devtools/device/devtools_android_bridge.cc
new file mode 100644
index 00000000000..a8ad1f01a8b
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/devtools_android_bridge.cc
@@ -0,0 +1,406 @@
+// 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/devtools/device/devtools_android_bridge.h"
+
+#include <stddef.h>
+#include <algorithm>
+#include <map>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/json/json_reader.h"
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/singleton.h"
+#include "base/message_loop/message_loop.h"
+#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/threading/thread.h"
+#include "base/values.h"
+#include "chrome/browser/devtools/device/adb/adb_device_provider.h"
+#include "chrome/browser/devtools/device/port_forwarding_controller.h"
+#include "chrome/browser/devtools/device/tcp_device_provider.h"
+#include "chrome/browser/devtools/device/usb/usb_device_provider.h"
+#include "chrome/browser/devtools/devtools_protocol.h"
+#include "chrome/browser/devtools/devtools_window.h"
+#include "chrome/browser/devtools/remote_debugging_server.h"
+#include "chrome/browser/profiles/profile.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"
+#include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/devtools_external_agent_proxy.h"
+#include "content/public/browser/devtools_external_agent_proxy_delegate.h"
+#include "net/base/escape.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_errors.h"
+
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+#include "chrome/browser/devtools/device/cast_device_provider.h"
+#endif
+
+using content::BrowserThread;
+using content::DevToolsAgentHost;
+
+namespace {
+
+const char kNewPageRequestWithURL[] = "/json/new?%s";
+const char kChromeDiscoveryURL[] = "localhost:9222";
+const char kNodeDiscoveryURL[] = "localhost:9229";
+
+bool BrowserIdFromString(const std::string& browser_id_str,
+ std::string* serial,
+ std::string* browser_id) {
+ size_t colon_pos = browser_id_str.find(':');
+ if (colon_pos == std::string::npos)
+ return false;
+ *serial = browser_id_str.substr(0, colon_pos);
+ *browser_id = browser_id_str.substr(colon_pos + 1);
+ return true;
+}
+
+static void NoOp(int, const std::string&) {}
+
+} // namespace
+
+// static
+DevToolsAndroidBridge::Factory* DevToolsAndroidBridge::Factory::GetInstance() {
+ return base::Singleton<DevToolsAndroidBridge::Factory>::get();
+}
+
+// static
+DevToolsAndroidBridge* DevToolsAndroidBridge::Factory::GetForProfile(
+ Profile* profile) {
+ return static_cast<DevToolsAndroidBridge*>(GetInstance()->
+ GetServiceForBrowserContext(profile->GetOriginalProfile(), true));
+}
+
+DevToolsAndroidBridge::Factory::Factory()
+ : BrowserContextKeyedServiceFactory(
+ "DevToolsAndroidBridge",
+ BrowserContextDependencyManager::GetInstance()) {
+}
+
+DevToolsAndroidBridge::Factory::~Factory() {}
+
+KeyedService* DevToolsAndroidBridge::Factory::BuildServiceInstanceFor(
+ content::BrowserContext* context) const {
+ Profile* profile = Profile::FromBrowserContext(context);
+
+ return new DevToolsAndroidBridge(profile);
+}
+
+scoped_refptr<content::DevToolsAgentHost>
+DevToolsAndroidBridge::GetBrowserAgentHost(
+ scoped_refptr<RemoteBrowser> browser) {
+ DeviceMap::iterator it = device_map_.find(browser->serial());
+ if (it == device_map_.end())
+ return nullptr;
+
+ return DevToolsDeviceDiscovery::CreateBrowserAgentHost(it->second, browser);
+}
+
+void DevToolsAndroidBridge::SendJsonRequest(
+ const std::string& browser_id_str,
+ const std::string& url,
+ const JsonRequestCallback& callback) {
+ std::string serial;
+ std::string browser_id;
+ if (!BrowserIdFromString(browser_id_str, &serial, &browser_id)) {
+ callback.Run(net::ERR_FAILED, std::string());
+ return;
+ }
+ DeviceMap::iterator it = device_map_.find(serial);
+ if (it == device_map_.end()) {
+ callback.Run(net::ERR_FAILED, std::string());
+ return;
+ }
+ it->second->SendJsonRequest(browser_id, url, callback);
+}
+
+void DevToolsAndroidBridge::OpenRemotePage(scoped_refptr<RemoteBrowser> browser,
+ const std::string& input_url) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ GURL gurl(input_url);
+ if (!gurl.is_valid()) {
+ gurl = GURL("http://" + input_url);
+ if (!gurl.is_valid())
+ return;
+ }
+ std::string url = gurl.spec();
+ RemoteBrowser::ParsedVersion parsed_version = browser->GetParsedVersion();
+
+ 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));
+}
+
+DevToolsAndroidBridge::DevToolsAndroidBridge(
+ Profile* profile)
+ : profile_(profile),
+ device_manager_(AndroidDeviceManager::Create()),
+ port_forwarding_controller_(new PortForwardingController(profile)),
+ weak_factory_(this) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ pref_change_registrar_.Init(profile_->GetPrefs());
+ pref_change_registrar_.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled,
+ base::Bind(&DevToolsAndroidBridge::CreateDeviceProviders,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(prefs::kDevToolsTCPDiscoveryConfig,
+ base::Bind(&DevToolsAndroidBridge::CreateDeviceProviders,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(prefs::kDevToolsDiscoverTCPTargetsEnabled,
+ base::Bind(&DevToolsAndroidBridge::CreateDeviceProviders,
+ base::Unretained(this)));
+ base::ListValue* target_discovery = new base::ListValue();
+ target_discovery->AppendString(kChromeDiscoveryURL);
+ target_discovery->AppendString(kNodeDiscoveryURL);
+ profile->GetPrefs()->SetDefaultPrefValue(
+ prefs::kDevToolsTCPDiscoveryConfig, target_discovery);
+ CreateDeviceProviders();
+}
+
+void DevToolsAndroidBridge::AddDeviceListListener(
+ DeviceListListener* listener) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ bool polling_was_off = !NeedsDeviceListPolling();
+ device_list_listeners_.push_back(listener);
+ if (polling_was_off)
+ StartDeviceListPolling();
+}
+
+void DevToolsAndroidBridge::RemoveDeviceListListener(
+ DeviceListListener* listener) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DeviceListListeners::iterator it = std::find(
+ device_list_listeners_.begin(), device_list_listeners_.end(), listener);
+ DCHECK(it != device_list_listeners_.end());
+ device_list_listeners_.erase(it);
+ if (!NeedsDeviceListPolling())
+ StopDeviceListPolling();
+}
+
+void DevToolsAndroidBridge::AddDeviceCountListener(
+ DeviceCountListener* listener) {
+ device_count_listeners_.push_back(listener);
+ if (device_count_listeners_.size() == 1)
+ StartDeviceCountPolling();
+}
+
+void DevToolsAndroidBridge::RemoveDeviceCountListener(
+ DeviceCountListener* listener) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DeviceCountListeners::iterator it = std::find(
+ device_count_listeners_.begin(), device_count_listeners_.end(), listener);
+ DCHECK(it != device_count_listeners_.end());
+ device_count_listeners_.erase(it);
+ if (device_count_listeners_.empty())
+ StopDeviceCountPolling();
+}
+
+void DevToolsAndroidBridge::AddPortForwardingListener(
+ PortForwardingListener* listener) {
+ bool polling_was_off = !NeedsDeviceListPolling();
+ port_forwarding_listeners_.push_back(listener);
+ if (polling_was_off)
+ StartDeviceListPolling();
+}
+
+void DevToolsAndroidBridge::RemovePortForwardingListener(
+ PortForwardingListener* listener) {
+ PortForwardingListeners::iterator it = std::find(
+ port_forwarding_listeners_.begin(),
+ port_forwarding_listeners_.end(),
+ listener);
+ DCHECK(it != port_forwarding_listeners_.end());
+ port_forwarding_listeners_.erase(it);
+ if (!NeedsDeviceListPolling())
+ StopDeviceListPolling();
+}
+
+DevToolsAndroidBridge::~DevToolsAndroidBridge() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(device_list_listeners_.empty());
+ DCHECK(device_count_listeners_.empty());
+ DCHECK(port_forwarding_listeners_.empty());
+}
+
+void DevToolsAndroidBridge::StartDeviceListPolling() {
+ device_discovery_.reset(new DevToolsDeviceDiscovery(device_manager_.get(),
+ base::Bind(&DevToolsAndroidBridge::ReceivedDeviceList,
+ base::Unretained(this))));
+ if (!task_scheduler_.is_null())
+ device_discovery_->SetScheduler(task_scheduler_);
+}
+
+void DevToolsAndroidBridge::StopDeviceListPolling() {
+ device_discovery_.reset();
+ device_map_.clear();
+ port_forwarding_controller_->CloseAllConnections();
+}
+
+bool DevToolsAndroidBridge::NeedsDeviceListPolling() {
+ return !device_list_listeners_.empty() || !port_forwarding_listeners_.empty();
+}
+
+void DevToolsAndroidBridge::ReceivedDeviceList(
+ const CompleteDevices& complete_devices) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ device_map_.clear();
+ RemoteDevices remote_devices;
+ for (const auto& pair : complete_devices) {
+ device_map_[pair.first->serial()] = pair.first;
+ remote_devices.push_back(pair.second);
+ }
+
+ DeviceListListeners copy(device_list_listeners_);
+ for (DeviceListListeners::iterator it = copy.begin(); it != copy.end(); ++it)
+ (*it)->DeviceListChanged(remote_devices);
+
+ ForwardingStatus status =
+ port_forwarding_controller_->DeviceListChanged(complete_devices);
+ PortForwardingListeners forwarding_listeners(port_forwarding_listeners_);
+ for (PortForwardingListeners::iterator it = forwarding_listeners.begin();
+ it != forwarding_listeners.end(); ++it) {
+ (*it)->PortStatusChanged(status);
+ }
+}
+
+void DevToolsAndroidBridge::StartDeviceCountPolling() {
+ device_count_callback_.Reset(
+ base::Bind(&DevToolsAndroidBridge::ReceivedDeviceCount, AsWeakPtr()));
+ RequestDeviceCount(device_count_callback_.callback());
+}
+
+void DevToolsAndroidBridge::StopDeviceCountPolling() {
+ device_count_callback_.Cancel();
+}
+
+void DevToolsAndroidBridge::RequestDeviceCount(
+ const base::Callback<void(int)>& callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if (device_count_listeners_.empty() ||
+ !callback.Equals(device_count_callback_.callback()))
+ return;
+
+ UsbDeviceProvider::CountDevices(callback);
+}
+
+void DevToolsAndroidBridge::ReceivedDeviceCount(int count) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ DeviceCountListeners copy(device_count_listeners_);
+ for (DeviceCountListeners::iterator it = copy.begin(); it != copy.end(); ++it)
+ (*it)->DeviceCountChanged(count);
+
+ if (device_count_listeners_.empty())
+ return;
+
+ task_scheduler_.Run(
+ base::Bind(&DevToolsAndroidBridge::RequestDeviceCount,
+ AsWeakPtr(), device_count_callback_.callback()));
+}
+
+static std::set<net::HostPortPair> ParseTargetDiscoveryPreferenceValue(
+ const base::ListValue* preferenceValue) {
+ std::set<net::HostPortPair> targets;
+ if (!preferenceValue || preferenceValue->empty())
+ return targets;
+ std::string address;
+ for (size_t i = 0; i < preferenceValue->GetSize(); i++) {
+ if (!preferenceValue->GetString(i, &address))
+ continue;
+ net::HostPortPair target = net::HostPortPair::FromString(address);
+ if (target.IsEmpty()) {
+ LOG(WARNING) << "Invalid target: " << address;
+ continue;
+ }
+ targets.insert(target);
+ }
+ return targets;
+}
+
+static scoped_refptr<TCPDeviceProvider> CreateTCPDeviceProvider(
+ const base::ListValue* targetDiscoveryConfig) {
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ std::set<net::HostPortPair> targets =
+ ParseTargetDiscoveryPreferenceValue(targetDiscoveryConfig);
+ if (targets.empty() &&
+ !command_line->HasSwitch(switches::kRemoteDebuggingTargets))
+ return nullptr;
+ std::string value =
+ command_line->GetSwitchValueASCII(switches::kRemoteDebuggingTargets);
+ std::vector<std::string> addresses = base::SplitString(
+ value, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ for (const std::string& address : addresses) {
+ net::HostPortPair target = net::HostPortPair::FromString(address);
+ if (target.IsEmpty()) {
+ LOG(WARNING) << "Invalid target: " << address;
+ continue;
+ }
+ targets.insert(target);
+ }
+ if (targets.empty())
+ return nullptr;
+ return new TCPDeviceProvider(targets);
+}
+
+void DevToolsAndroidBridge::CreateDeviceProviders() {
+ AndroidDeviceManager::DeviceProviders device_providers;
+ PrefService* service = profile_->GetPrefs();
+ const base::ListValue* targets =
+ service->GetBoolean(prefs::kDevToolsDiscoverTCPTargetsEnabled)
+ ? service->GetList(prefs::kDevToolsTCPDiscoveryConfig)
+ : nullptr;
+ scoped_refptr<TCPDeviceProvider> provider = CreateTCPDeviceProvider(targets);
+ if (tcp_provider_callback_)
+ tcp_provider_callback_.Run(provider);
+
+ if (provider)
+ device_providers.push_back(provider);
+
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+ device_providers.push_back(new CastDeviceProvider());
+#endif
+
+ device_providers.push_back(new AdbDeviceProvider());
+
+ const PrefService::Preference* pref =
+ service->FindPreference(prefs::kDevToolsDiscoverUsbDevicesEnabled);
+ const base::Value* pref_value = pref->GetValue();
+
+ bool enabled;
+ if (pref_value->GetAsBoolean(&enabled) && enabled) {
+ device_providers.push_back(new UsbDeviceProvider(profile_));
+ }
+
+ device_manager_->SetDeviceProviders(device_providers);
+ if (NeedsDeviceListPolling()) {
+ StopDeviceListPolling();
+ StartDeviceListPolling();
+ }
+}
+
+void DevToolsAndroidBridge::set_tcp_provider_callback_for_test(
+ TCPProviderCallback callback) {
+ tcp_provider_callback_ = callback;
+ CreateDeviceProviders();
+}
diff --git a/chromium/chrome/browser/devtools/device/devtools_android_bridge.h b/chromium/chrome/browser/devtools/device/devtools_android_bridge.h
new file mode 100644
index 00000000000..78736498ba4
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/devtools_android_bridge.h
@@ -0,0 +1,193 @@
+// 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_DEVTOOLS_DEVICE_DEVTOOLS_ANDROID_BRIDGE_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVICE_DEVTOOLS_ANDROID_BRIDGE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/cancelable_callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/devtools/device/android_device_manager.h"
+#include "chrome/browser/devtools/device/devtools_device_discovery.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "content/public/browser/devtools_agent_host.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace base {
+template<typename T> struct DefaultSingletonTraits;
+} // namespace base
+
+namespace content {
+class BrowserContext;
+}
+
+class PortForwardingController;
+class Profile;
+class TCPDeviceProvider;
+
+class DevToolsAndroidBridge : public KeyedService {
+ public:
+ class Factory : public BrowserContextKeyedServiceFactory {
+ public:
+ // Returns singleton instance of DevToolsAndroidBridge.
+ static Factory* GetInstance();
+
+ // Returns DevToolsAndroidBridge associated with |profile|.
+ static DevToolsAndroidBridge* GetForProfile(Profile* profile);
+
+ private:
+ friend struct base::DefaultSingletonTraits<Factory>;
+
+ Factory();
+ ~Factory() override;
+
+ // BrowserContextKeyedServiceFactory overrides:
+ KeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* context) const override;
+ DISALLOW_COPY_AND_ASSIGN(Factory);
+ };
+
+ using RemotePage = DevToolsDeviceDiscovery::RemotePage;
+ using RemotePages = DevToolsDeviceDiscovery::RemotePages;
+ using RemoteBrowser = DevToolsDeviceDiscovery::RemoteBrowser;
+ using RemoteBrowsers = DevToolsDeviceDiscovery::RemoteBrowsers;
+ using RemoteDevice = DevToolsDeviceDiscovery::RemoteDevice;
+ using RemoteDevices = DevToolsDeviceDiscovery::RemoteDevices;
+ using CompleteDevice = DevToolsDeviceDiscovery::CompleteDevice;
+ using CompleteDevices = DevToolsDeviceDiscovery::CompleteDevices;
+ using DeviceListCallback = DevToolsDeviceDiscovery::DeviceListCallback;
+
+ using JsonRequestCallback = base::Callback<void(int, const std::string&)>;
+
+ class DeviceListListener {
+ public:
+ virtual void DeviceListChanged(const RemoteDevices& devices) = 0;
+ protected:
+ virtual ~DeviceListListener() {}
+ };
+
+ explicit DevToolsAndroidBridge(Profile* profile);
+ void AddDeviceListListener(DeviceListListener* listener);
+ void RemoveDeviceListListener(DeviceListListener* listener);
+
+ class DeviceCountListener {
+ public:
+ virtual void DeviceCountChanged(int count) = 0;
+ protected:
+ virtual ~DeviceCountListener() {}
+ };
+
+ void AddDeviceCountListener(DeviceCountListener* listener);
+ void RemoveDeviceCountListener(DeviceCountListener* listener);
+
+ using PortStatus = int;
+ using PortStatusMap = std::map<int, PortStatus>;
+ using BrowserStatus = std::pair<scoped_refptr<RemoteBrowser>, PortStatusMap>;
+ using ForwardingStatus = std::vector<BrowserStatus>;
+
+ class PortForwardingListener {
+ public:
+ using PortStatusMap = DevToolsAndroidBridge::PortStatusMap;
+ using BrowserStatus = DevToolsAndroidBridge::BrowserStatus;
+ using ForwardingStatus = DevToolsAndroidBridge::ForwardingStatus;
+
+ virtual void PortStatusChanged(const ForwardingStatus&) = 0;
+ protected:
+ virtual ~PortForwardingListener() {}
+ };
+
+ void AddPortForwardingListener(PortForwardingListener* listener);
+ void RemovePortForwardingListener(PortForwardingListener* listener);
+
+ void set_device_providers_for_test(
+ const AndroidDeviceManager::DeviceProviders& device_providers) {
+ device_manager_->SetDeviceProviders(device_providers);
+ }
+
+ void set_task_scheduler_for_test(
+ base::Callback<void(const base::Closure&)> scheduler) {
+ task_scheduler_ = scheduler;
+ }
+
+ using RemotePageCallback = base::Callback<void(scoped_refptr<RemotePage>)>;
+ void OpenRemotePage(scoped_refptr<RemoteBrowser> browser,
+ const std::string& url);
+
+ scoped_refptr<content::DevToolsAgentHost> GetBrowserAgentHost(
+ scoped_refptr<RemoteBrowser> browser);
+
+ void SendJsonRequest(const std::string& browser_id_str,
+ const std::string& url,
+ const JsonRequestCallback& callback);
+
+ using TCPProviderCallback =
+ base::Callback<void(scoped_refptr<TCPDeviceProvider>)>;
+ void set_tcp_provider_callback_for_test(TCPProviderCallback callback);
+
+ private:
+ friend struct content::BrowserThread::DeleteOnThread<
+ content::BrowserThread::UI>;
+ friend class base::DeleteHelper<DevToolsAndroidBridge>;
+
+ ~DevToolsAndroidBridge() override;
+
+ void StartDeviceListPolling();
+ void StopDeviceListPolling();
+ bool NeedsDeviceListPolling();
+
+ void RequestDeviceList(const DeviceListCallback& callback);
+ void ReceivedDeviceList(const CompleteDevices& complete_devices);
+
+ void StartDeviceCountPolling();
+ void StopDeviceCountPolling();
+ void RequestDeviceCount(const base::Callback<void(int)>& callback);
+ void ReceivedDeviceCount(int count);
+
+ static void ScheduleTaskDefault(const base::Closure& task);
+
+ void CreateDeviceProviders();
+
+ base::WeakPtr<DevToolsAndroidBridge> AsWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+ }
+
+ Profile* const profile_;
+ const std::unique_ptr<AndroidDeviceManager> device_manager_;
+
+ using DeviceMap =
+ std::map<std::string, scoped_refptr<AndroidDeviceManager::Device> >;
+ DeviceMap device_map_;
+
+ using DeviceListListeners = std::vector<DeviceListListener*>;
+ DeviceListListeners device_list_listeners_;
+
+ using DeviceCountListeners = std::vector<DeviceCountListener*>;
+ DeviceCountListeners device_count_listeners_;
+ base::CancelableCallback<void(int)> device_count_callback_;
+ base::Callback<void(const base::Closure&)> task_scheduler_;
+
+ using PortForwardingListeners = std::vector<PortForwardingListener*>;
+ PortForwardingListeners port_forwarding_listeners_;
+ std::unique_ptr<PortForwardingController> port_forwarding_controller_;
+
+ PrefChangeRegistrar pref_change_registrar_;
+
+ TCPProviderCallback tcp_provider_callback_;
+
+ std::unique_ptr<DevToolsDeviceDiscovery> device_discovery_;
+
+ base::WeakPtrFactory<DevToolsAndroidBridge> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsAndroidBridge);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVICE_DEVTOOLS_ANDROID_BRIDGE_H_
diff --git a/chromium/chrome/browser/devtools/device/devtools_android_bridge_browsertest.cc b/chromium/chrome/browser/devtools/device/devtools_android_bridge_browsertest.cc
new file mode 100644
index 00000000000..376a9bbf539
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/devtools_android_bridge_browsertest.cc
@@ -0,0 +1,144 @@
+// 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 <algorithm>
+#include <array>
+
+#include "base/values.h"
+#include "chrome/browser/devtools/device/devtools_android_bridge.h"
+#include "chrome/browser/devtools/device/tcp_device_provider.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/chrome_switches.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/prefs/pref_service.h"
+
+class DevToolsAndroidBridgeTest : public InProcessBrowserTest {
+};
+
+static void assign_from_callback(scoped_refptr<TCPDeviceProvider>* store,
+ int* invocation_counter,
+ scoped_refptr<TCPDeviceProvider> value) {
+ (*invocation_counter)++;
+ *store = value;
+}
+
+static std::string SetToString(const std::set<std::string>& values) {
+ std::ostringstream result;
+ std::copy(values.begin(), values.end(),
+ std::ostream_iterator<std::string>(result, ", "));
+ std::string result_string = result.str();
+ return result_string.substr(0, result_string.length() - 2);
+}
+
+static std::string AllTargetsString(
+ scoped_refptr<TCPDeviceProvider> provider) {
+ std::set<std::string> actual;
+ for (const net::HostPortPair& hostport : provider->get_targets_for_test())
+ actual.insert(hostport.ToString());
+ return SetToString(actual);
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsAndroidBridgeTest, DiscoveryListChanges) {
+ Profile* profile = browser()->profile();
+
+ PrefService* service = profile->GetPrefs();
+ service->ClearPref(prefs::kDevToolsTCPDiscoveryConfig);
+ service->SetBoolean(prefs::kDevToolsDiscoverTCPTargetsEnabled, true);
+
+ DevToolsAndroidBridge* bridge =
+ DevToolsAndroidBridge::Factory::GetForProfile(profile);
+
+ scoped_refptr<TCPDeviceProvider> provider;
+ int called = 0;
+ bridge->set_tcp_provider_callback_for_test(
+ base::Bind(assign_from_callback, &provider, &called));
+
+ EXPECT_LT(0, called);
+ EXPECT_NE(nullptr, provider);
+
+ EXPECT_STREQ("localhost:9222, localhost:9229",
+ AllTargetsString(provider).c_str());
+
+ int invocations = called;
+ base::ListValue list;
+ list.AppendString("somehost:2000");
+
+ service->Set(prefs::kDevToolsTCPDiscoveryConfig, list);
+
+ EXPECT_LT(invocations, called);
+ EXPECT_NE(nullptr, provider);
+ EXPECT_STREQ("somehost:2000", AllTargetsString(provider).c_str());
+
+ invocations = called;
+ list.Clear();
+ service->Set(prefs::kDevToolsTCPDiscoveryConfig, list);
+
+ EXPECT_LT(invocations, called);
+ EXPECT_EQ(nullptr, provider);
+ invocations = called;
+
+ list.AppendString("b:1");
+ list.AppendString("c:2");
+ list.AppendString("<not really a good address.");
+ list.AppendString("d:3");
+ list.AppendString("c:2");
+ service->Set(prefs::kDevToolsTCPDiscoveryConfig, list);
+
+ EXPECT_LT(invocations, called);
+ EXPECT_NE(nullptr, provider);
+ EXPECT_STREQ("b:1, c:2, d:3", AllTargetsString(provider).c_str());
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsAndroidBridgeTest, DefaultValues) {
+ Profile* profile = browser()->profile();
+
+ PrefService* service = profile->GetPrefs();
+ DevToolsAndroidBridge::Factory::GetForProfile(profile);
+ service->ClearPref(prefs::kDevToolsDiscoverTCPTargetsEnabled);
+ service->ClearPref(prefs::kDevToolsTCPDiscoveryConfig);
+
+ const base::ListValue* targets =
+ service->GetList(prefs::kDevToolsTCPDiscoveryConfig);
+ EXPECT_NE(nullptr, targets);
+ EXPECT_EQ(2ul, targets->GetSize());
+
+ std::set<std::string> actual;
+ for (size_t i = 0; i < targets->GetSize(); i++) {
+ std::string value;
+ targets->GetString(i, &value);
+ actual.insert(value);
+ }
+ EXPECT_STREQ("localhost:9222, localhost:9229", SetToString(actual).c_str());
+ EXPECT_TRUE(service->GetBoolean(prefs::kDevToolsDiscoverTCPTargetsEnabled));
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsAndroidBridgeTest, TCPEnableChange) {
+ Profile* profile = browser()->profile();
+
+ PrefService* service = profile->GetPrefs();
+ service->ClearPref(prefs::kDevToolsTCPDiscoveryConfig);
+ service->ClearPref(prefs::kDevToolsDiscoverTCPTargetsEnabled);
+
+ DevToolsAndroidBridge* bridge =
+ DevToolsAndroidBridge::Factory::GetForProfile(profile);
+
+ scoped_refptr<TCPDeviceProvider> provider;
+ int called = 0;
+ bridge->set_tcp_provider_callback_for_test(
+ base::Bind(assign_from_callback, &provider, &called));
+
+ EXPECT_NE(nullptr, provider);
+
+ service->SetBoolean(prefs::kDevToolsDiscoverTCPTargetsEnabled, true);
+
+ EXPECT_NE(nullptr, provider);
+ EXPECT_STREQ("localhost:9222, localhost:9229",
+ AllTargetsString(provider).c_str());
+
+ service->SetBoolean(prefs::kDevToolsDiscoverTCPTargetsEnabled, false);
+
+ EXPECT_EQ(nullptr, provider);
+}
diff --git a/chromium/chrome/browser/devtools/device/devtools_device_discovery.cc b/chromium/chrome/browser/devtools/device/devtools_device_discovery.cc
new file mode 100644
index 00000000000..fb9901e38d8
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/devtools_device_discovery.cc
@@ -0,0 +1,626 @@
+// 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/devtools/device/devtools_device_discovery.h"
+
+#include "base/bind.h"
+#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/user_metrics.h"
+#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/values.h"
+#include "chrome/browser/devtools/devtools_protocol.h"
+#include "chrome/browser/devtools/devtools_window.h"
+#include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/devtools_external_agent_proxy.h"
+#include "content/public/browser/devtools_external_agent_proxy_delegate.h"
+#include "net/base/escape.h"
+
+using content::BrowserThread;
+using content::DevToolsAgentHost;
+using RemoteBrowser = DevToolsDeviceDiscovery::RemoteBrowser;
+using RemoteDevice = DevToolsDeviceDiscovery::RemoteDevice;
+using RemotePage = DevToolsDeviceDiscovery::RemotePage;
+
+namespace {
+
+const char kPageListRequest[] = "/json";
+const char kVersionRequest[] = "/json/version";
+const char kClosePageRequest[] = "/json/close/%s";
+const char kActivatePageRequest[] = "/json/activate/%s";
+const char kBrowserTargetSocket[] = "/devtools/browser";
+const int kPollingIntervalMs = 1000;
+
+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,
+ FROM_HERE,
+ task,
+ base::TimeDelta::FromMilliseconds(kPollingIntervalMs));
+}
+
+// ProtocolCommand ------------------------------------------------------------
+
+class ProtocolCommand
+ : public AndroidDeviceManager::AndroidWebSocket::Delegate {
+ public:
+ ProtocolCommand(
+ scoped_refptr<AndroidDeviceManager::Device> device,
+ const std::string& socket,
+ const std::string& target_path,
+ const std::string& command,
+ const base::Closure callback);
+
+ private:
+ void OnSocketOpened() override;
+ void OnFrameRead(const std::string& message) override;
+ void OnSocketClosed() override;
+ ~ProtocolCommand() override;
+
+ const std::string command_;
+ const base::Closure callback_;
+ std::unique_ptr<AndroidDeviceManager::AndroidWebSocket> web_socket_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProtocolCommand);
+};
+
+ProtocolCommand::ProtocolCommand(
+ scoped_refptr<AndroidDeviceManager::Device> device,
+ const std::string& socket,
+ const std::string& target_path,
+ const std::string& command,
+ const base::Closure callback)
+ : command_(command),
+ callback_(callback),
+ web_socket_(device->CreateWebSocket(socket, target_path, this)) {
+}
+
+void ProtocolCommand::OnSocketOpened() {
+ web_socket_->SendFrame(command_);
+}
+
+void ProtocolCommand::OnFrameRead(const std::string& message) {
+ delete this;
+}
+
+void ProtocolCommand::OnSocketClosed() {
+ delete this;
+}
+
+ProtocolCommand::~ProtocolCommand() {
+ if (!callback_.is_null())
+ callback_.Run();
+}
+
+// AgentHostDelegate ----------------------------------------------------------
+
+class AgentHostDelegate
+ : public content::DevToolsExternalAgentProxyDelegate,
+ public AndroidDeviceManager::AndroidWebSocket::Delegate {
+ public:
+ static scoped_refptr<content::DevToolsAgentHost> GetOrCreateAgentHost(
+ scoped_refptr<AndroidDeviceManager::Device> device,
+ const std::string& browser_id,
+ const std::string& local_id,
+ const std::string& target_path,
+ const std::string& type,
+ base::DictionaryValue* value);
+ ~AgentHostDelegate() override;
+
+ private:
+ AgentHostDelegate(
+ scoped_refptr<AndroidDeviceManager::Device> device,
+ const std::string& browser_id,
+ const std::string& local_id,
+ const std::string& target_path,
+ const std::string& type,
+ base::DictionaryValue* value);
+ // DevToolsExternalAgentProxyDelegate overrides.
+ void Attach(content::DevToolsExternalAgentProxy* proxy) override;
+ void Detach() override;
+ std::string GetType() override;
+ std::string GetTitle() override;
+ std::string GetDescription() override;
+ GURL GetURL() override;
+ GURL GetFaviconURL() override;
+ std::string GetFrontendURL() override;
+ bool Activate() override;
+ void Reload() override;
+ bool Close() override;
+ base::TimeTicks GetLastActivityTime() override;
+ void SendMessageToBackend(const std::string& message) override;
+
+ void OnSocketOpened() override;
+ void OnFrameRead(const std::string& message) override;
+ void OnSocketClosed() override;
+
+ void SendProtocolCommand(const std::string& target_path,
+ const std::string& method,
+ std::unique_ptr<base::DictionaryValue> params,
+ const base::Closure callback);
+
+ scoped_refptr<AndroidDeviceManager::Device> device_;
+ std::string browser_id_;
+ std::string local_id_;
+ std::string target_path_;
+ std::string remote_type_;
+ std::string remote_id_;
+ std::string frontend_url_;
+ std::string title_;
+ std::string description_;
+ GURL url_;
+ GURL favicon_url_;
+ bool socket_opened_;
+ std::vector<std::string> pending_messages_;
+ std::unique_ptr<AndroidDeviceManager::AndroidWebSocket> web_socket_;
+ content::DevToolsAgentHost* agent_host_;
+ content::DevToolsExternalAgentProxy* proxy_;
+ DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate);
+};
+
+static std::string GetStringProperty(base::DictionaryValue* value,
+ const std::string& name) {
+ std::string result;
+ value->GetString(name, &result);
+ return result;
+}
+
+static std::string BuildUniqueTargetId(
+ const std::string& serial,
+ const std::string& browser_id,
+ base::DictionaryValue* value) {
+ return base::StringPrintf("%s:%s:%s", serial.c_str(),
+ browser_id.c_str(), GetStringProperty(value, "id").c_str());
+}
+
+static std::string GetFrontendURLFromValue(base::DictionaryValue* value) {
+ std::string frontend_url = GetStringProperty(value, "devtoolsFrontendUrl");
+ size_t ws_param = frontend_url.find("?ws");
+ if (ws_param != std::string::npos)
+ frontend_url = frontend_url.substr(0, ws_param);
+ if (base::StartsWith(frontend_url, "http:", base::CompareCase::SENSITIVE))
+ frontend_url = "https:" + frontend_url.substr(5);
+ return frontend_url;
+}
+
+static std::string GetTargetPath(base::DictionaryValue* value) {
+ std::string target_path = GetStringProperty(value, "webSocketDebuggerUrl");
+
+ if (base::StartsWith(target_path, "ws://", base::CompareCase::SENSITIVE)) {
+ size_t pos = target_path.find("/", 5);
+ if (pos == std::string::npos)
+ pos = 5;
+ target_path = target_path.substr(pos);
+ } else {
+ target_path = std::string();
+ }
+ return target_path;
+}
+
+// static
+scoped_refptr<content::DevToolsAgentHost>
+AgentHostDelegate::GetOrCreateAgentHost(
+ scoped_refptr<AndroidDeviceManager::Device> device,
+ const std::string& browser_id,
+ const std::string& local_id,
+ const std::string& target_path,
+ const std::string& type,
+ base::DictionaryValue* value) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ scoped_refptr<DevToolsAgentHost> result =
+ DevToolsAgentHost::GetForId(local_id);
+ if (result)
+ return result;
+
+ AgentHostDelegate* delegate = new AgentHostDelegate(
+ device, browser_id, local_id, target_path, type, value);
+ result = content::DevToolsAgentHost::Forward(
+ local_id, base::WrapUnique(delegate));
+ delegate->agent_host_ = result.get();
+ return result;
+}
+
+AgentHostDelegate::AgentHostDelegate(
+ scoped_refptr<AndroidDeviceManager::Device> device,
+ const std::string& browser_id,
+ const std::string& local_id,
+ const std::string& target_path,
+ const std::string& type,
+ base::DictionaryValue* value)
+ : device_(device),
+ browser_id_(browser_id),
+ local_id_(local_id),
+ target_path_(target_path),
+ remote_type_(type),
+ remote_id_(value ? GetStringProperty(value, "id") : ""),
+ frontend_url_(value ? GetFrontendURLFromValue(value) : ""),
+ title_(value ? base::UTF16ToUTF8(net::UnescapeForHTML(base::UTF8ToUTF16(
+ GetStringProperty(value, "title")))) : ""),
+ description_(value ? GetStringProperty(value, "description") : ""),
+ url_(GURL(value ? GetStringProperty(value, "url") : "")),
+ favicon_url_(GURL(value ? GetStringProperty(value, "faviconUrl") : "")),
+ socket_opened_(false),
+ agent_host_(nullptr),
+ proxy_(nullptr) {
+}
+
+AgentHostDelegate::~AgentHostDelegate() {
+}
+
+void AgentHostDelegate::Attach(content::DevToolsExternalAgentProxy* proxy) {
+ proxy_ = proxy;
+ base::RecordAction(
+ base::StartsWith(browser_id_, kWebViewSocketPrefix,
+ base::CompareCase::SENSITIVE)
+ ? base::UserMetricsAction("DevTools_InspectAndroidWebView")
+ : base::UserMetricsAction("DevTools_InspectAndroidPage"));
+ web_socket_.reset(
+ device_->CreateWebSocket(browser_id_, target_path_, this));
+}
+
+void AgentHostDelegate::Detach() {
+ web_socket_.reset();
+ proxy_ = nullptr;
+}
+
+std::string AgentHostDelegate::GetType() {
+ return remote_type_;
+}
+
+std::string AgentHostDelegate::GetTitle() {
+ return title_;
+}
+
+std::string AgentHostDelegate::GetDescription() {
+ return description_;
+}
+
+GURL AgentHostDelegate::GetURL() {
+ return url_;
+}
+
+GURL AgentHostDelegate::GetFaviconURL() {
+ return favicon_url_;
+}
+
+std::string AgentHostDelegate::GetFrontendURL() {
+ return frontend_url_;
+}
+
+bool AgentHostDelegate::Activate() {
+ std::string request = base::StringPrintf(kActivatePageRequest,
+ remote_id_.c_str());
+ device_->SendJsonRequest(browser_id_, request, base::Bind(&NoOp));
+ return true;
+}
+
+void AgentHostDelegate::Reload() {
+ SendProtocolCommand(target_path_, kPageReloadCommand, nullptr,
+ base::Closure());
+}
+
+bool AgentHostDelegate::Close() {
+ std::string request = base::StringPrintf(kClosePageRequest,
+ remote_id_.c_str());
+ device_->SendJsonRequest(browser_id_, request, base::Bind(&NoOp));
+ return true;
+}
+
+base::TimeTicks AgentHostDelegate::GetLastActivityTime() {
+ return base::TimeTicks();
+}
+
+void AgentHostDelegate::SendMessageToBackend(const std::string& message) {
+ // We could have detached due to physical connection being closed.
+ if (!proxy_)
+ return;
+ if (socket_opened_)
+ web_socket_->SendFrame(message);
+ else
+ pending_messages_.push_back(message);
+}
+
+void AgentHostDelegate::OnSocketOpened() {
+ socket_opened_ = true;
+ for (std::vector<std::string>::iterator it = pending_messages_.begin();
+ it != pending_messages_.end(); ++it) {
+ SendMessageToBackend(*it);
+ }
+ pending_messages_.clear();
+}
+
+void AgentHostDelegate::OnFrameRead(const std::string& message) {
+ if (proxy_)
+ proxy_->DispatchOnClientHost(message);
+}
+
+void AgentHostDelegate::OnSocketClosed() {
+ content::DevToolsExternalAgentProxy* proxy = proxy_;
+ if (proxy) {
+ std::string message = "{ \"method\": \"Inspector.detached\", "
+ "\"params\": { \"reason\": \"Connection lost.\"} }";
+ proxy->DispatchOnClientHost(message);
+ Detach();
+ proxy->ConnectionClosed(); // May delete |this|.
+ }
+}
+
+void AgentHostDelegate::SendProtocolCommand(
+ const std::string& target_path,
+ const std::string& method,
+ std::unique_ptr<base::DictionaryValue> params,
+ const base::Closure callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (target_path.empty())
+ return;
+ new ProtocolCommand(
+ device_, browser_id_, target_path,
+ DevToolsProtocol::SerializeCommand(1, method, std::move(params)),
+ callback);
+}
+
+} // namespace
+
+// DevToolsDeviceDiscovery::DiscoveryRequest ----------------------------------
+
+class DevToolsDeviceDiscovery::DiscoveryRequest
+ : public base::RefCountedThreadSafe<DiscoveryRequest,
+ BrowserThread::DeleteOnUIThread> {
+ public:
+ DiscoveryRequest(AndroidDeviceManager* device_manager,
+ const DevToolsDeviceDiscovery::DeviceListCallback& callback);
+ private:
+ friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
+ friend class base::DeleteHelper<DiscoveryRequest>;
+ virtual ~DiscoveryRequest();
+
+ void ReceivedDevices(const AndroidDeviceManager::Devices& devices);
+ void ReceivedDeviceInfo(scoped_refptr<AndroidDeviceManager::Device> device,
+ const AndroidDeviceManager::DeviceInfo& device_info);
+ void ReceivedVersion(scoped_refptr<RemoteBrowser>,
+ int result,
+ const std::string& response);
+ void ReceivedPages(scoped_refptr<AndroidDeviceManager::Device> device,
+ scoped_refptr<RemoteBrowser>,
+ int result,
+ const std::string& response);
+
+ DevToolsDeviceDiscovery::DeviceListCallback callback_;
+ DevToolsDeviceDiscovery::CompleteDevices complete_devices_;
+};
+
+DevToolsDeviceDiscovery::DiscoveryRequest::DiscoveryRequest(
+ AndroidDeviceManager* device_manager,
+ const DevToolsDeviceDiscovery::DeviceListCallback& callback)
+ : callback_(callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ device_manager->QueryDevices(
+ base::Bind(&DiscoveryRequest::ReceivedDevices, this));
+}
+
+DevToolsDeviceDiscovery::DiscoveryRequest::~DiscoveryRequest() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ callback_.Run(complete_devices_);
+}
+
+void DevToolsDeviceDiscovery::DiscoveryRequest::ReceivedDevices(
+ const AndroidDeviceManager::Devices& devices) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ for (const auto& device : devices) {
+ device->QueryDeviceInfo(
+ base::Bind(&DiscoveryRequest::ReceivedDeviceInfo, this, device));
+ }
+}
+
+void DevToolsDeviceDiscovery::DiscoveryRequest::ReceivedDeviceInfo(
+ scoped_refptr<AndroidDeviceManager::Device> device,
+ const AndroidDeviceManager::DeviceInfo& device_info) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ scoped_refptr<RemoteDevice> remote_device =
+ new RemoteDevice(device->serial(), device_info);
+ complete_devices_.push_back(std::make_pair(device, remote_device));
+ for (RemoteBrowsers::iterator it = remote_device->browsers().begin();
+ it != remote_device->browsers().end(); ++it) {
+ device->SendJsonRequest(
+ (*it)->socket(),
+ kVersionRequest,
+ base::Bind(&DiscoveryRequest::ReceivedVersion, this, *it));
+ device->SendJsonRequest(
+ (*it)->socket(),
+ kPageListRequest,
+ base::Bind(&DiscoveryRequest::ReceivedPages, this, device, *it));
+ }
+}
+
+void DevToolsDeviceDiscovery::DiscoveryRequest::ReceivedVersion(
+ scoped_refptr<RemoteBrowser> browser,
+ int result,
+ const std::string& response) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (result < 0)
+ return;
+ // Parse version, append to package name if available,
+ std::unique_ptr<base::Value> value = base::JSONReader::Read(response);
+ base::DictionaryValue* dict;
+ if (value && value->GetAsDictionary(&dict)) {
+ std::string browser_name;
+ if (dict->GetString("Browser", &browser_name)) {
+ std::vector<std::string> parts = base::SplitString(
+ browser_name, "/", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ if (parts.size() == 2)
+ browser->version_ = parts[1];
+ else
+ browser->version_ = browser_name;
+ }
+ std::string package;
+ if (dict->GetString("Android-Package", &package)) {
+ browser->display_name_ =
+ AndroidDeviceManager::GetBrowserName(browser->socket(), package);
+ }
+ }
+}
+
+void DevToolsDeviceDiscovery::DiscoveryRequest::ReceivedPages(
+ scoped_refptr<AndroidDeviceManager::Device> device,
+ scoped_refptr<RemoteBrowser> browser,
+ int result,
+ const std::string& response) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (result < 0)
+ return;
+ std::unique_ptr<base::Value> value = base::JSONReader::Read(response);
+ base::ListValue* list_value;
+ if (value && value->GetAsList(&list_value)) {
+ for (const auto& page_value : *list_value) {
+ const base::DictionaryValue* dict;
+ if (page_value.GetAsDictionary(&dict))
+ browser->pages_.push_back(
+ new RemotePage(device, browser->browser_id_, *dict));
+ }
+ }
+}
+
+// DevToolsDeviceDiscovery::RemotePage ----------------------------------------
+
+DevToolsDeviceDiscovery::RemotePage::RemotePage(
+ scoped_refptr<AndroidDeviceManager::Device> device,
+ const std::string& browser_id,
+ const base::DictionaryValue& dict)
+ : device_(device),
+ browser_id_(browser_id),
+ dict_(dict.DeepCopy()) {
+}
+
+DevToolsDeviceDiscovery::RemotePage::~RemotePage() {
+}
+
+scoped_refptr<content::DevToolsAgentHost>
+DevToolsDeviceDiscovery::RemotePage::CreateTarget() {
+ std::string local_id = BuildUniqueTargetId(device_->serial(),
+ browser_id_,
+ dict_.get());
+ std::string target_path = GetTargetPath(dict_.get());
+ std::string type = GetStringProperty(dict_.get(), "type");
+ agent_host_ = AgentHostDelegate::GetOrCreateAgentHost(
+ device_, browser_id_, local_id, target_path, type, dict_.get());
+ return agent_host_;
+}
+
+// DevToolsDeviceDiscovery::RemoteBrowser -------------------------------------
+
+DevToolsDeviceDiscovery::RemoteBrowser::RemoteBrowser(
+ const std::string& serial,
+ const AndroidDeviceManager::BrowserInfo& browser_info)
+ : serial_(serial),
+ browser_id_(browser_info.socket_name),
+ display_name_(browser_info.display_name),
+ user_(browser_info.user),
+ type_(browser_info.type) {
+}
+
+bool DevToolsDeviceDiscovery::RemoteBrowser::IsChrome() {
+ return type_ == AndroidDeviceManager::BrowserInfo::kTypeChrome;
+}
+
+std::string DevToolsDeviceDiscovery::RemoteBrowser::GetId() {
+ return serial() + ":" + socket();
+}
+
+DevToolsDeviceDiscovery::RemoteBrowser::ParsedVersion
+DevToolsDeviceDiscovery::RemoteBrowser::GetParsedVersion() {
+ ParsedVersion result;
+ for (const base::StringPiece& part :
+ base::SplitStringPiece(
+ version_, ".", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
+ int value = 0;
+ base::StringToInt(part, &value);
+ result.push_back(value);
+ }
+ return result;
+}
+
+DevToolsDeviceDiscovery::RemoteBrowser::~RemoteBrowser() {
+}
+
+// DevToolsDeviceDiscovery::RemoteDevice --------------------------------------
+
+DevToolsDeviceDiscovery::RemoteDevice::RemoteDevice(
+ const std::string& serial,
+ const AndroidDeviceManager::DeviceInfo& device_info)
+ : serial_(serial),
+ model_(device_info.model),
+ connected_(device_info.connected),
+ screen_size_(device_info.screen_size) {
+ for (std::vector<AndroidDeviceManager::BrowserInfo>::const_iterator it =
+ device_info.browser_info.begin();
+ it != device_info.browser_info.end();
+ ++it) {
+ browsers_.push_back(new RemoteBrowser(serial, *it));
+ }
+}
+
+DevToolsDeviceDiscovery::RemoteDevice::~RemoteDevice() {
+}
+
+// DevToolsDeviceDiscovery ----------------------------------------------------
+
+DevToolsDeviceDiscovery::DevToolsDeviceDiscovery(
+ AndroidDeviceManager* device_manager,
+ const DeviceListCallback& callback)
+ : device_manager_(device_manager),
+ callback_(callback),
+ task_scheduler_(base::Bind(&ScheduleTaskDefault)),
+ weak_factory_(this) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ RequestDeviceList();
+}
+
+DevToolsDeviceDiscovery::~DevToolsDeviceDiscovery() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+void DevToolsDeviceDiscovery::SetScheduler(
+ base::Callback<void(const base::Closure&)> scheduler) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ task_scheduler_ = scheduler;
+}
+
+// static
+scoped_refptr<content::DevToolsAgentHost>
+DevToolsDeviceDiscovery::CreateBrowserAgentHost(
+ scoped_refptr<AndroidDeviceManager::Device> device,
+ scoped_refptr<RemoteBrowser> browser) {
+ return AgentHostDelegate::GetOrCreateAgentHost(
+ device,
+ browser->browser_id_,
+ "adb:" + browser->serial() + ":" + browser->socket(),
+ kBrowserTargetSocket, DevToolsAgentHost::kTypeBrowser, nullptr);
+}
+
+void DevToolsDeviceDiscovery::RequestDeviceList() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ new DiscoveryRequest(
+ device_manager_,
+ base::Bind(&DevToolsDeviceDiscovery::ReceivedDeviceList,
+ weak_factory_.GetWeakPtr()));
+}
+
+void DevToolsDeviceDiscovery::ReceivedDeviceList(
+ const CompleteDevices& complete_devices) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ task_scheduler_.Run(base::Bind(&DevToolsDeviceDiscovery::RequestDeviceList,
+ weak_factory_.GetWeakPtr()));
+ // |callback_| should be run last as it may destroy |this|.
+ callback_.Run(complete_devices);
+}
diff --git a/chromium/chrome/browser/devtools/device/devtools_device_discovery.h b/chromium/chrome/browser/devtools/device/devtools_device_discovery.h
new file mode 100644
index 00000000000..6a2e66dc617
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/devtools_device_discovery.h
@@ -0,0 +1,145 @@
+// 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_DEVTOOLS_DEVICE_DEVTOOLS_DEVICE_DISCOVERY_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVICE_DEVTOOLS_DEVICE_DISCOVERY_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/devtools/device/android_device_manager.h"
+#include "content/public/browser/devtools_agent_host.h"
+
+class DevToolsDeviceDiscovery {
+ public:
+ class RemotePage : public base::RefCountedThreadSafe<RemotePage> {
+ public:
+ scoped_refptr<AndroidDeviceManager::Device> device() { return device_; }
+ const std::string& socket() { return browser_id_; }
+ const std::string& frontend_url() { return frontend_url_; }
+ scoped_refptr<content::DevToolsAgentHost> CreateTarget();
+
+ private:
+ friend class base::RefCountedThreadSafe<RemotePage>;
+ friend class DevToolsDeviceDiscovery;
+
+ RemotePage(scoped_refptr<AndroidDeviceManager::Device> device,
+ const std::string& browser_id,
+ const base::DictionaryValue& dict);
+
+ virtual ~RemotePage();
+
+ scoped_refptr<AndroidDeviceManager::Device> device_;
+ std::string browser_id_;
+ std::string frontend_url_;
+ std::unique_ptr<base::DictionaryValue> dict_;
+ scoped_refptr<content::DevToolsAgentHost> agent_host_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemotePage);
+ };
+
+ using RemotePages = std::vector<scoped_refptr<RemotePage>>;
+
+ class RemoteBrowser : public base::RefCountedThreadSafe<RemoteBrowser> {
+ public:
+ const std::string& serial() { return serial_; }
+ const std::string& socket() { return browser_id_; }
+ const std::string& display_name() { return display_name_; }
+ const std::string& user() { return user_; }
+ const std::string& version() { return version_; }
+ const RemotePages& pages() { return pages_; }
+
+ bool IsChrome();
+ std::string GetId();
+
+ using ParsedVersion = std::vector<int>;
+ ParsedVersion GetParsedVersion();
+
+ private:
+ friend class base::RefCountedThreadSafe<RemoteBrowser>;
+ friend class DevToolsDeviceDiscovery;
+
+ RemoteBrowser(const std::string& serial,
+ const AndroidDeviceManager::BrowserInfo& browser_info);
+
+ virtual ~RemoteBrowser();
+
+ std::string serial_;
+ std::string browser_id_;
+ std::string display_name_;
+ std::string user_;
+ AndroidDeviceManager::BrowserInfo::Type type_;
+ std::string version_;
+ RemotePages pages_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemoteBrowser);
+ };
+
+ using RemoteBrowsers = std::vector<scoped_refptr<RemoteBrowser>>;
+
+ class RemoteDevice : public base::RefCountedThreadSafe<RemoteDevice> {
+ public:
+ std::string serial() { return serial_; }
+ std::string model() { return model_; }
+ bool is_connected() { return connected_; }
+ RemoteBrowsers& browsers() { return browsers_; }
+ gfx::Size screen_size() { return screen_size_; }
+
+ private:
+ friend class base::RefCountedThreadSafe<RemoteDevice>;
+ friend class DevToolsDeviceDiscovery;
+
+ RemoteDevice(const std::string& serial,
+ const AndroidDeviceManager::DeviceInfo& device_info);
+
+ virtual ~RemoteDevice();
+
+ std::string serial_;
+ std::string model_;
+ bool connected_;
+ RemoteBrowsers browsers_;
+ gfx::Size screen_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemoteDevice);
+ };
+
+ using RemoteDevices = std::vector<scoped_refptr<RemoteDevice>>;
+
+ using CompleteDevice =
+ std::pair<scoped_refptr<AndroidDeviceManager::Device>,
+ scoped_refptr<RemoteDevice>>;
+ using CompleteDevices = std::vector<CompleteDevice>;
+ using DeviceListCallback = base::Callback<void(const CompleteDevices&)>;
+
+ DevToolsDeviceDiscovery(
+ AndroidDeviceManager* device_manager,
+ const DeviceListCallback& callback);
+ ~DevToolsDeviceDiscovery();
+
+ void SetScheduler(base::Callback<void(const base::Closure&)> scheduler);
+
+ static scoped_refptr<content::DevToolsAgentHost> CreateBrowserAgentHost(
+ scoped_refptr<AndroidDeviceManager::Device> device,
+ scoped_refptr<RemoteBrowser> browser);
+
+ private:
+ class DiscoveryRequest;
+
+ void RequestDeviceList();
+ void ReceivedDeviceList(const CompleteDevices& complete_devices);
+
+ AndroidDeviceManager* device_manager_;
+ const DeviceListCallback callback_;
+ base::Callback<void(const base::Closure&)> task_scheduler_;
+ base::WeakPtrFactory<DevToolsDeviceDiscovery> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsDeviceDiscovery);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVICE_DEVTOOLS_DEVICE_DISCOVERY_H_
diff --git a/chromium/chrome/browser/devtools/device/port_forwarding_browsertest.cc b/chromium/chrome/browser/devtools/device/port_forwarding_browsertest.cc
new file mode 100644
index 00000000000..016b64b7dba
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/port_forwarding_browsertest.cc
@@ -0,0 +1,190 @@
+// 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/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/devtools/device/devtools_android_bridge.h"
+#include "chrome/browser/devtools/device/tcp_device_provider.h"
+#include "chrome/browser/devtools/remote_debugging_server.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_switches.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/prefs/pref_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+
+namespace {
+const char kPortForwardingTestPage[] = "/devtools/port_forwarding/main.html";
+
+const int kDefaultDebuggingPort = 9223;
+const int kAlternativeDebuggingPort = 9224;
+
+}
+
+class PortForwardingTest: public InProcessBrowserTest {
+ virtual int GetRemoteDebuggingPort() {
+ return kDefaultDebuggingPort;
+ }
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ InProcessBrowserTest::SetUpCommandLine(command_line);
+ command_line->AppendSwitchASCII(switches::kRemoteDebuggingPort,
+ base::IntToString(GetRemoteDebuggingPort()));
+ }
+
+ protected:
+ class Listener : public DevToolsAndroidBridge::PortForwardingListener {
+ public:
+ explicit Listener(Profile* profile)
+ : profile_(profile),
+ skip_empty_devices_(true) {
+ DevToolsAndroidBridge::Factory::GetForProfile(profile_)->
+ AddPortForwardingListener(this);
+ }
+
+ ~Listener() override {
+ DevToolsAndroidBridge::Factory::GetForProfile(profile_)->
+ RemovePortForwardingListener(this);
+ }
+
+ void PortStatusChanged(const ForwardingStatus& status) override {
+ if (status.empty() && skip_empty_devices_)
+ return;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
+ }
+
+ void set_skip_empty_devices(bool skip_empty_devices) {
+ skip_empty_devices_ = skip_empty_devices;
+ }
+
+ private:
+ Profile* profile_;
+ bool skip_empty_devices_;
+ };
+};
+
+// Flaky on all platforms. https://crbug.com/477696
+IN_PROC_BROWSER_TEST_F(PortForwardingTest,
+ DISABLED_LoadPageWithStyleAnsScript) {
+ Profile* profile = browser()->profile();
+
+ AndroidDeviceManager::DeviceProviders device_providers;
+
+ device_providers.push_back(
+ TCPDeviceProvider::CreateForLocalhost(kDefaultDebuggingPort));
+ DevToolsAndroidBridge::Factory::GetForProfile(profile)->
+ set_device_providers_for_test(device_providers);
+
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL original_url = embedded_test_server()->GetURL(kPortForwardingTestPage);
+
+ std::string forwarding_port("8000");
+ GURL forwarding_url(original_url.scheme() + "://" +
+ original_url.host() + ":" + forwarding_port + original_url.path());
+
+ PrefService* prefs = profile->GetPrefs();
+ prefs->SetBoolean(prefs::kDevToolsPortForwardingEnabled, true);
+
+ base::DictionaryValue config;
+ config.SetString(
+ forwarding_port, original_url.host() + ":" + original_url.port());
+ prefs->Set(prefs::kDevToolsPortForwardingConfig, config);
+
+ Listener wait_for_port_forwarding(profile);
+ content::RunMessageLoop();
+
+ RemoteDebuggingServer::EnableTetheringForDebug();
+
+ ui_test_utils::NavigateToURL(browser(), forwarding_url);
+
+ content::RenderViewHost* rvh = browser()->tab_strip_model()->
+ GetWebContentsAt(0)->GetRenderViewHost();
+
+ std::string result;
+ ASSERT_TRUE(
+ content::ExecuteScriptAndExtractString(
+ rvh,
+ "window.domAutomationController.send(document.title)",
+ &result));
+ ASSERT_EQ("Port forwarding test", result) << "Document has not loaded.";
+
+ ASSERT_TRUE(
+ content::ExecuteScriptAndExtractString(
+ rvh,
+ "window.domAutomationController.send(getBodyTextContent())",
+ &result));
+ ASSERT_EQ("content", result) << "Javascript has not loaded.";
+
+ ASSERT_TRUE(
+ content::ExecuteScriptAndExtractString(
+ rvh,
+ "window.domAutomationController.send(getBodyMarginLeft())",
+ &result));
+ ASSERT_EQ("100px", result) << "CSS has not loaded.";
+
+ // Test that disabling port forwarding is handled normally.
+ wait_for_port_forwarding.set_skip_empty_devices(false);
+ prefs->SetBoolean(prefs::kDevToolsPortForwardingEnabled, false);
+ content::RunMessageLoop();
+}
+
+class PortForwardingDisconnectTest : public PortForwardingTest {
+ int GetRemoteDebuggingPort() override {
+ return kAlternativeDebuggingPort;
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(PortForwardingDisconnectTest, DisconnectOnRelease) {
+ Profile* profile = browser()->profile();
+
+ AndroidDeviceManager::DeviceProviders device_providers;
+
+ scoped_refptr<TCPDeviceProvider> self_provider(
+ TCPDeviceProvider::CreateForLocalhost(kAlternativeDebuggingPort));
+ device_providers.push_back(self_provider);
+
+ DevToolsAndroidBridge::Factory::GetForProfile(profile)->
+ set_device_providers_for_test(device_providers);
+
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL original_url = embedded_test_server()->GetURL(kPortForwardingTestPage);
+
+ std::string forwarding_port("8000");
+ GURL forwarding_url(original_url.scheme() + "://" +
+ original_url.host() + ":" + forwarding_port + original_url.path());
+
+ PrefService* prefs = profile->GetPrefs();
+ prefs->SetBoolean(prefs::kDevToolsPortForwardingEnabled, true);
+
+ base::DictionaryValue config;
+ config.SetString(
+ forwarding_port, original_url.host() + ":" + original_url.port());
+ prefs->Set(prefs::kDevToolsPortForwardingConfig, config);
+
+ std::unique_ptr<Listener> wait_for_port_forwarding(new Listener(profile));
+ content::RunMessageLoop();
+
+ base::RunLoop run_loop;
+
+ self_provider->set_release_callback_for_test(
+ base::Bind(base::IgnoreResult(&base::SingleThreadTaskRunner::PostTask),
+ base::ThreadTaskRunnerHandle::Get(), FROM_HERE,
+ run_loop.QuitWhenIdleClosure()));
+ wait_for_port_forwarding.reset();
+
+ content::RunThisRunLoop(&run_loop);
+}
diff --git a/chromium/chrome/browser/devtools/device/port_forwarding_controller.cc b/chromium/chrome/browser/devtools/device/port_forwarding_controller.cc
new file mode 100644
index 00000000000..35587ac62e3
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/port_forwarding_controller.cc
@@ -0,0 +1,500 @@
+// 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/devtools/device/port_forwarding_controller.h"
+
+#include <map>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/threading/non_thread_safe.h"
+#include "chrome/browser/devtools/devtools_protocol.h"
+#include "chrome/browser/devtools/devtools_protocol_constants.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/address_list.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/dns/host_resolver.h"
+#include "net/log/net_log_source.h"
+#include "net/log/net_log_with_source.h"
+#include "net/socket/tcp_client_socket.h"
+#include "third_party/WebKit/public/public_features.h"
+
+using content::BrowserThread;
+
+namespace {
+
+const int kBufferSize = 16 * 1024;
+
+enum {
+ kStatusError = -3,
+ kStatusDisconnecting = -2,
+ kStatusConnecting = -1,
+ kStatusOK = 0,
+};
+
+namespace tethering = ::chrome::devtools::Tethering;
+
+static const char kDevToolsRemoteBrowserTarget[] = "/devtools/browser";
+
+class SocketTunnel : public base::NonThreadSafe {
+ public:
+ static void StartTunnel(const std::string& host,
+ int port,
+ int result,
+ std::unique_ptr<net::StreamSocket> socket) {
+ if (result == net::OK)
+ new SocketTunnel(std::move(socket), host, port);
+ }
+
+ private:
+ SocketTunnel(std::unique_ptr<net::StreamSocket> socket,
+ const std::string& host,
+ int port)
+ : remote_socket_(std::move(socket)),
+ pending_writes_(0),
+ pending_destruction_(false) {
+ host_resolver_ = net::HostResolver::CreateDefaultResolver(nullptr);
+ net::HostResolver::RequestInfo request_info(net::HostPortPair(host, port));
+ int result = host_resolver_->Resolve(
+ request_info, net::DEFAULT_PRIORITY, &address_list_,
+ base::Bind(&SocketTunnel::OnResolved, base::Unretained(this)),
+ &request_, net::NetLogWithSource());
+ if (result != net::ERR_IO_PENDING)
+ OnResolved(result);
+ }
+
+ void OnResolved(int result) {
+ if (result < 0) {
+ SelfDestruct();
+ return;
+ }
+
+ host_socket_.reset(new net::TCPClientSocket(address_list_, nullptr, nullptr,
+ net::NetLogSource()));
+ result = host_socket_->Connect(base::Bind(&SocketTunnel::OnConnected,
+ base::Unretained(this)));
+ if (result != net::ERR_IO_PENDING)
+ OnConnected(result);
+ }
+
+ void OnConnected(int result) {
+ if (result < 0) {
+ SelfDestruct();
+ return;
+ }
+
+ ++pending_writes_; // avoid SelfDestruct in first Pump
+ Pump(host_socket_.get(), remote_socket_.get());
+ --pending_writes_;
+ if (pending_destruction_) {
+ SelfDestruct();
+ } else {
+ Pump(remote_socket_.get(), host_socket_.get());
+ }
+ }
+
+ void Pump(net::StreamSocket* from, net::StreamSocket* to) {
+ scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kBufferSize);
+ int result = from->Read(
+ buffer.get(),
+ kBufferSize,
+ base::Bind(
+ &SocketTunnel::OnRead, base::Unretained(this), from, to, buffer));
+ if (result != net::ERR_IO_PENDING)
+ OnRead(from, to, buffer, result);
+ }
+
+ void OnRead(net::StreamSocket* from,
+ net::StreamSocket* to,
+ scoped_refptr<net::IOBuffer> buffer,
+ int result) {
+ if (result <= 0) {
+ SelfDestruct();
+ return;
+ }
+
+ int total = result;
+ scoped_refptr<net::DrainableIOBuffer> drainable =
+ new net::DrainableIOBuffer(buffer.get(), total);
+
+ ++pending_writes_;
+ result = to->Write(drainable.get(),
+ total,
+ base::Bind(&SocketTunnel::OnWritten,
+ base::Unretained(this),
+ drainable,
+ from,
+ to));
+ if (result != net::ERR_IO_PENDING)
+ OnWritten(drainable, from, to, result);
+ }
+
+ void OnWritten(scoped_refptr<net::DrainableIOBuffer> drainable,
+ net::StreamSocket* from,
+ net::StreamSocket* to,
+ int result) {
+ --pending_writes_;
+ if (result < 0) {
+ SelfDestruct();
+ return;
+ }
+
+ drainable->DidConsume(result);
+ if (drainable->BytesRemaining() > 0) {
+ ++pending_writes_;
+ result = to->Write(drainable.get(),
+ drainable->BytesRemaining(),
+ base::Bind(&SocketTunnel::OnWritten,
+ base::Unretained(this),
+ drainable,
+ from,
+ to));
+ if (result != net::ERR_IO_PENDING)
+ OnWritten(drainable, from, to, result);
+ return;
+ }
+
+ if (pending_destruction_) {
+ SelfDestruct();
+ return;
+ }
+ Pump(from, to);
+ }
+
+ void SelfDestruct() {
+ if (pending_writes_ > 0) {
+ pending_destruction_ = true;
+ return;
+ }
+ delete this;
+ }
+
+ std::unique_ptr<net::StreamSocket> remote_socket_;
+ std::unique_ptr<net::StreamSocket> host_socket_;
+ std::unique_ptr<net::HostResolver> host_resolver_;
+ std::unique_ptr<net::HostResolver::Request> request_;
+ net::AddressList address_list_;
+ int pending_writes_;
+ bool pending_destruction_;
+};
+
+} // namespace
+
+class PortForwardingController::Connection
+ : public AndroidDeviceManager::AndroidWebSocket::Delegate {
+ public:
+ Connection(Registry* registry,
+ scoped_refptr<AndroidDeviceManager::Device> device,
+ scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
+ const ForwardingMap& forwarding_map);
+ ~Connection() override;
+
+ const PortStatusMap& GetPortStatusMap();
+
+ void UpdateForwardingMap(const ForwardingMap& new_forwarding_map);
+
+ scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser() {
+ return browser_;
+ }
+
+ private:
+ friend struct content::BrowserThread::DeleteOnThread<
+ content::BrowserThread::UI>;
+ friend class base::DeleteHelper<Connection>;
+
+ typedef std::map<int, std::string> ForwardingMap;
+ typedef base::Callback<void(PortStatus)> CommandCallback;
+ typedef std::map<int, CommandCallback> CommandCallbackMap;
+
+ void SerializeChanges(const std::string& method,
+ const ForwardingMap& old_map,
+ const ForwardingMap& new_map);
+
+ void SendCommand(const std::string& method, int port);
+ bool ProcessResponse(const std::string& json);
+
+ void ProcessBindResponse(int port, PortStatus status);
+ void ProcessUnbindResponse(int port, PortStatus status);
+
+ // DevToolsAndroidBridge::AndroidWebSocket::Delegate implementation:
+ void OnSocketOpened() override;
+ void OnFrameRead(const std::string& message) override;
+ void OnSocketClosed() override;
+
+ PortForwardingController::Registry* registry_;
+ scoped_refptr<AndroidDeviceManager::Device> device_;
+ scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser_;
+ std::unique_ptr<AndroidDeviceManager::AndroidWebSocket> web_socket_;
+ int command_id_;
+ bool connected_;
+ ForwardingMap forwarding_map_;
+ CommandCallbackMap pending_responses_;
+ PortStatusMap port_status_;
+
+ DISALLOW_COPY_AND_ASSIGN(Connection);
+};
+
+PortForwardingController::Connection::Connection(
+ Registry* registry,
+ scoped_refptr<AndroidDeviceManager::Device> device,
+ scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
+ const ForwardingMap& forwarding_map)
+ : registry_(registry),
+ device_(device),
+ browser_(browser),
+ command_id_(0),
+ connected_(false),
+ forwarding_map_(forwarding_map) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ (*registry_)[device_->serial()] = this;
+ web_socket_.reset(
+ device_->CreateWebSocket(browser->socket(),
+ kDevToolsRemoteBrowserTarget, this));
+}
+
+PortForwardingController::Connection::~Connection() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(registry_->find(device_->serial()) != registry_->end());
+ registry_->erase(device_->serial());
+}
+
+void PortForwardingController::Connection::UpdateForwardingMap(
+ const ForwardingMap& new_forwarding_map) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (connected_) {
+ SerializeChanges(tethering::unbind::kName,
+ new_forwarding_map, forwarding_map_);
+ SerializeChanges(tethering::bind::kName,
+ forwarding_map_, new_forwarding_map);
+ }
+ forwarding_map_ = new_forwarding_map;
+}
+
+void PortForwardingController::Connection::SerializeChanges(
+ const std::string& method,
+ const ForwardingMap& old_map,
+ const ForwardingMap& new_map) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ for (ForwardingMap::const_iterator new_it(new_map.begin());
+ new_it != new_map.end(); ++new_it) {
+ int port = new_it->first;
+ const std::string& location = new_it->second;
+ ForwardingMap::const_iterator old_it = old_map.find(port);
+ if (old_it != old_map.end() && old_it->second == location)
+ continue; // The port points to the same location in both configs, skip.
+
+ SendCommand(method, port);
+ }
+}
+
+void PortForwardingController::Connection::SendCommand(
+ const std::string& method, int port) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ std::unique_ptr<base::DictionaryValue> params(new base::DictionaryValue);
+ if (method == tethering::bind::kName) {
+ params->SetInteger(tethering::bind::kParamPort, port);
+ } else {
+ DCHECK_EQ(tethering::unbind::kName, method);
+ params->SetInteger(tethering::unbind::kParamPort, port);
+ }
+ int id = ++command_id_;
+
+ if (method == tethering::bind::kName) {
+ pending_responses_[id] =
+ base::Bind(&Connection::ProcessBindResponse,
+ base::Unretained(this), port);
+#if BUILDFLAG(DEBUG_DEVTOOLS)
+ port_status_[port] = kStatusConnecting;
+#endif // BUILDFLAG(DEBUG_DEVTOOLS)
+ } else {
+ PortStatusMap::iterator it = port_status_.find(port);
+ if (it != port_status_.end() && it->second == kStatusError) {
+ // The bind command failed on this port, do not attempt unbind.
+ port_status_.erase(it);
+ return;
+ }
+
+ pending_responses_[id] =
+ base::Bind(&Connection::ProcessUnbindResponse,
+ base::Unretained(this), port);
+#if BUILDFLAG(DEBUG_DEVTOOLS)
+ port_status_[port] = kStatusDisconnecting;
+#endif // BUILDFLAG(DEBUG_DEVTOOLS)
+ }
+
+ web_socket_->SendFrame(
+ DevToolsProtocol::SerializeCommand(id, method, std::move(params)));
+}
+
+bool PortForwardingController::Connection::ProcessResponse(
+ const std::string& message) {
+ int id = 0;
+ int error_code = 0;
+ if (!DevToolsProtocol::ParseResponse(message, &id, &error_code))
+ return false;
+
+ CommandCallbackMap::iterator it = pending_responses_.find(id);
+ if (it == pending_responses_.end())
+ return false;
+
+ it->second.Run(error_code ? kStatusError : kStatusOK);
+ pending_responses_.erase(it);
+ return true;
+}
+
+void PortForwardingController::Connection::ProcessBindResponse(
+ int port, PortStatus status) {
+ port_status_[port] = status;
+}
+
+void PortForwardingController::Connection::ProcessUnbindResponse(
+ int port, PortStatus status) {
+ PortStatusMap::iterator it = port_status_.find(port);
+ if (it == port_status_.end())
+ return;
+ if (status == kStatusError)
+ it->second = status;
+ else
+ port_status_.erase(it);
+}
+
+const PortForwardingController::PortStatusMap&
+PortForwardingController::Connection::GetPortStatusMap() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ return port_status_;
+}
+
+void PortForwardingController::Connection::OnSocketOpened() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ connected_ = true;
+ SerializeChanges(tethering::bind::kName, ForwardingMap(), forwarding_map_);
+}
+
+void PortForwardingController::Connection::OnSocketClosed() {
+ delete this;
+}
+
+void PortForwardingController::Connection::OnFrameRead(
+ const std::string& message) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (ProcessResponse(message))
+ return;
+
+ std::string method;
+ std::unique_ptr<base::DictionaryValue> params;
+ if (!DevToolsProtocol::ParseNotification(message, &method, &params))
+ return;
+
+ if (method != tethering::accepted::kName || !params)
+ return;
+
+ int port;
+ std::string connection_id;
+ if (!params->GetInteger(tethering::accepted::kParamPort, &port) ||
+ !params->GetString(tethering::accepted::kParamConnectionId,
+ &connection_id))
+ return;
+
+ std::map<int, std::string>::iterator it = forwarding_map_.find(port);
+ if (it == forwarding_map_.end())
+ return;
+
+ std::string location = it->second;
+ std::vector<std::string> tokens = base::SplitString(
+ location, ":", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ int destination_port = 0;
+ if (tokens.size() != 2 || !base::StringToInt(tokens[1], &destination_port))
+ return;
+ std::string destination_host = tokens[0];
+
+ device_->OpenSocket(
+ connection_id.c_str(),
+ base::Bind(&SocketTunnel::StartTunnel,
+ destination_host,
+ destination_port));
+}
+
+PortForwardingController::PortForwardingController(Profile* profile)
+ : pref_service_(profile->GetPrefs()) {
+ pref_change_registrar_.Init(pref_service_);
+ base::Closure callback = base::Bind(
+ &PortForwardingController::OnPrefsChange, base::Unretained(this));
+ pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled, callback);
+ pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig, callback);
+ OnPrefsChange();
+}
+
+PortForwardingController::~PortForwardingController() {}
+
+PortForwardingController::ForwardingStatus
+PortForwardingController::DeviceListChanged(
+ const DevToolsAndroidBridge::CompleteDevices& complete_devices) {
+ ForwardingStatus status;
+ if (forwarding_map_.empty())
+ return status;
+
+ for (const auto& pair : complete_devices) {
+ scoped_refptr<AndroidDeviceManager::Device> device(pair.first);
+ scoped_refptr<DevToolsAndroidBridge::RemoteDevice> remote_device(
+ pair.second);
+ if (!remote_device->is_connected())
+ continue;
+ Registry::iterator rit = registry_.find(remote_device->serial());
+ if (rit == registry_.end()) {
+ if (remote_device->browsers().size() > 0) {
+ new Connection(&registry_, device, remote_device->browsers()[0],
+ forwarding_map_);
+ }
+ } else {
+ status.push_back(std::make_pair(rit->second->browser(),
+ rit->second->GetPortStatusMap()));
+ }
+ }
+ return status;
+}
+
+void PortForwardingController::CloseAllConnections() {
+ Registry copy(registry_);
+ for (auto& entry : copy)
+ delete entry.second;
+}
+
+void PortForwardingController::OnPrefsChange() {
+ forwarding_map_.clear();
+
+ if (pref_service_->GetBoolean(prefs::kDevToolsPortForwardingEnabled)) {
+ const base::DictionaryValue* dict =
+ pref_service_->GetDictionary(prefs::kDevToolsPortForwardingConfig);
+ for (base::DictionaryValue::Iterator it(*dict);
+ !it.IsAtEnd(); it.Advance()) {
+ int port_num;
+ std::string location;
+ if (base::StringToInt(it.key(), &port_num) &&
+ dict->GetString(it.key(), &location))
+ forwarding_map_[port_num] = location;
+ }
+ }
+
+ if (!forwarding_map_.empty())
+ UpdateConnections();
+ else
+ CloseAllConnections();
+}
+
+void PortForwardingController::UpdateConnections() {
+ for (Registry::iterator it = registry_.begin(); it != registry_.end(); ++it)
+ it->second->UpdateForwardingMap(forwarding_map_);
+}
diff --git a/chromium/chrome/browser/devtools/device/port_forwarding_controller.h b/chromium/chrome/browser/devtools/device/port_forwarding_controller.h
new file mode 100644
index 00000000000..6d9e2d59b9d
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/port_forwarding_controller.h
@@ -0,0 +1,53 @@
+// 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_DEVTOOLS_DEVICE_PORT_FORWARDING_CONTROLLER_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVICE_PORT_FORWARDING_CONTROLLER_H_
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/devtools/device/devtools_android_bridge.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/prefs/pref_change_registrar.h"
+
+class PrefService;
+class Profile;
+
+class PortForwardingController {
+ public:
+ typedef DevToolsAndroidBridge::PortStatus PortStatus;
+ typedef DevToolsAndroidBridge::PortStatusMap PortStatusMap;
+ typedef DevToolsAndroidBridge::BrowserStatus BrowserStatus;
+ typedef DevToolsAndroidBridge::ForwardingStatus ForwardingStatus;
+
+ explicit PortForwardingController(Profile* profile);
+
+ virtual ~PortForwardingController();
+
+ ForwardingStatus DeviceListChanged(
+ const DevToolsAndroidBridge::CompleteDevices& complete_devices);
+ void CloseAllConnections();
+
+ private:
+ class Connection;
+ typedef std::map<std::string, Connection*> Registry;
+
+ void OnPrefsChange();
+
+ void UpdateConnections();
+
+ PrefService* pref_service_;
+ PrefChangeRegistrar pref_change_registrar_;
+ Registry registry_;
+
+ typedef std::map<int, std::string> ForwardingMap;
+ ForwardingMap forwarding_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(PortForwardingController);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVICE_PORT_FORWARDING_CONTROLLER_H_
diff --git a/chromium/chrome/browser/devtools/device/tcp_device_provider.cc b/chromium/chrome/browser/devtools/device/tcp_device_provider.cc
new file mode 100644
index 00000000000..29d3868e3e8
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/tcp_device_provider.cc
@@ -0,0 +1,140 @@
+// 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/devtools/device/tcp_device_provider.h"
+
+#include <utility>
+
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/devtools/device/adb/adb_client_socket.h"
+#include "net/base/net_errors.h"
+#include "net/dns/host_resolver.h"
+#include "net/log/net_log_source.h"
+#include "net/log/net_log_with_source.h"
+#include "net/socket/tcp_client_socket.h"
+
+namespace {
+
+const char kDeviceModel[] = "Remote Target";
+const char kBrowserName[] = "Target";
+
+static void RunSocketCallback(
+ const AndroidDeviceManager::SocketCallback& callback,
+ std::unique_ptr<net::StreamSocket> socket,
+ int result) {
+ callback.Run(result, std::move(socket));
+}
+
+class ResolveHostAndOpenSocket final {
+ public:
+ ResolveHostAndOpenSocket(const net::HostPortPair& address,
+ const AdbClientSocket::SocketCallback& callback)
+ : callback_(callback) {
+ host_resolver_ = net::HostResolver::CreateDefaultResolver(nullptr);
+ net::HostResolver::RequestInfo request_info(address);
+ int result = host_resolver_->Resolve(
+ request_info, net::DEFAULT_PRIORITY, &address_list_,
+ base::Bind(&ResolveHostAndOpenSocket::OnResolved,
+ base::Unretained(this)),
+ &request_, net::NetLogWithSource());
+ if (result != net::ERR_IO_PENDING)
+ OnResolved(result);
+ }
+
+ private:
+ void OnResolved(int result) {
+ if (result < 0) {
+ RunSocketCallback(callback_, nullptr, result);
+ delete this;
+ return;
+ }
+ std::unique_ptr<net::StreamSocket> socket(new net::TCPClientSocket(
+ address_list_, NULL, NULL, net::NetLogSource()));
+ net::StreamSocket* socket_ptr = socket.get();
+ net::CompletionCallback on_connect =
+ base::Bind(&RunSocketCallback, callback_, base::Passed(&socket));
+ result = socket_ptr->Connect(on_connect);
+ if (result != net::ERR_IO_PENDING)
+ on_connect.Run(result);
+ delete this;
+ }
+
+ std::unique_ptr<net::HostResolver> host_resolver_;
+ std::unique_ptr<net::HostResolver::Request> request_;
+ net::AddressList address_list_;
+ AdbClientSocket::SocketCallback callback_;
+};
+
+} // namespace
+
+scoped_refptr<TCPDeviceProvider> TCPDeviceProvider::CreateForLocalhost(
+ uint16_t port) {
+ TCPDeviceProvider::HostPortSet targets;
+ targets.insert(net::HostPortPair("127.0.0.1", port));
+ return new TCPDeviceProvider(targets);
+}
+
+TCPDeviceProvider::TCPDeviceProvider(const HostPortSet& targets)
+ : targets_(targets) {
+}
+
+void TCPDeviceProvider::QueryDevices(const SerialsCallback& callback) {
+ std::vector<std::string> result;
+ for (const net::HostPortPair& target : targets_) {
+ const std::string& host = target.host();
+ if (base::ContainsValue(result, host))
+ continue;
+ result.push_back(host);
+ }
+ callback.Run(result);
+}
+
+void TCPDeviceProvider::QueryDeviceInfo(const std::string& serial,
+ const DeviceInfoCallback& callback) {
+ AndroidDeviceManager::DeviceInfo device_info;
+ device_info.model = kDeviceModel;
+ device_info.connected = true;
+
+ for (const net::HostPortPair& target : targets_) {
+ if (serial != target.host())
+ continue;
+ AndroidDeviceManager::BrowserInfo browser_info;
+ browser_info.socket_name = base::UintToString(target.port());
+ browser_info.display_name = kBrowserName;
+ browser_info.type = AndroidDeviceManager::BrowserInfo::kTypeChrome;
+
+ device_info.browser_info.push_back(browser_info);
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(callback, device_info));
+}
+
+void TCPDeviceProvider::OpenSocket(const std::string& serial,
+ const std::string& socket_name,
+ const SocketCallback& callback) {
+ // Use plain socket for remote debugging and port forwarding on Desktop
+ // (debugging purposes).
+ int port;
+ base::StringToInt(socket_name, &port);
+ net::HostPortPair host_port(serial, port);
+ new ResolveHostAndOpenSocket(host_port, callback);
+}
+
+void TCPDeviceProvider::ReleaseDevice(const std::string& serial) {
+ if (!release_callback_.is_null())
+ release_callback_.Run();
+}
+
+void TCPDeviceProvider::set_release_callback_for_test(
+ const base::Closure& callback) {
+ release_callback_ = callback;
+}
+
+TCPDeviceProvider::~TCPDeviceProvider() {
+}
diff --git a/chromium/chrome/browser/devtools/device/tcp_device_provider.h b/chromium/chrome/browser/devtools/device/tcp_device_provider.h
new file mode 100644
index 00000000000..25ce2a81cfd
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/tcp_device_provider.h
@@ -0,0 +1,46 @@
+// 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_DEVTOOLS_DEVICE_TCP_DEVICE_PROVIDER_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVICE_TCP_DEVICE_PROVIDER_H_
+
+#include <stdint.h>
+
+#include <set>
+
+#include "chrome/browser/devtools/device/android_device_manager.h"
+#include "net/base/host_port_pair.h"
+
+// Instantiate this class only in a test and/or when the DEBUG_DEVTOOLS
+// BUILDFLAG is set.
+class TCPDeviceProvider : public AndroidDeviceManager::DeviceProvider {
+ public:
+ static scoped_refptr<TCPDeviceProvider> CreateForLocalhost(uint16_t port);
+
+ using HostPortSet = std::set<net::HostPortPair>;
+ explicit TCPDeviceProvider(const HostPortSet& targets);
+
+ void QueryDevices(const SerialsCallback& callback) override;
+
+ void QueryDeviceInfo(const std::string& serial,
+ const DeviceInfoCallback& callback) override;
+
+ void OpenSocket(const std::string& serial,
+ const std::string& socket_name,
+ const SocketCallback& callback) override;
+
+ void ReleaseDevice(const std::string& serial) override;
+
+ void set_release_callback_for_test(const base::Closure& callback);
+
+ HostPortSet get_targets_for_test() { return targets_; }
+
+ private:
+ ~TCPDeviceProvider() override;
+
+ HostPortSet targets_;
+ base::Closure release_callback_;
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVICE_TCP_DEVICE_PROVIDER_H_
diff --git a/chromium/chrome/browser/devtools/device/usb/android_rsa.cc b/chromium/chrome/browser/devtools/device/usb/android_rsa.cc
new file mode 100644
index 00000000000..e9a26b14602
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/usb/android_rsa.cc
@@ -0,0 +1,280 @@
+// 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/devtools/device/usb/android_rsa.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <limits>
+#include <memory>
+
+#include "base/base64.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "components/sync_preferences/pref_service_syncable.h"
+#include "crypto/rsa_private_key.h"
+#include "crypto/signature_creator.h"
+#include "net/cert/asn1_util.h"
+
+namespace {
+
+const size_t kRSANumWords = 64;
+const size_t kBigIntSize = 1024;
+
+static const char kDummyRSAPublicKey[] =
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6OSJ64q+ZLg7VV2ojEPh5TRbYjwbT"
+ "TifSPeFIV45CHnbTWYiiIn41wrozpYizNsMWZUBjdah1N78WVhbyDrnr0bDgFp+gXjfVppa3I"
+ "gjiohEcemK3omXi3GDMK8ERhriLUKfQS842SXtQ8I+KoZtpCkGM//0h7+P+Rhm0WwdipIRMhR"
+ "8haNAeyDiiCvqJcvevv2T52vqKtS3aWz+GjaTJJLVWydEpz9WdvWeLfFVhe2ZnqwwZNa30Qoj"
+ "fsnvjaMwK2MU7uYfRBPuvLyK5QESWBpArNDd6ULl8Y+NU6kwNOVDc87OASCVEM1gw2IMi2mo2"
+ "WO5ywp0UWRiGZCkK+wOFQIDAQAB";
+
+typedef struct RSAPublicKey {
+ int len; // Length of n[] in number of uint32_t
+ uint32_t n0inv; // -1 / n[0] mod 2^32
+ uint32_t n[kRSANumWords]; // modulus as little endian array
+ uint32_t rr[kRSANumWords]; // R^2 as little endian array
+ int exponent; // 3 or 65537
+} RSAPublicKey;
+
+// http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
+// a * x + b * y = gcd(a, b) = d
+void ExtendedEuclid(uint64_t a,
+ uint64_t b,
+ uint64_t* x,
+ uint64_t* y,
+ uint64_t* d) {
+ uint64_t x1 = 0, x2 = 1, y1 = 1, y2 = 0;
+
+ while (b > 0) {
+ uint64_t q = a / b;
+ uint64_t r = a % b;
+ *x = x2 - q * x1;
+ *y = y2 - q * y1;
+ a = b;
+ b = r;
+ x2 = x1;
+ x1 = *x;
+ y2 = y1;
+ y1 = *y;
+ }
+
+ *d = a;
+ *x = x2;
+ *y = y2;
+}
+
+uint32_t ModInverse(uint64_t a, uint64_t m) {
+ uint64_t d, x, y;
+ ExtendedEuclid(a, m, &x, &y, &d);
+ if (d == 1)
+ return static_cast<uint32_t>(x);
+ return 0;
+}
+
+uint32_t* BnNew() {
+ uint32_t* result = new uint32_t[kBigIntSize];
+ memset(result, 0, kBigIntSize * sizeof(uint32_t));
+ return result;
+}
+
+void BnFree(uint32_t* a) {
+ delete[] a;
+}
+
+uint32_t* BnCopy(uint32_t* a) {
+ uint32_t* result = new uint32_t[kBigIntSize];
+ memcpy(result, a, kBigIntSize * sizeof(uint32_t));
+ return result;
+}
+
+uint32_t* BnMul(uint32_t* a, uint32_t b) {
+ uint32_t* result = BnNew();
+ uint64_t carry_over = 0;
+ for (size_t i = 0; i < kBigIntSize; ++i) {
+ carry_over += static_cast<uint64_t>(a[i]) * b;
+ result[i] = carry_over & std::numeric_limits<uint32_t>::max();
+ carry_over >>= 32;
+ }
+ return result;
+}
+
+void BnSub(uint32_t* a, uint32_t* b) {
+ int carry_over = 0;
+ for (size_t i = 0; i < kBigIntSize; ++i) {
+ int64_t sub = static_cast<int64_t>(a[i]) - b[i] - carry_over;
+ carry_over = 0;
+ if (sub < 0) {
+ carry_over = 1;
+ sub += 0x100000000LL;
+ }
+ a[i] = static_cast<uint32_t>(sub);
+ }
+}
+
+void BnLeftShift(uint32_t* a, int offset) {
+ for (int i = kBigIntSize - offset - 1; i >= 0; --i)
+ a[i + offset] = a[i];
+ for (int i = 0; i < offset; ++i)
+ a[i] = 0;
+}
+
+int BnCompare(uint32_t* a, uint32_t* b) {
+ for (int i = kBigIntSize - 1; i >= 0; --i) {
+ if (a[i] > b[i])
+ return 1;
+ if (a[i] < b[i])
+ return -1;
+ }
+ return 0;
+}
+
+uint64_t BnGuess(uint32_t* a, uint32_t* b, uint64_t from, uint64_t to) {
+ if (from + 1 >= to)
+ return from;
+
+ uint64_t guess = (from + to) / 2;
+ uint32_t* t = BnMul(b, static_cast<uint32_t>(guess));
+ int result = BnCompare(a, t);
+ BnFree(t);
+ if (result > 0)
+ return BnGuess(a, b, guess, to);
+ if (result < 0)
+ return BnGuess(a, b, from, guess);
+ return guess;
+}
+
+void BnDiv(uint32_t* a, uint32_t* b, uint32_t** pq, uint32_t** pr) {
+ if (BnCompare(a, b) < 0) {
+ if (pq)
+ *pq = BnNew();
+ if (pr)
+ *pr = BnCopy(a);
+ return;
+ }
+
+ int oa = kBigIntSize - 1;
+ int ob = kBigIntSize - 1;
+ for (; oa > 0 && !a[oa]; --oa) {}
+ for (; ob > 0 && !b[ob]; --ob) {}
+ uint32_t* q = BnNew();
+ uint32_t* ca = BnCopy(a);
+
+ int digit = a[oa] < b[ob] ? oa - ob - 1 : oa - ob;
+
+ for (; digit >= 0; --digit) {
+ uint32_t* shifted_b = BnCopy(b);
+ BnLeftShift(shifted_b, digit);
+ uint32_t value = static_cast<uint32_t>(BnGuess(
+ ca, shifted_b, 0,
+ static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + 1));
+ q[digit] = value;
+ uint32_t* t = BnMul(shifted_b, value);
+ BnSub(ca, t);
+ BnFree(t);
+ BnFree(shifted_b);
+ }
+
+ if (pq)
+ *pq = q;
+ else
+ BnFree(q);
+ if (pr)
+ *pr = ca;
+ else
+ BnFree(ca);
+}
+
+} // namespace
+
+std::unique_ptr<crypto::RSAPrivateKey> AndroidRSAPrivateKey(Profile* profile) {
+ std::string encoded_key =
+ profile->GetPrefs()->GetString(prefs::kDevToolsAdbKey);
+ std::string decoded_key;
+ std::unique_ptr<crypto::RSAPrivateKey> key;
+ if (!encoded_key.empty() && base::Base64Decode(encoded_key, &decoded_key)) {
+ std::vector<uint8_t> key_info(decoded_key.begin(), decoded_key.end());
+ key = crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_info);
+ }
+ if (!key) {
+ key = crypto::RSAPrivateKey::Create(2048);
+ std::vector<uint8_t> key_info;
+ if (!key || !key->ExportPrivateKey(&key_info))
+ return nullptr;
+
+ std::string key_string(key_info.begin(), key_info.end());
+ base::Base64Encode(key_string, &encoded_key);
+ profile->GetPrefs()->SetString(prefs::kDevToolsAdbKey,
+ encoded_key);
+ }
+ return key;
+}
+
+std::string AndroidRSAPublicKey(crypto::RSAPrivateKey* key) {
+ std::vector<uint8_t> public_key;
+ if (!key)
+ return kDummyRSAPublicKey;
+
+ key->ExportPublicKey(&public_key);
+ std::string asn1(public_key.begin(), public_key.end());
+
+ base::StringPiece pk;
+ if (!net::asn1::ExtractSubjectPublicKeyFromSPKI(asn1, &pk))
+ return kDummyRSAPublicKey;
+
+ // Skip 10 byte asn1 prefix to the modulus.
+ std::vector<uint8_t> pk_data(pk.data() + 10, pk.data() + pk.length());
+ uint32_t* n = BnNew();
+ for (size_t i = 0; i < kRSANumWords; ++i) {
+ uint32_t t = pk_data[4 * i];
+ t = t << 8;
+ t += pk_data[4 * i + 1];
+ t = t << 8;
+ t += pk_data[4 * i + 2];
+ t = t << 8;
+ t += pk_data[4 * i + 3];
+ n[kRSANumWords - i - 1] = t;
+ }
+ uint64_t n0 = n[0];
+
+ RSAPublicKey pkey;
+ pkey.len = kRSANumWords;
+ pkey.exponent = 65537; // Fixed public exponent
+ pkey.n0inv = 0 - ModInverse(n0, 0x100000000LL);
+ if (pkey.n0inv == 0)
+ return kDummyRSAPublicKey;
+
+ uint32_t* r = BnNew();
+ r[kRSANumWords * 2] = 1;
+
+ uint32_t* rr;
+ BnDiv(r, n, NULL, &rr);
+
+ for (size_t i = 0; i < kRSANumWords; ++i) {
+ pkey.n[i] = n[i];
+ pkey.rr[i] = rr[i];
+ }
+
+ BnFree(n);
+ BnFree(r);
+ BnFree(rr);
+
+ std::string output;
+ std::string input(reinterpret_cast<char*>(&pkey), sizeof(pkey));
+ base::Base64Encode(input, &output);
+ return output;
+}
+
+std::string AndroidRSASign(crypto::RSAPrivateKey* key,
+ const std::string& body) {
+ std::vector<uint8_t> digest(body.begin(), body.end());
+ std::vector<uint8_t> result;
+ if (!crypto::SignatureCreator::Sign(key, crypto::SignatureCreator::SHA1,
+ digest.data(), digest.size(), &result)) {
+ return std::string();
+ }
+ return std::string(result.begin(), result.end());
+}
diff --git a/chromium/chrome/browser/devtools/device/usb/android_rsa.h b/chromium/chrome/browser/devtools/device/usb/android_rsa.h
new file mode 100644
index 00000000000..4942589b6ce
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/usb/android_rsa.h
@@ -0,0 +1,24 @@
+// 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_DEVTOOLS_DEVICE_USB_ANDROID_RSA_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVICE_USB_ANDROID_RSA_H_
+
+#include <memory>
+#include <string>
+
+namespace crypto {
+class RSAPrivateKey;
+}
+
+class Profile;
+
+std::unique_ptr<crypto::RSAPrivateKey> AndroidRSAPrivateKey(Profile* profile);
+
+std::string AndroidRSAPublicKey(crypto::RSAPrivateKey* key);
+
+std::string AndroidRSASign(crypto::RSAPrivateKey* key,
+ const std::string& body);
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVICE_USB_ANDROID_RSA_H_
diff --git a/chromium/chrome/browser/devtools/device/usb/android_usb_browsertest.cc b/chromium/chrome/browser/devtools/device/usb/android_usb_browsertest.cc
new file mode 100644
index 00000000000..dec93dfb8bc
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/usb/android_usb_browsertest.cc
@@ -0,0 +1,790 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <unordered_map>
+#include <utility>
+
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/devtools/device/adb/mock_adb_server.h"
+#include "chrome/browser/devtools/device/devtools_android_bridge.h"
+#include "chrome/browser/devtools/device/usb/android_usb_device.h"
+#include "chrome/browser/devtools/device/usb/usb_device_provider.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/test/test_utils.h"
+#include "device/base/device_client.h"
+#include "device/usb/mock_usb_service.h"
+#include "device/usb/usb_descriptors.h"
+#include "device/usb/usb_device.h"
+#include "device/usb/usb_device_handle.h"
+#include "net/base/io_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::BrowserThread;
+using device::DeviceClient;
+using device::MockUsbService;
+using device::UsbConfigDescriptor;
+using device::UsbDevice;
+using device::UsbDeviceHandle;
+using device::UsbEndpointDescriptor;
+using device::UsbInterfaceDescriptor;
+using device::UsbService;
+using device::UsbSynchronizationType;
+using device::UsbTransferDirection;
+using device::UsbTransferStatus;
+using device::UsbTransferType;
+using device::UsbUsageType;
+
+namespace {
+
+struct NoConfigTraits {
+ static const int kClass = 0xff;
+ static const int kSubclass = 0x42;
+ static const int kProtocol = 0x1;
+ static const bool kBreaks = false;
+ static const bool kConfigured = false;
+};
+
+struct AndroidTraits {
+ static const int kClass = 0xff;
+ static const int kSubclass = 0x42;
+ static const int kProtocol = 0x1;
+ static const bool kBreaks = false;
+ static const bool kConfigured = true;
+};
+
+struct NonAndroidTraits {
+ static const int kClass = 0xf0;
+ static const int kSubclass = 0x42;
+ static const int kProtocol = 0x2;
+ static const bool kBreaks = false;
+ static const bool kConfigured = true;
+};
+
+struct BreakingAndroidTraits {
+ static const int kClass = 0xff;
+ static const int kSubclass = 0x42;
+ static const int kProtocol = 0x1;
+ static const bool kBreaks = true;
+ static const bool kConfigured = true;
+};
+
+const uint32_t kMaxPayload = 4096;
+const uint32_t kVersion = 0x01000000;
+
+const char kDeviceManufacturer[] = "Test Manufacturer";
+const char kDeviceModel[] = "Nexus 6";
+const char kDeviceSerial[] = "01498B321301A00A";
+
+template <class T>
+class MockUsbDevice;
+
+class MockLocalSocket : public MockAndroidConnection::Delegate {
+ public:
+ using Callback = base::Callback<void(int command,
+ const std::string& message)>;
+
+ MockLocalSocket(const Callback& callback,
+ const std::string& serial,
+ const std::string& command)
+ : callback_(callback),
+ connection_(new MockAndroidConnection(this, serial, command)) {
+ }
+
+ void Receive(const std::string& data) {
+ connection_->Receive(data);
+ }
+
+ private:
+ void SendSuccess(const std::string& message) override {
+ if (!message.empty())
+ callback_.Run(AdbMessage::kCommandWRTE, message);
+ }
+
+ void SendRaw(const std::string& message) override {
+ callback_.Run(AdbMessage::kCommandWRTE, message);
+ }
+
+ void Close() override {
+ callback_.Run(AdbMessage::kCommandCLSE, std::string());
+ }
+
+ Callback callback_;
+ std::unique_ptr<MockAndroidConnection> connection_;
+};
+
+template <class T>
+class MockUsbDeviceHandle : public UsbDeviceHandle {
+ public:
+ explicit MockUsbDeviceHandle(MockUsbDevice<T>* device)
+ : device_(device),
+ remaining_body_length_(0),
+ last_local_socket_(0),
+ broken_(false) {
+ }
+
+ scoped_refptr<UsbDevice> GetDevice() const override {
+ return device_;
+ }
+
+ void Close() override {
+ device_->set_open(false);
+ device_ = nullptr;
+ }
+
+ void SetConfiguration(int configuration_value,
+ const ResultCallback& callback) override {
+ NOTIMPLEMENTED();
+ }
+
+ void ClaimInterface(int interface_number,
+ const ResultCallback& callback) override {
+ bool success = false;
+ if (device_->claimed_interfaces_.find(interface_number) ==
+ device_->claimed_interfaces_.end()) {
+ device_->claimed_interfaces_.insert(interface_number);
+ success = true;
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(callback, success));
+ }
+
+ void ReleaseInterface(int interface_number,
+ const ResultCallback& callback) override {
+ bool success = false;
+ if (device_->claimed_interfaces_.find(interface_number) ==
+ device_->claimed_interfaces_.end())
+ success = false;
+
+ device_->claimed_interfaces_.erase(interface_number);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(callback, success));
+ }
+
+ void SetInterfaceAlternateSetting(int interface_number,
+ int alternate_setting,
+ const ResultCallback& callback) override {
+ NOTIMPLEMENTED();
+ }
+
+ void ResetDevice(const ResultCallback& callback) override {
+ NOTIMPLEMENTED();
+ }
+
+ void ClearHalt(uint8_t endpoint, const ResultCallback& callback) override {
+ NOTIMPLEMENTED();
+ }
+
+ // Async IO. Can be called on any thread.
+ void ControlTransfer(UsbTransferDirection direction,
+ device::UsbControlTransferType request_type,
+ device::UsbControlTransferRecipient recipient,
+ uint8_t request,
+ uint16_t value,
+ uint16_t index,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t length,
+ unsigned int timeout,
+ const TransferCallback& callback) override {}
+
+ void GenericTransfer(UsbTransferDirection direction,
+ uint8_t endpoint,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t length,
+ unsigned int timeout,
+ const TransferCallback& callback) override {
+ if (direction == device::UsbTransferDirection::OUTBOUND) {
+ if (remaining_body_length_ == 0) {
+ std::vector<uint32_t> header(6);
+ memcpy(&header[0], buffer->data(), length);
+ current_message_.reset(
+ new AdbMessage(header[0], header[1], header[2], std::string()));
+ remaining_body_length_ = header[3];
+ uint32_t magic = header[5];
+ if ((current_message_->command ^ 0xffffffff) != magic) {
+ DCHECK(false) << "Header checksum error";
+ return;
+ }
+ } else {
+ DCHECK(current_message_.get());
+ current_message_->body += std::string(buffer->data(), length);
+ remaining_body_length_ -= length;
+ }
+
+ if (remaining_body_length_ == 0) {
+ ProcessIncoming();
+ }
+
+ device::UsbTransferStatus status = broken_
+ ? UsbTransferStatus::TRANSFER_ERROR
+ : UsbTransferStatus::COMPLETED;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(callback, status, nullptr, 0));
+ ProcessQueries();
+ } else if (direction == device::UsbTransferDirection::INBOUND) {
+ queries_.push(Query(callback, buffer, length));
+ ProcessQueries();
+ }
+ }
+
+ const UsbInterfaceDescriptor* FindInterfaceByEndpoint(
+ uint8_t endpoint_address) {
+ NOTIMPLEMENTED();
+ return nullptr;
+ }
+
+ template <class D>
+ void append(D data) {
+ std::copy(reinterpret_cast<char*>(&data),
+ (reinterpret_cast<char*>(&data)) + sizeof(D),
+ std::back_inserter(output_buffer_));
+ }
+
+ // Copied from AndroidUsbDevice::Checksum
+ uint32_t Checksum(const std::string& data) {
+ unsigned char* x = (unsigned char*)data.data();
+ int count = data.length();
+ uint32_t sum = 0;
+ while (count-- > 0)
+ sum += *x++;
+ return sum;
+ }
+
+ void ProcessIncoming() {
+ DCHECK(current_message_.get());
+ switch (current_message_->command) {
+ case AdbMessage::kCommandCNXN: {
+ WriteResponse(kVersion,
+ kMaxPayload,
+ AdbMessage::kCommandCNXN,
+ "device::ro.product.name=SampleProduct;ro.product.model="
+ "SampleModel;ro.product.device=SampleDevice;");
+ break;
+ }
+ case AdbMessage::kCommandCLSE: {
+ WriteResponse(0,
+ current_message_->arg0,
+ AdbMessage::kCommandCLSE,
+ std::string());
+ local_sockets_.erase(current_message_->arg0);
+ break;
+ }
+ case AdbMessage::kCommandWRTE: {
+ if (T::kBreaks) {
+ broken_ = true;
+ return;
+ }
+ auto it = local_sockets_.find(current_message_->arg0);
+ if (it == local_sockets_.end())
+ return;
+
+ DCHECK(current_message_->arg1 != 0);
+ WriteResponse(current_message_->arg1,
+ current_message_->arg0,
+ AdbMessage::kCommandOKAY,
+ std::string());
+ it->second->Receive(current_message_->body);
+ break;
+ }
+ case AdbMessage::kCommandOPEN: {
+ DCHECK(current_message_->arg1 == 0);
+ DCHECK(current_message_->arg0 != 0);
+ std::string response;
+ WriteResponse(++last_local_socket_,
+ current_message_->arg0,
+ AdbMessage::kCommandOKAY,
+ std::string());
+ local_sockets_[current_message_->arg0] =
+ base::MakeUnique<MockLocalSocket>(
+ base::Bind(&MockUsbDeviceHandle::WriteResponse,
+ base::Unretained(this), last_local_socket_,
+ current_message_->arg0),
+ kDeviceSerial, current_message_->body.substr(
+ 0, current_message_->body.size() - 1));
+ return;
+ }
+ default: {
+ return;
+ }
+ }
+ ProcessQueries();
+ }
+
+ void WriteResponse(int arg0, int arg1, int command, const std::string& body) {
+ append(command);
+ append(arg0);
+ append(arg1);
+ bool add_zero = !body.empty() && (command != AdbMessage::kCommandWRTE);
+ append(static_cast<uint32_t>(body.size() + (add_zero ? 1 : 0)));
+ append(Checksum(body));
+ append(command ^ 0xffffffff);
+ std::copy(body.begin(), body.end(), std::back_inserter(output_buffer_));
+ if (add_zero) {
+ output_buffer_.push_back(0);
+ }
+ ProcessQueries();
+ }
+
+ void ProcessQueries() {
+ if (queries_.empty())
+ return;
+ Query query = queries_.front();
+ if (broken_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(query.callback, UsbTransferStatus::TRANSFER_ERROR,
+ nullptr, 0));
+ }
+
+ if (query.size > output_buffer_.size())
+ return;
+
+ queries_.pop();
+ std::copy(output_buffer_.begin(),
+ output_buffer_.begin() + query.size,
+ query.buffer->data());
+ output_buffer_.erase(output_buffer_.begin(),
+ output_buffer_.begin() + query.size);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(query.callback, UsbTransferStatus::COMPLETED,
+ query.buffer, query.size));
+ }
+
+ void IsochronousTransferIn(
+ uint8_t endpoint_number,
+ const std::vector<uint32_t>& packet_lengths,
+ unsigned int timeout,
+ const IsochronousTransferCallback& callback) override {}
+
+ void IsochronousTransferOut(
+ uint8_t endpoint_number,
+ scoped_refptr<net::IOBuffer> buffer,
+ const std::vector<uint32_t>& packet_lengths,
+ unsigned int timeout,
+ const IsochronousTransferCallback& callback) override {}
+
+ protected:
+ virtual ~MockUsbDeviceHandle() {}
+
+ struct Query {
+ TransferCallback callback;
+ scoped_refptr<net::IOBuffer> buffer;
+ size_t size;
+
+ Query(TransferCallback callback,
+ scoped_refptr<net::IOBuffer> buffer,
+ int size)
+ : callback(callback), buffer(buffer), size(size) {}
+ };
+
+ scoped_refptr<MockUsbDevice<T> > device_;
+ uint32_t remaining_body_length_;
+ std::unique_ptr<AdbMessage> current_message_;
+ std::vector<char> output_buffer_;
+ std::queue<Query> queries_;
+ std::unordered_map<int, std::unique_ptr<MockLocalSocket>> local_sockets_;
+ int last_local_socket_;
+ bool broken_;
+};
+
+template <class T>
+class MockUsbDevice : public UsbDevice {
+ public:
+ MockUsbDevice()
+ : UsbDevice(0x0200, // usb_version
+ 0, // device_class
+ 0, // device_subclass
+ 0, // device_protocol
+ 0, // vendor_id
+ 0, // product_id
+ 0x0100, // device_version
+ base::UTF8ToUTF16(kDeviceManufacturer),
+ base::UTF8ToUTF16(kDeviceModel),
+ base::UTF8ToUTF16(kDeviceSerial)) {
+ UsbConfigDescriptor config_desc(1, false, false, 0);
+ UsbInterfaceDescriptor interface_desc(0, 0, T::kClass, T::kSubclass,
+ T::kProtocol);
+ interface_desc.endpoints.emplace_back(0x81, 0x02, 512, 0);
+ interface_desc.endpoints.emplace_back(0x01, 0x02, 512, 0);
+ config_desc.interfaces.push_back(interface_desc);
+ descriptor_.configurations.push_back(config_desc);
+ if (T::kConfigured)
+ ActiveConfigurationChanged(1);
+ }
+
+ void Open(const OpenCallback& callback) override {
+ // While most operating systems allow multiple applications to open a
+ // device simultaneously so that they may claim separate interfaces DevTools
+ // will always be trying to claim the same interface and so multiple
+ // connections are more likely to cause problems. https://crbug.com/725320
+ EXPECT_FALSE(open_);
+ open_ = true;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(callback,
+ make_scoped_refptr(new MockUsbDeviceHandle<T>(this))));
+ }
+
+ void set_open(bool open) { open_ = open; }
+
+ bool open_ = false;
+ std::set<int> claimed_interfaces_;
+
+ protected:
+ virtual ~MockUsbDevice() {}
+};
+
+class MockUsbServiceForCheckingTraits : public MockUsbService {
+ public:
+ MockUsbServiceForCheckingTraits() : step_(0) {}
+
+ void GetDevices(const GetDevicesCallback& callback) override {
+ std::vector<scoped_refptr<UsbDevice>> devices;
+ // This switch should be kept in sync with
+ // AndroidUsbBrowserTest::DeviceCountChanged.
+ switch (step_) {
+ case 0:
+ // No devices.
+ break;
+ case 1:
+ // Android device.
+ devices.push_back(new MockUsbDevice<AndroidTraits>());
+ break;
+ case 2:
+ // Android and non-android device.
+ devices.push_back(new MockUsbDevice<AndroidTraits>());
+ devices.push_back(new MockUsbDevice<NonAndroidTraits>());
+ break;
+ case 3:
+ // Non-android device.
+ devices.push_back(new MockUsbDevice<NonAndroidTraits>());
+ break;
+ }
+ step_++;
+ callback.Run(devices);
+ }
+
+ private:
+ int step_;
+};
+
+class TestDeviceClient : public DeviceClient {
+ public:
+ explicit TestDeviceClient(std::unique_ptr<UsbService> service)
+ : DeviceClient(), usb_service_(std::move(service)) {}
+ ~TestDeviceClient() override {}
+
+ private:
+ UsbService* GetUsbService() override { return usb_service_.get(); }
+
+ std::unique_ptr<UsbService> usb_service_;
+};
+
+class DevToolsAndroidBridgeWarmUp
+ : public DevToolsAndroidBridge::DeviceCountListener {
+ public:
+ DevToolsAndroidBridgeWarmUp(base::Closure closure,
+ DevToolsAndroidBridge* adb_bridge)
+ : closure_(closure), adb_bridge_(adb_bridge) {}
+
+ void DeviceCountChanged(int count) override {
+ adb_bridge_->RemoveDeviceCountListener(this);
+ closure_.Run();
+ }
+
+ base::Closure closure_;
+ DevToolsAndroidBridge* adb_bridge_;
+};
+
+class AndroidUsbDiscoveryTest : public InProcessBrowserTest {
+ protected:
+ AndroidUsbDiscoveryTest()
+ : scheduler_invoked_(0) {
+ }
+
+ void SetUpOnMainThread() override {
+ device_client_.reset(new TestDeviceClient(CreateMockService()));
+ adb_bridge_ =
+ DevToolsAndroidBridge::Factory::GetForProfile(browser()->profile());
+ DCHECK(adb_bridge_);
+ adb_bridge_->set_task_scheduler_for_test(base::Bind(
+ &AndroidUsbDiscoveryTest::ScheduleDeviceCountRequest,
+ base::Unretained(this)));
+
+ scoped_refptr<UsbDeviceProvider> provider =
+ new UsbDeviceProvider(browser()->profile());
+
+ AndroidDeviceManager::DeviceProviders providers;
+ providers.push_back(provider);
+ adb_bridge_->set_device_providers_for_test(providers);
+ runner_ = new content::MessageLoopRunner;
+ }
+
+ void ScheduleDeviceCountRequest(const base::Closure& request) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ scheduler_invoked_++;
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, request);
+ }
+
+ virtual std::unique_ptr<MockUsbService> CreateMockService() {
+ std::unique_ptr<MockUsbService> service(new MockUsbService());
+ service->AddDevice(new MockUsbDevice<AndroidTraits>());
+ return service;
+ }
+
+ scoped_refptr<content::MessageLoopRunner> runner_;
+ std::unique_ptr<TestDeviceClient> device_client_;
+ DevToolsAndroidBridge* adb_bridge_;
+ int scheduler_invoked_;
+};
+
+class AndroidUsbCountTest : public AndroidUsbDiscoveryTest {
+ protected:
+ void SetUpOnMainThread() override {
+ AndroidUsbDiscoveryTest::SetUpOnMainThread();
+ DevToolsAndroidBridgeWarmUp warmup(runner_->QuitClosure(), adb_bridge_);
+ adb_bridge_->AddDeviceCountListener(&warmup);
+ runner_->Run();
+ runner_ = new content::MessageLoopRunner;
+ }
+};
+
+class AndroidUsbTraitsTest : public AndroidUsbDiscoveryTest {
+ protected:
+ std::unique_ptr<MockUsbService> CreateMockService() override {
+ return base::MakeUnique<MockUsbServiceForCheckingTraits>();
+ }
+};
+
+class AndroidBreakingUsbTest : public AndroidUsbDiscoveryTest {
+ protected:
+ std::unique_ptr<MockUsbService> CreateMockService() override {
+ std::unique_ptr<MockUsbService> service(new MockUsbService());
+ service->AddDevice(new MockUsbDevice<BreakingAndroidTraits>());
+ return service;
+ }
+};
+
+class AndroidNoConfigUsbTest : public AndroidUsbDiscoveryTest {
+ protected:
+ std::unique_ptr<MockUsbService> CreateMockService() override {
+ std::unique_ptr<MockUsbService> service(new MockUsbService());
+ service->AddDevice(new MockUsbDevice<AndroidTraits>());
+ service->AddDevice(new MockUsbDevice<NoConfigTraits>());
+ return service;
+ }
+};
+
+class MockListListener : public DevToolsAndroidBridge::DeviceListListener {
+ public:
+ MockListListener(DevToolsAndroidBridge* adb_bridge,
+ const base::Closure& callback)
+ : adb_bridge_(adb_bridge),
+ callback_(callback) {
+ }
+
+ void DeviceListChanged(
+ const DevToolsAndroidBridge::RemoteDevices& devices) override {
+ if (devices.size() > 0) {
+ for (const auto& device : devices) {
+ if (device->is_connected()) {
+ ASSERT_EQ(kDeviceModel, device->model());
+ ASSERT_EQ(kDeviceSerial, device->serial());
+ adb_bridge_->RemoveDeviceListListener(this);
+ callback_.Run();
+ break;
+ }
+ }
+ }
+ }
+
+ DevToolsAndroidBridge* adb_bridge_;
+ base::Closure callback_;
+};
+
+class MockCountListener : public DevToolsAndroidBridge::DeviceCountListener {
+ public:
+ explicit MockCountListener(DevToolsAndroidBridge* adb_bridge)
+ : adb_bridge_(adb_bridge), invoked_(0) {}
+
+ void DeviceCountChanged(int count) override {
+ ++invoked_;
+ adb_bridge_->RemoveDeviceCountListener(this);
+ Shutdown();
+ }
+
+ void Shutdown() { base::MessageLoop::current()->QuitWhenIdle(); }
+
+ DevToolsAndroidBridge* adb_bridge_;
+ int invoked_;
+};
+
+class MockCountListenerWithReAdd : public MockCountListener {
+ public:
+ explicit MockCountListenerWithReAdd(
+ DevToolsAndroidBridge* adb_bridge)
+ : MockCountListener(adb_bridge),
+ readd_count_(2) {
+ }
+
+ void DeviceCountChanged(int count) override {
+ ++invoked_;
+ adb_bridge_->RemoveDeviceCountListener(this);
+ if (readd_count_ > 0) {
+ readd_count_--;
+ adb_bridge_->AddDeviceCountListener(this);
+ adb_bridge_->RemoveDeviceCountListener(this);
+ adb_bridge_->AddDeviceCountListener(this);
+ } else {
+ Shutdown();
+ }
+ }
+
+ int readd_count_;
+};
+
+class MockCountListenerWithReAddWhileQueued : public MockCountListener {
+ public:
+ MockCountListenerWithReAddWhileQueued(
+ DevToolsAndroidBridge* adb_bridge)
+ : MockCountListener(adb_bridge),
+ readded_(false) {
+ }
+
+ void DeviceCountChanged(int count) override {
+ ++invoked_;
+ if (!readded_) {
+ readded_ = true;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&MockCountListenerWithReAddWhileQueued::ReAdd,
+ base::Unretained(this)));
+ } else {
+ adb_bridge_->RemoveDeviceCountListener(this);
+ Shutdown();
+ }
+ }
+
+ void ReAdd() {
+ adb_bridge_->RemoveDeviceCountListener(this);
+ adb_bridge_->AddDeviceCountListener(this);
+ }
+
+ bool readded_;
+};
+
+class MockCountListenerForCheckingTraits : public MockCountListener {
+ public:
+ MockCountListenerForCheckingTraits(
+ DevToolsAndroidBridge* adb_bridge)
+ : MockCountListener(adb_bridge),
+ step_(0) {
+ }
+ void DeviceCountChanged(int count) override {
+ switch (step_) {
+ case 0:
+ // Check for 0 devices when no devices present.
+ EXPECT_EQ(0, count);
+ break;
+ case 1:
+ // Check for 1 device when only android device present.
+ EXPECT_EQ(1, count);
+ break;
+ case 2:
+ // Check for 1 device when android and non-android devices present.
+ EXPECT_EQ(1, count);
+ break;
+ case 3:
+ // Check for 0 devices when only non-android devices present.
+ EXPECT_EQ(0, count);
+ adb_bridge_->RemoveDeviceCountListener(this);
+ Shutdown();
+ break;
+ default:
+ EXPECT_TRUE(false) << "Unknown step " << step_;
+ }
+ step_++;
+ }
+
+ int step_;
+};
+
+} // namespace
+
+IN_PROC_BROWSER_TEST_F(AndroidUsbDiscoveryTest, TestDeviceDiscovery) {
+ MockListListener listener(adb_bridge_, runner_->QuitClosure());
+ adb_bridge_->AddDeviceListListener(&listener);
+ runner_->Run();
+}
+
+IN_PROC_BROWSER_TEST_F(AndroidBreakingUsbTest, TestDeviceBreaking) {
+ MockListListener listener(adb_bridge_, runner_->QuitClosure());
+ adb_bridge_->AddDeviceListListener(&listener);
+ runner_->Run();
+}
+
+IN_PROC_BROWSER_TEST_F(AndroidNoConfigUsbTest, TestDeviceNoConfig) {
+ MockListListener listener(adb_bridge_, runner_->QuitClosure());
+ adb_bridge_->AddDeviceListListener(&listener);
+ runner_->Run();
+}
+
+IN_PROC_BROWSER_TEST_F(AndroidUsbCountTest,
+ TestNoMultipleCallsRemoveInCallback) {
+ MockCountListener listener(adb_bridge_);
+ adb_bridge_->AddDeviceCountListener(&listener);
+ runner_->Run();
+ EXPECT_EQ(1, listener.invoked_);
+ EXPECT_EQ(listener.invoked_ - 1, scheduler_invoked_);
+ EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting());
+}
+
+IN_PROC_BROWSER_TEST_F(AndroidUsbCountTest,
+ TestNoMultipleCallsRemoveAddInCallback) {
+ MockCountListenerWithReAdd listener(adb_bridge_);
+ adb_bridge_->AddDeviceCountListener(&listener);
+ runner_->Run();
+ EXPECT_EQ(3, listener.invoked_);
+ EXPECT_EQ(listener.invoked_ - 1, scheduler_invoked_);
+ EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting());
+}
+
+IN_PROC_BROWSER_TEST_F(AndroidUsbCountTest,
+ TestNoMultipleCallsRemoveAddOnStart) {
+ MockCountListener listener(adb_bridge_);
+ adb_bridge_->AddDeviceCountListener(&listener);
+ adb_bridge_->RemoveDeviceCountListener(&listener);
+ adb_bridge_->AddDeviceCountListener(&listener);
+ runner_->Run();
+ EXPECT_EQ(1, listener.invoked_);
+ EXPECT_EQ(listener.invoked_ - 1, scheduler_invoked_);
+ EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting());
+}
+
+IN_PROC_BROWSER_TEST_F(AndroidUsbCountTest,
+ TestNoMultipleCallsRemoveAddWhileQueued) {
+ MockCountListenerWithReAddWhileQueued listener(adb_bridge_);
+ adb_bridge_->AddDeviceCountListener(&listener);
+ runner_->Run();
+ EXPECT_EQ(2, listener.invoked_);
+ EXPECT_EQ(listener.invoked_ - 1, scheduler_invoked_);
+ EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting());
+}
+
+IN_PROC_BROWSER_TEST_F(AndroidUsbTraitsTest, TestDeviceCounting) {
+ MockCountListenerForCheckingTraits listener(adb_bridge_);
+ adb_bridge_->AddDeviceCountListener(&listener);
+ runner_->Run();
+}
diff --git a/chromium/chrome/browser/devtools/device/usb/android_usb_device.cc b/chromium/chrome/browser/devtools/device/usb/android_usb_device.cc
new file mode 100644
index 00000000000..c8acbd19d80
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/usb/android_usb_device.cc
@@ -0,0 +1,692 @@
+// 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/devtools/device/usb/android_usb_device.h"
+
+#include <set>
+#include <utility>
+
+#include "base/barrier_closure.h"
+#include "base/base64.h"
+#include "base/lazy_instance.h"
+#include "base/memory/ptr_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/devtools/device/usb/android_rsa.h"
+#include "chrome/browser/devtools/device/usb/android_usb_socket.h"
+#include "content/public/browser/browser_thread.h"
+#include "crypto/rsa_private_key.h"
+#include "device/base/device_client.h"
+#include "device/usb/usb_descriptors.h"
+#include "device/usb/usb_device.h"
+#include "device/usb/usb_service.h"
+#include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/socket/stream_socket.h"
+
+using device::UsbConfigDescriptor;
+using device::UsbDevice;
+using device::UsbDeviceHandle;
+using device::UsbInterfaceDescriptor;
+using device::UsbEndpointDescriptor;
+using device::UsbService;
+using device::UsbTransferDirection;
+using device::UsbTransferStatus;
+using device::UsbTransferType;
+
+namespace {
+
+const size_t kHeaderSize = 24;
+
+const int kAdbClass = 0xff;
+const int kAdbSubclass = 0x42;
+const int kAdbProtocol = 0x1;
+
+const int kUsbTimeout = 0;
+
+const uint32_t kMaxPayload = 4096;
+const uint32_t kVersion = 0x01000000;
+
+static const char kHostConnectMessage[] = "host::";
+
+using content::BrowserThread;
+
+typedef std::vector<scoped_refptr<UsbDevice> > UsbDevices;
+typedef std::set<scoped_refptr<UsbDevice> > UsbDeviceSet;
+
+// Stores android wrappers around claimed usb devices on caller thread.
+base::LazyInstance<std::vector<AndroidUsbDevice*>>::Leaky g_devices =
+ LAZY_INSTANCE_INITIALIZER;
+
+// Stores the GUIDs of devices that are currently opened so that they are not
+// re-probed.
+base::LazyInstance<std::vector<std::string>>::Leaky g_open_devices =
+ LAZY_INSTANCE_INITIALIZER;
+
+bool IsAndroidInterface(const UsbInterfaceDescriptor& interface) {
+ if (interface.alternate_setting != 0 ||
+ interface.interface_class != kAdbClass ||
+ interface.interface_subclass != kAdbSubclass ||
+ interface.interface_protocol != kAdbProtocol ||
+ interface.endpoints.size() != 2) {
+ return false;
+ }
+ return true;
+}
+
+void CountAndroidDevices(const base::Callback<void(int)>& callback,
+ const UsbDevices& devices) {
+ int device_count = 0;
+ for (const scoped_refptr<UsbDevice>& device : devices) {
+ const UsbConfigDescriptor* config = device->active_configuration();
+ if (config) {
+ for (const UsbInterfaceDescriptor& iface : config->interfaces) {
+ if (IsAndroidInterface(iface)) {
+ ++device_count;
+ }
+ }
+ }
+ }
+
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::BindOnce(callback, device_count));
+}
+
+uint32_t Checksum(const std::string& data) {
+ unsigned char* x = (unsigned char*)data.data();
+ int count = data.length();
+ uint32_t sum = 0;
+ while (count-- > 0)
+ sum += *x++;
+ return sum;
+}
+
+void DumpMessage(bool outgoing, const char* data, size_t length) {
+#if 0
+ std::string result = "";
+ if (length == kHeaderSize) {
+ for (size_t i = 0; i < 24; ++i) {
+ result += base::StringPrintf("%02x",
+ data[i] > 0 ? data[i] : (data[i] + 0x100) & 0xFF);
+ if ((i + 1) % 4 == 0)
+ result += " ";
+ }
+ for (size_t i = 0; i < 24; ++i) {
+ if (data[i] >= 0x20 && data[i] <= 0x7E)
+ result += data[i];
+ else
+ result += ".";
+ }
+ } else {
+ result = base::StringPrintf("%d: ", (int)length);
+ for (size_t i = 0; i < length; ++i) {
+ if (data[i] >= 0x20 && data[i] <= 0x7E)
+ result += data[i];
+ else
+ result += ".";
+ }
+ }
+ LOG(ERROR) << (outgoing ? "[out] " : "[ in] ") << result;
+#endif // 0
+}
+
+void CloseDevice(scoped_refptr<UsbDeviceHandle> usb_device,
+ bool release_successful) {
+ base::Erase(g_open_devices.Get(), usb_device->GetDevice()->guid());
+ usb_device->Close();
+}
+
+void ReleaseInterface(scoped_refptr<UsbDeviceHandle> usb_device,
+ int interface_id) {
+ usb_device->ReleaseInterface(interface_id,
+ base::Bind(&CloseDevice, usb_device));
+}
+
+void RespondOnCallerThread(const AndroidUsbDevicesCallback& callback,
+ AndroidUsbDevices* new_devices) {
+ std::unique_ptr<AndroidUsbDevices> devices(new_devices);
+
+ // Add raw pointers to the newly claimed devices.
+ for (const scoped_refptr<AndroidUsbDevice>& device : *devices) {
+ g_devices.Get().push_back(device.get());
+ }
+
+ // Return all claimed devices.
+ AndroidUsbDevices result(g_devices.Get().begin(), g_devices.Get().end());
+ callback.Run(result);
+}
+
+void RespondOnUIThread(
+ const AndroidUsbDevicesCallback& callback,
+ AndroidUsbDevices* devices,
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ caller_task_runner->PostTask(
+ FROM_HERE, base::BindOnce(&RespondOnCallerThread, callback, devices));
+}
+
+void CreateDeviceOnInterfaceClaimed(AndroidUsbDevices* devices,
+ crypto::RSAPrivateKey* rsa_key,
+ scoped_refptr<UsbDeviceHandle> usb_handle,
+ int inbound_address,
+ int outbound_address,
+ int zero_mask,
+ int interface_number,
+ const base::Closure& barrier,
+ bool success) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (success) {
+ devices->push_back(new AndroidUsbDevice(
+ rsa_key, usb_handle,
+ base::UTF16ToASCII(usb_handle->GetDevice()->serial_number()),
+ inbound_address, outbound_address, zero_mask, interface_number));
+ } else {
+ usb_handle->Close();
+ }
+ barrier.Run();
+}
+
+void OnDeviceOpened(AndroidUsbDevices* devices,
+ const std::string& device_guid,
+ crypto::RSAPrivateKey* rsa_key,
+ int inbound_address,
+ int outbound_address,
+ int zero_mask,
+ int interface_number,
+ const base::Closure& barrier,
+ scoped_refptr<UsbDeviceHandle> usb_handle) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if (usb_handle.get()) {
+ usb_handle->ClaimInterface(
+ interface_number,
+ base::Bind(&CreateDeviceOnInterfaceClaimed, devices, rsa_key,
+ usb_handle, inbound_address, outbound_address, zero_mask,
+ interface_number, barrier));
+ } else {
+ base::Erase(g_open_devices.Get(), device_guid);
+ barrier.Run();
+ }
+}
+
+void OpenAndroidDevice(AndroidUsbDevices* devices,
+ crypto::RSAPrivateKey* rsa_key,
+ const base::Closure& barrier,
+ scoped_refptr<UsbDevice> device,
+ int interface_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (device->serial_number().empty()) {
+ barrier.Run();
+ return;
+ }
+
+ const UsbConfigDescriptor* config = device->active_configuration();
+ if (!config) {
+ barrier.Run();
+ return;
+ }
+
+ const UsbInterfaceDescriptor& interface = config->interfaces[interface_id];
+ int inbound_address = 0;
+ int outbound_address = 0;
+ int zero_mask = 0;
+
+ for (const UsbEndpointDescriptor& endpoint : interface.endpoints) {
+ if (endpoint.transfer_type != UsbTransferType::BULK)
+ continue;
+ if (endpoint.direction == UsbTransferDirection::INBOUND)
+ inbound_address = endpoint.address;
+ else
+ outbound_address = endpoint.address;
+ zero_mask = endpoint.maximum_packet_size - 1;
+ }
+
+ if (inbound_address == 0 || outbound_address == 0) {
+ barrier.Run();
+ return;
+ }
+
+ if (base::ContainsValue(g_open_devices.Get(), device->guid())) {
+ // |device| is already open, do not make parallel attempts to connect to it.
+ barrier.Run();
+ return;
+ }
+
+ g_open_devices.Get().push_back(device->guid());
+ device->Open(base::Bind(&OnDeviceOpened, devices, device->guid(), rsa_key,
+ inbound_address, outbound_address, zero_mask,
+ interface.interface_number, barrier));
+}
+
+void OpenAndroidDevices(
+ crypto::RSAPrivateKey* rsa_key,
+ const AndroidUsbDevicesCallback& callback,
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
+ const UsbDevices& usb_devices) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ // Add new devices.
+ AndroidUsbDevices* devices = new AndroidUsbDevices();
+ base::Closure barrier = base::BarrierClosure(
+ usb_devices.size(),
+ base::Bind(&RespondOnUIThread, callback, devices, caller_task_runner));
+
+ for (const scoped_refptr<UsbDevice>& device : usb_devices) {
+ const UsbConfigDescriptor* config = device->active_configuration();
+ if (!config) {
+ barrier.Run();
+ continue;
+ }
+ bool has_android_interface = false;
+ for (size_t j = 0; j < config->interfaces.size(); ++j) {
+ if (!IsAndroidInterface(config->interfaces[j])) {
+ continue;
+ }
+
+ OpenAndroidDevice(devices, rsa_key, barrier, device, j);
+ has_android_interface = true;
+ break;
+ }
+ if (!has_android_interface) {
+ barrier.Run();
+ }
+ }
+}
+
+void EnumerateOnUIThread(
+ crypto::RSAPrivateKey* rsa_key,
+ const AndroidUsbDevicesCallback& callback,
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ UsbService* service = device::DeviceClient::Get()->GetUsbService();
+ if (service == NULL) {
+ caller_task_runner->PostTask(FROM_HERE,
+ base::BindOnce(callback, AndroidUsbDevices()));
+ } else {
+ service->GetDevices(
+ base::Bind(&OpenAndroidDevices, rsa_key, callback, caller_task_runner));
+ }
+}
+
+} // namespace
+
+AdbMessage::AdbMessage(uint32_t command,
+ uint32_t arg0,
+ uint32_t arg1,
+ const std::string& body)
+ : command(command), arg0(arg0), arg1(arg1), body(body) {}
+
+AdbMessage::~AdbMessage() {
+}
+
+// static
+void AndroidUsbDevice::CountDevices(const base::Callback<void(int)>& callback) {
+ UsbService* service = device::DeviceClient::Get()->GetUsbService();
+ if (service != NULL) {
+ service->GetDevices(base::Bind(&CountAndroidDevices, callback));
+ } else {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ base::BindOnce(callback, 0));
+ }
+}
+
+// static
+void AndroidUsbDevice::Enumerate(crypto::RSAPrivateKey* rsa_key,
+ const AndroidUsbDevicesCallback& callback) {
+ // Collect devices with closed handles.
+ for (AndroidUsbDevice* device : g_devices.Get()) {
+ if (device->usb_handle_.get()) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::BindOnce(&AndroidUsbDevice::TerminateIfReleased, device,
+ device->usb_handle_));
+ }
+ }
+
+ // Then look for the new devices.
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::BindOnce(&EnumerateOnUIThread, rsa_key, callback,
+ base::ThreadTaskRunnerHandle::Get()));
+}
+
+AndroidUsbDevice::AndroidUsbDevice(crypto::RSAPrivateKey* rsa_key,
+ scoped_refptr<UsbDeviceHandle> usb_device,
+ const std::string& serial,
+ int inbound_address,
+ int outbound_address,
+ int zero_mask,
+ int interface_id)
+ : rsa_key_(rsa_key->Copy()),
+ usb_handle_(usb_device),
+ serial_(serial),
+ inbound_address_(inbound_address),
+ outbound_address_(outbound_address),
+ zero_mask_(zero_mask),
+ interface_id_(interface_id),
+ is_connected_(false),
+ signature_sent_(false),
+ last_socket_id_(256),
+ weak_factory_(this) {
+}
+
+void AndroidUsbDevice::InitOnCallerThread() {
+ if (task_runner_)
+ return;
+ task_runner_ = base::ThreadTaskRunnerHandle::Get();
+ Queue(base::MakeUnique<AdbMessage>(AdbMessage::kCommandCNXN, kVersion,
+ kMaxPayload, kHostConnectMessage));
+ ReadHeader();
+}
+
+net::StreamSocket* AndroidUsbDevice::CreateSocket(const std::string& command) {
+ if (!usb_handle_.get())
+ return NULL;
+
+ uint32_t socket_id = ++last_socket_id_;
+ sockets_[socket_id] = new AndroidUsbSocket(this, socket_id, command,
+ base::Bind(&AndroidUsbDevice::SocketDeleted, this, socket_id));
+ return sockets_[socket_id];
+}
+
+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);
+ // Delay open request if not yet connected.
+ if (!is_connected_) {
+ pending_messages_.push_back(std::move(message));
+ return;
+ }
+ Queue(std::move(message));
+}
+
+AndroidUsbDevice::~AndroidUsbDevice() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ Terminate();
+}
+
+void AndroidUsbDevice::Queue(std::unique_ptr<AdbMessage> message) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ // Queue header.
+ std::vector<uint32_t> header;
+ header.push_back(message->command);
+ header.push_back(message->arg0);
+ header.push_back(message->arg1);
+ bool append_zero = true;
+ if (message->body.empty())
+ append_zero = false;
+ if (message->command == AdbMessage::kCommandAUTH &&
+ message->arg0 == AdbMessage::kAuthSignature)
+ append_zero = false;
+ if (message->command == AdbMessage::kCommandWRTE)
+ append_zero = false;
+
+ size_t body_length = message->body.length() + (append_zero ? 1 : 0);
+ header.push_back(body_length);
+ header.push_back(Checksum(message->body));
+ header.push_back(message->command ^ 0xffffffff);
+ scoped_refptr<net::IOBufferWithSize> header_buffer =
+ new net::IOBufferWithSize(kHeaderSize);
+ memcpy(header_buffer.get()->data(), &header[0], kHeaderSize);
+ outgoing_queue_.push(header_buffer);
+
+ // Queue body.
+ if (!message->body.empty()) {
+ scoped_refptr<net::IOBufferWithSize> body_buffer =
+ new net::IOBufferWithSize(body_length);
+ memcpy(body_buffer->data(), message->body.data(), message->body.length());
+ if (append_zero)
+ body_buffer->data()[body_length - 1] = 0;
+ outgoing_queue_.push(body_buffer);
+ if (zero_mask_ && (body_length & zero_mask_) == 0) {
+ // Send a zero length packet.
+ outgoing_queue_.push(new net::IOBufferWithSize(0));
+ }
+ }
+ ProcessOutgoing();
+}
+
+void AndroidUsbDevice::ProcessOutgoing() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ if (outgoing_queue_.empty() || !usb_handle_.get())
+ return;
+
+ BulkMessage message = outgoing_queue_.front();
+ outgoing_queue_.pop();
+ DumpMessage(true, message->data(), message->size());
+
+ usb_handle_->GenericTransfer(
+ UsbTransferDirection::OUTBOUND, outbound_address_, message,
+ message->size(), kUsbTimeout,
+ base::Bind(&AndroidUsbDevice::OutgoingMessageSent,
+ weak_factory_.GetWeakPtr()));
+}
+
+void AndroidUsbDevice::OutgoingMessageSent(UsbTransferStatus status,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t result) {
+ if (status != UsbTransferStatus::COMPLETED)
+ return;
+
+ task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&AndroidUsbDevice::ProcessOutgoing, this));
+}
+
+void AndroidUsbDevice::ReadHeader() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ if (!usb_handle_.get()) {
+ return;
+ }
+
+ scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kHeaderSize);
+ usb_handle_->GenericTransfer(
+ UsbTransferDirection::INBOUND, inbound_address_, buffer, kHeaderSize,
+ kUsbTimeout,
+ base::Bind(&AndroidUsbDevice::ParseHeader, weak_factory_.GetWeakPtr()));
+}
+
+void AndroidUsbDevice::ParseHeader(UsbTransferStatus status,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t result) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ if (status == UsbTransferStatus::TIMEOUT) {
+ task_runner_->PostTask(FROM_HERE,
+ base::BindOnce(&AndroidUsbDevice::ReadHeader, this));
+ return;
+ }
+
+ if (status != UsbTransferStatus::COMPLETED || result != kHeaderSize) {
+ TransferError(status);
+ return;
+ }
+
+ DumpMessage(false, buffer->data(), result);
+ std::vector<uint32_t> header(6);
+ memcpy(&header[0], buffer->data(), result);
+ std::unique_ptr<AdbMessage> message(
+ new AdbMessage(header[0], header[1], header[2], ""));
+ uint32_t data_length = header[3];
+ uint32_t data_check = header[4];
+ uint32_t magic = header[5];
+ if ((message->command ^ 0xffffffff) != magic) {
+ TransferError(UsbTransferStatus::TRANSFER_ERROR);
+ return;
+ }
+
+ if (data_length == 0) {
+ task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&AndroidUsbDevice::HandleIncoming, this,
+ base::Passed(&message)));
+ } else {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&AndroidUsbDevice::ReadBody, this,
+ base::Passed(&message), data_length, data_check));
+ }
+}
+
+void AndroidUsbDevice::ReadBody(std::unique_ptr<AdbMessage> message,
+ uint32_t data_length,
+ uint32_t data_check) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ if (!usb_handle_.get()) {
+ return;
+ }
+
+ scoped_refptr<net::IOBuffer> buffer =
+ new net::IOBuffer(static_cast<size_t>(data_length));
+ usb_handle_->GenericTransfer(
+ UsbTransferDirection::INBOUND, inbound_address_, buffer, data_length,
+ kUsbTimeout,
+ base::Bind(&AndroidUsbDevice::ParseBody, weak_factory_.GetWeakPtr(),
+ base::Passed(&message), data_length, data_check));
+}
+
+void AndroidUsbDevice::ParseBody(std::unique_ptr<AdbMessage> message,
+ uint32_t data_length,
+ uint32_t data_check,
+ UsbTransferStatus status,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t result) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ if (status == UsbTransferStatus::TIMEOUT) {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&AndroidUsbDevice::ReadBody, this,
+ base::Passed(&message), data_length, data_check));
+ return;
+ }
+
+ if (status != UsbTransferStatus::COMPLETED ||
+ static_cast<uint32_t>(result) != data_length) {
+ TransferError(status);
+ return;
+ }
+
+ DumpMessage(false, buffer->data(), data_length);
+ message->body = std::string(buffer->data(), result);
+ if (Checksum(message->body) != data_check) {
+ TransferError(UsbTransferStatus::TRANSFER_ERROR);
+ return;
+ }
+
+ task_runner_->PostTask(FROM_HERE,
+ base::BindOnce(&AndroidUsbDevice::HandleIncoming, this,
+ base::Passed(&message)));
+}
+
+void AndroidUsbDevice::HandleIncoming(std::unique_ptr<AdbMessage> message) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ switch (message->command) {
+ case AdbMessage::kCommandAUTH:
+ {
+ DCHECK_EQ(message->arg0, static_cast<uint32_t>(AdbMessage::kAuthToken));
+ if (signature_sent_) {
+ Queue(base::MakeUnique<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,
+ AdbMessage::kAuthSignature, 0,
+ signature));
+ } else {
+ Queue(base::MakeUnique<AdbMessage>(
+ AdbMessage::kCommandAUTH, AdbMessage::kAuthRSAPublicKey, 0,
+ AndroidRSAPublicKey(rsa_key_.get())));
+ }
+ }
+ }
+ break;
+ case AdbMessage::kCommandCNXN:
+ {
+ is_connected_ = true;
+ PendingMessages pending;
+ pending.swap(pending_messages_);
+ for (auto& msg : pending)
+ Queue(std::move(msg));
+ }
+ break;
+ case AdbMessage::kCommandOKAY:
+ case AdbMessage::kCommandWRTE:
+ case AdbMessage::kCommandCLSE:
+ {
+ AndroidUsbSockets::iterator it = sockets_.find(message->arg1);
+ if (it != sockets_.end())
+ it->second->HandleIncoming(std::move(message));
+ }
+ break;
+ default:
+ break;
+ }
+ ReadHeader();
+}
+
+void AndroidUsbDevice::TransferError(UsbTransferStatus status) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ Terminate();
+}
+
+void AndroidUsbDevice::TerminateIfReleased(
+ scoped_refptr<UsbDeviceHandle> usb_handle) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (usb_handle->GetDevice().get()) {
+ return;
+ }
+
+ task_runner_->PostTask(FROM_HERE,
+ base::BindOnce(&AndroidUsbDevice::Terminate, this));
+}
+
+void AndroidUsbDevice::Terminate() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ std::vector<AndroidUsbDevice*>::iterator it =
+ std::find(g_devices.Get().begin(), g_devices.Get().end(), this);
+ if (it != g_devices.Get().end())
+ g_devices.Get().erase(it);
+
+ if (!usb_handle_.get())
+ return;
+
+ // Make sure we zero-out handle so that closing connections did not open
+ // new connections.
+ scoped_refptr<UsbDeviceHandle> usb_handle = usb_handle_;
+ usb_handle_ = NULL;
+
+ // Iterate over copy.
+ AndroidUsbSockets sockets(sockets_);
+ for (AndroidUsbSockets::iterator it = sockets.begin();
+ it != sockets.end(); ++it) {
+ it->second->Terminated(true);
+ }
+ DCHECK(sockets_.empty());
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::BindOnce(&ReleaseInterface, usb_handle, interface_id_));
+}
+
+void AndroidUsbDevice::SocketDeleted(uint32_t socket_id) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ sockets_.erase(socket_id);
+}
diff --git a/chromium/chrome/browser/devtools/device/usb/android_usb_device.h b/chromium/chrome/browser/devtools/device/usb/android_usb_device.h
new file mode 100644
index 00000000000..23b21b3e210
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/usb/android_usb_device.h
@@ -0,0 +1,171 @@
+// 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_DEVTOOLS_DEVICE_USB_ANDROID_USB_DEVICE_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVICE_USB_ANDROID_USB_DEVICE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <queue>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "device/usb/usb_device_handle.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace crypto {
+class RSAPrivateKey;
+}
+
+namespace net {
+class IOBuffer;
+class IOBufferWithSize;
+class StreamSocket;
+}
+
+class AndroidUsbSocket;
+
+class AdbMessage {
+ public:
+ enum Command {
+ kCommandSYNC = 0x434e5953,
+ kCommandCNXN = 0x4e584e43,
+ kCommandOPEN = 0x4e45504f,
+ kCommandOKAY = 0x59414b4f,
+ kCommandCLSE = 0x45534c43,
+ kCommandWRTE = 0x45545257,
+ kCommandAUTH = 0x48545541
+ };
+
+ enum Auth {
+ kAuthToken = 1,
+ kAuthSignature = 2,
+ kAuthRSAPublicKey = 3
+ };
+
+ AdbMessage(uint32_t command,
+ uint32_t arg0,
+ uint32_t arg1,
+ const std::string& body);
+ ~AdbMessage();
+
+ uint32_t command;
+ uint32_t arg0;
+ uint32_t arg1;
+ std::string body;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AdbMessage);
+};
+
+class AndroidUsbDevice;
+typedef std::vector<scoped_refptr<AndroidUsbDevice> > AndroidUsbDevices;
+typedef base::Callback<void(const AndroidUsbDevices&)>
+ AndroidUsbDevicesCallback;
+
+class AndroidUsbDevice : public base::RefCountedThreadSafe<AndroidUsbDevice> {
+ public:
+ static void CountDevices(const base::Callback<void(int)>& callback);
+ static void Enumerate(crypto::RSAPrivateKey* rsa_key,
+ const AndroidUsbDevicesCallback& callback);
+
+ AndroidUsbDevice(crypto::RSAPrivateKey* rsa_key,
+ scoped_refptr<device::UsbDeviceHandle> device,
+ const std::string& serial,
+ int inbound_address,
+ int outbound_address,
+ int zero_mask,
+ int interface_id);
+
+ void InitOnCallerThread();
+
+ net::StreamSocket* CreateSocket(const std::string& command);
+
+ void Send(uint32_t command,
+ uint32_t arg0,
+ uint32_t arg1,
+ const std::string& body);
+
+ scoped_refptr<device::UsbDeviceHandle> usb_device() { return usb_handle_; }
+
+ std::string serial() { return serial_; }
+
+ bool is_connected() { return is_connected_; }
+
+ private:
+ friend class base::RefCountedThreadSafe<AndroidUsbDevice>;
+ virtual ~AndroidUsbDevice();
+
+ void Queue(std::unique_ptr<AdbMessage> message);
+ void ProcessOutgoing();
+ void OutgoingMessageSent(device::UsbTransferStatus status,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t result);
+
+ void ReadHeader();
+ void ParseHeader(device::UsbTransferStatus status,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t result);
+
+ void ReadBody(std::unique_ptr<AdbMessage> message,
+ uint32_t data_length,
+ uint32_t data_check);
+ void ParseBody(std::unique_ptr<AdbMessage> message,
+ uint32_t data_length,
+ uint32_t data_check,
+ device::UsbTransferStatus status,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t result);
+
+ void HandleIncoming(std::unique_ptr<AdbMessage> message);
+
+ void TransferError(device::UsbTransferStatus status);
+
+ void TerminateIfReleased(scoped_refptr<device::UsbDeviceHandle> usb_handle);
+ void Terminate();
+
+ void SocketDeleted(uint32_t socket_id);
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+ std::unique_ptr<crypto::RSAPrivateKey> rsa_key_;
+
+ // Device info
+ scoped_refptr<device::UsbDeviceHandle> usb_handle_;
+ std::string serial_;
+ int inbound_address_;
+ int outbound_address_;
+ int zero_mask_;
+ int interface_id_;
+
+ bool is_connected_;
+ bool signature_sent_;
+
+ // Created sockets info
+ uint32_t last_socket_id_;
+ typedef std::map<uint32_t, AndroidUsbSocket*> AndroidUsbSockets;
+ AndroidUsbSockets sockets_;
+
+ // Outgoing bulk queue
+ typedef scoped_refptr<net::IOBufferWithSize> BulkMessage;
+ std::queue<BulkMessage> outgoing_queue_;
+
+ // Outgoing messages pending connect
+ using PendingMessages = std::vector<std::unique_ptr<AdbMessage>>;
+ PendingMessages pending_messages_;
+
+ base::WeakPtrFactory<AndroidUsbDevice> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(AndroidUsbDevice);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVICE_USB_ANDROID_USB_DEVICE_H_
diff --git a/chromium/chrome/browser/devtools/device/usb/android_usb_socket.cc b/chromium/chrome/browser/devtools/device/usb/android_usb_socket.cc
new file mode 100644
index 00000000000..8736c76f45b
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/usb/android_usb_socket.cc
@@ -0,0 +1,262 @@
+// 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/devtools/device/usb/android_usb_socket.h"
+
+#include <stddef.h>
+
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "net/base/io_buffer.h"
+#include "net/base/ip_address.h"
+#include "net/base/net_errors.h"
+
+namespace {
+
+const int kMaxPayload = 4096;
+
+} // namespace
+
+AndroidUsbSocket::AndroidUsbSocket(scoped_refptr<AndroidUsbDevice> device,
+ uint32_t socket_id,
+ const std::string& command,
+ base::Closure delete_callback)
+ : device_(device),
+ command_(command),
+ local_id_(socket_id),
+ remote_id_(0),
+ is_connected_(false),
+ delete_callback_(delete_callback),
+ weak_factory_(this) {}
+
+AndroidUsbSocket::~AndroidUsbSocket() {
+ DCHECK(CalledOnValidThread());
+ if (is_connected_)
+ Disconnect();
+ if (!delete_callback_.is_null())
+ delete_callback_.Run();
+}
+
+void AndroidUsbSocket::HandleIncoming(std::unique_ptr<AdbMessage> message) {
+ if (!device_.get())
+ return;
+
+ CHECK_EQ(message->arg1, local_id_);
+ switch (message->command) {
+ case AdbMessage::kCommandOKAY:
+ if (!is_connected_) {
+ remote_id_ = message->arg0;
+ is_connected_ = true;
+ if (!connect_callback_.is_null())
+ base::ResetAndReturn(&connect_callback_).Run(net::OK);
+ // "this" can be deleted.
+ } else {
+ RespondToWriter(write_length_);
+ // "this" can be deleted.
+ }
+ break;
+ case AdbMessage::kCommandWRTE:
+ device_->Send(AdbMessage::kCommandOKAY, local_id_, message->arg0, "");
+ read_buffer_ += message->body;
+ // Allow WRTE over new connection even though OKAY ack was not received.
+ if (!is_connected_) {
+ remote_id_ = message->arg0;
+ is_connected_ = true;
+ if (!connect_callback_.is_null())
+ base::ResetAndReturn(&connect_callback_).Run(net::OK);
+ // "this" can be deleted.
+ } else {
+ RespondToReader(false);
+ // "this" can be deleted.
+ }
+ break;
+ case AdbMessage::kCommandCLSE:
+ if (is_connected_)
+ device_->Send(AdbMessage::kCommandCLSE, local_id_, 0, "");
+ Terminated(true);
+ // "this" can be deleted.
+ break;
+ default:
+ break;
+ }
+}
+
+void AndroidUsbSocket::Terminated(bool closed_by_device) {
+ is_connected_ = false;
+
+ // Break the socket -> device connection, release the device.
+ device_ = nullptr;
+ base::ResetAndReturn(&delete_callback_).Run();
+
+ if (!closed_by_device)
+ return;
+
+ // Respond to pending callbacks.
+ if (!connect_callback_.is_null()) {
+ base::ResetAndReturn(&connect_callback_).Run(net::ERR_FAILED);
+ // "this" can be deleted.
+ return;
+ }
+ base::WeakPtr<AndroidUsbSocket> weak_this = weak_factory_.GetWeakPtr();
+ RespondToReader(true);
+ // "this" can be deleted.
+ if (weak_this) {
+ RespondToWriter(net::ERR_FAILED);
+ // "this" can be deleted.
+ }
+}
+
+int AndroidUsbSocket::Read(net::IOBuffer* buffer,
+ int length,
+ const net::CompletionCallback& callback) {
+ DCHECK(!callback.is_null());
+ if (!is_connected_)
+ return device_.get() ? net::ERR_SOCKET_NOT_CONNECTED : 0;
+
+ DCHECK(read_callback_.is_null());
+ if (read_buffer_.empty()) {
+ read_callback_ = callback;
+ read_io_buffer_ = buffer;
+ read_length_ = length;
+ return net::ERR_IO_PENDING;
+ }
+
+ size_t bytes_to_copy = static_cast<size_t>(length) > read_buffer_.length() ?
+ read_buffer_.length() : static_cast<size_t>(length);
+ memcpy(buffer->data(), read_buffer_.data(), bytes_to_copy);
+ if (read_buffer_.length() > bytes_to_copy)
+ read_buffer_ = read_buffer_.substr(bytes_to_copy);
+ else
+ read_buffer_ = std::string();
+ return bytes_to_copy;
+}
+
+int AndroidUsbSocket::Write(net::IOBuffer* buffer,
+ int length,
+ const net::CompletionCallback& callback) {
+ DCHECK(!callback.is_null());
+ if (!is_connected_)
+ return net::ERR_SOCKET_NOT_CONNECTED;
+
+ if (length > kMaxPayload)
+ length = kMaxPayload;
+
+ DCHECK(write_callback_.is_null());
+ write_callback_ = callback;
+ write_length_ = length;
+ device_->Send(AdbMessage::kCommandWRTE, local_id_, remote_id_,
+ std::string(buffer->data(), length));
+ return net::ERR_IO_PENDING;
+}
+
+int AndroidUsbSocket::SetReceiveBufferSize(int32_t size) {
+ NOTIMPLEMENTED();
+ return net::ERR_NOT_IMPLEMENTED;
+}
+
+int AndroidUsbSocket::SetSendBufferSize(int32_t size) {
+ NOTIMPLEMENTED();
+ return net::ERR_NOT_IMPLEMENTED;
+}
+
+int AndroidUsbSocket::Connect(const net::CompletionCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!callback.is_null());
+ if (!device_.get())
+ return net::ERR_FAILED;
+
+ DCHECK(!is_connected_);
+ DCHECK(connect_callback_.is_null());
+ connect_callback_ = callback;
+ device_->Send(AdbMessage::kCommandOPEN, local_id_, 0, command_);
+ return net::ERR_IO_PENDING;
+}
+
+void AndroidUsbSocket::Disconnect() {
+ if (!device_.get())
+ return;
+ device_->Send(AdbMessage::kCommandCLSE, local_id_, remote_id_, "");
+ Terminated(false);
+}
+
+bool AndroidUsbSocket::IsConnected() const {
+ DCHECK(CalledOnValidThread());
+ return is_connected_;
+}
+
+bool AndroidUsbSocket::IsConnectedAndIdle() const {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+int AndroidUsbSocket::GetPeerAddress(net::IPEndPoint* address) const {
+ *address = net::IPEndPoint(net::IPAddress(0, 0, 0, 0), 0);
+ return net::OK;
+}
+
+int AndroidUsbSocket::GetLocalAddress(net::IPEndPoint* address) const {
+ NOTIMPLEMENTED();
+ return net::ERR_NOT_IMPLEMENTED;
+}
+
+const net::NetLogWithSource& AndroidUsbSocket::NetLog() const {
+ return net_log_;
+}
+
+void AndroidUsbSocket::SetSubresourceSpeculation() {
+ NOTIMPLEMENTED();
+}
+
+void AndroidUsbSocket::SetOmniboxSpeculation() {
+ NOTIMPLEMENTED();
+}
+
+bool AndroidUsbSocket::WasEverUsed() const {
+ NOTIMPLEMENTED();
+ return true;
+}
+
+bool AndroidUsbSocket::WasAlpnNegotiated() const {
+ NOTIMPLEMENTED();
+ return true;
+}
+
+net::NextProto AndroidUsbSocket::GetNegotiatedProtocol() const {
+ NOTIMPLEMENTED();
+ return net::kProtoUnknown;
+}
+
+bool AndroidUsbSocket::GetSSLInfo(net::SSLInfo* ssl_info) {
+ return false;
+}
+
+void AndroidUsbSocket::GetConnectionAttempts(
+ net::ConnectionAttempts* out) const {
+ out->clear();
+}
+
+int64_t AndroidUsbSocket::GetTotalReceivedBytes() const {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+void AndroidUsbSocket::RespondToReader(bool disconnect) {
+ if (read_callback_.is_null() || (read_buffer_.empty() && !disconnect))
+ return;
+ size_t bytes_to_copy =
+ static_cast<size_t>(read_length_) > read_buffer_.length() ?
+ read_buffer_.length() : static_cast<size_t>(read_length_);
+ memcpy(read_io_buffer_->data(), read_buffer_.data(), bytes_to_copy);
+ if (read_buffer_.length() > bytes_to_copy)
+ read_buffer_ = read_buffer_.substr(bytes_to_copy);
+ else
+ read_buffer_ = std::string();
+ base::ResetAndReturn(&read_callback_).Run(bytes_to_copy);
+}
+
+void AndroidUsbSocket::RespondToWriter(int result) {
+ if (!write_callback_.is_null())
+ base::ResetAndReturn(&write_callback_).Run(result);
+}
diff --git a/chromium/chrome/browser/devtools/device/usb/android_usb_socket.h b/chromium/chrome/browser/devtools/device/usb/android_usb_socket.h
new file mode 100644
index 00000000000..8d3811fa415
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/usb/android_usb_socket.h
@@ -0,0 +1,85 @@
+// 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_DEVTOOLS_DEVICE_USB_ANDROID_USB_SOCKET_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVICE_USB_ANDROID_USB_SOCKET_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/non_thread_safe.h"
+#include "chrome/browser/devtools/device/usb/android_usb_device.h"
+#include "net/base/ip_endpoint.h"
+#include "net/log/net_log_with_source.h"
+#include "net/socket/stream_socket.h"
+
+class AndroidUsbSocket : public net::StreamSocket,
+ public base::NonThreadSafe {
+ public:
+ AndroidUsbSocket(scoped_refptr<AndroidUsbDevice> device,
+ uint32_t socket_id,
+ const std::string& command,
+ base::Closure delete_callback);
+ ~AndroidUsbSocket() override;
+
+ void HandleIncoming(std::unique_ptr<AdbMessage> message);
+
+ void Terminated(bool closed_by_device);
+
+ // net::StreamSocket implementation.
+ int Read(net::IOBuffer* buf,
+ int buf_len,
+ const net::CompletionCallback& callback) override;
+ int Write(net::IOBuffer* buf,
+ int buf_len,
+ const net::CompletionCallback& callback) override;
+ int SetReceiveBufferSize(int32_t size) override;
+ int SetSendBufferSize(int32_t size) override;
+ int Connect(const net::CompletionCallback& callback) override;
+ void Disconnect() override;
+ bool IsConnected() const override;
+ bool IsConnectedAndIdle() const override;
+ int GetPeerAddress(net::IPEndPoint* address) const override;
+ int GetLocalAddress(net::IPEndPoint* address) const override;
+ const net::NetLogWithSource& NetLog() const override;
+ void SetSubresourceSpeculation() override;
+ void SetOmniboxSpeculation() override;
+ bool WasEverUsed() const override;
+ bool WasAlpnNegotiated() const override;
+ net::NextProto GetNegotiatedProtocol() const override;
+ bool GetSSLInfo(net::SSLInfo* ssl_info) override;
+ void GetConnectionAttempts(net::ConnectionAttempts* out) const override;
+ void ClearConnectionAttempts() override {}
+ void AddConnectionAttempts(const net::ConnectionAttempts& attempts) override {
+ }
+ int64_t GetTotalReceivedBytes() const override;
+
+ private:
+ void RespondToReader(bool disconnect);
+ void RespondToWriter(int result);
+
+ scoped_refptr<AndroidUsbDevice> device_;
+ std::string command_;
+ uint32_t local_id_;
+ uint32_t remote_id_;
+ net::NetLogWithSource net_log_;
+ bool is_connected_;
+ std::string read_buffer_;
+ scoped_refptr<net::IOBuffer> read_io_buffer_;
+ int read_length_;
+ int write_length_;
+ net::CompletionCallback connect_callback_;
+ net::CompletionCallback read_callback_;
+ net::CompletionCallback write_callback_;
+ base::Closure delete_callback_;
+ base::WeakPtrFactory<AndroidUsbSocket> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(AndroidUsbSocket);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVICE_USB_ANDROID_USB_SOCKET_H_
diff --git a/chromium/chrome/browser/devtools/device/usb/usb_device_provider.cc b/chromium/chrome/browser/devtools/device/usb/usb_device_provider.cc
new file mode 100644
index 00000000000..2dfdf9f25f3
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/usb/usb_device_provider.cc
@@ -0,0 +1,154 @@
+// 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/devtools/device/usb/usb_device_provider.h"
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/devtools/device/usb/android_rsa.h"
+#include "chrome/browser/devtools/device/usb/android_usb_device.h"
+#include "crypto/rsa_private_key.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/socket/stream_socket.h"
+
+namespace {
+
+const char kLocalAbstractCommand[] = "localabstract:%s";
+
+const int kBufferSize = 16 * 1024;
+
+void OnOpenSocket(const UsbDeviceProvider::SocketCallback& callback,
+ net::StreamSocket* socket_raw,
+ int result) {
+ std::unique_ptr<net::StreamSocket> socket(socket_raw);
+ if (result != net::OK)
+ socket.reset();
+ callback.Run(result, std::move(socket));
+}
+
+void OnRead(net::StreamSocket* socket,
+ scoped_refptr<net::IOBuffer> buffer,
+ const std::string& data,
+ const UsbDeviceProvider::CommandCallback& callback,
+ int result) {
+ if (result <= 0) {
+ callback.Run(result, result == 0 ? data : std::string());
+ delete socket;
+ return;
+ }
+
+ std::string new_data = data + std::string(buffer->data(), result);
+ result =
+ socket->Read(buffer.get(),
+ kBufferSize,
+ base::Bind(&OnRead, socket, buffer, new_data, callback));
+ if (result != net::ERR_IO_PENDING)
+ OnRead(socket, buffer, new_data, callback, result);
+}
+
+void OpenedForCommand(const UsbDeviceProvider::CommandCallback& callback,
+ net::StreamSocket* socket,
+ int result) {
+ if (result != net::OK) {
+ callback.Run(result, std::string());
+ return;
+ }
+ scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kBufferSize);
+ result = socket->Read(
+ buffer.get(),
+ kBufferSize,
+ base::Bind(&OnRead, socket, buffer, std::string(), callback));
+ if (result != net::ERR_IO_PENDING)
+ OnRead(socket, buffer, std::string(), callback, result);
+}
+
+void RunCommand(scoped_refptr<AndroidUsbDevice> device,
+ const std::string& command,
+ const UsbDeviceProvider::CommandCallback& callback) {
+ net::StreamSocket* socket = device->CreateSocket(command);
+ if (!socket) {
+ callback.Run(net::ERR_CONNECTION_FAILED, std::string());
+ return;
+ }
+ int result = socket->Connect(
+ base::Bind(&OpenedForCommand, callback, socket));
+ if (result != net::ERR_IO_PENDING)
+ callback.Run(result, std::string());
+}
+
+} // namespace
+
+// static
+void UsbDeviceProvider::CountDevices(
+ const base::Callback<void(int)>& callback) {
+ AndroidUsbDevice::CountDevices(callback);
+}
+
+UsbDeviceProvider::UsbDeviceProvider(Profile* profile){
+ rsa_key_ = AndroidRSAPrivateKey(profile);
+}
+
+void UsbDeviceProvider::QueryDevices(const SerialsCallback& callback) {
+ AndroidUsbDevice::Enumerate(
+ rsa_key_.get(),
+ base::Bind(&UsbDeviceProvider::EnumeratedDevices, this, callback));
+}
+
+void UsbDeviceProvider::QueryDeviceInfo(const std::string& serial,
+ const DeviceInfoCallback& callback) {
+ UsbDeviceMap::iterator it = device_map_.find(serial);
+ if (it == device_map_.end() || !it->second->is_connected()) {
+ AndroidDeviceManager::DeviceInfo offline_info;
+ callback.Run(offline_info);
+ return;
+ }
+ AndroidDeviceManager::QueryDeviceInfo(base::Bind(&RunCommand, it->second),
+ callback);
+}
+
+void UsbDeviceProvider::OpenSocket(const std::string& serial,
+ const std::string& name,
+ const SocketCallback& callback) {
+ UsbDeviceMap::iterator it = device_map_.find(serial);
+ if (it == device_map_.end()) {
+ callback.Run(net::ERR_CONNECTION_FAILED,
+ base::WrapUnique<net::StreamSocket>(NULL));
+ return;
+ }
+ std::string socket_name =
+ base::StringPrintf(kLocalAbstractCommand, name.c_str());
+ net::StreamSocket* socket = it->second->CreateSocket(socket_name);
+ if (!socket) {
+ callback.Run(net::ERR_CONNECTION_FAILED,
+ base::WrapUnique<net::StreamSocket>(NULL));
+ return;
+ }
+ int result = socket->Connect(base::Bind(&OnOpenSocket, callback, socket));
+ if (result != net::ERR_IO_PENDING)
+ callback.Run(result, base::WrapUnique<net::StreamSocket>(NULL));
+}
+
+void UsbDeviceProvider::ReleaseDevice(const std::string& serial) {
+ device_map_.erase(serial);
+}
+
+UsbDeviceProvider::~UsbDeviceProvider() {
+}
+
+void UsbDeviceProvider::EnumeratedDevices(const SerialsCallback& callback,
+ const AndroidUsbDevices& devices) {
+ std::vector<std::string> result;
+ device_map_.clear();
+ for (AndroidUsbDevices::const_iterator it = devices.begin();
+ it != devices.end(); ++it) {
+ result.push_back((*it)->serial());
+ device_map_[(*it)->serial()] = *it;
+ (*it)->InitOnCallerThread();
+ }
+ callback.Run(result);
+}
+
diff --git a/chromium/chrome/browser/devtools/device/usb/usb_device_provider.h b/chromium/chrome/browser/devtools/device/usb/usb_device_provider.h
new file mode 100644
index 00000000000..02d06843f77
--- /dev/null
+++ b/chromium/chrome/browser/devtools/device/usb/usb_device_provider.h
@@ -0,0 +1,46 @@
+// 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_DEVTOOLS_DEVICE_USB_USB_DEVICE_PROVIDER_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVICE_USB_USB_DEVICE_PROVIDER_H_
+
+#include "chrome/browser/devtools/device/android_device_manager.h"
+
+namespace crypto {
+class RSAPrivateKey;
+}
+
+class AndroidUsbDevice;
+
+class UsbDeviceProvider : public AndroidDeviceManager::DeviceProvider {
+ public:
+ static void CountDevices(const base::Callback<void(int)>& callback);
+
+ explicit UsbDeviceProvider(Profile* profile);
+
+ void QueryDevices(const SerialsCallback& callback) override;
+
+ void QueryDeviceInfo(const std::string& serial,
+ const DeviceInfoCallback& callback) override;
+
+ void OpenSocket(const std::string& serial,
+ const std::string& socket_name,
+ const SocketCallback& callback) override;
+
+ void ReleaseDevice(const std::string& serial) override;
+
+ private:
+ ~UsbDeviceProvider() override;
+
+ void EnumeratedDevices(
+ const SerialsCallback& callback,
+ const std::vector<scoped_refptr<AndroidUsbDevice> >& devices);
+
+ typedef std::map<std::string, scoped_refptr<AndroidUsbDevice> > UsbDeviceMap;
+
+ std::unique_ptr<crypto::RSAPrivateKey> rsa_key_;
+ UsbDeviceMap device_map_;
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVICE_USB_USB_DEVICE_PROVIDER_H_
diff --git a/chromium/chrome/browser/devtools/devtools_auto_opener.cc b/chromium/chrome/browser/devtools/devtools_auto_opener.cc
new file mode 100644
index 00000000000..834030e387d
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_auto_opener.cc
@@ -0,0 +1,24 @@
+// 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 "chrome/browser/devtools/devtools_auto_opener.h"
+
+#include "base/command_line.h"
+#include "chrome/browser/devtools/devtools_window.h"
+
+DevToolsAutoOpener::DevToolsAutoOpener()
+ : browser_tab_strip_tracker_(this, nullptr, nullptr) {
+ browser_tab_strip_tracker_.Init();
+}
+
+DevToolsAutoOpener::~DevToolsAutoOpener() {
+}
+
+void DevToolsAutoOpener::TabInsertedAt(TabStripModel* tab_strip_model,
+ content::WebContents* contents,
+ int index,
+ bool foreground) {
+ if (!DevToolsWindow::IsDevToolsWindow(contents))
+ DevToolsWindow::OpenDevToolsWindow(contents);
+}
diff --git a/chromium/chrome/browser/devtools/devtools_auto_opener.h b/chromium/chrome/browser/devtools/devtools_auto_opener.h
new file mode 100644
index 00000000000..7efe2ea7b79
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_auto_opener.h
@@ -0,0 +1,28 @@
+// 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.
+
+#ifndef CHROME_BROWSER_DEVTOOLS_DEVTOOLS_AUTO_OPENER_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_AUTO_OPENER_H_
+
+#include "chrome/browser/ui/browser_tab_strip_tracker.h"
+#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
+
+class DevToolsAutoOpener : public TabStripModelObserver {
+ public:
+ DevToolsAutoOpener();
+ ~DevToolsAutoOpener() override;
+
+ private:
+ // TabStripModelObserver overrides.
+ void TabInsertedAt(TabStripModel* tab_strip_model,
+ content::WebContents* contents,
+ int index,
+ bool foreground) override;
+
+ BrowserTabStripTracker browser_tab_strip_tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsAutoOpener);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_AUTO_OPENER_H_
diff --git a/chromium/chrome/browser/devtools/devtools_contents_resizing_strategy.cc b/chromium/chrome/browser/devtools/devtools_contents_resizing_strategy.cc
new file mode 100644
index 00000000000..b11cd81456c
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_contents_resizing_strategy.cc
@@ -0,0 +1,53 @@
+// 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/devtools/devtools_contents_resizing_strategy.h"
+
+#include <algorithm>
+
+DevToolsContentsResizingStrategy::DevToolsContentsResizingStrategy()
+ : hide_inspected_contents_(false) {
+}
+
+DevToolsContentsResizingStrategy::DevToolsContentsResizingStrategy(
+ const gfx::Rect& bounds)
+ : bounds_(bounds),
+ hide_inspected_contents_(bounds_.IsEmpty() && !bounds_.x() &&
+ !bounds_.y()) {
+}
+
+
+void DevToolsContentsResizingStrategy::CopyFrom(
+ const DevToolsContentsResizingStrategy& strategy) {
+ bounds_ = strategy.bounds();
+ hide_inspected_contents_ = strategy.hide_inspected_contents();
+}
+
+bool DevToolsContentsResizingStrategy::Equals(
+ const DevToolsContentsResizingStrategy& strategy) {
+ return bounds_ == strategy.bounds() &&
+ hide_inspected_contents_ == strategy.hide_inspected_contents();
+}
+
+void ApplyDevToolsContentsResizingStrategy(
+ const DevToolsContentsResizingStrategy& strategy,
+ const gfx::Size& container_size,
+ gfx::Rect* new_devtools_bounds,
+ gfx::Rect* new_contents_bounds) {
+ new_devtools_bounds->SetRect(
+ 0, 0, container_size.width(), container_size.height());
+
+ const gfx::Rect& bounds = strategy.bounds();
+ if (bounds.size().IsEmpty() && !strategy.hide_inspected_contents()) {
+ new_contents_bounds->SetRect(
+ 0, 0, container_size.width(), container_size.height());
+ return;
+ }
+
+ int left = std::min(bounds.x(), container_size.width());
+ int top = std::min(bounds.y(), container_size.height());
+ int width = std::min(bounds.width(), container_size.width() - left);
+ int height = std::min(bounds.height(), container_size.height() - top);
+ new_contents_bounds->SetRect(left, top, width, height);
+}
diff --git a/chromium/chrome/browser/devtools/devtools_contents_resizing_strategy.h b/chromium/chrome/browser/devtools/devtools_contents_resizing_strategy.h
new file mode 100644
index 00000000000..9a8d22cc64e
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_contents_resizing_strategy.h
@@ -0,0 +1,48 @@
+// 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_DEVTOOLS_DEVTOOLS_CONTENTS_RESIZING_STRATEGY_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_CONTENTS_RESIZING_STRATEGY_H_
+
+#include "base/macros.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+// This class knows how to resize both DevTools and inspected WebContents
+// inside a browser window hierarchy.
+class DevToolsContentsResizingStrategy {
+ public:
+ DevToolsContentsResizingStrategy();
+ explicit DevToolsContentsResizingStrategy(
+ const gfx::Rect& bounds);
+
+ void CopyFrom(const DevToolsContentsResizingStrategy& strategy);
+ bool Equals(const DevToolsContentsResizingStrategy& strategy);
+
+ const gfx::Rect& bounds() const { return bounds_; }
+ bool hide_inspected_contents() const { return hide_inspected_contents_; }
+
+ private:
+ // Contents bounds. When non-empty, used instead of insets.
+ gfx::Rect bounds_;
+
+ // Determines whether inspected contents is visible.
+ bool hide_inspected_contents_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsContentsResizingStrategy);
+};
+
+// Applies contents resizing strategy, producing bounds for devtools and
+// page contents views. Generally, page contents view is placed atop of devtools
+// inside a common parent view, which size should be passed in |container_size|.
+// When unknown, providing empty rect as previous devtools and contents bounds
+// is allowed.
+void ApplyDevToolsContentsResizingStrategy(
+ const DevToolsContentsResizingStrategy& strategy,
+ const gfx::Size& container_size,
+ gfx::Rect* new_devtools_bounds,
+ gfx::Rect* new_contents_bounds);
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_CONTENTS_RESIZING_STRATEGY_H_
diff --git a/chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc b/chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
new file mode 100644
index 00000000000..cb88e6262c1
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
@@ -0,0 +1,221 @@
+// 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/devtools/devtools_embedder_message_dispatcher.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+
+namespace {
+
+using DispatchCallback = DevToolsEmbedderMessageDispatcher::DispatchCallback;
+
+bool GetValue(const base::Value& value, std::string* result) {
+ return value.GetAsString(result);
+}
+
+bool GetValue(const base::Value& value, int* result) {
+ return value.GetAsInteger(result);
+}
+
+bool GetValue(const base::Value& value, bool* result) {
+ return value.GetAsBoolean(result);
+}
+
+bool GetValue(const base::Value& value, gfx::Rect* rect) {
+ const base::DictionaryValue* dict;
+ if (!value.GetAsDictionary(&dict))
+ return false;
+ int x = 0;
+ int y = 0;
+ int width = 0;
+ int height = 0;
+ if (!dict->GetInteger("x", &x) ||
+ !dict->GetInteger("y", &y) ||
+ !dict->GetInteger("width", &width) ||
+ !dict->GetInteger("height", &height))
+ return false;
+ rect->SetRect(x, y, width, height);
+ return true;
+}
+
+template <typename T>
+struct StorageTraits {
+ using StorageType = T;
+};
+
+template <typename T>
+struct StorageTraits<const T&> {
+ using StorageType = T;
+};
+
+template <typename... Ts>
+struct ParamTuple {
+ bool Parse(const base::ListValue& list,
+ const base::ListValue::const_iterator& it) {
+ return it == list.end();
+ }
+
+ template <typename H, typename... As>
+ void Apply(const H& handler, As... args) {
+ handler.Run(args...);
+ }
+};
+
+template <typename T, typename... Ts>
+struct ParamTuple<T, Ts...> {
+ bool Parse(const base::ListValue& list,
+ const base::ListValue::const_iterator& it) {
+ return it != list.end() && GetValue(*it, &head) && tail.Parse(list, it + 1);
+ }
+
+ template <typename H, typename... As>
+ void Apply(const H& handler, As... args) {
+ tail.template Apply<H, As..., T>(handler, args..., head);
+ }
+
+ typename StorageTraits<T>::StorageType head;
+ ParamTuple<Ts...> tail;
+};
+
+template<typename... As>
+bool ParseAndHandle(const base::Callback<void(As...)>& handler,
+ const DispatchCallback& callback,
+ const base::ListValue& list) {
+ ParamTuple<As...> tuple;
+ if (!tuple.Parse(list, list.begin()))
+ return false;
+ tuple.Apply(handler);
+ return true;
+}
+
+template<typename... As>
+bool ParseAndHandleWithCallback(
+ const base::Callback<void(const DispatchCallback&, As...)>& handler,
+ const DispatchCallback& callback,
+ const base::ListValue& list) {
+ ParamTuple<As...> tuple;
+ if (!tuple.Parse(list, list.begin()))
+ return false;
+ tuple.Apply(handler, callback);
+ return true;
+}
+
+} // namespace
+
+/**
+ * Dispatcher for messages sent from the frontend running in an
+ * isolated renderer (chrome-devtools:// or chrome://inspect) to the embedder
+ * in the browser.
+ *
+ * The messages are sent via InspectorFrontendHost.sendMessageToEmbedder or
+ * chrome.send method accordingly.
+ */
+class DispatcherImpl : public DevToolsEmbedderMessageDispatcher {
+ public:
+ ~DispatcherImpl() override {}
+
+ bool Dispatch(const DispatchCallback& callback,
+ const std::string& method,
+ const base::ListValue* params) override {
+ HandlerMap::iterator it = handlers_.find(method);
+ return it != handlers_.end() && it->second.Run(callback, *params);
+ }
+
+ template<typename... As>
+ void RegisterHandler(const std::string& method,
+ void (Delegate::*handler)(As...),
+ Delegate* delegate) {
+ handlers_[method] = base::Bind(&ParseAndHandle<As...>,
+ base::Bind(handler,
+ base::Unretained(delegate)));
+ }
+
+ template<typename... As>
+ void RegisterHandlerWithCallback(
+ const std::string& method,
+ void (Delegate::*handler)(const DispatchCallback&, As...),
+ Delegate* delegate) {
+ handlers_[method] = base::Bind(&ParseAndHandleWithCallback<As...>,
+ base::Bind(handler,
+ base::Unretained(delegate)));
+ }
+
+
+ private:
+ using Handler = base::Callback<bool(const DispatchCallback&,
+ const base::ListValue&)>;
+ using HandlerMap = std::map<std::string, Handler>;
+ HandlerMap handlers_;
+};
+
+// static
+DevToolsEmbedderMessageDispatcher*
+DevToolsEmbedderMessageDispatcher::CreateForDevToolsFrontend(
+ Delegate* delegate) {
+ DispatcherImpl* d = new DispatcherImpl();
+
+ d->RegisterHandler("bringToFront", &Delegate::ActivateWindow, delegate);
+ d->RegisterHandler("closeWindow", &Delegate::CloseWindow, delegate);
+ d->RegisterHandler("loadCompleted", &Delegate::LoadCompleted, delegate);
+ d->RegisterHandler("setInspectedPageBounds",
+ &Delegate::SetInspectedPageBounds, delegate);
+ d->RegisterHandler("inspectElementCompleted",
+ &Delegate::InspectElementCompleted, delegate);
+ d->RegisterHandler("inspectedURLChanged",
+ &Delegate::InspectedURLChanged, delegate);
+ d->RegisterHandlerWithCallback("setIsDocked",
+ &Delegate::SetIsDocked, delegate);
+ d->RegisterHandler("openInNewTab", &Delegate::OpenInNewTab, delegate);
+ d->RegisterHandler("save", &Delegate::SaveToFile, delegate);
+ d->RegisterHandler("append", &Delegate::AppendToFile, delegate);
+ d->RegisterHandler("requestFileSystems",
+ &Delegate::RequestFileSystems, delegate);
+ d->RegisterHandler("addFileSystem", &Delegate::AddFileSystem, delegate);
+ d->RegisterHandler("removeFileSystem", &Delegate::RemoveFileSystem, delegate);
+ d->RegisterHandler("upgradeDraggedFileSystemPermissions",
+ &Delegate::UpgradeDraggedFileSystemPermissions, delegate);
+ d->RegisterHandler("indexPath", &Delegate::IndexPath, delegate);
+ d->RegisterHandlerWithCallback("loadNetworkResource",
+ &Delegate::LoadNetworkResource, delegate);
+ d->RegisterHandler("stopIndexing", &Delegate::StopIndexing, delegate);
+ d->RegisterHandler("searchInPath", &Delegate::SearchInPath, delegate);
+ d->RegisterHandler("setWhitelistedShortcuts",
+ &Delegate::SetWhitelistedShortcuts, delegate);
+ d->RegisterHandler("setEyeDropperActive", &Delegate::SetEyeDropperActive,
+ delegate);
+ d->RegisterHandler("showCertificateViewer",
+ &Delegate::ShowCertificateViewer, delegate);
+ d->RegisterHandler("zoomIn", &Delegate::ZoomIn, delegate);
+ d->RegisterHandler("zoomOut", &Delegate::ZoomOut, delegate);
+ d->RegisterHandler("resetZoom", &Delegate::ResetZoom, delegate);
+ d->RegisterHandler("setDevicesDiscoveryConfig",
+ &Delegate::SetDevicesDiscoveryConfig, delegate);
+ d->RegisterHandler("setDevicesUpdatesEnabled",
+ &Delegate::SetDevicesUpdatesEnabled, delegate);
+ d->RegisterHandler("performActionOnRemotePage",
+ &Delegate::PerformActionOnRemotePage, delegate);
+ d->RegisterHandler("openRemotePage", &Delegate::OpenRemotePage, delegate);
+ d->RegisterHandler("openNodeFrontend", &Delegate::OpenNodeFrontend, delegate);
+ d->RegisterHandler("dispatchProtocolMessage",
+ &Delegate::DispatchProtocolMessageFromDevToolsFrontend,
+ delegate);
+ d->RegisterHandler("recordEnumeratedHistogram",
+ &Delegate::RecordEnumeratedHistogram, delegate);
+ d->RegisterHandlerWithCallback("sendJsonRequest",
+ &Delegate::SendJsonRequest, delegate);
+ d->RegisterHandlerWithCallback("getPreferences",
+ &Delegate::GetPreferences, delegate);
+ d->RegisterHandler("setPreference",
+ &Delegate::SetPreference, delegate);
+ d->RegisterHandler("removePreference",
+ &Delegate::RemovePreference, delegate);
+ d->RegisterHandler("clearPreferences",
+ &Delegate::ClearPreferences, delegate);
+ d->RegisterHandlerWithCallback("reattach",
+ &Delegate::Reattach, delegate);
+ d->RegisterHandler("readyForTest",
+ &Delegate::ReadyForTest, delegate);
+ return d;
+}
diff --git a/chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.h b/chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
new file mode 100644
index 00000000000..db6a8f10178
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
@@ -0,0 +1,110 @@
+// 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_DEVTOOLS_DEVTOOLS_EMBEDDER_MESSAGE_DISPATCHER_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_EMBEDDER_MESSAGE_DISPATCHER_H_
+
+#include <map>
+#include <string>
+
+#include "base/callback.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace base {
+class ListValue;
+class Value;
+}
+
+/**
+ * Dispatcher for messages sent from the DevTools frontend running in an
+ * isolated renderer (on chrome-devtools://) to the embedder in the browser.
+ *
+ * The messages are sent via InspectorFrontendHost.sendMessageToEmbedder method.
+ */
+class DevToolsEmbedderMessageDispatcher {
+ public:
+ class Delegate {
+ public:
+ using DispatchCallback = base::Callback<void(const base::Value*)>;
+
+ virtual ~Delegate() {}
+
+ virtual void ActivateWindow() = 0;
+ virtual void CloseWindow() = 0;
+ virtual void LoadCompleted() = 0;
+ virtual void SetInspectedPageBounds(const gfx::Rect& rect) = 0;
+ virtual void InspectElementCompleted() = 0;
+ virtual void InspectedURLChanged(const std::string& url) = 0;
+ virtual void SetIsDocked(const DispatchCallback& callback,
+ bool is_docked) = 0;
+ virtual void OpenInNewTab(const std::string& url) = 0;
+ virtual void SaveToFile(const std::string& url,
+ const std::string& content,
+ bool save_as) = 0;
+ virtual void AppendToFile(const std::string& url,
+ const std::string& content) = 0;
+ virtual void RequestFileSystems() = 0;
+ virtual void AddFileSystem(const std::string& file_system_path) = 0;
+ virtual void RemoveFileSystem(const std::string& file_system_path) = 0;
+ virtual void UpgradeDraggedFileSystemPermissions(
+ const std::string& file_system_url) = 0;
+ virtual void IndexPath(int index_request_id,
+ const std::string& file_system_path) = 0;
+ virtual void StopIndexing(int index_request_id) = 0;
+ virtual void LoadNetworkResource(const DispatchCallback& callback,
+ const std::string& url,
+ const std::string& headers,
+ int stream_id) = 0;
+ virtual void SearchInPath(int search_request_id,
+ const std::string& file_system_path,
+ const std::string& query) = 0;
+ virtual void SetWhitelistedShortcuts(const std::string& message) = 0;
+ virtual void SetEyeDropperActive(bool active) = 0;
+ virtual void ShowCertificateViewer(const std::string& cert_chain) = 0;
+ virtual void ZoomIn() = 0;
+ virtual void ZoomOut() = 0;
+ virtual void ResetZoom() = 0;
+ virtual void SetDevicesUpdatesEnabled(bool enabled) = 0;
+ virtual void SetDevicesDiscoveryConfig(
+ bool discover_usb_devices,
+ bool port_forwarding_enabled,
+ const std::string& port_forwarding_config,
+ bool network_discovery_enabled,
+ const std::string& network_discovery_config) = 0;
+ virtual void PerformActionOnRemotePage(const std::string& page_id,
+ const std::string& action) = 0;
+ virtual void OpenRemotePage(const std::string& browser_id,
+ const std::string& url) = 0;
+ virtual void OpenNodeFrontend() = 0;
+ virtual void GetPreferences(const DispatchCallback& callback) = 0;
+ virtual void SetPreference(const std::string& name,
+ const std::string& value) = 0;
+ virtual void RemovePreference(const std::string& name) = 0;
+ virtual void ClearPreferences() = 0;
+ virtual void DispatchProtocolMessageFromDevToolsFrontend(
+ const std::string& message) = 0;
+ virtual void RecordEnumeratedHistogram(const std::string& name,
+ int sample,
+ int boundary_value) = 0;
+ virtual void SendJsonRequest(const DispatchCallback& callback,
+ const std::string& browser_id,
+ const std::string& url) = 0;
+ virtual void Reattach(const DispatchCallback& callback) = 0;
+ virtual void ReadyForTest() = 0;
+ };
+
+ using DispatchCallback = Delegate::DispatchCallback;
+
+ virtual ~DevToolsEmbedderMessageDispatcher() {}
+ virtual bool Dispatch(const DispatchCallback& callback,
+ const std::string& method,
+ const base::ListValue* params) = 0;
+
+ static DevToolsEmbedderMessageDispatcher* CreateForDevToolsFrontend(
+ Delegate* delegate);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_EMBEDDER_MESSAGE_DISPATCHER_H_
diff --git a/chromium/chrome/browser/devtools/devtools_eye_dropper.cc b/chromium/chrome/browser/devtools/devtools_eye_dropper.cc
new file mode 100644
index 00000000000..bb38173b238
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_eye_dropper.cc
@@ -0,0 +1,249 @@
+// 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/devtools/devtools_eye_dropper.h"
+
+#include "base/bind.h"
+#include "build/build_config.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/cursor_info.h"
+#include "content/public/common/screen_info.h"
+#include "third_party/WebKit/public/platform/WebInputEvent.h"
+#include "third_party/WebKit/public/platform/WebMouseEvent.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkPath.h"
+#include "ui/gfx/geometry/size_conversions.h"
+
+DevToolsEyeDropper::DevToolsEyeDropper(content::WebContents* web_contents,
+ EyeDropperCallback callback)
+ : content::WebContentsObserver(web_contents),
+ callback_(callback),
+ last_cursor_x_(-1),
+ last_cursor_y_(-1),
+ host_(nullptr),
+ weak_factory_(this) {
+ mouse_event_callback_ =
+ base::Bind(&DevToolsEyeDropper::HandleMouseEvent, base::Unretained(this));
+ content::RenderViewHost* rvh = web_contents->GetRenderViewHost();
+ if (rvh) {
+ AttachToHost(rvh->GetWidget());
+ UpdateFrame();
+ }
+}
+
+DevToolsEyeDropper::~DevToolsEyeDropper() {
+ DetachFromHost();
+}
+
+void DevToolsEyeDropper::AttachToHost(content::RenderWidgetHost* host) {
+ host_ = host;
+ host_->AddMouseEventCallback(mouse_event_callback_);
+}
+
+void DevToolsEyeDropper::DetachFromHost() {
+ if (!host_)
+ return;
+ host_->RemoveMouseEventCallback(mouse_event_callback_);
+ content::CursorInfo cursor_info;
+ cursor_info.type = blink::WebCursorInfo::kTypePointer;
+ host_->SetCursor(cursor_info);
+ host_ = nullptr;
+}
+
+void DevToolsEyeDropper::RenderViewCreated(content::RenderViewHost* host) {
+ if (!host_) {
+ AttachToHost(host->GetWidget());
+ UpdateFrame();
+ }
+}
+
+void DevToolsEyeDropper::RenderViewDeleted(content::RenderViewHost* host) {
+ if (host->GetWidget() == host_) {
+ DetachFromHost();
+ ResetFrame();
+ }
+}
+
+void DevToolsEyeDropper::RenderViewHostChanged(
+ content::RenderViewHost* old_host,
+ content::RenderViewHost* new_host) {
+ if ((old_host && old_host->GetWidget() == host_) || (!old_host && !host_)) {
+ DetachFromHost();
+ AttachToHost(new_host->GetWidget());
+ UpdateFrame();
+ }
+}
+
+void DevToolsEyeDropper::DidReceiveCompositorFrame() {
+ UpdateFrame();
+}
+
+void DevToolsEyeDropper::UpdateFrame() {
+ if (!host_ || !host_->GetView())
+ return;
+
+ // TODO(miu): This is the wrong size. It's the size of the view on-screen, and
+ // not the rendering size of the view. The latter is what is wanted here, so
+ // that the resulting bitmap's pixel coordinates line-up with the
+ // blink::WebMouseEvent coordinates. http://crbug.com/73362
+ 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);
+}
+
+void DevToolsEyeDropper::ResetFrame() {
+ frame_.reset();
+ last_cursor_x_ = -1;
+ last_cursor_y_ = -1;
+}
+
+void DevToolsEyeDropper::FrameUpdated(const SkBitmap& bitmap,
+ content::ReadbackResponse response) {
+ if (response == content::READBACK_SUCCESS) {
+ frame_ = bitmap;
+ UpdateCursor();
+ }
+}
+
+bool DevToolsEyeDropper::HandleMouseEvent(const blink::WebMouseEvent& event) {
+ last_cursor_x_ = event.PositionInWidget().x;
+ last_cursor_y_ = event.PositionInWidget().y;
+ if (frame_.drawsNothing())
+ return true;
+
+ if (event.button == blink::WebMouseEvent::Button::kLeft &&
+ (event.GetType() == blink::WebInputEvent::kMouseDown ||
+ event.GetType() == blink::WebInputEvent::kMouseMove)) {
+ if (last_cursor_x_ < 0 || last_cursor_x_ >= frame_.width() ||
+ last_cursor_y_ < 0 || last_cursor_y_ >= frame_.height()) {
+ return true;
+ }
+
+ SkColor sk_color = frame_.getColor(last_cursor_x_, last_cursor_y_);
+ callback_.Run(SkColorGetR(sk_color), SkColorGetG(sk_color),
+ SkColorGetB(sk_color), SkColorGetA(sk_color));
+ }
+ UpdateCursor();
+ return true;
+}
+
+void DevToolsEyeDropper::UpdateCursor() {
+ if (!host_ || frame_.drawsNothing())
+ return;
+
+ if (last_cursor_x_ < 0 || last_cursor_x_ >= frame_.width() ||
+ last_cursor_y_ < 0 || last_cursor_y_ >= frame_.height()) {
+ return;
+ }
+
+// Due to platform limitations, we are using two different cursors
+// depending on the platform. Mac and Win have large cursors with two circles
+// for original spot and its magnified projection; Linux gets smaller (64 px)
+// magnified projection only with centered hotspot.
+// Mac Retina requires cursor to be > 120px in order to render smoothly.
+
+#if defined(OS_LINUX)
+ const float kCursorSize = 63;
+ const float kDiameter = 63;
+ const float kHotspotOffset = 32;
+ const float kHotspotRadius = 0;
+ const float kPixelSize = 9;
+#else
+ const float kCursorSize = 150;
+ const float kDiameter = 110;
+ const float kHotspotOffset = 25;
+ const float kHotspotRadius = 5;
+ const float kPixelSize = 10;
+#endif
+
+ content::ScreenInfo screen_info;
+ host_->GetScreenInfo(&screen_info);
+ double device_scale_factor = screen_info.device_scale_factor;
+
+ SkBitmap result;
+ result.allocN32Pixels(kCursorSize * device_scale_factor,
+ kCursorSize * device_scale_factor);
+ result.eraseARGB(0, 0, 0, 0);
+
+ SkCanvas canvas(result);
+ canvas.scale(device_scale_factor, device_scale_factor);
+ canvas.translate(0.5f, 0.5f);
+
+ SkPaint paint;
+
+ // Paint original spot with cross.
+ if (kHotspotRadius > 0) {
+ paint.setStrokeWidth(1);
+ paint.setAntiAlias(false);
+ paint.setColor(SK_ColorDKGRAY);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ canvas.drawLine(kHotspotOffset, kHotspotOffset - 2 * kHotspotRadius,
+ kHotspotOffset, kHotspotOffset - kHotspotRadius, paint);
+ canvas.drawLine(kHotspotOffset, kHotspotOffset + kHotspotRadius,
+ kHotspotOffset, kHotspotOffset + 2 * kHotspotRadius, paint);
+ canvas.drawLine(kHotspotOffset - 2 * kHotspotRadius, kHotspotOffset,
+ kHotspotOffset - kHotspotRadius, kHotspotOffset, paint);
+ canvas.drawLine(kHotspotOffset + kHotspotRadius, kHotspotOffset,
+ kHotspotOffset + 2 * kHotspotRadius, kHotspotOffset, paint);
+
+ paint.setStrokeWidth(2);
+ paint.setAntiAlias(true);
+ canvas.drawCircle(kHotspotOffset, kHotspotOffset, kHotspotRadius, paint);
+ }
+
+ // Clip circle for magnified projection.
+ float padding = (kCursorSize - kDiameter) / 2;
+ SkPath clip_path;
+ clip_path.addOval(SkRect::MakeXYWH(padding, padding, kDiameter, kDiameter));
+ clip_path.close();
+ canvas.clipPath(clip_path, SkClipOp::kIntersect, true);
+
+ // Project pixels.
+ int pixel_count = kDiameter / kPixelSize;
+ SkRect src_rect = SkRect::MakeXYWH(last_cursor_x_ - pixel_count / 2,
+ last_cursor_y_ - pixel_count / 2,
+ pixel_count, pixel_count);
+ SkRect dst_rect = SkRect::MakeXYWH(padding, padding, kDiameter, kDiameter);
+ canvas.drawBitmapRect(frame_, src_rect, dst_rect, NULL);
+
+ // Paint grid.
+ paint.setStrokeWidth(1);
+ paint.setAntiAlias(false);
+ paint.setColor(SK_ColorGRAY);
+ for (int i = 0; i < pixel_count; ++i) {
+ canvas.drawLine(padding + i * kPixelSize, padding, padding + i * kPixelSize,
+ kCursorSize - padding, paint);
+ canvas.drawLine(padding, padding + i * kPixelSize, kCursorSize - padding,
+ padding + i * kPixelSize, paint);
+ }
+
+ // Paint central pixel in red.
+ SkRect pixel =
+ SkRect::MakeXYWH((kCursorSize - kPixelSize) / 2,
+ (kCursorSize - kPixelSize) / 2, kPixelSize, kPixelSize);
+ paint.setColor(SK_ColorRED);
+ paint.setStyle(SkPaint::kStroke_Style);
+ canvas.drawRect(pixel, paint);
+
+ // Paint outline.
+ paint.setStrokeWidth(2);
+ paint.setColor(SK_ColorDKGRAY);
+ paint.setAntiAlias(true);
+ canvas.drawCircle(kCursorSize / 2, kCursorSize / 2, kDiameter / 2, paint);
+
+ content::CursorInfo cursor_info;
+ cursor_info.type = blink::WebCursorInfo::kTypeCustom;
+ cursor_info.image_scale_factor = device_scale_factor;
+ cursor_info.custom_image = result;
+ cursor_info.hotspot = gfx::Point(kHotspotOffset * device_scale_factor,
+ kHotspotOffset * device_scale_factor);
+ host_->SetCursor(cursor_info);
+}
diff --git a/chromium/chrome/browser/devtools/devtools_eye_dropper.h b/chromium/chrome/browser/devtools/devtools_eye_dropper.h
new file mode 100644
index 00000000000..8d92d53c4e0
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_eye_dropper.h
@@ -0,0 +1,55 @@
+// 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_DEVTOOLS_DEVTOOLS_EYE_DROPPER_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_EYE_DROPPER_H_
+
+#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"
+
+namespace blink {
+class WebMouseEvent;
+}
+
+class DevToolsEyeDropper : public content::WebContentsObserver {
+ public:
+ typedef base::Callback<void(int, int, int, int)> EyeDropperCallback;
+
+ DevToolsEyeDropper(content::WebContents* web_contents,
+ EyeDropperCallback callback);
+ ~DevToolsEyeDropper() override;
+
+ private:
+ void AttachToHost(content::RenderWidgetHost* host);
+ void DetachFromHost();
+
+ // content::WebContentsObserver.
+ void DidReceiveCompositorFrame() override;
+ void RenderViewCreated(content::RenderViewHost* host) override;
+ void RenderViewDeleted(content::RenderViewHost* host) override;
+ void RenderViewHostChanged(content::RenderViewHost* old_host,
+ content::RenderViewHost* new_host) override;
+
+ void UpdateFrame();
+ void ResetFrame();
+ void FrameUpdated(const SkBitmap&, content::ReadbackResponse);
+ bool HandleMouseEvent(const blink::WebMouseEvent& event);
+ void UpdateCursor();
+
+ EyeDropperCallback callback_;
+ SkBitmap frame_;
+ int last_cursor_x_;
+ int last_cursor_y_;
+ content::RenderWidgetHost::MouseEventCallback mouse_event_callback_;
+ content::RenderWidgetHost* host_;
+ base::WeakPtrFactory<DevToolsEyeDropper> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsEyeDropper);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_EYE_DROPPER_H_
diff --git a/chromium/chrome/browser/devtools/devtools_file_helper.cc b/chromium/chrome/browser/devtools/devtools_file_helper.cc
new file mode 100644
index 00000000000..28599b7b178
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_file_helper.cc
@@ -0,0 +1,465 @@
+// 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/devtools/devtools_file_helper.h"
+
+#include <set>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "base/md5.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/value_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/devtools/devtools_file_watcher.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/chrome_select_file_policy.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/child_process_security_policy.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/url_constants.h"
+#include "storage/browser/fileapi/file_system_url.h"
+#include "storage/browser/fileapi/isolated_context.h"
+#include "storage/common/fileapi/file_system_util.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+
+using base::Bind;
+using base::Callback;
+using content::BrowserContext;
+using content::BrowserThread;
+using content::DownloadManager;
+using content::RenderViewHost;
+using content::WebContents;
+using std::set;
+
+namespace {
+
+static const char kRootName[] = "<root>";
+
+base::LazyInstance<base::FilePath>::Leaky
+ g_last_save_path = LAZY_INSTANCE_INITIALIZER;
+
+typedef Callback<void(const base::FilePath&)> SelectedCallback;
+typedef Callback<void(void)> CanceledCallback;
+
+class SelectFileDialog : public ui::SelectFileDialog::Listener,
+ public base::RefCounted<SelectFileDialog> {
+ public:
+ SelectFileDialog(const SelectedCallback& selected_callback,
+ const CanceledCallback& canceled_callback,
+ WebContents* web_contents)
+ : selected_callback_(selected_callback),
+ canceled_callback_(canceled_callback),
+ web_contents_(web_contents) {
+ select_file_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_contents));
+ }
+
+ void Show(ui::SelectFileDialog::Type type,
+ const base::FilePath& default_path) {
+ AddRef(); // Balanced in the three listener outcomes.
+ select_file_dialog_->SelectFile(
+ type,
+ base::string16(),
+ default_path,
+ NULL,
+ 0,
+ base::FilePath::StringType(),
+ platform_util::GetTopLevel(web_contents_->GetNativeView()),
+ NULL);
+ }
+
+ // ui::SelectFileDialog::Listener implementation.
+ void FileSelected(const base::FilePath& path,
+ int index,
+ void* params) override {
+ selected_callback_.Run(path);
+ Release(); // Balanced in ::Show.
+ }
+
+ void MultiFilesSelected(const std::vector<base::FilePath>& files,
+ void* params) override {
+ Release(); // Balanced in ::Show.
+ NOTREACHED() << "Should not be able to select multiple files";
+ }
+
+ void FileSelectionCanceled(void* params) override {
+ if (!canceled_callback_.is_null())
+ canceled_callback_.Run();
+ Release(); // Balanced in ::Show.
+ }
+
+ private:
+ friend class base::RefCounted<SelectFileDialog>;
+ ~SelectFileDialog() override {}
+
+ scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
+ SelectedCallback selected_callback_;
+ CanceledCallback canceled_callback_;
+ WebContents* web_contents_;
+
+ DISALLOW_COPY_AND_ASSIGN(SelectFileDialog);
+};
+
+void WriteToFile(const base::FilePath& path, const std::string& content) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ DCHECK(!path.empty());
+
+ base::WriteFile(path, content.c_str(), content.length());
+}
+
+void AppendToFile(const base::FilePath& path, const std::string& content) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ DCHECK(!path.empty());
+
+ base::AppendToFile(path, content.c_str(), content.size());
+}
+
+storage::IsolatedContext* isolated_context() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ storage::IsolatedContext* isolated_context =
+ storage::IsolatedContext::GetInstance();
+ DCHECK(isolated_context);
+ return isolated_context;
+}
+
+std::string RegisterFileSystem(WebContents* web_contents,
+ const base::FilePath& path) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ CHECK(web_contents->GetURL().SchemeIs(content::kChromeDevToolsScheme));
+ std::string root_name(kRootName);
+ std::string file_system_id = isolated_context()->RegisterFileSystemForPath(
+ storage::kFileSystemTypeNativeLocal, std::string(), path, &root_name);
+
+ content::ChildProcessSecurityPolicy* policy =
+ content::ChildProcessSecurityPolicy::GetInstance();
+ RenderViewHost* render_view_host = web_contents->GetRenderViewHost();
+ int renderer_id = render_view_host->GetProcess()->GetID();
+ policy->GrantReadFileSystem(renderer_id, file_system_id);
+ policy->GrantWriteFileSystem(renderer_id, file_system_id);
+ policy->GrantCreateFileForFileSystem(renderer_id, file_system_id);
+ policy->GrantDeleteFromFileSystem(renderer_id, file_system_id);
+
+ // We only need file level access for reading FileEntries. Saving FileEntries
+ // just needs the file system to have read/write access, which is granted
+ // above if required.
+ if (!policy->CanReadFile(renderer_id, path))
+ policy->GrantReadFile(renderer_id, path);
+ return file_system_id;
+}
+
+DevToolsFileHelper::FileSystem CreateFileSystemStruct(
+ WebContents* web_contents,
+ const std::string& file_system_id,
+ const std::string& file_system_path) {
+ const GURL origin = web_contents->GetURL().GetOrigin();
+ std::string file_system_name =
+ storage::GetIsolatedFileSystemName(origin, file_system_id);
+ std::string root_url = storage::GetIsolatedFileSystemRootURIString(
+ origin, file_system_id, kRootName);
+ return DevToolsFileHelper::FileSystem(file_system_name,
+ root_url,
+ file_system_path);
+}
+
+set<std::string> GetAddedFileSystemPaths(Profile* profile) {
+ const base::DictionaryValue* file_systems_paths_value =
+ profile->GetPrefs()->GetDictionary(prefs::kDevToolsFileSystemPaths);
+ set<std::string> result;
+ for (base::DictionaryValue::Iterator it(*file_systems_paths_value);
+ !it.IsAtEnd(); it.Advance()) {
+ result.insert(it.key());
+ }
+ return result;
+}
+
+} // namespace
+
+DevToolsFileHelper::FileSystem::FileSystem() {
+}
+
+DevToolsFileHelper::FileSystem::FileSystem(const std::string& file_system_name,
+ const std::string& root_url,
+ const std::string& file_system_path)
+ : file_system_name(file_system_name),
+ root_url(root_url),
+ file_system_path(file_system_path) {
+}
+
+DevToolsFileHelper::DevToolsFileHelper(WebContents* web_contents,
+ Profile* profile,
+ Delegate* delegate)
+ : web_contents_(web_contents),
+ profile_(profile),
+ delegate_(delegate),
+ weak_factory_(this) {
+ pref_change_registrar_.Init(profile_->GetPrefs());
+ pref_change_registrar_.Add(prefs::kDevToolsFileSystemPaths,
+ base::Bind(&DevToolsFileHelper::FileSystemPathsSettingChanged,
+ base::Unretained(this)));
+ file_watcher_.reset(new DevToolsFileWatcher(
+ base::Bind(&DevToolsFileHelper::FilePathsChanged,
+ weak_factory_.GetWeakPtr())));
+}
+
+DevToolsFileHelper::~DevToolsFileHelper() {
+ BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE,
+ file_watcher_.release());
+}
+
+void DevToolsFileHelper::Save(const std::string& url,
+ const std::string& content,
+ bool save_as,
+ const SaveCallback& saveCallback,
+ const SaveCallback& cancelCallback) {
+ PathsMap::iterator it = saved_files_.find(url);
+ if (it != saved_files_.end() && !save_as) {
+ SaveAsFileSelected(url, content, saveCallback, it->second);
+ return;
+ }
+
+ const base::DictionaryValue* file_map =
+ profile_->GetPrefs()->GetDictionary(prefs::kDevToolsEditedFiles);
+ base::FilePath initial_path;
+
+ const base::Value* path_value;
+ if (file_map->Get(base::MD5String(url), &path_value))
+ base::GetValueAsFilePath(*path_value, &initial_path);
+
+ if (initial_path.empty()) {
+ GURL gurl(url);
+ std::string suggested_file_name = gurl.is_valid() ?
+ gurl.ExtractFileName() : url;
+
+ if (suggested_file_name.length() > 64)
+ suggested_file_name = suggested_file_name.substr(0, 64);
+
+ if (!g_last_save_path.Pointer()->empty()) {
+ initial_path = g_last_save_path.Pointer()->DirName().AppendASCII(
+ suggested_file_name);
+ } else {
+ base::FilePath download_path = DownloadPrefs::FromDownloadManager(
+ BrowserContext::GetDownloadManager(profile_))->DownloadPath();
+ initial_path = download_path.AppendASCII(suggested_file_name);
+ }
+ }
+
+ scoped_refptr<SelectFileDialog> select_file_dialog = new SelectFileDialog(
+ Bind(&DevToolsFileHelper::SaveAsFileSelected,
+ weak_factory_.GetWeakPtr(),
+ url,
+ content,
+ saveCallback),
+ cancelCallback,
+ web_contents_);
+ select_file_dialog->Show(ui::SelectFileDialog::SELECT_SAVEAS_FILE,
+ initial_path);
+}
+
+void DevToolsFileHelper::Append(const std::string& url,
+ const std::string& content,
+ const AppendCallback& callback) {
+ PathsMap::iterator it = saved_files_.find(url);
+ if (it == saved_files_.end())
+ return;
+ callback.Run();
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ BindOnce(&AppendToFile, it->second, content));
+}
+
+void DevToolsFileHelper::SaveAsFileSelected(const std::string& url,
+ const std::string& content,
+ const SaveCallback& callback,
+ const base::FilePath& path) {
+ *g_last_save_path.Pointer() = path;
+ saved_files_[url] = path;
+
+ DictionaryPrefUpdate update(profile_->GetPrefs(),
+ prefs::kDevToolsEditedFiles);
+ base::DictionaryValue* files_map = update.Get();
+ files_map->SetWithoutPathExpansion(base::MD5String(url),
+ base::CreateFilePathValue(path));
+ callback.Run();
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ BindOnce(&WriteToFile, path, content));
+}
+
+void DevToolsFileHelper::AddFileSystem(
+ const std::string& file_system_path,
+ const ShowInfoBarCallback& show_info_bar_callback) {
+ if (file_system_path.empty()) {
+ scoped_refptr<SelectFileDialog> select_file_dialog = new SelectFileDialog(
+ Bind(&DevToolsFileHelper::InnerAddFileSystem,
+ weak_factory_.GetWeakPtr(), show_info_bar_callback),
+ base::Closure(),
+ web_contents_);
+ select_file_dialog->Show(ui::SelectFileDialog::SELECT_FOLDER,
+ base::FilePath());
+ } else {
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ BindOnce(&DevToolsFileHelper::CheckProjectFileExistsAndAddFileSystem,
+ weak_factory_.GetWeakPtr(), show_info_bar_callback,
+ base::FilePath::FromUTF8Unsafe(file_system_path)));
+ }
+}
+
+void DevToolsFileHelper::CheckProjectFileExistsAndAddFileSystem(
+ const ShowInfoBarCallback& show_info_bar_callback,
+ const base::FilePath& path) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ if (base::PathExists(path.Append(FILE_PATH_LITERAL(".devtools")))) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ BindOnce(&DevToolsFileHelper::InnerAddFileSystem,
+ weak_factory_.GetWeakPtr(), show_info_bar_callback, path));
+ }
+}
+
+void DevToolsFileHelper::UpgradeDraggedFileSystemPermissions(
+ const std::string& file_system_url,
+ const ShowInfoBarCallback& show_info_bar_callback) {
+ storage::FileSystemURL root_url =
+ isolated_context()->CrackURL(GURL(file_system_url));
+ if (!root_url.is_valid() || !root_url.path().empty())
+ return;
+
+ std::vector<storage::MountPoints::MountPointInfo> mount_points;
+ isolated_context()->GetDraggedFileInfo(root_url.filesystem_id(),
+ &mount_points);
+
+ std::vector<storage::MountPoints::MountPointInfo>::const_iterator it =
+ mount_points.begin();
+ for (; it != mount_points.end(); ++it)
+ InnerAddFileSystem(show_info_bar_callback, it->path);
+}
+
+void DevToolsFileHelper::InnerAddFileSystem(
+ const ShowInfoBarCallback& show_info_bar_callback,
+ const base::FilePath& path) {
+ std::string file_system_path = path.AsUTF8Unsafe();
+
+ const base::DictionaryValue* file_systems_paths_value =
+ profile_->GetPrefs()->GetDictionary(prefs::kDevToolsFileSystemPaths);
+ if (file_systems_paths_value->HasKey(file_system_path))
+ return;
+
+ std::string path_display_name = path.AsEndingWithSeparator().AsUTF8Unsafe();
+ base::string16 message = l10n_util::GetStringFUTF16(
+ IDS_DEV_TOOLS_CONFIRM_ADD_FILE_SYSTEM_MESSAGE,
+ base::UTF8ToUTF16(path_display_name));
+ show_info_bar_callback.Run(
+ message,
+ Bind(&DevToolsFileHelper::AddUserConfirmedFileSystem,
+ weak_factory_.GetWeakPtr(), path));
+}
+
+void DevToolsFileHelper::AddUserConfirmedFileSystem(
+ const base::FilePath& path,
+ bool allowed) {
+ if (!allowed)
+ return;
+
+ std::string file_system_id = RegisterFileSystem(web_contents_, path);
+ std::string file_system_path = path.AsUTF8Unsafe();
+
+ DictionaryPrefUpdate update(profile_->GetPrefs(),
+ prefs::kDevToolsFileSystemPaths);
+ base::DictionaryValue* file_systems_paths_value = update.Get();
+ file_systems_paths_value->SetWithoutPathExpansion(
+ file_system_path, base::MakeUnique<base::Value>());
+}
+
+std::vector<DevToolsFileHelper::FileSystem>
+DevToolsFileHelper::GetFileSystems() {
+ file_system_paths_ = GetAddedFileSystemPaths(profile_);
+ std::vector<FileSystem> file_systems;
+ for (auto file_system_path : file_system_paths_) {
+ base::FilePath path = base::FilePath::FromUTF8Unsafe(file_system_path);
+ std::string file_system_id = RegisterFileSystem(web_contents_, path);
+ FileSystem filesystem = CreateFileSystemStruct(web_contents_,
+ file_system_id,
+ file_system_path);
+ file_systems.push_back(filesystem);
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ BindOnce(&DevToolsFileWatcher::AddWatch,
+ base::Unretained(file_watcher_.get()), path));
+ }
+ return file_systems;
+}
+
+void DevToolsFileHelper::RemoveFileSystem(const std::string& file_system_path) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ base::FilePath path = base::FilePath::FromUTF8Unsafe(file_system_path);
+ isolated_context()->RevokeFileSystemByPath(path);
+
+ DictionaryPrefUpdate update(profile_->GetPrefs(),
+ prefs::kDevToolsFileSystemPaths);
+ base::DictionaryValue* file_systems_paths_value = update.Get();
+ file_systems_paths_value->RemoveWithoutPathExpansion(file_system_path, NULL);
+}
+
+bool DevToolsFileHelper::IsFileSystemAdded(
+ const std::string& file_system_path) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ set<std::string> file_system_paths = GetAddedFileSystemPaths(profile_);
+ return file_system_paths.find(file_system_path) != file_system_paths.end();
+}
+
+void DevToolsFileHelper::FileSystemPathsSettingChanged() {
+ std::set<std::string> remaining;
+ remaining.swap(file_system_paths_);
+
+ for (auto file_system_path : GetAddedFileSystemPaths(profile_)) {
+ if (remaining.find(file_system_path) == remaining.end()) {
+ base::FilePath path = base::FilePath::FromUTF8Unsafe(file_system_path);
+ std::string file_system_id = RegisterFileSystem(web_contents_, path);
+ FileSystem filesystem = CreateFileSystemStruct(web_contents_,
+ file_system_id,
+ file_system_path);
+ delegate_->FileSystemAdded(filesystem);
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ BindOnce(&DevToolsFileWatcher::AddWatch,
+ base::Unretained(file_watcher_.get()), path));
+ } else {
+ remaining.erase(file_system_path);
+ }
+ file_system_paths_.insert(file_system_path);
+ }
+
+ for (auto file_system_path : remaining) {
+ delegate_->FileSystemRemoved(file_system_path);
+ base::FilePath path = base::FilePath::FromUTF8Unsafe(file_system_path);
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ BindOnce(&DevToolsFileWatcher::RemoveWatch,
+ base::Unretained(file_watcher_.get()), path));
+ }
+}
+
+void DevToolsFileHelper::FilePathsChanged(
+ const std::vector<std::string>& changed_paths,
+ const std::vector<std::string>& added_paths,
+ const std::vector<std::string>& removed_paths) {
+ delegate_->FilePathsChanged(changed_paths, added_paths, removed_paths);
+}
diff --git a/chromium/chrome/browser/devtools/devtools_file_helper.h b/chromium/chrome/browser/devtools/devtools_file_helper.h
new file mode 100644
index 00000000000..e613d02595d
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_file_helper.h
@@ -0,0 +1,154 @@
+// 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.
+
+#ifndef CHROME_BROWSER_DEVTOOLS_DEVTOOLS_FILE_HELPER_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_FILE_HELPER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+#include "components/prefs/pref_change_registrar.h"
+
+class DevToolsFileWatcher;
+class Profile;
+
+namespace base {
+class FilePath;
+}
+
+namespace content {
+class WebContents;
+}
+
+class DevToolsFileHelper {
+ public:
+ struct FileSystem {
+ FileSystem();
+ FileSystem(const std::string& file_system_name,
+ const std::string& root_url,
+ const std::string& file_system_path);
+
+ std::string file_system_name;
+ std::string root_url;
+ std::string file_system_path;
+ };
+
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+ virtual void FileSystemAdded(const FileSystem& file_system) = 0;
+ virtual void FileSystemRemoved(const std::string& file_system_path) = 0;
+ virtual void FilePathsChanged(
+ const std::vector<std::string>& changed_paths,
+ const std::vector<std::string>& added_paths,
+ const std::vector<std::string>& removed_paths) = 0;
+ };
+
+ DevToolsFileHelper(content::WebContents* web_contents, Profile* profile,
+ Delegate* delegate);
+ ~DevToolsFileHelper();
+
+ typedef base::Callback<void(void)> SaveCallback;
+ typedef base::Callback<void(void)> AppendCallback;
+ typedef base::Callback<void(const base::string16&,
+ const base::Callback<void(bool)>&)>
+ ShowInfoBarCallback;
+
+ // Saves |content| to the file and associates its path with given |url|.
+ // If client is calling this method with given |url| for the first time
+ // or |save_as| is true, confirmation dialog is shown to the user.
+ void Save(const std::string& url,
+ const std::string& content,
+ bool save_as,
+ const SaveCallback& saveCallback,
+ const SaveCallback& cancelCallback);
+
+ // Append |content| to the file that has been associated with given |url|.
+ // The |url| can be associated with a file via calling Save method.
+ // If the Save method has not been called for this |url|, then
+ // Append method does nothing.
+ void Append(const std::string& url,
+ const std::string& content,
+ const AppendCallback& callback);
+
+ // 1. If empty |file_system_path| is passed, shows select folder dialog.
+ // If user cancels folder selection, passes empty FileSystem struct to
+ // |callback|.
+ // If non-empty |file_system_path| is passed, verifies that corresponding
+ // folder contains the ".devtools" project file, if not, passes empty
+ // FileSystem struct to |callback|.
+ // 2. Shows infobar by means of |show_info_bar_callback| to let the user
+ // decide whether to grant security permissions or not.
+ // If user allows adding file system in infobar, grants renderer
+ // read/write permissions, registers isolated file system for it and
+ // passes FileSystem struct to |callback|. Saves file system path to prefs.
+ // If user denies adding file system in infobar, passes error string to
+ // |callback|.
+ void AddFileSystem(const std::string& file_system_path,
+ const ShowInfoBarCallback& show_info_bar_callback);
+
+ // Upgrades dragged file system permissions to a read-write access.
+ // Shows infobar by means of |show_info_bar_callback| to let the user decide
+ // whether to grant security permissions or not.
+ // If user allows adding file system in infobar, grants renderer read/write
+ // permissions, registers isolated file system for it and passes FileSystem
+ // struct to |callback|. Saves file system path to prefs.
+ // If user denies adding file system in infobar, passes error string to
+ // |callback|.
+ void UpgradeDraggedFileSystemPermissions(
+ const std::string& file_system_url,
+ const ShowInfoBarCallback& show_info_bar_callback);
+
+ // Loads file system paths from prefs, grants permissions and registers
+ // isolated file system for those of them that contain magic file and passes
+ // FileSystem structs for registered file systems to |callback|.
+ std::vector<FileSystem> GetFileSystems();
+
+ // Removes isolated file system for given |file_system_path|.
+ void RemoveFileSystem(const std::string& file_system_path);
+
+ // Returns whether access to the folder on given |file_system_path| was
+ // granted.
+ bool IsFileSystemAdded(const std::string& file_system_path);
+
+ private:
+ void SaveAsFileSelected(const std::string& url,
+ const std::string& content,
+ const SaveCallback& callback,
+ const base::FilePath& path);
+ void InnerAddFileSystem(
+ const ShowInfoBarCallback& show_info_bar_callback,
+ const base::FilePath& path);
+ void CheckProjectFileExistsAndAddFileSystem(
+ const ShowInfoBarCallback& show_info_bar_callback,
+ const base::FilePath& path);
+ void AddUserConfirmedFileSystem(
+ const base::FilePath& path,
+ bool allowed);
+ void FileSystemPathsSettingChanged();
+ void FilePathsChanged(const std::vector<std::string>& changed_paths,
+ const std::vector<std::string>& added_paths,
+ const std::vector<std::string>& removed_paths);
+
+ content::WebContents* web_contents_;
+ Profile* profile_;
+ DevToolsFileHelper::Delegate* delegate_;
+ typedef std::map<std::string, base::FilePath> PathsMap;
+ PathsMap saved_files_;
+ PrefChangeRegistrar pref_change_registrar_;
+ std::set<std::string> file_system_paths_;
+ std::unique_ptr<DevToolsFileWatcher> file_watcher_;
+ base::WeakPtrFactory<DevToolsFileHelper> weak_factory_;
+ DISALLOW_COPY_AND_ASSIGN(DevToolsFileHelper);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_FILE_HELPER_H_
diff --git a/chromium/chrome/browser/devtools/devtools_file_system_indexer.cc b/chromium/chrome/browser/devtools/devtools_file_system_indexer.cc
new file mode 100644
index 00000000000..b538fe1a205
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_file_system_indexer.cc
@@ -0,0 +1,459 @@
+// 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/devtools/devtools_file_system_indexer.h"
+
+#include <stddef.h>
+
+#include <iterator>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
+#include "base/files/file_util_proxy.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/public/browser/browser_thread.h"
+
+using base::Bind;
+using base::Callback;
+using base::FileEnumerator;
+using base::FilePath;
+using base::Time;
+using base::TimeDelta;
+using base::TimeTicks;
+using content::BrowserThread;
+using std::map;
+using std::set;
+using std::string;
+using std::vector;
+
+namespace {
+
+typedef int32_t Trigram;
+typedef char TrigramChar;
+typedef uint16_t FileId;
+
+const int kMinTimeoutBetweenWorkedNitification = 200;
+// Trigram characters include all ASCII printable characters (32-126) except for
+// the capital letters, because the index is case insensitive.
+const size_t kTrigramCharacterCount = 126 - 'Z' - 1 + 'A' - ' ' + 1;
+const size_t kTrigramCount =
+ kTrigramCharacterCount * kTrigramCharacterCount * kTrigramCharacterCount;
+const int kMaxReadLength = 10 * 1024;
+const TrigramChar kUndefinedTrigramChar = -1;
+const TrigramChar kBinaryTrigramChar = -2;
+const Trigram kUndefinedTrigram = -1;
+
+class Index {
+ public:
+ Index();
+ // Index is only instantiated as a leak LazyInstance, so the destructor is
+ // never called.
+ ~Index() = delete;
+
+ Time LastModifiedTimeForFile(const FilePath& file_path);
+ void SetTrigramsForFile(const FilePath& file_path,
+ const vector<Trigram>& index,
+ const Time& time);
+ vector<FilePath> Search(string query);
+ void NormalizeVectors();
+
+ private:
+ FileId GetFileId(const FilePath& file_path);
+
+ typedef map<FilePath, FileId> FileIdsMap;
+ FileIdsMap file_ids_;
+ FileId last_file_id_;
+ // The index in this vector is the trigram id.
+ vector<vector<FileId> > index_;
+ typedef map<FilePath, Time> IndexedFilesMap;
+ IndexedFilesMap index_times_;
+ vector<bool> is_normalized_;
+
+ DISALLOW_COPY_AND_ASSIGN(Index);
+};
+
+base::LazyInstance<Index>::Leaky g_trigram_index = LAZY_INSTANCE_INITIALIZER;
+
+TrigramChar TrigramCharForChar(char c) {
+ static TrigramChar* trigram_chars = nullptr;
+ if (!trigram_chars) {
+ trigram_chars = new TrigramChar[256];
+ for (size_t i = 0; i < 256; ++i) {
+ if (i > 127) {
+ trigram_chars[i] = kUndefinedTrigramChar;
+ continue;
+ }
+ char ch = static_cast<char>(i);
+ if (ch == '\t')
+ ch = ' ';
+ if (base::IsAsciiUpper(ch))
+ ch = ch - 'A' + 'a';
+
+ bool is_binary_char = ch < 9 || (ch >= 14 && ch < 32) || ch == 127;
+ if (is_binary_char) {
+ trigram_chars[i] = kBinaryTrigramChar;
+ continue;
+ }
+
+ if (ch < ' ') {
+ trigram_chars[i] = kUndefinedTrigramChar;
+ continue;
+ }
+
+ if (ch >= 'Z')
+ ch = ch - 'Z' - 1 + 'A';
+ ch -= ' ';
+ char signed_trigram_count = static_cast<char>(kTrigramCharacterCount);
+ CHECK(ch >= 0 && ch < signed_trigram_count);
+ trigram_chars[i] = ch;
+ }
+ }
+ unsigned char uc = static_cast<unsigned char>(c);
+ return trigram_chars[uc];
+}
+
+Trigram TrigramAtIndex(const vector<TrigramChar>& trigram_chars, size_t index) {
+ static int kTrigramCharacterCountSquared =
+ kTrigramCharacterCount * kTrigramCharacterCount;
+ if (trigram_chars[index] == kUndefinedTrigramChar ||
+ trigram_chars[index + 1] == kUndefinedTrigramChar ||
+ trigram_chars[index + 2] == kUndefinedTrigramChar)
+ return kUndefinedTrigram;
+ Trigram trigram = kTrigramCharacterCountSquared * trigram_chars[index] +
+ kTrigramCharacterCount * trigram_chars[index + 1] +
+ trigram_chars[index + 2];
+ return trigram;
+}
+
+Index::Index() : last_file_id_(0) {
+ index_.resize(kTrigramCount);
+ is_normalized_.resize(kTrigramCount);
+ std::fill(is_normalized_.begin(), is_normalized_.end(), true);
+}
+
+Time Index::LastModifiedTimeForFile(const FilePath& file_path) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ Time last_modified_time;
+ if (index_times_.find(file_path) != index_times_.end())
+ last_modified_time = index_times_[file_path];
+ return last_modified_time;
+}
+
+void Index::SetTrigramsForFile(const FilePath& file_path,
+ const vector<Trigram>& index,
+ const Time& time) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ FileId file_id = GetFileId(file_path);
+ vector<Trigram>::const_iterator it = index.begin();
+ for (; it != index.end(); ++it) {
+ Trigram trigram = *it;
+ index_[trigram].push_back(file_id);
+ is_normalized_[trigram] = false;
+ }
+ index_times_[file_path] = time;
+}
+
+vector<FilePath> Index::Search(string query) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ const char* data = query.c_str();
+ vector<TrigramChar> trigram_chars;
+ trigram_chars.reserve(query.size());
+ for (size_t i = 0; i < query.size(); ++i) {
+ TrigramChar trigram_char = TrigramCharForChar(data[i]);
+ if (trigram_char == kBinaryTrigramChar)
+ trigram_char = kUndefinedTrigramChar;
+ trigram_chars.push_back(trigram_char);
+ }
+ vector<Trigram> trigrams;
+ for (size_t i = 0; i + 2 < query.size(); ++i) {
+ Trigram trigram = TrigramAtIndex(trigram_chars, i);
+ if (trigram != kUndefinedTrigram)
+ trigrams.push_back(trigram);
+ }
+ set<FileId> file_ids;
+ bool first = true;
+ vector<Trigram>::const_iterator it = trigrams.begin();
+ for (; it != trigrams.end(); ++it) {
+ Trigram trigram = *it;
+ if (first) {
+ std::copy(index_[trigram].begin(),
+ index_[trigram].end(),
+ std::inserter(file_ids, file_ids.begin()));
+ first = false;
+ continue;
+ }
+ set<FileId> intersection = base::STLSetIntersection<set<FileId> >(
+ file_ids, index_[trigram]);
+ file_ids.swap(intersection);
+ }
+ vector<FilePath> result;
+ FileIdsMap::const_iterator ids_it = file_ids_.begin();
+ for (; ids_it != file_ids_.end(); ++ids_it) {
+ if (trigrams.size() == 0 ||
+ file_ids.find(ids_it->second) != file_ids.end()) {
+ result.push_back(ids_it->first);
+ }
+ }
+ return result;
+}
+
+FileId Index::GetFileId(const FilePath& file_path) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ string file_path_str = file_path.AsUTF8Unsafe();
+ if (file_ids_.find(file_path) != file_ids_.end())
+ return file_ids_[file_path];
+ file_ids_[file_path] = ++last_file_id_;
+ return last_file_id_;
+}
+
+void Index::NormalizeVectors() {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ for (size_t i = 0; i < kTrigramCount; ++i) {
+ if (!is_normalized_[i]) {
+ std::sort(index_[i].begin(), index_[i].end());
+ if (index_[i].capacity() > index_[i].size())
+ vector<FileId>(index_[i]).swap(index_[i]);
+ is_normalized_[i] = true;
+ }
+ }
+}
+
+typedef Callback<void(bool, const vector<bool>&)> IndexerCallback;
+
+} // namespace
+
+DevToolsFileSystemIndexer::FileSystemIndexingJob::FileSystemIndexingJob(
+ const FilePath& file_system_path,
+ const TotalWorkCallback& total_work_callback,
+ const WorkedCallback& worked_callback,
+ const DoneCallback& done_callback)
+ : file_system_path_(file_system_path),
+ total_work_callback_(total_work_callback),
+ worked_callback_(worked_callback),
+ done_callback_(done_callback),
+ current_file_(
+ BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get()),
+ files_indexed_(0),
+ stopped_(false) {
+ current_trigrams_set_.resize(kTrigramCount);
+ current_trigrams_.reserve(kTrigramCount);
+}
+
+DevToolsFileSystemIndexer::FileSystemIndexingJob::~FileSystemIndexingJob() {}
+
+void DevToolsFileSystemIndexer::FileSystemIndexingJob::Start() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ BindOnce(&FileSystemIndexingJob::CollectFilesToIndex, this));
+}
+
+void DevToolsFileSystemIndexer::FileSystemIndexingJob::Stop() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ BindOnce(&FileSystemIndexingJob::StopOnFileThread, this));
+}
+
+void DevToolsFileSystemIndexer::FileSystemIndexingJob::StopOnFileThread() {
+ stopped_ = true;
+}
+
+void DevToolsFileSystemIndexer::FileSystemIndexingJob::CollectFilesToIndex() {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ if (stopped_)
+ return;
+ if (!file_enumerator_) {
+ file_enumerator_.reset(
+ new FileEnumerator(file_system_path_, true, FileEnumerator::FILES));
+ }
+ FilePath file_path = file_enumerator_->Next();
+ if (file_path.empty()) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ BindOnce(total_work_callback_, file_path_times_.size()));
+ indexing_it_ = file_path_times_.begin();
+ IndexFiles();
+ return;
+ }
+ Time saved_last_modified_time =
+ g_trigram_index.Get().LastModifiedTimeForFile(file_path);
+ FileEnumerator::FileInfo file_info = file_enumerator_->GetInfo();
+ Time current_last_modified_time = file_info.GetLastModifiedTime();
+ if (current_last_modified_time > saved_last_modified_time) {
+ file_path_times_[file_path] = current_last_modified_time;
+ }
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ BindOnce(&FileSystemIndexingJob::CollectFilesToIndex, this));
+}
+
+void DevToolsFileSystemIndexer::FileSystemIndexingJob::IndexFiles() {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ if (stopped_)
+ return;
+ if (indexing_it_ == file_path_times_.end()) {
+ g_trigram_index.Get().NormalizeVectors();
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, done_callback_);
+ return;
+ }
+ FilePath file_path = indexing_it_->first;
+ current_file_.CreateOrOpen(
+ file_path,
+ base::File::FLAG_OPEN | base::File::FLAG_READ,
+ Bind(&FileSystemIndexingJob::StartFileIndexing, this));
+}
+
+void DevToolsFileSystemIndexer::FileSystemIndexingJob::StartFileIndexing(
+ base::File::Error error) {
+ if (!current_file_.IsValid()) {
+ FinishFileIndexing(false);
+ return;
+ }
+ current_file_offset_ = 0;
+ current_trigrams_.clear();
+ std::fill(current_trigrams_set_.begin(), current_trigrams_set_.end(), false);
+ ReadFromFile();
+}
+
+void DevToolsFileSystemIndexer::FileSystemIndexingJob::ReadFromFile() {
+ if (stopped_) {
+ CloseFile();
+ return;
+ }
+ current_file_.Read(current_file_offset_, kMaxReadLength,
+ Bind(&FileSystemIndexingJob::OnRead, this));
+}
+
+void DevToolsFileSystemIndexer::FileSystemIndexingJob::OnRead(
+ base::File::Error error,
+ const char* data,
+ int bytes_read) {
+ if (error != base::File::FILE_OK) {
+ FinishFileIndexing(false);
+ return;
+ }
+
+ if (!bytes_read || bytes_read < 3) {
+ FinishFileIndexing(true);
+ return;
+ }
+
+ size_t size = static_cast<size_t>(bytes_read);
+ vector<TrigramChar> trigram_chars;
+ trigram_chars.reserve(size);
+ for (size_t i = 0; i < size; ++i) {
+ TrigramChar trigram_char = TrigramCharForChar(data[i]);
+ if (trigram_char == kBinaryTrigramChar) {
+ current_trigrams_.clear();
+ FinishFileIndexing(true);
+ return;
+ }
+ trigram_chars.push_back(trigram_char);
+ }
+
+ for (size_t i = 0; i + 2 < size; ++i) {
+ Trigram trigram = TrigramAtIndex(trigram_chars, i);
+ if ((trigram != kUndefinedTrigram) && !current_trigrams_set_[trigram]) {
+ current_trigrams_set_[trigram] = true;
+ current_trigrams_.push_back(trigram);
+ }
+ }
+ current_file_offset_ += bytes_read - 2;
+ ReadFromFile();
+}
+
+void DevToolsFileSystemIndexer::FileSystemIndexingJob::FinishFileIndexing(
+ bool success) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ CloseFile();
+ if (success) {
+ FilePath file_path = indexing_it_->first;
+ g_trigram_index.Get().SetTrigramsForFile(
+ file_path, current_trigrams_, file_path_times_[file_path]);
+ }
+ ReportWorked();
+ ++indexing_it_;
+ IndexFiles();
+}
+
+void DevToolsFileSystemIndexer::FileSystemIndexingJob::CloseFile() {
+ if (current_file_.IsValid())
+ current_file_.Close(Bind(&FileSystemIndexingJob::CloseCallback, this));
+}
+
+void DevToolsFileSystemIndexer::FileSystemIndexingJob::CloseCallback(
+ base::File::Error error) {}
+
+void DevToolsFileSystemIndexer::FileSystemIndexingJob::ReportWorked() {
+ TimeTicks current_time = TimeTicks::Now();
+ bool should_send_worked_nitification = true;
+ if (!last_worked_notification_time_.is_null()) {
+ TimeDelta delta = current_time - last_worked_notification_time_;
+ if (delta.InMilliseconds() < kMinTimeoutBetweenWorkedNitification)
+ should_send_worked_nitification = false;
+ }
+ ++files_indexed_;
+ if (should_send_worked_nitification) {
+ last_worked_notification_time_ = current_time;
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ BindOnce(worked_callback_, files_indexed_));
+ files_indexed_ = 0;
+ }
+}
+
+DevToolsFileSystemIndexer::DevToolsFileSystemIndexer() {
+}
+
+DevToolsFileSystemIndexer::~DevToolsFileSystemIndexer() {}
+
+scoped_refptr<DevToolsFileSystemIndexer::FileSystemIndexingJob>
+DevToolsFileSystemIndexer::IndexPath(
+ const string& file_system_path,
+ const TotalWorkCallback& total_work_callback,
+ const WorkedCallback& worked_callback,
+ const DoneCallback& done_callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ scoped_refptr<FileSystemIndexingJob> indexing_job =
+ new FileSystemIndexingJob(FilePath::FromUTF8Unsafe(file_system_path),
+ total_work_callback,
+ worked_callback,
+ done_callback);
+ indexing_job->Start();
+ return indexing_job;
+}
+
+void DevToolsFileSystemIndexer::SearchInPath(const string& file_system_path,
+ const string& query,
+ const SearchCallback& callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ BindOnce(&DevToolsFileSystemIndexer::SearchInPathOnFileThread, this,
+ file_system_path, query, callback));
+}
+
+void DevToolsFileSystemIndexer::SearchInPathOnFileThread(
+ const string& file_system_path,
+ const string& query,
+ const SearchCallback& callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ vector<FilePath> file_paths = g_trigram_index.Get().Search(query);
+ vector<string> result;
+ FilePath path = FilePath::FromUTF8Unsafe(file_system_path);
+ vector<FilePath>::const_iterator it = file_paths.begin();
+ for (; it != file_paths.end(); ++it) {
+ if (path.IsParent(*it))
+ result.push_back(it->AsUTF8Unsafe());
+ }
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ BindOnce(callback, result));
+}
diff --git a/chromium/chrome/browser/devtools/devtools_file_system_indexer.h b/chromium/chrome/browser/devtools/devtools_file_system_indexer.h
new file mode 100644
index 00000000000..c747467c740
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_file_system_indexer.h
@@ -0,0 +1,108 @@
+// 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_DEVTOOLS_DEVTOOLS_FILE_SYSTEM_INDEXER_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_FILE_SYSTEM_INDEXER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/file_proxy.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+
+namespace base {
+class FilePath;
+class FileEnumerator;
+class Time;
+}
+
+class DevToolsFileSystemIndexer
+ : public base::RefCountedThreadSafe<DevToolsFileSystemIndexer> {
+ public:
+
+ typedef base::Callback<void(int)> TotalWorkCallback;
+ typedef base::Callback<void(int)> WorkedCallback;
+ typedef base::Callback<void()> DoneCallback;
+ typedef base::Callback<void(const std::vector<std::string>&)> SearchCallback;
+
+ class FileSystemIndexingJob : public base::RefCounted<FileSystemIndexingJob> {
+ public:
+ void Stop();
+
+ private:
+ friend class base::RefCounted<FileSystemIndexingJob>;
+ friend class DevToolsFileSystemIndexer;
+ FileSystemIndexingJob(const base::FilePath& file_system_path,
+ const TotalWorkCallback& total_work_callback,
+ const WorkedCallback& worked_callback,
+ const DoneCallback& done_callback);
+ virtual ~FileSystemIndexingJob();
+
+ void Start();
+ void StopOnFileThread();
+ void CollectFilesToIndex();
+ void IndexFiles();
+ void StartFileIndexing(base::File::Error error);
+ void ReadFromFile();
+ void OnRead(base::File::Error error,
+ const char* data,
+ int bytes_read);
+ void FinishFileIndexing(bool success);
+ void CloseFile();
+ void CloseCallback(base::File::Error error);
+ void ReportWorked();
+
+ base::FilePath file_system_path_;
+ TotalWorkCallback total_work_callback_;
+ WorkedCallback worked_callback_;
+ DoneCallback done_callback_;
+ std::unique_ptr<base::FileEnumerator> file_enumerator_;
+ typedef std::map<base::FilePath, base::Time> FilePathTimesMap;
+ FilePathTimesMap file_path_times_;
+ FilePathTimesMap::const_iterator indexing_it_;
+ base::FileProxy current_file_;
+ int64_t current_file_offset_;
+ typedef int32_t Trigram;
+ std::vector<Trigram> current_trigrams_;
+ // The index in this vector is the trigram id.
+ std::vector<bool> current_trigrams_set_;
+ base::TimeTicks last_worked_notification_time_;
+ int files_indexed_;
+ bool stopped_;
+ };
+
+ DevToolsFileSystemIndexer();
+
+ // Performs file system indexing for given |file_system_path| and sends
+ // progress callbacks.
+ scoped_refptr<FileSystemIndexingJob> IndexPath(
+ const std::string& file_system_path,
+ const TotalWorkCallback& total_work_callback,
+ const WorkedCallback& worked_callback,
+ const DoneCallback& done_callback);
+
+ // Performs trigram search for given |query| in |file_system_path|.
+ void SearchInPath(const std::string& file_system_path,
+ const std::string& query,
+ const SearchCallback& callback);
+
+ private:
+ friend class base::RefCountedThreadSafe<DevToolsFileSystemIndexer>;
+
+ virtual ~DevToolsFileSystemIndexer();
+
+ void SearchInPathOnFileThread(const std::string& file_system_path,
+ const std::string& query,
+ const SearchCallback& callback);
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsFileSystemIndexer);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_FILE_SYSTEM_INDEXER_H_
diff --git a/chromium/chrome/browser/devtools/devtools_file_watcher.cc b/chromium/chrome/browser/devtools/devtools_file_watcher.cc
new file mode 100644
index 00000000000..e97ef213464
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_file_watcher.cc
@@ -0,0 +1,205 @@
+// Copyright (c) 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.
+
+#include "chrome/browser/devtools/devtools_file_watcher.h"
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <set>
+
+#include "base/bind.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/file_path_watcher.h"
+#include "base/files/file_util.h"
+#include "base/memory/ref_counted.h"
+#include "content/public/browser/browser_thread.h"
+
+using content::BrowserThread;
+
+static int kFirstThrottleTimeout = 10;
+static int kDefaultThrottleTimeout = 200;
+
+// DevToolsFileWatcher::SharedFileWatcher --------------------------------------
+
+class DevToolsFileWatcher::SharedFileWatcher :
+ public base::RefCounted<SharedFileWatcher> {
+ public:
+ SharedFileWatcher();
+
+ void AddListener(DevToolsFileWatcher* watcher);
+ void RemoveListener(DevToolsFileWatcher* watcher);
+ void AddWatch(const base::FilePath& path);
+ void RemoveWatch(const base::FilePath& path);
+
+ private:
+ friend class base::RefCounted<
+ DevToolsFileWatcher::SharedFileWatcher>;
+ ~SharedFileWatcher();
+
+ using FilePathTimesMap = std::map<base::FilePath, base::Time>;
+ void GetModificationTimes(const base::FilePath& path,
+ FilePathTimesMap* file_path_times);
+ void DirectoryChanged(const base::FilePath& path, bool error);
+ void DispatchNotifications();
+
+ std::vector<DevToolsFileWatcher*> listeners_;
+ std::map<base::FilePath, std::unique_ptr<base::FilePathWatcher>> watchers_;
+ std::map<base::FilePath, FilePathTimesMap> file_path_times_;
+ std::set<base::FilePath> pending_paths_;
+ base::Time last_event_time_;
+ base::TimeDelta last_dispatch_cost_;
+};
+
+DevToolsFileWatcher::SharedFileWatcher::SharedFileWatcher()
+ : last_dispatch_cost_(
+ base::TimeDelta::FromMilliseconds(kDefaultThrottleTimeout)) {
+ DevToolsFileWatcher::s_shared_watcher_ = this;
+}
+
+DevToolsFileWatcher::SharedFileWatcher::~SharedFileWatcher() {
+ DevToolsFileWatcher::s_shared_watcher_ = nullptr;
+}
+
+void DevToolsFileWatcher::SharedFileWatcher::AddListener(
+ DevToolsFileWatcher* watcher) {
+ listeners_.push_back(watcher);
+}
+
+void DevToolsFileWatcher::SharedFileWatcher::RemoveListener(
+ DevToolsFileWatcher* watcher) {
+ auto it = std::find(listeners_.begin(), listeners_.end(), watcher);
+ listeners_.erase(it);
+}
+
+void DevToolsFileWatcher::SharedFileWatcher::AddWatch(
+ const base::FilePath& path) {
+ if (watchers_.find(path) != watchers_.end())
+ return;
+ if (!base::FilePathWatcher::RecursiveWatchAvailable())
+ return;
+ watchers_[path].reset(new base::FilePathWatcher());
+ bool success = watchers_[path]->Watch(
+ path, true,
+ base::Bind(&SharedFileWatcher::DirectoryChanged, base::Unretained(this)));
+ if (!success)
+ return;
+
+ GetModificationTimes(path, &file_path_times_[path]);
+}
+
+void DevToolsFileWatcher::SharedFileWatcher::GetModificationTimes(
+ const base::FilePath& path,
+ FilePathTimesMap* times_map) {
+ base::FileEnumerator enumerator(path, true, base::FileEnumerator::FILES);
+ base::FilePath file_path = enumerator.Next();
+ while (!file_path.empty()) {
+ base::FileEnumerator::FileInfo file_info = enumerator.GetInfo();
+ (*times_map)[file_path] = file_info.GetLastModifiedTime();
+ file_path = enumerator.Next();
+ }
+}
+
+void DevToolsFileWatcher::SharedFileWatcher::RemoveWatch(
+ const base::FilePath& path) {
+ watchers_.erase(path);
+}
+
+void DevToolsFileWatcher::SharedFileWatcher::DirectoryChanged(
+ const base::FilePath& path,
+ bool error) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ pending_paths_.insert(path);
+ if (pending_paths_.size() > 1)
+ return; // PostDelayedTask is already pending.
+
+ base::Time now = base::Time::Now();
+ // Quickly dispatch first chunk.
+ base::TimeDelta shedule_for =
+ now - last_event_time_ > last_dispatch_cost_ ?
+ base::TimeDelta::FromMilliseconds(kFirstThrottleTimeout) :
+ last_dispatch_cost_ * 2;
+
+ BrowserThread::PostDelayedTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::BindOnce(
+ &DevToolsFileWatcher::SharedFileWatcher::DispatchNotifications, this),
+ shedule_for);
+ last_event_time_ = now;
+}
+
+void DevToolsFileWatcher::SharedFileWatcher::DispatchNotifications() {
+ if (!pending_paths_.size())
+ return;
+ base::Time start = base::Time::Now();
+ std::vector<std::string> added_paths;
+ std::vector<std::string> removed_paths;
+ std::vector<std::string> changed_paths;
+
+ for (const auto& path : pending_paths_) {
+ FilePathTimesMap& old_times = file_path_times_[path];
+ FilePathTimesMap current_times;
+ GetModificationTimes(path, &current_times);
+ for (const auto& path_time : current_times) {
+ const base::FilePath& path = path_time.first;
+ auto old_timestamp = old_times.find(path);
+ if (old_timestamp == old_times.end())
+ added_paths.push_back(path.AsUTF8Unsafe());
+ else if (old_timestamp->second != path_time.second)
+ changed_paths.push_back(path.AsUTF8Unsafe());
+ }
+ for (const auto& path_time : old_times) {
+ const base::FilePath& path = path_time.first;
+ if (current_times.find(path) == current_times.end())
+ removed_paths.push_back(path.AsUTF8Unsafe());
+ }
+ old_times.swap(current_times);
+ }
+ pending_paths_.clear();
+
+ for (auto* watcher : listeners_) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::BindOnce(watcher->callback_, changed_paths,
+ added_paths, removed_paths));
+ }
+ last_dispatch_cost_ = base::Time::Now() - start;
+}
+
+// static
+DevToolsFileWatcher::SharedFileWatcher*
+DevToolsFileWatcher::s_shared_watcher_ = nullptr;
+
+// DevToolsFileWatcher ---------------------------------------------------------
+
+DevToolsFileWatcher::DevToolsFileWatcher(const WatchCallback& callback)
+ : callback_(callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::BindOnce(&DevToolsFileWatcher::InitSharedWatcher,
+ base::Unretained(this)));
+}
+
+DevToolsFileWatcher::~DevToolsFileWatcher() {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ shared_watcher_->RemoveListener(this);
+}
+
+void DevToolsFileWatcher::InitSharedWatcher() {
+ if (!DevToolsFileWatcher::s_shared_watcher_)
+ new SharedFileWatcher();
+ shared_watcher_ = DevToolsFileWatcher::s_shared_watcher_;
+ shared_watcher_->AddListener(this);
+}
+
+void DevToolsFileWatcher::AddWatch(const base::FilePath& path) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ shared_watcher_->AddWatch(path);
+}
+
+void DevToolsFileWatcher::RemoveWatch(const base::FilePath& path) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ shared_watcher_->RemoveWatch(path);
+}
diff --git a/chromium/chrome/browser/devtools/devtools_file_watcher.h b/chromium/chrome/browser/devtools/devtools_file_watcher.h
new file mode 100644
index 00000000000..eb597fccfa6
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_file_watcher.h
@@ -0,0 +1,40 @@
+// Copyright (c) 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.
+
+#ifndef CHROME_BROWSER_DEVTOOLS_DEVTOOLS_FILE_WATCHER_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_FILE_WATCHER_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+
+namespace base {
+class FilePath;
+}
+
+class DevToolsFileWatcher {
+ public:
+ using WatchCallback = base::Callback<void(const std::vector<std::string>&,
+ const std::vector<std::string>&,
+ const std::vector<std::string>&)>;
+ explicit DevToolsFileWatcher(const WatchCallback& callback);
+ ~DevToolsFileWatcher();
+
+ void AddWatch(const base::FilePath& path);
+ void RemoveWatch(const base::FilePath& path);
+
+ private:
+ class SharedFileWatcher;
+ static SharedFileWatcher* s_shared_watcher_;
+
+ void InitSharedWatcher();
+ void FileChanged(const base::FilePath&, int);
+
+ scoped_refptr<SharedFileWatcher> shared_watcher_;
+ WatchCallback callback_;
+ DISALLOW_COPY_AND_ASSIGN(DevToolsFileWatcher);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_FILE_WATCHER_H_
diff --git a/chromium/chrome/browser/devtools/devtools_network_conditions.cc b/chromium/chrome/browser/devtools/devtools_network_conditions.cc
new file mode 100644
index 00000000000..e80172142d8
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_network_conditions.cc
@@ -0,0 +1,40 @@
+// 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/devtools/devtools_network_conditions.h"
+
+#include "url/gurl.h"
+
+DevToolsNetworkConditions::DevToolsNetworkConditions()
+ : offline_(false),
+ latency_(0),
+ download_throughput_(0),
+ upload_throughput_(0) {
+}
+
+DevToolsNetworkConditions::DevToolsNetworkConditions(bool offline)
+ : offline_(offline),
+ latency_(0),
+ download_throughput_(0),
+ upload_throughput_(0) {
+}
+
+DevToolsNetworkConditions::DevToolsNetworkConditions(
+ bool offline,
+ double latency,
+ double download_throughput,
+ double upload_throughput)
+ : offline_(offline),
+ latency_(latency),
+ download_throughput_(download_throughput),
+ upload_throughput_(upload_throughput) {
+}
+
+DevToolsNetworkConditions::~DevToolsNetworkConditions() {
+}
+
+bool DevToolsNetworkConditions::IsThrottling() const {
+ return !offline_ && ((latency_ != 0) || (download_throughput_ != 0.0) ||
+ (upload_throughput_ != 0));
+}
diff --git a/chromium/chrome/browser/devtools/devtools_network_conditions.h b/chromium/chrome/browser/devtools/devtools_network_conditions.h
new file mode 100644
index 00000000000..818437f536e
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_network_conditions.h
@@ -0,0 +1,41 @@
+// 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_DEVTOOLS_DEVTOOLS_NETWORK_CONDITIONS_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_CONDITIONS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+
+// DevToolsNetworkConditions holds information about desired network conditions.
+class DevToolsNetworkConditions {
+ public:
+ DevToolsNetworkConditions();
+ ~DevToolsNetworkConditions();
+
+ explicit DevToolsNetworkConditions(bool offline);
+ DevToolsNetworkConditions(bool offline,
+ double latency,
+ double download_throughput,
+ double upload_throughput);
+
+ bool IsThrottling() const;
+
+ bool offline() const { return offline_; }
+ double latency() const { return latency_; }
+ double download_throughput() const { return download_throughput_; }
+ double upload_throughput() const { return upload_throughput_; }
+
+ private:
+ const bool offline_;
+ const double latency_;
+ const double download_throughput_;
+ const double upload_throughput_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkConditions);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_CONDITIONS_H_
diff --git a/chromium/chrome/browser/devtools/devtools_network_controller.cc b/chromium/chrome/browser/devtools/devtools_network_controller.cc
new file mode 100644
index 00000000000..5c583f5e580
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_network_controller.cc
@@ -0,0 +1,71 @@
+// 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/devtools/devtools_network_controller.h"
+
+#include <utility>
+
+#include "chrome/browser/devtools/devtools_network_conditions.h"
+#include "chrome/browser/devtools/devtools_network_interceptor.h"
+#include "net/http/http_request_info.h"
+
+DevToolsNetworkController::DevToolsNetworkController()
+ : appcache_interceptor_(new DevToolsNetworkInterceptor()) {}
+
+DevToolsNetworkController::~DevToolsNetworkController() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+DevToolsNetworkInterceptor* DevToolsNetworkController::GetInterceptor(
+ const std::string& client_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (interceptors_.empty() || client_id.empty())
+ return nullptr;
+
+ auto it = interceptors_.find(client_id);
+ if (it == interceptors_.end())
+ return nullptr;
+
+ return it->second.get();
+}
+
+void DevToolsNetworkController::SetNetworkState(
+ const std::string& client_id,
+ std::unique_ptr<DevToolsNetworkConditions> conditions) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ auto it = interceptors_.find(client_id);
+ if (it == interceptors_.end()) {
+ if (!conditions)
+ return;
+ std::unique_ptr<DevToolsNetworkInterceptor> new_interceptor(
+ new DevToolsNetworkInterceptor());
+ new_interceptor->UpdateConditions(std::move(conditions));
+ interceptors_[client_id] = std::move(new_interceptor);
+ } else {
+ if (!conditions) {
+ std::unique_ptr<DevToolsNetworkConditions> online_conditions(
+ new DevToolsNetworkConditions());
+ it->second->UpdateConditions(std::move(online_conditions));
+ interceptors_.erase(client_id);
+ } else {
+ it->second->UpdateConditions(std::move(conditions));
+ }
+ }
+
+ bool has_offline_interceptors = false;
+ for (const auto& interceptor : interceptors_) {
+ if (interceptor.second->IsOffline()) {
+ has_offline_interceptors = true;
+ break;
+ }
+ }
+
+ bool is_appcache_offline = appcache_interceptor_->IsOffline();
+ if (is_appcache_offline != has_offline_interceptors) {
+ std::unique_ptr<DevToolsNetworkConditions> appcache_conditions(
+ new DevToolsNetworkConditions(has_offline_interceptors));
+ appcache_interceptor_->UpdateConditions(std::move(appcache_conditions));
+ }
+}
diff --git a/chromium/chrome/browser/devtools/devtools_network_controller.h b/chromium/chrome/browser/devtools/devtools_network_controller.h
new file mode 100644
index 00000000000..ea3f856129d
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_network_controller.h
@@ -0,0 +1,44 @@
+// 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_DEVTOOLS_DEVTOOLS_NETWORK_CONTROLLER_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_CONTROLLER_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+
+class DevToolsNetworkConditions;
+class DevToolsNetworkInterceptor;
+
+// DevToolsNetworkController manages interceptors identified by client id
+// and their throttling conditions.
+class DevToolsNetworkController {
+ public:
+ DevToolsNetworkController();
+ virtual ~DevToolsNetworkController();
+
+ // Applies network emulation configuration.
+ void SetNetworkState(const std::string& client_id,
+ std::unique_ptr<DevToolsNetworkConditions> conditions);
+
+ DevToolsNetworkInterceptor* GetInterceptor(
+ const std::string& client_id);
+
+ private:
+ using InterceptorMap =
+ std::unordered_map<std::string,
+ std::unique_ptr<DevToolsNetworkInterceptor>>;
+
+ std::unique_ptr<DevToolsNetworkInterceptor> appcache_interceptor_;
+ InterceptorMap interceptors_;
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkController);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_CONTROLLER_H_
diff --git a/chromium/chrome/browser/devtools/devtools_network_controller_handle.cc b/chromium/chrome/browser/devtools/devtools_network_controller_handle.cc
new file mode 100644
index 00000000000..d83aea36499
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_network_controller_handle.cc
@@ -0,0 +1,55 @@
+// 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.
+
+#include "chrome/browser/devtools/devtools_network_controller_handle.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "chrome/browser/devtools/devtools_network_conditions.h"
+#include "chrome/browser/devtools/devtools_network_controller.h"
+#include "content/public/browser/browser_thread.h"
+
+using content::BrowserThread;
+
+DevToolsNetworkControllerHandle::DevToolsNetworkControllerHandle() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+DevToolsNetworkControllerHandle::~DevToolsNetworkControllerHandle() {}
+
+void DevToolsNetworkControllerHandle::SetNetworkState(
+ const std::string& client_id,
+ std::unique_ptr<DevToolsNetworkConditions> conditions) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&DevToolsNetworkControllerHandle::SetNetworkStateOnIO,
+ base::Unretained(this), client_id,
+ base::Passed(&conditions)));
+}
+
+DevToolsNetworkController* DevToolsNetworkControllerHandle::GetController() {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ LazyInitialize();
+ return controller_.get();
+}
+
+void DevToolsNetworkControllerHandle::LazyInitialize() {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ if (!controller_)
+ controller_.reset(new DevToolsNetworkController);
+}
+
+void DevToolsNetworkControllerHandle::SetNetworkStateOnIO(
+ const std::string& client_id,
+ std::unique_ptr<DevToolsNetworkConditions> conditions) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ LazyInitialize();
+ controller_->SetNetworkState(client_id, std::move(conditions));
+}
diff --git a/chromium/chrome/browser/devtools/devtools_network_controller_handle.h b/chromium/chrome/browser/devtools/devtools_network_controller_handle.h
new file mode 100644
index 00000000000..e818204b289
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_network_controller_handle.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_CONTROLLER_HANDLE_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_CONTROLLER_HANDLE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+
+class DevToolsNetworkConditions;
+class DevToolsNetworkController;
+
+// A handle to manage an IO-thread DevToolsNetworkController on the IO thread
+// while allowing SetNetworkState to be called from the UI thread. Must be
+// created on the UI thread and destroyed on the IO thread.
+class DevToolsNetworkControllerHandle {
+ public:
+ DevToolsNetworkControllerHandle();
+ ~DevToolsNetworkControllerHandle();
+
+ // Called on the UI thread.
+ void SetNetworkState(const std::string& client_id,
+ std::unique_ptr<DevToolsNetworkConditions> conditions);
+
+ // Called on the IO thread.
+ DevToolsNetworkController* GetController();
+
+ private:
+ void LazyInitialize();
+ void SetNetworkStateOnIO(
+ const std::string& client_id,
+ std::unique_ptr<DevToolsNetworkConditions> conditions);
+
+ std::unique_ptr<DevToolsNetworkController> controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkControllerHandle);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_CONTROLLER_HANDLE_H_
diff --git a/chromium/chrome/browser/devtools/devtools_network_controller_unittest.cc b/chromium/chrome/browser/devtools/devtools_network_controller_unittest.cc
new file mode 100644
index 00000000000..5463d082a1e
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_network_controller_unittest.cc
@@ -0,0 +1,329 @@
+// 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/devtools/devtools_network_controller.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "chrome/browser/devtools/devtools_network_conditions.h"
+#include "chrome/browser/devtools/devtools_network_interceptor.h"
+#include "chrome/browser/devtools/devtools_network_transaction.h"
+#include "chrome/browser/devtools/devtools_network_upload_data_stream.h"
+#include "net/base/chunked_upload_data_stream.h"
+#include "net/http/http_transaction_test_util.h"
+#include "net/log/net_log_with_source.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace test {
+
+using net::kSimpleGET_Transaction;
+using net::MockHttpRequest;
+using net::MockNetworkLayer;
+using net::MockTransaction;
+using net::TEST_MODE_SYNC_NET_START;
+
+const char kClientId[] = "42";
+const char kAnotherClientId[] = "24";
+const char kUploadData[] = "upload_data";
+int64_t kUploadIdentifier = 17;
+
+class TestCallback {
+ public:
+ TestCallback() : run_count_(0), value_(0) {}
+ void Run(int value) {
+ run_count_++;
+ value_ = value;
+ }
+ int run_count() { return run_count_; }
+ int value() { return value_; }
+
+ private:
+ int run_count_;
+ int value_;
+};
+
+class DevToolsNetworkControllerHelper {
+ public:
+ DevToolsNetworkControllerHelper() :
+ completion_callback_(
+ base::Bind(&TestCallback::Run, base::Unretained(&callback_))),
+ mock_transaction_(kSimpleGET_Transaction),
+ buffer_(new net::IOBuffer(64)) {
+ mock_transaction_.test_mode = TEST_MODE_SYNC_NET_START;
+ mock_transaction_.url = "http://dot.com";
+ mock_transaction_.request_headers =
+ "X-DevTools-Emulate-Network-Conditions-Client-Id: 42\r\n";
+ AddMockTransaction(&mock_transaction_);
+
+ std::unique_ptr<net::HttpTransaction> network_transaction;
+ network_layer_.CreateTransaction(
+ net::DEFAULT_PRIORITY, &network_transaction);
+ transaction_.reset(new DevToolsNetworkTransaction(
+ &controller_, std::move(network_transaction)));
+ }
+
+ void SetNetworkState(bool offline, double download, double upload) {
+ std::unique_ptr<DevToolsNetworkConditions> conditions(
+ new DevToolsNetworkConditions(offline, 0, download, upload));
+ controller_.SetNetworkState(kClientId, std::move(conditions));
+ }
+
+ void SetNetworkState(const std::string& id, bool offline) {
+ std::unique_ptr<DevToolsNetworkConditions> conditions(
+ new DevToolsNetworkConditions(offline));
+ controller_.SetNetworkState(id, std::move(conditions));
+ }
+
+ int Start(bool with_upload) {
+ request_.reset(new MockHttpRequest(mock_transaction_));
+
+ if (with_upload) {
+ upload_data_stream_.reset(
+ new net::ChunkedUploadDataStream(kUploadIdentifier));
+ upload_data_stream_->AppendData(
+ kUploadData, arraysize(kUploadData), true);
+ request_->upload_data_stream = upload_data_stream_.get();
+ }
+
+ int rv = transaction_->Start(request_.get(), completion_callback_,
+ net::NetLogWithSource());
+ EXPECT_EQ(with_upload, !!transaction_->custom_upload_data_stream_);
+ return rv;
+ }
+
+ int Read() {
+ return transaction_->Read(buffer_.get(), 64, completion_callback_);
+ }
+
+ bool ShouldFail() {
+ if (transaction_->interceptor_)
+ return transaction_->interceptor_->IsOffline();
+ DevToolsNetworkInterceptor* interceptor =
+ controller_.GetInterceptor(kClientId);
+ EXPECT_TRUE(!!interceptor);
+ return interceptor->IsOffline();
+ }
+
+ bool HasStarted() {
+ return !!transaction_->request_;
+ }
+
+ bool HasFailed() {
+ return transaction_->failed_;
+ }
+
+ void CancelTransaction() {
+ transaction_.reset();
+ }
+
+ int ReadUploadData() {
+ EXPECT_EQ(net::OK, transaction_->custom_upload_data_stream_->Init(
+ completion_callback_, net::NetLogWithSource()));
+ return transaction_->custom_upload_data_stream_->Read(
+ buffer_.get(), 64, completion_callback_);
+ }
+
+ ~DevToolsNetworkControllerHelper() {
+ RemoveMockTransaction(&mock_transaction_);
+ }
+
+ TestCallback* callback() { return &callback_; }
+ DevToolsNetworkController* controller() { return &controller_; }
+ DevToolsNetworkTransaction* transaction() { return transaction_.get(); }
+
+ private:
+ base::MessageLoop message_loop_;
+ MockNetworkLayer network_layer_;
+ TestCallback callback_;
+ net::CompletionCallback completion_callback_;
+ MockTransaction mock_transaction_;
+ DevToolsNetworkController controller_;
+ std::unique_ptr<DevToolsNetworkTransaction> transaction_;
+ scoped_refptr<net::IOBuffer> buffer_;
+ std::unique_ptr<net::ChunkedUploadDataStream> upload_data_stream_;
+ std::unique_ptr<MockHttpRequest> request_;
+};
+
+TEST(DevToolsNetworkControllerTest, SingleDisableEnable) {
+ DevToolsNetworkControllerHelper helper;
+ helper.SetNetworkState(false, 0, 0);
+ helper.Start(false);
+
+ EXPECT_FALSE(helper.ShouldFail());
+ helper.SetNetworkState(true, 0, 0);
+ EXPECT_TRUE(helper.ShouldFail());
+ helper.SetNetworkState(false, 0, 0);
+ EXPECT_FALSE(helper.ShouldFail());
+
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST(DevToolsNetworkControllerTest, InterceptorIsolation) {
+ DevToolsNetworkControllerHelper helper;
+ helper.SetNetworkState(false, 0, 0);
+ helper.Start(false);
+
+ EXPECT_FALSE(helper.ShouldFail());
+ helper.SetNetworkState(kAnotherClientId, true);
+ EXPECT_FALSE(helper.ShouldFail());
+ helper.SetNetworkState(true, 0, 0);
+ EXPECT_TRUE(helper.ShouldFail());
+
+ helper.SetNetworkState(kAnotherClientId, false);
+ helper.SetNetworkState(false, 0, 0);
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST(DevToolsNetworkControllerTest, FailOnStart) {
+ DevToolsNetworkControllerHelper helper;
+ helper.SetNetworkState(true, 0, 0);
+
+ int rv = helper.Start(false);
+ EXPECT_EQ(rv, net::ERR_INTERNET_DISCONNECTED);
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(helper.callback()->run_count(), 0);
+}
+
+TEST(DevToolsNetworkControllerTest, FailRunningTransaction) {
+ DevToolsNetworkControllerHelper helper;
+ helper.SetNetworkState(false, 0, 0);
+ TestCallback* callback = helper.callback();
+
+ int rv = helper.Start(false);
+ EXPECT_EQ(rv, net::OK);
+
+ rv = helper.Read();
+ EXPECT_EQ(rv, net::ERR_IO_PENDING);
+ EXPECT_EQ(callback->run_count(), 0);
+
+ helper.SetNetworkState(true, 0, 0);
+ EXPECT_EQ(callback->run_count(), 0);
+
+ // Wait until HttpTrancation completes reading and invokes callback.
+ // DevToolsNetworkTransaction should report error instead.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(callback->run_count(), 1);
+ EXPECT_EQ(callback->value(), net::ERR_INTERNET_DISCONNECTED);
+
+ // Check that transaction is not failed second time.
+ helper.SetNetworkState(false, 0, 0);
+ helper.SetNetworkState(true, 0, 0);
+ EXPECT_EQ(callback->run_count(), 1);
+}
+
+TEST(DevToolsNetworkControllerTest, ReadAfterFail) {
+ DevToolsNetworkControllerHelper helper;
+ helper.SetNetworkState(false, 0, 0);
+
+ int rv = helper.Start(false);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_TRUE(helper.HasStarted());
+
+ helper.SetNetworkState(true, 0, 0);
+ // Not failed yet, as no IO was initiated.
+ EXPECT_FALSE(helper.HasFailed());
+
+ rv = helper.Read();
+ // Fails on first IO.
+ EXPECT_EQ(rv, net::ERR_INTERNET_DISCONNECTED);
+
+ // Check that callback is never invoked.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(helper.callback()->run_count(), 0);
+}
+
+TEST(DevToolsNetworkControllerTest, CancelTransaction) {
+ DevToolsNetworkControllerHelper helper;
+ helper.SetNetworkState(false, 0, 0);
+
+ int rv = helper.Start(false);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_TRUE(helper.HasStarted());
+ helper.CancelTransaction();
+
+ // Should not crash.
+ helper.SetNetworkState(true, 0, 0);
+ helper.SetNetworkState(false, 0, 0);
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST(DevToolsNetworkControllerTest, CancelFailedTransaction) {
+ DevToolsNetworkControllerHelper helper;
+ helper.SetNetworkState(true, 0, 0);
+
+ int rv = helper.Start(false);
+ EXPECT_EQ(rv, net::ERR_INTERNET_DISCONNECTED);
+ EXPECT_TRUE(helper.HasStarted());
+ helper.CancelTransaction();
+
+ // Should not crash.
+ helper.SetNetworkState(true, 0, 0);
+ helper.SetNetworkState(false, 0, 0);
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST(DevToolsNetworkControllerTest, UploadDoesNotFail) {
+ DevToolsNetworkControllerHelper helper;
+ helper.SetNetworkState(true, 0, 0);
+ int rv = helper.Start(true);
+ EXPECT_EQ(rv, net::ERR_INTERNET_DISCONNECTED);
+ rv = helper.ReadUploadData();
+ EXPECT_EQ(rv, static_cast<int>(arraysize(kUploadData)));
+}
+
+TEST(DevToolsNetworkControllerTest, DownloadOnly) {
+ DevToolsNetworkControllerHelper helper;
+ TestCallback* callback = helper.callback();
+
+ helper.SetNetworkState(false, 10000000, 0);
+ int rv = helper.Start(false);
+ EXPECT_EQ(rv, net::ERR_IO_PENDING);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(callback->run_count(), 1);
+ EXPECT_GE(callback->value(), net::OK);
+
+ rv = helper.Read();
+ EXPECT_EQ(rv, net::ERR_IO_PENDING);
+ EXPECT_EQ(callback->run_count(), 1);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(callback->run_count(), 2);
+ EXPECT_GE(callback->value(), net::OK);
+}
+
+TEST(DevToolsNetworkControllerTest, UploadOnly) {
+ DevToolsNetworkControllerHelper helper;
+ TestCallback* callback = helper.callback();
+
+ helper.SetNetworkState(false, 0, 1000000);
+ int rv = helper.Start(true);
+ EXPECT_EQ(rv, net::OK);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(callback->run_count(), 0);
+
+ rv = helper.Read();
+ EXPECT_EQ(rv, net::ERR_IO_PENDING);
+ EXPECT_EQ(callback->run_count(), 0);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(callback->run_count(), 1);
+ EXPECT_GE(callback->value(), net::OK);
+
+ rv = helper.ReadUploadData();
+ EXPECT_EQ(rv, net::ERR_IO_PENDING);
+ EXPECT_EQ(callback->run_count(), 1);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(callback->run_count(), 2);
+ EXPECT_EQ(callback->value(), static_cast<int>(arraysize(kUploadData)));
+}
+
+} // namespace test
diff --git a/chromium/chrome/browser/devtools/devtools_network_interceptor.cc b/chromium/chrome/browser/devtools/devtools_network_interceptor.cc
new file mode 100644
index 00000000000..432bb6bcbc5
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_network_interceptor.cc
@@ -0,0 +1,290 @@
+// 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/devtools/devtools_network_interceptor.h"
+
+#include <stddef.h>
+#include <algorithm>
+#include <limits>
+#include <utility>
+
+#include "base/time/time.h"
+#include "chrome/browser/devtools/devtools_network_conditions.h"
+#include "net/base/net_errors.h"
+
+namespace {
+
+int64_t kPacketSize = 1500;
+
+base::TimeDelta CalculateTickLength(double throughput) {
+ if (!throughput)
+ return base::TimeDelta();
+ int64_t us_tick_length = (1000000L * kPacketSize) / throughput;
+ DCHECK(us_tick_length != 0);
+ if (us_tick_length == 0)
+ us_tick_length = 1;
+ return base::TimeDelta::FromMicroseconds(us_tick_length);
+}
+
+} // namespace
+
+DevToolsNetworkInterceptor::ThrottleRecord::ThrottleRecord() {
+}
+
+DevToolsNetworkInterceptor::ThrottleRecord::ThrottleRecord(
+ const ThrottleRecord& other) = default;
+
+DevToolsNetworkInterceptor::ThrottleRecord::~ThrottleRecord() {
+}
+
+DevToolsNetworkInterceptor::DevToolsNetworkInterceptor()
+ : conditions_(new DevToolsNetworkConditions()),
+ download_last_tick_(0),
+ upload_last_tick_(0),
+ weak_ptr_factory_(this) {
+}
+
+DevToolsNetworkInterceptor::~DevToolsNetworkInterceptor() {
+}
+
+base::WeakPtr<DevToolsNetworkInterceptor>
+DevToolsNetworkInterceptor::GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+}
+
+void DevToolsNetworkInterceptor::FinishRecords(
+ ThrottleRecords* records, bool offline) {
+ ThrottleRecords temp;
+ temp.swap(*records);
+ for (const ThrottleRecord& record : temp) {
+ bool failed = offline && !record.is_upload;
+ record.callback.Run(
+ failed ? net::ERR_INTERNET_DISCONNECTED : record.result,
+ record.bytes);
+ }
+}
+
+void DevToolsNetworkInterceptor::UpdateConditions(
+ std::unique_ptr<DevToolsNetworkConditions> conditions) {
+ DCHECK(conditions);
+ base::TimeTicks now = base::TimeTicks::Now();
+ if (conditions_->IsThrottling())
+ UpdateThrottled(now);
+
+ conditions_ = std::move(conditions);
+
+ bool offline = conditions_->offline();
+ if (offline || !conditions_->IsThrottling()) {
+ timer_.Stop();
+ FinishRecords(&download_, offline);
+ FinishRecords(&upload_, offline);
+ FinishRecords(&suspended_, offline);
+ return;
+ }
+
+ // Throttling.
+ DCHECK(conditions_->download_throughput() != 0 ||
+ conditions_->upload_throughput() != 0);
+ offset_ = now;
+
+ download_last_tick_ = 0;
+ download_tick_length_ = CalculateTickLength(
+ conditions_->download_throughput());
+
+ upload_last_tick_ = 0;
+ upload_tick_length_ = CalculateTickLength(conditions_->upload_throughput());
+
+ latency_length_ = base::TimeDelta();
+ double latency = conditions_->latency();
+ if (latency > 0)
+ latency_length_ = base::TimeDelta::FromMillisecondsD(latency);
+ ArmTimer(now);
+}
+
+uint64_t DevToolsNetworkInterceptor::UpdateThrottledRecords(
+ base::TimeTicks now,
+ ThrottleRecords* records,
+ uint64_t last_tick,
+ base::TimeDelta tick_length) {
+ if (tick_length.is_zero()) {
+ DCHECK(records->empty());
+ return last_tick;
+ }
+
+ int64_t new_tick = (now - offset_) / tick_length;
+ int64_t ticks = new_tick - last_tick;
+
+ int64_t length = records->size();
+ if (!length)
+ return new_tick;
+
+ int64_t shift = ticks % length;
+ for (int64_t i = 0; i < length; ++i) {
+ (*records)[i].bytes -=
+ (ticks / length) * kPacketSize + (i < shift ? kPacketSize : 0);
+ }
+ std::rotate(records->begin(), records->begin() + shift, records->end());
+ return new_tick;
+}
+
+void DevToolsNetworkInterceptor::UpdateThrottled(base::TimeTicks now) {
+ download_last_tick_ = UpdateThrottledRecords(
+ now, &download_, download_last_tick_, download_tick_length_);
+ upload_last_tick_ = UpdateThrottledRecords(
+ now, &upload_, upload_last_tick_, upload_tick_length_);
+ UpdateSuspended(now);
+}
+
+void DevToolsNetworkInterceptor::UpdateSuspended(base::TimeTicks now) {
+ int64_t activation_baseline =
+ (now - latency_length_ - base::TimeTicks()).InMicroseconds();
+ ThrottleRecords suspended;
+ for (const ThrottleRecord& record : suspended_) {
+ if (record.send_end <= activation_baseline) {
+ if (record.is_upload)
+ upload_.push_back(record);
+ else
+ download_.push_back(record);
+ } else {
+ suspended.push_back(record);
+ }
+ }
+ suspended_.swap(suspended);
+}
+
+void DevToolsNetworkInterceptor::CollectFinished(
+ ThrottleRecords* records, ThrottleRecords* finished) {
+ ThrottleRecords active;
+ for (const ThrottleRecord& record : *records) {
+ if (record.bytes < 0)
+ finished->push_back(record);
+ else
+ active.push_back(record);
+ }
+ records->swap(active);
+}
+
+void DevToolsNetworkInterceptor::OnTimer() {
+ base::TimeTicks now = base::TimeTicks::Now();
+ UpdateThrottled(now);
+
+ ThrottleRecords finished;
+ CollectFinished(&download_, &finished);
+ CollectFinished(&upload_, &finished);
+ for (const ThrottleRecord& record : finished)
+ record.callback.Run(record.result, record.bytes);
+
+ ArmTimer(now);
+}
+
+base::TimeTicks DevToolsNetworkInterceptor::CalculateDesiredTime(
+ const ThrottleRecords& records,
+ uint64_t last_tick,
+ base::TimeDelta tick_length) {
+ int64_t min_ticks_left = 0x10000L;
+ size_t count = records.size();
+ for (size_t i = 0; i < count; ++i) {
+ int64_t packets_left = (records[i].bytes + kPacketSize - 1) / kPacketSize;
+ int64_t ticks_left = (i + 1) + count * (packets_left - 1);
+ if (i == 0 || ticks_left < min_ticks_left)
+ min_ticks_left = ticks_left;
+ }
+ return offset_ + tick_length * (last_tick + min_ticks_left);
+}
+
+void DevToolsNetworkInterceptor::ArmTimer(base::TimeTicks now) {
+ size_t suspend_count = suspended_.size();
+ if (download_.empty() && upload_.empty() && !suspend_count)
+ return;
+
+ base::TimeTicks desired_time = CalculateDesiredTime(
+ download_, download_last_tick_, download_tick_length_);
+
+ base::TimeTicks upload_time = CalculateDesiredTime(
+ upload_, upload_last_tick_, upload_tick_length_);
+ if (upload_time < desired_time)
+ desired_time = upload_time;
+
+ int64_t min_baseline = std::numeric_limits<int64_t>::max();
+ for (size_t i = 0; i < suspend_count; ++i) {
+ if (suspended_[i].send_end < min_baseline)
+ min_baseline = suspended_[i].send_end;
+ }
+ if (suspend_count) {
+ base::TimeTicks activation_time = base::TimeTicks() +
+ base::TimeDelta::FromMicroseconds(min_baseline) + latency_length_;
+ if (activation_time < desired_time)
+ desired_time = activation_time;
+ }
+
+ timer_.Start(
+ FROM_HERE,
+ desired_time - now,
+ base::Bind(
+ &DevToolsNetworkInterceptor::OnTimer,
+ base::Unretained(this)));
+}
+
+int DevToolsNetworkInterceptor::StartThrottle(
+ int result,
+ int64_t bytes,
+ base::TimeTicks send_end,
+ bool start,
+ bool is_upload,
+ const ThrottleCallback& callback) {
+ if (result < 0)
+ return result;
+
+ if (conditions_->offline())
+ return is_upload ? result : net::ERR_INTERNET_DISCONNECTED;
+
+ if ((is_upload && !conditions_->upload_throughput()) ||
+ (!is_upload && !conditions_->download_throughput())) {
+ return result;
+ }
+
+ ThrottleRecord record;
+ record.result = result;
+ record.bytes = bytes;
+ record.callback = callback;
+ // TODO(dgozman): use upload throughput.
+ record.is_upload = is_upload;
+
+ base::TimeTicks now = base::TimeTicks::Now();
+ UpdateThrottled(now);
+ if (start && !latency_length_.is_zero()) {
+ record.send_end = (send_end - base::TimeTicks()).InMicroseconds();
+ suspended_.push_back(record);
+ UpdateSuspended(now);
+ } else {
+ if (is_upload)
+ upload_.push_back(record);
+ else
+ download_.push_back(record);
+ }
+ ArmTimer(now);
+
+ return net::ERR_IO_PENDING;
+}
+
+void DevToolsNetworkInterceptor::StopThrottle(
+ const ThrottleCallback& callback) {
+ RemoveRecord(&download_, callback);
+ RemoveRecord(&upload_, callback);
+ RemoveRecord(&suspended_, callback);
+}
+
+void DevToolsNetworkInterceptor::RemoveRecord(
+ ThrottleRecords* records, const ThrottleCallback& callback) {
+ records->erase(
+ std::remove_if(records->begin(), records->end(),
+ [&callback](const ThrottleRecord& record){
+ return record.callback.Equals(callback);
+ }),
+ records->end());
+}
+
+bool DevToolsNetworkInterceptor::IsOffline() {
+ return conditions_->offline();
+}
diff --git a/chromium/chrome/browser/devtools/devtools_network_interceptor.h b/chromium/chrome/browser/devtools/devtools_network_interceptor.h
new file mode 100644
index 00000000000..34b7399707a
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_network_interceptor.h
@@ -0,0 +1,103 @@
+// 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_DEVTOOLS_DEVTOOLS_NETWORK_INTERCEPTOR_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_INTERCEPTOR_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/timer/timer.h"
+
+class DevToolsNetworkConditions;
+
+namespace base {
+class TimeDelta;
+class TimeTicks;
+}
+
+// DevToolsNetworkInterceptor emulates network conditions for transactions with
+// specific client id.
+class DevToolsNetworkInterceptor {
+ public:
+ using ThrottleCallback = base::Callback<void(int, int64_t)>;
+
+ DevToolsNetworkInterceptor();
+ virtual ~DevToolsNetworkInterceptor();
+
+ base::WeakPtr<DevToolsNetworkInterceptor> GetWeakPtr();
+
+ // Applies network emulation configuration.
+ void UpdateConditions(std::unique_ptr<DevToolsNetworkConditions> conditions);
+
+ // Throttles with |is_upload == true| always succeed, even in offline mode.
+ int StartThrottle(int result,
+ int64_t bytes,
+ base::TimeTicks send_end,
+ bool start,
+ bool is_upload,
+ const ThrottleCallback& callback);
+ void StopThrottle(const ThrottleCallback& callback);
+
+ bool IsOffline();
+
+ private:
+ struct ThrottleRecord {
+ public:
+ ThrottleRecord();
+ ThrottleRecord(const ThrottleRecord& other);
+ ~ThrottleRecord();
+ int result;
+ int64_t bytes;
+ int64_t send_end;
+ bool is_upload;
+ ThrottleCallback callback;
+ };
+ using ThrottleRecords = std::vector<ThrottleRecord>;
+
+ void FinishRecords(ThrottleRecords* records, bool offline);
+
+ uint64_t UpdateThrottledRecords(base::TimeTicks now, ThrottleRecords* records,
+ uint64_t last_tick, base::TimeDelta tick_length);
+ void UpdateThrottled(base::TimeTicks now);
+ void UpdateSuspended(base::TimeTicks now);
+
+ void CollectFinished(ThrottleRecords* records, ThrottleRecords* finished);
+ void OnTimer();
+
+ base::TimeTicks CalculateDesiredTime(const ThrottleRecords& records,
+ uint64_t last_tick, base::TimeDelta tick_length);
+ void ArmTimer(base::TimeTicks now);
+
+ void RemoveRecord(ThrottleRecords* records, const ThrottleCallback& callback);
+
+ std::unique_ptr<DevToolsNetworkConditions> conditions_;
+
+ // Throttables suspended for a "latency" period.
+ ThrottleRecords suspended_;
+
+ // Throttables waiting certain amount of transfer to be "accounted".
+ ThrottleRecords download_;
+ ThrottleRecords upload_;
+
+ base::OneShotTimer timer_;
+ base::TimeTicks offset_;
+ base::TimeDelta download_tick_length_;
+ base::TimeDelta upload_tick_length_;
+ base::TimeDelta latency_length_;
+ uint64_t download_last_tick_;
+ uint64_t upload_last_tick_;
+
+ base::WeakPtrFactory<DevToolsNetworkInterceptor> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkInterceptor);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_INTERCEPTOR_H_
diff --git a/chromium/chrome/browser/devtools/devtools_network_protocol_handler.cc b/chromium/chrome/browser/devtools/devtools_network_protocol_handler.cc
new file mode 100644
index 00000000000..2c39ad7ace6
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_network_protocol_handler.cc
@@ -0,0 +1,116 @@
+// 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/devtools/devtools_network_protocol_handler.h"
+
+#include <utility>
+
+#include "base/values.h"
+#include "chrome/browser/devtools/devtools_network_conditions.h"
+#include "chrome/browser/devtools/devtools_network_controller_handle.h"
+#include "chrome/browser/devtools/devtools_protocol_constants.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/web_contents.h"
+
+DevToolsNetworkProtocolHandler::DevToolsNetworkProtocolHandler() {
+}
+
+DevToolsNetworkProtocolHandler::~DevToolsNetworkProtocolHandler() {
+}
+
+base::DictionaryValue* DevToolsNetworkProtocolHandler::HandleCommand(
+ content::DevToolsAgentHost* agent_host,
+ base::DictionaryValue* command_dict) {
+ int id = 0;
+ std::string method;
+ base::DictionaryValue* params = nullptr;
+ if (!DevToolsProtocol::ParseCommand(command_dict, &id, &method, &params))
+ return nullptr;
+
+ namespace network = ::chrome::devtools::Network;
+
+ if (method == network::emulateNetworkConditions::kName)
+ return EmulateNetworkConditions(agent_host, id, params).release();
+
+ if (method == network::canEmulateNetworkConditions::kName)
+ return CanEmulateNetworkConditions(agent_host, id, params).release();
+
+ return nullptr;
+}
+
+std::unique_ptr<base::DictionaryValue>
+DevToolsNetworkProtocolHandler::CanEmulateNetworkConditions(
+ content::DevToolsAgentHost* agent_host,
+ int command_id,
+ base::DictionaryValue* params) {
+ std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
+ result->SetBoolean(chrome::devtools::kResult, true);
+ return DevToolsProtocol::CreateSuccessResponse(command_id, std::move(result));
+}
+
+std::unique_ptr<base::DictionaryValue>
+DevToolsNetworkProtocolHandler::EmulateNetworkConditions(
+ content::DevToolsAgentHost* agent_host,
+ int command_id,
+ base::DictionaryValue* params) {
+ namespace names = ::chrome::devtools::Network::emulateNetworkConditions;
+
+ bool offline = false;
+ if (!params || !params->GetBoolean(names::kParamOffline, &offline)) {
+ return DevToolsProtocol::CreateInvalidParamsResponse(
+ command_id, names::kParamOffline);
+ }
+ double latency = 0.0;
+ if (!params->GetDouble(names::kParamLatency, &latency)) {
+ return DevToolsProtocol::CreateInvalidParamsResponse(
+ command_id, names::kParamLatency);
+ }
+ if (latency < 0.0)
+ latency = 0.0;
+
+ double download_throughput = 0.0;
+ if (!params->GetDouble(names::kParamDownloadThroughput,
+ &download_throughput)) {
+ return DevToolsProtocol::CreateInvalidParamsResponse(
+ command_id, names::kParamDownloadThroughput);
+ }
+ if (download_throughput < 0.0)
+ download_throughput = 0.0;
+
+ double upload_throughput = 0.0;
+ if (!params->GetDouble(names::kParamUploadThroughput, &upload_throughput)) {
+ return DevToolsProtocol::CreateInvalidParamsResponse(
+ command_id, names::kParamUploadThroughput);
+ }
+ if (upload_throughput < 0.0)
+ upload_throughput = 0.0;
+
+ std::unique_ptr<DevToolsNetworkConditions> conditions(
+ new DevToolsNetworkConditions(offline, latency, download_throughput,
+ upload_throughput));
+
+ UpdateNetworkState(agent_host, std::move(conditions));
+ return nullptr; // Fall-through.
+}
+
+void DevToolsNetworkProtocolHandler::UpdateNetworkState(
+ content::DevToolsAgentHost* agent_host,
+ std::unique_ptr<DevToolsNetworkConditions> conditions) {
+ Profile* profile = Profile::FromBrowserContext(
+ agent_host->GetBrowserContext());
+ if (!profile)
+ return;
+ profile->GetDevToolsNetworkControllerHandle()->SetNetworkState(
+ agent_host->GetId(), std::move(conditions));
+}
+
+void DevToolsNetworkProtocolHandler::DevToolsAgentStateChanged(
+ content::DevToolsAgentHost* agent_host,
+ bool attached) {
+ std::unique_ptr<DevToolsNetworkConditions> conditions;
+ if (attached)
+ conditions.reset(new DevToolsNetworkConditions());
+ UpdateNetworkState(agent_host, std::move(conditions));
+}
diff --git a/chromium/chrome/browser/devtools/devtools_network_protocol_handler.h b/chromium/chrome/browser/devtools/devtools_network_protocol_handler.h
new file mode 100644
index 00000000000..e8d941b1d83
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_network_protocol_handler.h
@@ -0,0 +1,49 @@
+// 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_DEVTOOLS_DEVTOOLS_NETWORK_PROTOCOL_HANDLER_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_PROTOCOL_HANDLER_H_
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/devtools/devtools_protocol.h"
+
+namespace content {
+class DevToolsAgentHost;
+}
+
+class DevToolsNetworkConditions;
+
+class DevToolsNetworkProtocolHandler {
+ public:
+ DevToolsNetworkProtocolHandler();
+ ~DevToolsNetworkProtocolHandler();
+
+ void DevToolsAgentStateChanged(content::DevToolsAgentHost* agent_host,
+ bool attached);
+ base::DictionaryValue* HandleCommand(
+ content::DevToolsAgentHost* agent_host,
+ base::DictionaryValue* command_dict);
+
+ private:
+ std::unique_ptr<base::DictionaryValue> CanEmulateNetworkConditions(
+ content::DevToolsAgentHost* agent_host,
+ int command_id,
+ base::DictionaryValue* params);
+
+ std::unique_ptr<base::DictionaryValue> EmulateNetworkConditions(
+ content::DevToolsAgentHost* agent_host,
+ int command_id,
+ base::DictionaryValue* params);
+
+ void UpdateNetworkState(
+ content::DevToolsAgentHost* agent_host,
+ std::unique_ptr<DevToolsNetworkConditions> conditions);
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkProtocolHandler);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_PROTOCOL_HANDLER_H_
diff --git a/chromium/chrome/browser/devtools/devtools_network_transaction.cc b/chromium/chrome/browser/devtools/devtools_network_transaction.cc
new file mode 100644
index 00000000000..982bfab76dd
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_network_transaction.cc
@@ -0,0 +1,303 @@
+// 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/devtools/devtools_network_transaction.h"
+
+#include <utility>
+
+#include "base/callback_helpers.h"
+#include "chrome/browser/devtools/devtools_network_controller.h"
+#include "chrome/browser/devtools/devtools_network_interceptor.h"
+#include "chrome/browser/devtools/devtools_network_upload_data_stream.h"
+#include "net/base/load_timing_info.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_network_transaction.h"
+#include "net/http/http_request_info.h"
+#include "net/socket/connection_attempts.h"
+
+// Keep in sync with X_DevTools_Emulate_Network_Conditions_Client_Id defined in
+// HTTPNames.json5.
+const char
+ DevToolsNetworkTransaction::kDevToolsEmulateNetworkConditionsClientId[] =
+ "X-DevTools-Emulate-Network-Conditions-Client-Id";
+
+DevToolsNetworkTransaction::DevToolsNetworkTransaction(
+ DevToolsNetworkController* controller,
+ std::unique_ptr<net::HttpTransaction> network_transaction)
+ : throttled_byte_count_(0),
+ controller_(controller),
+ network_transaction_(std::move(network_transaction)),
+ request_(nullptr),
+ failed_(false) {
+ DCHECK(controller);
+}
+
+DevToolsNetworkTransaction::~DevToolsNetworkTransaction() {
+ if (interceptor_ && !throttle_callback_.is_null())
+ interceptor_->StopThrottle(throttle_callback_);
+}
+
+void DevToolsNetworkTransaction::IOCallback(
+ const net::CompletionCallback& callback, bool start, int result) {
+ result = Throttle(callback, start, result);
+ if (result != net::ERR_IO_PENDING)
+ callback.Run(result);
+}
+
+int DevToolsNetworkTransaction::Throttle(
+ const net::CompletionCallback& callback, bool start, int result) {
+ if (failed_)
+ return net::ERR_INTERNET_DISCONNECTED;
+ if (!interceptor_ || result < 0)
+ return result;
+
+ base::TimeTicks send_end;
+ if (start) {
+ throttled_byte_count_ += network_transaction_->GetTotalReceivedBytes();
+ net::LoadTimingInfo load_timing_info;
+ if (GetLoadTimingInfo(&load_timing_info)) {
+ send_end = load_timing_info.send_end;
+ if (!load_timing_info.push_start.is_null())
+ start = false;
+ }
+ if (send_end.is_null())
+ send_end = base::TimeTicks::Now();
+ }
+ if (result > 0)
+ throttled_byte_count_ += result;
+
+ throttle_callback_ = base::Bind(&DevToolsNetworkTransaction::ThrottleCallback,
+ base::Unretained(this), callback);
+ int rv = interceptor_->StartThrottle(result, throttled_byte_count_, send_end,
+ start, false, throttle_callback_);
+ if (rv != net::ERR_IO_PENDING)
+ throttle_callback_.Reset();
+ if (rv == net::ERR_INTERNET_DISCONNECTED)
+ Fail();
+ return rv;
+}
+
+void DevToolsNetworkTransaction::ThrottleCallback(
+ const net::CompletionCallback& callback, int result, int64_t bytes) {
+ DCHECK(!throttle_callback_.is_null());
+ throttle_callback_.Reset();
+ if (result == net::ERR_INTERNET_DISCONNECTED)
+ Fail();
+ throttled_byte_count_ = bytes;
+ callback.Run(result);
+}
+
+void DevToolsNetworkTransaction::Fail() {
+ DCHECK(request_);
+ DCHECK(!failed_);
+ failed_ = true;
+ network_transaction_->SetBeforeNetworkStartCallback(
+ BeforeNetworkStartCallback());
+ if (interceptor_)
+ interceptor_.reset();
+}
+
+bool DevToolsNetworkTransaction::CheckFailed() {
+ if (failed_)
+ return true;
+ if (interceptor_ && interceptor_->IsOffline()) {
+ Fail();
+ return true;
+ }
+ return false;
+}
+
+int DevToolsNetworkTransaction::Start(const net::HttpRequestInfo* request,
+ const net::CompletionCallback& callback,
+ const net::NetLogWithSource& net_log) {
+ DCHECK(request);
+ request_ = request;
+
+ std::string client_id;
+ bool has_devtools_client_id = request_->extra_headers.HasHeader(
+ kDevToolsEmulateNetworkConditionsClientId);
+ if (has_devtools_client_id) {
+ custom_request_.reset(new net::HttpRequestInfo(*request_));
+ custom_request_->extra_headers.GetHeader(
+ kDevToolsEmulateNetworkConditionsClientId, &client_id);
+ custom_request_->extra_headers.RemoveHeader(
+ kDevToolsEmulateNetworkConditionsClientId);
+
+ if (request_->upload_data_stream) {
+ custom_upload_data_stream_.reset(
+ new DevToolsNetworkUploadDataStream(request_->upload_data_stream));
+ custom_request_->upload_data_stream = custom_upload_data_stream_.get();
+ }
+
+ request_ = custom_request_.get();
+ }
+
+ DevToolsNetworkInterceptor* interceptor =
+ controller_->GetInterceptor(client_id);
+ if (interceptor) {
+ interceptor_ = interceptor->GetWeakPtr();
+ if (custom_upload_data_stream_)
+ custom_upload_data_stream_->SetInterceptor(interceptor);
+ }
+
+ if (CheckFailed())
+ return net::ERR_INTERNET_DISCONNECTED;
+
+ if (!interceptor_)
+ return network_transaction_->Start(request_, callback, net_log);
+
+ int result = network_transaction_->Start(request_,
+ base::Bind(&DevToolsNetworkTransaction::IOCallback,
+ base::Unretained(this), callback, true),
+ net_log);
+ return Throttle(callback, true, result);
+}
+
+int DevToolsNetworkTransaction::RestartIgnoringLastError(
+ const net::CompletionCallback& callback) {
+ if (CheckFailed())
+ return net::ERR_INTERNET_DISCONNECTED;
+ if (!interceptor_)
+ return network_transaction_->RestartIgnoringLastError(callback);
+
+ int result = network_transaction_->RestartIgnoringLastError(
+ base::Bind(&DevToolsNetworkTransaction::IOCallback,
+ base::Unretained(this), callback, true));
+ return Throttle(callback, true, result);
+}
+
+int DevToolsNetworkTransaction::RestartWithCertificate(
+ net::X509Certificate* client_cert,
+ net::SSLPrivateKey* client_private_key,
+ const net::CompletionCallback& callback) {
+ if (CheckFailed())
+ return net::ERR_INTERNET_DISCONNECTED;
+ if (!interceptor_) {
+ return network_transaction_->RestartWithCertificate(
+ client_cert, client_private_key, callback);
+ }
+
+ int result = network_transaction_->RestartWithCertificate(
+ client_cert, client_private_key,
+ base::Bind(&DevToolsNetworkTransaction::IOCallback,
+ base::Unretained(this), callback, true));
+ return Throttle(callback, true, result);
+}
+
+int DevToolsNetworkTransaction::RestartWithAuth(
+ const net::AuthCredentials& credentials,
+ const net::CompletionCallback& callback) {
+ if (CheckFailed())
+ return net::ERR_INTERNET_DISCONNECTED;
+ if (!interceptor_)
+ return network_transaction_->RestartWithAuth(credentials, callback);
+
+ int result = network_transaction_->RestartWithAuth(credentials,
+ base::Bind(&DevToolsNetworkTransaction::IOCallback,
+ base::Unretained(this), callback, true));
+ return Throttle(callback, true, result);
+}
+
+bool DevToolsNetworkTransaction::IsReadyToRestartForAuth() {
+ return network_transaction_->IsReadyToRestartForAuth();
+}
+
+int DevToolsNetworkTransaction::Read(
+ net::IOBuffer* buf,
+ int buf_len,
+ const net::CompletionCallback& callback) {
+ if (CheckFailed())
+ return net::ERR_INTERNET_DISCONNECTED;
+ if (!interceptor_)
+ return network_transaction_->Read(buf, buf_len, callback);
+
+ int result = network_transaction_->Read(buf, buf_len,
+ base::Bind(&DevToolsNetworkTransaction::IOCallback,
+ base::Unretained(this), callback, false));
+ // URLRequestJob relies on synchronous end-of-stream notification.
+ if (result == 0)
+ return result;
+ return Throttle(callback, false, result);
+}
+
+void DevToolsNetworkTransaction::StopCaching() {
+ network_transaction_->StopCaching();
+}
+
+bool DevToolsNetworkTransaction::GetFullRequestHeaders(
+ net::HttpRequestHeaders* headers) const {
+ return network_transaction_->GetFullRequestHeaders(headers);
+}
+
+int64_t DevToolsNetworkTransaction::GetTotalReceivedBytes() const {
+ return network_transaction_->GetTotalReceivedBytes();
+}
+
+int64_t DevToolsNetworkTransaction::GetTotalSentBytes() const {
+ return network_transaction_->GetTotalSentBytes();
+}
+
+void DevToolsNetworkTransaction::DoneReading() {
+ network_transaction_->DoneReading();
+}
+
+const net::HttpResponseInfo*
+DevToolsNetworkTransaction::GetResponseInfo() const {
+ return network_transaction_->GetResponseInfo();
+}
+
+net::LoadState DevToolsNetworkTransaction::GetLoadState() const {
+ return network_transaction_->GetLoadState();
+}
+
+void DevToolsNetworkTransaction::SetQuicServerInfo(
+ net::QuicServerInfo* quic_server_info) {
+ network_transaction_->SetQuicServerInfo(quic_server_info);
+}
+
+bool DevToolsNetworkTransaction::GetLoadTimingInfo(
+ net::LoadTimingInfo* load_timing_info) const {
+ return network_transaction_->GetLoadTimingInfo(load_timing_info);
+}
+
+bool DevToolsNetworkTransaction::GetRemoteEndpoint(
+ net::IPEndPoint* endpoint) const {
+ return network_transaction_->GetRemoteEndpoint(endpoint);
+}
+
+void DevToolsNetworkTransaction::PopulateNetErrorDetails(
+ net::NetErrorDetails* details) const {
+ return network_transaction_->PopulateNetErrorDetails(details);
+}
+
+void DevToolsNetworkTransaction::SetPriority(net::RequestPriority priority) {
+ network_transaction_->SetPriority(priority);
+}
+
+void DevToolsNetworkTransaction::SetWebSocketHandshakeStreamCreateHelper(
+ net::WebSocketHandshakeStreamBase::CreateHelper* create_helper) {
+ network_transaction_->SetWebSocketHandshakeStreamCreateHelper(create_helper);
+}
+
+void DevToolsNetworkTransaction::SetBeforeNetworkStartCallback(
+ const BeforeNetworkStartCallback& callback) {
+ network_transaction_->SetBeforeNetworkStartCallback(callback);
+}
+
+void DevToolsNetworkTransaction::SetBeforeHeadersSentCallback(
+ const BeforeHeadersSentCallback& callback) {
+ network_transaction_->SetBeforeHeadersSentCallback(callback);
+}
+
+int DevToolsNetworkTransaction::ResumeNetworkStart() {
+ if (CheckFailed())
+ return net::ERR_INTERNET_DISCONNECTED;
+ return network_transaction_->ResumeNetworkStart();
+}
+
+void
+DevToolsNetworkTransaction::GetConnectionAttempts(net::ConnectionAttempts* out)
+const {
+ network_transaction_->GetConnectionAttempts(out);
+}
diff --git a/chromium/chrome/browser/devtools/devtools_network_transaction.h b/chromium/chrome/browser/devtools/devtools_network_transaction.h
new file mode 100644
index 00000000000..cb13c423c03
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_network_transaction.h
@@ -0,0 +1,133 @@
+// 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_DEVTOOLS_DEVTOOLS_NETWORK_TRANSACTION_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_TRANSACTION_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/devtools/devtools_network_interceptor.h"
+#include "net/base/completion_callback.h"
+#include "net/base/load_states.h"
+#include "net/base/net_error_details.h"
+#include "net/base/request_priority.h"
+#include "net/http/http_transaction.h"
+#include "net/websockets/websocket_handshake_stream_base.h"
+
+class DevToolsNetworkController;
+class DevToolsNetworkUploadDataStream;
+
+namespace net {
+class AuthCredentials;
+class HttpRequestHeaders;
+struct HttpRequestInfo;
+class HttpResponseInfo;
+class IOBuffer;
+struct LoadTimingInfo;
+class NetLogWithSource;
+class X509Certificate;
+} // namespace net
+
+namespace test {
+class DevToolsNetworkControllerHelper;
+}
+
+// DevToolsNetworkTransaction is a wrapper for network transaction. All
+// HttpTransaction methods are proxied to real transaction, but |callback|
+// parameter is saved and replaced with proxy callback. Fail method should be
+// used to simulate network outage. It runs saved callback (if any) with
+// net::ERR_INTERNET_DISCONNECTED result value.
+class DevToolsNetworkTransaction
+ : public net::HttpTransaction {
+ public:
+ static const char kDevToolsEmulateNetworkConditionsClientId[];
+
+ DevToolsNetworkTransaction(
+ DevToolsNetworkController* controller,
+ std::unique_ptr<net::HttpTransaction> network_transaction);
+
+ ~DevToolsNetworkTransaction() override;
+
+ // HttpTransaction methods:
+ int Start(const net::HttpRequestInfo* request,
+ const net::CompletionCallback& callback,
+ const net::NetLogWithSource& net_log) override;
+ int RestartIgnoringLastError(
+ const net::CompletionCallback& callback) override;
+ int RestartWithCertificate(net::X509Certificate* client_cert,
+ net::SSLPrivateKey* client_private_key,
+ const net::CompletionCallback& callback) override;
+ int RestartWithAuth(const net::AuthCredentials& credentials,
+ const net::CompletionCallback& callback) override;
+ bool IsReadyToRestartForAuth() override;
+
+ int Read(net::IOBuffer* buf,
+ int buf_len,
+ const net::CompletionCallback& callback) override;
+ void StopCaching() override;
+ bool GetFullRequestHeaders(net::HttpRequestHeaders* headers) const override;
+ int64_t GetTotalReceivedBytes() const override;
+ int64_t GetTotalSentBytes() const override;
+ void DoneReading() override;
+ const net::HttpResponseInfo* GetResponseInfo() const override;
+ net::LoadState GetLoadState() const override;
+ void SetQuicServerInfo(net::QuicServerInfo* quic_server_info) override;
+ bool GetLoadTimingInfo(net::LoadTimingInfo* load_timing_info) const override;
+ bool GetRemoteEndpoint(net::IPEndPoint* endpoint) const override;
+ void PopulateNetErrorDetails(net::NetErrorDetails* details) const override;
+ void SetPriority(net::RequestPriority priority) override;
+ void SetWebSocketHandshakeStreamCreateHelper(
+ net::WebSocketHandshakeStreamBase::CreateHelper* create_helper) override;
+ void SetBeforeNetworkStartCallback(
+ const BeforeNetworkStartCallback& callback) override;
+ void SetBeforeHeadersSentCallback(
+ const BeforeHeadersSentCallback& callback) override;
+ int ResumeNetworkStart() override;
+ void GetConnectionAttempts(net::ConnectionAttempts* out) const override;
+
+ protected:
+ friend class test::DevToolsNetworkControllerHelper;
+
+ private:
+ void Fail();
+ bool CheckFailed();
+
+ void IOCallback(const net::CompletionCallback& callback,
+ bool start,
+ int result);
+ int Throttle(const net::CompletionCallback& callback,
+ bool start,
+ int result);
+ void ThrottleCallback(const net::CompletionCallback& callback,
+ int result,
+ int64_t bytes);
+
+ DevToolsNetworkInterceptor::ThrottleCallback throttle_callback_;
+ int64_t throttled_byte_count_;
+
+ DevToolsNetworkController* controller_;
+ base::WeakPtr<DevToolsNetworkInterceptor> interceptor_;
+
+ // Modified upload data stream. Should be destructed after |custom_request_|.
+ std::unique_ptr<DevToolsNetworkUploadDataStream> custom_upload_data_stream_;
+
+ // Modified request. Should be destructed after |network_transaction_|.
+ std::unique_ptr<net::HttpRequestInfo> custom_request_;
+
+ // Real network transaction.
+ std::unique_ptr<net::HttpTransaction> network_transaction_;
+
+ const net::HttpRequestInfo* request_;
+
+ // True if Fail was already invoked.
+ bool failed_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkTransaction);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_TRANSACTION_H_
diff --git a/chromium/chrome/browser/devtools/devtools_network_transaction_factory.cc b/chromium/chrome/browser/devtools/devtools_network_transaction_factory.cc
new file mode 100644
index 00000000000..0b71ab2e9da
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_network_transaction_factory.cc
@@ -0,0 +1,51 @@
+// 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/devtools/devtools_network_transaction_factory.h"
+
+#include <set>
+#include <string>
+#include <utility>
+
+#include "chrome/browser/devtools/devtools_network_controller.h"
+#include "chrome/browser/devtools/devtools_network_transaction.h"
+#include "content/public/browser/service_worker_context.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_network_layer.h"
+#include "net/http/http_network_transaction.h"
+
+DevToolsNetworkTransactionFactory::DevToolsNetworkTransactionFactory(
+ DevToolsNetworkController* controller,
+ net::HttpNetworkSession* session)
+ : controller_(controller),
+ network_layer_(new net::HttpNetworkLayer(session)) {
+ std::set<std::string> headers;
+ headers.insert(
+ DevToolsNetworkTransaction::kDevToolsEmulateNetworkConditionsClientId);
+ content::ServiceWorkerContext::AddExcludedHeadersForFetchEvent(headers);
+}
+
+DevToolsNetworkTransactionFactory::~DevToolsNetworkTransactionFactory() {
+}
+
+int DevToolsNetworkTransactionFactory::CreateTransaction(
+ net::RequestPriority priority,
+ std::unique_ptr<net::HttpTransaction>* trans) {
+ std::unique_ptr<net::HttpTransaction> network_transaction;
+ int rv = network_layer_->CreateTransaction(priority, &network_transaction);
+ if (rv != net::OK) {
+ return rv;
+ }
+ trans->reset(new DevToolsNetworkTransaction(controller_,
+ std::move(network_transaction)));
+ return net::OK;
+}
+
+net::HttpCache* DevToolsNetworkTransactionFactory::GetCache() {
+ return network_layer_->GetCache();
+}
+
+net::HttpNetworkSession* DevToolsNetworkTransactionFactory::GetSession() {
+ return network_layer_->GetSession();
+}
diff --git a/chromium/chrome/browser/devtools/devtools_network_transaction_factory.h b/chromium/chrome/browser/devtools/devtools_network_transaction_factory.h
new file mode 100644
index 00000000000..c53d6fb4872
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_network_transaction_factory.h
@@ -0,0 +1,43 @@
+// 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_DEVTOOLS_DEVTOOLS_NETWORK_TRANSACTION_FACTORY_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_TRANSACTION_FACTORY_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "net/base/request_priority.h"
+#include "net/http/http_transaction_factory.h"
+
+class DevToolsNetworkController;
+
+namespace net {
+class HttpCache;
+class HttpNetworkSession;
+class HttpTransaction;
+}
+
+// NetworkTransactionFactory wraps HttpNetworkTransactions.
+class DevToolsNetworkTransactionFactory : public net::HttpTransactionFactory {
+ public:
+ DevToolsNetworkTransactionFactory(
+ DevToolsNetworkController* controller,
+ net::HttpNetworkSession* session);
+ ~DevToolsNetworkTransactionFactory() override;
+
+ // net::HttpTransactionFactory methods:
+ int CreateTransaction(net::RequestPriority priority,
+ std::unique_ptr<net::HttpTransaction>* trans) override;
+ net::HttpCache* GetCache() override;
+ net::HttpNetworkSession* GetSession() override;
+
+ private:
+ DevToolsNetworkController* controller_;
+ std::unique_ptr<net::HttpTransactionFactory> network_layer_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkTransactionFactory);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_TRANSACTION_FACTORY_H_
diff --git a/chromium/chrome/browser/devtools/devtools_network_upload_data_stream.cc b/chromium/chrome/browser/devtools/devtools_network_upload_data_stream.cc
new file mode 100644
index 00000000000..a85193a9833
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_network_upload_data_stream.cc
@@ -0,0 +1,92 @@
+// 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.
+
+#include "chrome/browser/devtools/devtools_network_upload_data_stream.h"
+
+#include "net/base/net_errors.h"
+
+DevToolsNetworkUploadDataStream::DevToolsNetworkUploadDataStream(
+ net::UploadDataStream* upload_data_stream)
+ : net::UploadDataStream(upload_data_stream->is_chunked(),
+ upload_data_stream->identifier()),
+ throttle_callback_(
+ base::Bind(&DevToolsNetworkUploadDataStream::ThrottleCallback,
+ base::Unretained(this))),
+ throttled_byte_count_(0),
+ upload_data_stream_(upload_data_stream) {
+}
+
+DevToolsNetworkUploadDataStream::~DevToolsNetworkUploadDataStream() {
+ if (interceptor_)
+ interceptor_->StopThrottle(throttle_callback_);
+}
+
+void DevToolsNetworkUploadDataStream::SetInterceptor(
+ DevToolsNetworkInterceptor* interceptor) {
+ DCHECK(!interceptor_);
+ if (interceptor)
+ interceptor_ = interceptor->GetWeakPtr();
+}
+
+bool DevToolsNetworkUploadDataStream::IsInMemory() const {
+ return false;
+}
+
+int DevToolsNetworkUploadDataStream::InitInternal(
+ const net::NetLogWithSource& net_log) {
+ throttled_byte_count_ = 0;
+ int result = upload_data_stream_->Init(
+ base::Bind(&DevToolsNetworkUploadDataStream::StreamInitCallback,
+ base::Unretained(this)),
+ net_log);
+ if (result == net::OK && !is_chunked())
+ SetSize(upload_data_stream_->size());
+ return result;
+}
+
+void DevToolsNetworkUploadDataStream::StreamInitCallback(int result) {
+ if (!is_chunked())
+ SetSize(upload_data_stream_->size());
+ OnInitCompleted(result);
+}
+
+int DevToolsNetworkUploadDataStream::ReadInternal(
+ net::IOBuffer* buf, int buf_len) {
+ int result = upload_data_stream_->Read(buf, buf_len,
+ base::Bind(&DevToolsNetworkUploadDataStream::StreamReadCallback,
+ base::Unretained(this)));
+ return ThrottleRead(result);
+}
+
+void DevToolsNetworkUploadDataStream::StreamReadCallback(int result) {
+ result = ThrottleRead(result);
+ if (result != net::ERR_IO_PENDING)
+ OnReadCompleted(result);
+}
+
+int DevToolsNetworkUploadDataStream::ThrottleRead(int result) {
+ if (is_chunked() && upload_data_stream_->IsEOF())
+ SetIsFinalChunk();
+
+ if (!interceptor_ || result < 0)
+ return result;
+
+ if (result > 0)
+ throttled_byte_count_ += result;
+ return interceptor_->StartThrottle(result, throttled_byte_count_,
+ base::TimeTicks(), false, true, throttle_callback_);
+}
+
+void DevToolsNetworkUploadDataStream::ThrottleCallback(
+ int result, int64_t bytes) {
+ throttled_byte_count_ = bytes;
+ OnReadCompleted(result);
+}
+
+void DevToolsNetworkUploadDataStream::ResetInternal() {
+ upload_data_stream_->Reset();
+ throttled_byte_count_ = 0;
+ if (interceptor_)
+ interceptor_->StopThrottle(throttle_callback_);
+}
diff --git a/chromium/chrome/browser/devtools/devtools_network_upload_data_stream.h b/chromium/chrome/browser/devtools/devtools_network_upload_data_stream.h
new file mode 100644
index 00000000000..f8d0194899c
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_network_upload_data_stream.h
@@ -0,0 +1,51 @@
+// 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.
+
+#ifndef CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_UPLOAD_DATA_STREAM_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_UPLOAD_DATA_STREAM_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/devtools/devtools_network_interceptor.h"
+#include "net/base/completion_callback.h"
+#include "net/base/upload_data_stream.h"
+
+class DevToolsNetworkInterceptor;
+
+// DevToolsNetworkUploadData is a wrapper for upload data stream, which proxies
+// methods and throttles after the original method succeeds.
+class DevToolsNetworkUploadDataStream : public net::UploadDataStream {
+ public:
+ // Supplied |upload_data_stream| must outlive this object.
+ explicit DevToolsNetworkUploadDataStream(
+ net::UploadDataStream* upload_data_stream);
+ ~DevToolsNetworkUploadDataStream() override;
+
+ void SetInterceptor(DevToolsNetworkInterceptor* interceptor);
+
+ private:
+ // net::UploadDataStream implementation.
+ bool IsInMemory() const override;
+ int InitInternal(const net::NetLogWithSource& net_log) override;
+ int ReadInternal(net::IOBuffer* buf, int buf_len) override;
+ void ResetInternal() override;
+
+ void StreamInitCallback(int result);
+ void StreamReadCallback(int result);
+
+ int ThrottleRead(int result);
+ void ThrottleCallback(int result, int64_t bytes);
+
+ DevToolsNetworkInterceptor::ThrottleCallback throttle_callback_;
+ int64_t throttled_byte_count_;
+
+ net::UploadDataStream* upload_data_stream_;
+ base::WeakPtr<DevToolsNetworkInterceptor> interceptor_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsNetworkUploadDataStream);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_UPLOAD_DATA_STREAM_H_
diff --git a/chromium/chrome/browser/devtools/devtools_protocol.cc b/chromium/chrome/browser/devtools/devtools_protocol.cc
new file mode 100644
index 00000000000..c6c8dad7545
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_protocol.cc
@@ -0,0 +1,149 @@
+// 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/devtools/devtools_protocol.h"
+
+#include <utility>
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+
+namespace {
+
+const char kErrorCodeParam[] = "code";
+const char kErrorParam[] = "error";
+const char kErrorMessageParam[] = "message";
+const char kIdParam[] = "id";
+const char kMethodParam[] = "method";
+const char kParamsParam[] = "params";
+const char kResultParam[] = "result";
+
+// JSON RPC 2.0 spec: http://www.jsonrpc.org/specification#error_object
+enum Error { kErrorInvalidParams = -32602, kErrorServerError = -32000 };
+
+} // namespace
+
+// static
+std::string DevToolsProtocol::SerializeCommand(
+ int command_id,
+ const std::string& method,
+ std::unique_ptr<base::DictionaryValue> params) {
+ base::DictionaryValue command;
+ command.SetInteger(kIdParam, command_id);
+ command.SetString(kMethodParam, method);
+ if (params)
+ command.Set(kParamsParam, std::move(params));
+
+ std::string json_command;
+ base::JSONWriter::Write(command, &json_command);
+ return json_command;
+}
+
+// static
+std::unique_ptr<base::DictionaryValue>
+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>();
+ error_object->SetInteger(kErrorCodeParam, kErrorInvalidParams);
+ error_object->SetString(kErrorMessageParam,
+ base::StringPrintf("Missing or invalid '%s' parameter", param.c_str()));
+ response->Set(kErrorParam, std::move(error_object));
+
+ return response;
+}
+
+// static
+std::unique_ptr<base::DictionaryValue> DevToolsProtocol::CreateErrorResponse(
+ int command_id,
+ 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>();
+ error_object->SetInteger(kErrorCodeParam, kErrorServerError);
+ error_object->SetString(kErrorMessageParam, error_message);
+ response->Set(kErrorParam, std::move(error_object));
+
+ return response;
+}
+
+// static
+std::unique_ptr<base::DictionaryValue> DevToolsProtocol::CreateSuccessResponse(
+ int command_id,
+ std::unique_ptr<base::DictionaryValue> result) {
+ std::unique_ptr<base::DictionaryValue> response(new base::DictionaryValue());
+ response->SetInteger(kIdParam, command_id);
+ response->Set(kResultParam, result
+ ? std::move(result)
+ : base::MakeUnique<base::DictionaryValue>());
+
+ return response;
+}
+
+// static
+bool DevToolsProtocol::ParseCommand(base::DictionaryValue* command,
+ int* command_id,
+ std::string* method,
+ base::DictionaryValue** params) {
+ if (!command)
+ return false;
+
+ if (!command->GetInteger(kIdParam, command_id) || *command_id < 0)
+ return false;
+
+ if (!command->GetString(kMethodParam, method))
+ return false;
+
+ if (!command->GetDictionary(kParamsParam, params))
+ *params = nullptr;
+
+ return true;
+}
+
+// static
+bool DevToolsProtocol::ParseNotification(
+ const std::string& json,
+ std::string* method,
+ std::unique_ptr<base::DictionaryValue>* params) {
+ std::unique_ptr<base::Value> value = base::JSONReader::Read(json);
+ if (!value || !value->IsType(base::Value::Type::DICTIONARY))
+ return false;
+
+ std::unique_ptr<base::DictionaryValue> dict(
+ static_cast<base::DictionaryValue*>(value.release()));
+
+ if (!dict->GetString(kMethodParam, method))
+ return false;
+
+ std::unique_ptr<base::Value> params_value;
+ dict->Remove(kParamsParam, &params_value);
+ if (params_value && params_value->IsType(base::Value::Type::DICTIONARY))
+ params->reset(static_cast<base::DictionaryValue*>(params_value.release()));
+
+ return true;
+}
+
+// static
+bool DevToolsProtocol::ParseResponse(const std::string& json,
+ int* command_id,
+ int* error_code) {
+ std::unique_ptr<base::Value> value = base::JSONReader::Read(json);
+ if (!value || !value->IsType(base::Value::Type::DICTIONARY))
+ return false;
+
+ std::unique_ptr<base::DictionaryValue> dict(
+ static_cast<base::DictionaryValue*>(value.release()));
+
+ if (!dict->GetInteger(kIdParam, command_id))
+ return false;
+
+ *error_code = 0;
+ base::DictionaryValue* error_dict = nullptr;
+ if (dict->GetDictionary(kErrorParam, &error_dict))
+ error_dict->GetInteger(kErrorCodeParam, error_code);
+ return true;
+}
diff --git a/chromium/chrome/browser/devtools/devtools_protocol.h b/chromium/chrome/browser/devtools/devtools_protocol.h
new file mode 100644
index 00000000000..7ba104df674
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_protocol.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef CHROME_BROWSER_DEVTOOLS_DEVTOOLS_PROTOCOL_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_PROTOCOL_H_
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/values.h"
+
+// Utility class for processing DevTools remote debugging messages.
+class DevToolsProtocol {
+ public:
+ // Caller maintains ownership of |command|. |*params| is owned by |command|.
+ static bool ParseCommand(base::DictionaryValue* command,
+ int* command_id,
+ std::string* method,
+ base::DictionaryValue** params);
+
+ static bool ParseNotification(const std::string& json,
+ std::string* method,
+ std::unique_ptr<base::DictionaryValue>* params);
+
+ static bool ParseResponse(const std::string& json,
+ int* command_id,
+ int* error_code);
+
+ static std::string SerializeCommand(
+ int command_id,
+ const std::string& method,
+ std::unique_ptr<base::DictionaryValue> params);
+
+ static std::unique_ptr<base::DictionaryValue> CreateSuccessResponse(
+ int command_id,
+ std::unique_ptr<base::DictionaryValue> result);
+
+ static std::unique_ptr<base::DictionaryValue> CreateInvalidParamsResponse(
+ int command_id,
+ const std::string& param);
+
+ static std::unique_ptr<base::DictionaryValue> CreateErrorResponse(
+ int command_id,
+ const std::string& error_message);
+
+ private:
+ DevToolsProtocol() {}
+ ~DevToolsProtocol() {}
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_PROTOCOL_H_
diff --git a/chromium/chrome/browser/devtools/devtools_protocol_constants_generator.py b/chromium/chrome/browser/devtools/devtools_protocol_constants_generator.py
new file mode 100755
index 00000000000..2ab803f3070
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_protocol_constants_generator.py
@@ -0,0 +1,202 @@
+#!/usr/bin/python
+# 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.
+
+import sys
+import string
+import json
+
+package = sys.argv[1]
+output_cc_path = sys.argv[2]
+output_h_path = sys.argv[3]
+blink_protocol_path = sys.argv[4]
+
+template_h = string.Template("""\
+// 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 ${PACKAGE}_BROWSER_DEVTOOLS_DEVTOOLS_PROTOCOL_CONSTANTS_H_
+#define ${PACKAGE}_BROWSER_DEVTOOLS_DEVTOOLS_PROTOCOL_CONSTANTS_H_
+
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// Generated by
+// chrome/browser/devtools/devtools_protocol_constants_generator.py from
+// gen/blink/core/inspector/protocol.json
+
+#include <string>
+
+namespace $package {
+namespace devtools {
+
+extern const char kProtocolVersion[];
+
+bool IsSupportedProtocolVersion(const std::string& version);
+
+extern const char kResult[];
+$contents
+
+} // devtools
+} // $package
+
+#endif // ${PACKAGE}_BROWSER_DEVTOOLS_DEVTOOLS_PROTOCOL_CONSTANTS_H_
+""")
+
+template_cc = string.Template("""\
+// 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.
+
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// Generated by
+// chrome/browser/devtools/devtools_protocol_constants_generator.py from
+// gen/blink/core/inspector/protocol.json
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "$package/browser/devtools/devtools_protocol_constants.h"
+
+namespace $package {
+namespace devtools {
+
+const char kProtocolVersion[] = "$major.$minor";
+
+bool IsSupportedProtocolVersion(const std::string& version) {
+ std::vector<base::StringPiece> tokens = base::SplitStringPiece(
+ version, ".", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ int major, minor;
+ return tokens.size() == 2 &&
+ base::StringToInt(tokens[0], &major) && major == $major &&
+ base::StringToInt(tokens[1], &minor) && minor <= $minor;
+}
+
+const char kResult[] = "result";
+$contents
+
+} // devtools
+} // $package
+""")
+
+def Capitalize(s):
+ return s[:1].capitalize() + s[1:]
+
+def ToIdentifier(s):
+ return "".join([Capitalize(part) for part in s.split("-")])
+
+references = []
+
+def CreateNamespace(domain_name, data, keys, prefixes, name = None):
+ result = {}
+ if name:
+ result["kName"] = name
+ for i, key in enumerate(keys):
+ if key in data:
+ for parameter in data[key]:
+ parameter_name = parameter["name"];
+ result[prefixes[i] + Capitalize(parameter_name)] = parameter_name
+ if "enum" in parameter:
+ enum_name = Capitalize(parameter_name)
+ result[enum_name] = {}
+ for enum in parameter["enum"]:
+ result[enum_name]["kEnum" + ToIdentifier(enum)] = enum
+ reference = ""
+ if "$ref" in parameter:
+ reference = parameter["$ref"]
+ if "items" in parameter and "$ref" in parameter["items"]:
+ reference = parameter["items"]["$ref"]
+ if reference:
+ if not "." in reference:
+ reference = domain_name + "." + reference
+ references.append(reference)
+ return result
+
+def FormatContents(tree, indent, format_string):
+ outer = dict((key, value) for key, value in tree.iteritems()
+ if not isinstance(value, dict))
+ inner = dict((key, value) for key, value in tree.iteritems()
+ if isinstance(value, dict))
+ body = ""
+ body += "".join(indent + format_string.format(key, value)
+ for (key, value) in sorted(outer.items()))
+ body += "".join(FormatNamespace(key, value, indent, format_string)
+ for (key, value) in sorted(inner.items()))
+ return body
+
+def FormatNamespace(title, tree, indent, format_string):
+ if (not tree):
+ return ""
+ body = '\n' + indent + "namespace " + title + " {\n"
+ body += FormatContents(tree, indent + " ", format_string)
+ body += indent + "} // " + title + "\n"
+ return body
+
+def CreateHeader(tree, output_file):
+ contents = FormatContents(tree, "", "extern const char {0}[];\n")
+ output_file.write(template_h.substitute({
+ "contents": contents,
+ "package": package,
+ "PACKAGE": package.upper()
+ }))
+
+def CreateBody(tree, version, output_file):
+ contents = FormatContents(tree, "", "const char {0}[] = \"{1}\";\n")
+ output_file.write(template_cc.substitute({
+ "major": version["major"],
+ "minor": version["minor"],
+ "contents": contents,
+ "package": package
+ }))
+
+blink_protocol_data = open(blink_protocol_path).read()
+blink_protocol = json.loads(blink_protocol_data)
+blink_version = blink_protocol["version"]
+
+domains = blink_protocol["domains"]
+
+namespace_tree = {}
+
+for domain in domains:
+ domain_value = {}
+ domain_namespace_name = Capitalize(domain["domain"])
+ if "commands" in domain:
+ for command in domain["commands"]:
+ domain_value[command["name"]] = CreateNamespace(domain["domain"],
+ command, ["parameters", "returns"], ["kParam", "kResponse"],
+ domain_namespace_name + "." + command["name"])
+
+ if "events" in domain:
+ for event in domain["events"]:
+ domain_value[event["name"]] = CreateNamespace(domain["domain"],
+ event, ["parameters"], ["kParam"],
+ domain_namespace_name + "." + event["name"])
+ if domain_value:
+ namespace_tree[domain_namespace_name] = domain_value
+
+while (references):
+ reference = references.pop();
+ path = reference.split(".");
+ parent_namespace = namespace_tree;
+ for path_segment in path[0:-1]:
+ if path_segment not in parent_namespace:
+ parent_namespace[path_segment] = {}
+ parent_namespace = parent_namespace[path_segment]
+ if (path[-1] not in parent_namespace):
+ try:
+ domain = [d for d in domains if d["domain"] == path[0]][0]
+ ref_type = [t for t in domain["types"] if t["id"] == path[1]][0]
+ parent_namespace[ref_type["id"]] = CreateNamespace(path[0],
+ ref_type, ["properties"], ["kParam"])
+ except IndexError:
+ sys.stderr.write("Failed to resolve type [{0}].\n".format(reference))
+ sys.exit(1)
+
+for (namespace_name, namespace) in namespace_tree.items():
+ namespace["kName"] = namespace_name
+
+with open(output_cc_path, "w") as f:
+ CreateBody(namespace_tree, blink_version, f)
+
+with open(output_h_path, "w") as f:
+ CreateHeader(namespace_tree, f)
diff --git a/chromium/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chromium/chrome/browser/devtools/devtools_sanity_browsertest.cc
new file mode 100644
index 00000000000..5ac3e8a1a17
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_sanity_browsertest.cc
@@ -0,0 +1,2082 @@
+// 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 <stddef.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/cancelable_callback.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/devtools/device/tcp_device_provider.h"
+#include "chrome/browser/devtools/devtools_window_testing.h"
+#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"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/extension_process_policy.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_chrome_web_ui_controller_factory.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/app_modal/javascript_app_modal_dialog.h"
+#include "components/app_modal/native_app_modal_dialog.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/child_process_data.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/worker_service.h"
+#include "content/public/browser/worker_service_observer.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/url_constants.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_registry_observer.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/notification_types.h"
+#include "extensions/browser/test_extension_registry_observer.h"
+#include "extensions/common/switches.h"
+#include "extensions/common/value_builder.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"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_filter.h"
+#include "net/url_request/url_request_http_job.h"
+#include "third_party/WebKit/public/platform/WebInputEvent.h"
+#include "ui/compositor/compositor_switches.h"
+#include "ui/gl/gl_switches.h"
+#include "url/gurl.h"
+
+using app_modal::AppModalDialog;
+using app_modal::JavaScriptAppModalDialog;
+using app_modal::NativeAppModalDialog;
+using content::BrowserThread;
+using content::DevToolsAgentHost;
+using content::NavigationController;
+using content::RenderFrameHost;
+using content::RenderViewHost;
+using content::WebContents;
+using content::WorkerService;
+using content::WorkerServiceObserver;
+using extensions::Extension;
+
+namespace {
+
+const char kDebuggerTestPage[] = "files/devtools/debugger_test_page.html";
+const char kPauseWhenLoadingDevTools[] =
+ "files/devtools/pause_when_loading_devtools.html";
+const char kPauseWhenScriptIsRunning[] =
+ "files/devtools/pause_when_script_is_running.html";
+const char kPageWithContentScript[] =
+ "files/devtools/page_with_content_script.html";
+const char kNavigateBackTestPage[] =
+ "files/devtools/navigate_back.html";
+const char kWindowOpenTestPage[] = "files/devtools/window_open.html";
+const char kLatencyInfoTestPage[] = "files/devtools/latency_info.html";
+const char kChunkedTestPage[] = "chunked";
+const char kPushTestPage[] = "files/devtools/push_test_page.html";
+// The resource is not really pushed, but mock url request job pretends it is.
+const char kPushTestResource[] = "devtools/image.png";
+const char kPushUseNullEndTime[] = "pushUseNullEndTime";
+const char kSlowTestPage[] =
+ "chunked?waitBeforeHeaders=100&waitBetweenChunks=100&chunksNumber=2";
+const char kSharedWorkerTestPage[] =
+ "files/workers/workers_ui_shared_worker.html";
+const char kSharedWorkerTestWorker[] =
+ "files/workers/workers_ui_shared_worker.js";
+const char kReloadSharedWorkerTestPage[] =
+ "files/workers/debug_shared_worker_initialization.html";
+const char kReloadSharedWorkerTestWorker[] =
+ "files/workers/debug_shared_worker_initialization.js";
+const char kEmulateNetworkConditionsPage[] =
+ "files/devtools/emulate_network_conditions.html";
+
+template <typename... T>
+void DispatchOnTestSuiteSkipCheck(DevToolsWindow* window,
+ const char* method,
+ T... args) {
+ RenderViewHost* rvh = DevToolsWindowTesting::Get(window)
+ ->main_web_contents()
+ ->GetRenderViewHost();
+ std::string result;
+ const char* args_array[] = {method, args...};
+ std::ostringstream script;
+ script << "uiTests.dispatchOnTestSuite([";
+ for (size_t i = 0; i < arraysize(args_array); ++i)
+ script << (i ? "," : "") << '\"' << args_array[i] << '\"';
+ script << "])";
+ ASSERT_TRUE(
+ content::ExecuteScriptAndExtractString(rvh, script.str(), &result));
+ EXPECT_EQ("[OK]", result);
+}
+
+template <typename... T>
+void DispatchOnTestSuite(DevToolsWindow* window,
+ const char* method,
+ T... args) {
+ std::string result;
+ RenderViewHost* rvh = DevToolsWindowTesting::Get(window)
+ ->main_web_contents()
+ ->GetRenderViewHost();
+ // At first check that JavaScript part of the front-end is loaded by
+ // checking that global variable uiTests exists(it's created after all js
+ // files have been loaded) and has runTest method.
+ ASSERT_TRUE(
+ content::ExecuteScriptAndExtractString(
+ rvh,
+ "window.domAutomationController.send("
+ " '' + (window.uiTests && (typeof uiTests.dispatchOnTestSuite)));",
+ &result));
+ ASSERT_EQ("function", result) << "DevTools front-end is broken.";
+ DispatchOnTestSuiteSkipCheck(window, method, args...);
+}
+
+void RunTestFunction(DevToolsWindow* window, const char* test_name) {
+ DispatchOnTestSuite(window, test_name);
+}
+
+void SwitchToPanel(DevToolsWindow* window, const char* panel) {
+ DispatchOnTestSuite(window, "switchToPanel", panel);
+}
+
+// Version of SwitchToPanel that works with extension-created panels.
+void SwitchToExtensionPanel(DevToolsWindow* window,
+ const Extension* devtools_extension,
+ const char* panel_name) {
+ // The full name is the concatenation of the extension URL (stripped of its
+ // trailing '/') and the |panel_name| that was passed to panels.create().
+ std::string prefix = base::TrimString(devtools_extension->url().spec(), "/",
+ base::TRIM_TRAILING)
+ .as_string();
+ SwitchToPanel(window, (prefix + panel_name).c_str());
+}
+
+class PushTimesMockURLRequestJob : public net::URLRequestMockHTTPJob {
+ public:
+ PushTimesMockURLRequestJob(net::URLRequest* request,
+ net::NetworkDelegate* network_delegate,
+ base::FilePath file_path)
+ : net::URLRequestMockHTTPJob(
+ request,
+ network_delegate,
+ file_path,
+ base::CreateTaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskPriority::BACKGROUND,
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {}
+
+ void Start() override {
+ load_timing_info_.socket_reused = true;
+ load_timing_info_.request_start_time = base::Time::Now();
+ load_timing_info_.request_start = base::TimeTicks::Now();
+ load_timing_info_.send_start = base::TimeTicks::Now();
+ load_timing_info_.send_end = base::TimeTicks::Now();
+ load_timing_info_.receive_headers_end = base::TimeTicks::Now();
+
+ net::URLRequestMockHTTPJob::Start();
+ }
+
+ void GetLoadTimingInfo(net::LoadTimingInfo* load_timing_info) const override {
+ load_timing_info_.push_start = load_timing_info_.request_start -
+ base::TimeDelta::FromMilliseconds(100);
+ if (load_timing_info_.push_end.is_null() &&
+ request()->url().query() != kPushUseNullEndTime) {
+ load_timing_info_.push_end = base::TimeTicks::Now();
+ }
+ *load_timing_info = load_timing_info_;
+ }
+
+ private:
+ mutable net::LoadTimingInfo load_timing_info_;
+ DISALLOW_COPY_AND_ASSIGN(PushTimesMockURLRequestJob);
+};
+
+class TestInterceptor : public net::URLRequestInterceptor {
+ public:
+ // Creates TestInterceptor and registers it with the URLRequestFilter,
+ // which takes ownership of it.
+ static void Register(const GURL& url, const base::FilePath& file_path) {
+ EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ net::URLRequestFilter::GetInstance()->AddHostnameInterceptor(
+ url.scheme(), url.host(),
+ base::WrapUnique(new TestInterceptor(url, file_path)));
+ }
+
+ // Unregisters previously created TestInterceptor, which should delete it.
+ static void Unregister(const GURL& url) {
+ EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ net::URLRequestFilter::GetInstance()->RemoveHostnameHandler(url.scheme(),
+ url.host());
+ }
+
+ // net::URLRequestJobFactory::ProtocolHandler implementation:
+ net::URLRequestJob* MaybeInterceptRequest(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const override {
+ EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (request->url().path() != url_.path())
+ return nullptr;
+ return new PushTimesMockURLRequestJob(request, network_delegate,
+ file_path_);
+ }
+
+ private:
+ TestInterceptor(const GURL& url, const base::FilePath& file_path)
+ : url_(url), file_path_(file_path) {}
+
+ const GURL url_;
+ const base::FilePath file_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestInterceptor);
+};
+
+} // namespace
+
+class DevToolsSanityTest : public InProcessBrowserTest {
+ public:
+ DevToolsSanityTest() : window_(NULL) {}
+
+ void SetUpOnMainThread() override {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ }
+
+ protected:
+ void RunTest(const std::string& test_name, const std::string& test_page) {
+ OpenDevToolsWindow(test_page, false);
+ RunTestFunction(window_, test_name.c_str());
+ CloseDevToolsWindow();
+ }
+
+ template <typename... T>
+ void RunTestMethod(const char* method, T... args) {
+ DispatchOnTestSuiteSkipCheck(window_, method, args...);
+ }
+
+ template <typename... T>
+ void DispatchAndWait(const char* method, T... args) {
+ DispatchOnTestSuiteSkipCheck(window_, "waitForAsync", method, args...);
+ }
+
+ template <typename... T>
+ void DispatchInPageAndWait(const char* method, T... args) {
+ DispatchAndWait("invokePageFunctionAsync", method, args...);
+ }
+
+ void LoadTestPage(const std::string& test_page) {
+ GURL url = spawned_test_server()->GetURL(test_page);
+ ui_test_utils::NavigateToURL(browser(), url);
+ }
+
+ void OpenDevToolsWindow(const std::string& test_page, bool is_docked) {
+ ASSERT_TRUE(spawned_test_server()->Start());
+ LoadTestPage(test_page);
+
+ window_ = DevToolsWindowTesting::OpenDevToolsWindowSync(GetInspectedTab(),
+ is_docked);
+ }
+
+ WebContents* GetInspectedTab() {
+ return browser()->tab_strip_model()->GetWebContentsAt(0);
+ }
+
+ void CloseDevToolsWindow() {
+ DevToolsWindowTesting::CloseDevToolsWindowSync(window_);
+ }
+
+ WebContents* main_web_contents() {
+ return DevToolsWindowTesting::Get(window_)->main_web_contents();
+ }
+
+ WebContents* toolbox_web_contents() {
+ return DevToolsWindowTesting::Get(window_)->toolbox_web_contents();
+ }
+
+ DevToolsWindow* window_;
+};
+
+// Used to block until a dev tools window gets beforeunload event.
+class DevToolsWindowBeforeUnloadObserver
+ : public content::WebContentsObserver {
+ public:
+ explicit DevToolsWindowBeforeUnloadObserver(DevToolsWindow*);
+ void Wait();
+ private:
+ // Invoked when the beforeunload handler fires.
+ void BeforeUnloadFired(const base::TimeTicks& proceed_time) override;
+
+ bool m_fired;
+ scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
+ DISALLOW_COPY_AND_ASSIGN(DevToolsWindowBeforeUnloadObserver);
+};
+
+DevToolsWindowBeforeUnloadObserver::DevToolsWindowBeforeUnloadObserver(
+ DevToolsWindow* devtools_window)
+ : WebContentsObserver(
+ DevToolsWindowTesting::Get(devtools_window)->main_web_contents()),
+ m_fired(false) {
+}
+
+void DevToolsWindowBeforeUnloadObserver::Wait() {
+ if (m_fired)
+ return;
+ message_loop_runner_ = new content::MessageLoopRunner;
+ message_loop_runner_->Run();
+}
+
+void DevToolsWindowBeforeUnloadObserver::BeforeUnloadFired(
+ const base::TimeTicks& proceed_time) {
+ m_fired = true;
+ if (message_loop_runner_.get())
+ message_loop_runner_->Quit();
+}
+
+class DevToolsBeforeUnloadTest: public DevToolsSanityTest {
+ public:
+ void CloseInspectedTab() {
+ browser()->tab_strip_model()->CloseWebContentsAt(0,
+ TabStripModel::CLOSE_NONE);
+ }
+
+ void CloseDevToolsWindowAsync() {
+ DevToolsWindowTesting::CloseDevToolsWindow(window_);
+ }
+
+ void CloseInspectedBrowser() {
+ chrome::CloseWindow(browser());
+ }
+
+ protected:
+ void InjectBeforeUnloadListener(content::WebContents* web_contents) {
+ ASSERT_TRUE(content::ExecuteScript(web_contents->GetRenderViewHost(),
+ "window.addEventListener('beforeunload',"
+ "function(event) { event.returnValue = 'Foo'; });"));
+ content::PrepContentsForBeforeUnloadTest(web_contents);
+ }
+
+ void RunBeforeUnloadSanityTest(bool is_docked,
+ base::Callback<void(void)> close_method,
+ bool wait_for_browser_close = true) {
+ OpenDevToolsWindow(kDebuggerTestPage, is_docked);
+ scoped_refptr<content::MessageLoopRunner> runner =
+ new content::MessageLoopRunner;
+ DevToolsWindowTesting::Get(window_)->
+ SetCloseCallback(runner->QuitClosure());
+ InjectBeforeUnloadListener(main_web_contents());
+ {
+ DevToolsWindowBeforeUnloadObserver before_unload_observer(window_);
+ close_method.Run();
+ CancelModalDialog();
+ before_unload_observer.Wait();
+ }
+ {
+ content::WindowedNotificationObserver close_observer(
+ chrome::NOTIFICATION_BROWSER_CLOSED,
+ content::Source<Browser>(browser()));
+ close_method.Run();
+ AcceptModalDialog();
+ if (wait_for_browser_close)
+ close_observer.Wait();
+ }
+ runner->Run();
+ }
+
+ DevToolsWindow* OpenDevToolWindowOnWebContents(
+ content::WebContents* contents, bool is_docked) {
+ DevToolsWindow* window =
+ DevToolsWindowTesting::OpenDevToolsWindowSync(contents, is_docked);
+ return window;
+ }
+
+ void OpenDevToolsPopupWindow(DevToolsWindow* devtools_window) {
+ content::WindowedNotificationObserver observer(
+ content::NOTIFICATION_LOAD_STOP,
+ content::NotificationService::AllSources());
+ ASSERT_TRUE(content::ExecuteScript(
+ DevToolsWindowTesting::Get(devtools_window)->
+ main_web_contents()->GetRenderViewHost(),
+ "window.open(\"\", \"\", \"location=0\");"));
+ observer.Wait();
+ }
+
+ void CloseDevToolsPopupWindow(DevToolsWindow* devtools_window) {
+ DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
+ }
+
+ void AcceptModalDialog() {
+ NativeAppModalDialog* native_dialog = GetDialog();
+ native_dialog->AcceptAppModalDialog();
+ }
+
+ void CancelModalDialog() {
+ NativeAppModalDialog* native_dialog = GetDialog();
+ native_dialog->CancelAppModalDialog();
+ }
+
+ NativeAppModalDialog* GetDialog() {
+ AppModalDialog* dialog = ui_test_utils::WaitForAppModalDialog();
+ EXPECT_TRUE(dialog->IsJavaScriptModalDialog());
+ JavaScriptAppModalDialog* js_dialog =
+ static_cast<JavaScriptAppModalDialog*>(dialog);
+ NativeAppModalDialog* native_dialog = js_dialog->native_dialog();
+ EXPECT_TRUE(native_dialog);
+ return native_dialog;
+ }
+};
+
+void TimeoutCallback(const std::string& timeout_message) {
+ ADD_FAILURE() << timeout_message;
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+// Base class for DevTools tests that test devtools functionality for
+// extensions and content scripts.
+class DevToolsExtensionTest : public DevToolsSanityTest,
+ public content::NotificationObserver {
+ public:
+ DevToolsExtensionTest() : DevToolsSanityTest() {
+ PathService::Get(chrome::DIR_TEST_DATA, &test_extensions_dir_);
+ test_extensions_dir_ = test_extensions_dir_.AppendASCII("devtools");
+ test_extensions_dir_ = test_extensions_dir_.AppendASCII("extensions");
+ }
+
+ protected:
+ // Load an extension from test\data\devtools\extensions\<extension_name>
+ void LoadExtension(const char* extension_name) {
+ base::FilePath path = test_extensions_dir_.AppendASCII(extension_name);
+ ASSERT_TRUE(LoadExtensionFromPath(path)) << "Failed to load extension.";
+ }
+
+ const Extension* LoadExtensionFromPath(const base::FilePath& path) {
+ ExtensionService* service = extensions::ExtensionSystem::Get(
+ browser()->profile())->extension_service();
+ extensions::ExtensionRegistry* registry =
+ extensions::ExtensionRegistry::Get(browser()->profile());
+ extensions::TestExtensionRegistryObserver observer(registry);
+ extensions::UnpackedInstaller::Create(service)->Load(path);
+ observer.WaitForExtensionLoaded();
+
+ if (!WaitForExtensionViewsToLoad())
+ return nullptr;
+
+ return GetExtensionByPath(registry->enabled_extensions(), path);
+ }
+
+ // Loads a dynamically generated extension populated with a bunch of test
+ // pages. |name| is the extension name to use in the manifest.
+ // |devtools_page|, if non-empty, indicates which test page should be be
+ // listed as a devtools_page in the manifest. If |devtools_page| is empty, a
+ // non-devtools extension is created instead. |panel_iframe_src| controls the
+ // src= attribute of the <iframe> element in the 'panel.html' test page.
+ const Extension* LoadExtensionForTest(const std::string& name,
+ const std::string& devtools_page,
+ const std::string& panel_iframe_src) {
+ test_extension_dirs_.push_back(
+ base::MakeUnique<extensions::TestExtensionDir>());
+ extensions::TestExtensionDir* dir = test_extension_dirs_.back().get();
+
+ extensions::DictionaryBuilder manifest;
+ manifest.Set("name", name)
+ .Set("version", "1")
+ .Set("manifest_version", 2)
+ // simple_test_page.html is currently the only page referenced outside
+ // of its own extension in the tests
+ .Set("web_accessible_resources",
+ extensions::ListBuilder().Append("simple_test_page.html").Build());
+
+ // If |devtools_page| isn't empty, make it a devtools extension in the
+ // manifest.
+ if (!devtools_page.empty())
+ manifest.Set("devtools_page", devtools_page);
+
+ dir->WriteManifest(manifest.ToJSON());
+
+ GURL http_frame_url =
+ embedded_test_server()->GetURL("a.com", "/popup_iframe.html");
+
+ // If this is a devtools extension, |devtools_page| will indicate which of
+ // these devtools_pages will end up being used. Different tests use
+ // different devtools_pages.
+ dir->WriteFile(FILE_PATH_LITERAL("web_devtools_page.html"),
+ "<html><body><iframe src='" + http_frame_url.spec() +
+ "'></iframe></body></html>");
+
+ dir->WriteFile(FILE_PATH_LITERAL("simple_devtools_page.html"),
+ "<html><body></body></html>");
+
+ dir->WriteFile(
+ FILE_PATH_LITERAL("panel_devtools_page.html"),
+ "<html><head><script "
+ "src='panel_devtools_page.js'></script></head><body></body></html>");
+
+ dir->WriteFile(FILE_PATH_LITERAL("panel_devtools_page.js"),
+ "chrome.devtools.panels.create('iframe_panel',\n"
+ " null,\n"
+ " 'panel.html',\n"
+ " function(panel) {\n"
+ " chrome.devtools.inspectedWindow.eval(\n"
+ " 'console.log(\"PASS\")');\n"
+ " }\n"
+ ");\n");
+
+ dir->WriteFile(FILE_PATH_LITERAL("sidebarpane_devtools_page.html"),
+ "<html><head><script src='sidebarpane_devtools_page.js'>"
+ "</script></head><body></body></html>");
+
+ dir->WriteFile(
+ FILE_PATH_LITERAL("sidebarpane_devtools_page.js"),
+ "chrome.devtools.panels.elements.createSidebarPane('iframe_pane',\n"
+ " function(sidebar) {\n"
+ " chrome.devtools.inspectedWindow.eval(\n"
+ " 'console.log(\"PASS\")');\n"
+ " sidebar.setPage('panel.html');\n"
+ " }\n"
+ ");\n");
+
+ dir->WriteFile(FILE_PATH_LITERAL("panel.html"),
+ "<html><body><iframe src='" + panel_iframe_src +
+ "'></iframe></body></html>");
+
+ dir->WriteFile(FILE_PATH_LITERAL("simple_test_page.html"),
+ "<html><body>This is a test</body></html>");
+
+ GURL web_url = embedded_test_server()->GetURL("a.com", "/title3.html");
+
+ dir->WriteFile(FILE_PATH_LITERAL("multi_frame_page.html"),
+ "<html><body><iframe src='about:blank'>"
+ "</iframe><iframe src='data:text/html,foo'>"
+ "</iframe><iframe src='" +
+ web_url.spec() + "'></iframe></body></html>");
+
+ // Install the extension.
+ return LoadExtensionFromPath(dir->UnpackedPath());
+ }
+
+ private:
+ const Extension* GetExtensionByPath(
+ const extensions::ExtensionSet& extensions,
+ const base::FilePath& path) {
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+ base::FilePath extension_path = base::MakeAbsoluteFilePath(path);
+ EXPECT_TRUE(!extension_path.empty());
+ for (const scoped_refptr<const Extension>& extension : extensions) {
+ if (extension->path() == extension_path) {
+ return extension.get();
+ }
+ }
+ return nullptr;
+ }
+
+ bool WaitForExtensionViewsToLoad() {
+ // Wait for all the extension render views that exist to finish loading.
+ // NOTE: This assumes that the extension views list is not changing while
+ // this method is running.
+
+ content::NotificationRegistrar registrar;
+ registrar.Add(this,
+ extensions::NOTIFICATION_EXTENSION_HOST_DID_STOP_FIRST_LOAD,
+ content::NotificationService::AllSources());
+ base::CancelableClosure timeout(
+ base::Bind(&TimeoutCallback, "Extension host load timed out."));
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, timeout.callback(), TestTimeouts::action_timeout());
+
+ extensions::ProcessManager* manager =
+ extensions::ProcessManager::Get(browser()->profile());
+ extensions::ProcessManager::FrameSet all_frames = manager->GetAllFrames();
+ for (extensions::ProcessManager::FrameSet::const_iterator iter =
+ all_frames.begin();
+ iter != all_frames.end();) {
+ if (!content::WebContents::FromRenderFrameHost(*iter)->IsLoading())
+ ++iter;
+ else
+ content::RunMessageLoop();
+ }
+
+ timeout.Cancel();
+ return true;
+ }
+
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override {
+ DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_HOST_DID_STOP_FIRST_LOAD,
+ type);
+ base::MessageLoopForUI::current()->QuitWhenIdle();
+ }
+
+ std::vector<std::unique_ptr<extensions::TestExtensionDir>>
+ test_extension_dirs_;
+ base::FilePath test_extensions_dir_;
+};
+
+class DevToolsExperimentalExtensionTest : public DevToolsExtensionTest {
+ public:
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ command_line->AppendSwitch(
+ extensions::switches::kEnableExperimentalExtensionApis);
+ }
+};
+
+class WorkerDevToolsSanityTest : public InProcessBrowserTest {
+ public:
+ WorkerDevToolsSanityTest() : window_(NULL) {}
+
+ protected:
+ class WorkerData : public base::RefCountedThreadSafe<WorkerData> {
+ public:
+ WorkerData() : worker_process_id(0), worker_route_id(0) {}
+ int worker_process_id;
+ int worker_route_id;
+
+ private:
+ friend class base::RefCountedThreadSafe<WorkerData>;
+ ~WorkerData() {}
+ };
+
+ class WorkerCreationObserver : public WorkerServiceObserver {
+ public:
+ explicit WorkerCreationObserver(const std::string& path,
+ WorkerData* worker_data)
+ : path_(path), worker_data_(worker_data) {}
+
+ private:
+ ~WorkerCreationObserver() override {}
+
+ void WorkerCreated(const GURL& url,
+ const base::string16& name,
+ int process_id,
+ int route_id) override {
+ if (url.path().rfind(path_) == std::string::npos)
+ return;
+ worker_data_->worker_process_id = process_id;
+ worker_data_->worker_route_id = route_id;
+ WorkerService::GetInstance()->RemoveObserver(this);
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::MessageLoop::QuitWhenIdleClosure());
+ delete this;
+ }
+ std::string path_;
+ scoped_refptr<WorkerData> worker_data_;
+ };
+
+ class WorkerTerminationObserver : public WorkerServiceObserver {
+ public:
+ explicit WorkerTerminationObserver(WorkerData* worker_data)
+ : worker_data_(worker_data) {
+ }
+
+ private:
+ ~WorkerTerminationObserver() override {}
+
+ void WorkerDestroyed(int process_id, int route_id) override {
+ ASSERT_EQ(worker_data_->worker_process_id, process_id);
+ ASSERT_EQ(worker_data_->worker_route_id, route_id);
+ WorkerService::GetInstance()->RemoveObserver(this);
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::MessageLoop::QuitWhenIdleClosure());
+ delete this;
+ }
+ scoped_refptr<WorkerData> worker_data_;
+ };
+
+ void RunTest(const char* test_name,
+ const char* test_page,
+ const char* worker_path) {
+ ASSERT_TRUE(spawned_test_server()->Start());
+ GURL url = spawned_test_server()->GetURL(test_page);
+ ui_test_utils::NavigateToURL(browser(), url);
+
+ scoped_refptr<WorkerData> worker_data =
+ WaitForFirstSharedWorker(worker_path);
+ OpenDevToolsWindowForSharedWorker(worker_data.get());
+ RunTestFunction(window_, test_name);
+ CloseDevToolsWindow();
+ }
+
+ static void TerminateWorkerOnIOThread(scoped_refptr<WorkerData> worker_data) {
+ if (!WorkerService::GetInstance()->TerminateWorker(
+ worker_data->worker_process_id, worker_data->worker_route_id))
+ FAIL() << "Failed to terminate worker.\n";
+ WorkerService::GetInstance()->AddObserver(
+ new WorkerTerminationObserver(worker_data.get()));
+ }
+
+ static void TerminateWorker(scoped_refptr<WorkerData> worker_data) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&TerminateWorkerOnIOThread, worker_data));
+ content::RunMessageLoop();
+ }
+
+ static void WaitForFirstSharedWorkerOnIOThread(
+ const std::string& path,
+ scoped_refptr<WorkerData> worker_data) {
+ std::vector<WorkerService::WorkerInfo> worker_info =
+ WorkerService::GetInstance()->GetWorkers();
+ for (size_t i = 0; i < worker_info.size(); i++) {
+ if (worker_info[i].url.path().rfind(path) == std::string::npos)
+ continue;
+ worker_data->worker_process_id = worker_info[0].process_id;
+ worker_data->worker_route_id = worker_info[0].route_id;
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::MessageLoop::QuitWhenIdleClosure());
+ return;
+ }
+
+ WorkerService::GetInstance()->AddObserver(
+ new WorkerCreationObserver(path, worker_data.get()));
+ }
+
+ static scoped_refptr<WorkerData> WaitForFirstSharedWorker(const char* path) {
+ scoped_refptr<WorkerData> worker_data(new WorkerData());
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&WaitForFirstSharedWorkerOnIOThread, path, worker_data));
+ content::RunMessageLoop();
+ return worker_data;
+ }
+
+ void OpenDevToolsWindowForSharedWorker(WorkerData* worker_data) {
+ Profile* profile = browser()->profile();
+ scoped_refptr<DevToolsAgentHost> agent_host(
+ DevToolsAgentHost::GetForWorker(
+ worker_data->worker_process_id,
+ worker_data->worker_route_id));
+ window_ = DevToolsWindowTesting::OpenDevToolsWindowForWorkerSync(
+ profile, agent_host.get());
+ }
+
+ void CloseDevToolsWindow() {
+ DevToolsWindowTesting::CloseDevToolsWindowSync(window_);
+ }
+
+ DevToolsWindow* window_;
+};
+
+// Tests that BeforeUnload event gets called on docked devtools if
+// we try to close them.
+IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, TestDockedDevToolsClose) {
+ RunBeforeUnloadSanityTest(true, base::Bind(
+ &DevToolsBeforeUnloadTest::CloseDevToolsWindowAsync,
+ base::Unretained(this)), false);
+}
+
+// Tests that BeforeUnload event gets called on docked devtools if
+// we try to close the inspected page.
+IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
+ TestDockedDevToolsInspectedTabClose) {
+ RunBeforeUnloadSanityTest(true, base::Bind(
+ &DevToolsBeforeUnloadTest::CloseInspectedTab,
+ base::Unretained(this)));
+}
+
+// Tests that BeforeUnload event gets called on docked devtools if
+// we try to close the inspected browser.
+IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
+ TestDockedDevToolsInspectedBrowserClose) {
+ RunBeforeUnloadSanityTest(true, base::Bind(
+ &DevToolsBeforeUnloadTest::CloseInspectedBrowser,
+ base::Unretained(this)));
+}
+
+// Tests that BeforeUnload event gets called on undocked devtools if
+// we try to close them.
+IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, TestUndockedDevToolsClose) {
+ RunBeforeUnloadSanityTest(false, base::Bind(
+ &DevToolsBeforeUnloadTest::CloseDevToolsWindowAsync,
+ base::Unretained(this)), false);
+}
+
+// Tests that BeforeUnload event gets called on undocked devtools if
+// we try to close the inspected page.
+IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
+ TestUndockedDevToolsInspectedTabClose) {
+ RunBeforeUnloadSanityTest(false, base::Bind(
+ &DevToolsBeforeUnloadTest::CloseInspectedTab,
+ base::Unretained(this)));
+}
+
+// Tests that BeforeUnload event gets called on undocked devtools if
+// we try to close the inspected browser.
+IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
+ TestUndockedDevToolsInspectedBrowserClose) {
+ RunBeforeUnloadSanityTest(false, base::Bind(
+ &DevToolsBeforeUnloadTest::CloseInspectedBrowser,
+ base::Unretained(this)));
+}
+
+// Tests that BeforeUnload event gets called on undocked devtools if
+// we try to exit application.
+IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
+ TestUndockedDevToolsApplicationClose) {
+ RunBeforeUnloadSanityTest(false, base::Bind(
+ &chrome::CloseAllBrowsers));
+}
+
+// Tests that inspected tab gets closed if devtools renderer
+// becomes unresponsive during beforeunload event interception.
+// @see http://crbug.com/322380
+// Disabled because of http://crbug.com/410327
+IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
+ DISABLED_TestUndockedDevToolsUnresponsive) {
+ ASSERT_TRUE(spawned_test_server()->Start());
+ LoadTestPage(kDebuggerTestPage);
+ DevToolsWindow* devtools_window = OpenDevToolWindowOnWebContents(
+ GetInspectedTab(), false);
+
+ scoped_refptr<content::MessageLoopRunner> runner =
+ new content::MessageLoopRunner;
+ DevToolsWindowTesting::Get(devtools_window)->SetCloseCallback(
+ runner->QuitClosure());
+
+ ASSERT_TRUE(content::ExecuteScript(
+ DevToolsWindowTesting::Get(devtools_window)->main_web_contents()->
+ GetRenderViewHost(),
+ "window.addEventListener('beforeunload',"
+ "function(event) { while (true); });"));
+ CloseInspectedTab();
+ runner->Run();
+}
+
+// Tests that closing worker inspector window does not cause browser crash
+// @see http://crbug.com/323031
+IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
+ TestWorkerWindowClosing) {
+ ASSERT_TRUE(spawned_test_server()->Start());
+ LoadTestPage(kDebuggerTestPage);
+ DevToolsWindow* devtools_window = OpenDevToolWindowOnWebContents(
+ GetInspectedTab(), false);
+
+ OpenDevToolsPopupWindow(devtools_window);
+ CloseDevToolsPopupWindow(devtools_window);
+}
+
+// Tests that BeforeUnload event gets called on devtools that are opened
+// on another devtools.
+IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
+ TestDevToolsOnDevTools) {
+ ASSERT_TRUE(spawned_test_server()->Start());
+ LoadTestPage(kDebuggerTestPage);
+
+ std::vector<DevToolsWindow*> windows;
+ std::vector<content::WindowedNotificationObserver*> close_observers;
+ content::WebContents* inspected_web_contents = GetInspectedTab();
+ for (int i = 0; i < 3; ++i) {
+ DevToolsWindow* devtools_window = OpenDevToolWindowOnWebContents(
+ inspected_web_contents, i == 0);
+ windows.push_back(devtools_window);
+ content::WindowedNotificationObserver* close_observer =
+ new content::WindowedNotificationObserver(
+ content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
+ content::Source<content::WebContents>(
+ DevToolsWindowTesting::Get(devtools_window)->
+ main_web_contents()));
+ close_observers.push_back(close_observer);
+ inspected_web_contents =
+ DevToolsWindowTesting::Get(devtools_window)->main_web_contents();
+ }
+
+ InjectBeforeUnloadListener(
+ DevToolsWindowTesting::Get(windows[0])->main_web_contents());
+ InjectBeforeUnloadListener(
+ DevToolsWindowTesting::Get(windows[2])->main_web_contents());
+ // Try to close second devtools.
+ {
+ content::WindowedNotificationObserver cancel_browser(
+ chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED,
+ content::NotificationService::AllSources());
+ chrome::CloseWindow(DevToolsWindowTesting::Get(windows[1])->browser());
+ CancelModalDialog();
+ cancel_browser.Wait();
+ }
+ // Try to close browser window.
+ {
+ content::WindowedNotificationObserver cancel_browser(
+ chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED,
+ content::NotificationService::AllSources());
+ chrome::CloseWindow(browser());
+ AcceptModalDialog();
+ CancelModalDialog();
+ cancel_browser.Wait();
+ }
+ // Try to exit application.
+ {
+ content::WindowedNotificationObserver close_observer(
+ chrome::NOTIFICATION_BROWSER_CLOSED,
+ content::Source<Browser>(browser()));
+ chrome::CloseAllBrowsers();
+ AcceptModalDialog();
+ AcceptModalDialog();
+ close_observer.Wait();
+ }
+ for (size_t i = 0; i < close_observers.size(); ++i) {
+ close_observers[i]->Wait();
+ delete close_observers[i];
+ }
+}
+
+// Tests scripts panel showing.
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestShowScriptsTab) {
+ RunTest("testShowScriptsTab", kDebuggerTestPage);
+}
+
+// Tests that scripts tab is populated with inspected scripts even if it
+// hadn't been shown by the moment inspected paged refreshed.
+// @see http://crbug.com/26312
+IN_PROC_BROWSER_TEST_F(
+ DevToolsSanityTest,
+ TestScriptsTabIsPopulatedOnInspectedPageRefresh) {
+ RunTest("testScriptsTabIsPopulatedOnInspectedPageRefresh",
+ kDebuggerTestPage);
+}
+
+// Tests that chrome.devtools extension is correctly exposed.
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
+ TestDevToolsExtensionAPI) {
+ LoadExtension("devtools_extension");
+ RunTest("waitForTestResultsInConsole", std::string());
+}
+
+// Tests that http Iframes within the visible devtools panel for the devtools
+// extension are rendered in their own processes and not in the devtools process
+// or the extension's process. This is tested because this is one of the
+// extension pages with devtools access
+// (https://developer.chrome.com/extensions/devtools). Also tests that frames
+// with data URLs and about:blank URLs are rendered in the devtools process,
+// unless a web OOPIF navigates itself to about:blank, in which case it does not
+// end up back in the devtools process. Also tests that when a web IFrame is
+// navigated back to a devtools extension page, it gets put back in the devtools
+// process.
+// http://crbug.com/570483
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
+ HttpIframeInDevToolsExtensionPanel) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ // Install the dynamically-generated extension.
+ const Extension* extension =
+ LoadExtensionForTest("Devtools Extension", "panel_devtools_page.html",
+ "/multi_frame_page.html");
+ ASSERT_TRUE(extension);
+
+ OpenDevToolsWindow(kDebuggerTestPage, false);
+
+ // Wait for the extension's panel to finish loading -- it'll output 'PASS'
+ // when it's installed. waitForTestResultsInConsole waits until that 'PASS'.
+ RunTestFunction(window_, "waitForTestResultsInConsole");
+
+ // Now that we know the panel is loaded, switch to it.
+ SwitchToExtensionPanel(window_, extension, "iframe_panel");
+ content::WaitForLoadStop(main_web_contents());
+
+ std::vector<RenderFrameHost*> rfhs = main_web_contents()->GetAllFrames();
+ EXPECT_EQ(7U, rfhs.size());
+
+ // This test creates a page with the following frame tree:
+ // - DevTools
+ // - devtools_page from DevTools extension
+ // - Panel (DevTools extension)
+ // - iframe (DevTools extension)
+ // - about:blank
+ // - data:
+ // - web URL
+
+ RenderFrameHost* main_devtools_rfh = main_web_contents()->GetMainFrame();
+ RenderFrameHost* devtools_extension_devtools_page_rfh =
+ ChildFrameAt(main_devtools_rfh, 0);
+ RenderFrameHost* devtools_extension_panel_rfh =
+ ChildFrameAt(main_devtools_rfh, 1);
+ RenderFrameHost* panel_frame_rfh =
+ ChildFrameAt(devtools_extension_panel_rfh, 0);
+ RenderFrameHost* about_blank_frame_rfh = ChildFrameAt(panel_frame_rfh, 0);
+ RenderFrameHost* data_frame_rfh = ChildFrameAt(panel_frame_rfh, 1);
+ RenderFrameHost* web_frame_rfh = ChildFrameAt(panel_frame_rfh, 2);
+
+ GURL web_url = embedded_test_server()->GetURL("a.com", "/title3.html");
+ GURL about_blank_url = GURL(url::kAboutBlankURL);
+ GURL data_url = GURL("data:text/html,foo");
+
+ EXPECT_TRUE(main_devtools_rfh->GetLastCommittedURL().SchemeIs(
+ content::kChromeDevToolsScheme));
+ EXPECT_EQ(extension->GetResourceURL("/panel_devtools_page.html"),
+ devtools_extension_devtools_page_rfh->GetLastCommittedURL());
+ EXPECT_EQ(extension->GetResourceURL("/panel.html"),
+ devtools_extension_panel_rfh->GetLastCommittedURL());
+ EXPECT_EQ(extension->GetResourceURL("/multi_frame_page.html"),
+ panel_frame_rfh->GetLastCommittedURL());
+ EXPECT_EQ(about_blank_url, about_blank_frame_rfh->GetLastCommittedURL());
+ EXPECT_EQ(data_url, data_frame_rfh->GetLastCommittedURL());
+ EXPECT_EQ(web_url, web_frame_rfh->GetLastCommittedURL());
+
+ content::SiteInstance* devtools_instance =
+ main_devtools_rfh->GetSiteInstance();
+ EXPECT_TRUE(
+ devtools_instance->GetSiteURL().SchemeIs(content::kChromeDevToolsScheme));
+ EXPECT_EQ(devtools_instance,
+ devtools_extension_devtools_page_rfh->GetSiteInstance());
+ EXPECT_EQ(devtools_instance, devtools_extension_panel_rfh->GetSiteInstance());
+ EXPECT_EQ(devtools_instance, panel_frame_rfh->GetSiteInstance());
+ EXPECT_EQ(devtools_instance, about_blank_frame_rfh->GetSiteInstance());
+ EXPECT_EQ(devtools_instance, data_frame_rfh->GetSiteInstance());
+ EXPECT_EQ(web_url.host(),
+ web_frame_rfh->GetSiteInstance()->GetSiteURL().host());
+ EXPECT_NE(devtools_instance, web_frame_rfh->GetSiteInstance());
+
+ // Check that if the web iframe navigates itself to about:blank, it stays in
+ // the web SiteInstance.
+ std::string about_blank_javascript = "location.href='about:blank';";
+
+ content::TestNavigationManager web_about_blank_manager(main_web_contents(),
+ about_blank_url);
+
+ ASSERT_TRUE(content::ExecuteScript(web_frame_rfh, about_blank_javascript));
+
+ web_about_blank_manager.WaitForNavigationFinished();
+
+ EXPECT_EQ(about_blank_url, web_frame_rfh->GetLastCommittedURL());
+ EXPECT_EQ(web_url.host(),
+ web_frame_rfh->GetSiteInstance()->GetSiteURL().host());
+ EXPECT_NE(devtools_instance, web_frame_rfh->GetSiteInstance());
+
+ // Check that if the web IFrame is navigated back to a devtools extension
+ // page, it gets put back in the devtools process.
+ GURL extension_simple_url =
+ extension->GetResourceURL("/simple_test_page.html");
+ std::string renavigation_javascript =
+ "location.href='" + extension_simple_url.spec() + "';";
+
+ content::TestNavigationManager renavigation_manager(main_web_contents(),
+ extension_simple_url);
+
+ ASSERT_TRUE(content::ExecuteScript(web_frame_rfh, renavigation_javascript));
+
+ renavigation_manager.WaitForNavigationFinished();
+
+ // The old RFH is no longer valid after the renavigation, so we must get the
+ // new one.
+ RenderFrameHost* extension_simple_frame_rfh =
+ ChildFrameAt(panel_frame_rfh, 2);
+
+ EXPECT_EQ(extension_simple_url,
+ extension_simple_frame_rfh->GetLastCommittedURL());
+ EXPECT_EQ(devtools_instance, extension_simple_frame_rfh->GetSiteInstance());
+}
+
+// Tests that http Iframes within the sidebar pane page for the devtools
+// extension that is visible in the elements panel are rendered in their own
+// processes and not in the devtools process or the extension's process. This
+// is tested because this is one of the extension pages with devtools access
+// (https://developer.chrome.com/extensions/devtools). http://crbug.com/570483
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
+ HttpIframeInDevToolsExtensionSideBarPane) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ GURL web_url = embedded_test_server()->GetURL("a.com", "/title3.html");
+
+ // Install the dynamically-generated extension.
+ const Extension* extension = LoadExtensionForTest(
+ "Devtools Extension", "sidebarpane_devtools_page.html", web_url.spec());
+ ASSERT_TRUE(extension);
+
+ OpenDevToolsWindow(kDebuggerTestPage, false);
+
+ // Wait for the extension's sidebarpane to finish loading -- it'll output
+ // 'PASS' when it's installed. waitForTestResultsInConsole waits until that
+ // 'PASS'.
+ RunTestFunction(window_, "waitForTestResultsInConsole");
+
+ // Now that we know the sidebarpane is loaded, switch to it.
+ content::TestNavigationManager web_manager(main_web_contents(), web_url);
+ SwitchToPanel(window_, "elements");
+ // This is a bit of a hack to switch to the sidebar pane in the elements panel
+ // that the Iframe has been added to.
+ SwitchToPanel(window_, "iframe_pane");
+ web_manager.WaitForNavigationFinished();
+
+ std::vector<RenderFrameHost*> rfhs = main_web_contents()->GetAllFrames();
+ EXPECT_EQ(4U, rfhs.size());
+
+ RenderFrameHost* main_devtools_rfh = main_web_contents()->GetMainFrame();
+ RenderFrameHost* devtools_extension_devtools_page_rfh =
+ ChildFrameAt(main_devtools_rfh, 0);
+ RenderFrameHost* devtools_sidebar_pane_extension_rfh =
+ ChildFrameAt(main_devtools_rfh, 1);
+ RenderFrameHost* http_iframe_rfh =
+ ChildFrameAt(devtools_sidebar_pane_extension_rfh, 0);
+
+ EXPECT_TRUE(main_devtools_rfh->GetLastCommittedURL().SchemeIs(
+ content::kChromeDevToolsScheme));
+ EXPECT_EQ(extension->GetResourceURL("/sidebarpane_devtools_page.html"),
+ devtools_extension_devtools_page_rfh->GetLastCommittedURL());
+ EXPECT_EQ(extension->GetResourceURL("/panel.html"),
+ devtools_sidebar_pane_extension_rfh->GetLastCommittedURL());
+ EXPECT_EQ(web_url, http_iframe_rfh->GetLastCommittedURL());
+
+ content::SiteInstance* devtools_instance =
+ main_devtools_rfh->GetSiteInstance();
+ EXPECT_TRUE(
+ devtools_instance->GetSiteURL().SchemeIs(content::kChromeDevToolsScheme));
+ EXPECT_EQ(devtools_instance,
+ devtools_extension_devtools_page_rfh->GetSiteInstance());
+ EXPECT_EQ(devtools_instance,
+ devtools_sidebar_pane_extension_rfh->GetSiteInstance());
+ EXPECT_EQ(web_url.host(),
+ http_iframe_rfh->GetSiteInstance()->GetSiteURL().host());
+ EXPECT_NE(devtools_instance, http_iframe_rfh->GetSiteInstance());
+}
+
+// Tests that http Iframes within the devtools background page, which is
+// different from the extension's background page, are rendered in their own
+// processes and not in the devtools process or the extension's process. This
+// is tested because this is one of the extension pages with devtools access
+// (https://developer.chrome.com/extensions/devtools). http://crbug.com/570483
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
+ HttpIframeInDevToolsExtensionDevtools) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ // Install the dynamically-generated extension.
+ const Extension* extension =
+ LoadExtensionForTest("Devtools Extension", "web_devtools_page.html",
+ "" /* panel_iframe_src */);
+ ASSERT_TRUE(extension);
+
+ // Wait for a 'DONE' message sent from popup_iframe.html, indicating that it
+ // loaded successfully.
+ content::DOMMessageQueue message_queue;
+ std::string message;
+ OpenDevToolsWindow(kDebuggerTestPage, false);
+
+ while (true) {
+ ASSERT_TRUE(message_queue.WaitForMessage(&message));
+ if (message == "\"DONE\"")
+ break;
+ }
+
+ std::vector<RenderFrameHost*> rfhs = main_web_contents()->GetAllFrames();
+ EXPECT_EQ(3U, rfhs.size());
+
+ RenderFrameHost* main_devtools_rfh = main_web_contents()->GetMainFrame();
+ RenderFrameHost* devtools_extension_devtools_page_rfh =
+ ChildFrameAt(main_devtools_rfh, 0);
+ RenderFrameHost* http_iframe_rfh =
+ ChildFrameAt(devtools_extension_devtools_page_rfh, 0);
+
+ GURL web_url = embedded_test_server()->GetURL("a.com", "/popup_iframe.html");
+
+ EXPECT_TRUE(main_devtools_rfh->GetLastCommittedURL().SchemeIs(
+ content::kChromeDevToolsScheme));
+ EXPECT_EQ(extension->GetResourceURL("/web_devtools_page.html"),
+ devtools_extension_devtools_page_rfh->GetLastCommittedURL());
+ EXPECT_EQ(web_url, http_iframe_rfh->GetLastCommittedURL());
+
+ content::SiteInstance* devtools_instance =
+ main_devtools_rfh->GetSiteInstance();
+ EXPECT_TRUE(
+ devtools_instance->GetSiteURL().SchemeIs(content::kChromeDevToolsScheme));
+ EXPECT_EQ(devtools_instance,
+ devtools_extension_devtools_page_rfh->GetSiteInstance());
+ EXPECT_EQ(web_url.host(),
+ http_iframe_rfh->GetSiteInstance()->GetSiteURL().host());
+ EXPECT_NE(devtools_instance, http_iframe_rfh->GetSiteInstance());
+}
+
+// Tests that iframes to a non-devtools extension embedded in a devtools
+// extension will be isolated from devtools and the devtools extension.
+// http://crbug.com/570483
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
+ NonDevToolsExtensionInDevToolsExtension) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ // Install the dynamically-generated non-devtools extension.
+ const Extension* non_devtools_extension =
+ LoadExtensionForTest("Non-DevTools Extension", "" /* devtools_page */,
+ "" /* panel_iframe_src */);
+ ASSERT_TRUE(non_devtools_extension);
+
+ GURL non_dt_extension_test_url =
+ non_devtools_extension->GetResourceURL("/simple_test_page.html");
+
+ // Install the dynamically-generated devtools extension.
+ const Extension* devtools_extension =
+ LoadExtensionForTest("Devtools Extension", "panel_devtools_page.html",
+ non_dt_extension_test_url.spec());
+ ASSERT_TRUE(devtools_extension);
+
+ OpenDevToolsWindow(kDebuggerTestPage, false);
+
+ // Wait for the extension's panel to finish loading -- it'll output 'PASS'
+ // when it's installed. waitForTestResultsInConsole waits until that 'PASS'.
+ RunTestFunction(window_, "waitForTestResultsInConsole");
+
+ // Now that we know the panel is loaded, switch to it.
+ content::TestNavigationManager non_devtools_manager(
+ main_web_contents(), non_dt_extension_test_url);
+ SwitchToExtensionPanel(window_, devtools_extension, "iframe_panel");
+ non_devtools_manager.WaitForNavigationFinished();
+
+ std::vector<RenderFrameHost*> rfhs = main_web_contents()->GetAllFrames();
+ EXPECT_EQ(4U, rfhs.size());
+
+ RenderFrameHost* main_devtools_rfh = main_web_contents()->GetMainFrame();
+ RenderFrameHost* devtools_extension_devtools_page_rfh =
+ ChildFrameAt(main_devtools_rfh, 0);
+ RenderFrameHost* devtools_extension_panel_rfh =
+ ChildFrameAt(main_devtools_rfh, 1);
+ RenderFrameHost* non_devtools_extension_rfh =
+ ChildFrameAt(devtools_extension_panel_rfh, 0);
+
+ EXPECT_TRUE(main_devtools_rfh->GetLastCommittedURL().SchemeIs(
+ content::kChromeDevToolsScheme));
+ EXPECT_EQ(devtools_extension->GetResourceURL("/panel_devtools_page.html"),
+ devtools_extension_devtools_page_rfh->GetLastCommittedURL());
+ EXPECT_EQ(devtools_extension->GetResourceURL("/panel.html"),
+ devtools_extension_panel_rfh->GetLastCommittedURL());
+ EXPECT_EQ(non_dt_extension_test_url,
+ non_devtools_extension_rfh->GetLastCommittedURL());
+
+ // simple_test_page.html's frame should be in |non_devtools_extension|'s
+ // process, not in devtools or |devtools_extension|'s process.
+ content::SiteInstance* devtools_instance =
+ main_devtools_rfh->GetSiteInstance();
+ EXPECT_TRUE(
+ devtools_instance->GetSiteURL().SchemeIs(content::kChromeDevToolsScheme));
+ EXPECT_EQ(devtools_instance,
+ devtools_extension_devtools_page_rfh->GetSiteInstance());
+ EXPECT_EQ(devtools_instance, devtools_extension_panel_rfh->GetSiteInstance());
+ EXPECT_EQ(non_dt_extension_test_url.GetOrigin(),
+ non_devtools_extension_rfh->GetSiteInstance()->GetSiteURL());
+ EXPECT_NE(devtools_instance, non_devtools_extension_rfh->GetSiteInstance());
+}
+
+// Tests that if a devtools extension's devtools panel page has a subframe to a
+// page for another devtools extension, the subframe is rendered in the devtools
+// process as well. http://crbug.com/570483
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
+ DevToolsExtensionInDevToolsExtension) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ // Install the dynamically-generated extension.
+ const Extension* devtools_b_extension =
+ LoadExtensionForTest("Devtools Extension B", "simple_devtools_page.html",
+ "" /* panel_iframe_src */);
+ ASSERT_TRUE(devtools_b_extension);
+
+ GURL extension_b_page_url =
+ devtools_b_extension->GetResourceURL("/simple_test_page.html");
+
+ // Install another dynamically-generated extension. This extension's
+ // panel.html's iframe will point to an extension b URL.
+ const Extension* devtools_a_extension =
+ LoadExtensionForTest("Devtools Extension A", "panel_devtools_page.html",
+ extension_b_page_url.spec());
+ ASSERT_TRUE(devtools_a_extension);
+
+ OpenDevToolsWindow(kDebuggerTestPage, false);
+
+ // Wait for the extension's panel to finish loading -- it'll output 'PASS'
+ // when it's installed. waitForTestResultsInConsole waits until that 'PASS'.
+ RunTestFunction(window_, "waitForTestResultsInConsole");
+
+ // Now that we know the panel is loaded, switch to it.
+ content::TestNavigationManager extension_b_manager(main_web_contents(),
+ extension_b_page_url);
+ SwitchToExtensionPanel(window_, devtools_a_extension, "iframe_panel");
+ extension_b_manager.WaitForNavigationFinished();
+
+ std::vector<RenderFrameHost*> rfhs = main_web_contents()->GetAllFrames();
+ EXPECT_EQ(5U, rfhs.size());
+
+ RenderFrameHost* main_devtools_rfh = main_web_contents()->GetMainFrame();
+
+ RenderFrameHost* devtools_extension_a_devtools_rfh =
+ content::FrameMatchingPredicate(
+ main_web_contents(), base::Bind(&content::FrameHasSourceUrl,
+ devtools_a_extension->GetResourceURL(
+ "/panel_devtools_page.html")));
+ EXPECT_TRUE(devtools_extension_a_devtools_rfh);
+ RenderFrameHost* devtools_extension_b_devtools_rfh =
+ content::FrameMatchingPredicate(
+ main_web_contents(), base::Bind(&content::FrameHasSourceUrl,
+ devtools_b_extension->GetResourceURL(
+ "/simple_devtools_page.html")));
+ EXPECT_TRUE(devtools_extension_b_devtools_rfh);
+
+ RenderFrameHost* devtools_extension_a_panel_rfh =
+ ChildFrameAt(main_devtools_rfh, 2);
+ RenderFrameHost* devtools_extension_b_frame_rfh =
+ ChildFrameAt(devtools_extension_a_panel_rfh, 0);
+
+ EXPECT_TRUE(main_devtools_rfh->GetLastCommittedURL().SchemeIs(
+ content::kChromeDevToolsScheme));
+ EXPECT_EQ(devtools_a_extension->GetResourceURL("/panel_devtools_page.html"),
+ devtools_extension_a_devtools_rfh->GetLastCommittedURL());
+ EXPECT_EQ(devtools_b_extension->GetResourceURL("/simple_devtools_page.html"),
+ devtools_extension_b_devtools_rfh->GetLastCommittedURL());
+ EXPECT_EQ(devtools_a_extension->GetResourceURL("/panel.html"),
+ devtools_extension_a_panel_rfh->GetLastCommittedURL());
+ EXPECT_EQ(extension_b_page_url,
+ devtools_extension_b_frame_rfh->GetLastCommittedURL());
+
+ // All frames should be in the devtools process.
+ content::SiteInstance* devtools_instance =
+ main_devtools_rfh->GetSiteInstance();
+ EXPECT_TRUE(
+ devtools_instance->GetSiteURL().SchemeIs(content::kChromeDevToolsScheme));
+ EXPECT_EQ(devtools_instance,
+ devtools_extension_a_devtools_rfh->GetSiteInstance());
+ EXPECT_EQ(devtools_instance,
+ devtools_extension_b_devtools_rfh->GetSiteInstance());
+ EXPECT_EQ(devtools_instance,
+ devtools_extension_a_panel_rfh->GetSiteInstance());
+ EXPECT_EQ(devtools_instance,
+ devtools_extension_b_frame_rfh->GetSiteInstance());
+}
+
+// Tests that a devtools extension can still have subframes to itself in a
+// "devtools page" and that they will be rendered within the devtools process as
+// well, not in the extension process. http://crbug.com/570483
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, DevToolsExtensionInItself) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ // Install the dynamically-generated extension.
+ const Extension* extension =
+ LoadExtensionForTest("Devtools Extension", "panel_devtools_page.html",
+ "/simple_test_page.html");
+ ASSERT_TRUE(extension);
+
+ OpenDevToolsWindow(kDebuggerTestPage, false);
+
+ // Wait for the extension's panel to finish loading -- it'll output 'PASS'
+ // when it's installed. waitForTestResultsInConsole waits until that 'PASS'.
+ RunTestFunction(window_, "waitForTestResultsInConsole");
+
+ // Now that we know the panel is loaded, switch to it.
+ GURL extension_test_url = extension->GetResourceURL("/simple_test_page.html");
+ content::TestNavigationManager test_page_manager(main_web_contents(),
+ extension_test_url);
+ SwitchToExtensionPanel(window_, extension, "iframe_panel");
+ test_page_manager.WaitForNavigationFinished();
+
+ std::vector<RenderFrameHost*> rfhs = main_web_contents()->GetAllFrames();
+ EXPECT_EQ(4U, rfhs.size());
+
+ RenderFrameHost* main_devtools_rfh = main_web_contents()->GetMainFrame();
+ RenderFrameHost* devtools_extension_devtools_page_rfh =
+ ChildFrameAt(main_devtools_rfh, 0);
+ RenderFrameHost* devtools_extension_panel_rfh =
+ ChildFrameAt(main_devtools_rfh, 1);
+ RenderFrameHost* devtools_extension_panel_frame_rfh =
+ ChildFrameAt(devtools_extension_panel_rfh, 0);
+
+ // All frames should be in the devtools process, including
+ // simple_test_page.html
+ EXPECT_TRUE(main_devtools_rfh->GetLastCommittedURL().SchemeIs(
+ content::kChromeDevToolsScheme));
+ EXPECT_EQ(extension->GetResourceURL("/panel_devtools_page.html"),
+ devtools_extension_devtools_page_rfh->GetLastCommittedURL());
+ EXPECT_EQ(extension->GetResourceURL("/panel.html"),
+ devtools_extension_panel_rfh->GetLastCommittedURL());
+ EXPECT_EQ(extension_test_url,
+ devtools_extension_panel_frame_rfh->GetLastCommittedURL());
+
+ content::SiteInstance* devtools_instance =
+ main_devtools_rfh->GetSiteInstance();
+ EXPECT_TRUE(
+ devtools_instance->GetSiteURL().SchemeIs(content::kChromeDevToolsScheme));
+ EXPECT_EQ(devtools_instance,
+ devtools_extension_devtools_page_rfh->GetSiteInstance());
+ EXPECT_EQ(devtools_instance, devtools_extension_panel_rfh->GetSiteInstance());
+ EXPECT_EQ(devtools_instance,
+ devtools_extension_panel_frame_rfh->GetSiteInstance());
+}
+
+// Tests that a devtools (not a devtools extension) Iframe can be injected into
+// devtools. http://crbug.com/570483
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, DevtoolsInDevTools) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ GURL devtools_url = GURL(chrome::kChromeUIDevToolsURL);
+
+ OpenDevToolsWindow(kDebuggerTestPage, false);
+
+ std::string javascript =
+ "var devtoolsFrame = document.createElement('iframe');"
+ "document.body.appendChild(devtoolsFrame);"
+ "devtoolsFrame.src = '" +
+ devtools_url.spec() + "';";
+
+ RenderFrameHost* main_devtools_rfh = main_web_contents()->GetMainFrame();
+
+ content::TestNavigationManager manager(main_web_contents(), devtools_url);
+ ASSERT_TRUE(content::ExecuteScript(main_devtools_rfh, javascript));
+ manager.WaitForNavigationFinished();
+
+ std::vector<RenderFrameHost*> rfhs = main_web_contents()->GetAllFrames();
+ EXPECT_EQ(2U, rfhs.size());
+ RenderFrameHost* devtools_iframe_rfh = ChildFrameAt(main_devtools_rfh, 0);
+ EXPECT_TRUE(main_devtools_rfh->GetLastCommittedURL().SchemeIs(
+ content::kChromeDevToolsScheme));
+ EXPECT_EQ(devtools_url, devtools_iframe_rfh->GetLastCommittedURL());
+ content::SiteInstance* devtools_instance =
+ main_devtools_rfh->GetSiteInstance();
+ EXPECT_TRUE(
+ devtools_instance->GetSiteURL().SchemeIs(content::kChromeDevToolsScheme));
+ EXPECT_EQ(devtools_instance, devtools_iframe_rfh->GetSiteInstance());
+
+ std::string message;
+ EXPECT_TRUE(ExecuteScriptAndExtractString(
+ devtools_iframe_rfh, "domAutomationController.send(document.origin)",
+ &message));
+ EXPECT_EQ(devtools_url.GetOrigin().spec(), message + "/");
+}
+
+// Some web features, when used from an extension, are subject to browser-side
+// security policy enforcement. Make sure they work properly from inside a
+// devtools extension.
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
+ DevToolsExtensionSecurityPolicyGrants) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ std::unique_ptr<extensions::TestExtensionDir> dir(
+ new extensions::TestExtensionDir());
+
+ extensions::DictionaryBuilder manifest;
+ dir->WriteManifest(extensions::DictionaryBuilder()
+ .Set("name", "Devtools Panel")
+ .Set("version", "1")
+ // Whitelist the script we stuff into the 'blob:' URL:
+ .Set("content_security_policy",
+ "script-src 'self' "
+ "'sha256-95xJWHeV+"
+ "1zjAKQufDVW0misgmR4gCjgpipP2LJ5iis='; "
+ "object-src 'none'")
+ .Set("manifest_version", 2)
+ .Set("devtools_page", "devtools.html")
+ .ToJSON());
+
+ dir->WriteFile(
+ FILE_PATH_LITERAL("devtools.html"),
+ "<html><head><script src='devtools.js'></script></head></html>");
+
+ dir->WriteFile(
+ FILE_PATH_LITERAL("devtools.js"),
+ "chrome.devtools.panels.create('the_panel_name',\n"
+ " null,\n"
+ " 'panel.html',\n"
+ " function(panel) {\n"
+ " chrome.devtools.inspectedWindow.eval('console.log(\"PASS\")');\n"
+ " }\n"
+ ");\n");
+
+ dir->WriteFile(FILE_PATH_LITERAL("panel.html"),
+ "<html><body>A panel."
+ "<script src='blob_xhr.js'></script>"
+ "<script src='blob_iframe.js'></script>"
+ "</body></html>");
+ // Creating blobs from chrome-extension:// origins is only permitted if the
+ // process has been granted permission to commit 'chrome-extension' schemes.
+ dir->WriteFile(
+ FILE_PATH_LITERAL("blob_xhr.js"),
+ "var blob_url = URL.createObjectURL(new Blob(['xhr blob contents']));\n"
+ "var xhr = new XMLHttpRequest();\n"
+ "xhr.open('GET', blob_url, true);\n"
+ "xhr.onload = function (e) {\n"
+ " domAutomationController.setAutomationId(0);\n"
+ " domAutomationController.send(xhr.response);\n"
+ "};\n"
+ "xhr.send(null);\n");
+ dir->WriteFile(
+ FILE_PATH_LITERAL("blob_iframe.js"),
+ "var payload = `"
+ "<html><body>iframe blob contents"
+ "<script>"
+ " domAutomationController.setAutomationId(0);"
+ " domAutomationController.send(document.body.innerText);\n"
+ "</script></body></html>"
+ "`;"
+ "document.body.appendChild(document.createElement('iframe')).src ="
+ " URL.createObjectURL(new Blob([payload], {type: 'text/html'}));");
+ // Install the extension.
+ const Extension* extension = LoadExtensionFromPath(dir->UnpackedPath());
+ ASSERT_TRUE(extension);
+
+ // Open a devtools window.
+ OpenDevToolsWindow(kDebuggerTestPage, false);
+
+ // Wait for the panel extension to finish loading -- it'll output 'PASS'
+ // when it's installed. waitForTestResultsInConsole waits until that 'PASS'.
+ RunTestFunction(window_, "waitForTestResultsInConsole");
+
+ // Now that we know the panel is loaded, switch to it. We'll wait until we
+ // see a 'DONE' message sent from popup_iframe.html, indicating that it
+ // loaded successfully.
+ content::DOMMessageQueue message_queue;
+ SwitchToExtensionPanel(window_, extension, "the_panel_name");
+ std::string message;
+ while (true) {
+ ASSERT_TRUE(message_queue.WaitForMessage(&message));
+ if (message == "\"xhr blob contents\"")
+ break;
+ }
+ while (true) {
+ ASSERT_TRUE(message_queue.WaitForMessage(&message));
+ if (message == "\"iframe blob contents\"")
+ break;
+ }
+}
+
+// Disabled on Windows due to flakiness. http://crbug.com/183649
+#if defined(OS_WIN)
+#define MAYBE_TestDevToolsExtensionMessaging DISABLED_TestDevToolsExtensionMessaging
+#else
+#define MAYBE_TestDevToolsExtensionMessaging TestDevToolsExtensionMessaging
+#endif
+
+// Tests that chrome.devtools extension can communicate with background page
+// using extension messaging.
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
+ MAYBE_TestDevToolsExtensionMessaging) {
+ LoadExtension("devtools_messaging");
+ RunTest("waitForTestResultsInConsole", std::string());
+}
+
+// Tests that chrome.experimental.devtools extension is correctly exposed
+// when the extension has experimental permission.
+IN_PROC_BROWSER_TEST_F(DevToolsExperimentalExtensionTest,
+ TestDevToolsExperimentalExtensionAPI) {
+ LoadExtension("devtools_experimental");
+ RunTest("waitForTestResultsInConsole", std::string());
+}
+
+// Tests that a content script is in the scripts list.
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
+ TestContentScriptIsPresent) {
+ LoadExtension("simple_content_script");
+ RunTest("testContentScriptIsPresent", kPageWithContentScript);
+}
+
+// Tests that console selector shows correct context names.
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
+ TestConsoleContextNames) {
+ LoadExtension("simple_content_script");
+ RunTest("testConsoleContextNames", kPageWithContentScript);
+}
+
+// Tests that scripts are not duplicated after Scripts Panel switch.
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,
+ TestNoScriptDuplicatesOnPanelSwitch) {
+ RunTest("testNoScriptDuplicatesOnPanelSwitch", kDebuggerTestPage);
+}
+
+// Tests that debugger works correctly if pause event occurs when DevTools
+// frontend is being loaded.
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,
+ TestPauseWhenLoadingDevTools) {
+ RunTest("testPauseWhenLoadingDevTools", kPauseWhenLoadingDevTools);
+}
+
+// Tests that pressing 'Pause' will pause script execution if the script
+// is already running.
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
+// Timing out on linux ARM bot: https://crbug/238453
+#define MAYBE_TestPauseWhenScriptIsRunning DISABLED_TestPauseWhenScriptIsRunning
+#else
+#define MAYBE_TestPauseWhenScriptIsRunning TestPauseWhenScriptIsRunning
+#endif
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,
+ MAYBE_TestPauseWhenScriptIsRunning) {
+ RunTest("testPauseWhenScriptIsRunning", kPauseWhenScriptIsRunning);
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestTempFileIncognito) {
+ GURL url("about:blank");
+ ui_test_utils::BrowserAddedObserver window_observer;
+ chrome::NewEmptyWindow(browser()->profile()->GetOffTheRecordProfile());
+ Browser* new_browser = window_observer.WaitForSingleNewBrowser();
+ ui_test_utils::NavigateToURL(new_browser, url);
+ DevToolsWindow* window = DevToolsWindowTesting::OpenDevToolsWindowSync(
+ new_browser->tab_strip_model()->GetWebContentsAt(0), false);
+ RunTestFunction(window, "testTempFile");
+ DevToolsWindowTesting::CloseDevToolsWindowSync(window);
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestTempFile) {
+ RunTest("testTempFile", kDebuggerTestPage);
+}
+
+// Tests network timing.
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestNetworkTiming) {
+ RunTest("testNetworkTiming", kSlowTestPage);
+}
+
+// Tests network size.
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestNetworkSize) {
+ RunTest("testNetworkSize", kChunkedTestPage);
+}
+
+// Tests raw headers text.
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestNetworkSyncSize) {
+ RunTest("testNetworkSyncSize", kChunkedTestPage);
+}
+
+// Tests raw headers text.
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestNetworkRawHeadersText) {
+ RunTest("testNetworkRawHeadersText", kChunkedTestPage);
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestNetworkPushTime) {
+ OpenDevToolsWindow(kPushTestPage, false);
+ GURL push_url = spawned_test_server()->GetURL(kPushTestResource);
+ base::FilePath file_path =
+ spawned_test_server()->document_root().AppendASCII(kPushTestResource);
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&TestInterceptor::Register, push_url, file_path));
+
+ DispatchOnTestSuite(window_, "testPushTimes", push_url.spec().c_str());
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&TestInterceptor::Unregister, push_url));
+
+ CloseDevToolsWindow();
+}
+
+// Tests that console messages are not duplicated on navigation back.
+#if defined(OS_WIN)
+// Flaking on windows swarm try runs: crbug.com/409285.
+#define MAYBE_TestConsoleOnNavigateBack DISABLED_TestConsoleOnNavigateBack
+#else
+#define MAYBE_TestConsoleOnNavigateBack TestConsoleOnNavigateBack
+#endif
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, MAYBE_TestConsoleOnNavigateBack) {
+ RunTest("testConsoleOnNavigateBack", kNavigateBackTestPage);
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestDeviceEmulation) {
+ RunTest("testDeviceMetricsOverrides", "about:blank");
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestDispatchKeyEventDoesNotCrash) {
+ RunTest("testDispatchKeyEventDoesNotCrash", "about:blank");
+}
+
+// Tests that settings are stored in profile correctly.
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestSettings) {
+ OpenDevToolsWindow("about:blank", true);
+ RunTestFunction(window_, "testSettings");
+ CloseDevToolsWindow();
+}
+
+// Tests that external navigation from inspector page is always handled by
+// DevToolsWindow and results in inspected page navigation.
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestDevToolsExternalNavigation) {
+ OpenDevToolsWindow(kDebuggerTestPage, true);
+ GURL url = spawned_test_server()->GetURL(kNavigateBackTestPage);
+ ui_test_utils::UrlLoadObserver observer(url,
+ content::NotificationService::AllSources());
+ ASSERT_TRUE(content::ExecuteScript(
+ main_web_contents(),
+ std::string("window.location = \"") + url.spec() + "\""));
+ observer.Wait();
+
+ ASSERT_TRUE(main_web_contents()->GetURL().
+ SchemeIs(content::kChromeDevToolsScheme));
+ ASSERT_EQ(url, GetInspectedTab()->GetURL());
+ CloseDevToolsWindow();
+}
+
+// Tests that toolbox window is loaded when DevTools window is undocked.
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestToolboxLoadedUndocked) {
+ OpenDevToolsWindow(kDebuggerTestPage, false);
+ ASSERT_TRUE(toolbox_web_contents());
+ DevToolsWindow* on_self =
+ DevToolsWindowTesting::OpenDevToolsWindowSync(main_web_contents(), false);
+ ASSERT_FALSE(DevToolsWindowTesting::Get(on_self)->toolbox_web_contents());
+ DevToolsWindowTesting::CloseDevToolsWindowSync(on_self);
+ CloseDevToolsWindow();
+}
+
+// Tests that toolbox window is not loaded when DevTools window is docked.
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestToolboxNotLoadedDocked) {
+ OpenDevToolsWindow(kDebuggerTestPage, true);
+ ASSERT_FALSE(toolbox_web_contents());
+ DevToolsWindow* on_self =
+ DevToolsWindowTesting::OpenDevToolsWindowSync(main_web_contents(), false);
+ ASSERT_FALSE(DevToolsWindowTesting::Get(on_self)->toolbox_web_contents());
+ DevToolsWindowTesting::CloseDevToolsWindowSync(on_self);
+ CloseDevToolsWindow();
+}
+
+// Tests that inspector will reattach to inspected page when it is reloaded
+// after a crash. See http://crbug.com/101952
+// Disabled. it doesn't check anything right now: http://crbug.com/461790
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, DISABLED_TestReattachAfterCrash) {
+ RunTest("testReattachAfterCrash", std::string());
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestPageWithNoJavaScript) {
+ OpenDevToolsWindow("about:blank", false);
+ std::string result;
+ ASSERT_TRUE(
+ content::ExecuteScriptAndExtractString(
+ main_web_contents()->GetRenderViewHost(),
+ "window.domAutomationController.send("
+ " '' + (window.uiTests && (typeof uiTests.dispatchOnTestSuite)));",
+ &result));
+ ASSERT_EQ("function", result) << "DevTools front-end is broken.";
+ CloseDevToolsWindow();
+}
+
+class DevToolsAutoOpenerTest : public DevToolsSanityTest {
+ public:
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ command_line->AppendSwitch(switches::kAutoOpenDevToolsForTabs);
+ observer_.reset(new DevToolsWindowCreationObserver());
+ }
+ protected:
+ std::unique_ptr<DevToolsWindowCreationObserver> observer_;
+};
+
+IN_PROC_BROWSER_TEST_F(DevToolsAutoOpenerTest, TestAutoOpenForTabs) {
+ {
+ DevToolsWindowCreationObserver observer;
+ AddTabAtIndexToBrowser(browser(), 0, GURL("about:blank"),
+ ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false);
+ observer.WaitForLoad();
+ }
+ Browser* new_browser = nullptr;
+ {
+ DevToolsWindowCreationObserver observer;
+ new_browser = CreateBrowser(browser()->profile());
+ observer.WaitForLoad();
+ }
+ {
+ DevToolsWindowCreationObserver observer;
+ AddTabAtIndexToBrowser(new_browser, 0, GURL("about:blank"),
+ ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false);
+ observer.WaitForLoad();
+ }
+ observer_->CloseAllSync();
+}
+
+class DevToolsReattachAfterCrashTest : public DevToolsSanityTest {
+ protected:
+ void RunTestWithPanel(const char* panel_name) {
+ OpenDevToolsWindow("about:blank", false);
+ SwitchToPanel(window_, panel_name);
+ ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+
+ content::RenderProcessHostWatcher crash_observer(
+ GetInspectedTab(),
+ content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+ ui_test_utils::NavigateToURL(browser(), GURL(content::kChromeUICrashURL));
+ crash_observer.Wait();
+ content::TestNavigationObserver navigation_observer(GetInspectedTab(), 1);
+ chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
+ navigation_observer.Wait();
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(DevToolsReattachAfterCrashTest,
+ TestReattachAfterCrashOnTimeline) {
+ RunTestWithPanel("timeline");
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsReattachAfterCrashTest,
+ TestReattachAfterCrashOnNetwork) {
+ RunTestWithPanel("network");
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, AutoAttachToWindowOpen) {
+ OpenDevToolsWindow(kWindowOpenTestPage, false);
+ DispatchOnTestSuite(window_, "enableAutoAttachToCreatedPages");
+ DevToolsWindowCreationObserver observer;
+ ASSERT_TRUE(content::ExecuteScript(
+ GetInspectedTab(), "window.open('window_open.html', '_blank');"));
+ observer.WaitForLoad();
+ DispatchOnTestSuite(observer.devtools_window(), "waitForDebuggerPaused");
+ DevToolsWindowTesting::CloseDevToolsWindowSync(observer.devtools_window());
+ CloseDevToolsWindow();
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, SecondTabAfterDevTools) {
+ OpenDevToolsWindow(kDebuggerTestPage, true);
+
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), spawned_test_server()->GetURL(kDebuggerTestPage),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+ WebContents* second = browser()->tab_strip_model()->GetActiveWebContents();
+
+ scoped_refptr<content::DevToolsAgentHost> agent(
+ content::DevToolsAgentHost::GetOrCreateFor(second));
+ EXPECT_EQ("page", agent->GetType());
+
+ CloseDevToolsWindow();
+}
+
+IN_PROC_BROWSER_TEST_F(WorkerDevToolsSanityTest, InspectSharedWorker) {
+ RunTest("testSharedWorker", kSharedWorkerTestPage, kSharedWorkerTestWorker);
+}
+
+// Flaky on multiple platforms. See http://crbug.com/432444
+IN_PROC_BROWSER_TEST_F(WorkerDevToolsSanityTest,
+ DISABLED_PauseInSharedWorkerInitialization) {
+ ASSERT_TRUE(spawned_test_server()->Start());
+ GURL url = spawned_test_server()->GetURL(kReloadSharedWorkerTestPage);
+ ui_test_utils::NavigateToURL(browser(), url);
+
+ scoped_refptr<WorkerData> worker_data =
+ WaitForFirstSharedWorker(kReloadSharedWorkerTestWorker);
+ OpenDevToolsWindowForSharedWorker(worker_data.get());
+
+ // We should make sure that the worker inspector has loaded before
+ // terminating worker.
+ RunTestFunction(window_, "testPauseInSharedWorkerInitialization1");
+
+ TerminateWorker(worker_data);
+
+ // Reload page to restart the worker.
+ ui_test_utils::NavigateToURL(browser(), url);
+
+ // Wait until worker script is paused on the debugger statement.
+ RunTestFunction(window_, "testPauseInSharedWorkerInitialization2");
+ CloseDevToolsWindow();
+}
+
+class DevToolsAgentHostTest : public InProcessBrowserTest {};
+
+// Tests DevToolsAgentHost retention by its target.
+IN_PROC_BROWSER_TEST_F(DevToolsAgentHostTest, TestAgentHostReleased) {
+ ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
+ WebContents* web_contents = browser()->tab_strip_model()->GetWebContentsAt(0);
+ DevToolsAgentHost* agent_raw =
+ DevToolsAgentHost::GetOrCreateFor(web_contents).get();
+ const std::string agent_id = agent_raw->GetId();
+ ASSERT_EQ(agent_raw, DevToolsAgentHost::GetForId(agent_id).get())
+ << "DevToolsAgentHost cannot be found by id";
+ browser()->tab_strip_model()->
+ CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
+ ASSERT_FALSE(DevToolsAgentHost::GetForId(agent_id).get())
+ << "DevToolsAgentHost is not released when the tab is closed";
+}
+
+class RemoteDebuggingTest : public ExtensionApiTest {
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ ExtensionApiTest::SetUpCommandLine(command_line);
+ command_line->AppendSwitchASCII(switches::kRemoteDebuggingPort, "9222");
+
+ // Override the extension root path.
+ PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
+ test_data_dir_ = test_data_dir_.AppendASCII("devtools");
+ }
+};
+
+// Fails on CrOS. crbug.com/431399
+#if defined(OS_CHROMEOS)
+#define MAYBE_RemoteDebugger DISABLED_RemoteDebugger
+#else
+#define MAYBE_RemoteDebugger RemoteDebugger
+#endif
+IN_PROC_BROWSER_TEST_F(RemoteDebuggingTest, MAYBE_RemoteDebugger) {
+ ASSERT_TRUE(RunExtensionTest("target_list")) << message_;
+}
+
+using DevToolsPolicyTest = InProcessBrowserTest;
+IN_PROC_BROWSER_TEST_F(DevToolsPolicyTest, PolicyTrue) {
+ browser()->profile()->GetPrefs()->SetBoolean(prefs::kDevToolsDisabled, true);
+ ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetWebContentsAt(0);
+ scoped_refptr<content::DevToolsAgentHost> agent(
+ content::DevToolsAgentHost::GetOrCreateFor(web_contents));
+ DevToolsWindow::OpenDevToolsWindow(web_contents);
+ DevToolsWindow* window = DevToolsWindow::FindDevToolsWindow(agent.get());
+ ASSERT_FALSE(window);
+}
+
+class DevToolsPixelOutputTests : public DevToolsSanityTest {
+ public:
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ command_line->AppendSwitch(switches::kEnablePixelOutputInTests);
+ command_line->AppendSwitch(switches::kUseGpuInTests);
+ }
+};
+
+// This test enables switches::kUseGpuInTests which causes false positives
+// with MemorySanitizer. This is also flakey on many configurations.
+#if defined(MEMORY_SANITIZER) || defined(ADDRESS_SANITIZER) || \
+ defined(OS_WIN)|| (defined(OS_CHROMEOS) && defined(OFFICIAL_BUILD))
+#define MAYBE_TestScreenshotRecording DISABLED_TestScreenshotRecording
+#else
+#define MAYBE_TestScreenshotRecording TestScreenshotRecording
+#endif
+IN_PROC_BROWSER_TEST_F(DevToolsPixelOutputTests,
+ MAYBE_TestScreenshotRecording) {
+ RunTest("testScreenshotRecording", std::string());
+}
+
+// This test enables switches::kUseGpuInTests which causes false positives
+// with MemorySanitizer.
+// Flaky on multiple platforms https://crbug.com/624215
+IN_PROC_BROWSER_TEST_F(DevToolsPixelOutputTests,
+ DISABLED_TestLatencyInfoInstrumentation) {
+ WebContents* web_contents = GetInspectedTab();
+ OpenDevToolsWindow(kLatencyInfoTestPage, false);
+ DispatchAndWait("startTimeline");
+
+ for (int i = 0; i < 3; ++i) {
+ SimulateMouseEvent(web_contents, blink::WebInputEvent::kMouseMove,
+ gfx::Point(30, 60));
+ DispatchInPageAndWait("waitForEvent", "mousemove");
+ }
+
+ SimulateMouseClickAt(web_contents, 0,
+ blink::WebPointerProperties::Button::kLeft,
+ gfx::Point(30, 60));
+ DispatchInPageAndWait("waitForEvent", "click");
+
+ SimulateMouseWheelEvent(web_contents, gfx::Point(300, 100),
+ gfx::Vector2d(0, 120));
+ DispatchInPageAndWait("waitForEvent", "wheel");
+
+ SimulateTapAt(web_contents, gfx::Point(30, 60));
+ DispatchInPageAndWait("waitForEvent", "gesturetap");
+
+ DispatchAndWait("stopTimeline");
+ RunTestMethod("checkInputEventsPresent", "MouseMove", "MouseDown",
+ "MouseWheel", "GestureTap");
+
+ CloseDevToolsWindow();
+}
+
+class DevToolsNetInfoTest : public DevToolsSanityTest {
+ protected:
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ command_line->AppendSwitch(switches::kEnableNetworkInformation);
+ command_line->AppendSwitch(
+ switches::kEnableExperimentalWebPlatformFeatures);
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(DevToolsNetInfoTest, EmulateNetworkConditions) {
+ RunTest("testEmulateNetworkConditions", kEmulateNetworkConditionsPage);
+}
+
+class StaticURLDataSource : public content::URLDataSource {
+ public:
+ StaticURLDataSource(const std::string& source, const std::string& content)
+ : source_(source), content_(content) {}
+
+ std::string GetSource() const override { return source_; }
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const GotDataCallback& callback) override {
+ std::string data(content_);
+ callback.Run(base::RefCountedString::TakeString(&data));
+ }
+ std::string GetMimeType(const std::string& path) const override {
+ return "text/html";
+ }
+ bool ShouldAddContentSecurityPolicy() const override { return false; }
+
+ private:
+ std::string source_;
+ std::string content_;
+ DISALLOW_COPY_AND_ASSIGN(StaticURLDataSource);
+};
+
+class MockWebUIProvider
+ : public TestChromeWebUIControllerFactory::WebUIProvider {
+ public:
+ MockWebUIProvider(const std::string& source, const std::string& content)
+ : source_(source), content_(content) {}
+
+ content::WebUIController* NewWebUI(content::WebUI* web_ui,
+ const GURL& url) override {
+ content::URLDataSource::Add(Profile::FromWebUI(web_ui),
+ new StaticURLDataSource(source_, content_));
+ return new content::WebUIController(web_ui);
+ }
+
+ private:
+ std::string source_;
+ std::string content_;
+ DISALLOW_COPY_AND_ASSIGN(MockWebUIProvider);
+};
+
+// This tests checks that window is correctly initialized when DevTools is
+// opened while navigation through history with forward and back actions.
+// (crbug.com/627407)
+// Flaky on Windows and ChromeOS. http://crbug.com/628174#c4
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,
+ DISABLED_TestWindowInitializedOnNavigateBack) {
+ TestChromeWebUIControllerFactory test_factory;
+ MockWebUIProvider mock_provider("dummyurl",
+ "<script>\n"
+ " window.abc = 239;\n"
+ " console.log(abc);\n"
+ "</script>");
+ test_factory.AddFactoryOverride(GURL("chrome://dummyurl").host(),
+ &mock_provider);
+ content::WebUIControllerFactory::RegisterFactory(&test_factory);
+
+ ui_test_utils::NavigateToURL(browser(), GURL("chrome://dummyurl"));
+ DevToolsWindow* window =
+ DevToolsWindowTesting::OpenDevToolsWindowSync(GetInspectedTab(), true);
+ chrome::DuplicateTab(browser());
+ chrome::SelectPreviousTab(browser());
+ ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
+ chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB);
+ RunTestFunction(window, "testWindowInitializedOnNavigateBack");
+
+ DevToolsWindowTesting::CloseDevToolsWindowSync(window);
+ content::WebUIControllerFactory::UnregisterFactoryForTesting(&test_factory);
+}
+
+// Tests scripts panel showing.
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestDevToolsSharedWorker) {
+ RunTest("testDevToolsSharedWorker", url::kAboutBlankURL);
+}
diff --git a/chromium/chrome/browser/devtools/devtools_sanity_interactive_browsertest.cc b/chromium/chrome/browser/devtools/devtools_sanity_interactive_browsertest.cc
new file mode 100644
index 00000000000..f8745e28fd9
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_sanity_interactive_browsertest.cc
@@ -0,0 +1,211 @@
+// 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 "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "build/build_config.h"
+#include "chrome/browser/devtools/chrome_devtools_manager_delegate.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
+#include "chrome/test/base/in_process_browser_test.h"
+
+#if defined(OS_MACOSX)
+#include "ui/base/test/scoped_fake_nswindow_fullscreen.h"
+#endif
+
+// Encapsulates waiting for the browser window to change state. This is
+// needed for example on Chrome desktop linux, where window state change is done
+// asynchronously as an event received from a different process.
+class CheckWaiter {
+ public:
+ explicit CheckWaiter(base::Callback<bool()> callback, bool expected)
+ : callback_(callback),
+ expected_(expected),
+ timeout_(base::Time::NowFromSystemTime() +
+ base::TimeDelta::FromSeconds(1)) {}
+ ~CheckWaiter() = default;
+
+ // Blocks until the browser window becomes maximized.
+ void Wait() {
+ if (Check())
+ return;
+
+ base::RunLoop run_loop;
+ quit_ = run_loop.QuitClosure();
+ run_loop.Run();
+ }
+
+ private:
+ bool Check() {
+ if (callback_.Run() != expected_ &&
+ base::Time::NowFromSystemTime() < timeout_) {
+ base::MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(base::IgnoreResult(&CheckWaiter::Check),
+ base::Unretained(this)));
+ return false;
+ }
+
+ // Quit the run_loop to end the wait.
+ if (!quit_.is_null())
+ base::ResetAndReturn(&quit_).Run();
+ return true;
+ }
+
+ base::Callback<bool()> callback_;
+ bool expected_;
+ base::Time timeout_;
+ // The waiter's RunLoop quit closure.
+ base::Closure quit_;
+
+ DISALLOW_COPY_AND_ASSIGN(CheckWaiter);
+};
+
+class DevToolsManagerDelegateTest : public InProcessBrowserTest {
+ public:
+ std::unique_ptr<base::DictionaryValue> SendCommand(std::string state) {
+ auto params = base::MakeUnique<base::DictionaryValue>();
+ auto bounds_object = base::MakeUnique<base::DictionaryValue>();
+ bounds_object->SetString("windowState", state);
+ params->Set("bounds", std::move(bounds_object));
+ params->SetInteger("windowId", browser()->session_id().id());
+ return ChromeDevToolsManagerDelegate::SetWindowBounds(0, params.get());
+ }
+
+ std::unique_ptr<base::DictionaryValue> UpdateBounds() {
+ auto params = base::MakeUnique<base::DictionaryValue>();
+ auto bounds_object = base::MakeUnique<base::DictionaryValue>();
+ bounds_object->SetString("windowState", "normal");
+ bounds_object->SetInteger("left", 200);
+ bounds_object->SetInteger("height", 400);
+ params->Set("bounds", std::move(bounds_object));
+ params->SetInteger("windowId", browser()->session_id().id());
+ return ChromeDevToolsManagerDelegate::SetWindowBounds(0, params.get());
+ }
+
+ void CheckIsMaximized(bool maximized) {
+ CheckWaiter(base::Bind(&BrowserWindow::IsMaximized,
+ base::Unretained(browser()->window())),
+ maximized)
+ .Wait();
+ EXPECT_EQ(maximized, browser()->window()->IsMaximized());
+ }
+
+ void CheckIsMinimized(bool minimized) {
+ CheckWaiter(base::Bind(&BrowserWindow::IsMinimized,
+ base::Unretained(browser()->window())),
+ minimized)
+ .Wait();
+ EXPECT_EQ(minimized, browser()->window()->IsMinimized());
+ }
+
+ void CheckIsFullscreen(bool fullscreen) {
+ CheckWaiter(base::Bind(&BrowserWindow::IsFullscreen,
+ base::Unretained(browser()->window())),
+ fullscreen)
+ .Wait();
+ EXPECT_EQ(fullscreen, browser()->window()->IsFullscreen());
+ }
+
+ bool IsWindowBoundsEqual(gfx::Rect expected) {
+ return browser()->window()->GetBounds() == expected;
+ }
+
+ void CheckWindowBounds(gfx::Rect expected) {
+ CheckWaiter(base::Bind(&DevToolsManagerDelegateTest::IsWindowBoundsEqual,
+ base::Unretained(this), expected),
+ true)
+ .Wait();
+ EXPECT_EQ(expected, browser()->window()->GetBounds());
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(DevToolsManagerDelegateTest, NormalWindowChangeBounds) {
+ browser()->window()->SetBounds(gfx::Rect(100, 100, 500, 600));
+ CheckWindowBounds(gfx::Rect(100, 100, 500, 600));
+ UpdateBounds();
+ CheckWindowBounds(gfx::Rect(200, 100, 500, 400));
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsManagerDelegateTest, NormalToMaximizedWindow) {
+ CheckIsMaximized(false);
+ SendCommand("maximized");
+ CheckIsMaximized(true);
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsManagerDelegateTest, NormalToMinimizedWindow) {
+ CheckIsMinimized(false);
+ SendCommand("minimized");
+ CheckIsMinimized(true);
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsManagerDelegateTest, NormalToFullscreenWindow) {
+#if defined(OS_MACOSX)
+ ui::test::ScopedFakeNSWindowFullscreen faker;
+#endif
+ CheckIsFullscreen(false);
+ SendCommand("fullscreen");
+#if defined(OS_MACOSX)
+ faker.FinishTransition();
+#endif
+ CheckIsFullscreen(true);
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsManagerDelegateTest,
+ MaximizedToMinimizedWindow) {
+ browser()->window()->Maximize();
+ CheckIsMaximized(true);
+
+ CheckIsMinimized(false);
+ SendCommand("minimized");
+ CheckIsMinimized(true);
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsManagerDelegateTest,
+ MaximizedToFullscreenWindow) {
+ browser()->window()->Maximize();
+ CheckIsMaximized(true);
+
+#if defined(OS_MACOSX)
+ ui::test::ScopedFakeNSWindowFullscreen faker;
+#endif
+ CheckIsFullscreen(false);
+ SendCommand("fullscreen");
+#if defined(OS_MACOSX)
+ faker.FinishTransition();
+#endif
+ CheckIsFullscreen(true);
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsManagerDelegateTest, ShowMinimizedWindow) {
+ browser()->window()->Minimize();
+ CheckIsMinimized(true);
+ SendCommand("normal");
+ CheckIsMinimized(false);
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsManagerDelegateTest, RestoreMaximizedWindow) {
+ browser()->window()->Maximize();
+ CheckIsMaximized(true);
+ SendCommand("normal");
+ CheckIsMaximized(false);
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsManagerDelegateTest, ExitFullscreenWindow) {
+#if defined(OS_MACOSX)
+ ui::test::ScopedFakeNSWindowFullscreen faker;
+#endif
+ browser()->window()->GetExclusiveAccessContext()->EnterFullscreen(
+ GURL(), EXCLUSIVE_ACCESS_BUBBLE_TYPE_NONE);
+#if defined(OS_MACOSX)
+ faker.FinishTransition();
+#endif
+ CheckIsFullscreen(true);
+ SendCommand("normal");
+#if defined(OS_MACOSX)
+ faker.FinishTransition();
+#endif
+ CheckIsFullscreen(false);
+}
diff --git a/chromium/chrome/browser/devtools/devtools_targets_ui.cc b/chromium/chrome/browser/devtools/devtools_targets_ui.cc
new file mode 100644
index 00000000000..ccd72ed9de3
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_targets_ui.cc
@@ -0,0 +1,490 @@
+// 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/devtools/devtools_targets_ui.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "chrome/browser/devtools/device/devtools_android_bridge.h"
+#include "chrome/browser/devtools/serialize_host_descriptions.h"
+#include "content/public/browser/browser_child_process_observer.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/child_process_data.h"
+#include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/worker_service.h"
+#include "content/public/browser/worker_service_observer.h"
+#include "content/public/common/process_type.h"
+#include "net/base/escape.h"
+
+using content::BrowserThread;
+using content::DevToolsAgentHost;
+
+namespace {
+
+const char kTargetSourceField[] = "source";
+const char kTargetSourceLocal[] = "local";
+const char kTargetSourceRemote[] = "remote";
+
+const char kTargetIdField[] = "id";
+const char kTargetTypeField[] = "type";
+const char kAttachedField[] = "attached";
+const char kUrlField[] = "url";
+const char kNameField[] = "name";
+const char kFaviconUrlField[] = "faviconUrl";
+const char kDescriptionField[] = "description";
+
+const char kGuestList[] = "guests";
+
+const char kAdbModelField[] = "adbModel";
+const char kAdbConnectedField[] = "adbConnected";
+const char kAdbSerialField[] = "adbSerial";
+const char kAdbBrowsersList[] = "browsers";
+const char kAdbDeviceIdFormat[] = "device:%s";
+
+const char kAdbBrowserNameField[] = "adbBrowserName";
+const char kAdbBrowserUserField[] = "adbBrowserUser";
+const char kAdbBrowserVersionField[] = "adbBrowserVersion";
+const char kAdbBrowserChromeVersionField[] = "adbBrowserChromeVersion";
+const char kAdbPagesList[] = "pages";
+
+const char kAdbScreenWidthField[] = "adbScreenWidth";
+const char kAdbScreenHeightField[] = "adbScreenHeight";
+
+const char kPortForwardingPorts[] = "ports";
+const char kPortForwardingBrowserId[] = "browserId";
+
+// CancelableTimer ------------------------------------------------------------
+
+class CancelableTimer {
+ public:
+ CancelableTimer(base::Closure callback, base::TimeDelta delay)
+ : callback_(callback),
+ weak_factory_(this) {
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTimer::Fire, weak_factory_.GetWeakPtr()),
+ delay);
+ }
+
+ private:
+ void Fire() { callback_.Run(); }
+
+ base::Closure callback_;
+ base::WeakPtrFactory<CancelableTimer> weak_factory_;
+};
+
+// WorkerObserver -------------------------------------------------------------
+
+class WorkerObserver
+ : public content::WorkerServiceObserver,
+ public base::RefCountedThreadSafe<WorkerObserver> {
+ public:
+ WorkerObserver() {}
+
+ void Start(base::Closure callback) {
+ DCHECK(callback_.is_null());
+ DCHECK(!callback.is_null());
+ callback_ = callback;
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&WorkerObserver::StartOnIOThread, this));
+ }
+
+ void Stop() {
+ DCHECK(!callback_.is_null());
+ callback_ = base::Closure();
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&WorkerObserver::StopOnIOThread, this));
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<WorkerObserver>;
+ ~WorkerObserver() override {}
+
+ // content::WorkerServiceObserver overrides:
+ void WorkerCreated(const GURL& url,
+ const base::string16& name,
+ int process_id,
+ int route_id) override {
+ NotifyOnIOThread();
+ }
+
+ void WorkerDestroyed(int process_id, int route_id) override {
+ NotifyOnIOThread();
+ }
+
+ void StartOnIOThread() {
+ content::WorkerService::GetInstance()->AddObserver(this);
+ }
+
+ void StopOnIOThread() {
+ content::WorkerService::GetInstance()->RemoveObserver(this);
+ }
+
+ void NotifyOnIOThread() {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::BindOnce(&WorkerObserver::NotifyOnUIThread, this));
+ }
+
+ void NotifyOnUIThread() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (callback_.is_null())
+ return;
+ callback_.Run();
+ }
+
+ // Accessed on UI thread.
+ base::Closure callback_;
+};
+
+// LocalTargetsUIHandler ---------------------------------------------
+
+class LocalTargetsUIHandler
+ : public DevToolsTargetsUIHandler,
+ public content::NotificationObserver {
+ public:
+ explicit LocalTargetsUIHandler(const Callback& callback);
+ ~LocalTargetsUIHandler() override;
+
+ // DevToolsTargetsUIHandler overrides.
+ void ForceUpdate() override;
+
+private:
+ // content::NotificationObserver overrides.
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ void ScheduleUpdate();
+ void UpdateTargets();
+ void SendTargets(const DevToolsAgentHost::List& targets);
+
+ content::NotificationRegistrar notification_registrar_;
+ std::unique_ptr<CancelableTimer> timer_;
+ scoped_refptr<WorkerObserver> observer_;
+ base::WeakPtrFactory<LocalTargetsUIHandler> weak_factory_;
+};
+
+LocalTargetsUIHandler::LocalTargetsUIHandler(
+ const Callback& callback)
+ : DevToolsTargetsUIHandler(kTargetSourceLocal, callback),
+ observer_(new WorkerObserver()),
+ weak_factory_(this) {
+ notification_registrar_.Add(this,
+ content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
+ content::NotificationService::AllSources());
+ notification_registrar_.Add(this,
+ content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
+ content::NotificationService::AllSources());
+ notification_registrar_.Add(this,
+ content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
+ content::NotificationService::AllSources());
+ observer_->Start(base::Bind(&LocalTargetsUIHandler::ScheduleUpdate,
+ base::Unretained(this)));
+ UpdateTargets();
+}
+
+LocalTargetsUIHandler::~LocalTargetsUIHandler() {
+ notification_registrar_.RemoveAll();
+ observer_->Stop();
+}
+
+void LocalTargetsUIHandler::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ ScheduleUpdate();
+}
+
+void LocalTargetsUIHandler::ForceUpdate() {
+ ScheduleUpdate();
+}
+
+void LocalTargetsUIHandler::ScheduleUpdate() {
+ const int kUpdateDelay = 100;
+ timer_.reset(
+ new CancelableTimer(
+ base::Bind(&LocalTargetsUIHandler::UpdateTargets,
+ base::Unretained(this)),
+ base::TimeDelta::FromMilliseconds(kUpdateDelay)));
+}
+
+void LocalTargetsUIHandler::UpdateTargets() {
+ SendTargets(DevToolsAgentHost::GetOrCreateAll());
+}
+
+void LocalTargetsUIHandler::SendTargets(
+ const content::DevToolsAgentHost::List& targets) {
+ std::vector<HostDescriptionNode> hosts;
+ hosts.reserve(targets.size());
+
+ targets_.clear();
+ for (const scoped_refptr<DevToolsAgentHost>& host : targets) {
+ targets_[host->GetId()] = host;
+ hosts.push_back(
+ {host->GetId(), host->GetParentId(), *Serialize(host.get())});
+ }
+
+ SendSerializedTargets(
+ SerializeHostDescriptions(std::move(hosts), kGuestList));
+}
+
+// AdbTargetsUIHandler --------------------------------------------------------
+
+class AdbTargetsUIHandler
+ : public DevToolsTargetsUIHandler,
+ public DevToolsAndroidBridge::DeviceListListener {
+ public:
+ AdbTargetsUIHandler(const Callback& callback, Profile* profile);
+ ~AdbTargetsUIHandler() override;
+
+ void Open(const std::string& browser_id, const std::string& url) override;
+
+ scoped_refptr<DevToolsAgentHost> GetBrowserAgentHost(
+ const std::string& browser_id) override;
+
+ private:
+ // DevToolsAndroidBridge::Listener overrides.
+ void DeviceListChanged(
+ const DevToolsAndroidBridge::RemoteDevices& devices) override;
+
+ DevToolsAndroidBridge* GetAndroidBridge();
+
+ Profile* const profile_;
+ DevToolsAndroidBridge* const android_bridge_;
+
+ typedef std::map<std::string,
+ scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> > RemoteBrowsers;
+ RemoteBrowsers remote_browsers_;
+};
+
+AdbTargetsUIHandler::AdbTargetsUIHandler(const Callback& callback,
+ Profile* profile)
+ : DevToolsTargetsUIHandler(kTargetSourceRemote, callback),
+ profile_(profile),
+ android_bridge_(
+ DevToolsAndroidBridge::Factory::GetForProfile(profile_)) {
+ if (android_bridge_)
+ android_bridge_->AddDeviceListListener(this);
+}
+
+AdbTargetsUIHandler::~AdbTargetsUIHandler() {
+ if (android_bridge_)
+ android_bridge_->RemoveDeviceListListener(this);
+}
+
+void AdbTargetsUIHandler::Open(const std::string& browser_id,
+ const std::string& url) {
+ RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
+ if (it != remote_browsers_.end() && android_bridge_)
+ android_bridge_->OpenRemotePage(it->second, url);
+}
+
+scoped_refptr<DevToolsAgentHost>
+AdbTargetsUIHandler::GetBrowserAgentHost(
+ const std::string& browser_id) {
+ RemoteBrowsers::iterator it = remote_browsers_.find(browser_id);
+ if (it == remote_browsers_.end() || !android_bridge_)
+ return nullptr;
+
+ return android_bridge_->GetBrowserAgentHost(it->second);
+}
+
+void AdbTargetsUIHandler::DeviceListChanged(
+ const DevToolsAndroidBridge::RemoteDevices& devices) {
+ remote_browsers_.clear();
+ targets_.clear();
+ if (!android_bridge_)
+ return;
+
+ base::ListValue device_list;
+ for (DevToolsAndroidBridge::RemoteDevices::const_iterator dit =
+ devices.begin(); dit != devices.end(); ++dit) {
+ DevToolsAndroidBridge::RemoteDevice* device = dit->get();
+ std::unique_ptr<base::DictionaryValue> device_data(
+ new base::DictionaryValue());
+ device_data->SetString(kAdbModelField, device->model());
+ device_data->SetString(kAdbSerialField, device->serial());
+ device_data->SetBoolean(kAdbConnectedField, device->is_connected());
+ std::string device_id = base::StringPrintf(
+ kAdbDeviceIdFormat,
+ device->serial().c_str());
+ device_data->SetString(kTargetIdField, device_id);
+ auto browser_list = base::MakeUnique<base::ListValue>();
+
+ DevToolsAndroidBridge::RemoteBrowsers& browsers = device->browsers();
+ for (DevToolsAndroidBridge::RemoteBrowsers::iterator bit =
+ browsers.begin(); bit != browsers.end(); ++bit) {
+ DevToolsAndroidBridge::RemoteBrowser* browser = bit->get();
+ std::unique_ptr<base::DictionaryValue> browser_data(
+ new base::DictionaryValue());
+ browser_data->SetString(kAdbBrowserNameField, browser->display_name());
+ browser_data->SetString(kAdbBrowserUserField, browser->user());
+ browser_data->SetString(kAdbBrowserVersionField, browser->version());
+ DevToolsAndroidBridge::RemoteBrowser::ParsedVersion parsed =
+ browser->GetParsedVersion();
+ browser_data->SetInteger(
+ kAdbBrowserChromeVersionField,
+ browser->IsChrome() && !parsed.empty() ? parsed[0] : 0);
+ std::string browser_id = browser->GetId();
+ browser_data->SetString(kTargetIdField, browser_id);
+ browser_data->SetString(kTargetSourceField, source_id());
+
+ auto page_list = base::MakeUnique<base::ListValue>();
+ remote_browsers_[browser_id] = browser;
+ for (const auto& page : browser->pages()) {
+ scoped_refptr<DevToolsAgentHost> host = page->CreateTarget();
+ std::unique_ptr<base::DictionaryValue> target_data =
+ Serialize(host.get());
+ // Pass the screen size in the target object to make sure that
+ // the caching logic does not prevent the target item from updating
+ // when the screen size changes.
+ gfx::Size screen_size = device->screen_size();
+ target_data->SetInteger(kAdbScreenWidthField, screen_size.width());
+ target_data->SetInteger(kAdbScreenHeightField, screen_size.height());
+ targets_[host->GetId()] = host;
+ page_list->Append(std::move(target_data));
+ }
+ browser_data->Set(kAdbPagesList, std::move(page_list));
+ browser_list->Append(std::move(browser_data));
+ }
+
+ device_data->Set(kAdbBrowsersList, std::move(browser_list));
+ device_list.Append(std::move(device_data));
+ }
+ SendSerializedTargets(device_list);
+}
+
+} // namespace
+
+// DevToolsTargetsUIHandler ---------------------------------------------------
+
+DevToolsTargetsUIHandler::DevToolsTargetsUIHandler(
+ const std::string& source_id,
+ const Callback& callback)
+ : source_id_(source_id),
+ callback_(callback) {
+}
+
+DevToolsTargetsUIHandler::~DevToolsTargetsUIHandler() {
+}
+
+// static
+std::unique_ptr<DevToolsTargetsUIHandler>
+DevToolsTargetsUIHandler::CreateForLocal(
+ const DevToolsTargetsUIHandler::Callback& callback) {
+ return std::unique_ptr<DevToolsTargetsUIHandler>(
+ new LocalTargetsUIHandler(callback));
+}
+
+// static
+std::unique_ptr<DevToolsTargetsUIHandler>
+DevToolsTargetsUIHandler::CreateForAdb(
+ const DevToolsTargetsUIHandler::Callback& callback,
+ Profile* profile) {
+ return std::unique_ptr<DevToolsTargetsUIHandler>(
+ new AdbTargetsUIHandler(callback, profile));
+}
+
+scoped_refptr<DevToolsAgentHost> DevToolsTargetsUIHandler::GetTarget(
+ const std::string& target_id) {
+ TargetMap::iterator it = targets_.find(target_id);
+ if (it != targets_.end())
+ return it->second;
+ return NULL;
+}
+
+void DevToolsTargetsUIHandler::Open(const std::string& browser_id,
+ const std::string& url) {
+}
+
+scoped_refptr<DevToolsAgentHost>
+DevToolsTargetsUIHandler::GetBrowserAgentHost(const std::string& browser_id) {
+ return NULL;
+}
+
+std::unique_ptr<base::DictionaryValue> DevToolsTargetsUIHandler::Serialize(
+ DevToolsAgentHost* host) {
+ auto target_data = base::MakeUnique<base::DictionaryValue>();
+ target_data->SetString(kTargetSourceField, source_id_);
+ target_data->SetString(kTargetIdField, host->GetId());
+ target_data->SetString(kTargetTypeField, host->GetType());
+ target_data->SetBoolean(kAttachedField, host->IsAttached());
+ target_data->SetString(kUrlField, host->GetURL().spec());
+ target_data->SetString(kNameField, host->GetTitle());
+ target_data->SetString(kFaviconUrlField, host->GetFaviconURL().spec());
+ target_data->SetString(kDescriptionField, host->GetDescription());
+ return target_data;
+}
+
+void DevToolsTargetsUIHandler::SendSerializedTargets(
+ const base::ListValue& list) {
+ callback_.Run(source_id_, list);
+}
+
+void DevToolsTargetsUIHandler::ForceUpdate() {
+}
+
+// PortForwardingStatusSerializer ---------------------------------------------
+
+PortForwardingStatusSerializer::PortForwardingStatusSerializer(
+ const Callback& callback, Profile* profile)
+ : callback_(callback),
+ profile_(profile) {
+ DevToolsAndroidBridge* android_bridge =
+ DevToolsAndroidBridge::Factory::GetForProfile(profile_);
+ if (android_bridge)
+ android_bridge->AddPortForwardingListener(this);
+}
+
+PortForwardingStatusSerializer::~PortForwardingStatusSerializer() {
+ DevToolsAndroidBridge* android_bridge =
+ DevToolsAndroidBridge::Factory::GetForProfile(profile_);
+ if (android_bridge)
+ android_bridge->RemovePortForwardingListener(this);
+}
+
+void PortForwardingStatusSerializer::PortStatusChanged(
+ const ForwardingStatus& status) {
+ base::DictionaryValue result;
+ for (ForwardingStatus::const_iterator sit = status.begin();
+ sit != status.end(); ++sit) {
+ auto port_status_dict = base::MakeUnique<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>();
+ device_status_dict->Set(kPortForwardingPorts, std::move(port_status_dict));
+ device_status_dict->SetString(kPortForwardingBrowserId,
+ sit->first->GetId());
+
+ std::string device_id = base::StringPrintf(
+ kAdbDeviceIdFormat,
+ sit->first->serial().c_str());
+ result.Set(device_id, std::move(device_status_dict));
+ }
+ callback_.Run(result);
+}
diff --git a/chromium/chrome/browser/devtools/devtools_targets_ui.h b/chromium/chrome/browser/devtools/devtools_targets_ui.h
new file mode 100644
index 00000000000..245cc333a3a
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_targets_ui.h
@@ -0,0 +1,82 @@
+// 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_DEVTOOLS_DEVTOOLS_TARGETS_UI_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_TARGETS_UI_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "chrome/browser/devtools/device/devtools_android_bridge.h"
+
+namespace base {
+class ListValue;
+class DictionaryValue;
+}
+
+class Profile;
+
+class DevToolsTargetsUIHandler {
+ public:
+ typedef base::Callback<void(const std::string&,
+ const base::ListValue&)> Callback;
+
+ DevToolsTargetsUIHandler(const std::string& source_id,
+ const Callback& callback);
+ virtual ~DevToolsTargetsUIHandler();
+
+ std::string source_id() const { return source_id_; }
+
+ static std::unique_ptr<DevToolsTargetsUIHandler> CreateForLocal(
+ const Callback& callback);
+
+ static std::unique_ptr<DevToolsTargetsUIHandler> CreateForAdb(
+ const Callback& callback,
+ Profile* profile);
+
+ scoped_refptr<content::DevToolsAgentHost> GetTarget(
+ const std::string& target_id);
+
+ virtual void Open(const std::string& browser_id, const std::string& url);
+
+ virtual scoped_refptr<content::DevToolsAgentHost> GetBrowserAgentHost(
+ const std::string& browser_id);
+
+ virtual void ForceUpdate();
+
+ protected:
+ std::unique_ptr<base::DictionaryValue> Serialize(
+ content::DevToolsAgentHost* host);
+ void SendSerializedTargets(const base::ListValue& list);
+
+ using TargetMap =
+ std::map<std::string, scoped_refptr<content::DevToolsAgentHost>>;
+ TargetMap targets_;
+
+ private:
+ const std::string source_id_;
+ Callback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsTargetsUIHandler);
+};
+
+class PortForwardingStatusSerializer
+ : private DevToolsAndroidBridge::PortForwardingListener {
+ public:
+ typedef base::Callback<void(const base::Value&)> Callback;
+
+ PortForwardingStatusSerializer(const Callback& callback, Profile* profile);
+ ~PortForwardingStatusSerializer() override;
+
+ void PortStatusChanged(const ForwardingStatus& status) override;
+
+ private:
+ Callback callback_;
+ Profile* profile_;
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_TARGETS_UI_H_
diff --git a/chromium/chrome/browser/devtools/devtools_toggle_action.cc b/chromium/chrome/browser/devtools/devtools_toggle_action.cc
new file mode 100644
index 00000000000..1d49216c304
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_toggle_action.cc
@@ -0,0 +1,74 @@
+// 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/devtools/devtools_toggle_action.h"
+
+DevToolsToggleAction::RevealParams::RevealParams(const base::string16& url,
+ size_t line_number,
+ size_t column_number)
+ : url(url), line_number(line_number), column_number(column_number) {
+}
+
+DevToolsToggleAction::RevealParams::~RevealParams() {
+}
+
+DevToolsToggleAction::DevToolsToggleAction(Type type) : type_(type) {
+}
+
+DevToolsToggleAction::DevToolsToggleAction(RevealParams* params)
+ : type_(kReveal), params_(params) {
+}
+
+DevToolsToggleAction::DevToolsToggleAction(const DevToolsToggleAction& rhs)
+ : type_(rhs.type_),
+ params_(rhs.params_.get() ? new RevealParams(*rhs.params_) : NULL) {
+}
+
+void DevToolsToggleAction::operator=(const DevToolsToggleAction& rhs) {
+ type_ = rhs.type_;
+ if (rhs.params_.get())
+ params_.reset(new RevealParams(*rhs.params_));
+}
+
+DevToolsToggleAction::~DevToolsToggleAction() {
+}
+
+// static
+DevToolsToggleAction DevToolsToggleAction::Show() {
+ return DevToolsToggleAction(kShow);
+}
+
+// static
+DevToolsToggleAction DevToolsToggleAction::ShowConsolePanel() {
+ return DevToolsToggleAction(kShowConsolePanel);
+}
+
+// static
+DevToolsToggleAction DevToolsToggleAction::ShowElementsPanel() {
+ return DevToolsToggleAction(kShowElementsPanel);
+}
+
+// static
+DevToolsToggleAction DevToolsToggleAction::Inspect() {
+ return DevToolsToggleAction(kInspect);
+}
+
+// static
+DevToolsToggleAction DevToolsToggleAction::Toggle() {
+ return DevToolsToggleAction(kToggle);
+}
+
+// static
+DevToolsToggleAction DevToolsToggleAction::Reveal(
+ const base::string16& url,
+ size_t line_number,
+ size_t column_number) {
+ return DevToolsToggleAction(
+ new RevealParams(url, line_number, column_number));
+}
+
+// static
+DevToolsToggleAction DevToolsToggleAction::NoOp() {
+ return DevToolsToggleAction(kNoOp);
+}
diff --git a/chromium/chrome/browser/devtools/devtools_toggle_action.h b/chromium/chrome/browser/devtools/devtools_toggle_action.h
new file mode 100644
index 00000000000..ec5c28b5fde
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_toggle_action.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2011 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_DEVTOOLS_DEVTOOLS_TOGGLE_ACTION_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_TOGGLE_ACTION_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/strings/string16.h"
+
+struct DevToolsToggleAction {
+ public:
+ enum Type {
+ kShow,
+ kShowConsolePanel,
+ kShowElementsPanel,
+ kInspect,
+ kToggle,
+ kReveal,
+ kNoOp
+ };
+
+ struct RevealParams {
+ RevealParams(const base::string16& url,
+ size_t line_number,
+ size_t column_number);
+ ~RevealParams();
+
+ base::string16 url;
+ size_t line_number;
+ size_t column_number;
+ };
+
+ void operator=(const DevToolsToggleAction& rhs);
+ DevToolsToggleAction(const DevToolsToggleAction& rhs);
+ ~DevToolsToggleAction();
+
+ static DevToolsToggleAction Show();
+ static DevToolsToggleAction ShowConsolePanel();
+ static DevToolsToggleAction ShowElementsPanel();
+ static DevToolsToggleAction Inspect();
+ static DevToolsToggleAction Toggle();
+ static DevToolsToggleAction Reveal(const base::string16& url,
+ size_t line_number,
+ size_t column_number);
+ static DevToolsToggleAction NoOp();
+
+ Type type() const { return type_; }
+ const RevealParams* params() const { return params_.get(); }
+
+ private:
+ explicit DevToolsToggleAction(Type type);
+ explicit DevToolsToggleAction(RevealParams* reveal_params);
+
+ // The type of action.
+ Type type_;
+
+ // Additional parameters for the Reveal action; NULL if of any other type.
+ std::unique_ptr<RevealParams> params_;
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_TOGGLE_ACTION_H_
diff --git a/chromium/chrome/browser/devtools/devtools_ui_bindings.cc b/chromium/chrome/browser/devtools/devtools_ui_bindings.cc
new file mode 100644
index 00000000000..cbdff152343
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -0,0 +1,1428 @@
+// 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/devtools/devtools_ui_bindings.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/base64.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/json/string_escape.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#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/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/devtools/devtools_file_watcher.h"
+#include "chrome/browser/devtools/devtools_protocol.h"
+#include "chrome/browser/devtools/global_confirm_info_bar.h"
+#include "chrome/browser/devtools/url_constants.h"
+#include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
+#include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/chrome_manifest_url_handlers.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/infobars/core/confirm_infobar_delegate.h"
+#include "components/infobars/core/infobar.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "components/sync_preferences/pref_service_syncable.h"
+#include "components/zoom/page_zoom.h"
+#include "content/public/browser/child_process_security_policy.h"
+#include "content/public/browser/devtools_external_agent_proxy.h"
+#include "content/public/browser/devtools_external_agent_proxy_delegate.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/reload_type.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/common/renderer_preferences.h"
+#include "content/public/common/url_constants.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "ipc/ipc_channel.h"
+#include "net/base/escape.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/url_util.h"
+#include "net/cert/x509_certificate.h"
+#include "net/http/http_response_headers.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_response_writer.h"
+#include "third_party/WebKit/public/public_features.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/page_transition_types.h"
+
+using base::DictionaryValue;
+using content::BrowserThread;
+
+namespace content {
+struct LoadCommittedDetails;
+struct FrameNavigateParams;
+}
+
+namespace {
+
+static const char kFrontendHostId[] = "id";
+static const char kFrontendHostMethod[] = "method";
+static const char kFrontendHostParams[] = "params";
+static const char kTitleFormat[] = "Developer Tools - %s";
+
+static const char kDevToolsActionTakenHistogram[] = "DevTools.ActionTaken";
+static const char kDevToolsPanelShownHistogram[] = "DevTools.PanelShown";
+
+static const char kRemotePageActionInspect[] = "inspect";
+static const char kRemotePageActionReload[] = "reload";
+static const char kRemotePageActionActivate[] = "activate";
+static const char kRemotePageActionClose[] = "close";
+
+static const char kConfigDiscoverUsbDevices[] = "discoverUsbDevices";
+static const char kConfigPortForwardingEnabled[] = "portForwardingEnabled";
+static const char kConfigPortForwardingConfig[] = "portForwardingConfig";
+static const char kConfigNetworkDiscoveryEnabled[] = "networkDiscoveryEnabled";
+static const char kConfigNetworkDiscoveryConfig[] = "networkDiscoveryConfig";
+
+// This constant should be in sync with
+// the constant at shell_devtools_frontend.cc.
+const size_t kMaxMessageChunkSize = IPC::Channel::kMaximumMessageSize / 4;
+
+typedef std::vector<DevToolsUIBindings*> DevToolsUIBindingsList;
+base::LazyInstance<DevToolsUIBindingsList>::Leaky g_instances =
+ LAZY_INSTANCE_INITIALIZER;
+
+std::unique_ptr<base::DictionaryValue> CreateFileSystemValue(
+ DevToolsFileHelper::FileSystem file_system) {
+ auto file_system_value = base::MakeUnique<base::DictionaryValue>();
+ file_system_value->SetString("fileSystemName", file_system.file_system_name);
+ file_system_value->SetString("rootURL", file_system.root_url);
+ file_system_value->SetString("fileSystemPath", file_system.file_system_path);
+ return file_system_value;
+}
+
+Browser* FindBrowser(content::WebContents* web_contents) {
+ for (auto* browser : *BrowserList::GetInstance()) {
+ int tab_index = browser->tab_strip_model()->GetIndexOfWebContents(
+ web_contents);
+ if (tab_index != TabStripModel::kNoTab)
+ return browser;
+ }
+ return NULL;
+}
+
+// DevToolsConfirmInfoBarDelegate ---------------------------------------------
+
+typedef base::Callback<void(bool)> InfoBarCallback;
+
+class DevToolsConfirmInfoBarDelegate : public ConfirmInfoBarDelegate {
+ public:
+ DevToolsConfirmInfoBarDelegate(
+ const InfoBarCallback& callback,
+ const base::string16& message);
+ ~DevToolsConfirmInfoBarDelegate() override;
+
+ private:
+ infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
+ base::string16 GetMessageText() const override;
+ base::string16 GetButtonLabel(InfoBarButton button) const override;
+ bool Accept() override;
+ bool Cancel() override;
+
+ InfoBarCallback callback_;
+ const base::string16 message_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsConfirmInfoBarDelegate);
+};
+
+DevToolsConfirmInfoBarDelegate::DevToolsConfirmInfoBarDelegate(
+ const InfoBarCallback& callback,
+ const base::string16& message)
+ : ConfirmInfoBarDelegate(),
+ callback_(callback),
+ message_(message) {
+}
+
+DevToolsConfirmInfoBarDelegate::~DevToolsConfirmInfoBarDelegate() {
+ if (!callback_.is_null())
+ callback_.Run(false);
+}
+
+infobars::InfoBarDelegate::InfoBarIdentifier
+DevToolsConfirmInfoBarDelegate::GetIdentifier() const {
+ return DEV_TOOLS_CONFIRM_INFOBAR_DELEGATE;
+}
+
+base::string16 DevToolsConfirmInfoBarDelegate::GetMessageText() const {
+ return message_;
+}
+
+base::string16 DevToolsConfirmInfoBarDelegate::GetButtonLabel(
+ InfoBarButton button) const {
+ return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
+ IDS_DEV_TOOLS_CONFIRM_ALLOW_BUTTON : IDS_DEV_TOOLS_CONFIRM_DENY_BUTTON);
+}
+
+bool DevToolsConfirmInfoBarDelegate::Accept() {
+ callback_.Run(true);
+ callback_.Reset();
+ return true;
+}
+
+bool DevToolsConfirmInfoBarDelegate::Cancel() {
+ callback_.Run(false);
+ callback_.Reset();
+ return true;
+}
+
+// DevToolsUIDefaultDelegate --------------------------------------------------
+
+class DefaultBindingsDelegate : public DevToolsUIBindings::Delegate {
+ public:
+ explicit DefaultBindingsDelegate(content::WebContents* web_contents)
+ : web_contents_(web_contents) {}
+
+ private:
+ ~DefaultBindingsDelegate() override {}
+
+ void ActivateWindow() override;
+ void CloseWindow() override {}
+ void Inspect(scoped_refptr<content::DevToolsAgentHost> host) override {}
+ void SetInspectedPageBounds(const gfx::Rect& rect) override {}
+ void InspectElementCompleted() override {}
+ void SetIsDocked(bool is_docked) override {}
+ void OpenInNewTab(const std::string& url) override;
+ void SetWhitelistedShortcuts(const std::string& message) override {}
+ void SetEyeDropperActive(bool active) override {}
+ void OpenNodeFrontend() override {}
+ using DispatchCallback =
+ DevToolsEmbedderMessageDispatcher::Delegate::DispatchCallback;
+
+ void InspectedContentsClosing() override;
+ void OnLoadCompleted() override {}
+ void ReadyForTest() override {}
+ InfoBarService* GetInfoBarService() override;
+ void RenderProcessGone(bool crashed) override {}
+
+ content::WebContents* web_contents_;
+ DISALLOW_COPY_AND_ASSIGN(DefaultBindingsDelegate);
+};
+
+void DefaultBindingsDelegate::ActivateWindow() {
+ web_contents_->GetDelegate()->ActivateContents(web_contents_);
+ web_contents_->Focus();
+}
+
+void DefaultBindingsDelegate::OpenInNewTab(const std::string& url) {
+ content::OpenURLParams params(GURL(url), content::Referrer(),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui::PAGE_TRANSITION_LINK, false);
+ Browser* browser = FindBrowser(web_contents_);
+ browser->OpenURL(params);
+}
+
+void DefaultBindingsDelegate::InspectedContentsClosing() {
+ web_contents_->ClosePage();
+}
+
+InfoBarService* DefaultBindingsDelegate::GetInfoBarService() {
+ return InfoBarService::FromWebContents(web_contents_);
+}
+
+// ResponseWriter -------------------------------------------------------------
+
+class ResponseWriter : public net::URLFetcherResponseWriter {
+ public:
+ ResponseWriter(base::WeakPtr<DevToolsUIBindings> bindings, int stream_id);
+ ~ResponseWriter() override;
+
+ // URLFetcherResponseWriter overrides:
+ int Initialize(const net::CompletionCallback& callback) override;
+ int Write(net::IOBuffer* buffer,
+ int num_bytes,
+ const net::CompletionCallback& callback) override;
+ int Finish(int net_error, const net::CompletionCallback& callback) override;
+
+ private:
+ base::WeakPtr<DevToolsUIBindings> bindings_;
+ int stream_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResponseWriter);
+};
+
+ResponseWriter::ResponseWriter(base::WeakPtr<DevToolsUIBindings> bindings,
+ int stream_id)
+ : bindings_(bindings),
+ stream_id_(stream_id) {
+}
+
+ResponseWriter::~ResponseWriter() {
+}
+
+int ResponseWriter::Initialize(const net::CompletionCallback& callback) {
+ return net::OK;
+}
+
+int ResponseWriter::Write(net::IOBuffer* buffer,
+ int num_bytes,
+ const net::CompletionCallback& callback) {
+ std::string chunk = std::string(buffer->data(), num_bytes);
+ bool encoded = false;
+ if (!base::IsStringUTF8(chunk)) {
+ encoded = true;
+ base::Base64Encode(chunk, &chunk);
+ }
+
+ base::Value* id = new base::Value(stream_id_);
+ base::Value* chunkValue = new base::Value(chunk);
+ base::Value* encodedValue = new base::Value(encoded);
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI, FROM_HERE,
+ base::BindOnce(&DevToolsUIBindings::CallClientFunction, bindings_,
+ "DevToolsAPI.streamWrite", base::Owned(id),
+ base::Owned(chunkValue), base::Owned(encodedValue)));
+ return num_bytes;
+}
+
+int ResponseWriter::Finish(int net_error,
+ const net::CompletionCallback& callback) {
+ return net::OK;
+}
+
+GURL SanitizeFrontendURL(
+ const GURL& url,
+ const std::string& scheme,
+ const std::string& host,
+ const std::string& path,
+ bool allow_query);
+
+std::string SanitizeRevision(const std::string& revision) {
+ for (size_t i = 0; i < revision.length(); i++) {
+ if (!(revision[i] == '@' && i == 0)
+ && !(revision[i] >= '0' && revision[i] <= '9')
+ && !(revision[i] >= 'a' && revision[i] <= 'z')
+ && !(revision[i] >= 'A' && revision[i] <= 'Z')) {
+ return std::string();
+ }
+ }
+ return revision;
+}
+
+std::string SanitizeFrontendPath(const std::string& path) {
+ for (size_t i = 0; i < path.length(); i++) {
+ if (path[i] != '/' && path[i] != '-' && path[i] != '_'
+ && path[i] != '.' && path[i] != '@'
+ && !(path[i] >= '0' && path[i] <= '9')
+ && !(path[i] >= 'a' && path[i] <= 'z')
+ && !(path[i] >= 'A' && path[i] <= 'Z')) {
+ return std::string();
+ }
+ }
+ return path;
+}
+
+std::string SanitizeEndpoint(const std::string& value) {
+ if (value.find('&') != std::string::npos
+ || value.find('?') != std::string::npos)
+ return std::string();
+ return value;
+}
+
+std::string SanitizeRemoteBase(const std::string& value) {
+ GURL url(value);
+ std::string path = url.path();
+ std::vector<std::string> parts = base::SplitString(
+ path, "/", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+ std::string revision = parts.size() > 2 ? parts[2] : "";
+ revision = SanitizeRevision(revision);
+ path = base::StringPrintf("/%s/%s/", kRemoteFrontendPath, revision.c_str());
+ return SanitizeFrontendURL(url, url::kHttpsScheme,
+ kRemoteFrontendDomain, path, false).spec();
+}
+
+std::string SanitizeRemoteFrontendURL(const std::string& value) {
+ GURL url(net::UnescapeURLComponent(value,
+ net::UnescapeRule::SPACES | net::UnescapeRule::PATH_SEPARATORS |
+ net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS |
+ net::UnescapeRule::REPLACE_PLUS_WITH_SPACE));
+ std::string path = url.path();
+ std::vector<std::string> parts = base::SplitString(
+ path, "/", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+ std::string revision = parts.size() > 2 ? parts[2] : "";
+ revision = SanitizeRevision(revision);
+ std::string filename = parts.size() ? parts[parts.size() - 1] : "";
+ if (filename != "devtools.html")
+ filename = "inspector.html";
+ path = base::StringPrintf("/serve_rev/%s/%s",
+ revision.c_str(), filename.c_str());
+ std::string sanitized = SanitizeFrontendURL(url, url::kHttpsScheme,
+ kRemoteFrontendDomain, path, true).spec();
+ return net::EscapeQueryParamValue(sanitized, false);
+}
+
+std::string SanitizeFrontendQueryParam(
+ const std::string& key,
+ const std::string& value) {
+ // Convert boolean flags to true.
+ if (key == "can_dock" || key == "debugFrontend" || key == "experiments" ||
+ key == "isSharedWorker" || key == "v8only" || key == "remoteFrontend" ||
+ key == "nodeFrontend")
+ return "true";
+
+ // Pass connection endpoints as is.
+ if (key == "ws" || key == "service-backend")
+ return SanitizeEndpoint(value);
+
+ // Only support undocked for old frontends.
+ if (key == "dockSide" && value == "undocked")
+ return value;
+
+ if (key == "panel" && (value == "elements" || value == "console"))
+ return value;
+
+ if (key == "remoteBase")
+ return SanitizeRemoteBase(value);
+
+ if (key == "remoteFrontendUrl")
+ return SanitizeRemoteFrontendURL(value);
+
+ return std::string();
+}
+
+GURL SanitizeFrontendURL(
+ const GURL& url,
+ const std::string& scheme,
+ const std::string& host,
+ const std::string& path,
+ bool allow_query) {
+ std::vector<std::string> query_parts;
+ if (allow_query) {
+ for (net::QueryIterator it(url); !it.IsAtEnd(); it.Advance()) {
+ std::string value = SanitizeFrontendQueryParam(it.GetKey(),
+ it.GetValue());
+ if (!value.empty()) {
+ query_parts.push_back(
+ base::StringPrintf("%s=%s", it.GetKey().c_str(), value.c_str()));
+ }
+ }
+ }
+ std::string query =
+ query_parts.empty() ? "" : "?" + base::JoinString(query_parts, "&");
+ std::string constructed = base::StringPrintf("%s://%s%s%s",
+ scheme.c_str(), host.c_str(), path.c_str(), query.c_str());
+ GURL result = GURL(constructed);
+ if (!result.is_valid())
+ return GURL();
+ return result;
+}
+
+} // namespace
+
+// DevToolsUIBindings::FrontendWebContentsObserver ----------------------------
+
+class DevToolsUIBindings::FrontendWebContentsObserver
+ : public content::WebContentsObserver {
+ public:
+ explicit FrontendWebContentsObserver(DevToolsUIBindings* ui_bindings);
+ ~FrontendWebContentsObserver() override;
+
+ private:
+ // contents::WebContentsObserver:
+ void RenderProcessGone(base::TerminationStatus status) override;
+ void ReadyToCommitNavigation(
+ content::NavigationHandle* navigation_handle) override;
+ void DocumentAvailableInMainFrame() override;
+ void DocumentOnLoadCompletedInMainFrame() override;
+ void DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) override;
+
+ DevToolsUIBindings* devtools_bindings_;
+ DISALLOW_COPY_AND_ASSIGN(FrontendWebContentsObserver);
+};
+
+DevToolsUIBindings::FrontendWebContentsObserver::FrontendWebContentsObserver(
+ DevToolsUIBindings* devtools_ui_bindings)
+ : WebContentsObserver(devtools_ui_bindings->web_contents()),
+ devtools_bindings_(devtools_ui_bindings) {
+}
+
+DevToolsUIBindings::FrontendWebContentsObserver::
+ ~FrontendWebContentsObserver() {
+}
+
+// static
+GURL DevToolsUIBindings::SanitizeFrontendURL(const GURL& url) {
+ return ::SanitizeFrontendURL(url, content::kChromeDevToolsScheme,
+ chrome::kChromeUIDevToolsHost, SanitizeFrontendPath(url.path()), true);
+}
+
+bool DevToolsUIBindings::IsValidFrontendURL(const GURL& url) {
+ if (url.SchemeIs(content::kChromeUIScheme) &&
+ url.host() == content::kChromeUITracingHost &&
+ !url.has_query() && !url.has_ref()) {
+ return true;
+ }
+
+ return SanitizeFrontendURL(url).spec() == url.spec();
+}
+
+void DevToolsUIBindings::FrontendWebContentsObserver::RenderProcessGone(
+ base::TerminationStatus status) {
+ bool crashed = true;
+ switch (status) {
+ case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
+ case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
+#if defined(OS_CHROMEOS)
+ case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM:
+#endif
+ case base::TERMINATION_STATUS_PROCESS_CRASHED:
+ case base::TERMINATION_STATUS_LAUNCH_FAILED:
+ if (devtools_bindings_->agent_host_.get())
+ devtools_bindings_->Detach();
+ break;
+ default:
+ crashed = false;
+ break;
+ }
+ devtools_bindings_->delegate_->RenderProcessGone(crashed);
+}
+
+void DevToolsUIBindings::FrontendWebContentsObserver::ReadyToCommitNavigation(
+ content::NavigationHandle* navigation_handle) {
+ if (navigation_handle->IsInMainFrame())
+ devtools_bindings_->UpdateFrontendHost(navigation_handle);
+}
+
+void DevToolsUIBindings::FrontendWebContentsObserver::
+ DocumentAvailableInMainFrame() {
+ devtools_bindings_->DocumentAvailableInMainFrame();
+}
+
+void DevToolsUIBindings::FrontendWebContentsObserver::
+ DocumentOnLoadCompletedInMainFrame() {
+ devtools_bindings_->DocumentOnLoadCompletedInMainFrame();
+}
+
+void DevToolsUIBindings::FrontendWebContentsObserver::DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) {
+ if (navigation_handle->IsInMainFrame() && navigation_handle->HasCommitted())
+ devtools_bindings_->DidNavigateMainFrame();
+}
+
+// DevToolsUIBindings ---------------------------------------------------------
+
+DevToolsUIBindings* DevToolsUIBindings::ForWebContents(
+ content::WebContents* web_contents) {
+ if (g_instances == NULL)
+ return NULL;
+ DevToolsUIBindingsList* instances = g_instances.Pointer();
+ for (DevToolsUIBindingsList::iterator it(instances->begin());
+ it != instances->end(); ++it) {
+ if ((*it)->web_contents() == web_contents)
+ return *it;
+ }
+ return NULL;
+}
+
+DevToolsUIBindings::DevToolsUIBindings(content::WebContents* web_contents)
+ : profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
+ android_bridge_(DevToolsAndroidBridge::Factory::GetForProfile(profile_)),
+ web_contents_(web_contents),
+ delegate_(new DefaultBindingsDelegate(web_contents_)),
+ devices_updates_enabled_(false),
+ frontend_loaded_(false),
+ reloading_(false),
+ weak_factory_(this) {
+ g_instances.Get().push_back(this);
+ frontend_contents_observer_.reset(new FrontendWebContentsObserver(this));
+ web_contents_->GetMutableRendererPrefs()->can_accept_load_drops = false;
+
+ file_helper_.reset(new DevToolsFileHelper(web_contents_, profile_, this));
+ file_system_indexer_ = new DevToolsFileSystemIndexer();
+ extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
+ web_contents_);
+
+ // Register on-load actions.
+ embedder_message_dispatcher_.reset(
+ DevToolsEmbedderMessageDispatcher::CreateForDevToolsFrontend(this));
+}
+
+DevToolsUIBindings::~DevToolsUIBindings() {
+ for (const auto& pair : pending_requests_)
+ delete pair.first;
+
+ if (agent_host_.get())
+ agent_host_->DetachClient(this);
+
+ for (IndexingJobsMap::const_iterator jobs_it(indexing_jobs_.begin());
+ jobs_it != indexing_jobs_.end(); ++jobs_it) {
+ jobs_it->second->Stop();
+ }
+ indexing_jobs_.clear();
+ SetDevicesUpdatesEnabled(false);
+
+ // Remove self from global list.
+ DevToolsUIBindingsList* instances = g_instances.Pointer();
+ DevToolsUIBindingsList::iterator it(
+ std::find(instances->begin(), instances->end(), this));
+ DCHECK(it != instances->end());
+ instances->erase(it);
+}
+
+// content::DevToolsFrontendHost::Delegate implementation ---------------------
+void DevToolsUIBindings::HandleMessageFromDevToolsFrontend(
+ const std::string& message) {
+ if (!frontend_host_)
+ return;
+ std::string method;
+ base::ListValue empty_params;
+ base::ListValue* params = &empty_params;
+
+ base::DictionaryValue* dict = NULL;
+ std::unique_ptr<base::Value> parsed_message = base::JSONReader::Read(message);
+ if (!parsed_message ||
+ !parsed_message->GetAsDictionary(&dict) ||
+ !dict->GetString(kFrontendHostMethod, &method) ||
+ (dict->HasKey(kFrontendHostParams) &&
+ !dict->GetList(kFrontendHostParams, &params))) {
+ LOG(ERROR) << "Invalid message was sent to embedder: " << message;
+ return;
+ }
+ int id = 0;
+ dict->GetInteger(kFrontendHostId, &id);
+ embedder_message_dispatcher_->Dispatch(
+ base::Bind(&DevToolsUIBindings::SendMessageAck,
+ weak_factory_.GetWeakPtr(),
+ id),
+ method,
+ params);
+}
+
+// content::DevToolsAgentHostClient implementation --------------------------
+void DevToolsUIBindings::DispatchProtocolMessage(
+ content::DevToolsAgentHost* agent_host, const std::string& message) {
+ DCHECK(agent_host == agent_host_.get());
+ if (!frontend_host_)
+ return;
+
+ if (message.length() < kMaxMessageChunkSize) {
+ std::string param;
+ base::EscapeJSONString(message, true, &param);
+ base::string16 javascript =
+ base::UTF8ToUTF16("DevToolsAPI.dispatchMessage(" + param + ");");
+ web_contents_->GetMainFrame()->ExecuteJavaScript(javascript);
+ return;
+ }
+
+ base::Value total_size(static_cast<int>(message.length()));
+ for (size_t pos = 0; pos < message.length(); pos += kMaxMessageChunkSize) {
+ base::Value message_value(message.substr(pos, kMaxMessageChunkSize));
+ CallClientFunction("DevToolsAPI.dispatchMessageChunk",
+ &message_value, pos ? NULL : &total_size, NULL);
+ }
+}
+
+void DevToolsUIBindings::AgentHostClosed(
+ content::DevToolsAgentHost* agent_host,
+ bool replaced_with_another_client) {
+ DCHECK(agent_host == agent_host_.get());
+ agent_host_ = NULL;
+ delegate_->InspectedContentsClosing();
+}
+
+void DevToolsUIBindings::SendMessageAck(int request_id,
+ const base::Value* arg) {
+ base::Value id_value(request_id);
+ CallClientFunction("DevToolsAPI.embedderMessageAck",
+ &id_value, arg, nullptr);
+}
+
+// DevToolsEmbedderMessageDispatcher::Delegate implementation -----------------
+
+void DevToolsUIBindings::ActivateWindow() {
+ delegate_->ActivateWindow();
+}
+
+void DevToolsUIBindings::CloseWindow() {
+ delegate_->CloseWindow();
+}
+
+void DevToolsUIBindings::LoadCompleted() {
+ FrontendLoaded();
+}
+
+void DevToolsUIBindings::SetInspectedPageBounds(const gfx::Rect& rect) {
+ delegate_->SetInspectedPageBounds(rect);
+}
+
+void DevToolsUIBindings::SetIsDocked(const DispatchCallback& callback,
+ bool dock_requested) {
+ delegate_->SetIsDocked(dock_requested);
+ callback.Run(nullptr);
+}
+
+void DevToolsUIBindings::InspectElementCompleted() {
+ delegate_->InspectElementCompleted();
+}
+
+void DevToolsUIBindings::InspectedURLChanged(const std::string& url) {
+ content::NavigationController& controller = web_contents()->GetController();
+ content::NavigationEntry* entry = controller.GetActiveEntry();
+ // DevTools UI is not localized.
+ web_contents()->UpdateTitleForEntry(
+ entry, base::UTF8ToUTF16(base::StringPrintf(kTitleFormat, url.c_str())));
+}
+
+void DevToolsUIBindings::LoadNetworkResource(const DispatchCallback& callback,
+ const std::string& url,
+ const std::string& headers,
+ int stream_id) {
+ GURL gurl(url);
+ if (!gurl.is_valid()) {
+ base::DictionaryValue response;
+ response.SetInteger("statusCode", 404);
+ callback.Run(&response);
+ return;
+ }
+ // Create traffic annotation tag.
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("devtools_network_resource", R"(
+ semantics {
+ sender: "Developer Tools"
+ description:
+ "When user opens Developer Tools, the browser may fetch additional "
+ "resources from the network to enrich the debugging experience "
+ "(e.g. source map resources)."
+ trigger: "User opens Developer Tools to debug a web page."
+ data: "Any resources requested by Developer Tools."
+ destination: WEBSITE
+ }
+ policy {
+ cookies_allowed: true
+ cookies_store: "user"
+ setting:
+ "It's not possible to disable this feature from settings."
+ chrome_policy {
+ DeveloperToolsDisabled {
+ policy_options {mode: MANDATORY}
+ DeveloperToolsDisabled: true
+ }
+ }
+ })");
+
+ net::URLFetcher* fetcher = net::URLFetcher::Create(gurl, net::URLFetcher::GET,
+ this, traffic_annotation)
+ .release();
+ pending_requests_[fetcher] = callback;
+ fetcher->SetRequestContext(profile_->GetRequestContext());
+ fetcher->SetExtraRequestHeaders(headers);
+ fetcher->SaveResponseWithWriter(
+ std::unique_ptr<net::URLFetcherResponseWriter>(
+ new ResponseWriter(weak_factory_.GetWeakPtr(), stream_id)));
+ fetcher->Start();
+}
+
+void DevToolsUIBindings::OpenInNewTab(const std::string& url) {
+ delegate_->OpenInNewTab(url);
+}
+
+void DevToolsUIBindings::SaveToFile(const std::string& url,
+ const std::string& content,
+ bool save_as) {
+ file_helper_->Save(url, content, save_as,
+ base::Bind(&DevToolsUIBindings::FileSavedAs,
+ weak_factory_.GetWeakPtr(), url),
+ base::Bind(&DevToolsUIBindings::CanceledFileSaveAs,
+ weak_factory_.GetWeakPtr(), url));
+}
+
+void DevToolsUIBindings::AppendToFile(const std::string& url,
+ const std::string& content) {
+ file_helper_->Append(url, content,
+ base::Bind(&DevToolsUIBindings::AppendedTo,
+ weak_factory_.GetWeakPtr(), url));
+}
+
+void DevToolsUIBindings::RequestFileSystems() {
+ CHECK(IsValidFrontendURL(web_contents_->GetURL()) && frontend_host_);
+ std::vector<DevToolsFileHelper::FileSystem> file_systems =
+ file_helper_->GetFileSystems();
+ base::ListValue file_systems_value;
+ for (size_t i = 0; i < file_systems.size(); ++i)
+ file_systems_value.Append(CreateFileSystemValue(file_systems[i]));
+ CallClientFunction("DevToolsAPI.fileSystemsLoaded",
+ &file_systems_value, NULL, NULL);
+}
+
+void DevToolsUIBindings::AddFileSystem(const std::string& file_system_path) {
+ CHECK(IsValidFrontendURL(web_contents_->GetURL()) && frontend_host_);
+ file_helper_->AddFileSystem(
+ file_system_path,
+ base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar,
+ weak_factory_.GetWeakPtr()));
+}
+
+void DevToolsUIBindings::RemoveFileSystem(const std::string& file_system_path) {
+ CHECK(IsValidFrontendURL(web_contents_->GetURL()) && frontend_host_);
+ file_helper_->RemoveFileSystem(file_system_path);
+}
+
+void DevToolsUIBindings::UpgradeDraggedFileSystemPermissions(
+ const std::string& file_system_url) {
+ CHECK(IsValidFrontendURL(web_contents_->GetURL()) && frontend_host_);
+ file_helper_->UpgradeDraggedFileSystemPermissions(
+ file_system_url,
+ base::Bind(&DevToolsUIBindings::ShowDevToolsConfirmInfoBar,
+ weak_factory_.GetWeakPtr()));
+}
+
+void DevToolsUIBindings::IndexPath(int index_request_id,
+ const std::string& file_system_path) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ CHECK(IsValidFrontendURL(web_contents_->GetURL()) && frontend_host_);
+ if (!file_helper_->IsFileSystemAdded(file_system_path)) {
+ IndexingDone(index_request_id, file_system_path);
+ return;
+ }
+ if (indexing_jobs_.count(index_request_id) != 0)
+ return;
+ indexing_jobs_[index_request_id] =
+ scoped_refptr<DevToolsFileSystemIndexer::FileSystemIndexingJob>(
+ file_system_indexer_->IndexPath(
+ file_system_path,
+ Bind(&DevToolsUIBindings::IndexingTotalWorkCalculated,
+ weak_factory_.GetWeakPtr(),
+ index_request_id,
+ file_system_path),
+ Bind(&DevToolsUIBindings::IndexingWorked,
+ weak_factory_.GetWeakPtr(),
+ index_request_id,
+ file_system_path),
+ Bind(&DevToolsUIBindings::IndexingDone,
+ weak_factory_.GetWeakPtr(),
+ index_request_id,
+ file_system_path)));
+}
+
+void DevToolsUIBindings::StopIndexing(int index_request_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ IndexingJobsMap::iterator it = indexing_jobs_.find(index_request_id);
+ if (it == indexing_jobs_.end())
+ return;
+ it->second->Stop();
+ indexing_jobs_.erase(it);
+}
+
+void DevToolsUIBindings::SearchInPath(int search_request_id,
+ const std::string& file_system_path,
+ const std::string& query) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ CHECK(IsValidFrontendURL(web_contents_->GetURL()) && frontend_host_);
+ if (!file_helper_->IsFileSystemAdded(file_system_path)) {
+ SearchCompleted(search_request_id,
+ file_system_path,
+ std::vector<std::string>());
+ return;
+ }
+ file_system_indexer_->SearchInPath(file_system_path,
+ query,
+ Bind(&DevToolsUIBindings::SearchCompleted,
+ weak_factory_.GetWeakPtr(),
+ search_request_id,
+ file_system_path));
+}
+
+void DevToolsUIBindings::SetWhitelistedShortcuts(const std::string& message) {
+ delegate_->SetWhitelistedShortcuts(message);
+}
+
+void DevToolsUIBindings::SetEyeDropperActive(bool active) {
+ delegate_->SetEyeDropperActive(active);
+}
+
+void DevToolsUIBindings::ShowCertificateViewer(const std::string& cert_chain) {
+ std::unique_ptr<base::Value> value =
+ base::JSONReader::Read(cert_chain);
+ if (!value || value->GetType() != base::Value::Type::LIST) {
+ NOTREACHED();
+ return;
+ }
+
+ std::unique_ptr<base::ListValue> list =
+ base::ListValue::From(std::move(value));
+ std::vector<std::string> decoded;
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ base::Value* item;
+ if (!list->Get(i, &item) || item->GetType() != base::Value::Type::STRING) {
+ NOTREACHED();
+ return;
+ }
+ std::string temp;
+ if (!item->GetAsString(&temp)) {
+ NOTREACHED();
+ return;
+ }
+ if (!base::Base64Decode(temp, &temp)) {
+ NOTREACHED();
+ return;
+ }
+ decoded.push_back(temp);
+ }
+
+ std::vector<base::StringPiece> cert_string_piece;
+ for (const auto& str : decoded)
+ cert_string_piece.push_back(str);
+ scoped_refptr<net::X509Certificate> cert =
+ net::X509Certificate::CreateFromDERCertChain(cert_string_piece);
+ if (!cert) {
+ NOTREACHED();
+ return;
+ }
+
+ if (!agent_host_ || !agent_host_->GetWebContents())
+ return;
+ content::WebContents* inspected_wc = agent_host_->GetWebContents();
+ web_contents_->GetDelegate()->ShowCertificateViewerInDevTools(
+ inspected_wc, cert.get());
+}
+
+void DevToolsUIBindings::ZoomIn() {
+ zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_IN);
+}
+
+void DevToolsUIBindings::ZoomOut() {
+ zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_OUT);
+}
+
+void DevToolsUIBindings::ResetZoom() {
+ zoom::PageZoom::Zoom(web_contents(), content::PAGE_ZOOM_RESET);
+}
+
+void DevToolsUIBindings::SetDevicesDiscoveryConfig(
+ bool discover_usb_devices,
+ bool port_forwarding_enabled,
+ const std::string& port_forwarding_config,
+ bool network_discovery_enabled,
+ const std::string& network_discovery_config) {
+ base::DictionaryValue* port_forwarding_dict = nullptr;
+ std::unique_ptr<base::Value> parsed_port_forwarding =
+ base::JSONReader::Read(port_forwarding_config);
+ if (!parsed_port_forwarding ||
+ !parsed_port_forwarding->GetAsDictionary(&port_forwarding_dict)) {
+ return;
+ }
+
+ base::ListValue* network_list = nullptr;
+ std::unique_ptr<base::Value> parsed_network =
+ base::JSONReader::Read(network_discovery_config);
+ if (!parsed_network || !parsed_network->GetAsList(&network_list))
+ return;
+
+ profile_->GetPrefs()->SetBoolean(
+ prefs::kDevToolsDiscoverUsbDevicesEnabled, discover_usb_devices);
+ profile_->GetPrefs()->SetBoolean(
+ prefs::kDevToolsPortForwardingEnabled, port_forwarding_enabled);
+ profile_->GetPrefs()->Set(prefs::kDevToolsPortForwardingConfig,
+ *port_forwarding_dict);
+ profile_->GetPrefs()->SetBoolean(prefs::kDevToolsDiscoverTCPTargetsEnabled,
+ network_discovery_enabled);
+ profile_->GetPrefs()->Set(prefs::kDevToolsTCPDiscoveryConfig, *network_list);
+}
+
+void DevToolsUIBindings::DevicesDiscoveryConfigUpdated() {
+ base::DictionaryValue config;
+ config.Set(kConfigDiscoverUsbDevices,
+ profile_->GetPrefs()
+ ->FindPreference(prefs::kDevToolsDiscoverUsbDevicesEnabled)
+ ->GetValue()
+ ->CreateDeepCopy());
+ config.Set(kConfigPortForwardingEnabled,
+ profile_->GetPrefs()
+ ->FindPreference(prefs::kDevToolsPortForwardingEnabled)
+ ->GetValue()
+ ->CreateDeepCopy());
+ config.Set(kConfigPortForwardingConfig,
+ profile_->GetPrefs()
+ ->FindPreference(prefs::kDevToolsPortForwardingConfig)
+ ->GetValue()
+ ->CreateDeepCopy());
+ config.Set(kConfigNetworkDiscoveryEnabled,
+ profile_->GetPrefs()
+ ->FindPreference(prefs::kDevToolsDiscoverTCPTargetsEnabled)
+ ->GetValue()
+ ->CreateDeepCopy());
+ config.Set(kConfigNetworkDiscoveryConfig,
+ profile_->GetPrefs()
+ ->FindPreference(prefs::kDevToolsTCPDiscoveryConfig)
+ ->GetValue()
+ ->CreateDeepCopy());
+ CallClientFunction("DevToolsAPI.devicesDiscoveryConfigChanged", &config,
+ nullptr, nullptr);
+}
+
+void DevToolsUIBindings::SendPortForwardingStatus(const base::Value& status) {
+ CallClientFunction("DevToolsAPI.devicesPortForwardingStatusChanged", &status,
+ nullptr, nullptr);
+}
+
+void DevToolsUIBindings::SetDevicesUpdatesEnabled(bool enabled) {
+ if (devices_updates_enabled_ == enabled)
+ return;
+ devices_updates_enabled_ = enabled;
+ if (enabled) {
+ remote_targets_handler_ = DevToolsTargetsUIHandler::CreateForAdb(
+ base::Bind(&DevToolsUIBindings::DevicesUpdated,
+ base::Unretained(this)),
+ profile_);
+ pref_change_registrar_.Init(profile_->GetPrefs());
+ pref_change_registrar_.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled,
+ base::Bind(&DevToolsUIBindings::DevicesDiscoveryConfigUpdated,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled,
+ base::Bind(&DevToolsUIBindings::DevicesDiscoveryConfigUpdated,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig,
+ base::Bind(&DevToolsUIBindings::DevicesDiscoveryConfigUpdated,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(
+ prefs::kDevToolsDiscoverTCPTargetsEnabled,
+ base::Bind(&DevToolsUIBindings::DevicesDiscoveryConfigUpdated,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(
+ prefs::kDevToolsTCPDiscoveryConfig,
+ base::Bind(&DevToolsUIBindings::DevicesDiscoveryConfigUpdated,
+ base::Unretained(this)));
+ port_status_serializer_.reset(new PortForwardingStatusSerializer(
+ base::Bind(&DevToolsUIBindings::SendPortForwardingStatus,
+ base::Unretained(this)),
+ profile_));
+ DevicesDiscoveryConfigUpdated();
+ } else {
+ remote_targets_handler_.reset();
+ port_status_serializer_.reset();
+ pref_change_registrar_.RemoveAll();
+ SendPortForwardingStatus(base::DictionaryValue());
+ }
+}
+
+void DevToolsUIBindings::PerformActionOnRemotePage(const std::string& page_id,
+ const std::string& action) {
+ if (!remote_targets_handler_)
+ return;
+ scoped_refptr<content::DevToolsAgentHost> host =
+ remote_targets_handler_->GetTarget(page_id);
+ if (!host)
+ return;
+ if (action == kRemotePageActionInspect)
+ delegate_->Inspect(host);
+ else if (action == kRemotePageActionReload)
+ host->Reload();
+ else if (action == kRemotePageActionActivate)
+ host->Activate();
+ else if (action == kRemotePageActionClose)
+ host->Close();
+}
+
+void DevToolsUIBindings::OpenRemotePage(const std::string& browser_id,
+ const std::string& url) {
+ if (!remote_targets_handler_)
+ return;
+ remote_targets_handler_->Open(browser_id, url);
+}
+
+void DevToolsUIBindings::OpenNodeFrontend() {
+ delegate_->OpenNodeFrontend();
+}
+
+void DevToolsUIBindings::GetPreferences(const DispatchCallback& callback) {
+ const DictionaryValue* prefs =
+ profile_->GetPrefs()->GetDictionary(prefs::kDevToolsPreferences);
+ callback.Run(prefs);
+}
+
+void DevToolsUIBindings::SetPreference(const std::string& name,
+ const std::string& value) {
+ DictionaryPrefUpdate update(profile_->GetPrefs(),
+ prefs::kDevToolsPreferences);
+ update.Get()->SetStringWithoutPathExpansion(name, value);
+}
+
+void DevToolsUIBindings::RemovePreference(const std::string& name) {
+ DictionaryPrefUpdate update(profile_->GetPrefs(),
+ prefs::kDevToolsPreferences);
+ update.Get()->RemoveWithoutPathExpansion(name, nullptr);
+}
+
+void DevToolsUIBindings::ClearPreferences() {
+ DictionaryPrefUpdate update(profile_->GetPrefs(),
+ prefs::kDevToolsPreferences);
+ update.Get()->Clear();
+}
+
+void DevToolsUIBindings::Reattach(const DispatchCallback& callback) {
+ if (agent_host_.get()) {
+ agent_host_->DetachClient(this);
+ agent_host_->AttachClient(this);
+ }
+ callback.Run(nullptr);
+}
+
+void DevToolsUIBindings::ReadyForTest() {
+ delegate_->ReadyForTest();
+}
+
+void DevToolsUIBindings::DispatchProtocolMessageFromDevToolsFrontend(
+ const std::string& message) {
+ if (agent_host_.get())
+ agent_host_->DispatchProtocolMessage(this, message);
+}
+
+void DevToolsUIBindings::RecordEnumeratedHistogram(const std::string& name,
+ int sample,
+ int boundary_value) {
+ if (!frontend_host_)
+ return;
+ if (!(boundary_value >= 0 && boundary_value <= 100 && sample >= 0 &&
+ sample < boundary_value)) {
+ // TODO(nick): Replace with chrome::bad_message::ReceivedBadMessage().
+ frontend_host_->BadMessageRecieved();
+ return;
+ }
+ // Each histogram name must follow a different code path in
+ // order to UMA_HISTOGRAM_EXACT_LINEAR work correctly.
+ if (name == kDevToolsActionTakenHistogram)
+ UMA_HISTOGRAM_EXACT_LINEAR(name, sample, boundary_value);
+ else if (name == kDevToolsPanelShownHistogram)
+ UMA_HISTOGRAM_EXACT_LINEAR(name, sample, boundary_value);
+ else
+ frontend_host_->BadMessageRecieved();
+}
+
+void DevToolsUIBindings::SendJsonRequest(const DispatchCallback& callback,
+ const std::string& browser_id,
+ const std::string& url) {
+ if (!android_bridge_) {
+ callback.Run(nullptr);
+ return;
+ }
+ android_bridge_->SendJsonRequest(browser_id, url,
+ base::Bind(&DevToolsUIBindings::JsonReceived,
+ weak_factory_.GetWeakPtr(),
+ callback));
+}
+
+void DevToolsUIBindings::JsonReceived(const DispatchCallback& callback,
+ int result,
+ const std::string& message) {
+ if (result != net::OK) {
+ callback.Run(nullptr);
+ return;
+ }
+ base::Value message_value(message);
+ callback.Run(&message_value);
+}
+
+void DevToolsUIBindings::OnURLFetchComplete(const net::URLFetcher* source) {
+ DCHECK(source);
+ PendingRequestsMap::iterator it = pending_requests_.find(source);
+ DCHECK(it != pending_requests_.end());
+
+ base::DictionaryValue response;
+ auto headers = base::MakeUnique<base::DictionaryValue>();
+ net::HttpResponseHeaders* rh = source->GetResponseHeaders();
+ response.SetInteger("statusCode", rh ? rh->response_code() : 200);
+
+ size_t iterator = 0;
+ std::string name;
+ std::string value;
+ while (rh && rh->EnumerateHeaderLines(&iterator, &name, &value))
+ headers->SetString(name, value);
+
+ response.Set("headers", std::move(headers));
+ it->second.Run(&response);
+ pending_requests_.erase(it);
+ delete source;
+}
+
+void DevToolsUIBindings::DeviceCountChanged(int count) {
+ base::Value value(count);
+ CallClientFunction("DevToolsAPI.deviceCountUpdated", &value, NULL,
+ NULL);
+}
+
+void DevToolsUIBindings::DevicesUpdated(
+ const std::string& source,
+ const base::ListValue& targets) {
+ CallClientFunction("DevToolsAPI.devicesUpdated", &targets, NULL,
+ NULL);
+}
+
+void DevToolsUIBindings::FileSavedAs(const std::string& url) {
+ base::Value url_value(url);
+ CallClientFunction("DevToolsAPI.savedURL", &url_value, NULL, NULL);
+}
+
+void DevToolsUIBindings::CanceledFileSaveAs(const std::string& url) {
+ base::Value url_value(url);
+ CallClientFunction("DevToolsAPI.canceledSaveURL",
+ &url_value, NULL, NULL);
+}
+
+void DevToolsUIBindings::AppendedTo(const std::string& url) {
+ base::Value url_value(url);
+ CallClientFunction("DevToolsAPI.appendedToURL", &url_value, NULL,
+ NULL);
+}
+
+void DevToolsUIBindings::FileSystemAdded(
+ const DevToolsFileHelper::FileSystem& file_system) {
+ std::unique_ptr<base::DictionaryValue> file_system_value(
+ CreateFileSystemValue(file_system));
+ CallClientFunction("DevToolsAPI.fileSystemAdded",
+ file_system_value.get(), NULL, NULL);
+}
+
+void DevToolsUIBindings::FileSystemRemoved(
+ const std::string& file_system_path) {
+ base::Value file_system_path_value(file_system_path);
+ CallClientFunction("DevToolsAPI.fileSystemRemoved",
+ &file_system_path_value, NULL, NULL);
+}
+
+void DevToolsUIBindings::FilePathsChanged(
+ const std::vector<std::string>& changed_paths,
+ const std::vector<std::string>& added_paths,
+ const std::vector<std::string>& removed_paths) {
+ base::ListValue changed, added, removed;
+ changed.AppendStrings(changed_paths);
+ added.AppendStrings(added_paths);
+ removed.AppendStrings(removed_paths);
+
+ CallClientFunction("DevToolsAPI.fileSystemFilesChangedAddedRemoved", &changed,
+ &added, &removed);
+}
+
+void DevToolsUIBindings::IndexingTotalWorkCalculated(
+ int request_id,
+ const std::string& file_system_path,
+ int total_work) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ base::Value request_id_value(request_id);
+ base::Value file_system_path_value(file_system_path);
+ base::Value total_work_value(total_work);
+ CallClientFunction("DevToolsAPI.indexingTotalWorkCalculated",
+ &request_id_value, &file_system_path_value,
+ &total_work_value);
+}
+
+void DevToolsUIBindings::IndexingWorked(int request_id,
+ const std::string& file_system_path,
+ int worked) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ base::Value request_id_value(request_id);
+ base::Value file_system_path_value(file_system_path);
+ base::Value worked_value(worked);
+ CallClientFunction("DevToolsAPI.indexingWorked", &request_id_value,
+ &file_system_path_value, &worked_value);
+}
+
+void DevToolsUIBindings::IndexingDone(int request_id,
+ const std::string& file_system_path) {
+ indexing_jobs_.erase(request_id);
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ base::Value request_id_value(request_id);
+ base::Value file_system_path_value(file_system_path);
+ CallClientFunction("DevToolsAPI.indexingDone", &request_id_value,
+ &file_system_path_value, NULL);
+}
+
+void DevToolsUIBindings::SearchCompleted(
+ int request_id,
+ const std::string& file_system_path,
+ const std::vector<std::string>& file_paths) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ base::ListValue file_paths_value;
+ for (std::vector<std::string>::const_iterator it(file_paths.begin());
+ it != file_paths.end(); ++it) {
+ file_paths_value.AppendString(*it);
+ }
+ base::Value request_id_value(request_id);
+ base::Value file_system_path_value(file_system_path);
+ CallClientFunction("DevToolsAPI.searchCompleted", &request_id_value,
+ &file_system_path_value, &file_paths_value);
+}
+
+void DevToolsUIBindings::ShowDevToolsConfirmInfoBar(
+ const base::string16& message,
+ const InfoBarCallback& callback) {
+ if (!delegate_->GetInfoBarService()) {
+ callback.Run(false);
+ return;
+ }
+ std::unique_ptr<DevToolsConfirmInfoBarDelegate> delegate(
+ new DevToolsConfirmInfoBarDelegate(callback, message));
+ GlobalConfirmInfoBar::Show(std::move(delegate));
+}
+
+void DevToolsUIBindings::UpdateFrontendHost(
+ content::NavigationHandle* navigation_handle) {
+ if (!IsValidFrontendURL(navigation_handle->GetURL())) {
+ LOG(ERROR) << "Attempt to navigate to an invalid DevTools front-end URL: "
+ << navigation_handle->GetURL().spec();
+ frontend_host_.reset();
+ return;
+ }
+ frontend_host_.reset(content::DevToolsFrontendHost::Create(
+ navigation_handle->GetRenderFrameHost(),
+ base::Bind(&DevToolsUIBindings::HandleMessageFromDevToolsFrontend,
+ base::Unretained(this))));
+}
+
+void DevToolsUIBindings::AddDevToolsExtensionsToClient() {
+ const extensions::ExtensionRegistry* registry =
+ extensions::ExtensionRegistry::Get(profile_->GetOriginalProfile());
+ if (!registry)
+ return;
+
+ base::ListValue results;
+ for (const scoped_refptr<const extensions::Extension>& extension :
+ registry->enabled_extensions()) {
+ if (extensions::chrome_manifest_urls::GetDevToolsPage(extension.get())
+ .is_empty())
+ continue;
+
+ // Each devtools extension will need to be able to run in the devtools
+ // process. Grant each specific extension's origin permission to load
+ // documents.
+ content::ChildProcessSecurityPolicy::GetInstance()->GrantOrigin(
+ web_contents_->GetMainFrame()->GetProcess()->GetID(),
+ url::Origin(extension->url()));
+
+ std::unique_ptr<base::DictionaryValue> extension_info(
+ new base::DictionaryValue());
+ extension_info->SetString(
+ "startPage",
+ extensions::chrome_manifest_urls::GetDevToolsPage(extension.get())
+ .spec());
+ extension_info->SetString("name", extension->name());
+ extension_info->SetBoolean("exposeExperimentalAPIs",
+ extension->permissions_data()->HasAPIPermission(
+ extensions::APIPermission::kExperimental));
+ results.Append(std::move(extension_info));
+ }
+
+ CallClientFunction("DevToolsAPI.addExtensions",
+ &results, NULL, NULL);
+}
+
+void DevToolsUIBindings::SetDelegate(Delegate* delegate) {
+ delegate_.reset(delegate);
+}
+
+void DevToolsUIBindings::AttachTo(
+ const scoped_refptr<content::DevToolsAgentHost>& agent_host) {
+ if (agent_host_.get())
+ Detach();
+ agent_host_ = agent_host;
+ // DevToolsUIBindings terminates existing debugging connections and starts
+ // debugging.
+ agent_host_->ForceAttachClient(this);
+}
+
+void DevToolsUIBindings::Reload() {
+ reloading_ = true;
+ web_contents_->GetController().Reload(content::ReloadType::NORMAL, false);
+}
+
+void DevToolsUIBindings::Detach() {
+ if (agent_host_.get())
+ agent_host_->DetachClient(this);
+ agent_host_ = NULL;
+}
+
+bool DevToolsUIBindings::IsAttachedTo(content::DevToolsAgentHost* agent_host) {
+ return agent_host_.get() == agent_host;
+}
+
+void DevToolsUIBindings::CallClientFunction(const std::string& function_name,
+ const base::Value* arg1,
+ const base::Value* arg2,
+ const base::Value* arg3) {
+ // If we're not exposing bindings, we shouldn't call functions either.
+ if (!frontend_host_)
+ return;
+ std::string javascript = function_name + "(";
+ if (arg1) {
+ std::string json;
+ base::JSONWriter::Write(*arg1, &json);
+ javascript.append(json);
+ if (arg2) {
+ base::JSONWriter::Write(*arg2, &json);
+ javascript.append(", ").append(json);
+ if (arg3) {
+ base::JSONWriter::Write(*arg3, &json);
+ javascript.append(", ").append(json);
+ }
+ }
+ }
+ javascript.append(");");
+ web_contents_->GetMainFrame()->ExecuteJavaScript(
+ base::UTF8ToUTF16(javascript));
+}
+
+void DevToolsUIBindings::DocumentAvailableInMainFrame() {
+ if (!reloading_)
+ return;
+ reloading_ = false;
+ if (agent_host_.get()) {
+ agent_host_->DetachClient(this);
+ agent_host_->AttachClient(this);
+ }
+}
+
+void DevToolsUIBindings::DocumentOnLoadCompletedInMainFrame() {
+ // In the DEBUG_DEVTOOLS mode, the DocumentOnLoadCompletedInMainFrame event
+ // arrives before the LoadCompleted event, thus it should not trigger the
+ // frontend load handling.
+#if !BUILDFLAG(DEBUG_DEVTOOLS)
+ FrontendLoaded();
+#endif
+}
+
+void DevToolsUIBindings::DidNavigateMainFrame() {
+ frontend_loaded_ = false;
+}
+
+void DevToolsUIBindings::FrontendLoaded() {
+ if (frontend_loaded_)
+ return;
+ frontend_loaded_ = true;
+
+ // Call delegate first - it seeds importants bit of information.
+ delegate_->OnLoadCompleted();
+
+ AddDevToolsExtensionsToClient();
+}
diff --git a/chromium/chrome/browser/devtools/devtools_ui_bindings.h b/chromium/chrome/browser/devtools/devtools_ui_bindings.h
new file mode 100644
index 00000000000..46e94ed4d55
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_ui_bindings.h
@@ -0,0 +1,249 @@
+// 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_DEVTOOLS_DEVTOOLS_UI_BINDINGS_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_UI_BINDINGS_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+#include "chrome/browser/devtools/device/devtools_android_bridge.h"
+#include "chrome/browser/devtools/devtools_embedder_message_dispatcher.h"
+#include "chrome/browser/devtools/devtools_file_helper.h"
+#include "chrome/browser/devtools/devtools_file_system_indexer.h"
+#include "chrome/browser/devtools/devtools_targets_ui.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/devtools_frontend_host.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "ui/gfx/geometry/size.h"
+
+class DevToolsAndroidBridge;
+class InfoBarService;
+class Profile;
+class PortForwardingStatusSerializer;
+
+namespace content {
+class NavigationHandle;
+class WebContents;
+}
+
+// Base implementation of DevTools bindings around front-end.
+class DevToolsUIBindings : public DevToolsEmbedderMessageDispatcher::Delegate,
+ public DevToolsAndroidBridge::DeviceCountListener,
+ public content::DevToolsAgentHostClient,
+ public net::URLFetcherDelegate,
+ public DevToolsFileHelper::Delegate {
+ public:
+ static DevToolsUIBindings* ForWebContents(
+ content::WebContents* web_contents);
+
+ static GURL SanitizeFrontendURL(const GURL& url);
+ static bool IsValidFrontendURL(const GURL& url);
+
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+ virtual void ActivateWindow() = 0;
+ virtual void CloseWindow() = 0;
+ virtual void Inspect(scoped_refptr<content::DevToolsAgentHost> host) = 0;
+ virtual void SetInspectedPageBounds(const gfx::Rect& rect) = 0;
+ virtual void InspectElementCompleted() = 0;
+ virtual void SetIsDocked(bool is_docked) = 0;
+ virtual void OpenInNewTab(const std::string& url) = 0;
+ virtual void SetWhitelistedShortcuts(const std::string& message) = 0;
+ virtual void SetEyeDropperActive(bool active) = 0;
+ virtual void OpenNodeFrontend() = 0;
+
+ virtual void InspectedContentsClosing() = 0;
+ virtual void OnLoadCompleted() = 0;
+ virtual void ReadyForTest() = 0;
+ virtual InfoBarService* GetInfoBarService() = 0;
+ virtual void RenderProcessGone(bool crashed) = 0;
+ };
+
+ explicit DevToolsUIBindings(content::WebContents* web_contents);
+ ~DevToolsUIBindings() override;
+
+ content::WebContents* web_contents() { return web_contents_; }
+ Profile* profile() { return profile_; }
+ content::DevToolsAgentHost* agent_host() { return agent_host_.get(); }
+
+ // Takes ownership over the |delegate|.
+ void SetDelegate(Delegate* delegate);
+ void CallClientFunction(const std::string& function_name,
+ const base::Value* arg1,
+ const base::Value* arg2,
+ const base::Value* arg3);
+ void AttachTo(const scoped_refptr<content::DevToolsAgentHost>& agent_host);
+ void Reload();
+ void Detach();
+ bool IsAttachedTo(content::DevToolsAgentHost* agent_host);
+
+ private:
+ void HandleMessageFromDevToolsFrontend(const std::string& message);
+
+ // content::DevToolsAgentHostClient implementation.
+ void DispatchProtocolMessage(content::DevToolsAgentHost* agent_host,
+ const std::string& message) override;
+ void AgentHostClosed(content::DevToolsAgentHost* agent_host,
+ bool replaced_with_another_client) override;
+
+ // DevToolsEmbedderMessageDispatcher::Delegate implementation.
+ void ActivateWindow() override;
+ void CloseWindow() override;
+ void LoadCompleted() override;
+ void SetInspectedPageBounds(const gfx::Rect& rect) override;
+ void InspectElementCompleted() override;
+ void InspectedURLChanged(const std::string& url) override;
+ void LoadNetworkResource(const DispatchCallback& callback,
+ const std::string& url,
+ const std::string& headers,
+ int stream_id) override;
+ void SetIsDocked(const DispatchCallback& callback, bool is_docked) override;
+ void OpenInNewTab(const std::string& url) override;
+ void SaveToFile(const std::string& url,
+ const std::string& content,
+ bool save_as) override;
+ void AppendToFile(const std::string& url,
+ const std::string& content) override;
+ void RequestFileSystems() override;
+ void AddFileSystem(const std::string& file_system_path) override;
+ void RemoveFileSystem(const std::string& file_system_path) override;
+ void UpgradeDraggedFileSystemPermissions(
+ const std::string& file_system_url) override;
+ void IndexPath(int index_request_id,
+ const std::string& file_system_path) override;
+ void StopIndexing(int index_request_id) override;
+ void SearchInPath(int search_request_id,
+ const std::string& file_system_path,
+ const std::string& query) override;
+ void SetWhitelistedShortcuts(const std::string& message) override;
+ void SetEyeDropperActive(bool active) override;
+ void ShowCertificateViewer(const std::string& cert_chain) override;
+ void ZoomIn() override;
+ void ZoomOut() override;
+ void ResetZoom() override;
+ void SetDevicesDiscoveryConfig(
+ bool discover_usb_devices,
+ bool port_forwarding_enabled,
+ const std::string& port_forwarding_config,
+ bool network_discovery_enabled,
+ const std::string& network_discovery_config) override;
+ void SetDevicesUpdatesEnabled(bool enabled) override;
+ void PerformActionOnRemotePage(const std::string& page_id,
+ const std::string& action) override;
+ void OpenRemotePage(const std::string& browser_id,
+ const std::string& url) override;
+ void OpenNodeFrontend() override;
+ void DispatchProtocolMessageFromDevToolsFrontend(
+ const std::string& message) override;
+ void RecordEnumeratedHistogram(const std::string& name,
+ int sample,
+ int boundary_value) override;
+ void SendJsonRequest(const DispatchCallback& callback,
+ const std::string& browser_id,
+ const std::string& url) override;
+ void GetPreferences(const DispatchCallback& callback) override;
+ void SetPreference(const std::string& name,
+ const std::string& value) override;
+ void RemovePreference(const std::string& name) override;
+ void ClearPreferences() override;
+ void Reattach(const DispatchCallback& callback) override;
+ void ReadyForTest() override;
+
+ // net::URLFetcherDelegate overrides.
+ void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+ void EnableRemoteDeviceCounter(bool enable);
+
+ void SendMessageAck(int request_id,
+ const base::Value* arg1);
+
+ // DevToolsAndroidBridge::DeviceCountListener override:
+ void DeviceCountChanged(int count) override;
+
+ // Forwards discovered devices to frontend.
+ virtual void DevicesUpdated(const std::string& source,
+ const base::ListValue& targets);
+
+ void DocumentAvailableInMainFrame();
+ void DocumentOnLoadCompletedInMainFrame();
+ void DidNavigateMainFrame();
+ void FrontendLoaded();
+
+ void JsonReceived(const DispatchCallback& callback,
+ int result,
+ const std::string& message);
+ void DevicesDiscoveryConfigUpdated();
+ void SendPortForwardingStatus(const base::Value& status);
+
+ // DevToolsFileHelper::Delegate overrides.
+ void FileSystemAdded(
+ const DevToolsFileHelper::FileSystem& file_system) override;
+ void FileSystemRemoved(const std::string& file_system_path) override;
+ void FilePathsChanged(const std::vector<std::string>& changed_paths,
+ const std::vector<std::string>& added_paths,
+ const std::vector<std::string>& removed_paths) override;
+
+ // DevToolsFileHelper callbacks.
+ void FileSavedAs(const std::string& url);
+ void CanceledFileSaveAs(const std::string& url);
+ void AppendedTo(const std::string& url);
+ void IndexingTotalWorkCalculated(int request_id,
+ const std::string& file_system_path,
+ int total_work);
+ void IndexingWorked(int request_id,
+ const std::string& file_system_path,
+ int worked);
+ void IndexingDone(int request_id, const std::string& file_system_path);
+ void SearchCompleted(int request_id,
+ const std::string& file_system_path,
+ const std::vector<std::string>& file_paths);
+ typedef base::Callback<void(bool)> InfoBarCallback;
+ void ShowDevToolsConfirmInfoBar(const base::string16& message,
+ const InfoBarCallback& callback);
+ void UpdateFrontendHost(content::NavigationHandle* navigation_handle);
+
+ // Extensions support.
+ void AddDevToolsExtensionsToClient();
+
+ class FrontendWebContentsObserver;
+ std::unique_ptr<FrontendWebContentsObserver> frontend_contents_observer_;
+
+ Profile* profile_;
+ DevToolsAndroidBridge* android_bridge_;
+ content::WebContents* web_contents_;
+ std::unique_ptr<Delegate> delegate_;
+ scoped_refptr<content::DevToolsAgentHost> agent_host_;
+ std::unique_ptr<content::DevToolsFrontendHost> frontend_host_;
+ std::unique_ptr<DevToolsFileHelper> file_helper_;
+ scoped_refptr<DevToolsFileSystemIndexer> file_system_indexer_;
+ typedef std::map<
+ int,
+ scoped_refptr<DevToolsFileSystemIndexer::FileSystemIndexingJob> >
+ IndexingJobsMap;
+ IndexingJobsMap indexing_jobs_;
+
+ bool devices_updates_enabled_;
+ bool frontend_loaded_;
+ bool reloading_;
+ std::unique_ptr<DevToolsTargetsUIHandler> remote_targets_handler_;
+ std::unique_ptr<PortForwardingStatusSerializer> port_status_serializer_;
+ PrefChangeRegistrar pref_change_registrar_;
+ std::unique_ptr<DevToolsEmbedderMessageDispatcher>
+ embedder_message_dispatcher_;
+ GURL url_;
+ using PendingRequestsMap = std::map<const net::URLFetcher*, DispatchCallback>;
+ PendingRequestsMap pending_requests_;
+ base::WeakPtrFactory<DevToolsUIBindings> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsUIBindings);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_UI_BINDINGS_H_
diff --git a/chromium/chrome/browser/devtools/devtools_ui_bindings_unittest.cc b/chromium/chrome/browser/devtools/devtools_ui_bindings_unittest.cc
new file mode 100644
index 00000000000..f1754a328e2
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_ui_bindings_unittest.cc
@@ -0,0 +1,102 @@
+// 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 "chrome/browser/devtools/devtools_ui_bindings.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class DevToolsUIBindingsTest : public testing::Test {
+};
+
+TEST_F(DevToolsUIBindingsTest, SanitizeFrontendURL) {
+ std::vector<std::pair<std::string, std::string>> tests = {
+ {"random-string",
+ "chrome-devtools://devtools/"},
+ {"http://valid.url/but/wrong",
+ "chrome-devtools://devtools/but/wrong"},
+ {"chrome-devtools://wrong-domain/",
+ "chrome-devtools://devtools/"},
+ {"chrome-devtools://devtools/bundled/devtools.html",
+ "chrome-devtools://devtools/bundled/devtools.html"},
+ {"chrome-devtools://devtools:1234/bundled/devtools.html#hash",
+ "chrome-devtools://devtools/bundled/devtools.html"},
+ {"chrome-devtools://devtools/some/random/path",
+ "chrome-devtools://devtools/some/random/path"},
+ {"chrome-devtools://devtools/bundled/devtools.html?experiments=true",
+ "chrome-devtools://devtools/bundled/devtools.html?experiments=true"},
+ {"chrome-devtools://devtools/bundled/devtools.html"
+ "?some-flag=flag&v8only=true&experiments=false&debugFrontend=a"
+ "&another-flag=another-flag&can_dock=false&isSharedWorker=notreally"
+ "&remoteFrontend=sure",
+ "chrome-devtools://devtools/bundled/devtools.html"
+ "?v8only=true&experiments=true&debugFrontend=true"
+ "&can_dock=true&isSharedWorker=true&remoteFrontend=true"},
+ {"chrome-devtools://devtools/?ws=any-value-is-fine",
+ "chrome-devtools://devtools/?ws=any-value-is-fine"},
+ {"chrome-devtools://devtools/"
+ "?service-backend=ws://localhost:9222/services",
+ "chrome-devtools://devtools/"
+ "?service-backend=ws://localhost:9222/services"},
+ {"chrome-devtools://devtools/?dockSide=undocked",
+ "chrome-devtools://devtools/?dockSide=undocked"},
+ {"chrome-devtools://devtools/?dockSide=dock-to-bottom",
+ "chrome-devtools://devtools/"},
+ {"chrome-devtools://devtools/?dockSide=bottom",
+ "chrome-devtools://devtools/"},
+ {"chrome-devtools://devtools/?remoteBase="
+ "http://example.com:1234/remote-base#hash",
+ "chrome-devtools://devtools/?remoteBase="
+ "https://chrome-devtools-frontend.appspot.com/"
+ "serve_file//"},
+ {"chrome-devtools://devtools/?ws=1%26evil%3dtrue",
+ "chrome-devtools://devtools/?ws=1%26evil%3dtrue"},
+ {"chrome-devtools://devtools/?remoteBase="
+ "https://chrome-devtools-frontend.appspot.com/some/path/"
+ "@123719741873/more/path.html",
+ "chrome-devtools://devtools/?remoteBase="
+ "https://chrome-devtools-frontend.appspot.com/serve_file/path/"},
+ {"chrome-devtools://devtools/?remoteBase="
+ "https://chrome-devtools-frontend.appspot.com/serve_file/"
+ "@123719741873/inspector.html%3FdebugFrontend%3Dfalse",
+ "chrome-devtools://devtools/?remoteBase="
+ "https://chrome-devtools-frontend.appspot.com/serve_file/"
+ "@123719741873/"},
+ {"chrome-devtools://devtools/bundled/inspector.html?"
+ "&remoteBase=https://chrome-devtools-frontend.appspot.com/serve_file/"
+ "@b4907cc5d602ff470740b2eb6344b517edecb7b9/&can_dock=true",
+ "chrome-devtools://devtools/bundled/inspector.html?"
+ "remoteBase=https://chrome-devtools-frontend.appspot.com/serve_file/"
+ "@b4907cc5d602ff470740b2eb6344b517edecb7b9/&can_dock=true"},
+ {"chrome-devtools://devtools/?remoteFrontendUrl="
+ "https://chrome-devtools-frontend.appspot.com/serve_rev/"
+ "@12345/inspector.html%3FdebugFrontend%3Dfalse",
+ "chrome-devtools://devtools/?remoteFrontendUrl="
+ "https%3A%2F%2Fchrome-devtools-frontend.appspot.com%2Fserve_rev"
+ "%2F%4012345%2Finspector.html%3FdebugFrontend%3Dtrue"},
+ {"chrome-devtools://devtools/?remoteFrontendUrl="
+ "https://chrome-devtools-frontend.appspot.com/serve_rev/"
+ "@12345/inspector.html%22></iframe>something",
+ "chrome-devtools://devtools/?remoteFrontendUrl="
+ "https%3A%2F%2Fchrome-devtools-frontend.appspot.com%2Fserve_rev"
+ "%2F%4012345%2Finspector.html"},
+ {"chrome-devtools://devtools/?remoteFrontendUrl="
+ "http://domain:1234/path/rev/a/filename.html%3Fparam%3Dvalue#hash",
+ "chrome-devtools://devtools/?remoteFrontendUrl="
+ "https%3A%2F%2Fchrome-devtools-frontend.appspot.com%2Fserve_rev"
+ "%2Frev%2Finspector.html"},
+ {"chrome-devtools://devtools/?experiments=whatever&remoteFrontendUrl="
+ "https://chrome-devtools-frontend.appspot.com/serve_rev/"
+ "@12345/devtools.html%3Fws%3Danyvalue%26experiments%3Dlikely"
+ "&unencoded=value&debugFrontend=true",
+ "chrome-devtools://devtools/?experiments=true&remoteFrontendUrl="
+ "https%3A%2F%2Fchrome-devtools-frontend.appspot.com%2Fserve_rev"
+ "%2F%4012345%2Fdevtools.html%3Fws%3Danyvalue%26experiments%3Dtrue"
+ "&debugFrontend=true"},
+ };
+
+ for (const auto& pair : tests) {
+ GURL url = GURL(pair.first);
+ url = DevToolsUIBindings::SanitizeFrontendURL(url);
+ EXPECT_EQ(pair.second, url.spec());
+ }
+}
diff --git a/chromium/chrome/browser/devtools/devtools_window.cc b/chromium/chrome/browser/devtools/devtools_window.cc
new file mode 100644
index 00000000000..0ba79b03613
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_window.cc
@@ -0,0 +1,1447 @@
+// 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/devtools/devtools_window.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/json/json_reader.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/certificate_viewer.h"
+#include "chrome/browser/data_use_measurement/data_use_web_contents_observer.h"
+#include "chrome/browser/devtools/devtools_eye_dropper.h"
+#include "chrome/browser/file_select_helper.h"
+#include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sessions/session_tab_helper.h"
+#include "chrome/browser/task_manager/web_contents_tags.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_dialogs.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/prefs/prefs_tab_helper.h"
+#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/devtools_ui.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "components/app_modal/javascript_dialog_manager.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "components/sync_preferences/pref_service_syncable.h"
+#include "components/zoom/page_zoom.h"
+#include "components/zoom/zoom_controller.h"
+#include "content/public/browser/browser_thread.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"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/url_constants.h"
+#include "net/base/escape.h"
+#include "third_party/WebKit/public/platform/WebGestureEvent.h"
+#include "third_party/WebKit/public/platform/WebInputEvent.h"
+#include "third_party/WebKit/public/public_features.h"
+#include "ui/base/page_transition_types.h"
+#include "ui/events/keycodes/dom/keycode_converter.h"
+#include "ui/events/keycodes/keyboard_code_conversion.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+
+using base::DictionaryValue;
+using blink::WebInputEvent;
+using content::BrowserThread;
+using content::DevToolsAgentHost;
+using content::WebContents;
+
+namespace {
+
+typedef std::vector<DevToolsWindow*> DevToolsWindows;
+base::LazyInstance<DevToolsWindows>::Leaky g_instances =
+ LAZY_INSTANCE_INITIALIZER;
+
+base::LazyInstance<std::vector<base::Callback<void(DevToolsWindow*)>>>::Leaky
+ g_creation_callbacks = LAZY_INSTANCE_INITIALIZER;
+
+static const char kKeyUpEventName[] = "keyup";
+static const char kKeyDownEventName[] = "keydown";
+
+bool FindInspectedBrowserAndTabIndex(
+ WebContents* inspected_web_contents, Browser** browser, int* tab) {
+ if (!inspected_web_contents)
+ return false;
+
+ for (auto* b : *BrowserList::GetInstance()) {
+ int tab_index =
+ b->tab_strip_model()->GetIndexOfWebContents(inspected_web_contents);
+ if (tab_index != TabStripModel::kNoTab) {
+ *browser = b;
+ *tab = tab_index;
+ return true;
+ }
+ }
+ return false;
+}
+
+void SetPreferencesFromJson(Profile* profile, const std::string& json) {
+ base::DictionaryValue* dict = nullptr;
+ std::unique_ptr<base::Value> parsed = base::JSONReader::Read(json);
+ if (!parsed || !parsed->GetAsDictionary(&dict))
+ return;
+ DictionaryPrefUpdate update(profile->GetPrefs(), prefs::kDevToolsPreferences);
+ for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
+ if (!it.value().IsType(base::Value::Type::STRING))
+ continue;
+ update.Get()->SetWithoutPathExpansion(
+ it.key(), it.value().CreateDeepCopy());
+ }
+}
+
+// DevToolsToolboxDelegate ----------------------------------------------------
+
+class DevToolsToolboxDelegate
+ : public content::WebContentsObserver,
+ public content::WebContentsDelegate {
+ public:
+ DevToolsToolboxDelegate(
+ WebContents* toolbox_contents,
+ DevToolsWindow::ObserverWithAccessor* web_contents_observer);
+ ~DevToolsToolboxDelegate() override;
+
+ content::WebContents* OpenURLFromTab(
+ content::WebContents* source,
+ const content::OpenURLParams& params) override;
+ content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
+ content::WebContents* source,
+ const content::NativeWebKeyboardEvent& event) override;
+ void HandleKeyboardEvent(
+ content::WebContents* source,
+ const content::NativeWebKeyboardEvent& event) override;
+ void WebContentsDestroyed() override;
+
+ private:
+ BrowserWindow* GetInspectedBrowserWindow();
+ DevToolsWindow::ObserverWithAccessor* inspected_contents_observer_;
+ DISALLOW_COPY_AND_ASSIGN(DevToolsToolboxDelegate);
+};
+
+DevToolsToolboxDelegate::DevToolsToolboxDelegate(
+ WebContents* toolbox_contents,
+ DevToolsWindow::ObserverWithAccessor* web_contents_observer)
+ : WebContentsObserver(toolbox_contents),
+ inspected_contents_observer_(web_contents_observer) {
+}
+
+DevToolsToolboxDelegate::~DevToolsToolboxDelegate() {
+}
+
+content::WebContents* DevToolsToolboxDelegate::OpenURLFromTab(
+ content::WebContents* source,
+ const content::OpenURLParams& params) {
+ DCHECK(source == web_contents());
+ if (!params.url.SchemeIs(content::kChromeDevToolsScheme))
+ return NULL;
+ content::NavigationController::LoadURLParams load_url_params(params.url);
+ source->GetController().LoadURLWithParams(load_url_params);
+ return source;
+}
+
+content::KeyboardEventProcessingResult
+DevToolsToolboxDelegate::PreHandleKeyboardEvent(
+ content::WebContents* source,
+ const content::NativeWebKeyboardEvent& event) {
+ BrowserWindow* window = GetInspectedBrowserWindow();
+ if (window)
+ return window->PreHandleKeyboardEvent(event);
+ return content::KeyboardEventProcessingResult::NOT_HANDLED;
+}
+
+void DevToolsToolboxDelegate::HandleKeyboardEvent(
+ content::WebContents* source,
+ const content::NativeWebKeyboardEvent& event) {
+ if (event.windows_key_code == 0x08) {
+ // Do not navigate back in history on Windows (http://crbug.com/74156).
+ return;
+ }
+ BrowserWindow* window = GetInspectedBrowserWindow();
+ if (window)
+ window->HandleKeyboardEvent(event);
+}
+
+void DevToolsToolboxDelegate::WebContentsDestroyed() {
+ delete this;
+}
+
+BrowserWindow* DevToolsToolboxDelegate::GetInspectedBrowserWindow() {
+ WebContents* inspected_contents =
+ inspected_contents_observer_->web_contents();
+ if (!inspected_contents)
+ return NULL;
+ Browser* browser = NULL;
+ int tab = 0;
+ if (FindInspectedBrowserAndTabIndex(inspected_contents, &browser, &tab))
+ return browser->window();
+ return NULL;
+}
+
+// static
+GURL DecorateFrontendURL(const GURL& base_url) {
+ std::string frontend_url = base_url.spec();
+ std::string url_string(
+ frontend_url +
+ ((frontend_url.find("?") == std::string::npos) ? "?" : "&") +
+ "dockSide=undocked"); // TODO(dgozman): remove this support in M38.
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch(switches::kEnableDevToolsExperiments))
+ url_string += "&experiments=true";
+
+ if (command_line->HasSwitch(switches::kDevToolsFlags)) {
+ url_string += "&" + command_line->GetSwitchValueASCII(
+ switches::kDevToolsFlags);
+ }
+
+#if BUILDFLAG(DEBUG_DEVTOOLS)
+ url_string += "&debugFrontend=true";
+#endif // BUILDFLAG(DEBUG_DEVTOOLS)
+
+ return GURL(url_string);
+}
+
+} // namespace
+
+// DevToolsEventForwarder -----------------------------------------------------
+
+class DevToolsEventForwarder {
+ public:
+ explicit DevToolsEventForwarder(DevToolsWindow* window)
+ : devtools_window_(window) {}
+
+ // Registers whitelisted shortcuts with the forwarder.
+ // Only registered keys will be forwarded to the DevTools frontend.
+ void SetWhitelistedShortcuts(const std::string& message);
+
+ // Forwards a keyboard event to the DevTools frontend if it is whitelisted.
+ // Returns |true| if the event has been forwarded, |false| otherwise.
+ bool ForwardEvent(const content::NativeWebKeyboardEvent& event);
+
+ private:
+ static bool KeyWhitelistingAllowed(int key_code, int modifiers);
+ static int CombineKeyCodeAndModifiers(int key_code, int modifiers);
+
+ DevToolsWindow* devtools_window_;
+ std::set<int> whitelisted_keys_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsEventForwarder);
+};
+
+void DevToolsEventForwarder::SetWhitelistedShortcuts(
+ const std::string& message) {
+ std::unique_ptr<base::Value> parsed_message = base::JSONReader::Read(message);
+ base::ListValue* shortcut_list;
+ if (!parsed_message || !parsed_message->GetAsList(&shortcut_list))
+ return;
+ base::ListValue::iterator it = shortcut_list->begin();
+ for (; it != shortcut_list->end(); ++it) {
+ base::DictionaryValue* dictionary;
+ if (!it->GetAsDictionary(&dictionary))
+ continue;
+ int key_code = 0;
+ dictionary->GetInteger("keyCode", &key_code);
+ if (key_code == 0)
+ continue;
+ int modifiers = 0;
+ dictionary->GetInteger("modifiers", &modifiers);
+ if (!KeyWhitelistingAllowed(key_code, modifiers)) {
+ LOG(WARNING) << "Key whitelisting forbidden: "
+ << "(" << key_code << "," << modifiers << ")";
+ continue;
+ }
+ whitelisted_keys_.insert(CombineKeyCodeAndModifiers(key_code, modifiers));
+ }
+}
+
+bool DevToolsEventForwarder::ForwardEvent(
+ const content::NativeWebKeyboardEvent& event) {
+ std::string event_type;
+ switch (event.GetType()) {
+ case WebInputEvent::kKeyDown:
+ case WebInputEvent::kRawKeyDown:
+ event_type = kKeyDownEventName;
+ break;
+ case WebInputEvent::kKeyUp:
+ event_type = kKeyUpEventName;
+ break;
+ default:
+ return false;
+ }
+
+ int key_code = ui::LocatedToNonLocatedKeyboardCode(
+ static_cast<ui::KeyboardCode>(event.windows_key_code));
+ int modifiers = event.GetModifiers() &
+ (WebInputEvent::kShiftKey | WebInputEvent::kControlKey |
+ WebInputEvent::kAltKey | WebInputEvent::kMetaKey);
+ int key = CombineKeyCodeAndModifiers(key_code, modifiers);
+ if (whitelisted_keys_.find(key) == whitelisted_keys_.end())
+ return false;
+
+ base::DictionaryValue event_data;
+ event_data.SetString("type", event_type);
+ event_data.SetString("key", ui::KeycodeConverter::DomKeyToKeyString(
+ static_cast<ui::DomKey>(event.dom_key)));
+ event_data.SetString("code", ui::KeycodeConverter::DomCodeToCodeString(
+ static_cast<ui::DomCode>(event.dom_code)));
+ event_data.SetInteger("keyCode", key_code);
+ event_data.SetInteger("modifiers", modifiers);
+ devtools_window_->bindings_->CallClientFunction(
+ "DevToolsAPI.keyEventUnhandled", &event_data, NULL, NULL);
+ return true;
+}
+
+int DevToolsEventForwarder::CombineKeyCodeAndModifiers(int key_code,
+ int modifiers) {
+ return key_code | (modifiers << 16);
+}
+
+bool DevToolsEventForwarder::KeyWhitelistingAllowed(int key_code,
+ int modifiers) {
+ return (ui::VKEY_F1 <= key_code && key_code <= ui::VKEY_F12) ||
+ modifiers != 0;
+}
+
+void DevToolsWindow::OpenNodeFrontend() {
+ DevToolsWindow::OpenNodeFrontendWindow(profile_);
+}
+
+// DevToolsWindow::ObserverWithAccessor -------------------------------
+
+DevToolsWindow::ObserverWithAccessor::ObserverWithAccessor(
+ WebContents* web_contents)
+ : WebContentsObserver(web_contents) {
+}
+
+DevToolsWindow::ObserverWithAccessor::~ObserverWithAccessor() {
+}
+
+// DevToolsWindow -------------------------------------------------------------
+
+const char DevToolsWindow::kDevToolsApp[] = "DevToolsApp";
+
+// static
+void DevToolsWindow::AddCreationCallbackForTest(
+ const CreationCallback& callback) {
+ g_creation_callbacks.Get().push_back(callback);
+}
+
+// static
+void DevToolsWindow::RemoveCreationCallbackForTest(
+ const CreationCallback& callback) {
+ for (size_t i = 0; i < g_creation_callbacks.Get().size(); ++i) {
+ if (g_creation_callbacks.Get().at(i).Equals(callback)) {
+ g_creation_callbacks.Get().erase(g_creation_callbacks.Get().begin() + i);
+ return;
+ }
+ }
+}
+
+DevToolsWindow::~DevToolsWindow() {
+ life_stage_ = kClosing;
+
+ UpdateBrowserWindow();
+ UpdateBrowserToolbar();
+
+ if (toolbox_web_contents_)
+ delete toolbox_web_contents_;
+
+ DevToolsWindows* instances = g_instances.Pointer();
+ DevToolsWindows::iterator it(
+ std::find(instances->begin(), instances->end(), this));
+ DCHECK(it != instances->end());
+ instances->erase(it);
+
+ if (!close_callback_.is_null()) {
+ close_callback_.Run();
+ close_callback_ = base::Closure();
+ }
+}
+
+// static
+void DevToolsWindow::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterDictionaryPref(prefs::kDevToolsEditedFiles);
+ registry->RegisterDictionaryPref(prefs::kDevToolsFileSystemPaths);
+ registry->RegisterStringPref(prefs::kDevToolsAdbKey, std::string());
+
+ registry->RegisterBooleanPref(prefs::kDevToolsDiscoverUsbDevicesEnabled,
+ true);
+ registry->RegisterBooleanPref(prefs::kDevToolsPortForwardingEnabled, false);
+ registry->RegisterBooleanPref(prefs::kDevToolsPortForwardingDefaultSet,
+ false);
+ registry->RegisterDictionaryPref(prefs::kDevToolsPortForwardingConfig);
+ registry->RegisterBooleanPref(prefs::kDevToolsDiscoverTCPTargetsEnabled,
+ true);
+ registry->RegisterListPref(prefs::kDevToolsTCPDiscoveryConfig);
+ registry->RegisterDictionaryPref(prefs::kDevToolsPreferences);
+}
+
+// static
+content::WebContents* DevToolsWindow::GetInTabWebContents(
+ WebContents* inspected_web_contents,
+ DevToolsContentsResizingStrategy* out_strategy) {
+ DevToolsWindow* window = GetInstanceForInspectedWebContents(
+ inspected_web_contents);
+ if (!window || window->life_stage_ == kClosing)
+ return NULL;
+
+ // Not yet loaded window is treated as docked, but we should not present it
+ // until we decided on docking.
+ bool is_docked_set = window->life_stage_ == kLoadCompleted ||
+ window->life_stage_ == kIsDockedSet;
+ if (!is_docked_set)
+ return NULL;
+
+ // Undocked window should have toolbox web contents.
+ if (!window->is_docked_ && !window->toolbox_web_contents_)
+ return NULL;
+
+ if (out_strategy)
+ out_strategy->CopyFrom(window->contents_resizing_strategy_);
+
+ return window->is_docked_ ? window->main_web_contents_ :
+ window->toolbox_web_contents_;
+}
+
+// static
+DevToolsWindow* DevToolsWindow::GetInstanceForInspectedWebContents(
+ WebContents* inspected_web_contents) {
+ if (!inspected_web_contents || g_instances == NULL)
+ return NULL;
+ DevToolsWindows* instances = g_instances.Pointer();
+ for (DevToolsWindows::iterator it(instances->begin()); it != instances->end();
+ ++it) {
+ if ((*it)->GetInspectedWebContents() == inspected_web_contents)
+ return *it;
+ }
+ return NULL;
+}
+
+// static
+bool DevToolsWindow::IsDevToolsWindow(content::WebContents* web_contents) {
+ if (!web_contents || g_instances == NULL)
+ return false;
+ DevToolsWindows* instances = g_instances.Pointer();
+ for (DevToolsWindows::iterator it(instances->begin()); it != instances->end();
+ ++it) {
+ if ((*it)->main_web_contents_ == web_contents ||
+ (*it)->toolbox_web_contents_ == web_contents)
+ return true;
+ }
+ return false;
+}
+
+// static
+void DevToolsWindow::OpenDevToolsWindowForWorker(
+ Profile* profile,
+ const scoped_refptr<DevToolsAgentHost>& worker_agent) {
+ DevToolsWindow* window = FindDevToolsWindow(worker_agent.get());
+ if (!window) {
+ window = DevToolsWindow::CreateDevToolsWindowForWorker(profile);
+ if (!window)
+ return;
+ window->bindings_->AttachTo(worker_agent);
+ }
+ window->ScheduleShow(DevToolsToggleAction::Show());
+}
+
+// static
+DevToolsWindow* DevToolsWindow::CreateDevToolsWindowForWorker(
+ Profile* profile) {
+ base::RecordAction(base::UserMetricsAction("DevTools_InspectWorker"));
+ return Create(profile, nullptr, kFrontendWorker, std::string(), false, "",
+ "");
+}
+
+// static
+void DevToolsWindow::OpenDevToolsWindow(
+ content::WebContents* inspected_web_contents) {
+ ToggleDevToolsWindow(
+ inspected_web_contents, true, DevToolsToggleAction::Show(), "");
+}
+
+// static
+void DevToolsWindow::OpenDevToolsWindow(
+ scoped_refptr<content::DevToolsAgentHost> agent_host,
+ Profile* profile) {
+ if (!profile)
+ profile = Profile::FromBrowserContext(agent_host->GetBrowserContext());
+
+ if (!profile)
+ return;
+
+ std::string type = agent_host->GetType();
+
+ bool is_worker = type == DevToolsAgentHost::kTypeServiceWorker ||
+ 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);
+ return;
+ }
+
+ if (is_worker) {
+ DevToolsWindow::OpenDevToolsWindowForWorker(profile, agent_host);
+ return;
+ }
+
+ if (type == content::DevToolsAgentHost::kTypeFrame) {
+ DevToolsWindow::OpenDevToolsWindowForFrame(profile, agent_host);
+ return;
+ }
+
+ content::WebContents* web_contents = agent_host->GetWebContents();
+ if (web_contents)
+ DevToolsWindow::OpenDevToolsWindow(web_contents);
+}
+
+// static
+void DevToolsWindow::OpenDevToolsWindow(
+ content::WebContents* inspected_web_contents,
+ const DevToolsToggleAction& action) {
+ ToggleDevToolsWindow(inspected_web_contents, true, action, "");
+}
+
+// static
+void DevToolsWindow::OpenDevToolsWindowForFrame(
+ Profile* profile,
+ const scoped_refptr<content::DevToolsAgentHost>& agent_host) {
+ DevToolsWindow* window = FindDevToolsWindow(agent_host.get());
+ if (!window) {
+ window = DevToolsWindow::Create(profile, nullptr, kFrontendDefault,
+ std::string(), false, std::string(),
+ std::string());
+ if (!window)
+ return;
+ window->bindings_->AttachTo(agent_host);
+ }
+ window->ScheduleShow(DevToolsToggleAction::Show());
+}
+
+// static
+void DevToolsWindow::ToggleDevToolsWindow(
+ Browser* browser,
+ const DevToolsToggleAction& action) {
+ if (action.type() == DevToolsToggleAction::kToggle &&
+ browser->is_devtools()) {
+ browser->tab_strip_model()->CloseAllTabs();
+ return;
+ }
+
+ ToggleDevToolsWindow(
+ browser->tab_strip_model()->GetActiveWebContents(),
+ action.type() == DevToolsToggleAction::kInspect,
+ action, "");
+}
+
+// static
+void DevToolsWindow::OpenExternalFrontend(
+ Profile* profile,
+ const std::string& frontend_url,
+ const scoped_refptr<content::DevToolsAgentHost>& agent_host,
+ FrontendType frontend_type) {
+ DevToolsWindow* window = FindDevToolsWindow(agent_host.get());
+ if (!window) {
+ window = Create(profile, nullptr, frontend_type,
+ DevToolsUI::GetProxyURL(frontend_url).spec(), false,
+ std::string(), std::string());
+ if (!window)
+ return;
+ window->bindings_->AttachTo(agent_host);
+ window->close_on_detach_ = false;
+ }
+
+ window->ScheduleShow(DevToolsToggleAction::Show());
+}
+
+// static
+void DevToolsWindow::OpenNodeFrontendWindow(Profile* profile) {
+ for (DevToolsWindow* window : g_instances.Get()) {
+ if (window->frontend_type_ == kFrontendNode) {
+ window->ActivateWindow();
+ return;
+ }
+ }
+
+ DevToolsWindow* window =
+ Create(profile, nullptr, kFrontendNode, std::string(), false,
+ std::string(), std::string());
+ if (!window)
+ return;
+ window->bindings_->AttachTo(DevToolsAgentHost::CreateForDiscovery());
+ window->ScheduleShow(DevToolsToggleAction::Show());
+}
+
+// static
+void DevToolsWindow::ToggleDevToolsWindow(
+ content::WebContents* inspected_web_contents,
+ bool force_open,
+ const DevToolsToggleAction& action,
+ const std::string& settings) {
+ scoped_refptr<DevToolsAgentHost> agent(
+ DevToolsAgentHost::GetOrCreateFor(inspected_web_contents));
+ DevToolsWindow* window = FindDevToolsWindow(agent.get());
+ bool do_open = force_open;
+ if (!window) {
+ Profile* profile = Profile::FromBrowserContext(
+ inspected_web_contents->GetBrowserContext());
+ base::RecordAction(base::UserMetricsAction("DevTools_InspectRenderer"));
+ std::string panel = "";
+ switch (action.type()) {
+ case DevToolsToggleAction::kInspect:
+ case DevToolsToggleAction::kShowElementsPanel:
+ panel = "elements";
+ break;
+ case DevToolsToggleAction::kShowConsolePanel:
+ panel = "console";
+ break;
+ case DevToolsToggleAction::kShow:
+ case DevToolsToggleAction::kToggle:
+ case DevToolsToggleAction::kReveal:
+ case DevToolsToggleAction::kNoOp:
+ break;
+ }
+ window = Create(profile, inspected_web_contents, kFrontendDefault,
+ std::string(), true, settings, panel);
+ if (!window)
+ return;
+ window->bindings_->AttachTo(agent.get());
+ do_open = true;
+ }
+
+ // Update toolbar to reflect DevTools changes.
+ window->UpdateBrowserToolbar();
+
+ // If window is docked and visible, we hide it on toggle. If window is
+ // undocked, we show (activate) it.
+ if (!window->is_docked_ || do_open)
+ window->ScheduleShow(action);
+ else
+ window->CloseWindow();
+}
+
+// static
+void DevToolsWindow::InspectElement(
+ content::RenderFrameHost* inspected_frame_host,
+ int x,
+ int y) {
+ WebContents* web_contents =
+ WebContents::FromRenderFrameHost(inspected_frame_host);
+ scoped_refptr<DevToolsAgentHost> agent(
+ DevToolsAgentHost::GetOrCreateFor(web_contents));
+ 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;
+ }
+}
+
+void DevToolsWindow::ScheduleShow(const DevToolsToggleAction& action) {
+ if (life_stage_ == kLoadCompleted) {
+ Show(action);
+ return;
+ }
+
+ // Action will be done only after load completed.
+ action_on_load_ = action;
+
+ if (!can_dock_) {
+ // No harm to show always-undocked window right away.
+ is_docked_ = false;
+ Show(DevToolsToggleAction::Show());
+ }
+}
+
+void DevToolsWindow::Show(const DevToolsToggleAction& action) {
+ if (life_stage_ == kClosing)
+ return;
+
+ if (action.type() == DevToolsToggleAction::kNoOp)
+ return;
+ if (is_docked_) {
+ DCHECK(can_dock_);
+ Browser* inspected_browser = NULL;
+ int inspected_tab_index = -1;
+ FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
+ &inspected_browser,
+ &inspected_tab_index);
+ DCHECK(inspected_browser);
+ DCHECK(inspected_tab_index != -1);
+
+ // Tell inspected browser to update splitter and switch to inspected panel.
+ BrowserWindow* inspected_window = inspected_browser->window();
+ main_web_contents_->SetDelegate(this);
+
+ TabStripModel* tab_strip_model = inspected_browser->tab_strip_model();
+ tab_strip_model->ActivateTabAt(inspected_tab_index, true);
+
+ inspected_window->UpdateDevTools();
+ main_web_contents_->SetInitialFocus();
+ inspected_window->Show();
+ // On Aura, focusing once is not enough. Do it again.
+ // Note that focusing only here but not before isn't enough either. We just
+ // need to focus twice.
+ main_web_contents_->SetInitialFocus();
+
+ PrefsTabHelper::CreateForWebContents(main_web_contents_);
+ main_web_contents_->GetRenderViewHost()->SyncRendererPrefs();
+
+ DoAction(action);
+ return;
+ }
+
+ // Avoid consecutive window switching if the devtools window has been opened
+ // and the Inspect Element shortcut is pressed in the inspected tab.
+ bool should_show_window =
+ !browser_ || (action.type() != DevToolsToggleAction::kInspect);
+
+ if (!browser_)
+ CreateDevToolsBrowser();
+
+ if (should_show_window) {
+ browser_->window()->Show();
+ main_web_contents_->SetInitialFocus();
+ }
+ if (toolbox_web_contents_)
+ UpdateBrowserWindow();
+
+ DoAction(action);
+}
+
+// static
+bool DevToolsWindow::HandleBeforeUnload(WebContents* frontend_contents,
+ bool proceed, bool* proceed_to_fire_unload) {
+ DevToolsWindow* window = AsDevToolsWindow(frontend_contents);
+ if (!window)
+ return false;
+ if (!window->intercepted_page_beforeunload_)
+ return false;
+ window->BeforeUnloadFired(frontend_contents, proceed,
+ proceed_to_fire_unload);
+ return true;
+}
+
+// static
+bool DevToolsWindow::InterceptPageBeforeUnload(WebContents* contents) {
+ DevToolsWindow* window =
+ DevToolsWindow::GetInstanceForInspectedWebContents(contents);
+ if (!window || window->intercepted_page_beforeunload_)
+ return false;
+
+ // Not yet loaded frontend will not handle beforeunload.
+ if (window->life_stage_ != kLoadCompleted)
+ return false;
+
+ window->intercepted_page_beforeunload_ = true;
+ // Handle case of devtools inspecting another devtools instance by passing
+ // the call up to the inspecting devtools instance.
+ if (!DevToolsWindow::InterceptPageBeforeUnload(window->main_web_contents_)) {
+ window->main_web_contents_->DispatchBeforeUnload();
+ }
+ return true;
+}
+
+// static
+bool DevToolsWindow::NeedsToInterceptBeforeUnload(
+ WebContents* contents) {
+ DevToolsWindow* window =
+ DevToolsWindow::GetInstanceForInspectedWebContents(contents);
+ return window && !window->intercepted_page_beforeunload_ &&
+ window->life_stage_ == kLoadCompleted;
+}
+
+// static
+bool DevToolsWindow::HasFiredBeforeUnloadEventForDevToolsBrowser(
+ Browser* browser) {
+ DCHECK(browser->is_devtools());
+ // When FastUnloadController is used, devtools frontend will be detached
+ // from the browser window at this point which means we've already fired
+ // beforeunload.
+ if (browser->tab_strip_model()->empty())
+ return true;
+ WebContents* contents =
+ browser->tab_strip_model()->GetWebContentsAt(0);
+ DevToolsWindow* window = AsDevToolsWindow(contents);
+ if (!window)
+ return false;
+ return window->intercepted_page_beforeunload_;
+}
+
+// static
+void DevToolsWindow::OnPageCloseCanceled(WebContents* contents) {
+ DevToolsWindow* window =
+ DevToolsWindow::GetInstanceForInspectedWebContents(contents);
+ if (!window)
+ return;
+ window->intercepted_page_beforeunload_ = false;
+ // Propagate to devtools opened on devtools if any.
+ DevToolsWindow::OnPageCloseCanceled(window->main_web_contents_);
+}
+
+DevToolsWindow::DevToolsWindow(FrontendType frontend_type,
+ Profile* profile,
+ WebContents* main_web_contents,
+ DevToolsUIBindings* bindings,
+ WebContents* inspected_web_contents,
+ bool can_dock)
+ : frontend_type_(frontend_type),
+ profile_(profile),
+ main_web_contents_(main_web_contents),
+ toolbox_web_contents_(nullptr),
+ bindings_(bindings),
+ browser_(nullptr),
+ is_docked_(true),
+ can_dock_(can_dock),
+ close_on_detach_(true),
+ // This initialization allows external front-end to work without changes.
+ // We don't wait for docking call, but instead immediately show undocked.
+ // Passing "dockSide=undocked" parameter ensures proper UI.
+ life_stage_(can_dock ? kNotLoaded : kIsDockedSet),
+ action_on_load_(DevToolsToggleAction::NoOp()),
+ intercepted_page_beforeunload_(false),
+ ready_for_test_(false) {
+ // Set up delegate, so we get fully-functional window immediately.
+ // It will not appear in UI though until |life_stage_ == kLoadCompleted|.
+ main_web_contents_->SetDelegate(this);
+ // Bindings take ownership over devtools as its delegate.
+ bindings_->SetDelegate(this);
+ data_use_measurement::DataUseWebContentsObserver::CreateForWebContents(
+ main_web_contents_);
+ // DevTools uses PageZoom::Zoom(), so main_web_contents_ requires a
+ // ZoomController.
+ zoom::ZoomController::CreateForWebContents(main_web_contents_);
+ zoom::ZoomController::FromWebContents(main_web_contents_)
+ ->SetShowsNotificationBubble(false);
+
+ g_instances.Get().push_back(this);
+
+ // There is no inspected_web_contents in case of various workers.
+ if (inspected_web_contents)
+ inspected_contents_observer_.reset(
+ new ObserverWithAccessor(inspected_web_contents));
+
+ // Initialize docked page to be of the right size.
+ if (can_dock_ && inspected_web_contents) {
+ content::RenderWidgetHostView* inspected_view =
+ inspected_web_contents->GetRenderWidgetHostView();
+ if (inspected_view && main_web_contents_->GetRenderWidgetHostView()) {
+ gfx::Size size = inspected_view->GetViewBounds().size();
+ main_web_contents_->GetRenderWidgetHostView()->SetSize(size);
+ }
+ }
+
+ event_forwarder_.reset(new DevToolsEventForwarder(this));
+
+ // Tag the DevTools main WebContents with its TaskManager specific UserData
+ // so that it shows up in the task manager.
+ task_manager::WebContentsTags::CreateForDevToolsContents(main_web_contents_);
+
+ std::vector<base::Callback<void(DevToolsWindow*)>> copy(
+ g_creation_callbacks.Get());
+ for (const auto& callback : copy)
+ callback.Run(this);
+}
+
+// static
+DevToolsWindow* DevToolsWindow::Create(
+ Profile* profile,
+ content::WebContents* inspected_web_contents,
+ FrontendType frontend_type,
+ const std::string& frontend_url,
+ bool can_dock,
+ const std::string& settings,
+ const std::string& panel) {
+ if (profile->GetPrefs()->GetBoolean(prefs::kDevToolsDisabled) ||
+ base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode))
+ return nullptr;
+
+ if (inspected_web_contents) {
+ // Check for a place to dock.
+ Browser* browser = nullptr;
+ int tab;
+ if (!FindInspectedBrowserAndTabIndex(inspected_web_contents,
+ &browser, &tab) ||
+ browser->is_type_popup()) {
+ can_dock = false;
+ }
+ }
+
+ // Create WebContents with devtools.
+ GURL url(
+ GetDevToolsURL(profile, frontend_type, frontend_url, can_dock, panel));
+ std::unique_ptr<WebContents> main_web_contents(
+ WebContents::Create(WebContents::CreateParams(profile)));
+ main_web_contents->GetController().LoadURL(
+ DecorateFrontendURL(url), content::Referrer(),
+ ui::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
+ DevToolsUIBindings* bindings =
+ DevToolsUIBindings::ForWebContents(main_web_contents.get());
+ if (!bindings)
+ return nullptr;
+ if (!settings.empty())
+ SetPreferencesFromJson(profile, settings);
+ return new DevToolsWindow(frontend_type, profile, main_web_contents.release(),
+ bindings, inspected_web_contents, can_dock);
+}
+
+// static
+GURL DevToolsWindow::GetDevToolsURL(Profile* profile,
+ FrontendType frontend_type,
+ const std::string& frontend_url,
+ bool can_dock,
+ const std::string& panel) {
+ std::string url(!frontend_url.empty() ? frontend_url
+ : chrome::kChromeUIDevToolsURL);
+ std::string url_string(url +
+ ((url.find("?") == std::string::npos) ? "?" : "&"));
+ switch (frontend_type) {
+ case kFrontendRemote:
+ url_string += "&remoteFrontend=true";
+ break;
+ case kFrontendWorker:
+ url_string += "&isSharedWorker=true";
+ break;
+ case kFrontendNode:
+ url_string += "&nodeFrontend=true";
+ // Fall through
+ case kFrontendV8:
+ url_string += "&v8only=true";
+ break;
+ case kFrontendDefault:
+ default:
+ 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;
+ return DevToolsUIBindings::SanitizeFrontendURL(GURL(url_string));
+}
+
+// static
+DevToolsWindow* DevToolsWindow::FindDevToolsWindow(
+ DevToolsAgentHost* agent_host) {
+ if (!agent_host || g_instances == NULL)
+ return NULL;
+ DevToolsWindows* instances = g_instances.Pointer();
+ for (DevToolsWindows::iterator it(instances->begin()); it != instances->end();
+ ++it) {
+ if ((*it)->bindings_->IsAttachedTo(agent_host))
+ return *it;
+ }
+ return NULL;
+}
+
+// static
+DevToolsWindow* DevToolsWindow::AsDevToolsWindow(
+ content::WebContents* web_contents) {
+ if (!web_contents || g_instances == NULL)
+ return NULL;
+ DevToolsWindows* instances = g_instances.Pointer();
+ for (DevToolsWindows::iterator it(instances->begin()); it != instances->end();
+ ++it) {
+ if ((*it)->main_web_contents_ == web_contents)
+ return *it;
+ }
+ return NULL;
+}
+
+WebContents* DevToolsWindow::OpenURLFromTab(
+ WebContents* source,
+ const content::OpenURLParams& params) {
+ DCHECK(source == main_web_contents_);
+ if (!params.url.SchemeIs(content::kChromeDevToolsScheme)) {
+ WebContents* inspected_web_contents = GetInspectedWebContents();
+ return inspected_web_contents ?
+ inspected_web_contents->OpenURL(params) : NULL;
+ }
+ bindings_->Reload();
+ return main_web_contents_;
+}
+
+void DevToolsWindow::ShowCertificateViewer(
+ scoped_refptr<net::X509Certificate> certificate) {
+ WebContents* inspected_contents = is_docked_ ?
+ GetInspectedWebContents() : main_web_contents_;
+ Browser* browser = NULL;
+ int tab = 0;
+ if (!FindInspectedBrowserAndTabIndex(inspected_contents, &browser, &tab))
+ return;
+ gfx::NativeWindow parent = browser->window()->GetNativeWindow();
+ ::ShowCertificateViewer(inspected_contents, parent, certificate.get());
+}
+
+void DevToolsWindow::ActivateContents(WebContents* contents) {
+ if (is_docked_) {
+ WebContents* inspected_tab = GetInspectedWebContents();
+ if (inspected_tab)
+ inspected_tab->GetDelegate()->ActivateContents(inspected_tab);
+ } else if (browser_) {
+ browser_->window()->Activate();
+ }
+}
+
+void DevToolsWindow::AddNewContents(WebContents* source,
+ WebContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_rect,
+ bool user_gesture,
+ bool* was_blocked) {
+ if (new_contents == toolbox_web_contents_) {
+ toolbox_web_contents_->SetDelegate(
+ new DevToolsToolboxDelegate(toolbox_web_contents_,
+ inspected_contents_observer_.get()));
+ if (main_web_contents_->GetRenderWidgetHostView() &&
+ toolbox_web_contents_->GetRenderWidgetHostView()) {
+ gfx::Size size =
+ main_web_contents_->GetRenderWidgetHostView()->GetViewBounds().size();
+ toolbox_web_contents_->GetRenderWidgetHostView()->SetSize(size);
+ }
+ UpdateBrowserWindow();
+ return;
+ }
+
+ WebContents* inspected_web_contents = GetInspectedWebContents();
+ if (inspected_web_contents) {
+ inspected_web_contents->GetDelegate()->AddNewContents(
+ source, new_contents, disposition, initial_rect, user_gesture,
+ was_blocked);
+ }
+}
+
+void DevToolsWindow::WebContentsCreated(
+ WebContents* source_contents,
+ int opener_render_process_id,
+ int opener_render_frame_id,
+ const std::string& frame_name,
+ const GURL& target_url,
+ WebContents* new_contents,
+ const base::Optional<content::WebContents::CreateParams>& create_params) {
+ if (target_url.SchemeIs(content::kChromeDevToolsScheme) &&
+ target_url.path().rfind("toolbox.html") != std::string::npos) {
+ CHECK(can_dock_);
+ if (toolbox_web_contents_)
+ delete toolbox_web_contents_;
+ toolbox_web_contents_ = new_contents;
+
+ // Tag the DevTools toolbox WebContents with its TaskManager specific
+ // UserData so that it shows up in the task manager.
+ task_manager::WebContentsTags::CreateForDevToolsContents(
+ toolbox_web_contents_);
+ data_use_measurement::DataUseWebContentsObserver::CreateForWebContents(
+ toolbox_web_contents_);
+ }
+}
+
+void DevToolsWindow::CloseContents(WebContents* source) {
+ CHECK(is_docked_);
+ life_stage_ = kClosing;
+ UpdateBrowserWindow();
+ // In case of docked main_web_contents_, we own it so delete here.
+ // Embedding DevTools window will be deleted as a result of
+ // DevToolsUIBindings destruction.
+ delete main_web_contents_;
+}
+
+void DevToolsWindow::ContentsZoomChange(bool zoom_in) {
+ DCHECK(is_docked_);
+ zoom::PageZoom::Zoom(main_web_contents_, zoom_in ? content::PAGE_ZOOM_IN
+ : content::PAGE_ZOOM_OUT);
+}
+
+void DevToolsWindow::BeforeUnloadFired(WebContents* tab,
+ bool proceed,
+ bool* proceed_to_fire_unload) {
+ if (!intercepted_page_beforeunload_) {
+ // Docked devtools window closed directly.
+ if (proceed)
+ bindings_->Detach();
+ *proceed_to_fire_unload = proceed;
+ } else {
+ // Inspected page is attempting to close.
+ WebContents* inspected_web_contents = GetInspectedWebContents();
+ if (proceed) {
+ inspected_web_contents->DispatchBeforeUnload();
+ } else {
+ bool should_proceed;
+ inspected_web_contents->GetDelegate()->BeforeUnloadFired(
+ inspected_web_contents, false, &should_proceed);
+ DCHECK(!should_proceed);
+ }
+ *proceed_to_fire_unload = false;
+ }
+}
+
+content::KeyboardEventProcessingResult DevToolsWindow::PreHandleKeyboardEvent(
+ WebContents* source,
+ const content::NativeWebKeyboardEvent& event) {
+ BrowserWindow* inspected_window = GetInspectedBrowserWindow();
+ if (inspected_window) {
+ return inspected_window->PreHandleKeyboardEvent(event);
+ }
+ return content::KeyboardEventProcessingResult::NOT_HANDLED;
+}
+
+void DevToolsWindow::HandleKeyboardEvent(
+ WebContents* source,
+ const content::NativeWebKeyboardEvent& event) {
+ if (event.windows_key_code == 0x08) {
+ // Do not navigate back in history on Windows (http://crbug.com/74156).
+ return;
+ }
+ BrowserWindow* inspected_window = GetInspectedBrowserWindow();
+ if (inspected_window)
+ inspected_window->HandleKeyboardEvent(event);
+}
+
+content::JavaScriptDialogManager* DevToolsWindow::GetJavaScriptDialogManager(
+ WebContents* source) {
+ return app_modal::JavaScriptDialogManager::GetInstance();
+}
+
+content::ColorChooser* DevToolsWindow::OpenColorChooser(
+ WebContents* web_contents,
+ SkColor initial_color,
+ const std::vector<content::ColorSuggestion>& suggestions) {
+ return chrome::ShowColorChooser(web_contents, initial_color);
+}
+
+void DevToolsWindow::RunFileChooser(content::RenderFrameHost* render_frame_host,
+ const content::FileChooserParams& params) {
+ FileSelectHelper::RunFileChooser(render_frame_host, params);
+}
+
+bool DevToolsWindow::PreHandleGestureEvent(
+ WebContents* source,
+ const blink::WebGestureEvent& event) {
+ // Disable pinch zooming.
+ return blink::WebInputEvent::IsPinchGestureEventType(event.GetType());
+}
+
+void DevToolsWindow::ShowCertificateViewerInDevTools(
+ content::WebContents* web_contents,
+ scoped_refptr<net::X509Certificate> certificate) {
+ ShowCertificateViewer(certificate);
+}
+
+void DevToolsWindow::ActivateWindow() {
+ if (life_stage_ != kLoadCompleted)
+ return;
+ if (is_docked_ && GetInspectedBrowserWindow())
+ main_web_contents_->Focus();
+ else if (!is_docked_ && !browser_->window()->IsActive())
+ browser_->window()->Activate();
+}
+
+void DevToolsWindow::CloseWindow() {
+ DCHECK(is_docked_);
+ life_stage_ = kClosing;
+ main_web_contents_->DispatchBeforeUnload();
+}
+
+void DevToolsWindow::Inspect(scoped_refptr<content::DevToolsAgentHost> host) {
+ DevToolsWindow::OpenDevToolsWindow(host, profile_);
+}
+
+void DevToolsWindow::SetInspectedPageBounds(const gfx::Rect& rect) {
+ DevToolsContentsResizingStrategy strategy(rect);
+ if (contents_resizing_strategy_.Equals(strategy))
+ return;
+
+ contents_resizing_strategy_.CopyFrom(strategy);
+ UpdateBrowserWindow();
+}
+
+void DevToolsWindow::InspectElementCompleted() {
+ if (!inspect_element_start_time_.is_null()) {
+ UMA_HISTOGRAM_TIMES("DevTools.InspectElement",
+ base::TimeTicks::Now() - inspect_element_start_time_);
+ inspect_element_start_time_ = base::TimeTicks();
+ }
+}
+
+void DevToolsWindow::SetIsDocked(bool dock_requested) {
+ if (life_stage_ == kClosing)
+ return;
+
+ DCHECK(can_dock_ || !dock_requested);
+ if (!can_dock_)
+ dock_requested = false;
+
+ bool was_docked = is_docked_;
+ is_docked_ = dock_requested;
+
+ if (life_stage_ != kLoadCompleted) {
+ // This is a first time call we waited for to initialize.
+ life_stage_ = life_stage_ == kOnLoadFired ? kLoadCompleted : kIsDockedSet;
+ if (life_stage_ == kLoadCompleted)
+ LoadCompleted();
+ return;
+ }
+
+ if (dock_requested == was_docked)
+ return;
+
+ if (dock_requested && !was_docked) {
+ // Detach window from the external devtools browser. It will lead to
+ // the browser object's close and delete. Remove observer first.
+ TabStripModel* tab_strip_model = browser_->tab_strip_model();
+ tab_strip_model->DetachWebContentsAt(
+ tab_strip_model->GetIndexOfWebContents(main_web_contents_));
+ browser_ = NULL;
+ } else if (!dock_requested && was_docked) {
+ UpdateBrowserWindow();
+ }
+
+ Show(DevToolsToggleAction::Show());
+}
+
+void DevToolsWindow::OpenInNewTab(const std::string& url) {
+ content::OpenURLParams params(GURL(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),
+ ui::PAGE_TRANSITION_LINK);
+ }
+}
+
+void DevToolsWindow::SetWhitelistedShortcuts(
+ const std::string& message) {
+ event_forwarder_->SetWhitelistedShortcuts(message);
+}
+
+void DevToolsWindow::SetEyeDropperActive(bool active) {
+ WebContents* web_contents = GetInspectedWebContents();
+ if (!web_contents)
+ return;
+ if (active) {
+ eye_dropper_.reset(new DevToolsEyeDropper(
+ web_contents, base::Bind(&DevToolsWindow::ColorPickedInEyeDropper,
+ base::Unretained(this))));
+ } else {
+ eye_dropper_.reset();
+ }
+}
+
+void DevToolsWindow::ColorPickedInEyeDropper(int r, int g, int b, int a) {
+ base::DictionaryValue color;
+ color.SetInteger("r", r);
+ color.SetInteger("g", g);
+ color.SetInteger("b", b);
+ color.SetInteger("a", a);
+ bindings_->CallClientFunction("DevToolsAPI.eyeDropperPickedColor", &color,
+ nullptr, nullptr);
+}
+
+void DevToolsWindow::InspectedContentsClosing() {
+ if (!close_on_detach_)
+ return;
+ intercepted_page_beforeunload_ = false;
+ life_stage_ = kClosing;
+ main_web_contents_->ClosePage();
+}
+
+InfoBarService* DevToolsWindow::GetInfoBarService() {
+ return is_docked_ ?
+ InfoBarService::FromWebContents(GetInspectedWebContents()) :
+ InfoBarService::FromWebContents(main_web_contents_);
+}
+
+void DevToolsWindow::RenderProcessGone(bool crashed) {
+ // Docked DevToolsWindow owns its main_web_contents_ and must delete it.
+ // Undocked main_web_contents_ are owned and handled by browser.
+ // see crbug.com/369932
+ if (is_docked_) {
+ CloseContents(main_web_contents_);
+ } else if (browser_ && crashed) {
+ browser_->window()->Close();
+ }
+}
+
+void DevToolsWindow::OnLoadCompleted() {
+ // First seed inspected tab id for extension APIs.
+ WebContents* inspected_web_contents = GetInspectedWebContents();
+ if (inspected_web_contents) {
+ SessionTabHelper* session_tab_helper =
+ SessionTabHelper::FromWebContents(inspected_web_contents);
+ if (session_tab_helper) {
+ base::Value tabId(session_tab_helper->session_id().id());
+ bindings_->CallClientFunction("DevToolsAPI.setInspectedTabId",
+ &tabId, NULL, NULL);
+ }
+ }
+
+ if (life_stage_ == kClosing)
+ return;
+
+ // We could be in kLoadCompleted state already if frontend reloads itself.
+ if (life_stage_ != kLoadCompleted) {
+ // Load is completed when both kIsDockedSet and kOnLoadFired happened.
+ // Here we set kOnLoadFired.
+ life_stage_ = life_stage_ == kIsDockedSet ? kLoadCompleted : kOnLoadFired;
+ }
+ if (life_stage_ == kLoadCompleted)
+ LoadCompleted();
+}
+
+void DevToolsWindow::ReadyForTest() {
+ ready_for_test_ = true;
+ if (!ready_for_test_callback_.is_null()) {
+ ready_for_test_callback_.Run();
+ ready_for_test_callback_ = base::Closure();
+ }
+}
+
+void DevToolsWindow::CreateDevToolsBrowser() {
+ PrefService* prefs = profile_->GetPrefs();
+ 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>();
+ dev_tools_defaults->SetInteger("left", 100);
+ dev_tools_defaults->SetInteger("top", 100);
+ dev_tools_defaults->SetInteger("right", 740);
+ dev_tools_defaults->SetInteger("bottom", 740);
+ dev_tools_defaults->SetBoolean("maximized", false);
+ dev_tools_defaults->SetBoolean("always_on_top", false);
+ wp_prefs->Set(kDevToolsApp, std::move(dev_tools_defaults));
+ }
+
+ browser_ = new Browser(Browser::CreateParams::CreateForDevTools(profile_));
+ browser_->tab_strip_model()->AddWebContents(
+ main_web_contents_, -1, ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
+ TabStripModel::ADD_ACTIVE);
+ main_web_contents_->GetRenderViewHost()->SyncRendererPrefs();
+}
+
+BrowserWindow* DevToolsWindow::GetInspectedBrowserWindow() {
+ Browser* browser = NULL;
+ int tab;
+ return FindInspectedBrowserAndTabIndex(GetInspectedWebContents(),
+ &browser, &tab) ?
+ browser->window() : NULL;
+}
+
+void DevToolsWindow::DoAction(const DevToolsToggleAction& action) {
+ switch (action.type()) {
+ case DevToolsToggleAction::kInspect:
+ bindings_->CallClientFunction("DevToolsAPI.enterInspectElementMode", NULL,
+ NULL, NULL);
+ break;
+
+ case DevToolsToggleAction::kShowElementsPanel:
+ case DevToolsToggleAction::kShowConsolePanel:
+ case DevToolsToggleAction::kShow:
+ case DevToolsToggleAction::kToggle:
+ // Do nothing.
+ break;
+
+ case DevToolsToggleAction::kReveal: {
+ const DevToolsToggleAction::RevealParams* params =
+ action.params();
+ CHECK(params);
+ base::Value url_value(params->url);
+ base::Value line_value(static_cast<int>(params->line_number));
+ base::Value column_value(static_cast<int>(params->column_number));
+ bindings_->CallClientFunction("DevToolsAPI.revealSourceLine",
+ &url_value, &line_value, &column_value);
+ break;
+ }
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+void DevToolsWindow::UpdateBrowserToolbar() {
+ BrowserWindow* inspected_window = GetInspectedBrowserWindow();
+ if (inspected_window)
+ inspected_window->UpdateToolbar(NULL);
+}
+
+void DevToolsWindow::UpdateBrowserWindow() {
+ BrowserWindow* inspected_window = GetInspectedBrowserWindow();
+ if (inspected_window)
+ inspected_window->UpdateDevTools();
+}
+
+WebContents* DevToolsWindow::GetInspectedWebContents() {
+ return inspected_contents_observer_
+ ? inspected_contents_observer_->web_contents()
+ : NULL;
+}
+
+void DevToolsWindow::LoadCompleted() {
+ Show(action_on_load_);
+ action_on_load_ = DevToolsToggleAction::NoOp();
+ if (!load_completed_callback_.is_null()) {
+ load_completed_callback_.Run();
+ load_completed_callback_ = base::Closure();
+ }
+}
+
+void DevToolsWindow::SetLoadCompletedCallback(const base::Closure& closure) {
+ if (life_stage_ == kLoadCompleted || life_stage_ == kClosing) {
+ if (!closure.is_null())
+ closure.Run();
+ return;
+ }
+ load_completed_callback_ = closure;
+}
+
+bool DevToolsWindow::ForwardKeyboardEvent(
+ const content::NativeWebKeyboardEvent& event) {
+ return event_forwarder_->ForwardEvent(event);
+}
+
+bool DevToolsWindow::ReloadInspectedWebContents(bool bypass_cache) {
+ // Only route reload via front-end if the agent is attached.
+ WebContents* wc = GetInspectedWebContents();
+ if (!wc || wc->GetCrashedStatus() != base::TERMINATION_STATUS_STILL_RUNNING)
+ return false;
+ base::Value bypass_cache_value(bypass_cache);
+ bindings_->CallClientFunction("DevToolsAPI.reloadInspectedPage",
+ &bypass_cache_value, nullptr, nullptr);
+ return true;
+}
diff --git a/chromium/chrome/browser/devtools/devtools_window.h b/chromium/chrome/browser/devtools/devtools_window.h
new file mode 100644
index 00000000000..8058d9c827f
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_window.h
@@ -0,0 +1,383 @@
+// 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.
+
+#ifndef CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_
+
+#include "base/macros.h"
+#include "chrome/browser/devtools/devtools_contents_resizing_strategy.h"
+#include "chrome/browser/devtools/devtools_toggle_action.h"
+#include "chrome/browser/devtools/devtools_ui_bindings.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/browser/web_contents_observer.h"
+
+class Browser;
+class BrowserWindow;
+class DevToolsWindowTesting;
+class DevToolsEventForwarder;
+class DevToolsEyeDropper;
+
+namespace content {
+class DevToolsAgentHost;
+struct NativeWebKeyboardEvent;
+class RenderFrameHost;
+}
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+class DevToolsWindow : public DevToolsUIBindings::Delegate,
+ public content::WebContentsDelegate {
+ public:
+ class ObserverWithAccessor : public content::WebContentsObserver {
+ public:
+ explicit ObserverWithAccessor(content::WebContents* web_contents);
+ ~ObserverWithAccessor() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ObserverWithAccessor);
+ };
+
+ static const char kDevToolsApp[];
+
+ ~DevToolsWindow() override;
+
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ // Return the DevToolsWindow for the given WebContents if one exists,
+ // otherwise NULL.
+ static DevToolsWindow* GetInstanceForInspectedWebContents(
+ content::WebContents* inspected_web_contents);
+
+ // Return the docked DevTools WebContents for the given inspected WebContents
+ // if one exists and should be shown in browser window, otherwise NULL.
+ // This method will return only fully initialized window ready to be
+ // presented in UI.
+ // If |out_strategy| is not NULL, it will contain resizing strategy.
+ // For immediately-ready-to-use but maybe not yet fully initialized DevTools
+ // use |GetInstanceForInspectedRenderViewHost| instead.
+ static content::WebContents* GetInTabWebContents(
+ content::WebContents* inspected_tab,
+ DevToolsContentsResizingStrategy* out_strategy);
+
+ static bool IsDevToolsWindow(content::WebContents* web_contents);
+ static DevToolsWindow* AsDevToolsWindow(content::WebContents* web_contents);
+ static DevToolsWindow* FindDevToolsWindow(content::DevToolsAgentHost*);
+
+ // Open or reveal DevTools window, and perform the specified action.
+ // How to get pointer to the created window see comments for
+ // ToggleDevToolsWindow().
+ static void OpenDevToolsWindow(content::WebContents* inspected_web_contents,
+ const DevToolsToggleAction& action);
+
+ // Open or reveal DevTools window, with no special action.
+ // How to get pointer to the created window see comments for
+ // ToggleDevToolsWindow().
+ static void OpenDevToolsWindow(content::WebContents* inspected_web_contents);
+
+ // Open or reveal DevTools window, with no special action. Use |profile| to
+ // open client window in, default to |host|'s profile if none given.
+ static void OpenDevToolsWindow(
+ scoped_refptr<content::DevToolsAgentHost> host,
+ Profile* profile);
+
+ // Perform specified action for current WebContents inside a |browser|.
+ // This may close currently open DevTools window.
+ // If DeveloperToolsDisabled policy is set, no DevTools window created.
+ // In case if needed pointer to the created window one should use
+ // DevToolsAgentHost and DevToolsWindow::FindDevToolsWindow(). E.g.:
+ //
+ // scoped_refptr<content::DevToolsAgentHost> agent(
+ // content::DevToolsAgentHost::GetOrCreateFor(inspected_web_contents));
+ // DevToolsWindow::ToggleDevToolsWindow(
+ // inspected_web_contents, DevToolsToggleAction::Show());
+ // DevToolsWindow* window = DevToolsWindow::FindDevToolsWindow(agent.get());
+ //
+ static void ToggleDevToolsWindow(
+ Browser* browser,
+ const DevToolsToggleAction& action);
+
+ // Node frontend is always undocked.
+ static void OpenNodeFrontendWindow(Profile* profile);
+
+ // Worker frontend is always undocked.
+ static void OpenDevToolsWindowForWorker(
+ Profile* profile,
+ const scoped_refptr<content::DevToolsAgentHost>& worker_agent);
+
+ static void InspectElement(content::RenderFrameHost* inspected_frame_host,
+ int x,
+ int y);
+
+ // Sets closure to be called after load is done. If already loaded, calls
+ // closure immediately.
+ void SetLoadCompletedCallback(const base::Closure& closure);
+
+ // Forwards an unhandled keyboard event to the DevTools frontend.
+ bool ForwardKeyboardEvent(const content::NativeWebKeyboardEvent& event);
+
+ // Reloads inspected web contents as if it was triggered from DevTools.
+ // Returns true if it has successfully handled reload, false if the caller
+ // is to proceed reload without DevTools interception.
+ bool ReloadInspectedWebContents(bool bypass_cache);
+
+ content::WebContents* OpenURLFromTab(
+ content::WebContents* source,
+ const content::OpenURLParams& params) override;
+
+ void ShowCertificateViewer(scoped_refptr<net::X509Certificate> certificate);
+
+ // BeforeUnload interception ////////////////////////////////////////////////
+
+ // In order to preserve any edits the user may have made in devtools, the
+ // beforeunload event of the inspected page is hooked - devtools gets the
+ // first shot at handling beforeunload and presents a dialog to the user. If
+ // the user accepts the dialog then the script is given a chance to handle
+ // it. This way 2 dialogs may be displayed: one from the devtools asking the
+ // user to confirm that they're ok with their devtools edits going away and
+ // another from the webpage as the result of its beforeunload handler.
+ // The following set of methods handle beforeunload event flow through
+ // devtools window. When the |contents| with devtools opened on them are
+ // getting closed, the following sequence of calls takes place:
+ // 1. |DevToolsWindow::InterceptPageBeforeUnload| is called and indicates
+ // whether devtools intercept the beforeunload event.
+ // If InterceptPageBeforeUnload() returns true then the following steps
+ // will take place; otherwise only step 4 will be reached and none of the
+ // corresponding functions in steps 2 & 3 will get called.
+ // 2. |DevToolsWindow::InterceptPageBeforeUnload| fires beforeunload event
+ // for devtools frontend, which will asynchronously call
+ // |WebContentsDelegate::BeforeUnloadFired| method.
+ // In case of docked devtools window, devtools are set as a delegate for
+ // its frontend, so method |DevToolsWindow::BeforeUnloadFired| will be
+ // called directly.
+ // If devtools window is undocked it's not set as the delegate so the call
+ // to BeforeUnloadFired is proxied through HandleBeforeUnload() rather
+ // than getting called directly.
+ // 3a. If |DevToolsWindow::BeforeUnloadFired| is called with |proceed|=false
+ // it calls throught to the content's BeforeUnloadFired(), which from the
+ // WebContents perspective looks the same as the |content|'s own
+ // beforeunload dialog having had it's 'stay on this page' button clicked.
+ // 3b. If |proceed| = true, then it fires beforeunload event on |contents|
+ // and everything proceeds as it normally would without the Devtools
+ // interception.
+ // 4. If the user cancels the dialog put up by either the WebContents or
+ // devtools frontend, then |contents|'s |BeforeUnloadFired| callback is
+ // called with the proceed argument set to false, this causes
+ // |DevToolsWindow::OnPageCloseCancelled| to be called.
+
+ // Devtools window in undocked state is not set as a delegate of
+ // its frontend. Instead, an instance of browser is set as the delegate, and
+ // thus beforeunload event callback from devtools frontend is not delivered
+ // to the instance of devtools window, which is solely responsible for
+ // managing custom beforeunload event flow.
+ // This is a helper method to route callback from
+ // |Browser::BeforeUnloadFired| back to |DevToolsWindow::BeforeUnloadFired|.
+ // * |proceed| - true if the user clicked 'ok' in the beforeunload dialog,
+ // false otherwise.
+ // * |proceed_to_fire_unload| - output parameter, whether we should continue
+ // to fire the unload event or stop things here.
+ // Returns true if devtools window is in a state of intercepting beforeunload
+ // event and if it will manage unload process on its own.
+ static bool HandleBeforeUnload(content::WebContents* contents,
+ bool proceed,
+ bool* proceed_to_fire_unload);
+
+ // Returns true if this contents beforeunload event was intercepted by
+ // devtools and false otherwise. If the event was intercepted, caller should
+ // not fire beforeunlaod event on |contents| itself as devtools window will
+ // take care of it, otherwise caller should continue handling the event as
+ // usual.
+ static bool InterceptPageBeforeUnload(content::WebContents* contents);
+
+ // Returns true if devtools browser has already fired its beforeunload event
+ // as a result of beforeunload event interception.
+ static bool HasFiredBeforeUnloadEventForDevToolsBrowser(Browser* browser);
+
+ // Returns true if devtools window would like to hook beforeunload event
+ // of this |contents|.
+ static bool NeedsToInterceptBeforeUnload(content::WebContents* contents);
+
+ // Notify devtools window that closing of |contents| was cancelled
+ // by user.
+ static void OnPageCloseCanceled(content::WebContents* contents);
+
+ content::WebContents* GetInspectedWebContents();
+
+ private:
+ friend class DevToolsWindowTesting;
+ friend class DevToolsWindowCreationObserver;
+
+ using CreationCallback = base::Callback<void(DevToolsWindow*)>;
+ static void AddCreationCallbackForTest(const CreationCallback& callback);
+ static void RemoveCreationCallbackForTest(const CreationCallback& callback);
+
+ static void OpenDevToolsWindowForFrame(
+ Profile* profile,
+ const scoped_refptr<content::DevToolsAgentHost>& agent_host);
+
+ // DevTools lifecycle typically follows this way:
+ // - Toggle/Open: client call;
+ // - Create;
+ // - ScheduleShow: setup window to be functional, but not yet show;
+ // - DocumentOnLoadCompletedInMainFrame: frontend loaded;
+ // - SetIsDocked: frontend decided on docking state;
+ // - OnLoadCompleted: ready to present frontend;
+ // - Show: actually placing frontend WebContents to a Browser or docked place;
+ // - DoAction: perform action passed in Toggle/Open;
+ // - ...;
+ // - CloseWindow: initiates before unload handling;
+ // - CloseContents: destroys frontend;
+ // - DevToolsWindow is dead once it's main_web_contents dies.
+ enum LifeStage {
+ kNotLoaded,
+ kOnLoadFired, // Implies SetIsDocked was not yet called.
+ kIsDockedSet, // Implies DocumentOnLoadCompleted was not yet called.
+ kLoadCompleted,
+ kClosing
+ };
+
+ enum FrontendType {
+ kFrontendDefault,
+ kFrontendRemote,
+ kFrontendWorker,
+ kFrontendV8,
+ kFrontendNode
+ };
+
+ DevToolsWindow(FrontendType frontend_type,
+ Profile* profile,
+ content::WebContents* main_web_contents,
+ DevToolsUIBindings* bindings,
+ content::WebContents* inspected_web_contents,
+ bool can_dock);
+
+ // External frontend is always undocked.
+ static void OpenExternalFrontend(
+ Profile* profile,
+ const std::string& frontend_uri,
+ const scoped_refptr<content::DevToolsAgentHost>& agent_host,
+ FrontendType frontend_type);
+
+ static DevToolsWindow* Create(Profile* profile,
+ content::WebContents* inspected_web_contents,
+ FrontendType frontend_type,
+ const std::string& frontend_url,
+ bool can_dock,
+ const std::string& settings,
+ const std::string& panel);
+ static GURL GetDevToolsURL(Profile* profile,
+ FrontendType frontend_type,
+ const std::string& frontend_url,
+ bool can_dock,
+ const std::string& panel);
+
+ static DevToolsWindow* CreateDevToolsWindowForWorker(Profile* profile);
+ static void ToggleDevToolsWindow(
+ content::WebContents* web_contents,
+ bool force_open,
+ const DevToolsToggleAction& action,
+ const std::string& settings);
+
+ // content::WebContentsDelegate:
+ void ActivateContents(content::WebContents* contents) override;
+ void AddNewContents(content::WebContents* source,
+ content::WebContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_rect,
+ bool user_gesture,
+ bool* was_blocked) override;
+ void WebContentsCreated(
+ content::WebContents* source_contents,
+ int opener_render_process_id,
+ int opener_render_frame_id,
+ const std::string& frame_name,
+ const GURL& target_url,
+ content::WebContents* new_contents,
+ const base::Optional<content::WebContents::CreateParams>& create_params)
+ override;
+ void CloseContents(content::WebContents* source) override;
+ void ContentsZoomChange(bool zoom_in) override;
+ void BeforeUnloadFired(content::WebContents* tab,
+ bool proceed,
+ bool* proceed_to_fire_unload) override;
+ content::KeyboardEventProcessingResult PreHandleKeyboardEvent(
+ content::WebContents* source,
+ const content::NativeWebKeyboardEvent& event) override;
+ void HandleKeyboardEvent(
+ content::WebContents* source,
+ const content::NativeWebKeyboardEvent& event) override;
+ content::JavaScriptDialogManager* GetJavaScriptDialogManager(
+ content::WebContents* source) override;
+ content::ColorChooser* OpenColorChooser(
+ content::WebContents* web_contents,
+ SkColor color,
+ const std::vector<content::ColorSuggestion>& suggestions) override;
+ void RunFileChooser(content::RenderFrameHost* render_frame_host,
+ const content::FileChooserParams& params) override;
+ bool PreHandleGestureEvent(content::WebContents* source,
+ const blink::WebGestureEvent& event) override;
+ void ShowCertificateViewerInDevTools(
+ content::WebContents* web_contents,
+ scoped_refptr<net::X509Certificate> certificate) override;
+
+ // content::DevToolsUIBindings::Delegate overrides
+ void ActivateWindow() override;
+ void CloseWindow() override;
+ void Inspect(scoped_refptr<content::DevToolsAgentHost> host) override;
+ void SetInspectedPageBounds(const gfx::Rect& rect) override;
+ void InspectElementCompleted() override;
+ void SetIsDocked(bool is_docked) override;
+ void OpenInNewTab(const std::string& url) override;
+ void SetWhitelistedShortcuts(const std::string& message) override;
+ void SetEyeDropperActive(bool active) override;
+ void OpenNodeFrontend() override;
+ void InspectedContentsClosing() override;
+ void OnLoadCompleted() override;
+ void ReadyForTest() override;
+ InfoBarService* GetInfoBarService() override;
+ void RenderProcessGone(bool crashed) override;
+
+ void ColorPickedInEyeDropper(int r, int g, int b, int a);
+ void CreateDevToolsBrowser();
+ BrowserWindow* GetInspectedBrowserWindow();
+ void ScheduleShow(const DevToolsToggleAction& action);
+ void Show(const DevToolsToggleAction& action);
+ void DoAction(const DevToolsToggleAction& action);
+ void LoadCompleted();
+ void UpdateBrowserToolbar();
+ void UpdateBrowserWindow();
+
+ std::unique_ptr<ObserverWithAccessor> inspected_contents_observer_;
+
+ FrontendType frontend_type_;
+ Profile* profile_;
+ content::WebContents* main_web_contents_;
+ content::WebContents* toolbox_web_contents_;
+ DevToolsUIBindings* bindings_;
+ Browser* browser_;
+ bool is_docked_;
+ const bool can_dock_;
+ bool close_on_detach_;
+ LifeStage life_stage_;
+ DevToolsToggleAction action_on_load_;
+ DevToolsContentsResizingStrategy contents_resizing_strategy_;
+ // True if we're in the process of handling a beforeunload event originating
+ // from the inspected webcontents, see InterceptPageBeforeUnload for details.
+ bool intercepted_page_beforeunload_;
+ base::Closure load_completed_callback_;
+ base::Closure close_callback_;
+ bool ready_for_test_;
+ base::Closure ready_for_test_callback_;
+
+ base::TimeTicks inspect_element_start_time_;
+ std::unique_ptr<DevToolsEventForwarder> event_forwarder_;
+ std::unique_ptr<DevToolsEyeDropper> eye_dropper_;
+
+ friend class DevToolsEventForwarder;
+ DISALLOW_COPY_AND_ASSIGN(DevToolsWindow);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_
diff --git a/chromium/chrome/browser/devtools/devtools_window_testing.cc b/chromium/chrome/browser/devtools/devtools_window_testing.cc
new file mode 100644
index 00000000000..e46c36a02e4
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_window_testing.cc
@@ -0,0 +1,207 @@
+// 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/devtools/devtools_window_testing.h"
+
+#include "base/lazy_instance.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/devtools/devtools_window.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/test_utils.h"
+
+namespace {
+
+const char kHarnessScript[] = "Tests.js";
+
+typedef std::vector<DevToolsWindowTesting*> DevToolsWindowTestings;
+base::LazyInstance<DevToolsWindowTestings>::Leaky g_instances =
+ LAZY_INSTANCE_INITIALIZER;
+
+}
+
+DevToolsWindowTesting::DevToolsWindowTesting(DevToolsWindow* window)
+ : devtools_window_(window) {
+ DCHECK(window);
+ window->close_callback_ =
+ base::Bind(&DevToolsWindowTesting::WindowClosed, window);
+ g_instances.Get().push_back(this);
+}
+
+DevToolsWindowTesting::~DevToolsWindowTesting() {
+ DevToolsWindowTestings* instances = g_instances.Pointer();
+ DevToolsWindowTestings::iterator it(
+ std::find(instances->begin(), instances->end(), this));
+ DCHECK(it != instances->end());
+ instances->erase(it);
+ if (!close_callback_.is_null()) {
+ close_callback_.Run();
+ close_callback_ = base::Closure();
+ }
+}
+
+// static
+DevToolsWindowTesting* DevToolsWindowTesting::Get(DevToolsWindow* window) {
+ DevToolsWindowTesting* testing = DevToolsWindowTesting::Find(window);
+ if (!testing)
+ testing = new DevToolsWindowTesting(window);
+ return testing;
+}
+
+// static
+DevToolsWindowTesting* DevToolsWindowTesting::Find(DevToolsWindow* window) {
+ if (g_instances == NULL)
+ return NULL;
+ DevToolsWindowTestings* instances = g_instances.Pointer();
+ for (DevToolsWindowTestings::iterator it(instances->begin());
+ it != instances->end();
+ ++it) {
+ if ((*it)->devtools_window_ == window)
+ return *it;
+ }
+ return NULL;
+}
+
+Browser* DevToolsWindowTesting::browser() {
+ return devtools_window_->browser_;
+}
+
+content::WebContents* DevToolsWindowTesting::main_web_contents() {
+ return devtools_window_->main_web_contents_;
+}
+
+content::WebContents* DevToolsWindowTesting::toolbox_web_contents() {
+ return devtools_window_->toolbox_web_contents_;
+}
+
+void DevToolsWindowTesting::SetInspectedPageBounds(const gfx::Rect& bounds) {
+ devtools_window_->SetInspectedPageBounds(bounds);
+}
+
+void DevToolsWindowTesting::SetCloseCallback(const base::Closure& closure) {
+ close_callback_ = closure;
+}
+
+// static
+void DevToolsWindowTesting::WindowClosed(DevToolsWindow* window) {
+ DevToolsWindowTesting* testing = DevToolsWindowTesting::Find(window);
+ if (testing)
+ delete testing;
+}
+
+// static
+void DevToolsWindowTesting::WaitForDevToolsWindowLoad(DevToolsWindow* window) {
+ if (!window->ready_for_test_) {
+ scoped_refptr<content::MessageLoopRunner> runner =
+ new content::MessageLoopRunner;
+ window->ready_for_test_callback_ = runner->QuitClosure();
+ runner->Run();
+ }
+ base::string16 harness = base::UTF8ToUTF16(
+ content::DevToolsFrontendHost::GetFrontendResource(kHarnessScript));
+ window->main_web_contents_->GetMainFrame()->ExecuteJavaScript(harness);
+}
+
+// static
+DevToolsWindow* DevToolsWindowTesting::OpenDevToolsWindowSync(
+ content::WebContents* inspected_web_contents,
+ bool is_docked) {
+ std::string settings = is_docked ?
+ "{\"isUnderTest\": true, \"currentDockState\":\"\\\"bottom\\\"\"}" :
+ "{\"isUnderTest\": true, \"currentDockState\":\"\\\"undocked\\\"\"}";
+ scoped_refptr<content::DevToolsAgentHost> agent(
+ content::DevToolsAgentHost::GetOrCreateFor(inspected_web_contents));
+ DevToolsWindow::ToggleDevToolsWindow(
+ inspected_web_contents, true, DevToolsToggleAction::Show(), settings);
+ DevToolsWindow* window = DevToolsWindow::FindDevToolsWindow(agent.get());
+ WaitForDevToolsWindowLoad(window);
+ return window;
+}
+
+// static
+DevToolsWindow* DevToolsWindowTesting::OpenDevToolsWindowSync(
+ Browser* browser,
+ bool is_docked) {
+ return OpenDevToolsWindowSync(
+ browser->tab_strip_model()->GetActiveWebContents(), is_docked);
+}
+
+// static
+DevToolsWindow* DevToolsWindowTesting::OpenDevToolsWindowForWorkerSync(
+ Profile* profile, content::DevToolsAgentHost* worker_agent) {
+ DevToolsWindow::OpenDevToolsWindowForWorker(
+ profile, worker_agent);
+ DevToolsWindow* window = DevToolsWindow::FindDevToolsWindow(worker_agent);
+ WaitForDevToolsWindowLoad(window);
+ return window;
+}
+
+// static
+void DevToolsWindowTesting::CloseDevToolsWindow(
+ DevToolsWindow* window) {
+ if (window->is_docked_) {
+ window->CloseWindow();
+ } else {
+ window->browser_->window()->Close();
+ }
+}
+
+// static
+void DevToolsWindowTesting::CloseDevToolsWindowSync(
+ DevToolsWindow* window) {
+ scoped_refptr<content::MessageLoopRunner> runner =
+ new content::MessageLoopRunner;
+ DevToolsWindowTesting::Get(window)->SetCloseCallback(runner->QuitClosure());
+ CloseDevToolsWindow(window);
+ runner->Run();
+}
+
+// DevToolsWindowCreationObserver ---------------------------------------------
+
+DevToolsWindowCreationObserver::DevToolsWindowCreationObserver()
+ : creation_callback_(base::Bind(
+ &DevToolsWindowCreationObserver::DevToolsWindowCreated,
+ base::Unretained(this))) {
+ DevToolsWindow::AddCreationCallbackForTest(creation_callback_);
+}
+
+DevToolsWindowCreationObserver::~DevToolsWindowCreationObserver() {
+ DevToolsWindow::RemoveCreationCallbackForTest(creation_callback_);
+}
+
+void DevToolsWindowCreationObserver::Wait() {
+ if (devtools_windows_.size())
+ return;
+ runner_ = new content::MessageLoopRunner();
+ runner_->Run();
+}
+
+void DevToolsWindowCreationObserver::WaitForLoad() {
+ Wait();
+ if (devtools_window())
+ DevToolsWindowTesting::WaitForDevToolsWindowLoad(devtools_window());
+}
+
+void DevToolsWindowCreationObserver::DevToolsWindowCreated(
+ DevToolsWindow* devtools_window) {
+ devtools_windows_.push_back(devtools_window);
+ if (runner_.get()) {
+ runner_->QuitClosure().Run();
+ runner_ = nullptr;
+ }
+}
+
+DevToolsWindow* DevToolsWindowCreationObserver::devtools_window() {
+ return !devtools_windows_.empty() ? devtools_windows_.back() : nullptr;
+}
+
+void DevToolsWindowCreationObserver::CloseAllSync() {
+ for (DevToolsWindow* window : devtools_windows_)
+ DevToolsWindowTesting::CloseDevToolsWindowSync(window);
+ devtools_windows_.clear();
+}
diff --git a/chromium/chrome/browser/devtools/devtools_window_testing.h b/chromium/chrome/browser/devtools/devtools_window_testing.h
new file mode 100644
index 00000000000..19fd9ee55d8
--- /dev/null
+++ b/chromium/chrome/browser/devtools/devtools_window_testing.h
@@ -0,0 +1,88 @@
+// 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_DEVTOOLS_DEVTOOLS_WINDOW_TESTING_H_
+#define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_TESTING_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "chrome/browser/devtools/devtools_window.h"
+#include "ui/gfx/geometry/rect.h"
+
+class Browser;
+class Profile;
+
+namespace content {
+class DevToolsAgentHost;
+class MessageLoopRunner;
+class WebContents;
+}
+
+class DevToolsWindowTesting {
+ public:
+ virtual ~DevToolsWindowTesting();
+
+ // The following methods block until DevToolsWindow is completely loaded.
+ static DevToolsWindow* OpenDevToolsWindowSync(
+ content::WebContents* inspected_web_contents,
+ bool is_docked);
+ static DevToolsWindow* OpenDevToolsWindowSync(
+ Browser* browser, bool is_docked);
+ static DevToolsWindow* OpenDevToolsWindowForWorkerSync(
+ Profile* profile, content::DevToolsAgentHost* worker_agent);
+
+ // Closes the window like it was user-initiated.
+ static void CloseDevToolsWindow(DevToolsWindow* window);
+ // Blocks until window is closed.
+ static void CloseDevToolsWindowSync(DevToolsWindow* window);
+
+ static DevToolsWindowTesting* Get(DevToolsWindow* window);
+
+ Browser* browser();
+ content::WebContents* main_web_contents();
+ content::WebContents* toolbox_web_contents();
+ void SetInspectedPageBounds(const gfx::Rect& bounds);
+ void SetCloseCallback(const base::Closure& closure);
+
+ private:
+ friend class DevToolsWindow;
+ friend class DevToolsWindowCreationObserver;
+
+ explicit DevToolsWindowTesting(DevToolsWindow* window);
+ static void WaitForDevToolsWindowLoad(DevToolsWindow* window);
+ static void WindowClosed(DevToolsWindow* window);
+ static DevToolsWindowTesting* Find(DevToolsWindow* window);
+
+ DevToolsWindow* devtools_window_;
+ base::Closure close_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsWindowTesting);
+};
+
+class DevToolsWindowCreationObserver {
+ public:
+ DevToolsWindowCreationObserver();
+ ~DevToolsWindowCreationObserver();
+
+ using DevToolsWindows = std::vector<DevToolsWindow*>;
+ const DevToolsWindows& devtools_windows() { return devtools_windows_; }
+ DevToolsWindow* devtools_window();
+
+ void Wait();
+ void WaitForLoad();
+ void CloseAllSync();
+
+ private:
+ friend class DevToolsWindow;
+
+ void DevToolsWindowCreated(DevToolsWindow* devtools_window);
+
+ base::Callback<void(DevToolsWindow*)> creation_callback_;
+ DevToolsWindows devtools_windows_;
+ scoped_refptr<content::MessageLoopRunner> runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsWindowCreationObserver);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_TESTING_H_
diff --git a/chromium/chrome/browser/devtools/frontend/devtools_discovery_page.html b/chromium/chrome/browser/devtools/frontend/devtools_discovery_page.html
new file mode 100644
index 00000000000..d10b8588f21
--- /dev/null
+++ b/chromium/chrome/browser/devtools/frontend/devtools_discovery_page.html
@@ -0,0 +1,197 @@
+<html>
+<head>
+<title>Inspectable pages</title>
+<style>
+body {
+ color: #222;
+ font-family: Helvetica, Arial, sans-serif;
+ margin: 0;
+ text-shadow: rgba(255, 255, 255, 0.496094) 0px 1px 0px;
+}
+
+#caption {
+ font-size: 16px;
+ margin-top: 15px;
+ margin-bottom: 10px;
+ margin-left: 20px;
+ height: 20px;
+ text-align: left;
+}
+
+#items {
+ display: flex;
+ flex-direction: column;
+ margin: 10px;
+}
+
+.item {
+ color: #222;
+ display: flex;
+ flex-direction: row;
+ text-decoration: none;
+ padding: 10px;
+ transition-property: background-color, border-color;
+ transition: background-color 0.15s, 0.15s;
+ transition-delay: 0ms, 0ms;
+}
+
+.item:not(.connected):hover {
+ background-color: rgba(242, 242, 242, 1);
+ border-color: rgba(110, 116, 128, 1);
+ color: black;
+}
+
+.item.connected:hover {
+ border-color: rgba(184, 184, 184, 1);
+ color: rgb(110, 116, 128);
+}
+
+.item.custom {
+ cursor: pointer;
+}
+
+.description {
+ display: flex;
+ flex-direction: column;
+}
+
+.title, .subtitle, .custom-url {
+ font-size: 13px;
+ margin: 4px 0px 0px 6px;
+ overflow: hidden;
+ padding-left: 20px;
+}
+
+.title {
+ background-repeat: no-repeat;
+ background-size: 16px;
+ font-size: 15px;
+}
+
+.custom-url {
+ display: flex;
+}
+
+.custom-url-label {
+ flex: 0 0 auto;
+}
+
+.custom-url-value {
+ font-family: monospace;
+ margin-left: 1em;
+}
+
+</style>
+
+<script>
+
+function onLoad() {
+ var tabsListRequest = new XMLHttpRequest();
+ tabsListRequest.open('GET', '/json/list', true);
+ tabsListRequest.onreadystatechange = onReady;
+ tabsListRequest.send();
+}
+
+function onReady() {
+ if(this.readyState == 4 && this.status == 200) {
+ if(this.response != null)
+ var responseJSON = JSON.parse(this.response);
+ for (var i = 0; i < responseJSON.length; ++i)
+ appendItem(responseJSON[i]);
+ }
+}
+
+function customFrontendURL(url) {
+ if (!url || !window.location.hash)
+ return null;
+
+ var hashParams = new URLSearchParams(location.hash.substring(1));
+ if (!hashParams.get("custom"))
+ return null;
+
+ var searchIndex = url.indexOf("?");
+ if (searchIndex === -1)
+ return null;
+ var originalParams = url.substring(searchIndex + 1);
+ if (hashParams.get("experiments"))
+ originalParams += "&experiments=true";
+
+ return "chrome-devtools://devtools/custom/inspector.html?" + originalParams;
+}
+
+function appendItem(item_object) {
+ var item_element;
+ var customURL = customFrontendURL(item_object.devtoolsFrontendUrl);
+ if (customURL) {
+ item_element = document.createElement('div');
+ item_element.title = item_object.title;
+ item_element.className = 'custom';
+ } else if (item_object.devtoolsFrontendUrl) {
+ item_element = document.createElement('a');
+ item_element.href = item_object.devtoolsFrontendUrl;
+ item_element.title = item_object.title;
+ } else {
+ item_element = document.createElement('div');
+ item_element.className = 'connected';
+ item_element.title = 'The tab already has an active debug session';
+ }
+ item_element.classList.add('item');
+
+ var description = document.createElement('div');
+ description.className = 'description';
+
+ var title = document.createElement('div');
+ title.className = 'title';
+ title.textContent = item_object.description || item_object.title;
+ if (item_object.faviconUrl) {
+ title.style.cssText = 'background-image:url(' +
+ item_object.faviconUrl + ')';
+ }
+ description.appendChild(title);
+
+ var subtitle = document.createElement('div');
+ subtitle.className = 'subtitle';
+ subtitle.textContent = (item_object.url || '').substring(0, 300);
+ description.appendChild(subtitle);
+
+ if (customURL) {
+ var urlContainer = document.createElement('div');
+ urlContainer.classList.add("custom-url");
+ var urlLabel = document.createElement('div');
+ urlLabel.classList.add("custom-url-label");
+ urlLabel.textContent = "Click to copy URL:";
+ urlContainer.appendChild(urlLabel);
+ var urlValue = document.createElement('div');
+ urlValue.classList.add("custom-url-value");
+ urlValue.textContent = customURL;
+ urlContainer.appendChild(urlValue);
+ description.appendChild(urlContainer);
+ item_element.addEventListener('click', selectNodeText.bind(null, urlValue));
+ }
+
+ item_element.appendChild(description);
+
+ document.getElementById('items').appendChild(item_element);
+}
+
+function selectNodeText(selectElement, event)
+{
+ var selection = window.getSelection();
+ if (!selection.isCollapsed)
+ return;
+ var range = document.createRange();
+ range.selectNode(selectElement);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ event.stopPropagation();
+ event.preventDefault();
+}
+</script>
+</head>
+<body onload='onLoad()'>
+ <div id='caption'>Inspectable pages</div>
+ <hr>
+ <div id='items'>
+ </div>
+</body>
+</html>
diff --git a/chromium/chrome/browser/devtools/global_confirm_info_bar.cc b/chromium/chrome/browser/devtools/global_confirm_info_bar.cc
new file mode 100644
index 00000000000..a1851f9f2df
--- /dev/null
+++ b/chromium/chrome/browser/devtools/global_confirm_info_bar.cc
@@ -0,0 +1,224 @@
+// 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.
+
+#include "chrome/browser/devtools/global_confirm_info_bar.h"
+
+#include <utility>
+
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "components/infobars/core/infobar.h"
+#include "ui/gfx/image/image.h"
+
+// InfoBarDelegateProxy -------------------------------------------------------
+
+class GlobalConfirmInfoBar::DelegateProxy : public ConfirmInfoBarDelegate {
+ public:
+ explicit DelegateProxy(base::WeakPtr<GlobalConfirmInfoBar> global_info_bar);
+ ~DelegateProxy() override;
+ void Detach();
+
+ private:
+ friend class GlobalConfirmInfoBar;
+
+ // ConfirmInfoBarDelegate overrides
+ infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
+ base::string16 GetMessageText() const override;
+ int GetButtons() const override;
+ base::string16 GetButtonLabel(InfoBarButton button) const override;
+ bool Accept() override;
+ bool Cancel() override;
+ base::string16 GetLinkText() const override;
+ GURL GetLinkURL() const override;
+ bool LinkClicked(WindowOpenDisposition disposition) override;
+ bool EqualsDelegate(infobars::InfoBarDelegate* delegate) const override;
+ void InfoBarDismissed() override;
+
+ infobars::InfoBar* info_bar_;
+ base::WeakPtr<GlobalConfirmInfoBar> global_info_bar_;
+
+ DISALLOW_COPY_AND_ASSIGN(DelegateProxy);
+};
+
+GlobalConfirmInfoBar::DelegateProxy::DelegateProxy(
+ base::WeakPtr<GlobalConfirmInfoBar> global_info_bar)
+ : info_bar_(nullptr),
+ global_info_bar_(global_info_bar) {
+}
+
+GlobalConfirmInfoBar::DelegateProxy::~DelegateProxy() {
+}
+
+infobars::InfoBarDelegate::InfoBarIdentifier
+GlobalConfirmInfoBar::DelegateProxy::GetIdentifier() const {
+ return global_info_bar_ ? global_info_bar_->delegate_->GetIdentifier()
+ : INVALID;
+}
+
+base::string16 GlobalConfirmInfoBar::DelegateProxy::GetMessageText() const {
+ return global_info_bar_ ? global_info_bar_->delegate_->GetMessageText()
+ : base::string16();
+}
+
+int GlobalConfirmInfoBar::DelegateProxy::GetButtons() const {
+ return global_info_bar_ ? global_info_bar_->delegate_->GetButtons()
+ : 0;
+}
+
+base::string16 GlobalConfirmInfoBar::DelegateProxy::GetButtonLabel(
+ InfoBarButton button) const {
+ return global_info_bar_ ? global_info_bar_->delegate_->GetButtonLabel(button)
+ : base::string16();
+}
+
+bool GlobalConfirmInfoBar::DelegateProxy::Accept() {
+ base::WeakPtr<GlobalConfirmInfoBar> info_bar = global_info_bar_;
+ // Remove the current InfoBar (the one whose Accept button is being clicked)
+ // from the control of GlobalConfirmInfoBar. This InfoBar will be closed by
+ // caller of this method, and we don't need GlobalConfirmInfoBar to close it.
+ // Furthermore, letting GlobalConfirmInfoBar close the current InfoBar can
+ // cause memory corruption when InfoBar animation is disabled.
+ if (info_bar) {
+ info_bar->OnInfoBarRemoved(info_bar_, false);
+ info_bar->delegate_->Accept();
+ }
+ // Could be destroyed after this point.
+ if (info_bar)
+ info_bar->Close();
+ return true;
+}
+
+bool GlobalConfirmInfoBar::DelegateProxy::Cancel() {
+ base::WeakPtr<GlobalConfirmInfoBar> info_bar = global_info_bar_;
+ // See comments in GlobalConfirmInfoBar::DelegateProxy::Accept().
+ if (info_bar) {
+ info_bar->OnInfoBarRemoved(info_bar_, false);
+ info_bar->delegate_->Cancel();
+ }
+ // Could be destroyed after this point.
+ if (info_bar)
+ info_bar->Close();
+ return true;
+}
+
+base::string16 GlobalConfirmInfoBar::DelegateProxy::GetLinkText() const {
+ return global_info_bar_ ? global_info_bar_->delegate_->GetLinkText()
+ : base::string16();
+}
+
+GURL GlobalConfirmInfoBar::DelegateProxy::GetLinkURL() const {
+ return global_info_bar_ ? global_info_bar_->delegate_->GetLinkURL()
+ : GURL();
+}
+
+bool GlobalConfirmInfoBar::DelegateProxy::LinkClicked(
+ WindowOpenDisposition disposition) {
+ return global_info_bar_ ?
+ global_info_bar_->delegate_->LinkClicked(disposition) : false;
+}
+
+bool GlobalConfirmInfoBar::DelegateProxy::EqualsDelegate(
+ infobars::InfoBarDelegate* delegate) const {
+ return delegate == this;
+}
+
+void GlobalConfirmInfoBar::DelegateProxy::InfoBarDismissed() {
+ base::WeakPtr<GlobalConfirmInfoBar> info_bar = global_info_bar_;
+ // See comments in GlobalConfirmInfoBar::DelegateProxy::Accept().
+ if (info_bar) {
+ info_bar->OnInfoBarRemoved(info_bar_, false);
+ info_bar->delegate_->InfoBarDismissed();
+ }
+ // Could be destroyed after this point.
+ if (info_bar)
+ info_bar->Close();
+}
+
+void GlobalConfirmInfoBar::DelegateProxy::Detach() {
+ global_info_bar_.reset();
+}
+
+// GlobalConfirmInfoBar -------------------------------------------------------
+
+// static
+base::WeakPtr<GlobalConfirmInfoBar> GlobalConfirmInfoBar::Show(
+ std::unique_ptr<ConfirmInfoBarDelegate> delegate) {
+ GlobalConfirmInfoBar* info_bar =
+ new GlobalConfirmInfoBar(std::move(delegate));
+ return info_bar->weak_factory_.GetWeakPtr();
+}
+
+void GlobalConfirmInfoBar::Close() {
+ delete this;
+}
+
+GlobalConfirmInfoBar::GlobalConfirmInfoBar(
+ std::unique_ptr<ConfirmInfoBarDelegate> delegate)
+ : delegate_(std::move(delegate)),
+ browser_tab_strip_tracker_(this, nullptr, nullptr),
+ weak_factory_(this) {
+ browser_tab_strip_tracker_.Init();
+}
+
+GlobalConfirmInfoBar::~GlobalConfirmInfoBar() {
+ while (!proxies_.empty()) {
+ auto it = proxies_.begin();
+ it->second->Detach();
+ it->first->RemoveObserver(this);
+ it->first->RemoveInfoBar(it->second->info_bar_);
+ proxies_.erase(it);
+ }
+}
+
+void GlobalConfirmInfoBar::TabInsertedAt(TabStripModel* tab_strip_model,
+ content::WebContents* web_contents,
+ int index,
+ bool foreground) {
+ MaybeAddInfoBar(web_contents);
+}
+
+void GlobalConfirmInfoBar::TabChangedAt(content::WebContents* web_contents,
+ int index,
+ TabChangeType change_type) {
+ MaybeAddInfoBar(web_contents);
+}
+
+void GlobalConfirmInfoBar::OnInfoBarRemoved(infobars::InfoBar* info_bar,
+ bool animate) {
+ // Do not process alien infobars.
+ for (auto it : proxies_) {
+ if (it.second->info_bar_ == info_bar) {
+ OnManagerShuttingDown(info_bar->owner());
+ break;
+ }
+ }
+}
+
+void GlobalConfirmInfoBar::OnManagerShuttingDown(
+ infobars::InfoBarManager* manager) {
+ manager->RemoveObserver(this);
+ proxies_.erase(manager);
+}
+
+void GlobalConfirmInfoBar::MaybeAddInfoBar(content::WebContents* web_contents) {
+ InfoBarService* infobar_service =
+ InfoBarService::FromWebContents(web_contents);
+ // WebContents from the tab strip must have the infobar service.
+ DCHECK(infobar_service);
+ if (ContainsKey(proxies_, infobar_service))
+ return;
+
+ std::unique_ptr<GlobalConfirmInfoBar::DelegateProxy> proxy(
+ new GlobalConfirmInfoBar::DelegateProxy(weak_factory_.GetWeakPtr()));
+ GlobalConfirmInfoBar::DelegateProxy* proxy_ptr = proxy.get();
+ infobars::InfoBar* added_bar = infobar_service->AddInfoBar(
+ infobar_service->CreateConfirmInfoBar(std::move(proxy)));
+
+ proxy_ptr->info_bar_ = added_bar;
+ DCHECK(added_bar);
+ proxies_[infobar_service] = proxy_ptr;
+ infobar_service->AddObserver(this);
+}
diff --git a/chromium/chrome/browser/devtools/global_confirm_info_bar.h b/chromium/chrome/browser/devtools/global_confirm_info_bar.h
new file mode 100644
index 00000000000..06e27f3a7eb
--- /dev/null
+++ b/chromium/chrome/browser/devtools/global_confirm_info_bar.h
@@ -0,0 +1,63 @@
+// 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_DEVTOOLS_GLOBAL_CONFIRM_INFO_BAR_H_
+#define CHROME_BROWSER_DEVTOOLS_GLOBAL_CONFIRM_INFO_BAR_H_
+
+#include <map>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/browser/ui/browser_tab_strip_tracker.h"
+#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
+#include "components/infobars/core/confirm_infobar_delegate.h"
+
+namespace content {
+class WebContents;
+}
+
+// GlobalConfirmInfoBar is shown for every tab in every browser until it
+// is dismissed or the close method is called.
+// It listens to all tabs in all browsers and adds/removes confirm infobar
+// to each of the tabs.
+class GlobalConfirmInfoBar : public TabStripModelObserver,
+ public infobars::InfoBarManager::Observer {
+ public:
+ static base::WeakPtr<GlobalConfirmInfoBar> Show(
+ std::unique_ptr<ConfirmInfoBarDelegate> delegate);
+ void Close();
+
+ // infobars::InfoBarManager::Observer:
+ void OnInfoBarRemoved(infobars::InfoBar* info_bar, bool animate) override;
+ void OnManagerShuttingDown(infobars::InfoBarManager* manager) override;
+
+ private:
+ explicit GlobalConfirmInfoBar(
+ std::unique_ptr<ConfirmInfoBarDelegate> delegate);
+ ~GlobalConfirmInfoBar() override;
+ class DelegateProxy;
+
+ // TabStripModelObserver:
+ void TabInsertedAt(TabStripModel* tab_strip_model,
+ content::WebContents* web_contents,
+ int index,
+ bool foreground) override;
+ void TabChangedAt(content::WebContents* web_contents,
+ int index,
+ TabChangeType change_type) override;
+
+ // Adds the info bar to the tab if it is missing.
+ void MaybeAddInfoBar(content::WebContents* web_contents);
+
+ std::unique_ptr<ConfirmInfoBarDelegate> delegate_;
+ std::map<infobars::InfoBarManager*, DelegateProxy*> proxies_;
+ BrowserTabStripTracker browser_tab_strip_tracker_;
+
+ base::WeakPtrFactory<GlobalConfirmInfoBar> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(GlobalConfirmInfoBar);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_GLOBAL_CONFIRM_INFO_BAR_H_
diff --git a/chromium/chrome/browser/devtools/remote_debugging_server.cc b/chromium/chrome/browser/devtools/remote_debugging_server.cc
new file mode 100644
index 00000000000..2cc82280262
--- /dev/null
+++ b/chromium/chrome/browser/devtools/remote_debugging_server.cc
@@ -0,0 +1,125 @@
+// 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/devtools/remote_debugging_server.h"
+
+#include <utility>
+
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/path_service.h"
+#include "base/strings/string_number_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/devtools/devtools_window.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/common/chrome_content_client.h"
+#include "chrome/common/chrome_paths.h"
+#include "components/version_info/version_info.h"
+#include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/devtools_socket_factory.h"
+#include "net/base/net_errors.h"
+#include "net/log/net_log_source.h"
+#include "net/socket/tcp_server_socket.h"
+#include "third_party/WebKit/public/public_features.h"
+#include "ui/base/resource/resource_bundle.h"
+
+namespace {
+
+base::LazyInstance<bool>::Leaky g_tethering_enabled = LAZY_INSTANCE_INITIALIZER;
+
+const uint16_t kMinTetheringPort = 9333;
+const uint16_t kMaxTetheringPort = 9444;
+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) {}
+
+ private:
+ std::unique_ptr<net::ServerSocket> CreateLocalHostServerSocket(int port) {
+ std::unique_ptr<net::ServerSocket> socket(
+ new net::TCPServerSocket(nullptr, net::NetLogSource()));
+ if (socket->ListenWithAddressAndPort(
+ "127.0.0.1", port, kBackLog) == net::OK)
+ return socket;
+ if (socket->ListenWithAddressAndPort("::1", port, kBackLog) == net::OK)
+ return socket;
+ return std::unique_ptr<net::ServerSocket>();
+ }
+
+ // content::DevToolsSocketFactory.
+ 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>();
+ }
+
+ std::unique_ptr<net::ServerSocket> CreateForTethering(
+ std::string* name) override {
+ if (!g_tethering_enabled.Get())
+ return std::unique_ptr<net::ServerSocket>();
+
+ if (last_tethering_port_ == kMaxTetheringPort)
+ last_tethering_port_ = kMinTetheringPort;
+ uint16_t port = ++last_tethering_port_;
+ *name = base::UintToString(port);
+ return CreateLocalHostServerSocket(port);
+ }
+
+ std::string address_;
+ uint16_t port_;
+ uint16_t last_tethering_port_;
+
+ DISALLOW_COPY_AND_ASSIGN(TCPServerSocketFactory);
+};
+
+} // namespace
+
+// static
+void RemoteDebuggingServer::EnableTetheringForDebug() {
+ g_tethering_enabled.Get() = true;
+}
+
+RemoteDebuggingServer::RemoteDebuggingServer(const std::string& ip,
+ uint16_t port) {
+ base::FilePath output_dir;
+ if (!port) {
+ // The client requested an ephemeral port. Must write the selected
+ // port to a well-known location in the profile directory to
+ // bootstrap the connection process.
+ bool result = PathService::Get(chrome::DIR_USER_DATA, &output_dir);
+ DCHECK(result);
+ }
+
+ base::FilePath debug_frontend_dir;
+#if BUILDFLAG(DEBUG_DEVTOOLS)
+ PathService::Get(chrome::DIR_INSPECTOR_DEBUG, &debug_frontend_dir);
+#endif
+
+ content::DevToolsAgentHost::StartRemoteDebuggingServer(
+ base::MakeUnique<TCPServerSocketFactory>(ip, port),
+ std::string(),
+ output_dir,
+ debug_frontend_dir,
+ version_info::GetProductNameAndVersionForUserAgent(),
+ ::GetUserAgent());
+}
+
+RemoteDebuggingServer::~RemoteDebuggingServer() {
+ // Ensure Profile is alive, because the whole DevTools subsystem
+ // accesses it during shutdown.
+ DCHECK(g_browser_process->profile_manager());
+ content::DevToolsAgentHost::StopRemoteDebuggingServer();
+}
diff --git a/chromium/chrome/browser/devtools/remote_debugging_server.h b/chromium/chrome/browser/devtools/remote_debugging_server.h
new file mode 100644
index 00000000000..e2ba3e78440
--- /dev/null
+++ b/chromium/chrome/browser/devtools/remote_debugging_server.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2011 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_DEVTOOLS_REMOTE_DEBUGGING_SERVER_H_
+#define CHROME_BROWSER_DEVTOOLS_REMOTE_DEBUGGING_SERVER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+
+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);
+ virtual ~RemoteDebuggingServer();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RemoteDebuggingServer);
+};
+
+#endif // CHROME_BROWSER_DEVTOOLS_REMOTE_DEBUGGING_SERVER_H_
diff --git a/chromium/chrome/browser/devtools/serialize_host_descriptions.cc b/chromium/chrome/browser/devtools/serialize_host_descriptions.cc
new file mode 100644
index 00000000000..26aa0206369
--- /dev/null
+++ b/chromium/chrome/browser/devtools/serialize_host_descriptions.cc
@@ -0,0 +1,106 @@
+// 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/devtools/serialize_host_descriptions.h"
+
+#include <map>
+#include <unordered_set>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_piece.h"
+
+namespace {
+
+// Returns the serialization of |root|. It expects |children[x]| to be the
+// vector of child nodes for all descendants |x| of |root|. The serialization
+// consists of taking the |representation| value of each node, starting in
+// leaves, and injecting children's representations into a ListValue under the
+// key |child_key| in the parent's |representation|. This is desctructive to the
+// representation stored with the nodes (which gets moved out of them).
+base::DictionaryValue Serialize(
+ base::StringPiece child_key,
+ base::DictionaryValue* root,
+ const std::map<base::DictionaryValue*, std::vector<base::DictionaryValue*>>&
+ children) {
+ auto children_list = base::MakeUnique<base::ListValue>();
+ auto child_it = children.find(root);
+ if (child_it != children.end()) {
+ for (base::DictionaryValue* child : child_it->second) {
+ children_list->base::Value::GetList().push_back(
+ Serialize(child_key, child, children));
+ }
+ }
+
+ if (!children_list->empty())
+ root->Set(child_key, std::move(children_list));
+ return std::move(*root);
+}
+
+// Takes a vector of host description and converts it into:
+// |children|: a map from a host's representation to representations of its
+// children,
+// |roots|: a set of representations of hosts with no parents, and
+// |representations|: a vector actually storing all those representations to
+// which the rest just points.
+void CreateDictionaryForest(
+ std::vector<HostDescriptionNode> hosts,
+ std::map<base::DictionaryValue*, std::vector<base::DictionaryValue*>>*
+ children,
+ std::unordered_set<base::DictionaryValue*>* roots,
+ std::vector<base::DictionaryValue>* representations) {
+ representations->reserve(hosts.size());
+ children->clear();
+ roots->clear();
+ representations->clear();
+
+ std::map<base::StringPiece, base::DictionaryValue*> name_to_representation;
+
+ // First move the representations and map the names to them.
+ for (HostDescriptionNode& node : hosts) {
+ representations->push_back(std::move(node.representation));
+ // If there are multiple nodes with the same name, subsequent insertions
+ // will be ignored, so only the first node with a given name will be
+ // referenced by |roots| and |children|.
+ name_to_representation.emplace(node.name, &representations->back());
+ }
+
+ // Now compute children.
+ for (HostDescriptionNode& node : hosts) {
+ base::DictionaryValue* node_rep = name_to_representation[node.name];
+ base::StringPiece parent_name = node.parent_name;
+ if (parent_name.empty()) {
+ roots->insert(node_rep);
+ continue;
+ }
+ auto node_it = name_to_representation.find(parent_name);
+ if (node_it == name_to_representation.end()) {
+ roots->insert(node_rep);
+ continue;
+ }
+ (*children)[name_to_representation[parent_name]].push_back(node_rep);
+ }
+}
+
+} // namespace
+
+base::ListValue SerializeHostDescriptions(
+ std::vector<HostDescriptionNode> hosts,
+ base::StringPiece child_key) {
+ // |representations| must outlive |children| and |roots|, which contain
+ // pointers to objects in |representations|.
+ std::vector<base::DictionaryValue> representations;
+ std::map<base::DictionaryValue*, std::vector<base::DictionaryValue*>>
+ children;
+ std::unordered_set<base::DictionaryValue*> roots;
+
+ CreateDictionaryForest(std::move(hosts), &children, &roots, &representations);
+
+ base::ListValue list_value;
+ for (auto* root : roots) {
+ list_value.base::Value::GetList().push_back(
+ Serialize(child_key, root, children));
+ }
+ return list_value;
+}
diff --git a/chromium/chrome/browser/devtools/serialize_host_descriptions.h b/chromium/chrome/browser/devtools/serialize_host_descriptions.h
new file mode 100644
index 00000000000..41779014748
--- /dev/null
+++ b/chromium/chrome/browser/devtools/serialize_host_descriptions.h
@@ -0,0 +1,29 @@
+// 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_DEVTOOLS_SERIALIZE_HOST_DESCRIPTIONS_H_
+#define CHROME_BROWSER_DEVTOOLS_SERIALIZE_HOST_DESCRIPTIONS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "base/values.h"
+
+// DevToolsAgentHost description to be serialized by SerializeHostDescriptions.
+struct HostDescriptionNode {
+ std::string name;
+ std::string parent_name;
+ base::DictionaryValue representation;
+};
+
+// A helper function taking a HostDescriptionNode representation of hosts and
+// producing a ListValue representation. The representation contains a list of
+// DictionaryValue for each rooti host, and has DictionaryValues of children
+// injected into a ListValue keyed |child_key| in the parent's DictionaryValue.
+base::ListValue SerializeHostDescriptions(
+ std::vector<HostDescriptionNode> hosts,
+ base::StringPiece child_key);
+
+#endif // CHROME_BROWSER_DEVTOOLS_SERIALIZE_HOST_DESCRIPTIONS_H_
diff --git a/chromium/chrome/browser/devtools/serialize_host_descriptions_unittest.cc b/chromium/chrome/browser/devtools/serialize_host_descriptions_unittest.cc
new file mode 100644
index 00000000000..856dc1a68ba
--- /dev/null
+++ b/chromium/chrome/browser/devtools/serialize_host_descriptions_unittest.cc
@@ -0,0 +1,138 @@
+// 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/devtools/serialize_host_descriptions.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/values.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::UnorderedElementsAre;
+
+namespace {
+
+HostDescriptionNode GetNodeWithLabel(const char* name, int label) {
+ HostDescriptionNode node = {name, std::string(), base::DictionaryValue()};
+ node.representation.SetInteger("label", label);
+ return node;
+}
+
+// Returns the list of children of |arg|.
+const base::Value* GetChildren(const base::Value& arg) {
+ const base::DictionaryValue* dict = nullptr;
+ EXPECT_TRUE(arg.GetAsDictionary(&dict));
+
+ const base::Value* children = nullptr;
+ if (!dict->Get("children", &children))
+ return nullptr;
+ EXPECT_EQ(base::Value::Type::LIST, children->type());
+ return children;
+}
+
+// Checks that |arg| is a description of a node with label |l|.
+bool CheckLabel(const base::Value& arg, int l) {
+ const base::DictionaryValue* dict = nullptr;
+ EXPECT_TRUE(arg.GetAsDictionary(&dict));
+ int result = 0;
+ if (!dict->GetInteger("label", &result))
+ return false;
+ return l == result;
+}
+
+// Matches every |arg| with label |label| and checks that it has no children.
+MATCHER_P(EmptyNode, label, "") {
+ if (!CheckLabel(arg, label))
+ return false;
+ EXPECT_FALSE(GetChildren(arg));
+ return true;
+}
+
+} // namespace
+
+TEST(SerializeHostDescriptionTest, Empty) {
+ base::ListValue result =
+ SerializeHostDescriptions(std::vector<HostDescriptionNode>(), "123");
+ EXPECT_THAT(result.base::Value::GetList(), ::testing::IsEmpty());
+}
+
+// Test serializing a forest of stubs (no edges).
+TEST(SerializeHostDescriptionTest, Stubs) {
+ base::ListValue result = SerializeHostDescriptions(
+ {GetNodeWithLabel("1", 1), GetNodeWithLabel("2", 2),
+ GetNodeWithLabel("3", 3)},
+ "children");
+ EXPECT_THAT(result.base::Value::GetList(),
+ UnorderedElementsAre(EmptyNode(1), EmptyNode(2), EmptyNode(3)));
+}
+
+// Test handling multiple nodes sharing the same name.
+TEST(SerializeHostDescriptionTest, SameNames) {
+ std::vector<HostDescriptionNode> nodes = {
+ GetNodeWithLabel("A", 1), GetNodeWithLabel("A", 2),
+ GetNodeWithLabel("A", 3), GetNodeWithLabel("B", 4),
+ GetNodeWithLabel("C", 5)};
+
+ base::ListValue result =
+ SerializeHostDescriptions(std::move(nodes), "children");
+
+ // Only the first node called "A", and both nodes "B" and "C" should be
+ // returned.
+ EXPECT_THAT(result.base::Value::GetList(),
+ UnorderedElementsAre(EmptyNode(1), EmptyNode(4), EmptyNode(5)));
+}
+
+// Test serializing a small forest, of this structure:
+// 5 -- 2 -- 4
+// 0 -- 6
+// \ 1
+// \ 3
+
+namespace {
+
+// Matchers for non-empty nodes specifically in this test:
+MATCHER(Node2, "") {
+ if (!CheckLabel(arg, 2))
+ return false;
+ EXPECT_THAT(GetChildren(arg)->GetList(), UnorderedElementsAre(EmptyNode(4)));
+ return true;
+}
+
+MATCHER(Node5, "") {
+ if (!CheckLabel(arg, 5))
+ return false;
+ EXPECT_THAT(GetChildren(arg)->GetList(), UnorderedElementsAre(Node2()));
+ return true;
+}
+
+MATCHER(Node0, "") {
+ if (!CheckLabel(arg, 0))
+ return false;
+ EXPECT_THAT(GetChildren(arg)->GetList(),
+ UnorderedElementsAre(EmptyNode(1), EmptyNode(3), EmptyNode(6)));
+ return true;
+}
+
+} // namespace
+
+TEST(SerializeHostDescriptionTest, Forest) {
+ std::vector<HostDescriptionNode> nodes(7);
+ const char* kNames[] = {"0", "1", "2", "3", "4", "5", "6"};
+ for (size_t i = 0; i < 7; ++i) {
+ nodes[i] = GetNodeWithLabel(kNames[i], i);
+ }
+ nodes[2].parent_name = "5";
+ nodes[4].parent_name = "2";
+ nodes[6].parent_name = "0";
+ nodes[1].parent_name = "0";
+ nodes[3].parent_name = "0";
+
+ base::ListValue result =
+ SerializeHostDescriptions(std::move(nodes), "children");
+
+ EXPECT_THAT(result.base::Value::GetList(),
+ UnorderedElementsAre(Node0(), Node5()));
+}
diff --git a/chromium/chrome/browser/devtools/url_constants.cc b/chromium/chrome/browser/devtools/url_constants.cc
new file mode 100644
index 00000000000..bab37d36dd1
--- /dev/null
+++ b/chromium/chrome/browser/devtools/url_constants.cc
@@ -0,0 +1,10 @@
+// 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 "chrome/browser/devtools/url_constants.h"
+
+const char kRemoteFrontendDomain[] = "chrome-devtools-frontend.appspot.com";
+const char kRemoteFrontendBase[] =
+ "https://chrome-devtools-frontend.appspot.com/";
+const char kRemoteFrontendPath[] = "serve_file";
diff --git a/chromium/chrome/browser/devtools/url_constants.h b/chromium/chrome/browser/devtools/url_constants.h
new file mode 100644
index 00000000000..48361e57e8b
--- /dev/null
+++ b/chromium/chrome/browser/devtools/url_constants.h
@@ -0,0 +1,12 @@
+// 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.
+
+#ifndef CHROME_BROWSER_DEVTOOLS_URL_CONSTANTS_H_
+#define CHROME_BROWSER_DEVTOOLS_URL_CONSTANTS_H_
+
+extern const char kRemoteFrontendDomain[];
+extern const char kRemoteFrontendBase[];
+extern const char kRemoteFrontendPath[];
+
+#endif // CHROME_BROWSER_DEVTOOLS_URL_CONSTANTS_H_
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 864c59507fa..1695aae3d44 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
@@ -462,7 +462,8 @@ Polymer({
var type = networkProperties.Type;
if (type != CrOnc.Type.WI_FI && type != CrOnc.Type.VPN)
return false;
- return this.isRemembered_(networkProperties);
+ return !this.isPolicySource(networkProperties.Source) &&
+ this.isRemembered_(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 db4af93867c..b0df9acbf1f 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
@@ -9,7 +9,11 @@
<dom-module id="settings-internet-known-networks-page">
<template>
- <style include="settings-shared"></style>
+ <style include="internet-shared">
+ cr-policy-indicator {
+ -webkit-margin-start: var(--settings-controlled-by-spacing);
+ }
+ </style>
<div class="settings-box first">
<div>$i18n{knownNetworksMessage}</div>
@@ -27,6 +31,11 @@
filter="networkIsPreferred_">
<div class="list-item">
<div class="start">[[item.Name]]</div>
+ <template is="dom-if" if="[[isPolicySource(item.Source))]]">
+ <cr-policy-indicator
+ indicator-type="[[getIndicatorTypeForSource(item.Source)]]">
+ </cr-policy-indicator>
+ </template>
<button class="subpage-arrow" is="paper-icon-button-light"
actionable on-tap="fireShowDetails_" tabindex$="[[tabindex]]"
aria-label$="[[item.Name]]">
@@ -49,6 +58,11 @@
filter="networkIsNotPreferred_">
<div class="list-item">
<div class="start">[[item.Name]]</div>
+ <template is="dom-if" if="[[isPolicySource(item.Source))]]">
+ <cr-policy-indicator
+ indicator-type="[[getIndicatorTypeForSource(item.Source)]]">
+ </cr-policy-indicator>
+ </template>
<button class="subpage-arrow" is="paper-icon-button-light"
actionable on-tap="fireShowDetails_" tabindex$="[[tabindex]]"
aria-label$="[[item.Name]]">
@@ -70,7 +84,8 @@
hidden="[[!showRemovePreferred_]]" on-tap="onRemovePreferredTap_">
$i18n{knownNetworksMenuRemovePreferred}
</button>
- <button class="dropdown-item" role="option" on-tap="onForgetTap_">
+ <button class="dropdown-item" disabled="[[!enableForget_]]" role="option"
+ on-tap="onForgetTap_">
$i18n{knownNetworksMenuForget}
</button>
</dialog>
diff --git a/chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js b/chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js
index cc333ac2e27..9c7c4a16438 100644
--- a/chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js
+++ b/chromium/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js
@@ -44,6 +44,14 @@ Polymer({
/** @private */
showRemovePreferred_: Boolean,
+
+ /**
+ * We always show 'Forget' since we do not know whether or not to enable
+ * it until we fetch the managed properties, and we do not want an empty
+ * menu.
+ * @private
+ */
+ enableForget_: Boolean,
},
/** @private {string} */
@@ -155,8 +163,8 @@ Polymer({
this.networkingPrivate.getManagedProperties(
this.selectedGuid_, function(properties) {
if (chrome.runtime.lastError || !properties) {
- this.showAddPreferred_ = false;
- this.showRemovePreferred_ = false;
+ console.error(
+ 'Unexpected error: ' + chrome.runtime.lastError.message);
return;
}
var preferred = button.hasAttribute('preferred');
@@ -167,6 +175,7 @@ Polymer({
this.showAddPreferred_ = !preferred;
this.showRemovePreferred_ = preferred;
}
+ this.enableForget_ = !this.isPolicySource(properties.Source);
/** @type {!CrActionMenuElement} */ (this.$.dotsMenu).showAt(button);
}.bind(this));
event.stopPropagation();
diff --git a/chromium/chrome/browser/ui/webui/DEPS b/chromium/chrome/browser/ui/webui/DEPS
new file mode 100644
index 00000000000..e54b94adcde
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/DEPS
@@ -0,0 +1,24 @@
+include_rules = [
+ "+components/invalidation",
+ "+components/onc",
+ "+components/proximity_auth",
+ "+components/sync/engine/events",
+ "+components/sync/sessions",
+ "+components/sync/base/weak_handle.h",
+ "+components/sync/js",
+ "+device/bluetooth",
+ "+extensions/strings/grit/extensions_strings.h",
+
+ # Generated files
+ "+js2webui/chrome/test/data",
+ "+third_party/cld",
+
+ # Other libraries.
+ "+third_party/angle", # For ANGLE version.
+ "+third_party/brotli", # For compressed resources.
+ "+third_party/zlib/zlib.h", # For compression level constants.
+
+ # DOM Distiller.
+ "+components/dom_distiller/core",
+ "+components/dom_distiller/webui",
+]
diff --git a/chromium/chrome/browser/ui/webui/OWNERS b/chromium/chrome/browser/ui/webui/OWNERS
new file mode 100644
index 00000000000..38e940d6cd1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/OWNERS
@@ -0,0 +1,28 @@
+file://ui/webui/PLATFORM_OWNERS
+
+per-file devtools_ui*=dgozman@chromium.org
+per-file devtools_ui*=pfeldman@chromium.org
+per-file inspect_ui*=dgozman@chromium.org
+per-file inspect_ui*=pfeldman@chromium.org
+
+# chrome://history is going through a lot of changes right now; make sure to
+# talk to one of the listed OWNERS before changing the pre-Material Design UI.
+per-file *history*=set noparent
+per-file *history*=calamity@chromium.org
+per-file *history*=dbeam@chromium.org
+per-file *history*=tsergeant@chromium.org
+
+per-file md_history_ui*=calamity@chromium.org
+per-file md_history_ui*=tsergeant@chromium.org
+
+per-file signin_internals_ui*=achuith@chromium.org
+
+per-file snippets_internals*=file://components/ntp_snippets/OWNERS
+per-file ntp_tiles_internals_ui.*=file://components/ntp_tiles/OWNERS
+per-file popular_sites_internals_ui.*=file://components/ntp_tiles/OWNERS
+per-file net_export_ui.*=file://net/OWNERS
+
+# Maintaining ownership from this file's original Chrome OS location.
+per-file device_log_ui*=stevenjb@chromium.org
+
+# COMPONENT: UI>Browser>WebUI
diff --git a/chromium/chrome/browser/ui/webui/PRESUBMIT.py b/chromium/chrome/browser/ui/webui/PRESUBMIT.py
new file mode 100644
index 00000000000..29efca16ab7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/PRESUBMIT.py
@@ -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.
+
+"""Presubmit script for files in chrome/browser/ui/webui.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+
+def _CheckWebDevStyle(input_api, output_api):
+ results = []
+ try:
+ import sys
+ old_sys_path = sys.path
+ cwd = input_api.PresubmitLocalPath()
+ sys.path += [input_api.os_path.join(cwd, '..', '..', '..', '..', 'tools')]
+ import web_dev_style.presubmit_support
+ results = web_dev_style.presubmit_support.CheckStyle(input_api, output_api)
+ finally:
+ sys.path = old_sys_path
+ return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+ return _CheckWebDevStyle(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+ return _CheckWebDevStyle(input_api, output_api)
diff --git a/chromium/chrome/browser/ui/webui/about_ui.cc b/chromium/chrome/browser/ui/webui/about_ui.cc
new file mode 100644
index 00000000000..f3922f56d61
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/about_ui.cc
@@ -0,0 +1,797 @@
+// 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/ui/webui/about_ui.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/format_macros.h"
+#include "base/i18n/number_formatting.h"
+#include "base/json/json_writer.h"
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/process/process_metrics.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.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/sys_info.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/threading/thread.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/about_flags.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/defaults.h"
+#include "chrome/browser/memory/tab_manager.h"
+#include "chrome/browser/memory/tab_stats.h"
+#include "chrome/browser/net/predictor.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/locale_settings.h"
+#include "components/grit/components_resources.h"
+#include "components/strings/grit/components_locale_settings.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/process_type.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "net/base/escape.h"
+#include "net/base/filename_util.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_status.h"
+#include "third_party/brotli/include/brotli/decode.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/jstemplate_builder.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "url/gurl.h"
+
+#if !defined(OS_ANDROID)
+#include "chrome/browser/ui/webui/theme_source.h"
+#endif
+
+#if defined(OS_WIN)
+#include "chrome/browser/win/enumerate_modules_model.h"
+#endif
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/browser_process_platform_part_chromeos.h"
+#include "chrome/browser/chromeos/customization/customization_document.h"
+#endif
+
+using base::Time;
+using base::TimeDelta;
+using content::BrowserThread;
+using content::WebContents;
+
+namespace {
+
+const char kCreditsJsPath[] = "credits.js";
+const char kStatsJsPath[] = "stats.js";
+const char kStringsJsPath[] = "strings.js";
+
+#if defined(OS_CHROMEOS)
+
+const char kKeyboardUtilsPath[] = "keyboard_utils.js";
+
+// chrome://terms falls back to offline page after kOnlineTermsTimeoutSec.
+const int kOnlineTermsTimeoutSec = 7;
+
+// Helper class that fetches the online Chrome OS terms. Empty string is
+// returned once fetching failed or exceeded |kOnlineTermsTimeoutSec|.
+class ChromeOSOnlineTermsHandler : public net::URLFetcherDelegate {
+ public:
+ typedef base::Callback<void (ChromeOSOnlineTermsHandler*)> FetchCallback;
+
+ explicit ChromeOSOnlineTermsHandler(const FetchCallback& callback,
+ const std::string& locale)
+ : fetch_callback_(callback) {
+ std::string eula_URL = base::StringPrintf(chrome::kOnlineEulaURLPath,
+ locale.c_str());
+ eula_fetcher_ =
+ net::URLFetcher::Create(0 /* ID used for testing */, GURL(eula_URL),
+ net::URLFetcher::GET, this);
+ eula_fetcher_->SetRequestContext(
+ g_browser_process->system_request_context());
+ eula_fetcher_->AddExtraRequestHeader("Accept: text/html");
+ eula_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SAVE_COOKIES |
+ net::LOAD_DISABLE_CACHE);
+ eula_fetcher_->Start();
+ // Abort the download attempt if it takes longer than one minute.
+ download_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromSeconds(kOnlineTermsTimeoutSec),
+ this,
+ &ChromeOSOnlineTermsHandler::OnDownloadTimeout);
+ }
+
+ void GetResponseResult(std::string* response_string) {
+ std::string mime_type;
+ if (!eula_fetcher_ ||
+ !eula_fetcher_->GetStatus().is_success() ||
+ eula_fetcher_->GetResponseCode() != 200 ||
+ !eula_fetcher_->GetResponseHeaders()->GetMimeType(&mime_type) ||
+ mime_type != "text/html" ||
+ !eula_fetcher_->GetResponseAsString(response_string)) {
+ response_string->clear();
+ }
+ }
+
+ private:
+ // Prevents allocation on the stack. ChromeOSOnlineTermsHandler should be
+ // created by 'operator new'. |this| takes care of destruction.
+ ~ChromeOSOnlineTermsHandler() override {}
+
+ // net::URLFetcherDelegate:
+ void OnURLFetchComplete(const net::URLFetcher* source) override {
+ if (source != eula_fetcher_.get()) {
+ NOTREACHED() << "Callback from foreign URL fetcher";
+ return;
+ }
+ fetch_callback_.Run(this);
+ delete this;
+ }
+
+ void OnDownloadTimeout() {
+ eula_fetcher_.reset();
+ fetch_callback_.Run(this);
+ delete this;
+ }
+
+ // Timer that enforces a timeout on the attempt to download the
+ // ChromeOS Terms.
+ base::OneShotTimer download_timer_;
+
+ // |fetch_callback_| called when fetching succeeded or failed.
+ FetchCallback fetch_callback_;
+
+ // Helper to fetch online eula.
+ std::unique_ptr<net::URLFetcher> eula_fetcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeOSOnlineTermsHandler);
+};
+
+class ChromeOSTermsHandler
+ : public base::RefCountedThreadSafe<ChromeOSTermsHandler> {
+ public:
+ static void Start(const std::string& path,
+ const content::URLDataSource::GotDataCallback& callback) {
+ scoped_refptr<ChromeOSTermsHandler> handler(
+ new ChromeOSTermsHandler(path, callback));
+ handler->StartOnUIThread();
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<ChromeOSTermsHandler>;
+
+ ChromeOSTermsHandler(const std::string& path,
+ const content::URLDataSource::GotDataCallback& callback)
+ : path_(path),
+ callback_(callback),
+ // Previously we were using "initial locale" http://crbug.com/145142
+ locale_(g_browser_process->GetApplicationLocale()) {
+ }
+
+ virtual ~ChromeOSTermsHandler() {}
+
+ void StartOnUIThread() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (path_ == chrome::kOemEulaURLPath) {
+ // Load local OEM EULA from the disk.
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&ChromeOSTermsHandler::LoadOemEulaFileOnFileThread, this));
+ } else {
+ // Try to load online version of ChromeOS terms first.
+ // ChromeOSOnlineTermsHandler object destroys itself.
+ new ChromeOSOnlineTermsHandler(
+ base::Bind(&ChromeOSTermsHandler::OnOnlineEULAFetched, this),
+ locale_);
+ }
+ }
+
+ void OnOnlineEULAFetched(ChromeOSOnlineTermsHandler* loader) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ loader->GetResponseResult(&contents_);
+ if (contents_.empty()) {
+ // Load local ChromeOS terms from the file.
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&ChromeOSTermsHandler::LoadEulaFileOnFileThread, this));
+ } else {
+ ResponseOnUIThread();
+ }
+ }
+
+ void LoadOemEulaFileOnFileThread() {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ const chromeos::StartupCustomizationDocument* customization =
+ chromeos::StartupCustomizationDocument::GetInstance();
+ if (customization->IsReady()) {
+ base::FilePath oem_eula_file_path;
+ if (net::FileURLToFilePath(GURL(customization->GetEULAPage(locale_)),
+ &oem_eula_file_path)) {
+ if (!base::ReadFileToString(oem_eula_file_path, &contents_)) {
+ contents_.clear();
+ }
+ }
+ }
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&ChromeOSTermsHandler::ResponseOnUIThread, this));
+ }
+
+ void LoadEulaFileOnFileThread() {
+ std::string file_path =
+ base::StringPrintf(chrome::kEULAPathFormat, locale_.c_str());
+ if (!base::ReadFileToString(base::FilePath(file_path), &contents_)) {
+ // No EULA for given language - try en-US as default.
+ file_path = base::StringPrintf(chrome::kEULAPathFormat, "en-US");
+ if (!base::ReadFileToString(base::FilePath(file_path), &contents_)) {
+ // File with EULA not found, ResponseOnUIThread will load EULA from
+ // resources if contents_ is empty.
+ contents_.clear();
+ }
+ }
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&ChromeOSTermsHandler::ResponseOnUIThread, this));
+ }
+
+ void ResponseOnUIThread() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ // If we fail to load Chrome OS EULA from disk, load it from resources.
+ // Do nothing if OEM EULA load failed.
+ if (contents_.empty() && path_ != chrome::kOemEulaURLPath)
+ contents_ = l10n_util::GetStringUTF8(IDS_TERMS_HTML);
+ callback_.Run(base::RefCountedString::TakeString(&contents_));
+ }
+
+ // Path in the URL.
+ const std::string path_;
+
+ // Callback to run with the response.
+ content::URLDataSource::GotDataCallback callback_;
+
+ // Locale of the EULA.
+ const std::string locale_;
+
+ // EULA contents that was loaded from file.
+ std::string contents_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeOSTermsHandler);
+};
+
+class ChromeOSCreditsHandler
+ : public base::RefCountedThreadSafe<ChromeOSCreditsHandler> {
+ public:
+ static void Start(const std::string& path,
+ const content::URLDataSource::GotDataCallback& callback) {
+ scoped_refptr<ChromeOSCreditsHandler> handler(
+ new ChromeOSCreditsHandler(path, callback));
+ handler->StartOnUIThread();
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<ChromeOSCreditsHandler>;
+
+ ChromeOSCreditsHandler(
+ const std::string& path,
+ const content::URLDataSource::GotDataCallback& callback)
+ : path_(path), callback_(callback) {}
+
+ virtual ~ChromeOSCreditsHandler() {}
+
+ void StartOnUIThread() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (path_ == kKeyboardUtilsPath) {
+ contents_ = ResourceBundle::GetSharedInstance()
+ .GetRawDataResource(IDR_KEYBOARD_UTILS_JS)
+ .as_string();
+ ResponseOnUIThread();
+ return;
+ }
+ // Load local Chrome OS credits from the disk.
+ base::PostTaskWithTraitsAndReply(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&ChromeOSCreditsHandler::LoadCreditsFileAsync, this),
+ base::Bind(&ChromeOSCreditsHandler::ResponseOnUIThread, this));
+ }
+
+ void LoadCreditsFileAsync() {
+ base::FilePath credits_file_path(chrome::kChromeOSCreditsPath);
+ if (!base::ReadFileToString(credits_file_path, &contents_)) {
+ // File with credits not found, ResponseOnUIThread will load credits
+ // from resources if contents_ is empty.
+ contents_.clear();
+ }
+ }
+
+ void ResponseOnUIThread() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ // If we fail to load Chrome OS credits from disk, load it from resources.
+ if (contents_.empty() && path_ != kKeyboardUtilsPath) {
+ contents_ = ResourceBundle::GetSharedInstance()
+ .GetRawDataResource(IDR_OS_CREDITS_HTML)
+ .as_string();
+ }
+ callback_.Run(base::RefCountedString::TakeString(&contents_));
+ }
+
+ // Path in the URL.
+ const std::string path_;
+
+ // Callback to run with the response.
+ content::URLDataSource::GotDataCallback callback_;
+
+ // Chrome OS credits contents that was loaded from file.
+ std::string contents_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeOSCreditsHandler);
+};
+#endif
+
+} // namespace
+
+// Individual about handlers ---------------------------------------------------
+
+namespace about_ui {
+
+void AppendHeader(std::string* output, int refresh,
+ const std::string& unescaped_title) {
+ output->append("<!DOCTYPE HTML>\n<html>\n<head>\n");
+ if (!unescaped_title.empty()) {
+ output->append("<title>");
+ output->append(net::EscapeForHTML(unescaped_title));
+ output->append("</title>\n");
+ }
+ output->append("<meta charset='utf-8'>\n");
+ if (refresh > 0) {
+ output->append("<meta http-equiv='refresh' content='");
+ output->append(base::IntToString(refresh));
+ output->append("'/>\n");
+ }
+}
+
+void AppendBody(std::string *output) {
+ output->append("</head>\n<body>\n");
+}
+
+void AppendFooter(std::string *output) {
+ output->append("</body>\n</html>\n");
+}
+
+} // namespace about_ui
+
+using about_ui::AppendHeader;
+using about_ui::AppendBody;
+using about_ui::AppendFooter;
+
+namespace {
+
+std::string ChromeURLs() {
+ std::string html;
+ AppendHeader(&html, 0, "Chrome URLs");
+ AppendBody(&html);
+ html += "<h2>List of Chrome URLs</h2>\n<ul>\n";
+ std::vector<std::string> hosts(
+ chrome::kChromeHostURLs,
+ chrome::kChromeHostURLs + chrome::kNumberOfChromeHostURLs);
+ std::sort(hosts.begin(), hosts.end());
+ for (std::vector<std::string>::const_iterator i = hosts.begin();
+ i != hosts.end(); ++i)
+ html += "<li><a href='chrome://" + *i + "/'>chrome://" + *i + "</a></li>\n";
+ html += "</ul>\n<h2>For Debug</h2>\n"
+ "<p>The following pages are for debugging purposes only. Because they "
+ "crash or hang the renderer, they're not linked directly; you can type "
+ "them into the address bar if you need them.</p>\n<ul>";
+ for (int i = 0; i < chrome::kNumberOfChromeDebugURLs; i++)
+ html += "<li>" + std::string(chrome::kChromeDebugURLs[i]) + "</li>\n";
+ html += "</ul>\n";
+ AppendFooter(&html);
+ return html;
+}
+
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+
+const char kAboutDiscardsRunCommand[] = "run";
+
+// Html output helper functions
+
+// Helper function to wrap HTML with a tag.
+std::string WrapWithTag(const std::string& tag, const std::string& text) {
+ return "<" + tag + ">" + text + "</" + tag + ">";
+}
+
+// Helper function to wrap Html with <td> tag.
+std::string WrapWithTD(const std::string& text) {
+ return "<td>" + text + "</td>";
+}
+
+// Helper function to wrap Html with <tr> tag.
+std::string WrapWithTR(const std::string& text) {
+ return "<tr>" + text + "</tr>";
+}
+
+std::string AddStringRow(const std::string& name, const std::string& value) {
+ std::string row;
+ row.append(WrapWithTD(name));
+ row.append(WrapWithTD(value));
+ return WrapWithTR(row);
+}
+
+void AddContentSecurityPolicy(std::string* output) {
+ output->append("<meta http-equiv='Content-Security-Policy' "
+ "content='default-src 'none';'>");
+}
+
+// TODO(stevenjb): L10N AboutDiscards.
+
+std::string BuildAboutDiscardsRunPage() {
+ std::string output;
+ AppendHeader(&output, 0, "About discards");
+ output.append(base::StringPrintf("<meta http-equiv='refresh' content='2;%s'>",
+ chrome::kChromeUIDiscardsURL));
+ AddContentSecurityPolicy(&output);
+ output.append(WrapWithTag("p", "Discarding a tab..."));
+ AppendFooter(&output);
+ return output;
+}
+
+std::vector<std::string> GetHtmlTabDescriptorsForDiscardPage() {
+ memory::TabManager* tab_manager = g_browser_process->GetTabManager();
+ memory::TabStatsList stats = tab_manager->GetTabStats();
+ std::vector<std::string> titles;
+ titles.reserve(stats.size());
+ for (memory::TabStatsList::iterator it = stats.begin(); it != stats.end();
+ ++it) {
+ std::string str;
+ str.reserve(4096);
+ str += "<b>";
+ str += it->is_app ? "[App] " : "";
+ str += it->is_internal_page ? "[Internal] " : "";
+ str += it->is_media ? "[Media] " : "";
+ str += it->is_pinned ? "[Pinned] " : "";
+ str += it->is_discarded ? "[Discarded] " : "";
+ str += "</b>";
+ str += net::EscapeForHTML(base::UTF16ToUTF8(it->title));
+#if defined(OS_CHROMEOS)
+ str += base::StringPrintf(" (%d) ", it->oom_score);
+#endif
+ if (!it->is_discarded) {
+ str += base::StringPrintf(" <a href='%s%s/%" PRId64 "'>Discard</a>",
+ chrome::kChromeUIDiscardsURL,
+ kAboutDiscardsRunCommand, it->tab_contents_id);
+ }
+ str += base::StringPrintf("&nbsp;&nbsp;(%d discards this session)",
+ it->discard_count);
+ titles.push_back(str);
+ }
+ return titles;
+}
+
+std::string AboutDiscards(const std::string& path) {
+ std::string output;
+ int64_t web_content_id;
+ memory::TabManager* tab_manager = g_browser_process->GetTabManager();
+
+ std::vector<std::string> path_split = base::SplitString(
+ path, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+ if (path_split.size() == 2 && path_split[0] == kAboutDiscardsRunCommand &&
+ base::StringToInt64(path_split[1], &web_content_id)) {
+ tab_manager->DiscardTabById(web_content_id);
+ return BuildAboutDiscardsRunPage();
+ } else if (path_split.size() == 1 &&
+ path_split[0] == kAboutDiscardsRunCommand) {
+ tab_manager->DiscardTab();
+ return BuildAboutDiscardsRunPage();
+ }
+
+ AppendHeader(&output, 0, "About discards");
+ AddContentSecurityPolicy(&output);
+ AppendBody(&output);
+ output.append("<h3>Discarded Tabs</h3>");
+ output.append(
+ "<p>Tabs sorted from most interesting to least interesting. The least "
+ "interesting tab may be discarded if we run out of physical memory.</p>");
+
+ std::vector<std::string> titles = GetHtmlTabDescriptorsForDiscardPage();
+ if (!titles.empty()) {
+ output.append("<ul>");
+ std::vector<std::string>::iterator it = titles.begin();
+ for ( ; it != titles.end(); ++it) {
+ output.append(WrapWithTag("li", *it));
+ }
+ output.append("</ul>");
+ } else {
+ output.append("<p>None found. Wait 10 seconds, then refresh.</p>");
+ }
+ output.append(base::StringPrintf("%d discards this session. ",
+ tab_manager->discard_count()));
+ output.append(base::StringPrintf("<a href='%s%s'>Discard tab now</a>",
+ chrome::kChromeUIDiscardsURL,
+ kAboutDiscardsRunCommand));
+
+ base::SystemMemoryInfoKB meminfo;
+ base::GetSystemMemoryInfo(&meminfo);
+ output.append("<h3>System memory information in MB</h3>");
+ output.append("<table>");
+ // Start with summary statistics.
+ output.append(AddStringRow(
+ "Total", base::IntToString(meminfo.total / 1024)));
+ output.append(AddStringRow(
+ "Free",
+ base::IntToString(base::SysInfo::AmountOfAvailablePhysicalMemory() /
+ 1024 / 1024)));
+#if defined(OS_CHROMEOS)
+ int mem_allocated_kb = meminfo.active_anon + meminfo.inactive_anon;
+#if defined(ARCH_CPU_ARM_FAMILY)
+ // ARM counts allocated graphics memory separately from anonymous.
+ if (meminfo.gem_size != -1)
+ mem_allocated_kb += meminfo.gem_size / 1024;
+#endif
+ output.append(AddStringRow(
+ "Allocated", base::IntToString(mem_allocated_kb / 1024)));
+ // Add some space, then detailed numbers.
+ output.append(AddStringRow("&nbsp;", "&nbsp;"));
+ output.append(AddStringRow(
+ "Buffered", base::IntToString(meminfo.buffers / 1024)));
+ output.append(AddStringRow(
+ "Cached", base::IntToString(meminfo.cached / 1024)));
+ output.append(AddStringRow(
+ "Active Anon", base::IntToString(meminfo.active_anon / 1024)));
+ output.append(AddStringRow(
+ "Inactive Anon", base::IntToString(meminfo.inactive_anon / 1024)));
+ output.append(AddStringRow(
+ "Shared", base::IntToString(meminfo.shmem / 1024)));
+ output.append(AddStringRow(
+ "Graphics", base::IntToString(meminfo.gem_size / 1024 / 1024)));
+#endif // OS_CHROMEOS
+ output.append("</table>");
+ AppendFooter(&output);
+ return output;
+}
+
+#endif // OS_WIN || OS_MACOSX || OS_LINUX
+
+// AboutDnsHandler bounces the request back to the IO thread to collect
+// the DNS information.
+class AboutDnsHandler : public base::RefCountedThreadSafe<AboutDnsHandler> {
+ public:
+ static void Start(Profile* profile,
+ const content::URLDataSource::GotDataCallback& callback) {
+ scoped_refptr<AboutDnsHandler> handler(
+ new AboutDnsHandler(profile, callback));
+ handler->StartOnUIThread();
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<AboutDnsHandler>;
+
+ AboutDnsHandler(Profile* profile,
+ const content::URLDataSource::GotDataCallback& callback)
+ : profile_(profile),
+ callback_(callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ }
+
+ virtual ~AboutDnsHandler() {}
+
+ // Calls FinishOnUIThread() on completion.
+ void StartOnUIThread() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ chrome_browser_net::Predictor* predictor = profile_->GetNetworkPredictor();
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&AboutDnsHandler::StartOnIOThread, this, predictor));
+ }
+
+ void StartOnIOThread(chrome_browser_net::Predictor* predictor) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ std::string data;
+ AppendHeader(&data, 0, "About DNS");
+ AppendBody(&data);
+ chrome_browser_net::Predictor::PredictorGetHtmlInfo(predictor, &data);
+ AppendFooter(&data);
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::BindOnce(&AboutDnsHandler::FinishOnUIThread, this, data));
+ }
+
+ void FinishOnUIThread(const std::string& data) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ std::string data_copy(data);
+ callback_.Run(base::RefCountedString::TakeString(&data_copy));
+ }
+
+ Profile* profile_;
+
+ // Callback to run with the response.
+ content::URLDataSource::GotDataCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(AboutDnsHandler);
+};
+
+#if defined(OS_LINUX) || defined(OS_OPENBSD)
+std::string AboutLinuxProxyConfig() {
+ std::string data;
+ AppendHeader(&data, 0,
+ l10n_util::GetStringUTF8(IDS_ABOUT_LINUX_PROXY_CONFIG_TITLE));
+ data.append("<style>body { max-width: 70ex; padding: 2ex 5ex; }</style>");
+ AppendBody(&data);
+ base::FilePath binary = base::CommandLine::ForCurrentProcess()->GetProgram();
+ data.append(
+ l10n_util::GetStringFUTF8(IDS_ABOUT_LINUX_PROXY_CONFIG_BODY,
+ l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
+ base::ASCIIToUTF16(binary.BaseName().value())));
+ AppendFooter(&data);
+ return data;
+}
+#endif
+
+} // namespace
+
+// AboutUIHTMLSource ----------------------------------------------------------
+
+AboutUIHTMLSource::AboutUIHTMLSource(const std::string& source_name,
+ Profile* profile)
+ : source_name_(source_name),
+ profile_(profile) {}
+
+AboutUIHTMLSource::~AboutUIHTMLSource() {}
+
+std::string AboutUIHTMLSource::GetSource() const {
+ return source_name_;
+}
+
+void AboutUIHTMLSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ std::string response;
+ // Add your data source here, in alphabetical order.
+ if (source_name_ == chrome::kChromeUIChromeURLsHost) {
+ response = ChromeURLs();
+ } else if (source_name_ == chrome::kChromeUICreditsHost) {
+ int idr = IDR_ABOUT_UI_CREDITS_HTML;
+ if (path == kCreditsJsPath)
+ idr = IDR_ABOUT_UI_CREDITS_JS;
+#if defined(OS_CHROMEOS)
+ else if (path == kKeyboardUtilsPath)
+ idr = IDR_KEYBOARD_UTILS_JS;
+#endif
+
+ base::StringPiece raw_response =
+ ResourceBundle::GetSharedInstance().GetRawDataResource(idr);
+ if (idr == IDR_ABOUT_UI_CREDITS_HTML) {
+ const uint8_t* next_encoded_byte =
+ reinterpret_cast<const uint8_t*>(raw_response.data());
+ size_t input_size_remaining = raw_response.size();
+ BrotliDecoderState* decoder =
+ BrotliDecoderCreateInstance(nullptr /* no custom allocator */,
+ nullptr /* no custom deallocator */,
+ nullptr /* no custom memory handle */);
+ CHECK(!!decoder);
+ while (!BrotliDecoderIsFinished(decoder)) {
+ size_t output_size_remaining = 0;
+ CHECK(BrotliDecoderDecompressStream(
+ decoder, &input_size_remaining, &next_encoded_byte,
+ &output_size_remaining, nullptr,
+ nullptr) != BROTLI_DECODER_RESULT_ERROR);
+ const uint8_t* output_buffer =
+ BrotliDecoderTakeOutput(decoder, &output_size_remaining);
+ response.insert(response.end(), output_buffer,
+ output_buffer + output_size_remaining);
+ }
+ BrotliDecoderDestroyInstance(decoder);
+ } else {
+ response = raw_response.as_string();
+ }
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+ } else if (source_name_ == chrome::kChromeUIDiscardsHost) {
+ response = AboutDiscards(path);
+#endif
+ } else if (source_name_ == chrome::kChromeUIDNSHost) {
+ AboutDnsHandler::Start(profile(), callback);
+ return;
+#if defined(OS_LINUX) || defined(OS_OPENBSD)
+ } else if (source_name_ == chrome::kChromeUILinuxProxyConfigHost) {
+ response = AboutLinuxProxyConfig();
+#endif
+#if defined(OS_CHROMEOS)
+ } else if (source_name_ == chrome::kChromeUIOSCreditsHost) {
+ ChromeOSCreditsHandler::Start(path, callback);
+ return;
+#endif
+#if !defined(OS_ANDROID)
+ } else if (source_name_ == chrome::kChromeUITermsHost) {
+#if defined(OS_CHROMEOS)
+ ChromeOSTermsHandler::Start(path, callback);
+ return;
+#else
+ response = l10n_util::GetStringUTF8(IDS_TERMS_HTML);
+#endif
+#endif
+ }
+
+ FinishDataRequest(response, callback);
+}
+
+void AboutUIHTMLSource::FinishDataRequest(
+ const std::string& html,
+ const content::URLDataSource::GotDataCallback& callback) {
+ std::string html_copy(html);
+ callback.Run(base::RefCountedString::TakeString(&html_copy));
+}
+
+std::string AboutUIHTMLSource::GetMimeType(const std::string& path) const {
+ if (path == kCreditsJsPath ||
+#if defined(OS_CHROMEOS)
+ path == kKeyboardUtilsPath ||
+#endif
+ path == kStatsJsPath ||
+ path == kStringsJsPath) {
+ return "application/javascript";
+ }
+ return "text/html";
+}
+
+bool AboutUIHTMLSource::ShouldAddContentSecurityPolicy() const {
+#if defined(OS_CHROMEOS)
+ if (source_name_ == chrome::kChromeUIOSCreditsHost)
+ return false;
+#endif
+ return content::URLDataSource::ShouldAddContentSecurityPolicy();
+}
+
+bool AboutUIHTMLSource::ShouldDenyXFrameOptions() const {
+#if defined(OS_CHROMEOS)
+ if (source_name_ == chrome::kChromeUITermsHost) {
+ // chrome://terms page is embedded in iframe to chrome://oobe.
+ return false;
+ }
+#endif
+ return content::URLDataSource::ShouldDenyXFrameOptions();
+}
+
+AboutUI::AboutUI(content::WebUI* web_ui, const std::string& name)
+ : WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+
+#if !defined(OS_ANDROID)
+ // Set up the chrome://theme/ source.
+ ThemeSource* theme = new ThemeSource(profile);
+ content::URLDataSource::Add(profile, theme);
+#endif
+
+ content::URLDataSource::Add(profile, new AboutUIHTMLSource(name, profile));
+}
diff --git a/chromium/chrome/browser/ui/webui/about_ui.h b/chromium/chrome/browser/ui/webui/about_ui.h
new file mode 100644
index 00000000000..2cfbea1b0c9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/about_ui.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2011 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_ABOUT_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_ABOUT_UI_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_ui_controller.h"
+
+class Profile;
+
+// We expose this class because the OOBE flow may need to explicitly add the
+// chrome://terms source outside of the normal flow.
+class AboutUIHTMLSource : public content::URLDataSource {
+ public:
+ // Construct a data source for the specified |source_name|.
+ AboutUIHTMLSource(const std::string& source_name, Profile* profile);
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ std::string GetMimeType(const std::string& path) const override;
+ bool ShouldAddContentSecurityPolicy() const override;
+ bool ShouldDenyXFrameOptions() const override;
+
+ // Send the response data.
+ void FinishDataRequest(
+ const std::string& html,
+ const content::URLDataSource::GotDataCallback& callback);
+
+ Profile* profile() { return profile_; }
+
+ private:
+ ~AboutUIHTMLSource() override;
+
+ std::string source_name_;
+ Profile* profile_;
+
+ DISALLOW_COPY_AND_ASSIGN(AboutUIHTMLSource);
+};
+
+class AboutUI : public content::WebUIController {
+ public:
+ explicit AboutUI(content::WebUI* web_ui, const std::string& host);
+ ~AboutUI() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AboutUI);
+};
+
+namespace about_ui {
+
+// Helper functions
+void AppendHeader(std::string* output, int refresh,
+ const std::string& unescaped_title);
+void AppendBody(std::string *output);
+void AppendFooter(std::string *output);
+
+} // namespace about_ui
+
+#endif // CHROME_BROWSER_UI_WEBUI_ABOUT_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/app_launcher_login_handler.cc b/chromium/chrome/browser/ui/webui/app_launcher_login_handler.cc
new file mode 100644
index 00000000000..e003c093f57
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/app_launcher_login_handler.cc
@@ -0,0 +1,265 @@
+// 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/ui/webui/app_launcher_login_handler.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.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 "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_metrics.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/signin_promo_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_window.h"
+#include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/webui/profile_info_watcher.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "content/public/browser/host_zoom_map.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/common/page_zoom.h"
+#include "net/base/escape.h"
+#include "skia/ext/image_operations.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/skia_util.h"
+
+using content::OpenURLParams;
+using content::Referrer;
+
+namespace {
+
+SkBitmap GetGAIAPictureForNTP(const gfx::Image& image) {
+ // This value must match the width and height value of login-status-icon
+ // in new_tab.css.
+ const int kLength = 27;
+ SkBitmap bmp = skia::ImageOperations::Resize(*image.ToSkBitmap(),
+ skia::ImageOperations::RESIZE_BEST, kLength, kLength);
+ SkCanvas canvas(bmp);
+
+ // Draw a gray border on the inside of the icon.
+ SkPaint paint;
+ paint.setColor(SkColorSetARGB(83, 0, 0, 0));
+ paint.setStyle(SkPaint::kStroke_Style);
+ canvas.drawRect(gfx::RectToSkRect(gfx::Rect(kLength - 1, kLength - 1)),
+ paint);
+ return bmp;
+}
+
+// Puts the |content| into an element with the given CSS class.
+base::string16 CreateElementWithClass(const base::string16& content,
+ const std::string& tag_name,
+ const std::string& css_class,
+ const std::string& extends_tag) {
+ base::string16 start_tag = base::ASCIIToUTF16("<" + tag_name +
+ " class='" + css_class + "' is='" + extends_tag + "'>");
+ base::string16 end_tag = base::ASCIIToUTF16("</" + tag_name + ">");
+ return start_tag + net::EscapeForHTML(content) + end_tag;
+}
+
+} // namespace
+
+AppLauncherLoginHandler::AppLauncherLoginHandler() {}
+
+AppLauncherLoginHandler::~AppLauncherLoginHandler() {}
+
+void AppLauncherLoginHandler::RegisterMessages() {
+ profile_info_watcher_.reset(new ProfileInfoWatcher(
+ Profile::FromWebUI(web_ui()),
+ base::Bind(&AppLauncherLoginHandler::UpdateLogin,
+ base::Unretained(this))));
+
+ web_ui()->RegisterMessageCallback("initializeSyncLogin",
+ base::Bind(&AppLauncherLoginHandler::HandleInitializeSyncLogin,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("showSyncLoginUI",
+ base::Bind(&AppLauncherLoginHandler::HandleShowSyncLoginUI,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("loginMessageSeen",
+ base::Bind(&AppLauncherLoginHandler::HandleLoginMessageSeen,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("showAdvancedLoginUI",
+ base::Bind(&AppLauncherLoginHandler::HandleShowAdvancedLoginUI,
+ base::Unretained(this)));
+}
+
+void AppLauncherLoginHandler::HandleInitializeSyncLogin(
+ const base::ListValue* args) {
+ UpdateLogin();
+}
+
+void AppLauncherLoginHandler::HandleShowSyncLoginUI(
+ const base::ListValue* args) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (!signin::ShouldShowPromo(profile))
+ return;
+
+ std::string username = SigninManagerFactory::GetForProfile(profile)
+ ->GetAuthenticatedAccountInfo()
+ .email;
+ if (!username.empty())
+ return;
+
+ content::WebContents* web_contents = web_ui()->GetWebContents();
+ Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
+ if (!browser)
+ return;
+
+ // The user isn't signed in, show the sign in promo.
+ signin_metrics::AccessPoint access_point =
+ web_contents->GetURL().spec() == chrome::kChromeUIAppsURL
+ ? signin_metrics::AccessPoint::ACCESS_POINT_APPS_PAGE_LINK
+ : signin_metrics::AccessPoint::ACCESS_POINT_NTP_LINK;
+ chrome::ShowBrowserSignin(browser, access_point);
+ RecordInHistogram(NTP_SIGN_IN_PROMO_CLICKED);
+}
+
+void AppLauncherLoginHandler::RecordInHistogram(int type) {
+ // Invalid type to record.
+ if (type < NTP_SIGN_IN_PROMO_VIEWED ||
+ type > NTP_SIGN_IN_PROMO_CLICKED) {
+ NOTREACHED();
+ } else {
+ UMA_HISTOGRAM_ENUMERATION("SyncPromo.NTPPromo", type,
+ NTP_SIGN_IN_PROMO_BUCKET_BOUNDARY);
+ }
+}
+
+void AppLauncherLoginHandler::HandleLoginMessageSeen(
+ const base::ListValue* args) {
+ Profile::FromWebUI(web_ui())->GetPrefs()->SetBoolean(
+ prefs::kSignInPromoShowNTPBubble, false);
+}
+
+void AppLauncherLoginHandler::HandleShowAdvancedLoginUI(
+ const base::ListValue* args) {
+ content::WebContents* web_contents = web_ui()->GetWebContents();
+ Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
+ if (!browser)
+ return;
+ signin_metrics::AccessPoint access_point =
+ web_contents->GetURL().spec() == chrome::kChromeUIAppsURL
+ ? signin_metrics::AccessPoint::ACCESS_POINT_APPS_PAGE_LINK
+ : signin_metrics::AccessPoint::ACCESS_POINT_NTP_LINK;
+ chrome::ShowBrowserSignin(browser, access_point);
+}
+
+void AppLauncherLoginHandler::UpdateLogin() {
+ std::string username = profile_info_watcher_->GetAuthenticatedUsername();
+ base::string16 header, sub_header;
+ std::string icon_url;
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (!username.empty()) {
+ ProfileAttributesStorage& storage =
+ g_browser_process->profile_manager()->GetProfileAttributesStorage();
+ ProfileAttributesEntry* entry;
+ if (storage.GetProfileAttributesWithPath(profile->GetPath(), &entry)) {
+ // Only show the profile picture and full name for the single profile
+ // case. In the multi-profile case the profile picture is visible in the
+ // title bar and the full name can be ambiguous.
+ if (storage.GetNumberOfProfiles() == 1) {
+ base::string16 name = entry->GetGAIAName();
+ if (!name.empty())
+ header = CreateElementWithClass(name, "span", "profile-name", "");
+ const gfx::Image* image = entry->GetGAIAPicture();
+ if (image)
+ icon_url = webui::GetBitmapDataUrl(GetGAIAPictureForNTP(*image));
+ }
+ if (header.empty()) {
+ header = CreateElementWithClass(base::UTF8ToUTF16(username), "span",
+ "profile-name", "");
+ }
+ }
+ } else {
+#if !defined(OS_CHROMEOS)
+ // Chromeos does not show this status header.
+ SigninManager* signin = SigninManagerFactory::GetForProfile(
+ profile->GetOriginalProfile());
+ if (!profile->IsLegacySupervised() && signin->IsSigninAllowed()) {
+ base::string16 signed_in_link = l10n_util::GetStringUTF16(
+ IDS_SYNC_PROMO_NOT_SIGNED_IN_STATUS_LINK);
+ signed_in_link =
+ CreateElementWithClass(signed_in_link, "a", "", "action-link");
+ header = l10n_util::GetStringFUTF16(
+ IDS_SYNC_PROMO_NOT_SIGNED_IN_STATUS_HEADER,
+ l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME));
+ sub_header = l10n_util::GetStringFUTF16(
+ IDS_SYNC_PROMO_NOT_SIGNED_IN_STATUS_SUB_HEADER, signed_in_link);
+
+ base::RecordAction(
+ web_ui()->GetWebContents()->GetURL().spec() ==
+ chrome::kChromeUIAppsURL
+ ? base::UserMetricsAction("Signin_Impression_FromAppsPageLink")
+ : base::UserMetricsAction("Signin_Impression_FromNTP"));
+ // Record that the user was shown the promo.
+ RecordInHistogram(NTP_SIGN_IN_PROMO_VIEWED);
+ }
+#endif
+ }
+
+ base::Value header_value(header);
+ base::Value sub_header_value(sub_header);
+ base::Value icon_url_value(icon_url);
+ base::Value is_user_signed_in(!username.empty());
+ web_ui()->CallJavascriptFunctionUnsafe("ntp.updateLogin", header_value,
+ sub_header_value, icon_url_value,
+ is_user_signed_in);
+}
+
+// static
+bool AppLauncherLoginHandler::ShouldShow(Profile* profile) {
+#if defined(OS_CHROMEOS)
+ // For now we don't care about showing sync status on Chrome OS. The promo
+ // UI and the avatar menu don't exist on that platform.
+ return false;
+#else
+ SigninManager* signin = SigninManagerFactory::GetForProfile(profile);
+ return !profile->IsOffTheRecord() && signin && signin->IsSigninAllowed();
+#endif
+}
+
+// static
+void AppLauncherLoginHandler::GetLocalizedValues(
+ Profile* profile, base::DictionaryValue* values) {
+ PrefService* prefs = profile->GetPrefs();
+ bool hide_sync = !prefs->GetBoolean(prefs::kSignInPromoShowNTPBubble);
+
+ base::string16 message = hide_sync ? base::string16() :
+ l10n_util::GetStringFUTF16(IDS_SYNC_PROMO_NTP_BUBBLE_MESSAGE,
+ l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME));
+
+ values->SetString("login_status_message", message);
+ values->SetString("login_status_url",
+ hide_sync ? std::string() : chrome::kSyncLearnMoreURL);
+ values->SetString("login_status_advanced",
+ hide_sync ? base::string16() :
+ l10n_util::GetStringUTF16(IDS_SYNC_PROMO_NTP_BUBBLE_ADVANCED));
+ values->SetString("login_status_dismiss",
+ hide_sync ? base::string16() :
+ l10n_util::GetStringUTF16(IDS_SYNC_PROMO_NTP_BUBBLE_OK));
+}
diff --git a/chromium/chrome/browser/ui/webui/app_launcher_login_handler.h b/chromium/chrome/browser/ui/webui/app_launcher_login_handler.h
new file mode 100644
index 00000000000..f678091feec
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/app_launcher_login_handler.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2011 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_LAUNCHER_LOGIN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_APP_LAUNCHER_LOGIN_HANDLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+class Profile;
+class ProfileInfoWatcher;
+
+namespace base {
+class DictionaryValue;
+}
+
+// The login handler currently simply displays the current logged in
+// username at the top of the NTP (and update itself when that changes).
+// In the future it may expand to allow users to login from the NTP.
+class AppLauncherLoginHandler : public content::WebUIMessageHandler {
+ public:
+ AppLauncherLoginHandler();
+ ~AppLauncherLoginHandler() override;
+
+ // WebUIMessageHandler implementation:
+ void RegisterMessages() override;
+
+ // Returns true if the login handler should be shown in a new tab page
+ // for the given |profile|. |profile| must not be NULL.
+ static bool ShouldShow(Profile* profile);
+
+ // Registers values (strings etc.) for the page.
+ static void GetLocalizedValues(Profile* profile,
+ base::DictionaryValue* values);
+
+ private:
+ // User actions while on the NTP when clicking on or viewing the sync promo.
+ enum NTPSignInPromoBuckets {
+ NTP_SIGN_IN_PROMO_VIEWED,
+ NTP_SIGN_IN_PROMO_CLICKED,
+ NTP_SIGN_IN_PROMO_BUCKET_BOUNDARY,
+ };
+
+ // Called from JS when the NTP is loaded. |args| is the list of arguments
+ // passed from JS and should be an empty list.
+ void HandleInitializeSyncLogin(const base::ListValue* args);
+
+ // Called from JS when the user clicks the login container. It shows the
+ // appropriate UI based on the current sync state. |args| is the list of
+ // arguments passed from JS and should be an empty list.
+ void HandleShowSyncLoginUI(const base::ListValue* args);
+
+ // Records actions in SyncPromo.NTPPromo histogram.
+ void RecordInHistogram(int type);
+
+ // Called from JS when the sync promo NTP bubble has been displayed. |args| is
+ // the list of arguments passed from JS and should be an empty list.
+ void HandleLoginMessageSeen(const base::ListValue* args);
+
+ // Called from JS when the user clicks on the advanced link the sync promo NTP
+ // bubble. Use use this to navigate to the sync settings page. |args| is the
+ // list of arguments passed from JS and should be an empty list.
+ void HandleShowAdvancedLoginUI(const base::ListValue* args);
+
+ // Internal helper method
+ void UpdateLogin();
+
+ // Watches this web UI's profile for info changes (e.g. authenticated username
+ // changes).
+ std::unique_ptr<ProfileInfoWatcher> profile_info_watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppLauncherLoginHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_APP_LAUNCHER_LOGIN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/app_launcher_page_ui.cc b/chromium/chrome/browser/ui/webui/app_launcher_page_ui.cc
new file mode 100644
index 00000000000..62ac8ab3c5b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/app_launcher_page_ui.cc
@@ -0,0 +1,151 @@
+// 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/ui/webui/app_launcher_page_ui.h"
+
+#include <string>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/metrics/histogram_macros.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/app_launcher_login_handler.h"
+#include "chrome/browser/ui/webui/metrics_handler.h"
+#include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
+#include "chrome/browser/ui/webui/ntp/app_resource_cache_factory.h"
+#include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
+#include "chrome/browser/ui/webui/ntp/ntp_resource_cache.h"
+#include "chrome/browser/ui/webui/theme_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/theme_resources.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/browser/extension_system.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+
+class ExtensionService;
+
+using content::BrowserThread;
+
+///////////////////////////////////////////////////////////////////////////////
+// AppLauncherPageUI
+
+AppLauncherPageUI::AppLauncherPageUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ web_ui->OverrideTitle(l10n_util::GetStringUTF16(IDS_APP_LAUNCHER_TAB_TITLE));
+
+ if (!GetProfile()->IsOffTheRecord()) {
+ ExtensionService* service =
+ extensions::ExtensionSystem::Get(GetProfile())->extension_service();
+ // We should not be launched without an ExtensionService.
+ DCHECK(service);
+ web_ui->AddMessageHandler(base::MakeUnique<AppLauncherHandler>(service));
+ web_ui->AddMessageHandler(base::MakeUnique<CoreAppLauncherHandler>());
+ web_ui->AddMessageHandler(base::MakeUnique<MetricsHandler>());
+ }
+
+ // The theme handler can require some CPU, so do it after hooking up the most
+ // visited handler. This allows the DB query for the new tab thumbs to happen
+ // earlier.
+ web_ui->AddMessageHandler(base::MakeUnique<ThemeHandler>());
+
+ std::unique_ptr<HTMLSource> html_source(
+ new HTMLSource(GetProfile()->GetOriginalProfile()));
+ content::URLDataSource::Add(GetProfile(), html_source.release());
+}
+
+AppLauncherPageUI::~AppLauncherPageUI() {
+}
+
+// static
+base::RefCountedMemory* AppLauncherPageUI::GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor) {
+ return ui::ResourceBundle::GetSharedInstance().
+ LoadDataResourceBytesForScale(IDR_BOOKMARK_BAR_APPS_SHORTCUT,
+ scale_factor);
+}
+
+bool AppLauncherPageUI::OverrideHandleWebUIMessage(
+ const GURL& source_url,
+ const std::string& message,
+ const base::ListValue& args) {
+ if (message == "getApps" &&
+ AppLauncherLoginHandler::ShouldShow(GetProfile())) {
+ web_ui()->AddMessageHandler(base::MakeUnique<AppLauncherLoginHandler>());
+ }
+ return false;
+}
+
+
+Profile* AppLauncherPageUI::GetProfile() const {
+ return Profile::FromWebUI(web_ui());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// HTMLSource
+
+AppLauncherPageUI::HTMLSource::HTMLSource(Profile* profile)
+ : profile_(profile) {
+}
+
+std::string AppLauncherPageUI::HTMLSource::GetSource() const {
+ return chrome::kChromeUIAppLauncherPageHost;
+}
+
+void AppLauncherPageUI::HTMLSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ NTPResourceCache* resource = AppResourceCacheFactory::GetForProfile(profile_);
+
+ content::WebContents* web_contents = wc_getter.Run();
+ content::RenderProcessHost* render_host =
+ web_contents ? web_contents->GetRenderProcessHost() : nullptr;
+ NTPResourceCache::WindowType win_type = NTPResourceCache::GetWindowType(
+ profile_, render_host);
+ scoped_refptr<base::RefCountedMemory> html_bytes(
+ resource->GetNewTabHTML(win_type));
+
+ callback.Run(html_bytes.get());
+}
+
+std::string AppLauncherPageUI::HTMLSource::GetMimeType(
+ const std::string& resource) const {
+ return "text/html";
+}
+
+bool AppLauncherPageUI::HTMLSource::ShouldReplaceExistingSource() const {
+ return false;
+}
+
+bool AppLauncherPageUI::HTMLSource::AllowCaching() const {
+ // Should not be cached to reflect dynamically-generated contents that may
+ // depend on user profiles.
+ return false;
+}
+
+std::string AppLauncherPageUI::HTMLSource::GetContentSecurityPolicyScriptSrc()
+ const {
+ // 'unsafe-inline' is added to script-src.
+ return "script-src chrome://resources 'self' 'unsafe-eval' 'unsafe-inline';";
+}
+
+std::string AppLauncherPageUI::HTMLSource::GetContentSecurityPolicyStyleSrc()
+ const {
+ return "style-src 'self' chrome://resources chrome://theme 'unsafe-inline';";
+}
+
+std::string AppLauncherPageUI::HTMLSource::GetContentSecurityPolicyImgSrc()
+ const {
+ return "img-src chrome://extension-icon chrome://theme chrome://resources "
+ "data:;";
+}
+
+AppLauncherPageUI::HTMLSource::~HTMLSource() {}
diff --git a/chromium/chrome/browser/ui/webui/app_launcher_page_ui.h b/chromium/chrome/browser/ui/webui/app_launcher_page_ui.h
new file mode 100644
index 00000000000..2bed010924c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/app_launcher_page_ui.h
@@ -0,0 +1,65 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_APP_LAUNCHER_PAGE_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_APP_LAUNCHER_PAGE_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "ui/base/layout.h"
+
+class Profile;
+
+namespace base {
+class RefCountedMemory;
+}
+
+// The WebUIController used for the app launcher page UI.
+class AppLauncherPageUI : public content::WebUIController {
+ public:
+ explicit AppLauncherPageUI(content::WebUI* web_ui);
+ ~AppLauncherPageUI() override;
+
+ static base::RefCountedMemory* GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor);
+
+ // content::WebUIController:
+ bool OverrideHandleWebUIMessage(const GURL& source_url,
+ const std::string& message,
+ const base::ListValue& args) override;
+
+ private:
+ class HTMLSource : public content::URLDataSource {
+ public:
+ explicit HTMLSource(Profile* profile);
+ ~HTMLSource() override;
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ std::string GetMimeType(const std::string&) const override;
+ bool ShouldReplaceExistingSource() const override;
+ bool AllowCaching() const override;
+ std::string GetContentSecurityPolicyScriptSrc() const override;
+ std::string GetContentSecurityPolicyStyleSrc() const override;
+ std::string GetContentSecurityPolicyImgSrc() const override;
+
+ private:
+
+ // Pointer back to the original profile.
+ Profile* profile_;
+
+ DISALLOW_COPY_AND_ASSIGN(HTMLSource);
+ };
+
+ Profile* GetProfile() const;
+
+ DISALLOW_COPY_AND_ASSIGN(AppLauncherPageUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_APP_LAUNCHER_PAGE_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/app_list/OWNERS b/chromium/chrome/browser/ui/webui/app_list/OWNERS
new file mode 100644
index 00000000000..2aebc091562
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/app_list/OWNERS
@@ -0,0 +1,3 @@
+calamity@chromium.org
+mgiuca@chromium.org
+tapted@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
new file mode 100644
index 00000000000..84869c4f540
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/app_list/start_page_browsertest.js
@@ -0,0 +1,161 @@
+// 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
new file mode 100644
index 00000000000..d8fd5b02f2b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/app_list/start_page_handler.cc
@@ -0,0 +1,112 @@
+// 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
new file mode 100644
index 00000000000..516413a3270
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/app_list/start_page_handler.h
@@ -0,0 +1,42 @@
+// 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
new file mode 100644
index 00000000000..be360aa789f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/app_list/start_page_ui.cc
@@ -0,0 +1,49 @@
+// 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/ptr_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(base::MakeUnique<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
new file mode 100644
index 00000000000..04089144dd3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/app_list/start_page_ui.h
@@ -0,0 +1,28 @@
+// 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/bidi_checker_web_ui_test.cc b/chromium/chrome/browser/ui/webui/bidi_checker_web_ui_test.cc
new file mode 100644
index 00000000000..acd5bd3da5d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/bidi_checker_web_ui_test.cc
@@ -0,0 +1,726 @@
+// 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/ui/webui/bidi_checker_web_ui_test.h"
+
+#include "base/base_paths.h"
+#include "base/i18n/rtl.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/autofill/personal_data_manager_factory.h"
+#include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/prefs/session_startup_pref.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_thread.h"
+#include "ui/base/resource/resource_bundle.h"
+
+// Test cases here are disabled on all platforms due to http://crbug.com/511439
+
+using autofill::AutofillProfile;
+using autofill::PersonalDataManager;
+
+static const base::FilePath::CharType* kWebUIBidiCheckerLibraryJS =
+ FILE_PATH_LITERAL("third_party/bidichecker/bidichecker_packaged.js");
+
+namespace {
+base::FilePath WebUIBidiCheckerLibraryJSPath() {
+ base::FilePath src_root;
+ if (!PathService::Get(base::DIR_SOURCE_ROOT, &src_root))
+ LOG(ERROR) << "Couldn't find source root";
+ return src_root.Append(kWebUIBidiCheckerLibraryJS);
+}
+
+// Since synchronization isn't complete for the ResourceBundle class, reload
+// locale resources on the IO thread.
+// crbug.com/95425, crbug.com/132752
+void ReloadLocaleResourcesOnIOThread(const std::string& new_locale) {
+ if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)) {
+ LOG(ERROR)
+ << content::BrowserThread::IO
+ << " != " << base::PlatformThread::CurrentId();
+ NOTREACHED();
+ }
+
+ std::string locale;
+ {
+ base::ThreadRestrictions::ScopedAllowIO allow_io_scope;
+ locale.assign(
+ ResourceBundle::GetSharedInstance().ReloadLocaleResources(new_locale));
+ }
+ ASSERT_FALSE(locale.empty());
+}
+
+// Since synchronization isn't complete for the ResourceBundle class, reload
+// locale resources on the IO thread.
+// crbug.com/95425, crbug.com/132752
+void ReloadLocaleResources(const std::string& new_locale) {
+ content::BrowserThread::PostTaskAndReply(
+ content::BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&ReloadLocaleResourcesOnIOThread,
+ base::ConstRef(new_locale)),
+ base::MessageLoop::QuitWhenIdleClosure());
+ content::RunMessageLoop();
+}
+
+} // namespace
+
+static const base::FilePath::CharType* kBidiCheckerTestsJS =
+ FILE_PATH_LITERAL("bidichecker_tests.js");
+
+void WebUIBidiCheckerBrowserTest::SetUp() {
+ argv_ = base::CommandLine::ForCurrentProcess()->GetArgs();
+}
+
+void WebUIBidiCheckerBrowserTest::TearDown() {
+ // Reset command line to the way it was before the test was run.
+ base::CommandLine::ForCurrentProcess()->InitFromArgv(argv_);
+}
+
+WebUIBidiCheckerBrowserTest::~WebUIBidiCheckerBrowserTest() {}
+
+WebUIBidiCheckerBrowserTest::WebUIBidiCheckerBrowserTest() {}
+
+void WebUIBidiCheckerBrowserTest::SetUpInProcessBrowserTestFixture() {
+ WebUIBrowserTest::SetUpInProcessBrowserTestFixture();
+ WebUIBrowserTest::AddLibrary(WebUIBidiCheckerLibraryJSPath());
+ WebUIBrowserTest::AddLibrary(base::FilePath(kBidiCheckerTestsJS));
+}
+
+void WebUIBidiCheckerBrowserTest::RunBidiCheckerOnPage(
+ const std::string& page_url, bool is_rtl) {
+ ui_test_utils::NavigateToURL(browser(), GURL(page_url));
+ ASSERT_TRUE(RunJavascriptTest("runBidiChecker", base::Value(page_url),
+ base::Value(is_rtl)));
+}
+
+void DISABLED_WebUIBidiCheckerBrowserTestLTR::RunBidiCheckerOnPage(
+ const std::string& page_url) {
+ WebUIBidiCheckerBrowserTest::RunBidiCheckerOnPage(page_url, false);
+}
+
+void DISABLED_WebUIBidiCheckerBrowserTestRTL::RunBidiCheckerOnPage(
+ const std::string& page_url) {
+ WebUIBidiCheckerBrowserTest::RunBidiCheckerOnPage(page_url, true);
+}
+
+void DISABLED_WebUIBidiCheckerBrowserTestRTL::SetUpOnMainThread() {
+ WebUIBidiCheckerBrowserTest::SetUpOnMainThread();
+ base::FilePath pak_path;
+ app_locale_ = base::i18n::GetConfiguredLocale();
+ ASSERT_TRUE(PathService::Get(base::FILE_MODULE, &pak_path));
+ pak_path = pak_path.DirName();
+ pak_path = pak_path.AppendASCII("pseudo_locales");
+ pak_path = pak_path.AppendASCII("fake-bidi");
+ pak_path = pak_path.ReplaceExtension(FILE_PATH_LITERAL("pak"));
+ ResourceBundle::GetSharedInstance().OverrideLocalePakForTest(pak_path);
+ ReloadLocaleResources("he");
+ base::i18n::SetICUDefaultLocale("he");
+}
+
+void DISABLED_WebUIBidiCheckerBrowserTestRTL::TearDownOnMainThread() {
+ WebUIBidiCheckerBrowserTest::TearDownOnMainThread();
+
+ base::i18n::SetICUDefaultLocale(app_locale_);
+ ResourceBundle::GetSharedInstance().OverrideLocalePakForTest(
+ base::FilePath());
+ ReloadLocaleResources(app_locale_);
+}
+
+// Tests
+
+//==============================
+// chrome://history
+//==============================
+
+static void SetupHistoryPageTest(Browser* browser,
+ const std::string& page_url,
+ const std::string& page_title) {
+ history::HistoryService* history_service =
+ HistoryServiceFactory::GetForProfile(browser->profile(),
+ ServiceAccessType::IMPLICIT_ACCESS);
+ const GURL history_url = GURL(page_url);
+ history_service->AddPage(
+ history_url, base::Time::Now(), history::SOURCE_BROWSED);
+ history_service->SetPageTitle(history_url, base::UTF8ToUTF16(page_title));
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestHistoryPage) {
+ // Test an Israeli news site with a Hebrew title.
+ SetupHistoryPageTest(browser(),
+ "http://www.ynet.co.il",
+ "\xD7\x91\xD7\x93\xD7\x99\xD7\xA7\xD7\x94\x21");
+ RunBidiCheckerOnPage(chrome::kChromeUIHistoryURL);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestHistoryPage) {
+ SetupHistoryPageTest(browser(), "http://www.google.com", "Google");
+ RunBidiCheckerOnPage(chrome::kChromeUIHistoryURL);
+}
+
+//==============================
+// chrome://about
+//==============================
+
+// This page isn't localized to an RTL language so we test it only in English.
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR, TestAboutPage) {
+ RunBidiCheckerOnPage(chrome::kChromeUIAboutURL);
+}
+
+//==============================
+// chrome://crashes
+//==============================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestCrashesPage) {
+ RunBidiCheckerOnPage(chrome::kChromeUICrashesURL);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestCrashesPage) {
+ RunBidiCheckerOnPage(chrome::kChromeUICrashesURL);
+}
+
+//==============================
+// chrome://downloads
+//==============================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestDownloadsPageLTR) {
+ RunBidiCheckerOnPage(chrome::kChromeUIDownloadsURL);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestDownloadsPageRTL) {
+ RunBidiCheckerOnPage(chrome::kChromeUIDownloadsURL);
+}
+
+//==============================
+// chrome://newtab
+//==============================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestNewTabPage) {
+ RunBidiCheckerOnPage(chrome::kChromeUINewTabURL);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestNewTabPage) {
+ RunBidiCheckerOnPage(chrome::kChromeUINewTabURL);
+}
+
+//==============================
+// chrome://settings-frame
+//==============================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsPage) {
+ RunBidiCheckerOnPage(chrome::kChromeUISettingsFrameURL);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsPage) {
+ RunBidiCheckerOnPage(chrome::kChromeUISettingsFrameURL);
+}
+
+static void SetupSettingsAutofillPageTest(Profile* profile,
+ const char* first_name,
+ const char* middle_name,
+ const char* last_name,
+ const char* email,
+ const char* company,
+ const char* address1,
+ const char* address2,
+ const char* city,
+ const char* state,
+ const char* zipcode,
+ const char* country,
+ const char* phone) {
+ autofill::test::DisableSystemServices(profile->GetPrefs());
+ AutofillProfile autofill_profile;
+ autofill::test::SetProfileInfo(&autofill_profile,
+ first_name,
+ middle_name,
+ last_name,
+ email,
+ company,
+ address1,
+ address2,
+ city,
+ state,
+ zipcode,
+ country,
+ phone);
+ PersonalDataManager* personal_data_manager =
+ autofill::PersonalDataManagerFactory::GetForProfile(profile);
+ ASSERT_TRUE(personal_data_manager);
+ personal_data_manager->AddProfile(autofill_profile);
+}
+
+static void TearDownSettingsAutofillPageTest() {
+ autofill::test::ReenableSystemServices();
+}
+
+// http://crbug.com/94642
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ DISABLED_TestSettingsAutofillPage) {
+ SetupSettingsAutofillPageTest(browser()->profile(),
+ "\xD7\x9E\xD7\xA9\xD7\x94",
+ "\xD7\x91",
+ "\xD7\x9B\xD7\x94\xD7\x9F",
+ "moshe.b.cohen@biditest.com",
+ "\xD7\x91\xD7\x93\xD7\x99\xD7\xA7\xD7\x94\x20"
+ "\xD7\x91\xD7\xA2\xD7\x9E",
+ "\xD7\x93\xD7\xA8\xD7\x9A\x20\xD7\x9E\xD7\xA0"
+ "\xD7\x97\xD7\x9D\x20\xD7\x91\xD7\x92\xD7"
+ "\x99\xD7\x9F\x20\x32\x33",
+ "\xD7\xA7\xD7\x95\xD7\x9E\xD7\x94\x20\x32\x36",
+ "\xD7\xAA\xD7\x9C\x20\xD7\x90\xD7\x91\xD7\x99"
+ "\xD7\x91",
+ "",
+ "66183",
+ "\xD7\x99\xD7\xA9\xD7\xA8\xD7\x90\xD7\x9C",
+ "0000");
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += std::string(chrome::kAutofillSubPage);
+ RunBidiCheckerOnPage(url);
+ TearDownSettingsAutofillPageTest();
+}
+
+// http://crbug.com/94642
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ DISABLED_TestSettingsAutofillPage) {
+ SetupSettingsAutofillPageTest(browser()->profile(),
+ "Milton",
+ "C.",
+ "Waddams",
+ "red.swingline@initech.com",
+ "Initech",
+ "4120 Freidrich Lane",
+ "Basement",
+ "Austin",
+ "Texas",
+ "78744",
+ "United States",
+ "5125551234");
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += std::string(chrome::kAutofillSubPage);
+ RunBidiCheckerOnPage(url);
+ TearDownSettingsAutofillPageTest();
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsClearBrowserDataPage) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += std::string(chrome::kClearBrowserDataSubPage);
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsClearBrowserDataPage) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += std::string(chrome::kClearBrowserDataSubPage);
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsContentSettingsPage) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += std::string(chrome::kContentSettingsSubPage);
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsContentSettingsPage) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += std::string(chrome::kContentSettingsSubPage);
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsContentSettingsExceptionsPage) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url +=
+ std::string(chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage);
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsContentSettingsExceptionsPage) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url +=
+ std::string(chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage);
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsLanguageOptionsPage) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += std::string(chrome::kLanguageOptionsSubPage);
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsLanguageOptionsPage) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += std::string(chrome::kLanguageOptionsSubPage);
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsSearchEnginesOptionsPage) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += std::string(chrome::kSearchEnginesSubPage);
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsSearchEnginesOptionsPage) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += std::string(chrome::kSearchEnginesSubPage);
+ RunBidiCheckerOnPage(url);
+}
+
+//===================================
+// chrome://settings-frame/startup
+//===================================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsFrameStartup) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += "startup";
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsFrameStartup) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += "startup";
+ RunBidiCheckerOnPage(url);
+}
+
+//===================================
+// chrome://settings-frame/importData
+//===================================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsFrameImportData) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kImportDataSubPage;
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsFrameImportData) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kImportDataSubPage;
+ RunBidiCheckerOnPage(url);
+}
+
+#if !defined(OS_CHROMEOS)
+//========================================
+// chrome://settings-frame/manageProfile
+//========================================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsFrameMangageProfile) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kManageProfileSubPage;
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsFrameMangageProfile) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kManageProfileSubPage;
+ RunBidiCheckerOnPage(url);
+}
+#endif // !defined(OS_CHROMEOS)
+
+//===================================================
+// chrome://settings-frame/contentExceptions#cookies
+//===================================================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsFrameContentExceptionsCookies) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage;
+ url += "#cookies";
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsFrameContentExceptionsCookies) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage;
+ url += "#cookies";
+ RunBidiCheckerOnPage(url);
+}
+
+//===================================================
+// chrome://settings-frame/contentExceptions#images
+//===================================================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsFrameContentExceptionsImages) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage;
+ url += "#images";
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsFrameContentExceptionsImages) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage;
+ url += "#images";
+ RunBidiCheckerOnPage(url);
+}
+
+//======================================================
+// chrome://settings-frame/contentExceptions#javascript
+//======================================================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsFrameContentExceptionsJavascript) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage;
+ url += "#javascript";
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsFrameContentExceptionsJavascript) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage;
+ url += "#javascript";
+ RunBidiCheckerOnPage(url);
+}
+
+//===================================================
+// chrome://settings-frame/contentExceptions#plugins
+//===================================================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsFrameContentExceptionsPlugins) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage;
+ url += "#plugins";
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsFrameContentExceptionsPlugins) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage;
+ url += "#plugins";
+ RunBidiCheckerOnPage(url);
+}
+
+//===================================================
+// chrome://settings-frame/contentExceptions#popups
+//===================================================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsFrameContentExceptionsPopups) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage;
+ url += "#popups";
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsFrameContentExceptionsPopups) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage;
+ url += "#popups";
+ RunBidiCheckerOnPage(url);
+}
+
+//===================================================
+// chrome://settings-frame/contentExceptions#location
+//===================================================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsFrameContentExceptionsLocation) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage;
+ url += "#location";
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsFrameContentExceptionsLocation) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage;
+ url += "#location";
+ RunBidiCheckerOnPage(url);
+}
+
+//===================================================
+// chrome://settings-frame/contentExceptions#notifications
+//===================================================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsFrameContentExceptionsNotifications) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage;
+ url += "#notifications";
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsFrameContentExceptionsNotifications) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage;
+ url += "#notifications";
+ RunBidiCheckerOnPage(url);
+}
+
+//===================================================
+// chrome://settings-frame/contentExceptions#mouselock
+//===================================================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsFrameContentExceptionsMouseLock) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage;
+ url += "#mouselock";
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsFrameContentExceptionsMouseLock) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kDeprecatedOptionsContentSettingsExceptionsSubPage;
+ url += "#mouselock";
+ RunBidiCheckerOnPage(url);
+}
+
+//========================================
+// chrome://settings-frame/handlers
+//========================================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsFrameHandler) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kHandlerSettingsSubPage;
+ RunBidiCheckerOnPage(url);
+}
+
+// Fails on chromeos. http://crbug.com/125367
+#if defined(OS_CHROMEOS)
+#define MAYBE_TestSettingsFrameHandler DISABLED_TestSettingsFrameHandler
+#else
+#define MAYBE_TestSettingsFrameHandler TestSettingsFrameHandler
+#endif
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ MAYBE_TestSettingsFrameHandler) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += chrome::kHandlerSettingsSubPage;
+ RunBidiCheckerOnPage(url);
+}
+
+//========================================
+// chrome://settings-frame/cookies
+//========================================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsFrameCookies) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += "cookies";
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsFrameCookies) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += "cookies";
+ RunBidiCheckerOnPage(url);
+}
+
+//========================================
+// chrome://settings-frame/passwords
+//========================================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestSettingsFramePasswords) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += "passwords";
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsFramePasswords) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += "passwords";
+ RunBidiCheckerOnPage(url);
+}
+
+//========================================
+// chrome://settings-frame/fonts
+//========================================
+
+#if defined(OS_MACOSX)
+#define MAYBE_TestSettingsFrameFonts DISABLED_TestSettingsFrameFonts
+#else
+#define MAYBE_TestSettingsFrameFonts TestSettingsFrameFonts
+#endif
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ MAYBE_TestSettingsFrameFonts) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += "fonts";
+ RunBidiCheckerOnPage(url);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestSettingsFrameFonts) {
+ std::string url(chrome::kChromeUISettingsFrameURL);
+ url += "fonts";
+ RunBidiCheckerOnPage(url);
+}
+
+// Test other uber iframes.
+
+//==============================
+// chrome://extensions-frame
+//==============================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR,
+ TestExtensionsFrame) {
+ RunBidiCheckerOnPage(chrome::kChromeUIExtensionsFrameURL);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL,
+ TestExtensionsFrame) {
+ RunBidiCheckerOnPage(chrome::kChromeUIExtensionsFrameURL);
+}
+
+//==============================
+// chrome://help-frame
+//==============================
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestLTR, TestHelpFrame) {
+ RunBidiCheckerOnPage(chrome::kChromeUIHelpFrameURL);
+}
+
+IN_PROC_BROWSER_TEST_F(DISABLED_WebUIBidiCheckerBrowserTestRTL, TestHelpFrame) {
+ RunBidiCheckerOnPage(chrome::kChromeUIHelpFrameURL);
+}
diff --git a/chromium/chrome/browser/ui/webui/bidi_checker_web_ui_test.h b/chromium/chrome/browser/ui/webui/bidi_checker_web_ui_test.h
new file mode 100644
index 00000000000..affa5d8c620
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/bidi_checker_web_ui_test.h
@@ -0,0 +1,59 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_BIDI_CHECKER_WEB_UI_TEST_H_
+#define CHROME_BROWSER_UI_WEBUI_BIDI_CHECKER_WEB_UI_TEST_H_
+
+#include "base/command_line.h"
+#include "chrome/test/base/web_ui_browser_test.h"
+
+// Base class for BidiChecker-based tests. Preloads the BidiChecker JS library
+// for each test.
+class WebUIBidiCheckerBrowserTest : public WebUIBrowserTest {
+ public:
+ ~WebUIBidiCheckerBrowserTest() override;
+
+ // testing::Test implementation.
+ void SetUp() override;
+ void TearDown() override;
+
+ protected:
+ WebUIBidiCheckerBrowserTest();
+
+ // Runs the Bidi Checker on the given page URL. |is_rtl| should be true when
+ // the active page locale is RTL.
+ void RunBidiCheckerOnPage(const std::string& page_url, bool is_rtl);
+
+ // Setup test path.
+ void SetUpInProcessBrowserTestFixture() override;
+
+ private:
+ // The command line args used to run the test before being changed in SetUp().
+ base::CommandLine::StringVector argv_;
+};
+
+// Base class for BidiChecker-based tests that run with an LTR UI.
+// Disabled on all platforms due to http://crbug.com/511439
+class DISABLED_WebUIBidiCheckerBrowserTestLTR
+ : public WebUIBidiCheckerBrowserTest {
+ public:
+ void RunBidiCheckerOnPage(const std::string& page_url);
+};
+
+// Base class for BidiChecker-based tests that run with an RTL UI.
+// Disabled on all platforms due to http://crbug.com/511439
+class DISABLED_WebUIBidiCheckerBrowserTestRTL
+ : public WebUIBidiCheckerBrowserTest {
+ public:
+ void RunBidiCheckerOnPage(const std::string& page_url);
+
+ protected:
+ void SetUpOnMainThread() override;
+ void TearDownOnMainThread() override;
+
+ // The app locale before we change it
+ std::string app_locale_;
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_BIDI_CHECKER_WEB_UI_TEST_H_
diff --git a/chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.cc b/chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.cc
new file mode 100644
index 00000000000..ab2833062d1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.cc
@@ -0,0 +1,71 @@
+// 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 "chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+BluetoothInternalsUI::BluetoothInternalsUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ // Set up the chrome://bluetooth-internals source.
+ content::WebUIDataSource* html_source =
+ content::WebUIDataSource::Create(chrome::kChromeUIBluetoothInternalsHost);
+
+ // Add required resources.
+ html_source->AddResourcePath("adapter_broker.js",
+ IDR_BLUETOOTH_INTERNALS_ADAPTER_BROKER_JS);
+ html_source->AddResourcePath("adapter_page.js",
+ IDR_BLUETOOTH_INTERNALS_ADAPTER_PAGE_JS);
+ html_source->AddResourcePath("bluetooth_internals.css",
+ IDR_BLUETOOTH_INTERNALS_CSS);
+ html_source->AddResourcePath("bluetooth_internals.js",
+ IDR_BLUETOOTH_INTERNALS_JS);
+ html_source->AddResourcePath("characteristic_list.js",
+ IDR_BLUETOOTH_INTERNALS_CHARACTERISTIC_LIST_JS);
+ html_source->AddResourcePath("descriptor_list.js",
+ IDR_BLUETOOTH_INTERNALS_DESCRIPTOR_LIST_JS);
+ html_source->AddResourcePath("device_broker.js",
+ IDR_BLUETOOTH_INTERNALS_DEVICE_BROKER_JS);
+ html_source->AddResourcePath("device_collection.js",
+ IDR_BLUETOOTH_INTERNALS_DEVICE_COLLECTION_JS);
+ html_source->AddResourcePath("device_details_page.js",
+ IDR_BLUETOOTH_INTERNALS_DEVICE_DETAILS_PAGE_JS);
+ html_source->AddResourcePath("device_table.js",
+ IDR_BLUETOOTH_INTERNALS_DEVICE_TABLE_JS);
+ html_source->AddResourcePath("devices_page.js",
+ IDR_BLUETOOTH_INTERNALS_DEVICES_PAGE_JS);
+ html_source->AddResourcePath("expandable_list.js",
+ IDR_BLUETOOTH_INTERNALS_EXPANDABLE_LIST_JS);
+ html_source->AddResourcePath("interfaces.js",
+ IDR_BLUETOOTH_INTERNALS_INTERFACES_JS);
+ html_source->AddResourcePath("object_fieldset.js",
+ IDR_BLUETOOTH_INTERNALS_OBJECT_FIELDSET_JS);
+ html_source->AddResourcePath("service_list.js",
+ IDR_BLUETOOTH_INTERNALS_SERVICE_LIST_JS);
+ html_source->AddResourcePath("sidebar.js",
+ IDR_BLUETOOTH_INTERNALS_SIDEBAR_JS);
+ html_source->AddResourcePath("snackbar.js",
+ IDR_BLUETOOTH_INTERNALS_SNACKBAR_JS);
+ html_source->AddResourcePath("value_control.js",
+ IDR_BLUETOOTH_INTERNALS_VALUE_CONTROL_JS);
+
+ html_source->AddResourcePath(
+ "device/bluetooth/public/interfaces/adapter.mojom",
+ IDR_BLUETOOTH_ADAPTER_MOJO_JS);
+ html_source->AddResourcePath(
+ "device/bluetooth/public/interfaces/device.mojom",
+ IDR_BLUETOOTH_DEVICE_MOJO_JS);
+ html_source->AddResourcePath("device/bluetooth/public/interfaces/uuid.mojom",
+ IDR_BLUETOOTH_UUID_MOJO_JS);
+ html_source->SetDefaultResource(IDR_BLUETOOTH_INTERNALS_HTML);
+ html_source->UseGzip(std::unordered_set<std::string>());
+
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, html_source);
+}
+
+BluetoothInternalsUI::~BluetoothInternalsUI() {}
diff --git a/chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.h b/chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.h
new file mode 100644
index 00000000000..4d39a811b0b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_BLUETOOTH_INTERNALS_BLUETOOTH_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_BLUETOOTH_INTERNALS_BLUETOOTH_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The WebUI for chrome://bluetooth-internals
+class BluetoothInternalsUI : public content::WebUIController {
+ public:
+ explicit BluetoothInternalsUI(content::WebUI* web_ui);
+ ~BluetoothInternalsUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BluetoothInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_BLUETOOTH_INTERNALS_BLUETOOTH_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/bookmarks_ui.cc b/chromium/chrome/browser/ui/webui/bookmarks_ui.cc
new file mode 100644
index 00000000000..1b7347e2829
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/bookmarks_ui.cc
@@ -0,0 +1,66 @@
+// 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/ui/webui/bookmarks_ui.h"
+
+#include "base/memory/ref_counted_memory.h"
+#include "base/message_loop/message_loop.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/theme_resources.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/resource/resource_bundle.h"
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// BookmarksUIHTMLSource
+//
+////////////////////////////////////////////////////////////////////////////////
+
+BookmarksUIHTMLSource::BookmarksUIHTMLSource() {
+}
+
+std::string BookmarksUIHTMLSource::GetSource() const {
+ return chrome::kChromeUIBookmarksHost;
+}
+
+void BookmarksUIHTMLSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ NOTREACHED() << "We should never get here since the extension should have"
+ << "been triggered";
+
+ callback.Run(NULL);
+}
+
+std::string BookmarksUIHTMLSource::GetMimeType(const std::string& path) const {
+ NOTREACHED() << "We should never get here since the extension should have"
+ << "been triggered";
+ return "text/html";
+}
+
+BookmarksUIHTMLSource::~BookmarksUIHTMLSource() {}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// BookmarksUI
+//
+////////////////////////////////////////////////////////////////////////////////
+
+BookmarksUI::BookmarksUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ BookmarksUIHTMLSource* html_source = new BookmarksUIHTMLSource();
+
+ // Set up the chrome://bookmarks/ source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::URLDataSource::Add(profile, html_source);
+}
+
+// static
+base::RefCountedMemory* BookmarksUI::GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor) {
+ return ui::ResourceBundle::GetSharedInstance().
+ LoadDataResourceBytesForScale(IDR_BOOKMARKS_FAVICON, scale_factor);
+}
diff --git a/chromium/chrome/browser/ui/webui/bookmarks_ui.h b/chromium/chrome/browser/ui/webui/bookmarks_ui.h
new file mode 100644
index 00000000000..f4d2ab6f2e4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/bookmarks_ui.h
@@ -0,0 +1,52 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_BOOKMARKS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_BOOKMARKS_UI_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "ui/base/layout.h"
+
+namespace base {
+class RefCountedMemory;
+}
+
+// This class provides the source for chrome://bookmarks/
+class BookmarksUIHTMLSource : public content::URLDataSource {
+ public:
+ BookmarksUIHTMLSource();
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ std::string GetMimeType(const std::string& path) const override;
+
+ private:
+ ~BookmarksUIHTMLSource() override;
+
+ DISALLOW_COPY_AND_ASSIGN(BookmarksUIHTMLSource);
+};
+
+// This class is used to hook up chrome://bookmarks/ which in turn gets
+// overridden by an extension.
+class BookmarksUI : public content::WebUIController {
+ public:
+ explicit BookmarksUI(content::WebUI* web_ui);
+
+ static base::RefCountedMemory* GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BookmarksUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_BOOKMARKS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/bookmarks_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/bookmarks_ui_browsertest.cc
new file mode 100644
index 00000000000..c2440d846f8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/bookmarks_ui_browsertest.cc
@@ -0,0 +1,117 @@
+// 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 "base/test/test_timeouts.h"
+#include "build/build_config.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.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 "content/public/browser/notification_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+
+class BookmarksTest : public InProcessBrowserTest {
+ public:
+ BookmarksTest() {}
+
+ void SetUpOnMainThread() override {
+ InProcessBrowserTest::SetUpOnMainThread();
+
+ // Re-enable accessibility checks when audit failures are resolved.
+ // AX_TEXT_01: http://crbug.com/559201
+ // AX_ARIA_08: http://crbug.com/559202
+ // EnableAccessibilityChecksForTestCase(true);
+ }
+
+ void OpenBookmarksManager() {
+ content::TestNavigationObserver navigation_observer(
+ browser()->tab_strip_model()->GetActiveWebContents(), 2);
+ navigation_observer.StartWatchingNewWebContents();
+
+ // Bring up the bookmarks manager tab.
+ chrome::ShowBookmarkManager(browser());
+ navigation_observer.Wait();
+ }
+
+ void AssertIsBookmarksPage(content::WebContents* tab) {
+ GURL url;
+ std::string out;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ tab,
+ "domAutomationController.send(location.protocol)",
+ &out));
+ ASSERT_EQ("chrome-extension:", out);
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ tab,
+ "domAutomationController.send(location.pathname)",
+ &out));
+ ASSERT_EQ("/main.html", out);
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(BookmarksTest, ShouldRedirectToExtension) {
+ ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIBookmarksURL));
+ AssertIsBookmarksPage(browser()->tab_strip_model()->GetActiveWebContents());
+}
+
+IN_PROC_BROWSER_TEST_F(BookmarksTest, CommandOpensBookmarksTab) {
+ ASSERT_EQ(1, browser()->tab_strip_model()->count());
+
+ // Bring up the bookmarks manager tab.
+ OpenBookmarksManager();
+ ASSERT_EQ(1, browser()->tab_strip_model()->count());
+ AssertIsBookmarksPage(browser()->tab_strip_model()->GetActiveWebContents());
+}
+
+// Flaky on Mac: https://crbug.com/524216
+#if defined(OS_MACOSX) || defined(OS_LINUX)
+#define MAYBE_CommandAgainGoesBackToBookmarksTab \
+ DISABLED_CommandAgainGoesBackToBookmarksTab
+#else
+#define MAYBE_CommandAgainGoesBackToBookmarksTab \
+ CommandAgainGoesBackToBookmarksTab
+#endif
+
+IN_PROC_BROWSER_TEST_F(BookmarksTest,
+ MAYBE_CommandAgainGoesBackToBookmarksTab) {
+ ui_test_utils::NavigateToURL(
+ browser(),
+ ui_test_utils::GetTestUrl(base::FilePath(),
+ base::FilePath().AppendASCII("simple.html")));
+ ASSERT_EQ(1, browser()->tab_strip_model()->count());
+
+ // Bring up the bookmarks manager tab.
+ OpenBookmarksManager();
+ ASSERT_EQ(2, browser()->tab_strip_model()->count());
+
+ AssertIsBookmarksPage(browser()->tab_strip_model()->GetActiveWebContents());
+
+ // Switch to first tab and run command again.
+ browser()->tab_strip_model()->ActivateTabAt(0, true);
+ chrome::ShowBookmarkManager(browser());
+
+ // Ensure the bookmarks ui tab is active.
+ ASSERT_EQ(1, browser()->tab_strip_model()->active_index());
+ ASSERT_EQ(2, browser()->tab_strip_model()->count());
+}
+
+IN_PROC_BROWSER_TEST_F(BookmarksTest, TwoCommandsOneTab) {
+ content::TestNavigationObserver navigation_observer(
+ browser()->tab_strip_model()->GetActiveWebContents());
+ chrome::ShowBookmarkManager(browser());
+ chrome::ShowBookmarkManager(browser());
+ navigation_observer.Wait();
+
+ ASSERT_EQ(1, browser()->tab_strip_model()->count());
+}
+
+IN_PROC_BROWSER_TEST_F(BookmarksTest, BookmarksLoaded) {
+ ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIBookmarksURL));
+ ASSERT_EQ(1, browser()->tab_strip_model()->count());
+ AssertIsBookmarksPage(browser()->tab_strip_model()->GetActiveWebContents());
+}
diff --git a/chromium/chrome/browser/ui/webui/browsing_history_handler.cc b/chromium/chrome/browser/ui/webui/browsing_history_handler.cc
new file mode 100644
index 00000000000..7a33df54030
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/browsing_history_handler.cc
@@ -0,0 +1,549 @@
+// 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.
+
+#include "chrome/browser/ui/webui/browsing_history_handler.h"
+
+#include <stddef.h>
+
+#include <set>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/i18n/rtl.h"
+#include "base/i18n/time_formatting.h"
+#include "base/logging.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/default_clock.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/bookmarks/bookmark_model_factory.h"
+#include "chrome/browser/favicon/large_icon_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/webui/favicon_source.h"
+#include "chrome/browser/ui/webui/large_icon_source.h"
+#include "chrome/common/features.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/browser/bookmark_utils.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/favicon/core/fallback_url_util.h"
+#include "components/favicon/core/large_icon_service.h"
+#include "components/query_parser/snippet.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/sync/device_info/device_info.h"
+#include "components/sync/device_info/device_info_tracker.h"
+#include "components/url_formatter/url_formatter.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/features/features.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/time_format.h"
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+#include "chrome/browser/supervised_user/supervised_user_navigation_observer.h"
+#include "chrome/browser/supervised_user/supervised_user_service.h"
+#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
+#include "chrome/browser/supervised_user/supervised_user_url_filter.h"
+#endif
+
+#if defined(OS_ANDROID)
+#include "chrome/browser/android/preferences/preferences_launcher.h"
+#else
+#include "chrome/common/chrome_features.h"
+#endif
+
+using bookmarks::BookmarkModel;
+
+namespace {
+
+// Identifiers for the type of device from which a history entry originated.
+static const char kDeviceTypeLaptop[] = "laptop";
+static const char kDeviceTypePhone[] = "phone";
+static const char kDeviceTypeTablet[] = "tablet";
+
+// Returns a localized version of |visit_time| including a relative
+// indicator (e.g. today, yesterday).
+base::string16 GetRelativeDateLocalized(base::Clock* clock,
+ const base::Time& visit_time) {
+ base::Time midnight = clock->Now().LocalMidnight();
+ base::string16 date_str = ui::TimeFormat::RelativeDate(visit_time, &midnight);
+ if (date_str.empty()) {
+ date_str = base::TimeFormatFriendlyDate(visit_time);
+ } else {
+ date_str = l10n_util::GetStringFUTF16(
+ IDS_HISTORY_DATE_WITH_RELATIVE_TIME,
+ date_str,
+ base::TimeFormatFriendlyDate(visit_time));
+ }
+ return date_str;
+}
+
+// Sets the correct year when substracting months from a date.
+void NormalizeMonths(base::Time::Exploded* exploded) {
+ // Decrease a year at a time until we have a proper date.
+ while (exploded->month < 1) {
+ exploded->month += 12;
+ exploded->year--;
+ }
+}
+
+// Gets the name and type of a device for the given sync client ID.
+// |name| and |type| are out parameters.
+void GetDeviceNameAndType(const browser_sync::ProfileSyncService* sync_service,
+ const std::string& client_id,
+ std::string* name,
+ std::string* type) {
+ // DeviceInfoTracker must be syncing in order for remote history entries to
+ // be available.
+ DCHECK(sync_service);
+ DCHECK(sync_service->GetDeviceInfoTracker());
+ DCHECK(sync_service->GetDeviceInfoTracker()->IsSyncing());
+
+ std::unique_ptr<syncer::DeviceInfo> device_info =
+ sync_service->GetDeviceInfoTracker()->GetDeviceInfo(client_id);
+ if (device_info.get()) {
+ *name = device_info->client_name();
+ switch (device_info->device_type()) {
+ case sync_pb::SyncEnums::TYPE_PHONE:
+ *type = kDeviceTypePhone;
+ break;
+ case sync_pb::SyncEnums::TYPE_TABLET:
+ *type = kDeviceTypeTablet;
+ break;
+ default:
+ *type = kDeviceTypeLaptop;
+ }
+ return;
+ }
+
+ *name = l10n_util::GetStringUTF8(IDS_HISTORY_UNKNOWN_DEVICE);
+ *type = kDeviceTypeLaptop;
+}
+
+// Formats |entry|'s URL and title and adds them to |result|.
+void SetHistoryEntryUrlAndTitle(BrowsingHistoryService::HistoryEntry* entry,
+ base::DictionaryValue* result) {
+ result->SetString("url", entry->url.spec());
+
+ bool using_url_as_the_title = false;
+ base::string16 title_to_set(entry->title);
+ if (entry->title.empty()) {
+ using_url_as_the_title = true;
+ title_to_set = base::UTF8ToUTF16(entry->url.spec());
+ }
+
+ // Since the title can contain BiDi text, we need to mark the text as either
+ // RTL or LTR, depending on the characters in the string. If we use the URL
+ // as the title, we mark the title as LTR since URLs are always treated as
+ // left to right strings.
+ if (base::i18n::IsRTL()) {
+ if (using_url_as_the_title)
+ base::i18n::WrapStringWithLTRFormatting(&title_to_set);
+ else
+ base::i18n::AdjustStringForLocaleDirection(&title_to_set);
+ }
+
+#if !defined(OS_ANDROID)
+ // Number of chars to truncate titles when making them "short".
+ static const size_t kShortTitleLength = 300;
+ if (title_to_set.size() > kShortTitleLength)
+ title_to_set.resize(kShortTitleLength);
+#endif
+
+ result->SetString("title", title_to_set);
+}
+
+// Converts |entry| to a DictionaryValue to be owned by the caller.
+std::unique_ptr<base::DictionaryValue> HistoryEntryToValue(
+ BrowsingHistoryService::HistoryEntry* entry,
+ BookmarkModel* bookmark_model,
+ SupervisedUserService* supervised_user_service,
+ const browser_sync::ProfileSyncService* sync_service) {
+ std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
+ SetHistoryEntryUrlAndTitle(entry, result.get());
+
+ base::string16 domain = url_formatter::IDNToUnicode(entry->url.host());
+ // When the domain is empty, use the scheme instead. This allows for a
+ // sensible treatment of e.g. file: URLs when group by domain is on.
+ if (domain.empty())
+ domain = base::UTF8ToUTF16(entry->url.scheme() + ":");
+
+ // The items which are to be written into result are also described in
+ // chrome/browser/resources/history/history.js in @typedef for
+ // HistoryEntry. Please update it whenever you add or remove
+ // any keys in result.
+ result->SetString("domain", domain);
+
+ result->SetString(
+ "fallbackFaviconText",
+ base::UTF16ToASCII(favicon::GetFallbackIconText(entry->url)));
+
+ result->SetDouble("time", entry->time.ToJsTime());
+
+ // Pass the timestamps in a list.
+ std::unique_ptr<base::ListValue> timestamps(new base::ListValue);
+ for (std::set<int64_t>::const_iterator it = entry->all_timestamps.begin();
+ it != entry->all_timestamps.end(); ++it) {
+ timestamps->AppendDouble(base::Time::FromInternalValue(*it).ToJsTime());
+ }
+ result->Set("allTimestamps", std::move(timestamps));
+
+ // Always pass the short date since it is needed both in the search and in
+ // the monthly view.
+ result->SetString("dateShort", base::TimeFormatShortDate(entry->time));
+
+ base::string16 snippet_string;
+ base::string16 date_relative_day;
+ base::string16 date_time_of_day;
+ bool is_blocked_visit = false;
+ int host_filtering_behavior = -1;
+
+ // Only pass in the strings we need (search results need a shortdate
+ // and snippet, browse results need day and time information). Makes sure that
+ // values of result are never undefined
+ if (entry->is_search_result) {
+ snippet_string = entry->snippet;
+ } else {
+ base::Time midnight = entry->clock->Now().LocalMidnight();
+ base::string16 date_str = ui::TimeFormat::RelativeDate(entry->time,
+ &midnight);
+ if (date_str.empty()) {
+ date_str = base::TimeFormatFriendlyDate(entry->time);
+ } else {
+ date_str = l10n_util::GetStringFUTF16(
+ IDS_HISTORY_DATE_WITH_RELATIVE_TIME,
+ date_str,
+ base::TimeFormatFriendlyDate(entry->time));
+ }
+ date_relative_day = date_str;
+ date_time_of_day = base::TimeFormatTimeOfDay(entry->time);
+ }
+
+ std::string device_name;
+ std::string device_type;
+ if (!entry->client_id.empty())
+ GetDeviceNameAndType(sync_service, entry->client_id, &device_name,
+ &device_type);
+ result->SetString("deviceName", device_name);
+ result->SetString("deviceType", device_type);
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ if (supervised_user_service) {
+ const SupervisedUserURLFilter* url_filter =
+ supervised_user_service->GetURLFilter();
+ int filtering_behavior =
+ url_filter->GetFilteringBehaviorForURL(entry->url.GetWithEmptyPath());
+ is_blocked_visit = entry->blocked_visit;
+ host_filtering_behavior = filtering_behavior;
+ }
+#endif
+
+ result->SetString("dateTimeOfDay", date_time_of_day);
+ result->SetString("dateRelativeDay", date_relative_day);
+ result->SetString("snippet", snippet_string);
+ result->SetBoolean("starred", bookmark_model->IsBookmarked(entry->url));
+ result->SetInteger("hostFilteringBehavior", host_filtering_behavior);
+ result->SetBoolean("blockedVisit", is_blocked_visit);
+
+ return result;
+}
+
+} // namespace
+
+BrowsingHistoryHandler::BrowsingHistoryHandler()
+ : clock_(new base::DefaultClock()),
+ browsing_history_service_(nullptr) {}
+
+BrowsingHistoryHandler::~BrowsingHistoryHandler() {
+}
+
+void BrowsingHistoryHandler::RegisterMessages() {
+ browsing_history_service_ = base::MakeUnique<BrowsingHistoryService>(
+ Profile::FromWebUI(web_ui()), this);
+
+ // Create our favicon data source.
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+#if defined(OS_ANDROID)
+ favicon::LargeIconService* large_icon_service =
+ LargeIconServiceFactory::GetForBrowserContext(profile);
+ content::URLDataSource::Add(profile, new LargeIconSource(large_icon_service));
+#else
+ content::URLDataSource::Add(profile, new FaviconSource(profile));
+#endif
+
+ web_ui()->RegisterMessageCallback("queryHistory",
+ base::Bind(&BrowsingHistoryHandler::HandleQueryHistory,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("removeVisits",
+ base::Bind(&BrowsingHistoryHandler::HandleRemoveVisits,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("clearBrowsingData",
+ base::Bind(&BrowsingHistoryHandler::HandleClearBrowsingData,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("removeBookmark",
+ base::Bind(&BrowsingHistoryHandler::HandleRemoveBookmark,
+ base::Unretained(this)));
+}
+
+bool BrowsingHistoryHandler::ExtractIntegerValueAtIndex(
+ const base::ListValue* value,
+ int index,
+ int* out_int) {
+ double double_value;
+ if (value->GetDouble(index, &double_value)) {
+ *out_int = static_cast<int>(double_value);
+ return true;
+ }
+ NOTREACHED();
+ return false;
+}
+
+void BrowsingHistoryHandler::HandleQueryHistory(const base::ListValue* args) {
+ history::QueryOptions options;
+
+ // Parse the arguments from JavaScript. There are five required arguments:
+ // - the text to search for (may be empty)
+ // - the offset from which the search should start (in multiples of week or
+ // month, set by the next argument).
+ // - the range (BrowsingHistoryHandler::Range) Enum value that sets the range
+ // of the query.
+ // - the end time for the query. Only results older than this time will be
+ // returned.
+ // - the maximum number of results to return (may be 0, meaning that there
+ // is no maximum).
+ base::string16 search_text = ExtractStringValue(args);
+ int offset;
+ if (!args->GetInteger(1, &offset)) {
+ NOTREACHED() << "Failed to convert argument 1. ";
+ return;
+ }
+ int range;
+ if (!args->GetInteger(2, &range)) {
+ NOTREACHED() << "Failed to convert argument 2. ";
+ return;
+ }
+
+ if (range == BrowsingHistoryHandler::MONTH)
+ SetQueryTimeInMonths(offset, &options);
+ else if (range == BrowsingHistoryHandler::WEEK)
+ SetQueryTimeInWeeks(offset, &options);
+
+ double end_time;
+ if (!args->GetDouble(3, &end_time)) {
+ NOTREACHED() << "Failed to convert argument 3. ";
+ return;
+ }
+ if (end_time)
+ options.end_time = base::Time::FromJsTime(end_time);
+
+ if (!ExtractIntegerValueAtIndex(args, 4, &options.max_count)) {
+ NOTREACHED() << "Failed to convert argument 4.";
+ return;
+ }
+
+ options.duplicate_policy = history::QueryOptions::REMOVE_DUPLICATES_PER_DAY;
+ browsing_history_service_->QueryHistory(search_text, options);
+}
+
+void BrowsingHistoryHandler::HandleRemoveVisits(const base::ListValue* args) {
+ std::vector<std::unique_ptr<BrowsingHistoryService::HistoryEntry>>
+ items_to_remove;
+ items_to_remove.reserve(args->GetSize());
+ for (base::ListValue::const_iterator it = args->begin();
+ it != args->end(); ++it) {
+ const base::DictionaryValue* deletion = NULL;
+ base::string16 url;
+ const base::ListValue* timestamps = NULL;
+
+ // Each argument is a dictionary with properties "url" and "timestamps".
+ if (!(it->GetAsDictionary(&deletion) && deletion->GetString("url", &url) &&
+ deletion->GetList("timestamps", &timestamps))) {
+ NOTREACHED() << "Unable to extract arguments";
+ return;
+ }
+ DCHECK(timestamps->GetSize() > 0);
+ std::unique_ptr<BrowsingHistoryService::HistoryEntry> entry(
+ new BrowsingHistoryService::HistoryEntry());
+
+ entry->url = GURL(url);
+
+ double timestamp;
+ for (base::ListValue::const_iterator ts_iterator = timestamps->begin();
+ ts_iterator != timestamps->end(); ++ts_iterator) {
+ if (!ts_iterator->GetAsDouble(&timestamp)) {
+ NOTREACHED() << "Unable to extract visit timestamp.";
+ continue;
+ }
+
+ base::Time visit_time = base::Time::FromJsTime(timestamp);
+ entry->all_timestamps.insert(visit_time.ToInternalValue());
+ }
+
+ items_to_remove.push_back(std::move(entry));
+ }
+
+ browsing_history_service_->RemoveVisits(&items_to_remove);
+ items_to_remove.clear();
+}
+
+void BrowsingHistoryHandler::HandleClearBrowsingData(
+ const base::ListValue* args) {
+#if defined(OS_ANDROID)
+ chrome::android::PreferencesLauncher::OpenClearBrowsingData(
+ web_ui()->GetWebContents());
+#else
+ // TODO(beng): This is an improper direct dependency on Browser. Route this
+ // through some sort of delegate.
+ Browser* browser = chrome::FindBrowserWithWebContents(
+ web_ui()->GetWebContents());
+ chrome::ShowClearBrowsingDataDialog(browser);
+#endif
+}
+
+void BrowsingHistoryHandler::HandleRemoveBookmark(const base::ListValue* args) {
+ base::string16 url = ExtractStringValue(args);
+ Profile* profile = Profile::FromWebUI(web_ui());
+ BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile);
+ bookmarks::RemoveAllBookmarks(model, GURL(url));
+}
+
+void BrowsingHistoryHandler::SetQueryTimeInWeeks(
+ int offset, history::QueryOptions* options) {
+ // LocalMidnight returns the beginning of the current day so get the
+ // beginning of the next one.
+ base::Time midnight =
+ clock_->Now().LocalMidnight() + base::TimeDelta::FromDays(1);
+ options->end_time = midnight -
+ base::TimeDelta::FromDays(7 * offset);
+ options->begin_time = midnight -
+ base::TimeDelta::FromDays(7 * (offset + 1));
+}
+
+void BrowsingHistoryHandler::SetQueryTimeInMonths(
+ int offset, history::QueryOptions* options) {
+ // Configure the begin point of the search to the start of the
+ // current month.
+ base::Time::Exploded exploded;
+ clock_->Now().LocalMidnight().LocalExplode(&exploded);
+ exploded.day_of_month = 1;
+
+ if (offset == 0) {
+ if (!base::Time::FromLocalExploded(exploded, &options->begin_time)) {
+ // TODO(maksims): implement errors handling here.
+ NOTIMPLEMENTED();
+ }
+
+ // Set the end time of this first search to null (which will
+ // show results from the future, should the user's clock have
+ // been set incorrectly).
+ options->end_time = base::Time();
+ } else {
+ // Go back |offset| months in the past. The end time is not inclusive, so
+ // use the first day of the |offset| - 1 and |offset| months (e.g. for
+ // the last month, |offset| = 1, use the first days of the last month and
+ // the current month.
+ exploded.month -= offset - 1;
+ // Set the correct year.
+ NormalizeMonths(&exploded);
+ if (!base::Time::FromLocalExploded(exploded, &options->end_time)) {
+ // TODO(maksims): implement errors handling here.
+ NOTIMPLEMENTED();
+ }
+
+ exploded.month -= 1;
+ // Set the correct year
+ NormalizeMonths(&exploded);
+ if (!base::Time::FromLocalExploded(exploded, &options->begin_time)) {
+ // TODO(maksims): implement errors handling here.
+ NOTIMPLEMENTED();
+ }
+ }
+}
+
+void BrowsingHistoryHandler::OnQueryComplete(
+ std::vector<BrowsingHistoryService::HistoryEntry>* results,
+ BrowsingHistoryService::QueryResultsInfo* query_results_info) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ BookmarkModel* bookmark_model =
+ BookmarkModelFactory::GetForBrowserContext(profile);
+ SupervisedUserService* supervised_user_service = NULL;
+#if defined(ENABLE_SUPERVISED_USERS)
+ if (profile->IsSupervised())
+ supervised_user_service =
+ SupervisedUserServiceFactory::GetForProfile(profile);
+#endif
+ browser_sync::ProfileSyncService* sync_service =
+ ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
+
+ // Convert the result vector into a ListValue.
+ base::ListValue results_value;
+ for (std::vector<BrowsingHistoryService::HistoryEntry>::iterator it =
+ results->begin(); it != results->end(); ++it) {
+ std::unique_ptr<base::Value> value(HistoryEntryToValue(
+ &(*it), bookmark_model, supervised_user_service, sync_service));
+ results_value.Append(std::move(value));
+ }
+
+ base::DictionaryValue results_info;
+ // The items which are to be written into results_info_value_ are also
+ // described in chrome/browser/resources/history/history.js in @typedef for
+ // HistoryQuery. Please update it whenever you add or remove any keys in
+ // results_info_value_.
+ results_info.SetString("term", query_results_info->search_text);
+ results_info.SetBoolean("finished", query_results_info->reached_beginning);
+ results_info.SetBoolean("hasSyncedResults",
+ query_results_info->has_synced_results);
+
+ // Add the specific dates that were searched to display them.
+ // TODO(sergiu): Put today if the start is in the future.
+ results_info.SetString(
+ "queryStartTime",
+ GetRelativeDateLocalized(clock_.get(), query_results_info->start_time));
+ results_info.SetString(
+ "queryEndTime",
+ GetRelativeDateLocalized(clock_.get(), query_results_info->end_time));
+
+// Not used in mobile UI, and cause ~16kb of code bloat (crbug/683386).
+#ifndef OS_ANDROID
+ // TODO(calamity): Clean up grouped-specific fields once grouped history is
+ // removed.
+ results_info.SetString(
+ "queryStartMonth",
+ base::TimeFormatMonthAndYear(query_results_info->start_time));
+ results_info.SetString(
+ "queryInterval",
+ base::DateIntervalFormat(query_results_info->start_time,
+ query_results_info->end_time,
+ base::DATE_FORMAT_MONTH_WEEKDAY_DAY));
+#endif
+
+ web_ui()->CallJavascriptFunctionUnsafe("historyResult", results_info,
+ results_value);
+}
+
+void BrowsingHistoryHandler::OnRemoveVisitsComplete() {
+ web_ui()->CallJavascriptFunctionUnsafe("deleteComplete");
+}
+
+void BrowsingHistoryHandler::OnRemoveVisitsFailed() {
+ web_ui()->CallJavascriptFunctionUnsafe("deleteFailed");
+}
+
+void BrowsingHistoryHandler::HistoryDeleted() {
+ web_ui()->CallJavascriptFunctionUnsafe("historyDeleted");
+}
+
+void BrowsingHistoryHandler::HasOtherFormsOfBrowsingHistory(
+ bool has_other_forms,
+ bool has_synced_results) {
+ web_ui()->CallJavascriptFunctionUnsafe("showNotification",
+ base::Value(has_synced_results),
+ base::Value(has_other_forms));
+}
diff --git a/chromium/chrome/browser/ui/webui/browsing_history_handler.h b/chromium/chrome/browser/ui/webui/browsing_history_handler.h
new file mode 100644
index 00000000000..0c9a95234e6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/browsing_history_handler.h
@@ -0,0 +1,98 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_BROWSING_HISTORY_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_BROWSING_HISTORY_HANDLER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "base/time/clock.h"
+#include "base/values.h"
+#include "chrome/browser/history/browsing_history_service_handler.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace history {
+struct QueryOptions;
+} // namespace history
+
+// The handler for Javascript messages related to the "history" view.
+class BrowsingHistoryHandler :
+ public content::WebUIMessageHandler,
+ public BrowsingHistoryServiceHandler {
+ public:
+ BrowsingHistoryHandler();
+ ~BrowsingHistoryHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // Handler for the "queryHistory" message.
+ void HandleQueryHistory(const base::ListValue* args);
+
+ // Handler for the "removeVisits" message.
+ void HandleRemoveVisits(const base::ListValue* args);
+
+ // Handler for "clearBrowsingData" message.
+ void HandleClearBrowsingData(const base::ListValue* args);
+
+ // Handler for "removeBookmark" message.
+ void HandleRemoveBookmark(const base::ListValue* args);
+
+ // BrowsingHistoryServiceHandler implementation.
+ void OnQueryComplete(
+ std::vector<BrowsingHistoryService::HistoryEntry>* results,
+ BrowsingHistoryService::QueryResultsInfo* query_results_info) override;
+ void OnRemoveVisitsComplete() override;
+ void OnRemoveVisitsFailed() override;
+ void HistoryDeleted() override;
+ void HasOtherFormsOfBrowsingHistory(
+ bool has_other_forms, bool has_synced_results) override;
+
+ // For tests.
+ void set_clock(std::unique_ptr<base::Clock> clock) {
+ clock_ = std::move(clock);
+ }
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(BrowsingHistoryHandlerTest,
+ ObservingWebHistoryDeletions);
+ FRIEND_TEST_ALL_PREFIXES(BrowsingHistoryHandlerTest, SetQueryTimeInWeeks);
+ FRIEND_TEST_ALL_PREFIXES(BrowsingHistoryHandlerTest, SetQueryTimeInMonths);
+ FRIEND_TEST_ALL_PREFIXES(BrowsingHistoryHandlerTest, MdTruncatesTitles);
+
+ // The range for which to return results:
+ // - ALLTIME: allows access to all the results in a paginated way.
+ // - WEEK: the last 7 days.
+ // - MONTH: the last calendar month.
+ enum Range {
+ ALL_TIME = 0,
+ WEEK = 1,
+ MONTH = 2
+ };
+
+ bool ExtractIntegerValueAtIndex(
+ const base::ListValue* value, int index, int* out_int);
+
+ // Sets the query options for a week-wide query, |offset| weeks ago.
+ void SetQueryTimeInWeeks(int offset, history::QueryOptions* options);
+
+ // Sets the query options for a monthly query, |offset| months ago.
+ void SetQueryTimeInMonths(int offset, history::QueryOptions* options);
+
+ // The clock used to vend times.
+ std::unique_ptr<base::Clock> clock_;
+
+ std::unique_ptr<BrowsingHistoryService> browsing_history_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowsingHistoryHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_BROWSING_HISTORY_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/browsing_history_handler_unittest.cc b/chromium/chrome/browser/ui/webui/browsing_history_handler_unittest.cc
new file mode 100644
index 00000000000..15ef926f324
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/browsing_history_handler_unittest.cc
@@ -0,0 +1,334 @@
+// 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/ui/webui/browsing_history_handler.h"
+
+#include <stdint.h>
+#include <memory>
+#include <set>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/simple_test_clock.h"
+#include "base/values.h"
+#include "chrome/browser/history/browsing_history_service.h"
+#include "chrome/browser/history/web_history_service_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/sync/profile_sync_service_factory.h"
+#include "chrome/browser/sync/profile_sync_test_util.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/browser_sync/test_profile_sync_service.h"
+#include "components/history/core/test/fake_web_history_service.h"
+#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
+#include "components/signin/core/browser/fake_signin_manager.h"
+#include "components/sync/base/model_type.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_web_ui.h"
+#include "net/http/http_status_code.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace {
+
+base::Time PretendNow() {
+ base::Time::Exploded exploded_reference_time;
+ exploded_reference_time.year = 2015;
+ exploded_reference_time.month = 1;
+ exploded_reference_time.day_of_month = 2;
+ exploded_reference_time.day_of_week = 5;
+ exploded_reference_time.hour = 11;
+ exploded_reference_time.minute = 0;
+ exploded_reference_time.second = 0;
+ exploded_reference_time.millisecond = 0;
+
+ base::Time out_time;
+ EXPECT_TRUE(
+ base::Time::FromLocalExploded(exploded_reference_time, &out_time));
+ return out_time;
+}
+
+void IgnoreBoolAndDoNothing(bool ignored_argument) {}
+
+class TestSyncService : public browser_sync::TestProfileSyncService {
+ public:
+ explicit TestSyncService(Profile* profile)
+ : browser_sync::TestProfileSyncService(
+ CreateProfileSyncServiceParamsForTest(profile)),
+ sync_active_(true) {}
+
+ bool IsSyncActive() const override { return sync_active_; }
+
+ syncer::ModelTypeSet GetActiveDataTypes() const override {
+ return syncer::ModelTypeSet::All();
+ }
+
+ void SetSyncActive(bool active) {
+ sync_active_ = active;
+ NotifyObservers();
+ }
+
+ private:
+ bool sync_active_;
+};
+
+class BrowsingHistoryHandlerWithWebUIForTesting
+ : public BrowsingHistoryHandler {
+ public:
+ explicit BrowsingHistoryHandlerWithWebUIForTesting(content::WebUI* web_ui)
+ : test_clock_(new base::SimpleTestClock()) {
+ set_clock(base::WrapUnique(test_clock_));
+ set_web_ui(web_ui);
+ test_clock_->SetNow(PretendNow());
+
+ }
+
+ base::SimpleTestClock* test_clock() { return test_clock_; }
+
+ private:
+ base::SimpleTestClock* test_clock_;
+};
+
+} // namespace
+
+class BrowsingHistoryHandlerTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ TestingProfile::Builder builder;
+ builder.AddTestingFactory(ProfileOAuth2TokenServiceFactory::GetInstance(),
+ &BuildFakeProfileOAuth2TokenService);
+ builder.AddTestingFactory(SigninManagerFactory::GetInstance(),
+ &BuildFakeSigninManagerBase);
+ builder.AddTestingFactory(ProfileSyncServiceFactory::GetInstance(),
+ &BuildFakeSyncService);
+ builder.AddTestingFactory(WebHistoryServiceFactory::GetInstance(),
+ &BuildFakeWebHistoryService);
+ profile_ = builder.Build();
+ profile_->CreateBookmarkModel(false);
+
+ sync_service_ = static_cast<TestSyncService*>(
+ ProfileSyncServiceFactory::GetForProfile(profile_.get()));
+ web_history_service_ = static_cast<history::FakeWebHistoryService*>(
+ WebHistoryServiceFactory::GetForProfile(profile_.get()));
+
+ web_contents_.reset(content::WebContents::Create(
+ content::WebContents::CreateParams(profile_.get())));
+ web_ui_.reset(new content::TestWebUI);
+ web_ui_->set_web_contents(web_contents_.get());
+ }
+
+ void TearDown() override {
+ web_contents_.reset();
+ web_ui_.reset();
+ profile_.reset();
+ }
+
+ Profile* profile() { return profile_.get(); }
+ TestSyncService* sync_service() { return sync_service_; }
+ history::WebHistoryService* web_history_service() {
+ return web_history_service_;
+ }
+ content::TestWebUI* web_ui() { return web_ui_.get(); }
+
+ private:
+ static std::unique_ptr<KeyedService> BuildFakeSyncService(
+ content::BrowserContext* context) {
+ return base::MakeUnique<TestSyncService>(
+ static_cast<TestingProfile*>(context));
+ }
+
+ static std::unique_ptr<KeyedService> BuildFakeWebHistoryService(
+ content::BrowserContext* context) {
+ Profile* profile = static_cast<TestingProfile*>(context);
+
+ std::unique_ptr<history::FakeWebHistoryService> service =
+ base::MakeUnique<history::FakeWebHistoryService>(
+ ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
+ SigninManagerFactory::GetForProfile(profile),
+ profile->GetRequestContext());
+ service->SetupFakeResponse(true /* success */, net::HTTP_OK);
+ return std::move(service);
+ }
+
+ content::TestBrowserThreadBundle thread_bundle_;
+ std::unique_ptr<TestingProfile> profile_;
+ TestSyncService* sync_service_;
+ history::FakeWebHistoryService* web_history_service_;
+ std::unique_ptr<content::TestWebUI> web_ui_;
+ std::unique_ptr<content::WebContents> web_contents_;
+};
+
+TEST_F(BrowsingHistoryHandlerTest, SetQueryTimeInWeeks) {
+ BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
+ history::QueryOptions options;
+
+ // Querying this week should result in end time being midnight tonight and
+ // begin time being midnight a week ago.
+ handler.SetQueryTimeInWeeks(0, &options);
+ base::Time midnight_tonight =
+ PretendNow().LocalMidnight() + base::TimeDelta::FromDays(1);
+ EXPECT_EQ(midnight_tonight, options.end_time);
+ base::Time midnight_a_week_ago =
+ midnight_tonight - base::TimeDelta::FromDays(7);
+ EXPECT_EQ(midnight_a_week_ago, options.begin_time);
+
+ // Querying 4 weeks ago should result in end time being midnight 4 weeks ago
+ // and begin time being midnight 5 weeks ago.
+ handler.SetQueryTimeInWeeks(4, &options);
+ base::Time midnight_4_weeks_ago =
+ PretendNow().LocalMidnight() - base::TimeDelta::FromDays(27);
+ EXPECT_EQ(midnight_4_weeks_ago, options.end_time);
+ base::Time midnight_5_weeks_ago =
+ midnight_4_weeks_ago - base::TimeDelta::FromDays(7);
+ EXPECT_EQ(midnight_5_weeks_ago, options.begin_time);
+}
+
+TEST_F(BrowsingHistoryHandlerTest, SetQueryTimeInMonths) {
+ BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
+ history::QueryOptions options;
+
+ base::Time::Exploded exploded_expected_time;
+ PretendNow().LocalExplode(&exploded_expected_time);
+
+ // Querying this month should result in end time being unset and begin time
+ // being midnight of the first.
+ handler.SetQueryTimeInMonths(0, &options);
+ EXPECT_EQ(base::Time(), options.end_time);
+
+ exploded_expected_time.day_of_month = 1;
+ exploded_expected_time.hour = 0;
+ base::Time first_jan_2015_midnight;
+ EXPECT_TRUE(base::Time::FromLocalExploded(exploded_expected_time,
+ &first_jan_2015_midnight));
+ EXPECT_EQ(first_jan_2015_midnight, options.begin_time);
+
+ // Querying 6 months ago should result in end time being 2014-08-01 and begin
+ // time being 2014-07-01.
+ handler.SetQueryTimeInMonths(6, &options);
+
+ exploded_expected_time.year = 2014;
+ exploded_expected_time.month = 8;
+ base::Time first_aug_2014_midnight;
+ EXPECT_TRUE(base::Time::FromLocalExploded(exploded_expected_time,
+ &first_aug_2014_midnight));
+ EXPECT_EQ(first_aug_2014_midnight, options.end_time);
+ exploded_expected_time.month = 7;
+ base::Time first_jul_2014_midnight;
+ EXPECT_TRUE(base::Time::FromLocalExploded(exploded_expected_time,
+ &first_jul_2014_midnight));
+ EXPECT_EQ(first_jul_2014_midnight, options.begin_time);
+}
+
+// Tests that BrowsingHistoryHandler is informed about WebHistoryService
+// deletions.
+TEST_F(BrowsingHistoryHandlerTest, ObservingWebHistoryDeletions) {
+ base::Callback<void(bool)> callback = base::Bind(&IgnoreBoolAndDoNothing);
+
+ // BrowsingHistoryHandler is informed about WebHistoryService history
+ // deletions.
+ {
+ sync_service()->SetSyncActive(true);
+ BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
+ handler.RegisterMessages();
+
+ web_history_service()->ExpireHistoryBetween(std::set<GURL>(), base::Time(),
+ base::Time::Max(), callback);
+
+ EXPECT_EQ(1U, web_ui()->call_data().size());
+ EXPECT_EQ("historyDeleted", web_ui()->call_data().back()->function_name());
+ }
+
+ // BrowsingHistoryHandler will be informed about WebHistoryService deletions
+ // even if history sync is activated later.
+ {
+ sync_service()->SetSyncActive(false);
+ BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
+ handler.RegisterMessages();
+ sync_service()->SetSyncActive(true);
+
+ web_history_service()->ExpireHistoryBetween(std::set<GURL>(), base::Time(),
+ base::Time::Max(), callback);
+
+ EXPECT_EQ(2U, web_ui()->call_data().size());
+ EXPECT_EQ("historyDeleted", web_ui()->call_data().back()->function_name());
+ }
+
+ // BrowsingHistoryHandler does not fire historyDeleted while a web history
+ // delete request is happening.
+ {
+ sync_service()->SetSyncActive(true);
+ BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
+ handler.RegisterMessages();
+
+ // Simulate an ongoing delete request.
+ handler.browsing_history_service_->has_pending_delete_request_ = true;
+
+ web_history_service()->ExpireHistoryBetween(
+ std::set<GURL>(), base::Time(), base::Time::Max(),
+ base::Bind(
+ &BrowsingHistoryService::RemoveWebHistoryComplete,
+ handler.browsing_history_service_->weak_factory_.GetWeakPtr()));
+
+ EXPECT_EQ(3U, web_ui()->call_data().size());
+ EXPECT_EQ("deleteComplete", web_ui()->call_data().back()->function_name());
+ }
+
+ // When history sync is not active, we don't listen to WebHistoryService
+ // deletions. The WebHistoryService object still exists (because it's a
+ // BrowserContextKeyedService), but is not visible to BrowsingHistoryHandler.
+ {
+ sync_service()->SetSyncActive(false);
+ BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
+ handler.RegisterMessages();
+
+ web_history_service()->ExpireHistoryBetween(std::set<GURL>(), base::Time(),
+ base::Time::Max(), callback);
+
+ // No additional WebUI calls were made.
+ EXPECT_EQ(3U, web_ui()->call_data().size());
+ }
+}
+
+#if !defined(OS_ANDROID)
+TEST_F(BrowsingHistoryHandlerTest, MdTruncatesTitles) {
+ history::URLResult long_result(
+ GURL("http://looooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
+ "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
+ "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
+ "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
+ "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
+ "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
+ "ngurlislong.com"), base::Time());
+ ASSERT_GT(long_result.url().spec().size(), 300u);
+
+ history::QueryResults results;
+ results.AppendURLBySwapping(&long_result);
+
+ BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
+ handler.RegisterMessages(); // Needed to create BrowsingHistoryService.
+ web_ui()->ClearTrackedCalls();
+
+ handler.browsing_history_service_->QueryComplete(
+ base::string16(), history::QueryOptions(), &results);
+ ASSERT_FALSE(web_ui()->call_data().empty());
+
+ const base::ListValue* arg2;
+ ASSERT_TRUE(web_ui()->call_data().front()->arg2()->GetAsList(&arg2));
+
+ const base::DictionaryValue* first_entry;
+ ASSERT_TRUE(arg2->GetDictionary(0, &first_entry));
+
+ base::string16 title;
+ ASSERT_TRUE(first_entry->GetString("title", &title));
+
+ ASSERT_EQ(0u, title.find(base::ASCIIToUTF16("http://loooo")));
+ EXPECT_EQ(300u, title.size());
+}
+#endif
diff --git a/chromium/chrome/browser/ui/webui/cast/OWNERS b/chromium/chrome/browser/ui/webui/cast/OWNERS
new file mode 100644
index 00000000000..5b34f4712ed
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/cast/OWNERS
@@ -0,0 +1 @@
+sheretov@chromium.org
diff --git a/chromium/chrome/browser/ui/webui/cast/cast_ui.cc b/chromium/chrome/browser/ui/webui/cast/cast_ui.cc
new file mode 100644
index 00000000000..13d5e2cccb4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/cast/cast_ui.cc
@@ -0,0 +1,41 @@
+// 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 "chrome/browser/ui/webui/cast/cast_ui.h"
+
+#include "chrome/browser/media/router/media_router_factory.h"
+#include "chrome/browser/media/router/mojo/media_router_mojo_impl.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+CastUI::CastUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ // Retrieve the ID of the component extension.
+ // TODO(crbug.com/597778): remove reference to MediaRouterMojoImpl.
+ auto* router = static_cast<media_router::MediaRouterMojoImpl*>(
+ media_router::MediaRouterFactory::GetApiForBrowserContext(
+ web_ui->GetWebContents()->GetBrowserContext()));
+ std::string extension_id = router->media_route_provider_extension_id();
+
+ // Set up the chrome://cast data source and add required resources.
+ content::WebUIDataSource* html_source =
+ content::WebUIDataSource::Create(chrome::kChromeUICastHost);
+
+ html_source->AddResourcePath("cast.css", IDR_CAST_CSS);
+ html_source->AddResourcePath("cast.js", IDR_CAST_JS);
+ html_source->AddResourcePath("cast_favicon.ico", IDR_CAST_FAVICON);
+ html_source->AddString("extensionId", extension_id);
+ html_source->SetJsonPath("strings.js");
+ html_source->SetDefaultResource(IDR_CAST_HTML);
+ html_source->OverrideContentSecurityPolicyObjectSrc("object-src * chrome:;");
+
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), html_source);
+}
+
+CastUI::~CastUI() {
+}
diff --git a/chromium/chrome/browser/ui/webui/cast/cast_ui.h b/chromium/chrome/browser/ui/webui/cast/cast_ui.h
new file mode 100644
index 00000000000..2cae6ddb476
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/cast/cast_ui.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CAST_CAST_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CAST_CAST_UI_H_
+
+#include "base/macros.h"
+#include "base/values.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The WebUI for chrome://cast
+class CastUI : public content::WebUIController {
+ public:
+ explicit CastUI(content::WebUI* web_ui);
+ ~CastUI() override;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CastUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_CAST_CAST_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/certificate_viewer_ui.cc b/chromium/chrome/browser/ui/webui/certificate_viewer_ui.cc
new file mode 100644
index 00000000000..d8ba1b0960e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/certificate_viewer_ui.cc
@@ -0,0 +1,89 @@
+// 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/ui/webui/certificate_viewer_ui.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/certificate_viewer_webui.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "ui/web_dialogs/web_dialog_delegate.h"
+
+namespace {
+
+content::WebUIDataSource* GetWebUIDataSource(const std::string& host) {
+ content::WebUIDataSource* html_source =
+ content::WebUIDataSource::Create(host);
+
+ // Localized strings.
+ html_source->AddLocalizedString("general", IDS_CERT_INFO_GENERAL_TAB_LABEL);
+ html_source->AddLocalizedString("details", IDS_CERT_INFO_DETAILS_TAB_LABEL);
+ html_source->AddLocalizedString("close", IDS_CLOSE);
+ html_source->AddLocalizedString("export",
+ IDS_CERT_DETAILS_EXPORT_CERTIFICATE);
+ html_source->AddLocalizedString("usages",
+ IDS_CERT_INFO_VERIFIED_USAGES_GROUP);
+ html_source->AddLocalizedString("issuedTo", IDS_CERT_INFO_SUBJECT_GROUP);
+ html_source->AddLocalizedString("issuedBy", IDS_CERT_INFO_ISSUER_GROUP);
+ html_source->AddLocalizedString("cn", IDS_CERT_INFO_COMMON_NAME_LABEL);
+ html_source->AddLocalizedString("o", IDS_CERT_INFO_ORGANIZATION_LABEL);
+ html_source->AddLocalizedString("ou",
+ IDS_CERT_INFO_ORGANIZATIONAL_UNIT_LABEL);
+ html_source->AddLocalizedString("validity", IDS_CERT_INFO_VALIDITY_GROUP);
+ html_source->AddLocalizedString("issuedOn", IDS_CERT_INFO_ISSUED_ON_LABEL);
+ html_source->AddLocalizedString("expiresOn", IDS_CERT_INFO_EXPIRES_ON_LABEL);
+ html_source->AddLocalizedString("fingerprints",
+ IDS_CERT_INFO_FINGERPRINTS_GROUP);
+ html_source->AddLocalizedString("sha256",
+ IDS_CERT_INFO_SHA256_FINGERPRINT_LABEL);
+ html_source->AddLocalizedString("sha1", IDS_CERT_INFO_SHA1_FINGERPRINT_LABEL);
+ html_source->AddLocalizedString("hierarchy",
+ IDS_CERT_DETAILS_CERTIFICATE_HIERARCHY_LABEL);
+ html_source->AddLocalizedString("certFields",
+ IDS_CERT_DETAILS_CERTIFICATE_FIELDS_LABEL);
+ html_source->AddLocalizedString("certFieldVal",
+ IDS_CERT_DETAILS_CERTIFICATE_FIELD_VALUE_LABEL);
+ html_source->SetJsonPath("strings.js");
+
+ // Add required resources.
+ html_source->AddResourcePath("certificate_viewer.js",
+ IDR_CERTIFICATE_VIEWER_JS);
+ html_source->AddResourcePath("certificate_viewer.css",
+ IDR_CERTIFICATE_VIEWER_CSS);
+ html_source->SetDefaultResource(IDR_CERTIFICATE_VIEWER_HTML);
+ return html_source;
+}
+
+} // namespace
+
+CertificateViewerModalDialogUI::CertificateViewerModalDialogUI(
+ content::WebUI* web_ui)
+ : ui::WebDialogUI(web_ui) {
+ // Set up the chrome://view-cert-dialog source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(
+ profile,
+ GetWebUIDataSource(chrome::kChromeUICertificateViewerDialogHost));
+}
+
+CertificateViewerModalDialogUI::~CertificateViewerModalDialogUI() {
+}
+
+CertificateViewerUI::CertificateViewerUI(content::WebUI* web_ui)
+ : ConstrainedWebDialogUI(web_ui) {
+ // Set up the chrome://view-cert source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(
+ profile,
+ GetWebUIDataSource(chrome::kChromeUICertificateViewerHost));
+}
+
+CertificateViewerUI::~CertificateViewerUI() {
+}
diff --git a/chromium/chrome/browser/ui/webui/certificate_viewer_ui.h b/chromium/chrome/browser/ui/webui/certificate_viewer_ui.h
new file mode 100644
index 00000000000..5355a07f13d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/certificate_viewer_ui.h
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CERTIFICATE_VIEWER_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CERTIFICATE_VIEWER_UI_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+// The WebUI for chrome://view-cert-dialog
+class CertificateViewerModalDialogUI : public ui::WebDialogUI {
+ public:
+ explicit CertificateViewerModalDialogUI(content::WebUI* web_ui);
+ ~CertificateViewerModalDialogUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CertificateViewerModalDialogUI);
+};
+
+// The WebUI for chrome://view-cert
+class CertificateViewerUI : public ConstrainedWebDialogUI {
+ public:
+ explicit CertificateViewerUI(content::WebUI* web_ui);
+ ~CertificateViewerUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CertificateViewerUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_CERTIFICATE_VIEWER_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/certificate_viewer_webui.cc b/chromium/chrome/browser/ui/webui/certificate_viewer_webui.cc
new file mode 100644
index 00000000000..eae1c60fa03
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/certificate_viewer_webui.cc
@@ -0,0 +1,500 @@
+// 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/ui/webui/certificate_viewer_webui.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/i18n/time_formatting.h"
+#include "base/json/json_writer.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/certificate_viewer.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/certificate_dialogs.h"
+#include "chrome/browser/ui/webui/certificate_viewer_ui.h"
+#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
+#include "chrome/common/net/x509_certificate_model.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/geometry/size.h"
+
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace {
+
+// Helper class for building a Value representation of a certificate. The class
+// gathers data for a single node of the representation tree and builds a
+// DictionaryValue out of that.
+class CertNodeBuilder {
+ public:
+ // Starts the node with "label" set to |label|.
+ explicit CertNodeBuilder(base::StringPiece label);
+
+ // Convenience version: Converts |label_id| to the corresponding resource
+ // string, then delegates to the other constructor.
+ explicit CertNodeBuilder(int label_id);
+
+ // Builder methods all return |*this| so that they can be chained in single
+ // expressions.
+
+ // Sets the "payload.val" field. Call this at most once.
+ CertNodeBuilder& Payload(base::StringPiece payload);
+
+ // Adds |child| in the list keyed "children". Can be called multiple times.
+ CertNodeBuilder& Child(std::unique_ptr<base::DictionaryValue> child);
+
+ // Similar to Child, but if the argument is null, then this does not add
+ // anything.
+ CertNodeBuilder& ChildIfNotNull(std::unique_ptr<base::DictionaryValue> child);
+
+ // Creates a DictionaryValue representation of the collected information. Only
+ // call this once.
+ std::unique_ptr<base::DictionaryValue> Build();
+
+ private:
+ base::DictionaryValue node_;
+ base::ListValue children_;
+ // |built_| is false until Build() is called. Once it is |true|, |node_| and
+ // |children_| are no longer valid for use.
+ bool built_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(CertNodeBuilder);
+};
+
+CertNodeBuilder::CertNodeBuilder(base::StringPiece label) {
+ node_.SetString("label", label);
+}
+
+CertNodeBuilder::CertNodeBuilder(int label_id)
+ : CertNodeBuilder(l10n_util::GetStringUTF8(label_id)) {}
+
+CertNodeBuilder& CertNodeBuilder::Payload(base::StringPiece payload) {
+ DCHECK(!node_.HasKey("payload.val"));
+ node_.SetString("payload.val", payload);
+ return *this;
+}
+
+CertNodeBuilder& CertNodeBuilder::Child(
+ std::unique_ptr<base::DictionaryValue> child) {
+ children_.Append(std::move(child));
+ return *this;
+}
+
+CertNodeBuilder& CertNodeBuilder::ChildIfNotNull(
+ std::unique_ptr<base::DictionaryValue> child) {
+ if (child)
+ return Child(std::move(child));
+ return *this;
+}
+
+std::unique_ptr<base::DictionaryValue> CertNodeBuilder::Build() {
+ DCHECK(!built_);
+ if (!children_.empty()) {
+ node_.Set("children", base::MakeUnique<base::Value>(std::move(children_)));
+ }
+ built_ = true;
+ return base::MakeUnique<base::DictionaryValue>(std::move(node_));
+}
+
+} // namespace
+
+// Shows a certificate using the WebUI certificate viewer.
+void ShowCertificateViewer(WebContents* web_contents,
+ gfx::NativeWindow parent,
+ net::X509Certificate* cert) {
+ CertificateViewerDialog* dialog = new CertificateViewerDialog(cert);
+ dialog->Show(web_contents, parent);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CertificateViewerDialog
+
+CertificateViewerModalDialog::CertificateViewerModalDialog(
+ net::X509Certificate* cert)
+ : cert_(cert), webui_(NULL), window_(NULL) {
+ // Construct the dialog title from the certificate.
+ title_ = l10n_util::GetStringFUTF16(
+ IDS_CERT_INFO_DIALOG_TITLE,
+ base::UTF8ToUTF16(
+ x509_certificate_model::GetTitle(cert_->os_cert_handle())));
+}
+
+CertificateViewerModalDialog::~CertificateViewerModalDialog() {
+}
+
+void CertificateViewerModalDialog::Show(content::WebContents* web_contents,
+ gfx::NativeWindow parent) {
+ window_ = chrome::ShowWebDialog(parent,
+ web_contents->GetBrowserContext(),
+ this);
+}
+
+gfx::NativeWindow
+CertificateViewerModalDialog::GetNativeWebContentsModalDialog() {
+#if defined(USE_AURA)
+ return window_;
+#else
+ NOTREACHED();
+ return NULL;
+#endif
+}
+
+ui::ModalType CertificateViewerModalDialog::GetDialogModalType() const {
+ return ui::MODAL_TYPE_SYSTEM;
+}
+
+base::string16 CertificateViewerModalDialog::GetDialogTitle() const {
+ return title_;
+}
+
+GURL CertificateViewerModalDialog::GetDialogContentURL() const {
+ return GURL(chrome::kChromeUICertificateViewerDialogURL);
+}
+
+void CertificateViewerModalDialog::GetWebUIMessageHandlers(
+ std::vector<WebUIMessageHandler*>* handlers) const {
+ handlers->push_back(new CertificateViewerDialogHandler(
+ const_cast<CertificateViewerModalDialog*>(this), cert_.get()));
+}
+
+void CertificateViewerModalDialog::GetDialogSize(gfx::Size* size) const {
+ const int kDefaultWidth = 544;
+ const int kDefaultHeight = 628;
+ size->SetSize(kDefaultWidth, kDefaultHeight);
+}
+
+std::string CertificateViewerModalDialog::GetDialogArgs() const {
+ std::string data;
+
+ // Certificate information. The keys in this dictionary's general key
+ // correspond to the IDs in the Html page.
+ base::DictionaryValue cert_info;
+ net::X509Certificate::OSCertHandle cert_hnd = cert_->os_cert_handle();
+
+ // Get the certificate chain.
+ net::X509Certificate::OSCertHandles cert_chain;
+ cert_chain.push_back(cert_->os_cert_handle());
+ const net::X509Certificate::OSCertHandles& certs =
+ cert_->GetIntermediateCertificates();
+ cert_chain.insert(cert_chain.end(), certs.begin(), certs.end());
+
+ // Certificate usage.
+ std::vector<std::string> usages;
+ x509_certificate_model::GetUsageStrings(cert_hnd, &usages);
+ std::string usagestr;
+ for (std::vector<std::string>::iterator it = usages.begin();
+ it != usages.end(); ++it) {
+ if (usagestr.length() > 0) {
+ usagestr += "\n";
+ }
+ usagestr += *it;
+ }
+ cert_info.SetString("general.usages", usagestr);
+
+ // Standard certificate details.
+ const std::string alternative_text =
+ l10n_util::GetStringUTF8(IDS_CERT_INFO_FIELD_NOT_PRESENT);
+ cert_info.SetString("general.title", l10n_util::GetStringFUTF8(
+ IDS_CERT_INFO_DIALOG_TITLE,
+ base::UTF8ToUTF16(x509_certificate_model::GetTitle(
+ cert_chain.front()))));
+
+ // Issued to information.
+ cert_info.SetString("general.issued-cn",
+ x509_certificate_model::GetSubjectCommonName(cert_hnd, alternative_text));
+ cert_info.SetString("general.issued-o",
+ x509_certificate_model::GetSubjectOrgName(cert_hnd, alternative_text));
+ cert_info.SetString("general.issued-ou",
+ x509_certificate_model::GetSubjectOrgUnitName(cert_hnd,
+ alternative_text));
+
+ // Issuer information.
+ cert_info.SetString("general.issuer-cn",
+ x509_certificate_model::GetIssuerCommonName(cert_hnd, alternative_text));
+ cert_info.SetString("general.issuer-o",
+ x509_certificate_model::GetIssuerOrgName(cert_hnd, alternative_text));
+ cert_info.SetString("general.issuer-ou",
+ x509_certificate_model::GetIssuerOrgUnitName(cert_hnd, alternative_text));
+
+ // Validity period.
+ base::Time issued, expires;
+ std::string issued_str, expires_str;
+ if (x509_certificate_model::GetTimes(cert_hnd, &issued, &expires)) {
+ issued_str = base::UTF16ToUTF8(
+ base::TimeFormatFriendlyDateAndTime(issued));
+ expires_str = base::UTF16ToUTF8(
+ base::TimeFormatFriendlyDateAndTime(expires));
+ } else {
+ issued_str = alternative_text;
+ expires_str = alternative_text;
+ }
+ cert_info.SetString("general.issue-date", issued_str);
+ cert_info.SetString("general.expiry-date", expires_str);
+
+ cert_info.SetString("general.sha256",
+ x509_certificate_model::HashCertSHA256(cert_hnd));
+ cert_info.SetString("general.sha1",
+ x509_certificate_model::HashCertSHA1(cert_hnd));
+
+ // Certificate hierarchy is constructed from bottom up.
+ std::unique_ptr<base::ListValue> children;
+ int index = 0;
+ for (net::X509Certificate::OSCertHandles::const_iterator i =
+ cert_chain.begin(); i != cert_chain.end(); ++i, ++index) {
+ std::unique_ptr<base::DictionaryValue> cert_node(
+ new base::DictionaryValue());
+ base::ListValue cert_details;
+ cert_node->SetString("label", x509_certificate_model::GetTitle(*i).c_str());
+ cert_node->SetDouble("payload.index", index);
+ // Add the child from the previous iteration.
+ if (children)
+ cert_node->Set("children", std::move(children));
+
+ // Add this node to the children list for the next iteration.
+ children = base::MakeUnique<base::ListValue>();
+ children->Append(std::move(cert_node));
+ }
+ // Set the last node as the top of the certificate hierarchy.
+ cert_info.Set("hierarchy", std::move(children));
+
+ base::JSONWriter::Write(cert_info, &data);
+
+ return data;
+}
+
+void CertificateViewerModalDialog::OnDialogShown(
+ content::WebUI* webui,
+ content::RenderViewHost* render_view_host) {
+ webui_ = webui;
+}
+
+void CertificateViewerModalDialog::OnDialogClosed(
+ const std::string& json_retval) {
+}
+
+void CertificateViewerModalDialog::OnCloseContents(WebContents* source,
+ bool* out_close_dialog) {
+ *out_close_dialog = true;
+}
+
+bool CertificateViewerModalDialog::ShouldShowDialogTitle() const {
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CertificateViewerDialog
+
+CertificateViewerDialog::CertificateViewerDialog(net::X509Certificate* cert)
+ : CertificateViewerModalDialog(cert),
+ dialog_(NULL) {
+}
+
+CertificateViewerDialog::~CertificateViewerDialog() {
+}
+
+void CertificateViewerDialog::Show(WebContents* web_contents,
+ gfx::NativeWindow parent) {
+ // TODO(bshe): UI tweaks needed for Aura HTML Dialog, such as adding padding
+ // on the title for Aura ConstrainedWebDialogUI.
+ dialog_ = ShowConstrainedWebDialog(web_contents->GetBrowserContext(), this,
+ web_contents);
+}
+
+gfx::NativeWindow CertificateViewerDialog::GetNativeWebContentsModalDialog() {
+ return dialog_->GetNativeDialog();
+}
+
+GURL CertificateViewerDialog::GetDialogContentURL() const {
+ return GURL(chrome::kChromeUICertificateViewerURL);
+}
+
+ui::ModalType CertificateViewerDialog::GetDialogModalType() const {
+ return ui::MODAL_TYPE_NONE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CertificateViewerDialogHandler
+
+CertificateViewerDialogHandler::CertificateViewerDialogHandler(
+ CertificateViewerModalDialog* dialog,
+ net::X509Certificate* cert)
+ : cert_(cert), dialog_(dialog) {
+ cert_chain_.push_back(cert_->os_cert_handle());
+ const net::X509Certificate::OSCertHandles& certs =
+ cert_->GetIntermediateCertificates();
+ cert_chain_.insert(cert_chain_.end(), certs.begin(), certs.end());
+}
+
+CertificateViewerDialogHandler::~CertificateViewerDialogHandler() {
+}
+
+void CertificateViewerDialogHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("exportCertificate",
+ base::Bind(&CertificateViewerDialogHandler::ExportCertificate,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("requestCertificateFields",
+ base::Bind(&CertificateViewerDialogHandler::RequestCertificateFields,
+ base::Unretained(this)));
+}
+
+void CertificateViewerDialogHandler::ExportCertificate(
+ const base::ListValue* args) {
+ int cert_index = GetCertificateIndex(args);
+ if (cert_index < 0)
+ return;
+
+ gfx::NativeWindow window =
+ platform_util::GetTopLevel(dialog_->GetNativeWebContentsModalDialog());
+ ShowCertExportDialog(web_ui()->GetWebContents(),
+ window,
+ cert_chain_.begin() + cert_index,
+ cert_chain_.end());
+}
+
+void CertificateViewerDialogHandler::RequestCertificateFields(
+ const base::ListValue* args) {
+ int cert_index = GetCertificateIndex(args);
+ if (cert_index < 0)
+ return;
+
+ net::X509Certificate::OSCertHandle cert = cert_chain_[cert_index];
+
+ CertNodeBuilder version_node(IDS_CERT_DETAILS_VERSION);
+ std::string version = x509_certificate_model::GetVersion(cert);
+ if (!version.empty()) {
+ version_node.Payload(l10n_util::GetStringFUTF8(
+ IDS_CERT_DETAILS_VERSION_FORMAT, base::UTF8ToUTF16(version)));
+ }
+
+ CertNodeBuilder issued_node_builder(IDS_CERT_DETAILS_NOT_BEFORE);
+ CertNodeBuilder expires_node_builder(IDS_CERT_DETAILS_NOT_AFTER);
+ base::Time issued, expires;
+ if (x509_certificate_model::GetTimes(cert, &issued, &expires)) {
+ issued_node_builder.Payload(base::UTF16ToUTF8(
+ base::TimeFormatShortDateAndTimeWithTimeZone(issued)));
+ expires_node_builder.Payload(base::UTF16ToUTF8(
+ base::TimeFormatShortDateAndTimeWithTimeZone(expires)));
+ }
+
+ x509_certificate_model::Extensions extensions;
+ x509_certificate_model::GetExtensions(
+ l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_CRITICAL),
+ l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_NON_CRITICAL),
+ cert, &extensions);
+
+ std::unique_ptr<base::DictionaryValue> details_extensions;
+ if (!extensions.empty()) {
+ CertNodeBuilder details_extensions_builder(IDS_CERT_DETAILS_EXTENSIONS);
+ for (const x509_certificate_model::Extension& extension : extensions) {
+ details_extensions_builder.Child(
+ CertNodeBuilder(extension.name).Payload(extension.value).Build());
+ }
+ details_extensions = details_extensions_builder.Build();
+ }
+
+ base::ListValue root_list;
+ root_list.Append(
+ CertNodeBuilder(x509_certificate_model::GetTitle(cert))
+ .Child(
+ CertNodeBuilder(
+ l10n_util::GetStringUTF8(IDS_CERT_DETAILS_CERTIFICATE))
+ // Main certificate fields.
+ .Child(version_node.Build())
+ .Child(
+ CertNodeBuilder(IDS_CERT_DETAILS_SERIAL_NUMBER)
+ .Payload(
+ x509_certificate_model::GetSerialNumberHexified(
+ cert, l10n_util::GetStringUTF8(
+ IDS_CERT_INFO_FIELD_NOT_PRESENT)))
+ .Build())
+ .Child(CertNodeBuilder(IDS_CERT_DETAILS_CERTIFICATE_SIG_ALG)
+ .Payload(x509_certificate_model::
+ ProcessSecAlgorithmSignature(cert))
+ .Build())
+ .Child(
+ CertNodeBuilder(IDS_CERT_DETAILS_ISSUER)
+ .Payload(x509_certificate_model::GetIssuerName(cert))
+ .Build())
+ // Validity period.
+ .Child(CertNodeBuilder(IDS_CERT_DETAILS_VALIDITY)
+ .Child(issued_node_builder.Build())
+ .Child(expires_node_builder.Build())
+ .Build())
+ .Child(
+ CertNodeBuilder(IDS_CERT_DETAILS_SUBJECT)
+ .Payload(x509_certificate_model::GetSubjectName(cert))
+ .Build())
+ // Subject key information.
+ .Child(
+ CertNodeBuilder(IDS_CERT_DETAILS_SUBJECT_KEY_INFO)
+ .Child(
+ CertNodeBuilder(IDS_CERT_DETAILS_SUBJECT_KEY_ALG)
+ .Payload(
+ x509_certificate_model::
+ ProcessSecAlgorithmSubjectPublicKey(
+ cert))
+ .Build())
+ .Child(CertNodeBuilder(IDS_CERT_DETAILS_SUBJECT_KEY)
+ .Payload(
+ x509_certificate_model::
+ ProcessSubjectPublicKeyInfo(cert))
+ .Build())
+ .Build())
+ // Extensions.
+ .ChildIfNotNull(std::move(details_extensions))
+ .Child(
+ CertNodeBuilder(IDS_CERT_DETAILS_CERTIFICATE_SIG_ALG)
+ .Payload(x509_certificate_model::
+ ProcessSecAlgorithmSignatureWrap(cert))
+ .Build())
+ .Child(CertNodeBuilder(IDS_CERT_DETAILS_CERTIFICATE_SIG_VALUE)
+ .Payload(x509_certificate_model::
+ ProcessRawBitsSignatureWrap(cert))
+ .Build())
+ .Child(
+ CertNodeBuilder(IDS_CERT_INFO_FINGERPRINTS_GROUP)
+ .Child(CertNodeBuilder(
+ IDS_CERT_INFO_SHA256_FINGERPRINT_LABEL)
+ .Payload(
+ x509_certificate_model::HashCertSHA256(
+ cert))
+ .Build())
+ .Child(
+ CertNodeBuilder(
+ IDS_CERT_INFO_SHA1_FINGERPRINT_LABEL)
+ .Payload(x509_certificate_model::HashCertSHA1(
+ cert))
+ .Build())
+ .Build())
+ .Build())
+ .Build());
+
+ // Send certificate information to javascript.
+ web_ui()->CallJavascriptFunctionUnsafe("cert_viewer.getCertificateFields",
+ root_list);
+}
+
+int CertificateViewerDialogHandler::GetCertificateIndex(
+ const base::ListValue* args) const {
+ int cert_index;
+ double val;
+ if (!(args->GetDouble(0, &val)))
+ return -1;
+ cert_index = static_cast<int>(val);
+ if (cert_index < 0 || cert_index >= static_cast<int>(cert_chain_.size()))
+ return -1;
+ return cert_index;
+}
diff --git a/chromium/chrome/browser/ui/webui/certificate_viewer_webui.h b/chromium/chrome/browser/ui/webui/certificate_viewer_webui.h
new file mode 100644
index 00000000000..530dfb7e613
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/certificate_viewer_webui.h
@@ -0,0 +1,140 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CERTIFICATE_VIEWER_WEBUI_H_
+#define CHROME_BROWSER_UI_WEBUI_CERTIFICATE_VIEWER_WEBUI_H_
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "net/cert/x509_certificate.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/web_dialogs/web_dialog_delegate.h"
+
+namespace content {
+class WebContents;
+}
+
+class ConstrainedWebDialogDelegate;
+
+// Modal dialog for displaying detailed certificate information. This is used in
+// chromeos builds to display detailed information in a modal dialog when the
+// user clicks on "View" from the Certificate Manager dialog.
+class CertificateViewerModalDialog : public ui::WebDialogDelegate {
+ public:
+ // Construct a certificate viewer for the passed in certificate. A reference
+ // to the certificate pointer is added for the lifetime of the certificate
+ // viewer.
+ explicit CertificateViewerModalDialog(
+ net::X509Certificate* cert);
+ ~CertificateViewerModalDialog() override;
+
+ virtual void Show(content::WebContents* web_contents,
+ gfx::NativeWindow parent);
+ virtual gfx::NativeWindow GetNativeWebContentsModalDialog();
+ const content::WebUI* GetWebUI() const { return webui_; }
+
+ protected:
+ // Overridden from ui::WebDialogDelegate:
+ ui::ModalType GetDialogModalType() const override;
+ base::string16 GetDialogTitle() const override;
+ GURL GetDialogContentURL() const override;
+ void GetWebUIMessageHandlers(
+ std::vector<content::WebUIMessageHandler*>* handlers) const override;
+ void GetDialogSize(gfx::Size* size) const override;
+ std::string GetDialogArgs() const override;
+ void OnDialogShown(content::WebUI* webui,
+ content::RenderViewHost* render_view_host) override;
+ void OnDialogClosed(const std::string& json_retval) override;
+ void OnCloseContents(content::WebContents* source,
+ bool* out_close_dialog) override;
+ bool ShouldShowDialogTitle() const override;
+
+ // The certificate being viewed.
+ scoped_refptr<net::X509Certificate> cert_;
+
+ // The title of the certificate viewer dialog, Certificate Viewer: CN.
+ base::string16 title_;
+
+ private:
+ content::WebUI* webui_;
+ gfx::NativeWindow window_;
+ DISALLOW_COPY_AND_ASSIGN(CertificateViewerModalDialog);
+};
+
+// Dialog for displaying detailed certificate information. This is used in linux
+// and chromeos builds to display detailed information in a floating dialog when
+// the user clicks on "Certificate Information" from the lock icon of a web site
+// or "View" from the Certificate Manager.
+class CertificateViewerDialog : public CertificateViewerModalDialog {
+ public:
+ // Construct a certificate viewer for the passed in certificate. A reference
+ // to the certificate pointer is added for the lifetime of the certificate
+ // viewer.
+ explicit CertificateViewerDialog(net::X509Certificate* cert);
+ ~CertificateViewerDialog() override;
+
+ // CertificateViewerModalDialog overrides.
+ void Show(content::WebContents* web_contents,
+ gfx::NativeWindow parent) override;
+ gfx::NativeWindow GetNativeWebContentsModalDialog() override;
+
+ protected:
+ // Overridden from ui::WebDialogDelegate:
+ GURL GetDialogContentURL() const override;
+ ui::ModalType GetDialogModalType() const override;
+
+ private:
+ ConstrainedWebDialogDelegate* dialog_;
+
+ DISALLOW_COPY_AND_ASSIGN(CertificateViewerDialog);
+};
+
+// Dialog handler which handles calls from the JS WebUI code to view certificate
+// details and export the certificate.
+class CertificateViewerDialogHandler : public content::WebUIMessageHandler {
+ public:
+ CertificateViewerDialogHandler(CertificateViewerModalDialog* dialog,
+ net::X509Certificate* cert);
+ ~CertificateViewerDialogHandler() override;
+
+ // Overridden from WebUIMessageHandler
+ void RegisterMessages() override;
+
+ private:
+ // Brings up the export certificate dialog for the chosen certificate in the
+ // chain.
+ //
+ // The input is an integer index to the certificate in the chain to export.
+ void ExportCertificate(const base::ListValue* args);
+
+ // Gets the details for a specific certificate in the certificate chain. Calls
+ // the javascript function cert_viewer.getCertificateFields with a tree
+ // structure containing the fields and values for certain nodes.
+ //
+ // The input is an integer index to the certificate in the chain to view.
+ void RequestCertificateFields(const base::ListValue* args);
+
+ // Helper function to get the certificate index from |args|. Returns -1 if
+ // the index is out of range.
+ int GetCertificateIndex(const base::ListValue* args) const;
+
+ // The certificate being viewed.
+ scoped_refptr<net::X509Certificate> cert_;
+
+ // The dialog.
+ CertificateViewerModalDialog* dialog_;
+
+ // The certificate chain. Does not take references, so only valid as long as
+ // |cert_| is.
+ net::X509Certificate::OSCertHandles cert_chain_;
+
+ DISALLOW_COPY_AND_ASSIGN(CertificateViewerDialogHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_CERTIFICATE_VIEWER_WEBUI_H_
diff --git a/chromium/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc b/chromium/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
new file mode 100644
index 00000000000..6f05f19400e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
@@ -0,0 +1,121 @@
+// Copyright (c) 2011 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/macros.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.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 "content/public/browser/navigation_details.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/notification_types.h"
+
+namespace {
+
+class NavigationNotificationObserver : public content::NotificationObserver {
+ public:
+ NavigationNotificationObserver()
+ : got_navigation_(false),
+ http_status_code_(0) {
+ registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
+ content::NotificationService::AllSources());
+ }
+
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override {
+ DCHECK_EQ(content::NOTIFICATION_NAV_ENTRY_COMMITTED, type);
+ got_navigation_ = true;
+ http_status_code_ =
+ content::Details<content::LoadCommittedDetails>(details)->
+ http_status_code;
+ }
+
+ int http_status_code() const { return http_status_code_; }
+ bool got_navigation() const { return got_navigation_; }
+
+ private:
+ content::NotificationRegistrar registrar_;
+ int got_navigation_;
+ int http_status_code_;
+
+ DISALLOW_COPY_AND_ASSIGN(NavigationNotificationObserver);
+};
+
+class NavigationObserver : public content::WebContentsObserver {
+public:
+ enum NavigationResult {
+ NOT_FINISHED,
+ ERROR_PAGE,
+ SUCCESS,
+ };
+
+ explicit NavigationObserver(content::WebContents* web_contents)
+ : WebContentsObserver(web_contents), navigation_result_(NOT_FINISHED) {}
+ ~NavigationObserver() override = default;
+
+ void DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) override {
+ navigation_result_ =
+ navigation_handle->IsErrorPage() ? ERROR_PAGE : SUCCESS;
+ }
+
+ NavigationResult navigation_result() const { return navigation_result_; }
+
+ void Reset() { navigation_result_ = NOT_FINISHED; }
+
+ private:
+ NavigationResult navigation_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(NavigationObserver);
+};
+
+} // namespace
+
+typedef InProcessBrowserTest ChromeURLDataManagerTest;
+
+// Makes sure navigating to the new tab page results in a http status code
+// of 200.
+IN_PROC_BROWSER_TEST_F(ChromeURLDataManagerTest, 200) {
+ NavigationNotificationObserver observer;
+ ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
+ EXPECT_TRUE(observer.got_navigation());
+ EXPECT_EQ(200, observer.http_status_code());
+}
+
+// Makes sure browser does not crash when navigating to an unknown resource.
+IN_PROC_BROWSER_TEST_F(ChromeURLDataManagerTest, UnknownResource) {
+ // Known resource
+ NavigationObserver observer(
+ browser()->tab_strip_model()->GetActiveWebContents());
+ ui_test_utils::NavigateToURL(
+ browser(), GURL("chrome://theme/IDR_SETTINGS_FAVICON"));
+ EXPECT_EQ(NavigationObserver::SUCCESS, observer.navigation_result());
+
+ // Unknown resource
+ observer.Reset();
+ ui_test_utils::NavigateToURL(
+ browser(), GURL("chrome://theme/IDR_ASDFGHJKL"));
+ EXPECT_EQ(NavigationObserver::ERROR_PAGE, observer.navigation_result());
+}
+
+// Makes sure browser does not crash when the resource scale is very large.
+IN_PROC_BROWSER_TEST_F(ChromeURLDataManagerTest, LargeResourceScale) {
+ // Valid scale
+ NavigationObserver observer(
+ browser()->tab_strip_model()->GetActiveWebContents());
+ ui_test_utils::NavigateToURL(
+ browser(), GURL("chrome://theme/IDR_SETTINGS_FAVICON@2x"));
+ EXPECT_EQ(NavigationObserver::SUCCESS, observer.navigation_result());
+
+ // Unreasonably large scale
+ observer.Reset();
+ ui_test_utils::NavigateToURL(
+ browser(), GURL("chrome://theme/IDR_SETTINGS_FAVICON@99999x"));
+ EXPECT_EQ(NavigationObserver::ERROR_PAGE, observer.navigation_result());
+}
diff --git a/chromium/chrome/browser/ui/webui/chrome_web_contents_handler.cc b/chromium/chrome/browser/ui/webui/chrome_web_contents_handler.cc
new file mode 100644
index 00000000000..419cc8bd1ae
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chrome_web_contents_handler.cc
@@ -0,0 +1,104 @@
+// 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/ui/webui/chrome_web_contents_handler.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_navigator.h"
+#include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "content/public/browser/web_contents.h"
+
+using content::BrowserContext;
+using content::OpenURLParams;
+using content::WebContents;
+
+ChromeWebContentsHandler::ChromeWebContentsHandler() {
+}
+
+ChromeWebContentsHandler::~ChromeWebContentsHandler() {
+}
+
+// Opens a new URL inside |source|. |context| is the browser context that the
+// browser should be owned by. |params| contains the URL to open and various
+// attributes such as disposition. On return |out_new_contents| contains the
+// WebContents the URL is opened in. Returns the web contents opened by the
+// browser.
+WebContents* ChromeWebContentsHandler::OpenURLFromTab(
+ content::BrowserContext* context,
+ WebContents* source,
+ const OpenURLParams& params) {
+ if (!context)
+ return NULL;
+
+ Profile* profile = Profile::FromBrowserContext(context);
+
+ Browser* browser = chrome::FindTabbedBrowser(profile, false);
+ const bool browser_created = !browser;
+ if (!browser) {
+ // TODO(erg): OpenURLParams should pass a user_gesture flag, pass it to
+ // CreateParams, and pass the real value to nav_params below.
+ browser =
+ new Browser(Browser::CreateParams(Browser::TYPE_TABBED, profile, true));
+ }
+ chrome::NavigateParams nav_params(browser, params.url, params.transition);
+ nav_params.referrer = params.referrer;
+ if (source && source->IsCrashed() &&
+ params.disposition == WindowOpenDisposition::CURRENT_TAB &&
+ ui::PageTransitionCoreTypeIs(params.transition,
+ ui::PAGE_TRANSITION_LINK)) {
+ nav_params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
+ } else {
+ nav_params.disposition = params.disposition;
+ }
+ nav_params.window_action = chrome::NavigateParams::SHOW_WINDOW;
+ nav_params.user_gesture = true;
+ chrome::Navigate(&nav_params);
+
+ // Close the browser if chrome::Navigate created a new one.
+ if (browser_created && (browser != nav_params.browser))
+ browser->window()->Close();
+
+ return nav_params.target_contents;
+}
+
+// Creates a new tab with |new_contents|. |context| is the browser context that
+// the browser should be owned by. |source| is the WebContent where the
+// operation originated. |disposition| controls how the new tab should be
+// opened. |initial_rect| is the position and size of the window if a new window
+// is created. |user_gesture| is true if the operation was started by a user
+// gesture.
+void ChromeWebContentsHandler::AddNewContents(
+ content::BrowserContext* context,
+ WebContents* source,
+ WebContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_rect,
+ bool user_gesture) {
+ if (!context)
+ return;
+
+ Profile* profile = Profile::FromBrowserContext(context);
+
+ Browser* browser = chrome::FindTabbedBrowser(profile, false);
+ const bool browser_created = !browser;
+ if (!browser) {
+ browser = new Browser(
+ Browser::CreateParams(Browser::TYPE_TABBED, profile, user_gesture));
+ }
+ chrome::NavigateParams params(browser, new_contents);
+ params.source_contents = source;
+ params.disposition = disposition;
+ params.window_bounds = initial_rect;
+ params.window_action = chrome::NavigateParams::SHOW_WINDOW;
+ params.user_gesture = user_gesture;
+ chrome::Navigate(&params);
+
+ // Close the browser if chrome::Navigate created a new one.
+ if (browser_created && (browser != params.browser))
+ browser->window()->Close();
+}
diff --git a/chromium/chrome/browser/ui/webui/chrome_web_contents_handler.h b/chromium/chrome/browser/ui/webui/chrome_web_contents_handler.h
new file mode 100644
index 00000000000..b88ea3e2441
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chrome_web_contents_handler.h
@@ -0,0 +1,34 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROME_WEB_CONTENTS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROME_WEB_CONTENTS_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "ui/web_dialogs/web_dialog_web_contents_delegate.h"
+
+class ChromeWebContentsHandler
+ : public ui::WebDialogWebContentsDelegate::WebContentsHandler {
+ public:
+ ChromeWebContentsHandler();
+ ~ChromeWebContentsHandler() override;
+
+ // Overridden from WebDialogWebContentsDelegate::WebContentsHandler:
+ content::WebContents* OpenURLFromTab(
+ content::BrowserContext* context,
+ content::WebContents* source,
+ const content::OpenURLParams& params) override;
+ void AddNewContents(content::BrowserContext* context,
+ content::WebContents* source,
+ content::WebContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_rect,
+ bool user_gesture) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ChromeWebContentsHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROME_WEB_CONTENTS_HANDLER_H_
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
new file mode 100644
index 00000000000..ad04d3c07d5
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -0,0 +1,828 @@
+// 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/ui/webui/chrome_web_ui_controller_factory.h"
+
+#include <stddef.h>
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "chrome/browser/about_flags.h"
+#include "chrome/browser/devtools/devtools_ui_bindings.h"
+#include "chrome/browser/dom_distiller/dom_distiller_service_factory.h"
+#include "chrome/browser/engagement/site_engagement_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/search/suggestions/suggestions_ui.h"
+#include "chrome/browser/ui/history_ui.h"
+#include "chrome/browser/ui/webui/about_ui.h"
+#include "chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.h"
+#include "chrome/browser/ui/webui/bookmarks_ui.h"
+#include "chrome/browser/ui/webui/components_ui.h"
+#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
+#include "chrome/browser/ui/webui/crashes_ui.h"
+#include "chrome/browser/ui/webui/device_log_ui.h"
+#include "chrome/browser/ui/webui/domain_reliability_internals_ui.h"
+#include "chrome/browser/ui/webui/engagement/site_engagement_ui.h"
+#include "chrome/browser/ui/webui/flags_ui.h"
+#include "chrome/browser/ui/webui/flash_ui.h"
+#include "chrome/browser/ui/webui/gcm_internals_ui.h"
+#include "chrome/browser/ui/webui/help/help_ui.h"
+#include "chrome/browser/ui/webui/identity_internals_ui.h"
+#include "chrome/browser/ui/webui/instant_ui.h"
+#include "chrome/browser/ui/webui/interstitials/interstitial_ui.h"
+#include "chrome/browser/ui/webui/invalidations_ui.h"
+#include "chrome/browser/ui/webui/local_state/local_state_ui.h"
+#include "chrome/browser/ui/webui/log_web_ui_url.h"
+#include "chrome/browser/ui/webui/net_export_ui.h"
+#include "chrome/browser/ui/webui/net_internals/net_internals_ui.h"
+#include "chrome/browser/ui/webui/ntp_tiles_internals_ui.h"
+#include "chrome/browser/ui/webui/omnibox/omnibox_ui.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "chrome/browser/ui/webui/password_manager_internals/password_manager_internals_ui.h"
+#include "chrome/browser/ui/webui/physical_web/physical_web_ui.h"
+#include "chrome/browser/ui/webui/policy_material_design_ui.h"
+#include "chrome/browser/ui/webui/policy_ui.h"
+#include "chrome/browser/ui/webui/predictors/predictors_ui.h"
+#include "chrome/browser/ui/webui/profiler_ui.h"
+#include "chrome/browser/ui/webui/quota_internals/quota_internals_ui.h"
+#include "chrome/browser/ui/webui/settings/md_settings_ui.h"
+#include "chrome/browser/ui/webui/settings_utils.h"
+#include "chrome/browser/ui/webui/signin_internals_ui.h"
+#include "chrome/browser/ui/webui/supervised_user_internals_ui.h"
+#include "chrome/browser/ui/webui/sync_internals_ui.h"
+#include "chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui.h"
+#include "chrome/browser/ui/webui/translate_internals/translate_internals_ui.h"
+#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/chrome_features.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 "components/dom_distiller/core/dom_distiller_constants.h"
+#include "components/dom_distiller/core/dom_distiller_features.h"
+#include "components/dom_distiller/core/dom_distiller_service.h"
+#include "components/dom_distiller/core/url_constants.h"
+#include "components/dom_distiller/webui/dom_distiller_ui.h"
+#include "components/favicon/core/favicon_service.h"
+#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/prefs/pref_service.h"
+#include "components/signin/core/common/profile_management_switches.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/url_utils.h"
+#include "extensions/features/features.h"
+#include "media/media_features.h"
+#include "ppapi/features/features.h"
+#include "printing/features/features.h"
+#include "ui/gfx/favicon_size.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+#include "url/gurl.h"
+
+#if !defined(DISABLE_NACL)
+#include "chrome/browser/ui/webui/nacl_ui.h"
+#endif
+
+#if BUILDFLAG(ENABLE_WEBRTC)
+#include "chrome/browser/ui/webui/media/webrtc_logs_ui.h"
+#endif
+
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
+#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
+#endif
+
+#if !defined(OS_ANDROID)
+#include "chrome/browser/media/router/media_router_feature.h"
+#include "chrome/browser/ui/webui/media_router/media_router_ui.h"
+#endif
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+#include "chrome/browser/ui/webui/cast/cast_ui.h"
+#endif
+
+#if defined(OS_ANDROID)
+#include "chrome/browser/ui/webui/offline/offline_internals_ui.h"
+#include "chrome/browser/ui/webui/popular_sites_internals_ui.h"
+#include "chrome/browser/ui/webui/snippets_internals_ui.h"
+#include "chrome/browser/ui/webui/webapks_ui.h"
+#else
+#include "chrome/browser/signin/easy_unlock_service.h"
+#include "chrome/browser/signin/easy_unlock_service_factory.h"
+#include "chrome/browser/ui/webui/devtools_ui.h"
+#include "chrome/browser/ui/webui/inspect_ui.h"
+#include "chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.h"
+#include "chrome/browser/ui/webui/md_downloads/md_downloads_ui.h"
+#include "chrome/browser/ui/webui/md_feedback/md_feedback_ui.h"
+#include "chrome/browser/ui/webui/md_history_ui.h"
+#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
+#include "chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_ui.h"
+#include "chrome/browser/ui/webui/system_info_ui.h"
+#include "chrome/browser/ui/webui/uber/uber_ui.h"
+#endif
+
+#if defined(OS_CHROMEOS)
+#include "base/sys_info.h"
+#include "chrome/browser/ui/webui/chromeos/bluetooth_pairing_ui.h"
+#include "chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.h"
+#include "chrome/browser/ui/webui/chromeos/choose_mobile_network_ui.h"
+#include "chrome/browser/ui/webui/chromeos/cryptohome_ui.h"
+#include "chrome/browser/ui/webui/chromeos/drive_internals_ui.h"
+#include "chrome/browser/ui/webui/chromeos/first_run/first_run_ui.h"
+#include "chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.h"
+#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
+#include "chrome/browser/ui/webui/chromeos/mobile_setup_ui.h"
+#include "chrome/browser/ui/webui/chromeos/network_ui.h"
+#include "chrome/browser/ui/webui/chromeos/power_ui.h"
+#include "chrome/browser/ui/webui/chromeos/proxy_settings_ui.h"
+#include "chrome/browser/ui/webui/chromeos/set_time_ui.h"
+#include "chrome/browser/ui/webui/chromeos/sim_unlock_ui.h"
+#include "chrome/browser/ui/webui/chromeos/slow_trace_ui.h"
+#include "chrome/browser/ui/webui/chromeos/slow_ui.h"
+#include "chrome/browser/ui/webui/voice_search_ui.h"
+#include "components/proximity_auth/webui/proximity_auth_ui.h"
+#include "components/proximity_auth/webui/url_constants.h"
+#endif
+
+#if defined(OS_CHROMEOS) && !defined(OFFICIAL_BUILD)
+#include "chrome/browser/ui/webui/chromeos/emulator/device_emulator_ui.h"
+#endif
+
+#if !defined(OS_CHROMEOS)
+#include "chrome/browser/ui/webui/app_launcher_page_ui.h"
+#endif
+
+#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
+#include "chrome/browser/ui/sync/sync_promo_ui.h"
+#include "chrome/browser/ui/webui/signin/inline_login_ui.h"
+#include "chrome/browser/ui/webui/signin/md_user_manager_ui.h"
+#include "chrome/browser/ui/webui/signin/signin_email_confirmation_ui.h"
+#include "chrome/browser/ui/webui/signin/signin_error_ui.h"
+#include "chrome/browser/ui/webui/signin/sync_confirmation_ui.h"
+#include "chrome/browser/ui/webui/welcome_ui.h"
+#endif
+
+#if defined(OS_WIN)
+#include "chrome/browser/ui/webui/cleanup_tool/cleanup_tool_ui.h"
+#include "chrome/browser/ui/webui/conflicts_ui.h"
+#include "chrome/browser/ui/webui/set_as_default_browser_ui_win.h"
+#include "chrome/browser/ui/webui/welcome_win10_ui.h"
+#endif
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+#include "chrome/browser/ui/webui/sandbox_internals_ui.h"
+#endif
+
+#if defined(USE_NSS_CERTS) && defined(USE_AURA)
+#include "chrome/browser/ui/webui/certificate_viewer_ui.h"
+#endif
+
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+#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/extensions/extension_constants.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/feature_switch.h"
+#include "extensions/common/manifest.h"
+#endif
+
+using content::WebUI;
+using content::WebUIController;
+using ui::WebDialogUI;
+
+namespace {
+
+// A function for creating a new WebUI. The caller owns the return value, which
+// may be NULL (for example, if the URL refers to an non-existent extension).
+typedef WebUIController* (*WebUIFactoryFunction)(WebUI* web_ui,
+ const GURL& url);
+
+// Template for defining WebUIFactoryFunction.
+template<class T>
+WebUIController* NewWebUI(WebUI* web_ui, const GURL& url) {
+ return new T(web_ui);
+}
+
+// Special case for older about: handlers.
+template<>
+WebUIController* NewWebUI<AboutUI>(WebUI* web_ui, const GURL& url) {
+ return new AboutUI(web_ui, url.host());
+}
+
+#if defined(OS_CHROMEOS)
+template<>
+WebUIController* NewWebUI<chromeos::OobeUI>(WebUI* web_ui, const GURL& url) {
+ return new chromeos::OobeUI(web_ui, url);
+}
+
+// Special case for chrome://proximity_auth.
+template <>
+WebUIController* NewWebUI<proximity_auth::ProximityAuthUI>(WebUI* web_ui,
+ const GURL& url) {
+ content::BrowserContext* browser_context =
+ web_ui->GetWebContents()->GetBrowserContext();
+ return new proximity_auth::ProximityAuthUI(
+ web_ui, EasyUnlockServiceFactory::GetForBrowserContext(browser_context)
+ ->proximity_auth_client());
+}
+#endif
+
+// Special cases for DOM distiller.
+template<>
+WebUIController* NewWebUI<dom_distiller::DomDistillerUi>(WebUI* web_ui,
+ const GURL& url) {
+ // The DomDistillerUi can not depend on components/dom_distiller/content,
+ // so inject the correct DomDistillerService from chrome/.
+ content::BrowserContext* browser_context =
+ web_ui->GetWebContents()->GetBrowserContext();
+ dom_distiller::DomDistillerService* service =
+ dom_distiller::DomDistillerServiceFactory::GetForBrowserContext(
+ browser_context);
+ return new dom_distiller::DomDistillerUi(
+ web_ui, service, dom_distiller::kDomDistillerScheme);
+}
+
+#if !defined(OS_ANDROID)
+template<>
+WebUIController* NewWebUI<settings::MdSettingsUI>(WebUI* web_ui,
+ const GURL& url) {
+ return new settings::MdSettingsUI(web_ui, url);
+}
+
+#if !defined(OS_CHROMEOS)
+template <>
+WebUIController* NewWebUI<WelcomeUI>(WebUI* web_ui, const GURL& url) {
+ return new WelcomeUI(web_ui, url);
+}
+#endif // !defined(OS_CHROMEOS)
+#endif // !defined(OS_ANDROID)
+
+#if defined(OS_WIN)
+template <>
+WebUIController* NewWebUI<WelcomeWin10UI>(WebUI* web_ui, const GURL& url) {
+ return new WelcomeWin10UI(web_ui, url);
+}
+#endif // defined(OS_WIN)
+
+bool IsAboutUI(const GURL& url) {
+ return (url.host_piece() == chrome::kChromeUIChromeURLsHost ||
+ url.host_piece() == chrome::kChromeUICreditsHost ||
+ url.host_piece() == chrome::kChromeUIDNSHost
+#if !defined(OS_ANDROID)
+ || url.host_piece() == chrome::kChromeUITermsHost
+#endif
+#if defined(OS_LINUX) || defined(OS_OPENBSD)
+ || url.host_piece() == chrome::kChromeUILinuxProxyConfigHost
+#endif
+#if defined(OS_CHROMEOS)
+ || url.host_piece() == chrome::kChromeUIOSCreditsHost
+#endif
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+ || url.host_piece() == chrome::kChromeUIDiscardsHost
+#endif
+ ); // NOLINT
+}
+
+// Returns a function that can be used to create the right type of WebUI for a
+// tab, based on its URL. Returns NULL if the URL doesn't have WebUI associated
+// with it.
+WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
+ Profile* profile,
+ const GURL& url) {
+ // This will get called a lot to check all URLs, so do a quick check of other
+ // schemes to filter out most URLs.
+ if (!url.SchemeIs(content::kChromeDevToolsScheme) &&
+ !url.SchemeIs(content::kChromeUIScheme)) {
+ return NULL;
+ }
+
+ /****************************************************************************
+ * Please keep this in alphabetical order. If #ifs or special logics are
+ * required, add it below in the appropriate section.
+ ***************************************************************************/
+ // We must compare hosts only since some of the Web UIs append extra stuff
+ // after the host name.
+ // All platform builds of Chrome will need to have a cloud printing
+ // dialog as backup. It's just that on Chrome OS, it's the only
+ // print dialog.
+ if (url.host_piece() == chrome::kChromeUIBluetoothInternalsHost)
+ return &NewWebUI<BluetoothInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUIComponentsHost)
+ return &NewWebUI<ComponentsUI>;
+ if (url.spec() == chrome::kChromeUIConstrainedHTMLTestURL)
+ return &NewWebUI<ConstrainedWebDialogUI>;
+ if (url.host_piece() == chrome::kChromeUICrashesHost)
+ return &NewWebUI<CrashesUI>;
+ if (url.host_piece() == chrome::kChromeUIDeviceLogHost)
+ return &NewWebUI<chromeos::DeviceLogUI>;
+ if (url.host_piece() == chrome::kChromeUIDomainReliabilityInternalsHost)
+ return &NewWebUI<DomainReliabilityInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUIFlagsHost)
+ return &NewWebUI<FlagsUI>;
+ if (url.host_piece() == chrome::kChromeUIGCMInternalsHost)
+ return &NewWebUI<GCMInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUIInstantHost)
+ return &NewWebUI<InstantUI>;
+ if (url.host_piece() == chrome::kChromeUIInterstitialHost)
+ return &NewWebUI<InterstitialUI>;
+ if (url.host_piece() == chrome::kChromeUIInvalidationsHost)
+ return &NewWebUI<InvalidationsUI>;
+ if (url.host_piece() == chrome::kChromeUILocalStateHost)
+ return &NewWebUI<LocalStateUI>;
+ if (url.host_piece() == chrome::kChromeUINetExportHost)
+ return &NewWebUI<NetExportUI>;
+ if (url.host_piece() == chrome::kChromeUINetInternalsHost)
+ return &NewWebUI<NetInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUINTPTilesInternalsHost)
+ return &NewWebUI<NTPTilesInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUIOmniboxHost)
+ return &NewWebUI<OmniboxUI>;
+ if (url.host_piece() == chrome::kChromeUIPasswordManagerInternalsHost)
+ return &NewWebUI<PasswordManagerInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUIPhysicalWebHost)
+ return &NewWebUI<PhysicalWebUI>;
+ if (url.host_piece() == chrome::kChromeUIPredictorsHost)
+ return &NewWebUI<PredictorsUI>;
+ if (url.host_piece() == chrome::kChromeUIProfilerHost)
+ return &NewWebUI<ProfilerUI>;
+ if (url.host() == chrome::kChromeUIQuotaInternalsHost)
+ return &NewWebUI<QuotaInternalsUI>;
+ if (url.host() == chrome::kChromeUISignInInternalsHost)
+ return &NewWebUI<SignInInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUISuggestionsHost)
+ return &NewWebUI<suggestions::SuggestionsUI>;
+ if (url.host_piece() == chrome::kChromeUISupervisedUserInternalsHost)
+ return &NewWebUI<SupervisedUserInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUISupervisedUserPassphrasePageHost)
+ return &NewWebUI<ConstrainedWebDialogUI>;
+ if (url.host_piece() == chrome::kChromeUISyncInternalsHost)
+ return &NewWebUI<SyncInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUISyncResourcesHost)
+ return &NewWebUI<WebDialogUI>;
+ if (url.host_piece() == chrome::kChromeUITaskSchedulerInternalsHost)
+ return &NewWebUI<TaskSchedulerInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUITranslateInternalsHost)
+ return &NewWebUI<TranslateInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUIUsbInternalsHost)
+ return &NewWebUI<UsbInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUIUserActionsHost)
+ return &NewWebUI<UserActionsUI>;
+ if (url.host_piece() == chrome::kChromeUIVersionHost)
+ return &NewWebUI<VersionUI>;
+
+ /****************************************************************************
+ * OS Specific #defines
+ ***************************************************************************/
+#if !defined(OS_ANDROID)
+#if !defined(OS_CHROMEOS)
+ // AppLauncherPage is not needed on Android or ChromeOS.
+ if (url.host_piece() == chrome::kChromeUIAppLauncherPageHost && profile &&
+ extensions::ExtensionSystem::Get(profile)->extension_service()) {
+ return &NewWebUI<AppLauncherPageUI>;
+ }
+#endif // !defined(OS_CHROMEOS)
+
+ // Bookmarks are part of NTP on Android.
+ if (url.host_piece() == chrome::kChromeUIBookmarksHost) {
+ return MdBookmarksUI::IsEnabled() ? &NewWebUI<MdBookmarksUI>
+ : &NewWebUI<BookmarksUI>;
+ }
+ // Downloads list on Android uses the built-in download manager.
+ if (url.host_piece() == chrome::kChromeUIDownloadsHost)
+ return &NewWebUI<MdDownloadsUI>;
+ // Material Design feedback. Feedback is implemented separately in
+ // Android.
+ if (url.host_piece() == chrome::kChromeUIFeedbackHost &&
+ ::switches::MdFeedbackEnabled()) {
+ return &NewWebUI<MdFeedbackUI>;
+ }
+ // Help is implemented with native UI elements on Android.
+ if (url.host_piece() == chrome::kChromeUIHelpFrameHost)
+ return &NewWebUI<HelpUI>;
+ // Identity API is not available on Android.
+ if (url.host_piece() == chrome::kChromeUIIdentityInternalsHost)
+ return &NewWebUI<IdentityInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUINewTabHost)
+ return &NewWebUI<NewTabUI>;
+ if (url.host_piece() == chrome::kChromeUIMdSettingsHost)
+ return &NewWebUI<settings::MdSettingsUI>;
+ // If the material design extensions page is enabled, it gets its own host.
+ // Otherwise, it's handled by the uber settings page.
+ if (url.host_piece() == chrome::kChromeUIExtensionsHost &&
+ base::FeatureList::IsEnabled(features::kMaterialDesignExtensions)) {
+ return &NewWebUI<extensions::ExtensionsUI>;
+ }
+ if (url.host_piece() == chrome::kChromeUIHistoryHost)
+ return &NewWebUI<MdHistoryUI>;
+ // Material Design Settings gets its own host, if enabled.
+ if (base::FeatureList::IsEnabled(features::kMaterialDesignSettings) &&
+ url.host_piece() == chrome::kChromeUISettingsHost) {
+ return &NewWebUI<settings::MdSettingsUI>;
+ }
+ // Settings are implemented with native UI elements on Android.
+ // Handle chrome://settings if settings in a window is enabled.
+ if (url.host_piece() == chrome::kChromeUISettingsFrameHost ||
+ (url.host_piece() == chrome::kChromeUISettingsHost &&
+ ::switches::SettingsWindowEnabled())) {
+ return &NewWebUI<options::OptionsUI>;
+ }
+ if (url.host_piece() == chrome::kChromeUISyncFileSystemInternalsHost)
+ return &NewWebUI<SyncFileSystemInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUISystemInfoHost)
+ return &NewWebUI<SystemInfoUI>;
+ // Uber frame is not used on Android.
+ if (url.host_piece() == chrome::kChromeUIUberFrameHost)
+ return &NewWebUI<UberFrameUI>;
+ // Uber page is not used on Android.
+ if (url.host_piece() == chrome::kChromeUIUberHost)
+ return &NewWebUI<UberUI>;
+#endif // !defined(OS_ANDROID)
+#if defined(OS_WIN)
+ if (base::FeatureList::IsEnabled(features::kCleanupToolUI) &&
+ url.host_piece() == chrome::kChromeUICleanupToolHost)
+ return &NewWebUI<CleanupToolUI>;
+ if (url.host_piece() == chrome::kChromeUIConflictsHost)
+ return &NewWebUI<ConflictsUI>;
+ if (url.host_piece() == chrome::kChromeUIMetroFlowHost)
+ return &NewWebUI<SetAsDefaultBrowserUI>;
+#endif
+#if defined(OS_CHROMEOS)
+ if (url.host_piece() == chrome::kChromeUIBluetoothPairingHost)
+ return &NewWebUI<chromeos::BluetoothPairingUI>;
+ if (url.host_piece() == chrome::kChromeUICertificateManagerHost)
+ return &NewWebUI<chromeos::CertificateManagerDialogUI>;
+ if (url.host_piece() == chrome::kChromeUIChooseMobileNetworkHost)
+ return &NewWebUI<chromeos::ChooseMobileNetworkUI>;
+ if (url.host_piece() == chrome::kChromeUICryptohomeHost)
+ return &NewWebUI<chromeos::CryptohomeUI>;
+ if (url.host_piece() == chrome::kChromeUIDriveInternalsHost)
+ return &NewWebUI<chromeos::DriveInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUIFirstRunHost)
+ return &NewWebUI<chromeos::FirstRunUI>;
+ if (url.host_piece() == chrome::kChromeUIKeyboardOverlayHost)
+ return &NewWebUI<KeyboardOverlayUI>;
+ if (url.host_piece() == chrome::kChromeUIMobileSetupHost)
+ return &NewWebUI<MobileSetupUI>;
+ if (url.host_piece() == chrome::kChromeUINetworkHost)
+ return &NewWebUI<chromeos::NetworkUI>;
+ if (url.host_piece() == chrome::kChromeUIOobeHost)
+ return &NewWebUI<chromeos::OobeUI>;
+ if (url.host_piece() == chrome::kChromeUIPowerHost)
+ return &NewWebUI<chromeos::PowerUI>;
+ if (url.host_piece() == proximity_auth::kChromeUIProximityAuthHost)
+ return &NewWebUI<proximity_auth::ProximityAuthUI>;
+ if (url.host_piece() == chrome::kChromeUIProxySettingsHost)
+ return &NewWebUI<chromeos::ProxySettingsUI>;
+ if (url.host_piece() == chrome::kChromeUISetTimeHost)
+ return &NewWebUI<chromeos::SetTimeUI>;
+ if (url.host_piece() == chrome::kChromeUISimUnlockHost)
+ return &NewWebUI<chromeos::SimUnlockUI>;
+ if (url.host_piece() == chrome::kChromeUISlowHost)
+ return &NewWebUI<chromeos::SlowUI>;
+ if (url.host_piece() == chrome::kChromeUISlowTraceHost)
+ return &NewWebUI<chromeos::SlowTraceController>;
+ if (url.host_piece() == chrome::kChromeUIVoiceSearchHost)
+ return &NewWebUI<VoiceSearchUI>;
+#if !defined(OFFICIAL_BUILD)
+ if (!base::SysInfo::IsRunningOnChromeOS()) {
+ if (url.host_piece() == chrome::kChromeUIDeviceEmulatorHost)
+ return &NewWebUI<DeviceEmulatorUI>;
+ }
+#endif // !defined(OFFICIAL_BUILD)
+#endif // defined(OS_CHROMEOS)
+#if defined(OS_ANDROID)
+ if (url.host_piece() == chrome::kChromeUIOfflineInternalsHost)
+ return &NewWebUI<OfflineInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUIPopularSitesInternalsHost)
+ return &NewWebUI<PopularSitesInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUISnippetsInternalsHost &&
+ !profile->IsOffTheRecord())
+ return &NewWebUI<SnippetsInternalsUI>;
+ if (url.host_piece() == chrome::kChromeUIWebApksHost)
+ return &NewWebUI<WebApksUI>;
+#else
+ if (url.SchemeIs(content::kChromeDevToolsScheme)) {
+ if (!DevToolsUIBindings::IsValidFrontendURL(url))
+ return nullptr;
+ return &NewWebUI<DevToolsUI>;
+ }
+ // chrome://inspect isn't supported on Android nor iOS. Page debugging is
+ // handled by a remote devtools on the host machine, and other elements, i.e.
+ // extensions aren't supported.
+ if (url.host_piece() == chrome::kChromeUIInspectHost)
+ return &NewWebUI<InspectUI>;
+#endif
+#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
+ if (url.host_piece() == chrome::kChromeUIChromeSigninHost)
+ return &NewWebUI<InlineLoginUI>;
+ if (url.host_piece() == chrome::kChromeUIMdUserManagerHost)
+ return &NewWebUI<MDUserManagerUI>;
+ if (url.host_piece() == chrome::kChromeUISigninErrorHost &&
+ (!profile->IsOffTheRecord() ||
+ profile->GetOriginalProfile()->IsSystemProfile()))
+ return &NewWebUI<SigninErrorUI>;
+ if (url.host_piece() == chrome::kChromeUISyncConfirmationHost &&
+ !profile->IsOffTheRecord())
+ return &NewWebUI<SyncConfirmationUI>;
+ if (url.host_piece() == chrome::kChromeUISigninEmailConfirmationHost &&
+ !profile->IsOffTheRecord())
+ return &NewWebUI<SigninEmailConfirmationUI>;
+ if (url.host_piece() == chrome::kChromeUIWelcomeHost)
+ return &NewWebUI<WelcomeUI>;
+#endif
+#if defined(OS_WIN)
+ if (url.host_piece() == chrome::kChromeUIWelcomeWin10Host)
+ return &NewWebUI<WelcomeWin10UI>;
+#endif // defined(OS_WIN)
+
+ /****************************************************************************
+ * Other #defines and special logics.
+ ***************************************************************************/
+#if !defined(DISABLE_NACL)
+ if (url.host_piece() == chrome::kChromeUINaClHost)
+ return &NewWebUI<NaClUI>;
+#endif
+#if (defined(OS_LINUX) && defined(TOOLKIT_VIEWS)) || defined(USE_AURA)
+ if (url.host_piece() == chrome::kChromeUITabModalConfirmDialogHost) {
+ return &NewWebUI<ConstrainedWebDialogUI>;
+ }
+#endif
+#if defined(USE_NSS_CERTS) && defined(USE_AURA)
+ if (url.host_piece() == chrome::kChromeUICertificateViewerHost)
+ return &NewWebUI<CertificateViewerUI>;
+#if defined(OS_CHROMEOS)
+ if (url.host_piece() == chrome::kChromeUICertificateViewerDialogHost)
+ return &NewWebUI<CertificateViewerModalDialogUI>;
+#endif
+#endif // USE_NSS_CERTS && USE_AURA
+
+ if (url.host_piece() == chrome::kChromeUIPolicyHost)
+ return &NewWebUI<PolicyUI>;
+ if (url.host_piece() == chrome::kChromeUIMdPolicyHost &&
+ switches::MdPolicyPageEnabled()) {
+ return &NewWebUI<PolicyMaterialDesignUI>;
+ }
+
+#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>;
+#endif
+#if BUILDFLAG(ENABLE_PLUGINS)
+ if (url.host_piece() == chrome::kChromeUIFlashHost)
+ return &NewWebUI<FlashUI>;
+#endif
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
+ if (url.host_piece() == chrome::kChromeUIPrintHost &&
+ !profile->GetPrefs()->GetBoolean(prefs::kPrintPreviewDisabled)) {
+ return &NewWebUI<PrintPreviewUI>;
+ }
+#endif
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+ if (url.host_piece() == chrome::kChromeUIDevicesHost) {
+ return &NewWebUI<LocalDiscoveryUI>;
+ }
+#endif
+#if BUILDFLAG(ENABLE_WEBRTC)
+ if (url.host_piece() == chrome::kChromeUIWebRtcLogsHost)
+ return &NewWebUI<WebRtcLogsUI>;
+#endif
+#if !defined(OS_ANDROID)
+ if (url.host_piece() == chrome::kChromeUIMediaRouterHost &&
+ media_router::MediaRouterEnabled(profile)) {
+ return &NewWebUI<media_router::MediaRouterUI>;
+ }
+#endif
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+ if (url.host_piece() == chrome::kChromeUICastHost &&
+ media_router::MediaRouterEnabled(profile)) {
+ return &NewWebUI<CastUI>;
+ }
+#endif
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+ if (url.host_piece() == chrome::kChromeUISandboxHost) {
+ return &NewWebUI<SandboxInternalsUI>;
+ }
+#endif
+ if (IsAboutUI(url))
+ return &NewWebUI<AboutUI>;
+
+ if (dom_distiller::IsEnableDomDistillerSet() &&
+ url.host_piece() == dom_distiller::kChromeUIDomDistillerHost) {
+ return &NewWebUI<dom_distiller::DomDistillerUi>;
+ }
+
+ if (SiteEngagementService::IsEnabled() &&
+ url.host_piece() == chrome::kChromeUISiteEngagementHost) {
+ return &NewWebUI<SiteEngagementUI>;
+ }
+
+ return NULL;
+}
+
+void RunFaviconCallbackAsync(
+ const favicon_base::FaviconResultsCallback& callback,
+ const std::vector<favicon_base::FaviconRawBitmapResult>* results) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&favicon::FaviconService::FaviconResultsCallbackRunner,
+ callback, base::Owned(results)));
+}
+
+} // namespace
+
+WebUI::TypeID ChromeWebUIControllerFactory::GetWebUIType(
+ content::BrowserContext* browser_context, const GURL& url) const {
+ Profile* profile = Profile::FromBrowserContext(browser_context);
+ WebUIFactoryFunction function = GetWebUIFactoryFunction(NULL, profile, url);
+ return function ? reinterpret_cast<WebUI::TypeID>(function) : WebUI::kNoWebUI;
+}
+
+bool ChromeWebUIControllerFactory::UseWebUIForURL(
+ content::BrowserContext* browser_context, const GURL& url) const {
+ return GetWebUIType(browser_context, url) != WebUI::kNoWebUI;
+}
+
+bool ChromeWebUIControllerFactory::UseWebUIBindingsForURL(
+ content::BrowserContext* browser_context, const GURL& url) const {
+ return UseWebUIForURL(browser_context, url);
+}
+
+WebUIController* ChromeWebUIControllerFactory::CreateWebUIControllerForURL(
+ WebUI* web_ui,
+ const GURL& url) const {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ WebUIFactoryFunction function = GetWebUIFactoryFunction(web_ui, profile, url);
+ if (!function)
+ return NULL;
+
+ if (web_ui->HasRenderFrame())
+ webui::LogWebUIUrl(url);
+
+ return (*function)(web_ui, url);
+}
+
+void ChromeWebUIControllerFactory::GetFaviconForURL(
+ Profile* profile,
+ const GURL& page_url,
+ const std::vector<int>& desired_sizes_in_pixel,
+ const favicon_base::FaviconResultsCallback& callback) const {
+ // Before determining whether page_url is an extension url, we must handle
+ // overrides. This changes urls in |kChromeUIScheme| to extension urls, and
+ // allows to use ExtensionWebUI::GetFaviconForURL.
+ GURL url(page_url);
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ ExtensionWebUI::HandleChromeURLOverride(&url, profile);
+
+ // 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) {
+ ExtensionWebUI::GetFaviconForURL(profile, url, callback);
+ return;
+ }
+#endif
+
+ std::vector<favicon_base::FaviconRawBitmapResult>* favicon_bitmap_results =
+ new std::vector<favicon_base::FaviconRawBitmapResult>();
+
+ // Use ui::GetSupportedScaleFactors instead of
+ // favicon_base::GetFaviconScales() because chrome favicons comes from
+ // resources.
+ std::vector<ui::ScaleFactor> resource_scale_factors =
+ ui::GetSupportedScaleFactors();
+
+ std::vector<gfx::Size> candidate_sizes;
+ for (size_t i = 0; i < resource_scale_factors.size(); ++i) {
+ float scale = ui::GetScaleForScaleFactor(resource_scale_factors[i]);
+ // Assume that GetFaviconResourceBytes() returns favicons which are
+ // |gfx::kFaviconSize| x |gfx::kFaviconSize| DIP.
+ int candidate_edge_size =
+ static_cast<int>(gfx::kFaviconSize * scale + 0.5f);
+ candidate_sizes.push_back(
+ gfx::Size(candidate_edge_size, candidate_edge_size));
+ }
+ std::vector<size_t> selected_indices;
+ SelectFaviconFrameIndices(
+ candidate_sizes, desired_sizes_in_pixel, &selected_indices, NULL);
+ for (size_t i = 0; i < selected_indices.size(); ++i) {
+ size_t selected_index = selected_indices[i];
+ ui::ScaleFactor selected_resource_scale =
+ resource_scale_factors[selected_index];
+
+ scoped_refptr<base::RefCountedMemory> bitmap(
+ GetFaviconResourceBytes(url, selected_resource_scale));
+ if (bitmap.get() && bitmap->size()) {
+ favicon_base::FaviconRawBitmapResult bitmap_result;
+ bitmap_result.bitmap_data = bitmap;
+ // Leave |bitmap_result|'s icon URL as the default of GURL().
+ bitmap_result.icon_type = favicon_base::FAVICON;
+ bitmap_result.pixel_size = candidate_sizes[selected_index];
+ favicon_bitmap_results->push_back(bitmap_result);
+ }
+ }
+
+ RunFaviconCallbackAsync(callback, favicon_bitmap_results);
+}
+
+// static
+ChromeWebUIControllerFactory* ChromeWebUIControllerFactory::GetInstance() {
+ return base::Singleton<ChromeWebUIControllerFactory>::get();
+}
+
+ChromeWebUIControllerFactory::ChromeWebUIControllerFactory() {
+}
+
+ChromeWebUIControllerFactory::~ChromeWebUIControllerFactory() {
+}
+
+base::RefCountedMemory* ChromeWebUIControllerFactory::GetFaviconResourceBytes(
+ const GURL& page_url, ui::ScaleFactor scale_factor) const {
+#if !defined(OS_ANDROID) // Bookmarks are part of NTP on Android.
+ // The bookmark manager is a chrome extension, so we have to check for it
+ // before we check for extension scheme.
+ if (page_url.host_piece() == extension_misc::kBookmarkManagerId ||
+ page_url.host_piece() == chrome::kChromeUIBookmarksHost) {
+ return BookmarksUI::GetFaviconResourceBytes(scale_factor);
+ }
+
+ // The extension scheme is handled in GetFaviconForURL.
+ if (page_url.SchemeIs(extensions::kExtensionScheme)) {
+ NOTREACHED();
+ return NULL;
+ }
+#endif
+
+ if (!content::HasWebUIScheme(page_url))
+ return NULL;
+
+ if (page_url.host_piece() == chrome::kChromeUIComponentsHost)
+ return ComponentsUI::GetFaviconResourceBytes(scale_factor);
+
+#if defined(OS_WIN)
+ if (page_url.host_piece() == chrome::kChromeUIConflictsHost)
+ return ConflictsUI::GetFaviconResourceBytes(scale_factor);
+#endif
+
+ if (page_url.host_piece() == chrome::kChromeUICrashesHost)
+ return CrashesUI::GetFaviconResourceBytes(scale_factor);
+
+ if (page_url.host_piece() == chrome::kChromeUIFlagsHost)
+ return FlagsUI::GetFaviconResourceBytes(scale_factor);
+
+ // TODO(dbeam): does this actually need to exist on all platforms?
+ if (page_url.host_piece() == chrome::kChromeUIHistoryHost)
+ return history_ui::GetFaviconResourceBytes(scale_factor);
+
+#if !defined(OS_ANDROID)
+#if !defined(OS_CHROMEOS)
+ // The Apps launcher page is not available on android or ChromeOS.
+ if (page_url.host_piece() == chrome::kChromeUIAppLauncherPageHost)
+ return AppLauncherPageUI::GetFaviconResourceBytes(scale_factor);
+#endif // !defined(OS_CHROMEOS)
+
+ // Flash is not available on android.
+ if (page_url.host_piece() == chrome::kChromeUIFlashHost)
+ return FlashUI::GetFaviconResourceBytes(scale_factor);
+
+ // Android uses the native download manager.
+ if (page_url.host_piece() == chrome::kChromeUIDownloadsHost)
+ return MdDownloadsUI::GetFaviconResourceBytes(scale_factor);
+
+ // Android doesn't use the Options/Settings pages.
+ if (page_url.host_piece() == chrome::kChromeUISettingsHost ||
+ page_url.host_piece() == chrome::kChromeUISettingsFrameHost ||
+ page_url.host_piece() == chrome::kChromeUIMdSettingsHost)
+ return settings_utils::GetFaviconResourceBytes(scale_factor);
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ if (page_url.host_piece() == chrome::kChromeUIExtensionsHost ||
+ page_url.host_piece() == chrome::kChromeUIExtensionsFrameHost)
+ return extensions::ExtensionsUI::GetFaviconResourceBytes(scale_factor);
+#endif // BUILDFLAG(ENABLE_EXTENSIONS)
+#endif // !defined(OS_ANDROID)
+
+ return NULL;
+}
diff --git a/chromium/chrome/browser/ui/webui/chrome_web_ui_controller_factory.h b/chromium/chrome/browser/ui/webui/chrome_web_ui_controller_factory.h
new file mode 100644
index 00000000000..29064b60b68
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chrome_web_ui_controller_factory.h
@@ -0,0 +1,61 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROME_WEB_UI_CONTROLLER_FACTORY_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROME_WEB_UI_CONTROLLER_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "components/favicon_base/favicon_callback.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_controller_factory.h"
+#include "ui/base/layout.h"
+
+class Profile;
+
+namespace base {
+class RefCountedMemory;
+}
+
+class ChromeWebUIControllerFactory : public content::WebUIControllerFactory {
+ public:
+ content::WebUI::TypeID GetWebUIType(content::BrowserContext* browser_context,
+ const GURL& url) const override;
+ bool UseWebUIForURL(content::BrowserContext* browser_context,
+ const GURL& url) const override;
+ bool UseWebUIBindingsForURL(content::BrowserContext* browser_context,
+ const GURL& url) const override;
+ content::WebUIController* CreateWebUIControllerForURL(
+ content::WebUI* web_ui,
+ const GURL& url) const override;
+
+ // Get the favicon for |page_url| and run |callback| with result when loaded.
+ // Note. |callback| is always run asynchronously.
+ void GetFaviconForURL(
+ Profile* profile,
+ const GURL& page_url,
+ const std::vector<int>& desired_sizes_in_pixel,
+ const favicon_base::FaviconResultsCallback& callback) const;
+
+ static ChromeWebUIControllerFactory* GetInstance();
+
+ protected:
+ ChromeWebUIControllerFactory();
+ ~ChromeWebUIControllerFactory() override;
+
+ private:
+ friend struct base::DefaultSingletonTraits<ChromeWebUIControllerFactory>;
+
+ // Gets the data for the favicon for a WebUI page. Returns NULL if the WebUI
+ // does not have a favicon.
+ // The returned favicon data must be
+ // |gfx::kFaviconSize| x |gfx::kFaviconSize| DIP. GetFaviconForURL() should
+ // be updated if this changes.
+ base::RefCountedMemory* GetFaviconResourceBytes(
+ const GURL& page_url, ui::ScaleFactor scale_factor) const;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeWebUIControllerFactory);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROME_WEB_UI_CONTROLLER_FACTORY_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/DEPS b/chromium/chrome/browser/ui/webui/chromeos/DEPS
new file mode 100644
index 00000000000..9175df117f1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+ "+components/login",
+ "+components/user_manager",
+ "+media/audio/sounds",
+]
+
+specific_include_rules = {
+ "drive_internals_ui\.cc": [
+ "+components/drive"
+ ],
+}
diff --git a/chromium/chrome/browser/ui/webui/chromeos/OWNERS b/chromium/chrome/browser/ui/webui/chromeos/OWNERS
new file mode 100644
index 00000000000..0d3e3784a72
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/OWNERS
@@ -0,0 +1,13 @@
+achuith@chromium.org
+michaelpg@chromium.org
+satorux@chromium.org
+stevenjb@chromium.org
+xiyuan@chromium.org
+zelidrag@chromium.org
+
+per-file choose_mobile_network_ui.*=armansito@chromium.org
+per-file network*=stevenjb@chromium.org
+per-file sim_unlock_ui.*=armansito@chromium.org
+per-file sim_unlock_ui.*=stevenjb@chromium.org
+per-file drive_internals_ui.*=hashimoto@chromium.org
+per-file drive_internals_ui.*=kinaba@chromium.org
diff --git a/chromium/chrome/browser/ui/webui/chromeos/bluetooth_pairing_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/bluetooth_pairing_ui.cc
new file mode 100644
index 00000000000..bbd209e0b09
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/bluetooth_pairing_ui.cc
@@ -0,0 +1,62 @@
+// 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/ui/webui/chromeos/bluetooth_pairing_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/options/chromeos/bluetooth_options_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace chromeos {
+
+BluetoothPairingUI::BluetoothPairingUI(content::WebUI* web_ui)
+ : WebDialogUI(web_ui) {
+ base::DictionaryValue localized_strings;
+
+ auto core_handler = base::MakeUnique<options::CoreChromeOSOptionsHandler>();
+ core_handler_ = core_handler.get();
+ web_ui->AddMessageHandler(std::move(core_handler));
+ core_handler_->set_handlers_host(this);
+ core_handler_->GetLocalizedValues(&localized_strings);
+
+ auto bluetooth_handler = base::MakeUnique<options::BluetoothOptionsHandler>();
+ bluetooth_handler_ = bluetooth_handler.get();
+ web_ui->AddMessageHandler(std::move(bluetooth_handler));
+ bluetooth_handler_->GetLocalizedValues(&localized_strings);
+
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIBluetoothPairingHost);
+ source->AddLocalizedStrings(localized_strings);
+ source->SetJsonPath("strings.js");
+ source->SetDefaultResource(IDR_BLUETOOTH_PAIR_DEVICE_HTML);
+ source->DisableContentSecurityPolicy();
+
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, source);
+}
+
+BluetoothPairingUI::~BluetoothPairingUI() {
+ // Uninitialize all registered handlers. The base class owns them and it will
+ // eventually delete them.
+ core_handler_->Uninitialize();
+ bluetooth_handler_->Uninitialize();
+}
+
+void BluetoothPairingUI::InitializeHandlers() {
+ core_handler_->InitializeHandler();
+ bluetooth_handler_->InitializeHandler();
+ core_handler_->InitializePage();
+ bluetooth_handler_->InitializePage();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/bluetooth_pairing_ui.h b/chromium/chrome/browser/ui/webui/chromeos/bluetooth_pairing_ui.h
new file mode 100644
index 00000000000..3690918354d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/bluetooth_pairing_ui.h
@@ -0,0 +1,38 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_BLUETOOTH_PAIRING_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_BLUETOOTH_PAIRING_UI_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+namespace chromeos {
+
+namespace options {
+class CoreChromeOSOptionsHandler;
+class BluetoothOptionsHandler;
+}
+
+// A WebUI to host bluetooth device pairing web ui.
+class BluetoothPairingUI : public ui::WebDialogUI,
+ public ::options::OptionsPageUIHandlerHost {
+ public:
+ explicit BluetoothPairingUI(content::WebUI* web_ui);
+ ~BluetoothPairingUI() override;
+
+ private:
+ // Overridden from OptionsPageUIHandlerHost:
+ void InitializeHandlers() override;
+
+ options::CoreChromeOSOptionsHandler* core_handler_ = nullptr;
+ options::BluetoothOptionsHandler* bluetooth_handler_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothPairingUI);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_BLUETOOTH_PAIRING_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/bluetooth_pairing_ui_browsertest-inl.h b/chromium/chrome/browser/ui/webui/chromeos/bluetooth_pairing_ui_browsertest-inl.h
new file mode 100644
index 00000000000..a08cae701fc
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/bluetooth_pairing_ui_browsertest-inl.h
@@ -0,0 +1,69 @@
+// 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.
+
+#include "ash/public/cpp/shell_window_ids.h"
+#include "base/auto_reset.h"
+#include "chrome/browser/chromeos/bluetooth/bluetooth_pairing_dialog.h"
+#include "chrome/test/base/web_ui_browser_test.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/test/browser_test_utils.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/test/mock_bluetooth_adapter.h"
+#include "device/bluetooth/test/mock_bluetooth_device.h"
+#include "extensions/browser/extension_function.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+class BluetoothPairingUITest : public WebUIBrowserTest {
+ public:
+ BluetoothPairingUITest();
+ ~BluetoothPairingUITest() override;
+
+ void ShowDialog();
+
+ private:
+ scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> mock_adapter_;
+ std::unique_ptr<device::MockBluetoothDevice> mock_device_;
+};
+
+BluetoothPairingUITest::BluetoothPairingUITest() {}
+
+BluetoothPairingUITest::~BluetoothPairingUITest() {}
+
+void BluetoothPairingUITest::ShowDialog() {
+ // Since we use mocks, callbacks are never called for the bluetooth API
+ // functions. :(
+ base::AutoReset<bool> ignore_did_respond(
+ &ExtensionFunction::ignore_all_did_respond_for_testing_do_not_use, true);
+ mock_adapter_ = new testing::NiceMock<device::MockBluetoothAdapter>();
+ device::BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);
+ EXPECT_CALL(*mock_adapter_, IsPresent())
+ .WillRepeatedly(testing::Return(true));
+ EXPECT_CALL(*mock_adapter_, IsPowered())
+ .WillRepeatedly(testing::Return(true));
+
+ const bool kNotPaired = false;
+ const bool kNotConnected = false;
+ mock_device_.reset(
+ new testing::NiceMock<device::MockBluetoothDevice>(
+ nullptr,
+ 0,
+ "Bluetooth 2.0 Mouse",
+ "28:CF:DA:00:00:00",
+ kNotPaired,
+ kNotConnected));
+
+ EXPECT_CALL(*mock_adapter_, GetDevice(testing::_))
+ .WillOnce(testing::Return(mock_device_.get()));
+
+ chromeos::BluetoothPairingDialog* dialog =
+ new chromeos::BluetoothPairingDialog(
+ mock_device_->GetAddress(), mock_device_->GetNameForDisplay(),
+ mock_device_->IsPaired(), mock_device_->IsConnected());
+ dialog->ShowInContainer(ash::kShellWindowId_SystemModalContainer);
+
+ content::WebUI* webui = dialog->GetWebUIForTest();
+ content::WebContents* webui_webcontents = webui->GetWebContents();
+ content::WaitForLoadStop(webui_webcontents);
+ SetWebUIInstance(webui);
+}
diff --git a/chromium/chrome/browser/ui/webui/chromeos/bluetooth_pairing_ui_browsertest.js b/chromium/chrome/browser/ui/webui/chromeos/bluetooth_pairing_ui_browsertest.js
new file mode 100644
index 00000000000..6a47c802dd5
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/bluetooth_pairing_ui_browsertest.js
@@ -0,0 +1,33 @@
+// 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.
+
+/**
+ * TestFixture for Bluetooth pairing dialog WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function BluetoothPairingUITest() {
+}
+
+BluetoothPairingUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /** @override */
+ typedefCppFixture: 'BluetoothPairingUITest',
+
+ /** @override */
+ testGenCppIncludes: function() {
+ GEN('#include "chrome/browser/ui/webui/chromeos/' +
+ 'bluetooth_pairing_ui_browsertest-inl.h"');
+ },
+
+ /** @override */
+ testGenPreamble: function() {
+ GEN('ShowDialog();');
+ },
+};
+
+TEST_F('BluetoothPairingUITest', 'Basic', function() {
+ assertEquals('chrome://bluetooth-pairing/', document.location.href);
+});
diff --git a/chromium/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_browsertest.js b/chromium/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_browsertest.js
new file mode 100644
index 00000000000..42b78858e2c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_browsertest.js
@@ -0,0 +1,30 @@
+// 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.
+
+/**
+ * TestFixture for kiosk certification manager dialog WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function CertificateManagerDialogWebUITest() {}
+
+CertificateManagerDialogWebUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /**
+ * Browse to the certification manager dialog page.
+ */
+ browsePreload: 'chrome://certificate-manager/',
+};
+
+// crbug.com/682497
+GEN('#if defined(ADDRESS_SANITIZER)');
+GEN('#define MAYBE_Basic DISABLED_Basic');
+GEN('#else');
+GEN('#define MAYBE_Basic Basic');
+GEN('#endif');
+// Sanity test of the WebUI could be opened with no errors.
+TEST_F('CertificateManagerDialogWebUITest', 'MAYBE_Basic', function() {
+ assertEquals(this.browsePreload, document.location.href);
+});
diff --git a/chromium/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.cc
new file mode 100644
index 00000000000..51096b2b2e1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.cc
@@ -0,0 +1,150 @@
+// 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/ui/webui/chromeos/certificate_manager_dialog_ui.h"
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/system/input_device_settings.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/options/certificate_manager_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chromeos/chromeos_constants.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/jstemplate_builder.h"
+#include "ui/base/webui/web_ui_util.h"
+
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace {
+
+const char kLocalizedStringsFile[] = "strings.js";
+
+class CertificateManagerDialogHTMLSource : public content::URLDataSource {
+ public:
+ explicit CertificateManagerDialogHTMLSource(
+ base::DictionaryValue* localized_strings);
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ std::string GetMimeType(const std::string&) const override {
+ return "text/html";
+ }
+ bool ShouldAddContentSecurityPolicy() const override { return false; }
+ bool AllowCaching() const override {
+ // Should not be cached to reflect dynamically-generated contents that may
+ // depend on current locale setting.
+ return false;
+ }
+
+ protected:
+ ~CertificateManagerDialogHTMLSource() override {}
+
+ private:
+ std::unique_ptr<base::DictionaryValue> localized_strings_;
+
+ DISALLOW_COPY_AND_ASSIGN(CertificateManagerDialogHTMLSource);
+};
+
+CertificateManagerDialogHTMLSource::CertificateManagerDialogHTMLSource(
+ base::DictionaryValue* localized_strings)
+ : localized_strings_(localized_strings) {
+}
+
+std::string CertificateManagerDialogHTMLSource::GetSource() const {
+ return chrome::kChromeUICertificateManagerHost;
+}
+
+void CertificateManagerDialogHTMLSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ scoped_refptr<base::RefCountedMemory> response_bytes;
+ const std::string& app_locale = g_browser_process->GetApplicationLocale();
+ webui::SetLoadTimeDataDefaults(app_locale, localized_strings_.get());
+
+ if (path == kLocalizedStringsFile) {
+ // Return dynamically-generated strings from memory.
+ std::string strings_js;
+ webui::AppendJsonJS(localized_strings_.get(), &strings_js);
+ response_bytes = base::RefCountedString::TakeString(&strings_js);
+ } else {
+ // Return (and cache) the main options html page as the default.
+ response_bytes = ui::ResourceBundle::GetSharedInstance().
+ LoadDataResourceBytes(IDR_CERT_MANAGER_DIALOG_HTML);
+ }
+
+ callback.Run(response_bytes.get());
+}
+
+} // namespace
+
+namespace chromeos {
+
+CertificateManagerDialogUI::CertificateManagerDialogUI(content::WebUI* web_ui)
+ : ui::WebDialogUI(web_ui), initialized_handlers_(false) {
+ // |localized_strings| will be owned by CertificateManagerDialogHTMLSource.
+ base::DictionaryValue* localized_strings = new base::DictionaryValue();
+
+ auto core_handler = base::MakeUnique<options::CoreChromeOSOptionsHandler>();
+ core_handler_ = core_handler.get();
+ web_ui->AddMessageHandler(std::move(core_handler));
+ core_handler_->set_handlers_host(this);
+ core_handler_->GetLocalizedValues(localized_strings);
+
+ auto cert_handler =
+ base::MakeUnique<::options::CertificateManagerHandler>(true);
+ cert_handler_ = cert_handler.get();
+ web_ui->AddMessageHandler(std::move(cert_handler));
+ cert_handler_->GetLocalizedValues(localized_strings);
+
+ bool keyboard_driven_oobe =
+ system::InputDeviceSettings::Get()->ForceKeyboardDrivenUINavigation();
+ localized_strings->SetString("highlightStrength",
+ keyboard_driven_oobe ? "strong" : "normal");
+
+ CertificateManagerDialogHTMLSource* source =
+ new CertificateManagerDialogHTMLSource(localized_strings);
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::URLDataSource::Add(profile, source);
+}
+
+CertificateManagerDialogUI::~CertificateManagerDialogUI() {
+ // Uninitialize all registered handlers. The base class owns them and it will
+ // eventually delete them.
+ core_handler_->Uninitialize();
+ cert_handler_->Uninitialize();
+}
+
+void CertificateManagerDialogUI::InitializeHandlers() {
+ // A new web page DOM has been brought up in an existing renderer, causing
+ // this method to be called twice. In that case, don't initialize the handlers
+ // again. Compare with options_ui.cc.
+ if (!initialized_handlers_) {
+ core_handler_->InitializeHandler();
+ cert_handler_->InitializeHandler();
+ initialized_handlers_ = true;
+ }
+ core_handler_->InitializePage();
+ cert_handler_->InitializePage();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.h b/chromium/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.h
new file mode 100644
index 00000000000..374ceb2a25e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.h
@@ -0,0 +1,43 @@
+// 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_UI_WEBUI_CHROMEOS_CERTIFICATE_MANAGER_DIALOG_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_CERTIFICATE_MANAGER_DIALOG_UI_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+namespace options {
+class CertificateManagerHandler;
+}
+
+namespace chromeos {
+
+namespace options {
+class CoreChromeOSOptionsHandler;
+}
+
+// A WebUI to host certificate manager split from the main settings page.
+class CertificateManagerDialogUI : public ui::WebDialogUI,
+ public ::options::OptionsPageUIHandlerHost {
+ public:
+ explicit CertificateManagerDialogUI(content::WebUI* web_ui);
+ ~CertificateManagerDialogUI() override;
+
+ private:
+ // Overridden from OptionsPageUIHandlerHost:
+ void InitializeHandlers() override;
+
+ bool initialized_handlers_;
+
+ ::options::CertificateManagerHandler* cert_handler_ = nullptr;
+ options::CoreChromeOSOptionsHandler* core_handler_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(CertificateManagerDialogUI);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_CERTIFICATE_MANAGER_DIALOG_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/choose_mobile_network_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/choose_mobile_network_ui.cc
new file mode 100644
index 00000000000..7196f2cb691
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/choose_mobile_network_ui.cc
@@ -0,0 +1,284 @@
+// 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/ui/webui/chromeos/choose_mobile_network_ui.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/network/device_state.h"
+#include "chromeos/network/network_device_handler.h"
+#include "chromeos/network/network_event_log.h"
+#include "chromeos/network/network_state_handler.h"
+#include "chromeos/network/network_state_handler_observer.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace chromeos {
+
+namespace {
+
+// JS API callbacks names.
+const char kJsApiCancel[] = "cancel";
+const char kJsApiConnect[] = "connect";
+const char kJsApiPageReady[] = "pageReady";
+
+// Page JS API function names.
+const char kJsApiShowNetworks[] = "mobile.ChooseNetwork.showNetworks";
+const char kJsApiShowScanning[] = "mobile.ChooseNetwork.showScanning";
+
+// Network properties.
+const char kNetworkIdProperty[] = "networkId";
+const char kOperatorNameProperty[] = "operatorName";
+const char kStatusProperty[] = "status";
+const char kTechnologyProperty[] = "technology";
+
+content::WebUIDataSource* CreateChooseMobileNetworkUIHTMLSource() {
+ content::WebUIDataSource* source = content::WebUIDataSource::Create(
+ chrome::kChromeUIChooseMobileNetworkHost);
+
+ source->AddLocalizedString("chooseNetworkTitle",
+ IDS_NETWORK_CHOOSE_MOBILE_NETWORK);
+ source->AddLocalizedString("scanningMsgLine1",
+ IDS_NETWORK_SCANNING_FOR_MOBILE_NETWORKS);
+ source->AddLocalizedString("scanningMsgLine2",
+ IDS_NETWORK_SCANNING_THIS_MAY_TAKE_A_MINUTE);
+ source->AddLocalizedString("noMobileNetworks",
+ IDS_NETWORK_NO_MOBILE_NETWORKS);
+ source->AddLocalizedString("connect", IDS_OPTIONS_SETTINGS_CONNECT);
+ source->AddLocalizedString("cancel", IDS_CANCEL);
+
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("choose_mobile_network.js",
+ IDR_CHOOSE_MOBILE_NETWORK_JS);
+ source->SetDefaultResource(IDR_CHOOSE_MOBILE_NETWORK_HTML);
+ return source;
+}
+
+chromeos::NetworkDeviceHandler* GetNetworkDeviceHandler() {
+ return chromeos::NetworkHandler::Get()->network_device_handler();
+}
+
+chromeos::NetworkStateHandler* GetNetworkStateHandler() {
+ return chromeos::NetworkHandler::Get()->network_state_handler();
+}
+
+void NetworkOperationErrorCallback(
+ const std::string& operation_name,
+ const std::string& error_name,
+ std::unique_ptr<base::DictionaryValue> error_data) {
+ NET_LOG_ERROR("Operation failed: " + error_name, operation_name);
+}
+
+class ChooseMobileNetworkHandler
+ : public WebUIMessageHandler,
+ public NetworkStateHandlerObserver {
+ public:
+ ChooseMobileNetworkHandler();
+ ~ChooseMobileNetworkHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // NetworkStateHandlerObserver implementation.
+ void DeviceListChanged() override;
+
+ private:
+ // Handlers for JS WebUI messages.
+ void HandleCancel(const base::ListValue* args);
+ void HandleConnect(const base::ListValue* args);
+ void HandlePageReady(const base::ListValue* args);
+
+ std::string device_path_;
+ base::ListValue networks_list_;
+ bool is_page_ready_;
+ bool scanning_;
+ bool has_pending_results_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChooseMobileNetworkHandler);
+};
+
+// ChooseMobileNetworkHandler implementation.
+
+ChooseMobileNetworkHandler::ChooseMobileNetworkHandler()
+ : is_page_ready_(false),
+ scanning_(false),
+ has_pending_results_(false) {
+ NetworkStateHandler* handler = GetNetworkStateHandler();
+ const DeviceState* cellular =
+ handler->GetDeviceStateByType(NetworkTypePattern::Cellular());
+ if (!cellular) {
+ NET_LOG_ERROR(
+ "A cellular device is not available.",
+ "Cannot initiate a cellular network scan without a cellular device.");
+ // If there is no cellular device, we set |has_pending_results_| to true so
+ // that HandlePageReady() will show "No networks found." on the web UI.
+ has_pending_results_ = true;
+ return;
+ }
+ handler->AddObserver(this, FROM_HERE);
+ device_path_ = cellular->path();
+ GetNetworkDeviceHandler()->ProposeScan(
+ device_path_,
+ base::Bind(&base::DoNothing),
+ base::Bind(&NetworkOperationErrorCallback, "ProposeScan"));
+}
+
+ChooseMobileNetworkHandler::~ChooseMobileNetworkHandler() {
+ GetNetworkStateHandler()->RemoveObserver(this, FROM_HERE);
+}
+
+void ChooseMobileNetworkHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ kJsApiCancel,
+ base::Bind(&ChooseMobileNetworkHandler::HandleCancel,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kJsApiConnect,
+ base::Bind(&ChooseMobileNetworkHandler::HandleConnect,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kJsApiPageReady,
+ base::Bind(&ChooseMobileNetworkHandler::HandlePageReady,
+ base::Unretained(this)));
+}
+
+void ChooseMobileNetworkHandler::DeviceListChanged() {
+ const DeviceState* cellular = GetNetworkStateHandler()->GetDeviceState(
+ device_path_);
+ networks_list_.Clear();
+ if (!cellular) {
+ LOG(WARNING) << "Cellular device with path '" << device_path_
+ << "' disappeared.";
+ return;
+ }
+ if (cellular->scanning()) {
+ NET_LOG_EVENT("ChooseMobileNetwork", "Device is scanning for networks.");
+ scanning_ = true;
+ if (is_page_ready_)
+ web_ui()->CallJavascriptFunctionUnsafe(kJsApiShowScanning);
+ return;
+ }
+ scanning_ = false;
+ const DeviceState::CellularScanResults& scan_results =
+ cellular->scan_results();
+ std::set<std::string> network_ids;
+ for (DeviceState::CellularScanResults::const_iterator it =
+ scan_results.begin(); it != scan_results.end(); ++it) {
+ // We need to remove duplicates from the list because same network with
+ // different technologies are listed multiple times. But ModemManager
+ // Register API doesn't allow technology to be specified so just show unique
+ // network in UI.
+ if (network_ids.insert(it->network_id).second) {
+ auto network = base::MakeUnique<base::DictionaryValue>();
+ network->SetString(kNetworkIdProperty, it->network_id);
+ if (!it->long_name.empty())
+ network->SetString(kOperatorNameProperty, it->long_name);
+ else if (!it->short_name.empty())
+ network->SetString(kOperatorNameProperty, it->short_name);
+ else
+ network->SetString(kOperatorNameProperty, it->network_id);
+ network->SetString(kStatusProperty, it->status);
+ network->SetString(kTechnologyProperty, it->technology);
+ networks_list_.Append(std::move(network));
+ }
+ }
+ if (is_page_ready_) {
+ web_ui()->CallJavascriptFunctionUnsafe(kJsApiShowNetworks, networks_list_);
+ networks_list_.Clear();
+ has_pending_results_ = false;
+ } else {
+ has_pending_results_ = true;
+ }
+}
+
+void ChooseMobileNetworkHandler::HandleCancel(const base::ListValue* args) {
+ const size_t kConnectParamCount = 0;
+ if (args->GetSize() != kConnectParamCount) {
+ NOTREACHED();
+ return;
+ }
+
+ if (device_path_.empty())
+ return;
+
+ // Switch to automatic mode.
+ GetNetworkDeviceHandler()->RegisterCellularNetwork(
+ device_path_,
+ "", // An empty string is for registration with the home network.
+ base::Bind(&base::DoNothing),
+ base::Bind(&NetworkOperationErrorCallback,
+ "Register in automatic mode."));
+}
+
+void ChooseMobileNetworkHandler::HandleConnect(const base::ListValue* args) {
+ std::string network_id;
+ const size_t kConnectParamCount = 1;
+ if (args->GetSize() != kConnectParamCount ||
+ !args->GetString(0, &network_id)) {
+ NOTREACHED();
+ return;
+ }
+
+ GetNetworkDeviceHandler()->RegisterCellularNetwork(
+ device_path_,
+ network_id,
+ base::Bind(&base::DoNothing),
+ base::Bind(&NetworkOperationErrorCallback,
+ std::string("Register to network: ") + network_id));
+}
+
+void ChooseMobileNetworkHandler::HandlePageReady(const base::ListValue* args) {
+ const size_t kConnectParamCount = 0;
+ if (args->GetSize() != kConnectParamCount) {
+ NOTREACHED();
+ return;
+ }
+
+ if (has_pending_results_) {
+ web_ui()->CallJavascriptFunctionUnsafe(kJsApiShowNetworks, networks_list_);
+ networks_list_.Clear();
+ has_pending_results_ = false;
+ } else if (scanning_) {
+ web_ui()->CallJavascriptFunctionUnsafe(kJsApiShowScanning);
+ }
+ is_page_ready_ = true;
+}
+
+} // namespace
+
+ChooseMobileNetworkUI::ChooseMobileNetworkUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<ChooseMobileNetworkHandler>());
+ // Set up the "chrome://choose-mobile-network" source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(
+ profile, CreateChooseMobileNetworkUIHTMLSource());
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/choose_mobile_network_ui.h b/chromium/chrome/browser/ui/webui/chromeos/choose_mobile_network_ui.h
new file mode 100644
index 00000000000..fcfba14f260
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/choose_mobile_network_ui.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2011 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_CHROMEOS_CHOOSE_MOBILE_NETWORK_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_CHOOSE_MOBILE_NETWORK_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace chromeos {
+
+// A custom WebUI that defines datasource for choosing cellular network dialog.
+class ChooseMobileNetworkUI : public content::WebUIController {
+ public:
+ explicit ChooseMobileNetworkUI(content::WebUI* web_ui);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ChooseMobileNetworkUI);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_CHOOSE_MOBILE_NETWORK_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/cryptohome_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/cryptohome_ui.cc
new file mode 100644
index 00000000000..d4fbf0b0013
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/cryptohome_ui.cc
@@ -0,0 +1,37 @@
+// 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/ui/webui/chromeos/cryptohome_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace chromeos {
+
+namespace {
+
+// Returns HTML data source for chrome://cryptohome.
+content::WebUIDataSource* CreateCryptohomeUIHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUICryptohomeHost);
+ source->AddResourcePath("cryptohome.js", IDR_CRYPTOHOME_JS);
+ source->SetDefaultResource(IDR_CRYPTOHOME_HTML);
+ return source;
+}
+
+} // namespace
+
+CryptohomeUI::CryptohomeUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<CryptohomeWebUIHandler>());
+
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreateCryptohomeUIHTMLSource());
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/cryptohome_ui.h b/chromium/chrome/browser/ui/webui/chromeos/cryptohome_ui.h
new file mode 100644
index 00000000000..f616d288417
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/cryptohome_ui.h
@@ -0,0 +1,25 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_CRYPTOHOME_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_CRYPTOHOME_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace chromeos {
+
+// WebUIController for chrome://cryptohome.
+class CryptohomeUI : public content::WebUIController {
+ public:
+ explicit CryptohomeUI(content::WebUI* web_ui);
+ ~CryptohomeUI() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CryptohomeUI);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_CRYPTOHOME_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.cc
new file mode 100644
index 00000000000..0a7d931c282
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.cc
@@ -0,0 +1,84 @@
+// 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/ui/webui/chromeos/cryptohome_web_ui_handler.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "chromeos/dbus/cryptohome_client.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_ui.h"
+#include "crypto/nss_util.h"
+
+using content::BrowserThread;
+
+namespace chromeos {
+
+CryptohomeWebUIHandler::CryptohomeWebUIHandler() : weak_ptr_factory_(this) {}
+
+CryptohomeWebUIHandler::~CryptohomeWebUIHandler() {}
+
+void CryptohomeWebUIHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "pageLoaded",
+ base::Bind(&CryptohomeWebUIHandler::OnPageLoaded,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CryptohomeWebUIHandler::OnPageLoaded(const base::ListValue* args) {
+ CryptohomeClient* cryptohome_client =
+ DBusThreadManager::Get()->GetCryptohomeClient();
+
+ cryptohome_client->IsMounted(GetCryptohomeBoolCallback("is-mounted"));
+ cryptohome_client->TpmIsReady(GetCryptohomeBoolCallback("tpm-is-ready"));
+ cryptohome_client->TpmIsEnabled(GetCryptohomeBoolCallback("tpm-is-enabled"));
+ cryptohome_client->TpmIsOwned(GetCryptohomeBoolCallback("tpm-is-owned"));
+ cryptohome_client->TpmIsBeingOwned(
+ GetCryptohomeBoolCallback("tpm-is-being-owned"));
+ cryptohome_client->Pkcs11IsTpmTokenReady(
+ GetCryptohomeBoolCallback("pkcs11-is-tpm-token-ready"));
+
+ BrowserThread::PostTaskAndReplyWithResult(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&crypto::IsTPMTokenReady, base::Closure()),
+ base::Bind(&CryptohomeWebUIHandler::DidGetNSSUtilInfoOnUIThread,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CryptohomeWebUIHandler::DidGetNSSUtilInfoOnUIThread(
+ bool is_tpm_token_ready) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ base::Value is_tpm_token_ready_value(is_tpm_token_ready);
+ SetCryptohomeProperty("is-tpm-token-ready", is_tpm_token_ready_value);
+}
+
+BoolDBusMethodCallback CryptohomeWebUIHandler::GetCryptohomeBoolCallback(
+ const std::string& destination_id) {
+ return base::Bind(&CryptohomeWebUIHandler::OnCryptohomeBoolProperty,
+ weak_ptr_factory_.GetWeakPtr(),
+ destination_id);
+}
+
+void CryptohomeWebUIHandler::OnCryptohomeBoolProperty(
+ const std::string& destination_id,
+ DBusMethodCallStatus call_status,
+ bool value) {
+ if (call_status != DBUS_METHOD_CALL_SUCCESS)
+ value = false;
+ base::Value fundamental_value(value);
+ SetCryptohomeProperty(destination_id, fundamental_value);
+}
+
+void CryptohomeWebUIHandler::SetCryptohomeProperty(
+ const std::string& destination_id,
+ const base::Value& value) {
+ base::Value destination_id_value(destination_id);
+ web_ui()->CallJavascriptFunctionUnsafe("SetCryptohomeProperty",
+ destination_id_value, value);
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.h b/chromium/chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.h
new file mode 100644
index 00000000000..741fb728d13
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/cryptohome_web_ui_handler.h
@@ -0,0 +1,58 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_CRYPTOHOME_WEB_UI_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_CRYPTOHOME_WEB_UI_HANDLER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chromeos/dbus/dbus_method_call_status.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace base {
+
+class Value;
+
+} // base
+
+namespace chromeos {
+
+// Class to handle messages from chrome://cryptohome.
+class CryptohomeWebUIHandler : public content::WebUIMessageHandler {
+ public:
+ CryptohomeWebUIHandler();
+
+ ~CryptohomeWebUIHandler() override;
+
+ // WebUIMessageHandler override.
+ void RegisterMessages() override;
+
+ private:
+ // This method is called from JavaScript.
+ void OnPageLoaded(const base::ListValue* args);
+
+ void DidGetNSSUtilInfoOnUIThread(bool is_tpm_token_ready);
+
+ // Returns a callback to handle Cryptohome property values.
+ BoolDBusMethodCallback GetCryptohomeBoolCallback(
+ const std::string& destination_id);
+
+ // This method is called when Cryptohome D-Bus method call completes.
+ void OnCryptohomeBoolProperty(const std::string& destination_id,
+ DBusMethodCallStatus call_status,
+ bool value);
+
+ // Sets textcontent of the element whose id is |destination_id| to |value|.
+ void SetCryptohomeProperty(const std::string& destination_id,
+ const base::Value& value);
+
+ base::WeakPtrFactory<CryptohomeWebUIHandler> weak_ptr_factory_;
+ DISALLOW_COPY_AND_ASSIGN(CryptohomeWebUIHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_CRYPTOHOME_WEB_UI_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
new file mode 100644
index 00000000000..c13d04e71fe
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
@@ -0,0 +1,906 @@
+// 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/ui/webui/chromeos/drive_internals_ui.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
+#include "base/format_macros.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/path_service.h"
+#include "base/strings/stringprintf.h"
+#include "base/sys_info.h"
+#include "base/task_scheduler/post_task.h"
+#include "chrome/browser/chromeos/drive/debug_info_collector.h"
+#include "chrome/browser/chromeos/drive/drive_integration_service.h"
+#include "chrome/browser/chromeos/drive/file_system_util.h"
+#include "chrome/browser/chromeos/file_manager/path_util.h"
+#include "chrome/browser/drive/drive_notification_manager_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "components/drive/drive.pb.h"
+#include "components/drive/drive_api_util.h"
+#include "components/drive/drive_notification_manager.h"
+#include "components/drive/drive_pref_names.h"
+#include "components/drive/event_logger.h"
+#include "components/drive/job_list.h"
+#include "components/drive/service/drive_service_interface.h"
+#include "components/prefs/pref_service.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 "content/public/browser/web_ui_message_handler.h"
+#include "google_apis/drive/auth_service.h"
+#include "google_apis/drive/drive_api_error_codes.h"
+#include "google_apis/drive/drive_api_parser.h"
+#include "google_apis/drive/time_util.h"
+
+using content::BrowserThread;
+
+namespace chromeos {
+
+namespace {
+
+// Gets metadata of all files and directories in |root_path|
+// recursively. Stores the result as a list of dictionaries like:
+//
+// [{ path: 'GCache/v1/tmp/<local_id>',
+// size: 12345,
+// is_directory: false,
+// last_modified: '2005-08-09T09:57:00-08:00',
+// },...]
+//
+// The list is sorted by the path.
+void GetGCacheContents(const base::FilePath& root_path,
+ base::ListValue* gcache_contents,
+ base::DictionaryValue* gcache_summary) {
+ DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(gcache_contents);
+ DCHECK(gcache_summary);
+
+ // Use this map to sort the result list by the path.
+ std::map<base::FilePath, std::unique_ptr<base::DictionaryValue>> files;
+
+ const int options = (base::FileEnumerator::FILES |
+ base::FileEnumerator::DIRECTORIES |
+ base::FileEnumerator::SHOW_SYM_LINKS);
+ base::FileEnumerator enumerator(root_path, true /* recursive */, options);
+
+ int64_t total_size = 0;
+ for (base::FilePath current = enumerator.Next(); !current.empty();
+ current = enumerator.Next()) {
+ base::FileEnumerator::FileInfo info = enumerator.GetInfo();
+ int64_t size = info.GetSize();
+ const bool is_directory = info.IsDirectory();
+ const bool is_symbolic_link = base::IsLink(info.GetName());
+ const base::Time last_modified = info.GetLastModifiedTime();
+
+ auto entry = base::MakeUnique<base::DictionaryValue>();
+ entry->SetString("path", current.value());
+ // Use double instead of integer for large files.
+ entry->SetDouble("size", size);
+ entry->SetBoolean("is_directory", is_directory);
+ entry->SetBoolean("is_symbolic_link", is_symbolic_link);
+ entry->SetString(
+ "last_modified",
+ google_apis::util::FormatTimeAsStringLocaltime(last_modified));
+ // Print lower 9 bits in octal format.
+ entry->SetString(
+ "permission",
+ base::StringPrintf("%03o", info.stat().st_mode & 0x1ff));
+ files[current] = std::move(entry);
+
+ total_size += size;
+ }
+
+ // Convert |files| into |gcache_contents|.
+ for (auto& it : files)
+ gcache_contents->Append(std::move(it.second));
+
+ gcache_summary->SetDouble("total_size", total_size);
+}
+
+// Gets the available disk space for the path |home_path|.
+void GetFreeDiskSpace(const base::FilePath& home_path,
+ base::DictionaryValue* local_storage_summary) {
+ DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(local_storage_summary);
+
+ const int64_t free_space = base::SysInfo::AmountOfFreeDiskSpace(home_path);
+ local_storage_summary->SetDouble("free_space", free_space);
+}
+
+// Formats |entry| into text.
+std::string FormatEntry(const base::FilePath& path,
+ const drive::ResourceEntry& entry) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ using base::StringAppendF;
+
+ std::string out;
+ StringAppendF(&out, "%s\n", path.AsUTF8Unsafe().c_str());
+ StringAppendF(&out, " title: %s\n", entry.title().c_str());
+ StringAppendF(&out, " local_id: %s\n", entry.local_id().c_str());
+ StringAppendF(&out, " resource_id: %s\n", entry.resource_id().c_str());
+ StringAppendF(&out, " parent_local_id: %s\n",
+ entry.parent_local_id().c_str());
+ StringAppendF(&out, " shared: %s\n", entry.shared() ? "true" : "false");
+ StringAppendF(&out, " shared_with_me: %s\n",
+ entry.shared_with_me() ? "true" : "false");
+
+ const drive::PlatformFileInfoProto& file_info = entry.file_info();
+ StringAppendF(&out, " file_info\n");
+ StringAppendF(&out, " size: %" PRId64 "\n", file_info.size());
+ StringAppendF(&out, " is_directory: %d\n", file_info.is_directory());
+ StringAppendF(&out, " is_symbolic_link: %d\n",
+ file_info.is_symbolic_link());
+
+ const base::Time last_modified = base::Time::FromInternalValue(
+ file_info.last_modified());
+ const base::Time last_accessed = base::Time::FromInternalValue(
+ file_info.last_accessed());
+ const base::Time creation_time = base::Time::FromInternalValue(
+ file_info.creation_time());
+ StringAppendF(&out, " last_modified: %s\n",
+ google_apis::util::FormatTimeAsString(last_modified).c_str());
+ StringAppendF(&out, " last_accessed: %s\n",
+ google_apis::util::FormatTimeAsString(last_accessed).c_str());
+ StringAppendF(&out, " creation_time: %s\n",
+ google_apis::util::FormatTimeAsString(creation_time).c_str());
+
+ if (entry.has_file_specific_info()) {
+ const drive::FileSpecificInfo& file_specific_info =
+ entry.file_specific_info();
+ StringAppendF(&out, " alternate_url: %s\n",
+ file_specific_info.alternate_url().c_str());
+ StringAppendF(&out, " content_mime_type: %s\n",
+ file_specific_info.content_mime_type().c_str());
+ StringAppendF(&out, " file_md5: %s\n",
+ file_specific_info.md5().c_str());
+ StringAppendF(&out, " document_extension: %s\n",
+ file_specific_info.document_extension().c_str());
+ StringAppendF(&out, " is_hosted_document: %d\n",
+ file_specific_info.is_hosted_document());
+ }
+
+ if (entry.has_directory_specific_info()) {
+ StringAppendF(&out, " directory_info\n");
+ const drive::DirectorySpecificInfo& directory_specific_info =
+ entry.directory_specific_info();
+ StringAppendF(&out, " changestamp: %" PRId64 "\n",
+ directory_specific_info.changestamp());
+ }
+
+ return out;
+}
+
+std::string SeverityToString(logging::LogSeverity severity) {
+ switch (severity) {
+ case logging::LOG_INFO:
+ return "info";
+ case logging::LOG_WARNING:
+ return "warning";
+ case logging::LOG_ERROR:
+ return "error";
+ default: // Treat all other higher severities as ERROR.
+ return "error";
+ }
+}
+
+// Appends {'key': key, 'value': value} dictionary to the |list|.
+void AppendKeyValue(base::ListValue* list,
+ const std::string& key,
+ const std::string& value) {
+ auto dict = base::MakeUnique<base::DictionaryValue>();
+ dict->SetString("key", key);
+ dict->SetString("value", value);
+ list->Append(std::move(dict));
+}
+
+// Class to handle messages from chrome://drive-internals.
+class DriveInternalsWebUIHandler : public content::WebUIMessageHandler {
+ public:
+ DriveInternalsWebUIHandler()
+ : last_sent_event_id_(-1),
+ weak_ptr_factory_(this) {
+ }
+
+ ~DriveInternalsWebUIHandler() override {}
+
+ private:
+ // WebUIMessageHandler override.
+ void RegisterMessages() override;
+
+ // Returns a DriveIntegrationService.
+ drive::DriveIntegrationService* GetIntegrationService();
+
+ // Returns a DriveService instance.
+ drive::DriveServiceInterface* GetDriveService();
+
+ // Returns a DebugInfoCollector instance.
+ drive::DebugInfoCollector* GetDebugInfoCollector();
+
+ // Called when the page is first loaded.
+ void OnPageLoaded(const base::ListValue* args);
+
+ // Updates respective sections.
+ void UpdateDriveRelatedPreferencesSection();
+ void UpdateConnectionStatusSection(
+ drive::DriveServiceInterface* drive_service);
+ void UpdateAboutResourceSection(
+ drive::DriveServiceInterface* drive_service);
+ void UpdateAppListSection(
+ drive::DriveServiceInterface* drive_service);
+ void UpdateLocalMetadataSection(
+ drive::DebugInfoCollector* debug_info_collector);
+ void UpdateDeltaUpdateStatusSection(
+ drive::DebugInfoCollector* debug_info_collector);
+ void UpdateInFlightOperationsSection(drive::JobListInterface* job_list);
+ void UpdateGCacheContentsSection();
+ void UpdateFileSystemContentsSection();
+ void UpdateLocalStorageUsageSection();
+ void UpdateCacheContentsSection(
+ drive::DebugInfoCollector* debug_info_collector);
+ void UpdateEventLogSection();
+ void UpdatePathConfigurationsSection();
+
+ // Called when GetGCacheContents() is complete.
+ void OnGetGCacheContents(base::ListValue* gcache_contents,
+ base::DictionaryValue* cache_summary);
+
+ // Called when GetResourceEntryByPath() is complete.
+ void OnGetResourceEntryByPath(const base::FilePath& path,
+ drive::FileError error,
+ std::unique_ptr<drive::ResourceEntry> entry);
+
+ // Called when ReadDirectoryByPath() is complete.
+ void OnReadDirectoryByPath(
+ const base::FilePath& parent_path,
+ drive::FileError error,
+ std::unique_ptr<drive::ResourceEntryVector> entries);
+
+ // Called as the iterator for DebugInfoCollector::IterateFileCache().
+ void UpdateCacheEntry(const std::string& local_id,
+ const drive::FileCacheEntry& cache_entry);
+
+ // Called when GetFreeDiskSpace() is complete.
+ void OnGetFreeDiskSpace(base::DictionaryValue* local_storage_summary);
+
+ // Called when GetAboutResource() call to DriveService is complete.
+ void OnGetAboutResource(
+ google_apis::DriveApiErrorCode status,
+ std::unique_ptr<google_apis::AboutResource> about_resource);
+
+ // Called when GetAppList() call to DriveService is complete.
+ void OnGetAppList(google_apis::DriveApiErrorCode status,
+ std::unique_ptr<google_apis::AppList> app_list);
+
+ // Callback for DebugInfoCollector::GetMetadata for local update.
+ void OnGetFilesystemMetadataForLocal(
+ const drive::FileSystemMetadata& metadata);
+
+ // Callback for DebugInfoCollector::GetMetadata for delta update.
+ void OnGetFilesystemMetadataForDeltaUpdate(
+ const drive::FileSystemMetadata& metadata);
+
+ // Called when the page requests periodic update.
+ void OnPeriodicUpdate(const base::ListValue* args);
+
+ // Called when the corresponding button on the page is pressed.
+ void ClearAccessToken(const base::ListValue* args);
+ void ClearRefreshToken(const base::ListValue* args);
+ void ResetDriveFileSystem(const base::ListValue* args);
+ void ListFileEntries(const base::ListValue* args);
+
+ // Called after file system reset for ResetDriveFileSystem is done.
+ void ResetFinished(bool success);
+
+ // The last event sent to the JavaScript side.
+ int last_sent_event_id_;
+
+ base::WeakPtrFactory<DriveInternalsWebUIHandler> weak_ptr_factory_;
+ DISALLOW_COPY_AND_ASSIGN(DriveInternalsWebUIHandler);
+};
+
+void DriveInternalsWebUIHandler::OnGetAboutResource(
+ google_apis::DriveApiErrorCode status,
+ std::unique_ptr<google_apis::AboutResource> parsed_about_resource) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if (status != google_apis::HTTP_SUCCESS) {
+ LOG(ERROR) << "Failed to get about resource";
+ return;
+ }
+ DCHECK(parsed_about_resource);
+
+ base::DictionaryValue about_resource;
+ about_resource.SetDouble("account-quota-total",
+ parsed_about_resource->quota_bytes_total());
+ about_resource.SetDouble("account-quota-used",
+ parsed_about_resource->quota_bytes_used_aggregate());
+ about_resource.SetDouble("account-largest-changestamp-remote",
+ parsed_about_resource->largest_change_id());
+ about_resource.SetString("root-resource-id",
+ parsed_about_resource->root_folder_id());
+
+ web_ui()->CallJavascriptFunctionUnsafe("updateAboutResource", about_resource);
+}
+
+void DriveInternalsWebUIHandler::OnGetAppList(
+ google_apis::DriveApiErrorCode status,
+ std::unique_ptr<google_apis::AppList> parsed_app_list) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if (status != google_apis::HTTP_SUCCESS) {
+ LOG(ERROR) << "Failed to get app list";
+ return;
+ }
+ DCHECK(parsed_app_list);
+
+ base::DictionaryValue app_list;
+ app_list.SetString("etag", parsed_app_list->etag());
+
+ auto items = base::MakeUnique<base::ListValue>();
+ for (size_t i = 0; i < parsed_app_list->items().size(); ++i) {
+ const google_apis::AppResource* app = parsed_app_list->items()[i].get();
+ auto app_data = base::MakeUnique<base::DictionaryValue>();
+ app_data->SetString("name", app->name());
+ app_data->SetString("application_id", app->application_id());
+ app_data->SetString("object_type", app->object_type());
+ app_data->SetBoolean("supports_create", app->supports_create());
+
+ items->Append(std::move(app_data));
+ }
+ app_list.Set("items", std::move(items));
+
+ web_ui()->CallJavascriptFunctionUnsafe("updateAppList", app_list);
+}
+
+void DriveInternalsWebUIHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "pageLoaded",
+ base::Bind(&DriveInternalsWebUIHandler::OnPageLoaded,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "periodicUpdate",
+ base::Bind(&DriveInternalsWebUIHandler::OnPeriodicUpdate,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "clearAccessToken",
+ base::Bind(&DriveInternalsWebUIHandler::ClearAccessToken,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "clearRefreshToken",
+ base::Bind(&DriveInternalsWebUIHandler::ClearRefreshToken,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "resetDriveFileSystem",
+ base::Bind(&DriveInternalsWebUIHandler::ResetDriveFileSystem,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "listFileEntries",
+ base::Bind(&DriveInternalsWebUIHandler::ListFileEntries,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+drive::DriveIntegrationService*
+DriveInternalsWebUIHandler::GetIntegrationService() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ drive::DriveIntegrationService* service =
+ drive::DriveIntegrationServiceFactory::FindForProfile(profile);
+ if (!service || !service->is_enabled())
+ return NULL;
+ return service;
+}
+
+drive::DriveServiceInterface* DriveInternalsWebUIHandler::GetDriveService() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ return drive::util::GetDriveServiceByProfile(profile);
+}
+
+drive::DebugInfoCollector* DriveInternalsWebUIHandler::GetDebugInfoCollector() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ drive::DriveIntegrationService* integration_service = GetIntegrationService();
+ return integration_service ?
+ integration_service->debug_info_collector() : NULL;
+}
+
+void DriveInternalsWebUIHandler::OnPageLoaded(const base::ListValue* args) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ drive::DriveIntegrationService* integration_service =
+ GetIntegrationService();
+ // |integration_service| may be NULL in the guest/incognito mode.
+ if (!integration_service)
+ return;
+
+ drive::DriveServiceInterface* drive_service =
+ integration_service->drive_service();
+ DCHECK(drive_service);
+ drive::DebugInfoCollector* debug_info_collector =
+ integration_service->debug_info_collector();
+ DCHECK(debug_info_collector);
+
+ UpdateDriveRelatedPreferencesSection();
+ UpdateConnectionStatusSection(drive_service);
+ UpdateAboutResourceSection(drive_service);
+ UpdateAppListSection(drive_service);
+ UpdateLocalMetadataSection(debug_info_collector);
+ UpdateDeltaUpdateStatusSection(debug_info_collector);
+ UpdateInFlightOperationsSection(integration_service->job_list());
+ UpdateGCacheContentsSection();
+ UpdateCacheContentsSection(debug_info_collector);
+ UpdateLocalStorageUsageSection();
+ UpdatePathConfigurationsSection();
+
+ // When the drive-internals page is reloaded by the reload key, the page
+ // content is recreated, but this WebUI object is not (instead, OnPageLoaded
+ // is called again). In that case, we have to forget the last sent ID here,
+ // and resent whole the logs to the page.
+ last_sent_event_id_ = -1;
+ UpdateEventLogSection();
+}
+
+void DriveInternalsWebUIHandler::UpdateDriveRelatedPreferencesSection() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ const char* kDriveRelatedPreferences[] = {
+ drive::prefs::kDisableDrive,
+ drive::prefs::kDisableDriveOverCellular,
+ drive::prefs::kDisableDriveHostedFiles,
+ };
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ PrefService* pref_service = profile->GetPrefs();
+
+ base::ListValue preferences;
+ for (size_t i = 0; i < arraysize(kDriveRelatedPreferences); ++i) {
+ const std::string key = kDriveRelatedPreferences[i];
+ // As of now, all preferences are boolean.
+ const std::string value =
+ (pref_service->GetBoolean(key.c_str()) ? "true" : "false");
+ AppendKeyValue(&preferences, key, value);
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("updateDriveRelatedPreferences",
+ preferences);
+}
+
+void DriveInternalsWebUIHandler::UpdateConnectionStatusSection(
+ drive::DriveServiceInterface* drive_service) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(drive_service);
+
+ std::string status;
+ switch (drive::util::GetDriveConnectionStatus(Profile::FromWebUI(web_ui()))) {
+ case drive::util::DRIVE_DISCONNECTED_NOSERVICE:
+ status = "no service";
+ break;
+ case drive::util::DRIVE_DISCONNECTED_NONETWORK:
+ status = "no network";
+ break;
+ case drive::util::DRIVE_DISCONNECTED_NOTREADY:
+ status = "not ready";
+ break;
+ case drive::util::DRIVE_CONNECTED_METERED:
+ status = "metered";
+ break;
+ case drive::util::DRIVE_CONNECTED:
+ status = "connected";
+ break;
+ }
+
+ base::DictionaryValue connection_status;
+ connection_status.SetString("status", status);
+ connection_status.SetBoolean("has-refresh-token",
+ drive_service->HasRefreshToken());
+ connection_status.SetBoolean("has-access-token",
+ drive_service->HasAccessToken());
+ web_ui()->CallJavascriptFunctionUnsafe("updateConnectionStatus",
+ connection_status);
+}
+
+void DriveInternalsWebUIHandler::UpdateAboutResourceSection(
+ drive::DriveServiceInterface* drive_service) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(drive_service);
+
+ drive_service->GetAboutResource(
+ base::Bind(&DriveInternalsWebUIHandler::OnGetAboutResource,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DriveInternalsWebUIHandler::UpdateAppListSection(
+ drive::DriveServiceInterface* drive_service) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(drive_service);
+
+ drive_service->GetAppList(
+ base::Bind(&DriveInternalsWebUIHandler::OnGetAppList,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DriveInternalsWebUIHandler::UpdateLocalMetadataSection(
+ drive::DebugInfoCollector* debug_info_collector) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(debug_info_collector);
+
+ debug_info_collector->GetMetadata(
+ base::Bind(&DriveInternalsWebUIHandler::OnGetFilesystemMetadataForLocal,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DriveInternalsWebUIHandler::OnGetFilesystemMetadataForLocal(
+ const drive::FileSystemMetadata& metadata) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ base::DictionaryValue local_metadata;
+ local_metadata.SetDouble("account-largest-changestamp-local",
+ metadata.largest_changestamp);
+ local_metadata.SetBoolean("account-metadata-refreshing", metadata.refreshing);
+ web_ui()->CallJavascriptFunctionUnsafe("updateLocalMetadata", local_metadata);
+}
+
+void DriveInternalsWebUIHandler::ClearAccessToken(const base::ListValue* args) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ drive::DriveServiceInterface* drive_service = GetDriveService();
+ if (drive_service)
+ drive_service->ClearAccessToken();
+}
+
+void DriveInternalsWebUIHandler::ClearRefreshToken(
+ const base::ListValue* args) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ drive::DriveServiceInterface* drive_service = GetDriveService();
+ if (drive_service)
+ drive_service->ClearRefreshToken();
+}
+
+void DriveInternalsWebUIHandler::ResetDriveFileSystem(
+ const base::ListValue* args) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ drive::DriveIntegrationService* integration_service =
+ GetIntegrationService();
+ if (integration_service) {
+ integration_service->ClearCacheAndRemountFileSystem(
+ base::Bind(&DriveInternalsWebUIHandler::ResetFinished,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+}
+
+void DriveInternalsWebUIHandler::ResetFinished(bool success) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ web_ui()->CallJavascriptFunctionUnsafe("updateResetStatus",
+ base::Value(success));
+}
+
+void DriveInternalsWebUIHandler::ListFileEntries(const base::ListValue* args) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ UpdateFileSystemContentsSection();
+}
+
+void DriveInternalsWebUIHandler::UpdateDeltaUpdateStatusSection(
+ drive::DebugInfoCollector* debug_info_collector) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(debug_info_collector);
+
+ debug_info_collector->GetMetadata(
+ base::Bind(
+ &DriveInternalsWebUIHandler::OnGetFilesystemMetadataForDeltaUpdate,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DriveInternalsWebUIHandler::OnGetFilesystemMetadataForDeltaUpdate(
+ const drive::FileSystemMetadata& metadata) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ drive::DriveNotificationManager* drive_notification_manager =
+ drive::DriveNotificationManagerFactory::FindForBrowserContext(profile);
+ if (!drive_notification_manager)
+ return;
+
+ base::DictionaryValue delta_update_status;
+ delta_update_status.SetBoolean(
+ "push-notification-enabled",
+ drive_notification_manager->push_notification_enabled());
+ delta_update_status.SetString(
+ "last-update-check-time",
+ google_apis::util::FormatTimeAsStringLocaltime(
+ metadata.last_update_check_time));
+ delta_update_status.SetString(
+ "last-update-check-error",
+ drive::FileErrorToString(metadata.last_update_check_error));
+
+ web_ui()->CallJavascriptFunctionUnsafe("updateDeltaUpdateStatus",
+ delta_update_status);
+}
+
+void DriveInternalsWebUIHandler::UpdateInFlightOperationsSection(
+ drive::JobListInterface* job_list) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(job_list);
+
+ std::vector<drive::JobInfo> info_list = job_list->GetJobInfoList();
+
+ base::ListValue in_flight_operations;
+ for (size_t i = 0; i < info_list.size(); ++i) {
+ const drive::JobInfo& info = info_list[i];
+
+ auto dict = base::MakeUnique<base::DictionaryValue>();
+ dict->SetInteger("id", info.job_id);
+ dict->SetString("type", drive::JobTypeToString(info.job_type));
+ dict->SetString("file_path", info.file_path.AsUTF8Unsafe());
+ dict->SetString("state", drive::JobStateToString(info.state));
+ dict->SetDouble("progress_current", info.num_completed_bytes);
+ dict->SetDouble("progress_total", info.num_total_bytes);
+ in_flight_operations.Append(std::move(dict));
+ }
+ web_ui()->CallJavascriptFunctionUnsafe("updateInFlightOperations",
+ in_flight_operations);
+}
+
+void DriveInternalsWebUIHandler::UpdateGCacheContentsSection() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ // Start updating the GCache contents section.
+ Profile* profile = Profile::FromWebUI(web_ui());
+ const base::FilePath root_path = drive::util::GetCacheRootPath(profile);
+ base::ListValue* gcache_contents = new base::ListValue;
+ base::DictionaryValue* gcache_summary = new base::DictionaryValue;
+ base::PostTaskWithTraitsAndReply(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+ base::Bind(&GetGCacheContents, root_path, gcache_contents,
+ gcache_summary),
+ base::Bind(&DriveInternalsWebUIHandler::OnGetGCacheContents,
+ weak_ptr_factory_.GetWeakPtr(), base::Owned(gcache_contents),
+ base::Owned(gcache_summary)));
+}
+
+void DriveInternalsWebUIHandler::UpdateFileSystemContentsSection() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ drive::DebugInfoCollector* debug_info_collector = GetDebugInfoCollector();
+ if (!debug_info_collector)
+ return;
+
+ // Start rendering the file system tree as text.
+ const base::FilePath root_path = drive::util::GetDriveGrandRootPath();
+
+ debug_info_collector->GetResourceEntry(
+ root_path,
+ base::Bind(&DriveInternalsWebUIHandler::OnGetResourceEntryByPath,
+ weak_ptr_factory_.GetWeakPtr(),
+ root_path));
+
+ debug_info_collector->ReadDirectory(
+ root_path,
+ base::Bind(&DriveInternalsWebUIHandler::OnReadDirectoryByPath,
+ weak_ptr_factory_.GetWeakPtr(),
+ root_path));
+}
+
+void DriveInternalsWebUIHandler::UpdateLocalStorageUsageSection() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ // Propagate the amount of local free space in bytes.
+ base::FilePath home_path;
+ if (PathService::Get(base::DIR_HOME, &home_path)) {
+ base::DictionaryValue* local_storage_summary = new base::DictionaryValue;
+ base::PostTaskWithTraitsAndReply(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+ base::Bind(&GetFreeDiskSpace, home_path, local_storage_summary),
+ base::Bind(&DriveInternalsWebUIHandler::OnGetFreeDiskSpace,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Owned(local_storage_summary)));
+ } else {
+ LOG(ERROR) << "Home directory not found";
+ }
+}
+
+void DriveInternalsWebUIHandler::UpdateCacheContentsSection(
+ drive::DebugInfoCollector* debug_info_collector) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(debug_info_collector);
+
+ debug_info_collector->IterateFileCache(
+ base::Bind(&DriveInternalsWebUIHandler::UpdateCacheEntry,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&base::DoNothing));
+}
+
+void DriveInternalsWebUIHandler::UpdateEventLogSection() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ drive::DriveIntegrationService* integration_service =
+ GetIntegrationService();
+ if (!integration_service)
+ return;
+
+ const std::vector<drive::EventLogger::Event> log =
+ integration_service->event_logger()->GetHistory();
+
+ base::ListValue list;
+ for (size_t i = 0; i < log.size(); ++i) {
+ // Skip events which were already sent.
+ if (log[i].id <= last_sent_event_id_)
+ continue;
+
+ std::string severity = SeverityToString(log[i].severity);
+
+ auto dict = base::MakeUnique<base::DictionaryValue>();
+ dict->SetString("key",
+ google_apis::util::FormatTimeAsStringLocaltime(log[i].when));
+ dict->SetString("value", "[" + severity + "] " + log[i].what);
+ dict->SetString("class", "log-" + severity);
+ list.Append(std::move(dict));
+ last_sent_event_id_ = log[i].id;
+ }
+ if (!list.empty())
+ web_ui()->CallJavascriptFunctionUnsafe("updateEventLog", list);
+}
+
+void DriveInternalsWebUIHandler::UpdatePathConfigurationsSection() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ Profile* const profile = Profile::FromWebUI(web_ui());
+
+ base::ListValue paths;
+
+ AppendKeyValue(
+ &paths, "Downloads",
+ file_manager::util::GetDownloadsFolderForProfile(profile).AsUTF8Unsafe());
+ AppendKeyValue(
+ &paths, "Drive",
+ drive::util::GetDriveMountPointPath(profile).AsUTF8Unsafe());
+
+ const char* kPathPreferences[] = {
+ prefs::kSelectFileLastDirectory,
+ prefs::kSaveFileDefaultDirectory,
+ prefs::kDownloadDefaultDirectory,
+ };
+ for (size_t i = 0; i < arraysize(kPathPreferences); ++i) {
+ const char* const key = kPathPreferences[i];
+ AppendKeyValue(&paths, key,
+ profile->GetPrefs()->GetFilePath(key).AsUTF8Unsafe());
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("updatePathConfigurations", paths);
+}
+
+void DriveInternalsWebUIHandler::OnGetGCacheContents(
+ base::ListValue* gcache_contents,
+ base::DictionaryValue* gcache_summary) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(gcache_contents);
+ DCHECK(gcache_summary);
+
+ web_ui()->CallJavascriptFunctionUnsafe("updateGCacheContents",
+ *gcache_contents, *gcache_summary);
+}
+
+void DriveInternalsWebUIHandler::OnGetResourceEntryByPath(
+ const base::FilePath& path,
+ drive::FileError error,
+ std::unique_ptr<drive::ResourceEntry> entry) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if (error == drive::FILE_ERROR_OK) {
+ DCHECK(entry.get());
+ const base::Value value(FormatEntry(path, *entry) + "\n");
+ web_ui()->CallJavascriptFunctionUnsafe("updateFileSystemContents", value);
+ }
+}
+
+void DriveInternalsWebUIHandler::OnReadDirectoryByPath(
+ const base::FilePath& parent_path,
+ drive::FileError error,
+ std::unique_ptr<drive::ResourceEntryVector> entries) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if (error == drive::FILE_ERROR_OK) {
+ DCHECK(entries.get());
+
+ drive::DebugInfoCollector* debug_info_collector = GetDebugInfoCollector();
+ std::string file_system_as_text;
+ for (size_t i = 0; i < entries->size(); ++i) {
+ const drive::ResourceEntry& entry = (*entries)[i];
+ const base::FilePath current_path = parent_path.Append(
+ base::FilePath::FromUTF8Unsafe(entry.base_name()));
+
+ file_system_as_text.append(FormatEntry(current_path, entry) + "\n");
+
+ if (entry.file_info().is_directory()) {
+ debug_info_collector->ReadDirectory(
+ current_path,
+ base::Bind(&DriveInternalsWebUIHandler::OnReadDirectoryByPath,
+ weak_ptr_factory_.GetWeakPtr(),
+ current_path));
+ }
+ }
+
+ // There may be pending ReadDirectoryByPath() calls, but we can update
+ // the page with what we have now. This results in progressive
+ // updates, which is good for a large file system.
+ const base::Value value(file_system_as_text);
+ web_ui()->CallJavascriptFunctionUnsafe("updateFileSystemContents", value);
+ }
+}
+
+void DriveInternalsWebUIHandler::UpdateCacheEntry(
+ const std::string& local_id,
+ const drive::FileCacheEntry& cache_entry) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ // Convert |cache_entry| into a dictionary.
+ base::DictionaryValue value;
+ value.SetString("local_id", local_id);
+ value.SetString("md5", cache_entry.md5());
+ value.SetBoolean("is_present", cache_entry.is_present());
+ value.SetBoolean("is_pinned", cache_entry.is_pinned());
+ value.SetBoolean("is_dirty", cache_entry.is_dirty());
+
+ web_ui()->CallJavascriptFunctionUnsafe("updateCacheContents", value);
+}
+
+void DriveInternalsWebUIHandler::OnGetFreeDiskSpace(
+ base::DictionaryValue* local_storage_summary) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(local_storage_summary);
+
+ web_ui()->CallJavascriptFunctionUnsafe("updateLocalStorageUsage",
+ *local_storage_summary);
+}
+
+void DriveInternalsWebUIHandler::OnPeriodicUpdate(const base::ListValue* args) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ drive::DriveIntegrationService* integration_service =
+ GetIntegrationService();
+ // |integration_service| may be NULL in the guest/incognito mode.
+ if (!integration_service)
+ return;
+
+ UpdateInFlightOperationsSection(integration_service->job_list());
+ UpdateEventLogSection();
+}
+
+} // namespace
+
+DriveInternalsUI::DriveInternalsUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<DriveInternalsWebUIHandler>());
+
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIDriveInternalsHost);
+ source->AddResourcePath("drive_internals.css", IDR_DRIVE_INTERNALS_CSS);
+ source->AddResourcePath("drive_internals.js", IDR_DRIVE_INTERNALS_JS);
+ source->SetDefaultResource(IDR_DRIVE_INTERNALS_HTML);
+
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, source);
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/drive_internals_ui.h b/chromium/chrome/browser/ui/webui/chromeos/drive_internals_ui.h
new file mode 100644
index 00000000000..de0009e025e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/drive_internals_ui.h
@@ -0,0 +1,25 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_DRIVE_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_DRIVE_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace chromeos {
+
+// The WebUI controller for chrome::drive-internals, that is used for
+// diagnosing issues of Drive on Chrome OS.
+class DriveInternalsUI : public content::WebUIController {
+ public:
+ explicit DriveInternalsUI(content::WebUI* web_ui);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DriveInternalsUI);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_DRIVE_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc
new file mode 100644
index 00000000000..c38c67ecfb8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc
@@ -0,0 +1,642 @@
+// 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.
+
+#include "chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.h"
+
+#include <stdint.h>
+#include <utility>
+
+#include "ash/shell.h"
+#include "ash/system/bluetooth/tray_bluetooth_helper.h"
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/system/fake_input_device_settings.h"
+#include "chrome/browser/chromeos/system/input_device_settings.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/fake_cras_audio_client.h"
+#include "chromeos/dbus/fake_power_manager_client.h"
+#include "content/public/browser/web_ui.h"
+#include "device/bluetooth/dbus/bluez_dbus_manager.h"
+#include "device/bluetooth/dbus/fake_bluetooth_adapter_client.h"
+#include "device/bluetooth/dbus/fake_bluetooth_device_client.h"
+
+namespace {
+
+// Define the name of the callback functions that will be used by JavaScript.
+const char kInitialize[] = "initializeDeviceEmulator";
+const char kBluetoothDiscoverFunction[] = "requestBluetoothDiscover";
+const char kBluetoothPairFunction[] = "requestBluetoothPair";
+const char kRequestBluetoothInfo[] = "requestBluetoothInfo";
+const char kRequestPowerInfo[] = "requestPowerInfo";
+const char kRequestAudioNodes[] = "requestAudioNodes";
+
+// Define update function that will update the state of the audio ui.
+const char kInsertAudioNode[] = "insertAudioNode";
+const char kRemoveAudioNode[] = "removeAudioNode";
+
+// Define update functions that will update the power properties to the
+// variables defined in the web UI.
+const char kRemoveBluetoothDevice[] = "removeBluetoothDevice";
+const char kUpdateBatteryPercent[] = "updateBatteryPercent";
+const char kUpdateBatteryState[] = "updateBatteryState";
+const char kUpdateTimeToEmpty[] = "updateTimeToEmpty";
+const char kUpdateTimeToFull[] = "updateTimeToFull";
+const char kUpdatePowerSources[] = "updatePowerSources";
+const char kUpdatePowerSourceId[] = "updatePowerSourceId";
+const char kSetHasTouchpad[] = "setHasTouchpad";
+const char kSetHasMouse[] = "setHasMouse";
+
+// Define callback functions that will update the JavaScript variable
+// and the web UI.
+const char kUpdateAudioNodes[] =
+ "device_emulator.audioSettings.updateAudioNodes";
+const char kAddBluetoothDeviceJSCallback[] =
+ "device_emulator.bluetoothSettings.addBluetoothDevice";
+const char kDevicePairedFromTrayJSCallback[] =
+ "device_emulator.bluetoothSettings.devicePairedFromTray";
+const char kDeviceRemovedFromMainAdapterJSCallback[] =
+ "device_emulator.bluetoothSettings.deviceRemovedFromMainAdapter";
+const char kPairFailedJSCallback[] =
+ "device_emulator.bluetoothSettings.pairFailed";
+const char kUpdateBluetoothInfoJSCallback[] =
+ "device_emulator.bluetoothSettings.updateBluetoothInfo";
+const char kUpdatePowerPropertiesJSCallback[] =
+ "device_emulator.batterySettings.updatePowerProperties";
+const char kTouchpadExistsCallback[] =
+ "device_emulator.inputDeviceSettings.setTouchpadExists";
+const char kMouseExistsCallback[] =
+ "device_emulator.inputDeviceSettings.setMouseExists";
+
+const char kPairedPropertyName[] = "Paired";
+
+// Wattages to use as max power for power sources.
+const double kPowerLevelHigh = 50;
+const double kPowerLevelLow = 2;
+
+} // namespace
+
+namespace chromeos {
+
+class DeviceEmulatorMessageHandler::BluetoothObserver
+ : public bluez::BluetoothDeviceClient::Observer {
+ public:
+ explicit BluetoothObserver(DeviceEmulatorMessageHandler* owner)
+ : owner_(owner) {
+ owner_->fake_bluetooth_device_client_->AddObserver(this);
+ }
+
+ ~BluetoothObserver() override {
+ owner_->fake_bluetooth_device_client_->RemoveObserver(this);
+ }
+
+ // chromeos::BluetoothDeviceClient::Observer.
+ void DeviceAdded(const dbus::ObjectPath& object_path) override;
+
+ // chromeos::BluetoothDeviceClient::Observer.
+ void DevicePropertyChanged(const dbus::ObjectPath& object_path,
+ const std::string& property_name) override;
+
+ // chromeos::BluetoothDeviceClient::Observer.
+ void DeviceRemoved(const dbus::ObjectPath& object_path) override;
+
+ private:
+ DeviceEmulatorMessageHandler* owner_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothObserver);
+};
+
+void DeviceEmulatorMessageHandler::BluetoothObserver::DeviceAdded(
+ const dbus::ObjectPath& object_path) {
+ std::unique_ptr<base::DictionaryValue> device =
+ owner_->GetDeviceInfo(object_path);
+
+ // Request to add the device to the view's list of devices.
+ owner_->web_ui()->CallJavascriptFunctionUnsafe(kAddBluetoothDeviceJSCallback,
+ *device);
+}
+
+void DeviceEmulatorMessageHandler::BluetoothObserver::DevicePropertyChanged(
+ const dbus::ObjectPath& object_path,
+ const std::string& property_name) {
+ if (property_name == kPairedPropertyName) {
+ owner_->web_ui()->CallJavascriptFunctionUnsafe(
+ kDevicePairedFromTrayJSCallback, base::Value(object_path.value()));
+ }
+}
+
+void DeviceEmulatorMessageHandler::BluetoothObserver::DeviceRemoved(
+ const dbus::ObjectPath& object_path) {
+ owner_->web_ui()->CallJavascriptFunctionUnsafe(
+ kDeviceRemovedFromMainAdapterJSCallback,
+ base::Value(object_path.value()));
+}
+
+class DeviceEmulatorMessageHandler::CrasAudioObserver
+ : public CrasAudioClient::Observer {
+ public:
+ explicit CrasAudioObserver(DeviceEmulatorMessageHandler* owner)
+ : owner_(owner) {
+ owner_->fake_cras_audio_client_->AddObserver(this);
+ }
+
+ ~CrasAudioObserver() override {
+ owner_->fake_cras_audio_client_->RemoveObserver(this);
+ }
+
+ // chromeos::CrasAudioClient::Observer.
+ void NodesChanged() override;
+
+ private:
+ DeviceEmulatorMessageHandler* owner_;
+ DISALLOW_COPY_AND_ASSIGN(CrasAudioObserver);
+};
+
+void DeviceEmulatorMessageHandler::CrasAudioObserver::NodesChanged() {
+ owner_->HandleRequestAudioNodes(nullptr);
+}
+
+class DeviceEmulatorMessageHandler::PowerObserver
+ : public PowerManagerClient::Observer {
+ public:
+ explicit PowerObserver(DeviceEmulatorMessageHandler* owner)
+ : owner_(owner) {
+ owner_->fake_power_manager_client_->AddObserver(this);
+ }
+
+ ~PowerObserver() override {
+ owner_->fake_power_manager_client_->RemoveObserver(this);
+ }
+
+ void PowerChanged(
+ const power_manager::PowerSupplyProperties& proto) override;
+
+ private:
+ DeviceEmulatorMessageHandler* owner_;
+
+ DISALLOW_COPY_AND_ASSIGN(PowerObserver);
+};
+
+void DeviceEmulatorMessageHandler::PowerObserver::PowerChanged(
+ const power_manager::PowerSupplyProperties& proto) {
+ base::DictionaryValue power_properties;
+
+ power_properties.SetInteger("battery_percent", proto.battery_percent());
+ power_properties.SetInteger("battery_state", proto.battery_state());
+ power_properties.SetInteger("external_power", proto.external_power());
+ power_properties.SetInteger("battery_time_to_empty_sec",
+ proto.battery_time_to_empty_sec());
+ power_properties.SetInteger("battery_time_to_full_sec",
+ proto.battery_time_to_full_sec());
+ power_properties.SetString("external_power_source_id",
+ proto.external_power_source_id());
+
+ owner_->web_ui()->CallJavascriptFunctionUnsafe(
+ kUpdatePowerPropertiesJSCallback, power_properties);
+}
+
+DeviceEmulatorMessageHandler::DeviceEmulatorMessageHandler()
+ : fake_bluetooth_device_client_(
+ static_cast<bluez::FakeBluetoothDeviceClient*>(
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothDeviceClient())),
+ fake_cras_audio_client_(static_cast<chromeos::FakeCrasAudioClient*>(
+ chromeos::DBusThreadManager::Get()
+ ->GetCrasAudioClient())),
+ fake_power_manager_client_(static_cast<chromeos::FakePowerManagerClient*>(
+ chromeos::DBusThreadManager::Get()
+ ->GetPowerManagerClient())),
+ weak_ptr_factory_(this) {}
+
+DeviceEmulatorMessageHandler::~DeviceEmulatorMessageHandler() {
+}
+
+void DeviceEmulatorMessageHandler::Init(const base::ListValue* args) {
+ AllowJavascript();
+}
+
+void DeviceEmulatorMessageHandler::RequestPowerInfo(
+ const base::ListValue* args) {
+ fake_power_manager_client_->RequestStatusUpdate();
+}
+
+void DeviceEmulatorMessageHandler::HandleRemoveBluetoothDevice(
+ const base::ListValue* args) {
+ std::string path;
+ CHECK(args->GetString(0, &path));
+ fake_bluetooth_device_client_->RemoveDevice(
+ dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
+ dbus::ObjectPath(path));
+}
+
+void DeviceEmulatorMessageHandler::HandleRequestBluetoothDiscover(
+ const base::ListValue* args) {
+ CreateBluetoothDeviceFromListValue(args);
+}
+
+void DeviceEmulatorMessageHandler::HandleRequestBluetoothInfo(
+ const base::ListValue* args) {
+ // Get a list containing paths of the devices which are connected to
+ // the main adapter.
+ std::vector<dbus::ObjectPath> paths =
+ fake_bluetooth_device_client_->GetDevicesForAdapter(
+ dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath));
+
+ base::ListValue devices;
+ // Get each device's properties.
+ for (const dbus::ObjectPath& path : paths) {
+ std::unique_ptr<base::DictionaryValue> device = GetDeviceInfo(path);
+ devices.Append(std::move(device));
+ }
+
+ std::unique_ptr<base::ListValue> predefined_devices =
+ fake_bluetooth_device_client_->GetBluetoothDevicesAsDictionaries();
+
+ base::ListValue pairing_method_options;
+ pairing_method_options.AppendString(
+ bluez::FakeBluetoothDeviceClient::kPairingMethodNone);
+ pairing_method_options.AppendString(
+ bluez::FakeBluetoothDeviceClient::kPairingMethodPinCode);
+ pairing_method_options.AppendString(
+ bluez::FakeBluetoothDeviceClient::kPairingMethodPassKey);
+
+ base::ListValue pairing_action_options;
+ pairing_action_options.AppendString(
+ bluez::FakeBluetoothDeviceClient::kPairingActionDisplay);
+ pairing_action_options.AppendString(
+ bluez::FakeBluetoothDeviceClient::kPairingActionRequest);
+ pairing_action_options.AppendString(
+ bluez::FakeBluetoothDeviceClient::kPairingActionConfirmation);
+ pairing_action_options.AppendString(
+ bluez::FakeBluetoothDeviceClient::kPairingActionFail);
+
+ // Send the list of devices to the view.
+ web_ui()->CallJavascriptFunctionUnsafe(
+ kUpdateBluetoothInfoJSCallback, *predefined_devices, devices,
+ pairing_method_options, pairing_action_options);
+}
+
+void DeviceEmulatorMessageHandler::HandleRequestBluetoothPair(
+ const base::ListValue* args) {
+ // Create the device if it does not already exist.
+ std::string path = CreateBluetoothDeviceFromListValue(args);
+ bluez::FakeBluetoothDeviceClient::Properties* props =
+ fake_bluetooth_device_client_->GetProperties(dbus::ObjectPath(path));
+
+ // Try to pair the device with the main adapter. The device is identified
+ // by its device ID, which, in this case is the same as its address.
+ ash::Shell::Get()->tray_bluetooth_helper()->ConnectToBluetoothDevice(
+ props->address.value());
+ if (!props->paired.value()) {
+ web_ui()->CallJavascriptFunctionUnsafe(kPairFailedJSCallback,
+ base::Value(path));
+ }
+}
+
+void DeviceEmulatorMessageHandler::HandleRequestAudioNodes(
+ const base::ListValue* args) {
+ // Get every active audio node and create a dictionary to
+ // send it to JavaScript.
+ base::ListValue audio_nodes;
+ for (const AudioNode& node : fake_cras_audio_client_->node_list()) {
+ std::unique_ptr<base::DictionaryValue> audio_node(
+ new base::DictionaryValue());
+
+ audio_node->SetBoolean("isInput", node.is_input);
+ audio_node->SetString("id", base::Uint64ToString(node.id));
+ audio_node->SetString("deviceName", node.device_name);
+ audio_node->SetString("type", node.type);
+ audio_node->SetString("name", node.name);
+ audio_node->SetBoolean("active", node.active);
+
+ audio_nodes.Append(std::move(audio_node));
+ }
+ web_ui()->CallJavascriptFunctionUnsafe(kUpdateAudioNodes, audio_nodes);
+}
+
+void DeviceEmulatorMessageHandler::HandleInsertAudioNode(
+ const base::ListValue* args) {
+ AudioNode audio_node;
+ const base::DictionaryValue* device_dict = nullptr;
+
+ CHECK(args->GetDictionary(0, &device_dict));
+ CHECK(device_dict->GetBoolean("isInput", &audio_node.is_input));
+ CHECK(device_dict->GetString("deviceName", &audio_node.device_name));
+ CHECK(device_dict->GetString("type", &audio_node.type));
+ CHECK(device_dict->GetString("name", &audio_node.name));
+ CHECK(device_dict->GetBoolean("active", &audio_node.active));
+
+ std::string tmp_id;
+ CHECK(device_dict->GetString("id", &tmp_id));
+ CHECK(base::StringToUint64(tmp_id, &audio_node.id));
+
+ fake_cras_audio_client_->InsertAudioNodeToList(audio_node);
+}
+
+void DeviceEmulatorMessageHandler::HandleRemoveAudioNode(
+ const base::ListValue* args) {
+ std::string tmp_id;
+ uint64_t id;
+ CHECK(args->GetString(0, &tmp_id));
+ CHECK(base::StringToUint64(tmp_id, &id));
+
+ fake_cras_audio_client_->RemoveAudioNodeFromList(id);
+}
+
+void DeviceEmulatorMessageHandler::HandleSetHasTouchpad(
+ const base::ListValue* args) {
+ bool has_touchpad;
+ CHECK(args->GetBoolean(0, &has_touchpad));
+
+ system::InputDeviceSettings::Get()->GetFakeInterface()->set_touchpad_exists(
+ has_touchpad);
+}
+
+void DeviceEmulatorMessageHandler::HandleSetHasMouse(
+ const base::ListValue* args) {
+ bool has_mouse;
+ CHECK(args->GetBoolean(0, &has_mouse));
+
+ system::InputDeviceSettings::Get()->GetFakeInterface()->set_mouse_exists(
+ has_mouse);
+}
+
+void DeviceEmulatorMessageHandler::UpdateBatteryPercent(
+ const base::ListValue* args) {
+ int new_percent;
+ if (args->GetInteger(0, &new_percent)) {
+ power_manager::PowerSupplyProperties props =
+ fake_power_manager_client_->props();
+ props.set_battery_percent(new_percent);
+ fake_power_manager_client_->UpdatePowerProperties(props);
+ }
+}
+
+void DeviceEmulatorMessageHandler::UpdateBatteryState(
+ const base::ListValue* args) {
+ int battery_state;
+ if (args->GetInteger(0, &battery_state)) {
+ power_manager::PowerSupplyProperties props =
+ fake_power_manager_client_->props();
+ props.set_battery_state(
+ static_cast<power_manager::PowerSupplyProperties_BatteryState>(
+ battery_state));
+ fake_power_manager_client_->UpdatePowerProperties(props);
+ }
+}
+
+void DeviceEmulatorMessageHandler::UpdateTimeToEmpty(
+ const base::ListValue* args) {
+ int new_time;
+ if (args->GetInteger(0, &new_time)) {
+ power_manager::PowerSupplyProperties props =
+ fake_power_manager_client_->props();
+ props.set_battery_time_to_empty_sec(new_time);
+ fake_power_manager_client_->UpdatePowerProperties(props);
+ }
+}
+
+void DeviceEmulatorMessageHandler::UpdateTimeToFull(
+ const base::ListValue* args) {
+ int new_time;
+ if (args->GetInteger(0, &new_time)) {
+ power_manager::PowerSupplyProperties props =
+ fake_power_manager_client_->props();
+ props.set_battery_time_to_full_sec(new_time);
+ fake_power_manager_client_->UpdatePowerProperties(props);
+ }
+}
+
+void DeviceEmulatorMessageHandler::UpdatePowerSources(
+ const base::ListValue* args) {
+ const base::ListValue* sources;
+ CHECK(args->GetList(0, &sources));
+ power_manager::PowerSupplyProperties props =
+ fake_power_manager_client_->props();
+
+ std::string selected_id = props.external_power_source_id();
+
+ props.clear_available_external_power_source();
+ props.set_external_power_source_id("");
+
+ // Try to find the previously selected source in the list.
+ const power_manager::PowerSupplyProperties_PowerSource* selected_source =
+ nullptr;
+ for (const auto& val : *sources) {
+ const base::DictionaryValue* dict;
+ CHECK(val.GetAsDictionary(&dict));
+ power_manager::PowerSupplyProperties_PowerSource* source =
+ props.add_available_external_power_source();
+ std::string id;
+ CHECK(dict->GetString("id", &id));
+ source->set_id(id);
+ std::string device_type;
+ CHECK(dict->GetString("type", &device_type));
+ bool dual_role = device_type == "DualRoleUSB";
+ source->set_active_by_default(!dual_role);
+ if (dual_role)
+ props.set_supports_dual_role_devices(true);
+ int port;
+ CHECK(dict->GetInteger("port", &port));
+ source->set_port(
+ static_cast<power_manager::PowerSupplyProperties_PowerSource_Port>(
+ port));
+ std::string power_level;
+ CHECK(dict->GetString("power", &power_level));
+ source->set_max_power(
+ power_level == "high" ? kPowerLevelHigh : kPowerLevelLow);
+ if (id == selected_id)
+ selected_source = source;
+ }
+
+ // Emulate the device's source selection process.
+ for (const auto& source : props.available_external_power_source()) {
+ if (!source.active_by_default())
+ continue;
+ if (selected_source && selected_source->active_by_default() &&
+ source.max_power() < selected_source->max_power()) {
+ continue;
+ }
+ selected_source = &source;
+ }
+
+ fake_power_manager_client_->UpdatePowerProperties(props);
+ fake_power_manager_client_->SetPowerSource(
+ selected_source ? selected_source->id() : "");
+}
+
+void DeviceEmulatorMessageHandler::UpdatePowerSourceId(
+ const base::ListValue* args) {
+ std::string id;
+ CHECK(args->GetString(0, &id));
+ fake_power_manager_client_->SetPowerSource(id);
+}
+
+void DeviceEmulatorMessageHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ kInitialize,
+ base::Bind(&DeviceEmulatorMessageHandler::Init, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kRequestPowerInfo,
+ base::Bind(&DeviceEmulatorMessageHandler::RequestPowerInfo,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kUpdateBatteryPercent,
+ base::Bind(&DeviceEmulatorMessageHandler::UpdateBatteryPercent,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kUpdateBatteryState,
+ base::Bind(&DeviceEmulatorMessageHandler::UpdateBatteryState,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kUpdateTimeToEmpty,
+ base::Bind(&DeviceEmulatorMessageHandler::UpdateTimeToEmpty,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kUpdateTimeToFull,
+ base::Bind(&DeviceEmulatorMessageHandler::UpdateTimeToFull,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kUpdatePowerSources,
+ base::Bind(&DeviceEmulatorMessageHandler::UpdatePowerSources,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kUpdatePowerSourceId,
+ base::Bind(&DeviceEmulatorMessageHandler::UpdatePowerSourceId,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kRequestAudioNodes,
+ base::Bind(&DeviceEmulatorMessageHandler::HandleRequestAudioNodes,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kInsertAudioNode,
+ base::Bind(&DeviceEmulatorMessageHandler::HandleInsertAudioNode,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kRemoveAudioNode,
+ base::Bind(&DeviceEmulatorMessageHandler::HandleRemoveAudioNode,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kBluetoothDiscoverFunction,
+ base::Bind(&DeviceEmulatorMessageHandler::HandleRequestBluetoothDiscover,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kBluetoothPairFunction,
+ base::Bind(&DeviceEmulatorMessageHandler::HandleRequestBluetoothPair,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kRequestBluetoothInfo,
+ base::Bind(&DeviceEmulatorMessageHandler::HandleRequestBluetoothInfo,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kRemoveBluetoothDevice,
+ base::Bind(&DeviceEmulatorMessageHandler::HandleRemoveBluetoothDevice,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kSetHasTouchpad,
+ base::Bind(&DeviceEmulatorMessageHandler::HandleSetHasTouchpad,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kSetHasMouse,
+ base::Bind(&DeviceEmulatorMessageHandler::HandleSetHasMouse,
+ base::Unretained(this)));
+}
+
+void DeviceEmulatorMessageHandler::OnJavascriptAllowed() {
+ bluetooth_observer_.reset(new BluetoothObserver(this));
+ cras_audio_observer_.reset(new CrasAudioObserver(this));
+ power_observer_.reset(new PowerObserver(this));
+
+ system::InputDeviceSettings::Get()->TouchpadExists(
+ base::Bind(&DeviceEmulatorMessageHandler::TouchpadExists,
+ weak_ptr_factory_.GetWeakPtr()));
+ system::InputDeviceSettings::Get()->MouseExists(
+ base::Bind(&DeviceEmulatorMessageHandler::MouseExists,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DeviceEmulatorMessageHandler::OnJavascriptDisallowed() {
+ bluetooth_observer_.reset();
+ cras_audio_observer_.reset();
+ power_observer_.reset();
+}
+
+std::string DeviceEmulatorMessageHandler::CreateBluetoothDeviceFromListValue(
+ const base::ListValue* args) {
+ const base::DictionaryValue* device_dict = nullptr;
+ bluez::FakeBluetoothDeviceClient::IncomingDeviceProperties props;
+
+ CHECK(args->GetDictionary(0, &device_dict));
+ CHECK(device_dict->GetString("path", &props.device_path));
+ CHECK(device_dict->GetString("name", &props.device_name));
+ CHECK(device_dict->GetString("alias", &props.device_alias));
+ CHECK(device_dict->GetString("address", &props.device_address));
+ CHECK(device_dict->GetString("pairingMethod", &props.pairing_method));
+ CHECK(device_dict->GetString("pairingAuthToken", &props.pairing_auth_token));
+ CHECK(device_dict->GetString("pairingAction", &props.pairing_action));
+ CHECK(device_dict->GetInteger("classValue", &props.device_class));
+ CHECK(device_dict->GetBoolean("isTrusted", &props.is_trusted));
+ CHECK(device_dict->GetBoolean("incoming", &props.incoming));
+
+ // Create the device and store it in the FakeBluetoothDeviceClient's observed
+ // list of devices.
+ fake_bluetooth_device_client_->CreateDeviceWithProperties(
+ dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath), props);
+
+ return props.device_path;
+}
+
+std::unique_ptr<base::DictionaryValue>
+DeviceEmulatorMessageHandler::GetDeviceInfo(
+ const dbus::ObjectPath& object_path) {
+ // Get the device's properties.
+ bluez::FakeBluetoothDeviceClient::Properties* props =
+ fake_bluetooth_device_client_->GetProperties(object_path);
+ std::unique_ptr<base::DictionaryValue> device(new base::DictionaryValue());
+ std::unique_ptr<base::ListValue> uuids(new base::ListValue);
+ bluez::FakeBluetoothDeviceClient::SimulatedPairingOptions* options =
+ fake_bluetooth_device_client_->GetPairingOptions(object_path);
+
+ device->SetString("path", object_path.value());
+ device->SetString("name", props->name.value());
+ device->SetString("alias", props->alias.value());
+ device->SetString("address", props->address.value());
+ if (options) {
+ device->SetString("pairingMethod", options->pairing_method);
+ device->SetString("pairingAuthToken", options->pairing_auth_token);
+ device->SetString("pairingAction", options->pairing_action);
+ } else {
+ device->SetString("pairingMethod", "");
+ device->SetString("pairingAuthToken", "");
+ device->SetString("pairingAction", "");
+ }
+ device->SetInteger("classValue", props->bluetooth_class.value());
+ device->SetBoolean("isTrusted", props->trusted.value());
+ device->SetBoolean("incoming", false);
+
+ for (const std::string& uuid : props->uuids.value()) {
+ uuids->AppendString(uuid);
+ }
+
+ device->Set("uuids", std::move(uuids));
+
+ return device;
+}
+
+void DeviceEmulatorMessageHandler::TouchpadExists(bool exists) {
+ if (!IsJavascriptAllowed())
+ return;
+ web_ui()->CallJavascriptFunctionUnsafe(kTouchpadExistsCallback,
+ base::Value(exists));
+}
+
+void DeviceEmulatorMessageHandler::MouseExists(bool exists) {
+ if (!IsJavascriptAllowed())
+ return;
+ web_ui()->CallJavascriptFunctionUnsafe(kMouseExistsCallback,
+ base::Value(exists));
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.h b/chromium/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.h
new file mode 100644
index 00000000000..9b5099d0082
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.h
@@ -0,0 +1,141 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_EMULATOR_DEVICE_EMULATOR_MESSAGE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_EMULATOR_DEVICE_EMULATOR_MESSAGE_HANDLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/system/pointer_device_observer.h"
+#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+} // namespace base
+
+namespace dbus {
+class ObjectPath;
+} // namespace dbus
+
+namespace bluez {
+class FakeBluetoothDeviceClient;
+}
+
+namespace chromeos {
+
+class FakeCrasAudioClient;
+class FakePowerManagerClient;
+
+// Handler class for the Device Emulator page operations.
+class DeviceEmulatorMessageHandler :
+ public system::PointerDeviceObserver::Observer,
+ public content::WebUIMessageHandler {
+ public:
+ DeviceEmulatorMessageHandler();
+ ~DeviceEmulatorMessageHandler() override;
+
+ // Adds |this| as an observer to all necessary objects.
+ void Init(const base::ListValue* args);
+
+ // Callback for the "removeBluetoothDevice" message. This is called by
+ // the view to remove a bluetooth device from the FakeBluetoothDeviceClient's
+ // observed list of devices.
+ void HandleRemoveBluetoothDevice(const base::ListValue* args);
+
+ // Callback for the "requestBluetoothDiscover" message. This asynchronously
+ // requests for the system to discover a certain device. The device's data
+ // should be passed into |args| as a dictionary. If the device does not
+ // already exist, then it will be created and attached to the main adapter.
+ void HandleRequestBluetoothDiscover(const base::ListValue* args);
+
+ // Callback for the "requestBluetoothInfo" message. This asynchronously
+ // requests for the devices which are already paired with the device.
+ void HandleRequestBluetoothInfo(const base::ListValue* args);
+
+ // Callback for the "requestBluetoothPair" message. This asynchronously
+ // requests for the system to pair a certain device. The device's data should
+ // be passed into |args| as a dictionary. If the device does not already
+ // exist, then it will be created and attached to the main adapter.
+ void HandleRequestBluetoothPair(const base::ListValue* args);
+
+ // Callback for the "requestAudioNodes" message. This asynchronously
+ // requests the audio node that is current set to active. It is possible
+ // that there can be multiple current active nodes.
+ void HandleRequestAudioNodes(const base::ListValue* args);
+
+ // Create a node and add the node to the current AudioNodeList in
+ // |fake_cras_audio_client_|.
+ void HandleInsertAudioNode(const base::ListValue* args);
+
+ // Removes an AudioNode from the current list in |fake_cras_audio_client_|.
+ // based on the node id.
+ void HandleRemoveAudioNode(const base::ListValue* args);
+
+ // Connects or disconnects a fake touchpad.
+ void HandleSetHasTouchpad(const base::ListValue* args);
+
+ // Connects or disconnects a fake mouse.
+ void HandleSetHasMouse(const base::ListValue* args);
+
+ // Callbacks for JS update methods. All these methods work
+ // asynchronously.
+ void UpdateBatteryPercent(const base::ListValue* args);
+ void UpdateBatteryState(const base::ListValue* args);
+ void UpdateExternalPower(const base::ListValue* args);
+ void UpdateTimeToEmpty(const base::ListValue* args);
+ void UpdateTimeToFull(const base::ListValue* args);
+ void UpdatePowerSources(const base::ListValue* args);
+ void UpdatePowerSourceId(const base::ListValue* args);
+
+ // content::WebUIMessageHandler:
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ // Callback for the "requestPowerInfo" message. This asynchronously requests
+ // for power settings such as battery percentage, external power, etc. to
+ // update the view.
+ void RequestPowerInfo(const base::ListValue* args);
+
+ private:
+ class BluetoothObserver;
+ class CrasAudioObserver;
+ class PowerObserver;
+
+ // Creates a bluetooth device with the properties given in |args|. |args|
+ // should contain a dictionary so that each dictionary value can be mapped
+ // to its respective property upon creating the device. Returns the device
+ // path.
+ std::string CreateBluetoothDeviceFromListValue(const base::ListValue* args);
+
+ // Builds a dictionary with each key representing a property of the device
+ // with path |object_path|.
+ std::unique_ptr<base::DictionaryValue> GetDeviceInfo(
+ const dbus::ObjectPath& object_path);
+
+ // system::PointerDeviceObserver::Observer:
+ void TouchpadExists(bool exists) override;
+ void MouseExists(bool exists) override;
+
+ bluez::FakeBluetoothDeviceClient* fake_bluetooth_device_client_;
+ std::unique_ptr<BluetoothObserver> bluetooth_observer_;
+
+ FakeCrasAudioClient* fake_cras_audio_client_;
+ std::unique_ptr<CrasAudioObserver> cras_audio_observer_;
+
+ FakePowerManagerClient* fake_power_manager_client_;
+ std::unique_ptr<PowerObserver> power_observer_;
+
+ base::WeakPtrFactory<DeviceEmulatorMessageHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeviceEmulatorMessageHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_EMULATOR_DEVICE_EMULATOR_MESSAGE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/emulator/device_emulator_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/emulator/device_emulator_ui.cc
new file mode 100644
index 00000000000..73308662775
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/emulator/device_emulator_ui.cc
@@ -0,0 +1,67 @@
+// 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.
+
+#include "chrome/browser/ui/webui/chromeos/emulator/device_emulator_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace {
+
+// Create data source for chrome://device-emulator/.
+content::WebUIDataSource* CreateDeviceEmulatorUIDataSource() {
+ content::WebUIDataSource* html =
+ content::WebUIDataSource::Create(chrome::kChromeUIDeviceEmulatorHost);
+
+ // Add resources.
+ html->AddResourcePath("audio_settings.html",
+ IDR_DEVICE_EMULATOR_AUDIO_SETTINGS_HTML);
+ html->AddResourcePath("audio_settings.js",
+ IDR_DEVICE_EMULATOR_AUDIO_SETTINGS_JS);
+ html->AddResourcePath("battery_settings.html",
+ IDR_DEVICE_EMULATOR_BATTERY_SETTINGS_HTML);
+ html->AddResourcePath("battery_settings.js",
+ IDR_DEVICE_EMULATOR_BATTERY_SETTINGS_JS);
+ html->AddResourcePath("bluetooth_settings.html",
+ IDR_DEVICE_EMULATOR_BLUETOOTH_SETTINGS_HTML);
+ html->AddResourcePath("bluetooth_settings.js",
+ IDR_DEVICE_EMULATOR_BLUETOOTH_SETTINGS_JS);
+ html->AddResourcePath("icons.html", IDR_DEVICE_EMULATOR_ICONS_HTML);
+ html->AddResourcePath("input_device_settings.html",
+ IDR_DEVICE_EMULATOR_INPUT_DEVICE_SETTINGS_HTML);
+ html->AddResourcePath("input_device_settings.js",
+ IDR_DEVICE_EMULATOR_INPUT_DEVICE_SETTINGS_JS);
+ html->AddResourcePath("device_emulator_pages.html",
+ IDR_DEVICE_EMULATOR_PAGES_HTML);
+ html->AddResourcePath("device_emulator_pages.js",
+ IDR_DEVICE_EMULATOR_PAGES_JS);
+
+ html->AddResourcePath("shared_styles.html",
+ IDR_DEVICE_EMULATOR_SHARED_STYLES_HTML);
+
+ html->SetDefaultResource(IDR_DEVICE_EMULATOR_HTML);
+
+ return html;
+}
+
+} // namespace
+
+DeviceEmulatorUI::DeviceEmulatorUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ web_ui->AddMessageHandler(
+ base::MakeUnique<chromeos::DeviceEmulatorMessageHandler>());
+
+ content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
+ CreateDeviceEmulatorUIDataSource());
+}
+
+DeviceEmulatorUI::~DeviceEmulatorUI() {
+}
diff --git a/chromium/chrome/browser/ui/webui/chromeos/emulator/device_emulator_ui.h b/chromium/chrome/browser/ui/webui/chromeos/emulator/device_emulator_ui.h
new file mode 100644
index 00000000000..5cb8ad622d8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/emulator/device_emulator_ui.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_EMULATOR_DEVICE_EMULATOR_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_EMULATOR_DEVICE_EMULATOR_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The WebUI handler for chrome://device-emulator
+class DeviceEmulatorUI : public content::WebUIController {
+ public:
+ explicit DeviceEmulatorUI(content::WebUI* web_ui);
+ ~DeviceEmulatorUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DeviceEmulatorUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_EMULATOR_DEVICE_EMULATOR_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/first_run/OWNERS b/chromium/chrome/browser/ui/webui/chromeos/first_run/OWNERS
new file mode 100644
index 00000000000..2dcc458322f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/first_run/OWNERS
@@ -0,0 +1 @@
+alemate@chromium.org
diff --git a/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_actor.cc b/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_actor.cc
new file mode 100644
index 00000000000..da8840f5ffa
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_actor.cc
@@ -0,0 +1,71 @@
+// 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/chromeos/first_run/first_run_actor.h"
+
+#include <limits>
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+
+namespace {
+const int kNoneValue = std::numeric_limits<int>::min();
+}
+
+namespace chromeos {
+
+FirstRunActor::StepPosition::StepPosition()
+ : top_(kNoneValue),
+ right_(kNoneValue),
+ bottom_(kNoneValue),
+ left_(kNoneValue) {
+}
+
+FirstRunActor::StepPosition& FirstRunActor::StepPosition::SetTop(int top) {
+ top_ = top;
+ return *this;
+}
+
+FirstRunActor::StepPosition& FirstRunActor::StepPosition::SetRight(int right) {
+ right_ = right;
+ return *this;
+}
+
+FirstRunActor::StepPosition&
+FirstRunActor::StepPosition::SetBottom(int bottom) {
+ bottom_ = bottom;
+ return *this;
+}
+
+FirstRunActor::StepPosition& FirstRunActor::StepPosition::SetLeft(int left) {
+ left_ = left;
+ return *this;
+}
+
+std::unique_ptr<base::DictionaryValue> FirstRunActor::StepPosition::AsValue()
+ const {
+ base::DictionaryValue* result = new base::DictionaryValue();
+ if (top_ != kNoneValue)
+ result->SetInteger("top", top_);
+ if (right_ != kNoneValue)
+ result->SetInteger("right", right_);
+ if (bottom_ != kNoneValue)
+ result->SetInteger("bottom", bottom_);
+ if (left_ != kNoneValue)
+ result->SetInteger("left", left_);
+ return base::WrapUnique(result);
+}
+
+FirstRunActor::FirstRunActor()
+ : delegate_(NULL) {
+}
+
+FirstRunActor::~FirstRunActor() {
+ if (delegate())
+ delegate()->OnActorDestroyed();
+ delegate_ = NULL;
+}
+
+} // namespace chromeos
+
diff --git a/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_actor.h b/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_actor.h
new file mode 100644
index 00000000000..56dafbf1a21
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_actor.h
@@ -0,0 +1,115 @@
+// 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_CHROMEOS_FIRST_RUN_FIRST_RUN_ACTOR_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_FIRST_RUN_FIRST_RUN_ACTOR_H_
+
+#include <memory>
+#include <string>
+
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace chromeos {
+
+class FirstRunActor {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Called after actor was initialized.
+ virtual void OnActorInitialized() = 0;
+
+ // Called when user clicked "Next" button in step with name |step_name|.
+ virtual void OnNextButtonClicked(const std::string& step_name) = 0;
+
+ // Called when user clicked "Keep exploring" button.
+ virtual void OnHelpButtonClicked() = 0;
+
+ // Called after step with |step_name| has been shown.
+ virtual void OnStepShown(const std::string& step_name) = 0;
+
+ // Called after step with |step_name| has been shown.
+ virtual void OnStepHidden(const std::string& step_name) = 0;
+
+ // Called in answer to Finalize() call.
+ virtual void OnActorFinalized() = 0;
+
+ // Notifies about about actor destruction.
+ virtual void OnActorDestroyed() = 0;
+ };
+
+ class StepPosition {
+ public:
+ // Initializes fields in "non-set" state.
+ StepPosition();
+
+ // Setters for properties. Return |*this|.
+ StepPosition& SetTop(int top);
+ StepPosition& SetRight(int right);
+ StepPosition& SetBottom(int bottom);
+ StepPosition& SetLeft(int left);
+
+ // Returns DictionaryValue containing set properties.
+ std::unique_ptr<base::DictionaryValue> AsValue() const;
+
+ private:
+ int top_;
+ int right_;
+ int bottom_;
+ int left_;
+ };
+
+ FirstRunActor();
+ virtual ~FirstRunActor();
+
+ // Returns |true| if actor is initialized. Other public methods can be called
+ // only if |IsInitialized| returns |true|.
+ virtual bool IsInitialized() = 0;
+
+ // Changes background visibility.
+ virtual void SetBackgroundVisible(bool visible) = 0;
+
+ // Adds rectangular hole to background with given position and dimensions.
+ virtual void AddRectangularHole(int x, int y, int width, int height) = 0;
+
+ // Adds round hole to background with given position and dimensions.
+ virtual void AddRoundHole(int x, int y, float radius) = 0;
+
+ // Removes all holes from background.
+ virtual void RemoveBackgroundHoles() = 0;
+
+ // Shows step with given name and position.
+ virtual void ShowStepPositioned(const std::string& name,
+ const StepPosition& position) = 0;
+
+ // Shows step with given name that points to given point.
+ virtual void ShowStepPointingTo(const std::string& name,
+ int x,
+ int y,
+ int offset) = 0;
+
+ // Hides currently shown step.
+ virtual void HideCurrentStep() = 0;
+
+ // Hides all the UI.
+ virtual void Finalize() = 0;
+
+ // Whether actor is finalizing now.
+ virtual bool IsFinalizing() = 0;
+
+ void set_delegate(Delegate* delegate) { delegate_ = delegate; }
+ Delegate* delegate() const { return delegate_; }
+
+ private:
+ Delegate* delegate_;
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_FIRST_RUN_FIRST_RUN_ACTOR_H_
+
diff --git a/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_handler.cc
new file mode 100644
index 00000000000..f3319f57c04
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_handler.cc
@@ -0,0 +1,134 @@
+// 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/chromeos/first_run/first_run_handler.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "content/public/browser/web_ui.h"
+
+namespace chromeos {
+
+FirstRunHandler::FirstRunHandler()
+ : is_initialized_(false),
+ is_finalizing_(false) {
+}
+
+bool FirstRunHandler::IsInitialized() {
+ return is_initialized_;
+}
+
+void FirstRunHandler::SetBackgroundVisible(bool visible) {
+ web_ui()->CallJavascriptFunctionUnsafe("cr.FirstRun.setBackgroundVisible",
+ base::Value(visible));
+}
+
+void FirstRunHandler::AddRectangularHole(int x, int y, int width, int height) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "cr.FirstRun.addRectangularHole", base::Value(x), base::Value(y),
+ base::Value(width), base::Value(height));
+}
+
+void FirstRunHandler::AddRoundHole(int x, int y, float radius) {
+ web_ui()->CallJavascriptFunctionUnsafe("cr.FirstRun.addRoundHole",
+ base::Value(x), base::Value(y),
+ base::Value(radius));
+}
+
+void FirstRunHandler::RemoveBackgroundHoles() {
+ web_ui()->CallJavascriptFunctionUnsafe("cr.FirstRun.removeHoles");
+}
+
+void FirstRunHandler::ShowStepPositioned(const std::string& name,
+ const StepPosition& position) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "cr.FirstRun.showStep", base::Value(name), *position.AsValue());
+}
+
+void FirstRunHandler::ShowStepPointingTo(const std::string& name,
+ int x,
+ int y,
+ int offset) {
+ auto null = base::MakeUnique<base::Value>();
+ base::ListValue point_with_offset;
+ point_with_offset.AppendInteger(x);
+ point_with_offset.AppendInteger(y);
+ point_with_offset.AppendInteger(offset);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "cr.FirstRun.showStep", base::Value(name), *null, point_with_offset);
+}
+
+void FirstRunHandler::HideCurrentStep() {
+ web_ui()->CallJavascriptFunctionUnsafe("cr.FirstRun.hideCurrentStep");
+}
+
+void FirstRunHandler::Finalize() {
+ is_finalizing_ = true;
+ web_ui()->CallJavascriptFunctionUnsafe("cr.FirstRun.finalize");
+}
+
+bool FirstRunHandler::IsFinalizing() {
+ return is_finalizing_;
+}
+
+void FirstRunHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("initialized",
+ base::Bind(&FirstRunHandler::HandleInitialized, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("nextButtonClicked",
+ base::Bind(&FirstRunHandler::HandleNextButtonClicked,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("helpButtonClicked",
+ base::Bind(&FirstRunHandler::HandleHelpButtonClicked,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("stepShown",
+ base::Bind(&FirstRunHandler::HandleStepShown,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("stepHidden",
+ base::Bind(&FirstRunHandler::HandleStepHidden,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("finalized",
+ base::Bind(&FirstRunHandler::HandleFinalized,
+ base::Unretained(this)));
+}
+
+void FirstRunHandler::HandleInitialized(const base::ListValue* args) {
+ is_initialized_ = true;
+ if (delegate())
+ delegate()->OnActorInitialized();
+}
+
+void FirstRunHandler::HandleNextButtonClicked(const base::ListValue* args) {
+ std::string step_name;
+ CHECK(args->GetString(0, &step_name));
+ if (delegate())
+ delegate()->OnNextButtonClicked(step_name);
+}
+
+void FirstRunHandler::HandleHelpButtonClicked(const base::ListValue* args) {
+ if (delegate())
+ delegate()->OnHelpButtonClicked();
+}
+
+void FirstRunHandler::HandleStepShown(const base::ListValue* args) {
+ std::string step_name;
+ CHECK(args->GetString(0, &step_name));
+ if (delegate())
+ delegate()->OnStepShown(step_name);
+}
+
+void FirstRunHandler::HandleStepHidden(const base::ListValue* args) {
+ std::string step_name;
+ CHECK(args->GetString(0, &step_name));
+ if (delegate())
+ delegate()->OnStepHidden(step_name);
+}
+
+void FirstRunHandler::HandleFinalized(const base::ListValue* args) {
+ is_finalizing_ = false;
+ if (delegate())
+ delegate()->OnActorFinalized();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_handler.h b/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_handler.h
new file mode 100644
index 00000000000..b5cbce35245
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_handler.h
@@ -0,0 +1,60 @@
+// 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_CHROMEOS_FIRST_RUN_FIRST_RUN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_FIRST_RUN_FIRST_RUN_HANDLER_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/chromeos/first_run/first_run_actor.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace chromeos {
+
+class StepPosition;
+
+class FirstRunHandler : public FirstRunActor,
+ public content::WebUIMessageHandler {
+ public:
+ FirstRunHandler();
+ // Overriden from FirstRunActor.
+ bool IsInitialized() override;
+ void SetBackgroundVisible(bool visible) override;
+ void AddRectangularHole(int x, int y, int width, int height) override;
+ void AddRoundHole(int x, int y, float radius) override;
+ void RemoveBackgroundHoles() override;
+ void ShowStepPositioned(const std::string& name,
+ const StepPosition& position) override;
+ void ShowStepPointingTo(const std::string& name,
+ int x,
+ int y,
+ int offset) override;
+ void HideCurrentStep() override;
+ void Finalize() override;
+ bool IsFinalizing() override;
+
+ private:
+ // Overriden from content::WebUIMessageHandler.
+ void RegisterMessages() override;
+
+ // Handlers for calls from JS.
+ void HandleInitialized(const base::ListValue* args);
+ void HandleNextButtonClicked(const base::ListValue* args);
+ void HandleHelpButtonClicked(const base::ListValue* args);
+ void HandleStepShown(const base::ListValue* args);
+ void HandleStepHidden(const base::ListValue* args);
+ void HandleFinalized(const base::ListValue* args);
+
+ bool is_initialized_;
+ bool is_finalizing_;
+
+ DISALLOW_COPY_AND_ASSIGN(FirstRunHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_FIRST_RUN_FIRST_RUN_HANDLER_H_
+
diff --git a/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_ui.cc
new file mode 100644
index 00000000000..c439b90da9a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_ui.cc
@@ -0,0 +1,113 @@
+// 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/chromeos/first_run/first_run_ui.h"
+
+#include "ash/shelf/shelf.h"
+#include "ash/shell.h"
+#include "base/command_line.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/chromeos/first_run/first_run_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_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"
+
+namespace {
+
+const char kFirstRunJSPath[] = "first_run.js";
+const char kShelfAlignmentBottom[] = "bottom";
+const char kShelfAlignmentLeft[] = "left";
+const char kShelfAlignmentRight[] = "right";
+
+void SetLocalizedStrings(base::DictionaryValue* localized_strings) {
+ localized_strings->SetString(
+ "appListHeader",
+ l10n_util::GetStringUTF16(IDS_FIRST_RUN_APP_LIST_STEP_HEADER));
+ localized_strings->SetString(
+ "appListText1",
+ l10n_util::GetStringUTF16(IDS_FIRST_RUN_APP_LIST_STEP_TEXT_1));
+ localized_strings->SetString(
+ "appListText2",
+ l10n_util::GetStringUTF16(IDS_FIRST_RUN_APP_LIST_STEP_TEXT_2));
+ localized_strings->SetString(
+ "trayHeader", l10n_util::GetStringUTF16(IDS_FIRST_RUN_TRAY_STEP_HEADER));
+ localized_strings->SetString(
+ "trayText", l10n_util::GetStringUTF16(IDS_FIRST_RUN_TRAY_STEP_TEXT));
+ localized_strings->SetString(
+ "helpHeader", l10n_util::GetStringUTF16(IDS_FIRST_RUN_HELP_STEP_HEADER));
+ base::string16 product_name =
+ l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME);
+ localized_strings->SetString(
+ "helpText1", l10n_util::GetStringFUTF16(IDS_FIRST_RUN_HELP_STEP_TEXT_1,
+ product_name));
+ localized_strings->SetString(
+ "helpText2", l10n_util::GetStringUTF16(IDS_FIRST_RUN_HELP_STEP_TEXT_2));
+ localized_strings->SetString(
+ "helpKeepExploringButton",
+ l10n_util::GetStringUTF16(IDS_FIRST_RUN_HELP_STEP_KEEP_EXPLORING_BUTTON));
+ localized_strings->SetString(
+ "helpFinishButton",
+ l10n_util::GetStringUTF16(IDS_FIRST_RUN_HELP_STEP_FINISH_BUTTON));
+ localized_strings->SetString(
+ "nextButton", l10n_util::GetStringUTF16(IDS_FIRST_RUN_NEXT_BUTTON));
+ localized_strings->SetBoolean(
+ "transitionsEnabled",
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kEnableFirstRunUITransitions));
+ localized_strings->SetString(
+ "accessibleTitle",
+ l10n_util::GetStringUTF16(IDS_FIRST_RUN_STEP_ACCESSIBLE_TITLE));
+ ash::Shelf* shelf = ash::Shelf::ForWindow(ash::Shell::GetPrimaryRootWindow());
+ std::string shelf_alignment;
+ switch (shelf->alignment()) {
+ case ash::SHELF_ALIGNMENT_BOTTOM:
+ case ash::SHELF_ALIGNMENT_BOTTOM_LOCKED:
+ shelf_alignment = kShelfAlignmentBottom;
+ break;
+ case ash::SHELF_ALIGNMENT_LEFT:
+ shelf_alignment = kShelfAlignmentLeft;
+ break;
+ case ash::SHELF_ALIGNMENT_RIGHT:
+ shelf_alignment = kShelfAlignmentRight;
+ break;
+ }
+ localized_strings->SetString("shelfAlignment", shelf_alignment);
+}
+
+content::WebUIDataSource* CreateDataSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIFirstRunHost);
+ source->SetJsonPath("strings.js");
+ source->SetDefaultResource(IDR_FIRST_RUN_HTML);
+ source->AddResourcePath(kFirstRunJSPath, IDR_FIRST_RUN_JS);
+ base::DictionaryValue localized_strings;
+ const std::string& app_locale = g_browser_process->GetApplicationLocale();
+ webui::SetLoadTimeDataDefaults(app_locale, &localized_strings);
+ SetLocalizedStrings(&localized_strings);
+ source->AddLocalizedStrings(localized_strings);
+ return source;
+}
+
+} // anonymous namespace
+
+namespace chromeos {
+
+FirstRunUI::FirstRunUI(content::WebUI* web_ui)
+ : WebUIController(web_ui),
+ actor_(NULL) {
+ auto handler = base::MakeUnique<FirstRunHandler>();
+ actor_ = handler.get();
+ web_ui->AddMessageHandler(std::move(handler));
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), CreateDataSource());
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_ui.h b/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_ui.h
new file mode 100644
index 00000000000..e53964d93c2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/first_run/first_run_ui.h
@@ -0,0 +1,33 @@
+// 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_CHROMEOS_FIRST_RUN_FIRST_RUN_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_FIRST_RUN_FIRST_RUN_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace content {
+class WebUI;
+}
+
+namespace chromeos {
+
+class FirstRunActor;
+
+// WebUI controller for first-run tutorial.
+class FirstRunUI : public content::WebUIController {
+ public:
+ explicit FirstRunUI(content::WebUI* web_ui);
+ FirstRunActor* get_actor() { return actor_; }
+ private:
+ FirstRunActor* actor_;
+
+ DISALLOW_COPY_AND_ASSIGN(FirstRunUI);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_FIRST_RUN_FIRST_RUN_UI_H_
+
diff --git a/chromium/chrome/browser/ui/webui/chromeos/image_source.cc b/chromium/chrome/browser/ui/webui/chromeos/image_source.cc
new file mode 100644
index 00000000000..743d22e8c67
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/image_source.cc
@@ -0,0 +1,125 @@
+// 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/ui/webui/chromeos/image_source.h"
+
+#include <stddef.h>
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/sequenced_task_runner.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_task_runner_handle.h"
+#include "chrome/browser/chromeos/login/users/avatar/user_image_loader.h"
+#include "chrome/common/url_constants.h"
+#include "components/user_manager/user_image/user_image.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/mime_util.h"
+
+using content::BrowserThread;
+
+namespace chromeos {
+namespace {
+
+const char* kWhitelistedDirectories[] = {
+ "regulatory_labels"
+};
+
+// Callback for user_manager::UserImageLoader.
+void ImageLoaded(
+ const content::URLDataSource::GotDataCallback& got_data_callback,
+ std::unique_ptr<user_manager::UserImage> user_image) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if (user_image->has_image_bytes())
+ got_data_callback.Run(user_image->image_bytes());
+ else
+ got_data_callback.Run(nullptr);
+}
+
+} // namespace
+
+ImageSource::ImageSource() : weak_factory_(this) {
+ base::SequencedWorkerPool* blocking_pool =
+ BrowserThread::GetBlockingPool();
+ task_runner_ = blocking_pool->GetSequencedTaskRunnerWithShutdownBehavior(
+ blocking_pool->GetSequenceToken(),
+ base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
+}
+
+ImageSource::~ImageSource() {
+}
+
+std::string ImageSource::GetSource() const {
+ return chrome::kChromeOSAssetHost;
+}
+
+void ImageSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& got_data_callback) {
+ if (!IsWhitelisted(path)) {
+ got_data_callback.Run(nullptr);
+ return;
+ }
+
+ const base::FilePath asset_dir(FILE_PATH_LITERAL(chrome::kChromeOSAssetPath));
+ const base::FilePath image_path = asset_dir.AppendASCII(path);
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+ base::Bind(&base::PathExists, image_path),
+ base::Bind(&ImageSource::StartDataRequestAfterPathExists,
+ weak_factory_.GetWeakPtr(), image_path, got_data_callback));
+}
+
+void ImageSource::StartDataRequestAfterPathExists(
+ const base::FilePath& image_path,
+ const content::URLDataSource::GotDataCallback& got_data_callback,
+ bool path_exists) {
+ if (path_exists) {
+ user_image_loader::StartWithFilePath(
+ task_runner_,
+ image_path,
+ ImageDecoder::DEFAULT_CODEC,
+ 0, // Do not crop.
+ base::Bind(&ImageLoaded, got_data_callback));
+ } else {
+ got_data_callback.Run(nullptr);
+ }
+}
+
+std::string ImageSource::GetMimeType(const std::string& path) const {
+ std::string mime_type;
+ std::string ext = base::FilePath(path).Extension();
+ if (!ext.empty())
+ net::GetWellKnownMimeTypeFromExtension(ext.substr(1), &mime_type);
+ return mime_type;
+}
+
+bool ImageSource::IsWhitelisted(const std::string& path) const {
+ base::FilePath file_path(path);
+ if (file_path.ReferencesParent())
+ return false;
+
+ // Check if the path starts with a whitelisted directory.
+ std::vector<std::string> components;
+ file_path.GetComponents(&components);
+ if (components.empty())
+ return false;
+
+ for (size_t i = 0; i < arraysize(kWhitelistedDirectories); i++) {
+ if (components[0] == kWhitelistedDirectories[i])
+ return true;
+ }
+ return false;
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/image_source.h b/chromium/chrome/browser/ui/webui/chromeos/image_source.h
new file mode 100644
index 00000000000..94f0deda1be
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/image_source.h
@@ -0,0 +1,58 @@
+// 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_UI_WEBUI_CHROMEOS_IMAGE_SOURCE_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_IMAGE_SOURCE_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/url_data_source.h"
+
+namespace base {
+class SequencedTaskRunner;
+}
+
+namespace chromeos {
+
+// Data source that reads and decodes an image from the RO file system.
+class ImageSource : public content::URLDataSource {
+ public:
+ ImageSource();
+ ~ImageSource() override;
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& got_data_callback)
+ override;
+
+ std::string GetMimeType(const std::string& path) const override;
+
+ private:
+ // Continuation from StartDataRequest().
+ void StartDataRequestAfterPathExists(
+ const base::FilePath& image_path,
+ const content::URLDataSource::GotDataCallback& got_data_callback,
+ bool path_exists);
+
+ // Checks whether we have allowed the image to be loaded.
+ bool IsWhitelisted(const std::string& path) const;
+
+ // The background task runner on which file I/O and image decoding are done.
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+ base::WeakPtrFactory<ImageSource> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageSource);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_IMAGE_SOURCE_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
new file mode 100644
index 00000000000..ae29d008423
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc
@@ -0,0 +1,436 @@
+// 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/ui/webui/chromeos/keyboard_overlay_ui.h"
+
+#include <stddef.h>
+
+#include "ash/shell.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/feature_list.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/page_navigator.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "ui/base/ime/chromeos/ime_keyboard.h"
+#include "ui/base/ime/chromeos/input_method_manager.h"
+#include "ui/chromeos/events/pref_names.h"
+#include "ui/display/manager/display_manager.h"
+
+using chromeos::input_method::ModifierKey;
+using content::WebUIMessageHandler;
+using ui::WebDialogUI;
+
+namespace {
+
+const char kLearnMoreURL[] =
+#if defined(OFFICIAL_BUILD)
+ "chrome-extension://honijodknafkokifofgiaalefdiedpko/"
+ "main.html?answer=1047364";
+#else
+ "https://support.google.com/chromebook/answer/183101";
+#endif
+
+struct ModifierToLabel {
+ const ModifierKey modifier;
+ const char* label;
+} kModifierToLabels[] = {
+ {chromeos::input_method::kSearchKey, "search"},
+ {chromeos::input_method::kControlKey, "ctrl"},
+ {chromeos::input_method::kAltKey, "alt"},
+ {chromeos::input_method::kVoidKey, "disabled"},
+ {chromeos::input_method::kCapsLockKey, "caps lock"},
+ {chromeos::input_method::kEscapeKey, "esc"},
+ {chromeos::input_method::kBackspaceKey, "backspace"},
+};
+
+struct I18nContentToMessage {
+ const char* i18n_content;
+ int message;
+} kI18nContentToMessage[] = {
+ {"keyboardOverlayLearnMore", IDS_KEYBOARD_OVERLAY_LEARN_MORE},
+ {"keyboardOverlayTitle", IDS_KEYBOARD_OVERLAY_TITLE},
+ {"keyboardOverlayEscKeyLabel", IDS_KEYBOARD_OVERLAY_ESC_KEY_LABEL},
+ {"keyboardOverlayBackKeyLabel", IDS_KEYBOARD_OVERLAY_BACK_KEY_LABEL},
+ {"keyboardOverlayForwardKeyLabel", IDS_KEYBOARD_OVERLAY_FORWARD_KEY_LABEL},
+ {"keyboardOverlayReloadKeyLabel", IDS_KEYBOARD_OVERLAY_RELOAD_KEY_LABEL},
+ {"keyboardOverlayFullScreenKeyLabel",
+ IDS_KEYBOARD_OVERLAY_FULL_SCREEN_KEY_LABEL},
+ {"keyboardOverlaySwitchWinKeyLabel",
+ IDS_KEYBOARD_OVERLAY_SWITCH_WIN_KEY_LABEL},
+ {"keyboardOverlayBrightDownKeyLabel",
+ IDS_KEYBOARD_OVERLAY_BRIGHT_DOWN_KEY_LABEL},
+ {"keyboardOverlayBrightUpKeyLabel",
+ IDS_KEYBOARD_OVERLAY_BRIGHT_UP_KEY_LABEL},
+ {"keyboardOverlayMuteKeyLabel", IDS_KEYBOARD_OVERLAY_MUTE_KEY_LABEL},
+ {"keyboardOverlayVolDownKeyLabel", IDS_KEYBOARD_OVERLAY_VOL_DOWN_KEY_LABEL},
+ {"keyboardOverlayVolUpKeyLabel", IDS_KEYBOARD_OVERLAY_VOL_UP_KEY_LABEL},
+ {"keyboardOverlayPowerKeyLabel", IDS_KEYBOARD_OVERLAY_POWER_KEY_LABEL},
+ {"keyboardOverlayBackspaceKeyLabel",
+ IDS_KEYBOARD_OVERLAY_BACKSPACE_KEY_LABEL},
+ {"keyboardOverlayTabKeyLabel", IDS_KEYBOARD_OVERLAY_TAB_KEY_LABEL},
+ {"keyboardOverlaySearchKeyLabel", IDS_KEYBOARD_OVERLAY_SEARCH_KEY_LABEL},
+ {"keyboardOverlayEnterKeyLabel", IDS_KEYBOARD_OVERLAY_ENTER_KEY_LABEL},
+ {"keyboardOverlayShiftKeyLabel", IDS_KEYBOARD_OVERLAY_SHIFT_KEY_LABEL},
+ {"keyboardOverlayCtrlKeyLabel", IDS_KEYBOARD_OVERLAY_CTRL_KEY_LABEL},
+ {"keyboardOverlayAltKeyLabel", IDS_KEYBOARD_OVERLAY_ALT_KEY_LABEL},
+ {"keyboardOverlayLeftKeyLabel", IDS_KEYBOARD_OVERLAY_LEFT_KEY_LABEL},
+ {"keyboardOverlayRightKeyLabel", IDS_KEYBOARD_OVERLAY_RIGHT_KEY_LABEL},
+ {"keyboardOverlayUpKeyLabel", IDS_KEYBOARD_OVERLAY_UP_KEY_LABEL},
+ {"keyboardOverlayDownKeyLabel", IDS_KEYBOARD_OVERLAY_DOWN_KEY_LABEL},
+ {"keyboardOverlayInstructions", IDS_KEYBOARD_OVERLAY_INSTRUCTIONS},
+ {"keyboardOverlayInstructionsHide", IDS_KEYBOARD_OVERLAY_INSTRUCTIONS_HIDE},
+ {"keyboardOverlayActivateLastShelfItem",
+ IDS_KEYBOARD_OVERLAY_ACTIVATE_LAST_SHELF_ITEM},
+ {"keyboardOverlayActivateLastTab", IDS_KEYBOARD_OVERLAY_ACTIVATE_LAST_TAB},
+ {"keyboardOverlayActivateShelfItem1",
+ IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_1},
+ {"keyboardOverlayActivateShelfItem2",
+ IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_2},
+ {"keyboardOverlayActivateShelfItem3",
+ IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_3},
+ {"keyboardOverlayActivateShelfItem4",
+ IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_4},
+ {"keyboardOverlayActivateShelfItem5",
+ IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_5},
+ {"keyboardOverlayActivateShelfItem6",
+ IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_6},
+ {"keyboardOverlayActivateShelfItem7",
+ IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_7},
+ {"keyboardOverlayActivateShelfItem8",
+ IDS_KEYBOARD_OVERLAY_ACTIVATE_SHELF_ITEM_8},
+ {"keyboardOverlayActivateNextTab", IDS_KEYBOARD_OVERLAY_ACTIVATE_NEXT_TAB},
+ {"keyboardOverlayActivatePreviousTab",
+ IDS_KEYBOARD_OVERLAY_ACTIVATE_PREVIOUS_TAB},
+ {"keyboardOverlayActivateTab1", IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_1},
+ {"keyboardOverlayActivateTab2", IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_2},
+ {"keyboardOverlayActivateTab3", IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_3},
+ {"keyboardOverlayActivateTab4", IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_4},
+ {"keyboardOverlayActivateTab5", IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_5},
+ {"keyboardOverlayActivateTab6", IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_6},
+ {"keyboardOverlayActivateTab7", IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_7},
+ {"keyboardOverlayActivateTab8", IDS_KEYBOARD_OVERLAY_ACTIVATE_TAB_8},
+ {"keyboardOverlayAddWwwAndComAndOpenAddress",
+ IDS_KEYBOARD_OVERLAY_ADD_WWW_AND_COM_AND_OPEN_ADDRESS},
+ {"keyboardOverlayBookmarkAllTabs", IDS_KEYBOARD_OVERLAY_BOOKMARK_ALL_TABS},
+ {"keyboardOverlayBookmarkCurrentPage",
+ IDS_KEYBOARD_OVERLAY_BOOKMARK_CURRENT_PAGE},
+ {"keyboardOverlayBookmarkManager", IDS_KEYBOARD_OVERLAY_BOOKMARK_MANAGER},
+ {"keyboardOverlayCenterWindow", IDS_KEYBOARD_OVERLAY_CENTER_WINDOW},
+ {"keyboardOverlayClearBrowsingDataDialog",
+ IDS_KEYBOARD_OVERLAY_CLEAR_BROWSING_DATA_DIALOG},
+ {"keyboardOverlayCloseTab", IDS_KEYBOARD_OVERLAY_CLOSE_TAB},
+ {"keyboardOverlayCloseWindow", IDS_KEYBOARD_OVERLAY_CLOSE_WINDOW},
+ {"keyboardOverlayContextMenu", IDS_KEYBOARD_OVERLAY_CONTEXT_MENU},
+ {"keyboardOverlayCopy", IDS_KEYBOARD_OVERLAY_COPY},
+ {"keyboardOverlayCut", IDS_KEYBOARD_OVERLAY_CUT},
+ {"keyboardOverlayCycleThroughInputMethods",
+ IDS_KEYBOARD_OVERLAY_CYCLE_THROUGH_INPUT_METHODS},
+ {"keyboardOverlayDecreaseKeyBrightness",
+ IDS_KEYBOARD_OVERLAY_DECREASE_KEY_BRIGHTNESS},
+ {"keyboardOverlayDelete", IDS_KEYBOARD_OVERLAY_DELETE},
+ {"keyboardOverlayDeleteWord", IDS_KEYBOARD_OVERLAY_DELETE_WORD},
+ {"keyboardOverlayDeveloperTools", IDS_KEYBOARD_OVERLAY_DEVELOPER_TOOLS},
+ {"keyboardOverlayDockWindowLeft", IDS_KEYBOARD_OVERLAY_DOCK_WINDOW_LEFT},
+ {"keyboardOverlayDockWindowRight", IDS_KEYBOARD_OVERLAY_DOCK_WINDOW_RIGHT},
+ {"keyboardOverlayDomInspector", IDS_KEYBOARD_OVERLAY_DOM_INSPECTOR},
+ {"keyboardOverlayDownloads", IDS_KEYBOARD_OVERLAY_DOWNLOADS},
+ {"keyboardOverlayEnd", IDS_KEYBOARD_OVERLAY_END},
+ {"keyboardOverlayF1", IDS_KEYBOARD_OVERLAY_F1},
+ {"keyboardOverlayF10", IDS_KEYBOARD_OVERLAY_F10},
+ {"keyboardOverlayF11", IDS_KEYBOARD_OVERLAY_F11},
+ {"keyboardOverlayF12", IDS_KEYBOARD_OVERLAY_F12},
+ {"keyboardOverlayF2", IDS_KEYBOARD_OVERLAY_F2},
+ {"keyboardOverlayF3", IDS_KEYBOARD_OVERLAY_F3},
+ {"keyboardOverlayF4", IDS_KEYBOARD_OVERLAY_F4},
+ {"keyboardOverlayF5", IDS_KEYBOARD_OVERLAY_F5},
+ {"keyboardOverlayF6", IDS_KEYBOARD_OVERLAY_F6},
+ {"keyboardOverlayF7", IDS_KEYBOARD_OVERLAY_F7},
+ {"keyboardOverlayF8", IDS_KEYBOARD_OVERLAY_F8},
+ {"keyboardOverlayF9", IDS_KEYBOARD_OVERLAY_F9},
+ {"keyboardOverlayFindPreviousText",
+ IDS_KEYBOARD_OVERLAY_FIND_PREVIOUS_TEXT},
+ {"keyboardOverlayFindText", IDS_KEYBOARD_OVERLAY_FIND_TEXT},
+ {"keyboardOverlayFindTextAgain", IDS_KEYBOARD_OVERLAY_FIND_TEXT_AGAIN},
+ {"keyboardOverlayFocusAddressBar", IDS_KEYBOARD_OVERLAY_FOCUS_ADDRESS_BAR},
+ {"keyboardOverlayFocusAddressBarInSearchMode",
+ IDS_KEYBOARD_OVERLAY_FOCUS_ADDRESS_BAR_IN_SEARCH_MODE},
+ {"keyboardOverlayFocusBookmarks", IDS_KEYBOARD_OVERLAY_FOCUS_BOOKMARKS},
+ {"keyboardOverlayFocusShelf", IDS_KEYBOARD_OVERLAY_FOCUS_SHELF},
+ {"keyboardOverlayFocusNextPane", IDS_KEYBOARD_OVERLAY_FOCUS_NEXT_PANE},
+ {"keyboardOverlayFocusPreviousPane",
+ IDS_KEYBOARD_OVERLAY_FOCUS_PREVIOUS_PANE},
+ {"keyboardOverlayFocusToolbar", IDS_KEYBOARD_OVERLAY_FOCUS_TOOLBAR},
+ {"keyboardOverlayGoBack", IDS_KEYBOARD_OVERLAY_GO_BACK},
+ {"keyboardOverlayGoForward", IDS_KEYBOARD_OVERLAY_GO_FORWARD},
+ {"keyboardOverlayHelp", IDS_KEYBOARD_OVERLAY_HELP},
+ {"keyboardOverlayHistory", IDS_KEYBOARD_OVERLAY_HISTORY},
+ {"keyboardOverlayHome", IDS_KEYBOARD_OVERLAY_HOME},
+ {"keyboardOverlayIncreaseKeyBrightness",
+ IDS_KEYBOARD_OVERLAY_INCREASE_KEY_BRIGHTNESS},
+ {"keyboardOverlayInputUnicodeCharacters",
+ IDS_KEYBOARD_OVERLAY_INPUT_UNICODE_CHARACTERS},
+ {"keyboardOverlayInsert", IDS_KEYBOARD_OVERLAY_INSERT},
+ {"keyboardOverlayJavascriptConsole",
+ IDS_KEYBOARD_OVERLAY_JAVASCRIPT_CONSOLE},
+ {"keyboardOverlayLockScreen", IDS_KEYBOARD_OVERLAY_LOCK_SCREEN},
+ {"keyboardOverlayLockScreenOrPowerOff",
+ IDS_KEYBOARD_OVERLAY_LOCK_SCREEN_OR_POWER_OFF},
+ {"keyboardOverlayMagnifierDecreaseZoom",
+ IDS_KEYBOARD_OVERLAY_MAGNIFIER_DECREASE_ZOOM},
+ {"keyboardOverlayMagnifierIncreaseZoom",
+ IDS_KEYBOARD_OVERLAY_MAGNIFIER_INCREASE_ZOOM},
+ {"keyboardOverlayMaximizeWindow", IDS_KEYBOARD_OVERLAY_MAXIMIZE_WINDOW},
+ {"keyboardOverlayMinimizeWindow", IDS_KEYBOARD_OVERLAY_MINIMIZE_WINDOW},
+ {"keyboardOverlayMirrorMonitors", IDS_KEYBOARD_OVERLAY_MIRROR_MONITORS},
+ {"keyboardOverlayNewIncognitoWindow",
+ IDS_KEYBOARD_OVERLAY_NEW_INCOGNITO_WINDOW},
+ {"keyboardOverlayNewTab", IDS_KEYBOARD_OVERLAY_NEW_TAB},
+ {"keyboardOverlayNewTerminal", IDS_KEYBOARD_OVERLAY_NEW_TERMINAL},
+ {"keyboardOverlayNewWindow", IDS_KEYBOARD_OVERLAY_NEW_WINDOW},
+ {"keyboardOverlayNextUser", IDS_KEYBOARD_OVERLAY_NEXT_USER},
+ {"keyboardOverlayNextWindow", IDS_KEYBOARD_OVERLAY_NEXT_WINDOW},
+ {"keyboardOverlayNextWord", IDS_KEYBOARD_OVERLAY_NEXT_WORD},
+ {"keyboardOverlayOpen", IDS_KEYBOARD_OVERLAY_OPEN},
+ {"keyboardOverlayOpenAddressInNewTab",
+ IDS_KEYBOARD_OVERLAY_OPEN_ADDRESS_IN_NEW_TAB},
+ {"keyboardOverlayOpenFileManager", IDS_KEYBOARD_OVERLAY_OPEN_FILE_MANAGER},
+ {"keyboardOverlayPageDown", IDS_KEYBOARD_OVERLAY_PAGE_DOWN},
+ {"keyboardOverlayPageUp", IDS_KEYBOARD_OVERLAY_PAGE_UP},
+ {"keyboardOverlayPaste", IDS_KEYBOARD_OVERLAY_PASTE},
+ {"keyboardOverlayPasteAsPlainText",
+ IDS_KEYBOARD_OVERLAY_PASTE_AS_PLAIN_TEXT},
+ {"keyboardOverlayPreviousUser", IDS_KEYBOARD_OVERLAY_PREVIOUS_USER},
+ {"keyboardOverlayPreviousWindow", IDS_KEYBOARD_OVERLAY_PREVIOUS_WINDOW},
+ {"keyboardOverlayPreviousWord", IDS_KEYBOARD_OVERLAY_PREVIOUS_WORD},
+ {"keyboardOverlayPrint", IDS_KEYBOARD_OVERLAY_PRINT},
+ {"keyboardOverlayReloadCurrentPage",
+ IDS_KEYBOARD_OVERLAY_RELOAD_CURRENT_PAGE},
+ {"keyboardOverlayReloadBypassingCache",
+ IDS_KEYBOARD_OVERLAY_RELOAD_BYPASSING_CACHE},
+ {"keyboardOverlayReopenLastClosedTab",
+ IDS_KEYBOARD_OVERLAY_REOPEN_LAST_CLOSED_TAB},
+ {"keyboardOverlayReportIssue", IDS_KEYBOARD_OVERLAY_REPORT_ISSUE},
+ {"keyboardOverlayResetScreenZoom", IDS_KEYBOARD_OVERLAY_RESET_SCREEN_ZOOM},
+ {"keyboardOverlayResetZoom", IDS_KEYBOARD_OVERLAY_RESET_ZOOM},
+ {"keyboardOverlayRotateScreen", IDS_KEYBOARD_OVERLAY_ROTATE_SCREEN},
+ {"keyboardOverlayRotateWindow", IDS_KEYBOARD_OVERLAY_ROTATE_WINDOW},
+ {"keyboardOverlaySave", IDS_KEYBOARD_OVERLAY_SAVE},
+ {"keyboardOverlayScreenshotRegion", IDS_KEYBOARD_OVERLAY_SCREENSHOT_REGION},
+ {"keyboardOverlayScreenshotWindow", IDS_KEYBOARD_OVERLAY_SCREENSHOT_WINDOW},
+ {"keyboardOverlayScrollUpOnePage", IDS_KEYBOARD_OVERLAY_SCROLL_UP_ONE_PAGE},
+ {"keyboardOverlaySelectAll", IDS_KEYBOARD_OVERLAY_SELECT_ALL},
+ {"keyboardOverlaySelectPreviousInputMethod",
+ IDS_KEYBOARD_OVERLAY_SELECT_PREVIOUS_INPUT_METHOD},
+ {"keyboardOverlaySelectWordAtATime",
+ IDS_KEYBOARD_OVERLAY_SELECT_WORD_AT_A_TIME},
+ {"keyboardOverlayShowImeBubble", IDS_KEYBOARD_OVERLAY_SHOW_IME_BUBBLE},
+ {"keyboardOverlayShowMessageCenter",
+ IDS_KEYBOARD_OVERLAY_SHOW_MESSAGE_CENTER},
+ {"keyboardOverlayShowStatusMenu", IDS_KEYBOARD_OVERLAY_SHOW_STATUS_MENU},
+ {"keyboardOverlayShowStylusTools", IDS_KEYBOARD_OVERLAY_SHOW_STYLUS_TOOLS},
+ {"keyboardOverlayShowWrenchMenu", IDS_KEYBOARD_OVERLAY_SHOW_WRENCH_MENU},
+ {"keyboardOverlaySignOut", IDS_KEYBOARD_OVERLAY_SIGN_OUT},
+ {"keyboardOverlaySuspend", IDS_KEYBOARD_OVERLAY_SUSPEND},
+ {"keyboardOverlaySwapPrimaryMonitor",
+ IDS_KEYBOARD_OVERLAY_SWAP_PRIMARY_MONITOR},
+ {"keyboardOverlayTakeScreenshot", IDS_KEYBOARD_OVERLAY_TAKE_SCREENSHOT},
+ {"keyboardOverlayTaskManager", IDS_KEYBOARD_OVERLAY_TASK_MANAGER},
+ {"keyboardOverlayToggleBookmarkBar",
+ IDS_KEYBOARD_OVERLAY_TOGGLE_BOOKMARK_BAR},
+ {"keyboardOverlayToggleCapsLock", IDS_KEYBOARD_OVERLAY_TOGGLE_CAPS_LOCK},
+ {"keyboardOverlayDisableCapsLock", IDS_KEYBOARD_OVERLAY_DISABLE_CAPS_LOCK},
+ {"keyboardOverlayToggleChromevoxSpokenFeedback",
+ IDS_KEYBOARD_OVERLAY_TOGGLE_CHROMEVOX_SPOKEN_FEEDBACK},
+ {"keyboardOverlayToggleHighContrastMode",
+ IDS_KEYBOARD_OVERLAY_TOGGLE_HIGH_CONTRAST_MODE},
+ {"keyboardOverlayToggleProjectionTouchHud",
+ IDS_KEYBOARD_OVERLAY_TOGGLE_PROJECTION_TOUCH_HUD},
+ {"keyboardOverlayTouchHudModeChange",
+ IDS_KEYBOARD_OVERLAY_TOUCH_HUD_MODE_CHANGE},
+ {"keyboardOverlayUndo", IDS_KEYBOARD_OVERLAY_UNDO},
+ {"keyboardOverlayUnpin", IDS_KEYBOARD_OVERLAY_UNPIN},
+ {"keyboardOverlayViewKeyboardOverlay",
+ IDS_KEYBOARD_OVERLAY_VIEW_KEYBOARD_OVERLAY},
+ {"keyboardOverlayViewSource", IDS_KEYBOARD_OVERLAY_VIEW_SOURCE},
+ {"keyboardOverlayWordMove", IDS_KEYBOARD_OVERLAY_WORD_MOVE},
+ {"keyboardOverlayZoomIn", IDS_KEYBOARD_OVERLAY_ZOOM_IN},
+ {"keyboardOverlayZoomOut", IDS_KEYBOARD_OVERLAY_ZOOM_OUT},
+ {"keyboardOverlayZoomScreenIn", IDS_KEYBOARD_OVERLAY_ZOOM_SCREEN_IN},
+ {"keyboardOverlayZoomScreenOut", IDS_KEYBOARD_OVERLAY_ZOOM_SCREEN_OUT},
+};
+
+bool TopRowKeysAreFunctionKeys(Profile* profile) {
+ if (!profile)
+ return false;
+
+ const PrefService* prefs = profile->GetPrefs();
+ return prefs ? prefs->GetBoolean(prefs::kLanguageSendFunctionKeys) : false;
+}
+
+std::string ModifierKeyToLabel(ModifierKey modifier) {
+ for (size_t i = 0; i < arraysize(kModifierToLabels); ++i) {
+ if (modifier == kModifierToLabels[i].modifier) {
+ return kModifierToLabels[i].label;
+ }
+ }
+ return "";
+}
+
+content::WebUIDataSource* CreateKeyboardOverlayUIHTMLSource(Profile* profile) {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIKeyboardOverlayHost);
+
+ for (size_t i = 0; i < arraysize(kI18nContentToMessage); ++i) {
+ source->AddLocalizedString(kI18nContentToMessage[i].i18n_content,
+ kI18nContentToMessage[i].message);
+ }
+
+ source->AddString("keyboardOverlayLearnMoreURL",
+ base::UTF8ToUTF16(kLearnMoreURL));
+ source->AddBoolean("keyboardOverlayHasChromeOSDiamondKey",
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kHasChromeOSDiamondKey));
+ source->AddBoolean("keyboardOverlayTopRowKeysAreFunctionKeys",
+ TopRowKeysAreFunctionKeys(profile));
+ ash::Shell* shell = ash::Shell::Get();
+ display::DisplayManager* display_manager = shell->display_manager();
+ source->AddBoolean("keyboardOverlayIsDisplayUIScalingEnabled",
+ display_manager->IsDisplayUIScalingEnabled());
+ source->AddBoolean(
+ "backspaceGoesBackFeatureEnabled",
+ base::FeatureList::IsEnabled(features::kBackspaceGoesBackFeature));
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("keyboard_overlay.js", IDR_KEYBOARD_OVERLAY_JS);
+ source->SetDefaultResource(IDR_KEYBOARD_OVERLAY_HTML);
+ return source;
+}
+
+} // namespace
+
+// The handler for Javascript messages related to the "keyboardoverlay" view.
+class KeyboardOverlayHandler
+ : public WebUIMessageHandler,
+ public base::SupportsWeakPtr<KeyboardOverlayHandler> {
+ public:
+ explicit KeyboardOverlayHandler(Profile* profile);
+ ~KeyboardOverlayHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ // Called when the page requires the input method ID corresponding to the
+ // current input method or keyboard layout during initialization.
+ void GetInputMethodId(const base::ListValue* args);
+
+ // Called when the page requres the information of modifier key remapping
+ // during the initialization.
+ void GetLabelMap(const base::ListValue* args);
+
+ // Called when the learn more link is clicked.
+ void OpenLearnMorePage(const base::ListValue* args);
+
+ Profile* profile_;
+
+ DISALLOW_COPY_AND_ASSIGN(KeyboardOverlayHandler);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// KeyboardOverlayHandler
+//
+////////////////////////////////////////////////////////////////////////////////
+KeyboardOverlayHandler::KeyboardOverlayHandler(Profile* profile)
+ : profile_(profile) {
+}
+
+KeyboardOverlayHandler::~KeyboardOverlayHandler() {
+}
+
+void KeyboardOverlayHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("getInputMethodId",
+ base::Bind(&KeyboardOverlayHandler::GetInputMethodId,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("getLabelMap",
+ base::Bind(&KeyboardOverlayHandler::GetLabelMap,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("openLearnMorePage",
+ base::Bind(&KeyboardOverlayHandler::OpenLearnMorePage,
+ base::Unretained(this)));
+}
+
+void KeyboardOverlayHandler::GetInputMethodId(const base::ListValue* args) {
+ chromeos::input_method::InputMethodManager* manager =
+ chromeos::input_method::InputMethodManager::Get();
+ const chromeos::input_method::InputMethodDescriptor& descriptor =
+ manager->GetActiveIMEState()->GetCurrentInputMethod();
+ base::Value param(descriptor.id());
+ web_ui()->CallJavascriptFunctionUnsafe("initKeyboardOverlayId", param);
+}
+
+void KeyboardOverlayHandler::GetLabelMap(const base::ListValue* args) {
+ DCHECK(profile_);
+ PrefService* pref_service = profile_->GetPrefs();
+ typedef std::map<ModifierKey, ModifierKey> ModifierMap;
+ ModifierMap modifier_map;
+ modifier_map[chromeos::input_method::kSearchKey] = static_cast<ModifierKey>(
+ pref_service->GetInteger(prefs::kLanguageRemapSearchKeyTo));
+ modifier_map[chromeos::input_method::kControlKey] = static_cast<ModifierKey>(
+ pref_service->GetInteger(prefs::kLanguageRemapControlKeyTo));
+ modifier_map[chromeos::input_method::kAltKey] = static_cast<ModifierKey>(
+ pref_service->GetInteger(prefs::kLanguageRemapAltKeyTo));
+ // TODO(mazda): Support prefs::kLanguageRemapCapsLockKeyTo once Caps Lock is
+ // added to the overlay UI.
+
+ base::DictionaryValue dict;
+ for (ModifierMap::const_iterator i = modifier_map.begin();
+ i != modifier_map.end(); ++i) {
+ dict.SetString(ModifierKeyToLabel(i->first), ModifierKeyToLabel(i->second));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("initIdentifierMap", dict);
+}
+
+void KeyboardOverlayHandler::OpenLearnMorePage(const base::ListValue* args) {
+ web_ui()->GetWebContents()->GetDelegate()->OpenURLFromTab(
+ web_ui()->GetWebContents(),
+ content::OpenURLParams(GURL(kLearnMoreURL), content::Referrer(),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui::PAGE_TRANSITION_LINK, false));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// KeyboardOverlayUI
+//
+////////////////////////////////////////////////////////////////////////////////
+
+KeyboardOverlayUI::KeyboardOverlayUI(content::WebUI* web_ui)
+ : WebDialogUI(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ web_ui->AddMessageHandler(base::MakeUnique<KeyboardOverlayHandler>(profile));
+
+ // Set up the chrome://keyboardoverlay/ source.
+ content::WebUIDataSource::Add(profile,
+ CreateKeyboardOverlayUIHTMLSource(profile));
+}
diff --git a/chromium/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.h b/chromium/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.h
new file mode 100644
index 00000000000..002dad9c8dd
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.h
@@ -0,0 +1,19 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_KEYBOARD_OVERLAY_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_KEYBOARD_OVERLAY_UI_H_
+
+#include "base/macros.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+class KeyboardOverlayUI : public ui::WebDialogUI {
+ public:
+ explicit KeyboardOverlayUI(content::WebUI* web_ui);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(KeyboardOverlayUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_KEYBOARD_OVERLAY_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui_browsertest.cc
new file mode 100644
index 00000000000..bbf48ed272a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui_browsertest.cc
@@ -0,0 +1,176 @@
+// 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 "ash/accelerators/accelerator_table.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.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 "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "content/public/test/browser_test_utils.h"
+#include "ui/events/keycodes/keyboard_code_conversion.h"
+
+namespace {
+
+class TestWebUIMessageHandler : public content::WebUIMessageHandler {
+ public:
+ TestWebUIMessageHandler() = default;
+ ~TestWebUIMessageHandler() override = default;
+
+ // content::WebUIMessageHandler:
+ void RegisterMessages() override {
+ web_ui()->RegisterMessageCallback(
+ "didPaint", base::Bind(&TestWebUIMessageHandler::HandleDidPaint,
+ base::Unretained(this)));
+ }
+
+ private:
+ void HandleDidPaint(const base::ListValue*) {}
+
+ DISALLOW_COPY_AND_ASSIGN(TestWebUIMessageHandler);
+};
+
+content::WebContents* StartKeyboardOverlayUI(Browser* browser) {
+ ui_test_utils::NavigateToURL(browser,
+ GURL(chrome::kChromeUIKeyboardOverlayURL));
+ content::WebContents* web_contents =
+ browser->tab_strip_model()->GetActiveWebContents();
+ web_contents->GetWebUI()->AddMessageHandler(
+ base::MakeUnique<TestWebUIMessageHandler>());
+ return web_contents;
+}
+
+bool IsDisplayUIScalingEnabled(content::WebContents* web_contents) {
+ bool is_display_ui_scaling_enabled;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+ web_contents,
+ "domAutomationController.send(isDisplayUIScalingEnabled());",
+ &is_display_ui_scaling_enabled));
+ return is_display_ui_scaling_enabled;
+}
+
+// Skip some accelerators in the tests:
+// 1. If the accelerator has no modifier, i.e. ui::EF_NONE, or for "Caps
+// Lock", such as ui::VKEY_MENU and ui::VKEY_LWIN, the logic to show it on
+// the keyboard overlay is not by the mapping of
+// keyboardOverlayData['shortcut'], so it can not be tested by this test.
+// 2. If it has debug modifiers: ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN |
+// ui::EF_SHIFT_DOWN
+// 3. If the shortcut action is device specific so it should not be shown
+// on the keyboard overlay, i.e. START_VOICE_INTERACTION.
+bool ShouldSkip(const ash::AcceleratorData& accelerator) {
+ return accelerator.keycode == ui::VKEY_MENU ||
+ accelerator.keycode == ui::VKEY_LWIN ||
+ accelerator.modifiers == ui::EF_NONE ||
+ accelerator.modifiers ==
+ (ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN) ||
+ accelerator.action == ash::START_VOICE_INTERACTION;
+}
+
+std::string KeyboardCodeToLabel(const ash::AcceleratorData& accelerator,
+ content::WebContents* web_contents) {
+ std::string label;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractString(
+ web_contents,
+ "domAutomationController.send("
+ " (function(number) {"
+ " if (!!KEYCODE_TO_LABEL[number]) {"
+ " return KEYCODE_TO_LABEL[number];"
+ " } else {"
+ " return 'NONE';"
+ " }"
+ " })(" +
+ std::to_string(static_cast<unsigned int>(accelerator.keycode)) +
+ " )"
+ ");",
+ &label));
+ if (label == "NONE") {
+ label = base::ToLowerASCII(static_cast<char>(
+ LocatedToNonLocatedKeyboardCode(accelerator.keycode)));
+ }
+ return label;
+}
+
+std::string GenerateShortcutKey(const ash::AcceleratorData& accelerator,
+ content::WebContents* web_contents) {
+ std::string shortcut = KeyboardCodeToLabel(accelerator, web_contents);
+ // The order of the "if" conditions should not be changed because the
+ // modifiers are expected to be alphabetical sorted in the generated
+ // shortcut.
+ if (accelerator.modifiers & ui::EF_ALT_DOWN)
+ shortcut.append("<>ALT");
+ if (accelerator.modifiers & ui::EF_CONTROL_DOWN)
+ shortcut.append("<>CTRL");
+ if (accelerator.modifiers & ui::EF_COMMAND_DOWN)
+ shortcut.append("<>SEARCH");
+ if (accelerator.modifiers & ui::EF_SHIFT_DOWN)
+ shortcut.append("<>SHIFT");
+ return shortcut;
+}
+
+bool ContainsShortcut(const std::string& shortcut,
+ content::WebContents* web_contents) {
+ bool contains;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+ web_contents,
+ "domAutomationController.send("
+ " !!keyboardOverlayData['shortcut']['" + shortcut + "']"
+ ");",
+ &contains));
+ return contains;
+}
+
+} // namespace
+
+using KeyboardOverlayUIBrowserTest = InProcessBrowserTest;
+
+IN_PROC_BROWSER_TEST_F(KeyboardOverlayUIBrowserTest,
+ AcceleratorsShouldHaveKeyboardOverlay) {
+ content::WebContents* const web_contents = StartKeyboardOverlayUI(browser());
+ const bool is_display_ui_scaling_enabled =
+ IsDisplayUIScalingEnabled(web_contents);
+ for (size_t i = 0; i < ash::kAcceleratorDataLength; ++i) {
+ const ash::AcceleratorData& entry = ash::kAcceleratorData[i];
+ if (ShouldSkip(entry))
+ continue;
+
+ const std::string shortcut = GenerateShortcutKey(entry, web_contents);
+ if (!is_display_ui_scaling_enabled) {
+ if (shortcut == "-<>CTRL<>SHIFT" || shortcut == "+<>CTRL<>SHIFT" ||
+ shortcut == "0<>CTRL<>SHIFT") {
+ continue;
+ }
+ }
+
+ EXPECT_TRUE(ContainsShortcut(shortcut, web_contents))
+ << "Please add the new accelerators to keyboard "
+ "overlay. Add one entry '" +
+ shortcut +
+ "' in the 'shortcut' section"
+ " at the bottom of the file of "
+ "'/chrome/browser/resources/chromeos/"
+ "keyboard_overlay_data.js'. Please keep it in "
+ "alphabetical order.";
+ }
+}
+
+IN_PROC_BROWSER_TEST_F(KeyboardOverlayUIBrowserTest,
+ DeprecatedAcceleratorsShouldNotHaveKeyboardOverlay) {
+ content::WebContents* const web_contents = StartKeyboardOverlayUI(browser());
+ for (size_t i = 0; i < ash::kDeprecatedAcceleratorsLength; ++i) {
+ const ash::AcceleratorData& entry = ash::kDeprecatedAccelerators[i];
+ if (ShouldSkip(entry))
+ continue;
+
+ const std::string shortcut = GenerateShortcutKey(entry, web_contents);
+ EXPECT_FALSE(ContainsShortcut(shortcut, web_contents))
+ << "Please remove the deprecated accelerator '" + shortcut +
+ "' from the 'shortcut' section"
+ " at the bottom of the file of "
+ "'/chrome/browser/resources/chromeos/"
+ "keyboard_overlay_data.js'.";
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/OWNERS b/chromium/chrome/browser/ui/webui/chromeos/login/OWNERS
new file mode 100644
index 00000000000..e7a8a061b62
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/OWNERS
@@ -0,0 +1,2 @@
+achuith@chromium.org
+alemate@chromium.org
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.cc
new file mode 100644
index 00000000000..3933864bdf0
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.cc
@@ -0,0 +1,285 @@
+// 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/chromeos/login/app_launch_splash_screen_handler.h"
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/browser/chromeos/login/screens/network_error.h"
+#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
+#include "chrome/grit/chrome_unscaled_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "components/login/localized_values_builder.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/web_ui_util.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.AppLaunchSplashScreen";
+
+// Returns network name by service path.
+std::string GetNetworkName(const std::string& service_path) {
+ const chromeos::NetworkState* network =
+ chromeos::NetworkHandler::Get()->network_state_handler()->GetNetworkState(
+ service_path);
+ if (!network)
+ return std::string();
+ return network->name();
+}
+
+} // namespace
+
+namespace chromeos {
+
+AppLaunchSplashScreenHandler::AppLaunchSplashScreenHandler(
+ const scoped_refptr<NetworkStateInformer>& network_state_informer,
+ ErrorScreen* error_screen)
+ : BaseScreenHandler(kScreenId),
+ network_state_informer_(network_state_informer),
+ error_screen_(error_screen) {
+ set_call_js_prefix(kJsScreenPath);
+ network_state_informer_->AddObserver(this);
+}
+
+AppLaunchSplashScreenHandler::~AppLaunchSplashScreenHandler() {
+ network_state_informer_->RemoveObserver(this);
+}
+
+void AppLaunchSplashScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("appStartMessage", IDS_APP_START_NETWORK_WAIT_MESSAGE);
+ builder->Add("configureNetwork", IDS_APP_START_CONFIGURE_NETWORK);
+
+ const base::string16 product_os_name =
+ l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_OS_NAME);
+ builder->Add(
+ "shortcutInfo",
+ l10n_util::GetStringFUTF16(IDS_APP_START_BAILOUT_SHORTCUT_FORMAT,
+ product_os_name));
+
+ builder->Add(
+ "productName",
+ l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_OS_NAME));
+}
+
+void AppLaunchSplashScreenHandler::Initialize() {
+ if (show_on_init_) {
+ show_on_init_ = false;
+ Show(app_id_);
+ }
+}
+
+void AppLaunchSplashScreenHandler::Show(const std::string& app_id) {
+ app_id_ = app_id;
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+
+ base::DictionaryValue data;
+ data.SetBoolean("shortcutEnabled",
+ !KioskAppManager::Get()->GetDisableBailoutShortcut());
+
+ auto app_info = base::MakeUnique<base::DictionaryValue>();
+ PopulateAppInfo(app_info.get());
+ data.Set("appInfo", std::move(app_info));
+
+ SetLaunchText(l10n_util::GetStringUTF8(GetProgressMessageFromState(state_)));
+ ShowScreenWithData(kScreenId, &data);
+}
+
+void AppLaunchSplashScreenHandler::RegisterMessages() {
+ AddCallback("configureNetwork",
+ &AppLaunchSplashScreenHandler::HandleConfigureNetwork);
+ AddCallback("cancelAppLaunch",
+ &AppLaunchSplashScreenHandler::HandleCancelAppLaunch);
+ AddCallback("continueAppLaunch",
+ &AppLaunchSplashScreenHandler::HandleContinueAppLaunch);
+ AddCallback("networkConfigRequest",
+ &AppLaunchSplashScreenHandler::HandleNetworkConfigRequested);
+}
+
+void AppLaunchSplashScreenHandler::Hide() {
+}
+
+void AppLaunchSplashScreenHandler::ToggleNetworkConfig(bool visible) {
+ CallJS("toggleNetworkConfig", visible);
+}
+
+void AppLaunchSplashScreenHandler::UpdateAppLaunchState(AppLaunchState state) {
+ if (state == state_)
+ return;
+
+ state_ = state;
+ if (page_is_ready()) {
+ SetLaunchText(
+ l10n_util::GetStringUTF8(GetProgressMessageFromState(state_)));
+ }
+ UpdateState(NetworkError::ERROR_REASON_UPDATE);
+}
+
+void AppLaunchSplashScreenHandler::SetDelegate(
+ AppLaunchSplashScreenHandler::Delegate* delegate) {
+ delegate_ = delegate;
+}
+
+void AppLaunchSplashScreenHandler::ShowNetworkConfigureUI() {
+ NetworkStateInformer::State state = network_state_informer_->state();
+ if (state == NetworkStateInformer::ONLINE) {
+ online_state_ = true;
+ if (!network_config_requested_) {
+ delegate_->OnNetworkStateChanged(true);
+ return;
+ }
+ }
+
+ const std::string network_path = network_state_informer_->network_path();
+ const std::string network_name = GetNetworkName(network_path);
+
+ error_screen_->SetUIState(NetworkError::UI_STATE_KIOSK_MODE);
+ error_screen_->AllowGuestSignin(false);
+ error_screen_->AllowOfflineLogin(false);
+
+ switch (state) {
+ case NetworkStateInformer::CAPTIVE_PORTAL: {
+ error_screen_->SetErrorState(NetworkError::ERROR_STATE_PORTAL,
+ network_name);
+ error_screen_->FixCaptivePortal();
+
+ break;
+ }
+ case NetworkStateInformer::PROXY_AUTH_REQUIRED: {
+ error_screen_->SetErrorState(NetworkError::ERROR_STATE_PROXY,
+ network_name);
+ break;
+ }
+ case NetworkStateInformer::OFFLINE: {
+ error_screen_->SetErrorState(NetworkError::ERROR_STATE_OFFLINE,
+ network_name);
+ break;
+ }
+ case NetworkStateInformer::ONLINE: {
+ error_screen_->SetErrorState(NetworkError::ERROR_STATE_KIOSK_ONLINE,
+ network_name);
+ break;
+ }
+ default:
+ error_screen_->SetErrorState(NetworkError::ERROR_STATE_OFFLINE,
+ network_name);
+ NOTREACHED();
+ break;
+ }
+
+ if (GetCurrentScreen() != OobeScreen::SCREEN_ERROR_MESSAGE)
+ error_screen_->SetParentScreen(kScreenId);
+ error_screen_->Show();
+}
+
+bool AppLaunchSplashScreenHandler::IsNetworkReady() {
+ return network_state_informer_->state() == NetworkStateInformer::ONLINE;
+}
+
+void AppLaunchSplashScreenHandler::OnNetworkReady() {
+ // Purposely leave blank because the online case is handled in UpdateState
+ // call below.
+}
+
+void AppLaunchSplashScreenHandler::UpdateState(
+ NetworkError::ErrorReason reason) {
+ if (!delegate_ ||
+ (state_ != APP_LAUNCH_STATE_PREPARING_NETWORK &&
+ state_ != APP_LAUNCH_STATE_NETWORK_WAIT_TIMEOUT)) {
+ return;
+ }
+
+ bool new_online_state =
+ network_state_informer_->state() == NetworkStateInformer::ONLINE;
+ delegate_->OnNetworkStateChanged(new_online_state);
+
+ online_state_ = new_online_state;
+}
+
+void AppLaunchSplashScreenHandler::PopulateAppInfo(
+ base::DictionaryValue* out_info) {
+ KioskAppManager::App app;
+ KioskAppManager::Get()->GetApp(app_id_, &app);
+
+ if (app.name.empty())
+ app.name = l10n_util::GetStringUTF8(IDS_SHORT_PRODUCT_NAME);
+
+ if (app.icon.isNull()) {
+ app.icon = *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
+ IDR_PRODUCT_LOGO_128);
+ }
+
+ out_info->SetString("name", app.name);
+ out_info->SetString("iconURL", webui::GetBitmapDataUrl(*app.icon.bitmap()));
+}
+
+void AppLaunchSplashScreenHandler::SetLaunchText(const std::string& text) {
+ CallJS("updateMessage", text);
+}
+
+int AppLaunchSplashScreenHandler::GetProgressMessageFromState(
+ AppLaunchState state) {
+ switch (state) {
+ case APP_LAUNCH_STATE_LOADING_AUTH_FILE:
+ case APP_LAUNCH_STATE_LOADING_TOKEN_SERVICE:
+ // TODO(zelidrag): Add better string for this one than "Please wait..."
+ return IDS_SYNC_SETUP_SPINNER_TITLE;
+ case APP_LAUNCH_STATE_PREPARING_NETWORK:
+ return IDS_APP_START_NETWORK_WAIT_MESSAGE;
+ case APP_LAUNCH_STATE_INSTALLING_APPLICATION:
+ return IDS_APP_START_APP_WAIT_MESSAGE;
+ case APP_LAUNCH_STATE_WAITING_APP_WINDOW:
+ return IDS_APP_START_WAIT_FOR_APP_WINDOW_MESSAGE;
+ case APP_LAUNCH_STATE_NETWORK_WAIT_TIMEOUT:
+ return IDS_APP_START_NETWORK_WAIT_TIMEOUT_MESSAGE;
+ case APP_LAUNCH_STATE_SHOWING_NETWORK_CONFIGURE_UI:
+ return IDS_APP_START_SHOWING_NETWORK_CONFIGURE_UI_MESSAGE;
+ }
+ return IDS_APP_START_NETWORK_WAIT_MESSAGE;
+}
+
+void AppLaunchSplashScreenHandler::HandleConfigureNetwork() {
+ if (delegate_)
+ delegate_->OnConfigureNetwork();
+ else
+ LOG(WARNING) << "No delegate set to handle network configuration.";
+}
+
+void AppLaunchSplashScreenHandler::HandleCancelAppLaunch() {
+ if (delegate_)
+ delegate_->OnCancelAppLaunch();
+ else
+ LOG(WARNING) << "No delegate set to handle cancel app launch";
+}
+
+void AppLaunchSplashScreenHandler::HandleNetworkConfigRequested() {
+ if (!delegate_ || network_config_done_)
+ return;
+
+ network_config_requested_ = true;
+ delegate_->OnNetworkConfigRequested(true);
+}
+
+void AppLaunchSplashScreenHandler::HandleContinueAppLaunch() {
+ DCHECK(online_state_);
+ if (delegate_ && online_state_) {
+ network_config_requested_ = false;
+ network_config_done_ = true;
+ delegate_->OnNetworkConfigRequested(false);
+ Show(app_id_);
+ }
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.h
new file mode 100644
index 00000000000..6419629070d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.h
@@ -0,0 +1,82 @@
+// 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_CHROMEOS_LOGIN_APP_LAUNCH_SPLASH_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_APP_LAUNCH_SPLASH_SCREEN_HANDLER_H_
+
+#include <set>
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/screens/app_launch_splash_screen_view.h"
+#include "chrome/browser/chromeos/login/screens/error_screen.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
+
+namespace chromeos {
+
+// A class that handles the WebUI hooks for the app launch splash screen.
+class AppLaunchSplashScreenHandler
+ : public BaseScreenHandler,
+ public AppLaunchSplashScreenView,
+ public NetworkStateInformer::NetworkStateInformerObserver {
+ public:
+ AppLaunchSplashScreenHandler(
+ const scoped_refptr<NetworkStateInformer>& network_state_informer,
+ ErrorScreen* error_screen);
+ ~AppLaunchSplashScreenHandler() override;
+
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void Initialize() override;
+
+ // WebUIMessageHandler implementation:
+ void RegisterMessages() override;
+
+ // AppLaunchSplashScreenActor implementation:
+ void Show(const std::string& app_id) override;
+ void Hide() override;
+ void ToggleNetworkConfig(bool visible) override;
+ void UpdateAppLaunchState(AppLaunchState state) override;
+ void SetDelegate(AppLaunchSplashScreenHandler::Delegate* delegate) override;
+ void ShowNetworkConfigureUI() override;
+ bool IsNetworkReady() override;
+
+ // NetworkStateInformer::NetworkStateInformerObserver implementation:
+ void OnNetworkReady() override;
+ void UpdateState(NetworkError::ErrorReason reason) override;
+
+ private:
+ void PopulateAppInfo(base::DictionaryValue* out_info);
+ void SetLaunchText(const std::string& text);
+ int GetProgressMessageFromState(AppLaunchState state);
+ void HandleConfigureNetwork();
+ void HandleCancelAppLaunch();
+ void HandleContinueAppLaunch();
+ void HandleNetworkConfigRequested();
+
+ AppLaunchSplashScreenHandler::Delegate* delegate_ = nullptr;
+ bool show_on_init_ = false;
+ std::string app_id_;
+ AppLaunchState state_ = APP_LAUNCH_STATE_LOADING_AUTH_FILE;
+
+ scoped_refptr<NetworkStateInformer> network_state_informer_;
+ ErrorScreen* error_screen_;
+
+ // True if we are online.
+ bool online_state_ = false;
+
+ // True if we have network config screen was already shown before.
+ bool network_config_done_ = false;
+
+ // True if we have manually requested network config screen.
+ bool network_config_requested_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(AppLaunchSplashScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_APP_LAUNCH_SPLASH_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/arc_kiosk_splash_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/arc_kiosk_splash_screen_handler.cc
new file mode 100644
index 00000000000..6b1ae2fd690
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/arc_kiosk_splash_screen_handler.cc
@@ -0,0 +1,122 @@
+// 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/chromeos/login/arc_kiosk_splash_screen_handler.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/grit/chrome_unscaled_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace {
+
+constexpr char kJsScreenPath[] = "login.ArcKioskSplashScreen";
+}
+
+namespace chromeos {
+
+ArcKioskSplashScreenHandler::ArcKioskSplashScreenHandler()
+ : BaseScreenHandler(kScreenId) {
+ set_call_js_prefix(kJsScreenPath);
+}
+
+ArcKioskSplashScreenHandler::~ArcKioskSplashScreenHandler() = default;
+
+void ArcKioskSplashScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("arcKioskStartMessage", IDS_APP_START_APP_WAIT_MESSAGE);
+
+ const base::string16 product_os_name =
+ l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_OS_NAME);
+ builder->Add("arcKioskShortcutInfo",
+ l10n_util::GetStringFUTF16(IDS_APP_START_BAILOUT_SHORTCUT_FORMAT,
+ product_os_name));
+ builder->Add("arcKioskProductName", product_os_name);
+}
+
+void ArcKioskSplashScreenHandler::Initialize() {
+ if (!show_on_init_)
+ return;
+ show_on_init_ = false;
+ Show();
+}
+
+void ArcKioskSplashScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+
+ base::DictionaryValue data;
+ // |data| will take ownership of |app_info|.
+ std::unique_ptr<base::DictionaryValue> app_info =
+ base::MakeUnique<base::DictionaryValue>();
+ PopulateAppInfo(app_info.get());
+ data.Set("appInfo", std::move(app_info));
+ ShowScreenWithData(kScreenId, &data);
+}
+
+void ArcKioskSplashScreenHandler::RegisterMessages() {
+ AddCallback("cancelArcKioskLaunch",
+ &ArcKioskSplashScreenHandler::HandleCancelArcKioskLaunch);
+}
+
+void ArcKioskSplashScreenHandler::UpdateArcKioskState(ArcKioskState state) {
+ if (!page_is_ready())
+ return;
+ SetLaunchText(l10n_util::GetStringUTF8(GetProgressMessageFromState(state)));
+}
+
+void ArcKioskSplashScreenHandler::SetDelegate(
+ ArcKioskSplashScreenHandler::Delegate* delegate) {
+ delegate_ = delegate;
+}
+
+void ArcKioskSplashScreenHandler::PopulateAppInfo(
+ base::DictionaryValue* out_info) {
+ out_info->SetString("name", l10n_util::GetStringUTF8(IDS_SHORT_PRODUCT_NAME));
+ out_info->SetString(
+ "iconURL",
+ webui::GetBitmapDataUrl(*ResourceBundle::GetSharedInstance()
+ .GetImageSkiaNamed(IDR_PRODUCT_LOGO_128)
+ ->bitmap()));
+}
+
+void ArcKioskSplashScreenHandler::SetLaunchText(const std::string& text) {
+ CallJS("updateArcKioskMessage", text);
+}
+
+int ArcKioskSplashScreenHandler::GetProgressMessageFromState(
+ ArcKioskState state) {
+ switch (state) {
+ case ArcKioskState::STARTING_SESSION:
+ return IDS_SYNC_SETUP_SPINNER_TITLE;
+ case ArcKioskState::WAITING_APP_LAUNCH:
+ return IDS_APP_START_APP_WAIT_MESSAGE;
+ case ArcKioskState::WAITING_APP_WINDOW:
+ return IDS_APP_START_WAIT_FOR_APP_WINDOW_MESSAGE;
+ default:
+ NOTREACHED();
+ break;
+ }
+ return IDS_SYNC_SETUP_SPINNER_TITLE;
+}
+
+void ArcKioskSplashScreenHandler::HandleCancelArcKioskLaunch() {
+ if (!delegate_) {
+ LOG(WARNING) << "No delegate set to handle cancel app launch";
+ return;
+ }
+ delegate_->OnCancelArcKioskLaunch();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/arc_kiosk_splash_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/arc_kiosk_splash_screen_handler.h
new file mode 100644
index 00000000000..b3a759b7204
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/arc_kiosk_splash_screen_handler.h
@@ -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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ARC_KIOSK_SPLASH_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ARC_KIOSK_SPLASH_SCREEN_HANDLER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/screens/arc_kiosk_splash_screen_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace chromeos {
+
+// A class that handles the WebUI hooks for the ARC kiosk splash screen.
+class ArcKioskSplashScreenHandler : public BaseScreenHandler,
+ public ArcKioskSplashScreenView {
+ public:
+ ArcKioskSplashScreenHandler();
+ ~ArcKioskSplashScreenHandler() override;
+
+ private:
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void Initialize() override;
+
+ // WebUIMessageHandler implementation:
+ void RegisterMessages() override;
+
+ // ArcKioskSplashScreenView implementation:
+ void Show() override;
+ void UpdateArcKioskState(ArcKioskState state) override;
+ void SetDelegate(ArcKioskSplashScreenHandler::Delegate* delegate) override;
+
+ void PopulateAppInfo(base::DictionaryValue* out_info);
+ void SetLaunchText(const std::string& text);
+ int GetProgressMessageFromState(ArcKioskState state);
+ void HandleCancelArcKioskLaunch();
+
+ ArcKioskSplashScreenHandler::Delegate* delegate_ = nullptr;
+ bool show_on_init_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(ArcKioskSplashScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ARC_KIOSK_SPLASH_SCREEN_HANDLER_H_
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
new file mode 100644
index 00000000000..2acd08bd722
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc
@@ -0,0 +1,186 @@
+// 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 "chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h"
+
+#include "base/i18n/timezone.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/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.ArcTermsOfServiceScreen";
+
+} // namespace
+
+namespace chromeos {
+
+ArcTermsOfServiceScreenHandler::ArcTermsOfServiceScreenHandler()
+ : BaseScreenHandler(kScreenId) {
+ set_call_js_prefix(kJsScreenPath);
+}
+
+ArcTermsOfServiceScreenHandler::~ArcTermsOfServiceScreenHandler() {
+ system::TimezoneSettings::GetInstance()->RemoveObserver(this);
+ for (auto& observer : observer_list_)
+ observer.OnViewDestroyed(this);
+}
+
+void ArcTermsOfServiceScreenHandler::RegisterMessages() {
+ AddCallback("arcTermsOfServiceSkip",
+ &ArcTermsOfServiceScreenHandler::HandleSkip);
+ AddCallback("arcTermsOfServiceAccept",
+ &ArcTermsOfServiceScreenHandler::HandleAccept);
+}
+
+void ArcTermsOfServiceScreenHandler::UpdateTimeZone() {
+ const std::string country_code = base::CountryCodeForCurrentTimezone();
+ if (country_code == last_applied_contry_code_)
+ return;
+ last_applied_contry_code_ = country_code;
+ CallJS("setCountryCode", country_code);
+}
+
+void ArcTermsOfServiceScreenHandler::TimezoneChanged(
+ const icu::TimeZone& timezone) {
+ UpdateTimeZone();
+}
+
+void ArcTermsOfServiceScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("arcTermsOfServiceScreenHeading", IDS_ARC_OOBE_TERMS_HEADING);
+ builder->Add("arcTermsOfServiceScreenDescription",
+ IDS_ARC_OOBE_TERMS_DESCRIPTION);
+ builder->Add("arcTermsOfServiceLoading", IDS_ARC_OOBE_TERMS_LOADING);
+ builder->Add("arcTermsOfServiceError", IDS_ARC_OOBE_TERMS_LOAD_ERROR);
+ builder->Add("arcTermsOfServiceSkipButton", IDS_ARC_OOBE_TERMS_BUTTON_SKIP);
+ builder->Add("arcTermsOfServiceRetryButton", IDS_ARC_OOBE_TERMS_BUTTON_RETRY);
+ builder->Add("arcTermsOfServiceAcceptButton",
+ IDS_ARC_OOBE_TERMS_BUTTON_ACCEPT);
+ 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("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("arcOverlayClose", IDS_ARC_OOBE_TERMS_POPUP_HELP_CLOSE_BUTTON);
+}
+
+void ArcTermsOfServiceScreenHandler::OnMetricsModeChanged(bool enabled,
+ bool managed) {
+ const Profile* const profile = ProfileManager::GetActiveUserProfile();
+ CHECK(profile);
+
+ const user_manager::User* user =
+ ProfileHelper::Get()->GetUserByProfile(profile);
+ CHECK(user);
+
+ const AccountId owner =
+ user_manager::UserManager::Get()->GetOwnerAccountId();
+
+ // Owner may not be set in case of initial account setup. Note, in case of
+ // enterprise enrolled devices owner is always empty and we need to account
+ // managed flag.
+ const bool owner_profile = !owner.is_valid() || user->GetAccountId() == owner;
+
+ if (owner_profile && !managed) {
+ 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;
+ CallJS("setMetricsMode", l10n_util::GetStringUTF16(message_id), true);
+ }
+}
+
+void ArcTermsOfServiceScreenHandler::OnBackupAndRestoreModeChanged(
+ bool enabled, bool managed) {
+ CallJS("setBackupAndRestoreMode", enabled, managed);
+}
+
+void ArcTermsOfServiceScreenHandler::OnLocationServicesModeChanged(
+ bool enabled, bool managed) {
+ CallJS("setLocationServicesMode", enabled, managed);
+}
+
+void ArcTermsOfServiceScreenHandler::AddObserver(
+ ArcTermsOfServiceScreenViewObserver* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void ArcTermsOfServiceScreenHandler::RemoveObserver(
+ ArcTermsOfServiceScreenViewObserver* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
+void ArcTermsOfServiceScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+
+ DoShow();
+}
+
+void ArcTermsOfServiceScreenHandler::Hide() {
+ system::TimezoneSettings::GetInstance()->RemoveObserver(this);
+ pref_handler_.reset();
+}
+
+void ArcTermsOfServiceScreenHandler::Initialize() {
+ if (!show_on_init_)
+ return;
+
+ Show();
+ show_on_init_ = false;
+}
+
+void ArcTermsOfServiceScreenHandler::DoShow() {
+ Profile* profile = ProfileManager::GetActiveUserProfile();
+ CHECK(profile);
+
+ // Enable ARC to match ArcSessionManager logic. ArcSessionManager expects that
+ // ARC is enabled (prefs::kArcEnabled = true) on showing Terms of Service. If
+ // user accepts ToS then prefs::kArcEnabled is left activated. If user skips
+ // ToS then prefs::kArcEnabled is automatically reset in ArcSessionManager.
+ profile->GetPrefs()->SetBoolean(prefs::kArcEnabled, true);
+
+ system::TimezoneSettings::GetInstance()->AddObserver(this);
+
+ ShowScreen(kScreenId);
+
+ UpdateTimeZone();
+ pref_handler_.reset(new arc::ArcOptInPreferenceHandler(
+ this, profile->GetPrefs()));
+ pref_handler_->Start();
+
+}
+
+void ArcTermsOfServiceScreenHandler::HandleSkip() {
+ for (auto& observer : observer_list_)
+ observer.OnSkip();
+}
+
+void ArcTermsOfServiceScreenHandler::HandleAccept(
+ bool enable_backup_restore,
+ bool enable_location_services) {
+ pref_handler_->EnableBackupRestore(enable_backup_restore);
+ pref_handler_->EnableLocationService(enable_location_services);
+ for (auto& observer : observer_list_)
+ observer.OnAccept();
+}
+
+} // namespace chromeos
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
new file mode 100644
index 00000000000..f37747ee3cc
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h
@@ -0,0 +1,80 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ARC_TERMS_OF_SERVICE_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ARC_TERMS_OF_SERVICE_SCREEN_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "chrome/browser/chromeos/arc/optin/arc_optin_preference_handler_observer.h"
+#include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "chromeos/settings/timezone_settings.h"
+
+namespace arc {
+class ArcOptInPreferenceHandler;
+}
+
+namespace chromeos {
+
+// The sole implementation of the ArcTermsOfServiceScreenView, using WebUI.
+class ArcTermsOfServiceScreenHandler
+ : public BaseScreenHandler,
+ public ArcTermsOfServiceScreenView,
+ public arc::ArcOptInPreferenceHandlerObserver,
+ public system::TimezoneSettings::Observer {
+ public:
+ ArcTermsOfServiceScreenHandler();
+ ~ArcTermsOfServiceScreenHandler() override;
+
+ // content::WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ // BaseScreenHandler:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+
+ // ArcTermsOfServiceScreenView:
+ void AddObserver(ArcTermsOfServiceScreenViewObserver* observer) override;
+ void RemoveObserver(ArcTermsOfServiceScreenViewObserver* observer) override;
+ void Show() override;
+ void Hide() override;
+
+ // system::TimezoneSettings::Observer:
+ void TimezoneChanged(const icu::TimeZone& timezone) override;
+
+ private:
+ // BaseScreenHandler:
+ void Initialize() override;
+
+ void DoShow();
+ void HandleSkip();
+ void HandleAccept(bool enable_backup_restore,
+ bool enable_location_services);
+ void UpdateTimeZone();
+
+ // arc::ArcOptInPreferenceHandlerObserver:
+ void OnMetricsModeChanged(bool enabled, bool managed) override;
+ void OnBackupAndRestoreModeChanged(bool enabled, bool managed) override;
+ void OnLocationServicesModeChanged(bool enabled, bool managed) override;
+
+ base::ObserverList<ArcTermsOfServiceScreenViewObserver, true> observer_list_;
+
+ // Whether the screen should be shown right after initialization.
+ bool show_on_init_ = false;
+
+ // To prevent redundant updates, keep last country code used for update.
+ std::string last_applied_contry_code_;
+
+ std::unique_ptr<arc::ArcOptInPreferenceHandler> pref_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArcTermsOfServiceScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ARC_TERMS_OF_SERVICE_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.cc
new file mode 100644
index 00000000000..0c3ce2cab45
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.cc
@@ -0,0 +1,63 @@
+// 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/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.h"
+
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.AutoEnrollmentCheckScreen";
+
+} // namespace
+
+namespace chromeos {
+
+AutoEnrollmentCheckScreenHandler::AutoEnrollmentCheckScreenHandler()
+ : BaseScreenHandler(kScreenId) {
+ set_call_js_prefix(kJsScreenPath);
+}
+
+AutoEnrollmentCheckScreenHandler::~AutoEnrollmentCheckScreenHandler() {
+ if (delegate_)
+ delegate_->OnViewDestroyed(this);
+}
+
+void AutoEnrollmentCheckScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+ ShowScreen(kScreenId);
+}
+
+void AutoEnrollmentCheckScreenHandler::SetDelegate(Delegate* delegate) {
+ delegate_ = delegate;
+ if (page_is_ready())
+ Initialize();
+}
+
+void AutoEnrollmentCheckScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("autoEnrollmentCheckScreenHeader",
+ IDS_AUTO_ENROLLMENT_CHECK_SCREEN_HEADER);
+ builder->Add("autoEnrollmentCheckMessage",
+ IDS_AUTO_ENROLLMENT_CHECK_SCREEN_MESSAGE);
+}
+
+void AutoEnrollmentCheckScreenHandler::Initialize() {
+ if (!page_is_ready() || !delegate_)
+ return;
+
+ if (show_on_init_) {
+ Show();
+ show_on_init_ = false;
+ }
+}
+
+void AutoEnrollmentCheckScreenHandler::RegisterMessages() {}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.h
new file mode 100644
index 00000000000..285fb75c996
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.h
@@ -0,0 +1,47 @@
+// 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_UI_WEBUI_CHROMEOS_LOGIN_AUTO_ENROLLMENT_CHECK_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_AUTO_ENROLLMENT_CHECK_SCREEN_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "content/public/browser/web_ui.h"
+
+namespace chromeos {
+
+// WebUI implementation of AutoEnrollmentCheckScreenActor.
+class AutoEnrollmentCheckScreenHandler : public AutoEnrollmentCheckScreenView,
+ public BaseScreenHandler {
+ public:
+ AutoEnrollmentCheckScreenHandler();
+ ~AutoEnrollmentCheckScreenHandler() override;
+
+ // AutoEnrollmentCheckScreenActor implementation:
+ void Show() override;
+ void SetDelegate(Delegate* delegate) override;
+
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void Initialize() override;
+
+ // WebUIMessageHandler implementation:
+ void RegisterMessages() override;
+
+ private:
+ Delegate* delegate_ = nullptr;
+
+ // Keeps whether screen should be shown right after initialization.
+ bool show_on_init_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(AutoEnrollmentCheckScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_AUTO_ENROLLMENT_CHECK_SCREEN_HANDLER_H_
+
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/base_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/base_screen_handler.cc
new file mode 100644
index 00000000000..8588ac9b34a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/base_screen_handler.cc
@@ -0,0 +1,14 @@
+// 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/ui/webui/chromeos/login/base_screen_handler.h"
+
+namespace chromeos {
+
+BaseScreenHandler::BaseScreenHandler(OobeScreen oobe_screen)
+ : oobe_screen_(oobe_screen) {}
+
+BaseScreenHandler::~BaseScreenHandler() {}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/base_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/base_screen_handler.h
new file mode 100644
index 00000000000..05916e7c70e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/base_screen_handler.h
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_BASE_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_BASE_SCREEN_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_webui_handler.h"
+
+namespace chromeos {
+
+// Base class for the OOBE/Login WebUI handlers which provide methods specific
+// to a particular OobeScreen.
+class BaseScreenHandler : public BaseWebUIHandler {
+ public:
+ explicit BaseScreenHandler(OobeScreen oobe_screen);
+ ~BaseScreenHandler() override;
+
+ OobeScreen oobe_screen() const { return oobe_screen_; }
+
+ private:
+ // OobeScreen that this handler corresponds to.
+ OobeScreen oobe_screen_ = OobeScreen::SCREEN_UNKNOWN;
+
+ DISALLOW_COPY_AND_ASSIGN(BaseScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_BASE_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/base_webui_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/base_webui_handler.cc
new file mode 100644
index 00000000000..aca69389faf
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/base_webui_handler.cc
@@ -0,0 +1,137 @@
+// 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/chromeos/login/base_webui_handler.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/login/screens/base_screen.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host.h"
+#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
+#include "components/login/localized_values_builder.h"
+#include "content/public/browser/web_ui.h"
+
+namespace chromeos {
+
+namespace {
+const char kMethodContextChanged[] = "contextChanged";
+} // namespace
+
+JSCallsContainer::JSCallsContainer() = default;
+
+JSCallsContainer::~JSCallsContainer() = default;
+
+BaseWebUIHandler::BaseWebUIHandler() = default;
+
+BaseWebUIHandler::BaseWebUIHandler(JSCallsContainer* js_calls_container)
+ : js_calls_container_(js_calls_container) {}
+
+BaseWebUIHandler::~BaseWebUIHandler() {
+ if (base_screen_)
+ base_screen_->set_model_view_channel(nullptr);
+}
+
+void BaseWebUIHandler::InitializeBase() {
+ page_is_ready_ = true;
+ Initialize();
+ if (!pending_context_changes_.empty()) {
+ CommitContextChanges(pending_context_changes_);
+ pending_context_changes_.Clear();
+ }
+}
+
+void BaseWebUIHandler::GetLocalizedStrings(base::DictionaryValue* dict) {
+ auto builder = base::MakeUnique<::login::LocalizedValuesBuilder>(dict);
+ DeclareLocalizedValues(builder.get());
+ GetAdditionalParameters(dict);
+}
+
+void BaseWebUIHandler::RegisterMessages() {
+ AddPrefixedCallback("userActed", &BaseScreenHandler::HandleUserAction);
+ AddPrefixedCallback("contextChanged",
+ &BaseScreenHandler::HandleContextChanged);
+ DeclareJSCallbacks();
+}
+
+void BaseWebUIHandler::CommitContextChanges(const base::DictionaryValue& diff) {
+ if (!page_is_ready())
+ pending_context_changes_.MergeDictionary(&diff);
+ else
+ CallJS(kMethodContextChanged, diff);
+}
+
+void BaseWebUIHandler::GetAdditionalParameters(base::DictionaryValue* dict) {}
+
+void BaseWebUIHandler::CallJS(const std::string& method) {
+ web_ui()->CallJavascriptFunctionUnsafe(FullMethodPath(method));
+}
+
+void BaseWebUIHandler::ShowScreen(OobeScreen screen) {
+ ShowScreenWithData(screen, nullptr);
+}
+
+void BaseWebUIHandler::ShowScreenWithData(OobeScreen screen,
+ const base::DictionaryValue* data) {
+ if (!web_ui())
+ return;
+ base::DictionaryValue screen_params;
+ screen_params.SetString("id", GetOobeScreenName(screen));
+ if (data) {
+ screen_params.SetWithoutPathExpansion("data",
+ base::MakeUnique<base::Value>(*data));
+ }
+ web_ui()->CallJavascriptFunctionUnsafe("cr.ui.Oobe.showScreen",
+ screen_params);
+}
+
+OobeUI* BaseWebUIHandler::GetOobeUI() const {
+ return static_cast<OobeUI*>(web_ui()->GetController());
+}
+
+OobeScreen BaseWebUIHandler::GetCurrentScreen() const {
+ OobeUI* oobe_ui = GetOobeUI();
+ if (!oobe_ui)
+ return OobeScreen::SCREEN_UNKNOWN;
+ return oobe_ui->current_screen();
+}
+
+gfx::NativeWindow BaseWebUIHandler::GetNativeWindow() {
+ return LoginDisplayHost::default_host()->GetNativeWindow();
+}
+
+void BaseWebUIHandler::SetBaseScreen(BaseScreen* base_screen) {
+ if (base_screen_ == base_screen)
+ return;
+ if (base_screen_)
+ base_screen_->set_model_view_channel(nullptr);
+ base_screen_ = base_screen;
+ if (base_screen_)
+ base_screen_->set_model_view_channel(this);
+}
+
+std::string BaseWebUIHandler::FullMethodPath(const std::string& method) const {
+ DCHECK(!method.empty());
+ return js_screen_path_prefix_ + method;
+}
+
+void BaseWebUIHandler::HandleUserAction(const std::string& action_id) {
+ if (base_screen_)
+ base_screen_->OnUserAction(action_id);
+}
+
+void BaseWebUIHandler::HandleContextChanged(const base::DictionaryValue* diff) {
+ if (diff && base_screen_)
+ base_screen_->OnContextChanged(*diff);
+}
+
+void BaseWebUIHandler::ExecuteDeferredJSCalls() {
+ DCHECK(!js_calls_container_->is_initialized());
+ js_calls_container_->mark_initialized();
+ for (const auto& deferred_js_call : js_calls_container_->deferred_js_calls())
+ deferred_js_call.Run();
+ js_calls_container_->deferred_js_calls().clear();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/base_webui_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/base_webui_handler.h
new file mode 100644
index 00000000000..e0d3f57c869
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/base_webui_handler.h
@@ -0,0 +1,274 @@
+// 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_CHROMEOS_LOGIN_BASE_WEBUI_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_BASE_WEBUI_HANDLER_H_
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/browser/chromeos/login/screens/model_view_channel.h"
+#include "components/login/base_screen_handler_utils.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+namespace login {
+class LocalizedValuesBuilder;
+}
+
+namespace chromeos {
+
+class BaseScreen;
+class OobeUI;
+
+// A helper class to store deferred Javascript calls, shared by subclasses of
+// BaseWebUIHandler.
+class JSCallsContainer {
+ public:
+ JSCallsContainer();
+ ~JSCallsContainer();
+
+ // Used to decide whether the JS call should be deferred.
+ bool is_initialized() const { return is_initialized_; }
+
+ // Used to mark the instance as intialized.
+ void mark_initialized() { is_initialized_ = true; }
+
+ // Used to add deferred calls to.
+ std::vector<base::Closure>& deferred_js_calls() { return deferred_js_calls_; }
+
+ private:
+ // Whether the instance is initialized.
+ //
+ // The instance becomes initialized after the corresponding message is
+ // received from Javascript side.
+ bool is_initialized_ = false;
+
+ // Javascript calls that have been deferred while the instance was not
+ // initialized yet.
+ std::vector<base::Closure> deferred_js_calls_;
+};
+
+// Base class for all oobe/login WebUI handlers. These handlers are the binding
+// layer that allow the C++ and JavaScript code to communicate.
+//
+// If the deriving type is associated with a specific OobeScreen, it should
+// derive from BaseScreenHandler instead of BaseWebUIHandler.
+//
+// TODO(jdufault): Move all OobeScreen related concepts out of BaseWebUIHandler
+// and into BaseScreenHandler.
+class BaseWebUIHandler : public content::WebUIMessageHandler,
+ public ModelViewChannel {
+ public:
+ BaseWebUIHandler();
+ explicit BaseWebUIHandler(JSCallsContainer* js_calls_container);
+ ~BaseWebUIHandler() override;
+
+ // Gets localized strings to be used on the page.
+ void GetLocalizedStrings(base::DictionaryValue* localized_strings);
+
+ // WebUIMessageHandler implementation:
+ void RegisterMessages() override;
+
+ // ModelViewChannel implementation:
+ void CommitContextChanges(const base::DictionaryValue& diff) override;
+
+ // This method is called when page is ready. It propagates to inherited class
+ // via virtual Initialize() method (see below).
+ void InitializeBase();
+
+ // Set the prefix used when running CallJs with a method. For example,
+ // set_call_js_prefix("Oobe")
+ // CallJs("lock") -> Invokes JS global named "Oobe.lock"
+ void set_call_js_prefix(const std::string& prefix) {
+ js_screen_path_prefix_ = prefix + ".";
+ }
+
+ void set_async_assets_load_id(const std::string& async_assets_load_id) {
+ async_assets_load_id_ = async_assets_load_id;
+ }
+ const std::string& async_assets_load_id() const {
+ return async_assets_load_id_;
+ }
+
+ protected:
+ // All subclasses should implement this method to provide localized values.
+ virtual void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) = 0;
+
+ // All subclasses should implement this method to register callbacks for JS
+ // messages.
+ //
+ // TODO (ygorshenin, crbug.com/433797): make this method purely vrtual when
+ // all screens will be switched to use ScreenContext.
+ virtual void DeclareJSCallbacks() {}
+
+ // Subclasses can override these methods to pass additional parameters
+ // to loadTimeData. Generally, it is a bad approach, and it should be replaced
+ // with Context at some point.
+ virtual void GetAdditionalParameters(base::DictionaryValue* parameters);
+
+ // Shortcut for calling JS methods on WebUI side.
+ void CallJS(const std::string& method);
+
+ template <typename A1>
+ void CallJS(const std::string& method, const A1& arg1) {
+ web_ui()->CallJavascriptFunctionUnsafe(FullMethodPath(method),
+ ::login::MakeValue(arg1));
+ }
+
+ template <typename A1, typename A2>
+ void CallJS(const std::string& method, const A1& arg1, const A2& arg2) {
+ web_ui()->CallJavascriptFunctionUnsafe(FullMethodPath(method),
+ ::login::MakeValue(arg1),
+ ::login::MakeValue(arg2));
+ }
+
+ template <typename A1, typename A2, typename A3>
+ void CallJS(const std::string& method,
+ const A1& arg1,
+ const A2& arg2,
+ const A3& arg3) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ FullMethodPath(method), ::login::MakeValue(arg1),
+ ::login::MakeValue(arg2), ::login::MakeValue(arg3));
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4>
+ void CallJS(const std::string& method,
+ const A1& arg1,
+ const A2& arg2,
+ const A3& arg3,
+ const A4& arg4) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ FullMethodPath(method), ::login::MakeValue(arg1),
+ ::login::MakeValue(arg2), ::login::MakeValue(arg3),
+ ::login::MakeValue(arg4));
+ }
+
+ template <typename... Args>
+ void CallJSOrDefer(const std::string& function_name, const Args&... args) {
+ DCHECK(js_calls_container_);
+ if (js_calls_container_->is_initialized()) {
+ CallJS(function_name, args...);
+ } else {
+ // Note that std::conditional is used here in order to obtain a sequence
+ // of base::Value types with the length equal to sizeof...(Args); the C++
+ // template parameter pack expansion rules require that the name of the
+ // parameter pack appears in the pattern, even though the elements of the
+ // Args pack are not actually in this code.
+ js_calls_container_->deferred_js_calls().push_back(base::Bind(
+ &BaseWebUIHandler::ExecuteDeferredJSCall<
+ typename std::conditional<true, base::Value, Args>::type...>,
+ base::Unretained(this), function_name,
+ base::Passed(::login::MakeValue(args).CreateDeepCopy())...));
+ }
+ }
+
+ // Executes Javascript calls that were deferred while the instance was not
+ // initialized yet.
+ void ExecuteDeferredJSCalls();
+
+ // Shortcut methods for adding WebUI callbacks.
+ template <typename T>
+ void AddRawCallback(const std::string& name,
+ void (T::*method)(const base::ListValue* args)) {
+ web_ui()->RegisterMessageCallback(
+ name, base::Bind(method, base::Unretained(static_cast<T*>(this))));
+ }
+
+ template <typename T, typename... Args>
+ void AddCallback(const std::string& name, void (T::*method)(Args...)) {
+ base::Callback<void(Args...)> callback =
+ base::Bind(method, base::Unretained(static_cast<T*>(this)));
+ web_ui()->RegisterMessageCallback(
+ name, base::Bind(&::login::CallbackWrapper<Args...>, callback));
+ }
+
+ template <typename Method>
+ void AddPrefixedCallback(const std::string& unprefixed_name,
+ const Method& method) {
+ AddCallback(FullMethodPath(unprefixed_name), method);
+ }
+
+ // Called when the page is ready and handler can do initialization.
+ virtual void Initialize() = 0;
+
+ // Show selected WebUI |screen|.
+ void ShowScreen(OobeScreen screen);
+ // Show selected WebUI |screen|. Pass screen initialization using the |data|
+ // parameter.
+ void ShowScreenWithData(OobeScreen screen, const base::DictionaryValue* data);
+
+ // Returns the OobeUI instance.
+ OobeUI* GetOobeUI() const;
+
+ // Returns current visible OOBE screen.
+ OobeScreen GetCurrentScreen() const;
+
+ // Whether page is ready.
+ bool page_is_ready() const { return page_is_ready_; }
+
+ // Returns the window which shows us.
+ virtual gfx::NativeWindow GetNativeWindow();
+
+ void SetBaseScreen(BaseScreen* base_screen);
+
+ private:
+ // Calls Javascript method.
+ //
+ // Note that the Args template parameter pack should consist of types
+ // convertible to base::Value.
+ template <typename... Args>
+ void ExecuteDeferredJSCall(const std::string& function_name,
+ std::unique_ptr<Args>... args) {
+ CallJS(function_name, *args...);
+ }
+
+ // Returns full name of JS method based on screen and method
+ // names.
+ std::string FullMethodPath(const std::string& method) const;
+
+ // Handles user action.
+ void HandleUserAction(const std::string& action_id);
+
+ // Handles situation when screen context is changed.
+ void HandleContextChanged(const base::DictionaryValue* diff);
+
+ // Keeps whether page is ready.
+ bool page_is_ready_ = false;
+
+ BaseScreen* base_screen_ = nullptr;
+
+ // Full name of the corresponding JS screen object. Can be empty, if
+ // there are no corresponding screen object or several different
+ // objects.
+ std::string js_screen_path_prefix_;
+
+ // The string id used in the async asset load in JS. If it is set to a
+ // non empty value, the Initialize will be deferred until the underlying load
+ // is finished.
+ std::string async_assets_load_id_;
+
+ // Pending changes to context which will be sent when the page will be ready.
+ base::DictionaryValue pending_context_changes_;
+
+ JSCallsContainer* js_calls_container_ = nullptr; // non-owning pointers.
+
+ DISALLOW_COPY_AND_ASSIGN(BaseWebUIHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_BASE_WEBUI_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/controller_pairing_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/controller_pairing_screen_handler.cc
new file mode 100644
index 00000000000..a2c7493c08e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/controller_pairing_screen_handler.cc
@@ -0,0 +1,175 @@
+// 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/ui/webui/chromeos/login/controller_pairing_screen_handler.h"
+
+#include "base/command_line.h"
+#include "base/strings/string_util.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "components/login/localized_values_builder.h"
+#include "content/public/browser/web_contents.h"
+
+namespace chromeos {
+
+namespace {
+
+const char kJsScreenPath[] = "login.ControllerPairingScreen";
+
+const char kMethodContextChanged[] = "contextChanged";
+
+const char kCallbackUserActed[] = "userActed";
+const char kCallbackContextChanged[] = "contextChanged";
+
+bool IsBootstrappingMaster() {
+ return base::CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kOobeBootstrappingMaster);
+}
+
+} // namespace
+
+ControllerPairingScreenHandler::ControllerPairingScreenHandler()
+ : BaseScreenHandler(kScreenId) {
+ set_call_js_prefix(kJsScreenPath);
+}
+
+ControllerPairingScreenHandler::~ControllerPairingScreenHandler() {
+ if (delegate_)
+ delegate_->OnViewDestroyed(this);
+}
+
+void ControllerPairingScreenHandler::HandleUserActed(
+ const std::string& action) {
+ if (!delegate_)
+ return;
+ delegate_->OnUserActed(action);
+}
+
+void ControllerPairingScreenHandler::HandleContextChanged(
+ const base::DictionaryValue* diff) {
+ if (!delegate_)
+ return;
+ delegate_->OnScreenContextChanged(*diff);
+}
+
+void ControllerPairingScreenHandler::Initialize() {
+ if (!page_is_ready() || !delegate_)
+ return;
+
+ if (show_on_init_) {
+ Show();
+ show_on_init_ = false;
+ }
+}
+
+void ControllerPairingScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ // TODO(dzhioev): Move the prefix logic to the base screen handler after
+ // migration.
+ std::string prefix;
+ base::RemoveChars(kJsScreenPath, ".", &prefix);
+
+ // TODO(xdai): Remove unnecessary strings.
+ builder->Add(prefix + "WelcomeTitle", IDS_PAIRING_CONTROLLER_WELCOME);
+ builder->Add(prefix + "Searching", IDS_PAIRING_CONTROLLER_SEARCHING);
+ builder->Add(prefix + "HelpBtn", IDS_PAIRING_NEED_HELP);
+ builder->Add(prefix + "TroubleConnectingTitle",
+ IDS_PAIRING_CONTROLLER_TROUBLE_CONNECTING);
+ builder->Add(prefix + "ConnectingAdvice",
+ IDS_PAIRING_CONTROLLER_CONNECTING_ADVICE);
+ builder->Add(prefix + "AdviceGotItBtn", IDS_PAIRING_CONTROLLER_ADVICE_GOT_IT);
+ builder->Add(prefix + "SelectTitle", IDS_PAIRING_CONTROLLER_SELECT_TITLE);
+ builder->Add(prefix + "ConnectBtn", IDS_PAIRING_CONTROLLER_CONNECT);
+ builder->Add(prefix + "Connecting", IDS_PAIRING_CONTROLLER_CONNECTING);
+ builder->Add(prefix + "ConfirmationTitle",
+ IDS_PAIRING_CONTROLLER_CONFIRMATION_TITLE);
+ builder->Add(prefix + "ConfirmationQuestion",
+ IDS_PAIRING_CONTROLLER_CONFIRMATION_QUESTION);
+ builder->Add(prefix + "RejectCodeBtn", IDS_PAIRING_CONTROLLER_REJECT_CODE);
+ builder->Add(prefix + "AcceptCodeBtn", IDS_PAIRING_CONTROLLER_ACCEPT_CODE);
+ builder->Add(prefix + "UpdateTitle", IDS_PAIRING_CONTROLLER_UPDATE_TITLE);
+ builder->Add(prefix + "UpdateText", IDS_PAIRING_CONTROLLER_UPDATE_TEXT);
+ builder->Add(prefix + "ConnectionLostTitle",
+ IDS_PAIRING_CONTROLLER_CONNECTION_LOST_TITLE);
+ builder->Add(prefix + "ConnectionLostText",
+ IDS_PAIRING_CONTROLLER_CONNECTION_LOST_TEXT);
+ builder->Add(prefix + "HostNetworkErrorTitle",
+ IDS_PAIRING_CONTROLLER_HOST_NETWORK_ERROR_TITLE);
+ builder->Add(prefix + "EnrollTitle", IDS_PAIRING_CONTROLLER_ENROLL_TITLE);
+ builder->Add(prefix + "EnrollText1", IDS_PAIRING_CONTROLLER_ENROLL_TEXT_1);
+ builder->Add(prefix + "EnrollText2", IDS_PAIRING_CONTROLLER_ENROLL_TEXT_2);
+ builder->Add(prefix + "ContinueBtn", IDS_PAIRING_CONTROLLER_CONTINUE);
+ builder->Add(prefix + "EnrollmentInProgress",
+ IDS_PAIRING_CONTROLLER_ENROLLMENT_IN_PROGRESS);
+ builder->Add(prefix + "EnrollmentErrorTitle",
+ IDS_PAIRING_ENROLLMENT_ERROR_TITLE);
+ builder->Add(prefix + "EnrollmentErrorHostRestarts",
+ IDS_PAIRING_CONTROLLER_ENROLLMENT_ERROR_HOST_RESTARTS);
+ builder->Add(prefix + "SuccessTitle", IDS_PAIRING_CONTROLLER_SUCCESS_TITLE);
+ builder->Add(prefix + "SuccessText", IDS_PAIRING_CONTROLLER_SUCCESS_TEXT);
+ builder->Add(prefix + "ContinueToHangoutsBtn",
+ IDS_PAIRING_CONTROLLER_CONTINUE_TO_HANGOUTS);
+
+ if (IsBootstrappingMaster()) {
+ // These strings are only for testing/demo purpose, so they are not put into
+ // grd file for translation.
+ builder->Add(prefix + "WelcomeTitle",
+ "Welcome to the bootstrapping process");
+ builder->Add(prefix + "Searching",
+ "Searching for nearby Chrome OS devices...");
+ builder->Add(prefix + "ConnectingAdvice",
+ "Please make sure that your Chrome OS device is turned on.");
+ builder->Add(prefix + "SelectTitle", "Select a Chrome OS device to set up");
+ builder->Add(prefix + "ConfirmationTitle", "Initialize the connection");
+ builder->Add(prefix + "ConfirmationQuestion",
+ "Does this code appear on your Chrome OS device\'s screen?");
+ builder->Add(prefix + "UpdateTitle", "Updating...");
+ builder->Add(prefix + "UpdateText",
+ "In order to bring you the latest features, your Chrome OS "
+ "device needs to update.");
+ builder->Add(prefix + "ConnectionLostTitle",
+ "Connection to Chrome OS device lost");
+ builder->Add(prefix + "ConnectionLostText",
+ " Lost connection to your Chrome OS device. Please move "
+ "closer, or check your device and try again.");
+ builder->Add(prefix + "HostNetworkErrorTitle",
+ "Failed to set up your Chrome OS device\'s network");
+ }
+}
+
+void ControllerPairingScreenHandler::RegisterMessages() {
+ AddPrefixedCallback(kCallbackUserActed,
+ &ControllerPairingScreenHandler::HandleUserActed);
+ AddPrefixedCallback(kCallbackContextChanged,
+ &ControllerPairingScreenHandler::HandleContextChanged);
+}
+
+void ControllerPairingScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+ ShowScreen(kScreenId);
+}
+
+void ControllerPairingScreenHandler::Hide() {
+}
+
+void ControllerPairingScreenHandler::SetDelegate(Delegate* delegate) {
+ delegate_ = delegate;
+ if (page_is_ready())
+ Initialize();
+}
+
+void ControllerPairingScreenHandler::OnContextChanged(
+ const base::DictionaryValue& diff) {
+ CallJS(kMethodContextChanged, diff);
+}
+
+content::BrowserContext* ControllerPairingScreenHandler::GetBrowserContext() {
+ return web_ui()->GetWebContents()->GetBrowserContext();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/controller_pairing_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/controller_pairing_screen_handler.h
new file mode 100644
index 00000000000..fd0c90dd6fa
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/controller_pairing_screen_handler.h
@@ -0,0 +1,47 @@
+// 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_UI_WEBUI_CHROMEOS_LOGIN_CONTROLLER_PAIRING_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_CONTROLLER_PAIRING_SCREEN_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/screens/controller_pairing_screen_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+
+namespace chromeos {
+
+class ControllerPairingScreenHandler : public ControllerPairingScreenView,
+ public BaseScreenHandler {
+ public:
+ ControllerPairingScreenHandler();
+ ~ControllerPairingScreenHandler() override;
+
+ private:
+ void HandleUserActed(const std::string& action);
+ void HandleContextChanged(const base::DictionaryValue* diff);
+
+ // Overridden from BaseScreenHandler:
+ void Initialize() override;
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+
+ // Overridden from content::WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ // Overridden from ControllerPairingScreenActor:
+ void Show() override;
+ void Hide() override;
+ void SetDelegate(Delegate* delegate) override;
+ void OnContextChanged(const base::DictionaryValue& diff) override;
+ content::BrowserContext* GetBrowserContext() override;
+
+ ControllerPairingScreenView::Delegate* delegate_ = nullptr;
+ bool show_on_init_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(ControllerPairingScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_CONTROLLER_PAIRING_SCREEN_HANDLER_H_
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
new file mode 100644
index 00000000000..b20cb02b3d7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
@@ -0,0 +1,520 @@
+// 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/ui/webui/chromeos/login/core_oobe_handler.h"
+
+#include <type_traits>
+
+#include "ash/accessibility_types.h"
+#include "ash/shell.h"
+#include "base/bind.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
+#include "chrome/browser/chromeos/accessibility/magnification_manager.h"
+#include "chrome/browser/chromeos/events/keyboard_driven_event_rewriter.h"
+#include "chrome/browser/chromeos/login/helper.h"
+#include "chrome/browser/chromeos/login/lock/screen_locker.h"
+#include "chrome/browser/chromeos/login/lock/webui_screen_locker.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host.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_cloud_policy_manager_chromeos.h"
+#include "chrome/browser/chromeos/system/input_device_settings.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/ui/ash/ash_util.h"
+#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
+#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_constants.h"
+#include "components/login/base_screen_handler_utils.h"
+#include "components/login/localized_values_builder.h"
+#include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/version_info/version_info.h"
+#include "google_apis/google_api_keys.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/events/event_sink.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/keyboard/keyboard_controller.h"
+
+namespace {
+
+const char kJsScreenPath[] = "cr.ui.Oobe";
+
+} // namespace
+
+namespace chromeos {
+
+// Note that show_oobe_ui_ defaults to false because WizardController assumes
+// OOBE UI is not visible by default.
+CoreOobeHandler::CoreOobeHandler(OobeUI* oobe_ui,
+ JSCallsContainer* js_calls_container)
+ : BaseWebUIHandler(js_calls_container),
+ oobe_ui_(oobe_ui),
+ version_info_updater_(this) {
+ DCHECK(js_calls_container);
+ set_call_js_prefix(kJsScreenPath);
+ if (!ash_util::IsRunningInMash()) {
+ AccessibilityManager* accessibility_manager = AccessibilityManager::Get();
+ CHECK(accessibility_manager);
+ accessibility_subscription_ = accessibility_manager->RegisterCallback(
+ base::Bind(&CoreOobeHandler::OnAccessibilityStatusChanged,
+ base::Unretained(this)));
+ } else {
+ NOTIMPLEMENTED();
+ }
+}
+
+CoreOobeHandler::~CoreOobeHandler() {
+}
+
+void CoreOobeHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("title", IDS_SHORT_PRODUCT_NAME);
+ builder->Add("productName", IDS_SHORT_PRODUCT_NAME);
+ builder->Add("learnMore", IDS_LEARN_MORE);
+
+ // OOBE accessibility options menu strings shown on each screen.
+ builder->Add("accessibilityLink", IDS_OOBE_ACCESSIBILITY_LINK);
+ builder->Add("spokenFeedbackOption", IDS_OOBE_SPOKEN_FEEDBACK_OPTION);
+ builder->Add("largeCursorOption", IDS_OOBE_LARGE_CURSOR_OPTION);
+ builder->Add("highContrastOption", IDS_OOBE_HIGH_CONTRAST_MODE_OPTION);
+ builder->Add("screenMagnifierOption", IDS_OOBE_SCREEN_MAGNIFIER_OPTION);
+ builder->Add("virtualKeyboardOption", IDS_OOBE_VIRTUAL_KEYBOARD_OPTION);
+ builder->Add("closeAccessibilityMenu", IDS_OOBE_CLOSE_ACCESSIBILITY_MENU);
+
+ // Strings for the device requisition prompt.
+ builder->Add("deviceRequisitionPromptCancel",
+ IDS_ENTERPRISE_DEVICE_REQUISITION_PROMPT_CANCEL);
+ builder->Add("deviceRequisitionPromptOk",
+ IDS_ENTERPRISE_DEVICE_REQUISITION_PROMPT_OK);
+ builder->Add("deviceRequisitionPromptText",
+ IDS_ENTERPRISE_DEVICE_REQUISITION_PROMPT_TEXT);
+ builder->Add("deviceRequisitionRemoraPromptCancel",
+ IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL);
+ builder->Add("deviceRequisitionRemoraPromptOk",
+ IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL);
+ builder->Add("deviceRequisitionRemoraPromptText",
+ IDS_ENTERPRISE_DEVICE_REQUISITION_REMORA_PROMPT_TEXT);
+ builder->Add("deviceRequisitionSharkPromptText",
+ IDS_ENTERPRISE_DEVICE_REQUISITION_SHARK_PROMPT_TEXT);
+
+ // Strings for Asset Identifier shown in version string.
+ builder->Add("assetIdLabel", IDS_OOBE_ASSET_ID_LABEL);
+
+ builder->AddF("missingAPIKeysNotice", IDS_LOGIN_API_KEYS_NOTICE,
+ base::ASCIIToUTF16(google_apis::kAPIKeysDevelopersHowToURL));
+}
+
+void CoreOobeHandler::Initialize() {
+ UpdateA11yState();
+ UpdateOobeUIVisibility();
+#if defined(OFFICIAL_BUILD)
+ version_info_updater_.StartUpdate(true);
+#else
+ version_info_updater_.StartUpdate(false);
+#endif
+ UpdateDeviceRequisition();
+ UpdateKeyboardState();
+ UpdateClientAreaSize();
+}
+
+void CoreOobeHandler::RegisterMessages() {
+ AddCallback("screenStateInitialize",
+ &CoreOobeHandler::HandleInitialized);
+ AddCallback("skipUpdateEnrollAfterEula",
+ &CoreOobeHandler::HandleSkipUpdateEnrollAfterEula);
+ AddCallback("updateCurrentScreen",
+ &CoreOobeHandler::HandleUpdateCurrentScreen);
+ AddCallback("enableHighContrast",
+ &CoreOobeHandler::HandleEnableHighContrast);
+ AddCallback("enableLargeCursor",
+ &CoreOobeHandler::HandleEnableLargeCursor);
+ AddCallback("enableVirtualKeyboard",
+ &CoreOobeHandler::HandleEnableVirtualKeyboard);
+ AddCallback("setForceDisableVirtualKeyboard",
+ &CoreOobeHandler::HandleSetForceDisableVirtualKeyboard);
+ AddCallback("enableScreenMagnifier",
+ &CoreOobeHandler::HandleEnableScreenMagnifier);
+ AddCallback("enableSpokenFeedback",
+ &CoreOobeHandler::HandleEnableSpokenFeedback);
+ AddCallback("setDeviceRequisition",
+ &CoreOobeHandler::HandleSetDeviceRequisition);
+ AddCallback("screenAssetsLoaded",
+ &CoreOobeHandler::HandleScreenAssetsLoaded);
+ AddRawCallback("skipToLoginForTesting",
+ &CoreOobeHandler::HandleSkipToLoginForTesting);
+ AddCallback("launchHelpApp",
+ &CoreOobeHandler::HandleLaunchHelpApp);
+ AddCallback("toggleResetScreen", &CoreOobeHandler::HandleToggleResetScreen);
+ AddCallback("toggleEnableDebuggingScreen",
+ &CoreOobeHandler::HandleEnableDebuggingScreen);
+ AddCallback("headerBarVisible",
+ &CoreOobeHandler::HandleHeaderBarVisible);
+ AddCallback("raiseTabKeyEvent", &CoreOobeHandler::HandleRaiseTabKeyEvent);
+ AddCallback("setOobeBootstrappingSlave",
+ &CoreOobeHandler::HandleSetOobeBootstrappingSlave);
+}
+
+void CoreOobeHandler::ShowSignInError(
+ int login_attempts,
+ const std::string& error_text,
+ const std::string& help_link_text,
+ HelpAppLauncher::HelpTopic help_topic_id) {
+ LOG(ERROR) << "CoreOobeHandler::ShowSignInError: error_text=" << error_text;
+ CallJSOrDefer("showSignInError", login_attempts, error_text,
+ help_link_text, static_cast<int>(help_topic_id));
+}
+
+void CoreOobeHandler::ShowTpmError() {
+ CallJSOrDefer("showTpmError");
+}
+
+void CoreOobeHandler::ShowDeviceResetScreen() {
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ if (!connector->IsEnterpriseManaged()) {
+ // Don't recreate WizardController if it already exists.
+ WizardController* wizard_controller =
+ WizardController::default_controller();
+ if (wizard_controller && !wizard_controller->login_screen_started()) {
+ wizard_controller->AdvanceToScreen(OobeScreen::SCREEN_OOBE_RESET);
+ } else {
+ DCHECK(LoginDisplayHost::default_host());
+ LoginDisplayHost::default_host()->StartWizard(
+ OobeScreen::SCREEN_OOBE_RESET);
+ }
+ }
+}
+
+void CoreOobeHandler::ShowEnableDebuggingScreen() {
+ // Don't recreate WizardController if it already exists.
+ WizardController* wizard_controller =
+ WizardController::default_controller();
+ if (wizard_controller && !wizard_controller->login_screen_started()) {
+ wizard_controller->AdvanceToScreen(
+ OobeScreen::SCREEN_OOBE_ENABLE_DEBUGGING);
+ }
+}
+
+void CoreOobeHandler::ShowActiveDirectoryPasswordChangeScreen(
+ const std::string& username) {
+ CallJSOrDefer("showActiveDirectoryPasswordChangeScreen", username);
+}
+
+void CoreOobeHandler::ShowSignInUI(const std::string& email) {
+ CallJSOrDefer("showSigninUI", email);
+}
+
+void CoreOobeHandler::ResetSignInUI(bool force_online) {
+ CallJSOrDefer("resetSigninUI", force_online);
+}
+
+void CoreOobeHandler::ClearUserPodPassword() {
+ CallJSOrDefer("clearUserPodPassword");
+}
+
+void CoreOobeHandler::RefocusCurrentPod() {
+ CallJSOrDefer("refocusCurrentPod");
+}
+
+void CoreOobeHandler::ShowPasswordChangedScreen(bool show_password_error,
+ const std::string& email) {
+ CallJSOrDefer("showPasswordChangedScreen", show_password_error, email);
+}
+
+void CoreOobeHandler::SetUsageStats(bool checked) {
+ CallJSOrDefer("setUsageStats", checked);
+}
+
+void CoreOobeHandler::SetOemEulaUrl(const std::string& oem_eula_url) {
+ CallJSOrDefer("setOemEulaUrl", oem_eula_url);
+}
+
+void CoreOobeHandler::SetTpmPassword(const std::string& tpm_password) {
+ CallJSOrDefer("setTpmPassword", tpm_password);
+}
+
+void CoreOobeHandler::ClearErrors() {
+ CallJSOrDefer("clearErrors");
+}
+
+void CoreOobeHandler::ReloadContent(const base::DictionaryValue& dictionary) {
+ CallJSOrDefer("reloadContent", dictionary);
+}
+
+void CoreOobeHandler::ReloadEulaContent(
+ const base::DictionaryValue& dictionary) {
+ CallJSOrDefer("reloadEulaContent", dictionary);
+}
+
+void CoreOobeHandler::ShowControlBar(bool show) {
+ CallJSOrDefer("showControlBar", show);
+}
+
+void CoreOobeHandler::SetVirtualKeyboardShown(bool shown) {
+ CallJSOrDefer("setVirtualKeyboardShown", shown);
+}
+
+void CoreOobeHandler::SetClientAreaSize(int width, int height) {
+ CallJSOrDefer("setClientAreaSize", width, height);
+}
+
+void CoreOobeHandler::HandleInitialized() {
+ ExecuteDeferredJSCalls();
+ oobe_ui_->InitializeHandlers();
+}
+
+void CoreOobeHandler::HandleSkipUpdateEnrollAfterEula() {
+ WizardController* controller = WizardController::default_controller();
+ DCHECK(controller);
+ if (controller)
+ controller->SkipUpdateEnrollAfterEula();
+}
+
+void CoreOobeHandler::HandleUpdateCurrentScreen(
+ const std::string& screen_name) {
+ const OobeScreen screen = GetOobeScreenFromName(screen_name);
+ oobe_ui_->CurrentScreenChanged(screen);
+ // TODO(mash): Support EventRewriterController; see crbug.com/647781
+ if (!ash_util::IsRunningInMash()) {
+ KeyboardDrivenEventRewriter::GetInstance()->SetArrowToTabRewritingEnabled(
+ screen == OobeScreen::SCREEN_OOBE_EULA);
+ }
+}
+
+void CoreOobeHandler::HandleEnableHighContrast(bool enabled) {
+ AccessibilityManager::Get()->EnableHighContrast(enabled);
+}
+
+void CoreOobeHandler::HandleEnableLargeCursor(bool enabled) {
+ AccessibilityManager::Get()->EnableLargeCursor(enabled);
+}
+
+void CoreOobeHandler::HandleEnableVirtualKeyboard(bool enabled) {
+ AccessibilityManager::Get()->EnableVirtualKeyboard(enabled);
+}
+
+void CoreOobeHandler::HandleSetForceDisableVirtualKeyboard(bool disable) {
+ scoped_keyboard_disabler_.SetForceDisableVirtualKeyboard(disable);
+
+ if (disable) {
+ keyboard::KeyboardController* controller =
+ keyboard::KeyboardController::GetInstance();
+ if (controller) {
+ controller->HideKeyboard(
+ keyboard::KeyboardController::HIDE_REASON_AUTOMATIC);
+ }
+ }
+}
+
+void CoreOobeHandler::HandleEnableScreenMagnifier(bool enabled) {
+ // TODO(nkostylev): Add support for partial screen magnifier.
+ DCHECK(MagnificationManager::Get());
+ MagnificationManager::Get()->SetMagnifierEnabled(enabled);
+}
+
+void CoreOobeHandler::HandleEnableSpokenFeedback(bool /* enabled */) {
+ // Checkbox is initialized on page init and updates when spoken feedback
+ // setting is changed so just toggle spoken feedback here.
+ AccessibilityManager::Get()->ToggleSpokenFeedback(
+ ash::A11Y_NOTIFICATION_NONE);
+}
+
+void CoreOobeHandler::HandleSetDeviceRequisition(
+ const std::string& requisition) {
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ std::string initial_requisition =
+ connector->GetDeviceCloudPolicyManager()->GetDeviceRequisition();
+ connector->GetDeviceCloudPolicyManager()->SetDeviceRequisition(requisition);
+ // Exit Chrome to force the restart as soon as a new requisition is set.
+ if (initial_requisition !=
+ connector->GetDeviceCloudPolicyManager()->GetDeviceRequisition()) {
+ chrome::AttemptRestart();
+ }
+}
+
+void CoreOobeHandler::HandleScreenAssetsLoaded(
+ const std::string& screen_async_load_id) {
+ oobe_ui_->OnScreenAssetsLoaded(screen_async_load_id);
+}
+
+void CoreOobeHandler::HandleSkipToLoginForTesting(
+ const base::ListValue* args) {
+ LoginScreenContext context(args);
+ if (WizardController::default_controller())
+ WizardController::default_controller()->SkipToLoginForTesting(context);
+}
+
+void CoreOobeHandler::HandleToggleResetScreen() {
+ ShowDeviceResetScreen();
+}
+
+void CoreOobeHandler::HandleEnableDebuggingScreen() {
+ ShowEnableDebuggingScreen();
+}
+
+void CoreOobeHandler::ShowOobeUI(bool show) {
+ if (show == show_oobe_ui_)
+ return;
+
+ show_oobe_ui_ = show;
+
+ if (page_is_ready())
+ UpdateOobeUIVisibility();
+}
+
+void CoreOobeHandler::UpdateShutdownAndRebootVisibility(
+ bool reboot_on_shutdown) {
+ CallJSOrDefer("showShutdown", !reboot_on_shutdown);
+}
+
+void CoreOobeHandler::UpdateA11yState() {
+ if (ash_util::IsRunningInMash()) {
+ NOTIMPLEMENTED();
+ return;
+ }
+ // TODO(dpolukhin): crbug.com/412891
+ DCHECK(MagnificationManager::Get());
+ base::DictionaryValue a11y_info;
+ a11y_info.SetBoolean("highContrastEnabled",
+ AccessibilityManager::Get()->IsHighContrastEnabled());
+ a11y_info.SetBoolean("largeCursorEnabled",
+ AccessibilityManager::Get()->IsLargeCursorEnabled());
+ a11y_info.SetBoolean("spokenFeedbackEnabled",
+ AccessibilityManager::Get()->IsSpokenFeedbackEnabled());
+ a11y_info.SetBoolean("screenMagnifierEnabled",
+ MagnificationManager::Get()->IsMagnifierEnabled());
+ a11y_info.SetBoolean("virtualKeyboardEnabled",
+ AccessibilityManager::Get()->IsVirtualKeyboardEnabled());
+ CallJSOrDefer("refreshA11yInfo", a11y_info);
+}
+
+void CoreOobeHandler::UpdateOobeUIVisibility() {
+ const std::string& display = oobe_ui_->display_type();
+ CallJSOrDefer("showAPIKeysNotice", !google_apis::HasKeysConfigured() &&
+ (display == OobeUI::kOobeDisplay ||
+ display == OobeUI::kLoginDisplay));
+
+ // Don't show version label on the stable channel by default.
+ bool should_show_version = true;
+ version_info::Channel channel = chrome::GetChannel();
+ if (channel == version_info::Channel::STABLE ||
+ channel == version_info::Channel::BETA) {
+ should_show_version = false;
+ }
+ CallJSOrDefer("showVersion", should_show_version);
+ CallJSOrDefer("showOobeUI", show_oobe_ui_);
+ if (system::InputDeviceSettings::Get()->ForceKeyboardDrivenUINavigation())
+ CallJSOrDefer("enableKeyboardFlow", true);
+}
+
+void CoreOobeHandler::OnOSVersionLabelTextUpdated(
+ const std::string& os_version_label_text) {
+ UpdateLabel("version", os_version_label_text);
+}
+
+void CoreOobeHandler::OnEnterpriseInfoUpdated(
+ const std::string& message_text, const std::string& asset_id) {
+ CallJSOrDefer("setEnterpriseInfo", message_text, asset_id);
+}
+
+void CoreOobeHandler::OnDeviceInfoUpdated(const std::string& bluetooth_name) {
+ CallJSOrDefer("setBluetoothDeviceInfo", bluetooth_name);
+}
+
+ui::EventSink* CoreOobeHandler::GetEventSink() {
+ return ash::Shell::GetPrimaryRootWindow()->GetHost()->event_sink();
+}
+
+void CoreOobeHandler::UpdateLabel(const std::string& id,
+ const std::string& text) {
+ CallJSOrDefer("setLabelText", id, text);
+}
+
+void CoreOobeHandler::UpdateDeviceRequisition() {
+ policy::DeviceCloudPolicyManagerChromeOS* policy_manager =
+ g_browser_process->platform_part()
+ ->browser_policy_connector_chromeos()
+ ->GetDeviceCloudPolicyManager();
+ if (policy_manager) {
+ CallJSOrDefer("updateDeviceRequisition",
+ policy_manager->GetDeviceRequisition());
+ }
+}
+
+void CoreOobeHandler::UpdateKeyboardState() {
+ keyboard::KeyboardController* keyboard_controller =
+ keyboard::KeyboardController::GetInstance();
+ if (keyboard_controller) {
+ gfx::Rect bounds = keyboard_controller->current_keyboard_bounds();
+ ShowControlBar(bounds.IsEmpty());
+ SetVirtualKeyboardShown(!bounds.IsEmpty());
+ }
+}
+
+void CoreOobeHandler::UpdateClientAreaSize() {
+ const gfx::Size size =
+ display::Screen::GetScreen()->GetPrimaryDisplay().size();
+ SetClientAreaSize(size.width(), size.height());
+}
+
+void CoreOobeHandler::OnAccessibilityStatusChanged(
+ const AccessibilityStatusEventDetails& details) {
+ if (details.notification_type == ACCESSIBILITY_MANAGER_SHUTDOWN)
+ accessibility_subscription_.reset();
+ else
+ UpdateA11yState();
+}
+
+void CoreOobeHandler::HandleLaunchHelpApp(double help_topic_id) {
+ if (!help_app_.get())
+ help_app_ = new HelpAppLauncher(GetNativeWindow());
+ help_app_->ShowHelpTopic(
+ static_cast<HelpAppLauncher::HelpTopic>(help_topic_id));
+}
+
+void CoreOobeHandler::HandleHeaderBarVisible() {
+ LoginDisplayHost* login_display_host = LoginDisplayHost::default_host();
+ if (login_display_host)
+ login_display_host->SetStatusAreaVisible(true);
+ if (ScreenLocker::default_screen_locker())
+ ScreenLocker::default_screen_locker()->delegate()->OnHeaderBarVisible();
+}
+
+void CoreOobeHandler::HandleRaiseTabKeyEvent(bool reverse) {
+ ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_TAB, ui::EF_NONE);
+ if (reverse)
+ event.set_flags(ui::EF_SHIFT_DOWN);
+ SendEventToSink(&event);
+}
+
+void CoreOobeHandler::HandleSetOobeBootstrappingSlave() {
+ const bool is_slave = g_browser_process->local_state()->GetBoolean(
+ prefs::kIsBootstrappingSlave);
+ if (is_slave)
+ return;
+ g_browser_process->local_state()->SetBoolean(prefs::kIsBootstrappingSlave,
+ true);
+ chrome::AttemptRestart();
+}
+
+void CoreOobeHandler::InitDemoModeDetection() {
+ demo_mode_detector_.InitDetection();
+}
+
+void CoreOobeHandler::StopDemoModeDetection() {
+ demo_mode_detector_.StopDetection();
+}
+
+} // namespace chromeos
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
new file mode 100644
index 00000000000..c81057a8070
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
@@ -0,0 +1,172 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_CORE_OOBE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_CORE_OOBE_HANDLER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
+#include "chrome/browser/chromeos/login/demo_mode/demo_mode_detector.h"
+#include "chrome/browser/chromeos/login/screens/core_oobe_view.h"
+#include "chrome/browser/chromeos/login/version_info_updater.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_webui_handler.h"
+#include "ui/events/event_source.h"
+#include "ui/keyboard/scoped_keyboard_disabler.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace ui {
+class EventSink;
+}
+
+namespace chromeos {
+
+class HelpAppLauncher;
+class OobeUI;
+
+// The core handler for Javascript messages related to the "oobe" view.
+class CoreOobeHandler : public BaseWebUIHandler,
+ public VersionInfoUpdater::Delegate,
+ public CoreOobeView,
+ public ui::EventSource {
+ public:
+ explicit CoreOobeHandler(OobeUI* oobe_ui,
+ JSCallsContainer* js_calls_container);
+ ~CoreOobeHandler() override;
+
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void Initialize() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // VersionInfoUpdater::Delegate implementation:
+ void OnOSVersionLabelTextUpdated(
+ const std::string& os_version_label_text) override;
+ void OnEnterpriseInfoUpdated(const std::string& message_text,
+ const std::string& asset_id) override;
+ void OnDeviceInfoUpdated(const std::string& bluetooth_name) override;
+
+ // ui::EventSource implementation:
+ ui::EventSink* GetEventSink() override;
+
+ // Show or hide OOBE UI.
+ void ShowOobeUI(bool show);
+
+ bool show_oobe_ui() const {
+ return show_oobe_ui_;
+ }
+
+ // If |reboot_on_shutdown| is true, the reboot button becomes visible
+ // and the shutdown button is hidden. Vice versa if |reboot_on_shutdown| is
+ // false.
+ void UpdateShutdownAndRebootVisibility(bool reboot_on_shutdown);
+
+ private:
+ // CoreOobeView implementation:
+ void ShowSignInError(int login_attempts,
+ const std::string& error_text,
+ const std::string& help_link_text,
+ HelpAppLauncher::HelpTopic help_topic_id) override;
+ void ShowTpmError() override;
+ void ShowSignInUI(const std::string& email) override;
+ void ResetSignInUI(bool force_online) override;
+ void ClearUserPodPassword() override;
+ void RefocusCurrentPod() override;
+ void ShowPasswordChangedScreen(bool show_password_error,
+ const std::string& email) override;
+ void SetUsageStats(bool checked) override;
+ void SetOemEulaUrl(const std::string& oem_eula_url) override;
+ void SetTpmPassword(const std::string& tmp_password) override;
+ void ClearErrors() override;
+ void ReloadContent(const base::DictionaryValue& dictionary) override;
+ void ReloadEulaContent(const base::DictionaryValue& dictionary) override;
+ void ShowControlBar(bool show) override;
+ void SetVirtualKeyboardShown(bool displayed) override;
+ void SetClientAreaSize(int width, int height) override;
+ void ShowDeviceResetScreen() override;
+ void ShowEnableDebuggingScreen() override;
+ void ShowActiveDirectoryPasswordChangeScreen(
+ const std::string& username) override;
+
+ void InitDemoModeDetection() override;
+ void StopDemoModeDetection() override;
+ void UpdateKeyboardState() override;
+
+ // Handlers for JS WebUI messages.
+ void HandleEnableLargeCursor(bool enabled);
+ void HandleEnableHighContrast(bool enabled);
+ void HandleEnableVirtualKeyboard(bool enabled);
+ void HandleSetForceDisableVirtualKeyboard(bool disable);
+ void HandleEnableScreenMagnifier(bool enabled);
+ void HandleEnableSpokenFeedback(bool /* enabled */);
+ void HandleInitialized();
+ void HandleSkipUpdateEnrollAfterEula();
+ void HandleUpdateCurrentScreen(const std::string& screen);
+ void HandleSetDeviceRequisition(const std::string& requisition);
+ void HandleScreenAssetsLoaded(const std::string& screen_async_load_id);
+ void HandleSkipToLoginForTesting(const base::ListValue* args);
+ void HandleLaunchHelpApp(double help_topic_id);
+ void HandleToggleResetScreen();
+ void HandleEnableDebuggingScreen();
+ void HandleHeaderBarVisible();
+ void HandleSetOobeBootstrappingSlave();
+
+ // When keyboard_utils.js arrow key down event is reached, raise it
+ // to tab/shift-tab event.
+ void HandleRaiseTabKeyEvent(bool reverse);
+
+ // Updates a11y menu state based on the current a11y features state(on/off).
+ void UpdateA11yState();
+
+ // Calls javascript to sync OOBE UI visibility with show_oobe_ui_.
+ void UpdateOobeUIVisibility();
+
+ // Updates label with specified id with specified text.
+ void UpdateLabel(const std::string& id, const std::string& text);
+
+ // Updates the device requisition string on the UI side.
+ void UpdateDeviceRequisition();
+
+ // Updates client area size based on the primary screen size.
+ void UpdateClientAreaSize();
+
+ // Notification of a change in the accessibility settings.
+ void OnAccessibilityStatusChanged(
+ const AccessibilityStatusEventDetails& details);
+
+ // Owner of this handler.
+ OobeUI* oobe_ui_ = nullptr;
+
+ // True if we should show OOBE instead of login.
+ bool show_oobe_ui_ = false;
+
+ // Updates when version info is changed.
+ VersionInfoUpdater version_info_updater_;
+
+ // Help application used for help dialogs.
+ scoped_refptr<HelpAppLauncher> help_app_;
+
+ std::unique_ptr<AccessibilityStatusSubscription> accessibility_subscription_;
+
+ DemoModeDetector demo_mode_detector_;
+
+ keyboard::ScopedKeyboardDisabler scoped_keyboard_disabler_;
+
+ DISALLOW_COPY_AND_ASSIGN(CoreOobeHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_CORE_OOBE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/device_disabled_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/device_disabled_screen_handler.cc
new file mode 100644
index 00000000000..6211c4fd4b2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/device_disabled_screen_handler.cc
@@ -0,0 +1,80 @@
+// 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/ui/webui/chromeos/login/device_disabled_screen_handler.h"
+
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.DeviceDisabledScreen";
+
+} // namespace
+
+namespace chromeos {
+
+DeviceDisabledScreenHandler::DeviceDisabledScreenHandler()
+ : BaseScreenHandler(kScreenId) {
+ set_call_js_prefix(kJsScreenPath);
+}
+
+DeviceDisabledScreenHandler::~DeviceDisabledScreenHandler() {
+ if (delegate_)
+ delegate_->OnViewDestroyed(this);
+}
+
+void DeviceDisabledScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+
+ if (delegate_) {
+ CallJS("setEnrollmentDomain", delegate_->GetEnrollmentDomain());
+ CallJS("setMessage", delegate_->GetMessage());
+ }
+ ShowScreen(kScreenId);
+}
+
+void DeviceDisabledScreenHandler::Hide() {
+ show_on_init_ = false;
+}
+
+void DeviceDisabledScreenHandler::SetDelegate(Delegate* delegate) {
+ delegate_ = delegate;
+ if (page_is_ready())
+ Initialize();
+}
+
+void DeviceDisabledScreenHandler::UpdateMessage(const std::string& message) {
+ if (page_is_ready())
+ CallJS("setMessage", message);
+}
+
+void DeviceDisabledScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("deviceDisabledHeading", IDS_DEVICE_DISABLED_HEADING);
+ builder->Add("deviceDisabledExplanationWithDomain",
+ IDS_DEVICE_DISABLED_EXPLANATION_WITH_DOMAIN);
+ builder->Add("deviceDisabledExplanationWithoutDomain",
+ IDS_DEVICE_DISABLED_EXPLANATION_WITHOUT_DOMAIN);
+}
+
+void DeviceDisabledScreenHandler::Initialize() {
+ if (!page_is_ready() || !delegate_)
+ return;
+
+ if (show_on_init_) {
+ Show();
+ show_on_init_ = false;
+ }
+}
+
+void DeviceDisabledScreenHandler::RegisterMessages() {
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/device_disabled_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/device_disabled_screen_handler.h
new file mode 100644
index 00000000000..35a466cdaa3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/device_disabled_screen_handler.h
@@ -0,0 +1,47 @@
+// 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_UI_WEBUI_CHROMEOS_LOGIN_DEVICE_DISABLED_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_DEVICE_DISABLED_SCREEN_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/screens/device_disabled_screen_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+
+namespace chromeos {
+
+// WebUI implementation of DeviceDisabledScreenActor.
+class DeviceDisabledScreenHandler : public DeviceDisabledScreenView,
+ public BaseScreenHandler {
+ public:
+ DeviceDisabledScreenHandler();
+ ~DeviceDisabledScreenHandler() override;
+
+ // DeviceDisabledScreenActor:
+ void Show() override;
+ void Hide() override;
+ void SetDelegate(Delegate* delegate) override;
+ void UpdateMessage(const std::string& message) override;
+
+ // BaseScreenHandler:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void Initialize() override;
+
+ private:
+ // WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ Delegate* delegate_ = nullptr;
+
+ // Indicates whether the screen should be shown right after initialization.
+ bool show_on_init_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(DeviceDisabledScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_DEVICE_DISABLED_SCREEN_HANDLER_H_
+
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.cc
new file mode 100644
index 00000000000..e1234fe5578
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.cc
@@ -0,0 +1,291 @@
+// Copyright (c) 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/ui/webui/chromeos/login/enable_debugging_screen_handler.h"
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/browser/chromeos/login/ui/login_web_dialog.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/cryptohome_client.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/debug_daemon_client.h"
+#include "chromeos/dbus/power_manager_client.h"
+#include "components/login/localized_values_builder.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.EnableDebuggingScreen";
+
+} // namespace
+
+namespace chromeos {
+
+EnableDebuggingScreenHandler::EnableDebuggingScreenHandler()
+ : BaseScreenHandler(kScreenId), weak_ptr_factory_(this) {
+ set_call_js_prefix(kJsScreenPath);
+}
+
+EnableDebuggingScreenHandler::~EnableDebuggingScreenHandler() {
+ if (delegate_)
+ delegate_->OnViewDestroyed(this);
+}
+
+void EnableDebuggingScreenHandler::ShowWithParams() {
+ ShowScreen(kScreenId);
+
+ UpdateUIState(UI_STATE_WAIT);
+
+ DVLOG(1) << "Showing enable debugging screen.";
+
+ // Wait for cryptohomed before checking debugd. See http://crbug.com/440506.
+ chromeos::CryptohomeClient* client =
+ chromeos::DBusThreadManager::Get()->GetCryptohomeClient();
+ client->WaitForServiceToBeAvailable(base::Bind(
+ &EnableDebuggingScreenHandler::OnCryptohomeDaemonAvailabilityChecked,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void EnableDebuggingScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+
+ ShowWithParams();
+}
+
+void EnableDebuggingScreenHandler::Hide() {
+ weak_ptr_factory_.InvalidateWeakPtrs();
+}
+
+void EnableDebuggingScreenHandler::SetDelegate(Delegate* delegate) {
+ delegate_ = delegate;
+ if (page_is_ready())
+ Initialize();
+}
+
+void EnableDebuggingScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("enableDebuggingScreenTitle",
+ IDS_ENABLE_DEBUGGING_SCREEN_TITLE);
+ builder->Add("enableDebuggingScreenAccessibleTitle",
+ IDS_ENABLE_DEBUGGING_SCREEN_TITLE);
+ builder->Add("enableDebuggingCancelButton", IDS_CANCEL);
+ builder->Add("enableDebuggingOKButton", IDS_OK);
+ builder->Add("enableDebuggingRemoveButton",
+ IDS_ENABLE_DEBUGGING_REMOVE_ROOTFS_BUTTON);
+ builder->Add("enableDebuggingEnableButton",
+ IDS_ENABLE_DEBUGGING_ENABLE_BUTTON);
+ builder->Add("enableDebuggingRemveRootfsMessage",
+ IDS_ENABLE_DEBUGGING_SCREEN_ROOTFS_REMOVE_MSG);
+ builder->Add("enableDebuggingLearnMore",
+ IDS_ENABLE_DEBUGGING_LEARN_MORE);
+ builder->Add("enableDebuggingSetupMessage",
+ IDS_ENABLE_DEBUGGING_SETUP_MESSAGE);
+ builder->Add("enableDebuggingWaitMessage",
+ IDS_ENABLE_DEBUGGING_WAIT_MESSAGE);
+ builder->AddF("enableDebuggingWarningTitle",
+ IDS_ENABLE_DEBUGGING_SCREEN_WARNING_MSG,
+ IDS_SHORT_PRODUCT_NAME);
+ builder->AddF("enableDebuggingDoneMessage",
+ IDS_ENABLE_DEBUGGING_DONE_MESSAGE,
+ IDS_SHORT_PRODUCT_NAME);
+ builder->Add("enableDebuggingErrorTitle",
+ IDS_ENABLE_DEBUGGING_ERROR_TITLE);
+ builder->AddF("enableDebuggingErrorMessage",
+ IDS_ENABLE_DEBUGGING_ERROR_MESSAGE,
+ IDS_SHORT_PRODUCT_NAME);
+ builder->Add("enableDebuggingPasswordLabel",
+ IDS_ENABLE_DEBUGGING_ROOT_PASSWORD_LABEL);
+ builder->Add("enableDebuggingConfirmPasswordLabel",
+ IDS_ENABLE_DEBUGGING_CONFIRM_PASSWORD_LABEL);
+ builder->Add("enableDebuggingPasswordLengthNote",
+ IDS_ENABLE_DEBUGGING_EMPTY_ROOT_PASSWORD_LABEL);
+}
+
+// static
+void EnableDebuggingScreenHandler::RegisterPrefs(PrefRegistrySimple* registry) {
+ registry->RegisterBooleanPref(prefs::kDebuggingFeaturesRequested, false);
+}
+
+void EnableDebuggingScreenHandler::Initialize() {
+ if (!page_is_ready() || !delegate_)
+ return;
+
+ if (show_on_init_) {
+ Show();
+ show_on_init_ = false;
+ }
+}
+
+void EnableDebuggingScreenHandler::RegisterMessages() {
+ AddCallback("enableDebuggingOnCancel",
+ &EnableDebuggingScreenHandler::HandleOnCancel);
+ AddCallback("enableDebuggingOnDone",
+ &EnableDebuggingScreenHandler::HandleOnDone);
+ AddCallback("enableDebuggingOnLearnMore",
+ &EnableDebuggingScreenHandler::HandleOnLearnMore);
+ AddCallback("enableDebuggingOnRemoveRootFSProtection",
+ &EnableDebuggingScreenHandler::HandleOnRemoveRootFSProtection);
+ AddCallback("enableDebuggingOnSetup",
+ &EnableDebuggingScreenHandler::HandleOnSetup);
+}
+
+void EnableDebuggingScreenHandler::HandleOnCancel() {
+ if (delegate_)
+ delegate_->OnExit(false);
+}
+
+void EnableDebuggingScreenHandler::HandleOnDone() {
+ if (delegate_)
+ delegate_->OnExit(true);
+}
+
+void EnableDebuggingScreenHandler::HandleOnRemoveRootFSProtection() {
+ UpdateUIState(UI_STATE_WAIT);
+ chromeos::DebugDaemonClient* client =
+ chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
+ client->RemoveRootfsVerification(
+ base::Bind(&EnableDebuggingScreenHandler::OnRemoveRootfsVerification,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void EnableDebuggingScreenHandler::HandleOnSetup(
+ const std::string& password) {
+ UpdateUIState(UI_STATE_WAIT);
+ chromeos::DebugDaemonClient* client =
+ chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
+ client->EnableDebuggingFeatures(
+ password,
+ base::Bind(&EnableDebuggingScreenHandler::OnEnableDebuggingFeatures,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void EnableDebuggingScreenHandler::OnCryptohomeDaemonAvailabilityChecked(
+ bool service_is_available) {
+ DVLOG(1) << "Enable-debugging-screen: cryptohomed availability="
+ << service_is_available;
+ if (!service_is_available) {
+ LOG(ERROR) << "Crypthomed is not available.";
+ UpdateUIState(UI_STATE_ERROR);
+ return;
+ }
+
+ chromeos::DebugDaemonClient* client =
+ chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
+ client->WaitForServiceToBeAvailable(base::Bind(
+ &EnableDebuggingScreenHandler::OnDebugDaemonServiceAvailabilityChecked,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void EnableDebuggingScreenHandler::OnDebugDaemonServiceAvailabilityChecked(
+ bool service_is_available) {
+ DVLOG(1) << "Enable-debugging-screen: debugd availability="
+ << service_is_available;
+ if (!service_is_available) {
+ LOG(ERROR) << "Debug daemon is not available.";
+ UpdateUIState(UI_STATE_ERROR);
+ return;
+ }
+
+ // Check the status of debugging features.
+ chromeos::DebugDaemonClient* client =
+ chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
+ client->QueryDebuggingFeatures(
+ base::Bind(&EnableDebuggingScreenHandler::OnQueryDebuggingFeatures,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+// Removes rootfs verification, add flag to start with enable debugging features
+// screen and reboots the machine.
+void EnableDebuggingScreenHandler::OnRemoveRootfsVerification(bool success) {
+ if (!success) {
+ UpdateUIState(UI_STATE_ERROR);
+ return;
+ }
+
+ PrefService* prefs = g_browser_process->local_state();
+ prefs->SetBoolean(prefs::kDebuggingFeaturesRequested, true);
+ prefs->CommitPendingWrite();
+ chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
+}
+
+void EnableDebuggingScreenHandler::OnEnableDebuggingFeatures(bool success) {
+ if (!success) {
+ UpdateUIState(UI_STATE_ERROR);
+ return;
+ }
+
+ UpdateUIState(UI_STATE_DONE);
+}
+
+void EnableDebuggingScreenHandler::OnQueryDebuggingFeatures(bool success,
+ int features_flag) {
+ DVLOG(1) << "Enable-debugging-screen: OnQueryDebuggingFeatures"
+ << ", success=" << success
+ << ", features=" << features_flag;
+ if (!success ||
+ features_flag == debugd::DevFeatureFlag::DEV_FEATURES_DISABLED) {
+ UpdateUIState(UI_STATE_ERROR);
+ return;
+ }
+
+ if ((features_flag &
+ debugd::DevFeatureFlag::DEV_FEATURE_ROOTFS_VERIFICATION_REMOVED) == 0) {
+ UpdateUIState(UI_STATE_REMOVE_PROTECTION);
+ return;
+ }
+
+ if ((features_flag & DebugDaemonClient::DEV_FEATURE_ALL_ENABLED) !=
+ DebugDaemonClient::DEV_FEATURE_ALL_ENABLED) {
+ UpdateUIState(UI_STATE_SETUP);
+ } else {
+ UpdateUIState(UI_STATE_DONE);
+ }
+}
+
+void EnableDebuggingScreenHandler::UpdateUIState(
+ EnableDebuggingScreenHandler::UIState state) {
+ if (state == UI_STATE_SETUP ||
+ state == UI_STATE_ERROR ||
+ state == UI_STATE_DONE) {
+ PrefService* prefs = g_browser_process->local_state();
+ prefs->ClearPref(prefs::kDebuggingFeaturesRequested);
+ prefs->CommitPendingWrite();
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "login.EnableDebuggingScreen.updateState",
+ base::Value(static_cast<int>(state)));
+}
+
+void EnableDebuggingScreenHandler::HandleOnLearnMore() {
+ VLOG(1) << "Trying to view the help article about debugging features.";
+ const std::string help_content =
+ l10n_util::GetStringUTF8(IDS_ENABLE_DEBUGGING_HELP);
+ const GURL data_url = GURL("data:text/html;charset=utf-8," + help_content);
+
+ LoginWebDialog* dialog = new LoginWebDialog(
+ Profile::FromWebUI(web_ui()),
+ NULL,
+ GetNativeWindow(),
+ base::string16(),
+ data_url);
+ dialog->Show();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.h
new file mode 100644
index 00000000000..30332fa14ee
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.h
@@ -0,0 +1,91 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ENABLE_DEBUGGING_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ENABLE_DEBUGGING_SCREEN_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/login/help_app_launcher.h"
+#include "chrome/browser/chromeos/login/screens/enable_debugging_screen_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+
+class PrefRegistrySimple;
+
+namespace chromeos {
+
+// WebUI implementation of EnableDebuggingScreenView.
+class EnableDebuggingScreenHandler : public EnableDebuggingScreenView,
+ public BaseScreenHandler {
+ public:
+ EnableDebuggingScreenHandler();
+ ~EnableDebuggingScreenHandler() override;
+
+ // EnableDebuggingScreenView implementation:
+ void Show() override;
+ void Hide() override;
+ void SetDelegate(Delegate* delegate) override;
+
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void Initialize() override;
+
+ // WebUIMessageHandler implementation:
+ void RegisterMessages() override;
+
+ // Registers Local State preferences.
+ static void RegisterPrefs(PrefRegistrySimple* registry);
+
+ private:
+ enum UIState {
+ UI_STATE_ERROR = -1,
+ UI_STATE_REMOVE_PROTECTION = 1,
+ UI_STATE_SETUP = 2,
+ UI_STATE_WAIT = 3,
+ UI_STATE_DONE = 4,
+ };
+
+ // JS messages handlers.
+ void HandleOnCancel();
+ void HandleOnDone();
+ void HandleOnLearnMore();
+ void HandleOnRemoveRootFSProtection();
+ void HandleOnSetup(const std::string& password);
+
+ void ShowWithParams();
+
+ // Callback for CryptohomeClient::WaitForServiceToBeAvailable
+ void OnCryptohomeDaemonAvailabilityChecked(bool service_is_available);
+
+ // Callback for DebugDaemonClient::WaitForServiceToBeAvailable
+ void OnDebugDaemonServiceAvailabilityChecked(bool service_is_available);
+
+ // Callback for DebugDaemonClient::EnableDebuggingFeatures().
+ void OnEnableDebuggingFeatures(bool success);
+
+ // Callback for DebugDaemonClient::QueryDebuggingFeatures().
+ void OnQueryDebuggingFeatures(bool success, int features_flag);
+
+ // Callback for DebugDaemonClient::RemoveRootfsVerification().
+ void OnRemoveRootfsVerification(bool success);
+
+ // Updates UI state.
+ void UpdateUIState(UIState state);
+
+ Delegate* delegate_ = nullptr;
+
+ // Keeps whether screen should be shown right after initialization.
+ bool show_on_init_ = false;
+
+ base::WeakPtrFactory<EnableDebuggingScreenHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(EnableDebuggingScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ENABLE_DEBUGGING_SCREEN_HANDLER_H_
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
new file mode 100644
index 00000000000..c57c266e14f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc
@@ -0,0 +1,566 @@
+// 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/chromeos/login/encryption_migration_screen_handler.h"
+
+#include <cmath>
+#include <string>
+#include <utility>
+
+#include "ash/system/devicetype_utils.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/sys_info.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/arc/arc_migration_constants.h"
+#include "chrome/browser/chromeos/login/ui/login_feedback.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "chromeos/cryptohome/async_method_caller.h"
+#include "chromeos/cryptohome/homedir_methods.h"
+#include "chromeos/dbus/cryptohome_client.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
+#include "chromeos/dbus/power_manager_client.h"
+#include "chromeos/dbus/power_policy_controller.h"
+#include "components/login/localized_values_builder.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/browser_thread.h"
+#include "device/power_save_blocker/power_save_blocker.h"
+#include "ui/base/text/bytes_formatting.h"
+
+namespace {
+
+constexpr char kJsScreenPath[] = "login.EncryptionMigrationScreen";
+
+// Path to the mount point to check the available space.
+constexpr char kCheckStoragePath[] = "/home";
+
+// JS API callbacks names.
+constexpr char kJsApiStartMigration[] = "startMigration";
+constexpr char kJsApiSkipMigration[] = "skipMigration";
+constexpr char kJsApiRequestRestartOnLowStorage[] =
+ "requestRestartOnLowStorage";
+constexpr char kJsApiRequestRestartOnFailure[] = "requestRestartOnFailure";
+constexpr char kJsApiOpenFeedbackDialog[] = "openFeedbackDialog";
+
+// UMA names.
+constexpr char kUmaNameFirstScreen[] = "Cryptohome.MigrationUI.FirstScreen";
+constexpr char kUmaNameUserChoice[] = "Cryptohome.MigrationUI.UserChoice";
+constexpr char kUmaNameMigrationResult[] =
+ "Cryptohome.MigrationUI.MigrationResult";
+constexpr char kUmaNameRemoveCryptohomeResult[] =
+ "Cryptohome.MigrationUI.RemoveCryptohomeResult";
+constexpr char kUmaNameConsumedBatteryPercent[] =
+ "Cryptohome.MigrationUI.ConsumedBatteryPercent";
+constexpr char kUmaNameVisibleScreen[] = "Cryptohome.MigrationUI.VisibleScreen";
+
+// This enum must match the numbering for MigrationUIFirstScreen in
+// histograms/enums.xml. Do not reorder or remove items, only add new items
+// before FIRST_SCREEN_COUNT.
+enum class FirstScreen {
+ FIRST_SCREEN_READY = 0,
+ FIRST_SCREEN_RESUME = 1,
+ FIRST_SCREEN_LOW_STORAGE = 2,
+ FIRST_SCREEN_COUNT
+};
+
+// This enum must match the numbering for MigrationUIUserChoice in
+// histograms/enums.xml. Do not reorder or remove items, only add new items
+// before USER_CHOICE_COUNT.
+enum class UserChoice {
+ USER_CHOICE_UPDATE = 0,
+ USER_CHOICE_SKIP = 1,
+ USER_CHOICE_RESTART_ON_FAILURE = 2,
+ USER_CHOICE_RESTART_ON_LOW_STORAGE = 3,
+ USER_CHOICE_REPORT_AN_ISSUE = 4,
+ USER_CHOICE_COUNT
+};
+
+// This enum must match the numbering for MigrationUIMigrationResult in
+// histograms/enums.xml. Do not reorder or remove items, only add new items
+// before COUNT.
+enum class MigrationResult {
+ SUCCESS_IN_NEW_MIGRATION = 0,
+ SUCCESS_IN_RESUMED_MIGRATION = 1,
+ GENERAL_FAILURE_IN_NEW_MIGRATION = 2,
+ GENERAL_FAILURE_IN_RESUMED_MIGRATION = 3,
+ REQUEST_FAILURE_IN_NEW_MIGRATION = 4,
+ REQUEST_FAILURE_IN_RESUMED_MIGRATION = 5,
+ MOUNT_FAILURE_IN_NEW_MIGRATION = 6,
+ MOUNT_FAILURE_IN_RESUMED_MIGRATION = 7,
+ COUNT
+};
+
+// This enum must match the numbering for MigrationUIRemoveCryptohomeResult in
+// histograms/enums.xml. Do not reorder or remove items, only add new items
+// before COUNT.
+enum class RemoveCryptohomeResult {
+ SUCCESS_IN_NEW_MIGRATION = 0,
+ SUCCESS_IN_RESUMED_MIGRATION = 1,
+ FAILURE_IN_NEW_MIGRATION = 2,
+ FAILURE_IN_RESUMED_MIGRATION = 3,
+ COUNT
+};
+
+bool IsTestingUI() {
+ return base::CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kTestEncryptionMigrationUI);
+}
+
+// Wrapper functions for histogram macros to avoid duplication of expanded code.
+void RecordFirstScreen(FirstScreen first_screen) {
+ UMA_HISTOGRAM_ENUMERATION(kUmaNameFirstScreen, first_screen,
+ FirstScreen::FIRST_SCREEN_COUNT);
+}
+
+void RecordUserChoice(UserChoice user_choice) {
+ UMA_HISTOGRAM_ENUMERATION(kUmaNameUserChoice, user_choice,
+ UserChoice::USER_CHOICE_COUNT);
+}
+
+void RecordMigrationResult(MigrationResult migration_result) {
+ UMA_HISTOGRAM_ENUMERATION(kUmaNameMigrationResult, migration_result,
+ MigrationResult::COUNT);
+}
+
+void RecordRemoveCryptohomeResult(bool success, bool is_resumed_migration) {
+ RemoveCryptohomeResult result =
+ success ? (is_resumed_migration
+ ? RemoveCryptohomeResult::SUCCESS_IN_RESUMED_MIGRATION
+ : RemoveCryptohomeResult::SUCCESS_IN_NEW_MIGRATION)
+ : (is_resumed_migration
+ ? RemoveCryptohomeResult::FAILURE_IN_RESUMED_MIGRATION
+ : RemoveCryptohomeResult::FAILURE_IN_NEW_MIGRATION);
+ UMA_HISTOGRAM_ENUMERATION(kUmaNameRemoveCryptohomeResult, result,
+ RemoveCryptohomeResult::COUNT);
+}
+
+} // namespace
+
+namespace chromeos {
+
+EncryptionMigrationScreenHandler::EncryptionMigrationScreenHandler()
+ : BaseScreenHandler(kScreenId), weak_ptr_factory_(this) {
+ set_call_js_prefix(kJsScreenPath);
+}
+
+EncryptionMigrationScreenHandler::~EncryptionMigrationScreenHandler() {
+ DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
+ if (delegate_)
+ delegate_->OnViewDestroyed(this);
+}
+
+void EncryptionMigrationScreenHandler::Show() {
+ if (!page_is_ready() || !delegate_) {
+ show_on_init_ = true;
+ return;
+ }
+ ShowScreen(kScreenId);
+}
+
+void EncryptionMigrationScreenHandler::Hide() {
+ show_on_init_ = false;
+}
+
+void EncryptionMigrationScreenHandler::SetDelegate(Delegate* delegate) {
+ delegate_ = delegate;
+ if (page_is_ready())
+ Initialize();
+}
+
+void EncryptionMigrationScreenHandler::SetUserContext(
+ const UserContext& user_context) {
+ user_context_ = user_context;
+}
+
+void EncryptionMigrationScreenHandler::SetShouldResume(bool should_resume) {
+ should_resume_ = should_resume;
+ CallJS("setIsResuming", should_resume_);
+}
+
+void EncryptionMigrationScreenHandler::SetContinueLoginCallback(
+ ContinueLoginCallback callback) {
+ continue_login_callback_ = std::move(callback);
+}
+
+void EncryptionMigrationScreenHandler::SetupInitialView() {
+ DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
+ CheckAvailableStorage();
+}
+
+void EncryptionMigrationScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("migrationReadyTitle", IDS_ENCRYPTION_MIGRATION_READY_TITLE);
+ builder->Add("migrationReadyDescription",
+ ash::SubstituteChromeOSDeviceType(
+ IDS_ENCRYPTION_MIGRATION_READY_DESCRIPTION));
+ builder->Add("migrationMigratingTitle",
+ IDS_ENCRYPTION_MIGRATION_MIGRATING_TITLE);
+ builder->Add("migrationMigratingDescription",
+ ash::SubstituteChromeOSDeviceType(
+ IDS_ENCRYPTION_MIGRATION_MIGRATING_DESCRIPTION));
+ builder->Add("migrationProgressLabel",
+ IDS_ENCRYPTION_MIGRATION_PROGRESS_LABEL);
+ builder->Add("migrationBatteryWarningLabel",
+ IDS_ENCRYPTION_MIGRATION_BATTERY_WARNING_LABEL);
+ builder->Add("migrationAskChargeMessage",
+ ash::SubstituteChromeOSDeviceType(
+ IDS_ENCRYPTION_MIGRATION_ASK_CHARGE_MESSAGE));
+ builder->Add("migrationNecessaryBatteryLevelLabel",
+ IDS_ENCRYPTION_MIGRATION_NECESSARY_BATTERY_LEVEL_MESSAGE);
+ builder->Add("migrationChargingLabel",
+ IDS_ENCRYPTION_MIGRATION_CHARGING_LABEL);
+ builder->Add("migrationFailedTitle", IDS_ENCRYPTION_MIGRATION_FAILED_TITLE);
+ builder->Add("migrationFailedSubtitle",
+ IDS_ENCRYPTION_MIGRATION_FAILED_SUBTITLE);
+ builder->Add("migrationFailedMessage",
+ ash::SubstituteChromeOSDeviceType(
+ IDS_ENCRYPTION_MIGRATION_FAILED_MESSAGE));
+ builder->Add("migrationNospaceWarningLabel",
+ IDS_ENCRYPTION_MIGRATION_NOSPACE_WARNING_LABEL);
+ builder->Add("migrationAskFreeSpaceMessage",
+ IDS_ENCRYPTION_MIGRATION_ASK_FREE_SPACE_MESSAGE);
+ builder->Add("migrationAvailableSpaceLabel",
+ IDS_ENCRYPTION_MIGRATION_AVAILABLE_SPACE_LABEL);
+ builder->Add("migrationNecessarySpaceLabel",
+ IDS_ENCRYPTION_MIGRATION_NECESSARY_SPACE_LABEL);
+ builder->Add("migrationButtonUpdate", IDS_ENCRYPTION_MIGRATION_BUTTON_UPDATE);
+ builder->Add("migrationButtonSkip", IDS_ENCRYPTION_MIGRATION_BUTTON_SKIP);
+ builder->Add("migrationButtonRestart",
+ IDS_ENCRYPTION_MIGRATION_BUTTON_RESTART);
+ builder->Add("migrationButtonContinue",
+ IDS_ENCRYPTION_MIGRATION_BUTTON_CONTINUE);
+ builder->Add("migrationButtonSignIn", IDS_ENCRYPTION_MIGRATION_BUTTON_SIGNIN);
+ builder->Add("migrationButtonReportAnIssue", IDS_REPORT_AN_ISSUE);
+}
+
+void EncryptionMigrationScreenHandler::Initialize() {
+ if (!page_is_ready() || !delegate_)
+ return;
+
+ if (show_on_init_) {
+ Show();
+ show_on_init_ = false;
+ }
+}
+
+void EncryptionMigrationScreenHandler::RegisterMessages() {
+ AddCallback(kJsApiStartMigration,
+ &EncryptionMigrationScreenHandler::HandleStartMigration);
+ AddCallback(kJsApiSkipMigration,
+ &EncryptionMigrationScreenHandler::HandleSkipMigration);
+ AddCallback(
+ kJsApiRequestRestartOnLowStorage,
+ &EncryptionMigrationScreenHandler::HandleRequestRestartOnLowStorage);
+ AddCallback(kJsApiRequestRestartOnFailure,
+ &EncryptionMigrationScreenHandler::HandleRequestRestartOnFailure);
+ AddCallback(kJsApiOpenFeedbackDialog,
+ &EncryptionMigrationScreenHandler::HandleOpenFeedbackDialog);
+}
+
+void EncryptionMigrationScreenHandler::PowerChanged(
+ const power_manager::PowerSupplyProperties& proto) {
+ if (proto.has_battery_percent()) {
+ if (!current_battery_percent_) {
+ // If initial battery level is below the minimum, migration should start
+ // automatically once the device is charged enough.
+ if (proto.battery_percent() < arc::kMigrationMinimumBatteryPercent)
+ should_migrate_on_enough_battery_ = true;
+ }
+ current_battery_percent_ = proto.battery_percent();
+ } else {
+ // If battery level is not provided, we regard it as 100% to start migration
+ // immediately.
+ current_battery_percent_ = 100.0;
+ }
+
+ CallJS("setBatteryState", *current_battery_percent_,
+ *current_battery_percent_ >= arc::kMigrationMinimumBatteryPercent,
+ proto.battery_state() ==
+ power_manager::PowerSupplyProperties_BatteryState_CHARGING);
+
+ // If the migration was already requested and the bettery level is enough now,
+ // The migration should start immediately.
+ if (*current_battery_percent_ >= arc::kMigrationMinimumBatteryPercent &&
+ should_migrate_on_enough_battery_) {
+ should_migrate_on_enough_battery_ = false;
+ StartMigration();
+ }
+}
+
+void EncryptionMigrationScreenHandler::HandleStartMigration() {
+ RecordUserChoice(UserChoice::USER_CHOICE_UPDATE);
+ WaitBatteryAndMigrate();
+}
+
+void EncryptionMigrationScreenHandler::HandleSkipMigration() {
+ RecordUserChoice(UserChoice::USER_CHOICE_SKIP);
+ // If the user skips migration, we mount the cryptohome without performing the
+ // migration by reusing UserContext and LoginPerformer which were used in the
+ // previous attempt and dropping |is_forcing_dircrypto| flag in UserContext.
+ // In this case, the user can not launch ARC apps in the session, and will be
+ // asked to do the migration again in the next log-in attempt.
+ if (!continue_login_callback_.is_null()) {
+ user_context_.SetIsForcingDircrypto(false);
+ std::move(continue_login_callback_).Run(user_context_);
+ }
+}
+
+void EncryptionMigrationScreenHandler::HandleRequestRestartOnLowStorage() {
+ RecordUserChoice(UserChoice::USER_CHOICE_RESTART_ON_LOW_STORAGE);
+ DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
+}
+
+void EncryptionMigrationScreenHandler::HandleRequestRestartOnFailure() {
+ RecordUserChoice(UserChoice::USER_CHOICE_RESTART_ON_FAILURE);
+ DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
+}
+
+void EncryptionMigrationScreenHandler::HandleOpenFeedbackDialog() {
+ RecordUserChoice(UserChoice::USER_CHOICE_REPORT_AN_ISSUE);
+ const std::string description = base::StringPrintf(
+ "Auto generated feedback for http://crbug.com/719266.\n"
+ "(uniquifier:%s)",
+ base::Int64ToString(base::Time::Now().ToInternalValue()).c_str());
+ login_feedback_.reset(new LoginFeedback(Profile::FromWebUI(web_ui())));
+ login_feedback_->Request(description, base::Closure());
+}
+
+void EncryptionMigrationScreenHandler::UpdateUIState(UIState state) {
+ if (state == current_ui_state_)
+ return;
+
+ current_ui_state_ = state;
+ CallJS("setUIState", static_cast<int>(state));
+
+ // When this handler is about to show the READY screen, we should get the
+ // latest battery status and show it on the screen.
+ if (state == UIState::READY)
+ DBusThreadManager::Get()->GetPowerManagerClient()->RequestStatusUpdate();
+
+ // We should block power save and not shut down on lid close during migration.
+ if (state == UIState::MIGRATING) {
+ StartBlockingPowerSave();
+ PowerPolicyController::Get()->SetEncryptionMigrationActive(true);
+ } else {
+ StopBlockingPowerSave();
+ PowerPolicyController::Get()->SetEncryptionMigrationActive(false);
+ }
+
+ // Record which screen is visible to the user.
+ // We record it after delay to make sure that the user was actually able
+ // to see the screen (i.e. the screen is not just a flash).
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(
+ &EncryptionMigrationScreenHandler::OnDelayedRecordVisibleScreen,
+ weak_ptr_factory_.GetWeakPtr(), state),
+ base::TimeDelta::FromSeconds(1));
+}
+
+void EncryptionMigrationScreenHandler::CheckAvailableStorage() {
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+ base::Bind(&base::SysInfo::AmountOfFreeDiskSpace,
+ base::FilePath(kCheckStoragePath)),
+ base::Bind(&EncryptionMigrationScreenHandler::OnGetAvailableStorage,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void EncryptionMigrationScreenHandler::OnGetAvailableStorage(int64_t size) {
+ if (size >= arc::kMigrationMinimumAvailableStorage || IsTestingUI()) {
+ if (should_resume_) {
+ RecordFirstScreen(FirstScreen::FIRST_SCREEN_RESUME);
+ WaitBatteryAndMigrate();
+ } else {
+ RecordFirstScreen(FirstScreen::FIRST_SCREEN_READY);
+ UpdateUIState(UIState::READY);
+ }
+ } else {
+ RecordFirstScreen(FirstScreen::FIRST_SCREEN_LOW_STORAGE);
+ CallJS("setAvailableSpaceInString", ui::FormatBytes(size));
+ CallJS("setNecessarySpaceInString",
+ ui::FormatBytes(arc::kMigrationMinimumAvailableStorage));
+ UpdateUIState(UIState::NOT_ENOUGH_STORAGE);
+ }
+}
+
+void EncryptionMigrationScreenHandler::WaitBatteryAndMigrate() {
+ if (current_battery_percent_ &&
+ *current_battery_percent_ >= arc::kMigrationMinimumBatteryPercent) {
+ StartMigration();
+ return;
+ }
+ UpdateUIState(UIState::READY);
+
+ should_migrate_on_enough_battery_ = true;
+ DBusThreadManager::Get()->GetPowerManagerClient()->RequestStatusUpdate();
+}
+
+void EncryptionMigrationScreenHandler::StartMigration() {
+ UpdateUIState(UIState::MIGRATING);
+ initial_battery_percent_ = *current_battery_percent_;
+
+ // Mount the existing eCryptfs vault to a temporary location for migration.
+ cryptohome::MountParameters mount(false);
+ mount.to_migrate_from_ecryptfs = true;
+ cryptohome::HomedirMethods::GetInstance()->MountEx(
+ cryptohome::Identification(user_context_.GetAccountId()),
+ cryptohome::Authorization(GetAuthKey()), mount,
+ base::Bind(&EncryptionMigrationScreenHandler::OnMountExistingVault,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void EncryptionMigrationScreenHandler::OnMountExistingVault(
+ bool success,
+ cryptohome::MountError return_code,
+ const std::string& mount_hash) {
+ if (!success || return_code != cryptohome::MOUNT_ERROR_NONE) {
+ RecordMigrationResult(
+ should_resume_ ? MigrationResult::MOUNT_FAILURE_IN_RESUMED_MIGRATION
+ : MigrationResult::MOUNT_FAILURE_IN_NEW_MIGRATION);
+ UpdateUIState(UIState::MIGRATION_FAILED);
+ return;
+ }
+
+ DBusThreadManager::Get()
+ ->GetCryptohomeClient()
+ ->SetDircryptoMigrationProgressHandler(
+ base::Bind(&EncryptionMigrationScreenHandler::OnMigrationProgress,
+ weak_ptr_factory_.GetWeakPtr()));
+ cryptohome::HomedirMethods::GetInstance()->MigrateToDircrypto(
+ cryptohome::Identification(user_context_.GetAccountId()),
+ base::Bind(&EncryptionMigrationScreenHandler::OnMigrationRequested,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void EncryptionMigrationScreenHandler::StartBlockingPowerSave() {
+ if (!power_save_blocker_) {
+ power_save_blocker_.reset(new device::PowerSaveBlocker(
+ device::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
+ device::PowerSaveBlocker::kReasonOther,
+ "Encryption migration is in progress...",
+ content::BrowserThread::GetTaskRunnerForThread(
+ content::BrowserThread::UI),
+ content::BrowserThread::GetTaskRunnerForThread(
+ content::BrowserThread::FILE)));
+ }
+}
+
+void EncryptionMigrationScreenHandler::StopBlockingPowerSave() {
+ if (power_save_blocker_.get()) {
+ power_save_blocker_.reset();
+ }
+}
+
+void EncryptionMigrationScreenHandler::RemoveCryptohome() {
+ // Set invalid token status so that user is forced to go through Gaia on the
+ // next sign-in.
+ user_manager::UserManager::Get()->SaveUserOAuthStatus(
+ user_context_.GetAccountId(),
+ user_manager::User::OAUTH2_TOKEN_STATUS_INVALID);
+ cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove(
+ cryptohome::Identification(user_context_.GetAccountId()),
+ base::Bind(&EncryptionMigrationScreenHandler::OnRemoveCryptohome,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void EncryptionMigrationScreenHandler::OnRemoveCryptohome(
+ bool success,
+ cryptohome::MountError return_code) {
+ LOG_IF(ERROR, !success) << "Removing cryptohome failed. return code: "
+ << return_code;
+ RecordRemoveCryptohomeResult(success, should_resume_);
+ UpdateUIState(UIState::MIGRATION_FAILED);
+}
+
+cryptohome::KeyDefinition EncryptionMigrationScreenHandler::GetAuthKey() {
+ // |auth_key| is created in the same manner as CryptohomeAuthenticator.
+ const Key* key = user_context_.GetKey();
+ // If the |key| is a plain text password, crash rather than attempting to
+ // mount the cryptohome with a plain text password.
+ CHECK_NE(Key::KEY_TYPE_PASSWORD_PLAIN, key->GetKeyType());
+ // Set the authentication's key label to an empty string, which is a wildcard
+ // allowing any key to match. This is necessary because cryptohomes created by
+ // Chrome OS M38 and older will have a legacy key with no label while those
+ // created by Chrome OS M39 and newer will have a key with the label
+ // kCryptohomeGAIAKeyLabel.
+ return cryptohome::KeyDefinition(key->GetSecret(), std::string(),
+ cryptohome::PRIV_DEFAULT);
+}
+
+void EncryptionMigrationScreenHandler::OnMigrationProgress(
+ cryptohome::DircryptoMigrationStatus status,
+ uint64_t current,
+ uint64_t total) {
+ switch (status) {
+ case cryptohome::DIRCRYPTO_MIGRATION_INITIALIZING:
+ UpdateUIState(UIState::MIGRATING);
+ break;
+ case cryptohome::DIRCRYPTO_MIGRATION_IN_PROGRESS:
+ UpdateUIState(UIState::MIGRATING);
+ CallJS("setMigrationProgress", static_cast<double>(current) / total);
+ break;
+ case cryptohome::DIRCRYPTO_MIGRATION_SUCCESS:
+ RecordMigrationResult(should_resume_
+ ? MigrationResult::SUCCESS_IN_RESUMED_MIGRATION
+ : MigrationResult::SUCCESS_IN_NEW_MIGRATION);
+ // If the battery level decreased during migration, record the consumed
+ // battery level.
+ if (*current_battery_percent_ < initial_battery_percent_) {
+ UMA_HISTOGRAM_PERCENTAGE(
+ kUmaNameConsumedBatteryPercent,
+ static_cast<int>(std::round(initial_battery_percent_ -
+ *current_battery_percent_)));
+ }
+ // Restart immediately after successful migration.
+ DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
+ break;
+ case cryptohome::DIRCRYPTO_MIGRATION_FAILED:
+ RecordMigrationResult(
+ should_resume_ ? MigrationResult::GENERAL_FAILURE_IN_RESUMED_MIGRATION
+ : MigrationResult::GENERAL_FAILURE_IN_NEW_MIGRATION);
+ // Stop listening to the progress updates.
+ DBusThreadManager::Get()
+ ->GetCryptohomeClient()
+ ->SetDircryptoMigrationProgressHandler(
+ CryptohomeClient::DircryptoMigrationProgessHandler());
+ // Shows error screen after removing user directory is completed.
+ RemoveCryptohome();
+ break;
+ default:
+ break;
+ }
+}
+
+void EncryptionMigrationScreenHandler::OnMigrationRequested(bool success) {
+ if (!success) {
+ LOG(ERROR) << "Requesting MigrateToDircrypto failed.";
+ RecordMigrationResult(
+ should_resume_ ? MigrationResult::REQUEST_FAILURE_IN_RESUMED_MIGRATION
+ : MigrationResult::REQUEST_FAILURE_IN_NEW_MIGRATION);
+ UpdateUIState(UIState::MIGRATION_FAILED);
+ }
+}
+
+void EncryptionMigrationScreenHandler::OnDelayedRecordVisibleScreen(
+ UIState ui_state) {
+ if (current_ui_state_ != ui_state)
+ return;
+
+ // If |current_ui_state_| is not changed for a second, record the current
+ // screen as a "visible" screen.
+ UMA_HISTOGRAM_ENUMERATION(kUmaNameVisibleScreen, ui_state, UIState::COUNT);
+}
+
+} // namespace chromeos
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
new file mode 100644
index 00000000000..5dba39d5b92
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.h
@@ -0,0 +1,141 @@
+// 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_CHROMEOS_LOGIN_ENCRYPTION_MIGRATION_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ENCRYPTION_MIGRATION_SCREEN_HANDLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "chrome/browser/chromeos/login/screens/encryption_migration_screen_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "chromeos/cryptohome/cryptohome_parameters.h"
+#include "chromeos/dbus/power_manager_client.h"
+#include "chromeos/login/auth/user_context.h"
+#include "third_party/cros_system_api/dbus/cryptohome/dbus-constants.h"
+
+namespace device {
+class PowerSaveBlocker;
+} // namespace device
+
+namespace chromeos {
+
+class LoginFeedback;
+
+// WebUI implementation of EncryptionMigrationScreenView
+class EncryptionMigrationScreenHandler : public EncryptionMigrationScreenView,
+ public BaseScreenHandler,
+ public PowerManagerClient::Observer {
+ public:
+ EncryptionMigrationScreenHandler();
+ ~EncryptionMigrationScreenHandler() override;
+
+ // EncryptionMigrationScreenView implementation:
+ void Show() override;
+ void Hide() override;
+ void SetDelegate(Delegate* delegate) override;
+ void SetUserContext(const UserContext& user_context) override;
+ void SetShouldResume(bool should_resume) override;
+ void SetContinueLoginCallback(ContinueLoginCallback callback) override;
+ void SetupInitialView() override;
+
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void Initialize() override;
+
+ private:
+ // Enumeration for migration UI state. These values must be kept in sync with
+ // EncryptionMigrationUIState in JS code, and match the numbering for
+ // MigrationUIScreen in histograms/enums.xml. Do not reorder or remove items,
+ // only add new items before COUNT.
+ enum UIState {
+ INITIAL = 0,
+ READY = 1,
+ MIGRATING = 2,
+ MIGRATION_FAILED = 3,
+ NOT_ENOUGH_STORAGE = 4,
+ COUNT
+ };
+
+ // WebUIMessageHandler implementation:
+ void RegisterMessages() override;
+
+ // PowerManagerClient::Observer implementation:
+ void PowerChanged(const power_manager::PowerSupplyProperties& proto) override;
+
+ // Handlers for JS API callbacks.
+ void HandleStartMigration();
+ void HandleSkipMigration();
+ void HandleRequestRestartOnLowStorage();
+ void HandleRequestRestartOnFailure();
+ void HandleOpenFeedbackDialog();
+
+ // Updates UI state.
+ void UpdateUIState(UIState state);
+
+ void CheckAvailableStorage();
+ void OnGetAvailableStorage(int64_t size);
+ void WaitBatteryAndMigrate();
+ void StartMigration();
+ void OnMountExistingVault(bool success,
+ cryptohome::MountError return_code,
+ const std::string& mount_hash);
+ void StartBlockingPowerSave();
+ void StopBlockingPowerSave();
+ // Removes cryptohome and shows the error screen after the removal finishes.
+ void RemoveCryptohome();
+ void OnRemoveCryptohome(bool success, cryptohome::MountError return_code);
+
+ // Creates authorization key for MountEx method using |user_context_|.
+ cryptohome::KeyDefinition GetAuthKey();
+
+ // Handlers for cryptohome API callbacks.
+ void OnMigrationProgress(cryptohome::DircryptoMigrationStatus status,
+ uint64_t current,
+ uint64_t total);
+ void OnMigrationRequested(bool success);
+
+ // Records UMA about visible screen after delay.
+ void OnDelayedRecordVisibleScreen(UIState state);
+
+ Delegate* delegate_ = nullptr;
+ bool show_on_init_ = false;
+
+ // The current UI state which should be refrected in the web UI.
+ UIState current_ui_state_ = INITIAL;
+
+ // The current user's UserContext, which is used to request the migration to
+ // cryptohome.
+ UserContext user_context_;
+
+ // The callback which is used to log in to the session from the migration UI.
+ ContinueLoginCallback continue_login_callback_;
+
+ // True if the system should resume the previous incomplete migration.
+ bool should_resume_ = false;
+
+ // The current battery level.
+ base::Optional<double> current_battery_percent_;
+
+ // True if the migration should start immediately once the battery level gets
+ // sufficient.
+ bool should_migrate_on_enough_battery_ = false;
+
+ // The battery level at the timing that the migration starts.
+ double initial_battery_percent_ = 0.0;
+
+ std::unique_ptr<device::PowerSaveBlocker> power_save_blocker_;
+
+ std::unique_ptr<LoginFeedback> login_feedback_;
+
+ base::WeakPtrFactory<EncryptionMigrationScreenHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(EncryptionMigrationScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ENCRYPTION_MIGRATION_SCREEN_HANDLER_H_
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
new file mode 100644
index 00000000000..19e4871d0a4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
@@ -0,0 +1,679 @@
+// 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/ui/webui/chromeos/login/enrollment_screen_handler.h"
+
+#include <algorithm>
+
+#include "ash/system/devicetype_utils.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/chromeos/login/error_screens_histogram_helper.h"
+#include "chrome/browser/chromeos/login/help_app_launcher.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/browser/chromeos/login/screens/network_error.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
+#include "chrome/browser/chromeos/policy/enrollment_status_chromeos.h"
+#include "chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/login/auth/authpolicy_login_helper.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "components/login/localized_values_builder.h"
+#include "components/policy/core/browser/cloud/message_util.h"
+#include "content/public/browser/browser_thread.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace chromeos {
+namespace {
+
+const char kJsScreenPath[] = "login.OAuthEnrollmentScreen";
+
+// Enrollment step names.
+const char kEnrollmentStepSignin[] = "signin";
+const char kEnrollmentStepAdJoin[] = "ad-join";
+const char kEnrollmentStepSuccess[] = "success";
+const char kEnrollmentStepWorking[] = "working";
+
+// Enrollment mode constants used in the UI. This needs to be kept in sync with
+// oobe_screen_oauth_enrollment.js.
+const char kEnrollmentModeUIForced[] = "forced";
+const char kEnrollmentModeUIManual[] = "manual";
+const char kEnrollmentModeUIRecovery[] = "recovery";
+
+// Converts |mode| to a mode identifier for the UI.
+std::string EnrollmentModeToUIMode(policy::EnrollmentConfig::Mode mode) {
+ switch (mode) {
+ case policy::EnrollmentConfig::MODE_NONE:
+ break;
+ case policy::EnrollmentConfig::MODE_MANUAL:
+ case policy::EnrollmentConfig::MODE_MANUAL_REENROLLMENT:
+ case policy::EnrollmentConfig::MODE_LOCAL_ADVERTISED:
+ case policy::EnrollmentConfig::MODE_SERVER_ADVERTISED:
+ case policy::EnrollmentConfig::MODE_ATTESTATION:
+ return kEnrollmentModeUIManual;
+ case policy::EnrollmentConfig::MODE_LOCAL_FORCED:
+ case policy::EnrollmentConfig::MODE_SERVER_FORCED:
+ case policy::EnrollmentConfig::MODE_ATTESTATION_FORCED:
+ return kEnrollmentModeUIForced;
+ case policy::EnrollmentConfig::MODE_RECOVERY:
+ return kEnrollmentModeUIRecovery;
+ }
+
+ NOTREACHED() << "Bad enrollment mode " << mode;
+ return kEnrollmentModeUIManual;
+}
+
+// Returns network name by service path.
+std::string GetNetworkName(const std::string& service_path) {
+ const NetworkState* network =
+ NetworkHandler::Get()->network_state_handler()->GetNetworkState(
+ service_path);
+ if (!network)
+ return std::string();
+ return network->name();
+}
+
+bool IsBehindCaptivePortal(NetworkStateInformer::State state,
+ NetworkError::ErrorReason reason) {
+ return state == NetworkStateInformer::CAPTIVE_PORTAL ||
+ reason == NetworkError::ERROR_REASON_PORTAL_DETECTED;
+}
+
+bool IsProxyError(NetworkStateInformer::State state,
+ NetworkError::ErrorReason reason) {
+ return state == NetworkStateInformer::PROXY_AUTH_REQUIRED ||
+ reason == NetworkError::ERROR_REASON_PROXY_AUTH_CANCELLED ||
+ reason == NetworkError::ERROR_REASON_PROXY_CONNECTION_FAILED;
+}
+
+
+// Returns the enterprise domain after enrollment, or an empty string.
+std::string GetEnterpriseDomain() {
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ return connector->GetEnterpriseDomain();
+}
+
+} // namespace
+
+// EnrollmentScreenHandler, public ------------------------------
+
+EnrollmentScreenHandler::EnrollmentScreenHandler(
+ const scoped_refptr<NetworkStateInformer>& network_state_informer,
+ ErrorScreen* error_screen)
+ : BaseScreenHandler(kScreenId),
+ network_state_informer_(network_state_informer),
+ error_screen_(error_screen),
+ histogram_helper_(new ErrorScreensHistogramHelper("Enrollment")),
+ weak_ptr_factory_(this) {
+ set_call_js_prefix(kJsScreenPath);
+ set_async_assets_load_id(GetOobeScreenName(kScreenId));
+ DCHECK(network_state_informer_.get());
+ DCHECK(error_screen_);
+ network_state_informer_->AddObserver(this);
+}
+
+EnrollmentScreenHandler::~EnrollmentScreenHandler() {
+ network_state_informer_->RemoveObserver(this);
+}
+
+// EnrollmentScreenHandler, WebUIMessageHandler implementation --
+
+void EnrollmentScreenHandler::RegisterMessages() {
+ AddCallback("toggleFakeEnrollment",
+ &EnrollmentScreenHandler::HandleToggleFakeEnrollment);
+ AddCallback("oauthEnrollClose",
+ &EnrollmentScreenHandler::HandleClose);
+ AddCallback("oauthEnrollCompleteLogin",
+ &EnrollmentScreenHandler::HandleCompleteLogin);
+ AddCallback("oauthEnrollAdCompleteLogin",
+ &EnrollmentScreenHandler::HandleAdCompleteLogin);
+ AddCallback("oauthEnrollRetry",
+ &EnrollmentScreenHandler::HandleRetry);
+ AddCallback("frameLoadingCompleted",
+ &EnrollmentScreenHandler::HandleFrameLoadingCompleted);
+ AddCallback("oauthEnrollAttributes",
+ &EnrollmentScreenHandler::HandleDeviceAttributesProvided);
+ AddCallback("oauthEnrollOnLearnMore",
+ &EnrollmentScreenHandler::HandleOnLearnMore);
+}
+
+// EnrollmentScreenHandler
+// EnrollmentScreenActor implementation -----------------------------------
+
+void EnrollmentScreenHandler::SetParameters(
+ Controller* controller,
+ const policy::EnrollmentConfig& config) {
+ CHECK(config.should_enroll());
+ controller_ = controller;
+ config_ = config;
+}
+
+void EnrollmentScreenHandler::Show() {
+ if (!page_is_ready())
+ show_on_init_ = true;
+ else
+ DoShow();
+}
+
+void EnrollmentScreenHandler::Hide() {
+}
+
+void EnrollmentScreenHandler::ShowSigninScreen() {
+ observe_network_failure_ = true;
+ ShowStep(kEnrollmentStepSignin);
+}
+
+void EnrollmentScreenHandler::ShowAdJoin() {
+ observe_network_failure_ = false;
+ if (!authpolicy_login_helper_)
+ authpolicy_login_helper_ = base::MakeUnique<AuthPolicyLoginHelper>();
+ ShowStep(kEnrollmentStepAdJoin);
+}
+
+void EnrollmentScreenHandler::ShowAttributePromptScreen(
+ const std::string& asset_id,
+ const std::string& location) {
+ CallJS("showAttributePromptStep", asset_id, location);
+}
+
+void EnrollmentScreenHandler::ShowEnrollmentSpinnerScreen() {
+ ShowStep(kEnrollmentStepWorking);
+}
+
+void EnrollmentScreenHandler::ShowAttestationBasedEnrollmentSuccessScreen(
+ const std::string& enterprise_domain) {
+ CallJS("showAttestationBasedEnrollmentSuccess", ash::GetChromeOSDeviceName(),
+ enterprise_domain);
+}
+
+void EnrollmentScreenHandler::ShowAuthError(
+ const GoogleServiceAuthError& error) {
+ switch (error.state()) {
+ case GoogleServiceAuthError::NONE:
+ case GoogleServiceAuthError::CAPTCHA_REQUIRED:
+ case GoogleServiceAuthError::TWO_FACTOR:
+ case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
+ case GoogleServiceAuthError::REQUEST_CANCELED:
+ case GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE:
+ case GoogleServiceAuthError::SERVICE_ERROR:
+ case GoogleServiceAuthError::WEB_LOGIN_REQUIRED:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_AUTH_FATAL_ERROR, false);
+ return;
+ case GoogleServiceAuthError::USER_NOT_SIGNED_UP:
+ case GoogleServiceAuthError::ACCOUNT_DELETED:
+ case GoogleServiceAuthError::ACCOUNT_DISABLED:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_AUTH_ACCOUNT_ERROR, true);
+ return;
+ case GoogleServiceAuthError::CONNECTION_FAILED:
+ case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_AUTH_NETWORK_ERROR, true);
+ return;
+ case GoogleServiceAuthError::HOSTED_NOT_ALLOWED_DEPRECATED:
+ case GoogleServiceAuthError::NUM_STATES:
+ break;
+ }
+ NOTREACHED();
+}
+
+void EnrollmentScreenHandler::ShowOtherError(
+ EnterpriseEnrollmentHelper::OtherError error) {
+ switch (error) {
+ case EnterpriseEnrollmentHelper::OTHER_ERROR_DOMAIN_MISMATCH:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_WRONG_USER, true);
+ return;
+ case EnterpriseEnrollmentHelper::OTHER_ERROR_FATAL:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_FATAL_ENROLLMENT_ERROR, true);
+ return;
+ }
+ NOTREACHED();
+}
+
+void EnrollmentScreenHandler::ShowEnrollmentStatus(
+ policy::EnrollmentStatus status) {
+ switch (status.status()) {
+ case policy::EnrollmentStatus::SUCCESS:
+ if (config_.is_mode_attestation())
+ ShowAttestationBasedEnrollmentSuccessScreen(GetEnterpriseDomain());
+ else
+ ShowStep(kEnrollmentStepSuccess);
+ return;
+ case policy::EnrollmentStatus::NO_STATE_KEYS:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_NO_STATE_KEYS, false);
+ return;
+ case policy::EnrollmentStatus::REGISTRATION_FAILED:
+ // Some special cases for generating a nicer message that's more helpful.
+ switch (status.client_status()) {
+ case policy::DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_ACCOUNT_ERROR, true);
+ break;
+ case policy::DM_STATUS_SERVICE_MISSING_LICENSES:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_MISSING_LICENSES_ERROR, true);
+ break;
+ case policy::DM_STATUS_SERVICE_DEPROVISIONED:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_DEPROVISIONED_ERROR, true);
+ break;
+ case policy::DM_STATUS_SERVICE_DOMAIN_MISMATCH:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_DOMAIN_MISMATCH_ERROR, true);
+ break;
+ default:
+ ShowErrorMessage(
+ l10n_util::GetStringFUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_STATUS_REGISTRATION_FAILED,
+ policy::FormatDeviceManagementStatus(status.client_status())),
+ true);
+ }
+ return;
+ case policy::EnrollmentStatus::ROBOT_AUTH_FETCH_FAILED:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_ROBOT_AUTH_FETCH_FAILED, true);
+ return;
+ case policy::EnrollmentStatus::ROBOT_REFRESH_FETCH_FAILED:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_ROBOT_REFRESH_FETCH_FAILED, true);
+ return;
+ case policy::EnrollmentStatus::ROBOT_REFRESH_STORE_FAILED:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_ROBOT_REFRESH_STORE_FAILED, true);
+ return;
+ case policy::EnrollmentStatus::REGISTRATION_BAD_MODE:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_REGISTRATION_BAD_MODE, false);
+ return;
+ case policy::EnrollmentStatus::REGISTRATION_CERT_FETCH_FAILED:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_REGISTRATION_CERT_FETCH_FAILED,
+ true);
+ return;
+ case policy::EnrollmentStatus::POLICY_FETCH_FAILED:
+ ShowErrorMessage(
+ l10n_util::GetStringFUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_STATUS_POLICY_FETCH_FAILED,
+ policy::FormatDeviceManagementStatus(status.client_status())),
+ true);
+ return;
+ case policy::EnrollmentStatus::VALIDATION_FAILED:
+ ShowErrorMessage(
+ l10n_util::GetStringFUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_STATUS_VALIDATION_FAILED,
+ policy::FormatValidationStatus(status.validation_status())),
+ true);
+ return;
+ case policy::EnrollmentStatus::LOCK_ERROR:
+ switch (status.lock_status()) {
+ case InstallAttributes::LOCK_SUCCESS:
+ case InstallAttributes::LOCK_NOT_READY:
+ // LOCK_SUCCESS is in contradiction of STATUS_LOCK_ERROR.
+ // LOCK_NOT_READY is transient, if retries are given up, LOCK_TIMEOUT
+ // is reported instead. This piece of code is unreached.
+ LOG(FATAL) << "Invalid lock status.";
+ return;
+ case InstallAttributes::LOCK_TIMEOUT:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_TIMEOUT, false);
+ return;
+ case InstallAttributes::LOCK_BACKEND_INVALID:
+ case InstallAttributes::LOCK_ALREADY_LOCKED:
+ case InstallAttributes::LOCK_SET_ERROR:
+ case InstallAttributes::LOCK_FINALIZE_ERROR:
+ case InstallAttributes::LOCK_READBACK_ERROR:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_ERROR, false);
+ return;
+ case InstallAttributes::LOCK_WRONG_DOMAIN:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_WRONG_USER, true);
+ return;
+ case InstallAttributes::LOCK_WRONG_MODE:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_WRONG_MODE, true);
+ return;
+ }
+ NOTREACHED();
+ return;
+ case policy::EnrollmentStatus::STORE_ERROR:
+ ShowErrorMessage(
+ l10n_util::GetStringFUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_STATUS_STORE_ERROR,
+ policy::FormatStoreStatus(status.store_status(),
+ status.validation_status())),
+ true);
+ return;
+ case policy::EnrollmentStatus::ATTRIBUTE_UPDATE_FAILED:
+ ShowErrorForDevice(IDS_ENTERPRISE_ENROLLMENT_ATTRIBUTE_ERROR, false);
+ return;
+ case policy::EnrollmentStatus::NO_MACHINE_IDENTIFICATION:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_STATUS_NO_MACHINE_IDENTIFICATION,
+ false);
+ return;
+ case policy::EnrollmentStatus::ACTIVE_DIRECTORY_POLICY_FETCH_FAILED:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_ERROR_ACTIVE_DIRECTORY_POLICY_FETCH,
+ false);
+ return;
+ case policy::EnrollmentStatus::DM_TOKEN_STORE_FAILED:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_ERROR_SAVE_DEVICE_CONFIGURATION,
+ false);
+ return;
+ }
+ NOTREACHED();
+}
+
+// EnrollmentScreenHandler BaseScreenHandler implementation -----
+
+void EnrollmentScreenHandler::Initialize() {
+ if (show_on_init_) {
+ Show();
+ show_on_init_ = false;
+ }
+}
+
+void EnrollmentScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("oauthEnrollScreenTitle",
+ IDS_ENTERPRISE_ENROLLMENT_SCREEN_TITLE);
+ builder->Add("oauthEnrollRetry", IDS_ENTERPRISE_ENROLLMENT_RETRY);
+ builder->Add("oauthEnrollDone", IDS_ENTERPRISE_ENROLLMENT_DONE);
+ builder->Add("oauthEnrollNextBtn", IDS_OFFLINE_LOGIN_NEXT_BUTTON_TEXT);
+ builder->Add("oauthEnrollSkip", IDS_ENTERPRISE_ENROLLMENT_SKIP);
+ builder->AddF("oauthEnrollSuccess", IDS_ENTERPRISE_ENROLLMENT_SUCCESS,
+ ash::GetChromeOSDeviceName());
+ builder->Add("oauthEnrollDeviceInformation",
+ IDS_ENTERPRISE_ENROLLMENT_DEVICE_INFORMATION);
+ builder->Add("oauthEnrollExplainAttributeLink",
+ IDS_ENTERPRISE_ENROLLMENT_EXPLAIN_ATTRIBUTE_LINK);
+ builder->Add("oauthEnrollAttributeExplanation",
+ IDS_ENTERPRISE_ENROLLMENT_ATTRIBUTE_EXPLANATION);
+ builder->Add("oauthEnrollAssetIdLabel",
+ IDS_ENTERPRISE_ENROLLMENT_ASSET_ID_LABEL);
+ builder->Add("oauthEnrollLocationLabel",
+ IDS_ENTERPRISE_ENROLLMENT_LOCATION_LABEL);
+ builder->Add("oauthEnrollWorking", IDS_ENTERPRISE_ENROLLMENT_WORKING_MESSAGE);
+ // Do not use AddF for this string as it will be rendered by the JS code.
+ builder->Add("oauthEnrollAbeSuccess", IDS_ENTERPRISE_ENROLLMENT_ABE_SUCCESS);
+ builder->Add("oauthEnrollAdMachineNameInput",
+ IDS_AD_MACHINE_NAME_INPUT_LABEL);
+ builder->Add("oauthEnrollAdDomainJoinWelcomeMessage",
+ IDS_AD_DOMAIN_JOIN_WELCOME_MESSAGE);
+ builder->Add("adLoginUsername", IDS_AD_LOGIN_USER);
+ builder->Add("adLoginInvalidUsername", IDS_AD_INVALID_USERNAME);
+ builder->Add("adLoginPassword", IDS_AD_LOGIN_PASSWORD);
+ builder->Add("adLoginInvalidPassword", IDS_AD_INVALID_PASSWORD);
+ builder->Add("adJoinErrorMachineNameInvalid", IDS_AD_MACHINENAME_INVALID);
+ builder->Add("adJoinErrorMachineNameTooLong", IDS_AD_MACHINENAME_TOO_LONG);
+}
+
+bool EnrollmentScreenHandler::IsOnEnrollmentScreen() const {
+ return (GetCurrentScreen() == kScreenId);
+}
+
+bool EnrollmentScreenHandler::IsEnrollmentScreenHiddenByError() const {
+ return (GetCurrentScreen() == OobeScreen::SCREEN_ERROR_MESSAGE &&
+ error_screen_->GetParentScreen() == kScreenId);
+}
+
+void EnrollmentScreenHandler::UpdateState(NetworkError::ErrorReason reason) {
+ UpdateStateInternal(reason, false);
+}
+
+// TODO(rsorokin): This function is mostly copied from SigninScreenHandler and
+// should be refactored in the future.
+void EnrollmentScreenHandler::UpdateStateInternal(
+ NetworkError::ErrorReason reason,
+ bool force_update) {
+ if (!force_update && !IsOnEnrollmentScreen() &&
+ !IsEnrollmentScreenHiddenByError()) {
+ return;
+ }
+
+ if (!force_update && !observe_network_failure_)
+ return;
+
+ NetworkStateInformer::State state = network_state_informer_->state();
+ const std::string network_path = network_state_informer_->network_path();
+ const bool is_online = (state == NetworkStateInformer::ONLINE);
+ const bool is_behind_captive_portal =
+ (state == NetworkStateInformer::CAPTIVE_PORTAL);
+ const bool is_frame_error = reason == NetworkError::ERROR_REASON_FRAME_ERROR;
+
+ LOG(WARNING) << "EnrollmentScreenHandler::UpdateState(): "
+ << "state=" << NetworkStateInformer::StatusString(state) << ", "
+ << "reason=" << NetworkError::ErrorReasonString(reason);
+
+ if (is_online || !is_behind_captive_portal)
+ error_screen_->HideCaptivePortal();
+
+ if (is_frame_error) {
+ LOG(WARNING) << "Retry page load";
+ // TODO(rsorokin): Too many consecutive reloads.
+ CallJS("doReload");
+ }
+
+ if (!is_online || is_frame_error)
+ SetupAndShowOfflineMessage(state, reason);
+ else
+ HideOfflineMessage(state, reason);
+}
+
+void EnrollmentScreenHandler::SetupAndShowOfflineMessage(
+ NetworkStateInformer::State state,
+ NetworkError::ErrorReason reason) {
+ const std::string network_path = network_state_informer_->network_path();
+ const bool is_behind_captive_portal = IsBehindCaptivePortal(state, reason);
+ const bool is_proxy_error = IsProxyError(state, reason);
+ const bool is_frame_error = reason == NetworkError::ERROR_REASON_FRAME_ERROR;
+
+ if (is_proxy_error) {
+ error_screen_->SetErrorState(NetworkError::ERROR_STATE_PROXY,
+ std::string());
+ } else if (is_behind_captive_portal) {
+ // Do not bother a user with obsessive captive portal showing. This
+ // check makes captive portal being shown only once: either when error
+ // screen is shown for the first time or when switching from another
+ // error screen (offline, proxy).
+ if (IsOnEnrollmentScreen() ||
+ (error_screen_->GetErrorState() != NetworkError::ERROR_STATE_PORTAL)) {
+ error_screen_->FixCaptivePortal();
+ }
+ const std::string network_name = GetNetworkName(network_path);
+ error_screen_->SetErrorState(NetworkError::ERROR_STATE_PORTAL,
+ network_name);
+ } else if (is_frame_error) {
+ error_screen_->SetErrorState(NetworkError::ERROR_STATE_AUTH_EXT_TIMEOUT,
+ std::string());
+ } else {
+ error_screen_->SetErrorState(NetworkError::ERROR_STATE_OFFLINE,
+ std::string());
+ }
+
+ if (GetCurrentScreen() != OobeScreen::SCREEN_ERROR_MESSAGE) {
+ const std::string network_type = network_state_informer_->network_type();
+ error_screen_->SetUIState(NetworkError::UI_STATE_SIGNIN);
+ error_screen_->SetParentScreen(kScreenId);
+ error_screen_->SetHideCallback(base::Bind(&EnrollmentScreenHandler::DoShow,
+ weak_ptr_factory_.GetWeakPtr()));
+ error_screen_->Show();
+ histogram_helper_->OnErrorShow(error_screen_->GetErrorState());
+ }
+}
+
+void EnrollmentScreenHandler::HideOfflineMessage(
+ NetworkStateInformer::State state,
+ NetworkError::ErrorReason reason) {
+ if (IsEnrollmentScreenHiddenByError())
+ error_screen_->Hide();
+ histogram_helper_->OnErrorHide();
+}
+
+// EnrollmentScreenHandler, private -----------------------------
+void EnrollmentScreenHandler::HandleToggleFakeEnrollment() {
+ policy::PolicyOAuth2TokenFetcher::UseFakeTokensForTesting();
+}
+
+void EnrollmentScreenHandler::HandleClose(const std::string& reason) {
+ DCHECK(controller_);
+
+ if (reason == "cancel") {
+ if (authpolicy_login_helper_)
+ authpolicy_login_helper_->CancelRequestsAndRestart();
+ controller_->OnCancel();
+ } else if (reason == "done") {
+ controller_->OnConfirmationClosed();
+ } else {
+ NOTREACHED();
+ }
+}
+
+void EnrollmentScreenHandler::HandleCompleteLogin(
+ const std::string& user,
+ const std::string& auth_code) {
+ observe_network_failure_ = false;
+ DCHECK(controller_);
+ controller_->OnLoginDone(gaia::SanitizeEmail(user), auth_code);
+}
+
+void EnrollmentScreenHandler::HandleAdCompleteLogin(
+ const std::string& machine_name,
+ 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, user_name, password,
+ base::BindOnce(&EnrollmentScreenHandler::HandleAdDomainJoin,
+ weak_ptr_factory_.GetWeakPtr(), machine_name, user_name));
+}
+
+void EnrollmentScreenHandler::HandleAdDomainJoin(
+ const std::string& machine_name,
+ const std::string& user_name,
+ authpolicy::ErrorType code) {
+ switch (code) {
+ case authpolicy::ERROR_NONE:
+ ShowEnrollmentSpinnerScreen();
+ controller_->OnAdJoined(gaia::ExtractDomainName(user_name));
+ return;
+ case authpolicy::ERROR_UNKNOWN:
+ case authpolicy::ERROR_DBUS_FAILURE:
+ case authpolicy::ERROR_NET_FAILED:
+ case authpolicy::ERROR_SMBCLIENT_FAILED:
+ case authpolicy::ERROR_PARSE_FAILED:
+ case authpolicy::ERROR_PARSE_PREG_FAILED:
+ case authpolicy::ERROR_BAD_GPOS:
+ case authpolicy::ERROR_LOCAL_IO:
+ case authpolicy::ERROR_STORE_POLICY_FAILED:
+ ShowError(IDS_AD_DOMAIN_JOIN_UNKNOWN_ERROR, true);
+ return;
+ case authpolicy::ERROR_NETWORK_PROBLEM:
+ ShowError(IDS_ENTERPRISE_ENROLLMENT_AUTH_NETWORK_ERROR, true);
+ return;
+ case authpolicy::ERROR_PARSE_UPN_FAILED:
+ case authpolicy::ERROR_BAD_USER_NAME:
+ CallJS("invalidateAd", machine_name, user_name,
+ static_cast<int>(ActiveDirectoryErrorState::BAD_USERNAME));
+ return;
+ case authpolicy::ERROR_BAD_PASSWORD:
+ CallJS("invalidateAd", machine_name, user_name,
+ static_cast<int>(ActiveDirectoryErrorState::BAD_PASSWORD));
+ return;
+ case authpolicy::ERROR_MACHINE_NAME_TOO_LONG:
+ CallJS(
+ "invalidateAd", machine_name, user_name,
+ static_cast<int>(ActiveDirectoryErrorState::MACHINE_NAME_TOO_LONG));
+ return;
+ case authpolicy::ERROR_INVALID_MACHINE_NAME:
+ CallJS("invalidateAd", machine_name, user_name,
+ static_cast<int>(ActiveDirectoryErrorState::MACHINE_NAME_INVALID));
+ return;
+ case authpolicy::ERROR_JOIN_ACCESS_DENIED:
+ ShowError(IDS_AD_USER_DENIED_TO_JOIN_MACHINE, true);
+ return;
+ case authpolicy::ERROR_USER_HIT_JOIN_QUOTA:
+ ShowError(IDS_AD_USER_HIT_JOIN_QUOTA, true);
+ return;
+ case authpolicy::ERROR_PASSWORD_EXPIRED:
+ case authpolicy::ERROR_CANNOT_RESOLVE_KDC:
+ case authpolicy::ERROR_KINIT_FAILED:
+ case authpolicy::ERROR_NOT_JOINED:
+ case authpolicy::ERROR_NOT_LOGGED_IN:
+ default:
+ LOG(WARNING) << "Unhandled error code: " << code;
+ CallJS("invalidateAd", machine_name, user_name,
+ static_cast<int>(ActiveDirectoryErrorState::NONE));
+ return;
+ }
+}
+
+void EnrollmentScreenHandler::HandleRetry() {
+ DCHECK(controller_);
+ controller_->OnRetry();
+}
+
+void EnrollmentScreenHandler::HandleFrameLoadingCompleted() {
+ if (network_state_informer_->state() != NetworkStateInformer::ONLINE)
+ return;
+
+ UpdateState(NetworkError::ERROR_REASON_UPDATE);
+}
+
+void EnrollmentScreenHandler::HandleDeviceAttributesProvided(
+ const std::string& asset_id,
+ const std::string& location) {
+ controller_->OnDeviceAttributeProvided(asset_id, location);
+}
+
+void EnrollmentScreenHandler::HandleOnLearnMore() {
+ if (!help_app_.get())
+ help_app_ = new HelpAppLauncher(GetNativeWindow());
+ help_app_->ShowHelpTopic(HelpAppLauncher::HELP_DEVICE_ATTRIBUTES);
+}
+
+void EnrollmentScreenHandler::ShowStep(const char* step) {
+ CallJS("showStep", std::string(step));
+}
+
+void EnrollmentScreenHandler::ShowError(int message_id, bool retry) {
+ ShowErrorMessage(l10n_util::GetStringUTF8(message_id), retry);
+}
+
+void EnrollmentScreenHandler::ShowErrorForDevice(int message_id, bool retry) {
+ ShowErrorMessage(
+ l10n_util::GetStringFUTF8(message_id, ash::GetChromeOSDeviceName()),
+ retry);
+}
+
+void EnrollmentScreenHandler::ShowErrorMessage(const std::string& message,
+ bool retry) {
+ CallJS("showError", message, retry);
+}
+
+void EnrollmentScreenHandler::DoShow() {
+ base::DictionaryValue screen_data;
+ screen_data.SetString("gaiaUrl", GaiaUrls::GetInstance()->gaia_url().spec());
+ screen_data.SetString("clientId",
+ GaiaUrls::GetInstance()->oauth2_chrome_client_id());
+ screen_data.SetString("enrollment_mode",
+ EnrollmentModeToUIMode(config_.mode));
+ screen_data.SetBoolean("attestationBased", config_.is_mode_attestation());
+ screen_data.SetString("management_domain", config_.management_domain);
+
+ policy::DeviceCloudPolicyManagerChromeOS* policy_manager =
+ g_browser_process->platform_part()
+ ->browser_policy_connector_chromeos()
+ ->GetDeviceCloudPolicyManager();
+ const bool cfm = policy_manager && policy_manager->IsRemoraRequisition();
+ screen_data.SetString("flow", cfm ? "cfm" : "enterprise");
+
+ ShowScreenWithData(OobeScreen::SCREEN_OOBE_ENROLLMENT, &screen_data);
+ if (first_show_) {
+ first_show_ = false;
+ UpdateStateInternal(NetworkError::ERROR_REASON_UPDATE, true);
+ }
+ histogram_helper_->OnScreenShow();
+}
+
+} // namespace chromeos
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
new file mode 100644
index 00000000000..c263fa28c48
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h
@@ -0,0 +1,169 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ENROLLMENT_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ENROLLMENT_SCREEN_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/enrollment/enrollment_screen_view.h"
+#include "chrome/browser/chromeos/login/enrollment/enterprise_enrollment_helper.h"
+#include "chrome/browser/chromeos/login/screens/error_screen.h"
+#include "chrome/browser/chromeos/policy/enrollment_config.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
+#include "net/base/net_errors.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+
+class AuthPolicyLoginHelper;
+class ErrorScreensHistogramHelper;
+class HelpAppLauncher;
+
+// Possible error states of the Active Directory screen. Must be in the same
+// order as ACTIVE_DIRECTORY_ERROR_STATE enum values.
+enum class ActiveDirectoryErrorState {
+ NONE = 0,
+ MACHINE_NAME_INVALID = 1,
+ MACHINE_NAME_TOO_LONG = 2,
+ BAD_USERNAME = 3,
+ BAD_PASSWORD = 4,
+};
+
+// WebUIMessageHandler implementation which handles events occurring on the
+// page, such as the user pressing the signin button.
+class EnrollmentScreenHandler
+ : public BaseScreenHandler,
+ public EnrollmentScreenView,
+ public NetworkStateInformer::NetworkStateInformerObserver {
+ public:
+ EnrollmentScreenHandler(
+ const scoped_refptr<NetworkStateInformer>& network_state_informer,
+ ErrorScreen* error_screen);
+ ~EnrollmentScreenHandler() override;
+
+ // Implements WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ // Implements EnrollmentScreenView:
+ void SetParameters(Controller* controller,
+ const policy::EnrollmentConfig& config) override;
+ void Show() override;
+ void Hide() override;
+ void ShowSigninScreen() override;
+ void ShowAdJoin() override;
+ void ShowAttributePromptScreen(const std::string& asset_id,
+ const std::string& location) override;
+ void ShowAttestationBasedEnrollmentSuccessScreen(
+ const std::string& enterprise_domain) override;
+ void ShowEnrollmentSpinnerScreen() override;
+ void ShowAuthError(const GoogleServiceAuthError& error) override;
+ void ShowEnrollmentStatus(policy::EnrollmentStatus status) override;
+ void ShowOtherError(
+ EnterpriseEnrollmentHelper::OtherError error_code) override;
+
+ // Implements BaseScreenHandler:
+ void Initialize() override;
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+
+ // Implements NetworkStateInformer::NetworkStateInformerObserver
+ void UpdateState(NetworkError::ErrorReason reason) override;
+
+ private:
+ // Handlers for WebUI messages.
+ void HandleToggleFakeEnrollment();
+ void HandleClose(const std::string& reason);
+ void HandleCompleteLogin(const std::string& user,
+ const std::string& auth_code);
+ void HandleAdCompleteLogin(const std::string& machine_name,
+ const std::string& user_name,
+ const std::string& password);
+ void HandleRetry();
+ void HandleFrameLoadingCompleted();
+ void HandleDeviceAttributesProvided(const std::string& asset_id,
+ const std::string& location);
+ void HandleOnLearnMore();
+
+ void UpdateStateInternal(NetworkError::ErrorReason reason, bool force_update);
+ void SetupAndShowOfflineMessage(NetworkStateInformer::State state,
+ NetworkError::ErrorReason reason);
+ void HideOfflineMessage(NetworkStateInformer::State state,
+ NetworkError::ErrorReason reason);
+
+ // Shows a given enrollment step.
+ void ShowStep(const char* step);
+
+ // Display the given i18n resource as error message.
+ void ShowError(int message_id, bool retry);
+
+ // Display the given i18n resource as an error message, with the $1
+ // substitution parameter replaced with the device's product name.
+ void ShowErrorForDevice(int message_id, bool retry);
+
+ // Display the given string as error message.
+ void ShowErrorMessage(const std::string& message, bool retry);
+
+ // Display the given i18n string as a progress message.
+ void ShowWorking(int message_id);
+
+ // Shows the screen.
+ void DoShow();
+
+ // Returns true if current visible screen is the enrollment sign-in page.
+ bool IsOnEnrollmentScreen() const;
+
+ // Returns true if current visible screen is the error screen over
+ // enrollment sign-in page.
+ bool IsEnrollmentScreenHiddenByError() const;
+
+ // Helper function to wait for AD password written to a pipe.
+ void OnPasswordPipeReady(const std::string& machine_name,
+ const std::string& user_name,
+ base::ScopedFD password_fd);
+ // Handler callback from AuthPolicyClient.
+ void HandleAdDomainJoin(const std::string& machine_name,
+ const std::string& user_name,
+ authpolicy::ErrorType code);
+
+ // Keeps the controller for this view.
+ Controller* controller_ = nullptr;
+
+ bool show_on_init_ = false;
+
+ // The enrollment configuration.
+ policy::EnrollmentConfig config_;
+
+ // True if screen was not shown yet.
+ bool first_show_ = true;
+
+ // Whether we should handle network errors on enrollment screen.
+ // True when signin screen step is shown.
+ bool observe_network_failure_ = false;
+
+ // Network state informer used to keep signin screen up.
+ scoped_refptr<NetworkStateInformer> network_state_informer_;
+
+ ErrorScreen* error_screen_ = nullptr;
+
+ std::unique_ptr<ErrorScreensHistogramHelper> histogram_helper_;
+
+ // Help application used for help dialogs.
+ scoped_refptr<HelpAppLauncher> help_app_;
+
+ // Helper to call AuthPolicyClient and cancel calls if needed. Used to join
+ // Active Directory domain.
+ std::unique_ptr<AuthPolicyLoginHelper> authpolicy_login_helper_;
+
+ base::WeakPtrFactory<EnrollmentScreenHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(EnrollmentScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ENROLLMENT_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc
new file mode 100644
index 00000000000..af75d9e1805
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc
@@ -0,0 +1,131 @@
+// 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/ui/webui/chromeos/login/error_screen_handler.h"
+
+#include "ash/system/devicetype_utils.h"
+#include "base/message_loop/message_loop.h"
+#include "base/time/time.h"
+#include "chrome/browser/chromeos/login/screens/error_screen.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+#include "ui/strings/grit/ui_strings.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.ErrorMessageScreen";
+
+} // namespace
+
+namespace chromeos {
+
+ErrorScreenHandler::ErrorScreenHandler()
+ : BaseScreenHandler(kScreenId), weak_ptr_factory_(this) {
+ set_call_js_prefix(kJsScreenPath);
+}
+
+ErrorScreenHandler::~ErrorScreenHandler() {
+ if (screen_)
+ screen_->OnViewDestroyed(this);
+}
+
+void ErrorScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+ BaseScreenHandler::ShowScreen(kScreenId);
+ if (screen_)
+ screen_->OnShow();
+ showing_ = true;
+}
+
+void ErrorScreenHandler::Hide() {
+ showing_ = false;
+ if (screen_)
+ screen_->OnHide();
+}
+
+void ErrorScreenHandler::Bind(ErrorScreen* screen) {
+ screen_ = screen;
+ BaseScreenHandler::SetBaseScreen(screen_);
+}
+
+void ErrorScreenHandler::Unbind() {
+ screen_ = nullptr;
+ BaseScreenHandler::SetBaseScreen(nullptr);
+}
+
+void ErrorScreenHandler::ShowOobeScreen(OobeScreen screen) {
+ ShowScreen(screen);
+}
+
+void ErrorScreenHandler::RegisterMessages() {
+ AddCallback("hideCaptivePortal",
+ &ErrorScreenHandler::HandleHideCaptivePortal);
+ BaseScreenHandler::RegisterMessages();
+}
+
+void ErrorScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("deviceType", ash::GetChromeOSDeviceName());
+ builder->Add("loginErrorTitle", IDS_LOGIN_ERROR_TITLE);
+ builder->Add("rollbackErrorTitle", IDS_RESET_SCREEN_REVERT_ERROR);
+ builder->Add("signinOfflineMessageBody",
+ ash::SubstituteChromeOSDeviceType(IDS_LOGIN_OFFLINE_MESSAGE));
+ builder->Add("kioskOfflineMessageBody", IDS_KIOSK_OFFLINE_MESSAGE);
+ builder->Add("kioskOnlineTitle", IDS_LOGIN_NETWORK_RESTORED_TITLE);
+ builder->Add("kioskOnlineMessageBody", IDS_KIOSK_ONLINE_MESSAGE);
+ builder->Add("autoEnrollmentOfflineMessageBody",
+ IDS_LOGIN_AUTO_ENROLLMENT_OFFLINE_MESSAGE);
+ builder->AddF("rollbackErrorMessageBody",
+ IDS_RESET_SCREEN_REVERT_ERROR_EXPLANATION,
+ IDS_SHORT_PRODUCT_NAME);
+ builder->Add("captivePortalTitle", IDS_LOGIN_MAYBE_CAPTIVE_PORTAL_TITLE);
+ builder->Add("captivePortalMessage", IDS_LOGIN_MAYBE_CAPTIVE_PORTAL);
+ builder->Add("captivePortalProxyMessage",
+ IDS_LOGIN_MAYBE_CAPTIVE_PORTAL_PROXY);
+ builder->Add("captivePortalNetworkSelect",
+ IDS_LOGIN_MAYBE_CAPTIVE_PORTAL_NETWORK_SELECT);
+ builder->Add("signinProxyMessageText", IDS_LOGIN_PROXY_ERROR_MESSAGE);
+ builder->Add("updateOfflineMessageBody",
+ ash::SubstituteChromeOSDeviceType(IDS_UPDATE_OFFLINE_MESSAGE));
+ builder->Add("updateProxyMessageText", IDS_UPDATE_PROXY_ERROR_MESSAGE);
+ builder->AddF("localStateErrorText0", IDS_LOCAL_STATE_ERROR_TEXT_0,
+ IDS_SHORT_PRODUCT_NAME);
+ builder->Add("localStateErrorText1", IDS_LOCAL_STATE_ERROR_TEXT_1);
+ builder->Add("localStateErrorPowerwashButton",
+ IDS_LOCAL_STATE_ERROR_POWERWASH_BUTTON);
+ builder->Add("connectingIndicatorText", IDS_LOGIN_CONNECTING_INDICATOR_TEXT);
+ builder->Add("guestSigninFixNetwork", IDS_LOGIN_GUEST_SIGNIN_FIX_NETWORK);
+ builder->Add("rebootButton", IDS_RELAUNCH_BUTTON);
+ builder->Add("diagnoseButton", IDS_DIAGNOSE_BUTTON);
+ builder->Add("configureCertsButton", IDS_MANAGE_CERTIFICATES);
+ builder->Add("continueButton", IDS_NETWORK_SELECTION_CONTINUE_BUTTON);
+ builder->Add("okButton", IDS_APP_OK);
+}
+
+void ErrorScreenHandler::Initialize() {
+ if (!page_is_ready())
+ return;
+
+ if (show_on_init_) {
+ // TODO(nkostylev): Check that context initial state is properly passed.
+ Show();
+ show_on_init_ = false;
+ }
+}
+
+void ErrorScreenHandler::OnConnectToNetworkRequested() {
+ if (showing_ && screen_)
+ screen_->OnUserAction(ErrorScreen::kUserActionConnectRequested);
+}
+
+void ErrorScreenHandler::HandleHideCaptivePortal() {
+ if (screen_)
+ screen_->HideCaptivePortal();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/error_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/error_screen_handler.h
new file mode 100644
index 00000000000..f29d0da24a9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/error_screen_handler.h
@@ -0,0 +1,62 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ERROR_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ERROR_SCREEN_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/screens/error_screen.h"
+#include "chrome/browser/chromeos/login/screens/network_error_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.h"
+
+namespace chromeos {
+
+// A class that handles the WebUI hooks in error screen.
+class ErrorScreenHandler : public BaseScreenHandler,
+ public NetworkErrorView,
+ public NetworkDropdownHandler::Observer {
+ public:
+ ErrorScreenHandler();
+ ~ErrorScreenHandler() override;
+
+ private:
+ // NetworkErrorView:
+ void Show() override;
+ void Hide() override;
+ void Bind(ErrorScreen* screen) override;
+ void Unbind() override;
+ void ShowOobeScreen(OobeScreen screen) override;
+
+ // WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ // BaseScreenHandler:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void Initialize() override;
+
+ // NetworkDropdownHandler:
+ void OnConnectToNetworkRequested() override;
+
+ // WebUI message handlers.
+ void HandleHideCaptivePortal();
+
+ // Non-owning ptr.
+ ErrorScreen* screen_ = nullptr;
+
+ // Should the screen be shown right after initialization?
+ bool show_on_init_ = false;
+
+ // Whether the error screen is currently shown.
+ bool showing_ = false;
+
+ base::WeakPtrFactory<ErrorScreenHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ErrorScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_ERROR_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc
new file mode 100644
index 00000000000..f8dfee03f22
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc
@@ -0,0 +1,244 @@
+// 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/ui/webui/chromeos/login/eula_screen_handler.h"
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/chromeos/login/help_app_launcher.h"
+#include "chrome/browser/chromeos/login/helper.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/browser/chromeos/login/screens/core_oobe_view.h"
+#include "chrome/browser/chromeos/login/screens/eula_screen.h"
+#include "chrome/browser/chromeos/login/ui/login_web_dialog.h"
+#include "chrome/browser/chromeos/login/ui/webui_login_display.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/locale_settings.h"
+#include "components/login/localized_values_builder.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_contents.h"
+#include "rlz/features/features.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/views/widget/widget.h"
+#include "url/gurl.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.EulaScreen";
+
+// Helper class to tweak display details of credits pages in the context
+// of OOBE/EULA step.
+class CreditsWebDialog : public chromeos::LoginWebDialog {
+ public:
+ CreditsWebDialog(Profile* profile,
+ gfx::NativeWindow parent_window,
+ int title_id,
+ const GURL& url)
+ : chromeos::LoginWebDialog(profile, NULL, parent_window,
+ l10n_util::GetStringUTF16(title_id),
+ url) {
+ }
+
+ void OnLoadingStateChanged(content::WebContents* source) override {
+ chromeos::LoginWebDialog::OnLoadingStateChanged(source);
+ // Remove visual elements that we can handle in EULA page.
+ bool is_loading = source->IsLoading();
+ if (!is_loading && source->GetWebUI()) {
+ source->GetWebUI()->CallJavascriptFunctionUnsafe(
+ "(function () {"
+ " document.body.classList.toggle('dialog', true);"
+ " keyboard.initializeKeyboardFlow(false);"
+ "})");
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CreditsWebDialog);
+};
+
+void ShowCreditsDialog(Profile* profile,
+ gfx::NativeWindow parent_window,
+ int title_id,
+ const GURL& credits_url) {
+ CreditsWebDialog* dialog = new CreditsWebDialog(profile,
+ parent_window,
+ title_id,
+ credits_url);
+ dialog->SetDialogSize(l10n_util::GetLocalizedContentsWidthInPixels(
+ IDS_CREDITS_APP_DIALOG_WIDTH_PIXELS),
+ l10n_util::GetLocalizedContentsWidthInPixels(
+ IDS_CREDITS_APP_DIALOG_HEIGHT_PIXELS));
+ dialog->Show();
+ // The dialog object will be deleted on dialog close.
+}
+
+} // namespace
+
+namespace chromeos {
+
+EulaScreenHandler::EulaScreenHandler(CoreOobeView* core_oobe_view)
+ : BaseScreenHandler(kScreenId),
+ core_oobe_view_(core_oobe_view),
+ weak_factory_(this) {
+ set_call_js_prefix(kJsScreenPath);
+}
+
+EulaScreenHandler::~EulaScreenHandler() {
+ if (screen_)
+ screen_->OnViewDestroyed(this);
+}
+
+void EulaScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+ ShowScreen(kScreenId);
+}
+
+void EulaScreenHandler::Hide() {
+}
+
+void EulaScreenHandler::Bind(EulaScreen* screen) {
+ screen_ = screen;
+ BaseScreenHandler::SetBaseScreen(screen_);
+ if (page_is_ready())
+ Initialize();
+}
+
+void EulaScreenHandler::Unbind() {
+ screen_ = nullptr;
+ BaseScreenHandler::SetBaseScreen(nullptr);
+}
+
+void EulaScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("eulaScreenTitle", IDS_EULA_SCREEN_TITLE);
+ builder->Add("eulaScreenAccessibleTitle", IDS_EULA_SCREEN_ACCESSIBLE_TITLE);
+ builder->Add("checkboxLogging", IDS_EULA_CHECKBOX_ENABLE_LOGGING);
+ builder->Add("back", IDS_EULA_BACK_BUTTON);
+ builder->Add("next", IDS_EULA_NEXT_BUTTON);
+ builder->Add("acceptAgreement", IDS_EULA_ACCEPT_AND_CONTINUE_BUTTON);
+ builder->Add("eulaSystemInstallationSettings",
+ IDS_EULA_SYSTEM_SECURITY_SETTING);
+
+ builder->Add("eulaTpmDesc", IDS_EULA_SECURE_MODULE_DESCRIPTION);
+ builder->Add("eulaTpmKeyDesc", IDS_EULA_SECURE_MODULE_KEY_DESCRIPTION);
+ builder->Add("eulaTpmDescPowerwash",
+ IDS_EULA_SECURE_MODULE_KEY_DESCRIPTION_POWERWASH);
+ builder->Add("eulaTpmBusy", IDS_EULA_SECURE_MODULE_BUSY);
+ ::login::GetSecureModuleUsed(base::Bind(
+ &EulaScreenHandler::UpdateLocalizedValues, weak_factory_.GetWeakPtr()));
+
+ builder->Add("eulaSystemInstallationSettingsOkButton", IDS_OK);
+ builder->Add("termsOfServiceLoading", IDS_TERMS_OF_SERVICE_SCREEN_LOADING);
+#if BUILDFLAG(ENABLE_RLZ)
+ builder->AddF("eulaRlzDesc",
+ IDS_EULA_RLZ_DESCRIPTION,
+ IDS_SHORT_PRODUCT_NAME,
+ IDS_PRODUCT_NAME);
+ builder->AddF("eulaRlzEnable",
+ IDS_EULA_RLZ_ENABLE,
+ IDS_SHORT_PRODUCT_OS_NAME);
+#endif
+
+ builder->Add("chromeCreditsLink", IDS_ABOUT_VERSION_LICENSE_EULA);
+ builder->Add("chromeosCreditsLink", IDS_ABOUT_CROS_VERSION_LICENSE_EULA);
+
+ /* MD-OOBE */
+ builder->Add("oobeEulaSectionTitle", IDS_OOBE_EULA_SECTION_TITLE);
+ builder->Add("oobeEulaIframeLabel", IDS_OOBE_EULA_IFRAME_LABEL);
+ builder->Add("oobeEulaAcceptAndContinueButtonText",
+ IDS_OOBE_EULA_ACCEPT_AND_CONTINUE_BUTTON_TEXT);
+}
+
+void EulaScreenHandler::DeclareJSCallbacks() {
+ AddCallback("eulaOnLearnMore", &EulaScreenHandler::HandleOnLearnMore);
+ AddCallback("eulaOnChromeOSCredits",
+ &EulaScreenHandler::HandleOnChromeOSCredits);
+ AddCallback("eulaOnChromeCredits", &EulaScreenHandler::HandleOnChromeCredits);
+ AddCallback("eulaOnLearnMore", &EulaScreenHandler::HandleOnLearnMore);
+ AddCallback("eulaOnInstallationSettingsPopupOpened",
+ &EulaScreenHandler::HandleOnInstallationSettingsPopupOpened);
+}
+
+void EulaScreenHandler::GetAdditionalParameters(base::DictionaryValue* dict) {
+#if BUILDFLAG(ENABLE_RLZ)
+ dict->SetString("rlzEnabled", "enabled");
+#else
+ dict->SetString("rlzEnabled", "disabled");
+#endif
+}
+
+void EulaScreenHandler::Initialize() {
+ if (!page_is_ready() || !screen_)
+ return;
+
+ core_oobe_view_->SetUsageStats(screen_->IsUsageStatsEnabled());
+
+ // This OEM EULA is a file:// URL which we're unable to load in iframe.
+ // Instead if it's defined we use chrome://terms/oem that will load same file.
+ if (!screen_->GetOemEulaUrl().is_empty())
+ core_oobe_view_->SetOemEulaUrl(chrome::kChromeUITermsOemURL);
+
+ if (show_on_init_) {
+ Show();
+ show_on_init_ = false;
+ }
+}
+
+void EulaScreenHandler::OnPasswordFetched(const std::string& tpm_password) {
+ core_oobe_view_->SetTpmPassword(tpm_password);
+}
+
+void EulaScreenHandler::HandleOnLearnMore() {
+ if (!help_app_.get())
+ help_app_ = new HelpAppLauncher(GetNativeWindow());
+ help_app_->ShowHelpTopic(HelpAppLauncher::HELP_STATS_USAGE);
+}
+
+void EulaScreenHandler::HandleOnChromeOSCredits() {
+ ShowCreditsDialog(
+ Profile::FromBrowserContext(
+ web_ui()->GetWebContents()->GetBrowserContext()),
+ GetNativeWindow(),
+ IDS_ABOUT_CROS_VERSION_LICENSE_EULA,
+ GURL(chrome::kChromeUIOSCreditsURL));
+}
+
+void EulaScreenHandler::HandleOnChromeCredits() {
+ ShowCreditsDialog(
+ Profile::FromBrowserContext(
+ web_ui()->GetWebContents()->GetBrowserContext()),
+ GetNativeWindow(),
+ IDS_ABOUT_VERSION_LICENSE_EULA,
+ GURL(chrome::kChromeUICreditsURL));
+}
+
+void EulaScreenHandler::HandleOnInstallationSettingsPopupOpened() {
+ if (screen_)
+ screen_->InitiatePasswordFetch();
+}
+
+void EulaScreenHandler::UpdateLocalizedValues(
+ ::login::SecureModuleUsed secure_module_used) {
+ base::DictionaryValue updated_secure_module_strings;
+ auto builder = base::MakeUnique<::login::LocalizedValuesBuilder>(
+ &updated_secure_module_strings);
+ if (secure_module_used == ::login::SecureModuleUsed::TPM) {
+ builder->Add("eulaTpmDesc", IDS_EULA_TPM_DESCRIPTION);
+ builder->Add("eulaTpmKeyDesc", IDS_EULA_TPM_KEY_DESCRIPTION);
+ builder->Add("eulaTpmDescPowerwash",
+ IDS_EULA_TPM_KEY_DESCRIPTION_POWERWASH);
+ builder->Add("eulaTpmBusy", IDS_EULA_TPM_BUSY);
+ core_oobe_view_->ReloadEulaContent(updated_secure_module_strings);
+ }
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h
new file mode 100644
index 00000000000..a0830d4c6e9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h
@@ -0,0 +1,74 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_EULA_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_EULA_SCREEN_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "chrome/browser/chromeos/login/screens/eula_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "chromeos/tpm/tpm_password_fetcher.h"
+#include "components/login/secure_module_util_chromeos.h"
+#include "content/public/browser/web_ui.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace chromeos {
+
+class CoreOobeView;
+class HelpAppLauncher;
+
+// WebUI implementation of EulaScreenView. It is used to interact
+// with the eula part of the JS page.
+class EulaScreenHandler : public EulaView,
+ public BaseScreenHandler,
+ public TpmPasswordFetcherDelegate {
+ public:
+ explicit EulaScreenHandler(CoreOobeView* core_oobe_view);
+ ~EulaScreenHandler() override;
+
+ // EulaView implementation:
+ void Show() override;
+ void Hide() override;
+ void Bind(EulaScreen* screen) override;
+ void Unbind() override;
+ void OnPasswordFetched(const std::string& tpm_password) override;
+
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void DeclareJSCallbacks() override;
+ void GetAdditionalParameters(base::DictionaryValue* dict) override;
+ void Initialize() override;
+
+ private:
+ // JS messages handlers.
+ void HandleOnLearnMore();
+ void HandleOnChromeCredits();
+ void HandleOnChromeOSCredits();
+ void HandleOnInstallationSettingsPopupOpened();
+
+ void UpdateLocalizedValues(::login::SecureModuleUsed secure_module_used);
+
+ EulaScreen* screen_ = nullptr;
+ CoreOobeView* core_oobe_view_ = nullptr;
+
+ // Help application used for help dialogs.
+ scoped_refptr<HelpAppLauncher> help_app_;
+
+ // Keeps whether screen should be shown right after initialization.
+ bool show_on_init_ = false;
+
+ base::WeakPtrFactory<EulaScreenHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(EulaScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_EULA_SCREEN_HANDLER_H_
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
new file mode 100644
index 00000000000..ed43fa46bf9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -0,0 +1,983 @@
+// 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/chromeos/login/gaia_screen_handler.h"
+
+#include "ash/system/devicetype_utils.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/guid.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_shutdown.h"
+#include "chrome/browser/chromeos/input_method/input_method_util.h"
+#include "chrome/browser/chromeos/language_preferences.h"
+#include "chrome/browser/chromeos/login/screens/network_error.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/proto/chrome_device_policy.pb.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/ui/webui/chromeos/login/enrollment_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
+#include "chrome/browser/ui/webui/signin/signin_utils.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "chromeos/login/auth/authpolicy_login_helper.h"
+#include "chromeos/login/auth/user_context.h"
+#include "chromeos/settings/cros_settings_names.h"
+#include "chromeos/system/devicetype.h"
+#include "chromeos/system/version_loader.h"
+#include "components/login/localized_values_builder.h"
+#include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/user_manager/known_user.h"
+#include "components/user_manager/user_manager.h"
+#include "components/version_info/version_info.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "ui/base/ime/chromeos/input_method_manager.h"
+
+using content::BrowserThread;
+namespace em = enterprise_management;
+
+namespace chromeos {
+
+namespace {
+
+const char kJsScreenPath[] = "login.GaiaSigninScreen";
+const char kAuthIframeParentName[] = "signin-frame";
+
+const char kRestrictiveProxyURL[] = "https://www.google.com/generate_204";
+
+const char kEndpointGen[] = "1.0";
+
+// The possible modes that the Gaia signin screen can be in.
+enum GaiaScreenMode {
+ // Default Gaia authentication will be used.
+ GAIA_SCREEN_MODE_DEFAULT = 0,
+
+ // Gaia offline mode will be used.
+ GAIA_SCREEN_MODE_OFFLINE = 1,
+
+ // An interstitial page will be used before SAML redirection.
+ GAIA_SCREEN_MODE_SAML_INTERSTITIAL = 2,
+
+ // Offline UI for Active Directory authentication.
+ GAIA_SCREEN_MODE_AD = 3,
+};
+
+policy::DeviceMode GetDeviceMode() {
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ return connector->GetDeviceMode();
+}
+
+GaiaScreenMode GetGaiaScreenMode(const std::string& email, bool use_offline) {
+ if (GetDeviceMode() == policy::DEVICE_MODE_ENTERPRISE_AD)
+ return GAIA_SCREEN_MODE_AD;
+
+ if (use_offline)
+ return GAIA_SCREEN_MODE_OFFLINE;
+
+ int authentication_behavior = 0;
+ CrosSettings::Get()->GetInteger(kLoginAuthenticationBehavior,
+ &authentication_behavior);
+ if (authentication_behavior ==
+ em::LoginAuthenticationBehaviorProto::SAML_INTERSTITIAL) {
+ if (email.empty())
+ return GAIA_SCREEN_MODE_SAML_INTERSTITIAL;
+
+ // If there's a populated email, we must check first that this user is using
+ // SAML in order to decide whether to show the interstitial page.
+ const user_manager::User* user = user_manager::UserManager::Get()->FindUser(
+ user_manager::known_user::GetAccountId(email, std::string() /* id */,
+ AccountType::UNKNOWN));
+
+ if (user && user->using_saml())
+ return GAIA_SCREEN_MODE_SAML_INTERSTITIAL;
+ }
+
+ return GAIA_SCREEN_MODE_DEFAULT;
+}
+
+std::string GetEnterpriseDomain() {
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ return connector->GetEnterpriseDomain();
+}
+
+std::string GetRealm() {
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ return connector->GetRealm();
+}
+
+std::string GetChromeType() {
+ switch (chromeos::GetDeviceType()) {
+ case chromeos::DeviceType::kChromebox:
+ return "chromebox";
+ case chromeos::DeviceType::kChromebase:
+ return "chromebase";
+ case chromeos::DeviceType::kChromebit:
+ return "chromebit";
+ case chromeos::DeviceType::kChromebook:
+ return "chromebook";
+ default:
+ return "chromedevice";
+ }
+}
+
+void UpdateAuthParams(base::DictionaryValue* params,
+ bool is_restrictive_proxy) {
+ CrosSettings* cros_settings = CrosSettings::Get();
+ bool allow_new_user = true;
+ cros_settings->GetBoolean(kAccountsPrefAllowNewUser, &allow_new_user);
+ bool allow_guest = true;
+ cros_settings->GetBoolean(kAccountsPrefAllowGuest, &allow_guest);
+ params->SetBoolean("guestSignin", allow_guest);
+
+ // nosignup flow if new users are not allowed.
+ if (!allow_new_user || is_restrictive_proxy)
+ params->SetString("flow", "nosignup");
+
+ // Allow supervised user creation only if:
+ // 1. Enterprise managed device > is allowed by policy.
+ // 2. Consumer device > owner exists.
+ // 3. New users are allowed by owner.
+ // 4. Supervised users are allowed by owner.
+ ChromeUserManager* user_manager = ChromeUserManager::Get();
+ bool supervised_users_can_create =
+ user_manager->AreSupervisedUsersAllowed() && allow_new_user &&
+ !user_manager->GetUsersAllowedForSupervisedUsersCreation().empty();
+ params->SetBoolean("supervisedUsersCanCreate", supervised_users_can_create);
+
+ // Now check whether we're in multi-profiles user adding scenario and
+ // disable GAIA right panel features if that's the case.
+ if (UserAddingScreen::Get()->IsRunning()) {
+ params->SetBoolean("guestSignin", false);
+ params->SetBoolean("supervisedUsersCanCreate", false);
+ }
+}
+
+void RecordSAMLScrapingVerificationResultInHistogram(bool success) {
+ UMA_HISTOGRAM_BOOLEAN("ChromeOS.SAML.Scraping.VerificationResult", success);
+}
+
+// The Task posted to PostTaskAndReply in StartClearingDnsCache on the IO
+// thread.
+void ClearDnsCache(IOThread* io_thread) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (browser_shutdown::IsTryingToQuit())
+ return;
+
+ io_thread->ClearHostCache(base::Callback<bool(const std::string&)>());
+}
+
+void PushFrontIMIfNotExists(const std::string& input_method,
+ std::vector<std::string>* input_methods) {
+ if (input_method.empty())
+ return;
+
+ if (!base::ContainsValue(*input_methods, input_method))
+ input_methods->insert(input_methods->begin(), input_method);
+}
+
+bool IsOnline(NetworkPortalDetector::CaptivePortalStatus status) {
+ return status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE;
+}
+
+} // namespace
+
+// A class that's used to specify the way how Gaia should be loaded.
+struct GaiaScreenHandler::GaiaContext {
+ GaiaContext();
+
+ // Forces Gaia to reload.
+ bool force_reload = false;
+
+ // Whether Gaia should be loaded in offline mode.
+ bool use_offline = false;
+
+ // Email of the current user.
+ std::string email;
+
+ // GAIA ID of the current user.
+ std::string gaia_id;
+
+ // GAPS cookie.
+ std::string gaps_cookie;
+};
+
+GaiaScreenHandler::GaiaContext::GaiaContext() {}
+
+GaiaScreenHandler::GaiaScreenHandler(
+ CoreOobeView* core_oobe_view,
+ const scoped_refptr<NetworkStateInformer>& network_state_informer)
+ : BaseScreenHandler(kScreenId),
+ network_state_informer_(network_state_informer),
+ core_oobe_view_(core_oobe_view),
+ weak_factory_(this) {
+ DCHECK(network_state_informer_.get());
+ set_call_js_prefix(kJsScreenPath);
+}
+
+GaiaScreenHandler::~GaiaScreenHandler() {
+ if (network_portal_detector_)
+ network_portal_detector_->RemoveObserver(this);
+}
+
+void GaiaScreenHandler::MaybePreloadAuthExtension() {
+ VLOG(1) << "MaybePreloadAuthExtension";
+
+ if (!network_portal_detector_) {
+ NetworkPortalDetectorImpl* detector = new NetworkPortalDetectorImpl(
+ g_browser_process->system_request_context(), false);
+ detector->set_portal_test_url(GURL(kRestrictiveProxyURL));
+ network_portal_detector_.reset(detector);
+ network_portal_detector_->AddObserver(this);
+ network_portal_detector_->Enable(true);
+ }
+
+ // If cookies clearing was initiated or |dns_clear_task_running_| then auth
+ // extension showing has already been initiated and preloading is pointless.
+ if (signin_screen_handler_->ShouldLoadGaia() && !gaia_silent_load_ &&
+ !cookies_cleared_ && !dns_clear_task_running_ &&
+ network_state_informer_->state() == NetworkStateInformer::ONLINE) {
+ gaia_silent_load_ = true;
+ gaia_silent_load_network_ = network_state_informer_->network_path();
+ LoadAuthExtension(true /* force */, false /* offline */);
+ }
+}
+
+void GaiaScreenHandler::DisableRestrictiveProxyCheckForTest() {
+ disable_restrictive_proxy_check_for_test_ = true;
+}
+
+void GaiaScreenHandler::LoadGaia(const GaiaContext& context) {
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&version_loader::GetVersion, version_loader::VERSION_SHORT),
+ base::Bind(&GaiaScreenHandler::LoadGaiaWithVersion,
+ weak_factory_.GetWeakPtr(), context));
+}
+
+void GaiaScreenHandler::LoadGaiaWithVersion(
+ const GaiaContext& context,
+ const std::string& platform_version) {
+ base::DictionaryValue params;
+
+ params.SetBoolean("forceReload", context.force_reload);
+ params.SetString("gaiaId", context.gaia_id);
+ params.SetBoolean("readOnlyEmail", true);
+ params.SetString("email", context.email);
+ params.SetString("gapsCookie", context.gaps_cookie);
+
+ UpdateAuthParams(&params, IsRestrictiveProxy());
+
+ GaiaScreenMode screen_mode = GetGaiaScreenMode(context.email,
+ context.use_offline);
+ params.SetInteger("screenMode", screen_mode);
+
+ if (screen_mode == GAIA_SCREEN_MODE_AD && !authpolicy_login_helper_)
+ authpolicy_login_helper_ = base::MakeUnique<AuthPolicyLoginHelper>();
+
+ if (screen_mode != GAIA_SCREEN_MODE_OFFLINE) {
+ const std::string app_locale = g_browser_process->GetApplicationLocale();
+ if (!app_locale.empty())
+ params.SetString("hl", app_locale);
+ }
+
+ std::string realm(GetRealm());
+ if (!realm.empty()) {
+ params.SetString("realm", realm);
+ }
+
+ std::string enterprise_domain(GetEnterpriseDomain());
+ if (!enterprise_domain.empty())
+ params.SetString("enterpriseDomain", enterprise_domain);
+
+ params.SetString("chromeType", GetChromeType());
+ params.SetString("clientId",
+ GaiaUrls::GetInstance()->oauth2_chrome_client_id());
+ params.SetString("clientVersion", version_info::GetVersionNumber());
+ if (!platform_version.empty())
+ params.SetString("platformVersion", platform_version);
+ params.SetString("releaseChannel", chrome::GetChannelString());
+ params.SetString("endpointGen", kEndpointGen);
+
+ std::string email_domain;
+ if (CrosSettings::Get()->GetString(kAccountsPrefLoginScreenDomainAutoComplete,
+ &email_domain) &&
+ !email_domain.empty()) {
+ params.SetString("emailDomain", email_domain);
+ }
+
+ params.SetString("gaiaUrl", GaiaUrls::GetInstance()->gaia_url().spec());
+
+ if (use_easy_bootstrap_) {
+ params.SetBoolean("useEafe", true);
+ // Easy login overrides.
+ std::string eafe_url = "https://easylogin.corp.google.com/";
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch(switches::kEafeUrl))
+ eafe_url = command_line->GetSwitchValueASCII(switches::kEafeUrl);
+ std::string eafe_path = "planters/cbaudioChrome";
+ if (command_line->HasSwitch(switches::kEafePath))
+ eafe_path = command_line->GetSwitchValueASCII(switches::kEafePath);
+
+ params.SetString("gaiaUrl", eafe_url);
+ params.SetString("gaiaPath", eafe_path);
+ }
+
+ frame_state_ = FRAME_STATE_LOADING;
+ CallJS("loadAuthExtension", params);
+}
+
+void GaiaScreenHandler::ReloadGaia(bool force_reload) {
+ if (frame_state_ == FRAME_STATE_LOADING && !force_reload) {
+ VLOG(1) << "Skipping reloading of Gaia since gaia is loading.";
+ return;
+ }
+ NetworkStateInformer::State state = network_state_informer_->state();
+ if (state != NetworkStateInformer::ONLINE &&
+ !signin_screen_handler_->proxy_auth_dialog_need_reload_) {
+ VLOG(1) << "Skipping reloading of Gaia since network state="
+ << NetworkStateInformer::StatusString(state);
+ return;
+ }
+
+ signin_screen_handler_->proxy_auth_dialog_need_reload_ = false;
+ VLOG(1) << "Reloading Gaia.";
+ frame_state_ = FRAME_STATE_LOADING;
+ LoadAuthExtension(force_reload, false /* offline */);
+}
+
+void GaiaScreenHandler::MonitorOfflineIdle(bool is_online) {
+ CallJS("monitorOfflineIdle", is_online);
+}
+
+void GaiaScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("signinScreenTitle", IDS_SIGNIN_SCREEN_TITLE_TAB_PROMPT);
+ builder->Add("guestSignin", IDS_BROWSE_WITHOUT_SIGNING_IN_HTML);
+ builder->Add("backButton", IDS_ACCNAME_BACK);
+ builder->Add("closeButton", IDS_CLOSE);
+ builder->Add("whitelistErrorConsumer", IDS_LOGIN_ERROR_WHITELIST);
+ 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("gaiaLoading", IDS_LOGIN_GAIA_LOADING_MESSAGE);
+
+ // Strings used by the SAML fatal error dialog.
+ builder->Add("fatalErrorMessageNoAccountDetails",
+ IDS_LOGIN_FATAL_ERROR_NO_ACCOUNT_DETAILS);
+ builder->Add("fatalErrorMessageNoPassword",
+ IDS_LOGIN_FATAL_ERROR_NO_PASSWORD);
+ builder->Add("fatalErrorMessageVerificationFailed",
+ IDS_LOGIN_FATAL_ERROR_PASSWORD_VERIFICATION);
+ builder->Add("fatalErrorMessageInsecureURL",
+ IDS_LOGIN_FATAL_ERROR_TEXT_INSECURE_URL);
+ builder->Add("fatalErrorDoneButton", IDS_DONE);
+ builder->Add("fatalErrorTryAgainButton",
+ IDS_LOGIN_FATAL_ERROR_TRY_AGAIN_BUTTON);
+
+ builder->AddF("loginWelcomeMessage", IDS_LOGIN_WELCOME_MESSAGE,
+ ash::GetChromeOSDeviceTypeResourceId());
+ builder->Add("offlineLoginEmail", IDS_OFFLINE_LOGIN_EMAIL);
+ builder->Add("offlineLoginPassword", IDS_OFFLINE_LOGIN_PASSWORD);
+ builder->Add("offlineLoginInvalidEmail", IDS_OFFLINE_LOGIN_INVALID_EMAIL);
+ builder->Add("offlineLoginInvalidPassword",
+ IDS_OFFLINE_LOGIN_INVALID_PASSWORD);
+ builder->Add("offlineLoginNextBtn", IDS_OFFLINE_LOGIN_NEXT_BUTTON_TEXT);
+ builder->Add("offlineLoginForgotPasswordBtn",
+ IDS_OFFLINE_LOGIN_FORGOT_PASSWORD_BUTTON_TEXT);
+ builder->Add("offlineLoginForgotPasswordDlg",
+ IDS_OFFLINE_LOGIN_FORGOT_PASSWORD_DIALOG_TEXT);
+ builder->Add("offlineLoginCloseBtn", IDS_OFFLINE_LOGIN_CLOSE_BUTTON_TEXT);
+ builder->Add("enterpriseInfoMessage", IDS_LOGIN_DEVICE_MANAGED_BY_NOTICE);
+ builder->Add("samlInterstitialMessage",
+ IDS_LOGIN_SAML_INTERSTITIAL_MESSAGE);
+ builder->Add("samlInterstitialChangeAccountLink",
+ IDS_LOGIN_SAML_INTERSTITIAL_CHANGE_ACCOUNT_LINK_TEXT);
+ builder->Add("samlInterstitialNextBtn",
+ IDS_LOGIN_SAML_INTERSTITIAL_NEXT_BUTTON_TEXT);
+
+ builder->Add("adAuthWelcomeMessage", IDS_AD_DOMAIN_AUTH_WELCOME_MESSAGE);
+ builder->Add("adLoginUser", IDS_AD_LOGIN_USER);
+ builder->Add("adLoginPassword", IDS_AD_LOGIN_PASSWORD);
+}
+
+void GaiaScreenHandler::Initialize() {
+}
+
+void GaiaScreenHandler::RegisterMessages() {
+ AddCallback("webviewLoadAborted",
+ &GaiaScreenHandler::HandleWebviewLoadAborted);
+ AddCallback("completeLogin", &GaiaScreenHandler::HandleCompleteLogin);
+ AddCallback("completeAuthentication",
+ &GaiaScreenHandler::HandleCompleteAuthentication);
+ AddCallback("completeAuthenticationAuthCodeOnly",
+ &GaiaScreenHandler::HandleCompleteAuthenticationAuthCodeOnly);
+ AddCallback("usingSAMLAPI", &GaiaScreenHandler::HandleUsingSAMLAPI);
+ AddCallback("scrapedPasswordCount",
+ &GaiaScreenHandler::HandleScrapedPasswordCount);
+ AddCallback("scrapedPasswordVerificationFailed",
+ &GaiaScreenHandler::HandleScrapedPasswordVerificationFailed);
+ AddCallback("loginWebuiReady", &GaiaScreenHandler::HandleGaiaUIReady);
+ AddCallback("toggleEasyBootstrap",
+ &GaiaScreenHandler::HandleToggleEasyBootstrap);
+ AddCallback("identifierEntered", &GaiaScreenHandler::HandleIdentifierEntered);
+ AddCallback("updateOfflineLogin",
+ &GaiaScreenHandler::set_offline_login_is_active);
+ AddCallback("authExtensionLoaded",
+ &GaiaScreenHandler::HandleAuthExtensionLoaded);
+ AddCallback("completeAdAuthentication",
+ &GaiaScreenHandler::HandleCompleteAdAuthentication);
+ AddCallback("completeAdPasswordChange",
+ &GaiaScreenHandler::HandleCompleteAdPasswordChange);
+ AddCallback("cancelAdAuthentication",
+ &GaiaScreenHandler::HandleCancelActiveDirectoryAuth);
+}
+
+void GaiaScreenHandler::OnPortalDetectionCompleted(
+ const NetworkState* network,
+ const NetworkPortalDetector::CaptivePortalState& state) {
+ VLOG(1) << "OnPortalDetectionCompleted "
+ << NetworkPortalDetector::CaptivePortalStatusString(state.status);
+
+ const NetworkPortalDetector::CaptivePortalStatus previous_status =
+ captive_portal_status_;
+ captive_portal_status_ = state.status;
+ if (offline_login_is_active() ||
+ IsOnline(captive_portal_status_) == IsOnline(previous_status) ||
+ disable_restrictive_proxy_check_for_test_ ||
+ GetCurrentScreen() != kScreenId)
+ return;
+
+ LoadAuthExtension(true /* force */, false /* offline */);
+}
+
+void GaiaScreenHandler::HandleIdentifierEntered(const std::string& user_email) {
+ if (!Delegate()->IsUserWhitelisted(user_manager::known_user::GetAccountId(
+ user_email, std::string() /* id */, AccountType::UNKNOWN)))
+ ShowWhitelistCheckFailedError();
+}
+
+void GaiaScreenHandler::HandleAuthExtensionLoaded() {
+ VLOG(1) << "Auth extension finished loading";
+ auth_extension_being_loaded_ = false;
+}
+
+void GaiaScreenHandler::HandleWebviewLoadAborted(
+ const std::string& error_reason_str) {
+ // TODO(nkostylev): Switch to int code once webview supports that.
+ // http://crbug.com/470483
+ if (error_reason_str == "ERR_ABORTED") {
+ LOG(WARNING) << "Ignoring Gaia webview error: " << error_reason_str;
+ return;
+ }
+
+ // TODO(nkostylev): Switch to int code once webview supports that.
+ // http://crbug.com/470483
+ // Extract some common codes used by SigninScreenHandler for now.
+ if (error_reason_str == "ERR_NAME_NOT_RESOLVED")
+ frame_error_ = net::ERR_NAME_NOT_RESOLVED;
+ else if (error_reason_str == "ERR_INTERNET_DISCONNECTED")
+ frame_error_ = net::ERR_INTERNET_DISCONNECTED;
+ else if (error_reason_str == "ERR_NETWORK_CHANGED")
+ frame_error_ = net::ERR_NETWORK_CHANGED;
+ else if (error_reason_str == "ERR_INTERNET_DISCONNECTED")
+ frame_error_ = net::ERR_INTERNET_DISCONNECTED;
+ else if (error_reason_str == "ERR_PROXY_CONNECTION_FAILED")
+ frame_error_ = net::ERR_PROXY_CONNECTION_FAILED;
+ else if (error_reason_str == "ERR_TUNNEL_CONNECTION_FAILED")
+ frame_error_ = net::ERR_TUNNEL_CONNECTION_FAILED;
+ else
+ frame_error_ = net::ERR_INTERNET_DISCONNECTED;
+
+ LOG(ERROR) << "Gaia webview error: " << error_reason_str;
+ NetworkError::ErrorReason error_reason =
+ NetworkError::ERROR_REASON_FRAME_ERROR;
+ frame_state_ = FRAME_STATE_ERROR;
+ UpdateState(error_reason);
+}
+
+AccountId GaiaScreenHandler::GetAccountId(
+ const std::string& authenticated_email,
+ const std::string& id,
+ const AccountType& account_type) const {
+ const std::string canonicalized_email =
+ gaia::CanonicalizeEmail(gaia::SanitizeEmail(authenticated_email));
+
+ const AccountId account_id = user_manager::known_user::GetAccountId(
+ authenticated_email, id, account_type);
+
+ if (account_id.GetUserEmail() != canonicalized_email) {
+ LOG(WARNING) << "Existing user '" << account_id.GetUserEmail()
+ << "' authenticated by alias '" << canonicalized_email << "'.";
+ }
+
+ return account_id;
+}
+
+void GaiaScreenHandler::DoAdAuth(
+ const std::string& username,
+ const Key& key,
+ authpolicy::ErrorType error,
+ const authpolicy::ActiveDirectoryAccountInfo& account_info) {
+ switch (error) {
+ case authpolicy::ERROR_NONE: {
+ DCHECK(account_info.has_account_id() &&
+ !account_info.account_id().empty());
+ const AccountId account_id(GetAccountId(
+ username, account_info.account_id(), AccountType::ACTIVE_DIRECTORY));
+ Delegate()->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);
+ break;
+ }
+ case authpolicy::ERROR_PASSWORD_EXPIRED:
+ core_oobe_view_->ShowActiveDirectoryPasswordChangeScreen(username);
+ break;
+ case authpolicy::ERROR_PARSE_UPN_FAILED:
+ case authpolicy::ERROR_BAD_USER_NAME:
+ CallJS("invalidateAd", username,
+ static_cast<int>(ActiveDirectoryErrorState::BAD_USERNAME));
+ return;
+ case authpolicy::ERROR_BAD_PASSWORD:
+ CallJS("invalidateAd", username,
+ static_cast<int>(ActiveDirectoryErrorState::BAD_PASSWORD));
+ case authpolicy::ERROR_UNKNOWN:
+ case authpolicy::ERROR_DBUS_FAILURE:
+ case authpolicy::ERROR_CANNOT_RESOLVE_KDC:
+ case authpolicy::ERROR_KINIT_FAILED:
+ case authpolicy::ERROR_NET_FAILED:
+ case authpolicy::ERROR_SMBCLIENT_FAILED:
+ case authpolicy::ERROR_PARSE_FAILED:
+ case authpolicy::ERROR_PARSE_PREG_FAILED:
+ case authpolicy::ERROR_BAD_GPOS:
+ case authpolicy::ERROR_LOCAL_IO:
+ case authpolicy::ERROR_NOT_JOINED:
+ case authpolicy::ERROR_NOT_LOGGED_IN:
+ case authpolicy::ERROR_STORE_POLICY_FAILED:
+ LoadAuthExtension(true, false /* offline */);
+ break;
+ default:
+ // TODO(rsorokin): Proper error handling.
+ DLOG(WARNING) << "Unhandled error code: " << error;
+ LoadAuthExtension(true, false /* offline */);
+ }
+}
+
+void GaiaScreenHandler::HandleCompleteAdAuthentication(
+ const std::string& username,
+ const std::string& password) {
+ Delegate()->SetDisplayEmail(username);
+ set_populated_email(username);
+ DCHECK(authpolicy_login_helper_);
+ authpolicy_login_helper_->AuthenticateUser(
+ username, std::string() /* object_guid */, password,
+ base::BindOnce(&GaiaScreenHandler::DoAdAuth, weak_factory_.GetWeakPtr(),
+ username, Key(password)));
+}
+
+void GaiaScreenHandler::HandleCompleteAdPasswordChange(
+ const std::string& username,
+ const std::string& old_password,
+ const std::string& new_password) {
+ Delegate()->SetDisplayEmail(username);
+ set_populated_email(username);
+
+ DCHECK(authpolicy_login_helper_);
+ authpolicy_login_helper_->AuthenticateUser(
+ username, std::string() /* object_guid */,
+ old_password + "\n" + new_password + "\n" + new_password,
+ base::Bind(&GaiaScreenHandler::DoAdAuth, weak_factory_.GetWeakPtr(),
+ username, Key(new_password)));
+}
+
+void GaiaScreenHandler::HandleCancelActiveDirectoryAuth() {
+ DCHECK(authpolicy_login_helper_);
+ authpolicy_login_helper_->CancelRequestsAndRestart();
+}
+
+void GaiaScreenHandler::HandleCompleteAuthentication(
+ const std::string& gaia_id,
+ const std::string& email,
+ const std::string& password,
+ const std::string& auth_code,
+ bool using_saml,
+ const std::string& gaps_cookie) {
+ if (!Delegate())
+ return;
+
+ DCHECK(!email.empty());
+ DCHECK(!gaia_id.empty());
+ const std::string sanitized_email = gaia::SanitizeEmail(email);
+ Delegate()->SetDisplayEmail(sanitized_email);
+
+ UserContext user_context(GetAccountId(email, gaia_id, AccountType::GOOGLE));
+ user_context.SetKey(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);
+}
+
+void GaiaScreenHandler::HandleCompleteAuthenticationAuthCodeOnly(
+ const std::string& auth_code) {
+ if (!Delegate())
+ return;
+
+ UserContext user_context;
+ user_context.SetAuthFlow(UserContext::AUTH_FLOW_EASY_BOOTSTRAP);
+ user_context.SetAuthCode(auth_code);
+ Delegate()->CompleteLogin(user_context);
+}
+
+void GaiaScreenHandler::HandleCompleteLogin(const std::string& gaia_id,
+ const std::string& typed_email,
+ const std::string& password,
+ bool using_saml) {
+ DoCompleteLogin(gaia_id, typed_email, password, using_saml);
+}
+
+void GaiaScreenHandler::HandleUsingSAMLAPI() {
+ SetSAMLPrincipalsAPIUsed(true);
+}
+
+void GaiaScreenHandler::HandleScrapedPasswordCount(int password_count) {
+ SetSAMLPrincipalsAPIUsed(false);
+ // Use a histogram that has 11 buckets, one for each of the values in [0, 9]
+ // and an overflow bucket at the end.
+ UMA_HISTOGRAM_ENUMERATION(
+ "ChromeOS.SAML.Scraping.PasswordCount", std::min(password_count, 10), 11);
+ if (password_count == 0)
+ HandleScrapedPasswordVerificationFailed();
+}
+
+void GaiaScreenHandler::HandleScrapedPasswordVerificationFailed() {
+ RecordSAMLScrapingVerificationResultInHistogram(false);
+}
+
+void GaiaScreenHandler::HandleToggleEasyBootstrap() {
+ use_easy_bootstrap_ = !use_easy_bootstrap_;
+ LoadAuthExtension(true /* force */, false /* offline */);
+}
+
+void GaiaScreenHandler::HandleGaiaUIReady() {
+ VLOG(1) << "Gaia is loaded";
+
+ // As we could miss and window.onload could already be called, restore
+ // focus to current pod (see crbug/175243).
+ if (gaia_silent_load_)
+ signin_screen_handler_->RefocusCurrentPod();
+
+ frame_error_ = net::OK;
+ frame_state_ = FRAME_STATE_LOADED;
+
+ if (network_state_informer_->state() == NetworkStateInformer::ONLINE)
+ UpdateState(NetworkError::ERROR_REASON_UPDATE);
+
+ if (test_expects_complete_login_)
+ SubmitLoginFormForTest();
+ if (Delegate())
+ Delegate()->OnGaiaScreenReady();
+}
+
+void GaiaScreenHandler::DoCompleteLogin(const std::string& gaia_id,
+ const std::string& typed_email,
+ const std::string& password,
+ bool using_saml) {
+ if (!Delegate())
+ return;
+
+ if (using_saml && !using_saml_api_)
+ RecordSAMLScrapingVerificationResultInHistogram(true);
+
+ DCHECK(!typed_email.empty());
+ DCHECK(!gaia_id.empty());
+ const std::string sanitized_email = gaia::SanitizeEmail(typed_email);
+ Delegate()->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);
+
+ if (test_expects_complete_login_) {
+ VLOG(2) << "Complete test login for " << typed_email
+ << ", requested=" << test_user_;
+
+ test_expects_complete_login_ = false;
+ test_user_.clear();
+ test_pass_.clear();
+ }
+}
+
+void GaiaScreenHandler::StartClearingDnsCache() {
+ if (dns_clear_task_running_ || !g_browser_process->io_thread())
+ return;
+
+ dns_cleared_ = false;
+ BrowserThread::PostTaskAndReply(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&ClearDnsCache, g_browser_process->io_thread()),
+ base::Bind(&GaiaScreenHandler::OnDnsCleared, weak_factory_.GetWeakPtr()));
+ dns_clear_task_running_ = true;
+}
+
+void GaiaScreenHandler::OnDnsCleared() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ dns_clear_task_running_ = false;
+ dns_cleared_ = true;
+ ShowGaiaScreenIfReady();
+}
+
+void GaiaScreenHandler::StartClearingCookies(
+ const base::Closure& on_clear_callback) {
+ cookies_cleared_ = false;
+ ProfileHelper* profile_helper = ProfileHelper::Get();
+ LOG_ASSERT(Profile::FromWebUI(web_ui()) ==
+ profile_helper->GetSigninProfile());
+ profile_helper->ClearSigninProfile(
+ base::Bind(&GaiaScreenHandler::OnCookiesCleared,
+ weak_factory_.GetWeakPtr(), on_clear_callback));
+}
+
+void GaiaScreenHandler::OnCookiesCleared(
+ const base::Closure& on_clear_callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ cookies_cleared_ = true;
+ on_clear_callback.Run();
+}
+
+void GaiaScreenHandler::ShowSigninScreenForTest(const std::string& username,
+ const std::string& password) {
+ VLOG(2) << "ShowSigninScreenForTest for user " << username
+ << ", frame_state=" << frame_state();
+
+ test_user_ = username;
+ test_pass_ = password;
+ test_expects_complete_login_ = true;
+
+ // Submit login form for test if gaia is ready. If gaia is loading, login
+ // will be attempted in HandleLoginWebuiReady after gaia is ready. Otherwise,
+ // reload gaia then follow the loading case.
+ if (frame_state() == GaiaScreenHandler::FRAME_STATE_LOADED) {
+ SubmitLoginFormForTest();
+ } else if (frame_state() != GaiaScreenHandler::FRAME_STATE_LOADING) {
+ signin_screen_handler_->OnShowAddUser();
+ }
+}
+
+void GaiaScreenHandler::SubmitLoginFormForTest() {
+ VLOG(2) << "Submit login form for test, user=" << test_user_;
+
+ content::RenderFrameHost* frame =
+ signin::GetAuthFrame(web_ui()->GetWebContents(), kAuthIframeParentName);
+
+ std::string code =
+ "document.getElementById('identifier').value = '" + test_user_ + "';"
+ "document.getElementById('nextButton').click();";
+ frame->ExecuteJavaScriptForTests(base::ASCIIToUTF16(code));
+
+ if (!test_pass_.empty()) {
+ code = "document.getElementById('password').value = '" + test_pass_ + "';";
+ code += "document.getElementById('nextButton').click();";
+ frame->ExecuteJavaScriptForTests(base::ASCIIToUTF16(code));
+ }
+
+ // Test properties are cleared in HandleCompleteLogin because the form
+ // submission might fail and login will not be attempted after reloading
+ // if they are cleared here.
+}
+
+void GaiaScreenHandler::SetSAMLPrincipalsAPIUsed(bool api_used) {
+ using_saml_api_ = api_used;
+ UMA_HISTOGRAM_BOOLEAN("ChromeOS.SAML.APIUsed", api_used);
+}
+
+void GaiaScreenHandler::ShowGaiaAsync() {
+ show_when_dns_and_cookies_cleared_ = true;
+ if (gaia_silent_load_ && populated_email_.empty()) {
+ dns_cleared_ = true;
+ cookies_cleared_ = true;
+ ShowGaiaScreenIfReady();
+ } else {
+ StartClearingDnsCache();
+ StartClearingCookies(base::Bind(&GaiaScreenHandler::ShowGaiaScreenIfReady,
+ weak_factory_.GetWeakPtr()));
+ }
+}
+
+void GaiaScreenHandler::CancelShowGaiaAsync() {
+ show_when_dns_and_cookies_cleared_ = false;
+}
+
+void GaiaScreenHandler::ShowGaiaScreenIfReady() {
+ if (!dns_cleared_ ||
+ !cookies_cleared_ ||
+ !show_when_dns_and_cookies_cleared_ ||
+ !Delegate()) {
+ return;
+ }
+
+ std::string active_network_path = network_state_informer_->network_path();
+ if (gaia_silent_load_ &&
+ (network_state_informer_->state() != NetworkStateInformer::ONLINE ||
+ gaia_silent_load_network_ != active_network_path)) {
+ // Network has changed. Force Gaia reload.
+ gaia_silent_load_ = false;
+ }
+
+ // Note that LoadAuthExtension clears |populated_email_|.
+ if (populated_email_.empty()) {
+ Delegate()->LoadSigninWallpaper();
+ } else {
+ Delegate()->LoadWallpaper(user_manager::known_user::GetAccountId(
+ populated_email_, std::string() /* id */, AccountType::UNKNOWN));
+ }
+
+ input_method::InputMethodManager* imm =
+ input_method::InputMethodManager::Get();
+
+ scoped_refptr<input_method::InputMethodManager::State> gaia_ime_state =
+ imm->GetActiveIMEState()->Clone();
+ imm->SetState(gaia_ime_state);
+
+ // Set Least Recently Used input method for the user.
+ if (!populated_email_.empty()) {
+ SigninScreenHandler::SetUserInputMethod(populated_email_,
+ gaia_ime_state.get());
+ } else {
+ std::vector<std::string> input_methods;
+ if (gaia_ime_state->GetAllowedInputMethods().empty()) {
+ input_methods =
+ imm->GetInputMethodUtil()->GetHardwareLoginInputMethodIds();
+ } else {
+ input_methods = gaia_ime_state->GetAllowedInputMethods();
+ }
+ const std::string owner_im = SigninScreenHandler::GetUserLastInputMethod(
+ user_manager::UserManager::Get()->GetOwnerAccountId().GetUserEmail());
+ const std::string system_im = g_browser_process->local_state()->GetString(
+ language_prefs::kPreferredKeyboardLayout);
+
+ PushFrontIMIfNotExists(owner_im, &input_methods);
+ PushFrontIMIfNotExists(system_im, &input_methods);
+
+ gaia_ime_state->EnableLoginLayouts(
+ g_browser_process->GetApplicationLocale(), input_methods);
+
+ if (!system_im.empty()) {
+ gaia_ime_state->ChangeInputMethod(system_im, false /* show_message */);
+ } else if (!owner_im.empty()) {
+ gaia_ime_state->ChangeInputMethod(owner_im, false /* show_message */);
+ }
+ }
+
+ LoadAuthExtension(!gaia_silent_load_ /* force */, false /* offline */);
+ signin_screen_handler_->UpdateUIState(
+ SigninScreenHandler::UI_STATE_GAIA_SIGNIN, nullptr);
+ core_oobe_view_->UpdateKeyboardState();
+
+ if (gaia_silent_load_) {
+ // The variable is assigned to false because silently loaded Gaia page was
+ // used.
+ gaia_silent_load_ = false;
+ }
+ UpdateState(NetworkError::ERROR_REASON_UPDATE);
+
+ if (core_oobe_view_) {
+ PrefService* prefs = g_browser_process->local_state();
+ if (prefs->GetBoolean(prefs::kFactoryResetRequested)) {
+ core_oobe_view_->ShowDeviceResetScreen();
+ } else if (prefs->GetBoolean(prefs::kDebuggingFeaturesRequested)) {
+ core_oobe_view_->ShowEnableDebuggingScreen();
+ }
+ }
+}
+
+void GaiaScreenHandler::ShowWhitelistCheckFailedError() {
+ base::DictionaryValue params;
+ params.SetBoolean("enterpriseManaged",
+ g_browser_process->platform_part()
+ ->browser_policy_connector_chromeos()
+ ->IsEnterpriseManaged());
+ CallJS("showWhitelistCheckFailedError", true, params);
+}
+
+void GaiaScreenHandler::LoadAuthExtension(bool force,
+ bool offline) {
+ VLOG(1) << "LoadAuthExtension, force: " << force
+ << ", offline: " << offline;
+
+ if (auth_extension_being_loaded_) {
+ VLOG(1) << "Skip loading the Auth extension as it's already being loaded";
+ return;
+ }
+
+ auth_extension_being_loaded_ = true;
+ GaiaContext context;
+ context.force_reload = force;
+ context.use_offline = offline;
+ context.email = populated_email_;
+
+ std::string gaia_id;
+ if (!context.email.empty() &&
+ user_manager::known_user::FindGaiaID(
+ AccountId::FromUserEmail(context.email), &gaia_id)) {
+ context.gaia_id = gaia_id;
+ }
+
+ if (!context.email.empty()) {
+ context.gaps_cookie = user_manager::known_user::GetGAPSCookie(
+ AccountId::FromUserEmail(gaia::CanonicalizeEmail(context.email)));
+ }
+
+ populated_email_.clear();
+
+ LoadGaia(context);
+}
+
+void GaiaScreenHandler::UpdateState(NetworkError::ErrorReason reason) {
+ if (signin_screen_handler_)
+ 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_);
+}
+
+} // namespace chromeos
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
new file mode 100644
index 00000000000..15a6b1072fd
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
@@ -0,0 +1,292 @@
+// 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_CHROMEOS_LOGIN_GAIA_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_GAIA_SCREEN_HANDLER_H_
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "chrome/browser/chromeos/login/screens/core_oobe_view.h"
+#include "chrome/browser/chromeos/login/screens/gaia_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
+#include "chromeos/network/portal_detector/network_portal_detector.h"
+#include "net/base/net_errors.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+class AccountId;
+
+namespace authpolicy {
+class ActiveDirectoryAccountInfo;
+}
+
+namespace chromeos {
+
+class AuthPolicyLoginHelper;
+class Key;
+class SigninScreenHandler;
+class SigninScreenHandlerDelegate;
+
+// A class that handles WebUI hooks in Gaia screen.
+class GaiaScreenHandler : public BaseScreenHandler,
+ public GaiaView,
+ public NetworkPortalDetector::Observer {
+ public:
+ enum FrameState {
+ FRAME_STATE_UNKNOWN = 0,
+ FRAME_STATE_LOADING,
+ FRAME_STATE_LOADED,
+ FRAME_STATE_ERROR
+ };
+
+ GaiaScreenHandler(
+ CoreOobeView* core_oobe_view,
+ const scoped_refptr<NetworkStateInformer>& network_state_informer);
+ ~GaiaScreenHandler() override;
+
+ // GaiaView:
+ void MaybePreloadAuthExtension() override;
+ void DisableRestrictiveProxyCheckForTest() override;
+
+ private:
+ // TODO (antrim@): remove this dependency.
+ friend class SigninScreenHandler;
+
+ struct GaiaContext;
+
+ void LoadGaia(const GaiaContext& context);
+
+ // Callback that loads GAIA after version information has been retrieved.
+ void LoadGaiaWithVersion(const GaiaContext& context,
+ const std::string& platform_version);
+
+ // Sends request to reload Gaia. If |force_reload| is true, request
+ // will be sent in any case, otherwise it will be sent only when Gaia is
+ // not loading right now.
+ void ReloadGaia(bool force_reload);
+
+ // Turns offline idle detection on or off. Idle detection should only be on if
+ // we're using the offline login page but the device is online.
+ void MonitorOfflineIdle(bool is_online);
+
+ // Show error UI at the end of GAIA flow when user is not whitelisted.
+ void ShowWhitelistCheckFailedError();
+
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void Initialize() override;
+
+ // WebUIMessageHandler implementation:
+ void RegisterMessages() override;
+
+ // NetworkPortalDetector::Observer implementation.
+ void OnPortalDetectionCompleted(
+ const NetworkState* network,
+ const NetworkPortalDetector::CaptivePortalState& state) override;
+
+ // WebUI message handlers.
+ void HandleWebviewLoadAborted(const std::string& error_reason_str);
+ void HandleCompleteAuthentication(const std::string& gaia_id,
+ const std::string& email,
+ const std::string& password,
+ const std::string& auth_code,
+ bool using_saml,
+ const std::string& gaps_cookie);
+ void HandleCompleteAuthenticationAuthCodeOnly(const std::string& auth_code);
+ void HandleCompleteLogin(const std::string& gaia_id,
+ const std::string& typed_email,
+ const std::string& password,
+ bool using_saml);
+
+ void HandleCompleteAdAuthentication(const std::string& username,
+ const std::string& password);
+
+ void HandleCompleteAdPasswordChange(const std::string& username,
+ const std::string& old_password,
+ const std::string& new_password);
+
+ void HandleCancelActiveDirectoryAuth();
+
+ void HandleUsingSAMLAPI();
+ void HandleScrapedPasswordCount(int password_count);
+ void HandleScrapedPasswordVerificationFailed();
+
+ void HandleGaiaUIReady();
+
+ void HandleToggleEasyBootstrap();
+
+ void HandleIdentifierEntered(const std::string& account_identifier);
+
+ void HandleAuthExtensionLoaded();
+
+ // Really handles the complete login message.
+ void DoCompleteLogin(const std::string& gaia_id,
+ const std::string& typed_email,
+ const std::string& password,
+ bool using_saml);
+
+ // Fill GAIA user name.
+ void set_populated_email(const std::string& populated_email) {
+ populated_email_ = populated_email;
+ }
+
+ // Kick off cookie / local storage cleanup.
+ void StartClearingCookies(const base::Closure& on_clear_callback);
+ void OnCookiesCleared(const base::Closure& on_clear_callback);
+
+ // Kick off DNS cache flushing.
+ void StartClearingDnsCache();
+ void OnDnsCleared();
+
+ // Callback for AuthPolicyClient.
+ void DoAdAuth(const std::string& username,
+ const Key& key,
+ authpolicy::ErrorType error,
+ const authpolicy::ActiveDirectoryAccountInfo& account_info);
+
+ // Callback for writing password into pipe.
+ void OnPasswordPipeReady(const std::string& username,
+ const Key& key,
+ base::ScopedFD password_fd);
+
+ // Show sign-in screen for the given credentials.
+ void ShowSigninScreenForTest(const std::string& username,
+ const std::string& password);
+ // Attempts login for test.
+ void SubmitLoginFormForTest();
+
+ // Updates the member variable and UMA histogram indicating whether the
+ // 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();
+
+ // Shows signin screen after dns cache and cookie cleanup operations finish.
+ void ShowGaiaScreenIfReady();
+
+ // Tells webui to load authentication extension. |force| is used to force the
+ // extension reloading, if it has already been loaded. |offline| is true when
+ // offline version of the extension should be used.
+ void LoadAuthExtension(bool force, bool offline);
+
+ // TODO (antrim@): GaiaScreenHandler should implement
+ // NetworkStateInformer::Observer.
+ void UpdateState(NetworkError::ErrorReason reason);
+
+ // TODO (antrim@): remove this dependency.
+ void set_signin_screen_handler(SigninScreenHandler* handler) {
+ signin_screen_handler_ = handler;
+ }
+
+ // Are we on a restrictive proxy?
+ bool IsRestrictiveProxy() const;
+
+ SigninScreenHandlerDelegate* Delegate();
+
+ // Returns temporary unused device Id.
+ std::string GetTemporaryDeviceId();
+
+ FrameState frame_state() const { return frame_state_; }
+ net::Error frame_error() const { return frame_error_; }
+
+ // Returns user canonical e-mail. Finds already used account alias, if
+ // user has already signed in.
+ AccountId GetAccountId(const std::string& authenticated_email,
+ const std::string& id,
+ const AccountType& account_type) const;
+
+ bool offline_login_is_active() const { return offline_login_is_active_; }
+ void set_offline_login_is_active(bool offline_login_is_active) {
+ offline_login_is_active_ = offline_login_is_active;
+ }
+
+ // Current state of Gaia frame.
+ FrameState frame_state_ = FRAME_STATE_UNKNOWN;
+
+ // Latest Gaia frame error.
+ net::Error frame_error_ = net::OK;
+
+ // Network state informer used to keep signin screen up.
+ scoped_refptr<NetworkStateInformer> network_state_informer_;
+
+ CoreOobeView* core_oobe_view_ = nullptr;
+
+ // Email to pre-populate with.
+ std::string populated_email_;
+
+ // True if dns cache cleanup is done.
+ bool dns_cleared_ = false;
+
+ // True if DNS cache task is already running.
+ bool dns_clear_task_running_ = false;
+
+ // True if cookie jar cleanup is done.
+ bool cookies_cleared_ = false;
+
+ // If true, the sign-in screen will be shown when DNS cache and cookie
+ // clean-up finish.
+ bool show_when_dns_and_cookies_cleared_ = false;
+
+ // Has Gaia page silent load been started for the current sign-in attempt?
+ bool gaia_silent_load_ = false;
+
+ // The active network at the moment when Gaia page was preloaded.
+ std::string gaia_silent_load_network_;
+
+ // If the user authenticated via SAML, this indicates whether the principals
+ // API was used.
+ bool using_saml_api_ = false;
+
+ // Test credentials.
+ std::string test_user_;
+ std::string test_pass_;
+ bool test_expects_complete_login_ = false;
+
+ // True if Easy bootstrap is enabled.
+ bool use_easy_bootstrap_ = false;
+
+ // True if proxy doesn't allow access to google.com/generate_204.
+ NetworkPortalDetector::CaptivePortalStatus captive_portal_status_ =
+ NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE;
+
+ std::unique_ptr<NetworkPortalDetector> network_portal_detector_;
+ bool disable_restrictive_proxy_check_for_test_ = false;
+
+ // Non-owning ptr to SigninScreenHandler instance. Should not be used
+ // in dtor.
+ // TODO (antrim@): GaiaScreenHandler shouldn't communicate with
+ // signin_screen_handler directly.
+ SigninScreenHandler* signin_screen_handler_ = nullptr;
+
+ // True if offline GAIA is active.
+ bool offline_login_is_active_ = false;
+
+ // True if the authentication extension is still loading.
+ bool auth_extension_being_loaded_ = false;
+
+ // Helper to call AuthPolicyClient and cancel calls if needed. Used to
+ // authenticate users against Active Directory server.
+ std::unique_ptr<AuthPolicyLoginHelper> authpolicy_login_helper_;
+
+ base::WeakPtrFactory<GaiaScreenHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(GaiaScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_GAIA_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.cc
new file mode 100644
index 00000000000..26b7f92b767
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.cc
@@ -0,0 +1,122 @@
+// 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/ui/webui/chromeos/login/hid_detection_screen_handler.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/browser/chromeos/login/screens/core_oobe_view.h"
+#include "chrome/browser/chromeos/login/screens/hid_detection_screen.h"
+#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "components/login/localized_values_builder.h"
+#include "components/prefs/pref_service.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.HIDDetectionScreen";
+
+} // namespace
+
+namespace chromeos {
+
+HIDDetectionScreenHandler::HIDDetectionScreenHandler(
+ CoreOobeView* core_oobe_view)
+ : BaseScreenHandler(kScreenId), core_oobe_view_(core_oobe_view) {
+ set_call_js_prefix(kJsScreenPath);
+}
+
+HIDDetectionScreenHandler::~HIDDetectionScreenHandler() {
+ if (screen_)
+ screen_->OnViewDestroyed(this);
+}
+
+void HIDDetectionScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+ core_oobe_view_->InitDemoModeDetection();
+
+ PrefService* local_state = g_browser_process->local_state();
+ int num_of_times_dialog_was_shown = local_state->GetInteger(
+ prefs::kTimesHIDDialogShown);
+ local_state->SetInteger(prefs::kTimesHIDDialogShown,
+ num_of_times_dialog_was_shown + 1);
+
+ ShowScreen(kScreenId);
+}
+
+void HIDDetectionScreenHandler::Hide() {
+}
+
+void HIDDetectionScreenHandler::Bind(HIDDetectionScreen* screen) {
+ screen_ = screen;
+ BaseScreenHandler::SetBaseScreen(screen_);
+ if (page_is_ready())
+ Initialize();
+}
+
+void HIDDetectionScreenHandler::Unbind() {
+ screen_ = nullptr;
+ BaseScreenHandler::SetBaseScreen(nullptr);
+}
+
+void HIDDetectionScreenHandler::CheckIsScreenRequired(
+ const base::Callback<void(bool)>& on_check_done) {
+ screen_->CheckIsScreenRequired(on_check_done);
+}
+
+void HIDDetectionScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("hidDetectionContinue", IDS_HID_DETECTION_CONTINUE_BUTTON);
+ builder->Add("hidDetectionInvitation", IDS_HID_DETECTION_INVITATION_TEXT);
+ builder->Add("hidDetectionPrerequisites",
+ IDS_HID_DETECTION_PRECONDITION_TEXT);
+ builder->Add("hidDetectionMouseSearching", IDS_HID_DETECTION_SEARCHING_MOUSE);
+ builder->Add("hidDetectionKeyboardSearching",
+ IDS_HID_DETECTION_SEARCHING_KEYBOARD);
+ builder->Add("hidDetectionUSBMouseConnected",
+ IDS_HID_DETECTION_CONNECTED_USB_MOUSE);
+ builder->Add("hidDetectionPointingDeviceConnected",
+ IDS_HID_DETECTION_CONNECTED_POINTING_DEVICE);
+ builder->Add("hidDetectionUSBKeyboardConnected",
+ IDS_HID_DETECTION_CONNECTED_USB_KEYBOARD);
+ builder->Add("hidDetectionBTMousePaired",
+ IDS_HID_DETECTION_PAIRED_BLUETOOTH_MOUSE);
+ builder->Add("hidDetectionBTEnterKey", IDS_HID_DETECTION_BLUETOOTH_ENTER_KEY);
+}
+
+void HIDDetectionScreenHandler::DeclareJSCallbacks() {
+ AddCallback(
+ "HIDDetectionOnContinue", &HIDDetectionScreenHandler::HandleOnContinue);
+}
+
+void HIDDetectionScreenHandler::Initialize() {
+ if (show_on_init_) {
+ Show();
+ show_on_init_ = false;
+ }
+}
+
+void HIDDetectionScreenHandler::HandleOnContinue() {
+ // Continue button pressed.
+ core_oobe_view_->StopDemoModeDetection();
+ if (screen_)
+ screen_->OnContinueButtonClicked();
+}
+
+// static
+void HIDDetectionScreenHandler::RegisterPrefs(PrefRegistrySimple* registry) {
+ registry->RegisterIntegerPref(prefs::kTimesHIDDialogShown, 0);
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.h
new file mode 100644
index 00000000000..154fc4b3ccb
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.h
@@ -0,0 +1,64 @@
+// 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_UI_WEBUI_CHROMEOS_LOGIN_HID_DETECTION_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_HID_DETECTION_SCREEN_HANDLER_H_
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/login/screens/hid_detection_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "content/public/browser/web_ui.h"
+
+namespace chromeos {
+
+class CoreOobeView;
+
+// WebUI implementation of HIDDetectionScreenView.
+class HIDDetectionScreenHandler
+ : public HIDDetectionView,
+ public BaseScreenHandler {
+ public:
+ explicit HIDDetectionScreenHandler(CoreOobeView* core_oobe_view);
+ ~HIDDetectionScreenHandler() override;
+
+ // HIDDetectionView implementation:
+ void Show() override;
+ void Hide() override;
+ void Bind(HIDDetectionScreen* screen) override;
+ void Unbind() override;
+ void CheckIsScreenRequired(
+ const base::Callback<void(bool)>& on_check_done) override;
+
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void DeclareJSCallbacks() override;
+ void Initialize() override;
+
+ // Registers the preference for derelict state.
+ static void RegisterPrefs(PrefRegistrySimple* registry);
+
+ private:
+ // JS messages handlers.
+ void HandleOnContinue();
+
+ HIDDetectionScreen* screen_ = nullptr;
+
+ CoreOobeView* core_oobe_view_ = nullptr;
+
+ // If true, Initialize() will call Show().
+ bool show_on_init_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(HIDDetectionScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_HID_DETECTION_SCREEN_HANDLER_H_
+
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/host_pairing_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/host_pairing_screen_handler.cc
new file mode 100644
index 00000000000..39ff5fe96d7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/host_pairing_screen_handler.cc
@@ -0,0 +1,256 @@
+// 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/ui/webui/chromeos/login/host_pairing_screen_handler.h"
+
+#include "base/command_line.h"
+#include "base/strings/string_util.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/browser/chromeos/policy/enrollment_status_chromeos.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "components/login/localized_values_builder.h"
+#include "components/policy/core/browser/cloud/message_util.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace chromeos {
+
+namespace {
+
+const char kJsScreenPath[] = "login.HostPairingScreen";
+
+const char kMethodContextChanged[] = "contextChanged";
+
+// Sent from JS when screen is ready to receive context updates.
+// TODO(dzhioev): Move 'contextReady' logic to the base screen handler when
+// all screens migrate to context-based communications.
+const char kCallbackContextReady[] = "contextReady";
+
+} // namespace
+
+HostPairingScreenHandler::HostPairingScreenHandler()
+ : BaseScreenHandler(kScreenId) {
+ set_call_js_prefix(kJsScreenPath);
+}
+
+HostPairingScreenHandler::~HostPairingScreenHandler() {
+ if (delegate_)
+ delegate_->OnViewDestroyed(this);
+}
+
+void HostPairingScreenHandler::HandleContextReady() {
+ js_context_ready_ = true;
+ OnContextChanged(context_cache_.storage());
+}
+
+void HostPairingScreenHandler::Initialize() {
+ if (!page_is_ready() || !delegate_)
+ return;
+
+ if (show_on_init_) {
+ Show();
+ show_on_init_ = false;
+ }
+}
+
+void HostPairingScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ // TODO(dzhioev): Move the prefix logic to the base screen handler after
+ // migration.
+ std::string prefix;
+ base::RemoveChars(kJsScreenPath, ".", &prefix);
+
+ // TODO(xdai): Clean up all unrelated strings and rename others if necessary.
+ builder->Add(prefix + "WelcomeTitle", IDS_PAIRING_HOST_WELCOME_TITLE);
+ builder->Add(prefix + "WelcomeText", IDS_PAIRING_HOST_WELCOME_TEXT);
+ builder->Add(prefix + "ConfirmationTitle", IDS_SLAVE_CONFIRMATION_TITLE);
+ builder->Add(prefix + "UpdatingTitle", IDS_PAIRING_HOST_UPDATING_TITLE);
+ builder->Add(prefix + "UpdatingText", IDS_PAIRING_HOST_UPDATING_TEXT);
+ builder->Add(prefix + "EnrollTitle", IDS_SLAVE_ENROLL_TITLE);
+ builder->Add(prefix + "EnrollingTitle", IDS_SLAVE_ENROLLMENT_IN_PROGRESS);
+ builder->Add(prefix + "DoneTitle", IDS_PAIRING_HOST_DONE_TITLE);
+ builder->Add(prefix + "DoneText", IDS_PAIRING_HOST_DONE_TEXT);
+ builder->Add(prefix + "EnrollmentErrorTitle",
+ IDS_SLAVE_ENROLLMENT_ERROR_TITLE);
+ builder->Add(prefix + "ErrorNeedsRestart",
+ IDS_PAIRING_HOST_ERROR_NEED_RESTART_TEXT);
+ builder->Add(prefix + "SetupBasicConfigTitle",
+ IDS_HOST_SETUP_BASIC_CONFIGURATION_TITLE);
+ builder->Add(prefix + "SetupNetworkErrorTitle",
+ IDS_HOST_SETUP_NETWORK_ERROR_TITLE);
+ builder->Add(prefix + "InitializationErrorTitle",
+ IDS_PAIRING_HOST_INITIALIZATION_ERROR_TITLE);
+ builder->Add(prefix + "ConnectionErrorTitle",
+ IDS_PAIRING_HOST_CONNECTION_ERROR_TITLE);
+ builder->Add(prefix + "ErrorNeedRestartText",
+ IDS_PAIRING_HOST_ERROR_NEED_RESTART_TEXT);
+ builder->Add(prefix + "ErrorNeedsRestart",
+ IDS_PAIRING_HOST_ERROR_NEED_RESTART_TEXT);
+}
+
+void HostPairingScreenHandler::RegisterMessages() {
+ AddPrefixedCallback(kCallbackContextReady,
+ &HostPairingScreenHandler::HandleContextReady);
+}
+
+void HostPairingScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+ ShowScreen(kScreenId);
+}
+
+void HostPairingScreenHandler::Hide() {
+}
+
+void HostPairingScreenHandler::SetDelegate(Delegate* delegate) {
+ delegate_ = delegate;
+ if (page_is_ready())
+ Initialize();
+}
+
+void HostPairingScreenHandler::OnContextChanged(
+ const base::DictionaryValue& diff) {
+ if (!js_context_ready_) {
+ context_cache_.ApplyChanges(diff, NULL);
+ return;
+ }
+ CallJS(kMethodContextChanged, diff);
+}
+
+std::string HostPairingScreenHandler::GetErrorStringFromAuthError(
+ const GoogleServiceAuthError& error) {
+ switch (error.state()) {
+ case GoogleServiceAuthError::NONE:
+ case GoogleServiceAuthError::CAPTCHA_REQUIRED:
+ case GoogleServiceAuthError::TWO_FACTOR:
+ case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
+ case GoogleServiceAuthError::REQUEST_CANCELED:
+ case GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE:
+ case GoogleServiceAuthError::SERVICE_ERROR:
+ case GoogleServiceAuthError::WEB_LOGIN_REQUIRED:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_AUTH_FATAL_ERROR);
+ case GoogleServiceAuthError::USER_NOT_SIGNED_UP:
+ case GoogleServiceAuthError::ACCOUNT_DELETED:
+ case GoogleServiceAuthError::ACCOUNT_DISABLED:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_AUTH_ACCOUNT_ERROR);
+ case GoogleServiceAuthError::CONNECTION_FAILED:
+ case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_AUTH_NETWORK_ERROR);
+ default:
+ return std::string();
+ }
+}
+
+std::string HostPairingScreenHandler::GetErrorStringFromEnrollmentError(
+ policy::EnrollmentStatus status) {
+ switch (status.status()) {
+ case policy::EnrollmentStatus::NO_STATE_KEYS:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_STATUS_NO_STATE_KEYS);
+ case policy::EnrollmentStatus::REGISTRATION_FAILED:
+ switch (status.client_status()) {
+ case policy::DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_ACCOUNT_ERROR);
+ case policy::DM_STATUS_SERVICE_MISSING_LICENSES:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_MISSING_LICENSES_ERROR);
+ case policy::DM_STATUS_SERVICE_DEPROVISIONED:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_DEPROVISIONED_ERROR);
+ case policy::DM_STATUS_SERVICE_DOMAIN_MISMATCH:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_DOMAIN_MISMATCH_ERROR);
+ default:
+ return l10n_util::GetStringFUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_STATUS_REGISTRATION_FAILED,
+ policy::FormatDeviceManagementStatus(status.client_status()));
+ }
+ case policy::EnrollmentStatus::ROBOT_AUTH_FETCH_FAILED:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_ROBOT_AUTH_FETCH_FAILED);
+ case policy::EnrollmentStatus::ROBOT_REFRESH_FETCH_FAILED:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_ROBOT_REFRESH_FETCH_FAILED);
+ case policy::EnrollmentStatus::ROBOT_REFRESH_STORE_FAILED:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_ROBOT_REFRESH_STORE_FAILED);
+ case policy::EnrollmentStatus::REGISTRATION_BAD_MODE:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_STATUS_REGISTRATION_BAD_MODE);
+ case policy::EnrollmentStatus::REGISTRATION_CERT_FETCH_FAILED:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_STATUS_REGISTRATION_CERT_FETCH_FAILED);
+ case policy::EnrollmentStatus::POLICY_FETCH_FAILED:
+ return l10n_util::GetStringFUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_STATUS_POLICY_FETCH_FAILED,
+ policy::FormatDeviceManagementStatus(status.client_status()));
+ case policy::EnrollmentStatus::VALIDATION_FAILED:
+ return l10n_util::GetStringFUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_STATUS_VALIDATION_FAILED,
+ policy::FormatValidationStatus(status.validation_status()));
+ case policy::EnrollmentStatus::LOCK_ERROR:
+ switch (status.lock_status()) {
+ case InstallAttributes::LOCK_TIMEOUT:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_TIMEOUT);
+ case InstallAttributes::LOCK_BACKEND_INVALID:
+ case InstallAttributes::LOCK_ALREADY_LOCKED:
+ case InstallAttributes::LOCK_SET_ERROR:
+ case InstallAttributes::LOCK_FINALIZE_ERROR:
+ case InstallAttributes::LOCK_READBACK_ERROR:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_ERROR);
+ case InstallAttributes::LOCK_WRONG_DOMAIN:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_WRONG_USER);
+ case InstallAttributes::LOCK_WRONG_MODE:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_WRONG_MODE);
+ default:
+ return std::string();
+ }
+ case policy::EnrollmentStatus::STORE_ERROR:
+ return l10n_util::GetStringFUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_STATUS_STORE_ERROR,
+ policy::FormatStoreStatus(status.store_status(),
+ status.validation_status()));
+ case policy::EnrollmentStatus::ATTRIBUTE_UPDATE_FAILED:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_ATTRIBUTE_ERROR);
+ case policy::EnrollmentStatus::NO_MACHINE_IDENTIFICATION:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_STATUS_NO_MACHINE_IDENTIFICATION);
+ case policy::EnrollmentStatus::ACTIVE_DIRECTORY_POLICY_FETCH_FAILED:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_ERROR_ACTIVE_DIRECTORY_POLICY_FETCH);
+ case policy::EnrollmentStatus::DM_TOKEN_STORE_FAILED:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_ERROR_SAVE_DEVICE_CONFIGURATION);
+ default:
+ return std::string();
+ }
+}
+
+std::string HostPairingScreenHandler::GetErrorStringFromOtherError(
+ EnterpriseEnrollmentHelper::OtherError error) {
+ switch (error) {
+ case EnterpriseEnrollmentHelper::OTHER_ERROR_DOMAIN_MISMATCH:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_WRONG_USER);
+ case EnterpriseEnrollmentHelper::OTHER_ERROR_FATAL:
+ return l10n_util::GetStringUTF8(
+ IDS_ENTERPRISE_ENROLLMENT_FATAL_ENROLLMENT_ERROR);
+ default:
+ return std::string();
+ }
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/host_pairing_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/host_pairing_screen_handler.h
new file mode 100644
index 00000000000..9064f8d5806
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/host_pairing_screen_handler.h
@@ -0,0 +1,56 @@
+// 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_UI_WEBUI_CHROMEOS_LOGIN_HOST_PAIRING_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_HOST_PAIRING_SCREEN_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/screens/host_pairing_screen_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "components/login/screens/screen_context.h"
+
+namespace chromeos {
+
+class HostPairingScreenHandler : public HostPairingScreenView,
+ public BaseScreenHandler {
+ public:
+ HostPairingScreenHandler();
+ ~HostPairingScreenHandler() override;
+
+ private:
+ void HandleContextReady();
+
+ // Overridden from BaseScreenHandler:
+ void Initialize() override;
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+
+ // Overridden from content::WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ // Overridden from HostPairingScreenActor:
+ void Show() override;
+ void Hide() override;
+ void SetDelegate(Delegate* delegate) override;
+ void OnContextChanged(const base::DictionaryValue& diff) override;
+ std::string GetErrorStringFromAuthError(
+ const GoogleServiceAuthError& error) override;
+ std::string GetErrorStringFromEnrollmentError(
+ policy::EnrollmentStatus status) override;
+ std::string GetErrorStringFromOtherError(
+ EnterpriseEnrollmentHelper::OtherError error) override;
+
+ HostPairingScreenView::Delegate* delegate_ = nullptr;
+ bool show_on_init_ = false;
+ bool js_context_ready_ = false;
+
+ // Caches context changes while JS part is not ready to receive messages.
+ ::login::ScreenContext context_cache_;
+
+ DISALLOW_COPY_AND_ASSIGN(HostPairingScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_HOST_PAIRING_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.cc
new file mode 100644
index 00000000000..ded2b603494
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.cc
@@ -0,0 +1,213 @@
+// 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/chromeos/login/kiosk_app_menu_handler.h"
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/sys_info.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h"
+#include "chrome/browser/chromeos/login/existing_user_controller.h"
+#include "chrome/browser/chromeos/login/screens/network_error.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/grit/extensions_browser_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/web_ui_util.h"
+
+namespace chromeos {
+
+namespace {
+
+// JS functions that define new and old kiosk UI API.
+const char kKioskSetAppsNewAPI[] = "login.AccountPickerScreen.setApps";
+const char kKioskSetAppsOldAPI[] = "login.AppsMenuButton.setApps";
+const char kKioskShowErrorNewAPI[] = "login.AccountPickerScreen.showAppError";
+const char kKioskShowErrorOldAPI[] = "login.AppsMenuButton.showError";
+
+} // namespace
+
+KioskAppMenuHandler::KioskAppMenuHandler(
+ const scoped_refptr<NetworkStateInformer>& network_state_informer)
+ : is_webui_initialized_(false),
+ network_state_informer_(network_state_informer),
+ weak_ptr_factory_(this) {
+ KioskAppManager::Get()->AddObserver(this);
+ network_state_informer_->AddObserver(this);
+ ArcKioskAppManager::Get()->AddObserver(this);
+}
+
+KioskAppMenuHandler::~KioskAppMenuHandler() {
+ KioskAppManager::Get()->RemoveObserver(this);
+ network_state_informer_->RemoveObserver(this);
+ ArcKioskAppManager::Get()->RemoveObserver(this);
+}
+
+void KioskAppMenuHandler::GetLocalizedStrings(
+ base::DictionaryValue* localized_strings) {
+ localized_strings->SetString(
+ "showApps",
+ l10n_util::GetStringUTF16(IDS_KIOSK_APPS_BUTTON));
+ localized_strings->SetString(
+ "confirmKioskAppDiagnosticModeFormat",
+ l10n_util::GetStringUTF16(IDS_LOGIN_CONFIRM_KIOSK_DIAGNOSTIC_FORMAT));
+ localized_strings->SetString(
+ "confirmKioskAppDiagnosticModeYes",
+ l10n_util::GetStringUTF16(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL));
+ localized_strings->SetString(
+ "confirmKioskAppDiagnosticModeNo",
+ l10n_util::GetStringUTF16(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL));
+ localized_strings->SetBoolean(
+ "kioskAppHasLaunchError",
+ KioskAppLaunchError::Get() != KioskAppLaunchError::NONE);
+}
+
+void KioskAppMenuHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("initializeKioskApps",
+ base::Bind(&KioskAppMenuHandler::HandleInitializeKioskApps,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("kioskAppsLoaded",
+ base::Bind(&KioskAppMenuHandler::HandleKioskAppsLoaded,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("checkKioskAppLaunchError",
+ base::Bind(&KioskAppMenuHandler::HandleCheckKioskAppLaunchError,
+ base::Unretained(this)));
+}
+
+// static
+bool KioskAppMenuHandler::EnableNewKioskUI() {
+ // Turn off new kiosk UI for M34/35.
+ // TODO(xiyuan, nkostylev): Revist for http://crbug.com/362062.
+ return false;
+}
+
+void KioskAppMenuHandler::SendKioskApps() {
+ if (!is_webui_initialized_)
+ return;
+
+ KioskAppManager::Apps apps;
+ KioskAppManager::Get()->GetApps(&apps);
+
+ base::ListValue apps_list;
+ for (size_t i = 0; i < apps.size(); ++i) {
+ const KioskAppManager::App& app_data = apps[i];
+
+ std::unique_ptr<base::DictionaryValue> app_info(
+ new base::DictionaryValue());
+ app_info->SetBoolean("isApp", true);
+ app_info->SetString("id", app_data.app_id);
+ app_info->SetBoolean("isAndroidApp", false);
+ // Unused for native apps. Added for consistency with Android apps.
+ app_info->SetString("account_email", app_data.account_id.GetUserEmail());
+ app_info->SetString("label", app_data.name);
+
+ std::string icon_url;
+ if (app_data.icon.isNull()) {
+ icon_url =
+ webui::GetBitmapDataUrl(*ResourceBundle::GetSharedInstance()
+ .GetImageNamed(IDR_APP_DEFAULT_ICON)
+ .ToSkBitmap());
+ } else {
+ icon_url = webui::GetBitmapDataUrl(*app_data.icon.bitmap());
+ }
+ app_info->SetString("iconUrl", icon_url);
+
+ apps_list.Append(std::move(app_info));
+ }
+
+ ArcKioskAppManager::Apps arc_apps;
+ ArcKioskAppManager::Get()->GetAllApps(&arc_apps);
+ for (size_t i = 0; i < arc_apps.size(); ++i) {
+ std::unique_ptr<base::DictionaryValue> app_info(
+ new base::DictionaryValue());
+ app_info->SetBoolean("isApp", true);
+ app_info->SetBoolean("isAndroidApp", true);
+ app_info->SetString("id", arc_apps[i]->app_id());
+ app_info->SetString("account_email",
+ arc_apps[i]->account_id().GetUserEmail());
+ app_info->SetString("label", arc_apps[i]->name());
+
+ std::string icon_url;
+ if (arc_apps[i]->icon().isNull()) {
+ icon_url =
+ webui::GetBitmapDataUrl(*ResourceBundle::GetSharedInstance()
+ .GetImageNamed(IDR_APP_DEFAULT_ICON)
+ .ToSkBitmap());
+ } else {
+ icon_url = webui::GetBitmapDataUrl(*arc_apps[i]->icon().bitmap());
+ }
+ app_info->SetString("iconUrl", icon_url);
+
+ apps_list.Append(std::move(app_info));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ EnableNewKioskUI() ? kKioskSetAppsNewAPI : kKioskSetAppsOldAPI,
+ apps_list);
+}
+
+void KioskAppMenuHandler::HandleInitializeKioskApps(
+ const base::ListValue* args) {
+ is_webui_initialized_ = true;
+ SendKioskApps();
+ UpdateState(NetworkError::ERROR_REASON_UPDATE);
+}
+
+void KioskAppMenuHandler::HandleKioskAppsLoaded(
+ const base::ListValue* args) {
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_KIOSK_APPS_LOADED,
+ content::NotificationService::AllSources(),
+ content::NotificationService::NoDetails());
+}
+
+void KioskAppMenuHandler::HandleCheckKioskAppLaunchError(
+ const base::ListValue* args) {
+ KioskAppLaunchError::Error error = KioskAppLaunchError::Get();
+ if (error == KioskAppLaunchError::NONE)
+ return;
+ KioskAppLaunchError::RecordMetricAndClear();
+
+ const std::string error_message = KioskAppLaunchError::GetErrorMessage(error);
+ bool new_kiosk_ui = EnableNewKioskUI();
+ web_ui()->CallJavascriptFunctionUnsafe(
+ new_kiosk_ui ? kKioskShowErrorNewAPI : kKioskShowErrorOldAPI,
+ base::Value(error_message));
+}
+
+void KioskAppMenuHandler::OnKioskAppsSettingsChanged() {
+ SendKioskApps();
+}
+
+void KioskAppMenuHandler::OnKioskAppDataChanged(const std::string& app_id) {
+ SendKioskApps();
+}
+
+void KioskAppMenuHandler::OnKioskAppDataLoadFailure(const std::string& app_id) {
+ SendKioskApps();
+}
+
+void KioskAppMenuHandler::UpdateState(NetworkError::ErrorReason reason) {
+ if (network_state_informer_->state() == NetworkStateInformer::ONLINE)
+ KioskAppManager::Get()->RetryFailedAppDataFetch();
+}
+
+void KioskAppMenuHandler::OnArcKioskAppsChanged() {
+ SendKioskApps();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.h
new file mode 100644
index 00000000000..ce1e5473701
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.h
@@ -0,0 +1,76 @@
+// 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_CHROMEOS_LOGIN_KIOSK_APP_MENU_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_KIOSK_APP_MENU_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h"
+#include "chrome/browser/chromeos/login/screens/network_error.h"
+#include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace chromeos {
+
+// KioskAppMenuHandler supplies kiosk apps data to apps menu on sign-in
+// screen when app mode is enabled and handles "launchKioskApp" request
+// from the apps menu.
+class KioskAppMenuHandler
+ : public content::WebUIMessageHandler,
+ public KioskAppManagerObserver,
+ public NetworkStateInformer::NetworkStateInformerObserver,
+ public ArcKioskAppManager::ArcKioskAppManagerObserver {
+ public:
+ explicit KioskAppMenuHandler(
+ const scoped_refptr<NetworkStateInformer>& network_state_informer);
+ ~KioskAppMenuHandler() override;
+
+ void GetLocalizedStrings(base::DictionaryValue* localized_strings);
+
+ // content::WebUIMessageHandler overrides:
+ void RegisterMessages() override;
+
+ // Returns true if new kiosk UI is enabled.
+ static bool EnableNewKioskUI();
+
+ private:
+ // Sends all kiosk apps to webui.
+ void SendKioskApps();
+
+ // JS callbacks.
+ void HandleInitializeKioskApps(const base::ListValue* args);
+ void HandleKioskAppsLoaded(const base::ListValue* args);
+ void HandleCheckKioskAppLaunchError(const base::ListValue* args);
+
+ // KioskAppManagerObserver overrides:
+ void OnKioskAppsSettingsChanged() override;
+ void OnKioskAppDataChanged(const std::string& app_id) override;
+ void OnKioskAppDataLoadFailure(const std::string& app_id) override;
+
+ // NetworkStateInformer::NetworkStateInformerObserver overrides:
+ void UpdateState(NetworkError::ErrorReason reason) override;
+
+ // ArcKioskAppManager::ArcKioskAppManagerObserver overrides:
+ void OnArcKioskAppsChanged() override;
+
+ // True when WebUI is initialized. Otherwise don't allow calling JS functions.
+ bool is_webui_initialized_;
+
+ scoped_refptr<NetworkStateInformer> network_state_informer_;
+
+ base::WeakPtrFactory<KioskAppMenuHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(KioskAppMenuHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_KIOSK_APP_MENU_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.cc
new file mode 100644
index 00000000000..d2d341d13a6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.cc
@@ -0,0 +1,160 @@
+// 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/chromeos/login/kiosk_autolaunch_screen_handler.h"
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/session_manager_client.h"
+#include "components/login/localized_values_builder.h"
+#include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
+#include "ui/base/webui/web_ui_util.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.AutolaunchScreen";
+
+} // namespace
+
+namespace chromeos {
+
+KioskAutolaunchScreenHandler::KioskAutolaunchScreenHandler()
+ : BaseScreenHandler(kScreenId) {
+ set_call_js_prefix(kJsScreenPath);
+ KioskAppManager::Get()->AddObserver(this);
+}
+
+KioskAutolaunchScreenHandler::~KioskAutolaunchScreenHandler() {
+ if (delegate_)
+ delegate_->OnViewDestroyed(this);
+
+ KioskAppManager::Get()->RemoveObserver(this);
+}
+
+void KioskAutolaunchScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+ UpdateKioskApp();
+ ShowScreen(kScreenId);
+}
+
+void KioskAutolaunchScreenHandler::SetDelegate(Delegate* delegate) {
+ delegate_ = delegate;
+ if (page_is_ready())
+ Initialize();
+}
+
+void KioskAutolaunchScreenHandler::UpdateKioskApp() {
+ if (!is_visible_)
+ return;
+
+ KioskAppManager* manager = KioskAppManager::Get();
+ KioskAppManager::App app;
+ std::string app_id = manager->GetAutoLaunchApp();
+ if (app_id.empty() ||
+ manager->IsAutoLaunchEnabled() ||
+ !manager->GetApp(app_id, &app)) {
+ return;
+ }
+
+ base::DictionaryValue app_info;
+ app_info.SetString("appName", app.name);
+
+ std::string icon_url("chrome://theme/IDR_APP_DEFAULT_ICON");
+ if (!app.icon.isNull())
+ icon_url = webui::GetBitmapDataUrl(*app.icon.bitmap());
+
+ app_info.SetString("appIconUrl", icon_url);
+ CallJS("updateApp", app_info);
+}
+
+void KioskAutolaunchScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("autolaunchTitle", IDS_AUTOSTART_WARNING_TITLE);
+ builder->Add("autolaunchWarningTitle", IDS_AUTOSTART_WARNING_TITLE);
+ builder->Add("autolaunchWarning", IDS_KIOSK_AUTOSTART_SCREEN_WARNING_MSG);
+ builder->Add("autolaunchConfirmButton", IDS_KIOSK_AUTOSTART_CONFIRM);
+ builder->Add("autolaunchCancelButton", IDS_CANCEL);
+}
+
+void KioskAutolaunchScreenHandler::Initialize() {
+ if (!page_is_ready() || !delegate_)
+ return;
+
+ if (show_on_init_) {
+ Show();
+ show_on_init_ = false;
+ }
+}
+
+void KioskAutolaunchScreenHandler::RegisterMessages() {
+ AddCallback("autolaunchVisible",
+ &KioskAutolaunchScreenHandler::HandleOnVisible);
+ AddCallback("autolaunchOnCancel",
+ &KioskAutolaunchScreenHandler::HandleOnCancel);
+ AddCallback("autolaunchOnConfirm",
+ &KioskAutolaunchScreenHandler::HandleOnConfirm);
+}
+
+void KioskAutolaunchScreenHandler::HandleOnCancel() {
+ KioskAppManager::Get()->RemoveObserver(this);
+ KioskAppManager::Get()->SetEnableAutoLaunch(false);
+ if (delegate_)
+ delegate_->OnExit(false);
+
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_KIOSK_AUTOLAUNCH_WARNING_COMPLETED,
+ content::NotificationService::AllSources(),
+ content::NotificationService::NoDetails());
+}
+
+void KioskAutolaunchScreenHandler::HandleOnConfirm() {
+ KioskAppManager::Get()->RemoveObserver(this);
+ KioskAppManager::Get()->SetEnableAutoLaunch(true);
+ if (delegate_)
+ delegate_->OnExit(true);
+
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_KIOSK_AUTOLAUNCH_WARNING_COMPLETED,
+ content::NotificationService::AllSources(),
+ content::NotificationService::NoDetails());
+}
+
+void KioskAutolaunchScreenHandler::HandleOnVisible() {
+ if (is_visible_)
+ return;
+
+ is_visible_ = true;
+ UpdateKioskApp();
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_KIOSK_AUTOLAUNCH_WARNING_VISIBLE,
+ content::NotificationService::AllSources(),
+ content::NotificationService::NoDetails());
+}
+
+void KioskAutolaunchScreenHandler::OnKioskAppsSettingsChanged() {
+ UpdateKioskApp();
+}
+
+void KioskAutolaunchScreenHandler::OnKioskAppDataChanged(
+ const std::string& app_id) {
+ UpdateKioskApp();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.h
new file mode 100644
index 00000000000..2b5a9f79bc5
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.h
@@ -0,0 +1,61 @@
+// 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_CHROMEOS_LOGIN_KIOSK_AUTOLAUNCH_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_KIOSK_AUTOLAUNCH_SCREEN_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h"
+#include "chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "content/public/browser/web_ui.h"
+
+namespace chromeos {
+
+// WebUI implementation of KioskAutolaunchScreenActor.
+class KioskAutolaunchScreenHandler : public KioskAutolaunchScreenView,
+ public KioskAppManagerObserver,
+ public BaseScreenHandler {
+ public:
+ KioskAutolaunchScreenHandler();
+ ~KioskAutolaunchScreenHandler() override;
+
+ // KioskAutolaunchScreenActor implementation:
+ void Show() override;
+ void SetDelegate(Delegate* delegate) override;
+
+ // KioskAppManagerObserver overrides:
+ void OnKioskAppsSettingsChanged() override;
+ void OnKioskAppDataChanged(const std::string& app_id) override;
+
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void Initialize() override;
+
+ // WebUIMessageHandler implementation:
+ void RegisterMessages() override;
+
+ private:
+ // Updates auto-start UI assets on JS side.
+ void UpdateKioskApp();
+
+ // JS messages handlers.
+ void HandleOnCancel();
+ void HandleOnConfirm();
+ void HandleOnVisible();
+
+ Delegate* delegate_ = nullptr;
+
+ // Keeps whether screen should be shown right after initialization.
+ bool show_on_init_ = false;
+ bool is_visible_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(KioskAutolaunchScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_KIOSK_AUTOLAUNCH_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.cc
new file mode 100644
index 00000000000..16d9eb7ce69
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.cc
@@ -0,0 +1,143 @@
+// 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/chromeos/login/kiosk_enable_screen_handler.h"
+
+#include <string>
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.KioskEnableScreen";
+
+} // namespace
+
+namespace chromeos {
+
+KioskEnableScreenHandler::KioskEnableScreenHandler()
+ : BaseScreenHandler(kScreenId), weak_ptr_factory_(this) {
+ set_call_js_prefix(kJsScreenPath);
+}
+
+KioskEnableScreenHandler::~KioskEnableScreenHandler() {
+ if (delegate_)
+ delegate_->OnViewDestroyed(this);
+}
+
+void KioskEnableScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+
+ KioskAppManager::Get()->GetConsumerKioskAutoLaunchStatus(
+ base::Bind(
+ &KioskEnableScreenHandler::OnGetConsumerKioskAutoLaunchStatus,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void KioskEnableScreenHandler::OnGetConsumerKioskAutoLaunchStatus(
+ KioskAppManager::ConsumerKioskAutoLaunchStatus status) {
+ is_configurable_ =
+ (status == KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE);
+ if (!is_configurable_) {
+ LOG(WARNING) << "Consumer kiosk auto launch feature is not configurable!";
+ return;
+ }
+
+ ShowScreen(kScreenId);
+
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_KIOSK_ENABLE_WARNING_VISIBLE,
+ content::NotificationService::AllSources(),
+ content::NotificationService::NoDetails());
+}
+
+void KioskEnableScreenHandler::SetDelegate(Delegate* delegate) {
+ delegate_ = delegate;
+ if (page_is_ready())
+ Initialize();
+}
+
+void KioskEnableScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("kioskEnableTitle", IDS_KIOSK_ENABLE_SCREEN_WARNING);
+ builder->Add("kioskEnableWarningText",
+ IDS_KIOSK_ENABLE_SCREEN_WARNING);
+ builder->Add("kioskEnableWarningDetails",
+ IDS_KIOSK_ENABLE_SCREEN_WARNING_DETAILS);
+ builder->Add("kioskEnableButton", IDS_KIOSK_ENABLE_SCREEN_ENABLE_BUTTON);
+ builder->Add("kioskCancelButton", IDS_CANCEL);
+ builder->Add("kioskOKButton", IDS_OK);
+ builder->Add("kioskEnableSuccessMsg", IDS_KIOSK_ENABLE_SCREEN_SUCCESS);
+ builder->Add("kioskEnableErrorMsg", IDS_KIOSK_ENABLE_SCREEN_ERROR);
+}
+
+void KioskEnableScreenHandler::Initialize() {
+ if (!page_is_ready() || !delegate_)
+ return;
+
+ if (show_on_init_) {
+ Show();
+ show_on_init_ = false;
+ }
+}
+
+void KioskEnableScreenHandler::RegisterMessages() {
+ AddCallback("kioskOnClose", &KioskEnableScreenHandler::HandleOnClose);
+ AddCallback("kioskOnEnable", &KioskEnableScreenHandler::HandleOnEnable);
+}
+
+void KioskEnableScreenHandler::HandleOnClose() {
+ if (delegate_)
+ delegate_->OnExit();
+
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_KIOSK_ENABLE_WARNING_COMPLETED,
+ content::NotificationService::AllSources(),
+ content::NotificationService::NoDetails());
+}
+
+void KioskEnableScreenHandler::HandleOnEnable() {
+ if (!is_configurable_) {
+ NOTREACHED();
+ if (delegate_)
+ delegate_->OnExit();
+
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_KIOSK_ENABLE_WARNING_COMPLETED,
+ content::NotificationService::AllSources(),
+ content::NotificationService::NoDetails());
+ return;
+ }
+
+ KioskAppManager::Get()->EnableConsumerKioskAutoLaunch(
+ base::Bind(&KioskEnableScreenHandler::OnEnableConsumerKioskAutoLaunch,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void KioskEnableScreenHandler::OnEnableConsumerKioskAutoLaunch(
+ bool success) {
+ if (!success)
+ LOG(WARNING) << "Consumer kiosk mode can't be enabled!";
+
+ CallJS("onCompleted", success);
+ if (success) {
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_KIOSK_ENABLED,
+ content::NotificationService::AllSources(),
+ content::NotificationService::NoDetails());
+ }
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.h
new file mode 100644
index 00000000000..3abc661c832
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.h
@@ -0,0 +1,63 @@
+// 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_CHROMEOS_LOGIN_KIOSK_ENABLE_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_KIOSK_ENABLE_SCREEN_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
+#include "chrome/browser/chromeos/login/screens/kiosk_enable_screen_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+
+namespace chromeos {
+
+// WebUI implementation of KioskEnableScreenActor.
+class KioskEnableScreenHandler : public KioskEnableScreenView,
+ public BaseScreenHandler {
+ public:
+ KioskEnableScreenHandler();
+ ~KioskEnableScreenHandler() override;
+
+ // KioskEnableScreenActor implementation:
+ void Show() override;
+ void SetDelegate(Delegate* delegate) override;
+
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void Initialize() override;
+
+ // WebUIMessageHandler implementation:
+ void RegisterMessages() override;
+
+ private:
+ // JS messages handlers.
+ void HandleOnClose();
+ void HandleOnEnable();
+
+ // Callback for KioskAppManager::EnableConsumerModeKiosk().
+ void OnEnableConsumerKioskAutoLaunch(bool success);
+
+ // Callback for KioskAppManager::GetConsumerKioskModeStatus().
+ void OnGetConsumerKioskAutoLaunchStatus(
+ KioskAppManager::ConsumerKioskAutoLaunchStatus status);
+
+ Delegate* delegate_ = nullptr;
+
+ // Keeps whether screen should be shown right after initialization.
+ bool show_on_init_ = false;
+
+ // True if machine's consumer kiosk mode is in a configurable state.
+ bool is_configurable_ = false;
+
+ base::WeakPtrFactory<KioskEnableScreenHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(KioskEnableScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_KIOSK_ENABLE_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/l10n_util.cc b/chromium/chrome/browser/ui/webui/chromeos/login/l10n_util.cc
new file mode 100644
index 00000000000..3b961134ff5
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/l10n_util.cc
@@ -0,0 +1,601 @@
+// 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/ui/webui/chromeos/login/l10n_util.h"
+
+#include <stddef.h>
+
+#include <iterator>
+#include <map>
+#include <memory>
+#include <set>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/i18n/rtl.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "base/stl_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/customization/customization_document.h"
+#include "chrome/browser/chromeos/input_method/input_method_util.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/browser_thread.h"
+#include "ui/base/ime/chromeos/component_extension_ime_manager.h"
+#include "ui/base/ime/chromeos/input_method_descriptor.h"
+#include "ui/base/ime/chromeos/input_method_manager.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace chromeos {
+
+const char kMostRelevantLanguagesDivider[] = "MOST_RELEVANT_LANGUAGES_DIVIDER";
+
+namespace {
+
+std::unique_ptr<base::DictionaryValue> CreateInputMethodsEntry(
+ const input_method::InputMethodDescriptor& method,
+ const std::string selected) {
+ input_method::InputMethodUtil* util =
+ input_method::InputMethodManager::Get()->GetInputMethodUtil();
+ const std::string& ime_id = method.id();
+ std::unique_ptr<base::DictionaryValue> input_method(
+ new base::DictionaryValue);
+ input_method->SetString("value", ime_id);
+ input_method->SetString(
+ "title", util->GetInputMethodLongNameStripped(method));
+ input_method->SetBoolean("selected", ime_id == selected);
+ return input_method;
+}
+
+// Returns true if element was inserted.
+bool InsertString(const std::string& str, std::set<std::string>* to) {
+ const std::pair<std::set<std::string>::iterator, bool> result =
+ to->insert(str);
+ return result.second;
+}
+
+void AddOptgroupOtherLayouts(base::ListValue* input_methods_list) {
+ std::unique_ptr<base::DictionaryValue> optgroup(new base::DictionaryValue);
+ optgroup->SetString(
+ "optionGroupName",
+ l10n_util::GetStringUTF16(IDS_OOBE_OTHER_KEYBOARD_LAYOUTS));
+ input_methods_list->Append(std::move(optgroup));
+}
+
+std::unique_ptr<base::DictionaryValue> CreateLanguageEntry(
+ const std::string& language_code,
+ const base::string16& language_display_name,
+ const base::string16& language_native_display_name) {
+ base::string16 display_name = language_display_name;
+ const bool markup_removal =
+ base::i18n::UnadjustStringForLocaleDirection(&display_name);
+ DCHECK(markup_removal);
+
+ const bool has_rtl_chars =
+ base::i18n::StringContainsStrongRTLChars(display_name);
+ const std::string directionality = has_rtl_chars ? "rtl" : "ltr";
+
+ auto dictionary = base::MakeUnique<base::DictionaryValue>();
+ dictionary->SetString("code", language_code);
+ dictionary->SetString("displayName", language_display_name);
+ dictionary->SetString("textDirection", directionality);
+ dictionary->SetString("nativeDisplayName", language_native_display_name);
+ return dictionary;
+}
+
+// Gets the list of languages with |descriptors| based on |base_language_codes|.
+// The |most_relevant_language_codes| will be first in the list. If
+// |insert_divider| is true, an entry with its "code" attribute set to
+// kMostRelevantLanguagesDivider is placed between the most relevant languages
+// and all others.
+std::unique_ptr<base::ListValue> GetLanguageList(
+ const input_method::InputMethodDescriptors& descriptors,
+ const std::vector<std::string>& base_language_codes,
+ const std::vector<std::string>& most_relevant_language_codes,
+ bool insert_divider) {
+ const std::string app_locale = g_browser_process->GetApplicationLocale();
+
+ std::set<std::string> language_codes;
+ // Collect the language codes from the supported input methods.
+ for (size_t i = 0; i < descriptors.size(); ++i) {
+ const input_method::InputMethodDescriptor& descriptor = descriptors[i];
+ const std::vector<std::string>& languages = descriptor.language_codes();
+ for (size_t i = 0; i < languages.size(); ++i)
+ language_codes.insert(languages[i]);
+ }
+
+ // Language sort order.
+ std::map<std::string, int /* index */> language_index;
+ for (size_t i = 0; i < most_relevant_language_codes.size(); ++i)
+ language_index[most_relevant_language_codes[i]] = i;
+
+ // Map of display name -> {language code, native_display_name}.
+ // In theory, we should be able to create a map that is sorted by
+ // display names using ICU comparator, but doing it is hard, thus we'll
+ // use an auxiliary vector to achieve the same result.
+ typedef std::pair<std::string, base::string16> LanguagePair;
+ typedef std::map<base::string16, LanguagePair> LanguageMap;
+ LanguageMap language_map;
+
+ // The auxiliary vector mentioned above (except the most relevant locales).
+ std::vector<base::string16> display_names;
+
+ // Separate vector of the most relevant locales.
+ std::vector<base::string16> most_relevant_locales_display_names(
+ most_relevant_language_codes.size());
+
+ size_t most_relevant_locales_count = 0;
+
+ // Build the list of display names, and build the language map.
+
+ // The list of configured locales might have entries not in
+ // base_language_codes. If there are unsupported language variants,
+ // but they resolve to backup locale within base_language_codes, also
+ // add them to the list.
+ for (std::map<std::string, int>::const_iterator it = language_index.begin();
+ it != language_index.end(); ++it) {
+ const std::string& language_id = it->first;
+
+ const std::string lang = l10n_util::GetLanguage(language_id);
+
+ // Ignore non-specific codes.
+ if (lang.empty() || lang == language_id)
+ continue;
+
+ if (base::ContainsValue(base_language_codes, language_id)) {
+ // Language is supported. No need to replace
+ continue;
+ }
+ std::string resolved_locale;
+ if (!l10n_util::CheckAndResolveLocale(language_id, &resolved_locale))
+ continue;
+
+ if (!base::ContainsValue(base_language_codes, resolved_locale)) {
+ // Resolved locale is not supported.
+ continue;
+ }
+
+ const base::string16 display_name =
+ l10n_util::GetDisplayNameForLocale(language_id, app_locale, true);
+ const base::string16 native_display_name =
+ l10n_util::GetDisplayNameForLocale(
+ language_id, language_id, true);
+
+ language_map[display_name] =
+ std::make_pair(language_id, native_display_name);
+
+ most_relevant_locales_display_names[it->second] = display_name;
+ ++most_relevant_locales_count;
+ }
+
+ // Translate language codes, generated from input methods.
+ for (std::set<std::string>::const_iterator it = language_codes.begin();
+ it != language_codes.end(); ++it) {
+ // Exclude the language which is not in |base_langauge_codes| even it has
+ // input methods.
+ if (!base::ContainsValue(base_language_codes, *it))
+ continue;
+
+ const base::string16 display_name =
+ l10n_util::GetDisplayNameForLocale(*it, app_locale, true);
+ const base::string16 native_display_name =
+ l10n_util::GetDisplayNameForLocale(*it, *it, true);
+
+ language_map[display_name] = std::make_pair(*it, native_display_name);
+
+ const std::map<std::string, int>::const_iterator index_pos =
+ language_index.find(*it);
+ if (index_pos != language_index.end()) {
+ base::string16& stored_display_name =
+ most_relevant_locales_display_names[index_pos->second];
+ if (stored_display_name.empty()) {
+ stored_display_name = display_name;
+ ++most_relevant_locales_count;
+ }
+ } else {
+ display_names.push_back(display_name);
+ }
+ }
+ DCHECK_EQ(display_names.size() + most_relevant_locales_count,
+ language_map.size());
+
+ // Build the list of display names, and build the language map.
+ for (size_t i = 0; i < base_language_codes.size(); ++i) {
+ // Skip this language if it was already added.
+ if (language_codes.find(base_language_codes[i]) != language_codes.end())
+ continue;
+
+ base::string16 display_name =
+ l10n_util::GetDisplayNameForLocale(
+ base_language_codes[i], app_locale, false);
+ base::string16 native_display_name =
+ l10n_util::GetDisplayNameForLocale(
+ base_language_codes[i], base_language_codes[i], false);
+ language_map[display_name] =
+ std::make_pair(base_language_codes[i], native_display_name);
+
+ const std::map<std::string, int>::const_iterator index_pos =
+ language_index.find(base_language_codes[i]);
+ if (index_pos != language_index.end()) {
+ most_relevant_locales_display_names[index_pos->second] = display_name;
+ ++most_relevant_locales_count;
+ } else {
+ display_names.push_back(display_name);
+ }
+ }
+
+ // Sort display names using locale specific sorter.
+ l10n_util::SortStrings16(app_locale, &display_names);
+ // Concatenate most_relevant_locales_display_names and display_names.
+ // Insert special divider in between.
+ std::vector<base::string16> out_display_names;
+ for (size_t i = 0; i < most_relevant_locales_display_names.size(); ++i) {
+ if (most_relevant_locales_display_names[i].size() == 0)
+ continue;
+ out_display_names.push_back(most_relevant_locales_display_names[i]);
+ }
+
+ base::string16 divider16;
+ if (insert_divider && !out_display_names.empty()) {
+ // Insert a divider if requested, but only if
+ // |most_relevant_locales_display_names| is not empty.
+ divider16 = base::ASCIIToUTF16(kMostRelevantLanguagesDivider);
+ out_display_names.push_back(divider16);
+ }
+
+ std::copy(display_names.begin(),
+ display_names.end(),
+ std::back_inserter(out_display_names));
+
+ // Build the language list from the language map.
+ std::unique_ptr<base::ListValue> language_list(new base::ListValue());
+ for (size_t i = 0; i < out_display_names.size(); ++i) {
+ // Sets the directionality of the display language name.
+ base::string16 display_name(out_display_names[i]);
+ if (insert_divider && display_name == divider16) {
+ // Insert divider.
+ auto dictionary = base::MakeUnique<base::DictionaryValue>();
+ dictionary->SetString("code", kMostRelevantLanguagesDivider);
+ language_list->Append(std::move(dictionary));
+ continue;
+ }
+
+ const LanguagePair& pair = language_map[out_display_names[i]];
+ language_list->Append(
+ CreateLanguageEntry(pair.first, out_display_names[i], pair.second));
+ }
+
+ return language_list;
+}
+
+// Note: this method updates |selected_locale| only if it is empty.
+void GetAndMergeKeyboardLayoutsForLocale(input_method::InputMethodUtil* util,
+ const std::string& locale,
+ std::string* selected_locale,
+ std::vector<std::string>* layouts) {
+ std::vector<std::string> layouts_from_locale;
+ util->GetInputMethodIdsFromLanguageCode(
+ locale, input_method::kKeyboardLayoutsOnly, &layouts_from_locale);
+ layouts->insert(layouts->end(), layouts_from_locale.begin(),
+ layouts_from_locale.end());
+ if (selected_locale->empty() && !layouts_from_locale.empty()) {
+ *selected_locale =
+ util->GetInputMethodDescriptorFromId(layouts_from_locale[0])->id();
+ }
+}
+
+// Invokes |callback| with a list of keyboard layouts that can be used for
+// |resolved_locale|.
+void GetKeyboardLayoutsForResolvedLocale(
+ const std::string& requested_locale,
+ const GetKeyboardLayoutsForLocaleCallback& callback,
+ const std::string& resolved_locale) {
+ input_method::InputMethodUtil* util =
+ input_method::InputMethodManager::Get()->GetInputMethodUtil();
+ std::vector<std::string> layouts = util->GetHardwareInputMethodIds();
+
+ // "Selected" will be set from the fist non-empty list.
+ std::string selected;
+ GetAndMergeKeyboardLayoutsForLocale(util, requested_locale, &selected,
+ &layouts);
+ GetAndMergeKeyboardLayoutsForLocale(util, resolved_locale, &selected,
+ &layouts);
+
+ std::unique_ptr<base::ListValue> input_methods_list(new base::ListValue);
+ std::set<std::string> input_methods_added;
+ for (std::vector<std::string>::const_iterator it = layouts.begin();
+ it != layouts.end(); ++it) {
+ const input_method::InputMethodDescriptor* ime =
+ util->GetInputMethodDescriptorFromId(*it);
+ if (!InsertString(ime->id(), &input_methods_added))
+ continue;
+ input_methods_list->Append(CreateInputMethodsEntry(*ime, selected));
+ }
+
+ callback.Run(std::move(input_methods_list));
+}
+
+// For "UI Language" drop-down menu at OOBE screen we need to decide which
+// entry to mark "selected". If user has just selected "requested_locale",
+// but "loaded_locale" was actually loaded, we mark original user choice
+// "selected" only if loaded_locale is a backup for "requested_locale".
+std::string CalculateSelectedLanguage(const std::string& requested_locale,
+ const std::string& loaded_locale) {
+ std::string resolved_locale;
+ if (!l10n_util::CheckAndResolveLocale(requested_locale, &resolved_locale))
+ return loaded_locale;
+
+ if (resolved_locale == loaded_locale)
+ return requested_locale;
+
+ return loaded_locale;
+}
+
+void ResolveLanguageListInThreadPool(
+ std::unique_ptr<chromeos::locale_util::LanguageSwitchResult>
+ language_switch_result,
+ const scoped_refptr<base::TaskRunner> task_runner,
+ const UILanguageListResolvedCallback& resolved_callback) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ std::string selected_language;
+ if (!language_switch_result) {
+ selected_language =
+ StartupCustomizationDocument::GetInstance()->initial_locale_default();
+ } else {
+ if (language_switch_result->success) {
+ if (language_switch_result->requested_locale ==
+ language_switch_result->loaded_locale) {
+ selected_language = language_switch_result->requested_locale;
+ } else {
+ selected_language =
+ CalculateSelectedLanguage(language_switch_result->requested_locale,
+ language_switch_result->loaded_locale);
+ }
+ } else {
+ selected_language = language_switch_result->loaded_locale;
+ }
+ }
+ const std::string selected_code =
+ selected_language.empty() ? g_browser_process->GetApplicationLocale()
+ : selected_language;
+
+ const std::string list_locale =
+ language_switch_result ? language_switch_result->loaded_locale
+ : g_browser_process->GetApplicationLocale();
+ std::unique_ptr<base::ListValue> language_list(
+ chromeos::GetUILanguageList(nullptr, selected_code));
+
+ task_runner->PostTask(
+ FROM_HERE, base::Bind(resolved_callback, base::Passed(&language_list),
+ list_locale, selected_language));
+}
+
+void AdjustUILanguageList(const std::string& selected,
+ base::ListValue* languages_list) {
+ for (size_t i = 0; i < languages_list->GetSize(); ++i) {
+ base::DictionaryValue* language_info = NULL;
+ if (!languages_list->GetDictionary(i, &language_info))
+ NOTREACHED();
+
+ std::string value;
+ language_info->GetString("code", &value);
+ std::string display_name;
+ language_info->GetString("displayName", &display_name);
+ std::string native_name;
+ language_info->GetString("nativeDisplayName", &native_name);
+
+ // If it's an option group divider, add field name.
+ if (value == kMostRelevantLanguagesDivider) {
+ language_info->SetString(
+ "optionGroupName",
+ l10n_util::GetStringUTF16(IDS_OOBE_OTHER_LANGUAGES));
+ }
+ if (display_name != native_name) {
+ display_name = base::StringPrintf("%s - %s",
+ display_name.c_str(),
+ native_name.c_str());
+ }
+
+ language_info->SetString("value", value);
+ language_info->SetString("title", display_name);
+ if (value == selected)
+ language_info->SetBoolean("selected", true);
+ }
+}
+
+} // namespace
+
+void ResolveUILanguageList(
+ std::unique_ptr<chromeos::locale_util::LanguageSwitchResult>
+ language_switch_result,
+ const UILanguageListResolvedCallback& callback) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ base::PostTaskWithTraits(
+ FROM_HERE, {base::MayBlock()},
+ base::BindOnce(&ResolveLanguageListInThreadPool,
+ base::Passed(&language_switch_result),
+ base::SequencedTaskRunnerHandle::Get(), callback));
+}
+
+std::unique_ptr<base::ListValue> GetMinimalUILanguageList() {
+ const std::string application_locale =
+ g_browser_process->GetApplicationLocale();
+ base::string16 language_native_display_name =
+ l10n_util::GetDisplayNameForLocale(
+ application_locale, application_locale, true);
+
+ std::unique_ptr<base::ListValue> language_list(new base::ListValue());
+ language_list->Append(CreateLanguageEntry(application_locale,
+ language_native_display_name,
+ language_native_display_name));
+ AdjustUILanguageList(std::string(), language_list.get());
+ return language_list;
+}
+
+std::unique_ptr<base::ListValue> GetUILanguageList(
+ const std::vector<std::string>* most_relevant_language_codes,
+ const std::string& selected) {
+ ComponentExtensionIMEManager* manager =
+ input_method::InputMethodManager::Get()
+ ->GetComponentExtensionIMEManager();
+ input_method::InputMethodDescriptors descriptors =
+ manager->GetXkbIMEAsInputMethodDescriptor();
+ std::unique_ptr<base::ListValue> languages_list(GetLanguageList(
+ descriptors, l10n_util::GetAvailableLocales(),
+ most_relevant_language_codes
+ ? *most_relevant_language_codes
+ : StartupCustomizationDocument::GetInstance()->configured_locales(),
+ true));
+ AdjustUILanguageList(selected, languages_list.get());
+ return languages_list;
+}
+
+std::string FindMostRelevantLocale(
+ const std::vector<std::string>& most_relevant_language_codes,
+ const base::ListValue& available_locales,
+ const std::string& fallback_locale) {
+ for (std::vector<std::string>::const_iterator most_relevant_it =
+ most_relevant_language_codes.begin();
+ most_relevant_it != most_relevant_language_codes.end();
+ ++most_relevant_it) {
+ for (base::ListValue::const_iterator available_it =
+ available_locales.begin();
+ available_it != available_locales.end(); ++available_it) {
+ const base::DictionaryValue* dict;
+ std::string available_locale;
+ if (!available_it->GetAsDictionary(&dict) ||
+ !dict->GetString("value", &available_locale)) {
+ NOTREACHED();
+ continue;
+ }
+ if (available_locale == *most_relevant_it)
+ return *most_relevant_it;
+ }
+ }
+
+ return fallback_locale;
+}
+
+std::unique_ptr<base::ListValue> GetAcceptLanguageList() {
+ // Collect the language codes from the supported accept-languages.
+ const std::string app_locale = g_browser_process->GetApplicationLocale();
+ std::vector<std::string> accept_language_codes;
+ l10n_util::GetAcceptLanguagesForLocale(app_locale, &accept_language_codes);
+ return GetLanguageList(
+ *input_method::InputMethodManager::Get()->GetSupportedInputMethods(),
+ accept_language_codes,
+ StartupCustomizationDocument::GetInstance()->configured_locales(),
+ false);
+}
+
+std::unique_ptr<base::ListValue> GetAndActivateLoginKeyboardLayouts(
+ const std::string& locale,
+ const std::string& selected,
+ bool activate_keyboards) {
+ std::unique_ptr<base::ListValue> input_methods_list(new base::ListValue);
+ input_method::InputMethodManager* manager =
+ input_method::InputMethodManager::Get();
+ input_method::InputMethodUtil* util = manager->GetInputMethodUtil();
+
+ const std::vector<std::string>& hardware_login_input_methods =
+ util->GetHardwareLoginInputMethodIds();
+
+ if (activate_keyboards) {
+ DCHECK(
+ ProfileHelper::IsSigninProfile(ProfileManager::GetActiveUserProfile()));
+ manager->GetActiveIMEState()->EnableLoginLayouts(
+ locale, hardware_login_input_methods);
+ }
+
+ std::unique_ptr<input_method::InputMethodDescriptors> input_methods(
+ manager->GetActiveIMEState()->GetActiveInputMethods());
+ std::set<std::string> input_methods_added;
+
+ for (std::vector<std::string>::const_iterator i =
+ hardware_login_input_methods.begin();
+ i != hardware_login_input_methods.end();
+ ++i) {
+ const input_method::InputMethodDescriptor* ime =
+ util->GetInputMethodDescriptorFromId(*i);
+ // Do not crash in case of misconfiguration.
+ if (ime) {
+ input_methods_added.insert(*i);
+ input_methods_list->Append(CreateInputMethodsEntry(*ime, selected));
+ } else {
+ NOTREACHED();
+ }
+ }
+
+ bool optgroup_added = false;
+ for (size_t i = 0; i < input_methods->size(); ++i) {
+ // Makes sure the id is in legacy xkb id format.
+ const std::string& ime_id = (*input_methods)[i].id();
+ if (!InsertString(ime_id, &input_methods_added))
+ continue;
+ if (!optgroup_added) {
+ optgroup_added = true;
+ AddOptgroupOtherLayouts(input_methods_list.get());
+ }
+ input_methods_list->Append(
+ CreateInputMethodsEntry((*input_methods)[i], selected));
+ }
+
+ // "xkb:us::eng" should always be in the list of available layouts.
+ const std::string us_keyboard_id =
+ util->GetFallbackInputMethodDescriptor().id();
+ if (input_methods_added.find(us_keyboard_id) == input_methods_added.end()) {
+ const input_method::InputMethodDescriptor* us_eng_descriptor =
+ util->GetInputMethodDescriptorFromId(us_keyboard_id);
+ DCHECK(us_eng_descriptor);
+ if (!optgroup_added) {
+ optgroup_added = true;
+ AddOptgroupOtherLayouts(input_methods_list.get());
+ }
+ input_methods_list->Append(
+ CreateInputMethodsEntry(*us_eng_descriptor, selected));
+ manager->GetActiveIMEState()->EnableInputMethod(us_keyboard_id);
+ }
+ return input_methods_list;
+}
+
+void GetKeyboardLayoutsForLocale(
+ const GetKeyboardLayoutsForLocaleCallback& callback,
+ const std::string& locale) {
+ // Resolve |locale| on a background thread, then continue on the current
+ // thread.
+ std::string (*get_application_locale)(const std::string&, bool) =
+ &l10n_util::GetApplicationLocale;
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE,
+ {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+ base::BindOnce(get_application_locale, locale,
+ false /* set_icu_locale */),
+ base::BindOnce(&GetKeyboardLayoutsForResolvedLocale, locale, callback));
+}
+
+std::unique_ptr<base::DictionaryValue> GetCurrentKeyboardLayout() {
+ const input_method::InputMethodDescriptor current_input_method =
+ input_method::InputMethodManager::Get()
+ ->GetActiveIMEState()
+ ->GetCurrentInputMethod();
+ return CreateInputMethodsEntry(current_input_method,
+ current_input_method.id());
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/l10n_util.h b/chromium/chrome/browser/ui/webui/chromeos/login/l10n_util.h
new file mode 100644
index 00000000000..53a71fb7964
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/l10n_util.h
@@ -0,0 +1,109 @@
+// 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_UI_WEBUI_CHROMEOS_LOGIN_L10N_UTIL_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_L10N_UTIL_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/strings/string16.h"
+#include "chrome/browser/chromeos/base/locale_util.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+namespace chromeos {
+
+typedef base::Callback<void(
+ std::unique_ptr<base::ListValue> /* new_language_list */,
+ const std::string& /* new_language_list_locale */,
+ const std::string& /* new_selected_language */)>
+ UILanguageListResolvedCallback;
+
+// GetUILanguageList() returns a concatenated list of the most relevant
+// languages followed by all others. An entry with its "code" attribute set to
+// this value is inserted in between.
+extern const char kMostRelevantLanguagesDivider[];
+
+// Utility methods for retrieving lists of supported locales and input methods /
+// keyboard layouts during OOBE and on the login screen.
+
+// Return a list of languages in which the UI can be shown. Each list entry is a
+// dictionary that contains data such as the language's locale code and a
+// display name. The list will consist of the |most_relevant_language_codes|,
+// followed by a divider and all other supported languages after that. If
+// |most_relevant_language_codes| is NULL, the most relevant languages are read
+// from initial_locale in VPD. If |selected| matches the locale code of any
+// entry in the resulting list, that entry will be marked as selected.
+std::unique_ptr<base::ListValue> GetUILanguageList(
+ const std::vector<std::string>* most_relevant_language_codes,
+ const std::string& selected);
+
+// Must be called on UI thread. Runs GetUILanguageList(), on Blocking Pool,
+// and calls |callback| on UI thread with result.
+// If |language_switch_result| is null, assume current browser locale is already
+// correct and has been successfully loaded.
+void ResolveUILanguageList(
+ std::unique_ptr<locale_util::LanguageSwitchResult> language_switch_result,
+ const UILanguageListResolvedCallback& callback);
+
+// Returns a minimal list of UI languages, which consists of active language
+// only. It is used as a placeholder until ResolveUILanguageList() finishes
+// on BlockingPool.
+std::unique_ptr<base::ListValue> GetMinimalUILanguageList();
+
+// Returns the most first entry of |most_relevant_language_codes| that is
+// actually available (present in |available_locales|). If none of the entries
+// are present in |available_locales|, returns the |fallback_locale|.
+std::string FindMostRelevantLocale(
+ const std::vector<std::string>& most_relevant_language_codes,
+ const base::ListValue& available_locales,
+ const std::string& fallback_locale);
+
+// Return a list of supported accept languages. The listed languages can be used
+// in the Accept-Language header. The return value will look like:
+// [{'code': 'fi', 'displayName': 'Finnish', 'nativeDisplayName': 'suomi'}, ...]
+// The most relevant languages, read from initial_locale in VPD, will be first
+// in the list.
+std::unique_ptr<base::ListValue> GetAcceptLanguageList();
+
+// Return a list of keyboard layouts that can be used for |locale| on the login
+// screen. Each list entry is a dictionary that contains data such as an ID and
+// a display name. The list will consist of the device's hardware layouts,
+// followed by a divider and locale-specific keyboard layouts, if any. The list
+// will also always contain the US keyboard layout. If |selected| matches the ID
+// of any entry in the resulting list, that entry will be marked as selected.
+// In addition to returning the list of keyboard layouts, this function also
+// activates them if |activate_keyboards| is true, so that they can be selected
+// by the user (e.g. by cycling through keyboard layouts via keyboard
+// shortcuts).
+std::unique_ptr<base::ListValue> GetAndActivateLoginKeyboardLayouts(
+ const std::string& locale,
+ const std::string& selected,
+ bool activate_keyboards);
+
+// Invokes |callback| with a list of keyboard layouts that can be used for
+// |locale|. Each list entry is a dictionary that contains data such as an ID
+// and a display name. The list will consist of the device's hardware layouts,
+// followed by a divider and locale-specific keyboard layouts, if any. All
+// layouts supported for |locale| are returned, including those that produce
+// non-Latin characters by default.
+typedef base::Callback<void(std::unique_ptr<base::ListValue>)>
+ GetKeyboardLayoutsForLocaleCallback;
+void GetKeyboardLayoutsForLocale(
+ const GetKeyboardLayoutsForLocaleCallback& callback,
+ const std::string& locale);
+
+// Returns the current keyboard layout, expressed as a dictionary that contains
+// data such as an ID and a display name.
+std::unique_ptr<base::DictionaryValue> GetCurrentKeyboardLayout();
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_L10N_UTIL_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/l10n_util_test_util.cc b/chromium/chrome/browser/ui/webui/chromeos/login/l10n_util_test_util.cc
new file mode 100644
index 00000000000..7189ed29cf2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/l10n_util_test_util.cc
@@ -0,0 +1,40 @@
+// 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/ui/webui/chromeos/login/l10n_util_test_util.h"
+
+#include <vector>
+
+#include "url/gurl.h"
+
+namespace chromeos {
+
+MockInputMethodManagerWithInputMethods::
+ MockInputMethodManagerWithInputMethods() {
+}
+
+MockInputMethodManagerWithInputMethods::
+ ~MockInputMethodManagerWithInputMethods() {
+}
+
+std::unique_ptr<input_method::InputMethodDescriptors>
+MockInputMethodManagerWithInputMethods::GetSupportedInputMethods() const {
+ return std::unique_ptr<input_method::InputMethodDescriptors>(
+ new input_method::InputMethodDescriptors(descriptors_));
+}
+
+void MockInputMethodManagerWithInputMethods::AddInputMethod(
+ const std::string& id,
+ const std::string& raw_layout,
+ const std::string& language_code) {
+ std::vector<std::string> layouts;
+ layouts.push_back(raw_layout);
+ std::vector<std::string> languages;
+ languages.push_back(language_code);
+ descriptors_.push_back(input_method::InputMethodDescriptor(
+ id, std::string(), std::string(), layouts, languages, true,
+ GURL(), GURL()));
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/l10n_util_test_util.h b/chromium/chrome/browser/ui/webui/chromeos/login/l10n_util_test_util.h
new file mode 100644
index 00000000000..8136458bc60
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/l10n_util_test_util.h
@@ -0,0 +1,41 @@
+// 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_UI_WEBUI_CHROMEOS_LOGIN_L10N_UTIL_TEST_UTIL_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_L10N_UTIL_TEST_UTIL_H_
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/input_method/mock_input_method_manager_impl.h"
+#include "ui/base/ime/chromeos/input_method_descriptor.h"
+
+namespace chromeos {
+
+class MockInputMethodManagerWithInputMethods
+ : public input_method::MockInputMethodManagerImpl {
+ public:
+ MockInputMethodManagerWithInputMethods();
+ ~MockInputMethodManagerWithInputMethods() override;
+
+ // input_method::MockInputMethodManagerImpl:
+ std::unique_ptr<input_method::InputMethodDescriptors>
+ GetSupportedInputMethods() const override;
+
+ void AddInputMethod(const std::string& id,
+ const std::string& raw_layout,
+ const std::string& language_code);
+
+ private:
+ input_method::InputMethodDescriptors descriptors_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockInputMethodManagerWithInputMethods);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_L10N_UTIL_TEST_UTIL_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/l10n_util_unittest.cc b/chromium/chrome/browser/ui/webui/chromeos/login/l10n_util_unittest.cc
new file mode 100644
index 00000000000..13aa6463824
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/l10n_util_unittest.cc
@@ -0,0 +1,230 @@
+// 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/ui/webui/chromeos/login/l10n_util.h"
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/at_exit.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/singleton.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/customization/customization_document.h"
+#include "chrome/browser/chromeos/input_method/input_method_configuration.h"
+#include "chrome/browser/ui/webui/chromeos/login/l10n_util_test_util.h"
+#include "chromeos/system/statistics_provider.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/ime/chromeos/component_extension_ime_manager.h"
+
+namespace chromeos {
+
+namespace {
+
+class MachineStatisticsInitializer {
+ public:
+ MachineStatisticsInitializer();
+
+ static MachineStatisticsInitializer* GetInstance();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MachineStatisticsInitializer);
+};
+
+MachineStatisticsInitializer::MachineStatisticsInitializer() {
+ base::MessageLoop loop;
+ chromeos::system::StatisticsProvider::GetInstance()
+ ->StartLoadingMachineStatistics(loop.task_runner(), false);
+ base::RunLoop().RunUntilIdle();
+}
+
+// static
+MachineStatisticsInitializer* MachineStatisticsInitializer::GetInstance() {
+ return base::Singleton<MachineStatisticsInitializer>::get();
+}
+
+void VerifyOnlyUILanguages(const base::ListValue& list) {
+ for (size_t i = 0; i < list.GetSize(); ++i) {
+ const base::DictionaryValue* dict;
+ ASSERT_TRUE(list.GetDictionary(i, &dict));
+ std::string code;
+ ASSERT_TRUE(dict->GetString("code", &code));
+ EXPECT_NE("is", code)
+ << "Icelandic is an example language which has input method "
+ << "but can't use it as UI language.";
+ }
+}
+
+void VerifyLanguageCode(const base::ListValue& list,
+ size_t index,
+ const std::string& expected_code) {
+ const base::DictionaryValue* dict;
+ ASSERT_TRUE(list.GetDictionary(index, &dict));
+ std::string actual_code;
+ ASSERT_TRUE(dict->GetString("code", &actual_code));
+ EXPECT_EQ(expected_code, actual_code)
+ << "Wrong language code at index " << index << ".";
+}
+
+} // namespace
+
+class L10nUtilTest : public testing::Test {
+ public:
+ L10nUtilTest();
+ ~L10nUtilTest() override;
+
+ // testing::Test:
+ void SetUp() override;
+ void TearDown() override;
+
+ void SetInputMethods1();
+ void SetInputMethods2();
+
+ private:
+ base::ShadowingAtExitManager at_exit_manager_;
+
+ MockInputMethodManagerWithInputMethods* input_manager_;
+};
+
+L10nUtilTest::L10nUtilTest()
+ : input_manager_(new MockInputMethodManagerWithInputMethods) {
+}
+
+L10nUtilTest::~L10nUtilTest() {
+}
+
+void L10nUtilTest::SetUp() {
+ chromeos::input_method::InitializeForTesting(input_manager_);
+ input_manager_->SetComponentExtensionIMEManager(
+ base::WrapUnique(new ComponentExtensionIMEManager));
+ MachineStatisticsInitializer::GetInstance(); // Ignore result.
+}
+
+void L10nUtilTest::TearDown() {
+ chromeos::input_method::Shutdown();
+}
+
+void L10nUtilTest::SetInputMethods1() {
+ input_manager_->AddInputMethod("xkb:us::eng", "us", "en-US");
+ input_manager_->AddInputMethod("xkb:fr::fra", "fr", "fr");
+ input_manager_->AddInputMethod("xkb:be::fra", "be", "fr");
+ input_manager_->AddInputMethod("xkb:is::ice", "is", "is");
+}
+
+void L10nUtilTest::SetInputMethods2() {
+ input_manager_->AddInputMethod("xkb:us::eng", "us", "en-US");
+ input_manager_->AddInputMethod("xkb:ch:fr:fra", "ch(fr)", "fr");
+ input_manager_->AddInputMethod("xkb:ch::ger", "ch", "de");
+ input_manager_->AddInputMethod("xkb:it::ita", "it", "it");
+ input_manager_->AddInputMethod("xkb:is::ice", "is", "is");
+}
+
+TEST_F(L10nUtilTest, GetUILanguageList) {
+ SetInputMethods1();
+
+ // This requires initialized StatisticsProvider (see L10nUtilTest()).
+ std::unique_ptr<base::ListValue> list(GetUILanguageList(NULL, std::string()));
+
+ VerifyOnlyUILanguages(*list);
+}
+
+TEST_F(L10nUtilTest, FindMostRelevantLocale) {
+ base::ListValue available_locales;
+ std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+ dict->SetString("value", "de");
+ available_locales.Append(std::move(dict));
+ dict.reset(new base::DictionaryValue);
+ dict->SetString("value", "fr");
+ available_locales.Append(std::move(dict));
+ dict.reset(new base::DictionaryValue);
+ dict->SetString("value", "en-GB");
+ available_locales.Append(std::move(dict));
+
+ std::vector<std::string> most_relevant_language_codes;
+ EXPECT_EQ("en-US", FindMostRelevantLocale(most_relevant_language_codes,
+ available_locales,
+ "en-US"));
+
+ most_relevant_language_codes.push_back("xx");
+ EXPECT_EQ("en-US", FindMostRelevantLocale(most_relevant_language_codes,
+ available_locales,
+ "en-US"));
+
+ most_relevant_language_codes.push_back("fr");
+ EXPECT_EQ("fr", FindMostRelevantLocale(most_relevant_language_codes,
+ available_locales,
+ "en-US"));
+
+ most_relevant_language_codes.push_back("de");
+ EXPECT_EQ("fr", FindMostRelevantLocale(most_relevant_language_codes,
+ available_locales,
+ "en-US"));
+}
+
+void InitStartupCustomizationDocumentForTesting(const std::string& manifest) {
+ StartupCustomizationDocument::GetInstance()->LoadManifestFromString(manifest);
+ StartupCustomizationDocument::GetInstance()->Init(
+ chromeos::system::StatisticsProvider::GetInstance());
+}
+
+const char kStartupManifest[] =
+ "{\n"
+ " \"version\": \"1.0\",\n"
+ " \"initial_locale\" : \"fr,en-US,de,is,it\",\n"
+ " \"initial_timezone\" : \"Europe/Zurich\",\n"
+ " \"keyboard_layout\" : \"xkb:ch:fr:fra\",\n"
+ " \"registration_url\" : \"http://www.google.com\",\n"
+ " \"setup_content\" : {\n"
+ " \"default\" : {\n"
+ " \"help_page\" : \"file:///opt/oem/help/en-US/help.html\",\n"
+ " \"eula_page\" : \"file:///opt/oem/eula/en-US/eula.html\",\n"
+ " },\n"
+ " },"
+ "}";
+
+TEST_F(L10nUtilTest, GetUILanguageListMulti) {
+ InitStartupCustomizationDocumentForTesting(kStartupManifest);
+ SetInputMethods2();
+
+ // This requires initialized StatisticsProvider (see L10nUtilTest()).
+ std::unique_ptr<base::ListValue> list(GetUILanguageList(NULL, std::string()));
+
+ VerifyOnlyUILanguages(*list);
+
+ // (4 languages (except Icelandic) + divider) = 5 + all other languages
+ ASSERT_LE(5u, list->GetSize());
+
+ VerifyLanguageCode(*list, 0, "fr");
+ VerifyLanguageCode(*list, 1, "en-US");
+ VerifyLanguageCode(*list, 2, "de");
+ VerifyLanguageCode(*list, 3, "it");
+ VerifyLanguageCode(*list, 4, kMostRelevantLanguagesDivider);
+}
+
+TEST_F(L10nUtilTest, GetUILanguageListWithMostRelevant) {
+ std::vector<std::string> most_relevant_language_codes;
+ most_relevant_language_codes.push_back("it");
+ most_relevant_language_codes.push_back("de");
+ most_relevant_language_codes.push_back("nonexistent");
+
+ // This requires initialized StatisticsProvider (see L10nUtilTest()).
+ std::unique_ptr<base::ListValue> list(
+ GetUILanguageList(&most_relevant_language_codes, std::string()));
+
+ VerifyOnlyUILanguages(*list);
+
+ ASSERT_LE(3u, list->GetSize());
+
+ VerifyLanguageCode(*list, 0, "it");
+ VerifyLanguageCode(*list, 1, "de");
+ VerifyLanguageCode(*list, 2, kMostRelevantLanguagesDivider);
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/native_window_delegate.h b/chromium/chrome/browser/ui/webui/chromeos/login/native_window_delegate.h
new file mode 100644
index 00000000000..f7453e022bd
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/native_window_delegate.h
@@ -0,0 +1,24 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_NATIVE_WINDOW_DELEGATE_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_NATIVE_WINDOW_DELEGATE_H_
+
+#include "ui/gfx/native_widget_types.h"
+
+namespace chromeos {
+
+// An interface to get gfx::NativeWindow.
+class NativeWindowDelegate {
+ public:
+ NativeWindowDelegate() {}
+ virtual ~NativeWindowDelegate() {}
+
+ // Returns corresponding native window.
+ virtual gfx::NativeWindow GetNativeWindow() const = 0;
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_NATIVE_WINDOW_DELEGATE_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown.cc b/chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown.cc
new file mode 100644
index 00000000000..deb66892b6e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown.cc
@@ -0,0 +1,215 @@
+// 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/ui/webui/chromeos/login/network_dropdown.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "ash/system/network/network_icon.h"
+#include "ash/system/network/network_icon_animation.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host.h"
+#include "chromeos/network/network_state_handler.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/models/menu_model.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "ui/gfx/font_list.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace {
+
+// Timeout between consecutive requests to network library for network
+// scan.
+const int kNetworkScanIntervalSecs = 60;
+
+} // namespace
+
+namespace chromeos {
+
+// WebUI specific implementation of the NetworkMenu class.
+class NetworkMenuWebUI : public NetworkMenu {
+ public:
+ NetworkMenuWebUI(NetworkMenu::Delegate* delegate, content::WebUI* web_ui);
+
+ // NetworkMenu override:
+ void UpdateMenu() override;
+
+ // Called when item with command |id| is chosen.
+ void OnItemChosen(int id);
+
+ private:
+ // Converts menu model into the ListValue, ready for passing to WebUI.
+ std::unique_ptr<base::ListValue> ConvertMenuModel(ui::MenuModel* model);
+
+ // WebUI where network menu is located.
+ content::WebUI* web_ui_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkMenuWebUI);
+};
+
+// NetworkMenuWebUI ------------------------------------------------------------
+
+NetworkMenuWebUI::NetworkMenuWebUI(NetworkMenu::Delegate* delegate,
+ content::WebUI* web_ui)
+ : NetworkMenu(delegate),
+ web_ui_(web_ui) {
+}
+
+void NetworkMenuWebUI::UpdateMenu() {
+ NetworkMenu::UpdateMenu();
+ if (web_ui_) {
+ std::unique_ptr<base::ListValue> list(ConvertMenuModel(GetMenuModel()));
+ web_ui_->CallJavascriptFunctionUnsafe("cr.ui.DropDown.updateNetworks",
+ *list);
+ }
+}
+
+void NetworkMenuWebUI::OnItemChosen(int id) {
+ int index;
+ ui::MenuModel* model = GetMenuModel();
+ if (!ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index))
+ return;
+ model->ActivatedAt(index);
+}
+
+std::unique_ptr<base::ListValue> NetworkMenuWebUI::ConvertMenuModel(
+ ui::MenuModel* model) {
+ auto list = base::MakeUnique<base::ListValue>();
+ for (int i = 0; i < model->GetItemCount(); ++i) {
+ ui::MenuModel::ItemType type = model->GetTypeAt(i);
+ int id;
+ if (type == ui::MenuModel::TYPE_SEPARATOR)
+ id = -2;
+ else
+ id = model->GetCommandIdAt(i);
+ auto item = base::MakeUnique<base::DictionaryValue>();
+ item->SetInteger("id", id);
+ base::string16 label = model->GetLabelAt(i);
+ base::ReplaceSubstringsAfterOffset(&label, 0, base::ASCIIToUTF16("&&"),
+ base::ASCIIToUTF16("&"));
+ item->SetString("label", label);
+ gfx::Image icon;
+ if (model->GetIconAt(i, &icon)) {
+ SkBitmap icon_bitmap = icon.ToImageSkia()->GetRepresentation(
+ web_ui_->GetDeviceScaleFactor()).sk_bitmap();
+ item->SetString("icon", webui::GetBitmapDataUrl(icon_bitmap));
+ }
+ if (id >= 0) {
+ item->SetBoolean("enabled", model->IsEnabledAt(i));
+ const gfx::FontList* font_list = model->GetLabelFontListAt(i);
+ if (font_list)
+ item->SetBoolean("bold",
+ font_list->GetFontWeight() == gfx::Font::Weight::BOLD);
+ }
+ if (type == ui::MenuModel::TYPE_SUBMENU)
+ item->Set("sub", ConvertMenuModel(model->GetSubmenuModelAt(i)));
+ list->Append(std::move(item));
+ }
+ return list;
+}
+
+// NetworkDropdown -------------------------------------------------------------
+
+NetworkDropdown::NetworkDropdown(View* view, content::WebUI* web_ui, bool oobe)
+ : view_(view), web_ui_(web_ui), oobe_(oobe) {
+ DCHECK(view_);
+ network_menu_.reset(new NetworkMenuWebUI(this, web_ui));
+ DCHECK(NetworkHandler::IsInitialized());
+ NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
+ handler->RequestScan();
+ handler->AddObserver(this, FROM_HERE);
+ Refresh();
+ network_scan_timer_.Start(
+ FROM_HERE,
+ base::TimeDelta::FromSeconds(kNetworkScanIntervalSecs),
+ this, &NetworkDropdown::RequestNetworkScan);
+}
+
+NetworkDropdown::~NetworkDropdown() {
+ ash::network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
+ if (NetworkHandler::IsInitialized()) {
+ NetworkHandler::Get()->network_state_handler()->RemoveObserver(
+ this, FROM_HERE);
+ }
+}
+
+void NetworkDropdown::OnItemChosen(int id) {
+ network_menu_->OnItemChosen(id);
+}
+
+gfx::NativeWindow NetworkDropdown::GetNativeWindow() const {
+ return LoginDisplayHost::default_host()->GetNativeWindow();
+}
+
+void NetworkDropdown::OpenButtonOptions() {
+ LoginDisplayHost::default_host()->OpenProxySettings();
+}
+
+bool NetworkDropdown::ShouldOpenButtonOptions() const {
+ return !oobe_;
+}
+
+void NetworkDropdown::OnConnectToNetworkRequested() {
+ view_->OnConnectToNetworkRequested();
+}
+
+void NetworkDropdown::DefaultNetworkChanged(const NetworkState* network) {
+ Refresh();
+}
+
+void NetworkDropdown::NetworkConnectionStateChanged(
+ const NetworkState* network) {
+ Refresh();
+}
+
+void NetworkDropdown::NetworkListChanged() {
+ Refresh();
+}
+
+void NetworkDropdown::NetworkIconChanged() {
+ SetNetworkIconAndText();
+}
+
+void NetworkDropdown::Refresh() {
+ SetNetworkIconAndText();
+ network_menu_->UpdateMenu();
+}
+
+void NetworkDropdown::SetNetworkIconAndText() {
+ base::string16 text;
+ gfx::ImageSkia icon_image;
+ bool animating = false;
+ ash::network_icon::GetDefaultNetworkImageAndLabel(
+ ash::network_icon::ICON_TYPE_LIST, &icon_image, &text, &animating);
+ if (animating) {
+ ash::network_icon::NetworkIconAnimation::GetInstance()->AddObserver(this);
+ } else {
+ ash::network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(
+ this);
+ }
+ SkBitmap icon_bitmap = icon_image.GetRepresentation(
+ web_ui_->GetDeviceScaleFactor()).sk_bitmap();
+ std::string icon_str;
+ if (!icon_image.isNull())
+ icon_str = webui::GetBitmapDataUrl(icon_bitmap);
+ base::Value title(text);
+ base::Value icon(icon_str);
+ web_ui_->CallJavascriptFunctionUnsafe("cr.ui.DropDown.updateNetworkTitle",
+ title, icon);
+}
+
+void NetworkDropdown::RequestNetworkScan() {
+ NetworkHandler::Get()->network_state_handler()->RequestScan();
+ Refresh();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown.h b/chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown.h
new file mode 100644
index 00000000000..3eaddd0025c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown.h
@@ -0,0 +1,87 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_NETWORK_DROPDOWN_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_NETWORK_DROPDOWN_H_
+
+#include <memory>
+
+#include "ash/system/network/network_icon_animation_observer.h"
+#include "base/macros.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/chromeos/status/network_menu.h"
+#include "chromeos/network/network_state_handler_observer.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace content {
+class WebUI;
+}
+
+namespace chromeos {
+
+class NetworkMenuWebUI;
+class NetworkState;
+
+// Class which implements network dropdown menu using WebUI.
+class NetworkDropdown : public NetworkMenu::Delegate,
+ public NetworkStateHandlerObserver,
+ public ash::network_icon::AnimationObserver {
+ public:
+ class View {
+ public:
+ virtual ~View() {}
+ virtual void OnConnectToNetworkRequested() = 0;
+ };
+
+ NetworkDropdown(View* view, content::WebUI* web_ui, bool oobe);
+ ~NetworkDropdown() override;
+
+ // This method should be called, when item with the given id is chosen.
+ void OnItemChosen(int id);
+
+ // NetworkMenu::Delegate
+ gfx::NativeWindow GetNativeWindow() const override;
+ void OpenButtonOptions() override;
+ bool ShouldOpenButtonOptions() const override;
+ void OnConnectToNetworkRequested() override;
+
+ // NetworkStateHandlerObserver
+ void DefaultNetworkChanged(const NetworkState* network) override;
+ void NetworkConnectionStateChanged(const NetworkState* network) override;
+ void NetworkListChanged() override;
+
+ // network_icon::AnimationObserver
+ void NetworkIconChanged() override;
+
+ // Refreshes control state. Usually there's no need to do it manually
+ // as control refreshes itself on network state change.
+ // Should be called on language change.
+ void Refresh();
+
+ private:
+ void SetNetworkIconAndText();
+
+ // Request a network scan and refreshes control state. Should be called
+ // by |network_scan_timer_| only.
+ void RequestNetworkScan();
+
+ // The Network menu.
+ std::unique_ptr<NetworkMenuWebUI> network_menu_;
+
+ View* view_;
+
+ content::WebUI* web_ui_;
+
+ // Is the dropdown shown on one of the OOBE screens.
+ bool oobe_;
+
+ // Timer used to periodically force network scan.
+ base::RepeatingTimer network_scan_timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkDropdown);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_NETWORK_DROPDOWN_H_
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
new file mode 100644
index 00000000000..fdfbf203060
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.cc
@@ -0,0 +1,123 @@
+// 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/ui/webui/chromeos/login/network_dropdown_handler.h"
+
+#include "chrome/browser/chromeos/login/ui/webui_login_display.h"
+#include "chrome/browser/chromeos/options/network_config_view.h"
+#include "chrome/browser/chromeos/ui/choose_mobile_network_dialog.h"
+#include "chrome/browser/ui/webui/chromeos/login/network_dropdown.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace {
+
+const char kJsScreenPath[] = "cr.ui.DropDown";
+
+// JS API callbacks names.
+const char kJsApiNetworkItemChosen[] = "networkItemChosen";
+const char kJsApiNetworkDropdownShow[] = "networkDropdownShow";
+const char kJsApiNetworkDropdownHide[] = "networkDropdownHide";
+const char kJsApiNetworkDropdownRefresh[] = "networkDropdownRefresh";
+const char kJsApiLaunchProxySettingsDialog[] = "launchProxySettingsDialog";
+const char kJsApiLaunchAddWiFiNetworkDialog[] = "launchAddWiFiNetworkDialog";
+const char kJsApiLaunchAddMobileNetworkDialog[] =
+ "launchAddMobileNetworkDialog";
+
+} // namespace
+
+namespace chromeos {
+
+NetworkDropdownHandler::NetworkDropdownHandler() {
+ set_call_js_prefix(kJsScreenPath);
+}
+
+NetworkDropdownHandler::~NetworkDropdownHandler() {
+}
+
+void NetworkDropdownHandler::AddObserver(Observer* observer) {
+ if (observer && !observers_.HasObserver(observer))
+ observers_.AddObserver(observer);
+}
+
+void NetworkDropdownHandler::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void NetworkDropdownHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("selectNetwork", IDS_NETWORK_SELECTION_SELECT);
+ builder->Add("selectAnotherNetwork", IDS_ANOTHER_NETWORK_SELECTION_SELECT);
+}
+
+void NetworkDropdownHandler::Initialize() {
+}
+
+void NetworkDropdownHandler::RegisterMessages() {
+ AddCallback(kJsApiNetworkItemChosen,
+ &NetworkDropdownHandler::HandleNetworkItemChosen);
+ AddCallback(kJsApiNetworkDropdownShow,
+ &NetworkDropdownHandler::HandleNetworkDropdownShow);
+ AddCallback(kJsApiNetworkDropdownHide,
+ &NetworkDropdownHandler::HandleNetworkDropdownHide);
+ AddCallback(kJsApiNetworkDropdownRefresh,
+ &NetworkDropdownHandler::HandleNetworkDropdownRefresh);
+
+ // MD-OOBE
+ AddCallback(kJsApiLaunchProxySettingsDialog,
+ &NetworkDropdownHandler::HandleLaunchProxySettingsDialog);
+ AddCallback(kJsApiLaunchAddWiFiNetworkDialog,
+ &NetworkDropdownHandler::HandleLaunchAddWiFiNetworkDialog);
+ AddCallback(kJsApiLaunchAddMobileNetworkDialog,
+ &NetworkDropdownHandler::HandleLaunchAddMobileNetworkDialog);
+}
+
+void NetworkDropdownHandler::HandleLaunchProxySettingsDialog() {
+ dropdown_->OpenButtonOptions();
+}
+
+void NetworkDropdownHandler::HandleLaunchAddWiFiNetworkDialog() {
+ gfx::NativeWindow native_window = GetNativeWindow();
+ NetworkConfigView::ShowForType(shill::kTypeWifi, native_window);
+}
+
+void NetworkDropdownHandler::HandleLaunchAddMobileNetworkDialog() {
+ gfx::NativeWindow native_window = GetNativeWindow();
+ ChooseMobileNetworkDialog::ShowDialog(native_window);
+}
+
+void NetworkDropdownHandler::OnConnectToNetworkRequested() {
+ for (Observer& observer : observers_)
+ observer.OnConnectToNetworkRequested();
+}
+
+void NetworkDropdownHandler::HandleNetworkItemChosen(double id) {
+ if (dropdown_.get()) {
+ dropdown_->OnItemChosen(static_cast<int>(id));
+ } else {
+ // It could happen with very low probability but still keep NOTREACHED to
+ // detect if it starts happening all the time.
+ NOTREACHED();
+ }
+}
+
+void NetworkDropdownHandler::HandleNetworkDropdownShow(
+ const std::string& element_id,
+ bool oobe) {
+ dropdown_.reset(new NetworkDropdown(this, web_ui(), oobe));
+}
+
+void NetworkDropdownHandler::HandleNetworkDropdownHide() {
+ dropdown_.reset();
+}
+
+void NetworkDropdownHandler::HandleNetworkDropdownRefresh() {
+ // Since language change is async,
+ // we may in theory be on another screen during this call.
+ if (dropdown_.get())
+ dropdown_->Refresh();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.h
new file mode 100644
index 00000000000..9e0841de272
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2011 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_CHROMEOS_LOGIN_NETWORK_DROPDOWN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_NETWORK_DROPDOWN_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_webui_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/network_dropdown.h"
+
+namespace chromeos {
+
+class NetworkDropdownHandler : public BaseWebUIHandler,
+ public NetworkDropdown::View {
+ public:
+ class Observer {
+ public:
+ virtual ~Observer() {}
+ virtual void OnConnectToNetworkRequested() = 0;
+ };
+
+ NetworkDropdownHandler();
+ ~NetworkDropdownHandler() override;
+
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void Initialize() override;
+
+ // WebUIMessageHandler implementation:
+ void RegisterMessages() override;
+
+ private:
+ // NetworkDropdown::Actor implementation:
+ void OnConnectToNetworkRequested() override;
+
+ // Handles choosing of the network menu item.
+ void HandleNetworkItemChosen(double id);
+ // Handles network drop-down showing.
+ void HandleNetworkDropdownShow(const std::string& element_id,
+ bool oobe);
+ // Handles network drop-down hiding.
+ void HandleNetworkDropdownHide();
+ // Handles network drop-down refresh.
+ void HandleNetworkDropdownRefresh();
+
+ void HandleLaunchProxySettingsDialog();
+ void HandleLaunchAddWiFiNetworkDialog();
+ void HandleLaunchAddMobileNetworkDialog();
+
+ std::unique_ptr<NetworkDropdown> dropdown_;
+
+ base::ObserverList<Observer> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkDropdownHandler);
+};
+
+} // namespace chromeos
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_NETWORK_DROPDOWN_HANDLER_H_
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
new file mode 100644
index 00000000000..a94c49633eb
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
@@ -0,0 +1,296 @@
+// 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/ui/webui/chromeos/login/network_screen_handler.h"
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task_runner_util.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
+#include "chrome/browser/chromeos/customization/customization_document.h"
+#include "chrome/browser/chromeos/idle_detector.h"
+#include "chrome/browser/chromeos/login/screens/core_oobe_view.h"
+#include "chrome/browser/chromeos/login/screens/network_screen.h"
+#include "chrome/browser/chromeos/login/ui/input_events_blocker.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
+#include "chrome/browser/chromeos/system/input_device_settings.h"
+#include "chrome/browser/chromeos/system/timezone_util.h"
+#include "chrome/browser/ui/webui/chromeos/login/l10n_util.h"
+#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_state_handler.h"
+#include "components/login/localized_values_builder.h"
+#include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/browser_thread.h"
+#include "ui/base/ime/chromeos/extension_ime_util.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/views/layout/fill_layout.h"
+#include "ui/views/widget/widget.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.NetworkScreen";
+
+} // namespace
+
+namespace chromeos {
+
+// NetworkScreenHandler, public: -----------------------------------------------
+
+NetworkScreenHandler::NetworkScreenHandler(CoreOobeView* core_oobe_view)
+ : BaseScreenHandler(kScreenId), core_oobe_view_(core_oobe_view) {
+ set_call_js_prefix(kJsScreenPath);
+ DCHECK(core_oobe_view_);
+}
+
+NetworkScreenHandler::~NetworkScreenHandler() {
+ if (screen_)
+ screen_->OnViewDestroyed(this);
+}
+
+// NetworkScreenHandler, NetworkScreenView implementation: ---------------------
+
+void NetworkScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+
+ PrefService* prefs = g_browser_process->local_state();
+ if (prefs->GetBoolean(prefs::kFactoryResetRequested)) {
+ if (core_oobe_view_)
+ core_oobe_view_->ShowDeviceResetScreen();
+
+ return;
+ } else if (prefs->GetBoolean(prefs::kDebuggingFeaturesRequested)) {
+ if (core_oobe_view_)
+ core_oobe_view_->ShowEnableDebuggingScreen();
+
+ return;
+ }
+
+ // Make sure all our network technologies are turned on. On OOBE, the user
+ // should be able to select any of the available networks on the device.
+ NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
+ handler->SetTechnologyEnabled(NetworkTypePattern::NonVirtual(),
+ true,
+ chromeos::network_handler::ErrorCallback());
+
+ base::DictionaryValue network_screen_params;
+ network_screen_params.SetBoolean("isDeveloperMode",
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kSystemDevMode));
+ ShowScreenWithData(kScreenId, &network_screen_params);
+ core_oobe_view_->InitDemoModeDetection();
+}
+
+void NetworkScreenHandler::Hide() {
+}
+
+void NetworkScreenHandler::Bind(NetworkScreen* screen) {
+ screen_ = screen;
+ BaseScreenHandler::SetBaseScreen(screen_);
+}
+
+void NetworkScreenHandler::Unbind() {
+ screen_ = nullptr;
+ BaseScreenHandler::SetBaseScreen(nullptr);
+}
+
+void NetworkScreenHandler::ShowError(const base::string16& message) {
+ CallJS("showError", message);
+}
+
+void NetworkScreenHandler::ClearErrors() {
+ if (page_is_ready())
+ core_oobe_view_->ClearErrors();
+}
+
+void NetworkScreenHandler::StopDemoModeDetection() {
+ core_oobe_view_->StopDemoModeDetection();
+}
+
+void NetworkScreenHandler::ShowConnectingStatus(
+ bool connecting,
+ const base::string16& network_id) {
+}
+
+void NetworkScreenHandler::ReloadLocalizedContent() {
+ base::DictionaryValue localized_strings;
+ GetOobeUI()->GetLocalizedStrings(&localized_strings);
+ core_oobe_view_->ReloadContent(localized_strings);
+}
+
+// NetworkScreenHandler, BaseScreenHandler implementation: --------------------
+
+void NetworkScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ if (system::InputDeviceSettings::Get()->ForceKeyboardDrivenUINavigation())
+ builder->Add("networkScreenGreeting", IDS_REMORA_CONFIRM_MESSAGE);
+ else
+ 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);
+ builder->Add("selectTimezone", IDS_OPTIONS_SETTINGS_TIMEZONE_DESCRIPTION);
+ builder->Add("timezoneDropdownLabel", IDS_TIMEZONE_DROPDOWN_LABEL);
+ builder->Add("proxySettings", IDS_OPTIONS_PROXIES_CONFIGURE_BUTTON);
+ builder->Add("continueButton", IDS_NETWORK_SELECTION_CONTINUE_BUTTON);
+ builder->Add("debuggingFeaturesLink", IDS_NETWORK_ENABLE_DEV_FEATURES_LINK);
+
+ // MD-OOBE
+ builder->Add("oobeOKButtonText", IDS_OOBE_OK_BUTTON_TEXT);
+ builder->Add("welcomeNextButtonText", IDS_OOBE_WELCOME_NEXT_BUTTON_TEXT);
+ builder->Add("languageButtonLabel", IDS_LANGUAGE_BUTTON_LABEL);
+ builder->Add("languageSectionTitle", IDS_LANGUAGE_SECTION_TITLE);
+ builder->Add("accessibilitySectionTitle", IDS_ACCESSIBILITY_SECTION_TITLE);
+ builder->Add("accessibilitySectionHint", IDS_ACCESSIBILITY_SECTION_HINT);
+ builder->Add("timezoneSectionTitle", IDS_TIMEZONE_SECTION_TITLE);
+ builder->Add("networkSectionTitle", IDS_NETWORK_SECTION_TITLE);
+ builder->Add("networkSectionHint", IDS_NETWORK_SECTION_HINT);
+
+ builder->Add("languageDropdownTitle", IDS_LANGUAGE_DROPDOWN_TITLE);
+ builder->Add("languageDropdownLabel", IDS_LANGUAGE_DROPDOWN_LABEL);
+ builder->Add("keyboardDropdownTitle", IDS_KEYBOARD_DROPDOWN_TITLE);
+ builder->Add("keyboardDropdownLabel", IDS_KEYBOARD_DROPDOWN_LABEL);
+ builder->Add("proxySettingsMenuName", IDS_PROXY_SETTINGS_MENU_NAME);
+ builder->Add("addWiFiNetworkMenuName", IDS_ADD_WI_FI_NETWORK_MENU_NAME);
+ builder->Add("addMobileNetworkMenuName", IDS_ADD_MOBILE_NETWORK_MENU_NAME);
+
+ builder->Add("highContrastOptionOff", IDS_HIGH_CONTRAST_OPTION_OFF);
+ builder->Add("highContrastOptionOn", IDS_HIGH_CONTRAST_OPTION_ON);
+ builder->Add("largeCursorOptionOff", IDS_LARGE_CURSOR_OPTION_OFF);
+ builder->Add("largeCursorOptionOn", IDS_LARGE_CURSOR_OPTION_ON);
+ builder->Add("screenMagnifierOptionOff", IDS_SCREEN_MAGNIFIER_OPTION_OFF);
+ builder->Add("screenMagnifierOptionOn", IDS_SCREEN_MAGNIFIER_OPTION_ON);
+ builder->Add("spokenFeedbackOptionOff", IDS_SPOKEN_FEEDBACK_OPTION_OFF);
+ builder->Add("spokenFeedbackOptionOn", IDS_SPOKEN_FEEDBACK_OPTION_ON);
+ builder->Add("virtualKeyboardOptionOff", IDS_VIRTUAL_KEYBOARD_OPTION_OFF);
+ builder->Add("virtualKeyboardOptionOn", IDS_VIRTUAL_KEYBOARD_OPTION_ON);
+
+ builder->Add("timezoneDropdownTitle", IDS_TIMEZONE_DROPDOWN_TITLE);
+ builder->Add("timezoneButtonText", IDS_TIMEZONE_BUTTON_TEXT);
+}
+
+void NetworkScreenHandler::GetAdditionalParameters(
+ base::DictionaryValue* dict) {
+ const std::string application_locale =
+ g_browser_process->GetApplicationLocale();
+ const std::string selected_input_method =
+ input_method::InputMethodManager::Get()
+ ->GetActiveIMEState()
+ ->GetCurrentInputMethod()
+ .id();
+
+ std::unique_ptr<base::ListValue> language_list;
+ if (screen_) {
+ if (screen_->language_list() &&
+ screen_->language_list_locale() == application_locale) {
+ language_list.reset(screen_->language_list()->DeepCopy());
+ } else {
+ screen_->UpdateLanguageList();
+ }
+ }
+
+ if (!language_list)
+ language_list = GetMinimalUILanguageList();
+
+ // GetAdditionalParameters() is called when OOBE language is updated.
+ // This happens in three different cases:
+ //
+ // 1) User selects new locale on OOBE screen. We need to sync active input
+ // methods with locale, so EnableLoginLayouts() is needed.
+ //
+ // 2) This is signin to public session. User has selected some locale & input
+ // method on "Public Session User POD". After "Login" button is pressed,
+ // new user session is created, locale & input method are changed (both
+ // asynchronously).
+ // But after public user session is started, "Terms of Service" dialog is
+ // shown. It is a part of OOBE UI screens, so it initiates reload of UI
+ // strings in new locale. It also happens asynchronously, that leads to race
+ // between "locale change", "input method change" and
+ // "EnableLoginLayouts()". This way EnableLoginLayouts() happens after user
+ // input method has been changed, resetting input method to hardware default.
+ //
+ // So we need to disable activation of login layouts if we are already in
+ // active user session.
+ //
+ // 3) This is the bootstrapping process for a "Slave" device. The locale &
+ // input of the "Slave" device is set up by a "Master" device. In this case we
+ // don't want EnableLoginLayout() to reset the input method to the hardware
+ // default method.
+ const bool is_slave = g_browser_process->local_state()->GetBoolean(
+ prefs::kOobeControllerDetected);
+
+ const bool enable_layouts =
+ !user_manager::UserManager::Get()->IsUserLoggedIn() && !is_slave;
+
+ dict->Set("languageList", std::move(language_list));
+ dict->Set("inputMethodsList",
+ GetAndActivateLoginKeyboardLayouts(
+ application_locale, selected_input_method, enable_layouts));
+ dict->Set("timezoneList", GetTimezoneList());
+}
+
+void NetworkScreenHandler::Initialize() {
+ if (show_on_init_) {
+ show_on_init_ = false;
+ Show();
+ }
+
+ // Reload localized strings if they are already resolved.
+ if (screen_ && screen_->language_list())
+ ReloadLocalizedContent();
+}
+
+// NetworkScreenHandler, private: ----------------------------------------------
+
+// static
+std::unique_ptr<base::ListValue> NetworkScreenHandler::GetTimezoneList() {
+ std::string current_timezone_id;
+ CrosSettings::Get()->GetString(kSystemTimezone, &current_timezone_id);
+
+ std::unique_ptr<base::ListValue> timezone_list(new base::ListValue);
+ std::unique_ptr<base::ListValue> timezones = system::GetTimezoneList();
+ for (size_t i = 0; i < timezones->GetSize(); ++i) {
+ const base::ListValue* timezone = NULL;
+ CHECK(timezones->GetList(i, &timezone));
+
+ std::string timezone_id;
+ CHECK(timezone->GetString(0, &timezone_id));
+
+ std::string timezone_name;
+ CHECK(timezone->GetString(1, &timezone_name));
+
+ std::unique_ptr<base::DictionaryValue> timezone_option(
+ new base::DictionaryValue);
+ timezone_option->SetString("value", timezone_id);
+ timezone_option->SetString("title", timezone_name);
+ timezone_option->SetBoolean("selected", timezone_id == current_timezone_id);
+ timezone_list->Append(std::move(timezone_option));
+ }
+
+ return timezone_list;
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/network_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/network_screen_handler.h
new file mode 100644
index 00000000000..cbf4f71202d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/network_screen_handler.h
@@ -0,0 +1,71 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_NETWORK_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_NETWORK_SCREEN_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/chromeos/base/locale_util.h"
+#include "chrome/browser/chromeos/login/screens/network_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "ui/base/ime/chromeos/component_extension_ime_manager.h"
+#include "ui/base/ime/chromeos/input_method_manager.h"
+#include "ui/gfx/geometry/point.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace chromeos {
+
+class CoreOobeView;
+
+// WebUI implementation of NetworkScreenView. It is used to interact with
+// the welcome screen (part of the page) of the OOBE.
+class NetworkScreenHandler : public NetworkView, public BaseScreenHandler {
+ public:
+ explicit NetworkScreenHandler(CoreOobeView* core_oobe_view);
+ ~NetworkScreenHandler() override;
+
+ private:
+ // NetworkView implementation:
+ void Show() override;
+ void Hide() override;
+ void Bind(NetworkScreen* screen) override;
+ void Unbind() override;
+ void ShowError(const base::string16& message) override;
+ void ClearErrors() override;
+ void StopDemoModeDetection() override;
+ void ShowConnectingStatus(bool connecting,
+ const base::string16& network_id) override;
+ void ReloadLocalizedContent() override;
+
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void GetAdditionalParameters(base::DictionaryValue* dict) override;
+ void Initialize() override;
+
+ // Returns available timezones. Caller gets the ownership.
+ static std::unique_ptr<base::ListValue> GetTimezoneList();
+
+ CoreOobeView* core_oobe_view_ = nullptr;
+ NetworkScreen* screen_ = nullptr;
+
+ // Keeps whether screen should be shown right after initialization.
+ bool show_on_init_ = false;
+
+ // Position of the network control.
+ gfx::Point network_control_pos_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_NETWORK_SCREEN_HANDLER_H_
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
new file mode 100644
index 00000000000..bf7af7ca047
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/network_state_informer.cc
@@ -0,0 +1,215 @@
+// 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/ui/webui/chromeos/login/network_state_informer.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/login/screens/network_error.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "chromeos/network/proxy/proxy_config_handler.h"
+#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 "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+
+namespace {
+
+const char kNetworkStateOffline[] = "offline";
+const char kNetworkStateOnline[] = "online";
+const char kNetworkStateCaptivePortal[] = "behind captive portal";
+const char kNetworkStateConnecting[] = "connecting";
+const char kNetworkStateProxyAuthRequired[] = "proxy auth required";
+
+NetworkStateInformer::State GetStateForDefaultNetwork() {
+ const NetworkState* network =
+ NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
+ if (!network)
+ return NetworkStateInformer::OFFLINE;
+
+ if (network_portal_detector::GetInstance()->IsEnabled()) {
+ NetworkPortalDetector::CaptivePortalState state =
+ network_portal_detector::GetInstance()->GetCaptivePortalState(
+ network->guid());
+ NetworkPortalDetector::CaptivePortalStatus status = state.status;
+ if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN &&
+ NetworkState::StateIsConnecting(network->connection_state())) {
+ return NetworkStateInformer::CONNECTING;
+ }
+ // For proxy-less networks rely on shill's online state if
+ // NetworkPortalDetector's state of current network is unknown.
+ if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE ||
+ (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN &&
+ !NetworkHandler::Get()
+ ->ui_proxy_config_service()
+ ->HasDefaultNetworkProxyConfigured() &&
+ network->connection_state() == shill::kStateOnline)) {
+ return NetworkStateInformer::ONLINE;
+ }
+ if (status ==
+ NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED &&
+ NetworkHandler::Get()
+ ->ui_proxy_config_service()
+ ->HasDefaultNetworkProxyConfigured()) {
+ return NetworkStateInformer::PROXY_AUTH_REQUIRED;
+ }
+ if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL ||
+ (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN &&
+ network->is_captive_portal()))
+ return NetworkStateInformer::CAPTIVE_PORTAL;
+ } else {
+ if (NetworkState::StateIsConnecting(network->connection_state()))
+ return NetworkStateInformer::CONNECTING;
+ if (network->connection_state() == shill::kStateOnline)
+ return NetworkStateInformer::ONLINE;
+ if (network->is_captive_portal())
+ return NetworkStateInformer::CAPTIVE_PORTAL;
+ }
+
+ // If there is no connection to the internet report it as online for the
+ // Active Directory devices. These devices does not have to be online to reach
+ // the server.
+ // TODO(rsorokin): Fix reporting network connectivity for Active Directory
+ // devices. (see crbug.com/685691)
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ if (connector->IsActiveDirectoryManaged())
+ return NetworkStateInformer::ONLINE;
+
+ return NetworkStateInformer::OFFLINE;
+}
+
+} // namespace
+
+NetworkStateInformer::NetworkStateInformer()
+ : state_(OFFLINE),
+ weak_ptr_factory_(this) {
+}
+
+NetworkStateInformer::~NetworkStateInformer() {
+ if (NetworkHandler::IsInitialized()) {
+ NetworkHandler::Get()->network_state_handler()->RemoveObserver(
+ this, FROM_HERE);
+ }
+ network_portal_detector::GetInstance()->RemoveObserver(this);
+}
+
+void NetworkStateInformer::Init() {
+ UpdateState();
+ NetworkHandler::Get()->network_state_handler()->AddObserver(
+ this, FROM_HERE);
+
+ network_portal_detector::GetInstance()->AddAndFireObserver(this);
+
+ registrar_.Add(this,
+ chrome::NOTIFICATION_LOGIN_PROXY_CHANGED,
+ content::NotificationService::AllSources());
+ registrar_.Add(this,
+ chrome::NOTIFICATION_SESSION_STARTED,
+ content::NotificationService::AllSources());
+}
+
+void NetworkStateInformer::AddObserver(NetworkStateInformerObserver* observer) {
+ if (!observers_.HasObserver(observer))
+ observers_.AddObserver(observer);
+}
+
+void NetworkStateInformer::RemoveObserver(
+ NetworkStateInformerObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void NetworkStateInformer::DefaultNetworkChanged(const NetworkState* network) {
+ UpdateStateAndNotify();
+}
+
+void NetworkStateInformer::OnPortalDetectionCompleted(
+ const NetworkState* network,
+ const NetworkPortalDetector::CaptivePortalState& state) {
+ UpdateStateAndNotify();
+}
+
+void NetworkStateInformer::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ if (type == chrome::NOTIFICATION_SESSION_STARTED)
+ registrar_.RemoveAll();
+ else if (type == chrome::NOTIFICATION_LOGIN_PROXY_CHANGED)
+ SendStateToObservers(NetworkError::ERROR_REASON_PROXY_CONFIG_CHANGED);
+ else
+ NOTREACHED() << "Unknown notification: " << type;
+}
+
+void NetworkStateInformer::OnPortalDetected() {
+ UpdateStateAndNotify();
+}
+
+// static
+const char* NetworkStateInformer::StatusString(State state) {
+ switch (state) {
+ case OFFLINE:
+ return kNetworkStateOffline;
+ case ONLINE:
+ return kNetworkStateOnline;
+ case CAPTIVE_PORTAL:
+ return kNetworkStateCaptivePortal;
+ case CONNECTING:
+ return kNetworkStateConnecting;
+ case PROXY_AUTH_REQUIRED:
+ return kNetworkStateProxyAuthRequired;
+ default:
+ NOTREACHED();
+ return NULL;
+ }
+}
+
+bool NetworkStateInformer::UpdateState() {
+ const NetworkState* default_network =
+ NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
+ State new_state = GetStateForDefaultNetwork();
+ std::string new_network_path;
+ std::string new_network_type;
+ if (default_network) {
+ new_network_path = default_network->path();
+ new_network_type = default_network->type();
+ }
+
+ bool updated = (new_state != state_) ||
+ (new_network_path != network_path_) ||
+ (new_network_type != network_type_);
+ state_ = new_state;
+ network_path_ = new_network_path;
+ network_type_ = new_network_type;
+
+ if (updated && state_ == ONLINE) {
+ for (NetworkStateInformerObserver& observer : observers_)
+ observer.OnNetworkReady();
+ }
+
+ return updated;
+}
+
+void NetworkStateInformer::UpdateStateAndNotify() {
+ if (UpdateState())
+ SendStateToObservers(NetworkError::ERROR_REASON_NETWORK_STATE_CHANGED);
+ else
+ SendStateToObservers(NetworkError::ERROR_REASON_UPDATE);
+}
+
+void NetworkStateInformer::SendStateToObservers(
+ NetworkError::ErrorReason reason) {
+ for (NetworkStateInformerObserver& observer : observers_)
+ observer.UpdateState(reason);
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/network_state_informer.h b/chromium/chrome/browser/ui/webui/chromeos/login/network_state_informer.h
new file mode 100644
index 00000000000..23b75edb858
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/network_state_informer.h
@@ -0,0 +1,109 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_NETWORK_STATE_INFORMER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_NETWORK_STATE_INFORMER_H_
+
+#include <map>
+#include <string>
+
+#include "base/cancelable_callback.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "chrome/browser/chromeos/login/screens/network_error.h"
+#include "chrome/browser/chromeos/login/ui/captive_portal_window_proxy.h"
+#include "chromeos/network/network_state_handler_observer.h"
+#include "chromeos/network/portal_detector/network_portal_detector.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_service.h"
+
+namespace chromeos {
+
+// Class which observes network state changes and calls registered callbacks.
+// State is considered changed if connection or the active network has been
+// changed. Also, it answers to the requests about current network state.
+class NetworkStateInformer
+ : public chromeos::NetworkStateHandlerObserver,
+ public chromeos::NetworkPortalDetector::Observer,
+ public content::NotificationObserver,
+ public CaptivePortalWindowProxyDelegate,
+ public base::RefCounted<NetworkStateInformer> {
+ public:
+ enum State {
+ OFFLINE = 0,
+ ONLINE,
+ CAPTIVE_PORTAL,
+ CONNECTING,
+ PROXY_AUTH_REQUIRED,
+ UNKNOWN
+ };
+
+ class NetworkStateInformerObserver {
+ public:
+ NetworkStateInformerObserver() {}
+ virtual ~NetworkStateInformerObserver() {}
+
+ virtual void UpdateState(NetworkError::ErrorReason reason) = 0;
+ virtual void OnNetworkReady() {}
+ };
+
+ NetworkStateInformer();
+
+ void Init();
+
+ // Adds observer to be notified when network state has been changed.
+ void AddObserver(NetworkStateInformerObserver* observer);
+
+ // Removes observer.
+ void RemoveObserver(NetworkStateInformerObserver* observer);
+
+ // NetworkStateHandlerObserver implementation:
+ void DefaultNetworkChanged(const NetworkState* network) override;
+
+ // NetworkPortalDetector::Observer implementation:
+ void OnPortalDetectionCompleted(
+ const NetworkState* network,
+ const NetworkPortalDetector::CaptivePortalState& state) override;
+
+ // content::NotificationObserver implementation.
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ // CaptivePortalWindowProxyDelegate implementation:
+ void OnPortalDetected() override;
+
+ State state() const { return state_; }
+ std::string network_path() const { return network_path_; }
+ std::string network_type() const { return network_type_; }
+
+ static const char* StatusString(State state);
+
+ private:
+ friend class base::RefCounted<NetworkStateInformer>;
+
+ ~NetworkStateInformer() override;
+
+ bool UpdateState();
+
+ void UpdateStateAndNotify();
+
+ void SendStateToObservers(NetworkError::ErrorReason reason);
+
+ State state_;
+ std::string network_path_;
+ std::string network_type_;
+
+ base::ObserverList<NetworkStateInformerObserver> observers_;
+ content::NotificationRegistrar registrar_;
+
+ base::WeakPtrFactory<NetworkStateInformer> weak_ptr_factory_;
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_NETWORK_STATE_INFORMER_H_
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
new file mode 100644
index 00000000000..5a5c82409e6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc
@@ -0,0 +1,78 @@
+// 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/chromeos/login/oobe_display_chooser.h"
+
+#include <stdint.h>
+
+#include "ash/display/window_tree_host_manager.h"
+#include "ash/shell.h"
+#include "base/stl_util.h"
+#include "content/public/browser/browser_thread.h"
+#include "ui/display/display.h"
+#include "ui/display/manager/display_manager.h"
+#include "ui/display/screen.h"
+#include "ui/events/devices/device_data_manager.h"
+#include "ui/events/devices/touchscreen_device.h"
+
+using content::BrowserThread;
+
+namespace chromeos {
+
+namespace {
+
+bool TouchSupportAvailable(const display::Display& display) {
+ return display.touch_support() ==
+ display::Display::TouchSupport::TOUCH_SUPPORT_AVAILABLE;
+}
+
+// TODO(felixe): More context at crbug.com/738885
+const uint16_t kDeviceIds[] = {0x0457, 0x266e};
+
+} // namespace
+
+OobeDisplayChooser::OobeDisplayChooser() : weak_ptr_factory_(this) {}
+
+OobeDisplayChooser::~OobeDisplayChooser() {}
+
+void OobeDisplayChooser::TryToPlaceUiOnTouchDisplay() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ // Don't (potentially) queue a second task to run MoveToTouchDisplay if one
+ // already is queued.
+ if (weak_ptr_factory_.HasWeakPtrs())
+ return;
+
+ display::Display primary_display =
+ display::Screen::GetScreen()->GetPrimaryDisplay();
+
+ if (primary_display.is_valid() && !TouchSupportAvailable(primary_display)) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&OobeDisplayChooser::MoveToTouchDisplay,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+}
+
+void OobeDisplayChooser::MoveToTouchDisplay() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ const ui::DeviceDataManager* device_manager =
+ ui::DeviceDataManager::GetInstance();
+ for (const ui::TouchscreenDevice& device :
+ device_manager->GetTouchscreenDevices()) {
+ if (!base::ContainsValue(kDeviceIds, device.vendor_id))
+ continue;
+
+ int64_t display_id =
+ device_manager->GetTargetDisplayForTouchDevice(device.id);
+ if (display_id == display::kInvalidDisplayId)
+ continue;
+
+ ash::Shell::Get()->window_tree_host_manager()->SetPrimaryDisplayId(
+ display_id);
+ break;
+ }
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.h b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.h
new file mode 100644
index 00000000000..f7e2def693e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.h
@@ -0,0 +1,32 @@
+// 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_CHROMEOS_LOGIN_OOBE_DISPLAY_CHOOSER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_OOBE_DISPLAY_CHOOSER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+
+namespace chromeos {
+
+class OobeDisplayChooser {
+ public:
+ OobeDisplayChooser();
+ ~OobeDisplayChooser();
+
+ // Tries to put the OOBE UI on a connected touch display (if available).
+ // Must be called on the BrowserThread::UI thread.
+ void TryToPlaceUiOnTouchDisplay();
+
+ private:
+ void MoveToTouchDisplay();
+
+ base::WeakPtrFactory<OobeDisplayChooser> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(OobeDisplayChooser);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_OOBE_DISPLAY_CHOOSER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_browsertest.cc b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_browsertest.cc
new file mode 100644
index 00000000000..5f83b1b6079
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_browsertest.cc
@@ -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.
+
+#include "chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.h"
+
+#include "ash/shell.h"
+#include "base/run_loop.h"
+#include "chrome/browser/chromeos/login/test/oobe_base_test.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chromeos/chromeos_switches.h"
+#include "ui/display/display.h"
+#include "ui/display/manager/display_manager.h"
+#include "ui/display/manager/managed_display_info.h"
+#include "ui/display/screen.h"
+#include "ui/display/test/display_manager_test_api.h"
+
+namespace chromeos {
+
+namespace {
+
+class OobeDisplayChooserTest : public OobeBaseTest {
+ public:
+ OobeDisplayChooserTest() {}
+ ~OobeDisplayChooserTest() override {}
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ command_line->AppendSwitch(switches::kOobeSkipPostLogin);
+
+ OobeBaseTest::SetUpCommandLine(command_line);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OobeDisplayChooserTest);
+};
+
+display::DisplayManager* display_manager() {
+ return ash::Shell::Get()->display_manager();
+}
+
+int64_t GetPrimaryDisplayId() {
+ return display::Screen::GetScreen()->GetPrimaryDisplay().id();
+}
+
+} // namespace
+
+// Test that display removal does not trigger CHECK in
+// WindowTreeHostManager::GetPrimaryDisplayId().
+IN_PROC_BROWSER_TEST_F(OobeDisplayChooserTest,
+ RemovingPrimaryDisplaySanityCheck) {
+ display::ManagedDisplayInfo info1(1, "x-1", false);
+ info1.SetBounds(gfx::Rect(0, 0, 1280, 800));
+ display::ManagedDisplayInfo info2(2, "x-2", false);
+ std::vector<display::ManagedDisplayInfo> info_list;
+ info2.SetBounds(gfx::Rect(0, 1280, 1280, 800));
+ info_list.push_back(info1);
+ info_list.push_back(info2);
+
+ display_manager()->OnNativeDisplaysChanged(info_list);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, GetPrimaryDisplayId());
+
+ info_list.erase(info_list.begin());
+ display_manager()->OnNativeDisplaysChanged(info_list);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(2, GetPrimaryDisplayId());
+}
+
+} // namespace chromeos
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
new file mode 100644
index 00000000000..528fd1c27a6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser_unittest.cc
@@ -0,0 +1,121 @@
+// 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/chromeos/login/oobe_display_chooser.h"
+
+#include <memory>
+#include <vector>
+
+#include "ash/display/display_configuration_controller.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "base/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/display/display.h"
+#include "ui/display/display_observer.h"
+#include "ui/display/manager/chromeos/touchscreen_util.h"
+#include "ui/display/manager/display_manager.h"
+#include "ui/display/screen.h"
+#include "ui/display/test/display_manager_test_api.h"
+#include "ui/events/devices/device_data_manager.h"
+#include "ui/events/devices/touchscreen_device.h"
+
+namespace chromeos {
+
+namespace {
+
+class OobeDisplayChooserTest : public ash::test::AshTestBase {
+ public:
+ OobeDisplayChooserTest() : ash::test::AshTestBase() {}
+
+ int64_t GetPrimaryDisplay() {
+ return display::Screen::GetScreen()->GetPrimaryDisplay().id();
+ }
+
+ void UpdateTouchscreenDevices(const ui::TouchscreenDevice& touchscreen) {
+ std::vector<ui::TouchscreenDevice> devices{touchscreen};
+
+ ui::DeviceHotplugEventObserver* manager =
+ ui::DeviceDataManager::GetInstance();
+ manager->OnTouchscreenDevicesUpdated(devices);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OobeDisplayChooserTest);
+};
+
+const uint16_t kWhitelistedId = 0x266e;
+
+} // namespace
+
+TEST_F(OobeDisplayChooserTest, PreferTouchAsPrimary) {
+ // Setup 2 displays, second one is intended to be a touch display
+ std::vector<display::ManagedDisplayInfo> display_info;
+ display_info.push_back(
+ display::ManagedDisplayInfo::CreateFromSpecWithID("0+0-3000x2000", 1));
+ display_info.push_back(
+ display::ManagedDisplayInfo::CreateFromSpecWithID("3000+0-800x600", 2));
+ display_manager()->OnNativeDisplaysChanged(display_info);
+ base::RunLoop().RunUntilIdle();
+
+ // Make sure the non-touch display is primary
+ ash::Shell::Get()->window_tree_host_manager()->SetPrimaryDisplayId(1);
+
+ // Setup corresponding TouchscreenDevice object
+ ui::TouchscreenDevice touchscreen =
+ ui::TouchscreenDevice(1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL,
+ "Touchscreen", gfx::Size(800, 600), 1);
+ touchscreen.vendor_id = kWhitelistedId;
+ UpdateTouchscreenDevices(touchscreen);
+ base::RunLoop().RunUntilIdle();
+
+ // Associate touchscreen device with display
+ display_info[1].AddInputDevice(touchscreen.id);
+ display_info[1].set_touch_support(display::Display::TOUCH_SUPPORT_AVAILABLE);
+ display_manager()->OnNativeDisplaysChanged(display_info);
+ base::RunLoop().RunUntilIdle();
+
+ OobeDisplayChooser display_chooser;
+ EXPECT_EQ(1, GetPrimaryDisplay());
+ display_chooser.TryToPlaceUiOnTouchDisplay();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(2, GetPrimaryDisplay());
+}
+
+TEST_F(OobeDisplayChooserTest, DontSwitchFromTouch) {
+ // Setup 2 displays, second one is intended to be a touch display
+ std::vector<display::ManagedDisplayInfo> display_info;
+ display_info.push_back(
+ 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_manager()->OnNativeDisplaysChanged(display_info);
+ base::RunLoop().RunUntilIdle();
+
+ // Make sure the non-touch display is primary
+ ash::Shell::Get()->window_tree_host_manager()->SetPrimaryDisplayId(1);
+
+ // Setup corresponding TouchscreenDevice object
+ ui::TouchscreenDevice touchscreen =
+ ui::TouchscreenDevice(1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL,
+ "Touchscreen", gfx::Size(800, 600), 1);
+ touchscreen.vendor_id = kWhitelistedId;
+ UpdateTouchscreenDevices(touchscreen);
+ base::RunLoop().RunUntilIdle();
+
+ // Associate touchscreen device with display
+ display_info[1].AddInputDevice(touchscreen.id);
+ display_info[1].set_touch_support(display::Display::TOUCH_SUPPORT_AVAILABLE);
+ display_manager()->OnNativeDisplaysChanged(display_info);
+ base::RunLoop().RunUntilIdle();
+
+ OobeDisplayChooser display_chooser;
+ EXPECT_EQ(1, GetPrimaryDisplay());
+ display_chooser.TryToPlaceUiOnTouchDisplay();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, GetPrimaryDisplay());
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
new file mode 100644
index 00000000000..e11aa39063e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
@@ -0,0 +1,646 @@
+// 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/ui/webui/chromeos/login/oobe_ui.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "ash/wm/screen_dimmer.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen_view.h"
+#include "chrome/browser/chromeos/login/enrollment/enrollment_screen_view.h"
+#include "chrome/browser/chromeos/login/screens/error_screen.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host.h"
+#include "chrome/browser/chromeos/login/wizard_controller.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/browser/chromeos/settings/shutdown_policy_handler.h"
+#include "chrome/browser/chromeos/system/input_device_settings.h"
+#include "chrome/browser/extensions/signin/gaia_auth_extension_loader.h"
+#include "chrome/browser/extensions/tab_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/ash_util.h"
+#include "chrome/browser/ui/webui/about_ui.h"
+#include "chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/arc_kiosk_splash_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/controller_pairing_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/device_disabled_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/error_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/host_pairing_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/network_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
+#include "chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.h"
+#include "chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/update_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.h"
+#include "chrome/browser/ui/webui/options/chromeos/user_image_source.h"
+#include "chrome/browser/ui/webui/test_files_request_filter.h"
+#include "chrome/browser/ui/webui/theme_source.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chrome_unscaled_resources.h"
+#include "chrome/grit/component_extension_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/common/content_switches.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "ui/display/display.h"
+#include "ui/events/devices/input_device.h"
+#include "ui/events/devices/input_device_manager.h"
+
+namespace chromeos {
+
+namespace {
+
+const char* kKnownDisplayTypes[] = {OobeUI::kOobeDisplay,
+ OobeUI::kLoginDisplay,
+ OobeUI::kLockDisplay,
+ OobeUI::kUserAddingDisplay,
+ OobeUI::kAppLaunchSplashDisplay,
+ OobeUI::kArcKioskSplashDisplay};
+
+OobeScreen kDimOverlayScreenIds[] = {
+ OobeScreen::SCREEN_CONFIRM_PASSWORD,
+ OobeScreen::SCREEN_GAIA_SIGNIN,
+ OobeScreen::SCREEN_OOBE_ENROLLMENT,
+ OobeScreen::SCREEN_PASSWORD_CHANGED,
+ OobeScreen::SCREEN_USER_IMAGE_PICKER
+};
+
+const char kStringsJSPath[] = "strings.js";
+const char kLockJSPath[] = "lock.js";
+const char kLoginJSPath[] = "login.js";
+const char kOobeJSPath[] = "oobe.js";
+const char kKeyboardUtilsJSPath[] = "keyboard_utils.js";
+const char kCustomElementsHTMLPath[] = "custom_elements.html";
+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";
+const char kArcPlaystoreCSSPath[] = "playstore.css";
+const char kArcPlaystoreJSPath[] = "playstore.js";
+const char kArcPlaystoreLogoPath[] = "playstore.svg";
+const char kProductLogoPath[] = "product-logo.png";
+
+// Creates a WebUIDataSource for chrome://oobe
+content::WebUIDataSource* CreateOobeUIDataSource(
+ const base::DictionaryValue& localized_strings,
+ const std::string& display_type) {
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIOobeHost);
+ source->AddLocalizedStrings(localized_strings);
+ source->SetJsonPath(kStringsJSPath);
+
+ if (display_type == OobeUI::kOobeDisplay) {
+ source->SetDefaultResource(IDR_OOBE_HTML);
+ source->AddResourcePath(kOobeJSPath, IDR_OOBE_JS);
+ source->AddResourcePath(kCustomElementsHTMLPath,
+ 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::kShowNonViewMdLogin)) {
+ 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);
+ } else {
+ 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);
+ }
+ 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::kShowMdLogin) ||
+ command_line->HasSwitch(chromeos::switches::kShowNonViewMdLogin)) {
+ source->SetDefaultResource(IDR_MD_LOGIN_HTML);
+ source->AddResourcePath(kLoginJSPath, IDR_MD_LOGIN_JS);
+ } else {
+ source->SetDefaultResource(IDR_LOGIN_HTML);
+ source->AddResourcePath(kLoginJSPath, IDR_LOGIN_JS);
+ }
+ source->AddResourcePath(kCustomElementsHTMLPath,
+ IDR_CUSTOM_ELEMENTS_LOGIN_HTML);
+ source->AddResourcePath(kCustomElementsJSPath,
+ IDR_CUSTOM_ELEMENTS_LOGIN_JS);
+ source->AddResourcePath(kCustomElementsUserPodHTMLPath,
+ IDR_CUSTOM_ELEMENTS_USER_POD_HTML);
+ }
+
+ // Required for postprocessing of Goolge PlayStore Terms.
+ source->AddResourcePath(kArcPlaystoreCSSPath, IDR_ARC_SUPPORT_PLAYSTORE_CSS);
+ source->AddResourcePath(kArcPlaystoreJSPath, IDR_ARC_SUPPORT_PLAYSTORE_JS);
+ source->AddResourcePath(kArcPlaystoreLogoPath,
+ IDR_ARC_SUPPORT_PLAYSTORE_LOGO);
+
+ // Required in encryption migration screen.
+ source->AddResourcePath(kProductLogoPath, IDR_PRODUCT_LOGO_64);
+
+ source->AddResourcePath(kKeyboardUtilsJSPath, IDR_KEYBOARD_UTILS_JS);
+ source->OverrideContentSecurityPolicyChildSrc(
+ base::StringPrintf(
+ "child-src chrome://terms/ %s/;",
+ extensions::kGaiaAuthExtensionOrigin));
+ source->OverrideContentSecurityPolicyObjectSrc(
+ "object-src chrome:;");
+
+ // Serve deferred resources.
+ source->AddResourcePath(kEnrollmentHTMLPath, IDR_OOBE_ENROLLMENT_HTML);
+ source->AddResourcePath(kEnrollmentCSSPath, IDR_OOBE_ENROLLMENT_CSS);
+ source->AddResourcePath(kEnrollmentJSPath, IDR_OOBE_ENROLLMENT_JS);
+
+ // Only add a filter when runing as test.
+ const bool is_running_test = command_line->HasSwitch(::switches::kTestName) ||
+ command_line->HasSwitch(::switches::kTestType);
+ if (is_running_test)
+ source->SetRequestFilter(::test::GetTestFilesRequestFilter());
+
+ return source;
+}
+
+std::string GetDisplayType(const GURL& url) {
+ std::string path = url.path().size() ? url.path().substr(1) : "";
+ if (std::find(kKnownDisplayTypes,
+ kKnownDisplayTypes + arraysize(kKnownDisplayTypes),
+ path) == kKnownDisplayTypes + arraysize(kKnownDisplayTypes)) {
+ LOG(ERROR) << "Unknown display type '" << path << "'. Setting default.";
+ return OobeUI::kLoginDisplay;
+ }
+ return path;
+}
+
+bool IsRemoraRequisitioned() {
+ policy::DeviceCloudPolicyManagerChromeOS* policy_manager =
+ g_browser_process->platform_part()
+ ->browser_policy_connector_chromeos()
+ ->GetDeviceCloudPolicyManager();
+ return policy_manager && policy_manager->IsRemoraRequisition();
+}
+
+} // namespace
+
+// static
+const char OobeUI::kOobeDisplay[] = "oobe";
+const char OobeUI::kLoginDisplay[] = "login";
+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";
+
+OobeUI::OobeUI(content::WebUI* web_ui, const GURL& url)
+ : WebUIController(web_ui) {
+ display_type_ = GetDisplayType(url);
+
+ network_state_informer_ = new NetworkStateInformer();
+ network_state_informer_->Init();
+
+ js_calls_container = base::MakeUnique<JSCallsContainer>();
+
+ auto core_handler =
+ base::MakeUnique<CoreOobeHandler>(this, js_calls_container.get());
+ core_handler_ = core_handler.get();
+ AddWebUIHandler(std::move(core_handler));
+
+ auto network_dropdown_handler = base::MakeUnique<NetworkDropdownHandler>();
+ network_dropdown_handler_ = network_dropdown_handler.get();
+ AddWebUIHandler(std::move(network_dropdown_handler));
+
+ AddScreenHandler(base::MakeUnique<UpdateScreenHandler>());
+
+ if (display_type_ == kOobeDisplay)
+ AddScreenHandler(base::MakeUnique<NetworkScreenHandler>(core_handler_));
+
+ AddScreenHandler(base::MakeUnique<EnableDebuggingScreenHandler>());
+
+ AddScreenHandler(base::MakeUnique<EulaScreenHandler>(core_handler_));
+
+ AddScreenHandler(base::MakeUnique<ResetScreenHandler>());
+
+ AddScreenHandler(base::MakeUnique<KioskAutolaunchScreenHandler>());
+
+ AddScreenHandler(base::MakeUnique<KioskEnableScreenHandler>());
+
+ auto supervised_user_creation_screen_handler =
+ base::MakeUnique<SupervisedUserCreationScreenHandler>();
+ supervised_user_creation_screen_view_ =
+ supervised_user_creation_screen_handler.get();
+ AddScreenHandler(std::move(supervised_user_creation_screen_handler));
+
+ AddScreenHandler(base::MakeUnique<WrongHWIDScreenHandler>());
+
+ AddScreenHandler(base::MakeUnique<AutoEnrollmentCheckScreenHandler>());
+
+ AddScreenHandler(base::MakeUnique<HIDDetectionScreenHandler>(core_handler_));
+
+ AddScreenHandler(base::MakeUnique<ErrorScreenHandler>());
+ network_dropdown_handler_->AddObserver(GetView<ErrorScreenHandler>());
+
+ error_screen_.reset(new ErrorScreen(nullptr, GetView<ErrorScreenHandler>()));
+ ErrorScreen* error_screen = error_screen_.get();
+
+ AddScreenHandler(base::MakeUnique<EnrollmentScreenHandler>(
+ network_state_informer_, error_screen));
+
+ AddScreenHandler(
+ base::MakeUnique<TermsOfServiceScreenHandler>(core_handler_));
+
+ AddScreenHandler(base::MakeUnique<ArcTermsOfServiceScreenHandler>());
+
+ AddScreenHandler(base::MakeUnique<UserImageScreenHandler>());
+
+ AddScreenHandler(base::MakeUnique<UserBoardScreenHandler>());
+
+ AddScreenHandler(base::MakeUnique<GaiaScreenHandler>(
+ core_handler_, network_state_informer_));
+
+ auto signin_screen_handler = base::MakeUnique<SigninScreenHandler>(
+ network_state_informer_, error_screen, core_handler_,
+ GetView<GaiaScreenHandler>(), js_calls_container.get());
+ signin_screen_handler_ = signin_screen_handler.get();
+ AddWebUIHandler(std::move(signin_screen_handler));
+
+ AddScreenHandler(base::MakeUnique<AppLaunchSplashScreenHandler>(
+ network_state_informer_, error_screen));
+
+ AddScreenHandler(base::MakeUnique<ArcKioskSplashScreenHandler>());
+
+ if (display_type_ == kOobeDisplay) {
+ AddScreenHandler(base::MakeUnique<ControllerPairingScreenHandler>());
+
+ AddScreenHandler(base::MakeUnique<HostPairingScreenHandler>());
+ }
+
+ AddScreenHandler(base::MakeUnique<DeviceDisabledScreenHandler>());
+
+ AddScreenHandler(base::MakeUnique<EncryptionMigrationScreenHandler>());
+
+ // Initialize KioskAppMenuHandler. Note that it is NOT a screen handler.
+ auto kiosk_app_menu_handler =
+ base::MakeUnique<KioskAppMenuHandler>(network_state_informer_);
+ kiosk_app_menu_handler_ = kiosk_app_menu_handler.get();
+ web_ui->AddMessageHandler(std::move(kiosk_app_menu_handler));
+
+ base::DictionaryValue localized_strings;
+ GetLocalizedStrings(&localized_strings);
+
+ Profile* profile = Profile::FromWebUI(web_ui);
+ // Set up the chrome://theme/ source, for Chrome logo.
+ ThemeSource* theme = new ThemeSource(profile);
+ content::URLDataSource::Add(profile, theme);
+
+ // Set up the chrome://terms/ data source, for EULA content.
+ AboutUIHTMLSource* about_source =
+ new AboutUIHTMLSource(chrome::kChromeUITermsHost, profile);
+ content::URLDataSource::Add(profile, about_source);
+
+ // Set up the chrome://oobe/ source.
+ content::WebUIDataSource* html_source =
+ CreateOobeUIDataSource(localized_strings, display_type_);
+ content::WebUIDataSource::Add(profile, html_source);
+ network_element::AddLocalizedStrings(html_source);
+
+ // Set up the chrome://userimage/ source.
+ options::UserImageSource* user_image_source =
+ new options::UserImageSource();
+ content::URLDataSource::Add(profile, user_image_source);
+
+ // TabHelper is required for OOBE webui to make webview working on it.
+ content::WebContents* contents = web_ui->GetWebContents();
+ extensions::TabHelper::CreateForWebContents(contents);
+
+ // TODO(felixe): Display iteration and primary display selection not supported
+ // in Mash. See http://crbug.com/720917.
+ if (!ash_util::IsRunningInMash() && IsRemoraRequisitioned())
+ oobe_display_chooser_ = base::MakeUnique<OobeDisplayChooser>();
+}
+
+OobeUI::~OobeUI() {
+ network_dropdown_handler_->RemoveObserver(GetView<ErrorScreenHandler>());
+ if (ash_util::IsRunningInMash()) {
+ // TODO: Ash needs to expose screen dimming api. See
+ // http://crbug.com/646034.
+ NOTIMPLEMENTED();
+ }
+}
+
+CoreOobeView* OobeUI::GetCoreOobeView() {
+ return core_handler_;
+}
+
+NetworkView* OobeUI::GetNetworkView() {
+ return GetView<NetworkScreenHandler>();
+}
+
+EulaView* OobeUI::GetEulaView() {
+ return GetView<EulaScreenHandler>();
+}
+
+UpdateView* OobeUI::GetUpdateView() {
+ return GetView<UpdateScreenHandler>();
+}
+
+EnableDebuggingScreenView* OobeUI::GetEnableDebuggingScreenView() {
+ return GetView<EnableDebuggingScreenHandler>();
+}
+
+EnrollmentScreenView* OobeUI::GetEnrollmentScreenView() {
+ return GetView<EnrollmentScreenHandler>();
+}
+
+ResetView* OobeUI::GetResetView() {
+ return GetView<ResetScreenHandler>();
+}
+
+KioskAutolaunchScreenView* OobeUI::GetKioskAutolaunchScreenView() {
+ return GetView<KioskAutolaunchScreenHandler>();
+}
+
+KioskEnableScreenView* OobeUI::GetKioskEnableScreenView() {
+ return GetView<KioskEnableScreenHandler>();
+}
+
+TermsOfServiceScreenView* OobeUI::GetTermsOfServiceScreenView() {
+ return GetView<TermsOfServiceScreenHandler>();
+}
+
+ArcTermsOfServiceScreenView* OobeUI::GetArcTermsOfServiceScreenView() {
+ return GetView<ArcTermsOfServiceScreenHandler>();
+}
+
+WrongHWIDScreenView* OobeUI::GetWrongHWIDScreenView() {
+ return GetView<WrongHWIDScreenHandler>();
+}
+
+AutoEnrollmentCheckScreenView* OobeUI::GetAutoEnrollmentCheckScreenView() {
+ return GetView<AutoEnrollmentCheckScreenHandler>();
+}
+
+HIDDetectionView* OobeUI::GetHIDDetectionView() {
+ return GetView<HIDDetectionScreenHandler>();
+}
+
+ControllerPairingScreenView* OobeUI::GetControllerPairingScreenView() {
+ return GetView<ControllerPairingScreenHandler>();
+}
+
+HostPairingScreenView* OobeUI::GetHostPairingScreenView() {
+ return GetView<HostPairingScreenHandler>();
+}
+
+DeviceDisabledScreenView* OobeUI::GetDeviceDisabledScreenView() {
+ return GetView<DeviceDisabledScreenHandler>();
+}
+
+EncryptionMigrationScreenView* OobeUI::GetEncryptionMigrationScreenView() {
+ return GetView<EncryptionMigrationScreenHandler>();
+}
+
+UserImageView* OobeUI::GetUserImageView() {
+ return GetView<UserImageScreenHandler>();
+}
+
+ErrorScreen* OobeUI::GetErrorScreen() {
+ return error_screen_.get();
+}
+
+SupervisedUserCreationScreenHandler*
+OobeUI::GetSupervisedUserCreationScreenView() {
+ return supervised_user_creation_screen_view_;
+}
+
+GaiaView* OobeUI::GetGaiaScreenView() {
+ return GetView<GaiaScreenHandler>();
+}
+
+UserBoardView* OobeUI::GetUserBoardView() {
+ return GetView<UserBoardScreenHandler>();
+}
+
+void OobeUI::OnShutdownPolicyChanged(bool reboot_on_shutdown) {
+ core_handler_->UpdateShutdownAndRebootVisibility(reboot_on_shutdown);
+}
+
+AppLaunchSplashScreenView* OobeUI::GetAppLaunchSplashScreenView() {
+ return GetView<AppLaunchSplashScreenHandler>();
+}
+
+ArcKioskSplashScreenView* OobeUI::GetArcKioskSplashScreenView() {
+ return GetView<ArcKioskSplashScreenHandler>();
+}
+
+void OobeUI::GetLocalizedStrings(base::DictionaryValue* localized_strings) {
+ for (BaseWebUIHandler* handler : webui_handlers_)
+ handler->GetLocalizedStrings(localized_strings);
+ const std::string& app_locale = g_browser_process->GetApplicationLocale();
+ webui::SetLoadTimeDataDefaults(app_locale, localized_strings);
+ kiosk_app_menu_handler_->GetLocalizedStrings(localized_strings);
+
+#if defined(GOOGLE_CHROME_BUILD)
+ localized_strings->SetString("buildType", "chrome");
+#else
+ localized_strings->SetString("buildType", "chromium");
+#endif
+
+ // If we're not doing boot animation then WebUI should trigger
+ // wallpaper load on boot.
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableBootAnimation)) {
+ localized_strings->SetString("bootIntoWallpaper", "on");
+ } else {
+ localized_strings->SetString("bootIntoWallpaper", "off");
+ }
+
+ bool keyboard_driven_oobe =
+ system::InputDeviceSettings::Get()->ForceKeyboardDrivenUINavigation();
+ localized_strings->SetString("highlightStrength",
+ keyboard_driven_oobe ? "strong" : "normal");
+
+ bool new_kiosk_ui = KioskAppMenuHandler::EnableNewKioskUI();
+ localized_strings->SetString("newKioskUI", new_kiosk_ui ? "on" : "off");
+ oobe_ui_md_mode_ =
+ g_browser_process->local_state()->GetBoolean(prefs::kOobeMdMode);
+ localized_strings->SetString("newOobeUI", oobe_ui_md_mode_ ? "on" : "off");
+}
+
+void OobeUI::AddWebUIHandler(std::unique_ptr<BaseWebUIHandler> handler) {
+ webui_handlers_.push_back(handler.get());
+ web_ui()->AddMessageHandler(std::move(handler));
+}
+
+void OobeUI::AddScreenHandler(std::unique_ptr<BaseScreenHandler> handler) {
+ webui_handlers_.push_back(handler.get());
+ screen_handlers_.push_back(handler.get());
+ web_ui()->AddMessageHandler(std::move(handler));
+}
+
+void OobeUI::InitializeHandlers() {
+ ready_ = true;
+ for (size_t i = 0; i < ready_callbacks_.size(); ++i)
+ ready_callbacks_[i].Run();
+ ready_callbacks_.clear();
+
+ // Notify 'initialize' for synchronously loaded screens.
+ for (BaseWebUIHandler* handler : webui_handlers_) {
+ if (handler->async_assets_load_id().empty())
+ handler->InitializeBase();
+ }
+
+ // Instantiate the ShutdownPolicyHandler.
+ shutdown_policy_handler_.reset(
+ new ShutdownPolicyHandler(CrosSettings::Get(), this));
+
+ // Trigger an initial update.
+ shutdown_policy_handler_->NotifyDelegateWithShutdownPolicy();
+}
+
+void OobeUI::CurrentScreenChanged(OobeScreen new_screen) {
+ previous_screen_ = current_screen_;
+
+ const bool should_dim =
+ std::find(std::begin(kDimOverlayScreenIds),
+ std::end(kDimOverlayScreenIds),
+ new_screen) != std::end(kDimOverlayScreenIds);
+ if (!ash_util::IsRunningInMash()) {
+ if (!screen_dimmer_) {
+ screen_dimmer_ = base::MakeUnique<ash::ScreenDimmer>(
+ ash::ScreenDimmer::Container::LOCK_SCREEN);
+ }
+ screen_dimmer_->set_at_bottom(true);
+ screen_dimmer_->SetDimming(should_dim);
+ } else {
+ // TODO: Ash needs to expose screen dimming api. See
+ // http://crbug.com/646034.
+ NOTIMPLEMENTED();
+ }
+
+ current_screen_ = new_screen;
+ for (Observer& observer : observer_list_)
+ observer.OnCurrentScreenChanged(current_screen_, new_screen);
+}
+
+void OobeUI::OnScreenAssetsLoaded(const std::string& async_assets_load_id) {
+ DCHECK(!async_assets_load_id.empty());
+
+ for (BaseWebUIHandler* handler : webui_handlers_) {
+ if (handler->async_assets_load_id() == async_assets_load_id)
+ handler->InitializeBase();
+ }
+}
+
+bool OobeUI::IsJSReady(const base::Closure& display_is_ready_callback) {
+ if (!ready_)
+ ready_callbacks_.push_back(display_is_ready_callback);
+ return ready_;
+}
+
+void OobeUI::ShowOobeUI(bool show) {
+ core_handler_->ShowOobeUI(show);
+
+ if (show && oobe_display_chooser_)
+ oobe_display_chooser_->TryToPlaceUiOnTouchDisplay();
+}
+
+void OobeUI::ShowSigninScreen(const LoginScreenContext& context,
+ SigninScreenHandlerDelegate* delegate,
+ NativeWindowDelegate* native_window_delegate) {
+ // Check our device mode.
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ if (connector->GetDeviceMode() == policy::DEVICE_MODE_LEGACY_RETAIL_MODE) {
+ // If we're in legacy retail mode, the best thing we can do is launch the
+ // new offline demo mode.
+ LoginDisplayHost::default_host()->StartDemoAppLaunch();
+ return;
+ }
+
+ signin_screen_handler_->SetDelegate(delegate);
+ signin_screen_handler_->SetNativeWindowDelegate(native_window_delegate);
+
+ LoginScreenContext actual_context(context);
+ actual_context.set_oobe_ui(core_handler_->show_oobe_ui());
+ signin_screen_handler_->Show(actual_context);
+}
+
+void OobeUI::ResetSigninScreenHandlerDelegate() {
+ signin_screen_handler_->SetDelegate(nullptr);
+ signin_screen_handler_->SetNativeWindowDelegate(nullptr);
+}
+
+
+void OobeUI::AddObserver(Observer* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void OobeUI::RemoveObserver(Observer* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
+void OobeUI::UpdateLocalizedStringsIfNeeded() {
+ if (oobe_ui_md_mode_ ==
+ g_browser_process->local_state()->GetBoolean(prefs::kOobeMdMode)) {
+ return;
+ }
+
+ base::DictionaryValue localized_strings;
+ GetLocalizedStrings(&localized_strings);
+ static_cast<CoreOobeView*>(core_handler_)->ReloadContent(localized_strings);
+}
+
+void OobeUI::OnDisplayConfigurationChanged() {
+ if (oobe_display_chooser_)
+ oobe_display_chooser_->TryToPlaceUiOnTouchDisplay();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.h b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
new file mode 100644
index 00000000000..a29a94c7c02
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
@@ -0,0 +1,259 @@
+// Copyright (c) 2011 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_CHROMEOS_LOGIN_OOBE_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_OOBE_UI_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/browser/chromeos/settings/shutdown_policy_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace ash {
+class ScreenDimmer;
+}
+
+namespace base {
+class DictionaryValue;
+} // namespace base
+
+namespace chromeos {
+class AppLaunchSplashScreenView;
+class ArcKioskSplashScreenView;
+class ArcTermsOfServiceScreenView;
+class AutoEnrollmentCheckScreenView;
+class BaseScreenHandler;
+class ControllerPairingScreenView;
+class CoreOobeView;
+class DeviceDisabledScreenView;
+class EnableDebuggingScreenView;
+class EncryptionMigrationScreenView;
+class EnrollmentScreenView;
+class EulaView;
+class ErrorScreen;
+class GaiaView;
+class HIDDetectionView;
+class HostPairingScreenView;
+class KioskAppMenuHandler;
+class KioskAutolaunchScreenView;
+class KioskEnableScreenView;
+class LoginScreenContext;
+class NativeWindowDelegate;
+class NetworkDropdownHandler;
+class NetworkStateInformer;
+class NetworkView;
+class OobeDisplayChooser;
+class SigninScreenHandler;
+class SigninScreenHandlerDelegate;
+class SupervisedUserCreationScreenHandler;
+class ResetView;
+class TermsOfServiceScreenView;
+class UserBoardView;
+class UserImageView;
+class UpdateView;
+class WrongHWIDScreenView;
+
+// A custom WebUI that defines datasource for out-of-box-experience (OOBE) UI:
+// - welcome screen (setup language/keyboard/network).
+// - eula screen (CrOS (+ OEM) EULA content/TPM password/crash reporting).
+// - update screen.
+class OobeUI : public content::WebUIController,
+ public ShutdownPolicyHandler::Delegate {
+ public:
+ // List of known types of OobeUI. Type added as path in chrome://oobe url, for
+ // example chrome://oobe/user-adding.
+ static const char kOobeDisplay[];
+ static const char kLoginDisplay[];
+ static const char kLockDisplay[];
+ static const char kUserAddingDisplay[];
+ static const char kAppLaunchSplashDisplay[];
+ static const char kArcKioskSplashDisplay[];
+
+ class Observer {
+ public:
+ Observer() {}
+ virtual void OnCurrentScreenChanged(OobeScreen current_screen,
+ OobeScreen new_screen) = 0;
+
+ protected:
+ virtual ~Observer() {}
+ DISALLOW_COPY(Observer);
+ };
+
+ OobeUI(content::WebUI* web_ui, const GURL& url);
+ ~OobeUI() override;
+
+ CoreOobeView* GetCoreOobeView();
+ NetworkView* GetNetworkView();
+ EulaView* GetEulaView();
+ UpdateView* GetUpdateView();
+ EnableDebuggingScreenView* GetEnableDebuggingScreenView();
+ EnrollmentScreenView* GetEnrollmentScreenView();
+ ResetView* GetResetView();
+ KioskAutolaunchScreenView* GetKioskAutolaunchScreenView();
+ KioskEnableScreenView* GetKioskEnableScreenView();
+ TermsOfServiceScreenView* GetTermsOfServiceScreenView();
+ ArcTermsOfServiceScreenView* GetArcTermsOfServiceScreenView();
+ UserImageView* GetUserImageView();
+ ErrorScreen* GetErrorScreen();
+ WrongHWIDScreenView* GetWrongHWIDScreenView();
+ AutoEnrollmentCheckScreenView* GetAutoEnrollmentCheckScreenView();
+ SupervisedUserCreationScreenHandler* GetSupervisedUserCreationScreenView();
+ AppLaunchSplashScreenView* GetAppLaunchSplashScreenView();
+ ArcKioskSplashScreenView* GetArcKioskSplashScreenView();
+ HIDDetectionView* GetHIDDetectionView();
+ ControllerPairingScreenView* GetControllerPairingScreenView();
+ HostPairingScreenView* GetHostPairingScreenView();
+ DeviceDisabledScreenView* GetDeviceDisabledScreenView();
+ EncryptionMigrationScreenView* GetEncryptionMigrationScreenView();
+ GaiaView* GetGaiaScreenView();
+ UserBoardView* GetUserBoardView();
+
+ // ShutdownPolicyHandler::Delegate
+ void OnShutdownPolicyChanged(bool reboot_on_shutdown) override;
+
+ // Collects localized strings from the owned handlers.
+ void GetLocalizedStrings(base::DictionaryValue* localized_strings);
+
+ // Initializes the handlers.
+ void InitializeHandlers();
+
+ // Called when the screen has changed.
+ void CurrentScreenChanged(OobeScreen screen);
+
+ // Invoked after the async assets load. The screen handler that has the same
+ // async assets load id will be initialized.
+ void OnScreenAssetsLoaded(const std::string& async_assets_load_id);
+
+ bool IsJSReady(const base::Closure& display_is_ready_callback);
+
+ // Shows or hides OOBE UI elements.
+ void ShowOobeUI(bool show);
+
+ // Shows the signin screen.
+ void ShowSigninScreen(const LoginScreenContext& context,
+ SigninScreenHandlerDelegate* delegate,
+ NativeWindowDelegate* native_window_delegate);
+
+ // Resets the delegate set in ShowSigninScreen.
+ void ResetSigninScreenHandlerDelegate();
+
+ // Add and remove observers for screen change events.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ OobeScreen current_screen() const { return current_screen_; }
+
+ OobeScreen previous_screen() const { return previous_screen_; }
+
+ const std::string& display_type() const { return display_type_; }
+
+ SigninScreenHandler* signin_screen_handler() {
+ return signin_screen_handler_;
+ }
+
+ NetworkStateInformer* network_state_informer_for_test() const {
+ return network_state_informer_.get();
+ }
+
+ // Does ReloadContent() if needed (for example, if material design mode has
+ // changed).
+ void UpdateLocalizedStringsIfNeeded();
+
+ // Re-evaluate OOBE display placement.
+ void OnDisplayConfigurationChanged();
+
+ private:
+ // Lookup a view by its statically registered OobeScreen.
+ template <typename TView>
+ TView* GetView() {
+ OobeScreen expected_screen = TView::kScreenId;
+ for (BaseScreenHandler* handler : screen_handlers_) {
+ if (expected_screen == handler->oobe_screen())
+ return static_cast<TView*>(handler);
+ }
+
+ NOTREACHED() << "Unable to find handler for screen "
+ << GetOobeScreenName(expected_screen);
+ return nullptr;
+ }
+
+ void AddWebUIHandler(std::unique_ptr<BaseWebUIHandler> handler);
+ void AddScreenHandler(std::unique_ptr<BaseScreenHandler> handler);
+
+ // Type of UI.
+ std::string display_type_;
+
+ // Reference to NetworkStateInformer that handles changes in network
+ // state.
+ scoped_refptr<NetworkStateInformer> network_state_informer_;
+
+ // Reference to CoreOobeHandler that handles common requests of Oobe page.
+ CoreOobeHandler* core_handler_ = nullptr;
+
+ // Reference to NetworkDropdownHandler that handles interaction with
+ // network dropdown.
+ NetworkDropdownHandler* network_dropdown_handler_ = nullptr;
+
+ SupervisedUserCreationScreenHandler* supervised_user_creation_screen_view_ =
+ nullptr;
+ // Reference to SigninScreenHandler that handles sign-in screen requests and
+ // forwards calls from native code to JS side.
+ SigninScreenHandler* signin_screen_handler_ = nullptr;
+
+ std::vector<BaseWebUIHandler*> webui_handlers_; // Non-owning pointers.
+ std::vector<BaseScreenHandler*> screen_handlers_; // Non-owning pointers.
+
+ KioskAppMenuHandler* kiosk_app_menu_handler_ =
+ nullptr; // Non-owning pointers.
+
+ std::unique_ptr<ErrorScreen> error_screen_;
+
+ // Id of the current oobe/login screen.
+ OobeScreen current_screen_ = OobeScreen::SCREEN_UNKNOWN;
+
+ // Id of the previous oobe/login screen.
+ OobeScreen previous_screen_ = OobeScreen::SCREEN_UNKNOWN;
+
+ // Flag that indicates whether JS part is fully loaded and ready to accept
+ // calls.
+ bool ready_ = false;
+
+ // This flag stores material-design mode (on/off) of currently displayed UI.
+ // If different version of UI is required, UI is updated.
+ bool oobe_ui_md_mode_ = false;
+
+ // Callbacks to notify when JS part is fully loaded and ready to accept calls.
+ std::vector<base::Closure> ready_callbacks_;
+
+ // List of registered observers.
+ base::ObserverList<Observer> observer_list_;
+
+ // Observer of CrosSettings watching the kRebootOnShutdown policy.
+ std::unique_ptr<ShutdownPolicyHandler> shutdown_policy_handler_;
+
+ std::unique_ptr<ash::ScreenDimmer> screen_dimmer_;
+
+ std::unique_ptr<OobeDisplayChooser> oobe_display_chooser_;
+
+ // Store the deferred JS calls before the screen handler instance is
+ // initialized.
+ std::unique_ptr<JSCallsContainer> js_calls_container;
+
+ DISALLOW_COPY_AND_ASSIGN(OobeUI);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_OOBE_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
new file mode 100644
index 00000000000..e786e4329b3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
@@ -0,0 +1,120 @@
+// 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/ui/webui/chromeos/login/reset_screen_handler.h"
+
+#include <string>
+
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/login/help_app_launcher.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/browser/chromeos/login/screens/reset_screen.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/dbus/session_manager_client.h"
+#include "components/login/localized_values_builder.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/strings/grit/components_strings.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.ResetScreen";
+
+} // namespace
+
+namespace chromeos {
+
+ResetScreenHandler::ResetScreenHandler() : BaseScreenHandler(kScreenId) {
+ set_call_js_prefix(kJsScreenPath);
+}
+
+ResetScreenHandler::~ResetScreenHandler() {
+ if (screen_)
+ screen_->OnViewDestroyed(this);
+}
+
+void ResetScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+ ShowScreen(kScreenId);
+}
+
+void ResetScreenHandler::Hide() {
+}
+
+void ResetScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("resetScreenTitle", IDS_RESET_SCREEN_TITLE);
+ builder->Add("resetScreenAccessibleTitle", IDS_RESET_SCREEN_TITLE);
+ builder->Add("resetScreenIconTitle", IDS_RESET_SCREEN_ICON_TITLE);
+ builder->Add("resetScreenIllustrationTitle",
+ IDS_RESET_SCREEN_ILLUSTRATION_TITLE);
+ builder->Add("cancelButton", IDS_CANCEL);
+
+ builder->Add("resetButtonRestart", IDS_RELAUNCH_BUTTON);
+ builder->Add("resetButtonPowerwash", IDS_RESET_SCREEN_POWERWASH);
+ builder->Add("resetButtonPowerwashAndRollback",
+ IDS_RESET_SCREEN_POWERWASH_AND_REVERT);
+
+ builder->Add("resetWarningDataDetails",
+ IDS_RESET_SCREEN_WARNING_DETAILS_DATA);
+ builder->Add("resetRestartMessage", IDS_RESET_SCREEN_RESTART_MSG);
+ builder->AddF("resetRevertPromise",
+ IDS_RESET_SCREEN_PREPARING_REVERT_PROMISE,
+ IDS_SHORT_PRODUCT_NAME);
+ builder->AddF("resetRevertSpinnerMessage",
+ IDS_RESET_SCREEN_PREPARING_REVERT_SPINNER_MESSAGE,
+ IDS_SHORT_PRODUCT_NAME);
+
+ // Variants for screen title.
+ builder->AddF("resetWarningTitle",
+ IDS_RESET_SCREEN_WARNING_MSG,
+ IDS_SHORT_PRODUCT_NAME);
+
+ // Variants for screen message.
+ builder->AddF("resetPowerwashWarningDetails",
+ IDS_RESET_SCREEN_WARNING_POWERWASH_MSG,
+ IDS_SHORT_PRODUCT_NAME);
+ builder->AddF("resetPowerwashRollbackWarningDetails",
+ IDS_RESET_SCREEN_WARNING_POWERWASH_AND_ROLLBACK_MSG,
+ IDS_SHORT_PRODUCT_NAME);
+
+ builder->Add("confirmPowerwashTitle", IDS_RESET_SCREEN_POPUP_POWERWASH_TITLE);
+ builder->Add("confirmRollbackTitle", IDS_RESET_SCREEN_POPUP_ROLLBACK_TITLE);
+ builder->Add("confirmPowerwashMessage",
+ IDS_RESET_SCREEN_POPUP_POWERWASH_TEXT);
+ builder->Add("confirmRollbackMessage", IDS_RESET_SCREEN_POPUP_ROLLBACK_TEXT);
+ builder->Add("confirmResetButton", IDS_RESET_SCREEN_POPUP_CONFIRM_BUTTON);
+}
+
+// static
+void ResetScreenHandler::RegisterPrefs(PrefRegistrySimple* registry) {
+ registry->RegisterBooleanPref(prefs::kFactoryResetRequested, false);
+}
+
+void ResetScreenHandler::Initialize() {
+ if (!page_is_ready())
+ return;
+
+ if (show_on_init_) {
+ Show();
+ show_on_init_ = false;
+ }
+}
+
+void ResetScreenHandler::Bind(ResetScreen* screen) {
+ screen_ = screen;
+ BaseScreenHandler::SetBaseScreen(screen_);
+}
+
+void ResetScreenHandler::Unbind() {
+ screen_ = nullptr;
+ BaseScreenHandler::SetBaseScreen(nullptr);
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h
new file mode 100644
index 00000000000..ff9544394ac
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h
@@ -0,0 +1,50 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_RESET_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_RESET_SCREEN_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/screens/reset_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "content/public/browser/web_ui.h"
+
+class PrefRegistrySimple;
+
+namespace chromeos {
+
+// WebUI implementation of ResetScreenActor.
+class ResetScreenHandler : public ResetView,
+ public BaseScreenHandler {
+ public:
+ ResetScreenHandler();
+ ~ResetScreenHandler() override;
+
+ // ResetView implementation:
+ void Bind(ResetScreen* screen) override;
+ void Unbind() override;
+ void Show() override;
+ void Hide() override;
+
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void Initialize() override;
+
+ // Registers Local State preferences.
+ static void RegisterPrefs(PrefRegistrySimple* registry);
+
+ private:
+ ResetScreen* screen_ = nullptr;
+
+ // If true, Initialize() will call Show().
+ bool show_on_init_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(ResetScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_RESET_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/screenlock_icon_provider.cc b/chromium/chrome/browser/ui/webui/chromeos/login/screenlock_icon_provider.cc
new file mode 100644
index 00000000000..5f9d5b20dad
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/screenlock_icon_provider.cc
@@ -0,0 +1,33 @@
+// 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/chromeos/login/screenlock_icon_provider.h"
+
+namespace chromeos {
+
+ScreenlockIconProvider::ScreenlockIconProvider() {}
+
+ScreenlockIconProvider::~ScreenlockIconProvider() {}
+
+void ScreenlockIconProvider::AddIcon(const std::string& username,
+ const gfx::Image& icon) {
+ user_icon_map_[username] = icon;
+}
+
+void ScreenlockIconProvider::RemoveIcon(const std::string& username) {
+ if (user_icon_map_.find(username) != user_icon_map_.end())
+ user_icon_map_.erase(username);
+}
+
+gfx::Image ScreenlockIconProvider::GetIcon(const std::string& username) {
+ if (user_icon_map_.find(username) == user_icon_map_.end())
+ return gfx::Image();
+ return user_icon_map_[username];
+}
+
+void ScreenlockIconProvider::Clear() {
+ user_icon_map_.clear();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/screenlock_icon_provider.h b/chromium/chrome/browser/ui/webui/chromeos/login/screenlock_icon_provider.h
new file mode 100644
index 00000000000..013fd336650
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/screenlock_icon_provider.h
@@ -0,0 +1,46 @@
+// 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_CHROMEOS_LOGIN_SCREENLOCK_ICON_PROVIDER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_SCREENLOCK_ICON_PROVIDER_H_
+
+#include <map>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/gfx/image/image.h"
+
+namespace chromeos {
+
+// Stores icon images used by the screenlockPrivate API. This class is
+// separate from ScreenlockIconSource for finer memory management.
+class ScreenlockIconProvider
+ : public base::SupportsWeakPtr<ScreenlockIconProvider> {
+ public:
+ ScreenlockIconProvider();
+ ~ScreenlockIconProvider();
+
+ // Adds an icon image for |username| to be stored.
+ void AddIcon(const std::string& username, const gfx::Image& icon);
+
+ // Removes icon image for |username|.
+ void RemoveIcon(const std::string& username);
+
+ // Returns the icon image set for |username|. If no icon is found, then
+ // this function returns an empty image.
+ gfx::Image GetIcon(const std::string& username);
+
+ // Removes all stored icon images.
+ void Clear();
+
+ private:
+ // Map of icons for the user pod buttons set by screenlockPrivate.showButton.
+ std::map<std::string, gfx::Image> user_icon_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScreenlockIconProvider);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_SCREENLOCK_ICON_PROVIDER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/screenlock_icon_source.cc b/chromium/chrome/browser/ui/webui/chromeos/login/screenlock_icon_source.cc
new file mode 100644
index 00000000000..bf07b1d5348
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/screenlock_icon_source.cc
@@ -0,0 +1,71 @@
+// 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/chromeos/login/screenlock_icon_source.h"
+
+#include "chrome/browser/chromeos/login/lock/screen_locker.h"
+#include "chrome/browser/ui/webui/chromeos/login/screenlock_icon_provider.h"
+#include "chrome/common/url_constants.h"
+#include "net/base/escape.h"
+
+namespace {
+
+gfx::Image GetDefaultIcon() {
+ return gfx::Image();
+}
+
+} // namespace
+
+namespace chromeos {
+
+////////////////////////////////////////////////////////////////////////////////
+// ScreenlockIconSource
+
+ScreenlockIconSource::ScreenlockIconSource(
+ base::WeakPtr<ScreenlockIconProvider> icon_provider)
+ : icon_provider_(icon_provider) {
+}
+
+ScreenlockIconSource::~ScreenlockIconSource() {}
+
+std::string ScreenlockIconSource::GetSource() const {
+ return std::string(chrome::kChromeUIScreenlockIconHost);
+}
+
+void ScreenlockIconSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ if (!icon_provider_) {
+ callback.Run(GetDefaultIcon().As1xPNGBytes().get());
+ return;
+ }
+
+ GURL url(chrome::kChromeUIScreenlockIconURL + path);
+ std::string username = net::UnescapeURLComponent(
+ url.path().substr(1),
+ net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS |
+ net::UnescapeRule::PATH_SEPARATORS | net::UnescapeRule::SPACES);
+
+ gfx::Image image = icon_provider_->GetIcon(username);
+ if (image.IsEmpty()) {
+ callback.Run(GetDefaultIcon().As1xPNGBytes().get());
+ return;
+ }
+
+ callback.Run(image.As1xPNGBytes().get());
+}
+
+std::string ScreenlockIconSource::GetMimeType(const std::string&) const {
+ return "image/png";
+}
+
+// static.
+std::string ScreenlockIconSource::GetIconURLForUser(
+ const std::string& username) {
+ return std::string(chrome::kChromeUIScreenlockIconURL) +
+ net::EscapePath(username);
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/screenlock_icon_source.h b/chromium/chrome/browser/ui/webui/chromeos/login/screenlock_icon_source.h
new file mode 100644
index 00000000000..98e86f8d8d2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/screenlock_icon_source.h
@@ -0,0 +1,44 @@
+// 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_CHROMEOS_LOGIN_SCREENLOCK_ICON_SOURCE_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_SCREENLOCK_ICON_SOURCE_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/url_data_source.h"
+
+namespace chromeos {
+
+class ScreenlockIconProvider;
+
+// A URL data source that serves icon images for the screenlockPrivate API.
+class ScreenlockIconSource : public content::URLDataSource {
+ public:
+ explicit ScreenlockIconSource(
+ base::WeakPtr<ScreenlockIconProvider> icon_provider);
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+
+ std::string GetMimeType(const std::string& path) const override;
+
+ // Constructs and returns the icon URL for a given user.
+ static std::string GetIconURLForUser(const std::string& username);
+
+ private:
+ ~ScreenlockIconSource() override;
+
+ base::WeakPtr<ScreenlockIconProvider> icon_provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScreenlockIconSource);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_SCREENLOCK_ICON_SOURCE_H_
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
new file mode 100644
index 00000000000..a412f9b72ea
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -0,0 +1,1716 @@
+// 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/ui/webui/chromeos/login/signin_screen_handler.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "ash/public/interfaces/constants.mojom.h"
+#include "ash/public/interfaces/tray_action.mojom.h"
+#include "ash/shell.h"
+#include "ash/system/devicetype_utils.h"
+#include "ash/wm/lock_state_controller.h"
+#include "base/bind.h"
+#include "base/i18n/number_formatting.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/sys_info.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/trace_event.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_process_platform_part_chromeos.h"
+#include "chrome/browser/browser_shutdown.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
+#include "chrome/browser/chromeos/input_method/input_method_util.h"
+#include "chrome/browser/chromeos/language_preferences.h"
+#include "chrome/browser/chromeos/lock_screen_apps/state_controller.h"
+#include "chrome/browser/chromeos/login/error_screens_histogram_helper.h"
+#include "chrome/browser/chromeos/login/hwid_checker.h"
+#include "chrome/browser/chromeos/login/lock/screen_locker.h"
+#include "chrome/browser/chromeos/login/lock/webui_screen_locker.h"
+#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h"
+#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h"
+#include "chrome/browser/chromeos/login/reauth_stats.h"
+#include "chrome/browser/chromeos/login/screens/core_oobe_view.h"
+#include "chrome/browser/chromeos/login/screens/network_error.h"
+#include "chrome/browser/chromeos/login/startup_utils.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
+#include "chrome/browser/chromeos/login/ui/login_feedback.h"
+#include "chrome/browser/chromeos/login/ui/webui_login_display.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"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/chromeos/system/system_clock.h"
+#include "chrome/browser/io_thread.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_metrics.h"
+#include "chrome/browser/signin/easy_unlock_service.h"
+#include "chrome/browser/ui/ash/session_controller_client.h"
+#include "chrome/browser/ui/webui/chromeos/login/error_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/l10n_util.h"
+#include "chrome/browser/ui/webui/chromeos/login/native_window_delegate.h"
+#include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/power_manager_client.h"
+#include "chromeos/login/auth/key.h"
+#include "chromeos/login/auth/user_context.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "components/login/localized_values_builder.h"
+#include "components/prefs/pref_registry_simple.h"
+#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"
+#include "components/user_manager/user_type.h"
+#include "components/version_info/version_info.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 "google_apis/gaia/gaia_auth_util.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+#include "ui/base/ime/chromeos/ime_keyboard.h"
+#include "ui/base/ime/chromeos/input_method_descriptor.h"
+#include "ui/base/ime/chromeos/input_method_manager.h"
+#include "ui/base/webui/web_ui_util.h"
+
+namespace {
+
+// Max number of users to show.
+const size_t kMaxUsers = 18;
+
+// Timeout to delay first notification about offline state for a
+// current network.
+const int kOfflineTimeoutSec = 5;
+
+// Timeout used to prevent infinite connecting to a flaky network.
+const int kConnectingTimeoutSec = 60;
+
+// Max number of Gaia Reload to Show Proxy Auth Dialog.
+const int kMaxGaiaReloadForProxyAuthDialog = 3;
+
+// Type of the login screen UI that is currently presented to user.
+const char kSourceGaiaSignin[] = "gaia-signin";
+const char kSourceAccountPicker[] = "account-picker";
+
+// Constants for lock screen apps activity state values:
+const char kNoLockScreenApps[] = "LOCK_SCREEN_APPS_STATE.NONE";
+const char kBackgroundLockScreenApps[] = "LOCK_SCREEN_APPS_STATE.BACKGROUND";
+const char kForegroundLockScreenApps[] = "LOCK_SCREEN_APPS_STATE.FOREGROUND";
+
+static bool Contains(const std::vector<std::string>& container,
+ const std::string& value) {
+ return std::find(container.begin(), container.end(), value) !=
+ container.end();
+}
+
+class CallOnReturn {
+ public:
+ explicit CallOnReturn(const base::Closure& callback)
+ : callback_(callback), call_scheduled_(false) {}
+
+ ~CallOnReturn() {
+ if (call_scheduled_ && !callback_.is_null())
+ callback_.Run();
+ }
+
+ void CancelScheduledCall() { call_scheduled_ = false; }
+ void ScheduleCall() { call_scheduled_ = true; }
+
+ private:
+ base::Closure callback_;
+ bool call_scheduled_;
+
+ DISALLOW_COPY_AND_ASSIGN(CallOnReturn);
+};
+
+} // namespace
+
+namespace chromeos {
+
+namespace {
+
+bool IsOnline(NetworkStateInformer::State state,
+ NetworkError::ErrorReason reason) {
+ return state == NetworkStateInformer::ONLINE &&
+ reason != NetworkError::ERROR_REASON_PORTAL_DETECTED &&
+ reason != NetworkError::ERROR_REASON_LOADING_TIMEOUT;
+}
+
+bool IsBehindCaptivePortal(NetworkStateInformer::State state,
+ NetworkError::ErrorReason reason) {
+ return state == NetworkStateInformer::CAPTIVE_PORTAL ||
+ reason == NetworkError::ERROR_REASON_PORTAL_DETECTED;
+}
+
+bool IsProxyError(NetworkStateInformer::State state,
+ NetworkError::ErrorReason reason,
+ net::Error frame_error) {
+ return state == NetworkStateInformer::PROXY_AUTH_REQUIRED ||
+ reason == NetworkError::ERROR_REASON_PROXY_AUTH_CANCELLED ||
+ reason == NetworkError::ERROR_REASON_PROXY_CONNECTION_FAILED ||
+ (reason == NetworkError::ERROR_REASON_FRAME_ERROR &&
+ (frame_error == net::ERR_PROXY_CONNECTION_FAILED ||
+ frame_error == net::ERR_TUNNEL_CONNECTION_FAILED));
+}
+
+bool IsSigninScreen(const OobeScreen screen) {
+ return screen == OobeScreen::SCREEN_GAIA_SIGNIN ||
+ screen == OobeScreen::SCREEN_ACCOUNT_PICKER;
+}
+
+bool IsSigninScreenError(NetworkError::ErrorState error_state) {
+ return error_state == NetworkError::ERROR_STATE_PORTAL ||
+ error_state == NetworkError::ERROR_STATE_OFFLINE ||
+ error_state == NetworkError::ERROR_STATE_PROXY ||
+ error_state == NetworkError::ERROR_STATE_AUTH_EXT_TIMEOUT;
+}
+
+// Returns network name by service path.
+std::string GetNetworkName(const std::string& service_path) {
+ const NetworkState* network = NetworkHandler::Get()->network_state_handler()->
+ GetNetworkState(service_path);
+ if (!network)
+ return std::string();
+ return network->name();
+}
+
+static bool SetUserInputMethodImpl(
+ const std::string& username,
+ const std::string& user_input_method,
+ input_method::InputMethodManager::State* ime_state) {
+ if (!chromeos::input_method::InputMethodManager::Get()->IsLoginKeyboard(
+ user_input_method)) {
+ LOG(WARNING) << "SetUserInputMethod('" << username
+ << "'): stored user last input method '" << user_input_method
+ << "' is no longer Full Latin Keyboard Language"
+ << " (entry dropped). Use hardware default instead.";
+
+ PrefService* const local_state = g_browser_process->local_state();
+ DictionaryPrefUpdate updater(local_state, prefs::kUsersLastInputMethod);
+
+ base::DictionaryValue* const users_last_input_methods = updater.Get();
+ if (users_last_input_methods != nullptr) {
+ users_last_input_methods->SetStringWithoutPathExpansion(username, "");
+ }
+ return false;
+ }
+
+ if (!Contains(ime_state->GetActiveInputMethodIds(), user_input_method)) {
+ if (!ime_state->EnableInputMethod(user_input_method)) {
+ DLOG(ERROR) << "SigninScreenHandler::SetUserInputMethod('" << username
+ << "'): user input method '" << user_input_method
+ << "' is not enabled and enabling failed (ignored!).";
+ }
+ }
+ ime_state->ChangeInputMethod(user_input_method, false /* show_message */);
+
+ return true;
+}
+
+void EnforcePolicyInputMethods(std::string user_input_method) {
+ chromeos::CrosSettings* cros_settings = chromeos::CrosSettings::Get();
+ const base::ListValue* login_screen_input_methods = nullptr;
+ if (!cros_settings->GetList(chromeos::kDeviceLoginScreenInputMethods,
+ &login_screen_input_methods)) {
+ return;
+ }
+
+ std::vector<std::string> allowed_input_methods;
+
+ // Add user's input method first so it is pre-selected.
+ if (!user_input_method.empty()) {
+ allowed_input_methods.push_back(user_input_method);
+ }
+
+ std::string input_method;
+ for (const auto& input_method_entry : *login_screen_input_methods) {
+ if (input_method_entry.GetAsString(&input_method))
+ allowed_input_methods.push_back(input_method);
+ }
+ chromeos::input_method::InputMethodManager* imm =
+ chromeos::input_method::InputMethodManager::Get();
+ imm->GetActiveIMEState()->SetAllowedInputMethods(allowed_input_methods);
+}
+
+void StopEnforcingPolicyInputMethods() {
+ // Empty means all input methods are allowed
+ std::vector<std::string> allowed_input_methods;
+ chromeos::input_method::InputMethodManager* imm =
+ chromeos::input_method::InputMethodManager::Get();
+ imm->GetActiveIMEState()->SetAllowedInputMethods(allowed_input_methods);
+}
+
+} // namespace
+
+// LoginScreenContext implementation ------------------------------------------
+
+LoginScreenContext::LoginScreenContext() {
+ Init();
+}
+
+LoginScreenContext::LoginScreenContext(const base::ListValue* args) {
+ Init();
+
+ if (!args || args->GetSize() == 0)
+ return;
+ std::string email;
+ if (args->GetString(0, &email))
+ email_ = email;
+}
+
+void LoginScreenContext::Init() {
+ oobe_ui_ = false;
+}
+
+// SigninScreenHandler implementation ------------------------------------------
+
+SigninScreenHandler::SigninScreenHandler(
+ const scoped_refptr<NetworkStateInformer>& network_state_informer,
+ ErrorScreen* error_screen,
+ CoreOobeView* core_oobe_view,
+ GaiaScreenHandler* gaia_screen_handler,
+ JSCallsContainer* js_calls_container)
+ : BaseWebUIHandler(js_calls_container),
+ network_state_informer_(network_state_informer),
+ error_screen_(error_screen),
+ core_oobe_view_(core_oobe_view),
+ caps_lock_enabled_(chromeos::input_method::InputMethodManager::Get()
+ ->GetImeKeyboard()
+ ->CapsLockIsEnabled()),
+ proxy_auth_dialog_reload_times_(kMaxGaiaReloadForProxyAuthDialog),
+ gaia_screen_handler_(gaia_screen_handler),
+ touch_view_binding_(this),
+ histogram_helper_(new ErrorScreensHistogramHelper("Signin")),
+ lock_screen_apps_observer_(this),
+ weak_factory_(this) {
+ DCHECK(network_state_informer_.get());
+ DCHECK(error_screen_);
+ DCHECK(core_oobe_view_);
+ DCHECK(js_calls_container);
+ gaia_screen_handler_->set_signin_screen_handler(this);
+ network_state_informer_->AddObserver(this);
+
+ registrar_.Add(this,
+ chrome::NOTIFICATION_AUTH_NEEDED,
+ content::NotificationService::AllSources());
+ registrar_.Add(this,
+ chrome::NOTIFICATION_AUTH_SUPPLIED,
+ content::NotificationService::AllSources());
+ registrar_.Add(this,
+ chrome::NOTIFICATION_AUTH_CANCELLED,
+ content::NotificationService::AllSources());
+
+ chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(
+ this);
+
+ chromeos::input_method::ImeKeyboard* keyboard =
+ chromeos::input_method::InputMethodManager::Get()->GetImeKeyboard();
+ if (keyboard)
+ keyboard->AddObserver(this);
+ allowed_input_methods_subscription_ =
+ chromeos::CrosSettings::Get()->AddSettingsObserver(
+ chromeos::kDeviceLoginScreenInputMethods,
+ base::Bind(&SigninScreenHandler::OnAllowedInputMethodsChanged,
+ base::Unretained(this)));
+
+ content::ServiceManagerConnection::GetForProcess()
+ ->GetConnector()
+ ->BindInterface(ash::mojom::kServiceName, &touch_view_manager_ptr_);
+ touch_view_manager_ptr_->AddObserver(
+ touch_view_binding_.CreateInterfacePtrAndBind());
+ if (ScreenLocker::default_screen_locker() &&
+ lock_screen_apps::StateController::IsEnabled()) {
+ lock_screen_apps_observer_.Add(lock_screen_apps::StateController::Get());
+ }
+}
+
+SigninScreenHandler::~SigninScreenHandler() {
+ OobeUI* oobe_ui = GetOobeUI();
+ if (oobe_ui && oobe_ui_observer_added_)
+ oobe_ui->RemoveObserver(this);
+ chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(
+ this);
+ chromeos::input_method::ImeKeyboard* keyboard =
+ chromeos::input_method::InputMethodManager::Get()->GetImeKeyboard();
+ if (keyboard)
+ keyboard->RemoveObserver(this);
+ StopEnforcingPolicyInputMethods();
+ weak_factory_.InvalidateWeakPtrs();
+ if (delegate_)
+ delegate_->SetWebUIHandler(nullptr);
+ network_state_informer_->RemoveObserver(this);
+ proximity_auth::ScreenlockBridge::Get()->SetLockHandler(nullptr);
+ proximity_auth::ScreenlockBridge::Get()->SetFocusedUser(EmptyAccountId());
+}
+
+// static
+std::string SigninScreenHandler::GetUserLastInputMethod(
+ const std::string& username) {
+ PrefService* const local_state = g_browser_process->local_state();
+ const base::DictionaryValue* users_last_input_methods =
+ local_state->GetDictionary(prefs::kUsersLastInputMethod);
+
+ if (!users_last_input_methods) {
+ DLOG(WARNING) << "GetUserLastInputMethod('" << username
+ << "'): no kUsersLastInputMethod";
+ return std::string();
+ }
+
+ std::string input_method;
+
+ if (!users_last_input_methods->GetStringWithoutPathExpansion(username,
+ &input_method)) {
+ DVLOG(0) << "GetUserLastInputMethod('" << username
+ << "'): no input method for this user";
+ return std::string();
+ }
+
+ return input_method;
+}
+
+// static
+// Update keyboard layout to least recently used by the user.
+void SigninScreenHandler::SetUserInputMethod(
+ const std::string& username,
+ input_method::InputMethodManager::State* ime_state) {
+ bool succeed = false;
+
+ const std::string input_method = GetUserLastInputMethod(username);
+
+ EnforcePolicyInputMethods(input_method);
+
+ if (!input_method.empty())
+ succeed = SetUserInputMethodImpl(username, input_method, ime_state);
+
+ // This is also a case when last layout is set only for a few local users,
+ // thus others need to be switched to default locale.
+ // Otherwise they will end up using another user's locale to log in.
+ if (!succeed) {
+ DVLOG(0) << "SetUserInputMethod('" << username
+ << "'): failed to set user layout. Switching to default.";
+
+ ime_state->SetInputMethodLoginDefault();
+ }
+}
+
+void SigninScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ // Format numbers to be used on the pin keyboard.
+ for (int j = 0; j <= 9; j++) {
+ builder->Add("pinKeyboard" + base::IntToString(j),
+ base::FormatNumber(int64_t{j}));
+ }
+
+ builder->Add("passwordHint", IDS_LOGIN_POD_EMPTY_PASSWORD_TEXT);
+ builder->Add("pinKeyboardPlaceholderPin",
+ IDS_PIN_KEYBOARD_HINT_TEXT_PIN);
+ builder->Add("pinKeyboardPlaceholderPinPassword",
+ IDS_PIN_KEYBOARD_HINT_TEXT_PIN_PASSWORD);
+ builder->Add("pinKeyboardDeleteAccessibleName",
+ IDS_PIN_KEYBOARD_DELETE_ACCESSIBLE_NAME);
+ builder->Add("fingerprintHint", IDS_FINGERPRINT_HINT_TEXT);
+ builder->Add("fingerprintIconMessage", IDS_FINGERPRINT_ICON_MESSAGE);
+ builder->Add("fingerprintSigningin", IDS_FINGERPRINT_LOGIN_TEXT);
+ builder->Add("fingerprintSigninFailed", IDS_FINGERPRINT_LOGIN_FAILED_TEXT);
+ builder->Add("signingIn", IDS_LOGIN_POD_SIGNING_IN);
+ builder->Add("podMenuButtonAccessibleName",
+ IDS_LOGIN_POD_MENU_BUTTON_ACCESSIBLE_NAME);
+ builder->Add("podMenuRemoveItemAccessibleName",
+ IDS_LOGIN_POD_MENU_REMOVE_ITEM_ACCESSIBLE_NAME);
+ builder->Add("passwordFieldAccessibleName",
+ IDS_LOGIN_POD_PASSWORD_FIELD_ACCESSIBLE_NAME);
+ builder->Add("submitButtonAccessibleName",
+ IDS_LOGIN_POD_SUBMIT_BUTTON_ACCESSIBLE_NAME);
+ builder->Add("signedIn", IDS_SCREEN_LOCK_ACTIVE_USER);
+ builder->Add("launchAppButton", IDS_LAUNCH_APP_BUTTON);
+ builder->Add("restart", IDS_RESTART_BUTTON);
+ builder->Add("shutDown", IDS_SHUTDOWN_BUTTON);
+ builder->Add("addUser", IDS_ADD_USER_BUTTON);
+ builder->Add("browseAsGuest", IDS_BROWSE_AS_GUEST_BUTTON);
+ builder->Add("moreOptions", IDS_MORE_OPTIONS_BUTTON);
+ builder->Add("addSupervisedUser",
+ IDS_CREATE_LEGACY_SUPERVISED_USER_MENU_LABEL);
+ builder->Add("cancel", IDS_CANCEL);
+ builder->Add("signOutUser", IDS_SCREEN_LOCK_SIGN_OUT);
+ builder->Add("unlockUser", IDS_SCREEN_LOCK_UNLOCK_USER_BUTTON);
+ builder->Add("offlineLogin", IDS_OFFLINE_LOGIN_HTML);
+ builder->Add("ownerUserPattern", IDS_LOGIN_POD_OWNER_USER);
+ builder->Add("removeUser", IDS_LOGIN_POD_REMOVE_USER);
+ builder->Add("errorTpmFailureTitle", IDS_LOGIN_ERROR_TPM_FAILURE_TITLE);
+ builder->Add("errorTpmFailureReboot", IDS_LOGIN_ERROR_TPM_FAILURE_REBOOT);
+ builder->Add("errorTpmFailureRebootButton",
+ IDS_LOGIN_ERROR_TPM_FAILURE_REBOOT_BUTTON);
+
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ builder->Add("disabledAddUserTooltip",
+ connector->IsEnterpriseManaged()
+ ? IDS_DISABLED_ADD_USER_TOOLTIP_ENTERPRISE
+ : IDS_DISABLED_ADD_USER_TOOLTIP);
+
+ builder->Add("supervisedUserExpiredTokenWarning",
+ IDS_SUPERVISED_USER_EXPIRED_TOKEN_WARNING);
+ builder->Add("signinBannerText", IDS_LOGIN_USER_ADDING_BANNER);
+
+ // Multi-profiles related strings.
+ builder->Add("multiProfilesRestrictedPolicyTitle",
+ IDS_MULTI_PROFILES_RESTRICTED_POLICY_TITLE);
+ builder->Add("multiProfilesNotAllowedPolicyMsg",
+ IDS_MULTI_PROFILES_NOT_ALLOWED_POLICY_MSG);
+ builder->Add("multiProfilesPrimaryOnlyPolicyMsg",
+ IDS_MULTI_PROFILES_PRIMARY_ONLY_POLICY_MSG);
+ builder->Add("multiProfilesOwnerPrimaryOnlyMsg",
+ IDS_MULTI_PROFILES_OWNER_PRIMARY_ONLY_MSG);
+
+ // Strings used by password changed dialog.
+ builder->Add("oldPasswordHint", IDS_LOGIN_PASSWORD_CHANGED_OLD_PASSWORD_HINT);
+ builder->Add("oldPasswordIncorrect",
+ IDS_LOGIN_PASSWORD_CHANGED_INCORRECT_OLD_PASSWORD);
+ builder->Add("proceedAnywayButton",
+ IDS_LOGIN_PASSWORD_CHANGED_PROCEED_ANYWAY_BUTTON);
+ builder->Add("nextButtonText", IDS_OFFLINE_LOGIN_NEXT_BUTTON_TEXT);
+ builder->Add("forgotOldPasswordButtonText",
+ IDS_LOGIN_PASSWORD_CHANGED_FORGOT_PASSWORD);
+ builder->AddF("passwordChangedTitle", IDS_LOGIN_PASSWORD_CHANGED_TITLE,
+ ash::GetChromeOSDeviceName());
+ builder->Add("passwordChangedProceedAnywayTitle",
+ IDS_LOGIN_PASSWORD_CHANGED_PROCEED_ANYWAY);
+ builder->Add("passwordChangedTryAgain", IDS_LOGIN_PASSWORD_CHANGED_TRY_AGAIN);
+ builder->Add("publicAccountInfoFormat", IDS_LOGIN_PUBLIC_ACCOUNT_INFO_FORMAT);
+ builder->Add("publicAccountReminder",
+ IDS_LOGIN_PUBLIC_ACCOUNT_SIGNOUT_REMINDER);
+ builder->Add("publicSessionLanguageAndInput",
+ IDS_LOGIN_PUBLIC_SESSION_LANGUAGE_AND_INPUT);
+ builder->Add("publicAccountEnter", IDS_LOGIN_PUBLIC_ACCOUNT_ENTER);
+ builder->Add("publicAccountEnterAccessibleName",
+ 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("publicAccountMonitoringInfo",
+ IDS_LOGIN_PUBLIC_ACCOUNT_MONITORING_INFO);
+ builder->Add("publicAccountMonitoringInfoItem1",
+ IDS_LOGIN_PUBLIC_ACCOUNT_MONITORING_INFO_ITEM_1);
+ builder->Add("publicAccountMonitoringInfoItem2",
+ IDS_LOGIN_PUBLIC_ACCOUNT_MONITORING_INFO_ITEM_2);
+ builder->Add("publicAccountMonitoringInfoItem3",
+ IDS_LOGIN_PUBLIC_ACCOUNT_MONITORING_INFO_ITEM_3);
+ builder->Add("publicAccountMonitoringInfoItem4",
+ IDS_LOGIN_PUBLIC_ACCOUNT_MONITORING_INFO_ITEM_4);
+ builder->Add("publicSessionSelectLanguage", IDS_LANGUAGE_SELECTION_SELECT);
+ builder->Add("publicSessionSelectKeyboard", IDS_KEYBOARD_SELECTION_SELECT);
+ builder->Add("removeUserWarningTextNonSyncNoStats", base::string16());
+ builder->Add("removeUserWarningTextNonSyncCalculating", base::string16());
+ builder->Add("removeUserWarningTextHistory", base::string16());
+ builder->Add("removeUserWarningTextPasswords", base::string16());
+ builder->Add("removeUserWarningTextBookmarks", base::string16());
+ builder->Add("removeUserWarningTextSettings", base::string16());
+ builder->Add("removeUserWarningTextCalculating", base::string16());
+ builder->Add("removeUserWarningTextSyncNoStats", base::string16());
+ builder->Add("removeUserWarningTextSyncCalculating", base::string16());
+ builder->AddF("removeLegacySupervisedUserWarningText",
+ IDS_LOGIN_POD_LEGACY_SUPERVISED_USER_REMOVE_WARNING,
+ base::UTF8ToUTF16(
+ chrome::kLegacySupervisedUserManagementDisplayURL));
+ builder->Add("removeNonOwnerUserWarningText",
+ IDS_LOGIN_POD_NON_OWNER_USER_REMOVE_WARNING);
+ builder->Add("removeUserWarningButtonTitle",
+ IDS_LOGIN_POD_USER_REMOVE_WARNING_BUTTON);
+ builder->Add("samlNotice", IDS_LOGIN_SAML_NOTICE);
+ builder->Add("samlNoticeWithVideo", IDS_LOGIN_SAML_NOTICE_WITH_VIDEO);
+ builder->AddF("confirmPasswordTitle", IDS_LOGIN_CONFIRM_PASSWORD_TITLE,
+ ash::GetChromeOSDeviceName());
+ builder->Add("manualPasswordTitle", IDS_LOGIN_MANUAL_PASSWORD_TITLE);
+ builder->Add("manualPasswordInputLabel",
+ IDS_LOGIN_MANUAL_PASSWORD_INPUT_LABEL);
+ builder->Add("manualPasswordMismatch",
+ IDS_LOGIN_MANUAL_PASSWORD_MISMATCH);
+ builder->Add("confirmPasswordLabel", IDS_LOGIN_CONFIRM_PASSWORD_LABEL);
+ builder->Add("confirmPasswordIncorrectPassword",
+ IDS_LOGIN_CONFIRM_PASSWORD_INCORRECT_PASSWORD);
+ builder->Add("accountSetupCancelDialogTitle",
+ IDS_LOGIN_ACCOUNT_SETUP_CANCEL_DIALOG_TITLE);
+ builder->Add("accountSetupCancelDialogNo",
+ IDS_LOGIN_ACCOUNT_SETUP_CANCEL_DIALOG_NO);
+ builder->Add("accountSetupCancelDialogYes",
+ IDS_LOGIN_ACCOUNT_SETUP_CANCEL_DIALOG_YES);
+
+ builder->Add("fatalEnrollmentError",
+ IDS_ENTERPRISE_ENROLLMENT_AUTH_FATAL_ERROR);
+ builder->Add("insecureURLEnrollmentError",
+ IDS_ENTERPRISE_ENROLLMENT_AUTH_INSECURE_URL_ERROR);
+
+ builder->Add("unrecoverableCryptohomeErrorMessageTitle",
+ IDS_LOGIN_UNRECOVERABLE_CRYPTOHOME_ERROR_TITLE);
+ builder->Add("unrecoverableCryptohomeErrorMessage",
+ IDS_LOGIN_UNRECOVERABLE_CRYPTOHOME_ERROR_MESSAGE);
+ builder->Add("unrecoverableCryptohomeErrorContinue",
+ IDS_LOGIN_UNRECOVERABLE_CRYPTOHOME_ERROR_CONTINUE);
+ builder->Add("unrecoverableCryptohomeErrorRecreatingProfile",
+ IDS_LOGIN_UNRECOVERABLE_CRYPTOHOME_ERROR_WAIT_MESSAGE);
+
+ builder->Add("adEnterOldPasswordHint", IDS_AD_PASSWORD_CHANGE_OLD_PASSWORD);
+ builder->Add("adEnterNewPasswordHint", IDS_AD_PASSWORD_CHANGE_NEW_PASSWORD);
+ builder->Add("adRepeatNewPasswordHint",
+ IDS_AD_PASSWORD_CHANGE_REPEAT_NEW_PASSWORD);
+ builder->Add("adPasswordChangeMessage", IDS_AD_PASSWORD_CHANGE_MESSAGE);
+ builder->Add("adOldPasswordError", IDS_AD_PASSWORD_CHANGE_INVALID_PASSWORD);
+ builder->Add("adNewPasswordError", IDS_AD_PASSWORD_CHANGE_PASSWORDS_MISMATCH);
+}
+
+void SigninScreenHandler::RegisterMessages() {
+ AddCallback("authenticateUser", &SigninScreenHandler::HandleAuthenticateUser);
+ AddCallback("launchIncognito", &SigninScreenHandler::HandleLaunchIncognito);
+ AddCallback("showSupervisedUserCreationScreen",
+ &SigninScreenHandler::HandleShowSupervisedUserCreationScreen);
+ AddCallback("launchPublicSession",
+ &SigninScreenHandler::HandleLaunchPublicSession);
+ AddRawCallback("offlineLogin", &SigninScreenHandler::HandleOfflineLogin);
+ AddCallback("rebootSystem", &SigninScreenHandler::HandleRebootSystem);
+ AddRawCallback("showAddUser", &SigninScreenHandler::HandleShowAddUser);
+ AddCallback("shutdownSystem", &SigninScreenHandler::HandleShutdownSystem);
+ AddCallback("loadWallpaper", &SigninScreenHandler::HandleLoadWallpaper);
+ AddCallback("removeUser", &SigninScreenHandler::HandleRemoveUser);
+ AddCallback("toggleEnrollmentScreen",
+ &SigninScreenHandler::HandleToggleEnrollmentScreen);
+ AddCallback("toggleEnableDebuggingScreen",
+ &SigninScreenHandler::HandleToggleEnableDebuggingScreen);
+ AddCallback("toggleKioskEnableScreen",
+ &SigninScreenHandler::HandleToggleKioskEnableScreen);
+ AddCallback("accountPickerReady",
+ &SigninScreenHandler::HandleAccountPickerReady);
+ AddCallback("wallpaperReady", &SigninScreenHandler::HandleWallpaperReady);
+ AddCallback("signOutUser", &SigninScreenHandler::HandleSignOutUser);
+ AddCallback("openProxySettings",
+ &SigninScreenHandler::HandleOpenProxySettings);
+ AddCallback("loginVisible", &SigninScreenHandler::HandleLoginVisible);
+ AddCallback("cancelPasswordChangedFlow",
+ &SigninScreenHandler::HandleCancelPasswordChangedFlow);
+ AddCallback("cancelUserAdding", &SigninScreenHandler::HandleCancelUserAdding);
+ AddCallback("migrateUserData", &SigninScreenHandler::HandleMigrateUserData);
+ AddCallback("resyncUserData", &SigninScreenHandler::HandleResyncUserData);
+ AddCallback("loginUIStateChanged",
+ &SigninScreenHandler::HandleLoginUIStateChanged);
+ AddCallback("unlockOnLoginSuccess",
+ &SigninScreenHandler::HandleUnlockOnLoginSuccess);
+ AddCallback("showLoadingTimeoutError",
+ &SigninScreenHandler::HandleShowLoadingTimeoutError);
+ AddCallback("focusPod", &SigninScreenHandler::HandleFocusPod);
+ AddCallback("noPodFocused", &SigninScreenHandler::HandleNoPodFocused);
+ AddCallback("getPublicSessionKeyboardLayouts",
+ &SigninScreenHandler::HandleGetPublicSessionKeyboardLayouts);
+ AddCallback("getTouchViewState",
+ &SigninScreenHandler::HandleGetTouchViewState);
+ AddCallback("logRemoveUserWarningShown",
+ &SigninScreenHandler::HandleLogRemoveUserWarningShown);
+ AddCallback("firstIncorrectPasswordAttempt",
+ &SigninScreenHandler::HandleFirstIncorrectPasswordAttempt);
+ AddCallback("maxIncorrectPasswordAttempts",
+ &SigninScreenHandler::HandleMaxIncorrectPasswordAttempts);
+ AddCallback("sendFeedbackAndResyncUserData",
+ &SigninScreenHandler::HandleSendFeedbackAndResyncUserData);
+
+ // This message is sent by the kiosk app menu, but is handled here
+ // so we can tell the delegate to launch the app.
+ AddCallback("launchKioskApp", &SigninScreenHandler::HandleLaunchKioskApp);
+ AddCallback("launchArcKioskApp",
+ &SigninScreenHandler::HandleLaunchArcKioskApp);
+ AddCallback("setLockScreenAppsState",
+ &SigninScreenHandler::HandleSetLockScreenAppsState);
+}
+
+void SigninScreenHandler::Show(const LoginScreenContext& context) {
+ CHECK(delegate_);
+
+ // Just initialize internal fields from context and call ShowImpl().
+ oobe_ui_ = context.oobe_ui();
+
+ std::string email;
+ email = context.email();
+ gaia_screen_handler_->set_populated_email(email);
+ ShowImpl();
+ histogram_helper_->OnScreenShow();
+}
+
+void SigninScreenHandler::SetDelegate(SigninScreenHandlerDelegate* delegate) {
+ delegate_ = delegate;
+ if (delegate_)
+ delegate_->SetWebUIHandler(this);
+}
+
+void SigninScreenHandler::SetNativeWindowDelegate(
+ NativeWindowDelegate* native_window_delegate) {
+ native_window_delegate_ = native_window_delegate;
+}
+
+void SigninScreenHandler::OnNetworkReady() {
+ VLOG(1) << "OnNetworkReady() call.";
+ gaia_screen_handler_->MaybePreloadAuthExtension();
+}
+
+void SigninScreenHandler::UpdateState(NetworkError::ErrorReason reason) {
+ // ERROR_REASON_FRAME_ERROR is an explicit signal from GAIA frame so it shoud
+ // force network error UI update.
+ bool force_update = reason == NetworkError::ERROR_REASON_FRAME_ERROR;
+ UpdateStateInternal(reason, force_update);
+}
+
+void SigninScreenHandler::SetFocusPODCallbackForTesting(
+ base::Closure callback) {
+ test_focus_pod_callback_ = callback;
+}
+
+void SigninScreenHandler::ZeroOfflineTimeoutForTesting() {
+ zero_offline_timeout_for_test_ = true;
+}
+
+bool SigninScreenHandler::GetKeyboardRemappedPrefValue(
+ const std::string& pref_name,
+ int* value) {
+ return focused_pod_account_id_ && focused_pod_account_id_->is_valid() &&
+ user_manager::known_user::GetIntegerPref(*focused_pod_account_id_,
+ pref_name, value);
+}
+
+// SigninScreenHandler, private: -----------------------------------------------
+
+void SigninScreenHandler::ShowImpl() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+
+ if (!ime_state_.get())
+ ime_state_ = input_method::InputMethodManager::Get()->GetActiveIMEState();
+
+ if (!oobe_ui_observer_added_) {
+ oobe_ui_observer_added_ = true;
+ GetOobeUI()->AddObserver(this);
+ }
+
+ if (oobe_ui_) {
+ // Shows new user sign-in for OOBE.
+ OnShowAddUser();
+ } else {
+ // Populates account picker. Animation is turned off for now until we
+ // figure out how to make it fast enough.
+ delegate_->HandleGetUsers();
+
+ // Reset Caps Lock state when login screen is shown.
+ input_method::InputMethodManager::Get()
+ ->GetImeKeyboard()
+ ->SetCapsLockEnabled(false);
+
+ base::DictionaryValue params;
+ params.SetBoolean("disableAddUser", AllWhitelistedUsersPresent());
+ UpdateUIState(UI_STATE_ACCOUNT_PICKER, &params);
+ }
+
+ // Enable pin for any users who can use it.
+ if (user_manager::UserManager::IsInitialized()) {
+ for (user_manager::User* user :
+ user_manager::UserManager::Get()->GetLoggedInUsers()) {
+ UpdatePinKeyboardState(user->GetAccountId());
+ }
+ }
+}
+
+void SigninScreenHandler::UpdateUIState(UIState ui_state,
+ base::DictionaryValue* params) {
+ switch (ui_state) {
+ case UI_STATE_GAIA_SIGNIN:
+ ui_state_ = UI_STATE_GAIA_SIGNIN;
+ ShowScreenWithData(OobeScreen::SCREEN_GAIA_SIGNIN, params);
+ break;
+ case UI_STATE_ACCOUNT_PICKER:
+ ui_state_ = UI_STATE_ACCOUNT_PICKER;
+ gaia_screen_handler_->CancelShowGaiaAsync();
+ ShowScreenWithData(OobeScreen::SCREEN_ACCOUNT_PICKER, params);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+// TODO(antrim@): split this method into small parts.
+// TODO(antrim@): move this logic to GaiaScreenHandler.
+void SigninScreenHandler::UpdateStateInternal(NetworkError::ErrorReason reason,
+ bool force_update) {
+ // Do nothing once user has signed in or sign in is in progress.
+ // TODO(antrim): We will end up here when processing network state
+ // notification but no ShowSigninScreen() was called so delegate_ will be
+ // nullptr. Network state processing logic does not belong here.
+ if (delegate_ &&
+ (delegate_->IsUserSigninCompleted() || delegate_->IsSigninInProgress())) {
+ return;
+ }
+
+ NetworkStateInformer::State state = network_state_informer_->state();
+ const std::string network_path = network_state_informer_->network_path();
+ const std::string network_name = GetNetworkName(network_path);
+
+ // Skip "update" notification about OFFLINE state from
+ // NetworkStateInformer if previous notification already was
+ // delayed.
+ if ((state == NetworkStateInformer::OFFLINE || has_pending_auth_ui_) &&
+ !force_update && !update_state_closure_.IsCancelled()) {
+ return;
+ }
+
+ update_state_closure_.Cancel();
+
+ if ((state == NetworkStateInformer::OFFLINE && !force_update) ||
+ has_pending_auth_ui_) {
+ update_state_closure_.Reset(
+ base::Bind(&SigninScreenHandler::UpdateStateInternal,
+ weak_factory_.GetWeakPtr(),
+ reason,
+ true));
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, update_state_closure_.callback(),
+ base::TimeDelta::FromSeconds(
+ zero_offline_timeout_for_test_ ? 0 : kOfflineTimeoutSec));
+ return;
+ }
+
+ // Don't show or hide error screen if we're in connecting state.
+ if (state == NetworkStateInformer::CONNECTING && !force_update) {
+ if (connecting_closure_.IsCancelled()) {
+ // First notification about CONNECTING state.
+ connecting_closure_.Reset(
+ base::Bind(&SigninScreenHandler::UpdateStateInternal,
+ weak_factory_.GetWeakPtr(),
+ reason,
+ true));
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, connecting_closure_.callback(),
+ base::TimeDelta::FromSeconds(kConnectingTimeoutSec));
+ }
+ return;
+ }
+ connecting_closure_.Cancel();
+
+ const bool is_online = IsOnline(state, reason);
+ const bool is_behind_captive_portal = IsBehindCaptivePortal(state, reason);
+ const bool is_gaia_loading_timeout =
+ (reason == NetworkError::ERROR_REASON_LOADING_TIMEOUT);
+ const bool is_gaia_error =
+ FrameError() != net::OK && FrameError() != net::ERR_NETWORK_CHANGED;
+ const bool is_gaia_signin = IsGaiaVisible() || IsGaiaHiddenByError();
+ const bool offline_login_active =
+ gaia_screen_handler_->offline_login_is_active();
+ const bool error_screen_should_overlay =
+ !offline_login_active && IsGaiaVisible();
+ const bool from_not_online_to_online_transition =
+ is_online && last_network_state_ != NetworkStateInformer::ONLINE;
+ last_network_state_ = state;
+ proxy_auth_dialog_need_reload_ =
+ (reason == NetworkError::ERROR_REASON_NETWORK_STATE_CHANGED) &&
+ (state == NetworkStateInformer::PROXY_AUTH_REQUIRED) &&
+ (proxy_auth_dialog_reload_times_ > 0);
+
+ CallOnReturn reload_gaia(base::Bind(
+ &SigninScreenHandler::ReloadGaia, weak_factory_.GetWeakPtr(), true));
+
+ if (is_online || !is_behind_captive_portal)
+ error_screen_->HideCaptivePortal();
+
+ // Hide offline message (if needed) and return if current screen is
+ // not a Gaia frame.
+ if (!is_gaia_signin) {
+ if (!IsSigninScreenHiddenByError())
+ HideOfflineMessage(state, reason);
+ return;
+ }
+
+ // Use the online login page if the user has not used the machine for awhile.
+ if (offline_login_active)
+ gaia_screen_handler_->MonitorOfflineIdle(is_online);
+
+ // Reload frame if network state is changed from {!ONLINE} -> ONLINE state.
+ if (reason == NetworkError::ERROR_REASON_NETWORK_STATE_CHANGED &&
+ from_not_online_to_online_transition) {
+ // Schedules a immediate retry.
+ LOG(WARNING) << "Retry frame load since network has been changed.";
+ gaia_reload_reason_ = reason;
+ reload_gaia.ScheduleCall();
+ }
+
+ if (reason == NetworkError::ERROR_REASON_PROXY_CONFIG_CHANGED &&
+ error_screen_should_overlay) {
+ // Schedules a immediate retry.
+ LOG(WARNING) << "Retry frameload since proxy settings has been changed.";
+ gaia_reload_reason_ = reason;
+ reload_gaia.ScheduleCall();
+ }
+
+ if (reason == NetworkError::ERROR_REASON_FRAME_ERROR &&
+ reason != gaia_reload_reason_ &&
+ !IsProxyError(state, reason, FrameError())) {
+ LOG(WARNING) << "Retry frame load due to reason: "
+ << NetworkError::ErrorReasonString(reason);
+ gaia_reload_reason_ = reason;
+ reload_gaia.ScheduleCall();
+ }
+
+ if (is_gaia_loading_timeout) {
+ LOG(WARNING) << "Retry frame load due to loading timeout.";
+ LOG(ERROR) << "UpdateStateInternal reload 4";
+ reload_gaia.ScheduleCall();
+ }
+
+ if (proxy_auth_dialog_need_reload_) {
+ --proxy_auth_dialog_reload_times_;
+ LOG(WARNING) << "Retry frame load to show proxy auth dialog";
+ reload_gaia.ScheduleCall();
+ }
+
+ if ((!is_online || is_gaia_loading_timeout || is_gaia_error) &&
+ !offline_login_active) {
+ SetupAndShowOfflineMessage(state, reason);
+ } else {
+ HideOfflineMessage(state, reason);
+
+ // Cancel scheduled GAIA reload (if any) to prevent double reloads.
+ reload_gaia.CancelScheduledCall();
+ }
+}
+
+void SigninScreenHandler::SetupAndShowOfflineMessage(
+ NetworkStateInformer::State state,
+ NetworkError::ErrorReason reason) {
+ const std::string network_path = network_state_informer_->network_path();
+ const bool is_behind_captive_portal = IsBehindCaptivePortal(state, reason);
+ const bool is_proxy_error = IsProxyError(state, reason, FrameError());
+ const bool is_gaia_loading_timeout =
+ (reason == NetworkError::ERROR_REASON_LOADING_TIMEOUT);
+
+ if (is_proxy_error) {
+ error_screen_->SetErrorState(NetworkError::ERROR_STATE_PROXY,
+ std::string());
+ } else if (is_behind_captive_portal) {
+ // Do not bother a user with obsessive captive portal showing. This
+ // check makes captive portal being shown only once: either when error
+ // screen is shown for the first time or when switching from another
+ // error screen (offline, proxy).
+ if (IsGaiaVisible() ||
+ (error_screen_->GetErrorState() != NetworkError::ERROR_STATE_PORTAL)) {
+ error_screen_->FixCaptivePortal();
+ }
+ const std::string network_name = GetNetworkName(network_path);
+ error_screen_->SetErrorState(NetworkError::ERROR_STATE_PORTAL,
+ network_name);
+ } else if (is_gaia_loading_timeout) {
+ error_screen_->SetErrorState(NetworkError::ERROR_STATE_AUTH_EXT_TIMEOUT,
+ std::string());
+ } else {
+ error_screen_->SetErrorState(NetworkError::ERROR_STATE_OFFLINE,
+ std::string());
+ }
+
+ const bool guest_signin_allowed =
+ IsGuestSigninAllowed() &&
+ IsSigninScreenError(error_screen_->GetErrorState());
+ error_screen_->AllowGuestSignin(guest_signin_allowed);
+
+ const bool offline_login_allowed =
+ IsSigninScreenError(error_screen_->GetErrorState()) &&
+ error_screen_->GetErrorState() !=
+ NetworkError::ERROR_STATE_AUTH_EXT_TIMEOUT;
+ error_screen_->AllowOfflineLogin(offline_login_allowed);
+
+ if (GetCurrentScreen() != OobeScreen::SCREEN_ERROR_MESSAGE) {
+ error_screen_->SetUIState(NetworkError::UI_STATE_SIGNIN);
+ error_screen_->SetParentScreen(OobeScreen::SCREEN_GAIA_SIGNIN);
+ error_screen_->Show();
+ histogram_helper_->OnErrorShow(error_screen_->GetErrorState());
+ }
+}
+
+void SigninScreenHandler::HideOfflineMessage(NetworkStateInformer::State state,
+ NetworkError::ErrorReason reason) {
+ if (!IsSigninScreenHiddenByError())
+ return;
+
+ gaia_reload_reason_ = NetworkError::ERROR_REASON_NONE;
+
+ error_screen_->Hide();
+ histogram_helper_->OnErrorHide();
+
+ // Forces a reload for Gaia screen on hiding error message.
+ if (IsGaiaVisible() || IsGaiaHiddenByError())
+ ReloadGaia(reason == NetworkError::ERROR_REASON_NETWORK_STATE_CHANGED);
+}
+
+void SigninScreenHandler::ReloadGaia(bool force_reload) {
+ gaia_screen_handler_->ReloadGaia(force_reload);
+}
+
+void SigninScreenHandler::Initialize() {
+ // Preload PIN keyboard if any of the users can authenticate via PIN.
+ if (user_manager::UserManager::IsInitialized()) {
+ for (user_manager::User* user :
+ user_manager::UserManager::Get()->GetUnlockUsers()) {
+ chromeos::quick_unlock::QuickUnlockStorage* quick_unlock_storage =
+ chromeos::quick_unlock::QuickUnlockFactory::GetForUser(user);
+ if (quick_unlock_storage &&
+ quick_unlock_storage->IsPinAuthenticationAvailable()) {
+ CallJS("cr.ui.Oobe.preloadPinKeyboard");
+ break;
+ }
+ }
+ }
+
+ // |delegate_| is null when we are preloading the lock screen.
+ if (delegate_ && show_on_init_) {
+ show_on_init_ = false;
+ ShowImpl();
+ }
+}
+
+gfx::NativeWindow SigninScreenHandler::GetNativeWindow() {
+ if (native_window_delegate_)
+ return native_window_delegate_->GetNativeWindow();
+ return nullptr;
+}
+
+void SigninScreenHandler::RegisterPrefs(PrefRegistrySimple* registry) {
+ registry->RegisterDictionaryPref(prefs::kUsersLastInputMethod);
+}
+
+void SigninScreenHandler::OnCurrentScreenChanged(OobeScreen current_screen,
+ OobeScreen new_screen) {
+ if (new_screen == OobeScreen::SCREEN_ACCOUNT_PICKER) {
+ // Restore active IME state if returning to user pod row screen.
+ input_method::InputMethodManager::Get()->SetState(ime_state_);
+ }
+}
+
+void SigninScreenHandler::ClearAndEnablePassword() {
+ core_oobe_view_->ResetSignInUI(false);
+}
+
+void SigninScreenHandler::ClearUserPodPassword() {
+ core_oobe_view_->ClearUserPodPassword();
+}
+
+void SigninScreenHandler::RefocusCurrentPod() {
+ core_oobe_view_->RefocusCurrentPod();
+}
+
+void SigninScreenHandler::UpdatePinKeyboardState(const AccountId& account_id) {
+ chromeos::quick_unlock::QuickUnlockStorage* quick_unlock_storage =
+ chromeos::quick_unlock::QuickUnlockFactory::GetForAccountId(account_id);
+ if (!quick_unlock_storage)
+ return;
+
+ bool is_enabled = quick_unlock_storage->IsPinAuthenticationAvailable();
+ CallJS("login.AccountPickerScreen.setPinEnabledForUser", account_id,
+ is_enabled);
+}
+
+void SigninScreenHandler::OnUserRemoved(const AccountId& account_id,
+ bool last_user_removed) {
+ CallJS("login.AccountPickerScreen.removeUser", account_id);
+ if (last_user_removed)
+ OnShowAddUser();
+}
+
+void SigninScreenHandler::OnUserImageChanged(const user_manager::User& user) {
+ if (page_is_ready()) {
+ CallJSOrDefer("login.AccountPickerScreen.updateUserImage",
+ user.GetAccountId());
+ }
+}
+
+void SigninScreenHandler::OnPreferencesChanged() {
+ // Make sure that one of the login UI is fully functional now, otherwise
+ // preferences update would be picked up next time it will be shown.
+ if (!webui_visible_) {
+ LOG(WARNING) << "Login UI is not active - postponed prefs change.";
+ preferences_changed_delayed_ = true;
+ return;
+ }
+
+ preferences_changed_delayed_ = false;
+
+ if (!delegate_)
+ return;
+
+ // Send the updated user list to the UI.
+ delegate_->HandleGetUsers();
+ if (GetCurrentScreen() == OobeScreen::SCREEN_ACCOUNT_PICKER &&
+ delegate_->ShowUsersHasChanged() &&
+ !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);
+ return;
+ }
+
+ if (delegate_->AllowNewUserChanged() || ui_state_ == UI_STATE_UNKNOWN) {
+ // We need to reload GAIA if UI_STATE_UNKNOWN or the allow new user setting
+ // has changed so that reloaded GAIA shows/hides the option to create a new
+ // account.
+ UpdateUIState(UI_STATE_ACCOUNT_PICKER, nullptr);
+ UpdateAddButtonStatus();
+ }
+}
+
+void SigninScreenHandler::ResetSigninScreenHandlerDelegate() {
+ SetDelegate(nullptr);
+}
+
+void SigninScreenHandler::ShowError(int login_attempts,
+ const std::string& error_text,
+ const std::string& help_link_text,
+ HelpAppLauncher::HelpTopic help_topic_id) {
+ core_oobe_view_->ShowSignInError(login_attempts, error_text, help_link_text,
+ help_topic_id);
+}
+
+void SigninScreenHandler::ShowErrorScreen(LoginDisplay::SigninError error_id) {
+ switch (error_id) {
+ case LoginDisplay::TPM_ERROR:
+ core_oobe_view_->ShowTpmError();
+ break;
+ default:
+ NOTREACHED() << "Unknown sign in error";
+ break;
+ }
+}
+
+void SigninScreenHandler::ShowSigninUI(const std::string& email) {
+ core_oobe_view_->ShowSignInUI(email);
+}
+
+void SigninScreenHandler::ShowPasswordChangedDialog(bool show_password_error,
+ const std::string& email) {
+ core_oobe_view_->ShowPasswordChangedScreen(show_password_error, email);
+}
+
+void SigninScreenHandler::ShowSigninScreenForCreds(
+ const std::string& username,
+ const std::string& password) {
+ gaia_screen_handler_->ShowSigninScreenForTest(username, password);
+}
+
+void SigninScreenHandler::ShowWhitelistCheckFailedError() {
+ gaia_screen_handler_->ShowWhitelistCheckFailedError();
+}
+
+void SigninScreenHandler::ShowUnrecoverableCrypthomeErrorDialog() {
+ CallJS("login.UnrecoverableCryptohomeErrorScreen.show");
+}
+
+void SigninScreenHandler::Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ switch (type) {
+ case chrome::NOTIFICATION_AUTH_NEEDED: {
+ has_pending_auth_ui_ = true;
+ break;
+ }
+ case chrome::NOTIFICATION_AUTH_SUPPLIED:
+ has_pending_auth_ui_ = false;
+ // Reload auth extension as proxy credentials are supplied.
+ if (!IsSigninScreenHiddenByError() && ui_state_ == UI_STATE_GAIA_SIGNIN)
+ ReloadGaia(true);
+ update_state_closure_.Cancel();
+ break;
+ case chrome::NOTIFICATION_AUTH_CANCELLED: {
+ // Don't reload auth extension if proxy auth dialog was cancelled.
+ has_pending_auth_ui_ = false;
+ update_state_closure_.Cancel();
+ break;
+ }
+ default:
+ NOTREACHED() << "Unexpected notification " << type;
+ }
+}
+
+void SigninScreenHandler::SuspendDone(const base::TimeDelta& sleep_duration) {
+ for (user_manager::User* user :
+ user_manager::UserManager::Get()->GetUnlockUsers()) {
+ UpdatePinKeyboardState(user->GetAccountId());
+ }
+}
+
+void SigninScreenHandler::OnTouchViewToggled(bool enabled) {
+ touch_view_enabled_ = enabled;
+ CallJSOrDefer("login.AccountPickerScreen.setTouchViewState", enabled);
+}
+
+void SigninScreenHandler::OnLockScreenNoteStateChanged(
+ ash::mojom::TrayActionState state) {
+ std::string lock_screen_apps_state;
+ switch (state) {
+ case ash::mojom::TrayActionState::kActive:
+ lock_screen_apps_state = kForegroundLockScreenApps;
+ break;
+ case ash::mojom::TrayActionState::kBackground:
+ lock_screen_apps_state = kBackgroundLockScreenApps;
+ break;
+ case ash::mojom::TrayActionState::kAvailable:
+ case ash::mojom::TrayActionState::kNotAvailable:
+ case ash::mojom::TrayActionState::kLaunching:
+ lock_screen_apps_state = kNoLockScreenApps;
+ break;
+ }
+ CallJSOrDefer("login.AccountPickerScreen.setLockScreenAppsState",
+ lock_screen_apps_state);
+}
+
+bool SigninScreenHandler::ShouldLoadGaia() const {
+ // Fetching of the extension is not started before account picker page is
+ // loaded because it can affect the loading speed.
+ // Do not load the extension for the screen locker, see crosbug.com/25018.
+ return !ScreenLocker::default_screen_locker() &&
+ is_account_picker_showing_first_time_;
+}
+
+void SigninScreenHandler::UpdateAddButtonStatus() {
+ CallJS("cr.ui.login.DisplayManager.updateAddUserButtonStatus",
+ AllWhitelistedUsersPresent());
+}
+
+void SigninScreenHandler::HandleAuthenticateUser(const AccountId& account_id,
+ const std::string& password,
+ bool authenticated_by_pin) {
+ if (!delegate_)
+ return;
+ DCHECK_EQ(account_id.GetUserEmail(),
+ gaia::SanitizeEmail(account_id.GetUserEmail()));
+ chromeos::quick_unlock::QuickUnlockStorage* quick_unlock_storage =
+ chromeos::quick_unlock::QuickUnlockFactory::GetForAccountId(account_id);
+ // If pin storage is unavailable, authenticated by PIN must be false.
+ DCHECK(!quick_unlock_storage ||
+ quick_unlock_storage->IsPinAuthenticationAvailable() ||
+ !authenticated_by_pin);
+
+ UserContext user_context(account_id);
+ user_context.SetKey(Key(password));
+ user_context.SetIsUsingPin(authenticated_by_pin);
+ if (account_id.GetAccountType() == AccountType::ACTIVE_DIRECTORY)
+ user_context.SetUserType(user_manager::USER_TYPE_ACTIVE_DIRECTORY);
+ delegate_->Login(user_context, SigninSpecifics());
+
+ UpdatePinKeyboardState(account_id);
+}
+
+void SigninScreenHandler::HandleLaunchIncognito() {
+ UserContext context(user_manager::USER_TYPE_GUEST, EmptyAccountId());
+ if (delegate_)
+ delegate_->Login(context, SigninSpecifics());
+}
+
+void SigninScreenHandler::HandleShowSupervisedUserCreationScreen() {
+ if (!user_manager::UserManager::Get()->AreSupervisedUsersAllowed()) {
+ LOG(ERROR) << "Managed users not allowed.";
+ return;
+ }
+ LoginDisplayHost::default_host()->StartWizard(
+ OobeScreen::SCREEN_CREATE_SUPERVISED_USER_FLOW);
+}
+
+void SigninScreenHandler::HandleLaunchPublicSession(
+ const AccountId& account_id,
+ const std::string& locale,
+ const std::string& input_method) {
+ if (!delegate_)
+ return;
+
+ UserContext context(user_manager::USER_TYPE_PUBLIC_ACCOUNT, account_id);
+ context.SetPublicSessionLocale(locale),
+ context.SetPublicSessionInputMethod(input_method);
+ delegate_->Login(context, SigninSpecifics());
+}
+
+void SigninScreenHandler::HandleOfflineLogin(const base::ListValue* args) {
+ if (!delegate_) {
+ NOTREACHED();
+ return;
+ }
+ std::string email;
+ args->GetString(0, &email);
+
+ gaia_screen_handler_->set_populated_email(email);
+ gaia_screen_handler_->LoadAuthExtension(true /* force */, true /* offline */);
+ UpdateUIState(UI_STATE_GAIA_SIGNIN, nullptr);
+}
+
+void SigninScreenHandler::HandleShutdownSystem() {
+ ash::Shell::Get()->lock_state_controller()->RequestShutdown();
+}
+
+void SigninScreenHandler::HandleLoadWallpaper(const AccountId& account_id) {
+ if (delegate_)
+ delegate_->LoadWallpaper(account_id);
+}
+
+void SigninScreenHandler::HandleRebootSystem() {
+ chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
+}
+
+void SigninScreenHandler::HandleRemoveUser(const AccountId& account_id) {
+ if (delegate_ &&
+ (delegate_->IsUserSigninCompleted() || delegate_->IsSigninInProgress())) {
+ return;
+ }
+
+ ProfileMetrics::LogProfileDeleteUser(
+ ProfileMetrics::DELETE_PROFILE_USER_MANAGER);
+
+ if (!delegate_)
+ return;
+ delegate_->RemoveUser(account_id);
+ UpdateAddButtonStatus();
+}
+
+void SigninScreenHandler::HandleShowAddUser(const base::ListValue* args) {
+ TRACE_EVENT_ASYNC_STEP_INTO0("ui",
+ "ShowLoginWebUI",
+ LoginDisplayHostImpl::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();
+}
+
+void SigninScreenHandler::HandleToggleEnableDebuggingScreen() {
+ if (delegate_)
+ delegate_->ShowEnableDebuggingScreen();
+}
+
+void SigninScreenHandler::HandleToggleKioskEnableScreen() {
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ if (delegate_ && !connector->IsEnterpriseManaged() &&
+ KioskAppManager::IsConsumerKioskEnabled() &&
+ LoginDisplayHost::default_host()) {
+ delegate_->ShowKioskEnableScreen();
+ }
+}
+
+void SigninScreenHandler::HandleToggleKioskAutolaunchScreen() {
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ if (delegate_ && !connector->IsEnterpriseManaged())
+ delegate_->ShowKioskAutolaunchScreen();
+}
+
+void SigninScreenHandler::LoadUsers(const base::ListValue& users_list,
+ bool showGuest) {
+ CallJSOrDefer("login.AccountPickerScreen.loadUsers", users_list,
+ delegate_->IsShowGuest());
+}
+
+void SigninScreenHandler::HandleAccountPickerReady() {
+ VLOG(0) << "Login WebUI >> AccountPickerReady";
+
+ if (delegate_ && !ScreenLocker::default_screen_locker() &&
+ !chromeos::IsMachineHWIDCorrect() &&
+ !oobe_ui_) {
+ delegate_->ShowWrongHWIDScreen();
+ return;
+ }
+
+ PrefService* prefs = g_browser_process->local_state();
+ if (prefs->GetBoolean(prefs::kFactoryResetRequested)) {
+ if (core_oobe_view_)
+ core_oobe_view_->ShowDeviceResetScreen();
+
+ return;
+ } else if (prefs->GetBoolean(prefs::kDebuggingFeaturesRequested)) {
+ if (core_oobe_view_)
+ core_oobe_view_->ShowEnableDebuggingScreen();
+
+ return;
+ }
+
+ is_account_picker_showing_first_time_ = true;
+
+ if (ScreenLocker::default_screen_locker() &&
+ lock_screen_apps::StateController::IsEnabled()) {
+ OnLockScreenNoteStateChanged(
+ lock_screen_apps::StateController::Get()->GetLockScreenNoteState());
+ }
+ if (delegate_)
+ delegate_->OnSigninScreenReady();
+}
+
+void SigninScreenHandler::HandleWallpaperReady() {
+ if (ScreenLocker::default_screen_locker()) {
+ ScreenLocker::default_screen_locker()
+ ->delegate()
+ ->OnLockBackgroundDisplayed();
+ }
+}
+
+void SigninScreenHandler::HandleSignOutUser() {
+ if (delegate_)
+ delegate_->Signout();
+}
+
+void SigninScreenHandler::HandleOpenProxySettings() {
+ LoginDisplayHost::default_host()->OpenProxySettings();
+}
+
+void SigninScreenHandler::HandleLoginVisible(const std::string& source) {
+ VLOG(1) << "Login WebUI >> loginVisible, src: " << source << ", "
+ << "webui_visible_: " << webui_visible_;
+ if (!webui_visible_) {
+ // There might be multiple messages from OOBE UI so send notifications after
+ // the first one only.
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
+ content::NotificationService::AllSources(),
+ content::NotificationService::NoDetails());
+ TRACE_EVENT_ASYNC_END0(
+ "ui", "ShowLoginWebUI", LoginDisplayHostImpl::kShowLoginWebUIid);
+ }
+ webui_visible_ = true;
+ if (preferences_changed_delayed_)
+ OnPreferencesChanged();
+ OnAllowedInputMethodsChanged();
+}
+
+void SigninScreenHandler::HandleCancelPasswordChangedFlow(
+ const AccountId& account_id) {
+ if (account_id.is_valid()) {
+ RecordReauthReason(account_id, ReauthReason::PASSWORD_UPDATE_SKIPPED);
+ }
+ gaia_screen_handler_->StartClearingCookies(
+ base::Bind(&SigninScreenHandler::CancelPasswordChangedFlowInternal,
+ weak_factory_.GetWeakPtr()));
+}
+
+void SigninScreenHandler::HandleCancelUserAdding() {
+ if (delegate_)
+ delegate_->CancelUserAdding();
+}
+
+void SigninScreenHandler::HandleMigrateUserData(
+ const std::string& old_password) {
+ if (delegate_)
+ delegate_->MigrateUserData(old_password);
+}
+
+void SigninScreenHandler::HandleResyncUserData() {
+ if (delegate_)
+ delegate_->ResyncUserData();
+}
+
+void SigninScreenHandler::HandleLoginUIStateChanged(const std::string& source,
+ bool active) {
+ VLOG(0) << "Login WebUI >> active: " << active << ", "
+ << "source: " << source;
+
+ if (!KioskAppManager::Get()->GetAutoLaunchApp().empty() &&
+ KioskAppManager::Get()->IsAutoLaunchRequested()) {
+ VLOG(0) << "Showing auto-launch warning";
+ // 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();
+ HandleToggleKioskAutolaunchScreen();
+ return;
+ }
+
+ if (source == kSourceGaiaSignin) {
+ ui_state_ = UI_STATE_GAIA_SIGNIN;
+ } else if (source == kSourceAccountPicker) {
+ ui_state_ = UI_STATE_ACCOUNT_PICKER;
+ } else {
+ NOTREACHED();
+ return;
+ }
+}
+
+void SigninScreenHandler::HandleUnlockOnLoginSuccess() {
+ DCHECK(user_manager::UserManager::Get()->IsUserLoggedIn());
+ if (ScreenLocker::default_screen_locker())
+ ScreenLocker::default_screen_locker()->UnlockOnLoginSuccess();
+}
+
+void SigninScreenHandler::HandleShowLoadingTimeoutError() {
+ UpdateState(NetworkError::ERROR_REASON_LOADING_TIMEOUT);
+}
+
+void SigninScreenHandler::HandleFocusPod(const AccountId& account_id) {
+ proximity_auth::ScreenlockBridge::Get()->SetFocusedUser(account_id);
+ if (delegate_)
+ delegate_->CheckUserStatus(account_id);
+ if (!test_focus_pod_callback_.is_null())
+ test_focus_pod_callback_.Run();
+
+ focused_pod_account_id_ = base::MakeUnique<AccountId>(account_id);
+
+ const user_manager::User* user =
+ user_manager::UserManager::Get()->FindUser(account_id);
+ // |user| may be nullptr in kiosk mode or unit tests.
+ if (user && user->is_logged_in() && !user->is_active()) {
+ SessionControllerClient::DoSwitchActiveUser(account_id);
+ } else {
+ SetUserInputMethod(account_id.GetUserEmail(), ime_state_.get());
+ SetKeyboardSettings(account_id);
+ WallpaperManager::Get()->SetUserWallpaperDelayed(account_id);
+
+ bool use_24hour_clock = false;
+ if (user_manager::known_user::GetBooleanPref(
+ account_id, prefs::kUse24HourClock, &use_24hour_clock)) {
+ g_browser_process->platform_part()
+ ->GetSystemClock()
+ ->SetLastFocusedPodHourClockType(
+ use_24hour_clock ? base::k24HourClock : base::k12HourClock);
+ }
+ }
+}
+
+void SigninScreenHandler::HandleNoPodFocused() {
+ focused_pod_account_id_.reset();
+ EnforcePolicyInputMethods(std::string());
+}
+
+void SigninScreenHandler::HandleGetPublicSessionKeyboardLayouts(
+ const AccountId& account_id,
+ const std::string& locale) {
+ GetKeyboardLayoutsForLocale(
+ base::Bind(&SigninScreenHandler::SendPublicSessionKeyboardLayouts,
+ weak_factory_.GetWeakPtr(), account_id, locale),
+ locale);
+}
+
+void SigninScreenHandler::SendPublicSessionKeyboardLayouts(
+ const AccountId& account_id,
+ const std::string& locale,
+ std::unique_ptr<base::ListValue> keyboard_layouts) {
+ CallJS("login.AccountPickerScreen.setPublicSessionKeyboardLayouts",
+ account_id, locale, *keyboard_layouts);
+}
+
+void SigninScreenHandler::HandleLaunchKioskApp(const AccountId& app_account_id,
+ bool diagnostic_mode) {
+ UserContext context(user_manager::USER_TYPE_KIOSK_APP, app_account_id);
+ SigninSpecifics specifics;
+ specifics.kiosk_diagnostic_mode = diagnostic_mode;
+ if (delegate_)
+ delegate_->Login(context, specifics);
+}
+
+void SigninScreenHandler::HandleLaunchArcKioskApp(
+ const AccountId& app_account_id) {
+ UserContext context(user_manager::USER_TYPE_ARC_KIOSK_APP, app_account_id);
+ if (delegate_)
+ delegate_->Login(context, SigninSpecifics());
+}
+
+void SigninScreenHandler::HandleGetTouchViewState() {
+ CallJS("login.AccountPickerScreen.setTouchViewState", touch_view_enabled_);
+}
+
+void SigninScreenHandler::HandleLogRemoveUserWarningShown() {
+ ProfileMetrics::LogProfileDeleteUser(
+ ProfileMetrics::DELETE_PROFILE_USER_MANAGER_SHOW_WARNING);
+}
+
+void SigninScreenHandler::HandleFirstIncorrectPasswordAttempt(
+ const AccountId& account_id) {
+ // TODO(ginkage): Fix this case once crbug.com/469987 is ready.
+ /*
+ if (user_manager::known_user::IsUsingSAML(email))
+ RecordReauthReason(email, ReauthReason::INCORRECT_SAML_PASSWORD_ENTERED);
+ */
+}
+
+void SigninScreenHandler::HandleMaxIncorrectPasswordAttempts(
+ const AccountId& account_id) {
+ RecordReauthReason(account_id, ReauthReason::INCORRECT_PASSWORD_ENTERED);
+}
+
+void SigninScreenHandler::HandleSendFeedbackAndResyncUserData() {
+ const std::string description = base::StringPrintf(
+ "Auto generated feedback for http://crbug.com/547857.\n"
+ "(uniquifier:%s)",
+ base::Int64ToString(base::Time::Now().ToInternalValue()).c_str());
+
+ login_feedback_.reset(new LoginFeedback(Profile::FromWebUI(web_ui())));
+ login_feedback_->Request(description,
+ base::Bind(&SigninScreenHandler::OnFeedbackFinished,
+ weak_factory_.GetWeakPtr()));
+}
+
+void SigninScreenHandler::HandleSetLockScreenAppsState(
+ const std::string& state) {
+ lock_screen_apps::StateController* state_controller =
+ lock_screen_apps::StateController::Get();
+
+ if (state == kBackgroundLockScreenApps)
+ state_controller->MoveToBackground();
+ else if (state == kForegroundLockScreenApps)
+ state_controller->MoveToForeground();
+}
+
+bool SigninScreenHandler::AllWhitelistedUsersPresent() {
+ CrosSettings* cros_settings = CrosSettings::Get();
+ bool allow_new_user = false;
+ cros_settings->GetBoolean(kAccountsPrefAllowNewUser, &allow_new_user);
+ if (allow_new_user)
+ return false;
+ user_manager::UserManager* user_manager = user_manager::UserManager::Get();
+ const user_manager::UserList& users = user_manager->GetUsers();
+ if (!delegate_ || users.size() > kMaxUsers) {
+ return false;
+ }
+ const base::ListValue* whitelist = nullptr;
+ if (!cros_settings->GetList(kAccountsPrefUsers, &whitelist) || !whitelist)
+ return false;
+ for (size_t i = 0; i < whitelist->GetSize(); ++i) {
+ std::string whitelisted_user;
+ // NB: Wildcards in the whitelist are also detected as not present here.
+ if (!whitelist->GetString(i, &whitelisted_user) ||
+ !user_manager->IsKnownUser(
+ AccountId::FromUserEmail(whitelisted_user))) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void SigninScreenHandler::CancelPasswordChangedFlowInternal() {
+ if (delegate_) {
+ ShowImpl();
+ delegate_->CancelPasswordChangedFlow();
+ }
+}
+
+bool SigninScreenHandler::IsGaiaVisible() const {
+ return IsSigninScreen(GetCurrentScreen()) &&
+ ui_state_ == UI_STATE_GAIA_SIGNIN;
+}
+
+bool SigninScreenHandler::IsGaiaHiddenByError() const {
+ return IsSigninScreenHiddenByError() &&
+ ui_state_ == UI_STATE_GAIA_SIGNIN;
+}
+
+bool SigninScreenHandler::IsSigninScreenHiddenByError() const {
+ return (GetCurrentScreen() == OobeScreen::SCREEN_ERROR_MESSAGE) &&
+ (IsSigninScreen(error_screen_->GetParentScreen()));
+}
+
+bool SigninScreenHandler::IsGuestSigninAllowed() const {
+ CrosSettings* cros_settings = CrosSettings::Get();
+ if (!cros_settings)
+ return false;
+ bool allow_guest;
+ cros_settings->GetBoolean(kAccountsPrefAllowGuest, &allow_guest);
+ return allow_guest;
+}
+
+void SigninScreenHandler::OnShowAddUser() {
+ is_account_picker_showing_first_time_ = false;
+ EnforcePolicyInputMethods(std::string());
+ gaia_screen_handler_->ShowGaiaAsync();
+}
+
+net::Error SigninScreenHandler::FrameError() const {
+ return gaia_screen_handler_->frame_error();
+}
+
+void SigninScreenHandler::OnCapsLockChanged(bool enabled) {
+ caps_lock_enabled_ = enabled;
+ if (page_is_ready())
+ CallJS("login.AccountPickerScreen.setCapsLockState", caps_lock_enabled_);
+}
+
+void SigninScreenHandler::OnFeedbackFinished() {
+ CallJS("login.UnrecoverableCryptohomeErrorScreen.resumeAfterFeedbackUI");
+
+ // Recreate user's cryptohome after the feedback is attempted.
+ HandleResyncUserData();
+}
+
+void SigninScreenHandler::OnAllowedInputMethodsChanged() {
+ if (!webui_visible_)
+ return;
+
+ if (focused_pod_account_id_) {
+ std::string user_input_method =
+ GetUserLastInputMethod(focused_pod_account_id_->GetUserEmail());
+ EnforcePolicyInputMethods(user_input_method);
+ } else {
+ EnforcePolicyInputMethods(std::string());
+ }
+}
+
+void SigninScreenHandler::SetKeyboardSettings(const AccountId& account_id) {
+ bool auto_repeat_enabled = language_prefs::kXkbAutoRepeatEnabled;
+ if (user_manager::known_user::GetBooleanPref(
+ account_id, prefs::kLanguageXkbAutoRepeatEnabled,
+ &auto_repeat_enabled) &&
+ !auto_repeat_enabled) {
+ input_method::InputMethodManager::Get()
+ ->GetImeKeyboard()
+ ->SetAutoRepeatEnabled(false);
+ return;
+ }
+
+ int auto_repeat_delay = language_prefs::kXkbAutoRepeatDelayInMs;
+ int auto_repeat_interval = language_prefs::kXkbAutoRepeatIntervalInMs;
+ user_manager::known_user::GetIntegerPref(
+ account_id, prefs::kLanguageXkbAutoRepeatDelay, &auto_repeat_delay);
+ user_manager::known_user::GetIntegerPref(
+ account_id, prefs::kLanguageXkbAutoRepeatInterval, &auto_repeat_interval);
+ input_method::AutoRepeatRate rate;
+ rate.initial_delay_in_ms = auto_repeat_delay;
+ rate.repeat_interval_in_ms = auto_repeat_interval;
+ input_method::InputMethodManager::Get()
+ ->GetImeKeyboard()
+ ->SetAutoRepeatEnabled(true);
+ input_method::InputMethodManager::Get()->GetImeKeyboard()->SetAutoRepeatRate(
+ rate);
+}
+
+} // namespace chromeos
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
new file mode 100644
index 00000000000..df423f53a72
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
@@ -0,0 +1,565 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_SIGNIN_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_SIGNIN_SCREEN_HANDLER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "ash/public/interfaces/touch_view.mojom.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/chromeos/lock_screen_apps/state_observer.h"
+#include "chrome/browser/chromeos/login/screens/error_screen.h"
+#include "chrome/browser/chromeos/login/signin_specifics.h"
+#include "chrome/browser/chromeos/login/ui/login_display.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_webui_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
+#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
+#include "chromeos/dbus/power_manager_client.h"
+#include "chromeos/network/portal_detector/network_portal_detector.h"
+#include "components/proximity_auth/screenlock_bridge.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_ui.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "net/base/net_errors.h"
+#include "ui/base/ime/chromeos/ime_keyboard.h"
+#include "ui/base/ime/chromeos/input_method_manager.h"
+#include "ui/events/event_handler.h"
+
+class AccountId;
+
+namespace ash {
+namespace mojom {
+enum class TrayActionState;
+}
+}
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+namespace lock_screen_apps {
+class StateController;
+}
+
+namespace chromeos {
+
+class CoreOobeView;
+class ErrorScreensHistogramHelper;
+class GaiaScreenHandler;
+class LoginFeedback;
+class NativeWindowDelegate;
+class SupervisedUserCreationScreenHandler;
+class User;
+class UserContext;
+
+// Helper class to pass initial parameters to the login screen.
+class LoginScreenContext {
+ public:
+ LoginScreenContext();
+ explicit LoginScreenContext(const base::ListValue* args);
+
+ void set_email(const std::string& email) { email_ = email; }
+ const std::string& email() const { return email_; }
+
+ void set_oobe_ui(bool oobe_ui) { oobe_ui_ = oobe_ui; }
+ bool oobe_ui() const { return oobe_ui_; }
+
+ private:
+ void Init();
+
+ std::string email_;
+ bool oobe_ui_;
+};
+
+// An interface for WebUILoginDisplay to call SigninScreenHandler.
+class LoginDisplayWebUIHandler {
+ public:
+ virtual void ClearAndEnablePassword() = 0;
+ virtual void ClearUserPodPassword() = 0;
+ virtual void OnUserRemoved(const AccountId& account_id,
+ bool last_user_removed) = 0;
+ virtual void OnUserImageChanged(const user_manager::User& user) = 0;
+ virtual void OnPreferencesChanged() = 0;
+ virtual void ResetSigninScreenHandlerDelegate() = 0;
+ virtual void ShowError(int login_attempts,
+ const std::string& error_text,
+ const std::string& help_link_text,
+ HelpAppLauncher::HelpTopic help_topic_id) = 0;
+ virtual void ShowErrorScreen(LoginDisplay::SigninError error_id) = 0;
+ virtual void ShowSigninUI(const std::string& email) = 0;
+ virtual void ShowPasswordChangedDialog(bool show_password_error,
+ const std::string& email) = 0;
+ // Show sign-in screen for the given credentials.
+ virtual void ShowSigninScreenForCreds(const std::string& username,
+ const std::string& password) = 0;
+ virtual void ShowWhitelistCheckFailedError() = 0;
+ virtual void ShowUnrecoverableCrypthomeErrorDialog() = 0;
+ virtual void LoadUsers(const base::ListValue& users_list,
+ bool show_guest) = 0;
+ protected:
+ virtual ~LoginDisplayWebUIHandler() {}
+};
+
+// An interface for SigninScreenHandler to call WebUILoginDisplay.
+class SigninScreenHandlerDelegate {
+ public:
+ // --------------- Password change flow methods.
+ // Cancels current password changed flow.
+ virtual void CancelPasswordChangedFlow() = 0;
+
+ // Decrypt cryptohome using user provided |old_password|
+ // and migrate to new password.
+ virtual void MigrateUserData(const std::string& old_password) = 0;
+
+ // Ignore password change, remove existing cryptohome and
+ // force full sync of user data.
+ virtual void ResyncUserData() = 0;
+
+ // --------------- Sign in/out methods.
+ // Sign in using username and password specified as a part of |user_context|.
+ // Used for both known and new users.
+ virtual void Login(const UserContext& user_context,
+ const SigninSpecifics& specifics) = 0;
+
+ // Returns true if sign in is in progress.
+ virtual bool IsSigninInProgress() const = 0;
+
+ // 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;
+
+ // Shows Enable Developer Features screen.
+ virtual void ShowEnableDebuggingScreen() = 0;
+
+ // Shows Kiosk Enable screen.
+ virtual void ShowKioskEnableScreen() = 0;
+
+ // Shows Reset screen.
+ virtual void ShowKioskAutolaunchScreen() = 0;
+
+ // Show wrong hwid screen.
+ virtual void ShowWrongHWIDScreen() = 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;
+
+ // Let the delegate know about the handler it is supposed to be using.
+ virtual void SetWebUIHandler(LoginDisplayWebUIHandler* webui_handler) = 0;
+
+ // Whether login as guest is available.
+ virtual bool IsShowGuest() const = 0;
+
+ // Whether to show the user pods or only GAIA sign in.
+ // Public sessions are always shown.
+ virtual bool IsShowUsers() const = 0;
+
+ // Whether the show user pods setting has changed.
+ virtual bool ShowUsersHasChanged() const = 0;
+
+ // Whether the create new account option in GAIA is enabled by the setting.
+ virtual bool IsAllowNewUser() const = 0;
+
+ // Whether the allow new user setting has changed.
+ virtual bool AllowNewUserChanged() const = 0;
+
+ // Whether user sign in has completed.
+ virtual bool IsUserSigninCompleted() const = 0;
+
+ // Request to (re)load user list.
+ virtual void HandleGetUsers() = 0;
+
+ // 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() {}
+};
+
+// A class that handles the WebUI hooks in sign-in screen in OobeUI and
+// LoginDisplay.
+class SigninScreenHandler
+ : public BaseWebUIHandler,
+ public LoginDisplayWebUIHandler,
+ public content::NotificationObserver,
+ public NetworkStateInformer::NetworkStateInformerObserver,
+ public PowerManagerClient::Observer,
+ public input_method::ImeKeyboard::Observer,
+ public ash::mojom::TouchViewObserver,
+ public lock_screen_apps::StateObserver,
+ public OobeUI::Observer {
+ public:
+ SigninScreenHandler(
+ const scoped_refptr<NetworkStateInformer>& network_state_informer,
+ ErrorScreen* error_screen,
+ CoreOobeView* core_oobe_view,
+ GaiaScreenHandler* gaia_screen_handler,
+ JSCallsContainer* js_calls_container);
+ ~SigninScreenHandler() override;
+
+ static std::string GetUserLastInputMethod(const std::string& username);
+
+ // Update current input method (namely keyboard layout) in the given IME state
+ // to last input method used by this user.
+ static void SetUserInputMethod(
+ const std::string& username,
+ input_method::InputMethodManager::State* ime_state);
+
+ // Shows the sign in screen.
+ void Show(const LoginScreenContext& context);
+
+ // Sets delegate to be used by the handler. It is guaranteed that valid
+ // delegate is set before Show() method will be called.
+ void SetDelegate(SigninScreenHandlerDelegate* delegate);
+
+ void SetNativeWindowDelegate(NativeWindowDelegate* native_window_delegate);
+
+ // NetworkStateInformer::NetworkStateInformerObserver implementation:
+ void OnNetworkReady() override;
+ void UpdateState(NetworkError::ErrorReason reason) override;
+
+ // Required Local State preferences.
+ static void RegisterPrefs(PrefRegistrySimple* registry);
+
+ // OobeUI::Observer implemetation.
+ void OnCurrentScreenChanged(OobeScreen current_screen,
+ OobeScreen new_screen) override;
+
+ void SetFocusPODCallbackForTesting(base::Closure callback);
+
+ // To avoid spurious error messages on flaky networks, the offline message is
+ // only shown if the network is offline for a threshold number of seconds.
+ // This method reduces the threshold to zero, allowing the offline message to
+ // show instantaneously in tests.
+ void ZeroOfflineTimeoutForTesting();
+
+ // Gets the keyboard remapped pref value for |pref_name| key. Returns true if
+ // successful, otherwise returns false.
+ bool GetKeyboardRemappedPrefValue(const std::string& pref_name, int* value);
+
+ private:
+ enum UIState {
+ UI_STATE_UNKNOWN = 0,
+ UI_STATE_GAIA_SIGNIN,
+ UI_STATE_ACCOUNT_PICKER,
+ };
+
+ friend class GaiaScreenHandler;
+ friend class ReportDnsCacheClearedOnUIThread;
+ friend class SupervisedUserCreationScreenHandler;
+
+ void ShowImpl();
+
+ // Updates current UI of the signin screen according to |ui_state|
+ // argument. Optionally it can pass screen initialization data via
+ // |params| argument.
+ void UpdateUIState(UIState ui_state, base::DictionaryValue* params);
+
+ void UpdateStateInternal(NetworkError::ErrorReason reason, bool force_update);
+ void SetupAndShowOfflineMessage(NetworkStateInformer::State state,
+ NetworkError::ErrorReason reason);
+ void HideOfflineMessage(NetworkStateInformer::State state,
+ NetworkError::ErrorReason reason);
+ void ReloadGaia(bool force_reload);
+
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void Initialize() override;
+ gfx::NativeWindow GetNativeWindow() override;
+
+ // WebUIMessageHandler implementation:
+ void RegisterMessages() override;
+
+ // LoginDisplayWebUIHandler implementation:
+ void ClearAndEnablePassword() override;
+ void ClearUserPodPassword() override;
+ void OnUserRemoved(const AccountId& account_id,
+ bool last_user_removed) override;
+ void OnUserImageChanged(const user_manager::User& user) override;
+ void OnPreferencesChanged() override;
+ void ResetSigninScreenHandlerDelegate() override;
+ void ShowError(int login_attempts,
+ const std::string& error_text,
+ const std::string& help_link_text,
+ HelpAppLauncher::HelpTopic help_topic_id) override;
+ void ShowSigninUI(const std::string& email) override;
+ void ShowPasswordChangedDialog(bool show_password_error,
+ const std::string& email) override;
+ void ShowErrorScreen(LoginDisplay::SigninError error_id) override;
+ void ShowSigninScreenForCreds(const std::string& username,
+ const std::string& password) override;
+ void ShowWhitelistCheckFailedError() override;
+ void ShowUnrecoverableCrypthomeErrorDialog() override;
+ void LoadUsers(const base::ListValue& users_list, bool show_guest) override;
+
+ // content::NotificationObserver implementation:
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ // PowerManagerClient::Observer implementation:
+ void SuspendDone(const base::TimeDelta& sleep_duration) override;
+
+ // ash::mojom::TouchView:
+ void OnTouchViewToggled(bool enabled) override;
+
+ // lock_screen_apps::StateObserver:
+ void OnLockScreenNoteStateChanged(ash::mojom::TrayActionState state) override;
+
+ void UpdateAddButtonStatus();
+
+ // Restore input focus to current user pod.
+ void RefocusCurrentPod();
+
+ // Enable or disable the pin keyboard for the given account.
+ void UpdatePinKeyboardState(const AccountId& account_id);
+
+ // WebUI message handlers.
+ void HandleGetUsers();
+ void HandleAuthenticateUser(const AccountId& account_id,
+ const std::string& password,
+ bool authenticated_by_pin);
+ void HandleAttemptUnlock(const std::string& username);
+ void HandleLaunchIncognito();
+ void HandleLaunchPublicSession(const AccountId& account_id,
+ const std::string& locale,
+ const std::string& input_method);
+ void HandleOfflineLogin(const base::ListValue* args);
+ void HandleShutdownSystem();
+ void HandleLoadWallpaper(const AccountId& account_id);
+ void HandleRebootSystem();
+ void HandleRemoveUser(const AccountId& account_id);
+ void HandleShowAddUser(const base::ListValue* args);
+ void HandleToggleEnrollmentScreen();
+ void HandleToggleEnrollmentAd();
+ void HandleToggleEnableDebuggingScreen();
+ void HandleToggleKioskEnableScreen();
+ void HandleToggleResetScreen();
+ void HandleToggleKioskAutolaunchScreen();
+ void HandleAccountPickerReady();
+ void HandleWallpaperReady();
+ void HandleSignOutUser();
+ void HandleOpenProxySettings();
+ void HandleLoginVisible(const std::string& source);
+ void HandleCancelPasswordChangedFlow(const AccountId& account_id);
+ void HandleCancelUserAdding();
+ void HandleMigrateUserData(const std::string& password);
+ void HandleResyncUserData();
+ void HandleLoginUIStateChanged(const std::string& source, bool active);
+ void HandleUnlockOnLoginSuccess();
+ void HandleLoginScreenUpdate();
+ void HandleShowLoadingTimeoutError();
+ void HandleShowSupervisedUserCreationScreen();
+ void HandleFocusPod(const AccountId& account_id);
+ void HandleNoPodFocused();
+ void HandleHardlockPod(const std::string& user_id);
+ void HandleLaunchKioskApp(const AccountId& app_account_id,
+ bool diagnostic_mode);
+ void HandleLaunchArcKioskApp(const AccountId& app_account_id);
+ void HandleGetPublicSessionKeyboardLayouts(const AccountId& account_id,
+ const std::string& locale);
+ void HandleGetTouchViewState();
+ void HandleLogRemoveUserWarningShown();
+ void HandleFirstIncorrectPasswordAttempt(const AccountId& account_id);
+ void HandleMaxIncorrectPasswordAttempts(const AccountId& account_id);
+ void HandleSendFeedbackAndResyncUserData();
+ void HandleSetLockScreenAppsState(const std::string& state);
+
+ // Sends the list of |keyboard_layouts| available for the |locale| that is
+ // currently selected for the public session identified by |user_id|.
+ void SendPublicSessionKeyboardLayouts(
+ const AccountId& account_id,
+ const std::string& locale,
+ std::unique_ptr<base::ListValue> keyboard_layouts);
+
+ // Returns true iff
+ // (i) log in is restricted to some user list,
+ // (ii) all users in the restricted list are present.
+ bool AllWhitelistedUsersPresent();
+
+ // Cancels password changed flow - switches back to login screen.
+ // Called as a callback after cookies are cleared.
+ void CancelPasswordChangedFlowInternal();
+
+ // Returns true if current visible screen is the Gaia sign-in page.
+ bool IsGaiaVisible() const;
+
+ // Returns true if current visible screen is the error screen over
+ // Gaia sign-in page.
+ bool IsGaiaHiddenByError() const;
+
+ // Returns true if current screen is the error screen over signin
+ // screen.
+ bool IsSigninScreenHiddenByError() const;
+
+ // Returns true if guest signin is allowed.
+ bool IsGuestSigninAllowed() const;
+
+ bool ShouldLoadGaia() const;
+
+ // Shows signin.
+ void OnShowAddUser();
+
+ net::Error FrameError() const;
+
+ // input_method::ImeKeyboard::Observer implementation:
+ void OnCapsLockChanged(bool enabled) override;
+
+ // Callback invoked after the feedback is finished.
+ void OnFeedbackFinished();
+
+ // Called when the cros property controlling allowed input methods changes.
+ void OnAllowedInputMethodsChanged();
+
+ // Update the keyboard settings for |account_id|.
+ void SetKeyboardSettings(const AccountId& account_id);
+
+ // Current UI state of the signin screen.
+ UIState ui_state_ = UI_STATE_UNKNOWN;
+
+ // A delegate that glues this handler with backend LoginDisplay.
+ SigninScreenHandlerDelegate* delegate_ = nullptr;
+
+ // A delegate used to get gfx::NativeWindow.
+ NativeWindowDelegate* native_window_delegate_ = nullptr;
+
+ // Whether screen should be shown right after initialization.
+ bool show_on_init_ = false;
+
+ // Keeps whether screen should be shown for OOBE.
+ bool oobe_ui_ = false;
+
+ // Is account picker being shown for the first time.
+ bool is_account_picker_showing_first_time_ = false;
+
+ // Network state informer used to keep signin screen up.
+ scoped_refptr<NetworkStateInformer> network_state_informer_;
+
+ // Set to true once |LOGIN_WEBUI_VISIBLE| notification is observed.
+ bool webui_visible_ = false;
+ bool preferences_changed_delayed_ = false;
+
+ ErrorScreen* error_screen_ = nullptr;
+ CoreOobeView* core_oobe_view_ = nullptr;
+
+ NetworkStateInformer::State last_network_state_ =
+ NetworkStateInformer::UNKNOWN;
+
+ base::CancelableClosure update_state_closure_;
+ base::CancelableClosure connecting_closure_;
+
+ content::NotificationRegistrar registrar_;
+
+ std::unique_ptr<CrosSettings::ObserverSubscription>
+ allowed_input_methods_subscription_;
+
+ // Whether there is an auth UI pending. This flag is set on receiving
+ // NOTIFICATION_AUTH_NEEDED and reset on either NOTIFICATION_AUTH_SUPPLIED or
+ // NOTIFICATION_AUTH_CANCELLED.
+ bool has_pending_auth_ui_ = false;
+
+ // Used for pending GAIA reloads.
+ NetworkError::ErrorReason gaia_reload_reason_ =
+ NetworkError::ERROR_REASON_NONE;
+
+ bool caps_lock_enabled_ = false;
+
+ // If network has accidentally changed to the one that requires proxy
+ // authentication, we will automatically reload gaia page that will bring
+ // "Proxy authentication" dialog to the user. To prevent flakiness, we will do
+ // it at most 3 times.
+ int proxy_auth_dialog_reload_times_;
+
+ // True if we need to reload gaia page to bring back "Proxy authentication"
+ // dialog.
+ bool proxy_auth_dialog_need_reload_ = false;
+
+ // Non-owning ptr.
+ // TODO(antrim@): remove this dependency.
+ GaiaScreenHandler* gaia_screen_handler_ = nullptr;
+
+ mojo::Binding<ash::mojom::TouchViewObserver> touch_view_binding_;
+ ash::mojom::TouchViewManagerPtr touch_view_manager_ptr_;
+ bool touch_view_enabled_ = false;
+
+ // Input Method Engine state used at signin screen.
+ scoped_refptr<input_method::InputMethodManager::State> ime_state_;
+
+ // This callback captures "focusPod finished" event for tests.
+ base::Closure test_focus_pod_callback_;
+
+ // True if SigninScreenHandler has already been added to OobeUI observers.
+ bool oobe_ui_observer_added_ = false;
+
+ bool zero_offline_timeout_for_test_ = false;
+
+ std::unique_ptr<ErrorScreensHistogramHelper> histogram_helper_;
+
+ std::unique_ptr<LoginFeedback> login_feedback_;
+
+ std::unique_ptr<AccountId> focused_pod_account_id_;
+
+ ScopedObserver<lock_screen_apps::StateController,
+ lock_screen_apps::StateObserver>
+ lock_screen_apps_observer_;
+
+ base::WeakPtrFactory<SigninScreenHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SigninScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_SIGNIN_SCREEN_HANDLER_H_
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
new file mode 100644
index 00000000000..c19b3ebe63c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/signin_userlist_unittest.cc
@@ -0,0 +1,133 @@
+// 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 <stddef.h>
+
+#include "ash/test/ash_test_base.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/screens/user_selection_screen.h"
+#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/scoped_user_manager_enabler.h"
+#include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/proximity_auth/screenlock_bridge.h"
+#include "components/signin/core/account_id/account_id.h"
+#include "components/user_manager/user.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const size_t kMaxUsers = 18; // same as in user_selection_screen.cc
+const char* kOwner = "owner@gmail.com";
+const char* kUsersPublic[] = {"public0@gmail.com", "public1@gmail.com"};
+const char* kUsers[] = {
+ "a0@gmail.com", "a1@gmail.com", "a2@gmail.com", "a3@gmail.com",
+ "a4@gmail.com", "a5@gmail.com", "a6@gmail.com", "a7@gmail.com",
+ "a8@gmail.com", "a9@gmail.com", "a10@gmail.com", "a11@gmail.com",
+ "a12@gmail.com", "a13@gmail.com", "a14@gmail.com", "a15@gmail.com",
+ "a16@gmail.com", "a17@gmail.com", kOwner, "a18@gmail.com"};
+
+} // namespace
+
+namespace chromeos {
+
+class SigninPrepareUserListTest : public ash::test::AshTestBase,
+ public MultiProfileUserControllerDelegate {
+ public:
+ SigninPrepareUserListTest()
+ : fake_user_manager_(new FakeChromeUserManager()),
+ user_manager_enabler_(fake_user_manager_) {}
+
+ ~SigninPrepareUserListTest() override {}
+
+ void SetUp() override {
+ ash::test::AshTestBase::SetUp();
+ profile_manager_.reset(
+ new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
+ ASSERT_TRUE(profile_manager_->SetUp());
+ controller_.reset(new MultiProfileUserController(
+ this, TestingBrowserProcess::GetGlobal()->local_state()));
+ fake_user_manager_->set_multi_profile_user_controller(controller_.get());
+
+ for (size_t i = 0; i < arraysize(kUsersPublic); ++i)
+ fake_user_manager_->AddPublicAccountUser(
+ AccountId::FromUserEmail(kUsersPublic[i]));
+
+ for (size_t i = 0; i < arraysize(kUsers); ++i)
+ fake_user_manager_->AddUser(AccountId::FromUserEmail(kUsers[i]));
+
+ fake_user_manager_->set_owner_id(AccountId::FromUserEmail(kOwner));
+
+ chromeos::WallpaperManager::Initialize();
+ }
+
+ void TearDown() override {
+ chromeos::WallpaperManager::Shutdown();
+ controller_.reset();
+ profile_manager_.reset();
+ ash::test::AshTestBase::TearDown();
+ }
+
+ // MultiProfileUserControllerDelegate overrides:
+ void OnUserNotAllowed(const std::string& user_email) override {}
+
+ FakeChromeUserManager* fake_user_manager_;
+ ScopedUserManagerEnabler user_manager_enabler_;
+ std::unique_ptr<TestingProfileManager> profile_manager_;
+ std::map<std::string, proximity_auth::ScreenlockBridge::LockHandler::AuthType>
+ user_auth_type_map;
+ std::unique_ptr<MultiProfileUserController> controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(SigninPrepareUserListTest);
+};
+
+TEST_F(SigninPrepareUserListTest, AlwaysKeepOwnerInList) {
+ EXPECT_LT(kMaxUsers, fake_user_manager_->GetUsers().size());
+ user_manager::UserList users_to_send =
+ UserSelectionScreen::PrepareUserListForSending(
+ fake_user_manager_->GetUsers(), AccountId::FromUserEmail(kOwner),
+ true /* is signin to add */);
+
+ EXPECT_EQ(kMaxUsers, users_to_send.size());
+ EXPECT_EQ(kOwner, users_to_send.back()->GetAccountId().GetUserEmail());
+
+ fake_user_manager_->RemoveUserFromList(
+ AccountId::FromUserEmail("a16@gmail.com"));
+ fake_user_manager_->RemoveUserFromList(
+ AccountId::FromUserEmail("a17@gmail.com"));
+ users_to_send = UserSelectionScreen::PrepareUserListForSending(
+ fake_user_manager_->GetUsers(), AccountId::FromUserEmail(kOwner),
+ true /* is signin to add */);
+
+ EXPECT_EQ(kMaxUsers, users_to_send.size());
+ EXPECT_EQ("a18@gmail.com",
+ users_to_send.back()->GetAccountId().GetUserEmail());
+ EXPECT_EQ(kOwner,
+ users_to_send[kMaxUsers - 2]->GetAccountId().GetUserEmail());
+}
+
+TEST_F(SigninPrepareUserListTest, PublicAccounts) {
+ user_manager::UserList users_to_send =
+ UserSelectionScreen::PrepareUserListForSending(
+ fake_user_manager_->GetUsers(), AccountId::FromUserEmail(kOwner),
+ true /* is signin to add */);
+
+ EXPECT_EQ(kMaxUsers, users_to_send.size());
+ EXPECT_EQ("a0@gmail.com",
+ users_to_send.front()->GetAccountId().GetUserEmail());
+
+ users_to_send = UserSelectionScreen::PrepareUserListForSending(
+ fake_user_manager_->GetUsers(), AccountId::FromUserEmail(kOwner),
+ false /* is signin to add */);
+
+ EXPECT_EQ(kMaxUsers, users_to_send.size());
+ EXPECT_EQ("public0@gmail.com",
+ users_to_send.front()->GetAccountId().GetUserEmail());
+}
+
+} // namespace chromeos
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
new file mode 100644
index 00000000000..840bb5a7fe5
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc
@@ -0,0 +1,477 @@
+// 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/ui/webui/chromeos/login/supervised_user_creation_screen_handler.h"
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/browser/chromeos/login/screens/user_selection_screen.h"
+#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/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/audio/chromeos_sounds.h"
+#include "components/login/localized_values_builder.h"
+#include "components/signin/core/account_id/account_id.h"
+#include "components/user_manager/user_manager.h"
+#include "components/user_manager/user_type.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+#include "media/audio/sounds/sounds_manager.h"
+#include "net/base/data_url.h"
+#include "net/base/escape.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+
+const char kJsScreenPath[] = "login.SupervisedUserCreationScreen";
+
+namespace chromeos {
+
+SupervisedUserCreationScreenHandler::SupervisedUserCreationScreenHandler()
+ : BaseScreenHandler(OobeScreen::SCREEN_CREATE_SUPERVISED_USER_FLOW) {
+ set_call_js_prefix(kJsScreenPath);
+ ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+ media::SoundsManager* manager = media::SoundsManager::Get();
+ manager->Initialize(SOUND_OBJECT_DELETE,
+ bundle.GetRawDataResource(IDR_SOUND_OBJECT_DELETE_WAV));
+ manager->Initialize(SOUND_CAMERA_SNAP,
+ bundle.GetRawDataResource(IDR_SOUND_CAMERA_SNAP_WAV));
+}
+
+SupervisedUserCreationScreenHandler::~SupervisedUserCreationScreenHandler() {
+ if (delegate_) {
+ delegate_->OnViewDestroyed(this);
+ }
+}
+
+void SupervisedUserCreationScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add(
+ "supervisedUserCreationFlowRetryButtonTitle",
+ IDS_CREATE_SUPERVISED_USER_CREATION_ERROR_RETRY_BUTTON_TITLE);
+ builder->Add(
+ "supervisedUserCreationFlowCancelButtonTitle",
+ IDS_CREATE_SUPERVISED_USER_CREATION_ERROR_CANCEL_BUTTON_TITLE);
+ builder->Add(
+ "supervisedUserCreationFlowGotitButtonTitle",
+ IDS_CREATE_SUPERVISED_USER_CREATION_GOT_IT_BUTTON_TITLE);
+
+ builder->Add("createSupervisedUserIntroTextTitle",
+ IDS_CREATE_SUPERVISED_INTRO_TEXT_TITLE);
+ builder->Add("createSupervisedUserIntroAlternateText",
+ IDS_CREATE_SUPERVISED_INTRO_ALTERNATE_TEXT);
+ builder->Add("createSupervisedUserIntroText1",
+ IDS_CREATE_SUPERVISED_INTRO_TEXT_1);
+ builder->Add("createSupervisedUserIntroManagerItem1",
+ IDS_CREATE_SUPERVISED_INTRO_MANAGER_ITEM_1);
+ builder->Add("createSupervisedUserIntroManagerItem2",
+ IDS_CREATE_SUPERVISED_INTRO_MANAGER_ITEM_2);
+ builder->Add("createSupervisedUserIntroManagerItem3",
+ IDS_CREATE_SUPERVISED_INTRO_MANAGER_ITEM_3);
+ builder->Add("createSupervisedUserIntroText2",
+ IDS_CREATE_SUPERVISED_INTRO_TEXT_2);
+ builder->AddF("createSupervisedUserIntroText3",
+ IDS_CREATE_SUPERVISED_INTRO_TEXT_3,
+ base::UTF8ToUTF16(
+ chrome::kLegacySupervisedUserManagementDisplayURL));
+
+ builder->Add("createSupervisedUserPickManagerTitle",
+ IDS_CREATE_SUPERVISED_USER_CREATE_PICK_MANAGER_TITLE);
+ builder->AddF("createSupervisedUserPickManagerTitleExplanation",
+ IDS_CREATE_SUPERVISED_USER_CREATE_PICK_MANAGER_EXPLANATION,
+ base::UTF8ToUTF16(
+ chrome::kLegacySupervisedUserManagementDisplayURL));
+ builder->Add("createSupervisedUserManagerPasswordHint",
+ IDS_CREATE_SUPERVISED_USER_CREATE_MANAGER_PASSWORD_HINT);
+ builder->Add("createSupervisedUserWrongManagerPasswordText",
+ IDS_CREATE_SUPERVISED_USER_MANAGER_PASSWORD_ERROR);
+
+ builder->Add("createSupervisedUserNameTitle",
+ IDS_CREATE_SUPERVISED_USER_CREATE_ACCOUNT_NAME_TITLE);
+ builder->Add("createSupervisedUserNameAccessibleTitle",
+ IDS_CREATE_SUPERVISED_USER_SETUP_ACCESSIBLE_TITLE);
+ builder->Add("createSupervisedUserNameExplanation",
+ IDS_CREATE_SUPERVISED_USER_CREATE_ACCOUNT_NAME_EXPLANATION);
+ builder->Add("createSupervisedUserNameHint",
+ IDS_CREATE_SUPERVISED_USER_CREATE_ACCOUNT_NAME_HINT);
+ builder->Add("createSupervisedUserPasswordTitle",
+ IDS_CREATE_SUPERVISED_USER_CREATE_PASSWORD_TITLE);
+ builder->Add("createSupervisedUserPasswordExplanation",
+ IDS_CREATE_SUPERVISED_USER_CREATE_PASSWORD_EXPLANATION);
+ builder->Add("createSupervisedUserPasswordHint",
+ IDS_CREATE_SUPERVISED_USER_CREATE_PASSWORD_HINT);
+ builder->Add("createSupervisedUserPasswordConfirmHint",
+ IDS_CREATE_SUPERVISED_USER_CREATE_PASSWORD_CONFIRM_HINT);
+ builder->Add("supervisedUserCreationFlowProceedButtonTitle",
+ IDS_CREATE_SUPERVISED_USER_CREATE_CONTINUE_BUTTON_TEXT);
+ builder->Add("supervisedUserCreationFlowStartButtonTitle",
+ IDS_CREATE_SUPERVISED_USER_CREATE_START_BUTTON_TEXT);
+ builder->Add("supervisedUserCreationFlowPrevButtonTitle",
+ IDS_CREATE_SUPERVISED_USER_CREATE_PREVIOUS_BUTTON_TEXT);
+ builder->Add("supervisedUserCreationFlowNextButtonTitle",
+ IDS_CREATE_SUPERVISED_USER_CREATE_NEXT_BUTTON_TEXT);
+ builder->Add("supervisedUserCreationFlowHandleErrorButtonTitle",
+ IDS_CREATE_SUPERVISED_USER_CREATE_HANDLE_ERROR_BUTTON_TEXT);
+ builder->Add("createSupervisedUserPasswordMismatchError",
+ IDS_CREATE_SUPERVISED_USER_CREATE_PASSWORD_MISMATCH_ERROR);
+
+ builder->Add("createSupervisedUserCreatedText1",
+ IDS_CREATE_SUPERVISED_USER_CREATED_1_TEXT_1);
+ builder->Add("createSupervisedUserCreatedText2",
+ IDS_CREATE_SUPERVISED_USER_CREATED_1_TEXT_2);
+ builder->Add("createSupervisedUserCreatedText3",
+ IDS_CREATE_SUPERVISED_USER_CREATED_1_TEXT_3);
+
+ builder->Add("importExistingSupervisedUserTitle",
+ IDS_IMPORT_EXISTING_LEGACY_SUPERVISED_USER_TITLE);
+ builder->Add("importSupervisedUserLink",
+ IDS_IMPORT_EXISTING_LEGACY_SUPERVISED_USER_TITLE);
+ builder->Add("importExistingSupervisedUserText",
+ IDS_IMPORT_EXISTING_LEGACY_SUPERVISED_USER_TEXT);
+ builder->Add("supervisedUserCreationFlowImportButtonTitle",
+ IDS_IMPORT_EXISTING_LEGACY_SUPERVISED_USER_OK);
+ builder->Add("createSupervisedUserLink",
+ IDS_CREATE_NEW_LEGACY_SUPERVISED_USER_LINK);
+ builder->Add("importBubbleText",
+ IDS_SUPERVISED_USER_IMPORT_BUBBLE_TEXT);
+ builder->Add("importUserExists",
+ IDS_SUPERVISED_USER_IMPORT_USER_EXIST);
+ builder->Add("importUsernameExists",
+ IDS_SUPERVISED_USER_IMPORT_USERNAME_EXIST);
+
+ builder->Add("managementURL",
+ chrome::kLegacySupervisedUserManagementDisplayURL);
+
+ // TODO(antrim) : this is an explicit code duplications with UserImageScreen.
+ // It should be removed by issue 251179.
+ builder->Add("takePhoto", IDS_OPTIONS_CHANGE_PICTURE_TAKE_PHOTO);
+ builder->Add("discardPhoto", IDS_OPTIONS_CHANGE_PICTURE_DISCARD_PHOTO);
+ builder->Add("flipPhoto", IDS_OPTIONS_CHANGE_PICTURE_FLIP_PHOTO);
+ builder->Add("photoFlippedAccessibleText",
+ IDS_OPTIONS_PHOTO_FLIP_ACCESSIBLE_TEXT);
+ builder->Add("photoFlippedBackAccessibleText",
+ IDS_OPTIONS_PHOTO_FLIPBACK_ACCESSIBLE_TEXT);
+ builder->Add("photoCaptureAccessibleText",
+ IDS_OPTIONS_PHOTO_CAPTURE_ACCESSIBLE_TEXT);
+ builder->Add("photoDiscardAccessibleText",
+ IDS_OPTIONS_PHOTO_DISCARD_ACCESSIBLE_TEXT);
+}
+
+void SupervisedUserCreationScreenHandler::Initialize() {}
+
+void SupervisedUserCreationScreenHandler::RegisterMessages() {
+ AddCallback("finishLocalSupervisedUserCreation",
+ &SupervisedUserCreationScreenHandler::
+ HandleFinishLocalSupervisedUserCreation);
+ AddCallback("abortLocalSupervisedUserCreation",
+ &SupervisedUserCreationScreenHandler::
+ HandleAbortLocalSupervisedUserCreation);
+ AddCallback("hideLocalSupervisedUserCreation",
+ &SupervisedUserCreationScreenHandler::
+ HandleHideLocalSupervisedUserCreation);
+ AddCallback("checkSupervisedUserName",
+ &SupervisedUserCreationScreenHandler::
+ HandleCheckSupervisedUserName);
+ AddCallback("authenticateManagerInSupervisedUserCreationFlow",
+ &SupervisedUserCreationScreenHandler::
+ HandleAuthenticateManager);
+ AddCallback("specifySupervisedUserCreationFlowUserData",
+ &SupervisedUserCreationScreenHandler::
+ HandleCreateSupervisedUser);
+ AddCallback("managerSelectedOnSupervisedUserCreationFlow",
+ &SupervisedUserCreationScreenHandler::
+ HandleManagerSelected);
+ AddCallback("userSelectedForImportInSupervisedUserCreationFlow",
+ &SupervisedUserCreationScreenHandler::
+ HandleImportUserSelected);
+ AddCallback("importSupervisedUser",
+ &SupervisedUserCreationScreenHandler::
+ HandleImportSupervisedUser);
+ AddCallback("importSupervisedUserWithPassword",
+ &SupervisedUserCreationScreenHandler::
+ HandleImportSupervisedUserWithPassword);
+
+
+ // TODO(antrim) : this is an explicit code duplications with UserImageScreen.
+ // It should be removed by issue 251179.
+ AddCallback("supervisedUserGetImages",
+ &SupervisedUserCreationScreenHandler::HandleGetImages);
+
+ AddCallback("supervisedUserPhotoTaken",
+ &SupervisedUserCreationScreenHandler::HandlePhotoTaken);
+ AddCallback("supervisedUserTakePhoto",
+ &SupervisedUserCreationScreenHandler::HandleTakePhoto);
+ AddCallback("supervisedUserDiscardPhoto",
+ &SupervisedUserCreationScreenHandler::HandleDiscardPhoto);
+ AddCallback("supervisedUserSelectImage",
+ &SupervisedUserCreationScreenHandler::HandleSelectImage);
+ AddCallback("currentSupervisedUserPage",
+ &SupervisedUserCreationScreenHandler::
+ HandleCurrentSupervisedUserPage);
+}
+
+void SupervisedUserCreationScreenHandler::Show() {
+ std::unique_ptr<base::DictionaryValue> data(new base::DictionaryValue());
+ std::unique_ptr<base::ListValue> users_list(new base::ListValue());
+ const user_manager::UserList& users =
+ ChromeUserManager::Get()->GetUsersAllowedForSupervisedUsersCreation();
+ std::string owner;
+ chromeos::CrosSettings::Get()->GetString(chromeos::kDeviceOwner, &owner);
+
+ for (user_manager::UserList::const_iterator it = users.begin();
+ it != users.end();
+ ++it) {
+ bool is_owner = ((*it)->GetAccountId().GetUserEmail() == owner);
+ auto user_dict = base::MakeUnique<base::DictionaryValue>();
+ UserSelectionScreen::FillUserDictionary(
+ *it, is_owner, false, /* is_signin_to_add */
+ proximity_auth::ScreenlockBridge::LockHandler::OFFLINE_PASSWORD,
+ NULL, /* public_session_recommended_locales */
+ user_dict.get());
+ users_list->Append(std::move(user_dict));
+ }
+ data->Set("managers", std::move(users_list));
+ ShowScreenWithData(OobeScreen::SCREEN_CREATE_SUPERVISED_USER_FLOW,
+ data.get());
+
+ if (!delegate_)
+ return;
+}
+
+void SupervisedUserCreationScreenHandler::Hide() {
+}
+
+void SupervisedUserCreationScreenHandler::ShowIntroPage() {
+ CallJS("showIntroPage");
+}
+
+void SupervisedUserCreationScreenHandler::ShowManagerPasswordError() {
+ CallJS("showManagerPasswordError");
+}
+
+void SupervisedUserCreationScreenHandler::ShowStatusMessage(
+ bool is_progress,
+ const base::string16& message) {
+ if (is_progress)
+ CallJS("showProgress", message);
+ else
+ CallJS("showStatusError", message);
+}
+
+void SupervisedUserCreationScreenHandler::ShowUsernamePage() {
+ CallJS("showUsernamePage");
+}
+
+void SupervisedUserCreationScreenHandler::ShowTutorialPage() {
+ CallJS("showTutorialPage");
+}
+
+void SupervisedUserCreationScreenHandler::ShowErrorPage(
+ const base::string16& title,
+ const base::string16& message,
+ const base::string16& button_text) {
+ CallJS("showErrorPage", title, message, button_text);
+}
+
+void SupervisedUserCreationScreenHandler::SetDelegate(Delegate* delegate) {
+ delegate_ = delegate;
+}
+
+void SupervisedUserCreationScreenHandler::
+ HandleFinishLocalSupervisedUserCreation() {
+ delegate_->FinishFlow();
+}
+
+void SupervisedUserCreationScreenHandler::
+ HandleAbortLocalSupervisedUserCreation() {
+ delegate_->AbortFlow();
+}
+
+void SupervisedUserCreationScreenHandler::
+ HandleHideLocalSupervisedUserCreation() {
+ delegate_->HideFlow();
+}
+
+void SupervisedUserCreationScreenHandler::HandleManagerSelected(
+ const AccountId& manager_id) {
+ if (!delegate_)
+ return;
+ WallpaperManager::Get()->SetUserWallpaperNow(manager_id);
+}
+
+void SupervisedUserCreationScreenHandler::HandleImportUserSelected(
+ const AccountId& account_id) {
+ if (!delegate_)
+ return;
+}
+
+void SupervisedUserCreationScreenHandler::HandleCheckSupervisedUserName(
+ const base::string16& name) {
+ std::string user_id;
+ if (NULL !=
+ ChromeUserManager::Get()->GetSupervisedUserManager()->FindByDisplayName(
+ base::CollapseWhitespace(name, true))) {
+ CallJS("supervisedUserNameError", name,
+ l10n_util::GetStringUTF16(
+ IDS_CREATE_SUPERVISED_USER_CREATE_USERNAME_ALREADY_EXISTS));
+ } else if (net::EscapeForHTML(name) != name) {
+ CallJS("supervisedUserNameError", name,
+ l10n_util::GetStringUTF16(
+ IDS_CREATE_SUPERVISED_USER_CREATE_ILLEGAL_USERNAME));
+ } else if (delegate_ && delegate_->FindUserByDisplayName(
+ base::CollapseWhitespace(name, true), &user_id)) {
+ CallJS("supervisedUserSuggestImport", name, user_id);
+ } else {
+ CallJS("supervisedUserNameOk", name);
+ }
+}
+
+void SupervisedUserCreationScreenHandler::HandleCreateSupervisedUser(
+ const base::string16& new_raw_user_name,
+ const std::string& new_user_password) {
+ if (!delegate_)
+ return;
+ const base::string16 new_user_name =
+ base::CollapseWhitespace(new_raw_user_name, true);
+ if (NULL !=
+ ChromeUserManager::Get()->GetSupervisedUserManager()->FindByDisplayName(
+ new_user_name)) {
+ CallJS("supervisedUserNameError", new_user_name,
+ l10n_util::GetStringFUTF16(
+ IDS_CREATE_SUPERVISED_USER_CREATE_USERNAME_ALREADY_EXISTS,
+ new_user_name));
+ return;
+ }
+ if (net::EscapeForHTML(new_user_name) != new_user_name) {
+ CallJS("supervisedUserNameError", new_user_name,
+ l10n_util::GetStringUTF16(
+ IDS_CREATE_SUPERVISED_USER_CREATE_ILLEGAL_USERNAME));
+ return;
+ }
+
+ if (new_user_password.length() == 0) {
+ CallJS("showPasswordError",
+ l10n_util::GetStringUTF16(
+ IDS_CREATE_SUPERVISED_USER_CREATE_PASSWORD_TOO_SHORT));
+ return;
+ }
+
+ ShowStatusMessage(true /* progress */, l10n_util::GetStringUTF16(
+ IDS_CREATE_SUPERVISED_USER_CREATION_CREATION_PROGRESS_MESSAGE));
+
+ delegate_->CreateSupervisedUser(new_user_name, new_user_password);
+}
+
+void SupervisedUserCreationScreenHandler::HandleImportSupervisedUser(
+ const AccountId& account_id) {
+ if (!delegate_)
+ return;
+
+ ShowStatusMessage(true /* progress */, l10n_util::GetStringUTF16(
+ IDS_CREATE_SUPERVISED_USER_CREATION_CREATION_PROGRESS_MESSAGE));
+
+ delegate_->ImportSupervisedUser(account_id.GetUserEmail());
+}
+
+void SupervisedUserCreationScreenHandler::
+ HandleImportSupervisedUserWithPassword(const AccountId& account_id,
+ const std::string& password) {
+ if (!delegate_)
+ return;
+
+ ShowStatusMessage(true /* progress */, l10n_util::GetStringUTF16(
+ IDS_CREATE_SUPERVISED_USER_CREATION_CREATION_PROGRESS_MESSAGE));
+
+ delegate_->ImportSupervisedUserWithPassword(account_id.GetUserEmail(),
+ password);
+}
+
+void SupervisedUserCreationScreenHandler::HandleAuthenticateManager(
+ const AccountId& manager_raw_account_id,
+ const std::string& manager_password) {
+ const AccountId manager_account_id = AccountId::FromUserEmailGaiaId(
+ gaia::SanitizeEmail(manager_raw_account_id.GetUserEmail()),
+ manager_raw_account_id.GetGaiaId());
+ delegate_->AuthenticateManager(manager_account_id, manager_password);
+}
+
+// TODO(antrim) : this is an explicit code duplications with UserImageScreen.
+// It should be removed by issue 251179.
+void SupervisedUserCreationScreenHandler::HandleGetImages() {
+ base::ListValue image_urls;
+ for (int i = default_user_image::kFirstDefaultImageIndex;
+ i < default_user_image::kDefaultImagesCount; ++i) {
+ std::unique_ptr<base::DictionaryValue> image_data(
+ new base::DictionaryValue);
+ image_data->SetString("url", default_user_image::GetDefaultImageUrl(i));
+ image_data->SetString("author",
+ l10n_util::GetStringUTF16(
+ default_user_image::kDefaultImageAuthorIDs[i]));
+ image_data->SetString("website",
+ l10n_util::GetStringUTF16(
+ default_user_image::kDefaultImageWebsiteIDs[i]));
+ image_data->SetString("title",
+ default_user_image::GetDefaultImageDescription(i));
+ image_urls.Append(std::move(image_data));
+ }
+ CallJS("setDefaultImages", image_urls);
+}
+
+void SupervisedUserCreationScreenHandler::HandlePhotoTaken
+ (const std::string& image_url) {
+ std::string mime_type, charset, raw_data;
+ if (!net::DataURL::Parse(GURL(image_url), &mime_type, &charset, &raw_data))
+ NOTREACHED();
+ DCHECK_EQ("image/png", mime_type);
+
+ if (delegate_)
+ delegate_->OnPhotoTaken(raw_data);
+}
+
+void SupervisedUserCreationScreenHandler::HandleTakePhoto() {
+ AccessibilityManager::Get()->PlayEarcon(
+ SOUND_CAMERA_SNAP, PlaySoundOption::SPOKEN_FEEDBACK_ENABLED);
+}
+
+void SupervisedUserCreationScreenHandler::HandleDiscardPhoto() {
+ AccessibilityManager::Get()->PlayEarcon(
+ SOUND_OBJECT_DELETE, PlaySoundOption::SPOKEN_FEEDBACK_ENABLED);
+}
+
+void SupervisedUserCreationScreenHandler::HandleSelectImage(
+ const std::string& image_url,
+ const std::string& image_type) {
+ if (delegate_)
+ delegate_->OnImageSelected(image_type, image_url);
+}
+
+void SupervisedUserCreationScreenHandler::HandleCurrentSupervisedUserPage(
+ const std::string& page) {
+ if (delegate_)
+ delegate_->OnPageSelected(page);
+}
+
+void SupervisedUserCreationScreenHandler::ShowPage(
+ const std::string& page) {
+ CallJS("showPage", page);
+}
+
+void SupervisedUserCreationScreenHandler::SetCameraPresent(bool present) {
+ CallJS("setCameraPresent", present);
+}
+
+void SupervisedUserCreationScreenHandler::ShowExistingSupervisedUsers(
+ const base::ListValue* users) {
+ CallJS("setExistingSupervisedUsers", *users);
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.h
new file mode 100644
index 00000000000..55e11d32906
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.h
@@ -0,0 +1,141 @@
+// 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_UI_WEBUI_CHROMEOS_LOGIN_SUPERVISED_USER_CREATION_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_SUPERVISED_USER_CREATION_SCREEN_HANDLER_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "chrome/browser/chromeos/login/users/default_user_image/default_user_images.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "content/public/browser/web_ui.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace chromeos {
+
+class SupervisedUserCreationScreenHandler : public BaseScreenHandler {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // This method is called, when view is being destroyed. Note, if Delegate
+ // is destroyed earlier then it has to call SetDelegate(nullptr).
+ virtual void OnViewDestroyed(SupervisedUserCreationScreenHandler* view) = 0;
+
+ // Starts supervised user creation flow, with manager identified by
+ // |manager_id| and |manager_password|.
+ virtual void AuthenticateManager(const AccountId& manager_account_id,
+ const std::string& manager_password) = 0;
+
+ // Starts supervised user creation flow, with supervised user that would
+ // have |display_name| and authenticated by the |supervised_user_password|.
+ virtual void CreateSupervisedUser(
+ const base::string16& display_name,
+ const std::string& supervised_user_password) = 0;
+
+ // Look up if user with name |display_name| already exist and can be
+ // imported. Returns user ID in |out_id|. Returns true if user was found,
+ // false otherwise.
+ virtual bool FindUserByDisplayName(const base::string16& display_name,
+ std::string *out_id) const = 0;
+
+ // Starts supervised user import flow for user identified with |user_id|.
+ virtual void ImportSupervisedUser(const std::string& user_id) = 0;
+ // Starts supervised user import flow for user identified with |user_id| and
+ // additional |password|.
+ virtual void ImportSupervisedUserWithPassword(
+ const std::string& user_id, const std::string& password) = 0;
+
+ virtual void AbortFlow() = 0;
+ virtual void FinishFlow() = 0;
+ virtual void HideFlow() = 0;
+
+ virtual void OnPhotoTaken(const std::string& raw_data) = 0;
+ virtual void OnImageSelected(const std::string& image_url,
+ const std::string& image_type) = 0;
+ virtual void OnImageAccepted() = 0;
+ virtual void OnPageSelected(const std::string& page) = 0;
+ };
+
+ SupervisedUserCreationScreenHandler();
+ ~SupervisedUserCreationScreenHandler() override;
+
+ virtual void Show();
+ virtual void Hide();
+ virtual void SetDelegate(Delegate* delegate);
+
+ void ShowManagerPasswordError();
+
+ void ShowIntroPage();
+ void ShowUsernamePage();
+
+ // Shows progress or error message close in the button area. |is_progress| is
+ // true for progress messages and false for error messages.
+ void ShowStatusMessage(bool is_progress, const base::string16& message);
+ void ShowTutorialPage();
+
+ void ShowErrorPage(const base::string16& title,
+ const base::string16& message,
+ const base::string16& button_text);
+
+ // Navigates to specified page.
+ void ShowPage(const std::string& page);
+
+ void SetCameraPresent(bool enabled);
+
+ void ShowExistingSupervisedUsers(const base::ListValue* users);
+
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void Initialize() override;
+
+ // WebUIMessageHandler implementation:
+ void RegisterMessages() override;
+
+ private:
+ // WebUI message handlers.
+ void HandleCheckSupervisedUserName(const base::string16& name);
+
+ void HandleManagerSelected(const AccountId& manager_id);
+ void HandleImportUserSelected(const AccountId& account_id);
+
+ void HandleFinishLocalSupervisedUserCreation();
+ void HandleAbortLocalSupervisedUserCreation();
+ void HandleHideLocalSupervisedUserCreation();
+ void HandleRetryLocalSupervisedUserCreation(const base::ListValue* args);
+ void HandleCurrentSupervisedUserPage(const std::string& current_page);
+
+ void HandleAuthenticateManager(const AccountId& manager_account_id,
+ const std::string& manager_password);
+ void HandleCreateSupervisedUser(const base::string16& new_raw_user_name,
+ const std::string& new_user_password);
+ void HandleImportSupervisedUser(const AccountId& account_id);
+ void HandleImportSupervisedUserWithPassword(const AccountId& account_id,
+ const std::string& password);
+
+ void HandleGetImages();
+ void HandlePhotoTaken(const std::string& image_url);
+ void HandleTakePhoto();
+ void HandleDiscardPhoto();
+ void HandleSelectImage(const std::string& image_url,
+ const std::string& image_type);
+
+ void UpdateText(const std::string& element_id, const base::string16& text);
+
+ Delegate* delegate_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(SupervisedUserCreationScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_SUPERVISED_USER_CREATION_SCREEN_HANDLER_H_
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
new file mode 100644
index 00000000000..e927f9b8cdc
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc
@@ -0,0 +1,213 @@
+// 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/ui/webui/chromeos/login/terms_of_service_screen_handler.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/strings/string_split.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/base/locale_util.h"
+#include "chrome/browser/chromeos/login/screens/core_oobe_view.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+#include "components/prefs/pref_service.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/ime/chromeos/input_method_manager.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.TermsOfServiceScreen";
+
+} // namespace
+
+namespace chromeos {
+
+TermsOfServiceScreenHandler::TermsOfServiceScreenHandler(
+ CoreOobeView* core_oobe_view)
+ : BaseScreenHandler(kScreenId), core_oobe_view_(core_oobe_view) {
+ set_call_js_prefix(kJsScreenPath);
+}
+
+TermsOfServiceScreenHandler::~TermsOfServiceScreenHandler() {
+ if (screen_)
+ screen_->OnViewDestroyed(this);
+}
+
+void TermsOfServiceScreenHandler::RegisterMessages() {
+ AddCallback("termsOfServiceBack",
+ &TermsOfServiceScreenHandler::HandleBack);
+ AddCallback("termsOfServiceAccept",
+ &TermsOfServiceScreenHandler::HandleAccept);
+}
+
+void TermsOfServiceScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("termsOfServiceScreenHeading",
+ IDS_TERMS_OF_SERVICE_SCREEN_HEADING);
+ builder->Add("termsOfServiceScreenSubheading",
+ IDS_TERMS_OF_SERVICE_SCREEN_SUBHEADING);
+ builder->Add("termsOfServiceContentHeading",
+ IDS_TERMS_OF_SERVICE_SCREEN_CONTENT_HEADING);
+ builder->Add("termsOfServiceLoading", IDS_TERMS_OF_SERVICE_SCREEN_LOADING);
+ builder->Add("termsOfServiceError", IDS_TERMS_OF_SERVICE_SCREEN_ERROR);
+ builder->Add("termsOfServiceTryAgain", IDS_TERMS_OF_SERVICE_SCREEN_TRY_AGAIN);
+ builder->Add("termsOfServiceBackButton",
+ IDS_TERMS_OF_SERVICE_SCREEN_BACK_BUTTON);
+ builder->Add("termsOfServiceAcceptButton",
+ IDS_TERMS_OF_SERVICE_SCREEN_ACCEPT_BUTTON);
+}
+
+void TermsOfServiceScreenHandler::SetDelegate(Delegate* screen) {
+ screen_ = screen;
+}
+
+void TermsOfServiceScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+
+ const std::string locale =
+ ProfileHelper::Get()
+ ->GetProfileByUserUnsafe(
+ user_manager::UserManager::Get()->GetActiveUser())
+ ->GetPrefs()
+ ->GetString(prefs::kApplicationLocale);
+
+ if (locale.empty() || locale == g_browser_process->GetApplicationLocale()) {
+ // If the user has not chosen a UI locale yet or the chosen locale matches
+ // the current UI locale, show the screen immediately.
+ DoShow();
+ return;
+ }
+
+ // Switch to the user's UI locale before showing the screen.
+ locale_util::SwitchLanguageCallback callback(
+ base::Bind(&TermsOfServiceScreenHandler::OnLanguageChangedCallback,
+ base::Unretained(this)));
+ locale_util::SwitchLanguage(locale,
+ true, // enable_locale_keyboard_layouts
+ false, // login_layouts_only
+ callback, ProfileManager::GetActiveUserProfile());
+}
+
+void TermsOfServiceScreenHandler::Hide() {
+}
+
+void TermsOfServiceScreenHandler::SetDomain(const std::string& domain) {
+ domain_ = domain;
+ UpdateDomainInUI();
+}
+
+void TermsOfServiceScreenHandler::OnLoadError() {
+ load_error_ = true;
+ terms_of_service_ = "";
+ UpdateTermsOfServiceInUI();
+}
+
+void TermsOfServiceScreenHandler::OnLoadSuccess(
+ const std::string& terms_of_service) {
+ load_error_ = false;
+ terms_of_service_ = terms_of_service;
+ UpdateTermsOfServiceInUI();
+}
+
+void TermsOfServiceScreenHandler::Initialize() {
+ if (show_on_init_) {
+ Show();
+ show_on_init_ = false;
+ }
+}
+
+void TermsOfServiceScreenHandler::OnLanguageChangedCallback(
+ const locale_util::LanguageSwitchResult& result) {
+ // Update the screen contents to the new locale.
+ base::DictionaryValue localized_strings;
+ GetOobeUI()->GetLocalizedStrings(&localized_strings);
+ core_oobe_view_->ReloadContent(localized_strings);
+
+ DoShow();
+}
+
+void TermsOfServiceScreenHandler::DoShow() {
+ // Determine the user's most preferred input method.
+ std::vector<std::string> input_methods = base::SplitString(
+ ProfileHelper::Get()
+ ->GetProfileByUserUnsafe(
+ user_manager::UserManager::Get()->GetActiveUser())
+ ->GetPrefs()
+ ->GetString(prefs::kLanguagePreloadEngines),
+ ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+ if (!input_methods.empty()) {
+ // If the user has a preferred input method, enable it and switch to it.
+ chromeos::input_method::InputMethodManager* input_method_manager =
+ chromeos::input_method::InputMethodManager::Get();
+ input_method_manager->GetActiveIMEState()->EnableInputMethod(
+ input_methods.front());
+ input_method_manager->GetActiveIMEState()->ChangeInputMethod(
+ input_methods.front(), false /* show_message */);
+ }
+
+ // Updates the domain name shown in the UI.
+ UpdateDomainInUI();
+
+ // Update the UI to show an error message or the Terms of Service.
+ UpdateTermsOfServiceInUI();
+
+ ShowScreen(kScreenId);
+}
+
+void TermsOfServiceScreenHandler::UpdateDomainInUI() {
+ if (page_is_ready())
+ CallJS("setDomain", domain_);
+}
+
+void TermsOfServiceScreenHandler::UpdateTermsOfServiceInUI() {
+ if (!page_is_ready())
+ return;
+
+ // If either |load_error_| or |terms_of_service_| is set, the download of the
+ // Terms of Service has completed and the UI should be updated. Otherwise, the
+ // download is still in progress and the UI will be updated when the
+ // OnLoadError() or the OnLoadSuccess() callback is called.
+ if (load_error_)
+ CallJS("setTermsOfServiceLoadError");
+ else if (!terms_of_service_.empty())
+ CallJS("setTermsOfService", terms_of_service_);
+}
+
+void TermsOfServiceScreenHandler::HandleBack() {
+ if (screen_)
+ screen_->OnDecline();
+}
+
+void TermsOfServiceScreenHandler::HandleAccept() {
+ if (!screen_)
+ return;
+
+ // If the Terms of Service have not been successfully downloaded, the "accept
+ // and continue" button should not be accessible. If the user managed to
+ // activate it somehow anway, do not treat this as acceptance of the Terms
+ // and Conditions and end the session instead, as if the user had declined.
+ if (terms_of_service_.empty())
+ screen_->OnDecline();
+ else
+ screen_->OnAccept();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.h
new file mode 100644
index 00000000000..e1ab44521de
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.h
@@ -0,0 +1,92 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_TERMS_OF_SERVICE_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_TERMS_OF_SERVICE_SCREEN_HANDLER_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/chromeos/base/locale_util.h"
+#include "chrome/browser/chromeos/login/screens/terms_of_service_screen_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+
+namespace chromeos {
+
+class CoreOobeView;
+
+// The sole implementation of the TermsOfServiceScreenView, using WebUI.
+class TermsOfServiceScreenHandler : public BaseScreenHandler,
+ public TermsOfServiceScreenView {
+ public:
+ explicit TermsOfServiceScreenHandler(CoreOobeView* core_oobe_view);
+ ~TermsOfServiceScreenHandler() override;
+
+ // content::WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ // BaseScreenHandler:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+
+ // TermsOfServiceScreenView:
+ void SetDelegate(Delegate* screen) override;
+ void Show() override;
+ void Hide() override;
+ void SetDomain(const std::string& domain) override;
+ void OnLoadError() override;
+ void OnLoadSuccess(const std::string& terms_of_service) override;
+
+ private:
+ // BaseScreenHandler:
+ void Initialize() override;
+
+ // Callback invoked after the UI locale has been changed.
+ void OnLanguageChangedCallback(
+ const locale_util::LanguageSwitchResult& result);
+
+ // Switch to the user's preferred input method and show the screen. This
+ // method is called after it has been ensured that the current UI locale
+ // matches the UI locale chosen by the user.
+ void DoShow();
+
+ // Update the domain name shown in the UI.
+ void UpdateDomainInUI();
+
+ // Update the UI to show an error message or the Terms of Service, depending
+ // on whether the download of the Terms of Service was successful. Does
+ // nothing if the download is still in progress.
+ void UpdateTermsOfServiceInUI();
+
+ // Called when the user declines the Terms of Service by clicking the "back"
+ // button.
+ void HandleBack();
+
+ // Called when the user accepts the Terms of Service by clicking the "accept
+ // and continue" button.
+ void HandleAccept();
+
+ TermsOfServiceScreenHandler::Delegate* screen_ = nullptr;
+
+ CoreOobeView* core_oobe_view_ = nullptr;
+
+ // Whether the screen should be shown right after initialization.
+ bool show_on_init_ = false;
+
+ // The domain name whose Terms of Service are being shown.
+ std::string domain_;
+
+ // Set to |true| when the download of the Terms of Service fails.
+ bool load_error_ = false;
+
+ // Set to the Terms of Service when the download is successful.
+ std::string terms_of_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(TermsOfServiceScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_TERMS_OF_SERVICE_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
new file mode 100644
index 00000000000..a1ca651ac92
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
@@ -0,0 +1,89 @@
+// 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/ui/webui/chromeos/login/update_screen_handler.h"
+
+#include <memory>
+
+#include "base/values.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/browser/chromeos/login/screens/update_screen.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.UpdateScreen";
+
+} // namespace
+
+namespace chromeos {
+
+UpdateScreenHandler::UpdateScreenHandler() : BaseScreenHandler(kScreenId) {
+ set_call_js_prefix(kJsScreenPath);
+}
+
+UpdateScreenHandler::~UpdateScreenHandler() {
+ if (screen_)
+ screen_->OnViewDestroyed(this);
+}
+
+void UpdateScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("checkingForUpdatesMsg", IDS_CHECKING_FOR_UPDATE_MSG);
+ builder->Add("installingUpdateDesc", IDS_UPDATE_MSG);
+ builder->Add("updateScreenTitle", IDS_UPDATE_SCREEN_TITLE);
+ builder->Add("updateScreenAccessibleTitle",
+ IDS_UPDATE_SCREEN_ACCESSIBLE_TITLE);
+ builder->Add("checkingForUpdates", IDS_CHECKING_FOR_UPDATES);
+ builder->Add("downloading", IDS_DOWNLOADING);
+ builder->Add("downloadingTimeLeftLong", IDS_DOWNLOADING_TIME_LEFT_LONG);
+ builder->Add("downloadingTimeLeftStatusOneHour",
+ IDS_DOWNLOADING_TIME_LEFT_STATUS_ONE_HOUR);
+ builder->Add("downloadingTimeLeftStatusMinutes",
+ IDS_DOWNLOADING_TIME_LEFT_STATUS_MINUTES);
+ builder->Add("downloadingTimeLeftSmall", IDS_DOWNLOADING_TIME_LEFT_SMALL);
+
+#if !defined(OFFICIAL_BUILD)
+ builder->Add("cancelUpdateHint", IDS_UPDATE_CANCEL);
+ builder->Add("cancelledUpdateMessage", IDS_UPDATE_CANCELLED);
+#else
+ builder->Add("cancelUpdateHint", IDS_EMPTY_STRING);
+ builder->Add("cancelledUpdateMessage", IDS_EMPTY_STRING);
+#endif
+
+ // For Material Design OOBE
+ builder->Add("updatingScreenTitle", IDS_UPDATING_SCREEN_TITLE);
+}
+
+void UpdateScreenHandler::Initialize() {
+ if (show_on_init_) {
+ Show();
+ show_on_init_ = false;
+ }
+}
+
+void UpdateScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+ ShowScreen(kScreenId);
+}
+
+void UpdateScreenHandler::Hide() {
+}
+
+void UpdateScreenHandler::Bind(UpdateScreen* screen) {
+ screen_ = screen;
+ BaseScreenHandler::SetBaseScreen(screen_);
+}
+
+void UpdateScreenHandler::Unbind() {
+ screen_ = nullptr;
+ BaseScreenHandler::SetBaseScreen(nullptr);
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/update_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/update_screen_handler.h
new file mode 100644
index 00000000000..a9482073332
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/update_screen_handler.h
@@ -0,0 +1,44 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_UPDATE_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_UPDATE_SCREEN_HANDLER_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/screens/update_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+
+namespace chromeos {
+
+class UpdateScreenHandler : public UpdateView, public BaseScreenHandler {
+ public:
+ UpdateScreenHandler();
+ ~UpdateScreenHandler() override;
+
+ private:
+ // UpdateView:
+ void Show() override;
+ void Hide() override;
+ void Bind(UpdateScreen* screen) override;
+ void Unbind() override;
+
+ // BaseScreenHandler:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void Initialize() override;
+
+ UpdateScreen* screen_ = nullptr;
+
+ // If true, Initialize() will call Show().
+ bool show_on_init_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(UpdateScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_UPDATE_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.cc
new file mode 100644
index 00000000000..0fca4ef2f46
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.cc
@@ -0,0 +1,105 @@
+// 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/ui/webui/chromeos/login/user_board_screen_handler.h"
+
+#include "chrome/browser/chromeos/login/screens/user_selection_screen.h"
+#include "components/login/localized_values_builder.h"
+
+namespace chromeos {
+
+UserBoardScreenHandler::UserBoardScreenHandler()
+ : BaseScreenHandler(kScreenId), weak_factory_(this) {}
+
+UserBoardScreenHandler::~UserBoardScreenHandler() {
+}
+
+void UserBoardScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+}
+
+void UserBoardScreenHandler::RegisterMessages() {
+ AddCallback("attemptUnlock", &UserBoardScreenHandler::HandleAttemptUnlock);
+ AddCallback("hardlockPod", &UserBoardScreenHandler::HandleHardlockPod);
+ AddCallback("recordClickOnLockIcon",
+ &UserBoardScreenHandler::HandleRecordClickOnLockIcon);
+}
+
+void UserBoardScreenHandler::Initialize() {
+}
+
+//----------------- Handlers
+
+void UserBoardScreenHandler::HandleHardlockPod(const AccountId& account_id) {
+ CHECK(screen_);
+ screen_->HardLockPod(account_id);
+}
+
+void UserBoardScreenHandler::HandleAttemptUnlock(const AccountId& account_id) {
+ CHECK(screen_);
+ screen_->AttemptEasyUnlock(account_id);
+}
+
+void UserBoardScreenHandler::HandleRecordClickOnLockIcon(
+ const AccountId& account_id) {
+ CHECK(screen_);
+ screen_->RecordClickOnLockIcon(account_id);
+}
+
+//----------------- API
+
+void UserBoardScreenHandler::SetPublicSessionDisplayName(
+ const AccountId& account_id,
+ const std::string& display_name) {
+ CallJS("login.AccountPickerScreen.setPublicSessionDisplayName", account_id,
+ display_name);
+}
+
+void UserBoardScreenHandler::SetPublicSessionLocales(
+ const AccountId& account_id,
+ std::unique_ptr<base::ListValue> locales,
+ const std::string& default_locale,
+ bool multiple_recommended_locales) {
+ CallJS("login.AccountPickerScreen.setPublicSessionLocales", account_id,
+ *locales, default_locale, multiple_recommended_locales);
+}
+
+void UserBoardScreenHandler::ShowBannerMessage(const base::string16& message) {
+ CallJS("login.AccountPickerScreen.showBannerMessage", message);
+}
+
+void UserBoardScreenHandler::ShowUserPodCustomIcon(
+ const AccountId& account_id,
+ const base::DictionaryValue& icon) {
+ CallJS("login.AccountPickerScreen.showUserPodCustomIcon", account_id, icon);
+}
+
+void UserBoardScreenHandler::HideUserPodCustomIcon(
+ const AccountId& account_id) {
+ CallJS("login.AccountPickerScreen.hideUserPodCustomIcon", account_id);
+}
+
+void UserBoardScreenHandler::SetAuthType(
+ const AccountId& account_id,
+ proximity_auth::ScreenlockBridge::LockHandler::AuthType auth_type,
+ const base::string16& initial_value) {
+ CallJS("login.AccountPickerScreen.setAuthType", account_id,
+ static_cast<int>(auth_type), base::Value(initial_value));
+}
+
+void UserBoardScreenHandler::Bind(UserSelectionScreen* screen) {
+ screen_ = screen;
+ BaseWebUIHandler::SetBaseScreen(screen_);
+}
+
+void UserBoardScreenHandler::Unbind() {
+ screen_ = nullptr;
+ BaseWebUIHandler::SetBaseScreen(nullptr);
+}
+
+base::WeakPtr<UserBoardView> UserBoardScreenHandler::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.h
new file mode 100644
index 00000000000..13936abcdaa
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.h
@@ -0,0 +1,68 @@
+// 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_CHROMEOS_LOGIN_USER_BOARD_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_USER_BOARD_SCREEN_HANDLER_H_
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/login/ui/views/user_board_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+
+namespace chromeos {
+
+// TODO(jdufault): Rename to UserSelectionScreenHandler and ensure this in the
+// right directory. See crbug.com/672142.
+
+// A class that handles WebUI hooks in Gaia screen.
+class UserBoardScreenHandler : public BaseScreenHandler, public UserBoardView {
+ public:
+ UserBoardScreenHandler();
+ ~UserBoardScreenHandler() override;
+
+ private:
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+
+ // WebUIMessageHandler implementation:
+ void RegisterMessages() override;
+ void Initialize() override;
+
+ // Handlers
+ void HandleHardlockPod(const AccountId& account_id);
+ void HandleAttemptUnlock(const AccountId& account_id);
+ void HandleRecordClickOnLockIcon(const AccountId& account_id);
+
+ // UserBoardView implementation:
+ void SetPublicSessionDisplayName(const AccountId& account_id,
+ const std::string& display_name) override;
+ void SetPublicSessionLocales(const AccountId& account_id,
+ std::unique_ptr<base::ListValue> locales,
+ const std::string& default_locale,
+ bool multiple_recommended_locales) override;
+ void ShowBannerMessage(const base::string16& message) override;
+ void ShowUserPodCustomIcon(const AccountId& account_id,
+ const base::DictionaryValue& icon) override;
+ void HideUserPodCustomIcon(const AccountId& account_id) override;
+ void SetAuthType(
+ const AccountId& account_id,
+ proximity_auth::ScreenlockBridge::LockHandler::AuthType auth_type,
+ const base::string16& initial_value) override;
+ void Bind(UserSelectionScreen* screen) override;
+ void Unbind() override;
+ base::WeakPtr<UserBoardView> GetWeakPtr() override;
+
+ UserSelectionScreen* screen_ = nullptr;
+ base::WeakPtrFactory<UserBoardScreenHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserBoardScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_USER_BOARD_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc
new file mode 100644
index 00000000000..d313a595f6b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc
@@ -0,0 +1,199 @@
+// 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/ui/webui/chromeos/login/user_image_screen_handler.h"
+
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
+#include "chrome/browser/chromeos/login/existing_user_controller.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/browser/chromeos/login/screens/user_image_screen.h"
+#include "chrome/browser/chromeos/login/ui/webui_login_display.h"
+#include "chrome/browser/chromeos/login/users/default_user_image/default_user_images.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/audio/chromeos_sounds.h"
+#include "components/login/localized_values_builder.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/user_manager/user.h"
+#include "media/audio/sounds/sounds_manager.h"
+#include "net/base/data_url.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "url/gurl.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.UserImageScreen";
+
+} // namespace
+
+namespace chromeos {
+
+UserImageScreenHandler::UserImageScreenHandler()
+ : BaseScreenHandler(kScreenId) {
+ set_call_js_prefix(kJsScreenPath);
+ ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+ media::SoundsManager* manager = media::SoundsManager::Get();
+ manager->Initialize(SOUND_OBJECT_DELETE,
+ bundle.GetRawDataResource(IDR_SOUND_OBJECT_DELETE_WAV));
+ manager->Initialize(SOUND_CAMERA_SNAP,
+ bundle.GetRawDataResource(IDR_SOUND_CAMERA_SNAP_WAV));
+}
+
+UserImageScreenHandler::~UserImageScreenHandler() {
+ if (screen_)
+ screen_->OnViewDestroyed(this);
+}
+
+void UserImageScreenHandler::Initialize() {
+ if (show_on_init_) {
+ Show();
+ show_on_init_ = false;
+ }
+}
+
+void UserImageScreenHandler::Bind(UserImageScreen* screen) {
+ screen_ = screen;
+ BaseScreenHandler::SetBaseScreen(screen_);
+}
+
+void UserImageScreenHandler::Unbind() {
+ screen_ = nullptr;
+ BaseScreenHandler::SetBaseScreen(nullptr);
+}
+
+void UserImageScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+ screen_show_time_ = base::Time::Now();
+ ShowScreen(kScreenId);
+
+ // When shown, query camera presence.
+ if (screen_ && is_ready_)
+ screen_->OnScreenReady();
+}
+
+void UserImageScreenHandler::Hide() {
+}
+
+void UserImageScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("userImageScreenTitle", IDS_USER_IMAGE_SCREEN_TITLE);
+ builder->Add("userImageScreenDescription",
+ IDS_OPTIONS_CHANGE_PICTURE_DIALOG_TEXT);
+ builder->Add("takePhoto", IDS_OPTIONS_CHANGE_PICTURE_TAKE_PHOTO);
+ builder->Add("discardPhoto", IDS_OPTIONS_CHANGE_PICTURE_DISCARD_PHOTO);
+ builder->Add("flipPhoto", IDS_OPTIONS_CHANGE_PICTURE_FLIP_PHOTO);
+ builder->Add("profilePhoto", IDS_IMAGE_SCREEN_PROFILE_PHOTO);
+ builder->Add("profilePhotoLoading",
+ IDS_IMAGE_SCREEN_PROFILE_LOADING_PHOTO);
+ builder->Add("okButtonText", IDS_OK);
+ builder->Add("authorCredit", IDS_OPTIONS_SET_WALLPAPER_AUTHOR_TEXT);
+ builder->Add("photoFromCamera", IDS_OPTIONS_CHANGE_PICTURE_PHOTO_FROM_CAMERA);
+ builder->Add("photoFlippedAccessibleText",
+ IDS_OPTIONS_PHOTO_FLIP_ACCESSIBLE_TEXT);
+ builder->Add("photoFlippedBackAccessibleText",
+ IDS_OPTIONS_PHOTO_FLIPBACK_ACCESSIBLE_TEXT);
+ builder->Add("photoCaptureAccessibleText",
+ IDS_OPTIONS_PHOTO_CAPTURE_ACCESSIBLE_TEXT);
+ builder->Add("photoDiscardAccessibleText",
+ IDS_OPTIONS_PHOTO_DISCARD_ACCESSIBLE_TEXT);
+ builder->Add("syncingPreferences", IDS_IMAGE_SCREEN_SYNCING_PREFERENCES);
+}
+
+void UserImageScreenHandler::RegisterMessages() {
+ AddCallback("getImages", &UserImageScreenHandler::HandleGetImages);
+ AddCallback("screenReady", &UserImageScreenHandler::HandleScreenReady);
+ AddCallback("takePhoto", &UserImageScreenHandler::HandleTakePhoto);
+ AddCallback("discardPhoto", &UserImageScreenHandler::HandleDiscardPhoto);
+ AddCallback("photoTaken", &UserImageScreenHandler::HandlePhotoTaken);
+ AddCallback("selectImage", &UserImageScreenHandler::HandleSelectImage);
+ AddCallback("onUserImageAccepted",
+ &UserImageScreenHandler::HandleImageAccepted);
+ AddCallback("onUserImageScreenShown",
+ &UserImageScreenHandler::HandleScreenShown);
+}
+
+// TODO(antrim) : It looks more like parameters for "Init" rather than callback.
+void UserImageScreenHandler::HandleGetImages() {
+ base::ListValue image_urls;
+ for (int i = default_user_image::kFirstDefaultImageIndex;
+ i < default_user_image::kDefaultImagesCount; ++i) {
+ std::unique_ptr<base::DictionaryValue> image_data(
+ new base::DictionaryValue);
+ image_data->SetString("url", default_user_image::GetDefaultImageUrl(i));
+ image_data->SetString("author",
+ l10n_util::GetStringUTF16(
+ default_user_image::kDefaultImageAuthorIDs[i]));
+ image_data->SetString("website",
+ l10n_util::GetStringUTF16(
+ default_user_image::kDefaultImageWebsiteIDs[i]));
+ image_data->SetString("title",
+ default_user_image::GetDefaultImageDescription(i));
+ image_urls.Append(std::move(image_data));
+ }
+ CallJS("setDefaultImages", image_urls);
+}
+
+void UserImageScreenHandler::HandleScreenReady() {
+ is_ready_ = true;
+ if (screen_)
+ screen_->OnScreenReady();
+}
+
+void UserImageScreenHandler::HandlePhotoTaken(const std::string& image_url) {
+ std::string mime_type, charset, raw_data;
+ if (!net::DataURL::Parse(GURL(image_url), &mime_type, &charset, &raw_data))
+ NOTREACHED();
+ DCHECK_EQ("image/png", mime_type);
+
+ if (screen_)
+ screen_->OnPhotoTaken(raw_data);
+}
+
+void UserImageScreenHandler::HandleTakePhoto() {
+ AccessibilityManager::Get()->PlayEarcon(
+ SOUND_CAMERA_SNAP, PlaySoundOption::SPOKEN_FEEDBACK_ENABLED);
+}
+
+void UserImageScreenHandler::HandleDiscardPhoto() {
+ AccessibilityManager::Get()->PlayEarcon(
+ SOUND_OBJECT_DELETE, PlaySoundOption::SPOKEN_FEEDBACK_ENABLED);
+}
+
+void UserImageScreenHandler::HandleSelectImage(const std::string& image_url,
+ const std::string& image_type,
+ bool is_user_selection) {
+ if (screen_)
+ screen_->OnImageSelected(image_type, image_url, is_user_selection);
+}
+
+void UserImageScreenHandler::HandleImageAccepted() {
+ if (screen_)
+ screen_->OnImageAccepted();
+}
+
+void UserImageScreenHandler::HandleScreenShown() {
+ DCHECK(!screen_show_time_.is_null());
+
+ base::TimeDelta delta = base::Time::Now() - screen_show_time_;
+ VLOG(1) << "Screen load time: " << delta.InSecondsF();
+ UMA_HISTOGRAM_TIMES("UserImage.ScreenIsShownTime", delta);
+}
+
+void UserImageScreenHandler::HideCurtain() {
+ CallJS("hideCurtain");
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h
new file mode 100644
index 00000000000..b3349df540b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h
@@ -0,0 +1,84 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_USER_IMAGE_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_USER_IMAGE_SCREEN_HANDLER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "chrome/browser/chromeos/login/screens/user_image_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace chromeos {
+
+// WebUI implementation of UserImageView. It is used to interact with the JS
+// page allowing user to select an avatar.
+class UserImageScreenHandler : public UserImageView, public BaseScreenHandler {
+ public:
+ UserImageScreenHandler();
+ ~UserImageScreenHandler() override;
+
+ private:
+ // BaseScreenHandler implementation:
+ void Initialize() override;
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+
+ // WebUIMessageHandler implementation:
+ void RegisterMessages() override;
+
+ // UserImageView implementation:
+ void Bind(UserImageScreen* screen) override;
+ void Unbind() override;
+ void Show() override;
+ void Hide() override;
+ void HideCurtain() override;
+
+ // Sends image data to the page.
+ void HandleGetImages();
+
+ // Screen ready to be shown.
+ void HandleScreenReady();
+
+ // Handles photo taken with WebRTC UI.
+ void HandlePhotoTaken(const std::string& image_url);
+
+ // Handles 'take-photo' button click.
+ void HandleTakePhoto();
+
+ // Handles 'discard-photo' button click.
+ void HandleDiscardPhoto();
+
+ // Handles clicking on default user image.
+ void HandleSelectImage(const std::string& image_url,
+ const std::string& image_type,
+ bool is_user_selection);
+
+ // Called when user accept the image closing the screen.
+ void HandleImageAccepted();
+
+ // Called when the user image screen has been loaded and shown.
+ void HandleScreenShown();
+
+ UserImageScreen* screen_ = nullptr;
+
+ // Keeps whether screen should be shown right after initialization.
+ bool show_on_init_ = false;
+
+ // Keeps whether screen has loaded all default images and redy to be shown.
+ bool is_ready_ = false;
+
+ base::Time screen_show_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserImageScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_USER_IMAGE_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc b/chromium/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc
new file mode 100644
index 00000000000..87b19203204
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc
@@ -0,0 +1,76 @@
+// 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/ui/webui/chromeos/login/wrong_hwid_screen_handler.h"
+
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.WrongHWIDScreen";
+
+} // namespace
+
+namespace chromeos {
+
+WrongHWIDScreenHandler::WrongHWIDScreenHandler()
+ : BaseScreenHandler(kScreenId) {
+ set_call_js_prefix(kJsScreenPath);
+}
+
+WrongHWIDScreenHandler::~WrongHWIDScreenHandler() {
+ if (delegate_)
+ delegate_->OnViewDestroyed(this);
+}
+
+void WrongHWIDScreenHandler::Show() {
+ if (!page_is_ready()) {
+ show_on_init_ = true;
+ return;
+ }
+ ShowScreen(kScreenId);
+}
+
+void WrongHWIDScreenHandler::Hide() {
+}
+
+void WrongHWIDScreenHandler::SetDelegate(Delegate* delegate) {
+ delegate_ = delegate;
+ if (page_is_ready())
+ Initialize();
+}
+
+void WrongHWIDScreenHandler::DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) {
+ builder->Add("wrongHWIDScreenHeader", IDS_WRONG_HWID_SCREEN_HEADER);
+ builder->Add("wrongHWIDMessageFirstPart",
+ IDS_WRONG_HWID_SCREEN_MESSAGE_FIRST_PART);
+ builder->Add("wrongHWIDMessageSecondPart",
+ IDS_WRONG_HWID_SCREEN_MESSAGE_SECOND_PART);
+ builder->Add("wrongHWIDScreenSkipLink",
+ IDS_WRONG_HWID_SCREEN_SKIP_LINK);
+}
+
+void WrongHWIDScreenHandler::Initialize() {
+ if (!page_is_ready() || !delegate_)
+ return;
+
+ if (show_on_init_) {
+ Show();
+ show_on_init_ = false;
+ }
+}
+
+void WrongHWIDScreenHandler::RegisterMessages() {
+ AddCallback("wrongHWIDOnSkip", &WrongHWIDScreenHandler::HandleOnSkip);
+}
+
+void WrongHWIDScreenHandler::HandleOnSkip() {
+ if (delegate_)
+ delegate_->OnExit();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.h b/chromium/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.h
new file mode 100644
index 00000000000..54e5ee4bfac
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.h
@@ -0,0 +1,51 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_WRONG_HWID_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_WRONG_HWID_SCREEN_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/screens/wrong_hwid_screen_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "content/public/browser/web_ui.h"
+
+namespace chromeos {
+
+// WebUI implementation of WrongHWIDScreenActor.
+class WrongHWIDScreenHandler : public WrongHWIDScreenView,
+ public BaseScreenHandler {
+ public:
+ WrongHWIDScreenHandler();
+ ~WrongHWIDScreenHandler() override;
+
+ // WrongHWIDScreenActor implementation:
+ void Show() override;
+ void Hide() override;
+ void SetDelegate(Delegate* delegate) override;
+
+ // BaseScreenHandler implementation:
+ void DeclareLocalizedValues(
+ ::login::LocalizedValuesBuilder* builder) override;
+ void Initialize() override;
+
+ // WebUIMessageHandler implementation:
+ void RegisterMessages() override;
+
+ private:
+ // JS messages handlers.
+ void HandleOnSkip();
+
+ Delegate* delegate_ = nullptr;
+
+ // Keeps whether screen should be shown right after initialization.
+ bool show_on_init_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(WrongHWIDScreenHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_WRONG_HWID_SCREEN_HANDLER_H_
+
diff --git a/chromium/chrome/browser/ui/webui/chromeos/mobile_setup_dialog.cc b/chromium/chrome/browser/ui/webui/chromeos/mobile_setup_dialog.cc
new file mode 100644
index 00000000000..da85adea8f4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/mobile_setup_dialog.cc
@@ -0,0 +1,167 @@
+// 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/ui/webui/chromeos/mobile_setup_dialog.h"
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_shutdown.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host.h"
+#include "chrome/browser/chromeos/login/ui/webui_login_view.h"
+#include "chrome/browser/chromeos/mobile/mobile_activator.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/simple_message_box.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "content/public/browser/browser_thread.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/views/widget/widget.h"
+#include "ui/web_dialogs/web_dialog_delegate.h"
+
+using chromeos::MobileActivator;
+using content::BrowserThread;
+using content::WebContents;
+using content::WebUIMessageHandler;
+using ui::WebDialogDelegate;
+
+class MobileSetupDialogDelegate : public WebDialogDelegate {
+ public:
+ static MobileSetupDialogDelegate* GetInstance();
+ void ShowDialog(const std::string& service_path);
+
+ protected:
+ friend struct base::DefaultSingletonTraits<MobileSetupDialogDelegate>;
+
+ MobileSetupDialogDelegate();
+ ~MobileSetupDialogDelegate() override;
+
+ void OnCloseDialog();
+
+ // WebDialogDelegate overrides.
+ ui::ModalType GetDialogModalType() const override;
+ base::string16 GetDialogTitle() const override;
+ GURL GetDialogContentURL() const override;
+ void GetWebUIMessageHandlers(
+ std::vector<WebUIMessageHandler*>* handlers) const override;
+ void GetDialogSize(gfx::Size* size) const override;
+ std::string GetDialogArgs() const override;
+ void OnDialogClosed(const std::string& json_retval) override;
+ void OnCloseContents(WebContents* source, bool* out_close_dialog) override;
+ bool ShouldShowDialogTitle() const override;
+ bool HandleContextMenu(const content::ContextMenuParams& params) override;
+
+ private:
+ gfx::NativeWindow dialog_window_;
+ // Cellular network service path.
+ std::string service_path_;
+ DISALLOW_COPY_AND_ASSIGN(MobileSetupDialogDelegate);
+};
+
+// static
+void MobileSetupDialog::ShowByNetworkId(const std::string& network_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ const chromeos::NetworkState* network =
+ chromeos::NetworkHandler::Get()
+ ->network_state_handler()
+ ->GetNetworkStateFromGuid(network_id);
+ if (!network) {
+ LOG(ERROR) << "MobileSetupDialog: Network ID not found: " << network_id;
+ return;
+ }
+ MobileSetupDialogDelegate::GetInstance()->ShowDialog(network->path());
+}
+
+// static
+MobileSetupDialogDelegate* MobileSetupDialogDelegate::GetInstance() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ return base::Singleton<MobileSetupDialogDelegate>::get();
+}
+
+MobileSetupDialogDelegate::MobileSetupDialogDelegate()
+ : dialog_window_(nullptr) {}
+
+MobileSetupDialogDelegate::~MobileSetupDialogDelegate() {
+}
+
+void MobileSetupDialogDelegate::ShowDialog(const std::string& service_path) {
+ service_path_ = service_path;
+
+ gfx::NativeWindow parent = nullptr;
+ // If we're on the login screen.
+ if (chromeos::LoginDisplayHost::default_host()) {
+ chromeos::WebUILoginView* login_view =
+ chromeos::LoginDisplayHost::default_host()->GetWebUILoginView();
+ if (login_view)
+ parent = login_view->GetNativeWindow();
+ }
+ // Only the primary user can change this.
+ dialog_window_ = chrome::ShowWebDialog(
+ parent,
+ ProfileManager::GetPrimaryUserProfile(),
+ this);
+}
+
+ui::ModalType MobileSetupDialogDelegate::GetDialogModalType() const {
+ return ui::MODAL_TYPE_SYSTEM;
+}
+
+base::string16 MobileSetupDialogDelegate::GetDialogTitle() const {
+ return l10n_util::GetStringUTF16(IDS_MOBILE_SETUP_TITLE);
+}
+
+GURL MobileSetupDialogDelegate::GetDialogContentURL() const {
+ std::string url(chrome::kChromeUIMobileSetupURL);
+ url.append(service_path_);
+ return GURL(url);
+}
+
+void MobileSetupDialogDelegate::GetWebUIMessageHandlers(
+ std::vector<WebUIMessageHandler*>* handlers) const {
+}
+
+void MobileSetupDialogDelegate::GetDialogSize(gfx::Size* size) const {
+ size->SetSize(850, 650);
+}
+
+std::string MobileSetupDialogDelegate::GetDialogArgs() const {
+ return std::string();
+}
+
+void MobileSetupDialogDelegate::OnDialogClosed(const std::string& json_retval) {
+ dialog_window_ = nullptr;
+}
+
+void MobileSetupDialogDelegate::OnCloseContents(WebContents* source,
+ bool* out_close_dialog) {
+ // If we're exiting, popping up the confirmation dialog can cause a
+ // crash. Note: IsTryingToQuit can be cancelled on other platforms by the
+ // onbeforeunload handler, except on ChromeOS. So IsTryingToQuit is the
+ // appropriate check to use here.
+ if (!dialog_window_ ||
+ !MobileActivator::GetInstance()->RunningActivation() ||
+ browser_shutdown::IsTryingToQuit()) {
+ *out_close_dialog = true;
+ return;
+ }
+
+ *out_close_dialog = chrome::ShowQuestionMessageBox(
+ dialog_window_, l10n_util::GetStringUTF16(IDS_MOBILE_SETUP_TITLE),
+ l10n_util::GetStringUTF16(IDS_MOBILE_CANCEL_ACTIVATION));
+}
+
+bool MobileSetupDialogDelegate::ShouldShowDialogTitle() const {
+ return true;
+}
+
+bool MobileSetupDialogDelegate::HandleContextMenu(
+ const content::ContextMenuParams& params) {
+ return true;
+}
diff --git a/chromium/chrome/browser/ui/webui/chromeos/mobile_setup_dialog.h b/chromium/chrome/browser/ui/webui/chromeos/mobile_setup_dialog.h
new file mode 100644
index 00000000000..14dcab048d7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/mobile_setup_dialog.h
@@ -0,0 +1,22 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_MOBILE_SETUP_DIALOG_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_MOBILE_SETUP_DIALOG_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+class MobileSetupDialog {
+ public:
+ static void ShowByNetworkId(const std::string& network_id);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MobileSetupDialog);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_MOBILE_SETUP_DIALOG_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc
new file mode 100644
index 00000000000..26089fd707d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc
@@ -0,0 +1,652 @@
+// 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/ui/webui/chromeos/mobile_setup_ui.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <map>
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/mobile/mobile_activator.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/locale_settings.h"
+#include "chromeos/network/device_state.h"
+#include "chromeos/network/network_configuration_handler.h"
+#include "chromeos/network/network_event_log.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "chromeos/network/network_state_handler_observer.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/jstemplate_builder.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "url/gurl.h"
+
+using chromeos::MobileActivator;
+using chromeos::NetworkHandler;
+using chromeos::NetworkState;
+using content::BrowserThread;
+using content::RenderViewHost;
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace {
+
+// Host page JS API function names.
+const char kJsApiStartActivation[] = "startActivation";
+const char kJsApiSetTransactionStatus[] = "setTransactionStatus";
+const char kJsApiPaymentPortalLoad[] = "paymentPortalLoad";
+const char kJsGetDeviceInfo[] = "getDeviceInfo";
+const char kJsApiResultOK[] = "ok";
+
+const char kJsDeviceStatusChangedCallback[] =
+ "mobile.MobileSetup.deviceStateChanged";
+const char kJsPortalFrameLoadFailedCallback[] =
+ "mobile.MobileSetup.portalFrameLoadError";
+const char kJsPortalFrameLoadCompletedCallback[] =
+ "mobile.MobileSetup.portalFrameLoadCompleted";
+const char kJsGetDeviceInfoCallback[] =
+ "mobile.MobileSetupPortal.onGotDeviceInfo";
+const char kJsConnectivityChangedCallback[] =
+ "mobile.MobileSetupPortal.onConnectivityChanged";
+
+void DataRequestFailed(
+ const std::string& service_path,
+ const content::URLDataSource::GotDataCallback& callback) {
+ NET_LOG_ERROR("Data Request Failed for Mobile Setup", service_path);
+ scoped_refptr<base::RefCountedBytes> html_bytes(new base::RefCountedBytes);
+ callback.Run(html_bytes.get());
+}
+
+// Converts the network properties into a JS object.
+void GetDeviceInfo(const base::DictionaryValue& properties,
+ base::DictionaryValue* value) {
+ std::string name;
+ properties.GetStringWithoutPathExpansion(shill::kNameProperty, &name);
+ std::string activation_type;
+ properties.GetStringWithoutPathExpansion(
+ shill::kActivationTypeProperty,
+ &activation_type);
+ const base::DictionaryValue* payment_dict;
+ std::string payment_url, post_method, post_data;
+ if (properties.GetDictionaryWithoutPathExpansion(
+ shill::kPaymentPortalProperty, &payment_dict)) {
+ payment_dict->GetStringWithoutPathExpansion(
+ shill::kPaymentPortalURL, &payment_url);
+ payment_dict->GetStringWithoutPathExpansion(
+ shill::kPaymentPortalMethod, &post_method);
+ payment_dict->GetStringWithoutPathExpansion(
+ shill::kPaymentPortalPostData, &post_data);
+ }
+
+ value->SetString("activation_type", activation_type);
+ value->SetString("carrier", name);
+ value->SetString("payment_url", payment_url);
+ if (base::LowerCaseEqualsASCII(post_method, "post") && !post_data.empty())
+ value->SetString("post_data", post_data);
+
+ // Use the cached DeviceState properties.
+ std::string device_path;
+ if (!properties.GetStringWithoutPathExpansion(
+ shill::kDeviceProperty, &device_path) ||
+ device_path.empty()) {
+ return;
+ }
+ const chromeos::DeviceState* device =
+ NetworkHandler::Get()->network_state_handler()->GetDeviceState(
+ device_path);
+ if (!device)
+ return;
+
+ value->SetString("MEID", device->meid());
+ value->SetString("IMEI", device->imei());
+ value->SetString("MDN", device->mdn());
+}
+
+void SetActivationStateAndError(MobileActivator::PlanActivationState state,
+ const std::string& error_description,
+ base::DictionaryValue* value) {
+ value->SetInteger("state", state);
+ if (!error_description.empty())
+ value->SetString("error", error_description);
+}
+
+} // namespace
+
+class MobileSetupUIHTMLSource : public content::URLDataSource {
+ public:
+ MobileSetupUIHTMLSource();
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ std::string GetMimeType(const std::string&) const override {
+ return "text/html";
+ }
+ bool ShouldAddContentSecurityPolicy() const override { return false; }
+ bool AllowCaching() const override {
+ // Should not be cached to reflect dynamically-generated contents that may
+ // depend on current settings.
+ return false;
+ }
+
+ private:
+ ~MobileSetupUIHTMLSource() override {}
+
+ void GetPropertiesAndStartDataRequest(
+ const content::URLDataSource::GotDataCallback& callback,
+ const std::string& service_path,
+ const base::DictionaryValue& properties);
+ void GetPropertiesFailure(
+ const content::URLDataSource::GotDataCallback& callback,
+ const std::string& service_path,
+ const std::string& error_name,
+ std::unique_ptr<base::DictionaryValue> error_data);
+
+ base::WeakPtrFactory<MobileSetupUIHTMLSource> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MobileSetupUIHTMLSource);
+};
+
+// The handler for Javascript messages related to the "register" view.
+class MobileSetupHandler
+ : public WebUIMessageHandler,
+ public MobileActivator::Observer,
+ public chromeos::NetworkStateHandlerObserver,
+ public base::SupportsWeakPtr<MobileSetupHandler> {
+ public:
+ MobileSetupHandler();
+ ~MobileSetupHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ enum Type {
+ TYPE_UNDETERMINED,
+ // The network is not yet activated, and the webui is in activation flow.
+ TYPE_ACTIVATION,
+ // The network is activated, the webui displays network portal.
+ TYPE_PORTAL,
+ // Same as TYPE_PORTAL, but the network technology is LTE. The webui is
+ // additionally aware of network manager state and whether the portal can be
+ // reached.
+ TYPE_PORTAL_LTE
+ };
+
+ // MobileActivator::Observer.
+ void OnActivationStateChanged(const NetworkState* network,
+ MobileActivator::PlanActivationState new_state,
+ const std::string& error_description) override;
+
+ // Callbacks for NetworkConfigurationHandler::GetProperties.
+ void GetPropertiesAndCallStatusChanged(
+ MobileActivator::PlanActivationState state,
+ const std::string& error_description,
+ const std::string& service_path,
+ const base::DictionaryValue& properties);
+ void GetPropertiesAndCallGetDeviceInfo(
+ const std::string& service_path,
+ const base::DictionaryValue& properties);
+ void GetPropertiesFailure(const std::string& service_path,
+ const std::string& callback_name,
+ const std::string& error_name,
+ std::unique_ptr<base::DictionaryValue> error_data);
+
+ // Handlers for JS WebUI messages.
+ void HandleSetTransactionStatus(const base::ListValue* args);
+ void HandleStartActivation(const base::ListValue* args);
+ void HandlePaymentPortalLoad(const base::ListValue* args);
+ void HandleGetDeviceInfo(const base::ListValue* args);
+
+ // NetworkStateHandlerObserver implementation.
+ void NetworkConnectionStateChanged(const NetworkState* network) override;
+ void DefaultNetworkChanged(const NetworkState* default_network) override;
+
+ // Updates |lte_portal_reachable_| for lte network |network| and notifies
+ // webui of the new state if the reachability changed or |force_notification|
+ // is set.
+ void UpdatePortalReachability(const NetworkState* network,
+ bool force_notification);
+
+ // Sends message to host registration page with system/user info data.
+ void SendDeviceInfo();
+
+ // Type of the mobilesetup webui deduced from received messages.
+ Type type_;
+ // Whether portal page for lte networks can be reached in current network
+ // connection state. This value is reflected in portal webui for lte networks.
+ // Initial value is true.
+ bool lte_portal_reachable_;
+ base::WeakPtrFactory<MobileSetupHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MobileSetupHandler);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// MobileSetupUIHTMLSource
+//
+////////////////////////////////////////////////////////////////////////////////
+
+MobileSetupUIHTMLSource::MobileSetupUIHTMLSource()
+ : weak_ptr_factory_(this) {
+}
+
+std::string MobileSetupUIHTMLSource::GetSource() const {
+ return chrome::kChromeUIMobileSetupHost;
+}
+
+void MobileSetupUIHTMLSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
+ path,
+ base::Bind(&MobileSetupUIHTMLSource::GetPropertiesAndStartDataRequest,
+ weak_ptr_factory_.GetWeakPtr(), callback),
+ base::Bind(&MobileSetupUIHTMLSource::GetPropertiesFailure,
+ weak_ptr_factory_.GetWeakPtr(), callback, path));
+}
+
+void MobileSetupUIHTMLSource::GetPropertiesAndStartDataRequest(
+ const content::URLDataSource::GotDataCallback& callback,
+ const std::string& service_path,
+ const base::DictionaryValue& properties) {
+ const base::DictionaryValue* payment_dict;
+ std::string name, usage_url, activation_state, payment_url;
+ if (!properties.GetStringWithoutPathExpansion(
+ shill::kNameProperty, &name) ||
+ !properties.GetStringWithoutPathExpansion(
+ shill::kUsageURLProperty, &usage_url) ||
+ !properties.GetStringWithoutPathExpansion(
+ shill::kActivationStateProperty, &activation_state) ||
+ !properties.GetDictionaryWithoutPathExpansion(
+ shill::kPaymentPortalProperty, &payment_dict) ||
+ !payment_dict->GetStringWithoutPathExpansion(
+ shill::kPaymentPortalURL, &payment_url)) {
+ DataRequestFailed(service_path, callback);
+ return;
+ }
+
+ if (payment_url.empty() && usage_url.empty() &&
+ activation_state != shill::kActivationStateActivated) {
+ DataRequestFailed(service_path, callback);
+ return;
+ }
+
+ NET_LOG_EVENT("Starting mobile setup", service_path);
+ base::DictionaryValue strings;
+
+ strings.SetString("connecting_header",
+ l10n_util::GetStringFUTF16(IDS_MOBILE_CONNECTING_HEADER,
+ base::UTF8ToUTF16(name)));
+ strings.SetString("error_header",
+ l10n_util::GetStringUTF16(IDS_MOBILE_ERROR_HEADER));
+ strings.SetString("activating_header",
+ l10n_util::GetStringUTF16(IDS_MOBILE_ACTIVATING_HEADER));
+ strings.SetString("completed_header",
+ l10n_util::GetStringUTF16(IDS_MOBILE_COMPLETED_HEADER));
+ strings.SetString("please_wait",
+ l10n_util::GetStringUTF16(IDS_MOBILE_PLEASE_WAIT));
+ strings.SetString("completed_text",
+ l10n_util::GetStringUTF16(IDS_MOBILE_COMPLETED_TEXT));
+ strings.SetString("portal_unreachable_header",
+ l10n_util::GetStringUTF16(IDS_MOBILE_NO_CONNECTION_HEADER));
+ strings.SetString("invalid_device_info_header",
+ l10n_util::GetStringUTF16(IDS_MOBILE_INVALID_DEVICE_INFO_HEADER));
+ strings.SetString("title", l10n_util::GetStringUTF16(IDS_MOBILE_SETUP_TITLE));
+ strings.SetString("close_button",
+ l10n_util::GetStringUTF16(IDS_CLOSE));
+ strings.SetString("cancel_button",
+ l10n_util::GetStringUTF16(IDS_CANCEL));
+ strings.SetString("ok_button",
+ l10n_util::GetStringUTF16(IDS_OK));
+
+ const std::string& app_locale = g_browser_process->GetApplicationLocale();
+ webui::SetLoadTimeDataDefaults(app_locale, &strings);
+
+ // The webui differs based on whether the network is activated or not. If the
+ // network is activated, the webui goes straight to portal. Otherwise the
+ // webui is used for activation flow.
+ std::string full_html;
+ if (activation_state == shill::kActivationStateActivated) {
+ static const base::StringPiece html_for_activated(
+ ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_MOBILE_SETUP_PORTAL_PAGE_HTML));
+ full_html = webui::GetI18nTemplateHtml(html_for_activated, &strings);
+ } else {
+ static const base::StringPiece html_for_non_activated(
+ ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_MOBILE_SETUP_PAGE_HTML));
+ full_html = webui::GetI18nTemplateHtml(html_for_non_activated, &strings);
+ }
+
+ callback.Run(base::RefCountedString::TakeString(&full_html));
+}
+
+void MobileSetupUIHTMLSource::GetPropertiesFailure(
+ const content::URLDataSource::GotDataCallback& callback,
+ const std::string& service_path,
+ const std::string& error_name,
+ std::unique_ptr<base::DictionaryValue> error_data) {
+ DataRequestFailed(service_path, callback);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// MobileSetupHandler
+//
+////////////////////////////////////////////////////////////////////////////////
+MobileSetupHandler::MobileSetupHandler()
+ : type_(TYPE_UNDETERMINED),
+ lte_portal_reachable_(true),
+ weak_ptr_factory_(this) {
+}
+
+MobileSetupHandler::~MobileSetupHandler() {
+ if (type_ == TYPE_ACTIVATION) {
+ MobileActivator::GetInstance()->RemoveObserver(this);
+ MobileActivator::GetInstance()->TerminateActivation();
+ } else if (type_ == TYPE_PORTAL_LTE) {
+ NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
+ FROM_HERE);
+ }
+}
+
+void MobileSetupHandler::OnActivationStateChanged(
+ const NetworkState* network,
+ MobileActivator::PlanActivationState state,
+ const std::string& error_description) {
+ DCHECK_EQ(TYPE_ACTIVATION, type_);
+ if (!web_ui())
+ return;
+
+ if (!network) {
+ base::DictionaryValue device_dict;
+ SetActivationStateAndError(state, error_description, &device_dict);
+ web_ui()->CallJavascriptFunctionUnsafe(kJsDeviceStatusChangedCallback,
+ device_dict);
+ return;
+ }
+
+ NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
+ network->path(),
+ base::Bind(&MobileSetupHandler::GetPropertiesAndCallStatusChanged,
+ weak_ptr_factory_.GetWeakPtr(), state, error_description),
+ base::Bind(&MobileSetupHandler::GetPropertiesFailure,
+ weak_ptr_factory_.GetWeakPtr(), network->path(),
+ kJsDeviceStatusChangedCallback));
+}
+
+void MobileSetupHandler::GetPropertiesAndCallStatusChanged(
+ MobileActivator::PlanActivationState state,
+ const std::string& error_description,
+ const std::string& service_path,
+ const base::DictionaryValue& properties) {
+ base::DictionaryValue device_dict;
+ GetDeviceInfo(properties, &device_dict);
+ SetActivationStateAndError(state, error_description, &device_dict);
+ web_ui()->CallJavascriptFunctionUnsafe(kJsDeviceStatusChangedCallback,
+ device_dict);
+}
+
+void MobileSetupHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(kJsApiStartActivation,
+ base::Bind(&MobileSetupHandler::HandleStartActivation,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kJsApiSetTransactionStatus,
+ base::Bind(&MobileSetupHandler::HandleSetTransactionStatus,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kJsApiPaymentPortalLoad,
+ base::Bind(&MobileSetupHandler::HandlePaymentPortalLoad,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kJsGetDeviceInfo,
+ base::Bind(&MobileSetupHandler::HandleGetDeviceInfo,
+ base::Unretained(this)));
+}
+
+void MobileSetupHandler::HandleStartActivation(const base::ListValue* args) {
+ DCHECK_EQ(TYPE_UNDETERMINED, type_);
+
+ if (!web_ui())
+ return;
+
+ std::string path = web_ui()->GetWebContents()->GetURL().path();
+ if (path.empty())
+ return;
+
+ LOG(WARNING) << "Starting activation for service " << path;
+
+ type_ = TYPE_ACTIVATION;
+ MobileActivator::GetInstance()->AddObserver(this);
+ MobileActivator::GetInstance()->InitiateActivation(path.substr(1));
+}
+
+void MobileSetupHandler::HandleSetTransactionStatus(
+ const base::ListValue* args) {
+ DCHECK_EQ(TYPE_ACTIVATION, type_);
+ if (!web_ui())
+ return;
+
+ const size_t kSetTransactionStatusParamCount = 1;
+ if (args->GetSize() != kSetTransactionStatusParamCount)
+ return;
+ // Get change callback function name.
+ std::string status;
+ if (!args->GetString(0, &status))
+ return;
+
+ MobileActivator::GetInstance()->OnSetTransactionStatus(
+ base::LowerCaseEqualsASCII(status, kJsApiResultOK));
+}
+
+void MobileSetupHandler::HandlePaymentPortalLoad(const base::ListValue* args) {
+ // Only activation flow webui is interested in these events.
+ if (type_ != TYPE_ACTIVATION || !web_ui())
+ return;
+
+ const size_t kPaymentPortalLoadParamCount = 1;
+ if (args->GetSize() != kPaymentPortalLoadParamCount)
+ return;
+ // Get change callback function name.
+ std::string result;
+ if (!args->GetString(0, &result))
+ return;
+
+ MobileActivator::GetInstance()->OnPortalLoaded(
+ base::LowerCaseEqualsASCII(result, kJsApiResultOK));
+}
+
+void MobileSetupHandler::HandleGetDeviceInfo(const base::ListValue* args) {
+ DCHECK_NE(TYPE_ACTIVATION, type_);
+ if (!web_ui())
+ return;
+
+ std::string path = web_ui()->GetWebContents()->GetURL().path();
+ if (path.empty())
+ return;
+
+ chromeos::NetworkStateHandler* nsh =
+ NetworkHandler::Get()->network_state_handler();
+ // TODO: Figure out why the path has an extra '/' in the front. (e.g. It is
+ // '//service/5' instead of '/service/5'.
+ const NetworkState* network = nsh->GetNetworkState(path.substr(1));
+ if (!network) {
+ web_ui()->GetWebContents()->Close();
+ return;
+ }
+
+ // If this is the initial call, update the network status and start observing
+ // network changes, but only for LTE networks. The other networks should
+ // ignore network status.
+ if (type_ == TYPE_UNDETERMINED) {
+ if (network->network_technology() == shill::kNetworkTechnologyLte ||
+ network->network_technology() == shill::kNetworkTechnologyLteAdvanced) {
+ type_ = TYPE_PORTAL_LTE;
+ nsh->AddObserver(this, FROM_HERE);
+ // Update the network status and notify the webui. This is the initial
+ // network state so the webui should be notified no matter what.
+ UpdatePortalReachability(network,
+ true /* force notification */);
+ } else {
+ type_ = TYPE_PORTAL;
+ // For non-LTE networks network state is ignored, so report the portal is
+ // reachable, so it gets shown.
+ web_ui()->CallJavascriptFunctionUnsafe(kJsConnectivityChangedCallback,
+ base::Value(true));
+ }
+ }
+
+ NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
+ network->path(),
+ base::Bind(&MobileSetupHandler::GetPropertiesAndCallGetDeviceInfo,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&MobileSetupHandler::GetPropertiesFailure,
+ weak_ptr_factory_.GetWeakPtr(), network->path(),
+ kJsGetDeviceInfoCallback));
+}
+
+void MobileSetupHandler::GetPropertiesAndCallGetDeviceInfo(
+ const std::string& service_path,
+ const base::DictionaryValue& properties) {
+ base::DictionaryValue device_info;
+ GetDeviceInfo(properties, &device_info);
+ web_ui()->CallJavascriptFunctionUnsafe(kJsGetDeviceInfoCallback, device_info);
+}
+
+void MobileSetupHandler::GetPropertiesFailure(
+ const std::string& service_path,
+ const std::string& callback_name,
+ const std::string& error_name,
+ std::unique_ptr<base::DictionaryValue> error_data) {
+ NET_LOG_ERROR("MobileActivator GetProperties Failed: " + error_name,
+ service_path);
+ // Invoke |callback_name| with an empty dictionary.
+ base::DictionaryValue device_dict;
+ web_ui()->CallJavascriptFunctionUnsafe(callback_name, device_dict);
+}
+
+void MobileSetupHandler::DefaultNetworkChanged(
+ const NetworkState* default_network) {
+ if (!web_ui())
+ return;
+
+ std::string path = web_ui()->GetWebContents()->GetURL().path().substr(1);
+ if (path.empty())
+ return;
+
+ const NetworkState* network =
+ NetworkHandler::Get()->network_state_handler()->GetNetworkState(path);
+ if (!network) {
+ LOG(ERROR) << "Service path lost";
+ web_ui()->GetWebContents()->Close();
+ return;
+ }
+
+ UpdatePortalReachability(network, false /* do not force notification */);
+}
+
+void MobileSetupHandler::NetworkConnectionStateChanged(
+ const NetworkState* network) {
+ if (!web_ui())
+ return;
+
+ std::string path = web_ui()->GetWebContents()->GetURL().path().substr(1);
+ if (path.empty() || path != network->path())
+ return;
+
+ UpdatePortalReachability(network, false /* do not force notification */);
+}
+
+void MobileSetupHandler::UpdatePortalReachability(
+ const NetworkState* network,
+ bool force_notification) {
+ DCHECK(web_ui());
+
+ DCHECK_EQ(type_, TYPE_PORTAL_LTE);
+
+ chromeos::NetworkStateHandler* nsh =
+ NetworkHandler::Get()->network_state_handler();
+ bool portal_reachable =
+ (network->IsConnectedState() ||
+ (nsh->DefaultNetwork() &&
+ nsh->DefaultNetwork()->connection_state() == shill::kStateOnline));
+
+ if (force_notification || portal_reachable != lte_portal_reachable_) {
+ web_ui()->CallJavascriptFunctionUnsafe(kJsConnectivityChangedCallback,
+ base::Value(portal_reachable));
+ }
+
+ lte_portal_reachable_ = portal_reachable;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// MobileSetupUI
+//
+////////////////////////////////////////////////////////////////////////////////
+
+MobileSetupUI::MobileSetupUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<MobileSetupHandler>());
+ MobileSetupUIHTMLSource* html_source = new MobileSetupUIHTMLSource();
+
+ // Set up the chrome://mobilesetup/ source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::URLDataSource::Add(profile, html_source);
+
+ content::WebContentsObserver::Observe(web_ui->GetWebContents());
+}
+
+void MobileSetupUI::DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) {
+ if (!navigation_handle->HasCommitted() ||
+ navigation_handle->GetRenderFrameHost()->GetFrameName() !=
+ "paymentForm") {
+ return;
+ }
+
+ if (navigation_handle->IsErrorPage()) {
+ base::Value result_value(-navigation_handle->GetNetErrorCode());
+ web_ui()->CallJavascriptFunctionUnsafe(kJsPortalFrameLoadFailedCallback,
+ result_value);
+ return;
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe(kJsPortalFrameLoadCompletedCallback);
+}
diff --git a/chromium/chrome/browser/ui/webui/chromeos/mobile_setup_ui.h b/chromium/chrome/browser/ui/webui/chromeos/mobile_setup_ui.h
new file mode 100644
index 00000000000..0fd9d74190e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/mobile_setup_ui.h
@@ -0,0 +1,29 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_MOBILE_SETUP_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_MOBILE_SETUP_UI_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// A custom WebUI that defines datasource for mobile setup registration page
+// that is used in Chrome OS activate modem and perform plan subscription tasks.
+class MobileSetupUI : public content::WebUIController,
+ public content::WebContentsObserver,
+ public base::SupportsWeakPtr<MobileSetupUI> {
+ public:
+ explicit MobileSetupUI(content::WebUI* web_ui);
+
+ private:
+ // content::WebContentsObserver overrides.
+ void DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) override;
+
+ DISALLOW_COPY_AND_ASSIGN(MobileSetupUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_MOBILE_SETUP_UI_H_
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
new file mode 100644
index 00000000000..eebb74d4f84
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.cc
@@ -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.
+
+#include "chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.h"
+
+#include "build/build_config.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace chromeos {
+namespace network_element {
+
+void AddLocalizedStrings(content::WebUIDataSource* html_source) {
+ struct {
+ const char* name;
+ int id;
+ } localized_strings[] = {
+ {"OncTypeCellular", IDS_NETWORK_TYPE_CELLULAR},
+ {"OncTypeEthernet", IDS_NETWORK_TYPE_ETHERNET},
+ {"OncTypeTether", IDS_NETWORK_TYPE_TETHER},
+ {"OncTypeVPN", IDS_NETWORK_TYPE_VPN},
+ {"OncTypeWiFi", IDS_NETWORK_TYPE_WIFI},
+ {"OncTypeWiMAX", IDS_NETWORK_TYPE_WIMAX},
+ {"networkListItemConnected", IDS_STATUSBAR_NETWORK_DEVICE_CONNECTED},
+ {"networkListItemConnecting", IDS_STATUSBAR_NETWORK_DEVICE_CONNECTING},
+ {"networkListItemConnectingTo", IDS_NETWORK_LIST_CONNECTING_TO},
+ {"networkListItemNotConnected", IDS_NETWORK_LIST_NOT_CONNECTED},
+ {"vpnNameTemplate", IDS_NETWORK_LIST_THIRD_PARTY_VPN_NAME_TEMPLATE},
+ };
+ for (const auto& entry : localized_strings)
+ html_source->AddLocalizedString(entry.name, entry.id);
+}
+
+} // namespace network_element
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.h b/chromium/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.h
new file mode 100644
index 00000000000..cbf065240f8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.h
@@ -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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_NETWORK_ELEMENT_LOCALIZED_STRINGS_PROVIDER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_NETWORK_ELEMENT_LOCALIZED_STRINGS_PROVIDER_H_
+
+namespace content {
+class WebUIDataSource;
+}
+
+namespace chromeos {
+namespace network_element {
+
+// Adds the strings needed for network elements to |html_source|. String ids
+// correspond to matching ids in ui/webui/resources/cr_elements/network/.
+void AddLocalizedStrings(content::WebUIDataSource* html_source);
+
+} // namespace network_element
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_NETWORK_ELEMENT_LOCALIZED_STRINGS_PROVIDER_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/network_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/network_ui.cc
new file mode 100644
index 00000000000..5cdb9f79d40
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/network_ui.cc
@@ -0,0 +1,231 @@
+// 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/chromeos/network_ui.h"
+
+#include <string>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/options/network_config_view.h"
+#include "chrome/browser/extensions/tab_helper.h"
+#include "chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/network/device_state.h"
+#include "chromeos/network/network_configuration_handler.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "components/device_event_log/device_event_log.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/chromeos/strings/grit/ui_chromeos_strings.h"
+
+namespace chromeos {
+
+namespace {
+
+bool GetServicePathFromGuid(const std::string& guid,
+ std::string* service_path) {
+ const NetworkState* network =
+ NetworkHandler::Get()->network_state_handler()->GetNetworkStateFromGuid(
+ guid);
+ if (!network)
+ return false;
+ *service_path = network->path();
+ return true;
+}
+
+void SetDeviceProperties(base::DictionaryValue* dictionary) {
+ std::string device;
+ dictionary->GetStringWithoutPathExpansion(shill::kDeviceProperty, &device);
+ const DeviceState* device_state =
+ NetworkHandler::Get()->network_state_handler()->GetDeviceState(device);
+ if (!device_state)
+ return;
+
+ std::unique_ptr<base::DictionaryValue> device_dictionary(
+ device_state->properties().DeepCopy());
+
+ if (!device_state->ip_configs().empty()) {
+ // Convert IPConfig dictionary to a ListValue.
+ std::unique_ptr<base::ListValue> ip_configs(new base::ListValue);
+ for (base::DictionaryValue::Iterator iter(device_state->ip_configs());
+ !iter.IsAtEnd(); iter.Advance()) {
+ ip_configs->Append(iter.value().CreateDeepCopy());
+ }
+ device_dictionary->SetWithoutPathExpansion(shill::kIPConfigsProperty,
+ std::move(ip_configs));
+ }
+ if (!device_dictionary->empty())
+ dictionary->Set(shill::kDeviceProperty, std::move(device_dictionary));
+}
+
+class NetworkConfigMessageHandler : public content::WebUIMessageHandler {
+ public:
+ NetworkConfigMessageHandler() : weak_ptr_factory_(this) {}
+ ~NetworkConfigMessageHandler() override {}
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override {
+ web_ui()->RegisterMessageCallback(
+ "getShillProperties",
+ base::Bind(&NetworkConfigMessageHandler::GetShillProperties,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "addNetwork",
+ base::Bind(&NetworkConfigMessageHandler::AddNetwork,
+ base::Unretained(this)));
+ }
+
+ private:
+ void GetShillProperties(const base::ListValue* arg_list) {
+ std::string guid;
+ if (!arg_list->GetString(0, &guid)) {
+ NOTREACHED();
+ ErrorCallback(guid, "Missing GUID in arg list", nullptr);
+ return;
+ }
+ std::string service_path;
+ if (!GetServicePathFromGuid(guid, &service_path)) {
+ ErrorCallback(guid, "Error.InvalidNetworkGuid", nullptr);
+ return;
+ }
+ NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
+ service_path,
+ base::Bind(&NetworkConfigMessageHandler::GetShillPropertiesSuccess,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&NetworkConfigMessageHandler::ErrorCallback,
+ weak_ptr_factory_.GetWeakPtr(), guid));
+ }
+
+ void GetShillPropertiesSuccess(
+ const std::string& service_path,
+ const base::DictionaryValue& dictionary) const {
+ std::unique_ptr<base::DictionaryValue> dictionary_copy(
+ dictionary.DeepCopy());
+
+ // Set the 'ServicePath' property for debugging.
+ dictionary_copy->SetStringWithoutPathExpansion("ServicePath", service_path);
+ // Set the device properties for debugging.
+ SetDeviceProperties(dictionary_copy.get());
+
+ base::ListValue return_arg_list;
+ return_arg_list.Append(std::move(dictionary_copy));
+ web_ui()->CallJavascriptFunctionUnsafe("NetworkUI.getShillPropertiesResult",
+ return_arg_list);
+ }
+
+ void ErrorCallback(
+ const std::string& guid,
+ const std::string& error_name,
+ std::unique_ptr<base::DictionaryValue> /* error_data */) const {
+ NET_LOG(ERROR) << "Shill Error: " << error_name << " guid=" << guid;
+ base::ListValue return_arg_list;
+ std::unique_ptr<base::DictionaryValue> dictionary;
+ dictionary->SetStringWithoutPathExpansion(shill::kGuidProperty, guid);
+ dictionary->SetStringWithoutPathExpansion("ShillError", error_name);
+ return_arg_list.Append(std::move(dictionary));
+ web_ui()->CallJavascriptFunctionUnsafe("NetworkUI.getShillPropertiesResult",
+ return_arg_list);
+ }
+
+ void AddNetwork(const base::ListValue* args) {
+ std::string onc_type;
+ args->GetString(0, &onc_type);
+ std::string shill_type = (onc_type == ::onc::network_type::kVPN)
+ ? shill::kTypeVPN
+ : shill::kTypeWifi;
+ NetworkConfigView::ShowForType(
+ shill_type, web_ui()->GetWebContents()->GetTopLevelNativeWindow());
+ }
+
+ base::WeakPtrFactory<NetworkConfigMessageHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkConfigMessageHandler);
+};
+
+} // namespace
+
+// static
+void NetworkUI::GetLocalizedStrings(base::DictionaryValue* localized_strings) {
+ localized_strings->SetString("titleText",
+ l10n_util::GetStringUTF16(IDS_NETWORK_UI_TITLE));
+
+ localized_strings->SetString("titleText",
+ l10n_util::GetStringUTF16(IDS_NETWORK_UI_TITLE));
+ localized_strings->SetString(
+ "autoRefreshText",
+ l10n_util::GetStringUTF16(IDS_NETWORK_UI_AUTO_REFRESH));
+ localized_strings->SetString(
+ "deviceLogLinkText", l10n_util::GetStringUTF16(IDS_DEVICE_LOG_LINK_TEXT));
+ localized_strings->SetString(
+ "networkRefreshText", l10n_util::GetStringUTF16(IDS_NETWORK_UI_REFRESH));
+ localized_strings->SetString(
+ "clickToExpandText", l10n_util::GetStringUTF16(IDS_NETWORK_UI_EXPAND));
+ localized_strings->SetString(
+ "propertyFormatText",
+ l10n_util::GetStringUTF16(IDS_NETWORK_UI_PROPERTY_FORMAT));
+
+ localized_strings->SetString(
+ "normalFormatOption",
+ l10n_util::GetStringUTF16(IDS_NETWORK_UI_FORMAT_NORMAL));
+ localized_strings->SetString(
+ "managedFormatOption",
+ l10n_util::GetStringUTF16(IDS_NETWORK_UI_FORMAT_MANAGED));
+ localized_strings->SetString(
+ "stateFormatOption",
+ l10n_util::GetStringUTF16(IDS_NETWORK_UI_FORMAT_STATE));
+ localized_strings->SetString(
+ "shillFormatOption",
+ l10n_util::GetStringUTF16(IDS_NETWORK_UI_FORMAT_SHILL));
+
+ localized_strings->SetString(
+ "globalPolicyLabel",
+ l10n_util::GetStringUTF16(IDS_NETWORK_UI_GLOBAL_POLICY));
+ localized_strings->SetString(
+ "visibleNetworksLabel",
+ l10n_util::GetStringUTF16(IDS_NETWORK_UI_VISIBLE_NETWORKS));
+ localized_strings->SetString(
+ "favoriteNetworksLabel",
+ l10n_util::GetStringUTF16(IDS_NETWORK_UI_FAVORITE_NETWORKS));
+}
+
+NetworkUI::NetworkUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<NetworkConfigMessageHandler>());
+
+ // Enable extension API calls in the WebUI.
+ extensions::TabHelper::CreateForWebContents(web_ui->GetWebContents());
+
+ base::DictionaryValue localized_strings;
+ GetLocalizedStrings(&localized_strings);
+
+ content::WebUIDataSource* html =
+ content::WebUIDataSource::Create(chrome::kChromeUINetworkHost);
+ html->AddLocalizedStrings(localized_strings);
+
+ network_element::AddLocalizedStrings(html);
+
+ html->SetJsonPath("strings.js");
+ html->AddResourcePath("network_ui.css", IDR_NETWORK_UI_CSS);
+ html->AddResourcePath("network_ui.js", IDR_NETWORK_UI_JS);
+ html->SetDefaultResource(IDR_NETWORK_UI_HTML);
+
+ content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
+ html);
+}
+
+NetworkUI::~NetworkUI() {
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/network_ui.h b/chromium/chrome/browser/ui/webui/chromeos/network_ui.h
new file mode 100644
index 00000000000..e06e6344abc
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/network_ui.h
@@ -0,0 +1,30 @@
+// 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_CHROMEOS_NETWORK_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_NETWORK_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace base {
+class DictionaryValue;
+};
+
+namespace chromeos {
+
+class NetworkUI : public content::WebUIController {
+ public:
+ explicit NetworkUI(content::WebUI* web_ui);
+ ~NetworkUI() override;
+
+ static void GetLocalizedStrings(base::DictionaryValue* localized_strings);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NetworkUI);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_NETWORK_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/power_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/power_ui.cc
new file mode 100644
index 00000000000..b593b0277e2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/power_ui.cc
@@ -0,0 +1,248 @@
+// 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/ui/webui/chromeos/power_ui.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/power/power_data_collector.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace chromeos {
+
+namespace {
+
+const char kStringsJsFile[] = "strings.js";
+
+const char kRequestBatteryChargeDataCallback[] = "requestBatteryChargeData";
+const char kOnRequestBatteryChargeDataFunction[] =
+ "powerUI.showBatteryChargeData";
+
+const char kRequestCpuIdleDataCallback[] = "requestCpuIdleData";
+const char kOnRequestCpuIdleDataFunction[] =
+ "powerUI.showCpuIdleData";
+
+const char kRequestCpuFreqDataCallback[] = "requestCpuFreqData";
+const char kOnRequestCpuFreqDataFunction[] =
+ "powerUI.showCpuFreqData";
+
+class PowerMessageHandler : public content::WebUIMessageHandler {
+ public:
+ PowerMessageHandler();
+ ~PowerMessageHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ void OnGetBatteryChargeData(const base::ListValue* value);
+ void OnGetCpuIdleData(const base::ListValue* value);
+ void OnGetCpuFreqData(const base::ListValue* value);
+ void GetJsStateOccupancyData(
+ const std::vector<CpuDataCollector::StateOccupancySampleDeque>& data,
+ const std::vector<std::string>& state_names,
+ base::ListValue* js_data);
+ void GetJsSystemResumedData(base::ListValue* value);
+};
+
+PowerMessageHandler::PowerMessageHandler() {
+}
+
+PowerMessageHandler::~PowerMessageHandler() {
+}
+
+void PowerMessageHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ kRequestBatteryChargeDataCallback,
+ base::Bind(&PowerMessageHandler::OnGetBatteryChargeData,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kRequestCpuIdleDataCallback,
+ base::Bind(&PowerMessageHandler::OnGetCpuIdleData,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kRequestCpuFreqDataCallback,
+ base::Bind(&PowerMessageHandler::OnGetCpuFreqData,
+ base::Unretained(this)));
+}
+
+void PowerMessageHandler::OnGetBatteryChargeData(const base::ListValue* value) {
+ const std::deque<PowerDataCollector::PowerSupplySample>& power_supply =
+ PowerDataCollector::Get()->power_supply_data();
+ base::ListValue js_power_supply_data;
+ for (size_t i = 0; i < power_supply.size(); ++i) {
+ const PowerDataCollector::PowerSupplySample& sample = power_supply[i];
+ std::unique_ptr<base::DictionaryValue> element(new base::DictionaryValue);
+ element->SetDouble("batteryPercent", sample.battery_percent);
+ element->SetDouble("batteryDischargeRate", sample.battery_discharge_rate);
+ element->SetBoolean("externalPower", sample.external_power);
+ element->SetDouble("time", sample.time.ToJsTime());
+
+ js_power_supply_data.Append(std::move(element));
+ }
+
+ base::ListValue js_system_resumed_data;
+ GetJsSystemResumedData(&js_system_resumed_data);
+
+ web_ui()->CallJavascriptFunctionUnsafe(kOnRequestBatteryChargeDataFunction,
+ js_power_supply_data,
+ js_system_resumed_data);
+}
+
+void PowerMessageHandler::OnGetCpuIdleData(const base::ListValue* value) {
+ const CpuDataCollector& cpu_data_collector =
+ PowerDataCollector::Get()->cpu_data_collector();
+
+ const std::vector<CpuDataCollector::StateOccupancySampleDeque>& idle_data =
+ cpu_data_collector.cpu_idle_state_data();
+ const std::vector<std::string>& idle_state_names =
+ cpu_data_collector.cpu_idle_state_names();
+ base::ListValue js_idle_data;
+ GetJsStateOccupancyData(idle_data, idle_state_names, &js_idle_data);
+
+ base::ListValue js_system_resumed_data;
+ GetJsSystemResumedData(&js_system_resumed_data);
+
+ web_ui()->CallJavascriptFunctionUnsafe(kOnRequestCpuIdleDataFunction,
+ js_idle_data, js_system_resumed_data);
+}
+
+void PowerMessageHandler::OnGetCpuFreqData(const base::ListValue* value) {
+ const CpuDataCollector& cpu_data_collector =
+ PowerDataCollector::Get()->cpu_data_collector();
+
+ const std::vector<CpuDataCollector::StateOccupancySampleDeque>& freq_data =
+ cpu_data_collector.cpu_freq_state_data();
+ const std::vector<std::string>& freq_state_names =
+ cpu_data_collector.cpu_freq_state_names();
+ base::ListValue js_freq_data;
+ GetJsStateOccupancyData(freq_data, freq_state_names, &js_freq_data);
+
+ base::ListValue js_system_resumed_data;
+ GetJsSystemResumedData(&js_system_resumed_data);
+
+ web_ui()->CallJavascriptFunctionUnsafe(kOnRequestCpuFreqDataFunction,
+ js_freq_data, js_system_resumed_data);
+}
+
+void PowerMessageHandler::GetJsSystemResumedData(base::ListValue *data) {
+ DCHECK(data);
+
+ const std::deque<PowerDataCollector::SystemResumedSample>& system_resumed =
+ PowerDataCollector::Get()->system_resumed_data();
+ for (size_t i = 0; i < system_resumed.size(); ++i) {
+ const PowerDataCollector::SystemResumedSample& sample = system_resumed[i];
+ std::unique_ptr<base::DictionaryValue> element(new base::DictionaryValue);
+ element->SetDouble("sleepDuration",
+ sample.sleep_duration.InMillisecondsF());
+ element->SetDouble("time", sample.time.ToJsTime());
+
+ data->Append(std::move(element));
+ }
+}
+
+void PowerMessageHandler::GetJsStateOccupancyData(
+ const std::vector<CpuDataCollector::StateOccupancySampleDeque>& data,
+ const std::vector<std::string>& state_names,
+ base::ListValue *js_data) {
+ for (unsigned int cpu = 0; cpu < data.size(); ++cpu) {
+ const CpuDataCollector::StateOccupancySampleDeque& sample_deque = data[cpu];
+ std::unique_ptr<base::ListValue> js_sample_list(new base::ListValue);
+ for (unsigned int i = 0; i < sample_deque.size(); ++i) {
+ const CpuDataCollector::StateOccupancySample& sample = sample_deque[i];
+ std::unique_ptr<base::DictionaryValue> js_sample(
+ new base::DictionaryValue);
+ js_sample->SetDouble("time", sample.time.ToJsTime());
+ js_sample->SetBoolean("cpuOnline", sample.cpu_online);
+
+ std::unique_ptr<base::DictionaryValue> state_dict(
+ new base::DictionaryValue);
+ for (size_t index = 0; index < sample.time_in_state.size(); ++index) {
+ state_dict->SetDouble(state_names[index],
+ sample.time_in_state[index].InMillisecondsF());
+ }
+ js_sample->Set("timeInState", std::move(state_dict));
+
+ js_sample_list->Append(std::move(js_sample));
+ }
+ js_data->Append(std::move(js_sample_list));
+ }
+}
+
+} // namespace
+
+PowerUI::PowerUI(content::WebUI* web_ui) : content::WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<PowerMessageHandler>());
+
+ content::WebUIDataSource* html =
+ content::WebUIDataSource::Create(chrome::kChromeUIPowerHost);
+
+ html->AddLocalizedString("titleText", IDS_ABOUT_POWER_TITLE);
+ html->AddLocalizedString("showButton", IDS_ABOUT_POWER_SHOW_BUTTON);
+ html->AddLocalizedString("hideButton", IDS_ABOUT_POWER_HIDE_BUTTON);
+ html->AddLocalizedString("reloadButton", IDS_ABOUT_POWER_RELOAD_BUTTON);
+ html->AddLocalizedString("notEnoughDataAvailableYet",
+ IDS_ABOUT_POWER_NOT_ENOUGH_DATA);
+ html->AddLocalizedString("systemSuspended",
+ IDS_ABOUT_POWER_SYSTEM_SUSPENDED);
+ html->AddLocalizedString("invalidData", IDS_ABOUT_POWER_INVALID);
+ html->AddLocalizedString("offlineText", IDS_ABOUT_POWER_OFFLINE);
+
+ html->AddLocalizedString("batteryChargeSectionTitle",
+ IDS_ABOUT_POWER_BATTERY_CHARGE_SECTION_TITLE);
+ html->AddLocalizedString("batteryChargePercentageHeader",
+ IDS_ABOUT_POWER_BATTERY_CHARGE_PERCENTAGE_HEADER);
+ html->AddLocalizedString("batteryDischargeRateHeader",
+ IDS_ABOUT_POWER_BATTERY_DISCHARGE_RATE_HEADER);
+ html->AddLocalizedString("dischargeRateLegendText",
+ IDS_ABOUT_POWER_DISCHARGE_RATE_LEGEND_TEXT);
+ html->AddLocalizedString("movingAverageLegendText",
+ IDS_ABOUT_POWER_MOVING_AVERAGE_LEGEND_TEXT);
+ html->AddLocalizedString("binnedAverageLegendText",
+ IDS_ABOUT_POWER_BINNED_AVERAGE_LEGEND_TEXT);
+ html->AddLocalizedString("averageOverText",
+ IDS_ABOUT_POWER_AVERAGE_OVER_TEXT);
+ html->AddLocalizedString("samplesText",
+ IDS_ABOUT_POWER_AVERAGE_SAMPLES_TEXT);
+
+ html->AddLocalizedString("cpuIdleSectionTitle",
+ IDS_ABOUT_POWER_CPU_IDLE_SECTION_TITLE);
+ html->AddLocalizedString("idleStateOccupancyPercentageHeader",
+ IDS_ABOUT_POWER_CPU_IDLE_STATE_OCCUPANCY_PERCENTAGE);
+
+ html->AddLocalizedString("cpuFreqSectionTitle",
+ IDS_ABOUT_POWER_CPU_FREQ_SECTION_TITLE);
+ html->AddLocalizedString("frequencyStateOccupancyPercentageHeader",
+ IDS_ABOUT_POWER_CPU_FREQ_STATE_OCCUPANCY_PERCENTAGE);
+
+ html->SetJsonPath(kStringsJsFile);
+
+ html->AddResourcePath("power.css", IDR_ABOUT_POWER_CSS);
+ html->AddResourcePath("power.js", IDR_ABOUT_POWER_JS);
+ html->SetDefaultResource(IDR_ABOUT_POWER_HTML);
+
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, html);
+}
+
+PowerUI::~PowerUI() {
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/power_ui.h b/chromium/chrome/browser/ui/webui/chromeos/power_ui.h
new file mode 100644
index 00000000000..19e21c903b5
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/power_ui.h
@@ -0,0 +1,24 @@
+// 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_UI_WEBUI_CHROMEOS_POWER_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_POWER_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace chromeos {
+
+class PowerUI : public content::WebUIController {
+ public:
+ explicit PowerUI(content::WebUI* web_ui);
+ ~PowerUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PowerUI);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_POWER_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/proxy_settings_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/proxy_settings_ui.cc
new file mode 100644
index 00000000000..6a1c0b4db82
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/proxy_settings_ui.cc
@@ -0,0 +1,140 @@
+// 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/ui/webui/chromeos/proxy_settings_ui.h"
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/system/input_device_settings.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/proxy_handler.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chromeos/chromeos_constants.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/jstemplate_builder.h"
+#include "ui/base/webui/web_ui_util.h"
+
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace {
+
+class ProxySettingsHTMLSource : public content::URLDataSource {
+ public:
+ explicit ProxySettingsHTMLSource(base::DictionaryValue* localized_strings);
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ std::string GetMimeType(const std::string&) const override {
+ return "text/html";
+ }
+ bool ShouldAddContentSecurityPolicy() const override { return false; }
+ bool AllowCaching() const override {
+ // Should not be cached to reflect dynamically-generated contents that
+ // may depend on current settings.
+ return false;
+ }
+
+ protected:
+ ~ProxySettingsHTMLSource() override {}
+
+ private:
+ std::unique_ptr<base::DictionaryValue> localized_strings_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProxySettingsHTMLSource);
+};
+
+ProxySettingsHTMLSource::ProxySettingsHTMLSource(
+ base::DictionaryValue* localized_strings)
+ : localized_strings_(localized_strings) {
+}
+
+std::string ProxySettingsHTMLSource::GetSource() const {
+ return chrome::kChromeUIProxySettingsHost;
+}
+
+void ProxySettingsHTMLSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ const std::string& app_locale = g_browser_process->GetApplicationLocale();
+ webui::SetLoadTimeDataDefaults(app_locale, localized_strings_.get());
+
+ static const base::StringPiece html(
+ ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_PROXY_SETTINGS_HTML));
+ std::string full_html = webui::GetI18nTemplateHtml(
+ html, localized_strings_.get());
+
+ callback.Run(base::RefCountedString::TakeString(&full_html));
+}
+
+} // namespace
+
+namespace chromeos {
+
+ProxySettingsUI::ProxySettingsUI(content::WebUI* web_ui)
+ : ui::WebDialogUI(web_ui), initialized_handlers_(false) {
+ // |localized_strings| will be owned by ProxySettingsHTMLSource.
+ base::DictionaryValue* localized_strings = new base::DictionaryValue();
+
+ auto core_handler = base::MakeUnique<options::CoreChromeOSOptionsHandler>();
+ core_handler_ = core_handler.get();
+ web_ui->AddMessageHandler(std::move(core_handler));
+ core_handler_->set_handlers_host(this);
+ core_handler_->GetLocalizedValues(localized_strings);
+
+ auto proxy_handler = base::MakeUnique<options::ProxyHandler>();
+ proxy_handler_ = proxy_handler.get();
+ web_ui->AddMessageHandler(std::move(proxy_handler));
+ proxy_handler_->GetLocalizedValues(localized_strings);
+
+ bool keyboard_driven_oobe =
+ system::InputDeviceSettings::Get()->ForceKeyboardDrivenUINavigation();
+ localized_strings->SetString("highlightStrength",
+ keyboard_driven_oobe ? "strong" : "normal");
+
+ ProxySettingsHTMLSource* source =
+ new ProxySettingsHTMLSource(localized_strings);
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::URLDataSource::Add(profile, source);
+}
+
+ProxySettingsUI::~ProxySettingsUI() {
+ // Uninitialize all registered handlers. The base class owns them and it will
+ // eventually delete them.
+ core_handler_->Uninitialize();
+ proxy_handler_->Uninitialize();
+}
+
+void ProxySettingsUI::InitializeHandlers() {
+ // A new web page DOM has been brought up in an existing renderer, causing
+ // this method to be called twice. In that case, don't initialize the handlers
+ // again. Compare with options_ui.cc.
+ if (!initialized_handlers_) {
+ core_handler_->InitializeHandler();
+ proxy_handler_->InitializeHandler();
+ initialized_handlers_ = true;
+ }
+ core_handler_->InitializePage();
+ proxy_handler_->InitializePage();
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/proxy_settings_ui.h b/chromium/chrome/browser/ui/webui/chromeos/proxy_settings_ui.h
new file mode 100644
index 00000000000..d0162c830cb
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/proxy_settings_ui.h
@@ -0,0 +1,41 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_PROXY_SETTINGS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_PROXY_SETTINGS_UI_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+namespace chromeos {
+
+namespace options {
+class CoreChromeOSOptionsHandler;
+class ProxyHandler;
+}
+
+// A WebUI to host proxy settings splitted from settings page for better
+// performance.
+class ProxySettingsUI : public ui::WebDialogUI,
+ public ::options::OptionsPageUIHandlerHost {
+ public:
+ explicit ProxySettingsUI(content::WebUI* web_ui);
+ ~ProxySettingsUI() override;
+
+ private:
+ // Overridden from OptionsPageUIHandlerHost:
+ void InitializeHandlers() override;
+
+ bool initialized_handlers_;
+
+ options::ProxyHandler* proxy_handler_ = nullptr;
+ options::CoreChromeOSOptionsHandler* core_handler_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(ProxySettingsUI);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_PROXY_SETTINGS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/set_time_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/set_time_ui.cc
new file mode 100644
index 00000000000..98871678f7e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/set_time_ui.cc
@@ -0,0 +1,144 @@
+// 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/ui/webui/chromeos/set_time_ui.h"
+
+#include <stdint.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/build_time.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/chromeos/system/timezone_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/system_clock_client.h"
+#include "chromeos/login/login_state.h"
+#include "chromeos/settings/timezone_settings.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace chromeos {
+
+namespace {
+
+class SetTimeMessageHandler : public content::WebUIMessageHandler,
+ public chromeos::SystemClockClient::Observer,
+ public system::TimezoneSettings::Observer {
+ public:
+ SetTimeMessageHandler() {
+ system::TimezoneSettings::GetInstance()->AddObserver(this);
+ chromeos::DBusThreadManager::Get()->GetSystemClockClient()->AddObserver(
+ this);
+ }
+
+ ~SetTimeMessageHandler() override {
+ system::TimezoneSettings::GetInstance()->RemoveObserver(this);
+ chromeos::DBusThreadManager::Get()->GetSystemClockClient()->RemoveObserver(
+ this);
+ }
+
+ // WebUIMessageHandler:
+ void RegisterMessages() override {
+ web_ui()->RegisterMessageCallback(
+ "setTimeInSeconds",
+ base::Bind(&SetTimeMessageHandler::OnSetTime, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setTimezone",
+ base::Bind(&SetTimeMessageHandler::OnSetTimezone,
+ base::Unretained(this)));
+ }
+
+ private:
+ // system::SystemClockClient::Observer:
+ void SystemClockUpdated() override {
+ web_ui()->CallJavascriptFunctionUnsafe("settime.TimeSetter.updateTime");
+ }
+
+ // system::TimezoneSettings::Observer:
+ void TimezoneChanged(const icu::TimeZone& timezone) override {
+ base::Value timezone_id(system::TimezoneSettings::GetTimezoneID(timezone));
+ web_ui()->CallJavascriptFunctionUnsafe("settime.TimeSetter.setTimezone",
+ timezone_id);
+ }
+
+ // Handler for Javascript call to set the system clock when the user sets a
+ // new time. Expects the time as the number of seconds since the Unix
+ // epoch, treated as a double.
+ void OnSetTime(const base::ListValue* args) {
+ double seconds;
+ if (!args->GetDouble(0, &seconds)) {
+ NOTREACHED();
+ return;
+ }
+
+ chromeos::DBusThreadManager::Get()->GetSystemClockClient()->SetTime(
+ static_cast<int64_t>(seconds));
+ }
+
+ // Handler for Javascript call to change the system time zone when the user
+ // selects a new time zone. Expects the time zone ID as a string, as it
+ // appears in the time zone option values.
+ void OnSetTimezone(const base::ListValue* args) {
+ std::string timezone_id;
+ if (!args->GetString(0, &timezone_id)) {
+ NOTREACHED();
+ return;
+ }
+
+ CrosSettings::Get()->SetString(kSystemTimezone, timezone_id);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(SetTimeMessageHandler);
+};
+
+} // namespace
+
+SetTimeUI::SetTimeUI(content::WebUI* web_ui) : WebDialogUI(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<SetTimeMessageHandler>());
+
+ // Set up the chrome://set-time source.
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUISetTimeHost);
+
+ source->AddLocalizedString("setTimeTitle", IDS_SET_TIME_TITLE);
+ source->AddLocalizedString("prompt", IDS_SET_TIME_PROMPT);
+ source->AddLocalizedString("doneButton", IDS_SET_TIME_BUTTON_CLOSE);
+ source->AddLocalizedString("timezone",
+ IDS_OPTIONS_SETTINGS_TIMEZONE_DESCRIPTION);
+ source->AddLocalizedString("dateLabel", IDS_SET_TIME_DATE_LABEL);
+ source->AddLocalizedString("timeLabel", IDS_SET_TIME_TIME_LABEL);
+
+ base::DictionaryValue values;
+ values.Set("timezoneList", chromeos::system::GetTimezoneList());
+
+ // If we are not logged in, we need to show the time zone dropdown.
+ // Otherwise, we can leave |currentTimezoneId| blank.
+ std::string current_timezone_id;
+ if (!LoginState::Get()->IsUserLoggedIn())
+ CrosSettings::Get()->GetString(kSystemTimezone, &current_timezone_id);
+ values.SetString("currentTimezoneId", current_timezone_id);
+ values.SetDouble("buildTime", base::GetBuildTime().ToJsTime());
+
+ source->AddLocalizedStrings(values);
+ source->SetJsonPath("strings.js");
+
+ source->AddResourcePath("set_time.css", IDR_SET_TIME_CSS);
+ source->AddResourcePath("set_time.js", IDR_SET_TIME_JS);
+ source->SetDefaultResource(IDR_SET_TIME_HTML);
+
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), source);
+}
+
+SetTimeUI::~SetTimeUI() {
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/set_time_ui.h b/chromium/chrome/browser/ui/webui/chromeos/set_time_ui.h
new file mode 100644
index 00000000000..a63a0ed5f92
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/set_time_ui.h
@@ -0,0 +1,25 @@
+// 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_UI_WEBUI_CHROMEOS_SET_TIME_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_SET_TIME_UI_H_
+
+#include "base/macros.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+namespace chromeos {
+
+// The WebUI for chrome://set-time.
+class SetTimeUI : public ui::WebDialogUI {
+ public:
+ explicit SetTimeUI(content::WebUI* web_ui);
+ ~SetTimeUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SetTimeUI);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_SET_TIME_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/set_time_ui_browsertest.js b/chromium/chrome/browser/ui/webui/chromeos/set_time_ui_browsertest.js
new file mode 100644
index 00000000000..1249c87450f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/set_time_ui_browsertest.js
@@ -0,0 +1,37 @@
+// 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.
+
+GEN('#if defined(OS_CHROMEOS)');
+
+/**
+ * SetTimeWebUITest tests loading and interacting with the SetTimeUI web UI,
+ * which is normally shown as a dialog.
+ * @constructor
+ * @extends {testing.Test}
+ */
+function SetTimeWebUITest() {}
+
+SetTimeWebUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /**
+ * Browse to set time dialog.
+ * @override
+ */
+ browsePreload: 'chrome://set-time/',
+};
+
+TEST_F('SetTimeWebUITest', 'testChangeTimezone', function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ var TimeSetter = settime.TimeSetter;
+
+ // Verify timezone.
+ TimeSetter.setTimezone('America/New_York');
+ expectEquals('America/New_York', $('timezone-select').value);
+ TimeSetter.setTimezone('Europe/Moscow');
+ expectEquals('Europe/Moscow', $('timezone-select').value);
+});
+
+GEN('#endif');
diff --git a/chromium/chrome/browser/ui/webui/chromeos/sim_unlock_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/sim_unlock_ui.cc
new file mode 100644
index 00000000000..7826d8e46e9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/sim_unlock_ui.cc
@@ -0,0 +1,776 @@
+// 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/ui/webui/chromeos/sim_unlock_ui.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_piece.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/sim_dialog_delegate.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/network/device_state.h"
+#include "chromeos/network/network_device_handler.h"
+#include "chromeos/network/network_event_log.h"
+#include "chromeos/network/network_state_handler.h"
+#include "chromeos/network/network_state_handler_observer.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/jstemplate_builder.h"
+#include "ui/base/webui/web_ui_util.h"
+
+using content::BrowserThread;
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace {
+
+// JS API callbacks names.
+const char kJsApiChangePinCode[] = "changePinCode";
+const char kJsApiEnterPinCode[] = "enterPinCode";
+const char kJsApiEnterPukCode[] = "enterPukCode";
+const char kJsApiProceedToPukInput[] = "proceedToPukInput";
+const char kJsApiSimStatusInitialize[] = "simStatusInitialize";
+
+// Page JS API function names.
+const char kJsApiSimStatusChanged[] = "mobile.SimUnlock.simStateChanged";
+
+// SIM state variables which are passed to the page.
+const char kState[] = "state";
+const char kError[] = "error";
+const char kTriesLeft[] = "tries";
+
+// Error constants, passed to the page.
+const char kErrorPin[] = "incorrectPin";
+const char kErrorOk[] = "ok";
+
+chromeos::NetworkDeviceHandler* GetNetworkDeviceHandler() {
+ return chromeos::NetworkHandler::Get()->network_device_handler();
+}
+
+chromeos::NetworkStateHandler* GetNetworkStateHandler() {
+ return chromeos::NetworkHandler::Get()->network_state_handler();
+}
+
+} // namespace
+
+namespace chromeos {
+
+class SimUnlockUIHTMLSource : public content::URLDataSource {
+ public:
+ SimUnlockUIHTMLSource();
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ std::string GetMimeType(const std::string&) const override {
+ return "text/html";
+ }
+ bool ShouldAddContentSecurityPolicy() const override { return false; }
+ bool AllowCaching() const override {
+ // Should not be cached to reflect dynamically-generated contents that may
+ // depend on current settings.
+ return false;
+ }
+
+ private:
+ ~SimUnlockUIHTMLSource() override {}
+
+ std::string service_path_;
+ DISALLOW_COPY_AND_ASSIGN(SimUnlockUIHTMLSource);
+};
+
+// The handler for Javascript messages related to the "sim-unlock" view.
+class SimUnlockHandler : public WebUIMessageHandler,
+ public base::SupportsWeakPtr<SimUnlockHandler>,
+ public NetworkStateHandlerObserver {
+ public:
+ SimUnlockHandler();
+ ~SimUnlockHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // NetworkStateHandlerObserver implementation.
+ void DeviceListChanged() override;
+
+ private:
+ // Should keep this state enum in sync with similar one in JS code.
+ // SIM_NOT_LOCKED_ASK_PIN - SIM card is not locked but we ask user
+ // for PIN input because PinRequired preference change was requested.
+ // SIM_NOT_LOCKED_CHANGE_PIN - SIM card is not locked, ask user for old PIN
+ // and new PIN to change it.
+ typedef enum SimUnlockState {
+ SIM_UNLOCK_LOADING = -1,
+ SIM_ABSENT_NOT_LOCKED = 0,
+ SIM_NOT_LOCKED_ASK_PIN = 1,
+ SIM_NOT_LOCKED_CHANGE_PIN = 2,
+ SIM_LOCKED_PIN = 3,
+ SIM_LOCKED_NO_PIN_TRIES_LEFT = 4,
+ SIM_LOCKED_PUK = 5,
+ SIM_LOCKED_NO_PUK_TRIES_LEFT = 6,
+ SIM_DISABLED = 7,
+ } SimUnlockState;
+
+ // Type of the SIM unlock code.
+ enum SimUnlockCode {
+ CODE_PIN,
+ CODE_PUK
+ };
+
+ enum PinOperationError {
+ PIN_ERROR_NONE = 0,
+ PIN_ERROR_UNKNOWN = 1,
+ PIN_ERROR_INCORRECT_CODE = 2,
+ PIN_ERROR_BLOCKED = 3
+ };
+
+ class TaskProxy : public base::RefCountedThreadSafe<TaskProxy> {
+ public:
+ explicit TaskProxy(const base::WeakPtr<SimUnlockHandler>& handler)
+ : handler_(handler),
+ code_type_() {
+ }
+
+ TaskProxy(const base::WeakPtr<SimUnlockHandler>& handler,
+ const std::string& code,
+ SimUnlockCode code_type)
+ : handler_(handler),
+ code_(code),
+ code_type_(code_type) {
+ }
+
+ void HandleEnterCode() {
+ if (handler_)
+ handler_->EnterCode(code_, code_type_);
+ }
+
+ void HandleInitialize() {
+ if (handler_)
+ handler_->InitializeSimStatus();
+ }
+
+ void HandleProceedToPukInput() {
+ if (handler_)
+ handler_->ProceedToPukInput();
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<TaskProxy>;
+
+ ~TaskProxy() {}
+
+ base::WeakPtr<SimUnlockHandler> handler_;
+
+ // Pending code input (PIN/PUK).
+ std::string code_;
+
+ // Pending code type.
+ SimUnlockCode code_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskProxy);
+ };
+
+ // Returns the cellular device that this dialog currently corresponds to.
+ const DeviceState* GetCellularDevice();
+
+ // Pass PIN/PUK code to shill and check status.
+ void EnterCode(const std::string& code, SimUnlockCode code_type);
+
+ // Methods to invoke shill PIN/PUK D-Bus operations.
+ void ChangeRequirePin(bool require_pin, const std::string& pin);
+ void EnterPin(const std::string& pin);
+ void ChangePin(const std::string& old_pin, const std::string& new_pin);
+ void UnblockPin(const std::string& puk, const std::string& new_pin);
+ void PinOperationSuccessCallback(const std::string& operation_name);
+ void PinOperationErrorCallback(
+ const std::string& operation_name,
+ const std::string& error_name,
+ std::unique_ptr<base::DictionaryValue> error_data);
+
+ // Called when an asynchronous PIN operation has completed.
+ void OnPinOperationCompleted(PinOperationError error);
+
+ // Single handler for PIN/PUK code operations.
+ void HandleEnterCode(SimUnlockCode code_type, const std::string& code);
+
+ // Handlers for JS WebUI messages.
+ void HandleChangePinCode(const base::ListValue* args);
+ void HandleEnterPinCode(const base::ListValue* args);
+ void HandleEnterPukCode(const base::ListValue* args);
+ void HandleProceedToPukInput(const base::ListValue* args);
+ void HandleSimStatusInitialize(const base::ListValue* args);
+
+ // Initialize current SIM card status, passes that to page.
+ void InitializeSimStatus();
+
+ // Checks whether SIM card is in PUK locked state and proceeds to PUK input.
+ void ProceedToPukInput();
+
+ // Processes current SIM card state and update internal state/page.
+ void ProcessSimCardState(const DeviceState* cellular);
+
+ // Updates page with the current state/SIM card info/error.
+ void UpdatePage(const DeviceState* cellular, const std::string& error_msg);
+
+ // Dialog internal state.
+ SimUnlockState state_;
+
+ // Path of the Cellular device that we monitor property updates from.
+ std::string cellular_device_path_;
+
+ // Type of the dialog: generic unlock/change pin/change PinRequire.
+ SimDialogDelegate::SimDialogMode dialog_mode_;
+
+ // New PIN value for the case when we unblock SIM card or change PIN.
+ std::string new_pin_;
+
+ // The initial lock type value, used to observe changes to lock status;
+ std::string sim_lock_type_;
+
+ // True if there's a pending PIN operation.
+ // That means that SIM lock state change will be received 2 times:
+ // OnNetworkDeviceSimLockChanged and OnPinOperationCompleted.
+ // First one should be ignored.
+ bool pending_pin_operation_;
+
+ base::WeakPtrFactory<SimUnlockHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SimUnlockHandler);
+};
+
+// SimUnlockUIHTMLSource -------------------------------------------------------
+
+SimUnlockUIHTMLSource::SimUnlockUIHTMLSource() {
+}
+
+std::string SimUnlockUIHTMLSource::GetSource() const {
+ return chrome::kChromeUISimUnlockHost;
+}
+
+void SimUnlockUIHTMLSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ base::DictionaryValue strings;
+ strings.SetString("title",
+ l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_ENTER_PIN_TITLE));
+ strings.SetString("ok", l10n_util::GetStringUTF16(IDS_OK));
+ strings.SetString("cancel", l10n_util::GetStringUTF16(IDS_CANCEL));
+ strings.SetString("enterPinTitle",
+ l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_ENTER_PIN_TITLE));
+ strings.SetString("enterPinMessage",
+ l10n_util::GetStringUTF16(IDS_SIM_ENTER_PIN_MESSAGE));
+ strings.SetString("enterPinTriesMessage",
+ l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_ENTER_PIN_TRIES_MESSAGE));
+ strings.SetString("incorrectPinTriesMessage",
+ l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_INCORRECT_PIN_TRIES_MESSAGE));
+ strings.SetString("incorrectPinTitle",
+ l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_INCORRECT_PIN_TITLE));
+ // TODO(nkostylev): Pass carrier name if we know that.
+ strings.SetString("noPinTriesLeft", l10n_util::GetStringFUTF16(
+ IDS_SIM_UNLOCK_NO_PIN_TRIES_LEFT_MESSAGE,
+ l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_DEFAULT_CARRIER)));
+ strings.SetString("enterPukButton",
+ l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_ENTER_PUK_BUTTON));
+ strings.SetString("enterPukTitle",
+ l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_ENTER_PUK_TITLE));
+ strings.SetString("enterPukWarning",
+ l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_ENTER_PUK_WARNING));
+ // TODO(nkostylev): Pass carrier name if we know that.
+ strings.SetString("enterPukMessage", l10n_util::GetStringFUTF16(
+ IDS_SIM_UNLOCK_ENTER_PUK_MESSAGE,
+ l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_DEFAULT_CARRIER)));
+ strings.SetString("choosePinTitle",
+ l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_CHOOSE_PIN_TITLE));
+ strings.SetString("choosePinMessage",
+ l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_CHOOSE_PIN_MESSAGE));
+ strings.SetString("newPin", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_CHANGE_PIN_NEW_PIN));
+ strings.SetString("retypeNewPin", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_CHANGE_PIN_RETYPE_PIN));
+ strings.SetString("pinsDontMatchMessage", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_PINS_DONT_MATCH_ERROR));
+ strings.SetString("noPukTriesLeft",
+ l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_NO_PUK_TRIES_LEFT_MESSAGE));
+ strings.SetString("simDisabledTitle",
+ l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_SIM_DISABLED_TITLE));
+ strings.SetString("simDisabledMessage",
+ l10n_util::GetStringUTF16(IDS_SIM_UNLOCK_SIM_DISABLED_MESSAGE));
+
+ strings.SetString("changePinTitle", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_CHANGE_PIN_TITLE));
+ strings.SetString("changePinMessage", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_CHANGE_PIN_MESSAGE));
+ strings.SetString("oldPin", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_CHANGE_PIN_OLD_PIN));
+
+ const std::string& app_locale = g_browser_process->GetApplicationLocale();
+ webui::SetLoadTimeDataDefaults(app_locale, &strings);
+
+ static const base::StringPiece html(
+ ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_SIM_UNLOCK_HTML));
+
+ std::string full_html = webui::GetI18nTemplateHtml(html, &strings);
+ callback.Run(base::RefCountedString::TakeString(&full_html));
+}
+
+// SimUnlockHandler ------------------------------------------------------------
+
+SimUnlockHandler::SimUnlockHandler()
+ : state_(SIM_UNLOCK_LOADING),
+ dialog_mode_(SimDialogDelegate::SIM_DIALOG_UNLOCK),
+ pending_pin_operation_(false),
+ weak_ptr_factory_(this) {
+ if (GetNetworkStateHandler()
+ ->GetTechnologyState(NetworkTypePattern::Cellular()) !=
+ NetworkStateHandler::TECHNOLOGY_UNAVAILABLE)
+ GetNetworkStateHandler()->AddObserver(this, FROM_HERE);
+}
+
+SimUnlockHandler::~SimUnlockHandler() {
+ GetNetworkStateHandler()->RemoveObserver(this, FROM_HERE);
+}
+
+void SimUnlockHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(kJsApiChangePinCode,
+ base::Bind(&SimUnlockHandler::HandleChangePinCode,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kJsApiEnterPinCode,
+ base::Bind(&SimUnlockHandler::HandleEnterPinCode,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kJsApiEnterPukCode,
+ base::Bind(&SimUnlockHandler::HandleEnterPukCode,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kJsApiProceedToPukInput,
+ base::Bind(&SimUnlockHandler::HandleProceedToPukInput,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kJsApiSimStatusInitialize,
+ base::Bind(&SimUnlockHandler::HandleSimStatusInitialize,
+ base::Unretained(this)));
+}
+
+void SimUnlockHandler::DeviceListChanged() {
+ const DeviceState* cellular_device = GetCellularDevice();
+ if (!cellular_device) {
+ LOG(WARNING) << "Cellular device with path '" << cellular_device_path_
+ << "' disappeared.";
+ ProcessSimCardState(NULL);
+ return;
+ }
+
+ // Process the SIM card state only if the lock state changed.
+ if (cellular_device->sim_lock_type() == sim_lock_type_)
+ return;
+
+ sim_lock_type_ = cellular_device->sim_lock_type();
+ uint32_t retries_left = cellular_device->sim_retries_left();
+ VLOG(1) << "OnNetworkDeviceSimLockChanged, lock: " << sim_lock_type_
+ << ", retries: " << retries_left;
+ // There's a pending PIN operation.
+ // Wait for it to finish and refresh state then.
+ if (!pending_pin_operation_)
+ ProcessSimCardState(cellular_device);
+}
+
+void SimUnlockHandler::OnPinOperationCompleted(PinOperationError error) {
+ pending_pin_operation_ = false;
+ VLOG(1) << "OnPinOperationCompleted, error: " << error;
+ const DeviceState* cellular = GetCellularDevice();
+ if (!cellular) {
+ VLOG(1) << "Cellular device disappeared. Dismissing dialog.";
+ ProcessSimCardState(NULL);
+ return;
+ }
+ if (state_ == SIM_NOT_LOCKED_ASK_PIN && error == PIN_ERROR_NONE) {
+ CHECK(dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_ON ||
+ dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_OFF);
+ // Dialog will close itself.
+ state_ = SIM_ABSENT_NOT_LOCKED;
+ } else if (state_ == SIM_NOT_LOCKED_CHANGE_PIN && error == PIN_ERROR_NONE) {
+ CHECK(dialog_mode_ == SimDialogDelegate::SIM_DIALOG_CHANGE_PIN);
+ // Dialog will close itself.
+ state_ = SIM_ABSENT_NOT_LOCKED;
+ }
+ // If previous EnterPIN was last PIN attempt and SIMLock state was already
+ // processed by OnNetworkDeviceChanged, let dialog stay on
+ // NO_PIN_RETRIES_LEFT step.
+ if (!(state_ == SIM_LOCKED_NO_PIN_TRIES_LEFT && error == PIN_ERROR_BLOCKED))
+ ProcessSimCardState(cellular);
+}
+
+const DeviceState* SimUnlockHandler::GetCellularDevice() {
+ return GetNetworkStateHandler()->GetDeviceState(cellular_device_path_);
+}
+
+void SimUnlockHandler::EnterCode(const std::string& code,
+ SimUnlockCode code_type) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ pending_pin_operation_ = true;
+
+ switch (code_type) {
+ case CODE_PIN:
+ if (dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_ON ||
+ dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_OFF) {
+ if (!sim_lock_type_.empty()) {
+ // If SIM is locked/absent, change RequirePin UI is not accessible.
+ NOTREACHED() <<
+ "Changing RequirePin pref on locked / uninitialized SIM.";
+ }
+ ChangeRequirePin(
+ dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_ON,
+ code);
+ } else if (dialog_mode_ == SimDialogDelegate::SIM_DIALOG_CHANGE_PIN) {
+ if (!sim_lock_type_.empty()) {
+ // If SIM is locked/absent, changing PIN UI is not accessible.
+ NOTREACHED() << "Changing PIN on locked / uninitialized SIM.";
+ }
+ ChangePin(code, new_pin_);
+ } else {
+ EnterPin(code);
+ }
+ break;
+ case CODE_PUK:
+ DCHECK(!new_pin_.empty());
+ UnblockPin(code, new_pin_);
+ break;
+ }
+}
+
+void SimUnlockHandler::ChangeRequirePin(bool require_pin,
+ const std::string& pin) {
+ const DeviceState* cellular = GetCellularDevice();
+ if (!cellular) {
+ NOTREACHED() << "Calling RequirePin method w/o cellular device.";
+ return;
+ }
+ std::string operation_name = "ChangeRequirePin";
+ NET_LOG_USER(operation_name, cellular->path());
+ GetNetworkDeviceHandler()->RequirePin(
+ cellular->path(),
+ require_pin,
+ pin,
+ base::Bind(&SimUnlockHandler::PinOperationSuccessCallback,
+ weak_ptr_factory_.GetWeakPtr(),
+ operation_name),
+ base::Bind(&SimUnlockHandler::PinOperationErrorCallback,
+ weak_ptr_factory_.GetWeakPtr(),
+ operation_name));
+}
+
+void SimUnlockHandler::EnterPin(const std::string& pin) {
+ const DeviceState* cellular = GetCellularDevice();
+ if (!cellular) {
+ NOTREACHED() << "Calling RequirePin method w/o cellular device.";
+ return;
+ }
+ std::string operation_name = "EnterPin";
+ NET_LOG_USER(operation_name, cellular->path());
+ GetNetworkDeviceHandler()->EnterPin(
+ cellular->path(),
+ pin,
+ base::Bind(&SimUnlockHandler::PinOperationSuccessCallback,
+ weak_ptr_factory_.GetWeakPtr(),
+ operation_name),
+ base::Bind(&SimUnlockHandler::PinOperationErrorCallback,
+ weak_ptr_factory_.GetWeakPtr(),
+ operation_name));
+}
+
+void SimUnlockHandler::ChangePin(const std::string& old_pin,
+ const std::string& new_pin) {
+ const DeviceState* cellular = GetCellularDevice();
+ if (!cellular) {
+ NOTREACHED() << "Calling RequirePin method w/o cellular device.";
+ return;
+ }
+ std::string operation_name = "ChangePin";
+ NET_LOG_USER(operation_name, cellular->path());
+ GetNetworkDeviceHandler()->ChangePin(
+ cellular->path(),
+ old_pin,
+ new_pin,
+ base::Bind(&SimUnlockHandler::PinOperationSuccessCallback,
+ weak_ptr_factory_.GetWeakPtr(),
+ operation_name),
+ base::Bind(&SimUnlockHandler::PinOperationErrorCallback,
+ weak_ptr_factory_.GetWeakPtr(),
+ operation_name));
+}
+
+void SimUnlockHandler::UnblockPin(const std::string& puk,
+ const std::string& new_pin) {
+ const DeviceState* cellular = GetCellularDevice();
+ if (!cellular) {
+ NOTREACHED() << "Calling RequirePin method w/o cellular device.";
+ return;
+ }
+ std::string operation_name = "UnblockPin";
+ NET_LOG_USER(operation_name, cellular->path());
+ GetNetworkDeviceHandler()->UnblockPin(
+ cellular->path(),
+ puk,
+ new_pin,
+ base::Bind(&SimUnlockHandler::PinOperationSuccessCallback,
+ weak_ptr_factory_.GetWeakPtr(),
+ operation_name),
+ base::Bind(&SimUnlockHandler::PinOperationErrorCallback,
+ weak_ptr_factory_.GetWeakPtr(),
+ operation_name));
+}
+
+void SimUnlockHandler::PinOperationSuccessCallback(
+ const std::string& operation_name) {
+ NET_LOG_DEBUG("Pin operation successful.", operation_name);
+ OnPinOperationCompleted(PIN_ERROR_NONE);
+}
+
+void SimUnlockHandler::PinOperationErrorCallback(
+ const std::string& operation_name,
+ const std::string& error_name,
+ std::unique_ptr<base::DictionaryValue> error_data) {
+ NET_LOG_ERROR("Pin operation failed: " + error_name, operation_name);
+ PinOperationError pin_error;
+ if (error_name == NetworkDeviceHandler::kErrorIncorrectPin ||
+ error_name == NetworkDeviceHandler::kErrorPinRequired)
+ pin_error = PIN_ERROR_INCORRECT_CODE;
+ else if (error_name == NetworkDeviceHandler::kErrorPinBlocked)
+ pin_error = PIN_ERROR_BLOCKED;
+ else
+ pin_error = PIN_ERROR_UNKNOWN;
+ OnPinOperationCompleted(pin_error);
+}
+
+void SimUnlockHandler::HandleChangePinCode(const base::ListValue* args) {
+ const size_t kChangePinParamCount = 2;
+ std::string pin;
+ std::string new_pin;
+ if (args->GetSize() != kChangePinParamCount ||
+ !args->GetString(0, &pin) ||
+ !args->GetString(1, &new_pin)) {
+ NOTREACHED();
+ return;
+ }
+ new_pin_ = new_pin;
+ HandleEnterCode(CODE_PIN, pin);
+}
+
+void SimUnlockHandler::HandleEnterCode(SimUnlockCode code_type,
+ const std::string& code) {
+ scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), code, code_type);
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&TaskProxy::HandleEnterCode, task.get()));
+}
+
+void SimUnlockHandler::HandleEnterPinCode(const base::ListValue* args) {
+ const size_t kEnterPinParamCount = 1;
+ std::string pin;
+ if (args->GetSize() != kEnterPinParamCount || !args->GetString(0, &pin)) {
+ NOTREACHED();
+ return;
+ }
+ HandleEnterCode(CODE_PIN, pin);
+}
+
+void SimUnlockHandler::HandleEnterPukCode(const base::ListValue* args) {
+ const size_t kEnterPukParamCount = 2;
+ std::string puk;
+ std::string new_pin;
+ if (args->GetSize() != kEnterPukParamCount ||
+ !args->GetString(0, &puk) ||
+ !args->GetString(1, &new_pin)) {
+ NOTREACHED();
+ return;
+ }
+ new_pin_ = new_pin;
+ HandleEnterCode(CODE_PUK, puk);
+}
+
+void SimUnlockHandler::HandleProceedToPukInput(const base::ListValue* args) {
+ const size_t kProceedToPukInputParamCount = 0;
+ if (args->GetSize() != kProceedToPukInputParamCount) {
+ NOTREACHED();
+ return;
+ }
+ scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr());
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&TaskProxy::HandleProceedToPukInput, task.get()));
+}
+
+void SimUnlockHandler::HandleSimStatusInitialize(const base::ListValue* args) {
+ const size_t kSimStatusInitializeParamCount = 1;
+ double mode;
+ if (args->GetSize() != kSimStatusInitializeParamCount ||
+ !args->GetDouble(0, &mode)) {
+ NOTREACHED();
+ return;
+ }
+ dialog_mode_ = static_cast<SimDialogDelegate::SimDialogMode>(mode);
+ VLOG(1) << "Initializing SIM dialog in mode: " << dialog_mode_;
+ scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr());
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&TaskProxy::HandleInitialize, task.get()));
+}
+
+void SimUnlockHandler::InitializeSimStatus() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ // TODO(armansito): For now, we're initializing the device path to the first
+ // available cellular device. We should try to obtain a specific device here,
+ // as there can be multiple cellular devices present.
+ const DeviceState* cellular_device =
+ GetNetworkStateHandler()
+ ->GetDeviceStateByType(NetworkTypePattern::Cellular());
+ if (cellular_device) {
+ cellular_device_path_ = cellular_device->path();
+ sim_lock_type_ = cellular_device->sim_lock_type();
+ }
+ ProcessSimCardState(cellular_device);
+}
+
+void SimUnlockHandler::ProceedToPukInput() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ ProcessSimCardState(GetCellularDevice());
+}
+
+void SimUnlockHandler::ProcessSimCardState(
+ const DeviceState* cellular) {
+ std::string error_msg;
+ if (cellular) {
+ uint32_t retries_left = cellular->sim_retries_left();
+ VLOG(1) << "Current state: " << state_ << " lock_type: " << sim_lock_type_
+ << " retries: " << retries_left;
+ switch (state_) {
+ case SIM_UNLOCK_LOADING:
+ if (sim_lock_type_ == shill::kSIMLockPin) {
+ state_ = SIM_LOCKED_PIN;
+ } else if (sim_lock_type_ == shill::kSIMLockPuk) {
+ if (retries_left > 0)
+ state_ = SIM_LOCKED_PUK;
+ else
+ state_ = SIM_DISABLED;
+ } else if (sim_lock_type_.empty()) {
+ if (dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_ON ||
+ dialog_mode_ == SimDialogDelegate::SIM_DIALOG_SET_LOCK_OFF) {
+ state_ = SIM_NOT_LOCKED_ASK_PIN;
+ } else if (dialog_mode_ == SimDialogDelegate::SIM_DIALOG_CHANGE_PIN) {
+ state_ = SIM_NOT_LOCKED_CHANGE_PIN;
+ } else {
+ state_ = SIM_ABSENT_NOT_LOCKED;
+ }
+ } else {
+ // SIM_UNKNOWN: when SIM status is not initialized (should not happen,
+ // since this UI is accessible when SIM is initialized)
+ // or SIM card is absent. In latter case just close dialog.
+ state_ = SIM_ABSENT_NOT_LOCKED;
+ }
+ break;
+ case SIM_ABSENT_NOT_LOCKED:
+ // Dialog will close itself in this case.
+ break;
+ case SIM_NOT_LOCKED_ASK_PIN:
+ case SIM_NOT_LOCKED_CHANGE_PIN:
+ // We always start in these states when SIM is unlocked.
+ // So if we get here while still being UNLOCKED,
+ // that means entered PIN was incorrect.
+ if (sim_lock_type_.empty()) {
+ error_msg = kErrorPin;
+ } else if (sim_lock_type_ == shill::kSIMLockPuk) {
+ state_ = SIM_LOCKED_NO_PIN_TRIES_LEFT;
+ } else {
+ NOTREACHED()
+ << "Change PIN / Set lock mode with unexpected SIM lock state";
+ state_ = SIM_ABSENT_NOT_LOCKED;
+ }
+ break;
+ case SIM_LOCKED_PIN:
+ if (sim_lock_type_ == shill::kSIMLockPuk) {
+ state_ = SIM_LOCKED_NO_PIN_TRIES_LEFT;
+ } else if (sim_lock_type_ == shill::kSIMLockPin) {
+ // Still locked with PIN.
+ error_msg = kErrorPin;
+ } else {
+ state_ = SIM_ABSENT_NOT_LOCKED;
+ }
+ break;
+ case SIM_LOCKED_NO_PIN_TRIES_LEFT:
+ // Proceed user to PUK input.
+ state_ = SIM_LOCKED_PUK;
+ break;
+ case SIM_LOCKED_PUK:
+ if (sim_lock_type_ != shill::kSIMLockPin &&
+ sim_lock_type_ != shill::kSIMLockPuk) {
+ state_ = SIM_ABSENT_NOT_LOCKED;
+ } else if (retries_left == 0) {
+ state_ = SIM_LOCKED_NO_PUK_TRIES_LEFT;
+ }
+ // Otherwise SIM card is still locked with PUK code.
+ // Dialog will display enter PUK screen with an updated retries count.
+ break;
+ case SIM_LOCKED_NO_PUK_TRIES_LEFT:
+ case SIM_DISABLED:
+ // User will close dialog manually.
+ break;
+ }
+ } else {
+ VLOG(1) << "Cellular device is absent.";
+ // No cellular device, should close dialog.
+ state_ = SIM_ABSENT_NOT_LOCKED;
+ }
+ VLOG(1) << "New state: " << state_;
+ UpdatePage(cellular, error_msg);
+}
+
+void SimUnlockHandler::UpdatePage(const DeviceState* cellular,
+ const std::string& error_msg) {
+ base::DictionaryValue sim_dict;
+ if (cellular)
+ sim_dict.SetInteger(kTriesLeft, cellular->sim_retries_left());
+ sim_dict.SetInteger(kState, state_);
+ if (!error_msg.empty())
+ sim_dict.SetString(kError, error_msg);
+ else
+ sim_dict.SetString(kError, kErrorOk);
+ web_ui()->CallJavascriptFunctionUnsafe(kJsApiSimStatusChanged, sim_dict);
+}
+
+// SimUnlockUI -----------------------------------------------------------------
+
+SimUnlockUI::SimUnlockUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<SimUnlockHandler>());
+ SimUnlockUIHTMLSource* html_source = new SimUnlockUIHTMLSource();
+
+ // Set up the chrome://sim-unlock/ source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::URLDataSource::Add(profile, html_source);
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/sim_unlock_ui.h b/chromium/chrome/browser/ui/webui/chromeos/sim_unlock_ui.h
new file mode 100644
index 00000000000..f7b03d8a206
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/sim_unlock_ui.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2011 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_CHROMEOS_SIM_UNLOCK_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_SIM_UNLOCK_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace chromeos {
+
+// A custom WebUI that defines datasource for SIM unlock dialog that is used
+// in Chrome OS for specific tasks:
+// - Unlock SIM card (enter PIN/PUK codes).
+// - Display "SIM card is blocked" message when there're no PUK tries left.
+class SimUnlockUI : public content::WebUIController {
+ public:
+ explicit SimUnlockUI(content::WebUI* web_ui);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SimUnlockUI);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_SIM_UNLOCK_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/slow_trace_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/slow_trace_ui.cc
new file mode 100644
index 00000000000..7faf7cd9f86
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/slow_trace_ui.cc
@@ -0,0 +1,86 @@
+// 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/chromeos/slow_trace_ui.h"
+
+#include <stddef.h>
+
+#include "base/bind.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "components/feedback/tracing_manager.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_ui.h"
+
+namespace chromeos {
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// SlowTraceSource
+//
+////////////////////////////////////////////////////////////////////////////////
+
+SlowTraceSource::SlowTraceSource() {
+}
+
+std::string SlowTraceSource::GetSource() const {
+ return chrome::kChromeUISlowTraceHost;
+}
+
+void SlowTraceSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ int trace_id = 0;
+ size_t pos = path.find('#');
+ TracingManager* manager = TracingManager::Get();
+ if (!manager ||
+ pos == std::string::npos ||
+ !base::StringToInt(path.substr(pos + 1), &trace_id)) {
+ callback.Run(NULL);
+ return;
+ }
+ manager->GetTraceData(trace_id,
+ base::Bind(&SlowTraceSource::OnGetTraceData,
+ base::Unretained(this),
+ callback));
+}
+
+std::string SlowTraceSource::GetMimeType(const std::string& path) const {
+ return "application/zip";
+}
+
+SlowTraceSource::~SlowTraceSource() {}
+
+void SlowTraceSource::OnGetTraceData(
+ const content::URLDataSource::GotDataCallback& callback,
+ scoped_refptr<base::RefCountedString> trace_data) {
+ callback.Run(trace_data.get());
+}
+
+bool SlowTraceSource::AllowCaching() const {
+ // Should not be cached to reflect dynamically-generated contents that may
+ // depend on current settings.
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// SlowTraceController
+//
+////////////////////////////////////////////////////////////////////////////////
+
+SlowTraceController::SlowTraceController(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ SlowTraceSource* html_source = new SlowTraceSource();
+
+ // Set up the chrome://slow_trace/ source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::URLDataSource::Add(profile, html_source);
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/slow_trace_ui.h b/chromium/chrome/browser/ui/webui/chromeos/slow_trace_ui.h
new file mode 100644
index 00000000000..94ff66ebcd2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/slow_trace_ui.h
@@ -0,0 +1,58 @@
+// 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_CHROMEOS_SLOW_TRACE_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_SLOW_TRACE_UI_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "ui/base/layout.h"
+
+namespace base {
+class RefCountedString;
+}
+
+namespace chromeos {
+
+// This class provides the source for chrome://slow_trace/. It needs to be a
+// separate handler that chrome://slow, because URLDataSource and
+// WebUIDataSource are not descended from each other, and WebUIDataSource
+// doesn't allow the MimeType to be dynamically specified.
+class SlowTraceSource : public content::URLDataSource {
+ public:
+ SlowTraceSource();
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ std::string GetMimeType(const std::string& path) const override;
+ bool AllowCaching() const override;
+
+ private:
+ ~SlowTraceSource() override;
+
+ void OnGetTraceData(const content::URLDataSource::GotDataCallback& callback,
+ scoped_refptr<base::RefCountedString> trace_data);
+
+ DISALLOW_COPY_AND_ASSIGN(SlowTraceSource);
+};
+
+class SlowTraceController : public content::WebUIController {
+ public:
+ explicit SlowTraceController(content::WebUI* web_ui);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SlowTraceController);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_SLOW_TRACE_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/chromeos/slow_ui.cc b/chromium/chrome/browser/ui/webui/chromeos/slow_ui.cc
new file mode 100644
index 00000000000..519deb508d9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/slow_ui.cc
@@ -0,0 +1,137 @@
+// 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/chromeos/slow_ui.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "ui/base/webui/jstemplate_builder.h"
+#include "ui/base/webui/web_ui_util.h"
+
+using content::WebUIMessageHandler;
+
+namespace {
+
+// JS API callbacks names.
+const char kJsApiDisableTracing[] = "disableTracing";
+const char kJsApiEnableTracing[] = "enableTracing";
+const char kJsApiLoadComplete[] = "loadComplete";
+
+// Page JS API function names.
+const char kJsApiTracingPrefChanged[] = "options.Slow.tracingPrefChanged";
+
+} // namespace
+
+namespace chromeos {
+
+content::WebUIDataSource* CreateSlowUIHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUISlowHost);
+
+ source->AddLocalizedString("slowDisable", IDS_SLOW_DISABLE);
+ source->AddLocalizedString("slowEnable", IDS_SLOW_ENABLE);
+ source->AddLocalizedString("slowDescription", IDS_SLOW_DESCRIPTION);
+ source->AddLocalizedString("slowWarning", IDS_SLOW_WARNING);
+
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("slow.js", IDR_SLOW_JS);
+ source->SetDefaultResource(IDR_SLOW_HTML);
+ return source;
+}
+
+// The handler for Javascript messages related to the "slow" view.
+class SlowHandler : public WebUIMessageHandler {
+ public:
+ explicit SlowHandler(Profile* profile);
+ ~SlowHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ void UpdatePage();
+
+ // Handlers for JS WebUI messages.
+ void HandleDisable(const base::ListValue* args);
+ void HandleEnable(const base::ListValue* args);
+ void LoadComplete(const base::ListValue* args);
+
+ Profile* profile_;
+ std::unique_ptr<PrefChangeRegistrar> user_pref_registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(SlowHandler);
+};
+
+// SlowHandler ------------------------------------------------------------
+
+SlowHandler::SlowHandler(Profile* profile) : profile_(profile) {
+}
+
+SlowHandler::~SlowHandler() {
+}
+
+void SlowHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(kJsApiDisableTracing,
+ base::Bind(&SlowHandler::HandleDisable, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kJsApiEnableTracing,
+ base::Bind(&SlowHandler::HandleEnable, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kJsApiLoadComplete,
+ base::Bind(&SlowHandler::LoadComplete, base::Unretained(this)));
+
+ user_pref_registrar_.reset(new PrefChangeRegistrar);
+ user_pref_registrar_->Init(profile_->GetPrefs());
+ user_pref_registrar_->Add(prefs::kPerformanceTracingEnabled,
+ base::Bind(&SlowHandler::UpdatePage,
+ base::Unretained(this)));
+}
+
+void SlowHandler::HandleDisable(const base::ListValue* args) {
+ PrefService* pref_service = profile_->GetPrefs();
+ pref_service->SetBoolean(prefs::kPerformanceTracingEnabled, false);
+}
+
+void SlowHandler::HandleEnable(const base::ListValue* args) {
+ PrefService* pref_service = profile_->GetPrefs();
+ pref_service->SetBoolean(prefs::kPerformanceTracingEnabled, true);
+}
+
+void SlowHandler::LoadComplete(const base::ListValue* args) {
+ UpdatePage();
+}
+
+void SlowHandler::UpdatePage() {
+ PrefService* pref_service = profile_->GetPrefs();
+ bool enabled = pref_service->GetBoolean(prefs::kPerformanceTracingEnabled);
+ base::Value pref_value(enabled);
+ web_ui()->CallJavascriptFunctionUnsafe(kJsApiTracingPrefChanged, pref_value);
+}
+
+// SlowUI -----------------------------------------------------------------
+
+SlowUI::SlowUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+
+ web_ui->AddMessageHandler(base::MakeUnique<SlowHandler>(profile));
+
+ // Set up the chrome://slow/ source.
+ content::WebUIDataSource::Add(profile, CreateSlowUIHTMLSource());
+}
+
+} // namespace chromeos
+
diff --git a/chromium/chrome/browser/ui/webui/chromeos/slow_ui.h b/chromium/chrome/browser/ui/webui/chromeos/slow_ui.h
new file mode 100644
index 00000000000..3ba37aecd91
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/slow_ui.h
@@ -0,0 +1,26 @@
+// 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_CHROMEOS_SLOW_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_SLOW_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace chromeos {
+
+// A custom WebUI that allows users to enable and disable performance tracing
+// for feedback reports.
+class SlowUI : public content::WebUIController {
+ public:
+ explicit SlowUI(content::WebUI* web_ui);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SlowUI);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_SLOW_UI_H_
+
diff --git a/chromium/chrome/browser/ui/webui/chromeos/ui_account_tweaks.cc b/chromium/chrome/browser/ui/webui/chromeos/ui_account_tweaks.cc
new file mode 100644
index 00000000000..85a7c7cf159
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/ui_account_tweaks.cc
@@ -0,0 +1,54 @@
+// 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/ui/webui/chromeos/ui_account_tweaks.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chromeos/settings/cros_settings_names.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace chromeos {
+
+void AddAccountUITweaksLocalizedValues(
+ base::DictionaryValue* localized_strings,
+ Profile* profile) {
+ DCHECK(localized_strings);
+
+ std::string owner_email;
+ CrosSettings::Get()->GetString(kDeviceOwner, &owner_email);
+ // Translate owner's email to the display email.
+ std::string display_email =
+ user_manager::UserManager::Get()->GetUserDisplayEmail(
+ AccountId::FromUserEmail(owner_email));
+ localized_strings->SetString("ownerUserId", display_email);
+
+ localized_strings->SetBoolean("currentUserIsOwner",
+ ProfileHelper::IsOwnerProfile(profile));
+
+ localized_strings->SetBoolean(
+ "loggedInAsGuest", user_manager::UserManager::Get()->IsLoggedInAsGuest());
+
+ localized_strings->SetBoolean(
+ "loggedInAsSupervisedUser",
+ user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser());
+
+ localized_strings->SetBoolean(
+ "loggedInAsPublicAccount",
+ user_manager::UserManager::Get()->IsLoggedInAsPublicAccount());
+}
+
+void AddAccountUITweaksLocalizedValues(
+ content::WebUIDataSource* source,
+ Profile* profile) {
+ DCHECK(source);
+ base::DictionaryValue dict;
+ AddAccountUITweaksLocalizedValues(&dict, profile);
+ source->AddLocalizedStrings(dict);
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/chromeos/ui_account_tweaks.h b/chromium/chrome/browser/ui/webui/chromeos/ui_account_tweaks.h
new file mode 100644
index 00000000000..271085ac74c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/chromeos/ui_account_tweaks.h
@@ -0,0 +1,38 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_UI_ACCOUNT_TWEAKS_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_UI_ACCOUNT_TWEAKS_H_
+
+#include "base/values.h"
+#include "base/compiler_specific.h"
+
+class Profile;
+
+namespace content {
+class WebUIDataSource;
+}
+
+namespace chromeos {
+
+/**
+ * Fills given dictionary with account status data (whether current user is
+ * owner/guest, id of the owner).
+ * @param localized_strings non-null dictionary that will be filled.
+ */
+void AddAccountUITweaksLocalizedValues(
+ base::DictionaryValue* localized_strings, Profile* profile);
+
+/**
+ * Fills given data source with account status data (whether current user is
+ * owner/guest, id of the owner).
+ * @param source non-null ui data source which localized values dictionary will
+ * be filled.
+ */
+void AddAccountUITweaksLocalizedValues(content::WebUIDataSource* source,
+ Profile* profile);
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_UI_ACCOUNT_TWEAKS_H_
diff --git a/chromium/chrome/browser/ui/webui/cleanup_tool/cleanup_action_handler.cc b/chromium/chrome/browser/ui/webui/cleanup_tool/cleanup_action_handler.cc
new file mode 100644
index 00000000000..d1e6bacc8ac
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/cleanup_tool/cleanup_action_handler.cc
@@ -0,0 +1,139 @@
+// 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/cleanup_tool/cleanup_action_handler.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/component_updater/sw_reporter_installer_win.h"
+#include "components/component_updater/component_updater_service.h"
+
+namespace {
+
+bool IsCleanupComponentRegistered() {
+ component_updater::ComponentUpdateService* cus =
+ g_browser_process->component_updater();
+ std::vector<std::string> component_ids = cus->GetComponentIDs();
+
+ return std::find(component_ids.begin(), component_ids.end(),
+ component_updater::kSwReporterComponentId) !=
+ component_ids.end();
+}
+
+// TODO(proberge): Connect this to the Cleanup Tool manager class once that's
+// implemented.
+void StartScan(base::Closure callback) {
+ callback.Run();
+}
+
+// TODO(proberge): Connect this to the Cleanup Tool manager class once that's
+// implemented.
+void StartCleanup(base::Closure callback) {
+ callback.Run();
+}
+
+// TODO(proberge): Localize strings once they are finalized.
+constexpr char kDetectionOkText[] = "No problems detected";
+constexpr char kDetectionUwSText[] = "2 potentially harmful programs detected";
+constexpr char kDetectionTimeText[] = "Last scanned today";
+constexpr char kCleanupUnsupportedText[] = "Chrome Cleanup is not supported.";
+constexpr char kUnsupportedHelpText[] = "Please try reinstalling Chrome";
+
+} // namespace
+
+CleanupActionHandler::CleanupActionHandler()
+ : callback_weak_ptr_factory_(this) {}
+
+CleanupActionHandler::~CleanupActionHandler() {}
+
+void CleanupActionHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "requestLastScanResult",
+ base::Bind(&CleanupActionHandler::HandleRequestLastScanResult,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "startScan", base::Bind(&CleanupActionHandler::HandleStartScan,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "startCleanup", base::Bind(&CleanupActionHandler::HandleStartCleanup,
+ base::Unretained(this)));
+}
+
+void CleanupActionHandler::OnJavascriptDisallowed() {
+ callback_weak_ptr_factory_.InvalidateWeakPtrs();
+}
+
+void CleanupActionHandler::HandleRequestLastScanResult(
+ const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ std::string webui_callback_id;
+ bool success = args->GetString(0, &webui_callback_id);
+ DCHECK(success);
+
+ base::DictionaryValue last_scan_results;
+ // TODO(proberge): Return real information about the last run.
+ last_scan_results.SetBoolean("hasScanResults", false);
+ last_scan_results.SetBoolean("isInfected", false);
+ last_scan_results.SetString("detectionStatusText", kDetectionOkText);
+ last_scan_results.SetString("detectionTimeText", kDetectionTimeText);
+
+ AllowJavascript();
+ ResolveJavascriptCallback(base::Value(webui_callback_id), last_scan_results);
+}
+
+void CleanupActionHandler::HandleStartScan(const base::ListValue* args) {
+ std::string webui_callback_id;
+ CHECK_EQ(1U, args->GetSize());
+ bool success = args->GetString(0, &webui_callback_id);
+ DCHECK(success);
+
+ if (!IsCleanupComponentRegistered()) {
+ base::DictionaryValue scan_results;
+ scan_results.SetBoolean("hasScanResults", false);
+ scan_results.SetBoolean("isInfected", false);
+ scan_results.SetString("detectionStatusText", kCleanupUnsupportedText);
+ scan_results.SetString("detectionTimeText", kUnsupportedHelpText);
+
+ AllowJavascript();
+ ResolveJavascriptCallback(base::Value(webui_callback_id), scan_results);
+ return;
+ }
+
+ StartScan(base::Bind(&CleanupActionHandler::ReportScanResults,
+ callback_weak_ptr_factory_.GetWeakPtr(),
+ webui_callback_id));
+}
+
+void CleanupActionHandler::ReportScanResults(const std::string& callback_id) {
+ base::DictionaryValue scan_results;
+ // TODO(proberge): Return real information about the scan.
+ scan_results.SetBoolean("hasScanResults", true);
+ scan_results.SetBoolean("isInfected", true);
+ scan_results.SetString("detectionStatusText", kDetectionUwSText);
+ scan_results.SetString("detectionTimeText", kDetectionTimeText);
+
+ AllowJavascript();
+ ResolveJavascriptCallback(base::Value(callback_id), scan_results);
+}
+
+void CleanupActionHandler::HandleStartCleanup(const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ std::string webui_callback_id;
+ bool success = args->GetString(0, &webui_callback_id);
+ DCHECK(success);
+
+ StartCleanup(base::Bind(&CleanupActionHandler::ReportCleanupResults,
+ base::Unretained(this), webui_callback_id));
+}
+
+void CleanupActionHandler::ReportCleanupResults(
+ const std::string& callback_id) {
+ base::DictionaryValue cleanup_results;
+ // TODO(proberge): Return real information about the cleanup.
+ cleanup_results.SetBoolean("wasCancelled", true);
+ cleanup_results.SetBoolean("requiresReboot", false);
+
+ ResolveJavascriptCallback(base::Value(callback_id), cleanup_results);
+}
diff --git a/chromium/chrome/browser/ui/webui/cleanup_tool/cleanup_action_handler.h b/chromium/chrome/browser/ui/webui/cleanup_tool/cleanup_action_handler.h
new file mode 100644
index 00000000000..97c87f05dfa
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/cleanup_tool/cleanup_action_handler.h
@@ -0,0 +1,44 @@
+// 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_CLEANUP_TOOL_CLEANUP_ACTION_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CLEANUP_TOOL_CLEANUP_ACTION_HANDLER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+// The handler for Javascript messages related to the "Chrome Cleanup" view.
+class CleanupActionHandler : public content::WebUIMessageHandler {
+ public:
+ CleanupActionHandler();
+ ~CleanupActionHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ // Invalidates the weak pointers in callbacks that are no longer safe to run.
+ void OnJavascriptDisallowed() override;
+
+ void HandleRequestLastScanResult(const base::ListValue* args);
+ void HandleStartScan(const base::ListValue* args);
+ void HandleStartCleanup(const base::ListValue* args);
+
+ // Returns the scan result initiated by HandleStartScan() to the Javascript
+ // caller refererenced by |callback_id|.
+ void ReportScanResults(const std::string& callback_id);
+
+ // Returns the cleanup result initiated by HandleStartCleanup() to the
+ // Javascript caller refererenced by |callback_id|.
+ void ReportCleanupResults(const std::string& callback_id);
+
+ // Used to cancel callbacks when JavaScript becomes disallowed.
+ base::WeakPtrFactory<CleanupActionHandler> callback_weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CleanupActionHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_CLEANUP_TOOL_CLEANUP_ACTION_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/cleanup_tool/cleanup_tool_ui.cc b/chromium/chrome/browser/ui/webui/cleanup_tool/cleanup_tool_ui.cc
new file mode 100644
index 00000000000..c22710465df
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/cleanup_tool/cleanup_tool_ui.cc
@@ -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.
+
+#include "chrome/browser/ui/webui/cleanup_tool/cleanup_tool_ui.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/cleanup_tool/cleanup_action_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+CleanupToolUI::CleanupToolUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ content::WebUIDataSource* html_source =
+ content::WebUIDataSource::Create(chrome::kChromeUICleanupToolHost);
+
+ // TODO(proberge): Localize strings once they are finalized.
+ html_source->AddString("title", "Chrome Cleanup");
+ html_source->AddString("sectionHeader",
+ "Remove suspicious or unwanted programs");
+ html_source->AddString("scanAction", "Scan Now");
+ html_source->AddString("cleaning", "Cleaning");
+ html_source->AddString("scanning", "Scanning");
+ html_source->AddString("cleanAction", "Run Chrome Cleanup");
+ html_source->AddString("about", "About Chrome Cleanup");
+ html_source->SetJsonPath("strings.js");
+
+ html_source->AddResourcePath("cleanup_browser_proxy.html",
+ IDR_CLEANUP_TOOL_BROWSER_PROXY_HTML);
+ html_source->AddResourcePath("cleanup_browser_proxy.js",
+ IDR_CLEANUP_TOOL_BROWSER_PROXY_JS);
+ html_source->AddResourcePath("icons.html", IDR_CLEANUP_TOOL_ICONS_HTML);
+ html_source->AddResourcePath("manager.html", IDR_CLEANUP_TOOL_MANAGER_HTML);
+ html_source->AddResourcePath("manager.js", IDR_CLEANUP_TOOL_MANAGER_JS);
+ html_source->AddResourcePath("toolbar.html", IDR_CLEANUP_TOOL_TOOLBAR_HTML);
+ html_source->AddResourcePath("toolbar.js", IDR_CLEANUP_TOOL_TOOLBAR_JS);
+ html_source->SetDefaultResource(IDR_CLEANUP_TOOL_HTML);
+
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, html_source);
+
+ web_ui->AddMessageHandler(base::MakeUnique<CleanupActionHandler>());
+}
+
+CleanupToolUI::~CleanupToolUI() {}
diff --git a/chromium/chrome/browser/ui/webui/cleanup_tool/cleanup_tool_ui.h b/chromium/chrome/browser/ui/webui/cleanup_tool/cleanup_tool_ui.h
new file mode 100644
index 00000000000..c5d2871d209
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/cleanup_tool/cleanup_tool_ui.h
@@ -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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CLEANUP_TOOL_CLEANUP_TOOL_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CLEANUP_TOOL_CLEANUP_TOOL_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The UI for chrome://cleanup, which will allow Windows users to see the
+// status of the last Chrome Cleanup Tool scan and to manually launch the
+// cleanup process.
+class CleanupToolUI : public content::WebUIController {
+ public:
+ explicit CleanupToolUI(content::WebUI* web_ui);
+ ~CleanupToolUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CleanupToolUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_CLEANUP_TOOL_CLEANUP_TOOL_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/components_ui.cc b/chromium/chrome/browser/ui/webui/components_ui.cc
new file mode 100644
index 00000000000..cc6fe4e601b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/components_ui.cc
@@ -0,0 +1,263 @@
+// 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/components_ui.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/theme_resources.h"
+#include "components/update_client/crx_update_item.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/ui/webui/chromeos/ui_account_tweaks.h"
+#endif
+
+using content::WebUIMessageHandler;
+
+namespace {
+
+content::WebUIDataSource* CreateComponentsUIHTMLSource(Profile* profile) {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIComponentsHost);
+
+ source->AddLocalizedString("componentsTitle", IDS_COMPONENTS_TITLE);
+ source->AddLocalizedString("componentsNoneInstalled",
+ IDS_COMPONENTS_NONE_INSTALLED);
+ source->AddLocalizedString("componentVersion", IDS_COMPONENTS_VERSION);
+ source->AddLocalizedString("checkUpdate", IDS_COMPONENTS_CHECK_FOR_UPDATE);
+ source->AddLocalizedString("noComponents", IDS_COMPONENTS_NO_COMPONENTS);
+ source->AddLocalizedString("statusLabel", IDS_COMPONENTS_STATUS_LABEL);
+ source->AddLocalizedString("checkingLabel", IDS_COMPONENTS_CHECKING_LABEL);
+
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("components.js", IDR_COMPONENTS_JS);
+ source->SetDefaultResource(IDR_COMPONENTS_HTML);
+#if defined(OS_CHROMEOS)
+ chromeos::AddAccountUITweaksLocalizedValues(source, profile);
+#endif
+ return source;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ComponentsDOMHandler
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// The handler for Javascript messages for the chrome://components/ page.
+class ComponentsDOMHandler : public WebUIMessageHandler {
+ public:
+ ComponentsDOMHandler();
+ ~ComponentsDOMHandler() override {}
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // Callback for the "requestComponentsData" message.
+ void HandleRequestComponentsData(const base::ListValue* args);
+
+ // Callback for the "checkUpdate" message.
+ void HandleCheckUpdate(const base::ListValue* args);
+
+ private:
+ content::NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(ComponentsDOMHandler);
+};
+
+ComponentsDOMHandler::ComponentsDOMHandler() {
+}
+
+void ComponentsDOMHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "requestComponentsData",
+ base::Bind(&ComponentsDOMHandler::HandleRequestComponentsData,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "checkUpdate",
+ base::Bind(&ComponentsDOMHandler::HandleCheckUpdate,
+ base::Unretained(this)));
+}
+
+void ComponentsDOMHandler::HandleRequestComponentsData(
+ const base::ListValue* args) {
+ base::DictionaryValue result;
+ result.Set("components", ComponentsUI::LoadComponents());
+ web_ui()->CallJavascriptFunctionUnsafe("returnComponentsData", result);
+}
+
+// This function is called when user presses button from html UI.
+// TODO(shrikant): We need to make this button available based on current
+// state e.g. If component state is currently updating then we need to disable
+// button. (https://code.google.com/p/chromium/issues/detail?id=272540)
+void ComponentsDOMHandler::HandleCheckUpdate(const base::ListValue* args) {
+ if (args->GetSize() != 1) {
+ NOTREACHED();
+ return;
+ }
+
+ std::string component_id;
+ if (!args->GetString(0, &component_id)) {
+ NOTREACHED();
+ return;
+ }
+
+ ComponentsUI::OnDemandUpdate(component_id);
+}
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// ComponentsUI
+//
+///////////////////////////////////////////////////////////////////////////////
+
+ComponentsUI::ComponentsUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<ComponentsDOMHandler>());
+
+ // Set up the chrome://components/ source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreateComponentsUIHTMLSource(profile));
+ component_updater::ComponentUpdateService* cus =
+ g_browser_process->component_updater();
+ cus->AddObserver(this);
+}
+
+ComponentsUI::~ComponentsUI() {
+ component_updater::ComponentUpdateService* cus =
+ g_browser_process->component_updater();
+ if (cus)
+ cus->RemoveObserver(this);
+}
+
+// static
+void ComponentsUI::OnDemandUpdate(const std::string& component_id) {
+ component_updater::ComponentUpdateService* cus =
+ g_browser_process->component_updater();
+ cus->GetOnDemandUpdater().OnDemandUpdate(component_id,
+ component_updater::Callback());
+}
+
+// static
+std::unique_ptr<base::ListValue> ComponentsUI::LoadComponents() {
+ component_updater::ComponentUpdateService* cus =
+ g_browser_process->component_updater();
+ std::vector<std::string> component_ids;
+ component_ids = cus->GetComponentIDs();
+
+ // Construct DictionaryValues to return to UI.
+ auto component_list = base::MakeUnique<base::ListValue>();
+ for (size_t j = 0; j < component_ids.size(); ++j) {
+ update_client::CrxUpdateItem item;
+ if (cus->GetComponentDetails(component_ids[j], &item)) {
+ std::unique_ptr<base::DictionaryValue> component_entry(
+ new base::DictionaryValue());
+ component_entry->SetString("id", component_ids[j]);
+ component_entry->SetString("name", item.component.name);
+ component_entry->SetString("version", item.component.version.GetString());
+ component_entry->SetString("status", ServiceStatusToString(item.state));
+ component_list->Append(std::move(component_entry));
+ }
+ }
+
+ return component_list;
+}
+
+// static
+base::RefCountedMemory* ComponentsUI::GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor) {
+ return ResourceBundle::GetSharedInstance().
+ LoadDataResourceBytesForScale(IDR_PLUGINS_FAVICON, scale_factor);
+}
+
+base::string16 ComponentsUI::ComponentEventToString(Events event) {
+ switch (event) {
+ case Events::COMPONENT_CHECKING_FOR_UPDATES:
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_EVT_STATUS_STARTED);
+ case Events::COMPONENT_WAIT:
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_EVT_STATUS_SLEEPING);
+ case Events::COMPONENT_UPDATE_FOUND:
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_EVT_STATUS_FOUND);
+ case Events::COMPONENT_UPDATE_READY:
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_EVT_STATUS_READY);
+ case Events::COMPONENT_UPDATED:
+ 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_DOWNLOADING:
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_EVT_STATUS_DOWNLOADING);
+ }
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_UNKNOWN);
+}
+
+base::string16 ComponentsUI::ServiceStatusToString(
+ update_client::ComponentState state) {
+ // TODO(sorin): handle kDownloaded. For now, just handle it as kUpdating.
+ switch (state) {
+ case update_client::ComponentState::kNew:
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_SVC_STATUS_NEW);
+ case update_client::ComponentState::kChecking:
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_SVC_STATUS_CHECKING);
+ case update_client::ComponentState::kCanUpdate:
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_SVC_STATUS_UPDATE);
+ case update_client::ComponentState::kDownloadingDiff:
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_SVC_STATUS_DNL_DIFF);
+ case update_client::ComponentState::kDownloading:
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_SVC_STATUS_DNL);
+ case update_client::ComponentState::kUpdatingDiff:
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_SVC_STATUS_UPDT_DIFF);
+ case update_client::ComponentState::kUpdating:
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_SVC_STATUS_UPDATING);
+ case update_client::ComponentState::kDownloaded:
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_SVC_STATUS_DOWNLOADED);
+ case update_client::ComponentState::kUpdated:
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_SVC_STATUS_UPDATED);
+ 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);
+ case update_client::ComponentState::kUninstalled: // Fall through.
+ case update_client::ComponentState::kLastStatus:
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_UNKNOWN);
+ }
+ return l10n_util::GetStringUTF16(IDS_COMPONENTS_UNKNOWN);
+}
+
+void ComponentsUI::OnEvent(Events event, const std::string& id) {
+ base::DictionaryValue parameters;
+ parameters.SetString("event", ComponentEventToString(event));
+ if (!id.empty()) {
+ if (event == Events::COMPONENT_UPDATED) {
+ auto* component_updater = g_browser_process->component_updater();
+ update_client::CrxUpdateItem item;
+ if (component_updater->GetComponentDetails(id, &item))
+ parameters.SetString("version", item.component.version.GetString());
+ }
+ parameters.SetString("id", id);
+ }
+ web_ui()->CallJavascriptFunctionUnsafe("onComponentEvent", parameters);
+}
diff --git a/chromium/chrome/browser/ui/webui/components_ui.h b/chromium/chrome/browser/ui/webui/components_ui.h
new file mode 100644
index 00000000000..b391cd1b6da
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/components_ui.h
@@ -0,0 +1,45 @@
+// 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_COMPONENTS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_COMPONENTS_UI_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "components/component_updater/component_updater_service.h"
+#include "components/update_client/update_client.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "ui/base/layout.h"
+
+namespace base {
+class ListValue;
+class RefCountedMemory;
+}
+
+class ComponentsUI : public content::WebUIController,
+ public component_updater::ServiceObserver {
+ public:
+ explicit ComponentsUI(content::WebUI* web_ui);
+ ~ComponentsUI() override;
+
+ static void OnDemandUpdate(const std::string& component_id);
+
+ static std::unique_ptr<base::ListValue> LoadComponents();
+
+ static base::RefCountedMemory* GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor);
+
+ // ServiceObserver implementation.
+ void OnEvent(Events event, const std::string& id) override;
+
+ private:
+ static base::string16 ComponentEventToString(Events event);
+ static base::string16 ServiceStatusToString(
+ update_client::ComponentState state);
+ DISALLOW_COPY_AND_ASSIGN(ComponentsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_COMPONENTS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/conflicts_ui.cc b/chromium/chrome/browser/ui/webui/conflicts_ui.cc
new file mode 100644
index 00000000000..7ed3878e02f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/conflicts_ui.cc
@@ -0,0 +1,189 @@
+// 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/ui/webui/conflicts_ui.h"
+
+#if defined(OS_WIN)
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/metrics/user_metrics.h"
+#include "base/scoped_observer.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/win/enumerate_modules_model.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/theme_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/layout.h"
+#include "ui/base/resource/resource_bundle.h"
+
+using base::UserMetricsAction;
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace {
+
+content::WebUIDataSource* CreateConflictsUIHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIConflictsHost);
+
+ source->AddLocalizedString("loadingMessage", IDS_CONFLICTS_LOADING_MESSAGE);
+ source->AddLocalizedString("modulesLongTitle",
+ IDS_CONFLICTS_CHECK_PAGE_TITLE_LONG);
+ source->AddLocalizedString("modulesBlurb", IDS_CONFLICTS_EXPLANATION_TEXT);
+ source->AddLocalizedString("moduleSuspectedBad",
+ IDS_CONFLICTS_CHECK_WARNING_SUSPECTED);
+ source->AddLocalizedString("moduleConfirmedBad",
+ IDS_CONFLICTS_CHECK_WARNING_CONFIRMED);
+ source->AddLocalizedString("helpCenterLink", IDS_LEARN_MORE);
+ source->AddLocalizedString("investigatingText",
+ IDS_CONFLICTS_CHECK_INVESTIGATING);
+ source->AddLocalizedString("modulesNoneLoaded",
+ IDS_CONFLICTS_NO_MODULES_LOADED);
+ source->AddLocalizedString("headerSoftware", IDS_CONFLICTS_HEADER_SOFTWARE);
+ source->AddLocalizedString("headerSignedBy", IDS_CONFLICTS_HEADER_SIGNED_BY);
+ source->AddLocalizedString("headerLocation", IDS_CONFLICTS_HEADER_LOCATION);
+ source->AddLocalizedString("headerVersion", IDS_CONFLICTS_HEADER_VERSION);
+ source->AddLocalizedString("headerHelpTip", IDS_CONFLICTS_HEADER_HELP_TIP);
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("conflicts.js", IDR_ABOUT_CONFLICTS_JS);
+ source->SetDefaultResource(IDR_ABOUT_CONFLICTS_HTML);
+ return source;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ConflictsDOMHandler
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// The handler for JavaScript messages for the about:conflicts page.
+class ConflictsDOMHandler : public WebUIMessageHandler,
+ public EnumerateModulesModel::Observer {
+ public:
+ ConflictsDOMHandler();
+ ~ConflictsDOMHandler() override {}
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // Callback for the "requestModuleList" message.
+ void HandleRequestModuleList(const base::ListValue* args);
+
+ private:
+ void SendModuleList();
+
+ // EnumerateModulesModel::Observer implementation.
+ void OnScanCompleted() override;
+
+ ScopedObserver<EnumerateModulesModel,
+ EnumerateModulesModel::Observer> observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConflictsDOMHandler);
+};
+
+ConflictsDOMHandler::ConflictsDOMHandler()
+ : observer_(this) {
+}
+
+void ConflictsDOMHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("requestModuleList",
+ base::Bind(&ConflictsDOMHandler::HandleRequestModuleList,
+ base::Unretained(this)));
+}
+
+void ConflictsDOMHandler::HandleRequestModuleList(const base::ListValue* args) {
+ // The request is handled asynchronously, and will callback via
+ // OnScanCompleted on completion.
+ auto* model = EnumerateModulesModel::GetInstance();
+
+ // The JS shouldn't be abusive and call 'requestModuleList' twice, but it's
+ // easy enough to defend against this.
+ if (!observer_.IsObserving(model)) {
+ observer_.Add(model);
+
+ // Ask the scan to be performed immediately, and not in background mode.
+ // This ensures the results are available ASAP for the UI.
+ model->ScanNow(false);
+ }
+}
+
+void ConflictsDOMHandler::SendModuleList() {
+ auto* loaded_modules = EnumerateModulesModel::GetInstance();
+ base::ListValue* list = loaded_modules->GetModuleList();
+ base::DictionaryValue results;
+ results.Set("moduleList", list);
+
+ // Add the section title and the total count for bad modules found.
+ int confirmed_bad = loaded_modules->confirmed_bad_modules_detected();
+ int suspected_bad = loaded_modules->suspected_bad_modules_detected();
+ base::string16 table_title;
+ if (!confirmed_bad && !suspected_bad) {
+ table_title += l10n_util::GetStringFUTF16(
+ IDS_CONFLICTS_CHECK_PAGE_TABLE_TITLE_SUFFIX_ONE,
+ base::IntToString16(list->GetSize()));
+ } else {
+ table_title += l10n_util::GetStringFUTF16(
+ IDS_CONFLICTS_CHECK_PAGE_TABLE_TITLE_SUFFIX_TWO,
+ base::IntToString16(list->GetSize()),
+ base::IntToString16(confirmed_bad),
+ base::IntToString16(suspected_bad));
+ }
+ results.SetString("modulesTableTitle", table_title);
+
+ web_ui()->CallJavascriptFunctionUnsafe("returnModuleList", results);
+}
+
+void ConflictsDOMHandler::OnScanCompleted() {
+ SendModuleList();
+ observer_.Remove(EnumerateModulesModel::GetInstance());
+}
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// ConflictsUI
+//
+///////////////////////////////////////////////////////////////////////////////
+
+ConflictsUI::ConflictsUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ base::RecordAction(UserMetricsAction("ViewAboutConflicts"));
+ web_ui->AddMessageHandler(base::MakeUnique<ConflictsDOMHandler>());
+
+ // Set up the about:conflicts source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreateConflictsUIHTMLSource());
+}
+
+// static
+base::RefCountedMemory* ConflictsUI::GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor) {
+ return static_cast<base::RefCountedMemory*>(
+ ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
+ IDR_CONFLICT_FAVICON, scale_factor));
+}
+
+#endif
diff --git a/chromium/chrome/browser/ui/webui/conflicts_ui.h b/chromium/chrome/browser/ui/webui/conflicts_ui.h
new file mode 100644
index 00000000000..46735f1430d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/conflicts_ui.h
@@ -0,0 +1,33 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CONFLICTS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CONFLICTS_UI_H_
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "ui/base/layout.h"
+
+#if defined(OS_WIN)
+
+namespace base {
+class RefCountedMemory;
+}
+
+// The Web UI handler for about:conflicts.
+class ConflictsUI : public content::WebUIController {
+ public:
+ explicit ConflictsUI(content::WebUI* web_ui);
+
+ static base::RefCountedMemory* GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ConflictsUI);
+};
+
+#endif
+
+#endif // CHROME_BROWSER_UI_WEBUI_CONFLICTS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.cc b/chromium/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.cc
new file mode 100644
index 00000000000..c42de33bb41
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.cc
@@ -0,0 +1,133 @@
+// 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/ui/webui/constrained_web_dialog_delegate_base.h"
+
+#include <string>
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/renderer_preferences_util.h"
+#include "chrome/browser/ui/webui/chrome_web_contents_handler.h"
+#include "components/zoom/zoom_controller.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/renderer_preferences.h"
+#include "ipc/ipc_message.h"
+#include "ui/web_dialogs/web_dialog_delegate.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+using content::NativeWebKeyboardEvent;
+using content::WebContents;
+using ui::WebDialogDelegate;
+using ui::WebDialogWebContentsDelegate;
+
+ConstrainedWebDialogDelegateBase::ConstrainedWebDialogDelegateBase(
+ content::BrowserContext* browser_context,
+ WebDialogDelegate* delegate,
+ WebDialogWebContentsDelegate* tab_delegate)
+ : WebDialogWebContentsDelegate(browser_context,
+ new ChromeWebContentsHandler),
+ web_dialog_delegate_(delegate),
+ closed_via_webui_(false) {
+ CHECK(delegate);
+ web_contents_ =
+ WebContents::Create(WebContents::CreateParams(browser_context));
+ web_contents_holder_.reset(web_contents_);
+ WebContentsObserver::Observe(web_contents_);
+ zoom::ZoomController::CreateForWebContents(web_contents_);
+ if (tab_delegate) {
+ override_tab_delegate_.reset(tab_delegate);
+ web_contents_->SetDelegate(tab_delegate);
+ } else {
+ web_contents_->SetDelegate(this);
+ }
+ content::RendererPreferences* prefs =
+ web_contents_->GetMutableRendererPrefs();
+ renderer_preferences_util::UpdateFromSystemSettings(
+ prefs, Profile::FromBrowserContext(browser_context), web_contents_);
+
+ web_contents_->GetRenderViewHost()->SyncRendererPrefs();
+
+ // Set |this| as a delegate so the ConstrainedWebDialogUI can retrieve it.
+ ConstrainedWebDialogUI::SetConstrainedDelegate(web_contents_, this);
+
+ web_contents_->GetController().LoadURL(delegate->GetDialogContentURL(),
+ content::Referrer(),
+ ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
+ std::string());
+}
+
+ConstrainedWebDialogDelegateBase::~ConstrainedWebDialogDelegateBase() {
+ if (web_contents_) {
+ // Remove reference to |this| in the WebContent since it will becomes
+ // invalid and the lifetime of the WebContent may exceed the one of this
+ // object.
+ ConstrainedWebDialogUI::ClearConstrainedDelegate(web_contents_);
+ }
+}
+
+const WebDialogDelegate*
+ ConstrainedWebDialogDelegateBase::GetWebDialogDelegate() const {
+ return web_dialog_delegate_.get();
+}
+
+WebDialogDelegate*
+ ConstrainedWebDialogDelegateBase::GetWebDialogDelegate() {
+ return web_dialog_delegate_.get();
+}
+
+void ConstrainedWebDialogDelegateBase::OnDialogCloseFromWebUI() {
+ closed_via_webui_ = true;
+ CloseContents(web_contents_);
+}
+
+bool ConstrainedWebDialogDelegateBase::closed_via_webui() const {
+ return closed_via_webui_;
+}
+
+std::unique_ptr<content::WebContents>
+ConstrainedWebDialogDelegateBase::ReleaseWebContents() {
+ return std::move(web_contents_holder_);
+}
+
+gfx::NativeWindow ConstrainedWebDialogDelegateBase::GetNativeDialog() {
+ NOTREACHED();
+ return NULL;
+}
+
+WebContents* ConstrainedWebDialogDelegateBase::GetWebContents() {
+ return web_contents_;
+}
+
+void ConstrainedWebDialogDelegateBase::HandleKeyboardEvent(
+ content::WebContents* source,
+ const NativeWebKeyboardEvent& event) {
+}
+
+gfx::Size ConstrainedWebDialogDelegateBase::GetConstrainedWebDialogMinimumSize()
+ const {
+ NOTREACHED();
+ return gfx::Size();
+}
+
+gfx::Size ConstrainedWebDialogDelegateBase::GetConstrainedWebDialogMaximumSize()
+ const {
+ NOTREACHED();
+ return gfx::Size();
+}
+
+gfx::Size
+ConstrainedWebDialogDelegateBase::GetConstrainedWebDialogPreferredSize() const {
+ NOTREACHED();
+ return gfx::Size();
+}
+
+void ConstrainedWebDialogDelegateBase::WebContentsDestroyed() {
+ web_contents_ = nullptr;
+}
+
+void ConstrainedWebDialogDelegateBase::ResizeToGivenSize(
+ const gfx::Size size) {
+ NOTREACHED();
+}
diff --git a/chromium/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.h b/chromium/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.h
new file mode 100644
index 00000000000..56111eb2d2f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/constrained_web_dialog_delegate_base.h
@@ -0,0 +1,81 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CONSTRAINED_WEB_DIALOG_DELEGATE_BASE_H_
+#define CHROME_BROWSER_UI_WEBUI_CONSTRAINED_WEB_DIALOG_DELEGATE_BASE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+#include "ui/web_dialogs/web_dialog_web_contents_delegate.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace ui {
+class WebDialogDelegate;
+}
+
+// Platform-agnostic base implementation of ConstrainedWebDialogDelegate.
+class ConstrainedWebDialogDelegateBase
+ : public ConstrainedWebDialogDelegate,
+ public content::WebContentsObserver,
+ public ui::WebDialogWebContentsDelegate {
+ public:
+ // |browser_context| and |delegate| must outlive |this| instance, whereas
+ // |this| will take ownership of |tab_delegate|.
+ ConstrainedWebDialogDelegateBase(content::BrowserContext* browser_context,
+ ui::WebDialogDelegate* delegate,
+ WebDialogWebContentsDelegate* tab_delegate);
+ ~ConstrainedWebDialogDelegateBase() override;
+
+ bool closed_via_webui() const;
+
+ // ConstrainedWebDialogDelegate interface.
+ const ui::WebDialogDelegate* GetWebDialogDelegate() const override;
+ ui::WebDialogDelegate* GetWebDialogDelegate() override;
+ void OnDialogCloseFromWebUI() override;
+ std::unique_ptr<content::WebContents> ReleaseWebContents() override;
+ content::WebContents* GetWebContents() override;
+ gfx::NativeWindow GetNativeDialog() override;
+ gfx::Size GetConstrainedWebDialogMinimumSize() const override;
+ gfx::Size GetConstrainedWebDialogMaximumSize() const override;
+ gfx::Size GetConstrainedWebDialogPreferredSize() const override;
+
+ // WebContentsObserver interface
+ void WebContentsDestroyed() override;
+
+ // WebDialogWebContentsDelegate interface.
+ void HandleKeyboardEvent(
+ content::WebContents* source,
+ const content::NativeWebKeyboardEvent& event) override;
+
+ // Resize the dialog to the given size.
+ virtual void ResizeToGivenSize(const gfx::Size size);
+
+ private:
+ std::unique_ptr<ui::WebDialogDelegate> web_dialog_delegate_;
+
+ // Holds the HTML to display in the constrained dialog.
+ std::unique_ptr<content::WebContents> web_contents_holder_;
+
+ // Pointer to the WebContents in |web_contents_holder_| for the lifetime of
+ // that object, even if ReleaseWebContents() gets called. If the WebContents
+ // gets destroyed, |web_contents_| will be set to a nullptr.
+ content::WebContents* web_contents_;
+
+ // Was the dialog closed from WebUI (in which case |web_dialog_delegate_|'s
+ // OnDialogClosed() method has already been called)?
+ bool closed_via_webui_;
+
+ std::unique_ptr<WebDialogWebContentsDelegate> override_tab_delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConstrainedWebDialogDelegateBase);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_CONSTRAINED_WEB_DIALOG_DELEGATE_BASE_H_
diff --git a/chromium/chrome/browser/ui/webui/constrained_web_dialog_ui.cc b/chromium/chrome/browser/ui/webui/constrained_web_dialog_ui.cc
new file mode 100644
index 00000000000..1ace21b4d8a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/constrained_web_dialog_ui.cc
@@ -0,0 +1,125 @@
+// 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/ui/webui/constrained_web_dialog_ui.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "extensions/features/features.h"
+#include "ui/web_dialogs/web_dialog_delegate.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "chrome/browser/extensions/tab_helper.h"
+#endif
+
+using content::RenderFrameHost;
+using content::RenderViewHost;
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace {
+
+const char kConstrainedWebDialogDelegateUserDataKey[] =
+ "ConstrainedWebDialogDelegateUserData";
+
+class ConstrainedWebDialogDelegateUserData
+ : public base::SupportsUserData::Data {
+ public:
+ explicit ConstrainedWebDialogDelegateUserData(
+ ConstrainedWebDialogDelegate* delegate) : delegate_(delegate) {}
+ ~ConstrainedWebDialogDelegateUserData() override {}
+
+ ConstrainedWebDialogDelegate* delegate() { return delegate_; }
+
+ private:
+ ConstrainedWebDialogDelegate* delegate_; // unowned
+
+ DISALLOW_COPY_AND_ASSIGN(ConstrainedWebDialogDelegateUserData);
+};
+
+} // namespace
+
+ConstrainedWebDialogUI::ConstrainedWebDialogUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ extensions::TabHelper::CreateForWebContents(web_ui->GetWebContents());
+#endif
+}
+
+ConstrainedWebDialogUI::~ConstrainedWebDialogUI() {
+}
+
+void ConstrainedWebDialogUI::RenderFrameCreated(
+ RenderFrameHost* render_frame_host) {
+ // Add a "dialogClose" callback which matches WebDialogUI behavior.
+ web_ui()->RegisterMessageCallback("dialogClose",
+ base::Bind(&ConstrainedWebDialogUI::OnDialogCloseMessage,
+ base::Unretained(this)));
+
+ ConstrainedWebDialogDelegate* delegate = GetConstrainedDelegate();
+ if (!delegate)
+ return;
+
+ ui::WebDialogDelegate* dialog_delegate = delegate->GetWebDialogDelegate();
+ std::vector<WebUIMessageHandler*> handlers;
+ dialog_delegate->GetWebUIMessageHandlers(&handlers);
+ RenderViewHost* render_view_host = render_frame_host->GetRenderViewHost();
+ render_view_host->SetWebUIProperty("dialogArguments",
+ dialog_delegate->GetDialogArgs());
+ for (WebUIMessageHandler* handler : handlers) {
+ web_ui()->AddMessageHandler(base::WrapUnique(handler));
+ }
+
+ dialog_delegate->OnDialogShown(web_ui(), render_view_host);
+}
+
+void ConstrainedWebDialogUI::OnDialogCloseMessage(const base::ListValue* args) {
+ ConstrainedWebDialogDelegate* delegate = GetConstrainedDelegate();
+ if (!delegate)
+ return;
+
+ std::string json_retval;
+ if (!args->empty() && !args->GetString(0, &json_retval))
+ NOTREACHED() << "Could not read JSON argument";
+ delegate->GetWebDialogDelegate()->OnDialogClosed(json_retval);
+ delegate->OnDialogCloseFromWebUI();
+}
+
+// static
+void ConstrainedWebDialogUI::SetConstrainedDelegate(
+ content::WebContents* web_contents,
+ ConstrainedWebDialogDelegate* delegate) {
+ web_contents->SetUserData(
+ &kConstrainedWebDialogDelegateUserDataKey,
+ base::MakeUnique<ConstrainedWebDialogDelegateUserData>(delegate));
+}
+
+// static
+void ConstrainedWebDialogUI::ClearConstrainedDelegate(
+ content::WebContents* web_contents) {
+ web_contents->RemoveUserData(&kConstrainedWebDialogDelegateUserDataKey);
+}
+
+ConstrainedWebDialogDelegate* ConstrainedWebDialogUI::GetConstrainedDelegate() {
+ ConstrainedWebDialogDelegateUserData* user_data =
+ static_cast<ConstrainedWebDialogDelegateUserData*>(
+ web_ui()->GetWebContents()->
+ GetUserData(&kConstrainedWebDialogDelegateUserDataKey));
+
+ return user_data ? user_data->delegate() : NULL;
+}
diff --git a/chromium/chrome/browser/ui/webui/constrained_web_dialog_ui.h b/chromium/chrome/browser/ui/webui/constrained_web_dialog_ui.h
new file mode 100644
index 00000000000..4b31caf7acd
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/constrained_web_dialog_ui.h
@@ -0,0 +1,119 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CONSTRAINED_WEB_DIALOG_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CONSTRAINED_WEB_DIALOG_UI_H_
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace gfx {
+class Size;
+}
+
+namespace content {
+class BrowserContext;
+class WebContents;
+}
+
+namespace ui {
+class WebDialogDelegate;
+}
+
+class ConstrainedWebDialogDelegate {
+ public:
+ virtual const ui::WebDialogDelegate* GetWebDialogDelegate() const = 0;
+ virtual ui::WebDialogDelegate* GetWebDialogDelegate() = 0;
+
+ // Called when the dialog is being closed in response to a "dialogClose"
+ // message from WebUI.
+ virtual void OnDialogCloseFromWebUI() = 0;
+
+ // If called, the dialog will release the ownership of its WebContents.
+ // The dialog will continue to use it until it is destroyed.
+ virtual std::unique_ptr<content::WebContents> ReleaseWebContents() = 0;
+
+ // Returns the WebContents owned by the constrained window.
+ virtual content::WebContents* GetWebContents() = 0;
+
+ // Returns the native type used to display the dialog.
+ virtual gfx::NativeWindow GetNativeDialog() = 0;
+
+ // Returns the minimum size for the dialog.
+ virtual gfx::Size GetConstrainedWebDialogMinimumSize() const = 0;
+
+ // Returns the maximum size for the dialog.
+ virtual gfx::Size GetConstrainedWebDialogMaximumSize() const = 0;
+
+ // Returns the preferred size for the dialog, or an empty size if
+ // the dialog has been closed.
+ virtual gfx::Size GetConstrainedWebDialogPreferredSize() const = 0;
+
+ protected:
+ virtual ~ConstrainedWebDialogDelegate() {}
+};
+
+// ConstrainedWebDialogUI is a facility to show HTML WebUI content
+// in a tab-modal constrained dialog. It is implemented as an adapter
+// between an WebDialogUI object and a web contents modal dialog.
+//
+// Since the web contents modal dialog requires platform-specific delegate
+// implementations, this class is just a factory stub.
+class ConstrainedWebDialogUI : public content::WebUIController {
+ public:
+ explicit ConstrainedWebDialogUI(content::WebUI* web_ui);
+ ~ConstrainedWebDialogUI() override;
+
+ // WebUIController implementation:
+ void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
+
+ // Sets the delegate on the WebContents.
+ static void SetConstrainedDelegate(content::WebContents* web_contents,
+ ConstrainedWebDialogDelegate* delegate);
+ static void ClearConstrainedDelegate(content::WebContents* web_contents);
+
+ protected:
+ // Returns the ConstrainedWebDialogDelegate saved with the WebContents.
+ // Returns NULL if no such delegate is set.
+ ConstrainedWebDialogDelegate* GetConstrainedDelegate();
+
+ private:
+ // JS Message Handler
+ void OnDialogCloseMessage(const base::ListValue* args);
+
+ DISALLOW_COPY_AND_ASSIGN(ConstrainedWebDialogUI);
+};
+
+// Create and show a constrained HTML dialog. The actual object that gets
+// created is a ConstrainedWebDialogDelegate, which later triggers construction
+// of a ConstrainedWebDialogUI object.
+// |browser_context| is used to construct the constrained HTML dialog's
+// WebContents.
+// |delegate| controls the behavior of the dialog.
+// |overshadowed| is the tab being overshadowed by the dialog.
+ConstrainedWebDialogDelegate* ShowConstrainedWebDialog(
+ content::BrowserContext* browser_context,
+ ui::WebDialogDelegate* delegate,
+ content::WebContents* overshadowed);
+
+// Create and show a constrained HTML dialog with auto-resize enabled. The
+// dialog is shown automatically after document load has completed to avoid UI
+// jankiness.
+// |browser_context| is used to construct the dialog's WebContents.
+// |delegate| controls the behavior of the dialog.
+// |overshadowed| is the tab being overshadowed by the dialog.
+// |min_size| is the minimum size of the dialog.
+// |max_size| is the maximum size of the dialog.
+ConstrainedWebDialogDelegate* ShowConstrainedWebDialogWithAutoResize(
+ content::BrowserContext* browser_context,
+ ui::WebDialogDelegate* delegate,
+ content::WebContents* overshadowed,
+ const gfx::Size& min_size,
+ const gfx::Size& max_size);
+
+#endif // CHROME_BROWSER_UI_WEBUI_CONSTRAINED_WEB_DIALOG_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/constrained_web_dialog_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/constrained_web_dialog_ui_browsertest.cc
new file mode 100644
index 00000000000..de42e4eb978
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/constrained_web_dialog_ui_browsertest.cc
@@ -0,0 +1,278 @@
+// 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 "base/callback.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.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/constrained_web_dialog_ui.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "components/web_modal/web_contents_modal_dialog_manager.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "ui/web_dialogs/test/test_web_dialog_delegate.h"
+
+using content::WebContents;
+using ui::WebDialogDelegate;
+using web_modal::WebContentsModalDialogManager;
+
+namespace {
+
+static const char kTestDataURL[] = "data:text/html,<!doctype html>"
+ "<body></body>"
+ "<style>"
+ "body { height: 150px; width: 150px; }"
+ "</style>";
+
+bool IsEqualSizes(gfx::Size expected,
+ ConstrainedWebDialogDelegate* dialog_delegate) {
+ return expected == dialog_delegate->GetConstrainedWebDialogPreferredSize();
+}
+
+std::string GetChangeDimensionsScript(int dimension) {
+ return base::StringPrintf("window.document.body.style.width = %d + 'px';"
+ "window.document.body.style.height = %d + 'px';", dimension, dimension);
+}
+
+class ConstrainedWebDialogBrowserTestObserver
+ : public content::WebContentsObserver {
+ public:
+ explicit ConstrainedWebDialogBrowserTestObserver(WebContents* contents)
+ : content::WebContentsObserver(contents),
+ contents_destroyed_(false) {
+ }
+ ~ConstrainedWebDialogBrowserTestObserver() override {}
+
+ bool contents_destroyed() { return contents_destroyed_; }
+
+ private:
+ void WebContentsDestroyed() override { contents_destroyed_ = true; }
+
+ bool contents_destroyed_;
+};
+
+class AutoResizingTestWebDialogDelegate
+ : public ui::test::TestWebDialogDelegate {
+ public:
+ explicit AutoResizingTestWebDialogDelegate(const GURL& url)
+ : TestWebDialogDelegate(url) {}
+ ~AutoResizingTestWebDialogDelegate() override {}
+
+ // Dialog delegates for auto-resizing dialogs are expected not to set |size|.
+ void GetDialogSize(gfx::Size* size) const override {}
+};
+
+} // namespace
+
+class ConstrainedWebDialogBrowserTest : public InProcessBrowserTest {
+ public:
+ ConstrainedWebDialogBrowserTest() {}
+
+ // Runs the current MessageLoop until |condition| is true or timeout.
+ bool RunLoopUntil(const base::Callback<bool()>& condition) {
+ const base::TimeTicks start_time = base::TimeTicks::Now();
+ while (!condition.Run()) {
+ const base::TimeTicks current_time = base::TimeTicks::Now();
+ if (current_time - start_time > base::TimeDelta::FromSeconds(5)) {
+ ADD_FAILURE() << "Condition not met within five seconds.";
+ return false;
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(),
+ base::TimeDelta::FromMilliseconds(20));
+ content::RunMessageLoop();
+ }
+ return true;
+ }
+
+ protected:
+ bool IsShowingWebContentsModalDialog(WebContents* web_contents) const {
+ WebContentsModalDialogManager* web_contents_modal_dialog_manager =
+ WebContentsModalDialogManager::FromWebContents(web_contents);
+ return web_contents_modal_dialog_manager->IsDialogActive();
+ }
+};
+
+// Tests that opening/closing the constrained window won't crash it.
+IN_PROC_BROWSER_TEST_F(ConstrainedWebDialogBrowserTest, BasicTest) {
+ // The delegate deletes itself.
+ WebDialogDelegate* delegate = new ui::test::TestWebDialogDelegate(
+ GURL(chrome::kChromeUIConstrainedHTMLTestURL));
+ WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+
+ ConstrainedWebDialogDelegate* dialog_delegate =
+ ShowConstrainedWebDialog(browser()->profile(), delegate, web_contents);
+ ASSERT_TRUE(dialog_delegate);
+ EXPECT_TRUE(dialog_delegate->GetNativeDialog());
+ EXPECT_TRUE(IsShowingWebContentsModalDialog(web_contents));
+}
+
+// Tests that ReleaseWebContents() works.
+IN_PROC_BROWSER_TEST_F(ConstrainedWebDialogBrowserTest, ReleaseWebContents) {
+ // The delegate deletes itself.
+ WebDialogDelegate* delegate = new ui::test::TestWebDialogDelegate(
+ GURL(chrome::kChromeUIConstrainedHTMLTestURL));
+ WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+
+ ConstrainedWebDialogDelegate* dialog_delegate =
+ ShowConstrainedWebDialog(browser()->profile(), delegate, web_contents);
+ ASSERT_TRUE(dialog_delegate);
+ WebContents* dialog_contents = dialog_delegate->GetWebContents();
+ ASSERT_TRUE(dialog_contents);
+ ASSERT_TRUE(IsShowingWebContentsModalDialog(web_contents));
+
+ ConstrainedWebDialogBrowserTestObserver observer(dialog_contents);
+ std::unique_ptr<WebContents> dialog_contents_holder =
+ dialog_delegate->ReleaseWebContents();
+ dialog_delegate->OnDialogCloseFromWebUI();
+
+ ASSERT_FALSE(observer.contents_destroyed());
+ EXPECT_FALSE(IsShowingWebContentsModalDialog(web_contents));
+ dialog_contents_holder.reset();
+ EXPECT_TRUE(observer.contents_destroyed());
+}
+
+// Tests that dialog autoresizes based on web contents when autoresizing
+// is enabled.
+IN_PROC_BROWSER_TEST_F(ConstrainedWebDialogBrowserTest,
+ ContentResizeInAutoResizingDialog) {
+ // During auto-resizing, dialogs size to (WebContents size) + 16.
+ const int dialog_border_space = 16;
+
+ // Expected dialog sizes after auto-resizing.
+ const int initial_size = 150 + dialog_border_space;
+ const int new_size = 175 + dialog_border_space;
+
+ // The delegate deletes itself.
+ WebDialogDelegate* delegate =
+ new AutoResizingTestWebDialogDelegate(GURL(kTestDataURL));
+ WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+
+ // Observes the next created WebContents.
+ content::TestNavigationObserver observer(NULL);
+ observer.StartWatchingNewWebContents();
+
+ gfx::Size min_size = gfx::Size(100, 100);
+ gfx::Size max_size = gfx::Size(200, 200);
+ gfx::Size initial_dialog_size;
+
+ // OSX windows must be initially created with non-empty dimensions. The
+ // autoresizeable dialog's window dimensions are determined after initial
+ // creation.
+#if defined(OS_MACOSX)
+ initial_dialog_size = gfx::Size(1, 1);
+#endif
+ delegate->GetDialogSize(&initial_dialog_size);
+
+ ConstrainedWebDialogDelegate* dialog_delegate =
+ ShowConstrainedWebDialogWithAutoResize(browser()->profile(), delegate,
+ web_contents, min_size,
+ max_size);
+ ASSERT_TRUE(dialog_delegate);
+ EXPECT_TRUE(dialog_delegate->GetNativeDialog());
+ ASSERT_FALSE(IsShowingWebContentsModalDialog(web_contents));
+ EXPECT_EQ(min_size, dialog_delegate->GetConstrainedWebDialogMinimumSize());
+ EXPECT_EQ(max_size, dialog_delegate->GetConstrainedWebDialogMaximumSize());
+
+ // Check for initial sizing. Dialog was created as a 400x400 dialog.
+ ASSERT_EQ(initial_dialog_size,
+ dialog_delegate->GetConstrainedWebDialogPreferredSize());
+
+ observer.Wait();
+
+ // Wait until the entire WebContents has loaded.
+ WaitForLoadStop(dialog_delegate->GetWebContents());
+
+ ASSERT_TRUE(IsShowingWebContentsModalDialog(web_contents));
+
+ // Resize to content's originally set dimensions.
+ ASSERT_TRUE(RunLoopUntil(base::Bind(
+ &IsEqualSizes,
+ gfx::Size(initial_size, initial_size),
+ dialog_delegate)));
+
+ // Resize to dimensions within expected bounds.
+ EXPECT_TRUE(ExecuteScript(dialog_delegate->GetWebContents(),
+ GetChangeDimensionsScript(175)));
+ ASSERT_TRUE(RunLoopUntil(base::Bind(
+ &IsEqualSizes,
+ gfx::Size(new_size, new_size),
+ dialog_delegate)));
+
+ // Resize to dimensions smaller than the minimum bounds.
+ EXPECT_TRUE(ExecuteScript(dialog_delegate->GetWebContents(),
+ GetChangeDimensionsScript(50)));
+ ASSERT_TRUE(RunLoopUntil(base::Bind(
+ &IsEqualSizes,
+ min_size,
+ dialog_delegate)));
+
+ // Resize to dimensions greater than the maximum bounds.
+ EXPECT_TRUE(ExecuteScript(dialog_delegate->GetWebContents(),
+ GetChangeDimensionsScript(250)));
+ ASSERT_TRUE(RunLoopUntil(base::Bind(
+ &IsEqualSizes,
+ max_size,
+ dialog_delegate)));
+}
+
+// Tests that dialog does not autoresize when autoresizing is not enabled.
+IN_PROC_BROWSER_TEST_F(ConstrainedWebDialogBrowserTest,
+ ContentResizeInNonAutoResizingDialog) {
+ // The delegate deletes itself.
+ WebDialogDelegate* delegate =
+ new ui::test::TestWebDialogDelegate(GURL(kTestDataURL));
+ WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+
+ ConstrainedWebDialogDelegate* dialog_delegate =
+ ShowConstrainedWebDialog(browser()->profile(), delegate, web_contents);
+ ASSERT_TRUE(dialog_delegate);
+ EXPECT_TRUE(dialog_delegate->GetNativeDialog());
+ EXPECT_TRUE(IsShowingWebContentsModalDialog(web_contents));
+
+ // Wait until the entire WebContents has loaded.
+ WaitForLoadStop(dialog_delegate->GetWebContents());
+
+ gfx::Size initial_dialog_size;
+ delegate->GetDialogSize(&initial_dialog_size);
+
+ // Check for initial sizing. Dialog was created as a 400x400 dialog.
+ ASSERT_EQ(initial_dialog_size,
+ dialog_delegate->GetConstrainedWebDialogPreferredSize());
+
+ // Resize <body> to dimension smaller than dialog.
+ EXPECT_TRUE(ExecuteScript(dialog_delegate->GetWebContents(),
+ GetChangeDimensionsScript(100)));
+ ASSERT_TRUE(RunLoopUntil(base::Bind(
+ &IsEqualSizes,
+ initial_dialog_size,
+ dialog_delegate)));
+
+ // Resize <body> to dimension larger than dialog.
+ EXPECT_TRUE(ExecuteScript(dialog_delegate->GetWebContents(),
+ GetChangeDimensionsScript(500)));
+ ASSERT_TRUE(RunLoopUntil(base::Bind(
+ &IsEqualSizes,
+ initial_dialog_size,
+ dialog_delegate)));
+}
diff --git a/chromium/chrome/browser/ui/webui/cookies_tree_model_util.cc b/chromium/chrome/browser/ui/webui/cookies_tree_model_util.cc
new file mode 100644
index 00000000000..0d9e80aa052
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/cookies_tree_model_util.cc
@@ -0,0 +1,401 @@
+// 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/ui/webui/cookies_tree_model_util.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/i18n/time_formatting.h"
+#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browsing_data/cookies_tree_model.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/cache_storage_context.h"
+#include "content/public/browser/indexed_db_context.h"
+#include "content/public/browser/service_worker_context.h"
+#include "extensions/features/features.h"
+#include "net/cookies/canonical_cookie.h"
+#include "storage/common/fileapi/file_system_types.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/text/bytes_formatting.h"
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/common/extension_set.h"
+#endif
+
+namespace {
+
+const char kKeyId[] = "id";
+const char kKeyTitle[] = "title";
+const char kKeyType[] = "type";
+const char kKeyHasChildren[] = "hasChildren";
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+const char kKeyAppsProtectingThis[] = "appsProtectingThis";
+#endif
+const char kKeyName[] = "name";
+const char kKeyContent[] = "content";
+const char kKeyDomain[] = "domain";
+const char kKeyPath[] = "path";
+const char kKeySendFor[] = "sendfor";
+const char kKeyAccessibleToScript[] = "accessibleToScript";
+const char kKeyDesc[] = "desc";
+const char kKeySize[] = "size";
+const char kKeyOrigin[] = "origin";
+const char kKeyManifest[] = "manifest";
+const char kKeyServerId[] = "serverId";
+
+const char kKeyAccessed[] = "accessed";
+const char kKeyCreated[] = "created";
+const char kKeyExpires[] = "expires";
+const char kKeyModified[] = "modified";
+
+const char kKeyPersistent[] = "persistent";
+const char kKeyTemporary[] = "temporary";
+
+const char kKeyTotalUsage[] = "totalUsage";
+const char kKeyTemporaryUsage[] = "temporaryUsage";
+const char kKeyPersistentUsage[] = "persistentUsage";
+
+const char kKeyCertType[] = "certType";
+
+const char kKeyScopes[] = "scopes";
+
+const int64_t kNegligibleUsage = 1024; // 1KiB
+
+} // namespace
+
+CookiesTreeModelUtil::CookiesTreeModelUtil() {
+}
+
+CookiesTreeModelUtil::~CookiesTreeModelUtil() {
+}
+
+std::string CookiesTreeModelUtil::GetTreeNodeId(const CookieTreeNode* node) {
+ CookieTreeNodeMap::const_iterator iter = node_map_.find(node);
+ if (iter != node_map_.end())
+ return base::IntToString(iter->second);
+
+ int32_t new_id = id_map_.Add(node);
+ node_map_[node] = new_id;
+ return base::IntToString(new_id);
+}
+
+bool CookiesTreeModelUtil::GetCookieTreeNodeDictionary(
+ const CookieTreeNode& node,
+ bool include_quota_nodes,
+ base::DictionaryValue* dict) {
+ // Use node's address as an id for WebUI to look it up.
+ dict->SetString(kKeyId, GetTreeNodeId(&node));
+ dict->SetString(kKeyTitle, node.GetTitle());
+ dict->SetBoolean(kKeyHasChildren, !node.empty());
+
+ switch (node.GetDetailedInfo().node_type) {
+ case CookieTreeNode::DetailedInfo::TYPE_HOST: {
+ dict->SetString(kKeyType, "origin");
+ break;
+ }
+ case CookieTreeNode::DetailedInfo::TYPE_COOKIE: {
+ dict->SetString(kKeyType, "cookie");
+
+ const net::CanonicalCookie& cookie = *node.GetDetailedInfo().cookie;
+
+ dict->SetString(kKeyName, cookie.Name());
+ dict->SetString(kKeyContent, cookie.Value());
+ dict->SetString(kKeyDomain, cookie.Domain());
+ dict->SetString(kKeyPath, cookie.Path());
+ dict->SetString(kKeySendFor,
+ l10n_util::GetStringUTF16(
+ CookiesTreeModel::GetSendForMessageID(cookie)));
+ std::string accessible = cookie.IsHttpOnly() ?
+ l10n_util::GetStringUTF8(IDS_COOKIES_COOKIE_ACCESSIBLE_TO_SCRIPT_NO) :
+ l10n_util::GetStringUTF8(IDS_COOKIES_COOKIE_ACCESSIBLE_TO_SCRIPT_YES);
+ dict->SetString(kKeyAccessibleToScript, accessible);
+ dict->SetString(kKeyCreated, base::UTF16ToUTF8(
+ base::TimeFormatFriendlyDateAndTime(cookie.CreationDate())));
+ dict->SetString(kKeyExpires, cookie.IsPersistent() ? base::UTF16ToUTF8(
+ base::TimeFormatFriendlyDateAndTime(cookie.ExpiryDate())) :
+ l10n_util::GetStringUTF8(IDS_COOKIES_COOKIE_EXPIRES_SESSION));
+
+ break;
+ }
+ case CookieTreeNode::DetailedInfo::TYPE_DATABASE: {
+ dict->SetString(kKeyType, "database");
+
+ const BrowsingDataDatabaseHelper::DatabaseInfo& database_info =
+ *node.GetDetailedInfo().database_info;
+
+ dict->SetString(kKeyName, database_info.database_name.empty() ?
+ l10n_util::GetStringUTF8(IDS_COOKIES_WEB_DATABASE_UNNAMED_NAME) :
+ database_info.database_name);
+ dict->SetString(kKeyDesc, database_info.description);
+ dict->SetString(kKeySize, ui::FormatBytes(database_info.size));
+ dict->SetString(kKeyModified, base::UTF16ToUTF8(
+ base::TimeFormatFriendlyDateAndTime(database_info.last_modified)));
+
+ break;
+ }
+ case CookieTreeNode::DetailedInfo::TYPE_LOCAL_STORAGE: {
+ dict->SetString(kKeyType, "local_storage");
+
+ const BrowsingDataLocalStorageHelper::LocalStorageInfo&
+ local_storage_info = *node.GetDetailedInfo().local_storage_info;
+
+ dict->SetString(kKeyOrigin, local_storage_info.origin_url.spec());
+ dict->SetString(kKeySize, ui::FormatBytes(local_storage_info.size));
+ dict->SetString(kKeyModified, base::UTF16ToUTF8(
+ base::TimeFormatFriendlyDateAndTime(
+ local_storage_info.last_modified)));
+
+ break;
+ }
+ case CookieTreeNode::DetailedInfo::TYPE_APPCACHE: {
+ dict->SetString(kKeyType, "app_cache");
+
+ const content::AppCacheInfo& appcache_info =
+ *node.GetDetailedInfo().appcache_info;
+
+ dict->SetString(kKeyManifest, appcache_info.manifest_url.spec());
+ dict->SetString(kKeySize, ui::FormatBytes(appcache_info.size));
+ dict->SetString(kKeyCreated, base::UTF16ToUTF8(
+ base::TimeFormatFriendlyDateAndTime(appcache_info.creation_time)));
+ dict->SetString(kKeyAccessed, base::UTF16ToUTF8(
+ base::TimeFormatFriendlyDateAndTime(appcache_info.last_access_time)));
+
+ break;
+ }
+ case CookieTreeNode::DetailedInfo::TYPE_INDEXED_DB: {
+ dict->SetString(kKeyType, "indexed_db");
+
+ const content::IndexedDBInfo& indexed_db_info =
+ *node.GetDetailedInfo().indexed_db_info;
+
+ dict->SetString(kKeyOrigin, indexed_db_info.origin.spec());
+ dict->SetString(kKeySize, ui::FormatBytes(indexed_db_info.size));
+ dict->SetString(kKeyModified,
+ base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
+ indexed_db_info.last_modified)));
+ break;
+ }
+ case CookieTreeNode::DetailedInfo::TYPE_FILE_SYSTEM: {
+ dict->SetString(kKeyType, "file_system");
+
+ const BrowsingDataFileSystemHelper::FileSystemInfo& file_system_info =
+ *node.GetDetailedInfo().file_system_info;
+ const storage::FileSystemType kPerm = storage::kFileSystemTypePersistent;
+ const storage::FileSystemType kTemp = storage::kFileSystemTypeTemporary;
+
+ dict->SetString(kKeyOrigin, file_system_info.origin.spec());
+ dict->SetString(
+ kKeyPersistent,
+ base::ContainsKey(file_system_info.usage_map, kPerm)
+ ? base::UTF16ToUTF8(ui::FormatBytes(
+ file_system_info.usage_map.find(kPerm)->second))
+ : l10n_util::GetStringUTF8(IDS_COOKIES_FILE_SYSTEM_USAGE_NONE));
+ dict->SetString(
+ kKeyTemporary,
+ base::ContainsKey(file_system_info.usage_map, kTemp)
+ ? base::UTF16ToUTF8(ui::FormatBytes(
+ file_system_info.usage_map.find(kTemp)->second))
+ : l10n_util::GetStringUTF8(IDS_COOKIES_FILE_SYSTEM_USAGE_NONE));
+ break;
+ }
+ case CookieTreeNode::DetailedInfo::TYPE_QUOTA: {
+ if (!include_quota_nodes)
+ return false;
+
+ dict->SetString(kKeyType, "quota");
+
+ const BrowsingDataQuotaHelper::QuotaInfo& quota_info =
+ *node.GetDetailedInfo().quota_info;
+ if (quota_info.temporary_usage + quota_info.persistent_usage <=
+ kNegligibleUsage)
+ return false;
+
+ dict->SetString(kKeyOrigin, quota_info.host);
+ dict->SetString(kKeyTotalUsage,
+ base::UTF16ToUTF8(ui::FormatBytes(
+ quota_info.temporary_usage +
+ quota_info.persistent_usage)));
+ dict->SetString(kKeyTemporaryUsage,
+ base::UTF16ToUTF8(ui::FormatBytes(
+ quota_info.temporary_usage)));
+ dict->SetString(kKeyPersistentUsage,
+ base::UTF16ToUTF8(ui::FormatBytes(
+ quota_info.persistent_usage)));
+ break;
+ }
+ case CookieTreeNode::DetailedInfo::TYPE_CHANNEL_ID: {
+ dict->SetString(kKeyType, "channel_id");
+
+ const net::ChannelIDStore::ChannelID& channel_id =
+ *node.GetDetailedInfo().channel_id;
+
+ dict->SetString(kKeyServerId, channel_id.server_identifier());
+ dict->SetString(kKeyCertType,
+ l10n_util::GetStringUTF8(IDS_CLIENT_CERT_ECDSA_SIGN));
+ dict->SetString(kKeyCreated, base::UTF16ToUTF8(
+ base::TimeFormatFriendlyDateAndTime(
+ channel_id.creation_time())));
+ break;
+ }
+ case CookieTreeNode::DetailedInfo::TYPE_SERVICE_WORKER: {
+ dict->SetString(kKeyType, "service_worker");
+
+ const content::ServiceWorkerUsageInfo& service_worker_info =
+ *node.GetDetailedInfo().service_worker_info;
+
+ dict->SetString(kKeyOrigin, service_worker_info.origin.spec());
+ dict->SetString(kKeySize,
+ ui::FormatBytes(service_worker_info.total_size_bytes));
+ auto scopes = base::MakeUnique<base::ListValue>();
+ for (std::vector<GURL>::const_iterator it =
+ service_worker_info.scopes.begin();
+ it != service_worker_info.scopes.end();
+ ++it) {
+ scopes->AppendString(it->spec());
+ }
+ dict->Set(kKeyScopes, std::move(scopes));
+ break;
+ }
+ case CookieTreeNode::DetailedInfo::TYPE_CACHE_STORAGE: {
+ dict->SetString(kKeyType, "cache_storage");
+
+ const content::CacheStorageUsageInfo& cache_storage_info =
+ *node.GetDetailedInfo().cache_storage_info;
+
+ dict->SetString(kKeyOrigin, cache_storage_info.origin.spec());
+ dict->SetString(kKeySize,
+ ui::FormatBytes(cache_storage_info.total_size_bytes));
+ dict->SetString(kKeyModified,
+ base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
+ cache_storage_info.last_modified)));
+ break;
+ }
+ case CookieTreeNode::DetailedInfo::TYPE_FLASH_LSO: {
+ dict->SetString(kKeyType, "flash_lso");
+
+ dict->SetString(kKeyDomain, node.GetDetailedInfo().flash_lso_domain);
+ break;
+ }
+ case CookieTreeNode::DetailedInfo::TYPE_MEDIA_LICENSE: {
+ dict->SetString(kKeyType, "media_license");
+
+ const BrowsingDataMediaLicenseHelper::MediaLicenseInfo&
+ media_license_info = *node.GetDetailedInfo().media_license_info;
+ dict->SetString(kKeyOrigin, media_license_info.origin.spec());
+ dict->SetString(kKeySize, ui::FormatBytes(media_license_info.size));
+ dict->SetString(kKeyModified,
+ base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
+ media_license_info.last_modified_time)));
+ break;
+ }
+ default:
+ break;
+ }
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ const extensions::ExtensionSet* protecting_apps =
+ node.GetModel()->ExtensionsProtectingNode(node);
+ if (protecting_apps && !protecting_apps->is_empty()) {
+ auto app_infos = base::MakeUnique<base::ListValue>();
+ for (extensions::ExtensionSet::const_iterator it = protecting_apps->begin();
+ it != protecting_apps->end(); ++it) {
+ std::unique_ptr<base::DictionaryValue> app_info(
+ new base::DictionaryValue());
+ app_info->SetString(kKeyId, (*it)->id());
+ app_info->SetString(kKeyName, (*it)->name());
+ app_infos->Append(std::move(app_info));
+ }
+ dict->Set(kKeyAppsProtectingThis, std::move(app_infos));
+ }
+#endif
+
+ return true;
+}
+
+void CookiesTreeModelUtil::GetChildNodeDetails(const CookieTreeNode* parent,
+ int start,
+ int count,
+ bool include_quota_nodes,
+ base::ListValue* list) {
+ std::string id_path = GetTreeNodeId(parent);
+ for (int i = 0; i < count; ++i) {
+ const CookieTreeNode* child = parent->GetChild(start + i);
+ int cookie_count = child->child_count();
+ std::string cookie_id_path = id_path + "," + GetTreeNodeId(child) + ",";
+ for (int k = 0; k < cookie_count; ++k) {
+ const CookieTreeNode* details = child->GetChild(k);
+ std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+ if (GetCookieTreeNodeDictionary(*details, include_quota_nodes,
+ dict.get())) {
+ // TODO(dschuyler): This ID path is an artifact from using tree nodes to
+ // hold the cookies. Can this be changed to a dictionary with a key
+ // lookup (and remove use of id_map_)?
+ dict->SetString("idPath", cookie_id_path + GetTreeNodeId(details));
+ list->Append(std::move(dict));
+ }
+ }
+ }
+}
+
+void CookiesTreeModelUtil::GetChildNodeList(const CookieTreeNode* parent,
+ int start,
+ int count,
+ bool include_quota_nodes,
+ base::ListValue* nodes) {
+ for (int i = 0; i < count; ++i) {
+ std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+ const CookieTreeNode* child = parent->GetChild(start + i);
+ if (GetCookieTreeNodeDictionary(*child, include_quota_nodes, dict.get()))
+ nodes->Append(std::move(dict));
+ }
+}
+
+const CookieTreeNode* CookiesTreeModelUtil::GetTreeNodeFromPath(
+ const CookieTreeNode* root,
+ const std::string& path) {
+ const CookieTreeNode* child = NULL;
+ const CookieTreeNode* parent = root;
+ int child_index = -1;
+
+ // Validate the tree path and get the node pointer.
+ for (const base::StringPiece& cur_node : base::SplitStringPiece(
+ path, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
+ int32_t node_id = 0;
+ if (!base::StringToInt(cur_node, &node_id))
+ break;
+
+ child = id_map_.Lookup(node_id);
+ child_index = parent->GetIndexOf(child);
+ if (child_index == -1)
+ break;
+
+ parent = child;
+ }
+
+ return child_index >= 0 ? child : NULL;
+}
+
+const CookieTreeNode* CookiesTreeModelUtil::GetTreeNodeFromTitle(
+ const CookieTreeNode* root,
+ const base::string16& title) {
+ // TODO(dschuyler): This method reduces an old O(n^2) lookup with an O(n)
+ // lookup for O(1) space, but it could be further improved to O(1) lookup if
+ // desired (by trading O(n) space for the time improvement).
+ int site_count = root->child_count();
+ for (int i = 0; i < site_count; ++i) {
+ const CookieTreeNode* child = root->GetChild(i);
+ if (title == child->GetTitle())
+ return child;
+ }
+ return nullptr;
+}
diff --git a/chromium/chrome/browser/ui/webui/cookies_tree_model_util.h b/chromium/chrome/browser/ui/webui/cookies_tree_model_util.h
new file mode 100644
index 00000000000..7cc6b3bc96a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/cookies_tree_model_util.h
@@ -0,0 +1,77 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_COOKIES_TREE_MODEL_UTIL_H_
+#define CHROME_BROWSER_UI_WEBUI_COOKIES_TREE_MODEL_UTIL_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+
+#include "base/id_map.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+
+class CookieTreeNode;
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+class CookiesTreeModelUtil {
+ public:
+ CookiesTreeModelUtil();
+ ~CookiesTreeModelUtil();
+
+ // Finds or creates an ID for given |node| and returns it as string.
+ std::string GetTreeNodeId(const CookieTreeNode* node);
+
+ // Append the details of the child nodes of |parent| in specified range.
+ void GetChildNodeDetails(const CookieTreeNode* parent,
+ int start,
+ int count,
+ bool include_quota_nodes,
+ base::ListValue* list);
+
+ // Append the children nodes of |parent| in specified range to |nodes| list.
+ void GetChildNodeList(const CookieTreeNode* parent,
+ int start,
+ int count,
+ bool include_quota_nodes,
+ base::ListValue* nodes);
+
+ // Gets tree node from |path| under |root|. |path| is comma separated list of
+ // ids. |id_map| translates ids into object pointers. Return NULL if |path|
+ // is not valid.
+ const CookieTreeNode* GetTreeNodeFromPath(const CookieTreeNode* root,
+ const std::string& path);
+
+ // Gets tree node from |title| under |root|. |title| is a node title. Return
+ // NULL if |title| is not found.
+ const CookieTreeNode* GetTreeNodeFromTitle(const CookieTreeNode* root,
+ const base::string16& title);
+
+ private:
+ using CookiesTreeNodeIdMap = IDMap<const CookieTreeNode*>;
+ using CookieTreeNodeMap = std::map<const CookieTreeNode*, int32_t>;
+
+ // Populate given |dict| with cookie tree node properties. |id_map| maps
+ // a CookieTreeNode to an ID and creates a new ID if |node| is not in the
+ // maps. Returns false if the |node| does not need to be shown.
+ bool GetCookieTreeNodeDictionary(const CookieTreeNode& node,
+ bool include_quota_nodes,
+ base::DictionaryValue* dict);
+
+ // IDMap to create unique ID and look up the object for an ID.
+ CookiesTreeNodeIdMap id_map_;
+
+ // Reverse look up map to find the ID for a node.
+ CookieTreeNodeMap node_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(CookiesTreeModelUtil);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_COOKIES_TREE_MODEL_UTIL_H_
diff --git a/chromium/chrome/browser/ui/webui/crashes_ui.cc b/chromium/chrome/browser/ui/webui/crashes_ui.cc
new file mode 100644
index 00000000000..e2d64ce3337
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/crashes_ui.cc
@@ -0,0 +1,246 @@
+// 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/ui/webui/crashes_ui.h"
+
+#include <stddef.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/sys_info.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/crash_upload_list/crash_upload_list.h"
+#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
+#include "chrome/browser/metrics/metrics_reporting_state.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/crash/core/browser/crashes_ui_util.h"
+#include "components/grit/components_resources.h"
+#include "components/grit/components_scaled_resources.h"
+#include "components/strings/grit/components_chromium_strings.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/version_info/version_info.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "ui/base/resource/resource_bundle.h"
+
+#if defined(OS_CHROMEOS)
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/debug_daemon_client.h"
+#endif
+
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace {
+
+content::WebUIDataSource* CreateCrashesUIHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUICrashesHost);
+
+ for (size_t i = 0; i < crash::kCrashesUILocalizedStringsCount; ++i) {
+ source->AddLocalizedString(
+ crash::kCrashesUILocalizedStrings[i].name,
+ crash::kCrashesUILocalizedStrings[i].resource_id);
+ }
+
+ source->AddLocalizedString(crash::kCrashesUIShortProductName,
+ IDS_SHORT_PRODUCT_NAME);
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath(crash::kCrashesUICrashesJS, IDR_CRASH_CRASHES_JS);
+ source->SetDefaultResource(IDR_CRASH_CRASHES_HTML);
+ return source;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// CrashesDOMHandler
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// The handler for Javascript messages for the chrome://crashes/ page.
+class CrashesDOMHandler : public WebUIMessageHandler,
+ public CrashUploadList::Delegate {
+ public:
+ CrashesDOMHandler();
+ ~CrashesDOMHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // CrashUploadList::Delegate implemenation.
+ void OnUploadListAvailable() override;
+
+ private:
+ // Asynchronously fetches the list of crashes. Called from JS.
+ void HandleRequestCrashes(const base::ListValue* args);
+
+#if defined(OS_CHROMEOS)
+ // Asynchronously triggers crash uploading. Called from JS.
+ void HandleRequestUploads(const base::ListValue* args);
+#endif
+
+ // Sends the recent crashes list JS.
+ void UpdateUI();
+
+ // Asynchronously requests a user triggered upload. Called from JS.
+ void HandleRequestSingleCrashUpload(const base::ListValue* args);
+
+ scoped_refptr<CrashUploadList> upload_list_;
+ bool list_available_;
+ bool first_load_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashesDOMHandler);
+};
+
+CrashesDOMHandler::CrashesDOMHandler()
+ : list_available_(false), first_load_(true) {
+ upload_list_ = CreateCrashUploadList(this);
+}
+
+CrashesDOMHandler::~CrashesDOMHandler() {
+ upload_list_->ClearDelegate();
+}
+
+void CrashesDOMHandler::RegisterMessages() {
+ upload_list_->LoadUploadListAsynchronously();
+ web_ui()->RegisterMessageCallback(
+ crash::kCrashesUIRequestCrashList,
+ base::Bind(&CrashesDOMHandler::HandleRequestCrashes,
+ base::Unretained(this)));
+
+#if defined(OS_CHROMEOS)
+ web_ui()->RegisterMessageCallback(
+ crash::kCrashesUIRequestCrashUpload,
+ base::Bind(&CrashesDOMHandler::HandleRequestUploads,
+ base::Unretained(this)));
+#endif
+
+ web_ui()->RegisterMessageCallback(
+ crash::kCrashesUIRequestSingleCrashUpload,
+ base::Bind(&CrashesDOMHandler::HandleRequestSingleCrashUpload,
+ base::Unretained(this)));
+}
+
+void CrashesDOMHandler::HandleRequestCrashes(const base::ListValue* args) {
+ if (first_load_) {
+ first_load_ = false;
+ if (list_available_)
+ UpdateUI();
+ } else {
+ list_available_ = false;
+ upload_list_->LoadUploadListAsynchronously();
+ }
+}
+
+#if defined(OS_CHROMEOS)
+void CrashesDOMHandler::HandleRequestUploads(const base::ListValue* args) {
+ chromeos::DebugDaemonClient* debugd_client =
+ chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
+ DCHECK(debugd_client);
+
+ debugd_client->UploadCrashes();
+}
+#endif
+
+void CrashesDOMHandler::OnUploadListAvailable() {
+ list_available_ = true;
+ if (!first_load_)
+ UpdateUI();
+}
+
+void CrashesDOMHandler::UpdateUI() {
+ bool crash_reporting_enabled =
+ ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled();
+
+ bool system_crash_reporter = false;
+#if defined(OS_CHROMEOS)
+ // Chrome OS has a system crash reporter.
+ system_crash_reporter = true;
+#endif
+
+ bool upload_list = crash_reporting_enabled;
+ bool support_manual_uploads = false;
+
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_ANDROID)
+ // Maunal uploads currently are supported only for Crashpad-using platforms
+ // and Android, and only if crash uploads are not disabled by policy.
+ support_manual_uploads =
+ crash_reporting_enabled || !IsMetricsReportingPolicyManaged();
+
+ // Show crash reports regardless of |crash_reporting_enabled| so that users
+ // can manually upload those reports.
+ upload_list = true;
+#endif
+
+ base::ListValue crash_list;
+ if (upload_list)
+ crash::UploadListToValue(upload_list_.get(), &crash_list);
+
+ base::Value enabled(crash_reporting_enabled);
+ base::Value dynamic_backend(system_crash_reporter);
+ base::Value manual_uploads(support_manual_uploads);
+ base::Value version(version_info::GetVersionNumber());
+ base::Value os_string(base::SysInfo::OperatingSystemName() + " " +
+ base::SysInfo::OperatingSystemVersion());
+
+ std::vector<const base::Value*> args;
+ args.push_back(&enabled);
+ args.push_back(&dynamic_backend);
+ args.push_back(&manual_uploads);
+ args.push_back(&crash_list);
+ args.push_back(&version);
+ args.push_back(&os_string);
+ web_ui()->CallJavascriptFunctionUnsafe(crash::kCrashesUIUpdateCrashList,
+ args);
+}
+
+void CrashesDOMHandler::HandleRequestSingleCrashUpload(
+ const base::ListValue* args) {
+ DCHECK(args);
+
+ std::string local_id;
+ bool success = args->GetString(0, &local_id);
+ DCHECK(success);
+
+ // Only allow manual uploads if crash uploads aren’t disabled by policy.
+ if (!ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled() &&
+ IsMetricsReportingPolicyManaged()) {
+ return;
+ }
+ upload_list_->RequestSingleCrashUploadAsync(local_id);
+}
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// CrashesUI
+//
+///////////////////////////////////////////////////////////////////////////////
+
+CrashesUI::CrashesUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<CrashesDOMHandler>());
+
+ // Set up the chrome://crashes/ source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreateCrashesUIHTMLSource());
+}
+
+// static
+base::RefCountedMemory* CrashesUI::GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor) {
+ return ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
+ IDR_CRASH_SAD_FAVICON, scale_factor);
+}
diff --git a/chromium/chrome/browser/ui/webui/crashes_ui.h b/chromium/chrome/browser/ui/webui/crashes_ui.h
new file mode 100644
index 00000000000..7292218a27c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/crashes_ui.h
@@ -0,0 +1,27 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CRASHES_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CRASHES_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "ui/base/layout.h"
+
+namespace base {
+class RefCountedMemory;
+}
+
+class CrashesUI : public content::WebUIController {
+ public:
+ explicit CrashesUI(content::WebUI* web_ui);
+
+ static base::RefCountedMemory* GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CrashesUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_CRASHES_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/device_log_ui.cc b/chromium/chrome/browser/ui/webui/device_log_ui.cc
new file mode 100644
index 00000000000..68a260d1ab9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/device_log_ui.cc
@@ -0,0 +1,93 @@
+// 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/ui/webui/device_log_ui.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/device_event_log/device_event_log.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace chromeos {
+
+namespace {
+
+class DeviceLogMessageHandler : public content::WebUIMessageHandler {
+ public:
+ DeviceLogMessageHandler() {}
+ ~DeviceLogMessageHandler() override {}
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override {
+ web_ui()->RegisterMessageCallback(
+ "DeviceLog.getLog",
+ base::Bind(&DeviceLogMessageHandler::GetLog, base::Unretained(this)));
+ }
+
+ private:
+ void GetLog(const base::ListValue* value) const {
+ base::Value data(device_event_log::GetAsString(
+ device_event_log::NEWEST_FIRST, "json", "",
+ device_event_log::LOG_LEVEL_DEBUG, 0));
+ web_ui()->CallJavascriptFunctionUnsafe("DeviceLogUI.getLogCallback", data);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(DeviceLogMessageHandler);
+};
+
+} // namespace
+
+DeviceLogUI::DeviceLogUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<DeviceLogMessageHandler>());
+
+ content::WebUIDataSource* html =
+ content::WebUIDataSource::Create(chrome::kChromeUIDeviceLogHost);
+
+ html->AddLocalizedString("titleText", IDS_DEVICE_LOG_TITLE);
+ html->AddLocalizedString("autoRefreshText", IDS_DEVICE_AUTO_REFRESH);
+ html->AddLocalizedString("logRefreshText", IDS_DEVICE_LOG_REFRESH);
+
+ html->AddLocalizedString("logLevelShowText", IDS_DEVICE_LOG_LEVEL_SHOW);
+ html->AddLocalizedString("logLevelErrorText", IDS_DEVICE_LOG_LEVEL_ERROR);
+ html->AddLocalizedString("logLevelUserText", IDS_DEVICE_LOG_LEVEL_USER);
+ html->AddLocalizedString("logLevelEventText", IDS_DEVICE_LOG_LEVEL_EVENT);
+ html->AddLocalizedString("logLevelDebugText", IDS_DEVICE_LOG_LEVEL_DEBUG);
+ html->AddLocalizedString("logLevelFileinfoText", IDS_DEVICE_LOG_FILEINFO);
+ html->AddLocalizedString("logLevelTimeDetailText",
+ IDS_DEVICE_LOG_TIME_DETAIL);
+
+ html->AddLocalizedString("logTypeLoginText", IDS_DEVICE_LOG_TYPE_LOGIN);
+ html->AddLocalizedString("logTypeNetworkText", IDS_DEVICE_LOG_TYPE_NETWORK);
+ html->AddLocalizedString("logTypePowerText", IDS_DEVICE_LOG_TYPE_POWER);
+ html->AddLocalizedString("logTypeBluetoothText",
+ IDS_DEVICE_LOG_TYPE_BLUETOOTH);
+ html->AddLocalizedString("logTypeUsbText", IDS_DEVICE_LOG_TYPE_USB);
+ html->AddLocalizedString("logTypeHidText", IDS_DEVICE_LOG_TYPE_HID);
+
+ html->AddLocalizedString("logEntryFormat", IDS_DEVICE_LOG_ENTRY);
+ html->SetJsonPath("strings.js");
+ html->AddResourcePath("device_log_ui.css", IDR_DEVICE_LOG_UI_CSS);
+ html->AddResourcePath("device_log_ui.js", IDR_DEVICE_LOG_UI_JS);
+ html->SetDefaultResource(IDR_DEVICE_LOG_UI_HTML);
+
+ content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
+ html);
+}
+
+DeviceLogUI::~DeviceLogUI() {
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/device_log_ui.h b/chromium/chrome/browser/ui/webui/device_log_ui.h
new file mode 100644
index 00000000000..cee91ebb113
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/device_log_ui.h
@@ -0,0 +1,24 @@
+// 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_UI_WEBUI_DEVICE_LOG_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_DEVICE_LOG_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace chromeos {
+
+class DeviceLogUI : public content::WebUIController {
+ public:
+ explicit DeviceLogUI(content::WebUI* web_ui);
+ ~DeviceLogUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DeviceLogUI);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_DEVICE_LOG_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/devtools_ui.cc b/chromium/chrome/browser/ui/webui/devtools_ui.cc
new file mode 100644
index 00000000000..be09da9590a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/devtools_ui.cc
@@ -0,0 +1,364 @@
+// 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/ui/webui/devtools_ui.h"
+
+#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_thread.h"
+#include "content/public/browser/devtools_frontend_host.h"
+#include "content/public/browser/site_instance.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"
+#include "content/public/common/user_agent.h"
+#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 "storage/browser/fileapi/file_system_context.h"
+#include "third_party/WebKit/public/public_features.h"
+
+using content::BrowserThread;
+using content::WebContents;
+
+namespace {
+
+std::string PathWithoutParams(const std::string& path) {
+ return GURL(std::string("chrome-devtools://devtools/") + path)
+ .path().substr(1);
+}
+
+const char kHttpNotFound[] = "HTTP/1.1 404 Not Found\n\n";
+
+// DevToolsDataSource ---------------------------------------------------------
+
+std::string GetMimeTypeForPath(const std::string& path) {
+ std::string filename = PathWithoutParams(path);
+ if (base::EndsWith(filename, ".html", base::CompareCase::INSENSITIVE_ASCII)) {
+ return "text/html";
+ } else if (base::EndsWith(filename, ".css",
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ return "text/css";
+ } else if (base::EndsWith(filename, ".js",
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ return "application/javascript";
+ } else if (base::EndsWith(filename, ".png",
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ return "image/png";
+ } else if (base::EndsWith(filename, ".gif",
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ return "image/gif";
+ } else if (base::EndsWith(filename, ".svg",
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ return "image/svg+xml";
+ } else if (base::EndsWith(filename, ".manifest",
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ return "text/cache-manifest";
+ }
+ return "text/html";
+}
+
+// An URLDataSource implementation that handles chrome-devtools://devtools/
+// requests. Three types of requests could be handled based on the URL path:
+// 1. /bundled/: bundled DevTools frontend is served.
+// 2. /remote/: remote DevTools frontend is served from App Engine.
+class DevToolsDataSource : public content::URLDataSource,
+ public net::URLFetcherDelegate {
+ public:
+ using GotDataCallback = content::URLDataSource::GotDataCallback;
+
+ explicit DevToolsDataSource(net::URLRequestContextGetter* request_context);
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const GotDataCallback& callback) override;
+
+ private:
+ // 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;
+
+ // Serves bundled DevTools frontend from ResourceBundle.
+ void StartBundledDataRequest(const std::string& path,
+ const GotDataCallback& callback);
+
+ // Serves remote DevTools frontend from hard-coded App Engine domain.
+ void StartRemoteDataRequest(const std::string& path,
+ const GotDataCallback& callback);
+
+ // Serves remote DevTools frontend from any endpoint, passed through
+ // command-line flag.
+ void StartCustomDataRequest(const GURL& url,
+ const GotDataCallback& callback);
+
+ ~DevToolsDataSource() override;
+
+ scoped_refptr<net::URLRequestContextGetter> request_context_;
+
+ using PendingRequestsMap = std::map<const net::URLFetcher*, GotDataCallback>;
+ PendingRequestsMap pending_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsDataSource);
+};
+
+DevToolsDataSource::DevToolsDataSource(
+ net::URLRequestContextGetter* request_context)
+ : request_context_(request_context) {
+}
+
+DevToolsDataSource::~DevToolsDataSource() {
+ for (const auto& pair : pending_) {
+ delete pair.first;
+ pair.second.Run(
+ new base::RefCountedStaticMemory(kHttpNotFound, strlen(kHttpNotFound)));
+ }
+}
+
+std::string DevToolsDataSource::GetSource() const {
+ return chrome::kChromeUIDevToolsHost;
+}
+
+void DevToolsDataSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ // Serve request from local bundle.
+ std::string bundled_path_prefix(chrome::kChromeUIDevToolsBundledPath);
+ bundled_path_prefix += "/";
+ if (base::StartsWith(path, bundled_path_prefix,
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ StartBundledDataRequest(path.substr(bundled_path_prefix.length()),
+ callback);
+ return;
+ }
+
+ // Serve request from remote location.
+ std::string remote_path_prefix(chrome::kChromeUIDevToolsRemotePath);
+ remote_path_prefix += "/";
+ if (base::StartsWith(path, remote_path_prefix,
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ StartRemoteDataRequest(path.substr(remote_path_prefix.length()),
+ callback);
+ return;
+ }
+
+ std::string custom_frontend_url =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kCustomDevtoolsFrontend);
+
+ if (custom_frontend_url.empty()) {
+ callback.Run(NULL);
+ return;
+ }
+
+ // Serve request from custom location.
+ std::string custom_path_prefix(chrome::kChromeUIDevToolsCustomPath);
+ custom_path_prefix += "/";
+
+ if (base::StartsWith(path, custom_path_prefix,
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ GURL url = GURL(custom_frontend_url +
+ path.substr(custom_path_prefix.length()));
+ StartCustomDataRequest(url, callback);
+ return;
+ }
+
+ callback.Run(NULL);
+}
+
+std::string DevToolsDataSource::GetMimeType(const std::string& path) const {
+ return GetMimeTypeForPath(path);
+}
+
+bool DevToolsDataSource::ShouldAddContentSecurityPolicy() const {
+ return false;
+}
+
+bool DevToolsDataSource::ShouldDenyXFrameOptions() const {
+ return false;
+}
+
+bool DevToolsDataSource::ShouldServeMimeTypeAsContentTypeHeader() const {
+ return true;
+}
+
+void DevToolsDataSource::StartBundledDataRequest(
+ const std::string& path,
+ const content::URLDataSource::GotDataCallback& callback) {
+ std::string filename = PathWithoutParams(path);
+ base::StringPiece resource =
+ content::DevToolsFrontendHost::GetFrontendResource(filename);
+
+ DLOG_IF(WARNING, resource.empty())
+ << "Unable to find dev tool resource: " << filename
+ << ". If you compiled with debug_devtools=1, try running with "
+ "--debug-devtools.";
+ scoped_refptr<base::RefCountedStaticMemory> bytes(
+ new base::RefCountedStaticMemory(resource.data(), resource.length()));
+ callback.Run(bytes.get());
+}
+
+void DevToolsDataSource::StartRemoteDataRequest(
+ const std::string& path,
+ const content::URLDataSource::GotDataCallback& callback) {
+ GURL url = GURL(kRemoteFrontendBase + path);
+ CHECK_EQ(url.host(), kRemoteFrontendDomain);
+ if (!url.is_valid()) {
+ callback.Run(
+ new base::RefCountedStaticMemory(kHttpNotFound, strlen(kHttpNotFound)));
+ return;
+ }
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("devtools_hard_coded_data_source", R"(
+ semantics {
+ sender: "Developer Tools Remote Data Request From Google"
+ description:
+ "This service fetches Chromium DevTools front-end files from the "
+ "cloud for the remote debugging scenario."
+ trigger:
+ "When user attaches to mobile phone for debugging."
+ data: "None"
+ destination: GOOGLE_OWNED_SERVICE
+ }
+ policy {
+ cookies_allowed: true
+ cookies_store: "user"
+ setting: "This feature cannot be disabled by settings."
+ chrome_policy {
+ DeveloperToolsDisabled {
+ policy_options {mode: MANDATORY}
+ DeveloperToolsDisabled: true
+ }
+ }
+ })");
+ net::URLFetcher* fetcher = net::URLFetcher::Create(url, net::URLFetcher::GET,
+ this, traffic_annotation)
+ .release();
+ pending_[fetcher] = callback;
+ fetcher->SetRequestContext(request_context_.get());
+ fetcher->Start();
+}
+
+void DevToolsDataSource::StartCustomDataRequest(
+ const GURL& url,
+ const content::URLDataSource::GotDataCallback& callback) {
+ if (!url.is_valid()) {
+ callback.Run(
+ new base::RefCountedStaticMemory(kHttpNotFound, strlen(kHttpNotFound)));
+ return;
+ }
+ net::NetworkTrafficAnnotationTag traffic_annotation =
+ net::DefineNetworkTrafficAnnotation("devtools_free_data_source", R"(
+ semantics {
+ sender: "Developer Tools Remote Data Request"
+ description:
+ "This service fetches Chromium DevTools front-end files from the "
+ "cloud for the remote debugging scenario. This can only happen if "
+ "a URL was passed on the commandline via flag "
+ "'--custom-devtools-frontend'. This URL overrides the default "
+ "fetching from a Google website, see "
+ "devtools_hard_coded_data_source."
+ trigger:
+ "When command line flag --custom-devtools-frontend is specified "
+ "and DevTools is opened."
+ data: "None"
+ destination: WEBSITE
+ }
+ policy {
+ cookies_allowed: true
+ cookies_store: "user"
+ setting: "This feature cannot be disabled by settings."
+ chrome_policy {
+ DeveloperToolsDisabled {
+ policy_options {mode: MANDATORY}
+ DeveloperToolsDisabled: true
+ }
+ }
+ })");
+ 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();
+}
+
+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);
+}
+
+} // namespace
+
+// DevToolsUI -----------------------------------------------------------------
+
+// static
+GURL DevToolsUI::GetProxyURL(const std::string& frontend_url) {
+ GURL url(frontend_url);
+ if (url.scheme() == content::kChromeDevToolsScheme &&
+ url.host() == chrome::kChromeUIDevToolsHost)
+ return GURL();
+ if (!url.is_valid() || url.host() != kRemoteFrontendDomain)
+ return GURL();
+ return GURL(base::StringPrintf("%s://%s/%s/%s",
+ content::kChromeDevToolsScheme,
+ chrome::kChromeUIDevToolsHost,
+ chrome::kChromeUIDevToolsRemotePath,
+ url.path().substr(1).c_str()));
+}
+
+// static
+GURL DevToolsUI::GetRemoteBaseURL() {
+ return GURL(base::StringPrintf(
+ "%s%s/%s/",
+ kRemoteFrontendBase,
+ kRemoteFrontendPath,
+ content::GetWebKitRevision().c_str()));
+}
+
+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()));
+
+ if (!profile->IsOffTheRecord())
+ return;
+ GURL url = web_ui->GetWebContents()->GetVisibleURL();
+ GURL site = content::SiteInstance::GetSiteForURL(profile, url);
+ content::BrowserContext::GetStoragePartitionForSite(profile, site)->
+ GetFileSystemContext()->EnableTemporaryFileSystemInIncognito();
+}
+
+DevToolsUI::~DevToolsUI() {
+}
diff --git a/chromium/chrome/browser/ui/webui/devtools_ui.h b/chromium/chrome/browser/ui/webui/devtools_ui.h
new file mode 100644
index 00000000000..809e4e4f1a2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/devtools_ui.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2011 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_DEVTOOLS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_DEVTOOLS_UI_H_
+
+#include "base/macros.h"
+#include "chrome/browser/devtools/devtools_ui_bindings.h"
+#include "content/public/browser/web_ui_controller.h"
+
+class DevToolsUI : public content::WebUIController {
+ public:
+ static GURL GetProxyURL(const std::string& frontend_url);
+ static GURL GetRemoteBaseURL();
+
+ explicit DevToolsUI(content::WebUI* web_ui);
+ ~DevToolsUI() override;
+
+ private:
+ DevToolsUIBindings bindings_;
+ DISALLOW_COPY_AND_ASSIGN(DevToolsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_DEVTOOLS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/domain_reliability_internals_ui.cc b/chromium/chrome/browser/ui/webui/domain_reliability_internals_ui.cc
new file mode 100644
index 00000000000..7a2aebd49be
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/domain_reliability_internals_ui.cc
@@ -0,0 +1,66 @@
+// 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/ui/webui/domain_reliability_internals_ui.h"
+
+#include "chrome/browser/domain_reliability/service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "components/domain_reliability/service.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+using domain_reliability::DomainReliabilityService;
+using domain_reliability::DomainReliabilityServiceFactory;
+
+DomainReliabilityInternalsUI::DomainReliabilityInternalsUI(
+ content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ content::WebUIDataSource* html_source = content::WebUIDataSource::Create(
+ chrome::kChromeUIDomainReliabilityInternalsHost);
+ html_source->SetJsonPath("strings.js");
+ html_source->AddResourcePath("domain_reliability_internals.css",
+ IDR_DOMAIN_RELIABILITY_INTERNALS_CSS);
+ html_source->AddResourcePath("domain_reliability_internals.js",
+ IDR_DOMAIN_RELIABILITY_INTERNALS_JS);
+ html_source->SetDefaultResource(IDR_DOMAIN_RELIABILITY_INTERNALS_HTML);
+ html_source->UseGzip(std::unordered_set<std::string>());
+
+ web_ui->RegisterMessageCallback("updateData",
+ base::Bind(&DomainReliabilityInternalsUI::UpdateData,
+ base::Unretained(this)));
+
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, html_source);
+}
+
+DomainReliabilityInternalsUI::~DomainReliabilityInternalsUI() {}
+
+void DomainReliabilityInternalsUI::UpdateData(
+ const base::ListValue* args) const {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ DomainReliabilityServiceFactory* factory =
+ DomainReliabilityServiceFactory::GetInstance();
+ DCHECK(profile);
+ DCHECK(factory);
+
+ DomainReliabilityService* service = factory->GetForBrowserContext(profile);
+ if (!service) {
+ base::DictionaryValue* data = new base::DictionaryValue();
+ data->SetString("error", "no_service");
+ OnDataUpdated(std::unique_ptr<base::Value>(data));
+ return;
+ }
+
+ service->GetWebUIData(base::Bind(
+ &DomainReliabilityInternalsUI::OnDataUpdated,
+ base::Unretained(this)));
+}
+
+void DomainReliabilityInternalsUI::OnDataUpdated(
+ std::unique_ptr<base::Value> data) const {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "DomainReliabilityInternals.onDataUpdated", *data);
+}
diff --git a/chromium/chrome/browser/ui/webui/domain_reliability_internals_ui.h b/chromium/chrome/browser/ui/webui/domain_reliability_internals_ui.h
new file mode 100644
index 00000000000..eb29eb65165
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/domain_reliability_internals_ui.h
@@ -0,0 +1,31 @@
+// 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_UI_WEBUI_DOMAIN_RELIABILITY_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_DOMAIN_RELIABILITY_INTERNALS_UI_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace base {
+class ListValue;
+class Value;
+} // namespace base
+
+// The WebUI for chrome://domain-reliability-internals
+class DomainReliabilityInternalsUI : public content::WebUIController {
+ public:
+ explicit DomainReliabilityInternalsUI(content::WebUI* web_ui);
+ ~DomainReliabilityInternalsUI() override;
+
+ private:
+ void UpdateData(const base::ListValue* args) const;
+ void OnDataUpdated(std::unique_ptr<base::Value> data) const;
+
+ DISALLOW_COPY_AND_ASSIGN(DomainReliabilityInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_DOMAIN_RELIABILITY_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/engagement/site_engagement_ui.cc b/chromium/chrome/browser/ui/webui/engagement/site_engagement_ui.cc
new file mode 100644
index 00000000000..28d701104f0
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/engagement/site_engagement_ui.cc
@@ -0,0 +1,100 @@
+// 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.
+
+#include "chrome/browser/ui/webui/engagement/site_engagement_ui.h"
+
+#include <cmath>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "chrome/browser/engagement/site_engagement_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace {
+
+// Implementation of mojom::SiteEngagementDetailsProvider that gets information
+// from the SiteEngagementService to provide data for the WebUI.
+class SiteEngagementDetailsProviderImpl
+ : public mojom::SiteEngagementDetailsProvider {
+ public:
+ // Instance is deleted when the supplied pipe is destroyed.
+ SiteEngagementDetailsProviderImpl(
+ Profile* profile,
+ mojo::InterfaceRequest<mojom::SiteEngagementDetailsProvider> request)
+ : profile_(profile), binding_(this, std::move(request)) {
+ DCHECK(profile_);
+ }
+
+ ~SiteEngagementDetailsProviderImpl() override {}
+
+ // mojom::SiteEngagementDetailsProvider overrides:
+ void GetSiteEngagementDetails(
+ const GetSiteEngagementDetailsCallback& callback) override {
+ SiteEngagementService* service = SiteEngagementService::Get(profile_);
+ std::vector<mojom::SiteEngagementDetails> scores = service->GetAllDetails();
+
+ std::vector<mojom::SiteEngagementDetailsPtr> engagement_info;
+ engagement_info.reserve(scores.size());
+ for (const auto& info : scores) {
+ mojom::SiteEngagementDetailsPtr origin_info(
+ mojom::SiteEngagementDetails::New());
+ *origin_info = std::move(info);
+ engagement_info.push_back(std::move(origin_info));
+ }
+
+ callback.Run(std::move(engagement_info));
+ }
+
+ void SetSiteEngagementBaseScoreForUrl(const GURL& origin,
+ double score) override {
+ if (!origin.is_valid() || score < 0 ||
+ score > SiteEngagementService::GetMaxPoints() || std::isnan(score)) {
+ return;
+ }
+
+ SiteEngagementService* service = SiteEngagementService::Get(profile_);
+ service->ResetBaseScoreForURL(origin, score);
+ }
+
+ private:
+ // The Profile* handed to us in our constructor.
+ Profile* profile_;
+
+ mojo::Binding<mojom::SiteEngagementDetailsProvider> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(SiteEngagementDetailsProviderImpl);
+};
+
+} // namespace
+
+SiteEngagementUI::SiteEngagementUI(content::WebUI* web_ui)
+ : MojoWebUIController<mojom::SiteEngagementDetailsProvider>(web_ui) {
+ // Set up the chrome://site-engagement/ source.
+ std::unique_ptr<content::WebUIDataSource> source(
+ content::WebUIDataSource::Create(chrome::kChromeUISiteEngagementHost));
+ source->AddResourcePath("site_engagement.js", IDR_SITE_ENGAGEMENT_JS);
+ source->AddResourcePath(
+ "chrome/browser/engagement/site_engagement_details.mojom",
+ IDR_SITE_ENGAGEMENT_MOJO_JS);
+ source->AddResourcePath("url/mojo/url.mojom", IDR_URL_MOJO_JS);
+ source->SetDefaultResource(IDR_SITE_ENGAGEMENT_HTML);
+ source->UseGzip(std::unordered_set<std::string>());
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), source.release());
+}
+
+SiteEngagementUI::~SiteEngagementUI() {}
+
+void SiteEngagementUI::BindUIHandler(
+ const service_manager::BindSourceInfo& source_info,
+ mojom::SiteEngagementDetailsProviderRequest request) {
+ ui_handler_.reset(new SiteEngagementDetailsProviderImpl(
+ Profile::FromWebUI(web_ui()), std::move(request)));
+}
diff --git a/chromium/chrome/browser/ui/webui/engagement/site_engagement_ui.h b/chromium/chrome/browser/ui/webui/engagement/site_engagement_ui.h
new file mode 100644
index 00000000000..d7a0c0faab6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/engagement/site_engagement_ui.h
@@ -0,0 +1,30 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ENGAGEMENT_SITE_ENGAGEMENT_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_ENGAGEMENT_SITE_ENGAGEMENT_UI_H_
+
+#include "base/macros.h"
+#include "chrome/browser/engagement/site_engagement_details.mojom.h"
+#include "chrome/browser/ui/webui/mojo_web_ui_controller.h"
+
+// The UI for chrome://site-engagement/.
+class SiteEngagementUI
+ : public MojoWebUIController<mojom::SiteEngagementDetailsProvider> {
+ public:
+ explicit SiteEngagementUI(content::WebUI* web_ui);
+ ~SiteEngagementUI() override;
+
+ private:
+ // MojoWebUIController overrides:
+ void BindUIHandler(
+ const service_manager::BindSourceInfo& source_info,
+ mojom::SiteEngagementDetailsProviderRequest request) override;
+
+ std::unique_ptr<mojom::SiteEngagementDetailsProvider> ui_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(SiteEngagementUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_ENGAGEMENT_SITE_ENGAGEMENT_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/extensions/OWNERS b/chromium/chrome/browser/ui/webui/extensions/OWNERS
new file mode 100644
index 00000000000..9f96570c5ab
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/OWNERS
@@ -0,0 +1,5 @@
+finnur@chromium.org
+rdevlin.cronin@chromium.org
+
+# TEAM: extensions-dev@chromium.org
+# COMPONENT: Platform>Extensions
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
new file mode 100644
index 00000000000..5a7b9f29924
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_browsertest.js
@@ -0,0 +1,265 @@
+// 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.
+
+GEN('#include "base/command_line.h"');
+
+/**
+ * TestFixture for kiosk app settings WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function KioskAppSettingsWebUITest() {}
+
+KioskAppSettingsWebUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /**
+ * Browse to the kiosk app settings page.
+ */
+ browsePreload: 'chrome://extensions-frame/',
+
+ /** @override */
+ commandLineSwitches: [{
+ switchName: 'enable-consumer-kiosk',
+ }],
+
+ /**
+ * Mock settings data.
+ * @private
+ */
+ settings_: {
+ apps: [
+ {
+ id: 'app_1',
+ name: 'App1 Name',
+ iconURL: '',
+ autoLaunch: false,
+ isLoading: false,
+ },
+ {
+ id: 'app_2',
+ name: '', // no name
+ iconURL: '',
+ autoLaunch: false,
+ isLoading: true,
+ },
+ ],
+ disableBailout: false,
+ hasAutoLaunchApp: false
+ },
+
+ /**
+ * Register a mock dictionary handler.
+ */
+ preLoad: function() {
+ this.makeAndRegisterMockHandler(
+ ['getKioskAppSettings',
+ 'addKioskApp',
+ 'removeKioskApp',
+ 'enableKioskAutoLaunch',
+ 'disableKioskAutoLaunch'
+ ]);
+ this.mockHandler.stubs().getKioskAppSettings().
+ will(callFunction(function() {
+ extensions.KioskAppsOverlay.setSettings(this.settings_);
+ }.bind(this)));
+ this.mockHandler.stubs().addKioskApp(ANYTHING);
+ this.mockHandler.stubs().removeKioskApp(ANYTHING);
+ this.mockHandler.stubs().enableKioskAutoLaunch(ANYTHING);
+ this.mockHandler.stubs().disableKioskAutoLaunch(ANYTHING);
+ },
+
+ setUp: function() {
+ // Shows the kiosk apps management overlay.
+ cr.dispatchSimpleEvent($('add-kiosk-app'), 'click');
+ }
+};
+
+// Test opening kiosk app settings has correct location and app items have
+// correct label.
+TEST_F('KioskAppSettingsWebUITest', 'testOpenKioskAppSettings', function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ var appItems = $('kiosk-app-list').items;
+ assertEquals(this.settings_.apps.length, appItems.length);
+ assertEquals(this.settings_.apps[0].name, appItems[0].name.textContent);
+ assertFalse(appItems[0].icon.classList.contains('spinner'));
+ assertEquals(this.settings_.apps[1].id, appItems[1].name.textContent);
+ assertTrue(appItems[1].icon.classList.contains('spinner'));
+});
+
+// Verify that enter key on 'kiosk-app-id-edit' adds an app.
+TEST_F('KioskAppSettingsWebUITest', 'testAddKioskApp', function() {
+ var testAppId = 'app_3';
+ var appIdInput = $('kiosk-app-id-edit');
+
+ appIdInput.value = testAppId;
+
+ this.mockHandler.expects(once()).addKioskApp([testAppId]);
+ var keypress = new KeyboardEvent('keypress', {cancelable: true, bubbles: true, key: 'Enter'});
+ appIdInput.dispatchEvent(keypress);
+});
+
+// Verify that the 'kiosk-app-add' button adds an app.
+TEST_F('KioskAppSettingsWebUITest', 'testAddKioskAppByAddButton', function() {
+ var testAppId = 'app_3';
+ $('kiosk-app-id-edit').value = testAppId;
+
+ this.mockHandler.expects(once()).addKioskApp([testAppId]);
+ cr.dispatchSimpleEvent($('kiosk-app-add'), 'click');
+});
+
+// Verify that the 'done' button adds an app.
+TEST_F('KioskAppSettingsWebUITest', 'testAddKioskAppByDoneButton', function() {
+ var testAppId = 'app_3';
+ $('kiosk-app-id-edit').value = testAppId;
+
+ this.mockHandler.expects(once()).addKioskApp([testAppId]);
+ cr.dispatchSimpleEvent($('kiosk-options-overlay-confirm'), 'click');
+});
+
+// Test the row delete button.
+TEST_F('KioskAppSettingsWebUITest', 'testRemoveKioskApp', function() {
+ var appItem = $('kiosk-app-list').items[0];
+ var appId = appItem.data.id;
+
+ this.mockHandler.expects(once()).removeKioskApp([appId]);
+ appItem.querySelector('.row-delete-button').click();
+});
+
+// Test enable/disable auto launch buttons.
+TEST_F('KioskAppSettingsWebUITest', 'testEnableDisableAutoLaunch', function() {
+ var appItem = $('kiosk-app-list').items[0];
+ var appId = appItem.data.id;
+
+ var enableAutoLaunchCalled = false;
+ this.mockHandler.expects(once()).enableKioskAutoLaunch([appId]).
+ will(callFunction(function() {
+ enableAutoLaunchCalled = true;
+ }));
+ appItem.querySelector('.enable-auto-launch-button').click();
+ expectTrue(enableAutoLaunchCalled);
+
+ var disableAutoLaunchCalled = false;
+ this.mockHandler.expects(once()).disableKioskAutoLaunch([appId]).
+ will(callFunction(function() {
+ disableAutoLaunchCalled = true;
+ }));
+ appItem.querySelector('.disable-auto-launch-button').click();
+ expectTrue(disableAutoLaunchCalled);
+});
+
+// Verify that updateApp updates app info.
+TEST_F('KioskAppSettingsWebUITest', 'testUpdateApp', function() {
+ var appItems = $('kiosk-app-list').items;
+ assertEquals(appItems[1].data.id, 'app_2');
+ expectEquals(appItems[1].data.name, '');
+ expectTrue(appItems[1].icon.classList.contains('spinner'));
+ expectFalse(appItems[1].autoLaunch);
+
+ // New data changes name, autoLaunch and isLoading.
+ var newName = 'Name for App2';
+ var newApp2 = {
+ id: 'app_2',
+ name: newName,
+ iconURL: '',
+ autoLaunch: true,
+ isLoading: false,
+ };
+ extensions.KioskAppsOverlay.updateApp(newApp2);
+
+ assertEquals('app_2', appItems[1].data.id);
+ expectEquals(newName, appItems[1].data.name, newName);
+ expectEquals(newName, appItems[1].name.textContent);
+ expectFalse(appItems[1].icon.classList.contains('spinner'));
+ expectTrue(appItems[1].autoLaunch);
+});
+
+// Verify that showError makes error banner visible.
+TEST_F('KioskAppSettingsWebUITest', 'testShowError', function() {
+ extensions.KioskAppsOverlay.showError('A bad app');
+ expectTrue($('kiosk-apps-error-banner').classList.contains('visible'));
+});
+
+// Verify that checking disable bailout checkbox brings up confirmation UI and
+// the check only remains when the confirmation UI is acknowledged.
+TEST_F('KioskAppSettingsWebUITest', 'testCheckDisableBailout', function() {
+ var checkbox = $('kiosk-disable-bailout-shortcut');
+ var confirmOverlay = $('kiosk-disable-bailout-confirm-overlay');
+ expectFalse(confirmOverlay.classList.contains('showing'));
+
+ // Un-checking the box does not trigger confirmation.
+ checkbox.checked = false;
+ cr.dispatchSimpleEvent(checkbox, 'change');
+ expectFalse(confirmOverlay.classList.contains('showing'));
+
+ // Checking the box trigger confirmation.
+ checkbox.checked = true;
+ cr.dispatchSimpleEvent(checkbox, 'change');
+ expectTrue(confirmOverlay.classList.contains('showing'));
+
+ // Confirm it and the check remains.
+ cr.dispatchSimpleEvent($('kiosk-disable-bailout-confirm-button'), 'click');
+ expectTrue(checkbox.checked);
+ expectFalse(confirmOverlay.classList.contains('showing'));
+
+ // And canceling resets the check.
+ checkbox.checked = true;
+ cr.dispatchSimpleEvent(checkbox, 'change');
+ expectTrue(confirmOverlay.classList.contains('showing'));
+ cr.dispatchSimpleEvent($('kiosk-disable-bailout-cancel-button'), 'click');
+ expectFalse(checkbox.checked);
+ expectFalse(confirmOverlay.classList.contains('showing'));
+});
+
+// Verify that disable bailout checkbox is hidden without kiosk auto launch.
+TEST_F('KioskAppSettingsWebUITest', 'testHideDisableBailout', function() {
+ var checkbox = $('kiosk-disable-bailout-shortcut');
+ var kioskEnabledSettings = {
+ kioskEnabled: true,
+ autoLaunchEnabled: true
+ };
+ extensions.KioskAppsOverlay.enableKiosk(kioskEnabledSettings);
+ expectFalse(checkbox.parentNode.hidden);
+
+ kioskEnabledSettings.autoLaunchEnabled = false;
+ extensions.KioskAppsOverlay.enableKiosk(kioskEnabledSettings);
+ expectTrue(checkbox.parentNode.hidden);
+});
+
+// Verify that disable bailout checkbox is disabled with no auto launch app.
+TEST_F('KioskAppSettingsWebUITest', 'testAllowDisableBailout', function() {
+ var checkbox = $('kiosk-disable-bailout-shortcut');
+
+ this.settings_.hasAutoLaunchApp = false;
+ extensions.KioskAppsOverlay.setSettings(this.settings_);
+ expectTrue(checkbox.disabled);
+
+ this.settings_.hasAutoLaunchApp = true;
+ extensions.KioskAppsOverlay.setSettings(this.settings_);
+ expectFalse(checkbox.disabled);
+});
+
+/**
+ * TestFixture for kiosk app settings when consumer kiosk is disabled.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function NoConsumerKioskWebUITest() {}
+
+NoConsumerKioskWebUITest.prototype = {
+ __proto__: KioskAppSettingsWebUITest.prototype,
+
+ /** @override */
+ commandLineSwitches: [],
+
+ /** @override */
+ setUp: function() {}
+};
+
+// Test kiosk app settings are not visible when consumer kiosk is disabled.
+TEST_F('NoConsumerKioskWebUITest', 'settingsHidden', function() {
+ assertEquals(this.browsePreload, document.location.href);
+ assertTrue($('add-kiosk-app').hidden);
+});
diff --git a/chromium/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.cc b/chromium/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.cc
new file mode 100644
index 00000000000..7651f052ee2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.cc
@@ -0,0 +1,379 @@
+// 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/extensions/chromeos/kiosk_apps_handler.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/strings/string_util.h"
+#include "base/sys_info.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "chromeos/settings/cros_settings_names.h"
+#include "components/crx_file/id_util.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "extensions/common/extension_urls.h"
+#include "extensions/grit/extensions_browser_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "url/gurl.h"
+
+namespace chromeos {
+
+namespace {
+
+// Populates app info dictionary with |app_data|.
+void PopulateAppDict(const KioskAppManager::App& app_data,
+ base::DictionaryValue* app_dict) {
+ std::string icon_url;
+ if (app_data.icon.isNull()) {
+ icon_url = webui::GetBitmapDataUrl(*ResourceBundle::GetSharedInstance()
+ .GetImageNamed(IDR_APP_DEFAULT_ICON)
+ .ToSkBitmap());
+ } else {
+ icon_url = webui::GetBitmapDataUrl(*app_data.icon.bitmap());
+ }
+
+ // The items which are to be written into app_dict are also described in
+ // chrome/browser/resources/extensions/chromeos/kiosk_app_list.js in @typedef
+ // for AppDict. Please update it whenever you add or remove any keys here.
+ app_dict->SetString("id", app_data.app_id);
+ app_dict->SetString("name", app_data.name);
+ app_dict->SetString("iconURL", icon_url);
+ app_dict->SetBoolean(
+ "autoLaunch",
+ KioskAppManager::Get()->GetAutoLaunchApp() == app_data.app_id &&
+ (KioskAppManager::Get()->IsAutoLaunchEnabled() ||
+ KioskAppManager::Get()->IsAutoLaunchRequested()));
+ app_dict->SetBoolean("isLoading", app_data.is_loading);
+}
+
+// Sanitize app id input value and extracts app id out of it.
+// Returns false if an app id could not be derived out of the input.
+bool ExtractsAppIdFromInput(const std::string& input,
+ std::string* app_id) {
+ if (crx_file::id_util::IdIsValid(input)) {
+ *app_id = input;
+ return true;
+ }
+
+ GURL webstore_url = GURL(input);
+ if (!webstore_url.is_valid())
+ return false;
+
+ GURL webstore_base_url =
+ GURL(extension_urls::GetWebstoreItemDetailURLPrefix());
+
+ if (webstore_url.scheme() != webstore_base_url.scheme() ||
+ webstore_url.host() != webstore_base_url.host() ||
+ !base::StartsWith(webstore_url.path(), webstore_base_url.path(),
+ base::CompareCase::SENSITIVE)) {
+ return false;
+ }
+
+ const std::string path = webstore_url.path();
+ const size_t last_slash = path.rfind('/');
+ if (last_slash == std::string::npos)
+ return false;
+
+ const std::string candidate_id = path.substr(last_slash + 1);
+ if (!crx_file::id_util::IdIsValid(candidate_id))
+ return false;
+
+ *app_id = candidate_id;
+ return true;
+}
+
+} // namespace
+
+KioskAppsHandler::KioskAppsHandler(OwnerSettingsServiceChromeOS* service)
+ : kiosk_app_manager_(KioskAppManager::Get()),
+ initialized_(false),
+ is_kiosk_enabled_(false),
+ is_auto_launch_enabled_(false),
+ owner_settings_service_(service),
+ weak_ptr_factory_(this) {
+ kiosk_app_manager_->AddObserver(this);
+}
+
+KioskAppsHandler::~KioskAppsHandler() {
+ kiosk_app_manager_->RemoveObserver(this);
+}
+
+void KioskAppsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("initializeKioskAppSettings",
+ base::Bind(&KioskAppsHandler::HandleInitializeKioskAppSettings,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("getKioskAppSettings",
+ base::Bind(&KioskAppsHandler::HandleGetKioskAppSettings,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("addKioskApp",
+ base::Bind(&KioskAppsHandler::HandleAddKioskApp,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("removeKioskApp",
+ base::Bind(&KioskAppsHandler::HandleRemoveKioskApp,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("enableKioskAutoLaunch",
+ base::Bind(&KioskAppsHandler::HandleEnableKioskAutoLaunch,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("disableKioskAutoLaunch",
+ base::Bind(&KioskAppsHandler::HandleDisableKioskAutoLaunch,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("setDisableBailoutShortcut",
+ base::Bind(&KioskAppsHandler::HandleSetDisableBailoutShortcut,
+ base::Unretained(this)));
+}
+
+void KioskAppsHandler::GetLocalizedValues(content::WebUIDataSource* source) {
+ source->AddString(
+ "addKioskAppButton",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ADD_KIOSK_APP_BUTTON));
+ source->AddString(
+ "kioskOverlayTitle",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_OVERLAY_TITLE));
+ source->AddString(
+ "addKioskApp",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_ADD_APP));
+ source->AddString(
+ "kioskAppIdEditHint",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_ADD_APP_HINT));
+ source->AddString(
+ "enableAutoLaunchButton",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_ENABLE_AUTO_LAUNCH));
+ source->AddString(
+ "disableAutoLaunchButton",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_DISABLE_AUTO_LAUNCH));
+ source->AddString(
+ "autoLaunch",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_AUTO_LAUNCH));
+ source->AddString(
+ "invalidApp",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_KIOSK_INVALID_APP));
+ source->AddString(
+ "kioskDiableBailoutShortcutLabel",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_KIOSK_DISABLE_BAILOUT_SHORTCUT_LABEL));
+ source->AddString(
+ "kioskDisableBailoutShortcutWarningBold",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_KIOSK_DISABLE_BAILOUT_SHORTCUT_WARNING_BOLD));
+ const base::string16 product_os_name =
+ l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_OS_NAME);
+ source->AddString(
+ "kioskDisableBailoutShortcutWarning",
+ l10n_util::GetStringFUTF16(
+ IDS_OPTIONS_KIOSK_DISABLE_BAILOUT_SHORTCUT_WARNING_FORMAT,
+ product_os_name));
+ source->AddString(
+ "kioskDisableBailoutShortcutConfirm",
+ l10n_util::GetStringUTF16(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL));
+ source->AddString(
+ "kioskDisableBailoutShortcutCancel",
+ l10n_util::GetStringUTF16(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL));
+ source->AddString("done", l10n_util::GetStringUTF16(IDS_DONE));
+ source->AddString("add", l10n_util::GetStringUTF16(IDS_ADD));
+}
+
+void KioskAppsHandler::OnKioskAppDataChanged(const std::string& app_id) {
+ UpdateApp(app_id);
+}
+
+void KioskAppsHandler::OnKioskAppDataLoadFailure(const std::string& app_id) {
+ ShowError(app_id);
+}
+
+void KioskAppsHandler::OnKioskExtensionLoadedInCache(
+ const std::string& app_id) {
+ UpdateApp(app_id);
+}
+
+void KioskAppsHandler::OnKioskExtensionDownloadFailed(
+ const std::string& app_id) {
+ ShowError(app_id);
+}
+
+void KioskAppsHandler::OnGetConsumerKioskAutoLaunchStatus(
+ chromeos::KioskAppManager::ConsumerKioskAutoLaunchStatus status) {
+ initialized_ = true;
+ if (KioskAppManager::IsConsumerKioskEnabled()) {
+ if (!base::SysInfo::IsRunningOnChromeOS()) {
+ // Enable everything when running on a dev box.
+ is_kiosk_enabled_ = true;
+ is_auto_launch_enabled_ = true;
+ } else {
+ // Enable consumer kiosk for owner and enable auto launch if configured.
+ is_kiosk_enabled_ =
+ ProfileHelper::IsOwnerProfile(Profile::FromWebUI(web_ui()));
+ is_auto_launch_enabled_ =
+ status == KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_ENABLED;
+ }
+ } else {
+ // Otherwise, consumer kiosk is disabled.
+ is_kiosk_enabled_ = false;
+ is_auto_launch_enabled_ = false;
+ }
+
+ if (is_kiosk_enabled_) {
+ base::DictionaryValue kiosk_params;
+ kiosk_params.SetBoolean("kioskEnabled", is_kiosk_enabled_);
+ kiosk_params.SetBoolean("autoLaunchEnabled", is_auto_launch_enabled_);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "extensions.KioskAppsOverlay.enableKiosk", kiosk_params);
+ }
+}
+
+
+void KioskAppsHandler::OnKioskAppsSettingsChanged() {
+ SendKioskAppSettings();
+}
+
+void KioskAppsHandler::SendKioskAppSettings() {
+ if (!initialized_ || !is_kiosk_enabled_)
+ return;
+
+ bool enable_bailout_shortcut;
+ if (!CrosSettings::Get()->GetBoolean(
+ kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled,
+ &enable_bailout_shortcut)) {
+ enable_bailout_shortcut = true;
+ }
+
+ base::DictionaryValue settings;
+ settings.SetBoolean("disableBailout", !enable_bailout_shortcut);
+ settings.SetBoolean("hasAutoLaunchApp",
+ !kiosk_app_manager_->GetAutoLaunchApp().empty());
+
+ KioskAppManager::Apps apps;
+ kiosk_app_manager_->GetApps(&apps);
+
+ std::unique_ptr<base::ListValue> apps_list(new base::ListValue);
+ for (size_t i = 0; i < apps.size(); ++i) {
+ const KioskAppManager::App& app_data = apps[i];
+
+ std::unique_ptr<base::DictionaryValue> app_info(new base::DictionaryValue);
+ PopulateAppDict(app_data, app_info.get());
+ apps_list->Append(std::move(app_info));
+ }
+ settings.SetWithoutPathExpansion("apps", std::move(apps_list));
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "extensions.KioskAppsOverlay.setSettings", settings);
+}
+
+void KioskAppsHandler::HandleInitializeKioskAppSettings(
+ const base::ListValue* args) {
+ KioskAppManager::Get()->GetConsumerKioskAutoLaunchStatus(
+ base::Bind(&KioskAppsHandler::OnGetConsumerKioskAutoLaunchStatus,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void KioskAppsHandler::HandleGetKioskAppSettings(const base::ListValue* args) {
+ SendKioskAppSettings();
+}
+
+
+void KioskAppsHandler::HandleAddKioskApp(const base::ListValue* args) {
+ if (!initialized_ || !is_kiosk_enabled_)
+ return;
+
+ std::string input;
+ CHECK(args->GetString(0, &input));
+
+ std::string app_id;
+ if (!ExtractsAppIdFromInput(input, &app_id)) {
+ OnKioskAppDataLoadFailure(input);
+ return;
+ }
+
+ kiosk_app_manager_->AddApp(app_id, owner_settings_service_);
+}
+
+void KioskAppsHandler::HandleRemoveKioskApp(const base::ListValue* args) {
+ if (!initialized_ || !is_kiosk_enabled_)
+ return;
+
+ std::string app_id;
+ CHECK(args->GetString(0, &app_id));
+
+ kiosk_app_manager_->RemoveApp(app_id, owner_settings_service_);
+}
+
+void KioskAppsHandler::HandleEnableKioskAutoLaunch(
+ const base::ListValue* args) {
+ if (!initialized_ || !is_kiosk_enabled_ || !is_auto_launch_enabled_)
+ return;
+
+ std::string app_id;
+ CHECK(args->GetString(0, &app_id));
+
+ kiosk_app_manager_->SetAutoLaunchApp(app_id, owner_settings_service_);
+}
+
+void KioskAppsHandler::HandleDisableKioskAutoLaunch(
+ const base::ListValue* args) {
+ if (!initialized_ || !is_kiosk_enabled_ || !is_auto_launch_enabled_)
+ return;
+
+ std::string app_id;
+ CHECK(args->GetString(0, &app_id));
+
+ std::string startup_app_id = kiosk_app_manager_->GetAutoLaunchApp();
+ if (startup_app_id != app_id)
+ return;
+
+ kiosk_app_manager_->SetAutoLaunchApp("", owner_settings_service_);
+}
+
+void KioskAppsHandler::HandleSetDisableBailoutShortcut(
+ const base::ListValue* args) {
+ if (!initialized_ || !is_kiosk_enabled_)
+ return;
+
+ bool disable_bailout_shortcut;
+ CHECK(args->GetBoolean(0, &disable_bailout_shortcut));
+
+ owner_settings_service_->SetBoolean(
+ kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled,
+ !disable_bailout_shortcut);
+}
+
+void KioskAppsHandler::UpdateApp(const std::string& app_id) {
+ KioskAppManager::App app_data;
+ if (!kiosk_app_manager_->GetApp(app_id, &app_data))
+ return;
+
+ base::DictionaryValue app_dict;
+ PopulateAppDict(app_data, &app_dict);
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "extensions.KioskAppsOverlay.updateApp", app_dict);
+}
+
+void KioskAppsHandler::ShowError(const std::string& app_id) {
+ base::Value app_id_value(app_id);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "extensions.KioskAppsOverlay.showError", app_id_value);
+
+ kiosk_app_manager_->RemoveApp(app_id, owner_settings_service_);
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.h b/chromium/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.h
new file mode 100644
index 00000000000..a2334e77a2e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.h
@@ -0,0 +1,81 @@
+// 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_EXTENSIONS_CHROMEOS_KIOSK_APPS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_EXTENSIONS_CHROMEOS_KIOSK_APPS_HANDLER_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace content {
+class WebUIDataSource;
+}
+
+namespace chromeos {
+
+class KioskAppManager;
+class OwnerSettingsServiceChromeOS;
+
+class KioskAppsHandler : public content::WebUIMessageHandler,
+ public KioskAppManagerObserver {
+ public:
+ explicit KioskAppsHandler(OwnerSettingsServiceChromeOS* service);
+ ~KioskAppsHandler() override;
+
+ void GetLocalizedValues(content::WebUIDataSource* source);
+
+ // content::WebUIMessageHandler overrides:
+ void RegisterMessages() override;
+
+ // KioskAppManagerObserver overrides:
+ void OnKioskAppDataChanged(const std::string& app_id) override;
+ void OnKioskAppDataLoadFailure(const std::string& app_id) override;
+ void OnKioskExtensionLoadedInCache(const std::string& app_id) override;
+ void OnKioskExtensionDownloadFailed(const std::string& app_id) override;
+
+ void OnKioskAppsSettingsChanged() override;
+
+ private:
+ // Sends all kiosk apps and settings to webui.
+ void SendKioskAppSettings();
+
+ // JS callbacks.
+ void HandleInitializeKioskAppSettings(const base::ListValue* args);
+ void HandleGetKioskAppSettings(const base::ListValue* args);
+ void HandleAddKioskApp(const base::ListValue* args);
+ void HandleRemoveKioskApp(const base::ListValue* args);
+ void HandleEnableKioskAutoLaunch(const base::ListValue* args);
+ void HandleDisableKioskAutoLaunch(const base::ListValue* args);
+ void HandleSetDisableBailoutShortcut(const base::ListValue* args);
+
+ void UpdateApp(const std::string& app_id);
+ void ShowError(const std::string& app_id);
+
+ // Callback for KioskAppManager::GetConsumerKioskModeStatus().
+ void OnGetConsumerKioskAutoLaunchStatus(
+ chromeos::KioskAppManager::ConsumerKioskAutoLaunchStatus status);
+
+ KioskAppManager* kiosk_app_manager_; // not owned.
+ bool initialized_;
+ bool is_kiosk_enabled_;
+ bool is_auto_launch_enabled_;
+ OwnerSettingsServiceChromeOS* const owner_settings_service_; // not owned
+ base::WeakPtrFactory<KioskAppsHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(KioskAppsHandler);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_CHROMEOS_KIOSK_APPS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/extensions/extension_basic_info.cc b/chromium/chrome/browser/ui/webui/extensions/extension_basic_info.cc
new file mode 100644
index 00000000000..3a0c247cd65
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/extension_basic_info.cc
@@ -0,0 +1,60 @@
+// 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/extensions/extension_basic_info.h"
+
+#include "base/values.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/manifest_handlers/kiosk_mode_info.h"
+#include "extensions/common/manifest_handlers/offline_enabled_info.h"
+#include "extensions/common/manifest_handlers/options_page_info.h"
+#include "extensions/common/manifest_url_handlers.h"
+
+namespace {
+
+// Keys in the dictionary returned by GetExtensionBasicInfo().
+const char kDescriptionKey[] = "description";
+const char kEnabledKey[] = "enabled";
+const char kHomepageUrlKey[] = "homepageUrl";
+const char kIdKey[] = "id";
+const char kNameKey[] = "name";
+const char kKioskEnabledKey[] = "kioskEnabled";
+const char kKioskOnlyKey[] = "kioskOnly";
+const char kOfflineEnabledKey[] = "offlineEnabled";
+const char kOptionsUrlKey[] = "optionsUrl";
+const char kDetailsUrlKey[] = "detailsUrl";
+const char kVersionKey[] = "version";
+const char kPackagedAppKey[] = "packagedApp";
+
+} // namespace
+
+namespace extensions {
+
+void GetExtensionBasicInfo(const Extension* extension,
+ bool enabled,
+ base::DictionaryValue* info) {
+ info->SetString(kIdKey, extension->id());
+ info->SetString(kNameKey, extension->name());
+ info->SetBoolean(kEnabledKey, enabled);
+ info->SetBoolean(kKioskEnabledKey,
+ KioskModeInfo::IsKioskEnabled(extension));
+ info->SetBoolean(kKioskOnlyKey,
+ KioskModeInfo::IsKioskOnly(extension));
+ info->SetBoolean(kOfflineEnabledKey,
+ OfflineEnabledInfo::IsOfflineEnabled(extension));
+ info->SetString(kVersionKey, extension->GetVersionForDisplay());
+ info->SetString(kDescriptionKey, extension->description());
+ info->SetString(
+ kOptionsUrlKey,
+ OptionsPageInfo::GetOptionsPage(extension).possibly_invalid_spec());
+ info->SetString(
+ kHomepageUrlKey,
+ ManifestURL::GetHomepageURL(extension).possibly_invalid_spec());
+ info->SetString(
+ kDetailsUrlKey,
+ ManifestURL::GetDetailsURL(extension).possibly_invalid_spec());
+ info->SetBoolean(kPackagedAppKey, extension->is_platform_app());
+}
+
+} // namespace extensions
diff --git a/chromium/chrome/browser/ui/webui/extensions/extension_basic_info.h b/chromium/chrome/browser/ui/webui/extensions/extension_basic_info.h
new file mode 100644
index 00000000000..51069ea6f11
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/extension_basic_info.h
@@ -0,0 +1,24 @@
+// 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_EXTENSIONS_EXTENSION_BASIC_INFO_H_
+#define CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_BASIC_INFO_H_
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace extensions {
+
+class Extension;
+
+// Fills the |info| dictionary with basic information about the extension.
+// |enabled| is injected for easier testing.
+void GetExtensionBasicInfo(const Extension* extension,
+ bool enabled,
+ base::DictionaryValue* info);
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_BASIC_INFO_H_
diff --git a/chromium/chrome/browser/ui/webui/extensions/extension_icon_source.cc b/chromium/chrome/browser/ui/webui/extensions/extension_icon_source.cc
new file mode 100644
index 00000000000..46f69c56a23
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/extension_icon_source.cc
@@ -0,0 +1,344 @@
+// 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/ui/webui/extensions/extension_icon_source.h"
+
+#include <stddef.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#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/threading/thread.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/favicon/favicon_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/component_extension_resources_map.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/image_loader.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_resource.h"
+#include "extensions/common/manifest_handlers/icons_handler.h"
+#include "extensions/grit/extensions_browser_resources.h"
+#include "skia/ext/image_operations.h"
+#include "ui/base/layout.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/color_utils.h"
+#include "ui/gfx/favicon_size.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/skbitmap_operations.h"
+#include "url/gurl.h"
+
+namespace extensions {
+
+namespace {
+
+scoped_refptr<base::RefCountedMemory> BitmapToMemory(const SkBitmap* image) {
+ base::RefCountedBytes* image_bytes = new base::RefCountedBytes;
+ gfx::PNGCodec::EncodeBGRASkBitmap(*image, false, &image_bytes->data());
+ return image_bytes;
+}
+
+SkBitmap DesaturateImage(const SkBitmap* image) {
+ color_utils::HSL shift = {-1, 0, 0.6};
+ return SkBitmapOperations::CreateHSLShiftedBitmap(*image, shift);
+}
+
+SkBitmap* ToBitmap(const unsigned char* data, size_t size) {
+ SkBitmap* decoded = new SkBitmap();
+ bool success = gfx::PNGCodec::Decode(data, size, decoded);
+ DCHECK(success);
+ return decoded;
+}
+
+} // namespace
+
+ExtensionIconSource::ExtensionIconSource(Profile* profile) : profile_(profile) {
+}
+
+struct ExtensionIconSource::ExtensionIconRequest {
+ content::URLDataSource::GotDataCallback callback;
+ scoped_refptr<const Extension> extension;
+ bool grayscale;
+ int size;
+ ExtensionIconSet::MatchType match;
+};
+
+// static
+GURL ExtensionIconSource::GetIconURL(const Extension* extension,
+ int icon_size,
+ ExtensionIconSet::MatchType match,
+ bool grayscale) {
+ GURL icon_url(base::StringPrintf("%s%s/%d/%d%s",
+ chrome::kChromeUIExtensionIconURL,
+ extension->id().c_str(),
+ icon_size,
+ match,
+ grayscale ? "?grayscale=true" : ""));
+ CHECK(icon_url.is_valid());
+ return icon_url;
+}
+
+// static
+SkBitmap* ExtensionIconSource::LoadImageByResourceId(int resource_id) {
+ base::StringPiece contents =
+ ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
+ resource_id, ui::SCALE_FACTOR_100P);
+
+ // Convert and return it.
+ const unsigned char* data =
+ reinterpret_cast<const unsigned char*>(contents.data());
+ return ToBitmap(data, contents.length());
+}
+
+std::string ExtensionIconSource::GetSource() const {
+ return chrome::kChromeUIExtensionIconHost;
+}
+
+std::string ExtensionIconSource::GetMimeType(const std::string&) const {
+ // We need to explicitly return a mime type, otherwise if the user tries to
+ // drag the image they get no extension.
+ return "image/png";
+}
+
+void ExtensionIconSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ // This is where everything gets started. First, parse the request and make
+ // the request data available for later.
+ static int next_id = 0;
+ if (!ParseData(path, ++next_id, callback)) {
+ // If the request data cannot be parsed, request parameters will not be
+ // added to |request_map_|.
+ // Send back the default application icon (not resized or desaturated) as
+ // the default response.
+ callback.Run(BitmapToMemory(GetDefaultAppImage()).get());
+ return;
+ }
+
+ ExtensionIconRequest* request = GetData(next_id);
+ ExtensionResource icon = IconsInfo::GetIconResource(
+ request->extension.get(), request->size, request->match);
+
+ if (icon.relative_path().empty()) {
+ LoadIconFailed(next_id);
+ } else {
+ LoadExtensionImage(icon, next_id);
+ }
+}
+
+bool ExtensionIconSource::AllowCaching() const {
+ // Should not be cached to reflect the latest contents that may be updated by
+ // Extensions.
+ return false;
+}
+
+ExtensionIconSource::~ExtensionIconSource() {
+}
+
+const SkBitmap* ExtensionIconSource::GetDefaultAppImage() {
+ if (!default_app_data_.get())
+ default_app_data_.reset(LoadImageByResourceId(IDR_APP_DEFAULT_ICON));
+
+ return default_app_data_.get();
+}
+
+const SkBitmap* ExtensionIconSource::GetDefaultExtensionImage() {
+ if (!default_extension_data_.get()) {
+ default_extension_data_.reset(
+ LoadImageByResourceId(IDR_EXTENSION_DEFAULT_ICON));
+ }
+
+ return default_extension_data_.get();
+}
+
+void ExtensionIconSource::FinalizeImage(const SkBitmap* image,
+ int request_id) {
+ SkBitmap bitmap;
+ ExtensionIconRequest* request = GetData(request_id);
+ if (request->grayscale)
+ bitmap = DesaturateImage(image);
+ else
+ bitmap = *image;
+
+ request->callback.Run(BitmapToMemory(&bitmap).get());
+ ClearData(request_id);
+}
+
+void ExtensionIconSource::LoadDefaultImage(int request_id) {
+ ExtensionIconRequest* request = GetData(request_id);
+ const SkBitmap* default_image = NULL;
+
+ if (request->extension->is_app())
+ default_image = GetDefaultAppImage();
+ else
+ default_image = GetDefaultExtensionImage();
+
+ SkBitmap resized_image(skia::ImageOperations::Resize(
+ *default_image, skia::ImageOperations::RESIZE_LANCZOS3,
+ request->size, request->size));
+
+ // There are cases where Resize returns an empty bitmap, for example if you
+ // ask for an image too large. In this case it is better to return the default
+ // image than returning nothing at all.
+ if (resized_image.empty())
+ resized_image = *default_image;
+
+ FinalizeImage(&resized_image, request_id);
+}
+
+void ExtensionIconSource::LoadExtensionImage(const ExtensionResource& icon,
+ int request_id) {
+ ExtensionIconRequest* request = GetData(request_id);
+ ImageLoader::Get(profile_)->LoadImageAsync(
+ request->extension.get(),
+ icon,
+ gfx::Size(request->size, request->size),
+ base::Bind(&ExtensionIconSource::OnImageLoaded, AsWeakPtr(), request_id));
+}
+
+void ExtensionIconSource::LoadFaviconImage(int request_id) {
+ favicon::FaviconService* favicon_service =
+ FaviconServiceFactory::GetForProfile(profile_,
+ ServiceAccessType::EXPLICIT_ACCESS);
+ // Fall back to the default icons if the service isn't available.
+ if (favicon_service == NULL) {
+ LoadDefaultImage(request_id);
+ return;
+ }
+
+ GURL favicon_url =
+ AppLaunchInfo::GetFullLaunchURL(GetData(request_id)->extension.get());
+ favicon_service->GetRawFaviconForPageURL(
+ favicon_url,
+ favicon_base::FAVICON,
+ gfx::kFaviconSize,
+ base::Bind(&ExtensionIconSource::OnFaviconDataAvailable,
+ base::Unretained(this),
+ request_id),
+ &cancelable_task_tracker_);
+}
+
+void ExtensionIconSource::OnFaviconDataAvailable(
+ int request_id,
+ const favicon_base::FaviconRawBitmapResult& bitmap_result) {
+ ExtensionIconRequest* request = GetData(request_id);
+
+ // Fallback to the default icon if there wasn't a favicon.
+ if (!bitmap_result.is_valid()) {
+ LoadDefaultImage(request_id);
+ return;
+ }
+
+ if (!request->grayscale) {
+ // If we don't need a grayscale image, then we can bypass FinalizeImage
+ // to avoid unnecessary conversions.
+ request->callback.Run(bitmap_result.bitmap_data.get());
+ ClearData(request_id);
+ } else {
+ FinalizeImage(ToBitmap(bitmap_result.bitmap_data->front(),
+ bitmap_result.bitmap_data->size()), request_id);
+ }
+}
+
+void ExtensionIconSource::OnImageLoaded(int request_id,
+ const gfx::Image& image) {
+ if (image.IsEmpty())
+ LoadIconFailed(request_id);
+ else
+ FinalizeImage(image.ToSkBitmap(), request_id);
+}
+
+void ExtensionIconSource::LoadIconFailed(int request_id) {
+ ExtensionIconRequest* request = GetData(request_id);
+ ExtensionResource icon = IconsInfo::GetIconResource(
+ request->extension.get(), request->size, request->match);
+
+ if (request->size == extension_misc::EXTENSION_ICON_BITTY)
+ LoadFaviconImage(request_id);
+ else
+ LoadDefaultImage(request_id);
+}
+
+bool ExtensionIconSource::ParseData(
+ const std::string& path,
+ int request_id,
+ const content::URLDataSource::GotDataCallback& callback) {
+ // Extract the parameters from the path by lower casing and splitting.
+ std::string path_lower = base::ToLowerASCII(path);
+ std::vector<std::string> path_parts = base::SplitString(
+ path_lower, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+ if (path_lower.empty() || path_parts.size() < 3)
+ return false;
+
+ std::string size_param = path_parts.at(1);
+ std::string match_param = path_parts.at(2);
+ match_param = match_param.substr(0, match_param.find('?'));
+
+ int size;
+ if (!base::StringToInt(size_param, &size))
+ return false;
+ if (size <= 0 || size > extension_misc::EXTENSION_ICON_GIGANTOR)
+ return false;
+
+ ExtensionIconSet::MatchType match_type;
+ int match_num;
+ if (!base::StringToInt(match_param, &match_num))
+ return false;
+ match_type = static_cast<ExtensionIconSet::MatchType>(match_num);
+ if (!(match_type == ExtensionIconSet::MATCH_EXACTLY ||
+ match_type == ExtensionIconSet::MATCH_SMALLER ||
+ match_type == ExtensionIconSet::MATCH_BIGGER))
+ match_type = ExtensionIconSet::MATCH_EXACTLY;
+
+ std::string extension_id = path_parts.at(0);
+ const Extension* extension = ExtensionSystem::Get(profile_)->
+ extension_service()->GetInstalledExtension(extension_id);
+ if (!extension)
+ return false;
+
+ bool grayscale = path_lower.find("grayscale=true") != std::string::npos;
+
+ SetData(request_id, callback, extension, grayscale, size, match_type);
+
+ return true;
+}
+
+void ExtensionIconSource::SetData(
+ int request_id,
+ const content::URLDataSource::GotDataCallback& callback,
+ const Extension* extension,
+ bool grayscale,
+ int size,
+ ExtensionIconSet::MatchType match) {
+ std::unique_ptr<ExtensionIconRequest> request =
+ base::MakeUnique<ExtensionIconRequest>();
+ request->callback = callback;
+ request->extension = extension;
+ request->grayscale = grayscale;
+ request->size = size;
+ request->match = match;
+ request_map_[request_id] = std::move(request);
+}
+
+ExtensionIconSource::ExtensionIconRequest* ExtensionIconSource::GetData(
+ int request_id) {
+ return request_map_[request_id].get();
+}
+
+void ExtensionIconSource::ClearData(int request_id) {
+ request_map_.erase(request_id);
+}
+
+} // namespace extensions
diff --git a/chromium/chrome/browser/ui/webui/extensions/extension_icon_source.h b/chromium/chrome/browser/ui/webui/extensions/extension_icon_source.h
new file mode 100644
index 00000000000..5b905318f32
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/extension_icon_source.h
@@ -0,0 +1,163 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_ICON_SOURCE_H_
+#define CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_ICON_SOURCE_H_
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "components/favicon/core/favicon_service.h"
+#include "content/public/browser/url_data_source.h"
+#include "extensions/common/extension_icon_set.h"
+#include "extensions/common/extension_resource.h"
+
+class ExtensionIconSet;
+class Profile;
+class SkBitmap;
+
+namespace extensions {
+class Extension;
+
+// ExtensionIconSource serves extension icons through network level chrome:
+// requests. Icons can be retrieved for any installed extension or app.
+//
+// The format for requesting an icon is as follows:
+// chrome://extension-icon/<extension_id>/<icon_size>/<match_type>?[options]
+//
+// Parameters (<> required, [] optional):
+// <extension_id> = the id of the extension
+// <icon_size> = the size of the icon, as the integer value of the
+// corresponding Extension:Icons enum.
+// <match_type> = the fallback matching policy, as the integer value of
+// the corresponding ExtensionIconSet::MatchType enum.
+// [options] = Optional transformations to apply. Supported options:
+// grayscale=true to desaturate the image.
+//
+// Examples:
+// chrome-extension://gbmgkahjioeacddebbnengilkgbkhodg/32/1?grayscale=true
+// (ICON_SMALL, MATCH_BIGGER, grayscale)
+// chrome-extension://gbmgkahjioeacddebbnengilkgbkhodg/128/0
+// (ICON_LARGE, MATCH_EXACTLY)
+//
+// We attempt to load icons from the following sources in order:
+// 1) The icons as listed in the extension / app manifests.
+// 2) If a 16px icon was requested, the favicon for extension's launch URL.
+// 3) The default extension / application icon if there are still no matches.
+//
+class ExtensionIconSource : public content::URLDataSource,
+ public base::SupportsWeakPtr<ExtensionIconSource> {
+ public:
+ explicit ExtensionIconSource(Profile* profile);
+
+ // Gets the URL of the |extension| icon in the given |icon_size|, falling back
+ // based on the |match| type. If |grayscale|, the URL will be for the
+ // desaturated version of the icon.
+ static GURL GetIconURL(const Extension* extension,
+ int icon_size,
+ ExtensionIconSet::MatchType match,
+ bool grayscale);
+
+ // A public utility function for accessing the bitmap of the image specified
+ // by |resource_id|.
+ static SkBitmap* LoadImageByResourceId(int resource_id);
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ std::string GetMimeType(const std::string&) const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ bool AllowCaching() const override;
+
+ private:
+ // Encapsulates the request parameters for |request_id|.
+ struct ExtensionIconRequest;
+
+ ~ExtensionIconSource() override;
+
+ // Returns the bitmap for the default app image.
+ const SkBitmap* GetDefaultAppImage();
+
+ // Returns the bitmap for the default extension.
+ const SkBitmap* GetDefaultExtensionImage();
+
+ // Performs any remaining transformations (like desaturating the |image|),
+ // then returns the |image| to the client and clears up any temporary data
+ // associated with the |request_id|.
+ void FinalizeImage(const SkBitmap* image, int request_id);
+
+ // Loads the default image for |request_id| and returns to the client.
+ void LoadDefaultImage(int request_id);
+
+ // Loads the extension's |icon| for the given |request_id| and returns the
+ // image to the client.
+ void LoadExtensionImage(const ExtensionResource& icon,
+ int request_id);
+
+ // Loads the favicon image for the app associated with the |request_id|. If
+ // the image does not exist, we fall back to the default image.
+ void LoadFaviconImage(int request_id);
+
+ // FaviconService callback
+ void OnFaviconDataAvailable(
+ int request_id,
+ const favicon_base::FaviconRawBitmapResult& bitmap_result);
+
+ // ImageLoader callback
+ void OnImageLoaded(int request_id, const gfx::Image& image);
+
+ // Called when the extension doesn't have an icon. We fall back to multiple
+ // sources, using the following order:
+ // 1) The icons as listed in the extension / app manifests.
+ // 2) If a 16px icon and the extension has a launch URL, see if Chrome
+ // has a corresponding favicon.
+ // 3) If still no matches, load the default extension / application icon.
+ void LoadIconFailed(int request_id);
+
+ // Parses and saves an ExtensionIconRequest for the URL |path| for the
+ // specified |request_id|.
+ bool ParseData(const std::string& path,
+ int request_id,
+ const content::URLDataSource::GotDataCallback& callback);
+
+ // Stores the parameters associated with the |request_id|, making them
+ // as an ExtensionIconRequest via GetData.
+ void SetData(int request_id,
+ const content::URLDataSource::GotDataCallback& callback,
+ const Extension* extension,
+ bool grayscale,
+ int size,
+ ExtensionIconSet::MatchType match);
+
+ // Returns the ExtensionIconRequest for the given |request_id|.
+ ExtensionIconRequest* GetData(int request_id);
+
+ // Removes temporary data associated with |request_id|.
+ void ClearData(int request_id);
+
+ Profile* profile_;
+
+ // Maps tracker ids to request ids.
+ std::map<int, int> tracker_map_;
+
+ // Maps request_ids to ExtensionIconRequests.
+ std::map<int, std::unique_ptr<ExtensionIconRequest>> request_map_;
+
+ std::unique_ptr<SkBitmap> default_app_data_;
+
+ std::unique_ptr<SkBitmap> default_extension_data_;
+
+ base::CancelableTaskTracker cancelable_task_tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionIconSource);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_ICON_SOURCE_H_
diff --git a/chromium/chrome/browser/ui/webui/extensions/extension_loader_handler.cc b/chromium/chrome/browser/ui/webui/extensions/extension_loader_handler.cc
new file mode 100644
index 00000000000..8bafdd5a1af
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/extension_loader_handler.cc
@@ -0,0 +1,225 @@
+// 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/ui/webui/extensions/extension_loader_handler.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/path_util.h"
+#include "chrome/browser/extensions/unpacked_installer.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/file_highlighter.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/manifest_constants.h"
+#include "third_party/re2/src/re2/re2.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace extensions {
+
+namespace {
+
+// Read a file to a string and return.
+std::string ReadFileToString(const base::FilePath& path) {
+ std::string data;
+ // This call can fail, but it doesn't matter for our purposes. If it fails,
+ // we simply return an empty string for the manifest, and ignore it.
+ base::ReadFileToString(path, &data);
+ return data;
+}
+
+} // namespace
+
+ExtensionLoaderHandler::ExtensionLoaderHandler(Profile* profile)
+ : profile_(profile),
+ extension_error_reporter_observer_(this),
+ ui_ready_(false),
+ weak_ptr_factory_(this) {
+ DCHECK(profile_);
+ extension_error_reporter_observer_.Add(ExtensionErrorReporter::GetInstance());
+}
+
+ExtensionLoaderHandler::~ExtensionLoaderHandler() {
+}
+
+void ExtensionLoaderHandler::GetLocalizedValues(
+ content::WebUIDataSource* source) {
+ source->AddString(
+ "extensionLoadErrorHeading",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_HEADING));
+ source->AddString(
+ "extensionLoadErrorMessage",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_MESSAGE));
+ source->AddString(
+ "extensionLoadErrorRetry",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_RETRY));
+ source->AddString(
+ "extensionLoadErrorGiveUp",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_GIVE_UP));
+ source->AddString(
+ "extensionLoadCouldNotLoadManifest",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_COULD_NOT_LOAD_MANIFEST));
+ source->AddString(
+ "extensionLoadAdditionalFailures",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ADDITIONAL_FAILURES));
+}
+
+void ExtensionLoaderHandler::RegisterMessages() {
+ // We observe WebContents in order to detect page refreshes, since notifying
+ // the frontend of load failures must be delayed until the page finishes
+ // loading. We never call Observe(NULL) because this object is constructed
+ // on page load and persists between refreshes.
+ content::WebContentsObserver::Observe(web_ui()->GetWebContents());
+
+ web_ui()->RegisterMessageCallback(
+ "extensionLoaderRetry",
+ base::Bind(&ExtensionLoaderHandler::HandleRetry,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "extensionLoaderIgnoreFailure",
+ base::Bind(&ExtensionLoaderHandler::HandleIgnoreFailure,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "extensionLoaderDisplayFailures",
+ base::Bind(&ExtensionLoaderHandler::HandleDisplayFailures,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+// static
+void ExtensionLoaderHandler::GetManifestError(
+ const std::string& error,
+ const base::FilePath& extension_path,
+ const GetManifestErrorCallback& callback) {
+ size_t line = 0u;
+ size_t column = 0u;
+ std::string regex = base::StringPrintf("%s Line: (\\d+), column: (\\d+), .*",
+ manifest_errors::kManifestParseError);
+ // If this was a JSON parse error, we can highlight the exact line with the
+ // error. Otherwise, we should still display the manifest (for consistency,
+ // reference, and so that if we ever make this really fancy and add an editor,
+ // it's ready).
+ //
+ // This regex call can fail, but if it does, we just don't highlight anything.
+ re2::RE2::FullMatch(error, regex, &line, &column);
+
+ // This will read the manifest and call AddFailure with the read manifest
+ // contents.
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
+ base::Bind(&ReadFileToString, extension_path.Append(kManifestFilename)),
+ base::Bind(callback, extension_path, error, line));
+}
+
+void ExtensionLoaderHandler::HandleRetry(const base::ListValue* args) {
+ DCHECK(args->empty());
+ const base::FilePath file_path = failed_paths_.back();
+ failed_paths_.pop_back();
+ LoadUnpackedExtension(file_path);
+}
+
+void ExtensionLoaderHandler::HandleIgnoreFailure(const base::ListValue* args) {
+ DCHECK(args->empty());
+ failed_paths_.pop_back();
+}
+
+void ExtensionLoaderHandler::HandleDisplayFailures(
+ const base::ListValue* args) {
+ DCHECK(args->empty());
+ ui_ready_ = true;
+
+ // Notify the frontend of any load failures that were triggered while the
+ // chrome://extensions page was loading.
+ if (!failures_.empty())
+ NotifyFrontendOfFailure();
+}
+
+void ExtensionLoaderHandler::LoadUnpackedExtension(
+ const base::FilePath& file_path) {
+ scoped_refptr<UnpackedInstaller> installer = UnpackedInstaller::Create(
+ ExtensionSystem::Get(profile_)->extension_service());
+
+ // We do our own error handling, so we don't want a load failure to trigger
+ // a dialog.
+ installer->set_be_noisy_on_failure(false);
+
+ installer->Load(file_path);
+}
+
+void ExtensionLoaderHandler::OnLoadFailure(
+ content::BrowserContext* browser_context,
+ const base::FilePath& file_path,
+ const std::string& error) {
+ // Only show errors from our browser context.
+ if (web_ui()->GetWebContents()->GetBrowserContext() != browser_context)
+ return;
+
+ GetManifestError(error, file_path,
+ base::Bind(&ExtensionLoaderHandler::AddFailure,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ExtensionLoaderHandler::DidStartNavigation(
+ content::NavigationHandle* navigation_handle) {
+ if (!navigation_handle->IsInMainFrame())
+ return;
+
+ // In the event of a page reload, we ensure that the frontend is not notified
+ // until the UI finishes loading, so we set |ui_ready_| to false. This is
+ // balanced in HandleDisplayFailures, which is called when the frontend is
+ // ready to receive failure notifications.
+ if (navigation_handle->GetReloadType() != content::ReloadType::NONE)
+ ui_ready_ = false;
+}
+
+void ExtensionLoaderHandler::AddFailure(
+ const base::FilePath& file_path,
+ const std::string& error,
+ size_t line_number,
+ const std::string& manifest) {
+ failed_paths_.push_back(file_path);
+ base::FilePath prettified_path = path_util::PrettifyPath(file_path);
+
+ std::unique_ptr<base::DictionaryValue> manifest_value(
+ new base::DictionaryValue());
+ SourceHighlighter highlighter(manifest, line_number);
+ // If the line number is 0, this highlights no regions, but still adds the
+ // full manifest.
+ highlighter.SetHighlightedRegions(manifest_value.get());
+
+ std::unique_ptr<base::DictionaryValue> failure(new base::DictionaryValue());
+ failure->SetString("path", prettified_path.LossyDisplayName());
+ failure->SetString("error", error);
+ failure->Set("manifest", std::move(manifest_value));
+ failures_.Append(std::move(failure));
+
+ // Only notify the frontend if the frontend UI is ready.
+ if (ui_ready_)
+ NotifyFrontendOfFailure();
+}
+
+void ExtensionLoaderHandler::NotifyFrontendOfFailure() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "extensions.ExtensionLoader.notifyLoadFailed", failures_);
+ failures_.Clear();
+}
+
+} // namespace extensions
diff --git a/chromium/chrome/browser/ui/webui/extensions/extension_loader_handler.h b/chromium/chrome/browser/ui/webui/extensions/extension_loader_handler.h
new file mode 100644
index 00000000000..a379fe1cd09
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/extension_loader_handler.h
@@ -0,0 +1,120 @@
+// 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_UI_WEBUI_EXTENSIONS_EXTENSION_LOADER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_LOADER_HANDLER_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/extension_error_reporter.h"
+#include "content/public/browser/reload_type.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace content {
+class WebUIDataSource;
+}
+
+class Profile;
+
+namespace extensions {
+
+class Extension;
+
+// The handler page for the Extension Commands UI overlay.
+class ExtensionLoaderHandler : public content::WebUIMessageHandler,
+ public ExtensionErrorReporter::Observer,
+ public content::WebContentsObserver {
+ public:
+ using GetManifestErrorCallback =
+ base::Callback<void(const base::FilePath& file_path,
+ const std::string& error,
+ size_t line_number,
+ const std::string& manifest)>;
+
+ explicit ExtensionLoaderHandler(Profile* profile);
+ ~ExtensionLoaderHandler() override;
+
+ // Fetches the localized values for the page and deposits them into |source|.
+ void GetLocalizedValues(content::WebUIDataSource* source);
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // TODO(devlin): Move this to developerPrivate.
+ static void GetManifestError(const std::string& error,
+ const base::FilePath& extension_path,
+ const GetManifestErrorCallback& callback);
+
+ private:
+ // Handle the 'extensionLoaderRetry' message.
+ void HandleRetry(const base::ListValue* args);
+
+ // Handle the 'extensionLoaderIgnoreFailure' message.
+ void HandleIgnoreFailure(const base::ListValue* args);
+
+ // Handle the 'extensionLoaderDisplayFailures' message.
+ void HandleDisplayFailures(const base::ListValue* args);
+
+ // Try to load an unpacked extension from the given |file_path|.
+ void LoadUnpackedExtension(const base::FilePath& file_path);
+
+ // ExtensionErrorReporter::Observer:
+ void OnLoadFailure(content::BrowserContext* browser_context,
+ const base::FilePath& file_path,
+ const std::string& error) override;
+
+ // content::WebContentsObserver:
+ void DidStartNavigation(
+ content::NavigationHandle* navigation_handle) override;
+
+ // Add a failure to |failures_|. If it was a manifest error, |manifest| will
+ // hold the manifest contents, and |line_number| will point to the line at
+ // which the error was found.
+ void AddFailure(const base::FilePath& file_path,
+ const std::string& error,
+ size_t line_number,
+ const std::string& manifest);
+
+ // Notify the frontend of all failures.
+ void NotifyFrontendOfFailure();
+
+ // The profile with which this Handler is associated.
+ Profile* profile_;
+
+ // Holds information about all unpacked extension install failures that
+ // were reported while the extensions page was loading.
+ base::ListValue failures_;
+
+ // Holds failed paths for load retries.
+ std::vector<base::FilePath> failed_paths_;
+
+ ScopedObserver<ExtensionErrorReporter, ExtensionErrorReporter::Observer>
+ extension_error_reporter_observer_;
+
+ // Set when the chrome://extensions page is fully loaded and the frontend is
+ // ready to receive failure notifications. We need this because the page
+ // fails to display failures if they are sent before the Javascript is loaded.
+ bool ui_ready_;
+
+ // Weak pointer factory for posting background tasks.
+ base::WeakPtrFactory<ExtensionLoaderHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionLoaderHandler);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_LOADER_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc b/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc
new file mode 100644
index 00000000000..ac235af96c8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc
@@ -0,0 +1,89 @@
+// 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/extensions/extension_settings_browsertest.h"
+
+#include "base/path_service.h"
+#include "chrome/browser/extensions/chrome_test_extension_loader.h"
+#include "chrome/browser/extensions/unpacked_installer.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/web_contents_sizer.h"
+#include "chrome/common/chrome_paths.h"
+#include "extensions/browser/extension_dialog_auto_confirm.h"
+#include "extensions/browser/extension_system.h"
+
+using extensions::Extension;
+using extensions::TestManagementPolicyProvider;
+
+ExtensionSettingsUIBrowserTest::ExtensionSettingsUIBrowserTest()
+ : policy_provider_(TestManagementPolicyProvider::PROHIBIT_MODIFY_STATUS |
+ TestManagementPolicyProvider::MUST_REMAIN_ENABLED |
+ TestManagementPolicyProvider::MUST_REMAIN_INSTALLED) {
+ CHECK(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_));
+ test_data_dir_ = test_data_dir_.AppendASCII("extensions");
+}
+
+ExtensionSettingsUIBrowserTest::~ExtensionSettingsUIBrowserTest() {}
+
+void ExtensionSettingsUIBrowserTest::InstallGoodExtension() {
+ EXPECT_TRUE(InstallExtension(test_data_dir_.AppendASCII("good.crx")));
+}
+
+void ExtensionSettingsUIBrowserTest::InstallErrorsExtension() {
+ EXPECT_TRUE(
+ InstallExtension(test_data_dir_.AppendASCII("error_console")
+ .AppendASCII("runtime_and_manifest_errors")));
+}
+
+void ExtensionSettingsUIBrowserTest::InstallSharedModule() {
+ base::FilePath shared_module_path =
+ test_data_dir_.AppendASCII("api_test").AppendASCII("shared_module");
+ EXPECT_TRUE(InstallExtension(shared_module_path.AppendASCII("shared")));
+ EXPECT_TRUE(InstallExtension(shared_module_path.AppendASCII("import_pass")));
+}
+
+void ExtensionSettingsUIBrowserTest::InstallPackagedApp() {
+ EXPECT_TRUE(InstallExtension(test_data_dir_.AppendASCII("packaged_app")));
+}
+
+void ExtensionSettingsUIBrowserTest::InstallHostedApp() {
+ EXPECT_TRUE(InstallExtension(test_data_dir_.AppendASCII("hosted_app")));
+}
+
+void ExtensionSettingsUIBrowserTest::InstallPlatformApp() {
+ EXPECT_TRUE(InstallExtension(
+ test_data_dir_.AppendASCII("platform_apps").AppendASCII("minimal")));
+}
+
+void ExtensionSettingsUIBrowserTest::AddManagedPolicyProvider() {
+ extensions::ExtensionSystem* extension_system =
+ extensions::ExtensionSystem::Get(browser()->profile());
+ extension_system->management_policy()->RegisterProvider(&policy_provider_);
+}
+
+void ExtensionSettingsUIBrowserTest::SetAutoConfirmUninstall() {
+ uninstall_auto_confirm_.reset(new extensions::ScopedTestDialogAutoConfirm(
+ extensions::ScopedTestDialogAutoConfirm::ACCEPT));
+}
+
+void ExtensionSettingsUIBrowserTest::EnableErrorConsole() {
+ error_console_override_.reset(new extensions::FeatureSwitch::ScopedOverride(
+ extensions::FeatureSwitch::error_console(), true));
+}
+
+void ExtensionSettingsUIBrowserTest::ShrinkWebContentsView() {
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ CHECK(web_contents);
+ ResizeWebContents(web_contents, gfx::Rect(0, 0, 400, 400));
+}
+
+const Extension* ExtensionSettingsUIBrowserTest::InstallExtension(
+ const base::FilePath& path) {
+ extensions::ChromeTestExtensionLoader loader(browser()->profile());
+ loader.set_ignore_manifest_warnings(true);
+ return loader.LoadExtension(path).get();
+}
diff --git a/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.h b/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.h
new file mode 100644
index 00000000000..1abb1df7e3b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.h
@@ -0,0 +1,68 @@
+// 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_EXTENSIONS_EXTENSION_SETTINGS_BROWSERTEST_H_
+#define CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_BROWSERTEST_H_
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "chrome/test/base/web_ui_browser_test.h"
+#include "extensions/browser/test_management_policy.h"
+#include "extensions/common/feature_switch.h"
+
+namespace extensions {
+class Extension;
+class ScopedTestDialogAutoConfirm;
+}
+
+// C++ test fixture used by extension_settings_browsertest.js.
+class ExtensionSettingsUIBrowserTest : public WebUIBrowserTest {
+ public:
+ ExtensionSettingsUIBrowserTest();
+ ~ExtensionSettingsUIBrowserTest() override;
+
+ protected:
+ void InstallGoodExtension();
+
+ void InstallErrorsExtension();
+
+ void InstallSharedModule();
+
+ void InstallPackagedApp();
+
+ void InstallHostedApp();
+
+ void InstallPlatformApp();
+
+ void AddManagedPolicyProvider();
+
+ void SetAutoConfirmUninstall();
+
+ // Enables the error console so errors are displayed in the extensions page.
+ void EnableErrorConsole();
+
+ // Shrinks the web contents view in order to ensure vertical overflow.
+ void ShrinkWebContentsView();
+
+ private:
+ const extensions::Extension* InstallExtension(const base::FilePath& path);
+
+ // Used to simulate managed extensions (by being registered as a provider).
+ extensions::TestManagementPolicyProvider policy_provider_;
+
+ base::FilePath test_data_dir_;
+
+ // Used to enable the error console.
+ std::unique_ptr<extensions::FeatureSwitch::ScopedOverride>
+ error_console_override_;
+
+ std::unique_ptr<extensions::ScopedTestDialogAutoConfirm>
+ uninstall_auto_confirm_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionSettingsUIBrowserTest);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_BROWSERTEST_H_
diff --git a/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js b/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js
new file mode 100644
index 00000000000..3bf00fe3c22
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/extension_settings_browsertest.js
@@ -0,0 +1,527 @@
+// 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.
+
+// TODO(dbeam): test for loading upacked extensions?
+
+GEN('#include "chrome/browser/ui/webui/extensions/' +
+ 'extension_settings_browsertest.h"');
+
+// The id of the extension from |InstallGoodExtension|.
+var GOOD_EXTENSION_ID = 'ldnnhddmnhbkjipkidpdiheffobcpfmf';
+
+// The id of the extension from |InstallErrorsExtension|.
+var ERROR_EXTENSION_ID = 'pdlpifnclfacjobnmbpngemkalkjamnf';
+
+/**
+ * Test C++ fixture for settings WebUI testing.
+ * @constructor
+ * @extends {testing.Test}
+ */
+function ExtensionSettingsUIBrowserTest() {}
+
+/**
+ * TestFixture for extension settings WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function ExtensionSettingsWebUITest() {}
+
+ExtensionSettingsWebUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /** @override */
+ isAsync: true,
+
+ /** @override */
+ runAccessibilityChecks: true,
+
+ /** @override */
+ accessibilityIssuesAreErrors: true,
+
+ /**
+ * A URL to load before starting each test.
+ * @type {string}
+ * @const
+ */
+ browsePreload: 'chrome://extensions-frame/',
+
+ /** @override */
+ typedefCppFixture: 'ExtensionSettingsUIBrowserTest',
+
+ /** @override */
+ setUp: function() {
+ testing.Test.prototype.setUp.call(this);
+ testing.Test.disableAnimationsAndTransitions();
+
+ // Enable when failure is resolved.
+ // AX_ARIA_08: http://crbug.com/560903
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'requiredOwnedAriaRoleMissing',
+ '#kiosk-app-list');
+ },
+
+ /**
+ * Holds an array of steps that should happen in order during a test.
+ * The last step should be |testDone|.
+ * @protected {Array<!Function>}
+ * */
+ steps: [],
+
+ /**
+ * Advances to the next step in the test. Every step should call this.
+ * @protected
+ * */
+ nextStep: function() {
+ assertTrue(this.steps.length > 0);
+ this.steps.shift().call(this);
+ },
+
+ /**
+ * Will wait for the page to load before calling the next step. This should be
+ * the first step in every test.
+ * @protected
+ * */
+ waitForPageLoad: function() {
+ assertEquals(this.browsePreload, document.location.href);
+ var extensionList = getRequiredElement('extension-settings-list');
+ extensionList.loadFinished.then(this.nextStep.bind(this));
+ },
+
+ /** @protected */
+ enableDeveloperMode: function() {
+ // Toggling developer mode triggers a page update, so we need to be able to
+ // wait for the update to finish.
+ $('extension-settings-list').resetLoadFinished();
+ var waitForPage = this.waitForPageLoad.bind(this);
+ document.addEventListener('devControlsVisibilityUpdated',
+ function devCallback() {
+ // Callback should only be handled once.
+ document.removeEventListener('devControlsVisibilityUpdated', devCallback);
+
+ chrome.developerPrivate.getProfileConfiguration(function(profileInfo) {
+ assertTrue(extensionSettings.classList.contains('dev-mode'));
+ assertTrue(profileInfo.inDeveloperMode);
+
+ // This event isn't thrown because transitions are disabled.
+ // Ensure transition here so that any dependent code does not break.
+ ensureTransitionEndEvent($('dev-controls'), 0);
+
+ waitForPage();
+ });
+ });
+
+ var extensionSettings = getRequiredElement('extension-settings');
+ assertFalse(extensionSettings.classList.contains('dev-mode'));
+ $('toggle-dev-on').click();
+ },
+
+ /** @protected */
+ testDeveloperMode: function() {
+ var next = this.nextStep.bind(this);
+ var checkDevModeIsOff = function() {
+ chrome.developerPrivate.getProfileConfiguration(function(profileInfo) {
+ assertFalse(profileInfo.inDeveloperMode);
+ next();
+ });
+ };
+ this.steps = [this.waitForPageLoad,
+ checkDevModeIsOff,
+ this.enableDeveloperMode,
+ testDone];
+ this.nextStep();
+ },
+};
+
+// Flaky: http://crbug.com/505506.
+// Verify that developer mode doesn't change behavior when the number of
+// extensions changes.
+TEST_F('ExtensionSettingsWebUITest', 'DISABLED_testDeveloperModeNoExtensions',
+ function() {
+ this.testDeveloperMode();
+});
+
+TEST_F('ExtensionSettingsWebUITest', 'testEmptyExtensionList', function() {
+ var verifyListIsHiddenAndEmpty = function() {
+ assertTrue($('extension-list-wrapper').hidden);
+ assertFalse($('no-extensions').hidden);
+ assertEquals(0, $('extension-settings-list').childNodes.length);
+ this.nextStep();
+ };
+
+ this.steps = [this.waitForPageLoad, verifyListIsHiddenAndEmpty, testDone];
+ this.nextStep();
+});
+
+TEST_F('ExtensionSettingsWebUITest', 'testChromeSendHandled', function() {
+ var testPackExtenion = function() {
+ // This dialog should be hidden at first.
+ assertFalse($('pack-extension-overlay').classList.contains('showing'));
+
+ // Show the dialog, which triggers a chrome.send() for metrics purposes.
+ cr.dispatchSimpleEvent($('pack-extension'), 'click');
+ assertTrue($('pack-extension-overlay').classList.contains('showing'));
+ this.nextStep();
+ };
+
+ this.steps = [this.waitForPageLoad, testPackExtenion, testDone];
+ this.nextStep();
+});
+
+/**
+ * @param {chrome.developerPrivate.EventType} eventType
+ * @param {function():void} callback
+ * @constructor
+ */
+function UpdateListener(eventType, callback) {
+ this.callback_ = callback;
+ this.eventType_ = eventType;
+ this.onItemStateChangedListener_ = this.onItemStateChanged_.bind(this);
+ chrome.developerPrivate.onItemStateChanged.addListener(
+ this.onItemStateChangedListener_);
+}
+
+UpdateListener.prototype = {
+ /** @private */
+ onItemStateChanged_: function(data) {
+ if (this.eventType_ == data.event_type) {
+ window.setTimeout(function() {
+ chrome.developerPrivate.onItemStateChanged.removeListener(
+ this.onItemStateChangedListener_);
+ this.callback_();
+ }.bind(this), 0);
+ }
+ }
+};
+
+function BasicExtensionSettingsWebUITest() {}
+
+BasicExtensionSettingsWebUITest.prototype = {
+ __proto__: ExtensionSettingsWebUITest.prototype,
+
+ /** @override */
+ testGenPreamble: function() {
+ // Install multiple types of extensions to ensure we handle each type.
+ // TODO(devlin): There are more types to add here.
+ GEN(' InstallGoodExtension();');
+ GEN(' InstallErrorsExtension();');
+ GEN(' InstallSharedModule();');
+ GEN(' InstallPackagedApp();');
+
+ GEN(' SetAutoConfirmUninstall();');
+ },
+
+ /** @protected */
+ verifyDisabledWorks: function() {
+ var listener = new UpdateListener(
+ chrome.developerPrivate.EventType.UNLOADED,
+ function() {
+ var node = getRequiredElement(GOOD_EXTENSION_ID);
+ assertTrue(node.classList.contains('inactive-extension'));
+ this.nextStep();
+ }.bind(this));
+ chrome.management.setEnabled(GOOD_EXTENSION_ID, false);
+ },
+
+ /** @protected */
+ verifyEnabledWorks: function() {
+ var listener = new UpdateListener(
+ chrome.developerPrivate.EventType.LOADED,
+ function() {
+ var node = getRequiredElement(GOOD_EXTENSION_ID);
+ assertFalse(node.classList.contains('inactive-extension'));
+ this.nextStep();
+ }.bind(this));
+ chrome.management.setEnabled(GOOD_EXTENSION_ID, true);
+ },
+
+ /** @protected */
+ verifyUninstallWorks: function() {
+ var listener = new UpdateListener(
+ chrome.developerPrivate.EventType.UNINSTALLED,
+ function() {
+ assertEquals(null, $(GOOD_EXTENSION_ID));
+ this.nextStep();
+ }.bind(this));
+ chrome.test.runWithUserGesture(function() {
+ chrome.management.uninstall(GOOD_EXTENSION_ID);
+ });
+ },
+};
+
+// Verify that developer mode doesn't change behavior when the number of
+// extensions changes.
+TEST_F('BasicExtensionSettingsWebUITest', 'testDeveloperModeManyExtensions',
+ function() {
+ this.testDeveloperMode();
+});
+
+TEST_F('BasicExtensionSettingsWebUITest', 'testDisable', function() {
+ this.steps = [this.waitForPageLoad, this.verifyDisabledWorks, testDone];
+ this.nextStep();
+});
+
+TEST_F('BasicExtensionSettingsWebUITest', 'testEnable', function() {
+ this.steps = [this.waitForPageLoad,
+ this.verifyDisabledWorks,
+ this.verifyEnabledWorks,
+ testDone];
+ this.nextStep();
+});
+
+TEST_F('BasicExtensionSettingsWebUITest', 'testUninstall', function() {
+ this.steps = [this.waitForPageLoad, this.verifyUninstallWorks, testDone];
+ this.nextStep();
+});
+
+TEST_F('BasicExtensionSettingsWebUITest', 'testNonEmptyExtensionList',
+ function() {
+ var verifyListIsNotHiddenAndEmpty = function() {
+ assertFalse($('extension-list-wrapper').hidden);
+ assertTrue($('no-extensions').hidden);
+ assertGT($('extension-settings-list').childNodes.length, 0);
+ this.nextStep();
+ };
+
+ this.steps = [this.waitForPageLoad, verifyListIsNotHiddenAndEmpty, testDone];
+ this.nextStep();
+});
+
+function AutoScrollExtensionSettingsWebUITest() {}
+
+/**
+ * A variation for testing auto-scroll when an id query param is passed in the
+ * url.
+ * @constructor
+ * @extends {BasicExtensionSettingsWebUITest}
+ */
+AutoScrollExtensionSettingsWebUITest.prototype = {
+ __proto__: BasicExtensionSettingsWebUITest.prototype,
+
+ /** @override */
+ browsePreload: 'chrome://extensions-frame/?id=' + GOOD_EXTENSION_ID,
+
+ /** @override */
+ testGenPreamble: function() {
+ BasicExtensionSettingsWebUITest.prototype.testGenPreamble.call(this);
+ // The window needs to be sufficiently small in order to ensure a scroll bar
+ // is available.
+ GEN(' ShrinkWebContentsView();');
+ },
+};
+
+TEST_F('AutoScrollExtensionSettingsWebUITest', 'testAutoScroll', function() {
+ var checkHasScrollbar = function() {
+ assertGT(document.body.scrollHeight, document.body.clientHeight);
+ this.nextStep();
+ };
+ var checkIsScrolled = function() {
+ assertGT(document.body.scrollTop, 0);
+ this.nextStep();
+ };
+ var checkScrolledToTop = function() {
+ assertEquals(0, document.body.scrollTop);
+ this.nextStep();
+ };
+ var scrollToTop = function() {
+ document.body.scrollTop = 0;
+ this.nextStep();
+ };
+ // Test that a) autoscroll works on first page load and b) updating the
+ // page doesn't result in autoscroll triggering again.
+ this.steps = [this.waitForPageLoad,
+ checkHasScrollbar,
+ checkIsScrolled,
+ scrollToTop,
+ this.enableDeveloperMode,
+ checkScrolledToTop,
+ testDone];
+ this.nextStep();
+});
+
+function ErrorConsoleExtensionSettingsWebUITest() {}
+
+ErrorConsoleExtensionSettingsWebUITest.prototype = {
+ __proto__: ExtensionSettingsWebUITest.prototype,
+
+ /** @override */
+ testGenPreamble: function() {
+ GEN(' EnableErrorConsole();');
+ GEN(' InstallGoodExtension();');
+ GEN(' InstallErrorsExtension();');
+ },
+};
+
+// Flaky on all platforms: http://crbug.com/499884, http://crbug.com/463245.
+TEST_F('ErrorConsoleExtensionSettingsWebUITest',
+ 'DISABLED_testErrorListButtonVisibility', function() {
+ var testButtonVisibility = function() {
+ var extensionList = $('extension-list-wrapper');
+
+ var visibleButtons = extensionList.querySelectorAll(
+ '.errors-link:not([hidden])');
+ expectEquals(1, visibleButtons.length);
+
+ if (visibleButtons.length > 0) {
+ var errorLink = $(ERROR_EXTENSION_ID).querySelector('.errors-link');
+ expectEquals(visibleButtons[0], errorLink);
+
+ var errorIcon = errorLink.querySelector('img');
+ expectTrue(errorIcon.classList.contains('extension-error-warning-icon'));
+ }
+
+ var hiddenButtons = extensionList.querySelectorAll('.errors-link[hidden]');
+ expectEquals(1, hiddenButtons.length);
+
+ this.nextStep();
+ };
+
+ this.steps = [this.waitForPageLoad,
+ this.enableDeveloperMode,
+ testButtonVisibility,
+ testDone];
+ this.nextStep();
+});
+
+/**
+ * TestFixture for extension settings WebUI testing (commands config edition).
+ * @extends {testing.Test}
+ * @constructor
+ */
+function SettingsCommandsExtensionSettingsWebUITest() {}
+
+SettingsCommandsExtensionSettingsWebUITest.prototype = {
+ __proto__: ExtensionSettingsWebUITest.prototype,
+
+ /**
+ * A URL to load before starting each test.
+ * @type {string}
+ * @const
+ */
+ browsePreload: 'chrome://extensions-frame/configureCommands',
+};
+
+TEST_F('SettingsCommandsExtensionSettingsWebUITest', 'testChromeSendHandler',
+ function() {
+ // Just navigating to the page should trigger the chrome.send().
+ var assertOverlayVisible = function() {
+ assertTrue($('extension-commands-overlay').classList.contains('showing'));
+ assertEquals($('extension-commands-overlay').getAttribute('aria-hidden'),
+ 'false');
+ this.nextStep();
+ };
+
+ this.steps = [this.waitForPageLoad, assertOverlayVisible, testDone];
+ this.nextStep();
+});
+
+TEST_F('SettingsCommandsExtensionSettingsWebUITest', 'extensionSettingsUri',
+ function() {
+ var closeCommandOverlay = function() {
+ assertTrue($('extension-commands-overlay').classList.contains('showing'));
+ assertEquals($('extension-commands-overlay').getAttribute('aria-hidden'),
+ 'false');
+ assertEquals(window.location.href,
+ 'chrome://extensions-frame/configureCommands');
+
+ // Close command overlay.
+ $('extension-commands-dismiss').click();
+
+ assertFalse($('extension-commands-overlay').classList.contains('showing'));
+ assertEquals($('extension-commands-overlay').getAttribute('aria-hidden'),
+ 'true');
+ this.nextStep();
+ };
+
+ var checkExtensionsUrl = function() {
+ // After closing the overlay, the URL shouldn't include commands overlay
+ // reference.
+ assertEquals(window.location.href, 'chrome://extensions-frame/');
+ this.nextStep();
+ };
+
+ this.steps = [this.waitForPageLoad,
+ closeCommandOverlay,
+ checkExtensionsUrl,
+ testDone];
+ this.nextStep();
+});
+
+/**
+ * @constructor
+ * @extends {ExtensionSettingsWebUITest}
+ */
+function InstallGoodExtensionSettingsWebUITest() {}
+
+InstallGoodExtensionSettingsWebUITest.prototype = {
+ __proto__: ExtensionSettingsWebUITest.prototype,
+
+ /** @override */
+ testGenPreamble: function() {
+ GEN(' InstallGoodExtension();');
+ },
+
+ emptyTestForAccessibility() {
+ this.steps = [this.waitForPageLoad, testDone];
+ this.nextStep();
+ },
+};
+
+TEST_F('InstallGoodExtensionSettingsWebUITest', 'testAccessibility',
+ function() {
+ this.emptyTestForAccessibility();
+});
+
+TEST_F('InstallGoodExtensionSettingsWebUITest', 'showOptions', function() {
+ var showExtensionOptions = function() {
+ var optionsOverlay = extensions.ExtensionOptionsOverlay.getInstance();
+ optionsOverlay.setExtensionAndShow(GOOD_EXTENSION_ID, 'GOOD!', '',
+ this.nextStep.bind(this));
+
+ // Preferred size changes don't happen in browser tests. Just fake it.
+ document.querySelector('extensionoptions').onpreferredsizechanged(
+ {width: 500, height: 500});
+ };
+
+ this.steps = [this.waitForPageLoad, showExtensionOptions, testDone];
+ this.nextStep();
+});
+
+/**
+ * @constructor
+ * @extends {InstallGoodExtensionSettingsWebUITest}
+ */
+function ManagedExtensionSettingsWebUITest() {}
+
+ManagedExtensionSettingsWebUITest.prototype = {
+ __proto__: InstallGoodExtensionSettingsWebUITest.prototype,
+
+ /** @override */
+ testGenPreamble: function() {
+ GEN(' AddManagedPolicyProvider();');
+ InstallGoodExtensionSettingsWebUITest.prototype.testGenPreamble.call(this);
+ },
+};
+
+TEST_F('ManagedExtensionSettingsWebUITest', 'testAccessibility', function() {
+ this.emptyTestForAccessibility();
+});
+
+/**
+ * @constructor
+ * @extends {InstallGoodExtensionSettingsWebUITest}
+ */
+function OptionsDialogExtensionSettingsWebUITest() {}
+
+OptionsDialogExtensionSettingsWebUITest.prototype = {
+ __proto__: InstallGoodExtensionSettingsWebUITest.prototype,
+
+ /** @override */
+ browsePreload: ExtensionSettingsWebUITest.prototype.browsePreload +
+ '?options=' + GOOD_EXTENSION_ID,
+};
+
+TEST_F('OptionsDialogExtensionSettingsWebUITest', 'testAccessibility',
+ function() {
+ this.emptyTestForAccessibility();
+});
diff --git a/chromium/chrome/browser/ui/webui/extensions/extension_settings_handler.cc b/chromium/chrome/browser/ui/webui/extensions/extension_settings_handler.cc
new file mode 100644
index 00000000000..7ecfc696862
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/extension_settings_handler.cc
@@ -0,0 +1,324 @@
+// 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/ui/webui/extensions/extension_settings_handler.h"
+
+#include <vector>
+
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/extension_service.h"
+#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/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/theme_resources.h"
+#include "components/google/core/browser/google_util.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_set.h"
+#include "extensions/common/extension_urls.h"
+#include "extensions/common/manifest.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+#include "chrome/browser/supervised_user/supervised_user_service.h"
+#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
+#endif
+
+namespace extensions {
+
+ExtensionSettingsHandler::ExtensionSettingsHandler()
+ : extension_service_(nullptr) {
+}
+
+ExtensionSettingsHandler::~ExtensionSettingsHandler() {
+}
+
+// static
+void ExtensionSettingsHandler::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterBooleanPref(
+ prefs::kExtensionsUIDeveloperMode,
+ false,
+ user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+ registry->RegisterBooleanPref(
+ prefs::kExtensionsUIDismissedADTPromo,
+ false,
+ user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+}
+
+void ExtensionSettingsHandler::GetLocalizedValues(
+ content::WebUIDataSource* source) {
+ source->AddString("extensionSettings",
+ l10n_util::GetStringUTF16(IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE));
+
+ source->AddString("extensionSettingsDeveloperMode",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_DEVELOPER_MODE_LINK));
+ source->AddString("extensionSettingsNoExtensions",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_NONE_INSTALLED));
+ source->AddString(
+ "extensionSettingsSuggestGallery",
+ l10n_util::GetStringFUTF16(
+ IDS_EXTENSIONS_NONE_INSTALLED_SUGGEST_GALLERY,
+ base::ASCIIToUTF16(
+ google_util::AppendGoogleLocaleParam(
+ GURL(extension_urls::GetWebstoreExtensionsCategoryURL()),
+ g_browser_process->GetApplicationLocale()).spec())));
+ source->AddString("extensionSettingsGetMoreExtensions",
+ l10n_util::GetStringUTF16(IDS_GET_MORE_EXTENSIONS));
+ source->AddString(
+ "extensionSettingsGetMoreExtensionsUrl",
+ base::ASCIIToUTF16(
+ google_util::AppendGoogleLocaleParam(
+ GURL(extension_urls::GetWebstoreExtensionsCategoryURL()),
+ g_browser_process->GetApplicationLocale()).spec()));
+ source->AddString("extensionSettingsExtensionId",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ID));
+ source->AddString("extensionSettingsExtensionPath",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_PATH));
+ source->AddString("extensionSettingsInspectViews",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSPECT_VIEWS));
+ source->AddString("extensionSettingsInstallWarnings",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALL_WARNINGS));
+ source->AddString("viewIncognito",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_VIEW_INCOGNITO));
+ source->AddString("viewInactive",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_VIEW_INACTIVE));
+ source->AddString("viewIframe",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_VIEW_IFRAME));
+ source->AddString("backgroundPage",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_BACKGROUND_PAGE));
+ source->AddString("extensionSettingsEnable",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE));
+ source->AddString("extensionSettingsEnabled",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLED));
+ source->AddString("extensionSettingsRemove",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_REMOVE));
+ source->AddString("extensionSettingsEnableIncognito",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE_INCOGNITO));
+ source->AddString("extensionSettingsEnableErrorCollection",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE_ERROR_COLLECTION));
+ source->AddString("extensionSettingsAllowFileAccess",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ALLOW_FILE_ACCESS));
+ source->AddString("extensionSettingsAllowOnAllUrls",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ALLOW_ON_ALL_URLS));
+ source->AddString("extensionSettingsIncognitoWarning",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_INCOGNITO_WARNING));
+ source->AddString("extensionSettingsReloadTerminated",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_RELOAD_TERMINATED));
+ source->AddString("extensionSettingsRepairCorrupted",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_REPAIR_CORRUPTED));
+ source->AddString("extensionSettingsLaunch",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_LAUNCH));
+ source->AddString("extensionSettingsReloadUnpacked",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_RELOAD_UNPACKED));
+ source->AddString("extensionSettingsOptions",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_OPTIONS_LINK));
+ if (CanShowAppInfoDialog()) {
+ source->AddString("extensionSettingsPermissions",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_INFO_LINK));
+ } else {
+ source->AddString(
+ "extensionSettingsPermissions",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_PERMISSIONS_LINK));
+ }
+ source->AddString("extensionSettingsVisitWebsite",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_VISIT_WEBSITE));
+ source->AddString("extensionSettingsVisitWebStore",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_VISIT_WEBSTORE));
+ source->AddString("extensionSettingsPolicyControlled",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_POLICY_CONTROLLED));
+ source->AddString("extensionSettingsPolicyRecommeneded",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_POLICY_RECOMMENDED));
+ source->AddString("extensionSettingsDependentExtensions",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_DEPENDENT_EXTENSIONS));
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ const SupervisedUserService* supervised_user_service =
+ SupervisedUserServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()));
+ source->AddString("extensionSettingsSupervisedUser",
+ supervised_user_service->GetExtensionsLockedMessage());
+#endif
+ source->AddString("loading",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOADING));
+ source->AddString("extensionSettingsCorruptInstall",
+ l10n_util::GetStringUTF16(
+ IDS_EXTENSIONS_CORRUPTED_EXTENSION));
+ source->AddString("extensionSettingsSuspiciousInstall",
+ l10n_util::GetStringFUTF16(
+ IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE,
+ l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE)));
+ source->AddString("extensionSettingsLearnMore",
+ l10n_util::GetStringUTF16(IDS_LEARN_MORE));
+ source->AddString("extensionSettingsSuspiciousInstallHelpUrl",
+ base::ASCIIToUTF16(
+ google_util::AppendGoogleLocaleParam(
+ GURL(chrome::kRemoveNonCWSExtensionURL),
+ g_browser_process->GetApplicationLocale()).spec()));
+ source->AddString("extensionSettingsShowButton",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_BUTTON));
+ source->AddString("extensionSettingsLoadUnpackedButton",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_UNPACKED_BUTTON));
+ source->AddString("extensionSettingsPackButton",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_PACK_BUTTON));
+ source->AddString("extensionSettingsCommandsLink",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_COMMANDS_CONFIGURE));
+ source->AddString("extensionSettingsUpdateButton",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_UPDATE_BUTTON));
+ source->AddString("extensionSettingsCrashMessage",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_CRASHED_EXTENSION));
+ source->AddString("extensionSettingsInDevelopment",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_IN_DEVELOPMENT));
+ source->AddString("extensionSettingsWarningsTitle",
+ l10n_util::GetStringUTF16(IDS_EXTENSION_WARNINGS_TITLE));
+ source->AddString("extensionSettingsShowDetails",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_DETAILS));
+ source->AddString("extensionSettingsHideDetails",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_HIDE_DETAILS));
+ source->AddString("extensionSettingsUpdateRequiredBePolicy",
+ l10n_util::GetStringUTF16(
+ IDS_EXTENSIONS_DISABLED_UPDATE_REQUIRED_BY_POLICY));
+
+ source->AddLocalizedString("extensionLogLevelInfo",
+ IDS_EXTENSIONS_LOG_LEVEL_INFO);
+ source->AddLocalizedString("extensionLogLevelWarn",
+ IDS_EXTENSIONS_LOG_LEVEL_WARN);
+ source->AddLocalizedString("extensionLogLevelError",
+ IDS_EXTENSIONS_LOG_LEVEL_ERROR);
+
+ // TODO(estade): comb through the above strings to find ones no longer used in
+ // uber extensions.
+ source->AddString("extensionUninstall",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_UNINSTALL));
+
+ // Pack Extension Overlay:
+ source->AddString("packExtensionOverlay",
+ l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_TITLE));
+ source->AddString("packExtensionHeading",
+ l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_HEADING));
+ source->AddString("packExtensionCommit",
+ l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_BUTTON));
+ source->AddString("ok", l10n_util::GetStringUTF16(IDS_OK));
+ source->AddString("cancel", l10n_util::GetStringUTF16(IDS_CANCEL));
+ source->AddString("packExtensionRootDir",
+ l10n_util::GetStringUTF16(
+ IDS_EXTENSION_PACK_DIALOG_ROOT_DIRECTORY_LABEL));
+ source->AddString("packExtensionPrivateKey",
+ l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_PRIVATE_KEY_LABEL));
+ source->AddString("packExtensionBrowseButton",
+ l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_BROWSE));
+ source->AddString("packExtensionProceedAnyway",
+ l10n_util::GetStringUTF16(IDS_EXTENSION_PROCEED_ANYWAY));
+ source->AddString("packExtensionWarningTitle",
+ l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_WARNING_TITLE));
+ source->AddString("packExtensionErrorTitle",
+ l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_ERROR_TITLE));
+
+ // Extension Error and Extension Error Overlay:
+ source->AddString("extensionErrorHeading",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_HEADING));
+ source->AddString("extensionErrorClearAll",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_CLEAR_ALL));
+ source->AddString("extensionErrorNoErrors",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_NO_ERRORS));
+ source->AddString(
+ "extensionErrorNoErrorsCodeMessage",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_NO_ERRORS_CODE_MESSAGE));
+ source->AddString("extensionErrorOverlayDone",
+ l10n_util::GetStringUTF16(IDS_DONE));
+ source->AddString(
+ "extensionErrorOverlayContextUrl",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_CONTEXT));
+ source->AddString(
+ "extensionErrorOverlayStackTrace",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_STACK_TRACE));
+ source->AddString(
+ "extensionErrorOverlayAnonymousFunction",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_ANONYMOUS_FUNCTION));
+ source->AddString(
+ "extensionErrorOverlayLaunchDevtools",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_LAUNCH_DEVTOOLS));
+ source->AddString(
+ "extensionErrorOverlayContextUnknown",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_CONTEXT_UNKNOWN));
+ source->AddString(
+ "extensionErrorOverlayNoCodeToDisplay",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_ERROR_NO_CODE_TO_DISPLAY));
+
+ // Extension Commands Overlay:
+ source->AddString("extensionCommandsOverlay",
+ l10n_util::GetStringUTF16(IDS_EXTENSION_COMMANDS_DIALOG_TITLE));
+ source->AddString("extensionCommandsEmpty",
+ l10n_util::GetStringUTF16(IDS_EXTENSION_COMMANDS_EMPTY));
+ source->AddString("extensionCommandsInactive",
+ l10n_util::GetStringUTF16(IDS_EXTENSION_COMMANDS_INACTIVE));
+ source->AddString("extensionCommandsStartTyping",
+ l10n_util::GetStringUTF16(IDS_EXTENSION_TYPE_SHORTCUT));
+ source->AddString("extensionCommandsDelete",
+ l10n_util::GetStringUTF16(IDS_EXTENSION_DELETE_SHORTCUT));
+ source->AddString("extensionCommandsGlobal",
+ l10n_util::GetStringUTF16(IDS_EXTENSION_COMMANDS_GLOBAL));
+ source->AddString("extensionCommandsRegular",
+ l10n_util::GetStringUTF16(IDS_EXTENSION_COMMANDS_NOT_GLOBAL));
+ source->AddString("ok", l10n_util::GetStringUTF16(IDS_OK));
+
+ // 'Bubble' text for the controlled-setting-indicator
+ source->AddString(
+ "extensionControlledSettingPolicy",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_POLICY));
+}
+
+void ExtensionSettingsHandler::DidStartNavigation(
+ content::NavigationHandle* navigation_handle) {
+ if (!navigation_handle->IsInMainFrame())
+ return;
+
+ if (navigation_handle->GetReloadType() != content::ReloadType::NONE)
+ ReloadUnpackedExtensions();
+}
+
+void ExtensionSettingsHandler::RegisterMessages() {
+ Profile* profile = Profile::FromWebUI(web_ui())->GetOriginalProfile();
+ extension_service_ =
+ extensions::ExtensionSystem::Get(profile)->extension_service();
+ // Clear the preference for the ADT Promo before fully removing it.
+ // TODO(devlin): Take this out when everyone's been updated.
+ Profile::FromWebUI(web_ui())->GetPrefs()->ClearPref(
+ prefs::kExtensionsUIDismissedADTPromo);
+
+ content::WebContentsObserver::Observe(web_ui()->GetWebContents());
+}
+
+void ExtensionSettingsHandler::ReloadUnpackedExtensions() {
+ ExtensionRegistry* registry =
+ ExtensionRegistry::Get(extension_service_->profile());
+ std::vector<const Extension*> unpacked_extensions;
+ for (const scoped_refptr<const extensions::Extension>& extension :
+ registry->enabled_extensions()) {
+ if (Manifest::IsUnpackedLocation(extension->location()))
+ unpacked_extensions.push_back(extension.get());
+ }
+
+ for (std::vector<const Extension*>::iterator iter =
+ unpacked_extensions.begin(); iter != unpacked_extensions.end(); ++iter) {
+ extension_service_->ReloadExtensionWithQuietFailure((*iter)->id());
+ }
+}
+
+} // namespace extensions
diff --git a/chromium/chrome/browser/ui/webui/extensions/extension_settings_handler.h b/chromium/chrome/browser/ui/webui/extensions/extension_settings_handler.h
new file mode 100644
index 00000000000..40f4982d600
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/extension_settings_handler.h
@@ -0,0 +1,57 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_HANDLER_H_
+
+#include "base/macros.h"
+#include "content/public/browser/reload_type.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+class ExtensionService;
+
+namespace content {
+class WebUIDataSource;
+}
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+namespace extensions {
+
+// Extension Settings UI handler.
+class ExtensionSettingsHandler : public content::WebUIMessageHandler,
+ public content::WebContentsObserver {
+ public:
+ ExtensionSettingsHandler();
+ ~ExtensionSettingsHandler() override;
+
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ // Note: This uses |web_ui()| from |WebUIMessageHandler|, so it must only be
+ // called after |web_ui->AddMessageHandler(this)| has been called.
+ void GetLocalizedValues(content::WebUIDataSource* source);
+
+ private:
+ // WebContentsObserver implementation.
+ void DidStartNavigation(
+ content::NavigationHandle* navigation_handle) override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // Helper method that reloads all unpacked extensions.
+ void ReloadUnpackedExtensions();
+
+ // Our model. Outlives us since it's owned by our containing profile.
+ ExtensionService* extension_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionSettingsHandler);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSION_SETTINGS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/extensions/extensions_ui.cc b/chromium/chrome/browser/ui/webui/extensions/extensions_ui.cc
new file mode 100644
index 00000000000..7ae6d563e79
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/extensions_ui.cc
@@ -0,0 +1,393 @@
+// 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/ui/webui/extensions/extensions_ui.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/timer/elapsed_timer.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/extensions/extension_loader_handler.h"
+#include "chrome/browser/ui/webui/extensions/extension_settings_handler.h"
+#include "chrome/browser/ui/webui/extensions/install_extension_handler.h"
+#include "chrome/browser/ui/webui/metrics_handler.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/theme_resources.h"
+#include "components/google/core/browser/google_util.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "extensions/common/extension_urls.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
+#include "chrome/browser/ui/webui/extensions/chromeos/kiosk_apps_handler.h"
+#endif
+
+namespace extensions {
+
+namespace {
+
+class ExtensionWebUiTimer : public content::WebContentsObserver {
+ public:
+ explicit ExtensionWebUiTimer(content::WebContents* web_contents, bool is_md)
+ : content::WebContentsObserver(web_contents), is_md_(is_md) {}
+ ~ExtensionWebUiTimer() override {}
+
+ void DidStartNavigation(
+ content::NavigationHandle* navigation_handle) override {
+ if (navigation_handle->IsInMainFrame() &&
+ !navigation_handle->IsSameDocument()) {
+ timer_.reset(new base::ElapsedTimer());
+ }
+ }
+
+ void DocumentLoadedInFrame(
+ content::RenderFrameHost* render_frame_host) override {
+ if (render_frame_host != web_contents()->GetMainFrame() ||
+ !timer_) { // See comment in DocumentOnLoadCompletedInMainFrame()
+ return;
+ }
+ if (is_md_) {
+ UMA_HISTOGRAM_TIMES("Extensions.WebUi.DocumentLoadedInMainFrameTime.MD",
+ timer_->Elapsed());
+ } else {
+ UMA_HISTOGRAM_TIMES("Extensions.WebUi.DocumentLoadedInMainFrameTime.Uber",
+ timer_->Elapsed());
+ }
+ }
+
+ void DocumentOnLoadCompletedInMainFrame() override {
+ // TODO(devlin): The usefulness of these metrics remains to be seen.
+ if (!timer_) {
+ // This object could have been created for a child RenderFrameHost so it
+ // would never get a DidStartNavigation with the main frame. However it
+ // will receive this current callback.
+ return;
+ }
+ if (is_md_) {
+ UMA_HISTOGRAM_TIMES("Extensions.WebUi.LoadCompletedInMainFrame.MD",
+ timer_->Elapsed());
+ } else {
+ UMA_HISTOGRAM_TIMES("Extensions.WebUi.LoadCompletedInMainFrame.Uber",
+ timer_->Elapsed());
+ }
+ timer_.reset();
+ }
+
+ void WebContentsDestroyed() override { delete this; }
+
+ private:
+ // Whether this is the MD version of the chrome://extensions page.
+ bool is_md_;
+
+ std::unique_ptr<base::ElapsedTimer> timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionWebUiTimer);
+};
+
+content::WebUIDataSource* CreateMdExtensionsSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIExtensionsHost);
+
+ source->SetJsonPath("strings.js");
+
+ source->AddLocalizedString("title",
+ IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE);
+ source->AddLocalizedString("toolbarTitle", IDS_MD_EXTENSIONS_TOOLBAR_TITLE);
+ source->AddLocalizedString("search", IDS_MD_EXTENSIONS_SEARCH);
+ // TODO(dpapad): Use a single merged string resource for "Clear search".
+ source->AddLocalizedString("clearSearch", IDS_DOWNLOAD_CLEAR_SEARCH);
+ source->AddLocalizedString("sidebarApps", IDS_MD_EXTENSIONS_SIDEBAR_APPS);
+ source->AddLocalizedString("sidebarExtensions",
+ IDS_MD_EXTENSIONS_SIDEBAR_EXTENSIONS);
+ source->AddLocalizedString("dropToInstall",
+ IDS_EXTENSIONS_INSTALL_DROP_TARGET);
+ source->AddLocalizedString("errorsPageHeading",
+ IDS_MD_EXTENSIONS_ERROR_PAGE_HEADING);
+ source->AddLocalizedString("getMoreExtensions",
+ IDS_MD_EXTENSIONS_SIDEBAR_GET_MORE_EXTENSIONS);
+ source->AddLocalizedString("keyboardShortcuts",
+ IDS_MD_EXTENSIONS_SIDEBAR_KEYBOARD_SHORTCUTS);
+ source->AddLocalizedString("itemId", IDS_MD_EXTENSIONS_ITEM_ID);
+ source->AddLocalizedString("itemInspectViews",
+ IDS_MD_EXTENSIONS_ITEM_INSPECT_VIEWS);
+ // NOTE: This text reads "<n> more". It's possible that it should be using
+ // a plural string instead. Unfortunately, this is non-trivial since we don't
+ // expose that capability to JS yet. Since we don't know it's a problem, use
+ // a simple placeholder for now.
+ source->AddLocalizedString("itemInspectViewsExtra",
+ IDS_MD_EXTENSIONS_ITEM_INSPECT_VIEWS_EXTRA);
+ source->AddLocalizedString("itemAllowIncognito",
+ IDS_MD_EXTENSIONS_ITEM_ALLOW_INCOGNITO);
+ source->AddLocalizedString("itemDescriptionLabel",
+ IDS_MD_EXTENSIONS_ITEM_DESCRIPTION);
+ source->AddLocalizedString("itemDependencies",
+ IDS_MD_EXTENSIONS_ITEM_DEPENDENCIES);
+ source->AddLocalizedString("itemDependentEntry",
+ IDS_MD_EXTENSIONS_DEPENDENT_ENTRY);
+ source->AddLocalizedString("itemDetails", IDS_MD_EXTENSIONS_ITEM_DETAILS);
+ source->AddLocalizedString("itemErrors", IDS_MD_EXTENSIONS_ITEM_ERRORS);
+ source->AddLocalizedString("itemIdHeading",
+ IDS_MD_EXTENSIONS_ITEM_ID_HEADING);
+ source->AddLocalizedString("itemOff", IDS_MD_EXTENSIONS_ITEM_OFF);
+ source->AddLocalizedString("itemOn", IDS_MD_EXTENSIONS_ITEM_ON);
+ source->AddLocalizedString("itemOptions", IDS_MD_EXTENSIONS_ITEM_OPTIONS);
+ source->AddLocalizedString("itemPermissions",
+ IDS_MD_EXTENSIONS_ITEM_PERMISSIONS);
+ source->AddLocalizedString("itemPermissionsEmpty",
+ IDS_MD_EXTENSIONS_ITEM_PERMISSIONS_EMPTY);
+ source->AddLocalizedString("itemRemove", IDS_MD_EXTENSIONS_ITEM_REMOVE);
+ source->AddLocalizedString("itemRemoveExtension",
+ IDS_MD_EXTENSIONS_ITEM_REMOVE_EXTENSION);
+ source->AddLocalizedString("itemSource",
+ IDS_MD_EXTENSIONS_ITEM_SOURCE);
+ source->AddLocalizedString("itemSourcePolicy",
+ IDS_MD_EXTENSIONS_ITEM_SOURCE_POLICY);
+ source->AddLocalizedString("itemSourceSideloaded",
+ IDS_MD_EXTENSIONS_ITEM_SOURCE_SIDELOADED);
+ source->AddLocalizedString("itemSourceUnpacked",
+ IDS_MD_EXTENSIONS_ITEM_SOURCE_UNPACKED);
+ source->AddLocalizedString("itemSourceWebstore",
+ IDS_MD_EXTENSIONS_ITEM_SOURCE_WEBSTORE);
+ source->AddLocalizedString("itemVersion",
+ IDS_MD_EXTENSIONS_ITEM_VERSION);
+ source->AddLocalizedString("itemAllowOnFileUrls",
+ IDS_EXTENSIONS_ALLOW_FILE_ACCESS);
+ source->AddLocalizedString("itemAllowOnAllSites",
+ IDS_EXTENSIONS_ALLOW_ON_ALL_URLS);
+ source->AddLocalizedString("itemCollectErrors",
+ IDS_EXTENSIONS_ENABLE_ERROR_COLLECTION);
+ source->AddLocalizedString("itemCorruptInstall",
+ IDS_EXTENSIONS_CORRUPTED_EXTENSION);
+ source->AddLocalizedString("itemRepair", IDS_EXTENSIONS_REPAIR_CORRUPTED);
+ source->AddLocalizedString("itemReload", IDS_EXTENSIONS_RELOAD_TERMINATED);
+ source->AddString(
+ "itemSuspiciousInstall",
+ l10n_util::GetStringFUTF16(
+ IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE,
+ l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE)));
+ source->AddLocalizedString(
+ "loadErrorCouldNotLoadManifest",
+ IDS_MD_EXTENSIONS_LOAD_ERROR_COULD_NOT_LOAD_MANIFEST);
+ source->AddLocalizedString("loadErrorHeading",
+ IDS_MD_EXTENSIONS_LOAD_ERROR_HEADING);
+ source->AddLocalizedString("loadErrorFileLabel",
+ IDS_MD_EXTENSIONS_LOAD_ERROR_FILE_LABEL);
+ source->AddLocalizedString("loadErrorErrorLabel",
+ IDS_MD_EXTENSIONS_LOAD_ERROR_ERROR_LABEL);
+ source->AddLocalizedString("loadErrorCancel",
+ IDS_MD_EXTENSIONS_LOAD_ERROR_CANCEL);
+ source->AddLocalizedString("loadErrorRetry",
+ IDS_MD_EXTENSIONS_LOAD_ERROR_RETRY);
+ source->AddLocalizedString("noErrorsToShow",
+ IDS_EXTENSIONS_ERROR_NO_ERRORS_CODE_MESSAGE);
+ source->AddLocalizedString("packDialogTitle",
+ IDS_MD_EXTENSIONS_PACK_DIALOG_TITLE);
+ source->AddLocalizedString("packDialogBrowse",
+ IDS_MD_EXTENSIONS_PACK_DIALOG_BROWSE_BUTTON);
+ source->AddLocalizedString(
+ "packDialogExtensionRoot",
+ IDS_MD_EXTENSIONS_PACK_DIALOG_EXTENSION_ROOT_LABEL);
+ source->AddLocalizedString("packDialogKeyFile",
+ IDS_MD_EXTENSIONS_PACK_DIALOG_KEY_FILE_LABEL);
+ source->AddLocalizedString("packDialogContent",
+ IDS_EXTENSION_PACK_DIALOG_HEADING);
+ source->AddLocalizedString("packDialogCancel",
+ IDS_MD_EXTENSIONS_PACK_DIALOG_CANCEL_BUTTON);
+ source->AddLocalizedString("packDialogConfirm",
+ IDS_MD_EXTENSIONS_PACK_DIALOG_CONFIRM_BUTTON);
+ source->AddLocalizedString("shortcutNotSet",
+ IDS_MD_EXTENSIONS_SHORTCUT_NOT_SET);
+ source->AddLocalizedString("shortcutScopeGlobal",
+ IDS_MD_EXTENSIONS_SHORTCUT_SCOPE_GLOBAL);
+ source->AddLocalizedString("shortcutScopeLabel",
+ IDS_MD_EXTENSIONS_SHORTCUT_SCOPE_LABEL);
+ source->AddLocalizedString("shortcutScopeInChrome",
+ IDS_MD_EXTENSIONS_SHORTCUT_SCOPE_IN_CHROME);
+ source->AddLocalizedString("shortcutTypeAShortcut",
+ IDS_MD_EXTENSIONS_TYPE_A_SHORTCUT);
+ source->AddLocalizedString("toolbarDevMode",
+ IDS_MD_EXTENSIONS_DEVELOPER_MODE);
+ source->AddLocalizedString("toolbarLoadUnpacked",
+ IDS_MD_EXTENSIONS_TOOLBAR_LOAD_UNPACKED);
+ source->AddLocalizedString("toolbarPack", IDS_MD_EXTENSIONS_TOOLBAR_PACK);
+ source->AddLocalizedString("toolbarUpdateNow",
+ IDS_MD_EXTENSIONS_TOOLBAR_UPDATE_NOW);
+ source->AddLocalizedString("viewBackgroundPage",
+ IDS_EXTENSIONS_BACKGROUND_PAGE);
+ source->AddLocalizedString("viewIncognito",
+ IDS_EXTENSIONS_VIEW_INCOGNITO);
+ source->AddLocalizedString("viewInactive",
+ IDS_EXTENSIONS_VIEW_INACTIVE);
+ source->AddLocalizedString("viewIframe",
+ IDS_EXTENSIONS_VIEW_IFRAME);
+ source->AddString(
+ "getMoreExtensionsUrl",
+ base::ASCIIToUTF16(
+ google_util::AppendGoogleLocaleParam(
+ GURL(extension_urls::GetWebstoreExtensionsCategoryURL()),
+ g_browser_process->GetApplicationLocale()).spec()));
+
+ source->AddResourcePath("animation_helper.html",
+ IDR_MD_EXTENSIONS_ANIMATION_HELPER_HTML);
+ source->AddResourcePath("animation_helper.js",
+ IDR_MD_EXTENSIONS_ANIMATION_HELPER_JS);
+ source->AddResourcePath("code_section.html",
+ IDR_MD_EXTENSIONS_CODE_SECTION_HTML);
+ source->AddResourcePath("code_section.js", IDR_MD_EXTENSIONS_CODE_SECTION_JS);
+ source->AddResourcePath("extensions.js", IDR_MD_EXTENSIONS_EXTENSIONS_JS);
+ source->AddResourcePath("drag_and_drop_handler.html",
+ IDR_EXTENSIONS_DRAG_AND_DROP_HANDLER_HTML);
+ source->AddResourcePath("drag_and_drop_handler.js",
+ IDR_EXTENSIONS_DRAG_AND_DROP_HANDLER_JS);
+ source->AddResourcePath("detail_view.html",
+ IDR_MD_EXTENSIONS_DETAIL_VIEW_HTML);
+ source->AddResourcePath("detail_view.js", IDR_MD_EXTENSIONS_DETAIL_VIEW_JS);
+ source->AddResourcePath("drop_overlay.html",
+ IDR_MD_EXTENSIONS_DROP_OVERLAY_HTML);
+ source->AddResourcePath("drop_overlay.js", IDR_MD_EXTENSIONS_DROP_OVERLAY_JS);
+ source->AddResourcePath("error_page.html", IDR_MD_EXTENSIONS_ERROR_PAGE_HTML);
+ source->AddResourcePath("error_page.js", IDR_MD_EXTENSIONS_ERROR_PAGE_JS);
+ source->AddResourcePath("keyboard_shortcuts.html",
+ IDR_MD_EXTENSIONS_KEYBOARD_SHORTCUTS_HTML);
+ source->AddResourcePath("keyboard_shortcuts.js",
+ IDR_MD_EXTENSIONS_KEYBOARD_SHORTCUTS_JS);
+ source->AddResourcePath("manager.html", IDR_MD_EXTENSIONS_MANAGER_HTML);
+ source->AddResourcePath("manager.js", IDR_MD_EXTENSIONS_MANAGER_JS);
+ source->AddResourcePath("icons.html", IDR_MD_EXTENSIONS_ICONS_HTML);
+ source->AddResourcePath("item.html", IDR_MD_EXTENSIONS_ITEM_HTML);
+ source->AddResourcePath("item.js", IDR_MD_EXTENSIONS_ITEM_JS);
+ source->AddResourcePath("item_list.html", IDR_MD_EXTENSIONS_ITEM_LIST_HTML);
+ source->AddResourcePath("item_list.js", IDR_MD_EXTENSIONS_ITEM_LIST_JS);
+ source->AddResourcePath("item_util.html", IDR_MD_EXTENSIONS_ITEM_UTIL_HTML);
+ source->AddResourcePath("item_util.js", IDR_MD_EXTENSIONS_ITEM_UTIL_JS);
+ source->AddResourcePath("load_error.html", IDR_MD_EXTENSIONS_LOAD_ERROR_HTML);
+ source->AddResourcePath("load_error.js", IDR_MD_EXTENSIONS_LOAD_ERROR_JS);
+ source->AddResourcePath("navigation_helper.html",
+ IDR_MD_EXTENSIONS_NAVIGATION_HELPER_HTML);
+ source->AddResourcePath("navigation_helper.js",
+ IDR_MD_EXTENSIONS_NAVIGATION_HELPER_JS);
+ source->AddResourcePath("options_dialog.html",
+ IDR_MD_EXTENSIONS_OPTIONS_DIALOG_HTML);
+ source->AddResourcePath("options_dialog.js",
+ IDR_MD_EXTENSIONS_OPTIONS_DIALOG_JS);
+ source->AddResourcePath("pack_dialog.html",
+ IDR_MD_EXTENSIONS_PACK_DIALOG_HTML);
+ source->AddResourcePath("pack_dialog.js", IDR_MD_EXTENSIONS_PACK_DIALOG_JS);
+ source->AddResourcePath("service.html", IDR_MD_EXTENSIONS_SERVICE_HTML);
+ source->AddResourcePath("service.js", IDR_MD_EXTENSIONS_SERVICE_JS);
+ source->AddResourcePath("shortcut_input.html",
+ IDR_MD_EXTENSIONS_SHORTCUT_INPUT_HTML);
+ source->AddResourcePath("shortcut_input.js",
+ IDR_MD_EXTENSIONS_SHORTCUT_INPUT_JS);
+ source->AddResourcePath("shortcut_util.html",
+ IDR_EXTENSIONS_SHORTCUT_UTIL_HTML);
+ source->AddResourcePath("shortcut_util.js", IDR_EXTENSIONS_SHORTCUT_UTIL_JS);
+ source->AddResourcePath("sidebar.html", IDR_MD_EXTENSIONS_SIDEBAR_HTML);
+ source->AddResourcePath("sidebar.js", IDR_MD_EXTENSIONS_SIDEBAR_JS);
+ source->AddResourcePath("strings.html", IDR_MD_EXTENSIONS_STRINGS_HTML);
+ source->AddResourcePath("toolbar.html", IDR_MD_EXTENSIONS_TOOLBAR_HTML);
+ source->AddResourcePath("toolbar.js", IDR_MD_EXTENSIONS_TOOLBAR_JS);
+ source->SetDefaultResource(IDR_MD_EXTENSIONS_EXTENSIONS_HTML);
+
+ return source;
+}
+
+content::WebUIDataSource* CreateExtensionsHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIExtensionsFrameHost);
+
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("extensions.js", IDR_EXTENSIONS_JS);
+ source->AddResourcePath("extension_command_list.js",
+ IDR_EXTENSION_COMMAND_LIST_JS);
+ source->AddResourcePath("extension_list.js", IDR_EXTENSION_LIST_JS);
+ source->SetDefaultResource(IDR_EXTENSIONS_HTML);
+ source->DisableDenyXFrameOptions();
+ return source;
+}
+
+} // namespace
+
+ExtensionsUI::ExtensionsUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource* source = nullptr;
+
+ bool is_md =
+ base::FeatureList::IsEnabled(features::kMaterialDesignExtensions);
+
+ if (is_md) {
+ source = CreateMdExtensionsSource();
+ auto install_extension_handler =
+ base::MakeUnique<InstallExtensionHandler>();
+ InstallExtensionHandler* handler = install_extension_handler.get();
+ web_ui->AddMessageHandler(std::move(install_extension_handler));
+ handler->GetLocalizedValues(source);
+ } else {
+ source = CreateExtensionsHTMLSource();
+
+ auto extension_settings_handler =
+ base::MakeUnique<ExtensionSettingsHandler>();
+ ExtensionSettingsHandler* settings_handler =
+ extension_settings_handler.get();
+ web_ui->AddMessageHandler(std::move(extension_settings_handler));
+ settings_handler->GetLocalizedValues(source);
+
+ auto extension_loader_handler =
+ base::MakeUnique<ExtensionLoaderHandler>(profile);
+ ExtensionLoaderHandler* loader_handler = extension_loader_handler.get();
+ web_ui->AddMessageHandler(std::move(extension_loader_handler));
+ loader_handler->GetLocalizedValues(source);
+
+ auto install_extension_handler =
+ base::MakeUnique<InstallExtensionHandler>();
+ InstallExtensionHandler* install_handler = install_extension_handler.get();
+ web_ui->AddMessageHandler(std::move(install_extension_handler));
+ install_handler->GetLocalizedValues(source);
+
+#if defined(OS_CHROMEOS)
+ auto kiosk_app_handler = base::MakeUnique<chromeos::KioskAppsHandler>(
+ chromeos::OwnerSettingsServiceChromeOSFactory::GetForBrowserContext(
+ profile));
+ chromeos::KioskAppsHandler* kiosk_handler = kiosk_app_handler.get();
+ web_ui->AddMessageHandler(std::move(kiosk_app_handler));
+ kiosk_handler->GetLocalizedValues(source);
+#endif
+
+ web_ui->AddMessageHandler(base::MakeUnique<MetricsHandler>());
+ }
+
+ // Need to allow <object> elements so that the <extensionoptions> browser
+ // plugin can be loaded within chrome://extensions.
+ source->OverrideContentSecurityPolicyObjectSrc("object-src 'self';");
+
+ content::WebUIDataSource::Add(profile, source);
+ // Handles its own lifetime.
+ new ExtensionWebUiTimer(web_ui->GetWebContents(), is_md);
+}
+
+ExtensionsUI::~ExtensionsUI() {}
+
+// static
+base::RefCountedMemory* ExtensionsUI::GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor) {
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+ return rb.LoadDataResourceBytesForScale(IDR_EXTENSIONS_FAVICON, scale_factor);
+}
+
+} // namespace extensions
diff --git a/chromium/chrome/browser/ui/webui/extensions/extensions_ui.h b/chromium/chrome/browser/ui/webui/extensions/extensions_ui.h
new file mode 100644
index 00000000000..ebc86e45546
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/extensions_ui.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2011 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_EXTENSIONS_EXTENSIONS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSIONS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "ui/base/layout.h"
+
+namespace base {
+class RefCountedMemory;
+}
+
+namespace extensions {
+
+class ExtensionsUI : public content::WebUIController {
+ public:
+ explicit ExtensionsUI(content::WebUI* web_ui);
+ ~ExtensionsUI() override;
+
+ static base::RefCountedMemory* GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ExtensionsUI);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_EXTENSIONS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/extensions/install_extension_handler.cc b/chromium/chrome/browser/ui/webui/extensions/install_extension_handler.cc
new file mode 100644
index 00000000000..5395f3270da
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/install_extension_handler.cc
@@ -0,0 +1,137 @@
+// 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/ui/webui/extensions/install_extension_handler.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.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"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/common/drop_data.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/feature_switch.h"
+#include "net/base/filename_util.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace extensions {
+
+InstallExtensionHandler::InstallExtensionHandler() {
+}
+
+InstallExtensionHandler::~InstallExtensionHandler() {
+}
+
+void InstallExtensionHandler::GetLocalizedValues(
+ content::WebUIDataSource* source) {
+ source->AddString(
+ "extensionSettingsInstallDropTarget",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALL_DROP_TARGET));
+}
+
+void InstallExtensionHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "startDrag",
+ base::Bind(&InstallExtensionHandler::HandleStartDragMessage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "stopDrag",
+ base::Bind(&InstallExtensionHandler::HandleStopDragMessage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "installDroppedFile",
+ base::Bind(&InstallExtensionHandler::HandleInstallMessage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "installDroppedDirectory",
+ base::Bind(&InstallExtensionHandler::HandleInstallDirectoryMessage,
+ base::Unretained(this)));
+}
+
+void InstallExtensionHandler::HandleStartDragMessage(
+ const base::ListValue* args) {
+ content::DropData* drop_data =
+ web_ui()->GetWebContents()->GetDropData();
+ if (!drop_data) {
+ DLOG(ERROR) << "No current drop data.";
+ return;
+ }
+
+ if (drop_data->filenames.empty()) {
+ DLOG(ERROR) << "Current drop data contains no files.";
+ return;
+ }
+
+ const ui::FileInfo& file_info = drop_data->filenames.front();
+
+ file_to_install_ = file_info.path;
+ // Use the display name if provided, for checking file names
+ // (.path is likely a random hash value in that case).
+ file_display_name_ =
+ file_info.display_name.empty() ? file_info.path : file_info.display_name;
+}
+
+void InstallExtensionHandler::HandleStopDragMessage(
+ const base::ListValue* args) {
+ file_to_install_.clear();
+ file_display_name_.clear();
+}
+
+void InstallExtensionHandler::HandleInstallMessage(
+ const base::ListValue* args) {
+ if (file_to_install_.empty()) {
+ LOG(ERROR) << "No file captured to install.";
+ return;
+ }
+
+ Profile* profile = Profile::FromBrowserContext(
+ web_ui()->GetWebContents()->GetBrowserContext());
+
+ if (file_display_name_.MatchesExtension(FILE_PATH_LITERAL(".zip"))) {
+ ZipFileInstaller::Create(ExtensionSystem::Get(profile)->extension_service())
+ ->LoadFromZipFile(file_to_install_);
+ } else {
+ std::unique_ptr<ExtensionInstallPrompt> prompt(
+ new ExtensionInstallPrompt(web_ui()->GetWebContents()));
+ scoped_refptr<CrxInstaller> crx_installer(CrxInstaller::Create(
+ ExtensionSystem::Get(profile)->extension_service(), std::move(prompt)));
+ crx_installer->set_error_on_unsupported_requirements(true);
+ crx_installer->set_off_store_install_allow_reason(
+ CrxInstaller::OffStoreInstallAllowedFromSettingsPage);
+ crx_installer->set_install_immediately(true);
+
+ if (file_display_name_.MatchesExtension(FILE_PATH_LITERAL(".user.js"))) {
+ crx_installer->InstallUserScript(
+ file_to_install_, net::FilePathToFileURL(file_to_install_));
+ } else if (file_display_name_.MatchesExtension(FILE_PATH_LITERAL(".crx"))) {
+ crx_installer->InstallCrx(file_to_install_);
+ } else {
+ CHECK(false);
+ }
+ }
+
+ file_to_install_.clear();
+ file_display_name_.clear();
+}
+
+void InstallExtensionHandler::HandleInstallDirectoryMessage(
+ const base::ListValue* args) {
+ Profile* profile = Profile::FromBrowserContext(
+ web_ui()->GetWebContents()->GetBrowserContext());
+ UnpackedInstaller::Create(
+ ExtensionSystem::Get(profile)->
+ extension_service())->Load(file_to_install_);
+}
+
+} // namespace extensions
diff --git a/chromium/chrome/browser/ui/webui/extensions/install_extension_handler.h b/chromium/chrome/browser/ui/webui/extensions/install_extension_handler.h
new file mode 100644
index 00000000000..265218b6ee0
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/extensions/install_extension_handler.h
@@ -0,0 +1,63 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_EXTENSIONS_INSTALL_EXTENSION_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_EXTENSIONS_INSTALL_EXTENSION_HANDLER_H_
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "base/values.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace content {
+class WebUIDataSource;
+}
+
+namespace extensions {
+
+// Handles installing an extension when its file is dragged onto the page.
+class InstallExtensionHandler : public content::WebUIMessageHandler {
+ public:
+ InstallExtensionHandler();
+ ~InstallExtensionHandler() override;
+
+ void GetLocalizedValues(content::WebUIDataSource* source);
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ // Handles a notification from the JavaScript that a drag has started. This is
+ // needed so that we can capture the file being dragged. If we wait until
+ // we receive a drop notification, the drop data in the browser process will
+ // have already been destroyed.
+ void HandleStartDragMessage(const base::ListValue* args);
+
+ // Handles a notification from the JavaScript that a drag has stopped.
+ void HandleStopDragMessage(const base::ListValue* args);
+
+ // Handles a notification from the JavaScript to install the file currently
+ // being dragged.
+ //
+ // IMPORTANT: We purposefully do not allow the JavaScript to specify the file
+ // to be installed as a precaution against the extension management page
+ // getting XSS'd.
+ void HandleInstallMessage(const base::ListValue* args);
+
+ // Handles a notification from the JavaScript to install the directory
+ // currently being dragged.
+ void HandleInstallDirectoryMessage(const base::ListValue* args);
+
+ // The path to the file that will be installed when HandleInstallMessage() is
+ // called.
+ base::FilePath file_to_install_;
+ base::FilePath file_display_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(InstallExtensionHandler);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_INSTALL_EXTENSION_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/fallback_icon_source.cc b/chromium/chrome/browser/ui/webui/fallback_icon_source.cc
new file mode 100644
index 00000000000..7fbe9f32a12
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/fallback_icon_source.cc
@@ -0,0 +1,99 @@
+// 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.
+
+#include "chrome/browser/ui/webui/fallback_icon_source.h"
+
+#include <vector>
+
+#include "base/memory/ref_counted_memory.h"
+#include "chrome/browser/search/instant_io_context.h"
+#include "chrome/common/url_constants.h"
+#include "components/favicon/core/fallback_icon_service.h"
+#include "components/favicon_base/fallback_icon_url_parser.h"
+#include "components/keyed_service/core/service_access_type.h"
+#include "net/url_request/url_request.h"
+#include "ui/gfx/favicon_size.h"
+#include "url/gurl.h"
+
+FallbackIconSource::FallbackIconSource(
+ favicon::FallbackIconService* fallback_icon_service)
+ : fallback_icon_service_(fallback_icon_service) {
+}
+
+FallbackIconSource::~FallbackIconSource() {
+}
+
+std::string FallbackIconSource::GetSource() const {
+ return chrome::kChromeUIFallbackIconHost;
+}
+
+void FallbackIconSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ chrome::ParsedFallbackIconPath parsed;
+ bool success = parsed.Parse(path);
+ if (!success) {
+ SendDefaultResponse(callback);
+ return;
+ }
+
+ GURL url(parsed.url_string());
+ if (url.is_empty() || !url.is_valid()) {
+ SendDefaultResponse(callback);
+ return;
+ }
+
+ SendFallbackIconHelper(
+ url, parsed.size_in_pixels(), parsed.style(), callback);
+}
+
+std::string FallbackIconSource::GetMimeType(const std::string&) const {
+ // We need to explicitly return a mime type, otherwise if the user tries to
+ // drag the image they get no extension.
+ return "image/png";
+}
+
+bool FallbackIconSource::AllowCaching() const {
+ return false;
+}
+
+bool FallbackIconSource::ShouldReplaceExistingSource() const {
+ // Leave the existing DataSource in place, otherwise we'll drop any pending
+ // requests on the floor.
+ return false;
+}
+
+bool FallbackIconSource::ShouldServiceRequest(
+ const GURL& url,
+ content::ResourceContext* resource_context,
+ int render_process_id) const {
+ if (url.SchemeIs(chrome::kChromeSearchScheme)) {
+ return InstantIOContext::ShouldServiceRequest(url, resource_context,
+ render_process_id);
+ }
+ return URLDataSource::ShouldServiceRequest(url, resource_context,
+ render_process_id);
+}
+
+void FallbackIconSource::SendFallbackIconHelper(
+ const GURL& url,
+ int size_in_pixels,
+ const favicon_base::FallbackIconStyle& style,
+ const content::URLDataSource::GotDataCallback& callback) {
+ if (!fallback_icon_service_) { // Can be null for tests.
+ callback.Run(nullptr); // Trigger "Not Found" response.
+ return;
+ }
+ std::vector<unsigned char> bitmap_data =
+ fallback_icon_service_->RenderFallbackIconBitmap(
+ url, size_in_pixels, style);
+ callback.Run(base::RefCountedBytes::TakeVector(&bitmap_data));
+}
+
+void FallbackIconSource::SendDefaultResponse(
+ const content::URLDataSource::GotDataCallback& callback) {
+ favicon_base::FallbackIconStyle default_style;
+ SendFallbackIconHelper(GURL(), gfx::kFaviconSize, default_style, callback);
+}
diff --git a/chromium/chrome/browser/ui/webui/fallback_icon_source.h b/chromium/chrome/browser/ui/webui/fallback_icon_source.h
new file mode 100644
index 00000000000..5526f77a0c5
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/fallback_icon_source.h
@@ -0,0 +1,91 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_FALLBACK_ICON_SOURCE_H_
+#define CHROME_BROWSER_UI_WEBUI_FALLBACK_ICON_SOURCE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "content/public/browser/url_data_source.h"
+
+class GURL;
+
+namespace favicon_base {
+struct FallbackIconStyle;
+}
+
+namespace favicon {
+class FallbackIconService;
+}
+
+// FallbackIconSource services explicit chrome:// requests for fallback icons.
+//
+// Format:
+// chrome://fallback-icon/size,bc,tc,fsr,r/url
+// All of the parameters except for the url are optional. However, the order of
+// the parameters is not interchangeable, and all "," must be in place.
+//
+// Parameter:
+// 'size'
+// Positive integer to specify the fallback icon's size in pixels.
+// 'bc'
+// Fallback icon's background color, as named CSS color, or RGB / ARGB /
+// RRGGBB / AARRGGBB hex formats (no leading "#").
+// 'tc'
+// Fallback icon text color, as named CSS color, or RGB / ARGB / RRGGBB /
+// AARRGGBB hex formats (no leading "#").
+// 'fsr'
+// Number in [0.0, 1.0] to specify the fallback icon's font size (pixels)
+// as a ratio to the icon's size.
+// 'r'
+// Number in [0.0, 1.0] to specify the fallback icon's roundness.
+// 0.0 specifies a square icon; 1.0 specifies a circle icon; intermediate
+// values specify a rounded square icon.
+// 'url'
+// String to specify the page URL of the fallback icon.
+//
+// Example: chrome://fallback-icon/32,red,#000,0.5,1.0/http://www.google.com/
+// This requests a 32x32 fallback icon for http://www.google.com, using
+// red as the background color, #000 as the text color, with font size of
+// 32 * 0.5 = 16, and the icon's background shape is a circle.
+class FallbackIconSource : public content::URLDataSource {
+ public:
+ // |fallback_icon_service| is owned by caller, and may be null.
+ explicit FallbackIconSource(
+ favicon::FallbackIconService* fallback_icon_service);
+
+ ~FallbackIconSource() override;
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ std::string GetMimeType(const std::string&) const override;
+ bool AllowCaching() const override;
+ bool ShouldReplaceExistingSource() const override;
+ bool ShouldServiceRequest(const GURL& url,
+ content::ResourceContext* resource_context,
+ int render_process_id) const override;
+
+ private:
+ void SendFallbackIconHelper(
+ const GURL& url,
+ int size_in_pixels,
+ const favicon_base::FallbackIconStyle& style,
+ const content::URLDataSource::GotDataCallback& callback);
+
+ // Sends the default fallback icon.
+ void SendDefaultResponse(
+ const content::URLDataSource::GotDataCallback& callback);
+
+ favicon::FallbackIconService* fallback_icon_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(FallbackIconSource);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_FALLBACK_ICON_SOURCE_H_
diff --git a/chromium/chrome/browser/ui/webui/favicon_source.cc b/chromium/chrome/browser/ui/webui/favicon_source.cc
new file mode 100644
index 00000000000..a36ccbfea18
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/favicon_source.cc
@@ -0,0 +1,202 @@
+// 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/ui/webui/favicon_source.h"
+
+#include <cmath>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/strings/string_number_conversions.h"
+#include "chrome/browser/favicon/favicon_service_factory.h"
+#include "chrome/browser/history/top_sites_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search/instant_io_context.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/common/url_constants.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/favicon_base/favicon_url_parser.h"
+#include "components/history/core/browser/top_sites.h"
+#include "components/sync_sessions/open_tabs_ui_delegate.h"
+#include "net/url_request/url_request.h"
+#include "ui/base/layout.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "ui/resources/grit/ui_resources.h"
+
+FaviconSource::IconRequest::IconRequest()
+ : size_in_dip(gfx::kFaviconSize), device_scale_factor(1.0f) {
+}
+
+FaviconSource::IconRequest::IconRequest(
+ const content::URLDataSource::GotDataCallback& cb,
+ const GURL& path,
+ int size,
+ float scale)
+ : callback(cb),
+ request_path(path),
+ size_in_dip(size),
+ device_scale_factor(scale) {
+}
+
+FaviconSource::IconRequest::IconRequest(const IconRequest& other) = default;
+
+FaviconSource::IconRequest::~IconRequest() {
+}
+
+FaviconSource::FaviconSource(Profile* profile)
+ : profile_(profile->GetOriginalProfile()) {}
+
+FaviconSource::~FaviconSource() {
+}
+
+std::string FaviconSource::GetSource() const {
+ return chrome::kChromeUIFaviconHost;
+}
+
+void FaviconSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ favicon::FaviconService* favicon_service =
+ FaviconServiceFactory::GetForProfile(profile_,
+ ServiceAccessType::EXPLICIT_ACCESS);
+ if (!favicon_service) {
+ SendDefaultResponse(callback);
+ return;
+ }
+
+ chrome::ParsedFaviconPath parsed;
+ bool success = chrome::ParseFaviconPath(path, &parsed);
+ if (!success) {
+ SendDefaultResponse(callback);
+ return;
+ }
+
+ GURL url(parsed.url);
+ int desired_size_in_pixel =
+ std::ceil(parsed.size_in_dip * parsed.device_scale_factor);
+
+ if (parsed.is_icon_url) {
+ // TODO(michaelbai): Change GetRawFavicon to support combination of
+ // IconType.
+ favicon_service->GetRawFavicon(
+ url,
+ favicon_base::FAVICON,
+ desired_size_in_pixel,
+ base::Bind(
+ &FaviconSource::OnFaviconDataAvailable,
+ base::Unretained(this),
+ IconRequest(
+ callback, url, parsed.size_in_dip, parsed.device_scale_factor)),
+ &cancelable_task_tracker_);
+ } else {
+ // Intercept requests for prepopulated pages if TopSites exists.
+ scoped_refptr<history::TopSites> top_sites =
+ TopSitesFactory::GetForProfile(profile_);
+ if (top_sites) {
+ for (const auto& prepopulated_page : top_sites->GetPrepopulatedPages()) {
+ if (url == prepopulated_page.most_visited.url) {
+ ui::ScaleFactor resource_scale_factor =
+ ui::GetSupportedScaleFactor(parsed.device_scale_factor);
+ callback.Run(
+ ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
+ prepopulated_page.favicon_id, resource_scale_factor));
+ return;
+ }
+ }
+ }
+
+ favicon_service->GetRawFaviconForPageURL(
+ url, favicon_base::FAVICON, desired_size_in_pixel,
+ base::Bind(&FaviconSource::OnFaviconDataAvailable,
+ base::Unretained(this),
+ IconRequest(callback, url, parsed.size_in_dip,
+ parsed.device_scale_factor)),
+ &cancelable_task_tracker_);
+ }
+}
+
+std::string FaviconSource::GetMimeType(const std::string&) const {
+ // We need to explicitly return a mime type, otherwise if the user tries to
+ // drag the image they get no extension.
+ return "image/png";
+}
+
+bool FaviconSource::AllowCaching() const {
+ return false;
+}
+
+bool FaviconSource::ShouldReplaceExistingSource() const {
+ // Leave the existing DataSource in place, otherwise we'll drop any pending
+ // requests on the floor.
+ return false;
+}
+
+bool FaviconSource::ShouldServiceRequest(
+ const GURL& url,
+ content::ResourceContext* resource_context,
+ int render_process_id) const {
+ if (url.SchemeIs(chrome::kChromeSearchScheme)) {
+ return InstantIOContext::ShouldServiceRequest(url, resource_context,
+ render_process_id);
+ }
+ return URLDataSource::ShouldServiceRequest(url, resource_context,
+ render_process_id);
+}
+
+bool FaviconSource::HandleMissingResource(const IconRequest& request) {
+ // If the favicon is not available, try to use the synced favicon.
+ browser_sync::ProfileSyncService* sync_service =
+ ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_);
+ sync_sessions::OpenTabsUIDelegate* open_tabs =
+ sync_service ? sync_service->GetOpenTabsUIDelegate() : nullptr;
+
+ scoped_refptr<base::RefCountedMemory> response;
+ if (open_tabs &&
+ open_tabs->GetSyncedFaviconForPageURL(request.request_path.spec(),
+ &response)) {
+ request.callback.Run(response.get());
+ return true;
+ }
+ return false;
+}
+
+void FaviconSource::OnFaviconDataAvailable(
+ const IconRequest& request,
+ const favicon_base::FaviconRawBitmapResult& bitmap_result) {
+ if (bitmap_result.is_valid()) {
+ // Forward the data along to the networking system.
+ request.callback.Run(bitmap_result.bitmap_data.get());
+ } else if (!HandleMissingResource(request)) {
+ SendDefaultResponse(request);
+ }
+}
+
+void FaviconSource::SendDefaultResponse(
+ const content::URLDataSource::GotDataCallback& callback) {
+ SendDefaultResponse(IconRequest(callback, GURL(), 16, 1.0f));
+}
+
+void FaviconSource::SendDefaultResponse(const IconRequest& icon_request) {
+ int resource_id;
+ switch (icon_request.size_in_dip) {
+ case 64:
+ resource_id = IDR_DEFAULT_FAVICON_64;
+ break;
+ case 32:
+ resource_id = IDR_DEFAULT_FAVICON_32;
+ break;
+ default:
+ resource_id = IDR_DEFAULT_FAVICON;
+ break;
+ }
+
+ base::RefCountedMemory* default_favicon =
+ ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
+ resource_id,
+ ui::GetSupportedScaleFactor(icon_request.device_scale_factor));
+
+ icon_request.callback.Run(default_favicon);
+}
diff --git a/chromium/chrome/browser/ui/webui/favicon_source.h b/chromium/chrome/browser/ui/webui/favicon_source.h
new file mode 100644
index 00000000000..14e0faf3fec
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/favicon_source.h
@@ -0,0 +1,117 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_FAVICON_SOURCE_H_
+#define CHROME_BROWSER_UI_WEBUI_FAVICON_SOURCE_H_
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "components/favicon/core/favicon_service.h"
+#include "content/public/browser/url_data_source.h"
+#include "ui/gfx/favicon_size.h"
+
+class Profile;
+
+// FaviconSource is the gateway between network-level chrome:
+// requests for favicons and the history backend that serves these.
+//
+// Format:
+// chrome://favicon/size&scalefactor/iconurl/url
+// Some parameters are optional as described below. However, the order of the
+// parameters is not interchangeable.
+//
+// Parameter:
+// 'url' Required
+// Specifies the page URL of the requested favicon. If the 'iconurl'
+// parameter is specified, the URL refers to the URL of the favicon image
+// instead.
+// 'size&scalefactor' Optional
+// Values: ['size/aa@bx/']
+// Specifies the requested favicon's size in DIP (aa) and the requested
+// favicon's scale factor. (b).
+// The supported requested DIP sizes are: 16x16, 32x32 and 64x64.
+// If the parameter is unspecified, the requested favicon's size defaults
+// to 16 and the requested scale factor defaults to 1x.
+// Example: chrome://favicon/size/16@2x/https://www.google.com/
+// 'iconurl' Optional
+// Values: ['iconurl']
+// 'iconurl': Specifies that the url parameter refers to the URL of
+// the favicon image as opposed to the URL of the page that the favicon is
+// on.
+// Example: chrome://favicon/iconurl/https://www.google.com/favicon.ico
+class FaviconSource : public content::URLDataSource {
+ public:
+ // |type| is the type of icon this FaviconSource will provide.
+ explicit FaviconSource(Profile* profile);
+
+ ~FaviconSource() override;
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ std::string GetMimeType(const std::string&) const override;
+ bool AllowCaching() const override;
+ bool ShouldReplaceExistingSource() const override;
+ bool ShouldServiceRequest(const GURL& url,
+ content::ResourceContext* resource_context,
+ int render_process_id) const override;
+
+ protected:
+ struct IconRequest {
+ IconRequest();
+ IconRequest(const content::URLDataSource::GotDataCallback& cb,
+ const GURL& path,
+ int size,
+ float scale);
+ IconRequest(const IconRequest& other);
+ ~IconRequest();
+
+ content::URLDataSource::GotDataCallback callback;
+ GURL request_path;
+ int size_in_dip;
+ float device_scale_factor;
+ };
+
+ // Called when the favicon data is missing to perform additional checks to
+ // locate the resource.
+ // |request| contains information for the failed request.
+ // Returns true if the missing resource is found.
+ virtual bool HandleMissingResource(const IconRequest& request);
+
+ Profile* profile_;
+
+ private:
+ // Defines the allowed pixel sizes for requested favicons.
+ enum IconSize {
+ SIZE_16,
+ SIZE_32,
+ SIZE_64,
+ NUM_SIZES
+ };
+
+ // Called when favicon data is available from the history backend.
+ void OnFaviconDataAvailable(
+ const IconRequest& request,
+ const favicon_base::FaviconRawBitmapResult& bitmap_result);
+
+ // Sends the 16x16 DIP 1x default favicon.
+ void SendDefaultResponse(
+ const content::URLDataSource::GotDataCallback& callback);
+
+ // Sends the default favicon.
+ void SendDefaultResponse(const IconRequest& request);
+
+ base::CancelableTaskTracker cancelable_task_tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(FaviconSource);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_FAVICON_SOURCE_H_
diff --git a/chromium/chrome/browser/ui/webui/fileicon_source.cc b/chromium/chrome/browser/ui/webui/fileicon_source.cc
new file mode 100644
index 00000000000..c048f30a6c8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/fileicon_source.cc
@@ -0,0 +1,168 @@
+// 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/ui/webui/fileicon_source.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_split.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "net/base/escape.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_skia.h"
+#include "url/gurl.h"
+
+namespace {
+
+typedef std::map<std::string, IconLoader::IconSize> QueryIconSizeMap;
+
+// The path used in internal URLs to file icon data.
+const char kFileIconPath[] = "fileicon";
+
+// URL parameter specifying icon size.
+const char kIconSize[] = "iconsize";
+
+// URL parameter specifying scale factor.
+const char kScaleFactor[] = "scale";
+
+// Assuming the url is of the form '/path?query', convert the path portion into
+// a FilePath and return the resulting |file_path| and |query|. The path
+// portion may have been encoded using encodeURIComponent().
+void GetFilePathAndQuery(const std::string& url,
+ base::FilePath* file_path,
+ std::string* query) {
+ // We receive the url with chrome://fileicon/ stripped but GURL expects it.
+ const GURL gurl("chrome://fileicon/" + url);
+ std::string path = net::UnescapeURLComponent(
+ gurl.path().substr(1),
+ net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS |
+ net::UnescapeRule::PATH_SEPARATORS | net::UnescapeRule::SPACES);
+
+ *file_path = base::FilePath::FromUTF8Unsafe(path);
+ *file_path = file_path->NormalizePathSeparators();
+ query->assign(gurl.query());
+}
+
+IconLoader::IconSize SizeStringToIconSize(const std::string& size_string) {
+ if (size_string == "small") return IconLoader::SMALL;
+ if (size_string == "large") return IconLoader::LARGE;
+ // We default to NORMAL if we don't recognize the size_string. Including
+ // size_string=="normal".
+ return IconLoader::NORMAL;
+}
+
+// Simple parser for data on the query.
+void ParseQueryParams(const std::string& query,
+ float* scale_factor,
+ IconLoader::IconSize* icon_size) {
+ base::StringPairs parameters;
+ if (icon_size)
+ *icon_size = IconLoader::NORMAL;
+ if (scale_factor)
+ *scale_factor = 1.0f;
+ base::SplitStringIntoKeyValuePairs(query, '=', '&', &parameters);
+ for (base::StringPairs::const_iterator iter = parameters.begin();
+ iter != parameters.end(); ++iter) {
+ if (icon_size && iter->first == kIconSize)
+ *icon_size = SizeStringToIconSize(iter->second);
+ else if (scale_factor && iter->first == kScaleFactor)
+ webui::ParseScaleFactor(iter->second, scale_factor);
+ }
+}
+
+} // namespace
+
+FileIconSource::IconRequestDetails::IconRequestDetails() : scale_factor(1.0f) {
+}
+
+FileIconSource::IconRequestDetails::IconRequestDetails(
+ const IconRequestDetails& other) = default;
+
+FileIconSource::IconRequestDetails::~IconRequestDetails() {
+}
+
+FileIconSource::FileIconSource() {}
+
+FileIconSource::~FileIconSource() {}
+
+void FileIconSource::FetchFileIcon(
+ const base::FilePath& path,
+ float scale_factor,
+ IconLoader::IconSize icon_size,
+ const content::URLDataSource::GotDataCallback& callback) {
+ IconManager* im = g_browser_process->icon_manager();
+ gfx::Image* icon = im->LookupIconFromFilepath(path, icon_size);
+
+ if (icon) {
+ scoped_refptr<base::RefCountedBytes> icon_data(new base::RefCountedBytes);
+ gfx::PNGCodec::EncodeBGRASkBitmap(
+ icon->ToImageSkia()->GetRepresentation(scale_factor).sk_bitmap(),
+ false,
+ &icon_data->data());
+
+ callback.Run(icon_data.get());
+ } else {
+ // Attach the ChromeURLDataManager request ID to the history request.
+ IconRequestDetails details;
+ details.callback = callback;
+ details.scale_factor = scale_factor;
+
+ // Icon was not in cache, go fetch it slowly.
+ im->LoadIcon(path,
+ icon_size,
+ base::Bind(&FileIconSource::OnFileIconDataAvailable,
+ base::Unretained(this), details),
+ &cancelable_task_tracker_);
+ }
+}
+
+std::string FileIconSource::GetSource() const {
+ return kFileIconPath;
+}
+
+void FileIconSource::StartDataRequest(
+ const std::string& url_path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ std::string query;
+ base::FilePath file_path;
+ IconLoader::IconSize icon_size;
+ float scale_factor = 1.0f;
+ GetFilePathAndQuery(url_path, &file_path, &query);
+ ParseQueryParams(query, &scale_factor, &icon_size);
+ FetchFileIcon(file_path, scale_factor, icon_size, callback);
+}
+
+std::string FileIconSource::GetMimeType(const std::string&) const {
+ // Rely on image decoder inferring the correct type.
+ return std::string();
+}
+
+bool FileIconSource::AllowCaching() const {
+ return false;
+}
+
+void FileIconSource::OnFileIconDataAvailable(const IconRequestDetails& details,
+ gfx::Image* icon) {
+ if (icon) {
+ scoped_refptr<base::RefCountedBytes> icon_data(new base::RefCountedBytes);
+ gfx::PNGCodec::EncodeBGRASkBitmap(
+ icon->ToImageSkia()->GetRepresentation(
+ details.scale_factor).sk_bitmap(),
+ false,
+ &icon_data->data());
+
+ details.callback.Run(icon_data.get());
+ } else {
+ // TODO(glen): send a dummy icon.
+ details.callback.Run(NULL);
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/fileicon_source.h b/chromium/chrome/browser/ui/webui/fileicon_source.h
new file mode 100644
index 00000000000..6b404673c7c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/fileicon_source.h
@@ -0,0 +1,70 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_FILEICON_SOURCE_H_
+#define CHROME_BROWSER_UI_WEBUI_FILEICON_SOURCE_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "chrome/browser/icon_manager.h"
+#include "content/public/browser/url_data_source.h"
+
+namespace gfx {
+class Image;
+}
+
+// FileIconSource is the gateway between network-level chrome:
+// requests for favicons and the history backend that serves these.
+class FileIconSource : public content::URLDataSource {
+ public:
+ FileIconSource();
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ std::string GetMimeType(const std::string&) const override;
+ bool AllowCaching() const override;
+
+ protected:
+ ~FileIconSource() override;
+
+ // Once the |path| and |icon_size| has been determined from the request, this
+ // function is called to perform the actual fetch. Declared as virtual for
+ // testing.
+ virtual void FetchFileIcon(
+ const base::FilePath& path,
+ float scale_factor,
+ IconLoader::IconSize icon_size,
+ const content::URLDataSource::GotDataCallback& callback);
+
+ private:
+ // Contains the necessary information for completing an icon fetch request.
+ struct IconRequestDetails {
+ IconRequestDetails();
+ IconRequestDetails(const IconRequestDetails& other);
+ ~IconRequestDetails();
+
+ // The callback to run with the response.
+ content::URLDataSource::GotDataCallback callback;
+
+ // The requested scale factor to respond with.
+ float scale_factor;
+ };
+
+ // Called when favicon data is available from the history backend.
+ void OnFileIconDataAvailable(const IconRequestDetails& details,
+ gfx::Image* icon);
+
+ // Tracks tasks requesting file icons.
+ base::CancelableTaskTracker cancelable_task_tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileIconSource);
+};
+#endif // CHROME_BROWSER_UI_WEBUI_FILEICON_SOURCE_H_
diff --git a/chromium/chrome/browser/ui/webui/fileicon_source_unittest.cc b/chromium/chrome/browser/ui/webui/fileicon_source_unittest.cc
new file mode 100644
index 00000000000..5ae72c24b93
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/fileicon_source_unittest.cc
@@ -0,0 +1,121 @@
+// 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/ui/webui/fileicon_source.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted_memory.h"
+#include "build/build_config.h"
+#include "chrome/browser/icon_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class TestFileIconSource : public FileIconSource {
+ public:
+ TestFileIconSource() {}
+
+ MOCK_METHOD4(FetchFileIcon,
+ void(const base::FilePath& path,
+ float scale_factor,
+ IconLoader::IconSize icon_size,
+ const content::URLDataSource::GotDataCallback& callback));
+
+ virtual ~TestFileIconSource() {}
+};
+
+class FileIconSourceTest : public testing::Test {
+ public:
+ FileIconSourceTest() = default;
+
+ static TestFileIconSource* CreateFileIconSource() {
+ return new TestFileIconSource();
+ }
+
+ private:
+ content::TestBrowserThreadBundle test_browser_thread_bundle_;
+};
+
+const struct FetchFileIconExpectation {
+ const char* request_path;
+ const base::FilePath::CharType* unescaped_path;
+ float scale_factor;
+ IconLoader::IconSize size;
+} kBasicExpectations[] = {
+ { "foo?bar", FILE_PATH_LITERAL("foo"), 1.0f, IconLoader::NORMAL },
+ { "foo?bar&scale=2x", FILE_PATH_LITERAL("foo"), 2.0f, IconLoader::NORMAL },
+ { "foo?iconsize=small", FILE_PATH_LITERAL("foo"), 1.0f, IconLoader::SMALL },
+ { "foo?iconsize=normal", FILE_PATH_LITERAL("foo"), 1.0f, IconLoader::NORMAL },
+ { "foo?iconsize=large", FILE_PATH_LITERAL("foo"), 1.0f, IconLoader::LARGE },
+ { "foo?iconsize=asdf", FILE_PATH_LITERAL("foo"), 1.0f, IconLoader::NORMAL },
+ { "foo?blah=b&iconsize=small", FILE_PATH_LITERAL("foo"), 1.0f,
+ IconLoader::SMALL },
+ { "foo?blah&iconsize=small", FILE_PATH_LITERAL("foo"), 1.0f,
+ IconLoader::SMALL },
+ { "a%3Fb%26c%3Dd.txt?iconsize=small", FILE_PATH_LITERAL("a?b&c=d.txt"), 1.0f,
+ IconLoader::SMALL },
+ { "a%3Ficonsize%3Dsmall?iconsize=large",
+ FILE_PATH_LITERAL("a?iconsize=small"), 1.0f, IconLoader::LARGE },
+ { "o%40%23%24%25%26*()%20%2B%3D%3F%2C%3A%3B%22.jpg",
+ FILE_PATH_LITERAL("o@#$%&*() +=?,:;\".jpg"), 1.0f, IconLoader::NORMAL },
+#if defined(OS_WIN)
+ { "c:/foo/bar/baz", FILE_PATH_LITERAL("c:\\foo\\bar\\baz"), 1.0f,
+ IconLoader::NORMAL },
+ { "/foo?bar=asdf&asdf", FILE_PATH_LITERAL("\\foo"), 1.0f,
+ IconLoader::NORMAL },
+ { "c%3A%2Fusers%2Ffoo%20user%2Fbar.txt",
+ FILE_PATH_LITERAL("c:\\users\\foo user\\bar.txt"), 1.0f,
+ IconLoader::NORMAL },
+ { "c%3A%2Fusers%2F%C2%A9%202000.pdf",
+ FILE_PATH_LITERAL("c:\\users\\\xa9 2000.pdf"), 1.0f, IconLoader::NORMAL },
+ { "%E0%B6%9A%E0%B6%BB%E0%B7%9D%E0%B6%B8%E0%B7%8A",
+ FILE_PATH_LITERAL("\x0d9a\x0dbb\x0ddd\x0db8\x0dca"), 1.0f,
+ IconLoader::NORMAL },
+ { "%2Ffoo%2Fbar", FILE_PATH_LITERAL("\\foo\\bar"), 1.0f, IconLoader::NORMAL },
+ { "%2Fbaz%20(1).txt?iconsize=small", FILE_PATH_LITERAL("\\baz (1).txt"),
+ 1.0f, IconLoader::SMALL },
+#else
+ { "/foo/bar/baz", FILE_PATH_LITERAL("/foo/bar/baz"), 1.0f,
+ IconLoader::NORMAL },
+ { "/foo?bar", FILE_PATH_LITERAL("/foo"), 1.0f, IconLoader::NORMAL },
+ { "%2Ffoo%2f%E0%B6%9A%E0%B6%BB%E0%B7%9D%E0%B6%B8%E0%B7%8A",
+ FILE_PATH_LITERAL("/foo/\xe0\xb6\x9a\xe0\xb6\xbb\xe0\xb7\x9d")
+ FILE_PATH_LITERAL("\xe0\xb6\xb8\xe0\xb7\x8a"), 1.0f, IconLoader::NORMAL },
+ { "%2Ffoo%2Fbar", FILE_PATH_LITERAL("/foo/bar"), 1.0f, IconLoader::NORMAL },
+ { "%2Fbaz%20(1).txt?iconsize=small", FILE_PATH_LITERAL("/baz (1).txt"), 1.0f,
+ IconLoader::SMALL },
+#endif
+};
+
+// Test that the callback is NULL.
+MATCHER(CallbackIsNull, "") {
+ return arg.is_null();
+}
+
+} // namespace
+
+TEST_F(FileIconSourceTest, FileIconSource_Parse) {
+ std::vector<ui::ScaleFactor> supported_scale_factors;
+ supported_scale_factors.push_back(ui::SCALE_FACTOR_100P);
+ supported_scale_factors.push_back(ui::SCALE_FACTOR_200P);
+ ui::test::ScopedSetSupportedScaleFactors scoped_supported(
+ supported_scale_factors);
+
+ for (unsigned i = 0; i < arraysize(kBasicExpectations); i++) {
+ std::unique_ptr<TestFileIconSource> source(CreateFileIconSource());
+ content::URLDataSource::GotDataCallback callback;
+ EXPECT_CALL(*source.get(),
+ FetchFileIcon(
+ base::FilePath(kBasicExpectations[i].unescaped_path),
+ kBasicExpectations[i].scale_factor,
+ kBasicExpectations[i].size, CallbackIsNull()));
+ source->StartDataRequest(
+ kBasicExpectations[i].request_path,
+ content::ResourceRequestInfo::WebContentsGetter(),
+ callback);
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/flags_ui.cc b/chromium/chrome/browser/ui/webui/flags_ui.cc
new file mode 100644
index 00000000000..75db64526c2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/flags_ui.cc
@@ -0,0 +1,345 @@
+// 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/ui/webui/flags_ui.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/about_flags.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "components/flags_ui/flags_ui_constants.h"
+#include "components/flags_ui/flags_ui_pref_names.h"
+#include "components/flags_ui/pref_service_flags_storage.h"
+#include "components/grit/components_resources.h"
+#include "components/grit/components_scaled_resources.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_chromium_strings.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/version_info/version_info.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+
+#if defined(OS_CHROMEOS)
+#include "base/sys_info.h"
+#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
+#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/user_manager/user_manager.h"
+#endif
+
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace {
+
+content::WebUIDataSource* CreateFlagsUIHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIFlagsHost);
+
+ source->AddLocalizedString(flags_ui::kFlagsLongTitle,
+ IDS_FLAGS_UI_LONG_TITLE);
+ source->AddLocalizedString(flags_ui::kFlagsTableTitle,
+ IDS_FLAGS_UI_TABLE_TITLE);
+ source->AddLocalizedString(flags_ui::kFlagsWarningHeader,
+ IDS_FLAGS_UI_WARNING_HEADER);
+ source->AddLocalizedString(flags_ui::kFlagsBlurb, IDS_FLAGS_UI_WARNING_TEXT);
+ source->AddLocalizedString(flags_ui::kChannelPromoBeta,
+ IDS_FLAGS_UI_PROMOTE_BETA_CHANNEL);
+ source->AddLocalizedString(flags_ui::kChannelPromoDev,
+ IDS_FLAGS_UI_PROMOTE_DEV_CHANNEL);
+ source->AddLocalizedString(flags_ui::kFlagsUnsupportedTableTitle,
+ IDS_FLAGS_UI_UNSUPPORTED_TABLE_TITLE);
+ source->AddLocalizedString(flags_ui::kFlagsNotSupported,
+ IDS_FLAGS_UI_NOT_AVAILABLE);
+ source->AddLocalizedString(flags_ui::kFlagsRestartNotice,
+ IDS_FLAGS_UI_RELAUNCH_NOTICE);
+ source->AddLocalizedString(flags_ui::kFlagsRestartButton,
+ IDS_FLAGS_UI_RELAUNCH_BUTTON);
+ source->AddLocalizedString(flags_ui::kResetAllButton,
+ IDS_FLAGS_UI_RESET_ALL_BUTTON);
+ source->AddLocalizedString(flags_ui::kDisable, IDS_FLAGS_UI_DISABLE);
+ source->AddLocalizedString(flags_ui::kEnable, IDS_FLAGS_UI_ENABLE);
+
+#if defined(OS_CHROMEOS)
+ if (!user_manager::UserManager::Get()->IsCurrentUserOwner() &&
+ base::SysInfo::IsRunningOnChromeOS()) {
+ // Set the strings to show which user can actually change the flags.
+ std::string owner;
+ chromeos::CrosSettings::Get()->GetString(chromeos::kDeviceOwner, &owner);
+ source->AddString(flags_ui::kOwnerWarning,
+ l10n_util::GetStringFUTF16(IDS_FLAGS_UI_SYSTEM_OWNER_ONLY,
+ base::UTF8ToUTF16(owner)));
+ } else {
+ // The warning will be only shown on ChromeOS, when the current user is not
+ // the owner.
+ source->AddString(flags_ui::kOwnerWarning, base::string16());
+ }
+#endif
+
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath(flags_ui::kFlagsJS, IDR_FLAGS_UI_FLAGS_JS);
+ source->SetDefaultResource(IDR_FLAGS_UI_FLAGS_HTML);
+ return source;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// FlagsDOMHandler
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// The handler for Javascript messages for the about:flags page.
+class FlagsDOMHandler : public WebUIMessageHandler {
+ public:
+ FlagsDOMHandler() : access_(flags_ui::kGeneralAccessFlagsOnly),
+ experimental_features_requested_(false) {
+ }
+ ~FlagsDOMHandler() override {}
+
+ // Initializes the DOM handler with the provided flags storage and flags
+ // access. If there were flags experiments requested from javascript before
+ // this was called, it calls |HandleRequestExperimentalFeatures| again.
+ void Init(flags_ui::FlagsStorage* flags_storage,
+ flags_ui::FlagAccess access);
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // Callback for the "requestExperimentFeatures" message.
+ void HandleRequestExperimentalFeatures(const base::ListValue* args);
+
+ // Callback for the "enableExperimentalFeature" message.
+ void HandleEnableExperimentalFeatureMessage(const base::ListValue* args);
+
+ // Callback for the "restartBrowser" message. Restores all tabs on restart.
+ void HandleRestartBrowser(const base::ListValue* args);
+
+ // Callback for the "resetAllFlags" message.
+ void HandleResetAllFlags(const base::ListValue* args);
+
+ private:
+ std::unique_ptr<flags_ui::FlagsStorage> flags_storage_;
+ flags_ui::FlagAccess access_;
+ bool experimental_features_requested_;
+
+ DISALLOW_COPY_AND_ASSIGN(FlagsDOMHandler);
+};
+
+void FlagsDOMHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ flags_ui::kRequestExperimentalFeatures,
+ base::Bind(&FlagsDOMHandler::HandleRequestExperimentalFeatures,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ flags_ui::kEnableExperimentalFeature,
+ base::Bind(&FlagsDOMHandler::HandleEnableExperimentalFeatureMessage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ flags_ui::kRestartBrowser,
+ base::Bind(&FlagsDOMHandler::HandleRestartBrowser,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ flags_ui::kResetAllFlags,
+ base::Bind(&FlagsDOMHandler::HandleResetAllFlags,
+ base::Unretained(this)));
+}
+
+void FlagsDOMHandler::Init(flags_ui::FlagsStorage* flags_storage,
+ flags_ui::FlagAccess access) {
+ flags_storage_.reset(flags_storage);
+ access_ = access;
+
+ if (experimental_features_requested_)
+ HandleRequestExperimentalFeatures(NULL);
+}
+
+void FlagsDOMHandler::HandleRequestExperimentalFeatures(
+ const base::ListValue* args) {
+ experimental_features_requested_ = true;
+ // Bail out if the handler hasn't been initialized yet. The request will be
+ // handled after the initialization.
+ if (!flags_storage_)
+ return;
+
+ base::DictionaryValue results;
+
+ std::unique_ptr<base::ListValue> supported_features(new base::ListValue);
+ std::unique_ptr<base::ListValue> unsupported_features(new base::ListValue);
+ about_flags::GetFlagFeatureEntries(flags_storage_.get(),
+ access_,
+ supported_features.get(),
+ unsupported_features.get());
+ results.Set(flags_ui::kSupportedFeatures, std::move(supported_features));
+ results.Set(flags_ui::kUnsupportedFeatures, std::move(unsupported_features));
+ results.SetBoolean(flags_ui::kNeedsRestart,
+ about_flags::IsRestartNeededToCommitChanges());
+ results.SetBoolean(flags_ui::kShowOwnerWarning,
+ access_ == flags_ui::kGeneralAccessFlagsOnly);
+
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+ version_info::Channel channel = chrome::GetChannel();
+ results.SetBoolean(flags_ui::kShowBetaChannelPromotion,
+ channel == version_info::Channel::STABLE);
+ results.SetBoolean(flags_ui::kShowDevChannelPromotion,
+ channel == version_info::Channel::BETA);
+#else
+ results.SetBoolean(flags_ui::kShowBetaChannelPromotion, false);
+ results.SetBoolean(flags_ui::kShowDevChannelPromotion, false);
+#endif
+ web_ui()->CallJavascriptFunctionUnsafe(flags_ui::kReturnExperimentalFeatures,
+ results);
+}
+
+void FlagsDOMHandler::HandleEnableExperimentalFeatureMessage(
+ const base::ListValue* args) {
+ DCHECK(flags_storage_);
+ DCHECK_EQ(2u, args->GetSize());
+ if (args->GetSize() != 2)
+ return;
+
+ std::string entry_internal_name;
+ std::string enable_str;
+ if (!args->GetString(0, &entry_internal_name) ||
+ !args->GetString(1, &enable_str))
+ return;
+
+ about_flags::SetFeatureEntryEnabled(flags_storage_.get(), entry_internal_name,
+ enable_str == "true");
+}
+
+void FlagsDOMHandler::HandleRestartBrowser(const base::ListValue* args) {
+ DCHECK(flags_storage_);
+#if defined(OS_CHROMEOS)
+ // On ChromeOS be less intrusive and restart inside the user session after
+ // we apply the newly selected flags.
+ base::CommandLine user_flags(base::CommandLine::NO_PROGRAM);
+ about_flags::ConvertFlagsToSwitches(flags_storage_.get(),
+ &user_flags,
+ flags_ui::kAddSentinels);
+ 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);
+#endif
+ chrome::AttemptRestart();
+}
+
+void FlagsDOMHandler::HandleResetAllFlags(const base::ListValue* args) {
+ DCHECK(flags_storage_);
+ about_flags::ResetAllFlags(flags_storage_.get());
+}
+
+
+#if defined(OS_CHROMEOS)
+// On ChromeOS verifying if the owner is signed in is async operation and only
+// after finishing it the UI can be properly populated. This function is the
+// callback for whether the owner is signed in. It will respectively pick the
+// proper PrefService for the flags interface.
+void FinishInitialization(base::WeakPtr<FlagsUI> flags_ui,
+ Profile* profile,
+ FlagsDOMHandler* dom_handler,
+ bool current_user_is_owner) {
+ // If the flags_ui has gone away, there's nothing to do.
+ if (!flags_ui)
+ return;
+
+ // On Chrome OS the owner can set system wide flags and other users can only
+ // set flags for their own session.
+ // Note that |dom_handler| is owned by the web ui that owns |flags_ui|, so
+ // it is still alive if |flags_ui| is.
+ if (current_user_is_owner) {
+ chromeos::OwnerSettingsServiceChromeOS* service =
+ chromeos::OwnerSettingsServiceChromeOSFactory::GetForBrowserContext(
+ profile);
+ dom_handler->Init(new chromeos::about_flags::OwnerFlagsStorage(
+ profile->GetPrefs(), service),
+ flags_ui::kOwnerAccessToFlags);
+ } else {
+ dom_handler->Init(
+ new flags_ui::PrefServiceFlagsStorage(profile->GetPrefs()),
+ flags_ui::kGeneralAccessFlagsOnly);
+ }
+}
+#endif
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// FlagsUI
+//
+///////////////////////////////////////////////////////////////////////////////
+
+FlagsUI::FlagsUI(content::WebUI* web_ui)
+ : WebUIController(web_ui),
+ weak_factory_(this) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+
+ auto handler_owner = base::MakeUnique<FlagsDOMHandler>();
+ FlagsDOMHandler* handler = handler_owner.get();
+ web_ui->AddMessageHandler(std::move(handler_owner));
+
+#if defined(OS_CHROMEOS)
+ if (base::SysInfo::IsRunningOnChromeOS() &&
+ chromeos::OwnerSettingsServiceChromeOSFactory::GetForBrowserContext(
+ profile)) {
+ chromeos::OwnerSettingsServiceChromeOS* service =
+ chromeos::OwnerSettingsServiceChromeOSFactory::GetForBrowserContext(
+ profile);
+ service->IsOwnerAsync(base::Bind(
+ &FinishInitialization, weak_factory_.GetWeakPtr(), profile, handler));
+ } else {
+ FinishInitialization(weak_factory_.GetWeakPtr(), profile, handler,
+ false /* current_user_is_owner */);
+ }
+#else
+ handler->Init(
+ new flags_ui::PrefServiceFlagsStorage(g_browser_process->local_state()),
+ flags_ui::kOwnerAccessToFlags);
+#endif
+
+ // Set up the about:flags source.
+ content::WebUIDataSource::Add(profile, CreateFlagsUIHTMLSource());
+}
+
+FlagsUI::~FlagsUI() {
+}
+
+// static
+base::RefCountedMemory* FlagsUI::GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor) {
+ return ResourceBundle::GetSharedInstance().
+ LoadDataResourceBytesForScale(IDR_FLAGS_FAVICON, scale_factor);
+}
diff --git a/chromium/chrome/browser/ui/webui/flags_ui.h b/chromium/chrome/browser/ui/webui/flags_ui.h
new file mode 100644
index 00000000000..f28cbaf3757
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/flags_ui.h
@@ -0,0 +1,36 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_FLAGS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_FLAGS_UI_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "build/build_config.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "ui/base/layout.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/settings/device_settings_service.h"
+#endif
+
+namespace base {
+class RefCountedMemory;
+}
+
+class FlagsUI : public content::WebUIController {
+ public:
+ explicit FlagsUI(content::WebUI* web_ui);
+ ~FlagsUI() override;
+
+ static base::RefCountedMemory* GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor);
+
+ private:
+ base::WeakPtrFactory<FlagsUI> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FlagsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_FLAGS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/flash_ui.cc b/chromium/chrome/browser/ui/webui/flash_ui.cc
new file mode 100644
index 00000000000..451ca502ba9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/flash_ui.cc
@@ -0,0 +1,401 @@
+// 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/ui/webui/flash_ui.h"
+
+#include <stddef.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/i18n/time_formatting.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/timer/timer.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/crash_upload_list/crash_upload_list.h"
+#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
+#include "chrome/browser/plugins/plugin_prefs.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/version_info/version_info.h"
+#include "content/public/browser/gpu_data_manager.h"
+#include "content/public/browser/gpu_data_manager_observer.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/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "content/public/common/content_constants.h"
+#include "content/public/common/webplugininfo.h"
+#include "gpu/config/gpu_info.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
+using base::ASCIIToUTF16;
+using base::UserMetricsAction;
+using content::GpuDataManager;
+using content::PluginService;
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace {
+
+const char kFlashPlugin[] = "Flash plugin";
+
+content::WebUIDataSource* CreateFlashUIHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIFlashHost);
+
+ source->AddLocalizedString("loadingMessage", IDS_FLASH_LOADING_MESSAGE);
+ source->AddLocalizedString("flashLongTitle", IDS_FLASH_TITLE_MESSAGE);
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("about_flash.js", IDR_ABOUT_FLASH_JS);
+ source->SetDefaultResource(IDR_ABOUT_FLASH_HTML);
+ return source;
+}
+
+const int kTimeout = 8 * 1000; // 8 seconds.
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// FlashDOMHandler
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// The handler for JavaScript messages for the about:flags page.
+class FlashDOMHandler : public WebUIMessageHandler,
+ public CrashUploadList::Delegate,
+ public content::GpuDataManagerObserver {
+ public:
+ FlashDOMHandler();
+ ~FlashDOMHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // CrashUploadList::Delegate implementation.
+ void OnUploadListAvailable() override;
+
+ // GpuDataManager::Observer implementation.
+ void OnGpuInfoUpdate() override;
+
+ // Callback for the "requestFlashInfo" message.
+ void HandleRequestFlashInfo(const base::ListValue* args);
+
+ // Callback for the Flash plugin information.
+ void OnGotPlugins(const std::vector<content::WebPluginInfo>& plugins);
+
+ private:
+ // Called when we think we might have enough information to return data back
+ // to the page.
+ void MaybeRespondToPage();
+
+ // In certain cases we might not get called back from the GPU process so we
+ // set an upper limit on the time we wait. This function gets called when the
+ // time has passed. This actually doesn't prevent the rest of the information
+ // to appear later, the page will just reflow when more information becomes
+ // available.
+ void OnTimeout();
+
+ // A timer to keep track of when the data fetching times out.
+ base::OneShotTimer timeout_;
+
+ // Crash list.
+ scoped_refptr<CrashUploadList> upload_list_;
+
+ // Whether the list of all crashes is available.
+ bool crash_list_available_;
+ // Whether the page has requested data.
+ bool page_has_requested_data_;
+ // Whether the GPU data has been collected.
+ bool has_gpu_info_;
+ // Whether the plugin information is ready.
+ bool has_plugin_info_;
+
+ base::WeakPtrFactory<FlashDOMHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FlashDOMHandler);
+};
+
+FlashDOMHandler::FlashDOMHandler()
+ : crash_list_available_(false),
+ page_has_requested_data_(false),
+ has_gpu_info_(false),
+ has_plugin_info_(false),
+ weak_ptr_factory_(this) {
+ // Request Crash data asynchronously.
+ upload_list_ = CreateCrashUploadList(this);
+ upload_list_->LoadUploadListAsynchronously();
+
+ // Watch for changes in GPUInfo.
+ GpuDataManager::GetInstance()->AddObserver(this);
+
+ // Tell GpuDataManager it should have full GpuInfo. If the
+ // GPU process has not run yet, this will trigger its launch.
+ GpuDataManager::GetInstance()->RequestCompleteGpuInfoIfNeeded();
+
+ // GPU access might not be allowed at all, which will cause us not to get a
+ // call back.
+ if (!GpuDataManager::GetInstance()->GpuAccessAllowed(NULL))
+ OnGpuInfoUpdate();
+
+ PluginService::GetInstance()->GetPlugins(base::Bind(
+ &FlashDOMHandler::OnGotPlugins, weak_ptr_factory_.GetWeakPtr()));
+
+ // And lastly, we fire off a timer to make sure we never get stuck at the
+ // "Loading..." message.
+ timeout_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kTimeout),
+ this, &FlashDOMHandler::OnTimeout);
+}
+
+FlashDOMHandler::~FlashDOMHandler() {
+ GpuDataManager::GetInstance()->RemoveObserver(this);
+ upload_list_->ClearDelegate();
+}
+
+void FlashDOMHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("requestFlashInfo",
+ base::Bind(&FlashDOMHandler::HandleRequestFlashInfo,
+ base::Unretained(this)));
+}
+
+void FlashDOMHandler::OnUploadListAvailable() {
+ crash_list_available_ = true;
+ MaybeRespondToPage();
+}
+
+void AddPair(base::ListValue* list,
+ const base::string16& key,
+ const base::string16& value) {
+ std::unique_ptr<base::DictionaryValue> results(new base::DictionaryValue());
+ results->SetString("key", key);
+ results->SetString("value", value);
+ list->Append(std::move(results));
+}
+
+void AddPair(base::ListValue* list,
+ const base::string16& key,
+ const std::string& value) {
+ AddPair(list, key, ASCIIToUTF16(value));
+}
+
+void FlashDOMHandler::HandleRequestFlashInfo(const base::ListValue* args) {
+ page_has_requested_data_ = true;
+ MaybeRespondToPage();
+}
+
+void FlashDOMHandler::OnGpuInfoUpdate() {
+ has_gpu_info_ = true;
+ MaybeRespondToPage();
+}
+
+void FlashDOMHandler::OnGotPlugins(
+ const std::vector<content::WebPluginInfo>& plugins) {
+ has_plugin_info_ = true;
+ MaybeRespondToPage();
+}
+
+void FlashDOMHandler::OnTimeout() {
+ // We don't set page_has_requested_data_ because that is guaranteed to appear
+ // and we shouldn't be responding to the page before then.
+ has_gpu_info_ = true;
+ crash_list_available_ = true;
+ has_plugin_info_ = true;
+ MaybeRespondToPage();
+}
+
+void FlashDOMHandler::MaybeRespondToPage() {
+ // We don't reply until everything is ready. The page is showing a 'loading'
+ // message until then. If you add criteria to this list, please update the
+ // function OnTimeout() as well.
+ if (!page_has_requested_data_ || !crash_list_available_ || !has_gpu_info_ ||
+ !has_plugin_info_) {
+ return;
+ }
+
+ timeout_.Stop();
+
+ // This is code that runs only when the user types in about:flash. We don't
+ // need to jump through hoops to offload this to the IO thread.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ auto list = base::MakeUnique<base::ListValue>();
+
+ // Chrome version information.
+ AddPair(list.get(), l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
+ version_info::GetVersionNumber() + " (" + chrome::GetChannelString() +
+ ")");
+
+ // OS version information.
+ std::string os_label = version_info::GetOSType();
+#if defined(OS_WIN)
+ base::win::OSInfo* os = base::win::OSInfo::GetInstance();
+ switch (os->version()) {
+ case base::win::VERSION_XP: os_label += " XP"; break;
+ case base::win::VERSION_SERVER_2003:
+ os_label += " Server 2003 or XP Pro 64 bit";
+ break;
+ case base::win::VERSION_VISTA: os_label += " Vista or Server 2008"; break;
+ case base::win::VERSION_WIN7: os_label += " 7 or Server 2008 R2"; break;
+ case base::win::VERSION_WIN8: os_label += " 8 or Server 2012"; break;
+ default: os_label += " UNKNOWN"; break;
+ }
+ os_label += " SP" + base::IntToString(os->service_pack().major);
+ if (os->service_pack().minor > 0)
+ os_label += "." + base::IntToString(os->service_pack().minor);
+ if (os->architecture() == base::win::OSInfo::X64_ARCHITECTURE)
+ os_label += " 64 bit";
+#endif
+ AddPair(list.get(), l10n_util::GetStringUTF16(IDS_VERSION_UI_OS), os_label);
+
+ // Obtain the version of the Flash plugins.
+ std::vector<content::WebPluginInfo> info_array;
+ PluginService::GetInstance()->GetPluginInfoArray(
+ GURL(), content::kFlashPluginSwfMimeType, false, &info_array, NULL);
+ if (info_array.empty()) {
+ AddPair(list.get(), ASCIIToUTF16(kFlashPlugin), "Not installed");
+ } else {
+ PluginPrefs* plugin_prefs =
+ PluginPrefs::GetForProfile(Profile::FromWebUI(web_ui())).get();
+ bool found_enabled = false;
+ for (size_t i = 0; i < info_array.size(); ++i) {
+ base::string16 flash_version = info_array[i].version + ASCIIToUTF16(" ") +
+ info_array[i].path.LossyDisplayName();
+ if (plugin_prefs->IsPluginEnabled(info_array[i])) {
+ // If we have already found an enabled Flash version, this one
+ // is not used.
+ if (found_enabled)
+ flash_version += ASCIIToUTF16(" (not used)");
+
+ found_enabled = true;
+ } else {
+ flash_version += ASCIIToUTF16(" (disabled)");
+ }
+ AddPair(list.get(), ASCIIToUTF16(kFlashPlugin), flash_version);
+ }
+ }
+
+ // Crash information.
+ AddPair(list.get(), base::string16(), "--- Crash data ---");
+ bool crash_reporting_enabled =
+ ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled();
+ if (crash_reporting_enabled) {
+ std::vector<CrashUploadList::UploadInfo> crashes;
+ upload_list_->GetUploads(10, &crashes);
+
+ for (std::vector<CrashUploadList::UploadInfo>::iterator i = crashes.begin();
+ i != crashes.end(); ++i) {
+ base::string16 crash_string(ASCIIToUTF16(i->upload_id));
+ crash_string += ASCIIToUTF16(" ");
+ crash_string += base::TimeFormatFriendlyDateAndTime(i->upload_time);
+ AddPair(list.get(), ASCIIToUTF16("crash id"), crash_string);
+ }
+ } else {
+ AddPair(list.get(), ASCIIToUTF16("Crash Reporting"),
+ "Enable crash reporting to see crash IDs");
+ AddPair(list.get(), ASCIIToUTF16("For more details"),
+ chrome::kLearnMoreReportingURL);
+ }
+
+ // GPU information.
+ AddPair(list.get(), base::string16(), "--- GPU information ---");
+ gpu::GPUInfo gpu_info = GpuDataManager::GetInstance()->GetGPUInfo();
+
+ std::string reason;
+ if (!GpuDataManager::GetInstance()->GpuAccessAllowed(&reason)) {
+ AddPair(list.get(), ASCIIToUTF16("WARNING:"),
+ "GPU access is not allowed: " + reason);
+ }
+#if defined(OS_WIN)
+ const gpu::DxDiagNode& node = gpu_info.dx_diagnostics;
+ for (std::map<std::string, gpu::DxDiagNode>::const_iterator it =
+ node.children.begin();
+ it != node.children.end();
+ ++it) {
+ for (std::map<std::string, std::string>::const_iterator it2 =
+ it->second.values.begin();
+ it2 != it->second.values.end();
+ ++it2) {
+ if (!it2->second.empty()) {
+ if (it2->first == "szDescription") {
+ AddPair(list.get(), ASCIIToUTF16("Graphics card"), it2->second);
+ } else if (it2->first == "szDriverNodeStrongName") {
+ AddPair(list.get(), ASCIIToUTF16("Driver name (strong)"),
+ it2->second);
+ } else if (it2->first == "szDriverName") {
+ AddPair(list.get(), ASCIIToUTF16("Driver display name"), it2->second);
+ }
+ }
+ }
+ }
+#endif
+
+ AddPair(list.get(), base::string16(), "--- GPU driver, more information ---");
+ AddPair(list.get(), ASCIIToUTF16("Vendor Id"),
+ base::StringPrintf("0x%04x", gpu_info.gpu.vendor_id));
+ AddPair(list.get(), ASCIIToUTF16("Device Id"),
+ base::StringPrintf("0x%04x", gpu_info.gpu.device_id));
+ AddPair(list.get(), ASCIIToUTF16("Driver vendor"), gpu_info.driver_vendor);
+ AddPair(list.get(), ASCIIToUTF16("Driver version"), gpu_info.driver_version);
+ AddPair(list.get(), ASCIIToUTF16("Driver date"), gpu_info.driver_date);
+ AddPair(list.get(), ASCIIToUTF16("Pixel shader version"),
+ gpu_info.pixel_shader_version);
+ AddPair(list.get(), ASCIIToUTF16("Vertex shader version"),
+ gpu_info.vertex_shader_version);
+ AddPair(list.get(), ASCIIToUTF16("GL_VENDOR"), gpu_info.gl_vendor);
+ AddPair(list.get(), ASCIIToUTF16("GL_RENDERER"), gpu_info.gl_renderer);
+ AddPair(list.get(), ASCIIToUTF16("GL_VERSION"), gpu_info.gl_version);
+ AddPair(list.get(), ASCIIToUTF16("GL_EXTENSIONS"), gpu_info.gl_extensions);
+
+ base::DictionaryValue flashInfo;
+ flashInfo.Set("flashInfo", std::move(list));
+ web_ui()->CallJavascriptFunctionUnsafe("returnFlashInfo", flashInfo);
+}
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// FlashUI
+//
+///////////////////////////////////////////////////////////////////////////////
+
+FlashUI::FlashUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ base::RecordAction(UserMetricsAction("ViewAboutFlash"));
+
+ web_ui->AddMessageHandler(base::MakeUnique<FlashDOMHandler>());
+
+ // Set up the about:flash source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreateFlashUIHTMLSource());
+}
+
+// static
+base::RefCountedMemory* FlashUI::GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor) {
+ // Use the default icon for now.
+ return NULL;
+}
diff --git a/chromium/chrome/browser/ui/webui/flash_ui.h b/chromium/chrome/browser/ui/webui/flash_ui.h
new file mode 100644
index 00000000000..c5670699cf4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/flash_ui.h
@@ -0,0 +1,28 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_FLASH_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_FLASH_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "ui/base/layout.h"
+
+namespace base {
+class RefCountedMemory;
+}
+
+// The Web UI handler for about:flash.
+class FlashUI : public content::WebUIController {
+ public:
+ explicit FlashUI(content::WebUI* web_ui);
+
+ static base::RefCountedMemory* GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FlashUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_FLASH_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/foreign_session_handler.cc b/chromium/chrome/browser/ui/webui/foreign_session_handler.cc
new file mode 100644
index 00000000000..425ffff9c0a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/foreign_session_handler.cc
@@ -0,0 +1,436 @@
+// 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/ui/webui/foreign_session_handler.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/i18n/time_formatting.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sessions/session_restore.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/time_format.h"
+#include "ui/base/webui/web_ui_util.h"
+
+namespace browser_sync {
+
+namespace {
+
+// Maximum number of sessions we're going to display on the NTP
+const size_t kMaxSessionsToShow = 10;
+
+// Helper method to create JSON compatible objects from Session objects.
+std::unique_ptr<base::DictionaryValue> SessionTabToValue(
+ const ::sessions::SessionTab& tab) {
+ if (tab.navigations.empty())
+ return nullptr;
+
+ int selected_index = std::min(tab.current_navigation_index,
+ static_cast<int>(tab.navigations.size() - 1));
+ const ::sessions::SerializedNavigationEntry& current_navigation =
+ tab.navigations.at(selected_index);
+ GURL tab_url = current_navigation.virtual_url();
+ if (!tab_url.is_valid() ||
+ tab_url.spec() == chrome::kChromeUINewTabURL) {
+ return nullptr;
+ }
+
+ std::unique_ptr<base::DictionaryValue> dictionary(
+ new base::DictionaryValue());
+ NewTabUI::SetUrlTitleAndDirection(dictionary.get(),
+ current_navigation.title(), tab_url);
+ dictionary->SetString("type", "tab");
+ dictionary->SetDouble("timestamp",
+ static_cast<double>(tab.timestamp.ToInternalValue()));
+ // TODO(jeremycho): This should probably be renamed to tabId to avoid
+ // confusion with the ID corresponding to a session. Investigate all the
+ // places (C++ and JS) where this is being used. (http://crbug.com/154865).
+ dictionary->SetInteger("sessionId", tab.tab_id.id());
+ return dictionary;
+}
+
+// Helper for initializing a boilerplate SessionWindow JSON compatible object.
+std::unique_ptr<base::DictionaryValue> BuildWindowData(
+ base::Time modification_time,
+ SessionID::id_type window_id) {
+ std::unique_ptr<base::DictionaryValue> dictionary(
+ new base::DictionaryValue());
+ // The items which are to be written into |dictionary| are also described in
+ // chrome/browser/resources/ntp4/other_sessions.js in @typedef for WindowData.
+ // Please update it whenever you add or remove any keys here.
+ dictionary->SetString("type", "window");
+ dictionary->SetDouble("timestamp", modification_time.ToInternalValue());
+ const base::TimeDelta last_synced = base::Time::Now() - modification_time;
+ // If clock skew leads to a future time, or we last synced less than a minute
+ // ago, output "Just now".
+ dictionary->SetString(
+ "userVisibleTimestamp",
+ last_synced < base::TimeDelta::FromMinutes(1)
+ ? l10n_util::GetStringUTF16(IDS_SYNC_TIME_JUST_NOW)
+ : ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_ELAPSED,
+ ui::TimeFormat::LENGTH_SHORT, last_synced));
+
+ dictionary->SetInteger("sessionId", window_id);
+ return dictionary;
+}
+
+// Helper method to create JSON compatible objects from SessionWindow objects.
+std::unique_ptr<base::DictionaryValue> SessionWindowToValue(
+ const ::sessions::SessionWindow& window) {
+ if (window.tabs.empty())
+ return nullptr;
+ std::unique_ptr<base::ListValue> tab_values(new base::ListValue());
+ // Calculate the last |modification_time| for all entries within a window.
+ base::Time modification_time = window.timestamp;
+ for (const std::unique_ptr<sessions::SessionTab>& tab : window.tabs) {
+ std::unique_ptr<base::DictionaryValue> tab_value(
+ SessionTabToValue(*tab.get()));
+ if (tab_value.get()) {
+ modification_time = std::max(modification_time,
+ tab->timestamp);
+ tab_values->Append(std::move(tab_value));
+ }
+ }
+ if (tab_values->GetSize() == 0)
+ return nullptr;
+ std::unique_ptr<base::DictionaryValue> dictionary(
+ BuildWindowData(window.timestamp, window.window_id.id()));
+ dictionary->Set("tabs", std::move(tab_values));
+ return dictionary;
+}
+
+} // namespace
+
+ForeignSessionHandler::ForeignSessionHandler() : scoped_observer_(this) {
+ load_attempt_time_ = base::TimeTicks::Now();
+}
+
+ForeignSessionHandler::~ForeignSessionHandler() {}
+
+// static
+void ForeignSessionHandler::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterDictionaryPref(prefs::kNtpCollapsedForeignSessions);
+}
+
+// static
+void ForeignSessionHandler::OpenForeignSessionTab(
+ content::WebUI* web_ui,
+ const std::string& session_string_value,
+ SessionID::id_type window_num,
+ SessionID::id_type tab_id,
+ const WindowOpenDisposition& disposition) {
+ sync_sessions::OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(web_ui);
+ if (!open_tabs)
+ return;
+
+ // We don't actually care about |window_num|, this is just a sanity check.
+ DCHECK_LT(kInvalidId, window_num);
+ const ::sessions::SessionTab* tab;
+ if (!open_tabs->GetForeignTab(session_string_value, tab_id, &tab)) {
+ LOG(ERROR) << "Failed to load foreign tab.";
+ return;
+ }
+ if (tab->navigations.empty()) {
+ LOG(ERROR) << "Foreign tab no longer has valid navigations.";
+ return;
+ }
+ SessionRestore::RestoreForeignSessionTab(
+ web_ui->GetWebContents(), *tab, disposition);
+}
+
+// static
+void ForeignSessionHandler::OpenForeignSessionWindows(
+ content::WebUI* web_ui,
+ const std::string& session_string_value,
+ SessionID::id_type window_num) {
+ sync_sessions::OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(web_ui);
+ if (!open_tabs)
+ return;
+
+ std::vector<const ::sessions::SessionWindow*> windows;
+ // Note: we don't own the ForeignSessions themselves.
+ if (!open_tabs->GetForeignSession(session_string_value, &windows)) {
+ LOG(ERROR) << "ForeignSessionHandler failed to get session data from"
+ "OpenTabsUIDelegate.";
+ return;
+ }
+ std::vector<const ::sessions::SessionWindow*>::const_iterator iter_begin =
+ windows.begin() + (window_num == kInvalidId ? 0 : window_num);
+ std::vector<const ::sessions::SessionWindow*>::const_iterator iter_end =
+ window_num == kInvalidId ?
+ std::vector<const ::sessions::SessionWindow*>::const_iterator(
+ windows.end()) : iter_begin + 1;
+ SessionRestore::RestoreForeignSessionWindows(Profile::FromWebUI(web_ui),
+ iter_begin, iter_end);
+}
+
+// static
+sync_sessions::OpenTabsUIDelegate* ForeignSessionHandler::GetOpenTabsUIDelegate(
+ content::WebUI* web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ browser_sync::ProfileSyncService* service =
+ ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
+
+ // Only return the delegate if it exists and it is done syncing sessions.
+ if (service && service->IsSyncActive())
+ return service->GetOpenTabsUIDelegate();
+
+ return NULL;
+}
+
+void ForeignSessionHandler::RegisterMessages() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+ browser_sync::ProfileSyncService* service =
+ ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
+
+ // NOTE: The ProfileSyncService can be null in tests.
+ if (service)
+ scoped_observer_.Add(service);
+
+ web_ui()->RegisterMessageCallback("deleteForeignSession",
+ base::Bind(&ForeignSessionHandler::HandleDeleteForeignSession,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("getForeignSessions",
+ base::Bind(&ForeignSessionHandler::HandleGetForeignSessions,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("openForeignSession",
+ base::Bind(&ForeignSessionHandler::HandleOpenForeignSession,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("setForeignSessionCollapsed",
+ base::Bind(&ForeignSessionHandler::HandleSetForeignSessionCollapsed,
+ base::Unretained(this)));
+}
+
+void ForeignSessionHandler::OnSyncConfigurationCompleted(
+ syncer::SyncService* sync) {
+ HandleGetForeignSessions(nullptr);
+}
+
+void ForeignSessionHandler::OnForeignSessionUpdated(syncer::SyncService* sync) {
+ HandleGetForeignSessions(nullptr);
+}
+
+base::string16 ForeignSessionHandler::FormatSessionTime(
+ const base::Time& time) {
+ // Return a time like "1 hour ago", "2 days ago", etc.
+ base::Time now = base::Time::Now();
+ // TimeFormat does not support negative TimeDelta values, so then we use 0.
+ return ui::TimeFormat::Simple(
+ ui::TimeFormat::FORMAT_ELAPSED, ui::TimeFormat::LENGTH_SHORT,
+ now < time ? base::TimeDelta() : now - time);
+}
+
+void ForeignSessionHandler::HandleGetForeignSessions(
+ const base::ListValue* /*args*/) {
+ sync_sessions::OpenTabsUIDelegate* open_tabs =
+ GetOpenTabsUIDelegate(web_ui());
+ std::vector<const sync_sessions::SyncedSession*> sessions;
+
+ base::ListValue session_list;
+ if (open_tabs && open_tabs->GetAllForeignSessions(&sessions)) {
+ if (!load_attempt_time_.is_null()) {
+ UMA_HISTOGRAM_TIMES("Sync.SessionsRefreshDelay",
+ base::TimeTicks::Now() - load_attempt_time_);
+ load_attempt_time_ = base::TimeTicks();
+ }
+
+ // Use a pref to keep track of sessions that were collapsed by the user.
+ // To prevent the pref from accumulating stale sessions, clear it each time
+ // and only add back sessions that are still current.
+ DictionaryPrefUpdate pref_update(Profile::FromWebUI(web_ui())->GetPrefs(),
+ prefs::kNtpCollapsedForeignSessions);
+ base::DictionaryValue* current_collapsed_sessions = pref_update.Get();
+ std::unique_ptr<base::DictionaryValue> collapsed_sessions(
+ current_collapsed_sessions->DeepCopy());
+ current_collapsed_sessions->Clear();
+
+ // Note: we don't own the SyncedSessions themselves.
+ for (size_t i = 0; i < sessions.size() && i < kMaxSessionsToShow; ++i) {
+ const sync_sessions::SyncedSession* session = sessions[i];
+ const std::string& session_tag = session->session_tag;
+ std::unique_ptr<base::DictionaryValue> session_data(
+ new base::DictionaryValue());
+ // The items which are to be written into |session_data| are also
+ // described in chrome/browser/resources/history/externs.js
+ // @typedef for ForeignSession. Please update it whenever you add or
+ // 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("modifiedTime",
+ FormatSessionTime(session->modified_time));
+ session_data->SetDouble("timestamp", session->modified_time.ToJsTime());
+
+ bool is_collapsed = collapsed_sessions->HasKey(session_tag);
+ session_data->SetBoolean("collapsed", is_collapsed);
+ if (is_collapsed)
+ current_collapsed_sessions->SetBoolean(session_tag, true);
+
+ std::unique_ptr<base::ListValue> window_list(new base::ListValue());
+ const std::string group_name =
+ base::FieldTrialList::FindFullName("TabSyncByRecency");
+ if (group_name != "Enabled") {
+ // Order tabs by visual order within window.
+ for (const auto& window_pair : session->windows) {
+ std::unique_ptr<base::DictionaryValue> window_data(
+ SessionWindowToValue(window_pair.second->wrapped_window));
+ if (window_data.get())
+ window_list->Append(std::move(window_data));
+ }
+ } else {
+ // Order tabs by recency. This involves creating a synthetic singleton
+ // window that contains all the tabs of the session.
+ base::Time modification_time;
+ std::vector<const ::sessions::SessionTab*> tabs;
+ open_tabs->GetForeignSessionTabs(session_tag, &tabs);
+ std::unique_ptr<base::ListValue> tab_values(new base::ListValue());
+ for (const ::sessions::SessionTab* tab : tabs) {
+ std::unique_ptr<base::DictionaryValue> tab_value(
+ SessionTabToValue(*tab));
+ if (tab_value.get()) {
+ modification_time = std::max(modification_time, tab->timestamp);
+ tab_values->Append(std::move(tab_value));
+ }
+ }
+ if (tab_values->GetSize() != 0) {
+ std::unique_ptr<base::DictionaryValue> window_data(
+ BuildWindowData(modification_time, 1));
+ window_data->Set("tabs", std::move(tab_values));
+ window_list->Append(std::move(window_data));
+ }
+ }
+
+ session_data->Set("windows", std::move(window_list));
+ session_list.Append(std::move(session_data));
+ }
+ }
+ web_ui()->CallJavascriptFunctionUnsafe("setForeignSessions", session_list);
+}
+
+void ForeignSessionHandler::HandleOpenForeignSession(
+ const base::ListValue* args) {
+ size_t num_args = args->GetSize();
+ // Expect either 1 or 8 args. For restoring an entire session, only
+ // one argument is required -- the session tag. To restore a tab,
+ // the additional args required are the window id, the tab id,
+ // and 4 properties of the event object (button, altKey, ctrlKey,
+ // metaKey, shiftKey) for determining how to open the tab.
+ if (num_args != 8U && num_args != 1U) {
+ LOG(ERROR) << "openForeignSession called with " << args->GetSize()
+ << " arguments.";
+ return;
+ }
+
+ // Extract the session tag (always provided).
+ std::string session_string_value;
+ if (!args->GetString(0, &session_string_value)) {
+ LOG(ERROR) << "Failed to extract session tag.";
+ return;
+ }
+
+ // Extract window number.
+ std::string window_num_str;
+ int window_num = kInvalidId;
+ if (num_args >= 2 && (!args->GetString(1, &window_num_str) ||
+ !base::StringToInt(window_num_str, &window_num))) {
+ LOG(ERROR) << "Failed to extract window number.";
+ return;
+ }
+
+ // Extract tab id.
+ std::string tab_id_str;
+ SessionID::id_type tab_id = kInvalidId;
+ if (num_args >= 3 && (!args->GetString(2, &tab_id_str) ||
+ !base::StringToInt(tab_id_str, &tab_id))) {
+ LOG(ERROR) << "Failed to extract tab SessionID.";
+ return;
+ }
+
+ if (tab_id != kInvalidId) {
+ WindowOpenDisposition disposition = webui::GetDispositionFromClick(args, 3);
+ OpenForeignSessionTab(
+ web_ui(), session_string_value, window_num, tab_id, disposition);
+ } else {
+ OpenForeignSessionWindows(web_ui(), session_string_value, window_num);
+ }
+}
+
+void ForeignSessionHandler::HandleDeleteForeignSession(
+ const base::ListValue* args) {
+ if (args->GetSize() != 1U) {
+ LOG(ERROR) << "Wrong number of args to deleteForeignSession";
+ return;
+ }
+
+ // Get the session tag argument (required).
+ std::string session_tag;
+ if (!args->GetString(0, &session_tag)) {
+ LOG(ERROR) << "Unable to extract session tag";
+ return;
+ }
+
+ sync_sessions::OpenTabsUIDelegate* open_tabs =
+ GetOpenTabsUIDelegate(web_ui());
+ if (open_tabs)
+ open_tabs->DeleteForeignSession(session_tag);
+}
+
+void ForeignSessionHandler::HandleSetForeignSessionCollapsed(
+ const base::ListValue* args) {
+ if (args->GetSize() != 2U) {
+ LOG(ERROR) << "Wrong number of args to setForeignSessionCollapsed";
+ return;
+ }
+
+ // Get the session tag argument (required).
+ std::string session_tag;
+ if (!args->GetString(0, &session_tag)) {
+ LOG(ERROR) << "Unable to extract session tag";
+ return;
+ }
+
+ bool is_collapsed;
+ if (!args->GetBoolean(1, &is_collapsed)) {
+ LOG(ERROR) << "Unable to extract boolean argument";
+ return;
+ }
+
+ // Store session tags for collapsed sessions in a preference so that the
+ // collapsed state persists.
+ PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+ DictionaryPrefUpdate update(prefs, prefs::kNtpCollapsedForeignSessions);
+ if (is_collapsed)
+ update.Get()->SetBoolean(session_tag, true);
+ else
+ update.Get()->Remove(session_tag, NULL);
+}
+
+} // namespace browser_sync
diff --git a/chromium/chrome/browser/ui/webui/foreign_session_handler.h b/chromium/chrome/browser/ui/webui/foreign_session_handler.h
new file mode 100644
index 00000000000..d0cb2452b03
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/foreign_session_handler.h
@@ -0,0 +1,97 @@
+// Copyright 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_FOREIGN_SESSION_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_FOREIGN_SESSION_HANDLER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "base/time/time.h"
+#include "chrome/browser/sessions/session_service.h"
+#include "components/sync/driver/sync_service_observer.h"
+#include "components/sync_sessions/open_tabs_ui_delegate.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace syncer {
+class SyncService;
+}
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+namespace browser_sync {
+
+class ForeignSessionHandler : public content::WebUIMessageHandler,
+ public syncer::SyncServiceObserver {
+ public:
+ // Invalid value, used to note that we don't have a tab or window number.
+ static const int kInvalidId = -1;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ ForeignSessionHandler();
+ ~ForeignSessionHandler() override;
+
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ static void OpenForeignSessionTab(content::WebUI* web_ui,
+ const std::string& session_string_value,
+ SessionID::id_type window_num,
+ SessionID::id_type tab_id,
+ const WindowOpenDisposition& disposition);
+
+ static void OpenForeignSessionWindows(content::WebUI* web_ui,
+ const std::string& session_string_value,
+ SessionID::id_type window_num);
+
+ // Returns a pointer to the current session model associator or NULL.
+ static sync_sessions::OpenTabsUIDelegate* GetOpenTabsUIDelegate(
+ content::WebUI* web_ui);
+
+ private:
+ // syncer::SyncServiceObserver:
+ void OnSyncConfigurationCompleted(syncer::SyncService* sync) override;
+ void OnForeignSessionUpdated(syncer::SyncService* sync) override;
+
+ // Returns a string used to show the user when a session was last modified.
+ base::string16 FormatSessionTime(const base::Time& time);
+
+ // Determines which session is to be opened, and then calls
+ // OpenForeignSession, to begin the process of opening a new browser window.
+ // This is a javascript callback handler.
+ void HandleOpenForeignSession(const base::ListValue* args);
+
+ // Determines whether foreign sessions should be obtained from the sync model.
+ // This is a javascript callback handler, and it is also called when the sync
+ // model has changed and the new tab page needs to reflect the changes.
+ void HandleGetForeignSessions(const base::ListValue* args);
+
+ // Delete a foreign session. This will remove it from the list of foreign
+ // sessions on all devices. It will reappear if the session is re-activated
+ // on the original device.
+ // This is a javascript callback handler.
+ void HandleDeleteForeignSession(const base::ListValue* args);
+
+ void HandleSetForeignSessionCollapsed(const base::ListValue* args);
+
+ // ScopedObserver used to observe the ProfileSyncService.
+ ScopedObserver<syncer::SyncService, syncer::SyncServiceObserver>
+ scoped_observer_;
+
+ // The time at which this WebUI was created. Used to calculate how long
+ // the WebUI was present before the sessions data was visible.
+ base::TimeTicks load_attempt_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(ForeignSessionHandler);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_UI_WEBUI_FOREIGN_SESSION_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/gcm_internals_ui.cc b/chromium/chrome/browser/ui/webui/gcm_internals_ui.cc
new file mode 100644
index 00000000000..310e5c844d4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/gcm_internals_ui.cc
@@ -0,0 +1,180 @@
+// 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/ui/webui/gcm_internals_ui.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "chrome/browser/gcm/gcm_profile_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "components/gcm_driver/gcm_client.h"
+#include "components/gcm_driver/gcm_driver.h"
+#include "components/gcm_driver/gcm_internals_constants.h"
+#include "components/gcm_driver/gcm_internals_helper.h"
+#include "components/gcm_driver/gcm_profile_service.h"
+#include "components/grit/components_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace {
+
+// Class acting as a controller of the chrome://gcm-internals WebUI.
+class GcmInternalsUIMessageHandler : public content::WebUIMessageHandler {
+ public:
+ GcmInternalsUIMessageHandler();
+ ~GcmInternalsUIMessageHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ // Return all of the GCM related infos to the gcm-internals page by calling
+ // Javascript callback function
+ // |gcm-internals.returnInfo()|.
+ void ReturnResults(Profile* profile, gcm::GCMProfileService* profile_service,
+ const gcm::GCMClient::GCMStatistics* stats) const;
+
+ // Request all of the GCM related infos through gcm profile service.
+ void RequestAllInfo(const base::ListValue* args);
+
+ // Enables/disables GCM activity recording through gcm profile service.
+ void SetRecording(const base::ListValue* args);
+
+ // Callback function of the request for all gcm related infos.
+ void RequestGCMStatisticsFinished(
+ const gcm::GCMClient::GCMStatistics& args) const;
+
+ // Factory for creating references in callbacks.
+ base::WeakPtrFactory<GcmInternalsUIMessageHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(GcmInternalsUIMessageHandler);
+};
+
+GcmInternalsUIMessageHandler::GcmInternalsUIMessageHandler()
+ : weak_ptr_factory_(this) {}
+
+GcmInternalsUIMessageHandler::~GcmInternalsUIMessageHandler() {}
+
+void GcmInternalsUIMessageHandler::ReturnResults(
+ Profile* profile,
+ gcm::GCMProfileService* profile_service,
+ const gcm::GCMClient::GCMStatistics* stats) const {
+ base::DictionaryValue results;
+ gcm_driver::SetGCMInternalsInfo(stats, profile_service, profile->GetPrefs(),
+ &results);
+ web_ui()->CallJavascriptFunctionUnsafe(gcm_driver::kSetGcmInternalsInfo,
+ results);
+}
+
+void GcmInternalsUIMessageHandler::RequestAllInfo(
+ const base::ListValue* args) {
+ if (args->GetSize() != 1) {
+ NOTREACHED();
+ return;
+ }
+ bool clear_logs = false;
+ if (!args->GetBoolean(0, &clear_logs)) {
+ NOTREACHED();
+ return;
+ }
+
+ gcm::GCMDriver::ClearActivityLogs clear_activity_logs =
+ clear_logs ? gcm::GCMDriver::CLEAR_LOGS : gcm::GCMDriver::KEEP_LOGS;
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ gcm::GCMProfileService* profile_service =
+ gcm::GCMProfileServiceFactory::GetForProfile(profile);
+
+ if (!profile_service || !profile_service->driver()) {
+ ReturnResults(profile, NULL, NULL);
+ } else {
+ profile_service->driver()->GetGCMStatistics(
+ base::Bind(&GcmInternalsUIMessageHandler::RequestGCMStatisticsFinished,
+ weak_ptr_factory_.GetWeakPtr()),
+ clear_activity_logs);
+ }
+}
+
+void GcmInternalsUIMessageHandler::SetRecording(const base::ListValue* args) {
+ if (args->GetSize() != 1) {
+ NOTREACHED();
+ return;
+ }
+ bool recording = false;
+ if (!args->GetBoolean(0, &recording)) {
+ NOTREACHED();
+ return;
+ }
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ gcm::GCMProfileService* profile_service =
+ gcm::GCMProfileServiceFactory::GetForProfile(profile);
+
+ if (!profile_service) {
+ ReturnResults(profile, NULL, NULL);
+ return;
+ }
+ // Get fresh stats after changing recording setting.
+ profile_service->driver()->SetGCMRecording(
+ base::Bind(
+ &GcmInternalsUIMessageHandler::RequestGCMStatisticsFinished,
+ weak_ptr_factory_.GetWeakPtr()),
+ recording);
+}
+
+void GcmInternalsUIMessageHandler::RequestGCMStatisticsFinished(
+ const gcm::GCMClient::GCMStatistics& stats) const {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ DCHECK(profile);
+ gcm::GCMProfileService* profile_service =
+ gcm::GCMProfileServiceFactory::GetForProfile(profile);
+ DCHECK(profile_service);
+ ReturnResults(profile, profile_service, &stats);
+}
+
+void GcmInternalsUIMessageHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ gcm_driver::kGetGcmInternalsInfo,
+ base::Bind(&GcmInternalsUIMessageHandler::RequestAllInfo,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ gcm_driver::kSetGcmInternalsRecording,
+ base::Bind(&GcmInternalsUIMessageHandler::SetRecording,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+} // namespace
+
+GCMInternalsUI::GCMInternalsUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ // Set up the chrome://gcm-internals source.
+ content::WebUIDataSource* html_source =
+ content::WebUIDataSource::Create(chrome::kChromeUIGCMInternalsHost);
+
+ html_source->SetJsonPath("strings.js");
+
+ // Add required resources.
+ html_source->AddResourcePath(gcm_driver::kGcmInternalsCSS,
+ IDR_GCM_DRIVER_GCM_INTERNALS_CSS);
+ html_source->AddResourcePath(gcm_driver::kGcmInternalsJS,
+ IDR_GCM_DRIVER_GCM_INTERNALS_JS);
+ html_source->SetDefaultResource(IDR_GCM_DRIVER_GCM_INTERNALS_HTML);
+ html_source->UseGzip(std::unordered_set<std::string>());
+
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, html_source);
+
+ web_ui->AddMessageHandler(base::MakeUnique<GcmInternalsUIMessageHandler>());
+}
+
+GCMInternalsUI::~GCMInternalsUI() {}
diff --git a/chromium/chrome/browser/ui/webui/gcm_internals_ui.h b/chromium/chrome/browser/ui/webui/gcm_internals_ui.h
new file mode 100644
index 00000000000..9a3b0e8427a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/gcm_internals_ui.h
@@ -0,0 +1,21 @@
+// 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_UI_WEBUI_GCM_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_GCM_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The WebUI for chrome://gcm-internals.
+class GCMInternalsUI : public content::WebUIController {
+ public:
+ explicit GCMInternalsUI(content::WebUI* web_ui);
+ ~GCMInternalsUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GCMInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_GCM_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/help/help_browsertest.js b/chromium/chrome/browser/ui/webui/help/help_browsertest.js
new file mode 100644
index 00000000000..45bd2727897
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/help/help_browsertest.js
@@ -0,0 +1,147 @@
+// 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.
+
+/**
+ * TestFixture for extension settings WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function HelpPageWebUITest() {}
+
+HelpPageWebUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /** @override */
+ runAccessibilityChecks: true,
+
+ /** @override */
+ accessibilityIssuesAreErrors: true,
+
+ browsePreload: 'chrome://help-frame/',
+};
+
+// Test opening extension settings has correct location.
+TEST_F('HelpPageWebUITest', 'testOpenHelpPage', function() {
+ assertEquals(this.browsePreload, document.location.href);
+});
+
+GEN('#if defined(OS_LINUX) || defined(GOOGLE_CHROME_BUILD)');
+
+TEST_F('HelpPageWebUITest', 'testUpdateStateIcon', function() {
+ var icon = $('update-status-icon');
+ help.HelpPage.setUpdateStatus('checking', '');
+ assertEquals(icon.getAttribute('class'), 'help-page-icon working');
+ help.HelpPage.setUpdateStatus('updating', '');
+ assertEquals(icon.getAttribute('class'), 'help-page-icon working');
+ help.HelpPage.setUpdateStatus('nearly_updated', '');
+ assertEquals(icon.getAttribute('class'), 'help-page-icon up-to-date');
+ help.HelpPage.setUpdateStatus('updated', '');
+ assertEquals(icon.getAttribute('class'), 'help-page-icon up-to-date');
+ help.HelpPage.setUpdateStatus('failed', '');
+ assertEquals(icon.getAttribute('class'), 'help-page-icon failed');
+ help.HelpPage.setUpdateStatus('disabled_by_admin', '');
+ assertEquals(icon.getAttribute('class'), 'help-page-icon disabled-by-admin');
+});
+
+// Test that repeated calls to setUpdateStatus work.
+TEST_F('HelpPageWebUITest', 'testUpdateState', function() {
+ var relaunch = $('relaunch');
+ var container = $('update-status-container');
+ var update = $('request-update');
+
+ help.HelpPage.setUpdateStatus('updated', '');
+ expectTrue(relaunch.hidden);
+ expectTrue(cr.isChromeOS == container.hidden);
+ expectTrue(!cr.isChromeOS || !update.hidden && !update.disabled);
+
+ help.HelpPage.setUpdateStatus('disabled', '');
+ expectTrue(relaunch.hidden);
+ expectTrue(container.hidden);
+ expectTrue(!cr.isChromeOS || update.hidden);
+
+ help.HelpPage.setUpdateStatus('nearly_updated', '');
+ expectTrue(!relaunch.hidden);
+ expectTrue(!container.hidden);
+ expectTrue(!cr.isChromeOS || update.hidden);
+
+ help.HelpPage.setUpdateStatus('disabled', '');
+ expectTrue($('relaunch').hidden);
+ expectTrue($('update-status-container').hidden);
+ expectTrue(!cr.isChromeOS || update.hidden);
+});
+
+GEN('#endif');
+
+GEN('#if defined(OS_CHROMEOS)');
+
+// Test that the request update button is shown and hidden properly.
+TEST_F('HelpPageWebUITest', 'testRequestUpdate', function() {
+ var container = $('update-status-container');
+ var update = $('request-update');
+ var policyIcon = $('controlled-feature-icon');
+
+ help.HelpPage.setUpdateStatus('updated', '');
+ expectTrue(container.hidden);
+ expectTrue(!update.hidden && !update.disabled);
+ expectTrue(policyIcon.hidden);
+
+ update.click();
+ expectTrue(!update.hidden && update.disabled);
+ expectFalse(container.hidden);
+ expectTrue(policyIcon.hidden);
+
+ help.HelpPage.setUpdateStatus('checking', '');
+ expectFalse(container.hidden);
+ expectTrue(!update.hidden && update.disabled);
+ expectTrue(policyIcon.hidden);
+
+ help.HelpPage.setUpdateStatus('failed', 'Error');
+ expectFalse(container.hidden);
+ expectTrue(!update.hidden && !update.disabled);
+ expectTrue(policyIcon.hidden);
+
+ update.click();
+ help.HelpPage.setUpdateStatus('checking', '');
+ expectFalse(container.hidden);
+ expectTrue(!update.hidden && update.disabled);
+ expectTrue(policyIcon.hidden);
+
+ help.HelpPage.setUpdateStatus('nearly_updated', '');
+ expectFalse(container.hidden);
+ expectTrue(update.hidden);
+ expectTrue(policyIcon.hidden);
+
+ help.HelpPage.setUpdateStatus('updated', '');
+ expectFalse(container.hidden);
+ expectTrue(!update.hidden && update.disabled);
+ expectTrue(policyIcon.hidden);
+
+ help.HelpPage.setUpdateStatus('disabled_by_admin', '');
+ expectTrue(container.hidden);
+ expectTrue(!update.hidden && update.disabled);
+ expectFalse(policyIcon.hidden);
+});
+
+// Test that the EndofLife String is shown and hidden properly.
+TEST_F('HelpPageWebUITest', 'testUpdateEolMessage', function() {
+ // Enable when failure is resolved.
+ // AX_TEXT_04: http://crbug.com/570563
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'linkWithUnclearPurpose',
+ '#eol-learnMore > A');
+
+ var updateStatusContainer = $('update-status-container');
+ var update = $('request-update');
+ var eolStatusContainer = $('eol-status-container');
+
+ help.HelpPage.updateEolMessage('device_supported', '');
+ expectTrue(eolStatusContainer.hidden);
+
+ help.HelpPage.updateEolMessage('device_endoflife', '');
+ expectFalse(eolStatusContainer.hidden);
+ expectTrue(update.disabled);
+ expectTrue(updateStatusContainer.hidden);
+});
+
+GEN('#endif');
diff --git a/chromium/chrome/browser/ui/webui/help/help_handler.cc b/chromium/chrome/browser/ui/webui/help/help_handler.cc
new file mode 100644
index 00000000000..6f729d1651b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/help/help_handler.cc
@@ -0,0 +1,766 @@
+// 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/ui/webui/help/help_handler.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "ash/system/devicetype_utils.h"
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/i18n/message_formatter.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/obsolete_system/obsolete_system.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/chrome_content_client.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/google/core/browser/google_util.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/policy_constants.h"
+#include "components/strings/grit/components_chromium_strings.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/version_info/version_info.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "v8/include/v8-version-string.h"
+
+#if defined(OS_CHROMEOS)
+#include "base/files/file_util_proxy.h"
+#include "base/i18n/time_formatting.h"
+#include "base/sys_info.h"
+#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
+#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/chromeos/settings/cros_settings.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/chromeos/image_source.h"
+#include "chrome/browser/ui/webui/help/help_utils_chromeos.h"
+#include "chrome/browser/ui/webui/help/version_updater_chromeos.h"
+#include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/power_manager_client.h"
+#include "chromeos/system/statistics_provider.h"
+#include "components/prefs/pref_service.h"
+#include "components/user_manager/user_manager.h"
+#endif
+
+using base::ListValue;
+using content::BrowserThread;
+
+namespace {
+
+#if defined(OS_CHROMEOS)
+
+// Directory containing the regulatory labels for supported regions.
+const char kRegulatoryLabelsDirectory[] = "regulatory_labels";
+
+// File names of the image file and the file containing alt text for the label.
+const char kRegulatoryLabelImageFilename[] = "label.png";
+const char kRegulatoryLabelTextFilename[] = "label.txt";
+
+// Default region code to use if there's no label for the VPD region code.
+const char kDefaultRegionCode[] = "us";
+
+struct RegulatoryLabel {
+ const std::string label_text;
+ const std::string image_url;
+};
+
+// Returns message that informs user that for update it's better to
+// connect to a network of one of the allowed types.
+base::string16 GetAllowedConnectionTypesMessage() {
+ // Old help page does not support interactive-updates over cellular, so just
+ // sets |interactive| to false to make its behavior the same as before.
+ if (help_utils_chromeos::IsUpdateOverCellularAllowed(
+ false /* interactive */)) {
+ return l10n_util::GetStringUTF16(IDS_UPGRADE_NETWORK_LIST_CELLULAR_ALLOWED);
+ } else {
+ return l10n_util::GetStringUTF16(
+ IDS_UPGRADE_NETWORK_LIST_CELLULAR_DISALLOWED);
+ }
+}
+
+// Returns true if the device is enterprise managed, false otherwise.
+bool IsEnterpriseManaged() {
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ return connector->IsEnterpriseManaged();
+}
+
+// Returns true if current user can change channel, false otherwise.
+bool CanChangeChannel(Profile* profile) {
+ bool value = false;
+ chromeos::CrosSettings::Get()->GetBoolean(chromeos::kReleaseChannelDelegated,
+ &value);
+
+ // On a managed machine we delegate this setting to the users of the same
+ // domain only if the policy value is "domain".
+ if (IsEnterpriseManaged()) {
+ if (!value)
+ return false;
+ // Get the currently logged in user and strip the domain part only.
+ std::string domain = "";
+ const user_manager::User* user =
+ profile ? chromeos::ProfileHelper::Get()->GetUserByProfile(profile)
+ : nullptr;
+ std::string email =
+ user ? user->GetAccountId().GetUserEmail() : std::string();
+ size_t at_pos = email.find('@');
+ if (at_pos != std::string::npos && at_pos + 1 < email.length())
+ domain = email.substr(email.find('@') + 1);
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ return domain == connector->GetEnterpriseDomain();
+ } else {
+ chromeos::OwnerSettingsServiceChromeOS* service =
+ chromeos::OwnerSettingsServiceChromeOSFactory::GetInstance()
+ ->GetForBrowserContext(profile);
+ // On non managed machines we have local owner who is the only one to change
+ // anything. Ensure that ReleaseChannelDelegated is false.
+ if (service && service->IsOwner())
+ return !value;
+ }
+ return false;
+}
+
+// Returns the path of the regulatory labels directory for a given region, if
+// found. Must be called from the blocking pool.
+base::FilePath GetRegulatoryLabelDirForRegion(const std::string& region) {
+ // Generate the path under the asset dir or URL host to the regulatory files
+ // for the region, e.g., "regulatory_labels/us/".
+ const base::FilePath region_path =
+ base::FilePath(kRegulatoryLabelsDirectory).AppendASCII(region);
+
+ // Check for file existence starting in /usr/share/chromeos-assets/, e.g.,
+ // "/usr/share/chromeos-assets/regulatory_labels/us/label.png".
+ const base::FilePath asset_dir(chrome::kChromeOSAssetPath);
+ if (base::PathExists(asset_dir.Append(region_path)
+ .AppendASCII(kRegulatoryLabelImageFilename))) {
+ return region_path;
+ }
+
+ return base::FilePath();
+}
+
+// Finds the directory for the regulatory label, using the VPD region code.
+// Also tries "us" as a fallback region. Must be called from the blocking pool.
+base::FilePath FindRegulatoryLabelDir() {
+ std::string region;
+ base::FilePath region_path;
+ // Use the VPD region code to find the label dir.
+ if (chromeos::system::StatisticsProvider::GetInstance()->GetMachineStatistic(
+ "region", &region) && !region.empty()) {
+ region_path = GetRegulatoryLabelDirForRegion(region);
+ }
+
+ // Try the fallback region code if no directory was found.
+ if (region_path.empty() && region != kDefaultRegionCode)
+ region_path = GetRegulatoryLabelDirForRegion(kDefaultRegionCode);
+
+ return region_path;
+}
+
+// Reads the file containing the regulatory label text, if found, relative to
+// the asset directory. Must be called from the blocking pool.
+std::string ReadRegulatoryLabelText(const base::FilePath& path) {
+ base::FilePath text_path(chrome::kChromeOSAssetPath);
+ text_path = text_path.Append(path);
+ text_path = text_path.AppendASCII(kRegulatoryLabelTextFilename);
+
+ std::string contents;
+ if (base::ReadFileToString(text_path, &contents))
+ return contents;
+ return std::string();
+}
+#endif // defined(OS_CHROMEOS)
+
+} // namespace
+
+HelpHandler::HelpHandler()
+ : policy_registrar_(
+ g_browser_process->policy_service(),
+ policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string())),
+ weak_factory_(this) {
+}
+
+HelpHandler::~HelpHandler() {
+}
+
+void HelpHandler::GetLocalizedValues(base::DictionaryValue* localized_strings) {
+ struct L10nResources {
+ const char* name;
+ int ids;
+ };
+
+ static L10nResources resources[] = {
+ {"aboutTitle", IDS_ABOUT_TITLE},
+#if defined(OS_CHROMEOS)
+ {"aboutProductTitle", IDS_PRODUCT_OS_NAME},
+#else
+ {"aboutProductTitle", IDS_PRODUCT_NAME},
+#endif
+ {"aboutProductDescription", IDS_ABOUT_PRODUCT_DESCRIPTION},
+ {"relaunch", IDS_RELAUNCH_BUTTON},
+#if defined(OS_CHROMEOS)
+ {"relaunchAndPowerwash", IDS_RELAUNCH_AND_POWERWASH_BUTTON},
+#endif
+ {"productName", IDS_PRODUCT_NAME},
+ {"updateCheckStarted", IDS_UPGRADE_CHECK_STARTED},
+ {"updating", IDS_UPGRADE_UPDATING},
+#if defined(OS_CHROMEOS) || defined(OS_WIN)
+ {"updateDisabledByPolicy", IDS_UPGRADE_DISABLED_BY_POLICY},
+#endif // defined(OS_CHROMEOS) || defined(OS_WIN)
+#if defined(OS_CHROMEOS)
+ {"updateButton", IDS_UPGRADE_BUTTON},
+ {"updatingChannelSwitch", IDS_UPGRADE_UPDATING_CHANNEL_SWITCH},
+#endif
+ {"updateAlmostDone", IDS_UPGRADE_SUCCESSFUL_RELAUNCH},
+#if defined(OS_CHROMEOS)
+ {"successfulChannelSwitch", IDS_UPGRADE_SUCCESSFUL_CHANNEL_SWITCH},
+#endif
+ {"getHelpWithChrome", IDS_GET_HELP_USING_CHROME},
+ {"reportAnIssue", IDS_REPORT_AN_ISSUE},
+#if defined(OS_CHROMEOS)
+ {"platform", IDS_PLATFORM_LABEL},
+ {"arcVersion", IDS_ARC_VERSION_LABEL},
+ {"firmware", IDS_ABOUT_PAGE_FIRMWARE},
+ {"showMoreInfo", IDS_SHOW_MORE_INFO},
+ {"hideMoreInfo", IDS_HIDE_MORE_INFO},
+ {"channel", IDS_ABOUT_PAGE_CHANNEL},
+ {"stable", IDS_ABOUT_PAGE_CHANNEL_STABLE},
+ {"beta", IDS_ABOUT_PAGE_CHANNEL_BETA},
+ {"dev", IDS_ABOUT_PAGE_CHANNEL_DEVELOPMENT},
+ {"devChannelDisclaimer", IDS_ABOUT_PAGE_CHANNEL_DEVELOPMENT_DISCLAIMER},
+ {"channel-changed", IDS_ABOUT_PAGE_CHANNEL_CHANGED},
+ {"currentChannelStable", IDS_ABOUT_PAGE_CURRENT_CHANNEL_STABLE},
+ {"currentChannelBeta", IDS_ABOUT_PAGE_CURRENT_CHANNEL_BETA},
+ {"currentChannelDev", IDS_ABOUT_PAGE_CURRENT_CHANNEL_DEV},
+ {"currentChannel", IDS_ABOUT_PAGE_CURRENT_CHANNEL},
+ {"channelChangeButton", IDS_ABOUT_PAGE_CHANNEL_CHANGE_BUTTON},
+ {"channelChangeDisallowedMessage",
+ IDS_ABOUT_PAGE_CHANNEL_CHANGE_DISALLOWED_MESSAGE},
+ {"channelChangePageTitle", IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_TITLE},
+ {"channelChangePagePowerwashTitle",
+ IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_POWERWASH_TITLE},
+ {"channelChangePagePowerwashMessage",
+ IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_POWERWASH_MESSAGE},
+ {"channelChangePageDelayedChangeTitle",
+ IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_DELAYED_CHANGE_TITLE},
+ {"channelChangePageUnstableTitle",
+ IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_UNSTABLE_TITLE},
+ {"channelChangePagePowerwashButton",
+ IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_POWERWASH_BUTTON},
+ {"channelChangePageChangeButton",
+ IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_CHANGE_BUTTON},
+ {"channelChangePageCancelButton",
+ IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_CANCEL_BUTTON},
+ {"userAgent", IDS_VERSION_UI_USER_AGENT},
+ {"commandLine", IDS_VERSION_UI_COMMAND_LINE},
+ {"buildDate", IDS_VERSION_UI_BUILD_DATE},
+#endif
+#if defined(OS_MACOSX) || defined(OS_WIN)
+ {"learnMore", IDS_LEARN_MORE},
+#endif
+#if defined(OS_MACOSX)
+ {"promote", IDS_ABOUT_CHROME_PROMOTE_UPDATER},
+#endif
+ };
+
+ for (size_t i = 0; i < arraysize(resources); ++i) {
+ localized_strings->SetString(resources[i].name,
+ l10n_util::GetStringUTF16(resources[i].ids));
+ }
+
+#if defined(OS_CHROMEOS)
+ localized_strings->SetString("upToDate", ash::SubstituteChromeOSDeviceType(
+ IDS_UPGRADE_UP_TO_DATE));
+#else
+ localized_strings->SetString("upToDate", l10n_util::GetStringUTF16(
+ IDS_UPGRADE_UP_TO_DATE));
+#endif
+
+ localized_strings->SetString("updateObsoleteSystem",
+ ObsoleteSystem::LocalizedObsoleteString());
+ localized_strings->SetString("updateObsoleteSystemURL",
+ ObsoleteSystem::GetLinkURL());
+
+ localized_strings->SetString(
+ "browserVersion",
+ l10n_util::GetStringFUTF16(IDS_ABOUT_PRODUCT_VERSION,
+ BuildBrowserVersionString()));
+
+ localized_strings->SetString(
+ "productCopyright",
+ base::i18n::MessageFormatter::FormatWithNumberedArgs(
+ l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_COPYRIGHT),
+ base::Time::Now()));
+
+ base::string16 license = l10n_util::GetStringFUTF16(
+ IDS_VERSION_UI_LICENSE, base::ASCIIToUTF16(chrome::kChromiumProjectURL),
+ base::ASCIIToUTF16(chrome::kChromeUICreditsURL));
+ localized_strings->SetString("productLicense", license);
+
+#if defined(OS_CHROMEOS)
+ base::string16 os_license = l10n_util::GetStringFUTF16(
+ IDS_ABOUT_CROS_VERSION_LICENSE,
+ base::ASCIIToUTF16(chrome::kChromeUIOSCreditsURL));
+ localized_strings->SetString("productOsLicense", os_license);
+
+ base::string16 product_name = l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME);
+ localized_strings->SetString(
+ "channelChangePageDelayedChangeMessage",
+ l10n_util::GetStringFUTF16(
+ IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_DELAYED_CHANGE_MESSAGE,
+ product_name));
+ localized_strings->SetString(
+ "channelChangePageUnstableMessage",
+ l10n_util::GetStringFUTF16(
+ IDS_ABOUT_PAGE_CHANNEL_CHANGE_PAGE_UNSTABLE_MESSAGE,
+ product_name));
+
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kDisableNewChannelSwitcherUI)) {
+ localized_strings->SetBoolean("disableNewChannelSwitcherUI", true);
+ }
+
+ localized_strings->SetString(
+ "eolLearnMore", l10n_util::GetStringFUTF16(
+ IDS_ABOUT_PAGE_EOL_LEARN_MORE,
+ base::ASCIIToUTF16(chrome::kEolNotificationURL)));
+#endif
+
+ base::string16 tos = l10n_util::GetStringFUTF16(
+ IDS_ABOUT_TERMS_OF_SERVICE, base::UTF8ToUTF16(chrome::kChromeUITermsURL));
+ localized_strings->SetString("productTOS", tos);
+
+ localized_strings->SetString("jsEngine", "V8");
+ localized_strings->SetString("jsEngineVersion", V8_VERSION_STRING);
+
+ localized_strings->SetString("userAgentInfo", GetUserAgent());
+
+ base::CommandLine::StringType command_line =
+ base::CommandLine::ForCurrentProcess()->GetCommandLineString();
+ localized_strings->SetString("commandLineInfo", command_line);
+}
+
+void HelpHandler::RegisterMessages() {
+ version_updater_.reset(VersionUpdater::Create(web_ui()->GetWebContents()));
+ registrar_.Add(this, chrome::NOTIFICATION_UPGRADE_RECOMMENDED,
+ content::NotificationService::AllSources());
+ policy_registrar_.Observe(
+ policy::key::kDeviceAutoUpdateDisabled,
+ base::Bind(&HelpHandler::OnDeviceAutoUpdatePolicyChanged,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback("onPageLoaded",
+ base::Bind(&HelpHandler::OnPageLoaded, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("relaunchNow",
+ base::Bind(&HelpHandler::RelaunchNow, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("openFeedbackDialog",
+ base::Bind(&HelpHandler::OpenFeedbackDialog, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("openHelpPage",
+ base::Bind(&HelpHandler::OpenHelpPage, base::Unretained(this)));
+#if defined(OS_CHROMEOS)
+ web_ui()->RegisterMessageCallback("setChannel",
+ base::Bind(&HelpHandler::SetChannel, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("relaunchAndPowerwash",
+ base::Bind(&HelpHandler::RelaunchAndPowerwash, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("requestUpdate",
+ base::Bind(&HelpHandler::RequestUpdate, base::Unretained(this)));
+#endif
+#if defined(OS_MACOSX)
+ web_ui()->RegisterMessageCallback("promoteUpdater",
+ base::Bind(&HelpHandler::PromoteUpdater, base::Unretained(this)));
+#endif
+
+#if defined(OS_CHROMEOS)
+ // Handler for the product label image, which will be shown if available.
+ content::URLDataSource::Add(Profile::FromWebUI(web_ui()),
+ new chromeos::ImageSource());
+#endif
+}
+
+void HelpHandler::Observe(int type, const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK_EQ(chrome::NOTIFICATION_UPGRADE_RECOMMENDED, type);
+
+ // A version update is installed and ready to go. Refresh the UI so the
+ // correct state will be shown.
+ RequestUpdate(nullptr);
+}
+
+// static
+base::string16 HelpHandler::BuildBrowserVersionString() {
+ std::string version = version_info::GetVersionNumber();
+
+ std::string modifier = chrome::GetChannelString();
+ if (!modifier.empty())
+ version += " " + modifier;
+
+#if defined(ARCH_CPU_64_BITS)
+ version += " (64-bit)";
+#endif
+
+ return base::UTF8ToUTF16(version);
+}
+
+void HelpHandler::OnDeviceAutoUpdatePolicyChanged(
+ const base::Value* previous_policy,
+ const base::Value* current_policy) {
+ bool previous_auto_update_disabled = false;
+ if (previous_policy)
+ CHECK(previous_policy->GetAsBoolean(&previous_auto_update_disabled));
+
+ bool current_auto_update_disabled = false;
+ if (current_policy)
+ CHECK(current_policy->GetAsBoolean(&current_auto_update_disabled));
+
+ if (current_auto_update_disabled != previous_auto_update_disabled) {
+ // Refresh the update status to refresh the status of the UI.
+ RefreshUpdateStatus();
+ }
+}
+
+void HelpHandler::RefreshUpdateStatus() {
+ // On Chrome OS, do not check for an update automatically.
+#if defined(OS_CHROMEOS)
+ static_cast<VersionUpdaterCros*>(version_updater_.get())->GetUpdateStatus(
+ base::Bind(&HelpHandler::SetUpdateStatus, base::Unretained(this)));
+#else
+ RequestUpdate(NULL);
+#endif
+}
+
+void HelpHandler::OnPageLoaded(const base::ListValue* args) {
+#if defined(OS_CHROMEOS)
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&chromeos::version_loader::GetVersion,
+ chromeos::version_loader::VERSION_FULL),
+ base::Bind(&HelpHandler::OnOSVersion, weak_factory_.GetWeakPtr()));
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&chromeos::version_loader::GetARCVersion),
+ base::Bind(&HelpHandler::OnARCVersion, weak_factory_.GetWeakPtr()));
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&chromeos::version_loader::GetFirmware),
+ base::Bind(&HelpHandler::OnOSFirmware, weak_factory_.GetWeakPtr()));
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "help.HelpPage.updateEnableReleaseChannel",
+ base::Value(CanChangeChannel(Profile::FromWebUI(web_ui()))));
+
+ base::Time build_time = base::SysInfo::GetLsbReleaseTime();
+ base::string16 build_date = base::TimeFormatFriendlyDate(build_time);
+ web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.setBuildDate",
+ base::Value(build_date));
+#endif // defined(OS_CHROMEOS)
+
+ RefreshUpdateStatus();
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "help.HelpPage.setObsoleteSystem",
+ base::Value(ObsoleteSystem::IsObsoleteNowOrSoon()));
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "help.HelpPage.setObsoleteSystemEndOfTheLine",
+ base::Value(ObsoleteSystem::IsObsoleteNowOrSoon() &&
+ ObsoleteSystem::IsEndOfTheLine()));
+
+#if defined(OS_CHROMEOS)
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "help.HelpPage.updateIsEnterpriseManaged",
+ base::Value(IsEnterpriseManaged()));
+ // First argument to GetChannel() is a flag that indicates whether
+ // current channel should be returned (if true) or target channel
+ // (otherwise).
+ version_updater_->GetChannel(true,
+ base::Bind(&HelpHandler::OnCurrentChannel, weak_factory_.GetWeakPtr()));
+ version_updater_->GetChannel(false,
+ base::Bind(&HelpHandler::OnTargetChannel, weak_factory_.GetWeakPtr()));
+
+ if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kDisableEolNotification)) {
+ version_updater_->GetEolStatus(
+ base::Bind(&HelpHandler::OnEolStatus, weak_factory_.GetWeakPtr()));
+ }
+
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&FindRegulatoryLabelDir),
+ base::Bind(&HelpHandler::OnRegulatoryLabelDirFound,
+ weak_factory_.GetWeakPtr()));
+#endif
+}
+
+#if defined(OS_MACOSX)
+void HelpHandler::PromoteUpdater(const base::ListValue* args) {
+ version_updater_->PromoteUpdater();
+}
+#endif
+
+void HelpHandler::RelaunchNow(const base::ListValue* args) {
+ DCHECK(args->empty());
+ chrome::AttemptRelaunch();
+}
+
+void HelpHandler::OpenFeedbackDialog(const base::ListValue* args) {
+ DCHECK(args->empty());
+ Browser* browser = chrome::FindBrowserWithWebContents(
+ web_ui()->GetWebContents());
+ chrome::OpenFeedbackDialog(browser,
+ chrome::kFeedbackSourceOldSettingsAboutPage);
+}
+
+void HelpHandler::OpenHelpPage(const base::ListValue* args) {
+ DCHECK(args->empty());
+ Browser* browser = chrome::FindBrowserWithWebContents(
+ web_ui()->GetWebContents());
+ chrome::ShowHelp(browser, chrome::HELP_SOURCE_WEBUI);
+}
+
+#if defined(OS_CHROMEOS)
+
+void HelpHandler::SetChannel(const base::ListValue* args) {
+ DCHECK(args->GetSize() == 2);
+
+ if (!CanChangeChannel(Profile::FromWebUI(web_ui()))) {
+ LOG(WARNING) << "Non-owner tried to change release track.";
+ return;
+ }
+
+ base::string16 channel;
+ bool is_powerwash_allowed;
+ if (!args->GetString(0, &channel) ||
+ !args->GetBoolean(1, &is_powerwash_allowed)) {
+ LOG(ERROR) << "Can't parse SetChannel() args";
+ return;
+ }
+
+ version_updater_->SetChannel(base::UTF16ToUTF8(channel),
+ is_powerwash_allowed);
+ if (user_manager::UserManager::Get()->IsCurrentUserOwner()) {
+ // Check for update after switching release channel.
+ version_updater_->CheckForUpdate(base::Bind(&HelpHandler::SetUpdateStatus,
+ base::Unretained(this)),
+ VersionUpdater::PromoteCallback());
+ }
+}
+
+void HelpHandler::RelaunchAndPowerwash(const base::ListValue* args) {
+ DCHECK(args->empty());
+
+ if (IsEnterpriseManaged())
+ return;
+
+ PrefService* prefs = g_browser_process->local_state();
+ prefs->SetBoolean(prefs::kFactoryResetRequested, true);
+ prefs->CommitPendingWrite();
+
+ // Perform sign out. Current chrome process will then terminate, new one will
+ // be launched (as if it was a restart).
+ chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
+}
+
+#endif // defined(OS_CHROMEOS)
+
+void HelpHandler::RequestUpdate(const base::ListValue* args) {
+ VersionUpdater::PromoteCallback promote_callback;
+ version_updater_->CheckForUpdate(
+ base::Bind(&HelpHandler::SetUpdateStatus, base::Unretained(this)),
+#if defined(OS_MACOSX)
+ base::Bind(&HelpHandler::SetPromotionState, base::Unretained(this)));
+#else
+ VersionUpdater::PromoteCallback());
+#endif // OS_MACOSX
+}
+
+void HelpHandler::SetUpdateStatus(VersionUpdater::Status status,
+ int progress,
+ const std::string& /* version */,
+ int64_t /* size */,
+ const base::string16& message) {
+ // Only UPDATING state should have progress set.
+ DCHECK(status == VersionUpdater::UPDATING || progress == 0);
+
+ std::string status_str;
+ switch (status) {
+ case VersionUpdater::CHECKING:
+ status_str = "checking";
+ break;
+ case VersionUpdater::UPDATING:
+ status_str = "updating";
+ break;
+ case VersionUpdater::NEARLY_UPDATED:
+ status_str = "nearly_updated";
+ break;
+ case VersionUpdater::UPDATED:
+ status_str = "updated";
+ break;
+ case VersionUpdater::FAILED:
+ case VersionUpdater::FAILED_OFFLINE:
+ case VersionUpdater::FAILED_CONNECTION_TYPE_DISALLOWED:
+ // Old help page does not support update over cellular connection. Treat this
+ // signal as FAILED.
+ case VersionUpdater::NEED_PERMISSION_TO_UPDATE:
+ status_str = "failed";
+ break;
+ case VersionUpdater::DISABLED:
+ status_str = "disabled";
+ break;
+ case VersionUpdater::DISABLED_BY_ADMIN:
+ status_str = "disabled_by_admin";
+ break;
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.setUpdateStatus",
+ base::Value(status_str),
+ base::Value(message));
+
+ if (status == VersionUpdater::UPDATING) {
+ web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.setProgress",
+ base::Value(progress));
+ }
+
+#if defined(OS_CHROMEOS)
+ if (status == VersionUpdater::FAILED_OFFLINE ||
+ status == VersionUpdater::FAILED_CONNECTION_TYPE_DISALLOWED) {
+ base::string16 types_msg = GetAllowedConnectionTypesMessage();
+ if (!types_msg.empty()) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "help.HelpPage.setAndShowAllowedConnectionTypesMsg",
+ base::Value(types_msg));
+ } else {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "help.HelpPage.showAllowedConnectionTypesMsg", base::Value(false));
+ }
+ } else {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "help.HelpPage.showAllowedConnectionTypesMsg", base::Value(false));
+ }
+#endif // defined(OS_CHROMEOS)
+}
+
+#if defined(OS_MACOSX)
+void HelpHandler::SetPromotionState(VersionUpdater::PromotionState state) {
+ std::string state_str;
+ switch (state) {
+ case VersionUpdater::PROMOTE_HIDDEN:
+ case VersionUpdater::PROMOTED:
+ state_str = "hidden";
+ break;
+ case VersionUpdater::PROMOTE_ENABLED:
+ state_str = "enabled";
+ break;
+ case VersionUpdater::PROMOTE_DISABLED:
+ state_str = "disabled";
+ break;
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.setPromotionState",
+ base::Value(state_str));
+}
+#endif // defined(OS_MACOSX)
+
+#if defined(OS_CHROMEOS)
+void HelpHandler::OnOSVersion(const std::string& version) {
+ web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.setOSVersion",
+ base::Value(version));
+}
+
+void HelpHandler::OnARCVersion(const std::string& firmware) {
+ web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.setARCVersion",
+ base::Value(firmware));
+}
+
+void HelpHandler::OnOSFirmware(const std::string& firmware) {
+ web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.setOSFirmware",
+ base::Value(firmware));
+}
+
+void HelpHandler::OnCurrentChannel(const std::string& channel) {
+ web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.updateCurrentChannel",
+ base::Value(channel));
+}
+
+void HelpHandler::OnTargetChannel(const std::string& channel) {
+ web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.updateTargetChannel",
+ base::Value(channel));
+}
+
+void HelpHandler::OnRegulatoryLabelDirFound(const base::FilePath& path) {
+ if (path.empty())
+ return;
+
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&ReadRegulatoryLabelText, path),
+ base::Bind(&HelpHandler::OnRegulatoryLabelTextRead,
+ weak_factory_.GetWeakPtr()));
+
+ // Send the image path to the WebUI.
+ OnRegulatoryLabelImageFound(path.AppendASCII(kRegulatoryLabelImageFilename));
+}
+
+void HelpHandler::OnRegulatoryLabelImageFound(const base::FilePath& path) {
+ std::string url = std::string("chrome://") + chrome::kChromeOSAssetHost +
+ "/" + path.MaybeAsASCII();
+ web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.setRegulatoryLabelPath",
+ base::Value(url));
+}
+
+void HelpHandler::OnRegulatoryLabelTextRead(const std::string& text) {
+ // Remove unnecessary whitespace.
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "help.HelpPage.setRegulatoryLabelText",
+ base::Value(base::CollapseWhitespaceASCII(text, true)));
+}
+
+void HelpHandler::OnEolStatus(update_engine::EndOfLifeStatus status) {
+ // Security only state is no longer supported.
+ if (status == update_engine::EndOfLifeStatus::kSecurityOnly ||
+ IsEnterpriseManaged()) {
+ return;
+ }
+
+ if (status == update_engine::EndOfLifeStatus::kSupported) {
+ web_ui()->CallJavascriptFunctionUnsafe("help.HelpPage.updateEolMessage",
+ base::Value("device_supported"),
+ base::Value(""));
+ } else {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "help.HelpPage.updateEolMessage", base::Value("device_endoflife"),
+ base::Value(l10n_util::GetStringUTF16(IDS_ABOUT_PAGE_EOL_EOL)));
+ }
+}
+
+#endif // defined(OS_CHROMEOS)
diff --git a/chromium/chrome/browser/ui/webui/help/help_handler.h b/chromium/chrome/browser/ui/webui/help/help_handler.h
new file mode 100644
index 00000000000..82b088bb8a0
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/help/help_handler.h
@@ -0,0 +1,139 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_HELP_HELP_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_HELP_HELP_HANDLER_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+#include "build/build_config.h"
+#include "chrome/browser/ui/webui/help/version_updater.h"
+#include "components/policy/core/common/policy_service.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+#if defined(OS_CHROMEOS)
+#include "base/task/cancelable_task_tracker.h"
+#include "chromeos/system/version_loader.h"
+#include "third_party/cros_system_api/dbus/update_engine/dbus-constants.h"
+#endif // defined(OS_CHROMEOS)
+
+namespace base {
+class DictionaryValue;
+class FilePath;
+class ListValue;
+}
+
+// WebUI message handler for the help page.
+class HelpHandler : public content::WebUIMessageHandler,
+ public content::NotificationObserver {
+ public:
+ HelpHandler();
+ ~HelpHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // Adds string values for the UI to |localized_strings|.
+ static void GetLocalizedValues(base::DictionaryValue* localized_strings);
+
+ // NotificationObserver implementation.
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ // Returns the browser version as a string.
+ static base::string16 BuildBrowserVersionString();
+
+ private:
+ void OnDeviceAutoUpdatePolicyChanged(const base::Value* previous_policy,
+ const base::Value* current_policy);
+
+ // On ChromeOS, this gets the current update status. On other platforms, it
+ // will request and perform an update (if one is available).
+ void RefreshUpdateStatus();
+
+ // Initializes querying values for the page.
+ void OnPageLoaded(const base::ListValue* args);
+
+#if defined(OS_MACOSX)
+ // Promotes the updater for all users.
+ void PromoteUpdater(const base::ListValue* args);
+#endif
+
+ // Relaunches the browser. |args| must be empty.
+ void RelaunchNow(const base::ListValue* args);
+
+ // Opens the feedback dialog. |args| must be empty.
+ void OpenFeedbackDialog(const base::ListValue* args);
+
+ // Opens the help page. |args| must be empty.
+ void OpenHelpPage(const base::ListValue* args);
+
+#if defined(OS_CHROMEOS)
+ // Sets the release track version.
+ void SetChannel(const base::ListValue* args);
+
+ // Performs relaunch and powerwash.
+ void RelaunchAndPowerwash(const base::ListValue* args);
+#endif
+
+ // Checks for and applies update.
+ void RequestUpdate(const base::ListValue* args);
+
+ // Callback method which forwards status updates to the page.
+ void SetUpdateStatus(VersionUpdater::Status status,
+ int progress,
+ const std::string& version,
+ int64_t size,
+ const base::string16& fail_message);
+
+#if defined(OS_MACOSX)
+ // Callback method which forwards promotion state to the page.
+ void SetPromotionState(VersionUpdater::PromotionState state);
+#endif
+
+#if defined(OS_CHROMEOS)
+ // Callbacks from VersionLoader.
+ void OnOSVersion(const std::string& version);
+ void OnARCVersion(const std::string& firmware);
+ void OnOSFirmware(const std::string& firmware);
+ void OnCurrentChannel(const std::string& channel);
+ void OnTargetChannel(const std::string& channel);
+
+ // Callback for when the directory with the regulatory label image and alt
+ // text has been found.
+ void OnRegulatoryLabelDirFound(const base::FilePath& path);
+
+ // Callback for setting the regulatory label source.
+ void OnRegulatoryLabelImageFound(const base::FilePath& path);
+
+ // Callback for setting the regulatory label alt text.
+ void OnRegulatoryLabelTextRead(const std::string& text);
+
+ // Callback for setting the eol string text.
+ void OnEolStatus(update_engine::EndOfLifeStatus status);
+#endif
+
+ // Specialized instance of the VersionUpdater used to update the browser.
+ std::unique_ptr<VersionUpdater> version_updater_;
+
+ // Used to observe notifications.
+ content::NotificationRegistrar registrar_;
+
+ // Used to observe changes in the |kDeviceAutoUpdateDisabled| policy.
+ policy::PolicyChangeRegistrar policy_registrar_;
+
+ // Used for callbacks.
+ base::WeakPtrFactory<HelpHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(HelpHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_HELP_HELP_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/help/help_ui.cc b/chromium/chrome/browser/ui/webui/help/help_ui.cc
new file mode 100644
index 00000000000..440fc7218ac
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/help/help_ui.cc
@@ -0,0 +1,46 @@
+// 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/ui/webui/help/help_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/help/help_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/theme_resources.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace {
+
+content::WebUIDataSource* CreateAboutPageHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIHelpFrameHost);
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("help.js", IDR_HELP_JS);
+ source->AddResourcePath("help_page.js", IDR_HELP_PAGE_JS);
+ source->AddResourcePath("channel_change_page.js", IDR_CHANNEL_CHANGE_PAGE_JS);
+ source->SetDefaultResource(IDR_HELP_HTML);
+ source->DisableDenyXFrameOptions();
+ return source;
+}
+
+} // namespace
+
+HelpUI::HelpUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource* source = CreateAboutPageHTMLSource();
+
+ base::DictionaryValue localized_strings;
+ HelpHandler::GetLocalizedValues(&localized_strings);
+ source->AddLocalizedStrings(localized_strings);
+ content::WebUIDataSource::Add(profile, source);
+ web_ui->AddMessageHandler(base::MakeUnique<HelpHandler>());
+}
+
+HelpUI::~HelpUI() {
+}
diff --git a/chromium/chrome/browser/ui/webui/help/help_ui.h b/chromium/chrome/browser/ui/webui/help/help_ui.h
new file mode 100644
index 00000000000..c2b8aca5c14
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/help/help_ui.h
@@ -0,0 +1,20 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_HELP_HELP_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_HELP_HELP_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+class HelpUI : public content::WebUIController {
+ public:
+ explicit HelpUI(content::WebUI* web_ui);
+ ~HelpUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HelpUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_HELP_HELP_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/help/help_utils_chromeos.cc b/chromium/chrome/browser/ui/webui/help/help_utils_chromeos.cc
new file mode 100644
index 00000000000..ea4884ec15f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/help/help_utils_chromeos.cc
@@ -0,0 +1,74 @@
+// 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/help/help_utils_chromeos.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_type_pattern.h"
+#include "chromeos/settings/cros_settings_names.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace help_utils_chromeos {
+
+bool IsUpdateOverCellularAllowed(bool interactive) {
+ // If this is a Cellular First device or the user actively checks for update,
+ // the default is to allow updates over cellular.
+ bool default_update_over_cellular_allowed =
+ interactive ? true : chromeos::switches::IsCellularFirstDevice();
+
+ // Device Policy overrides the defaults.
+ chromeos::CrosSettings* settings = chromeos::CrosSettings::Get();
+ if (!settings)
+ return default_update_over_cellular_allowed;
+
+ const base::Value* raw_types_value =
+ settings->GetPref(chromeos::kAllowedConnectionTypesForUpdate);
+ if (!raw_types_value)
+ return default_update_over_cellular_allowed;
+ const base::ListValue* types_value;
+ CHECK(raw_types_value->GetAsList(&types_value));
+ for (size_t i = 0; i < types_value->GetSize(); ++i) {
+ int connection_type;
+ if (!types_value->GetInteger(i, &connection_type)) {
+ LOG(WARNING) << "Can't parse connection type #" << i;
+ continue;
+ }
+ if (connection_type == 4)
+ return true;
+ }
+ // Device policy does not allow updates over cellular, as cellular is not
+ // included in allowed connection types for updates.
+ return false;
+}
+
+base::string16 GetConnectionTypeAsUTF16(const chromeos::NetworkState* network) {
+ const std::string type =
+ network->IsUsingMobileData() ? shill::kTypeCellular : network->type();
+ if (chromeos::NetworkTypePattern::Ethernet().MatchesType(type))
+ return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_ETHERNET);
+ if (type == shill::kTypeWifi)
+ return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_WIFI);
+ if (type == shill::kTypeWimax)
+ return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_WIMAX);
+ if (type == shill::kTypeBluetooth)
+ return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_BLUETOOTH);
+ if (type == shill::kTypeCellular)
+ return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_CELLULAR);
+ if (type == shill::kTypeVPN)
+ return l10n_util::GetStringUTF16(IDS_NETWORK_TYPE_VPN);
+ NOTREACHED();
+ return base::string16();
+}
+
+} // namespace help_utils_chromeos
diff --git a/chromium/chrome/browser/ui/webui/help/help_utils_chromeos.h b/chromium/chrome/browser/ui/webui/help/help_utils_chromeos.h
new file mode 100644
index 00000000000..ec388efd4bf
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/help/help_utils_chromeos.h
@@ -0,0 +1,28 @@
+// 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_HELP_HELP_UTILS_CHROMEOS_H_
+#define CHROME_BROWSER_UI_WEBUI_HELP_HELP_UTILS_CHROMEOS_H_
+
+#include "base/strings/string16.h"
+
+namespace chromeos {
+class NetworkState;
+}
+
+namespace help_utils_chromeos {
+
+// Returns true if updates over cellular networks are allowed. If |interactive|
+// is true (e.g. the user clicked on a 'check for updates' button), updates over
+// cellular are allowed unless prohibited by policy. If |interactive| is false,
+// updates over cellular may be allowed by default for the device type, or by
+// policy.
+bool IsUpdateOverCellularAllowed(bool interactive);
+
+// Returns localized name for the connection |type|.
+base::string16 GetConnectionTypeAsUTF16(const chromeos::NetworkState* network);
+
+} // namespace help_utils_chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_HELP_HELP_UTILS_CHROMEOS_H_
diff --git a/chromium/chrome/browser/ui/webui/help/version_updater.h b/chromium/chrome/browser/ui/webui/help/version_updater.h
new file mode 100644
index 00000000000..d36b26fb472
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/help/version_updater.h
@@ -0,0 +1,106 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_HELP_VERSION_UPDATER_H_
+#define CHROME_BROWSER_UI_WEBUI_HELP_VERSION_UPDATER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/strings/string16.h"
+#include "build/build_config.h"
+
+#if defined(OS_CHROMEOS)
+#include "third_party/cros_system_api/dbus/update_engine/dbus-constants.h"
+#endif // defined(OS_CHROMEOS)
+
+namespace content {
+class WebContents;
+}
+
+// Interface implemented to expose per-platform updating functionality.
+class VersionUpdater {
+ public:
+ // Update process state machine.
+ enum Status {
+ CHECKING,
+ NEED_PERMISSION_TO_UPDATE,
+ UPDATING,
+ NEARLY_UPDATED,
+ UPDATED,
+ FAILED,
+ FAILED_OFFLINE,
+ FAILED_CONNECTION_TYPE_DISALLOWED,
+ DISABLED,
+ DISABLED_BY_ADMIN
+ };
+
+ // Promotion state (Mac-only).
+ enum PromotionState {
+ PROMOTE_HIDDEN,
+ PROMOTE_ENABLED,
+ PROMOTE_DISABLED,
+ PROMOTED,
+ };
+
+ // TODO(jhawkins): Use a delegate interface instead of multiple callback
+ // types.
+#if defined(OS_CHROMEOS)
+ typedef base::Callback<void(const std::string&)> ChannelCallback;
+ typedef base::Callback<void(update_engine::EndOfLifeStatus status)>
+ EolStatusCallback;
+#endif
+
+ // Used to update the client of status changes. int parameter is the progress
+ // and should only be non-zero for the UPDATING state.
+ // std::string parameter is the version of the available update and should be
+ // empty string when update is not available.
+ // int64_t parameter is the size in bytes of the available update and should
+ // be 0 when update is not available.
+ // base::string16 parameter is a message explaining a failure.
+ typedef base::Callback<
+ void(Status, int, const std::string&, int64_t, const base::string16&)>
+ StatusCallback;
+
+ // Used to show or hide the promote UI elements. Mac-only.
+ typedef base::Callback<void(PromotionState)> PromoteCallback;
+
+ virtual ~VersionUpdater() {}
+
+ // Sub-classes must implement this method to create the respective
+ // specialization. |web_contents| may be null, in which case any required UX
+ // (e.g., UAC to elevate on Windows) may not be associated with any existing
+ // browser windows.
+ static VersionUpdater* Create(content::WebContents* web_contents);
+
+ // Begins the update process by checking for update availability.
+ // |status_callback| is called for each status update. |promote_callback|
+ // (which is only used on the Mac) can be used to show or hide the promote UI
+ // elements.
+ virtual void CheckForUpdate(const StatusCallback& status_callback,
+ const PromoteCallback& promote_callback) = 0;
+
+#if defined(OS_MACOSX)
+ // Make updates available for all users.
+ virtual void PromoteUpdater() const = 0;
+#endif
+
+#if defined(OS_CHROMEOS)
+ virtual void SetChannel(const std::string& channel,
+ bool is_powerwash_allowed) = 0;
+ virtual void GetChannel(bool get_current_channel,
+ const ChannelCallback& callback) = 0;
+ virtual void GetEolStatus(const EolStatusCallback& callback) = 0;
+
+ // Set the update over cellular target in |target_version| and |target_size|
+ // arguments maintained by update engine. The arguments are later used by
+ // update engine to match the given target with the server head and to allow
+ // update over cellular to this given target.
+ virtual void SetUpdateOverCellularTarget(const StatusCallback& callback,
+ const std::string& target_version,
+ int64_t target_size) = 0;
+#endif
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_HELP_VERSION_UPDATER_H_
diff --git a/chromium/chrome/browser/ui/webui/help/version_updater_basic.cc b/chromium/chrome/browser/ui/webui/help/version_updater_basic.cc
new file mode 100644
index 00000000000..8cb85a35079
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/help/version_updater_basic.cc
@@ -0,0 +1,21 @@
+// 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/ui/webui/help/version_updater_basic.h"
+
+#include "base/strings/string16.h"
+#include "chrome/browser/upgrade_detector.h"
+
+void VersionUpdaterBasic::CheckForUpdate(
+ const StatusCallback& status_callback,
+ const PromoteCallback&) {
+ if (UpgradeDetector::GetInstance()->notify_upgrade())
+ status_callback.Run(NEARLY_UPDATED, 0, std::string(), 0, base::string16());
+ else
+ status_callback.Run(DISABLED, 0, std::string(), 0, base::string16());
+}
+
+VersionUpdater* VersionUpdater::Create(content::WebContents* web_contents) {
+ return new VersionUpdaterBasic;
+}
diff --git a/chromium/chrome/browser/ui/webui/help/version_updater_basic.h b/chromium/chrome/browser/ui/webui/help/version_updater_basic.h
new file mode 100644
index 00000000000..8fbb47a79ef
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/help/version_updater_basic.h
@@ -0,0 +1,29 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_HELP_VERSION_UPDATER_BASIC_H_
+#define CHROME_BROWSER_UI_WEBUI_HELP_VERSION_UPDATER_BASIC_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/help/version_updater.h"
+
+// Bare bones implementation just checks if a new version is ready.
+class VersionUpdaterBasic : public VersionUpdater {
+ public:
+ // VersionUpdater implementation.
+ void CheckForUpdate(const StatusCallback& callback,
+ const PromoteCallback&) override;
+ protected:
+ friend class VersionUpdater;
+
+ // Clients must use VersionUpdater::Create().
+ VersionUpdaterBasic() {}
+ ~VersionUpdaterBasic() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(VersionUpdaterBasic);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_HELP_VERSION_UPDATER_BASIC_H_
diff --git a/chromium/chrome/browser/ui/webui/help/version_updater_chromeos.cc b/chromium/chrome/browser/ui/webui/help/version_updater_chromeos.cc
new file mode 100644
index 00000000000..f5a75e9a87a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/help/version_updater_chromeos.cc
@@ -0,0 +1,317 @@
+// 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/ui/webui/help/version_updater_chromeos.h"
+
+#include <cmath>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/strings/string_number_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/login/startup_utils.h"
+#include "chrome/browser/chromeos/login/wizard_controller.h"
+#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
+#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/ui/webui/help/help_utils_chromeos.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/power_manager_client.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "chromeos/settings/cros_settings_names.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using chromeos::CrosSettings;
+using chromeos::DBusThreadManager;
+using chromeos::OwnerSettingsServiceChromeOS;
+using chromeos::OwnerSettingsServiceChromeOSFactory;
+using chromeos::UpdateEngineClient;
+using chromeos::WizardController;
+
+namespace {
+
+// Network status in the context of device update.
+enum NetworkStatus {
+ // It's allowed to use current network for update.
+ NETWORK_STATUS_ALLOWED = 0,
+ // It's disallowed to use current network for update.
+ NETWORK_STATUS_DISALLOWED,
+ // Device is in offline state.
+ NETWORK_STATUS_OFFLINE
+};
+
+const bool kDefaultAutoUpdateDisabled = false;
+
+NetworkStatus GetNetworkStatus(bool interactive,
+ const chromeos::NetworkState* network) {
+ if (!network || !network->IsConnectedState()) // Offline state.
+ return NETWORK_STATUS_OFFLINE;
+
+ if (network->type() == shill::kTypeBluetooth)
+ return NETWORK_STATUS_DISALLOWED;
+
+ // Treats tethered networks as cellular networks.
+ if (network->IsUsingMobileData() &&
+ !help_utils_chromeos::IsUpdateOverCellularAllowed(interactive)) {
+ return NETWORK_STATUS_DISALLOWED;
+ }
+ return NETWORK_STATUS_ALLOWED;
+}
+
+// Returns true if auto-update is disabled by the system administrator.
+bool IsAutoUpdateDisabled() {
+ bool update_disabled = kDefaultAutoUpdateDisabled;
+ chromeos::CrosSettings* settings = chromeos::CrosSettings::Get();
+ if (!settings)
+ return update_disabled;
+ const base::Value* update_disabled_value =
+ settings->GetPref(chromeos::kUpdateDisabled);
+ if (update_disabled_value)
+ CHECK(update_disabled_value->GetAsBoolean(&update_disabled));
+ return update_disabled;
+}
+
+// Returns whether an update is allowed. If not, it calls the callback with
+// the appropriate status. |interactive| indicates whether the user is actively
+// checking for updates.
+bool EnsureCanUpdate(bool interactive,
+ const VersionUpdater::StatusCallback& callback) {
+ if (IsAutoUpdateDisabled()) {
+ callback.Run(VersionUpdater::DISABLED_BY_ADMIN, 0, std::string(), 0,
+ l10n_util::GetStringUTF16(IDS_UPGRADE_DISABLED_BY_POLICY));
+ return false;
+ }
+
+ chromeos::NetworkStateHandler* network_state_handler =
+ chromeos::NetworkHandler::Get()->network_state_handler();
+ const chromeos::NetworkState* network =
+ network_state_handler->DefaultNetwork();
+
+ // Don't allow an update if we're currently offline or connected
+ // to a network for which updates are disallowed.
+ NetworkStatus status = GetNetworkStatus(interactive, network);
+ if (status == NETWORK_STATUS_OFFLINE) {
+ callback.Run(VersionUpdater::FAILED_OFFLINE, 0, std::string(), 0,
+ l10n_util::GetStringUTF16(IDS_UPGRADE_OFFLINE));
+ return false;
+ } else if (status == NETWORK_STATUS_DISALLOWED) {
+ base::string16 message = l10n_util::GetStringFUTF16(
+ IDS_UPGRADE_DISALLOWED,
+ help_utils_chromeos::GetConnectionTypeAsUTF16(network));
+ callback.Run(VersionUpdater::FAILED_CONNECTION_TYPE_DISALLOWED, 0,
+ std::string(), 0, message);
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+VersionUpdater* VersionUpdater::Create(content::WebContents* web_contents) {
+ return new VersionUpdaterCros(web_contents);
+}
+
+void VersionUpdaterCros::GetUpdateStatus(const StatusCallback& callback) {
+ callback_ = callback;
+
+ // User is not actively checking for updates.
+ if (!EnsureCanUpdate(false /* interactive */, callback))
+ return;
+
+ UpdateEngineClient* update_engine_client =
+ DBusThreadManager::Get()->GetUpdateEngineClient();
+ if (!update_engine_client->HasObserver(this))
+ update_engine_client->AddObserver(this);
+
+ this->UpdateStatusChanged(
+ DBusThreadManager::Get()->GetUpdateEngineClient()->GetLastStatus());
+}
+
+void VersionUpdaterCros::CheckForUpdate(const StatusCallback& callback,
+ const PromoteCallback&) {
+ callback_ = callback;
+
+ // User is actively checking for updates.
+ if (!EnsureCanUpdate(true /* interactive */, callback))
+ return;
+
+ UpdateEngineClient* update_engine_client =
+ DBusThreadManager::Get()->GetUpdateEngineClient();
+ if (!update_engine_client->HasObserver(this))
+ update_engine_client->AddObserver(this);
+
+ if (update_engine_client->GetLastStatus().status !=
+ UpdateEngineClient::UPDATE_STATUS_IDLE) {
+ check_for_update_when_idle_ = true;
+ return;
+ }
+ check_for_update_when_idle_ = false;
+
+ // Make sure that libcros is loaded and OOBE is complete.
+ if (!WizardController::default_controller() ||
+ chromeos::StartupUtils::IsDeviceRegistered()) {
+ update_engine_client->RequestUpdateCheck(base::Bind(
+ &VersionUpdaterCros::OnUpdateCheck, weak_ptr_factory_.GetWeakPtr()));
+ }
+}
+
+void VersionUpdaterCros::SetChannel(const std::string& channel,
+ bool is_powerwash_allowed) {
+ OwnerSettingsServiceChromeOS* service =
+ context_
+ ? OwnerSettingsServiceChromeOSFactory::GetInstance()
+ ->GetForBrowserContext(context_)
+ : nullptr;
+ // For local owner set the field in the policy blob.
+ if (service)
+ service->SetString(chromeos::kReleaseChannel, channel);
+ DBusThreadManager::Get()->GetUpdateEngineClient()->
+ SetChannel(channel, is_powerwash_allowed);
+}
+
+void VersionUpdaterCros::SetUpdateOverCellularTarget(
+ const StatusCallback& callback,
+ const std::string& target_version,
+ int64_t target_size) {
+ callback_ = callback;
+ DBusThreadManager::Get()
+ ->GetUpdateEngineClient()
+ ->SetUpdateOverCellularTarget(
+ target_version, target_size,
+ base::Bind(&VersionUpdaterCros::OnSetUpdateOverCellularTarget,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void VersionUpdaterCros::OnSetUpdateOverCellularTarget(bool success) {
+ if (success) {
+ // Target is set successfully, so we can proceed to update.
+ CheckForUpdate(callback_, VersionUpdater::PromoteCallback());
+ } else {
+ // TODO(weidongg/691108): invoke callback to signal about page to show
+ // appropriate error message.
+ LOG(ERROR) << "Error setting update over cellular target.";
+ callback_.Run(VersionUpdater::FAILED, 0, std::string(), 0,
+ base::string16());
+ }
+}
+
+void VersionUpdaterCros::GetChannel(bool get_current_channel,
+ const ChannelCallback& cb) {
+ UpdateEngineClient* update_engine_client =
+ DBusThreadManager::Get()->GetUpdateEngineClient();
+
+ // Request the channel information. Bind to a weak_ptr bound method rather
+ // than passing |cb| directly so that |cb| does not outlive |this|.
+ update_engine_client->GetChannel(
+ get_current_channel, base::Bind(&VersionUpdaterCros::OnGetChannel,
+ weak_ptr_factory_.GetWeakPtr(), cb));
+}
+
+void VersionUpdaterCros::OnGetChannel(const ChannelCallback& cb,
+ const std::string& current_channel) {
+ cb.Run(current_channel);
+}
+
+void VersionUpdaterCros::GetEolStatus(const EolStatusCallback& cb) {
+ UpdateEngineClient* update_engine_client =
+ DBusThreadManager::Get()->GetUpdateEngineClient();
+
+ // Request the Eol Status. Bind to a weak_ptr bound method rather than passing
+ // |cb| directly so that |cb| does not outlive |this|.
+ update_engine_client->GetEolStatus(base::Bind(
+ &VersionUpdaterCros::OnGetEolStatus, weak_ptr_factory_.GetWeakPtr(), cb));
+}
+
+void VersionUpdaterCros::OnGetEolStatus(const EolStatusCallback& cb,
+ update_engine::EndOfLifeStatus status) {
+ cb.Run(status);
+}
+
+VersionUpdaterCros::VersionUpdaterCros(content::WebContents* web_contents)
+ : context_(web_contents ? web_contents->GetBrowserContext() : nullptr),
+ last_operation_(UpdateEngineClient::UPDATE_STATUS_IDLE),
+ check_for_update_when_idle_(false),
+ weak_ptr_factory_(this) {
+}
+
+VersionUpdaterCros::~VersionUpdaterCros() {
+ UpdateEngineClient* update_engine_client =
+ DBusThreadManager::Get()->GetUpdateEngineClient();
+ update_engine_client->RemoveObserver(this);
+}
+
+void VersionUpdaterCros::UpdateStatusChanged(
+ const UpdateEngineClient::Status& status) {
+ Status my_status = UPDATED;
+ int progress = 0;
+ std::string version = status.new_version;
+ int64_t size = status.new_size;
+ base::string16 message;
+
+ // If the updater is currently idle, just show the last operation (unless it
+ // was previously checking for an update -- in that case, the system is
+ // up to date now). See http://crbug.com/120063 for details.
+ UpdateEngineClient::UpdateStatusOperation operation_to_show = status.status;
+ if (status.status == UpdateEngineClient::UPDATE_STATUS_IDLE &&
+ last_operation_ !=
+ UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE) {
+ operation_to_show = last_operation_;
+ }
+
+ switch (operation_to_show) {
+ case UpdateEngineClient::UPDATE_STATUS_ERROR:
+ case UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT:
+ case UpdateEngineClient::UPDATE_STATUS_ATTEMPTING_ROLLBACK:
+ // This path previously used the FAILED status and IDS_UPGRADE_ERROR, but
+ // the update engine reports errors for some conditions that shouldn't
+ // actually be displayed as errors to users: http://crbug.com/146919.
+ // Just use the UPDATED status instead.
+ break;
+ case UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE:
+ my_status = CHECKING;
+ break;
+ case UpdateEngineClient::UPDATE_STATUS_DOWNLOADING:
+ progress = static_cast<int>(round(status.download_progress * 100));
+ // Fall through.
+ case UpdateEngineClient::UPDATE_STATUS_UPDATE_AVAILABLE:
+ my_status = UPDATING;
+ break;
+ case UpdateEngineClient::UPDATE_STATUS_NEED_PERMISSION_TO_UPDATE:
+ my_status = NEED_PERMISSION_TO_UPDATE;
+ break;
+ case UpdateEngineClient::UPDATE_STATUS_VERIFYING:
+ case UpdateEngineClient::UPDATE_STATUS_FINALIZING:
+ // Once the download is finished, keep the progress at 100; it shouldn't
+ // go down while the status is the same.
+ progress = 100;
+ my_status = UPDATING;
+ break;
+ case UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT:
+ my_status = NEARLY_UPDATED;
+ break;
+ default:
+ break;
+ }
+
+ callback_.Run(my_status, progress, version, size, message);
+ last_operation_ = status.status;
+
+ if (check_for_update_when_idle_ &&
+ status.status == UpdateEngineClient::UPDATE_STATUS_IDLE) {
+ CheckForUpdate(callback_, VersionUpdater::PromoteCallback());
+ }
+}
+
+void VersionUpdaterCros::OnUpdateCheck(
+ UpdateEngineClient::UpdateCheckResult result) {
+ // If version updating is not implemented, this binary is the most up-to-date
+ // possible with respect to automatic updating.
+ if (result == UpdateEngineClient::UPDATE_RESULT_NOTIMPLEMENTED)
+ callback_.Run(UPDATED, 0, std::string(), 0, base::string16());
+}
diff --git a/chromium/chrome/browser/ui/webui/help/version_updater_chromeos.h b/chromium/chrome/browser/ui/webui/help/version_updater_chromeos.h
new file mode 100644
index 00000000000..b6de0cb2f63
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/help/version_updater_chromeos.h
@@ -0,0 +1,81 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_HELP_VERSION_UPDATER_CHROMEOS_H_
+#define CHROME_BROWSER_UI_WEBUI_HELP_VERSION_UPDATER_CHROMEOS_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/webui/help/version_updater.h"
+#include "chromeos/dbus/update_engine_client.h"
+
+namespace content {
+class BrowserContext;
+class WebContents;
+}
+
+class VersionUpdaterCros : public VersionUpdater,
+ public chromeos::UpdateEngineClient::Observer {
+ public:
+ // VersionUpdater implementation.
+ void CheckForUpdate(const StatusCallback& callback,
+ const PromoteCallback&) override;
+ void SetChannel(const std::string& channel,
+ bool is_powerwash_allowed) override;
+ void GetChannel(bool get_current_channel,
+ const ChannelCallback& callback) override;
+ void SetUpdateOverCellularTarget(const StatusCallback& callback,
+ const std::string& target_version,
+ int64_t target_size) override;
+
+ // Gets the last update status, without triggering a new check or download.
+ void GetUpdateStatus(const StatusCallback& callback);
+
+ void GetEolStatus(const EolStatusCallback& callback) override;
+
+ protected:
+ friend class VersionUpdater;
+
+ // Clients must use VersionUpdater::Create().
+ explicit VersionUpdaterCros(content::WebContents* web_contents);
+ ~VersionUpdaterCros() override;
+
+ private:
+ // UpdateEngineClient::Observer implementation.
+ void UpdateStatusChanged(
+ const chromeos::UpdateEngineClient::Status& status) override;
+
+ // Callback from UpdateEngineClient::RequestUpdateCheck().
+ void OnUpdateCheck(chromeos::UpdateEngineClient::UpdateCheckResult result);
+
+ // Callback from UpdateEngineClient::SetUpdateOverCellularTarget().
+ void OnSetUpdateOverCellularTarget(bool success);
+
+ // Callback from UpdateEngineClient::GetChannel().
+ void OnGetChannel(const ChannelCallback& cb,
+ const std::string& current_channel);
+
+ // Callback from UpdateEngineClient::GetEolStatus().
+ void OnGetEolStatus(const EolStatusCallback& cb,
+ update_engine::EndOfLifeStatus status);
+
+ // BrowserContext in which the class was instantiated.
+ content::BrowserContext* context_;
+
+ // Callback used to communicate update status to the client.
+ StatusCallback callback_;
+
+ // Last state received via UpdateStatusChanged().
+ chromeos::UpdateEngineClient::UpdateStatusOperation last_operation_;
+
+ // True if an update check should be scheduled when the update engine is idle.
+ bool check_for_update_when_idle_;
+
+ base::WeakPtrFactory<VersionUpdaterCros> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(VersionUpdaterCros);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_HELP_VERSION_UPDATER_CHROMEOS_H_
diff --git a/chromium/chrome/browser/ui/webui/help/version_updater_chromeos_unittest.cc b/chromium/chrome/browser/ui/webui/help/version_updater_chromeos_unittest.cc
new file mode 100644
index 00000000000..3db6e62b139
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/help/version_updater_chromeos_unittest.cc
@@ -0,0 +1,155 @@
+// 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/ui/webui/help/version_updater_chromeos.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "chrome/browser/chromeos/login/users/mock_user_manager.h"
+#include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/chromeos/settings/device_settings_service.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/fake_update_engine_client.h"
+#include "chromeos/dbus/shill_service_client.h"
+#include "chromeos/network/network_handler.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+using ::testing::AtLeast;
+using ::testing::Return;
+
+namespace chromeos {
+
+namespace {
+
+void CheckNotification(VersionUpdater::Status /* status */,
+ int /* progress */,
+ const std::string& /* version */,
+ int64_t /* size */,
+ const base::string16& /* message */) {}
+
+} // namespace
+
+class VersionUpdaterCrosTest : public ::testing::Test {
+ protected:
+ VersionUpdaterCrosTest()
+ : version_updater_(VersionUpdater::Create(nullptr)),
+ fake_update_engine_client_(NULL),
+ mock_user_manager_(new MockUserManager()),
+ user_manager_enabler_(mock_user_manager_) {}
+
+ ~VersionUpdaterCrosTest() override {}
+
+ void SetUp() override {
+ fake_update_engine_client_ = new FakeUpdateEngineClient();
+ std::unique_ptr<DBusThreadManagerSetter> dbus_setter =
+ DBusThreadManager::GetSetterForTesting();
+ dbus_setter->SetUpdateEngineClient(
+ std::unique_ptr<UpdateEngineClient>(fake_update_engine_client_));
+
+ EXPECT_CALL(*mock_user_manager_, IsCurrentUserOwner())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_user_manager_, Shutdown()).Times(AtLeast(0));
+
+ DeviceSettingsService::Initialize();
+ CrosSettings::Initialize();
+
+ NetworkHandler::Initialize();
+ ShillServiceClient::TestInterface* service_test =
+ DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
+ service_test->AddService("/service/eth",
+ "eth" /* guid */,
+ "eth",
+ shill::kTypeEthernet, shill::kStateOnline,
+ true /* visible */);
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void TearDown() override {
+ NetworkHandler::Shutdown();
+
+ CrosSettings::Shutdown();
+ DeviceSettingsService::Shutdown();
+ }
+
+ content::TestBrowserThreadBundle thread_bundle_;
+ std::unique_ptr<VersionUpdater> version_updater_;
+ FakeUpdateEngineClient* fake_update_engine_client_; // Not owned.
+
+ MockUserManager* mock_user_manager_; // Not owned.
+ ScopedUserManagerEnabler user_manager_enabler_;
+
+ DISALLOW_COPY_AND_ASSIGN(VersionUpdaterCrosTest);
+};
+
+// The test checks following behaviour:
+// 1. The device is currently on the dev channel and an user decides to switch
+// to the beta channel.
+// 2. In the middle of channel switch the user decides to switch to the stable
+// channel.
+// 3. Update engine reports an error because downloading channel (beta) is not
+// equal
+// to the target channel (stable).
+// 4. When update engine becomes idle downloading of the stable channel is
+// initiated.
+TEST_F(VersionUpdaterCrosTest, TwoOverlappingSetChannelRequests) {
+ version_updater_->SetChannel("beta-channel", true);
+
+ {
+ UpdateEngineClient::Status status;
+ status.status = UpdateEngineClient::UPDATE_STATUS_IDLE;
+ fake_update_engine_client_->set_default_status(status);
+ fake_update_engine_client_->NotifyObserversThatStatusChanged(status);
+ }
+
+ EXPECT_EQ(0, fake_update_engine_client_->request_update_check_call_count());
+
+ // IDLE -> DOWNLOADING transition after update check.
+ version_updater_->CheckForUpdate(base::Bind(&CheckNotification),
+ VersionUpdater::PromoteCallback());
+ EXPECT_EQ(1, fake_update_engine_client_->request_update_check_call_count());
+
+ {
+ UpdateEngineClient::Status status;
+ status.status = UpdateEngineClient::UPDATE_STATUS_DOWNLOADING;
+ status.download_progress = 0.1;
+ fake_update_engine_client_->set_default_status(status);
+ fake_update_engine_client_->NotifyObserversThatStatusChanged(status);
+ }
+
+ version_updater_->SetChannel("stable-channel", true);
+
+ // DOWNLOADING -> REPORTING_ERROR_EVENT transition since target channel is not
+ // equal to downloading channel now.
+ {
+ UpdateEngineClient::Status status;
+ status.status = UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT;
+ fake_update_engine_client_->set_default_status(status);
+ fake_update_engine_client_->NotifyObserversThatStatusChanged(status);
+ }
+
+ version_updater_->CheckForUpdate(base::Bind(&CheckNotification),
+ VersionUpdater::PromoteCallback());
+ EXPECT_EQ(1, fake_update_engine_client_->request_update_check_call_count());
+
+ // REPORTING_ERROR_EVENT -> IDLE transition, update check should be
+ // automatically scheduled.
+ {
+ UpdateEngineClient::Status status;
+ status.status = UpdateEngineClient::UPDATE_STATUS_IDLE;
+ fake_update_engine_client_->set_default_status(status);
+ fake_update_engine_client_->NotifyObserversThatStatusChanged(status);
+ }
+
+ EXPECT_EQ(2, fake_update_engine_client_->request_update_check_call_count());
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/help/version_updater_mac.h b/chromium/chrome/browser/ui/webui/help/version_updater_mac.h
new file mode 100644
index 00000000000..ab08e17ac3d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/help/version_updater_mac.h
@@ -0,0 +1,59 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_HELP_VERSION_UPDATER_MAC_H_
+#define CHROME_BROWSER_UI_WEBUI_HELP_VERSION_UPDATER_MAC_H_
+
+#import <AppKit/AppKit.h>
+
+#include "base/compiler_specific.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/help/version_updater.h"
+
+@class KeystoneObserver;
+
+// OS X implementation of version update functionality, used by the WebUI
+// About/Help page.
+class VersionUpdaterMac : public VersionUpdater {
+ public:
+ // VersionUpdater implementation.
+ void CheckForUpdate(const StatusCallback& status_callback,
+ const PromoteCallback& promote_callback) override;
+ void PromoteUpdater() const override;
+
+ // Process status updates received from Keystone. The dictionary will contain
+ // an AutoupdateStatus value as an intValue at key kAutoupdateStatusStatus. If
+ // a version is available (see AutoupdateStatus), it will be present at key
+ // kAutoupdateStatusVersion.
+ void UpdateStatus(NSDictionary* status);
+
+ protected:
+ friend class VersionUpdater;
+
+ // Clients must use VersionUpdater::Create().
+ VersionUpdaterMac();
+ ~VersionUpdaterMac() override;
+
+ private:
+ // Update the visibility state of promote button.
+ void UpdateShowPromoteButton();
+
+ // Callback used to communicate update status to the client.
+ StatusCallback status_callback_;
+
+ // Callback used to show or hide the promote UI elements.
+ PromoteCallback promote_callback_;
+
+ // The visible state of the promote button.
+ bool show_promote_button_;
+
+ // The observer that will receive keystone status updates.
+ base::scoped_nsobject<KeystoneObserver> keystone_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(VersionUpdaterMac);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_HELP_VERSION_UPDATER_MAC_H_
+
diff --git a/chromium/chrome/browser/ui/webui/help/version_updater_mac.mm b/chromium/chrome/browser/ui/webui/help/version_updater_mac.mm
new file mode 100644
index 00000000000..c7f0393ab93
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/help/version_updater_mac.mm
@@ -0,0 +1,281 @@
+// 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/ui/webui/help/version_updater_mac.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#import "chrome/browser/mac/keystone_glue.h"
+#include "chrome/browser/obsolete_system/obsolete_system.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "net/base/escape.h"
+#include "ui/base/l10n/l10n_util.h"
+
+// KeystoneObserver is a simple notification observer for Keystone status
+// updates. It will be created and managed by VersionUpdaterMac.
+@interface KeystoneObserver : NSObject {
+ @private
+ VersionUpdaterMac* versionUpdater_; // Weak.
+}
+
+// Initialize an observer with an updater. The updater owns this object.
+- (id)initWithUpdater:(VersionUpdaterMac*)updater;
+
+// Notification callback, called with the status of keystone operations.
+- (void)handleStatusNotification:(NSNotification*)notification;
+
+@end // @interface KeystoneObserver
+
+@implementation KeystoneObserver
+
+- (id)initWithUpdater:(VersionUpdaterMac*)updater {
+ if ((self = [super init])) {
+ versionUpdater_ = updater;
+ NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
+ [center addObserver:self
+ selector:@selector(handleStatusNotification:)
+ name:kAutoupdateStatusNotification
+ object:nil];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [super dealloc];
+}
+
+- (void)handleStatusNotification:(NSNotification*)notification {
+ versionUpdater_->UpdateStatus([notification userInfo]);
+}
+
+@end // @implementation KeystoneObserver
+
+VersionUpdater* VersionUpdater::Create(
+ content::WebContents* /* web_contents */) {
+ return new VersionUpdaterMac;
+}
+
+VersionUpdaterMac::VersionUpdaterMac()
+ : show_promote_button_(false),
+ keystone_observer_([[KeystoneObserver alloc] initWithUpdater:this]) {
+}
+
+VersionUpdaterMac::~VersionUpdaterMac() {
+}
+
+void VersionUpdaterMac::CheckForUpdate(
+ const StatusCallback& status_callback,
+ const PromoteCallback& promote_callback) {
+ // Copy the callbacks, we will re-use this for the remaining lifetime
+ // of this object.
+ status_callback_ = status_callback;
+ promote_callback_ = promote_callback;
+
+ KeystoneGlue* keystone_glue = [KeystoneGlue defaultKeystoneGlue];
+ if (keystone_glue && ![keystone_glue isOnReadOnlyFilesystem]) {
+ AutoupdateStatus recent_status = [keystone_glue recentStatus];
+ if ([keystone_glue asyncOperationPending] ||
+ recent_status == kAutoupdateRegisterFailed ||
+ recent_status == kAutoupdateNeedsPromotion) {
+ // If an asynchronous update operation is currently pending, such as a
+ // check for updates or an update installation attempt, set the status
+ // up correspondingly without launching a new update check.
+ //
+ // If registration failed, no other operations make sense, so just go
+ // straight to the error.
+ UpdateStatus([[keystone_glue recentNotification] userInfo]);
+ } else {
+ // Launch a new update check, even if one was already completed, because
+ // a new update may be available or a new update may have been installed
+ // in the background since the last time the Help page was displayed.
+ [keystone_glue checkForUpdate];
+
+ // Immediately, kAutoupdateStatusNotification will be posted, with status
+ // kAutoupdateChecking.
+ //
+ // Upon completion, kAutoupdateStatusNotification will be posted with a
+ // status indicating the result of the check.
+ }
+
+ UpdateShowPromoteButton();
+ } else {
+ // There is no glue, or the application is on a read-only filesystem.
+ // Updates and promotions are impossible.
+ status_callback_.Run(DISABLED, 0, std::string(), 0, base::string16());
+ }
+}
+
+void VersionUpdaterMac::PromoteUpdater() const {
+ // Tell Keystone to make software updates available for all users.
+ [[KeystoneGlue defaultKeystoneGlue] promoteTicket];
+
+ // Immediately, kAutoupdateStatusNotification will be posted, and
+ // UpdateStatus() will be called with status kAutoupdatePromoting.
+ //
+ // Upon completion, kAutoupdateStatusNotification will be posted, and
+ // UpdateStatus() will be called with a status indicating a result of the
+ // installation attempt.
+ //
+ // If the promotion was successful, KeystoneGlue will re-register the ticket
+ // and UpdateStatus() will be called again indicating first that
+ // registration is in progress and subsequently that it has completed.
+}
+
+void VersionUpdaterMac::UpdateStatus(NSDictionary* dictionary) {
+ AutoupdateStatus keystone_status = static_cast<AutoupdateStatus>(
+ [base::mac::ObjCCastStrict<NSNumber>(
+ [dictionary objectForKey:kAutoupdateStatusStatus]) intValue]);
+ std::string error_messages = base::SysNSStringToUTF8(
+ base::mac::ObjCCastStrict<NSString>(
+ [dictionary objectForKey:kAutoupdateStatusErrorMessages]));
+
+ bool enable_promote_button = true;
+ base::string16 message;
+
+ Status status;
+ switch (keystone_status) {
+ case kAutoupdateRegistering:
+ case kAutoupdateChecking:
+ status = CHECKING;
+ enable_promote_button = false;
+ break;
+
+ case kAutoupdateRegistered:
+ case kAutoupdatePromoted:
+ UpdateShowPromoteButton();
+ // Go straight into an update check. Return immediately, this routine
+ // will be re-entered shortly with kAutoupdateChecking.
+ [[KeystoneGlue defaultKeystoneGlue] checkForUpdate];
+ return;
+
+ case kAutoupdateCurrent:
+ status = UPDATED;
+ break;
+
+ case kAutoupdateAvailable:
+ // Install the update automatically. Return immediately, this routine
+ // will be re-entered shortly with kAutoupdateInstalling.
+ [[KeystoneGlue defaultKeystoneGlue] installUpdate];
+ return;
+
+ case kAutoupdateInstalling:
+ status = UPDATING;
+ enable_promote_button = false;
+ break;
+
+ case kAutoupdateInstalled:
+ status = NEARLY_UPDATED;
+ break;
+
+ case kAutoupdatePromoting:
+#if 1
+ // TODO(mark): KSRegistration currently handles the promotion
+ // synchronously, meaning that the main thread's loop doesn't spin,
+ // meaning that animations and other updates to the window won't occur
+ // until KSRegistration is done with promotion. This looks laggy and bad
+ // and probably qualifies as "jank." For now, there just won't be any
+ // visual feedback while promotion is in progress, but it should complete
+ // (or fail) very quickly. http://b/2290009.
+ return;
+#endif
+ status = CHECKING;
+ enable_promote_button = false;
+ break;
+
+ case kAutoupdateRegisterFailed:
+ enable_promote_button = false;
+ // Fall through.
+ case kAutoupdateCheckFailed:
+ case kAutoupdateInstallFailed:
+ case kAutoupdatePromoteFailed:
+ status = FAILED;
+ message = l10n_util::GetStringFUTF16Int(IDS_UPGRADE_ERROR,
+ keystone_status);
+ break;
+
+ case kAutoupdateNeedsPromotion:
+ {
+ status = FAILED;
+ base::string16 product_name =
+ l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
+ message = l10n_util::GetStringFUTF16(IDS_PROMOTE_INFOBAR_TEXT,
+ product_name);
+ }
+ break;
+
+ default:
+ NOTREACHED();
+ return;
+ }
+
+ // If there are any detailed error messages being passed along by Keystone,
+ // log them. If we have an error to display, include the detail messages
+ // below the error in a <pre> block. Don't bother displaying detail messages
+ // on a success/in-progress/indeterminate status.
+ if (!error_messages.empty()) {
+ VLOG(1) << "Update error messages: " << error_messages;
+
+ if (status == FAILED) {
+ if (!message.empty()) {
+ message += base::UTF8ToUTF16("<br/><br/>");
+ }
+
+ message += l10n_util::GetStringUTF16(IDS_UPGRADE_ERROR_DETAILS);
+ message += base::UTF8ToUTF16("<br/><pre>");
+ message += base::UTF8ToUTF16(net::EscapeForHTML(error_messages));
+ message += base::UTF8ToUTF16("</pre>");
+ }
+ }
+
+ if (!status_callback_.is_null())
+ status_callback_.Run(status, 0, std::string(), 0, message);
+
+ PromotionState promotion_state;
+ if (!promote_callback_.is_null()) {
+ KeystoneGlue* keystone_glue = [KeystoneGlue defaultKeystoneGlue];
+ if (keystone_glue && [keystone_glue isAutoupdateEnabledForAllUsers]) {
+ promotion_state = PROMOTED;
+ } else {
+ promotion_state = PROMOTE_HIDDEN;
+
+ if (show_promote_button_) {
+ promotion_state = enable_promote_button ? PROMOTE_ENABLED
+ : PROMOTE_DISABLED;
+ }
+ }
+
+ promote_callback_.Run(promotion_state);
+ }
+}
+
+void VersionUpdaterMac::UpdateShowPromoteButton() {
+ if (ObsoleteSystem::IsObsoleteNowOrSoon()) {
+ // Promotion is moot upon reaching the end of the line.
+ show_promote_button_ = false;
+ return;
+ }
+
+ KeystoneGlue* keystone_glue = [KeystoneGlue defaultKeystoneGlue];
+ AutoupdateStatus recent_status = [keystone_glue recentStatus];
+ if (recent_status == kAutoupdateRegistering ||
+ recent_status == kAutoupdateRegisterFailed ||
+ recent_status == kAutoupdatePromoted) {
+ // Promotion isn't possible at this point.
+ show_promote_button_ = false;
+ } else if (recent_status == kAutoupdatePromoting ||
+ recent_status == kAutoupdatePromoteFailed) {
+ // Show promotion UI because the user either just clicked that button or
+ // because the user should be able to click it again.
+ show_promote_button_ = true;
+ } else {
+ // Show the promote button if promotion is a possibility.
+ show_promote_button_ = [keystone_glue wantsPromotion];
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/help/version_updater_win.cc b/chromium/chrome/browser/ui/webui/help/version_updater_win.cc
new file mode 100644
index 00000000000..43bcd5945b4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/help/version_updater_win.cc
@@ -0,0 +1,150 @@
+// 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/ui/webui/help/version_updater_win.h"
+
+#include "base/memory/weak_ptr.h"
+#include "base/task_runner_util.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/win/win_util.h"
+#include "base/win/windows_version.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/first_run/upgrade_util.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/native_widget_types.h"
+
+VersionUpdaterWin::VersionUpdaterWin(gfx::AcceleratedWidget owner_widget)
+ : owner_widget_(owner_widget), weak_factory_(this) {
+}
+
+VersionUpdaterWin::~VersionUpdaterWin() {
+}
+
+void VersionUpdaterWin::CheckForUpdate(const StatusCallback& callback,
+ const PromoteCallback&) {
+ // There is no supported integration with Google Update for Chromium.
+ callback_ = callback;
+
+ // On-demand updates for Chrome don't work in Vista RTM when UAC is turned
+ // off. So, in this case, the version updater must not mention
+ // on-demand updates. Silent updates (in the background) should still
+ // work as before - enabling UAC or installing the latest service pack
+ // for Vista is another option.
+ if (!(base::win::GetVersion() == base::win::VERSION_VISTA &&
+ (base::win::OSInfo::GetInstance()->service_pack().major == 0) &&
+ !base::win::UserAccountControlIsEnabled())) {
+ callback_.Run(CHECKING, 0, std::string(), 0, base::string16());
+ BeginUpdateCheckOnFileThread(false /* !install_update_if_possible */);
+ }
+}
+
+void VersionUpdaterWin::OnUpdateCheckComplete(
+ const base::string16& new_version) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ Status status = CHECKING;
+ if (new_version.empty()) {
+ // Google Update says that no new version is available. Check to see if a
+ // restart is needed for a previously-applied update to take effect.
+ if (base::PostTaskAndReplyWithResult(
+ content::BrowserThread::GetBlockingPool(),
+ FROM_HERE,
+ base::Bind(&upgrade_util::IsUpdatePendingRestart),
+ base::Bind(&VersionUpdaterWin::OnPendingRestartCheck,
+ weak_factory_.GetWeakPtr()))) {
+ // Early exit since callback_ will be Run in OnPendingRestartCheck.
+ return;
+ }
+ // Failure to post the task means that Chrome is shutting down. A pending
+ // update (if there is one) will be applied as Chrome exits, so tell the
+ // caller that it is up to date in either case.
+ status = UPDATED;
+ } else {
+ // Notify the caller that the update is now beginning and initiate it.
+ status = UPDATING;
+ BeginUpdateCheckOnFileThread(true /* install_update_if_possible */);
+ }
+ callback_.Run(status, 0, std::string(), 0, base::string16());
+}
+
+void VersionUpdaterWin::OnUpgradeProgress(int progress,
+ const base::string16& new_version) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ callback_.Run(UPDATING, progress, std::string(), 0, base::string16());
+}
+
+void VersionUpdaterWin::OnUpgradeComplete(const base::string16& new_version) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ callback_.Run(NEARLY_UPDATED, 0, std::string(), 0, base::string16());
+}
+
+void VersionUpdaterWin::OnError(GoogleUpdateErrorCode error_code,
+ const base::string16& html_error_message,
+ const base::string16& new_version) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ base::string16 message;
+ Status status = FAILED;
+
+ switch (error_code) {
+ case GOOGLE_UPDATE_DISABLED_BY_POLICY:
+ status = DISABLED_BY_ADMIN;
+ message =
+ l10n_util::GetStringUTF16(IDS_UPGRADE_DISABLED_BY_POLICY);
+ break;
+ case GOOGLE_UPDATE_DISABLED_BY_POLICY_AUTO_ONLY:
+ status = DISABLED_BY_ADMIN;
+ message =
+ l10n_util::GetStringUTF16(IDS_UPGRADE_DISABLED_BY_POLICY_MANUAL);
+ break;
+ default:
+ // html_error_message mentions error_code so don't combine messages.
+ if (html_error_message.empty()) {
+ message = l10n_util::GetStringFUTF16Int(IDS_UPGRADE_ERROR, error_code);
+ } else {
+ message = l10n_util::GetStringFUTF16(
+ IDS_ABOUT_BOX_ERROR_DURING_UPDATE_CHECK, html_error_message);
+ }
+ break;
+ }
+ callback_.Run(status, 0, std::string(), 0, message);
+}
+
+void VersionUpdaterWin::BeginUpdateCheckOnFileThread(
+ bool install_update_if_possible) {
+ // Disconnect from any previous attempts to avoid redundant callbacks.
+ weak_factory_.InvalidateWeakPtrs();
+ BeginUpdateCheck(content::BrowserThread::GetTaskRunnerForThread(
+ content::BrowserThread::FILE),
+ g_browser_process->GetApplicationLocale(),
+ install_update_if_possible, owner_widget_,
+ weak_factory_.GetWeakPtr());
+}
+
+void VersionUpdaterWin::OnPendingRestartCheck(bool is_update_pending_restart) {
+ callback_.Run(is_update_pending_restart ? NEARLY_UPDATED : UPDATED, 0,
+ std::string(), 0, base::string16());
+}
+
+VersionUpdater* VersionUpdater::Create(content::WebContents* web_contents) {
+ // Retrieve the HWND for the browser window that is hosting the update check.
+ // This will be used as the parent for a UAC prompt, if needed. It's possible
+ // this this window will no longer have focus by the time UAC is needed. In
+ // that case, the UAC prompt will appear in the taskbar and will require a
+ // user click. This is the least surprising thing we can do for the user, and
+ // is the intended behavior for Windows applications.
+ // It's also possible that the browser window hosting the update check will
+ // have been closed by the time the UAC prompt is needed. In this case, the
+ // web contents may no longer be hosted in a window, leading either
+ // GetTopLevelNativeWindow or GetHost to return null. Passing nullptr to
+ // VersionUpdaterWin will then also cause the UAC prompt to appear in the task
+ // bar.
+ gfx::NativeWindow window = web_contents->GetTopLevelNativeWindow();
+ aura::WindowTreeHost* window_tree_host = window ? window->GetHost() : nullptr;
+ return new VersionUpdaterWin(
+ window_tree_host ? window_tree_host->GetAcceleratedWidget() : nullptr);
+}
diff --git a/chromium/chrome/browser/ui/webui/help/version_updater_win.h b/chromium/chrome/browser/ui/webui/help/version_updater_win.h
new file mode 100644
index 00000000000..eab092d1002
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/help/version_updater_win.h
@@ -0,0 +1,59 @@
+// 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.
+
+// Windows implementation of version update functionality, used by the WebUI
+// About/Help page.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_HELP_VERSION_UPDATER_WIN_H_
+#define CHROME_BROWSER_UI_WEBUI_HELP_VERSION_UPDATER_WIN_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+#include "chrome/browser/google/google_update_win.h"
+#include "chrome/browser/ui/webui/help/version_updater.h"
+
+class VersionUpdaterWin : public VersionUpdater,
+ public UpdateCheckDelegate {
+ public:
+ // |owner_widget| is the parent widget hosting the update check UI. Any UI
+ // needed to install an update (e.g., a UAC prompt for a system-level install)
+ // will be parented to this widget. |owner_widget| may be given a value of
+ // nullptr in which case the UAC prompt will be parented to the desktop.
+ explicit VersionUpdaterWin(gfx::AcceleratedWidget owner_widget);
+ ~VersionUpdaterWin() override;
+
+ // VersionUpdater:
+ void CheckForUpdate(const StatusCallback& callback,
+ const PromoteCallback&) override;
+
+ // UpdateCheckDelegate:
+ void OnUpdateCheckComplete(const base::string16& new_version) override;
+ void OnUpgradeProgress(int progress,
+ const base::string16& new_version) override;
+ void OnUpgradeComplete(const base::string16& new_version) override;
+ void OnError(GoogleUpdateErrorCode error_code,
+ const base::string16& html_error_message,
+ const base::string16& new_version) override;
+
+ private:
+ void BeginUpdateCheckOnFileThread(bool install_update_if_possible);
+
+ // A task run on the UI thread with the result of checking for a pending
+ // restart.
+ void OnPendingRestartCheck(bool is_update_pending_restart);
+
+ // The widget owning the UI for the update check.
+ gfx::AcceleratedWidget owner_widget_;
+
+ // Callback used to communicate update status to the client.
+ StatusCallback callback_;
+
+ // Used for callbacks.
+ base::WeakPtrFactory<VersionUpdaterWin> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(VersionUpdaterWin);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_HELP_VERSION_UPDATER_WIN_H_
diff --git a/chromium/chrome/browser/ui/webui/history_login_handler.cc b/chromium/chrome/browser/ui/webui/history_login_handler.cc
new file mode 100644
index 00000000000..33d1b520b43
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/history_login_handler.cc
@@ -0,0 +1,62 @@
+// 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.
+
+#include "chrome/browser/ui/webui/history_login_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/webui/profile_info_watcher.h"
+#include "components/signin/core/browser/signin_metrics.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+
+HistoryLoginHandler::HistoryLoginHandler(const base::Closure& signin_callback)
+ : signin_callback_(signin_callback) {}
+
+HistoryLoginHandler::~HistoryLoginHandler() {}
+
+void HistoryLoginHandler::RegisterMessages() {
+ profile_info_watcher_.reset(new ProfileInfoWatcher(
+ Profile::FromWebUI(web_ui()),
+ base::Bind(&HistoryLoginHandler::ProfileInfoChanged,
+ base::Unretained(this))));
+
+ web_ui()->RegisterMessageCallback("otherDevicesInitialized",
+ base::Bind(&HistoryLoginHandler::HandleOtherDevicesInitialized,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback("startSignInFlow",
+ base::Bind(&HistoryLoginHandler::HandleStartSignInFlow,
+ base::Unretained(this)));
+}
+
+void HistoryLoginHandler::HandleOtherDevicesInitialized(
+ const base::ListValue* /*args*/) {
+ AllowJavascript();
+ ProfileInfoChanged();
+}
+
+void HistoryLoginHandler::ProfileInfoChanged() {
+ bool signed_in = !profile_info_watcher_->GetAuthenticatedUsername().empty();
+ if (!signin_callback_.is_null())
+ signin_callback_.Run();
+
+ if (IsJavascriptAllowed())
+ CallJavascriptFunction("updateSignInState", base::Value(signed_in));
+}
+
+void HistoryLoginHandler::HandleStartSignInFlow(
+ const base::ListValue* /*args*/) {
+ Browser* browser =
+ chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
+ browser->window()->ShowAvatarBubbleFromAvatarButton(
+ BrowserWindow::AVATAR_BUBBLE_MODE_SIGNIN, signin::ManageAccountsParams(),
+ signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS, false);
+}
diff --git a/chromium/chrome/browser/ui/webui/history_login_handler.h b/chromium/chrome/browser/ui/webui/history_login_handler.h
new file mode 100644
index 00000000000..82ceb744034
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/history_login_handler.h
@@ -0,0 +1,44 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_HISTORY_LOGIN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_HISTORY_LOGIN_HANDLER_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+class ProfileInfoWatcher;
+
+// The handler for login-related messages from chrome://history.
+class HistoryLoginHandler : public content::WebUIMessageHandler {
+ public:
+ explicit HistoryLoginHandler(const base::Closure& signin_callback);
+ ~HistoryLoginHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ // Handler for the "otherDevicesInitialized" message. No args.
+ void HandleOtherDevicesInitialized(const base::ListValue* args);
+
+ // Handler for the "startSignInFlow" message. No args.
+ void HandleStartSignInFlow(const base::ListValue* args);
+
+ // Called by |profile_info_watcher_| on desktop if profile info changes.
+ void ProfileInfoChanged();
+
+ // Watches this web UI's profile for info changes (e.g. authenticated username
+ // changes).
+ std::unique_ptr<ProfileInfoWatcher> profile_info_watcher_;
+
+ base::Closure signin_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(HistoryLoginHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_HISTORY_LOGIN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/identity_internals_ui.cc b/chromium/chrome/browser/ui/webui/identity_internals_ui.cc
new file mode 100644
index 00000000000..d519d8ab6d9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/identity_internals_ui.cc
@@ -0,0 +1,331 @@
+// 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/identity_internals_ui.h"
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/bind.h"
+#include "base/i18n/time_formatting.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/api/identity/identity_api.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "extensions/browser/extension_registry.h"
+#include "google_apis/gaia/gaia_auth_fetcher.h"
+#include "google_apis/gaia/gaia_constants.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+// Properties of the Javascript object representing a token.
+const char kExtensionId[] = "extensionId";
+const char kExtensionName[] = "extensionName";
+const char kScopes[] = "scopes";
+const char kStatus[] = "status";
+const char kTokenExpirationTime[] = "expirationTime";
+const char kAccessToken[] = "accessToken";
+
+// RevokeToken message parameter offsets.
+const int kRevokeTokenExtensionOffset = 0;
+const int kRevokeTokenTokenOffset = 1;
+
+class IdentityInternalsTokenRevoker;
+
+// Class acting as a controller of the chrome://identity-internals WebUI.
+class IdentityInternalsUIMessageHandler : public content::WebUIMessageHandler {
+ public:
+ IdentityInternalsUIMessageHandler();
+ ~IdentityInternalsUIMessageHandler() override;
+
+ // Ensures that a proper clean up happens after a token is revoked. That
+ // includes deleting the |token_revoker|, removing the token from Identity API
+ // cache and updating the UI that the token is gone.
+ void OnTokenRevokerDone(IdentityInternalsTokenRevoker* token_revoker);
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ // Gets the name of an extension referred to by |token_cache_key| as a string.
+ const std::string GetExtensionName(
+ const extensions::ExtensionTokenKey& token_cache_key);
+
+ // Gets a list of scopes specified in |token_cache_key| and returns a pointer
+ // to a ListValue containing the scopes. The caller gets ownership of the
+ // returned object.
+ std::unique_ptr<base::ListValue> GetScopes(
+ const extensions::ExtensionTokenKey& token_cache_key);
+
+ // Gets a localized status of the access token in |token_cache_value|.
+ const base::string16 GetStatus(
+ const extensions::IdentityTokenCacheValue& token_cache_value);
+
+ // Gets a string representation of an expiration time of the access token in
+ // |token_cache_value|.
+ const std::string GetExpirationTime(
+ const extensions::IdentityTokenCacheValue& token_cache_value);
+
+ // Converts a pair of |token_cache_key| and |token_cache_value| to a
+ // DictionaryValue object with corresponding information in a localized and
+ // readable form and returns a pointer to created object.
+ std::unique_ptr<base::DictionaryValue> GetInfoForToken(
+ const extensions::ExtensionTokenKey& token_cache_key,
+ const extensions::IdentityTokenCacheValue& token_cache_value);
+
+ // Gets all of the tokens stored in IdentityAPI token cache and returns them
+ // to the caller using Javascript callback function
+ // |identity_internals.returnTokens()|.
+ void GetInfoForAllTokens(const base::ListValue* args);
+
+ // Initiates revoking of the token, based on the extension ID and token
+ // passed as entries in the |args| list. Updates the caller of completion
+ // using Javascript callback function |identity_internals.tokenRevokeDone()|.
+ void RevokeToken(const base::ListValue* args);
+
+ // A vector of token revokers that are currently revoking tokens.
+ std::vector<std::unique_ptr<IdentityInternalsTokenRevoker>> token_revokers_;
+};
+
+// Handles the revoking of an access token and helps performing the clean up
+// after it is revoked by holding information about the access token and related
+// extension ID.
+class IdentityInternalsTokenRevoker : public GaiaAuthConsumer {
+ public:
+ // Revokes |access_token| from extension with |extension_id|.
+ // |profile| is required for its request context. |consumer| will be
+ // notified when revocation succeeds via |OnTokenRevokerDone()|.
+ IdentityInternalsTokenRevoker(const std::string& extension_id,
+ const std::string& access_token,
+ Profile* profile,
+ IdentityInternalsUIMessageHandler* consumer);
+ ~IdentityInternalsTokenRevoker() override;
+
+ // Returns the access token being revoked.
+ const std::string& access_token() const { return access_token_; }
+
+ // Returns the ID of the extension the access token is related to.
+ const std::string& extension_id() const { return extension_id_; }
+
+ // GaiaAuthConsumer implementation.
+ void OnOAuth2RevokeTokenCompleted() override;
+
+ private:
+ // An object used to start a token revoke request.
+ GaiaAuthFetcher fetcher_;
+ // An ID of an extension the access token is related to.
+ const std::string extension_id_;
+ // The access token to revoke.
+ const std::string access_token_;
+ // An object that needs to be notified once the access token is revoked.
+ IdentityInternalsUIMessageHandler* consumer_; // weak.
+
+ DISALLOW_COPY_AND_ASSIGN(IdentityInternalsTokenRevoker);
+};
+
+IdentityInternalsUIMessageHandler::IdentityInternalsUIMessageHandler() {}
+
+IdentityInternalsUIMessageHandler::~IdentityInternalsUIMessageHandler() {}
+
+void IdentityInternalsUIMessageHandler::OnTokenRevokerDone(
+ IdentityInternalsTokenRevoker* token_revoker) {
+ extensions::IdentityAPI* api =
+ extensions::IdentityAPI::GetFactoryInstance()->Get(
+ Profile::FromWebUI(web_ui()));
+ // API can be null in incognito, but then we shouldn't be in this function.
+ // This case is handled because this is called from a renderer process
+ // which could conceivably be compromised.
+ CHECK(api);
+
+ // Remove token from the cache.
+ api->EraseCachedToken(token_revoker->extension_id(),
+ token_revoker->access_token());
+
+ // Update view about the token being removed.
+ base::ListValue result;
+ result.AppendString(token_revoker->access_token());
+ web_ui()->CallJavascriptFunctionUnsafe("identity_internals.tokenRevokeDone",
+ result);
+
+ // Erase the revoker.
+ for (auto iter = token_revokers_.begin(); iter != token_revokers_.end();
+ ++iter) {
+ if (iter->get() == token_revoker) {
+ token_revokers_.erase(iter);
+ return;
+ }
+ }
+ DCHECK(false) << "revoker should have been in the list";
+}
+
+const std::string IdentityInternalsUIMessageHandler::GetExtensionName(
+ const extensions::ExtensionTokenKey& token_cache_key) {
+ const extensions::ExtensionRegistry* registry =
+ extensions::ExtensionRegistry::Get(Profile::FromWebUI(web_ui()));
+ const extensions::Extension* extension =
+ registry->enabled_extensions().GetByID(token_cache_key.extension_id);
+ if (!extension)
+ return std::string();
+ return extension->name();
+}
+
+std::unique_ptr<base::ListValue> IdentityInternalsUIMessageHandler::GetScopes(
+ const extensions::ExtensionTokenKey& token_cache_key) {
+ auto scopes_value = base::MakeUnique<base::ListValue>();
+ for (std::set<std::string>::const_iterator
+ iter = token_cache_key.scopes.begin();
+ iter != token_cache_key.scopes.end(); ++iter) {
+ scopes_value->AppendString(*iter);
+ }
+ return scopes_value;
+}
+
+const base::string16 IdentityInternalsUIMessageHandler::GetStatus(
+ const extensions::IdentityTokenCacheValue& token_cache_value) {
+ switch (token_cache_value.status()) {
+ case extensions::IdentityTokenCacheValue::CACHE_STATUS_ADVICE:
+ // Fallthrough to NOT FOUND case, as ADVICE is short lived.
+ case extensions::IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND:
+ return l10n_util::GetStringUTF16(
+ IDS_IDENTITY_INTERNALS_TOKEN_NOT_FOUND);
+ case extensions::IdentityTokenCacheValue::CACHE_STATUS_TOKEN:
+ return l10n_util::GetStringUTF16(
+ IDS_IDENTITY_INTERNALS_TOKEN_PRESENT);
+ }
+ NOTREACHED();
+ return base::string16();
+}
+
+const std::string IdentityInternalsUIMessageHandler::GetExpirationTime(
+ const extensions::IdentityTokenCacheValue& token_cache_value) {
+ return base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
+ token_cache_value.expiration_time()));
+}
+
+std::unique_ptr<base::DictionaryValue>
+IdentityInternalsUIMessageHandler::GetInfoForToken(
+ const extensions::ExtensionTokenKey& token_cache_key,
+ const extensions::IdentityTokenCacheValue& token_cache_value) {
+ std::unique_ptr<base::DictionaryValue> token_data(
+ new base::DictionaryValue());
+ token_data->SetString(kExtensionId, token_cache_key.extension_id);
+ token_data->SetString(kExtensionName, GetExtensionName(token_cache_key));
+ token_data->Set(kScopes, GetScopes(token_cache_key));
+ token_data->SetString(kStatus, GetStatus(token_cache_value));
+ token_data->SetString(kAccessToken, token_cache_value.token());
+ token_data->SetString(kTokenExpirationTime,
+ GetExpirationTime(token_cache_value));
+ return token_data;
+}
+
+void IdentityInternalsUIMessageHandler::GetInfoForAllTokens(
+ const base::ListValue* args) {
+ base::ListValue results;
+ extensions::IdentityAPI::CachedTokens tokens;
+ // The API can be null in incognito.
+ extensions::IdentityAPI* api =
+ extensions::IdentityAPI::GetFactoryInstance()->Get(
+ Profile::FromWebUI(web_ui()));
+ if (api)
+ tokens = api->GetAllCachedTokens();
+ for (extensions::IdentityAPI::CachedTokens::const_iterator
+ iter = tokens.begin(); iter != tokens.end(); ++iter) {
+ results.Append(GetInfoForToken(iter->first, iter->second));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("identity_internals.returnTokens",
+ results);
+}
+
+void IdentityInternalsUIMessageHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("identityInternalsGetTokens",
+ base::Bind(&IdentityInternalsUIMessageHandler::GetInfoForAllTokens,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("identityInternalsRevokeToken",
+ base::Bind(&IdentityInternalsUIMessageHandler::RevokeToken,
+ base::Unretained(this)));
+}
+
+void IdentityInternalsUIMessageHandler::RevokeToken(
+ const base::ListValue* args) {
+ std::string extension_id;
+ std::string access_token;
+ args->GetString(kRevokeTokenExtensionOffset, &extension_id);
+ args->GetString(kRevokeTokenTokenOffset, &access_token);
+ token_revokers_.push_back(base::MakeUnique<IdentityInternalsTokenRevoker>(
+ extension_id, access_token, Profile::FromWebUI(web_ui()), this));
+}
+
+IdentityInternalsTokenRevoker::IdentityInternalsTokenRevoker(
+ const std::string& extension_id,
+ const std::string& access_token,
+ Profile* profile,
+ IdentityInternalsUIMessageHandler* consumer)
+ : fetcher_(this, GaiaConstants::kChromeSource,
+ profile->GetRequestContext()),
+ extension_id_(extension_id),
+ access_token_(access_token),
+ consumer_(consumer) {
+ DCHECK(consumer_);
+ fetcher_.StartRevokeOAuth2Token(access_token);
+}
+
+IdentityInternalsTokenRevoker::~IdentityInternalsTokenRevoker() {}
+
+void IdentityInternalsTokenRevoker::OnOAuth2RevokeTokenCompleted() {
+ consumer_->OnTokenRevokerDone(this);
+}
+
+} // namespace
+
+IdentityInternalsUI::IdentityInternalsUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ // chrome://identity-internals source.
+ content::WebUIDataSource* html_source =
+ content::WebUIDataSource::Create(chrome::kChromeUIIdentityInternalsHost);
+
+ // Localized strings
+ html_source->AddLocalizedString("tokenCacheHeader",
+ IDS_IDENTITY_INTERNALS_TOKEN_CACHE_TEXT);
+ html_source->AddLocalizedString("accessToken",
+ IDS_IDENTITY_INTERNALS_ACCESS_TOKEN);
+ html_source->AddLocalizedString("extensionName",
+ IDS_IDENTITY_INTERNALS_EXTENSION_NAME);
+ html_source->AddLocalizedString("extensionId",
+ IDS_IDENTITY_INTERNALS_EXTENSION_ID);
+ html_source->AddLocalizedString("tokenStatus",
+ IDS_IDENTITY_INTERNALS_TOKEN_STATUS);
+ html_source->AddLocalizedString("expirationTime",
+ IDS_IDENTITY_INTERNALS_EXPIRATION_TIME);
+ html_source->AddLocalizedString("scopes",
+ IDS_IDENTITY_INTERNALS_SCOPES);
+ html_source->AddLocalizedString("revoke",
+ IDS_IDENTITY_INTERNALS_REVOKE);
+ html_source->SetJsonPath("strings.js");
+
+ // Required resources
+ html_source->AddResourcePath("identity_internals.css",
+ IDR_IDENTITY_INTERNALS_CSS);
+ html_source->AddResourcePath("identity_internals.js",
+ IDR_IDENTITY_INTERNALS_JS);
+ html_source->SetDefaultResource(IDR_IDENTITY_INTERNALS_HTML);
+
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), html_source);
+
+ web_ui->AddMessageHandler(
+ base::MakeUnique<IdentityInternalsUIMessageHandler>());
+}
+
+IdentityInternalsUI::~IdentityInternalsUI() {}
diff --git a/chromium/chrome/browser/ui/webui/identity_internals_ui.h b/chromium/chrome/browser/ui/webui/identity_internals_ui.h
new file mode 100644
index 00000000000..a3e965c538b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/identity_internals_ui.h
@@ -0,0 +1,22 @@
+// 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_IDENTITY_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_IDENTITY_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The WebUI for chrome://identity-internals
+class IdentityInternalsUI
+ : public content::WebUIController {
+ public:
+ explicit IdentityInternalsUI(content::WebUI* web_ui);
+ ~IdentityInternalsUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(IdentityInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_IDENTITY_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/identity_internals_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/identity_internals_ui_browsertest.cc
new file mode 100644
index 00000000000..82f47b98aa1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/identity_internals_ui_browsertest.cc
@@ -0,0 +1,60 @@
+// 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/identity_internals_ui_browsertest.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "chrome/browser/extensions/api/identity/extension_token_key.h"
+#include "chrome/browser/extensions/api/identity/identity_api.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+
+namespace {
+
+const char kChromeWebStoreId[] = "ahfgeienlihckogmohjhadlkjgocpleb";
+const int kOneHour = 3600;
+} // namespace
+
+IdentityInternalsUIBrowserTest::IdentityInternalsUIBrowserTest() {}
+
+IdentityInternalsUIBrowserTest::~IdentityInternalsUIBrowserTest() {}
+
+void IdentityInternalsUIBrowserTest::SetupTokenCache(int number_of_tokens) {
+ for (int number = 0; number < number_of_tokens; ++number) {
+ const std::string token_number = base::IntToString(number);
+ std::string token_id("token");
+ token_id += token_number;
+ std::string extension_id("extension");
+ extension_id += token_number;
+ std::vector<std::string> scopes;
+ scopes.push_back(std::string("scope_1_") + token_number);
+ scopes.push_back(std::string("scope_2_") + token_number);
+ AddTokenToCache(token_id, extension_id, scopes, kOneHour);
+ }
+}
+
+void IdentityInternalsUIBrowserTest::SetupTokenCacheWithStoreApp() {
+ std::vector<std::string> scopes;
+ scopes.push_back(std::string("store_scope1"));
+ scopes.push_back(std::string("store_scope2"));
+ AddTokenToCache("store_token", kChromeWebStoreId, scopes, kOneHour);
+}
+
+void IdentityInternalsUIBrowserTest::AddTokenToCache(
+ const std::string token_id,
+ const std::string extension_id,
+ const std::vector<std::string>& scopes,
+ int time_to_live) {
+ extensions::IdentityTokenCacheValue token_cache_value =
+ extensions::IdentityTokenCacheValue(token_id,
+ base::TimeDelta::FromSeconds(time_to_live));
+ extensions::ExtensionTokenKey key(
+ extension_id,
+ "test@example.com",
+ std::set<std::string>(scopes.begin(), scopes.end()));
+ extensions::IdentityAPI::GetFactoryInstance()
+ ->Get(browser()->profile())
+ ->SetCachedToken(key, token_cache_value);
+}
diff --git a/chromium/chrome/browser/ui/webui/identity_internals_ui_browsertest.h b/chromium/chrome/browser/ui/webui/identity_internals_ui_browsertest.h
new file mode 100644
index 00000000000..ab9dd46e357
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/identity_internals_ui_browsertest.h
@@ -0,0 +1,33 @@
+// 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_IDENTITY_INTERNALS_UI_BROWSERTEST_H_
+#define CHROME_BROWSER_UI_WEBUI_IDENTITY_INTERNALS_UI_BROWSERTEST_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "chrome/test/base/web_ui_browser_test.h"
+
+class IdentityInternalsUIBrowserTest : public WebUIBrowserTest {
+ public:
+ IdentityInternalsUIBrowserTest();
+ ~IdentityInternalsUIBrowserTest() override;
+
+ protected:
+ void SetupTokenCache(int number_of_tokens);
+
+ void SetupTokenCacheWithStoreApp();
+
+ private:
+ void AddTokenToCache(const std::string token_id,
+ const std::string extension_id,
+ const std::vector<std::string>& scopes,
+ int time_to_live);
+
+ DISALLOW_COPY_AND_ASSIGN(IdentityInternalsUIBrowserTest);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_IDENTITY_INTERNALS_UI_BROWSERTEST_H_
diff --git a/chromium/chrome/browser/ui/webui/identity_internals_ui_browsertest.js b/chromium/chrome/browser/ui/webui/identity_internals_ui_browsertest.js
new file mode 100644
index 00000000000..f71a7608c9d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/identity_internals_ui_browsertest.js
@@ -0,0 +1,260 @@
+// 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.
+
+GEN('#include "chrome/browser/ui/webui/identity_internals_ui_browsertest.h"');
+
+/**
+ * Test C++ fixture for downloads WebUI testing.
+ * @constructor
+ * @extends {testing.Test}
+ */
+function IdentityInternalsUIBrowserTest() {}
+
+/**
+ * Base fixture for Downloads WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function BaseIdentityInternalsWebUITest() {}
+
+BaseIdentityInternalsWebUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /**
+ * Browse to the downloads page & call our preLoad().
+ */
+ browsePreload: 'chrome://identity-internals',
+
+ /** @override */
+ typedefCppFixture: 'IdentityInternalsUIBrowserTest',
+
+ /**
+ * Gets all of the token entries on the page.
+ * @return {!NodeList} Elements displaying token information.
+ */
+ getTokens: function() {
+ return document.querySelectorAll('#token-list > div');
+ },
+
+ /**
+ * Gets the expiration time displayed on the page for a given entry.
+ * @param {Element} tokenEntry Display element holding token information.
+ * @return {Date} Expiration date of the token.
+ */
+ getExpirationTime: function(tokenEntry) {
+ // Full-date format has 'at' between date and time in en-US, but
+ // ECMAScript's Date.parse cannot grok it.
+ return Date.parse(tokenEntry.querySelector('.expiration-time')
+ .innerText.replace(' at ', ' '));
+ },
+
+ /**
+ * Gets the extension id displayed on the page for a given entry.
+ * @param {Element} tokenEntry Display element holding token information.
+ * @return {string} Extension Id of the token.
+ */
+ getExtensionId: function(tokenEntry) {
+ return tokenEntry.querySelector('.extension-id').innerText;
+ },
+
+ /**
+ * Gets the extension name displayed on the page for a given entry.
+ * @param {Element} tokenEntry Display element holding token information.
+ * @return {string} Extension Name of the token.
+ */
+ getExtensionName: function(tokenEntry) {
+ return tokenEntry.querySelector('.extension-name').innerText;
+ },
+
+ /**
+ * Gets the revoke button of the token entry.
+ * @param {Element} tokenEntry Diplsy element holding token information.
+ * @return {HTMLButtonElement} Revoke button belonging related to the token.
+ */
+ getRevokeButton: function(tokenEntry) {
+ return tokenEntry.querySelector('.revoke-button');
+ },
+
+ /**
+ * Gets the token ID displayed on the page for a given entry.
+ * @param {Element} tokenEntry Display element holding token information.
+ * @return {string} Token ID of the token.
+ */
+ getAccessToken: function(tokenEntry) {
+ return tokenEntry.querySelector('.access-token').innerText;
+ },
+
+ /**
+ * Gets the token status displayed on the page for a given entry.
+ * @param {Element} tokenEntry Display element holding token information.
+ * @return {string} Token status of the token.
+ */
+ getTokenStatus: function(tokenEntry) {
+ return tokenEntry.querySelector('.token-status').innerText;
+ },
+
+ /**
+ * Gets the token scopes displayed on the page for a given entry.
+ * @param {Element} tokenEntry Display element holding token information.
+ * @return {string[]} Token scopes of the token.
+ */
+ getScopes: function(tokenEntry) {
+ return tokenEntry.querySelector('.scope-list')
+ .innerHTML.split('<br>');
+ },
+};
+
+// Test verifying chrome://identity-internals Web UI when the token cache is
+// empty.
+TEST_F('BaseIdentityInternalsWebUITest', 'emptyTokenCache', function() {
+ var tokenListEntries = this.getTokens();
+ expectEquals(0, tokenListEntries.length);
+});
+
+/**
+ * Fixture for Identity Internals Web UI testing with a single token in the
+ * Identity API token cache.
+ * @extends {BaseIdentityInternalsWebUITest}
+ * @constructor
+ */
+function IdentityInternalsSingleTokenWebUITest() {}
+
+IdentityInternalsSingleTokenWebUITest.prototype = {
+ __proto__: BaseIdentityInternalsWebUITest.prototype,
+
+ /** @override */
+ testGenPreamble: function() {
+ GEN(' SetupTokenCacheWithStoreApp();');
+ },
+};
+
+// Test for listing a token cache with a single token. It uses a known extension
+// - the Chrome Web Store, in order to check the extension name.
+TEST_F('IdentityInternalsSingleTokenWebUITest', 'getAllTokens', function() {
+ var tokenListEntries = this.getTokens();
+ expectEquals(1, tokenListEntries.length);
+ expectEquals('Web Store', this.getExtensionName(tokenListEntries[0]));
+ expectEquals('ahfgeienlihckogmohjhadlkjgocpleb',
+ this.getExtensionId(tokenListEntries[0]));
+ expectEquals('store_token', this.getAccessToken(tokenListEntries[0]));
+ expectEquals('Token Present', this.getTokenStatus(tokenListEntries[0]));
+ expectLT(this.getExpirationTime(tokenListEntries[0]) - new Date(),
+ 3600 * 1000);
+ var scopes = this.getScopes(tokenListEntries[0]);
+ expectEquals(3, scopes.length);
+ expectEquals('store_scope1', scopes[0]);
+ expectEquals('store_scope2', scopes[1]);
+ expectEquals('', scopes[2]);
+});
+
+// Test ensuring the getters on the BaseIdentityInternalsWebUITest work
+// correctly. They are implemented on the child class, because the parent does
+// not have any tokens to display.
+TEST_F('IdentityInternalsSingleTokenWebUITest', 'verifyGetters', function() {
+ var tokenListEntries = document.querySelectorAll('#token-list > div');
+ var actualTokens = this.getTokens();
+ expectEquals(tokenListEntries.length, actualTokens.length);
+ expectEquals(tokenListEntries[0], actualTokens[0]);
+ expectEquals(this.getExtensionName(tokenListEntries[0]),
+ tokenListEntries[0].querySelector('.extension-name').innerText);
+ expectEquals(this.getExtensionId(tokenListEntries[0]),
+ tokenListEntries[0].querySelector('.extension-id').innerText);
+ expectEquals(this.getAccessToken(tokenListEntries[0]),
+ tokenListEntries[0].querySelector('.access-token').innerText);
+ expectEquals(this.getTokenStatus(tokenListEntries[0]),
+ tokenListEntries[0].querySelector('.token-status').innerText);
+ // Full-date format has 'at' between date and time in en-US, but
+ // ECMAScript's Date.parse cannot grok it.
+ expectEquals(this.getExpirationTime(tokenListEntries[0]),
+ Date.parse(tokenListEntries[0].querySelector('.expiration-time')
+ .innerText.replace(' at ', ' ')));
+ var scopes = tokenListEntries[0].querySelector('.scope-list')
+ .innerHTML.split('<br>');
+ var actualScopes = this.getScopes(tokenListEntries[0]);
+ expectEquals(scopes.length, actualScopes.length);
+ for (var i = 0; i < scopes.length; i++) {
+ expectEquals(scopes[i], actualScopes[i]);
+ }
+});
+
+/**
+ * Fixture for Identity Internals Web UI testing with multiple tokens in the
+ * Identity API token cache.
+ * @extends {BaseIdentityInternalsWebUITest}
+ * @constructor
+ */
+function IdentityInternalsMultipleTokensWebUITest() {}
+
+IdentityInternalsMultipleTokensWebUITest.prototype = {
+ __proto__: BaseIdentityInternalsWebUITest.prototype,
+
+ /** @override */
+ testGenPreamble: function() {
+ GEN(' SetupTokenCache(2);');
+ },
+};
+
+// Test for listing a token cache with multiple tokens. Names of the extensions
+// are empty, because extensions are faked, and not present in the extension
+// service.
+TEST_F('IdentityInternalsMultipleTokensWebUITest', 'getAllTokens', function() {
+ var tokenListEntries = this.getTokens();
+ expectEquals(2, tokenListEntries.length);
+ expectEquals('', this.getExtensionName(tokenListEntries[0]));
+ expectEquals('extension0',
+ this.getExtensionId(tokenListEntries[0]));
+ expectEquals('token0', this.getAccessToken(tokenListEntries[0]));
+ expectEquals('Token Present', this.getTokenStatus(tokenListEntries[0]));
+ expectLT(this.getExpirationTime(tokenListEntries[0]) - new Date(),
+ 3600 * 1000);
+ var scopes = this.getScopes(tokenListEntries[0]);
+ expectEquals(3, scopes.length);
+ expectEquals('scope_1_0', scopes[0]);
+ expectEquals('scope_2_0', scopes[1]);
+ expectEquals('', scopes[2]);
+ expectEquals('', this.getExtensionName(tokenListEntries[1]));
+ expectEquals('extension1',
+ this.getExtensionId(tokenListEntries[1]));
+ expectEquals('token1', this.getAccessToken(tokenListEntries[1]));
+ expectEquals('Token Present', this.getTokenStatus(tokenListEntries[1]));
+ expectLT(this.getExpirationTime(tokenListEntries[1]) - new Date(),
+ 3600 * 1000);
+ var scopes = this.getScopes(tokenListEntries[1]);
+ expectEquals(3, scopes.length);
+ expectEquals('scope_1_1', scopes[0]);
+ expectEquals('scope_2_1', scopes[1]);
+ expectEquals('', scopes[2]);
+});
+
+/**
+ * Fixture for asynchronous testing of Identity Internals Web UI with multiple
+ * tokens in Identity API token cache.
+ * @extends {IdentityInternalsMultipleTokensWebUITest}
+ * @constructor
+ */
+function IdentityInternalsWebUITestAsync() {}
+
+IdentityInternalsWebUITestAsync.prototype = {
+ __proto__: IdentityInternalsMultipleTokensWebUITest.prototype,
+
+ /** @override */
+ isAsync: true,
+};
+
+TEST_F('IdentityInternalsWebUITestAsync', 'revokeToken', function() {
+ var tokenListBefore = this.getTokens();
+ expectEquals(2, tokenListBefore.length);
+ var tokenRevokeDone = identity_internals.tokenRevokeDone;
+ identity_internals.tokenRevokeDone = this.continueTest(
+ WhenTestDone.ALWAYS, function(accessTokens) {
+ tokenRevokeDone.call(identity_internals, accessTokens);
+ identity_internals.tokenRevokeDone = tokenRevokeDone;
+ var tokenListAfter = this.getTokens();
+ expectEquals(1, tokenListAfter.length);
+ expectEquals(this.getAccessToken(tokenListBefore[0]),
+ this.getAccessToken(tokenListAfter[0]));
+ }.bind(this));
+ this.getRevokeButton(tokenListBefore[1]).click();
+});
+
diff --git a/chromium/chrome/browser/ui/webui/inspect_ui.cc b/chromium/chrome/browser/ui/webui/inspect_ui.cc
new file mode 100644
index 00000000000..03ba8fae04c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/inspect_ui.cc
@@ -0,0 +1,635 @@
+// 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/ui/webui/inspect_ui.h"
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/user_metrics.h"
+#include "chrome/browser/devtools/devtools_targets_ui.h"
+#include "chrome/browser/devtools/devtools_ui_bindings.h"
+#include "chrome/browser/devtools/devtools_window.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/browser/ui/singleton_tabs.h"
+#include "chrome/browser/ui/webui/theme_source.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "components/prefs/pref_service.h"
+#include "components/ui_devtools/devtools_server.h"
+#include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "content/public/common/frame_navigate_params.h"
+
+using content::DevToolsAgentHost;
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace {
+
+const char kInitUICommand[] = "init-ui";
+const char kInspectCommand[] = "inspect";
+const char kInspectAdditionalCommand[] = "inspect-additional";
+const char kActivateCommand[] = "activate";
+const char kCloseCommand[] = "close";
+const char kReloadCommand[] = "reload";
+const char kOpenCommand[] = "open";
+const char kInspectBrowser[] = "inspect-browser";
+const char kLocalHost[] = "localhost";
+
+const char kDiscoverUsbDevicesEnabledCommand[] =
+ "set-discover-usb-devices-enabled";
+const char kPortForwardingEnabledCommand[] =
+ "set-port-forwarding-enabled";
+const char kPortForwardingConfigCommand[] = "set-port-forwarding-config";
+const char kDiscoverTCPTargetsEnabledCommand[] =
+ "set-discover-tcp-targets-enabled";
+const char kTCPDiscoveryConfigCommand[] = "set-tcp-discovery-config";
+const char kOpenNodeFrontendCommand[] = "open-node-frontend";
+
+const char kPortForwardingDefaultPort[] = "8080";
+const char kPortForwardingDefaultLocation[] = "localhost:8080";
+
+const char kNameField[] = "name";
+const char kUrlField[] = "url";
+const char kIsAdditionalField[] = "isAdditional";
+
+void GetUiDevToolsTargets(base::ListValue& targets) {
+ for (const auto& client_pair :
+ ui::devtools::UiDevToolsServer::GetClientNamesAndUrls()) {
+ auto target_data = base::MakeUnique<base::DictionaryValue>();
+ target_data->SetString(kNameField, client_pair.first);
+ target_data->SetString(kUrlField, client_pair.second);
+ target_data->SetBoolean(kIsAdditionalField, true);
+ targets.Append(std::move(target_data));
+ }
+}
+
+// InspectMessageHandler --------------------------------------------
+
+class InspectMessageHandler : public WebUIMessageHandler {
+ public:
+ explicit InspectMessageHandler(InspectUI* inspect_ui)
+ : inspect_ui_(inspect_ui) {}
+ ~InspectMessageHandler() override {}
+
+ private:
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ void HandleInitUICommand(const base::ListValue* args);
+ void HandleInspectCommand(const base::ListValue* args);
+ void HandleInspectAdditionalCommand(const base::ListValue* args);
+ void HandleActivateCommand(const base::ListValue* args);
+ void HandleCloseCommand(const base::ListValue* args);
+ void HandleReloadCommand(const base::ListValue* args);
+ void HandleOpenCommand(const base::ListValue* args);
+ void HandleInspectBrowserCommand(const base::ListValue* args);
+ void HandleBooleanPrefChanged(const char* pref_name,
+ const base::ListValue* args);
+ void HandlePortForwardingConfigCommand(const base::ListValue* args);
+ void HandleTCPDiscoveryConfigCommand(const base::ListValue* args);
+ void HandleOpenNodeFrontendCommand(const base::ListValue* args);
+
+ InspectUI* inspect_ui_;
+
+ DISALLOW_COPY_AND_ASSIGN(InspectMessageHandler);
+};
+
+void InspectMessageHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(kInitUICommand,
+ base::Bind(&InspectMessageHandler::HandleInitUICommand,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kInspectCommand,
+ base::Bind(&InspectMessageHandler::HandleInspectCommand,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kInspectAdditionalCommand,
+ base::Bind(&InspectMessageHandler::HandleInspectAdditionalCommand,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kActivateCommand,
+ base::Bind(&InspectMessageHandler::HandleActivateCommand,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kCloseCommand,
+ base::Bind(&InspectMessageHandler::HandleCloseCommand,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kDiscoverUsbDevicesEnabledCommand,
+ base::Bind(&InspectMessageHandler::HandleBooleanPrefChanged,
+ base::Unretained(this),
+ &prefs::kDevToolsDiscoverUsbDevicesEnabled[0]));
+ web_ui()->RegisterMessageCallback(kPortForwardingEnabledCommand,
+ base::Bind(&InspectMessageHandler::HandleBooleanPrefChanged,
+ base::Unretained(this),
+ &prefs::kDevToolsPortForwardingEnabled[0]));
+ web_ui()->RegisterMessageCallback(kPortForwardingConfigCommand,
+ base::Bind(&InspectMessageHandler::HandlePortForwardingConfigCommand,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kDiscoverTCPTargetsEnabledCommand,
+ base::Bind(&InspectMessageHandler::HandleBooleanPrefChanged,
+ base::Unretained(this),
+ &prefs::kDevToolsDiscoverTCPTargetsEnabled[0]));
+ web_ui()->RegisterMessageCallback(kTCPDiscoveryConfigCommand,
+ base::Bind(&InspectMessageHandler::HandleTCPDiscoveryConfigCommand,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kOpenNodeFrontendCommand,
+ base::Bind(&InspectMessageHandler::HandleOpenNodeFrontendCommand,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kReloadCommand,
+ base::Bind(&InspectMessageHandler::HandleReloadCommand,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kOpenCommand,
+ base::Bind(&InspectMessageHandler::HandleOpenCommand,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kInspectBrowser,
+ base::Bind(&InspectMessageHandler::HandleInspectBrowserCommand,
+ base::Unretained(this)));
+}
+
+void InspectMessageHandler::HandleInitUICommand(const base::ListValue*) {
+ inspect_ui_->InitUI();
+}
+
+static bool ParseStringArgs(const base::ListValue* args,
+ std::string* arg0,
+ std::string* arg1,
+ std::string* arg2 = 0) {
+ int arg_size = args->GetSize();
+ return (!arg0 || (arg_size > 0 && args->GetString(0, arg0))) &&
+ (!arg1 || (arg_size > 1 && args->GetString(1, arg1))) &&
+ (!arg2 || (arg_size > 2 && args->GetString(2, arg2)));
+}
+
+void InspectMessageHandler::HandleInspectCommand(const base::ListValue* args) {
+ std::string source;
+ std::string id;
+ if (ParseStringArgs(args, &source, &id))
+ inspect_ui_->Inspect(source, id);
+}
+
+void InspectMessageHandler::HandleInspectAdditionalCommand(
+ const base::ListValue* args) {
+ std::string url;
+ if (ParseStringArgs(args, &url, nullptr)) {
+ WebContents* inspect_ui = web_ui()->GetWebContents();
+ web_ui()->GetWebContents()->GetDelegate()->OpenURLFromTab(
+ inspect_ui,
+ content::OpenURLParams(GURL(url), content::Referrer(),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false));
+ }
+}
+
+void InspectMessageHandler::HandleActivateCommand(const base::ListValue* args) {
+ std::string source;
+ std::string id;
+ if (ParseStringArgs(args, &source, &id))
+ inspect_ui_->Activate(source, id);
+}
+
+void InspectMessageHandler::HandleCloseCommand(const base::ListValue* args) {
+ std::string source;
+ std::string id;
+ if (ParseStringArgs(args, &source, &id))
+ inspect_ui_->Close(source, id);
+}
+
+void InspectMessageHandler::HandleReloadCommand(const base::ListValue* args) {
+ std::string source;
+ std::string id;
+ if (ParseStringArgs(args, &source, &id))
+ inspect_ui_->Reload(source, id);
+}
+
+void InspectMessageHandler::HandleOpenCommand(const base::ListValue* args) {
+ std::string source_id;
+ std::string browser_id;
+ std::string url;
+ if (ParseStringArgs(args, &source_id, &browser_id, &url))
+ inspect_ui_->Open(source_id, browser_id, url);
+}
+
+void InspectMessageHandler::HandleInspectBrowserCommand(
+ const base::ListValue* args) {
+ std::string source_id;
+ std::string browser_id;
+ std::string front_end;
+ if (ParseStringArgs(args, &source_id, &browser_id, &front_end)) {
+ inspect_ui_->InspectBrowserWithCustomFrontend(
+ source_id, browser_id, GURL(front_end));
+ }
+}
+
+void InspectMessageHandler::HandleBooleanPrefChanged(
+ const char* pref_name,
+ const base::ListValue* args) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (!profile)
+ return;
+
+ bool enabled;
+ if (args->GetSize() == 1 && args->GetBoolean(0, &enabled))
+ profile->GetPrefs()->SetBoolean(pref_name, enabled);
+}
+
+void InspectMessageHandler::HandlePortForwardingConfigCommand(
+ const base::ListValue* args) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (!profile)
+ return;
+
+ const base::DictionaryValue* dict_src;
+ if (args->GetSize() == 1 && args->GetDictionary(0, &dict_src))
+ profile->GetPrefs()->Set(prefs::kDevToolsPortForwardingConfig, *dict_src);
+}
+
+void InspectMessageHandler::HandleTCPDiscoveryConfigCommand(
+ const base::ListValue* args) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (!profile)
+ return;
+
+ const base::ListValue* list_src;
+ if (args->GetSize() == 1 && args->GetList(0, &list_src))
+ profile->GetPrefs()->Set(prefs::kDevToolsTCPDiscoveryConfig, *list_src);
+}
+
+void InspectMessageHandler::HandleOpenNodeFrontendCommand(
+ const base::ListValue* args) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (!profile)
+ return;
+ DevToolsWindow::OpenNodeFrontendWindow(profile);
+}
+
+// DevToolsUIBindingsEnabler ----------------------------------------
+
+class DevToolsUIBindingsEnabler
+ : public content::WebContentsObserver {
+ public:
+ DevToolsUIBindingsEnabler(WebContents* web_contents,
+ const GURL& url);
+ ~DevToolsUIBindingsEnabler() override {}
+
+ DevToolsUIBindings* GetBindings();
+
+ private:
+ // contents::WebContentsObserver overrides.
+ void WebContentsDestroyed() override;
+ void DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) override;
+
+ DevToolsUIBindings bindings_;
+ GURL url_;
+ DISALLOW_COPY_AND_ASSIGN(DevToolsUIBindingsEnabler);
+};
+
+DevToolsUIBindingsEnabler::DevToolsUIBindingsEnabler(
+ WebContents* web_contents,
+ const GURL& url)
+ : WebContentsObserver(web_contents),
+ bindings_(web_contents),
+ url_(url) {
+}
+
+DevToolsUIBindings* DevToolsUIBindingsEnabler::GetBindings() {
+ return &bindings_;
+}
+
+void DevToolsUIBindingsEnabler::WebContentsDestroyed() {
+ delete this;
+}
+
+void DevToolsUIBindingsEnabler::DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) {
+ if (!navigation_handle->IsInMainFrame() || !navigation_handle->HasCommitted())
+ return;
+
+ if (url_ != navigation_handle->GetURL())
+ delete this;
+}
+
+} // namespace
+
+// InspectUI --------------------------------------------------------
+
+InspectUI::InspectUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<InspectMessageHandler>(this));
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreateInspectUIHTMLSource());
+
+ // Set up the chrome://theme/ source.
+ ThemeSource* theme = new ThemeSource(profile);
+ content::URLDataSource::Add(profile, theme);
+}
+
+InspectUI::~InspectUI() {
+ StopListeningNotifications();
+}
+
+void InspectUI::InitUI() {
+ SetPortForwardingDefaults();
+ StartListeningNotifications();
+ UpdateDiscoverUsbDevicesEnabled();
+ UpdatePortForwardingEnabled();
+ UpdatePortForwardingConfig();
+ UpdateTCPDiscoveryEnabled();
+ UpdateTCPDiscoveryConfig();
+}
+
+void InspectUI::Inspect(const std::string& source_id,
+ const std::string& target_id) {
+ scoped_refptr<DevToolsAgentHost> target = FindTarget(source_id, target_id);
+ if (target) {
+ const std::string target_type = target->GetType();
+ Profile* profile = Profile::FromBrowserContext(
+ web_ui()->GetWebContents()->GetBrowserContext());
+ DevToolsWindow::OpenDevToolsWindow(target, profile);
+ ForceUpdateIfNeeded(source_id, target_type);
+ }
+}
+
+void InspectUI::Activate(const std::string& source_id,
+ const std::string& target_id) {
+ scoped_refptr<DevToolsAgentHost> target = FindTarget(source_id, target_id);
+ if (target) {
+ const std::string target_type = target->GetType();
+ target->Activate();
+ ForceUpdateIfNeeded(source_id, target_type);
+ }
+}
+
+void InspectUI::Close(const std::string& source_id,
+ const std::string& target_id) {
+ scoped_refptr<DevToolsAgentHost> target = FindTarget(source_id, target_id);
+ if (target) {
+ const std::string target_type = target->GetType();
+ target->Close();
+ ForceUpdateIfNeeded(source_id, target_type);
+ }
+}
+
+void InspectUI::Reload(const std::string& source_id,
+ const std::string& target_id) {
+ scoped_refptr<DevToolsAgentHost> target = FindTarget(source_id, target_id);
+ if (target) {
+ const std::string target_type = target->GetType();
+ target->Reload();
+ ForceUpdateIfNeeded(source_id, target_type);
+ }
+}
+
+void InspectUI::Open(const std::string& source_id,
+ const std::string& browser_id,
+ const std::string& url) {
+ DevToolsTargetsUIHandler* handler = FindTargetHandler(source_id);
+ if (handler)
+ handler->Open(browser_id, url);
+}
+
+void InspectUI::InspectBrowserWithCustomFrontend(
+ const std::string& source_id,
+ const std::string& browser_id,
+ const GURL& frontend_url) {
+ if (!frontend_url.SchemeIs(content::kChromeUIScheme) &&
+ !frontend_url.SchemeIs(content::kChromeDevToolsScheme) &&
+ frontend_url.host() != kLocalHost) {
+ return;
+ }
+
+ DevToolsTargetsUIHandler* handler = FindTargetHandler(source_id);
+ if (!handler)
+ return;
+
+ // Fetch agent host from remote browser.
+ scoped_refptr<content::DevToolsAgentHost> agent_host =
+ handler->GetBrowserAgentHost(browser_id);
+ if (agent_host->IsAttached())
+ return;
+
+ // Create web contents for the front-end.
+ WebContents* inspect_ui = web_ui()->GetWebContents();
+ WebContents* front_end = inspect_ui->GetDelegate()->OpenURLFromTab(
+ inspect_ui,
+ content::OpenURLParams(frontend_url, content::Referrer(),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false));
+
+ // Install devtools bindings.
+ DevToolsUIBindingsEnabler* bindings_enabler =
+ new DevToolsUIBindingsEnabler(front_end, frontend_url);
+ bindings_enabler->GetBindings()->AttachTo(agent_host);
+}
+
+void InspectUI::InspectDevices(Browser* browser) {
+ base::RecordAction(base::UserMetricsAction("InspectDevices"));
+ chrome::NavigateParams params(chrome::GetSingletonTabNavigateParams(
+ browser, GURL(chrome::kChromeUIInspectURL)));
+ params.path_behavior = chrome::NavigateParams::IGNORE_AND_NAVIGATE;
+ ShowSingletonTabOverwritingNTP(browser, params);
+}
+
+void InspectUI::Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ if (source == content::Source<WebContents>(web_ui()->GetWebContents()))
+ StopListeningNotifications();
+}
+
+void InspectUI::StartListeningNotifications() {
+ if (!target_handlers_.empty()) // Possible when reloading the page.
+ StopListeningNotifications();
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+ DevToolsTargetsUIHandler::Callback callback =
+ base::Bind(&InspectUI::PopulateTargets, base::Unretained(this));
+
+ base::ListValue additional_targets;
+ GetUiDevToolsTargets(additional_targets);
+ PopulateAdditionalTargets(additional_targets);
+
+ AddTargetUIHandler(
+ DevToolsTargetsUIHandler::CreateForLocal(callback));
+ if (profile->IsOffTheRecord()) {
+ ShowIncognitoWarning();
+ } else {
+ AddTargetUIHandler(
+ DevToolsTargetsUIHandler::CreateForAdb(callback, profile));
+ }
+
+ port_status_serializer_.reset(
+ new PortForwardingStatusSerializer(
+ base::Bind(&InspectUI::PopulatePortStatus, base::Unretained(this)),
+ profile));
+
+ notification_registrar_.Add(this,
+ content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
+ content::NotificationService::AllSources());
+
+ pref_change_registrar_.Init(profile->GetPrefs());
+ pref_change_registrar_.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled,
+ base::Bind(&InspectUI::UpdateDiscoverUsbDevicesEnabled,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled,
+ base::Bind(&InspectUI::UpdatePortForwardingEnabled,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig,
+ base::Bind(&InspectUI::UpdatePortForwardingConfig,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(prefs::kDevToolsDiscoverTCPTargetsEnabled,
+ base::Bind(&InspectUI::UpdateTCPDiscoveryEnabled,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(prefs::kDevToolsTCPDiscoveryConfig,
+ base::Bind(&InspectUI::UpdateTCPDiscoveryConfig,
+ base::Unretained(this)));
+}
+
+void InspectUI::StopListeningNotifications() {
+ if (target_handlers_.empty())
+ return;
+
+ target_handlers_.clear();
+
+ port_status_serializer_.reset();
+
+ notification_registrar_.RemoveAll();
+ pref_change_registrar_.RemoveAll();
+}
+
+content::WebUIDataSource* InspectUI::CreateInspectUIHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIInspectHost);
+ source->AddResourcePath("inspect.css", IDR_INSPECT_CSS);
+ source->AddResourcePath("inspect.js", IDR_INSPECT_JS);
+ source->SetDefaultResource(IDR_INSPECT_HTML);
+ return source;
+}
+
+void InspectUI::UpdateDiscoverUsbDevicesEnabled() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "updateDiscoverUsbDevicesEnabled",
+ *GetPrefValue(prefs::kDevToolsDiscoverUsbDevicesEnabled));
+}
+
+void InspectUI::UpdatePortForwardingEnabled() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "updatePortForwardingEnabled",
+ *GetPrefValue(prefs::kDevToolsPortForwardingEnabled));
+}
+
+void InspectUI::UpdatePortForwardingConfig() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "updatePortForwardingConfig",
+ *GetPrefValue(prefs::kDevToolsPortForwardingConfig));
+}
+
+void InspectUI::UpdateTCPDiscoveryEnabled() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "updateTCPDiscoveryEnabled",
+ *GetPrefValue(prefs::kDevToolsDiscoverTCPTargetsEnabled));
+}
+
+void InspectUI::UpdateTCPDiscoveryConfig() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "updateTCPDiscoveryConfig",
+ *GetPrefValue(prefs::kDevToolsTCPDiscoveryConfig));
+}
+
+void InspectUI::SetPortForwardingDefaults() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ PrefService* prefs = profile->GetPrefs();
+
+ bool default_set;
+ if (!GetPrefValue(prefs::kDevToolsPortForwardingDefaultSet)->
+ GetAsBoolean(&default_set) || default_set) {
+ return;
+ }
+
+ // This is the first chrome://inspect invocation on a fresh profile or after
+ // upgrade from a version that did not have kDevToolsPortForwardingDefaultSet.
+ prefs->SetBoolean(prefs::kDevToolsPortForwardingDefaultSet, true);
+
+ bool enabled;
+ const base::DictionaryValue* config;
+ if (!GetPrefValue(prefs::kDevToolsPortForwardingEnabled)->
+ GetAsBoolean(&enabled) ||
+ !GetPrefValue(prefs::kDevToolsPortForwardingConfig)->
+ GetAsDictionary(&config)) {
+ return;
+ }
+
+ // Do nothing if user already took explicit action.
+ if (enabled || config->size() != 0)
+ return;
+
+ base::DictionaryValue default_config;
+ default_config.SetString(
+ kPortForwardingDefaultPort, kPortForwardingDefaultLocation);
+ prefs->Set(prefs::kDevToolsPortForwardingConfig, default_config);
+}
+
+const base::Value* InspectUI::GetPrefValue(const char* name) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ return profile->GetPrefs()->FindPreference(name)->GetValue();
+}
+
+void InspectUI::AddTargetUIHandler(
+ std::unique_ptr<DevToolsTargetsUIHandler> handler) {
+ std::string id = handler->source_id();
+ target_handlers_[id] = std::move(handler);
+}
+
+DevToolsTargetsUIHandler* InspectUI::FindTargetHandler(
+ const std::string& source_id) {
+ auto it = target_handlers_.find(source_id);
+ return it != target_handlers_.end() ? it->second.get() : nullptr;
+}
+
+scoped_refptr<content::DevToolsAgentHost> InspectUI::FindTarget(
+ const std::string& source_id, const std::string& target_id) {
+ auto it = target_handlers_.find(source_id);
+ return it != target_handlers_.end() ?
+ it->second->GetTarget(target_id) : nullptr;
+}
+
+void InspectUI::PopulateTargets(const std::string& source,
+ const base::ListValue& targets) {
+ web_ui()->CallJavascriptFunctionUnsafe("populateTargets", base::Value(source),
+ targets);
+}
+
+void InspectUI::PopulateAdditionalTargets(const base::ListValue& targets) {
+ web_ui()->CallJavascriptFunctionUnsafe("populateAdditionalTargets", targets);
+}
+
+void InspectUI::ForceUpdateIfNeeded(const std::string& source_id,
+ const std::string& target_type) {
+ // TODO(dgozman): remove this after moving discovery to protocol.
+ // See crbug.com/398049.
+ if (target_type != content::DevToolsAgentHost::kTypeServiceWorker)
+ return;
+ DevToolsTargetsUIHandler* handler = FindTargetHandler(source_id);
+ if (handler)
+ handler->ForceUpdate();
+}
+
+void InspectUI::PopulatePortStatus(const base::Value& status) {
+ web_ui()->CallJavascriptFunctionUnsafe("populatePortStatus", status);
+}
+
+void InspectUI::ShowIncognitoWarning() {
+ web_ui()->CallJavascriptFunctionUnsafe("showIncognitoWarning");
+}
diff --git a/chromium/chrome/browser/ui/webui/inspect_ui.h b/chromium/chrome/browser/ui/webui/inspect_ui.h
new file mode 100644
index 00000000000..86102222eaa
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/inspect_ui.h
@@ -0,0 +1,109 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_INSPECT_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_INSPECT_UI_H_
+
+#include <map>
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace base {
+class Value;
+class ListValue;
+}
+
+namespace content {
+class DevToolsAgentHost;
+}
+
+class Browser;
+class DevToolsTargetsUIHandler;
+class PortForwardingStatusSerializer;
+
+class InspectUI : public content::WebUIController,
+ public content::NotificationObserver {
+ public:
+ explicit InspectUI(content::WebUI* web_ui);
+ ~InspectUI() override;
+
+ void InitUI();
+ void Inspect(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);
+ void Open(const std::string& source_id,
+ const std::string& browser_id,
+ const std::string& url);
+ void InspectBrowserWithCustomFrontend(
+ const std::string& source_id,
+ const std::string& browser_id,
+ const GURL& frontend_url);
+
+ static void InspectDevices(Browser* browser);
+
+ private:
+ // content::NotificationObserver overrides.
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ void StartListeningNotifications();
+ void StopListeningNotifications();
+
+ content::WebUIDataSource* CreateInspectUIHTMLSource();
+
+ void UpdateDiscoverUsbDevicesEnabled();
+ void UpdatePortForwardingEnabled();
+ void UpdatePortForwardingConfig();
+ void UpdateTCPDiscoveryEnabled();
+ void UpdateTCPDiscoveryConfig();
+
+ void SetPortForwardingDefaults();
+
+ const base::Value* GetPrefValue(const char* name);
+
+ void AddTargetUIHandler(std::unique_ptr<DevToolsTargetsUIHandler> handler);
+
+ DevToolsTargetsUIHandler* FindTargetHandler(
+ const std::string& source_id);
+ scoped_refptr<content::DevToolsAgentHost> FindTarget(
+ const std::string& source_id,
+ const std::string& target_id);
+
+ void PopulateTargets(const std::string& source_id,
+ const base::ListValue& targets);
+
+ void PopulateAdditionalTargets(const base::ListValue& targets);
+
+ void ForceUpdateIfNeeded(const std::string& source_id,
+ const std::string& target_type);
+
+ void PopulatePortStatus(const base::Value& status);
+
+ void ShowIncognitoWarning();
+
+ // A scoped container for notification registries.
+ content::NotificationRegistrar notification_registrar_;
+
+ // A scoped container for preference change registries.
+ PrefChangeRegistrar pref_change_registrar_;
+
+ std::map<std::string, std::unique_ptr<DevToolsTargetsUIHandler>>
+ target_handlers_;
+
+ std::unique_ptr<PortForwardingStatusSerializer> port_status_serializer_;
+
+ DISALLOW_COPY_AND_ASSIGN(InspectUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_INSPECT_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/inspect_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/inspect_ui_browsertest.cc
new file mode 100644
index 00000000000..8be94d29b34
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/inspect_ui_browsertest.cc
@@ -0,0 +1,90 @@
+// 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 "base/macros.h"
+#include "chrome/browser/devtools/device/adb/adb_device_provider.h"
+#include "chrome/browser/devtools/device/adb/mock_adb_server.h"
+#include "chrome/browser/devtools/device/devtools_android_bridge.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "chrome/test/base/web_ui_browser_test.h"
+#include "content/public/browser/navigation_details.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+
+using content::WebContents;
+
+namespace {
+
+const char kSharedWorkerTestPage[] = "/workers/workers_ui_shared_worker.html";
+const char kSharedWorkerJs[] = "/workers/workers_ui_shared_worker.js";
+
+class InspectUITest : public WebUIBrowserTest {
+ public:
+ InspectUITest() {}
+
+ void SetUpOnMainThread() override {
+ WebUIBrowserTest::SetUpOnMainThread();
+ AddLibrary(base::FilePath(FILE_PATH_LITERAL("inspect_ui_test.js")));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InspectUITest);
+};
+
+IN_PROC_BROWSER_TEST_F(InspectUITest, InspectUIPage) {
+ ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIInspectURL));
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
+ "testTargetListed", base::Value("#pages"),
+ base::Value("populateWebContentsTargets"),
+ base::Value(chrome::kChromeUIInspectURL)));
+}
+
+IN_PROC_BROWSER_TEST_F(InspectUITest, SharedWorker) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL url = embedded_test_server()->GetURL(kSharedWorkerTestPage);
+ ui_test_utils::NavigateToURL(browser(), url);
+
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL(chrome::kChromeUIInspectURL),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
+ "testTargetListed", base::Value("#workers"),
+ base::Value("populateWorkerTargets"), base::Value(kSharedWorkerJs)));
+
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
+ "testTargetListed", base::Value("#pages"),
+ base::Value("populateWebContentsTargets"),
+ base::Value(kSharedWorkerTestPage)));
+}
+
+// Flaky due to failure to bind a hardcoded port. crbug.com/566057
+IN_PROC_BROWSER_TEST_F(InspectUITest, DISABLED_AndroidTargets) {
+ DevToolsAndroidBridge* android_bridge =
+ DevToolsAndroidBridge::Factory::GetForProfile(browser()->profile());
+ AndroidDeviceManager::DeviceProviders providers;
+ providers.push_back(new AdbDeviceProvider());
+ android_bridge->set_device_providers_for_test(providers);
+
+ StartMockAdbServer(FlushWithSize);
+
+ ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIInspectURL));
+
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest("testAdbTargetsListed"));
+
+ StopMockAdbServer();
+}
+
+IN_PROC_BROWSER_TEST_F(InspectUITest, ReloadCrash) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIInspectURL));
+ ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIInspectURL));
+}
+
+} // namespace
diff --git a/chromium/chrome/browser/ui/webui/instant_ui.cc b/chromium/chrome/browser/ui/webui/instant_ui.cc
new file mode 100644
index 00000000000..298ebe8d9a2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/instant_ui.cc
@@ -0,0 +1,190 @@
+// 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/ui/webui/instant_ui.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_instant_controller.h"
+#include "chrome/browser/ui/search/instant_controller.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+#if !defined(OS_ANDROID)
+#include "chrome/browser/ui/browser.h"
+#endif
+
+namespace {
+
+content::WebUIDataSource* CreateInstantHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIInstantHost);
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("instant.js", IDR_INSTANT_JS);
+ source->AddResourcePath("instant.css", IDR_INSTANT_CSS);
+ source->SetDefaultResource(IDR_INSTANT_HTML);
+ return source;
+}
+
+#if !defined(OS_ANDROID)
+std::string FormatTime(int64_t time) {
+ base::Time::Exploded exploded;
+ base::Time::FromInternalValue(time).UTCExplode(&exploded);
+ return base::StringPrintf("%04d-%02d-%02d %02d:%02d:%02d.%03d",
+ exploded.year, exploded.month, exploded.day_of_month,
+ exploded.hour, exploded.minute, exploded.second, exploded.millisecond);
+}
+#endif // !defined(OS_ANDROID)
+
+// This class receives JavaScript messages from the renderer.
+// Note that the WebUI infrastructure runs on the UI thread, therefore all of
+// this class's methods are expected to run on the UI thread.
+class InstantUIMessageHandler
+ : public content::WebUIMessageHandler,
+ public base::SupportsWeakPtr<InstantUIMessageHandler> {
+ public:
+ InstantUIMessageHandler();
+ ~InstantUIMessageHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ void GetPreferenceValue(const base::ListValue* args);
+ void SetPreferenceValue(const base::ListValue* args);
+ void GetDebugInfo(const base::ListValue* value);
+ void ClearDebugInfo(const base::ListValue* value);
+
+ DISALLOW_COPY_AND_ASSIGN(InstantUIMessageHandler);
+};
+
+InstantUIMessageHandler::InstantUIMessageHandler() {}
+
+InstantUIMessageHandler::~InstantUIMessageHandler() {}
+
+void InstantUIMessageHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "getPreferenceValue",
+ base::Bind(&InstantUIMessageHandler::GetPreferenceValue,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setPreferenceValue",
+ base::Bind(&InstantUIMessageHandler::SetPreferenceValue,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getDebugInfo",
+ base::Bind(&InstantUIMessageHandler::GetDebugInfo,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "clearDebugInfo",
+ base::Bind(&InstantUIMessageHandler::ClearDebugInfo,
+ base::Unretained(this)));
+}
+
+void InstantUIMessageHandler::GetPreferenceValue(const base::ListValue* args) {
+ std::string pref_name;
+ if (!args->GetString(0, &pref_name)) return;
+
+ base::Value pref_name_value(pref_name);
+ if (pref_name == prefs::kInstantUIZeroSuggestUrlPrefix) {
+ PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+ base::Value arg(prefs->GetString(pref_name.c_str()));
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "instantConfig.getPreferenceValueResult", pref_name_value, arg);
+ }
+}
+
+void InstantUIMessageHandler::SetPreferenceValue(const base::ListValue* args) {
+ std::string pref_name;
+ if (!args->GetString(0, &pref_name)) return;
+
+ if (pref_name == prefs::kInstantUIZeroSuggestUrlPrefix) {
+ std::string value;
+ if (!args->GetString(1, &value))
+ return;
+ PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+ prefs->SetString(pref_name, value);
+ }
+}
+
+void InstantUIMessageHandler::GetDebugInfo(const base::ListValue* args) {
+#if !defined(OS_ANDROID)
+ typedef std::pair<int64_t, std::string> DebugEvent;
+
+ if (!web_ui()->GetWebContents())
+ return;
+ Browser* browser = chrome::FindBrowserWithWebContents(
+ web_ui()->GetWebContents());
+ if (!browser || !browser->instant_controller())
+ return;
+
+ InstantController* instant = browser->instant_controller()->instant();
+ const std::list<DebugEvent>& events = instant->debug_events();
+
+ base::DictionaryValue data;
+ auto entries = base::MakeUnique<base::ListValue>();
+ for (std::list<DebugEvent>::const_iterator it = events.begin();
+ it != events.end(); ++it) {
+ std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
+ entry->SetString("time", FormatTime(it->first));
+ entry->SetString("text", it->second);
+ entries->Append(std::move(entry));
+ }
+ data.Set("entries", std::move(entries));
+
+ web_ui()->CallJavascriptFunctionUnsafe("instantConfig.getDebugInfoResult",
+ data);
+#endif
+}
+
+void InstantUIMessageHandler::ClearDebugInfo(const base::ListValue* args) {
+#if !defined(OS_ANDROID)
+ if (!web_ui()->GetWebContents())
+ return;
+ Browser* browser = chrome::FindBrowserWithWebContents(
+ web_ui()->GetWebContents());
+ if (!browser || !browser->instant_controller())
+ return;
+
+ browser->instant_controller()->instant()->ClearDebugEvents();
+#endif
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// InstantUI
+
+InstantUI::InstantUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<InstantUIMessageHandler>());
+
+ // Set up the chrome://instant/ source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreateInstantHTMLSource());
+}
+
+// static
+void InstantUI::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterStringPref(prefs::kInstantUIZeroSuggestUrlPrefix,
+ std::string());
+}
diff --git a/chromium/chrome/browser/ui/webui/instant_ui.h b/chromium/chrome/browser/ui/webui/instant_ui.h
new file mode 100644
index 00000000000..25ba89fbf92
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/instant_ui.h
@@ -0,0 +1,28 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_INSTANT_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_INSTANT_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+// Provides configuration options for Instant web search and shows debug info.
+class InstantUI : public content::WebUIController {
+ public:
+ // Constructs an instance using |web_ui| for its data sources and message
+ // handlers.
+ explicit InstantUI(content::WebUI* web_ui);
+
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InstantUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_INSTANT_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/interstitials/OWNERS b/chromium/chrome/browser/ui/webui/interstitials/OWNERS
new file mode 100644
index 00000000000..c0ec25738fe
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/interstitials/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/interstitials/OWNERS
diff --git a/chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.cc b/chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
new file mode 100644
index 00000000000..6841d01ce59
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
@@ -0,0 +1,491 @@
+// 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/ui/webui/interstitials/interstitial_ui.h"
+
+#include "base/atomic_sequence_num.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/time/time.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/browser/safe_browsing/test_safe_browsing_blocking_page_quiet.h"
+#include "chrome/browser/safe_browsing/ui_manager.h"
+#include "chrome/browser/ssl/bad_clock_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/url_constants.h"
+#include "components/grit/components_resources.h"
+#include "components/security_interstitials/core/ssl_error_ui.h"
+#include "components/supervised_user_error_page/supervised_user_error_page.h"
+#include "content/public/browser/interstitial_page_delegate.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "crypto/rsa_private_key.h"
+#include "net/base/net_errors.h"
+#include "net/base/url_util.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util.h"
+#include "net/ssl/ssl_info.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/web_ui_util.h"
+
+#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
+#include "chrome/browser/ssl/captive_portal_blocking_page.h"
+#endif
+
+using security_interstitials::TestSafeBrowsingBlockingPageQuiet;
+
+namespace {
+
+// NSS requires that serial numbers be unique even for the same issuer;
+// as all fake certificates will contain the same issuer name, it's
+// necessary to ensure the serial number is unique, as otherwise
+// NSS will fail to parse.
+base::StaticAtomicSequenceNumber g_serial_number;
+
+scoped_refptr<net::X509Certificate> CreateFakeCert() {
+ std::unique_ptr<crypto::RSAPrivateKey> unused_key;
+ std::string cert_der;
+ if (!net::x509_util::CreateKeyAndSelfSignedCert(
+ "CN=Error", static_cast<uint32_t>(g_serial_number.GetNext()),
+ base::Time::Now() - base::TimeDelta::FromMinutes(5),
+ base::Time::Now() + base::TimeDelta::FromMinutes(5), &unused_key,
+ &cert_der)) {
+ return nullptr;
+ }
+
+ return net::X509Certificate::CreateFromBytes(cert_der.data(),
+ cert_der.size());
+}
+
+// Implementation of chrome://interstitials demonstration pages. This code is
+// not used in displaying any real interstitials.
+class InterstitialHTMLSource : public content::URLDataSource {
+ public:
+ InterstitialHTMLSource() = default;
+ ~InterstitialHTMLSource() override = default;
+
+ // content::URLDataSource:
+ std::string GetMimeType(const std::string& mime_type) const override;
+ std::string GetSource() const override;
+ std::string GetContentSecurityPolicyScriptSrc() const override;
+ std::string GetContentSecurityPolicyStyleSrc() const override;
+ std::string GetContentSecurityPolicyImgSrc() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+
+ private:
+ std::string GetSupervisedUserInterstitialHTML(const std::string& path);
+
+ DISALLOW_COPY_AND_ASSIGN(InterstitialHTMLSource);
+};
+
+#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
+// Provides fake connection information to the captive portal blocking page so
+// that both Wi-Fi and non Wi-Fi blocking pages can be displayed.
+class CaptivePortalBlockingPageWithNetInfo : public CaptivePortalBlockingPage {
+ public:
+ CaptivePortalBlockingPageWithNetInfo(
+ content::WebContents* web_contents,
+ const GURL& request_url,
+ const GURL& login_url,
+ const net::SSLInfo& ssl_info,
+ const base::Callback<void(content::CertificateRequestResultType)>&
+ callback,
+ bool is_wifi,
+ const std::string& wifi_ssid)
+ : CaptivePortalBlockingPage(web_contents,
+ request_url,
+ login_url,
+ nullptr,
+ ssl_info,
+ callback),
+ is_wifi_(is_wifi),
+ wifi_ssid_(wifi_ssid) {}
+
+ private:
+ // CaptivePortalBlockingPage methods:
+ bool IsWifiConnection() const override { return is_wifi_; }
+ std::string GetWiFiSSID() const override { return wifi_ssid_; }
+
+ const bool is_wifi_;
+ const std::string wifi_ssid_;
+
+ DISALLOW_COPY_AND_ASSIGN(CaptivePortalBlockingPageWithNetInfo);
+};
+#endif
+
+SSLBlockingPage* CreateSSLBlockingPage(content::WebContents* web_contents) {
+ // Random parameters for SSL blocking page.
+ int cert_error = net::ERR_CERT_CONTAINS_ERRORS;
+ GURL request_url("https://example.com");
+ bool overridable = false;
+ bool strict_enforcement = false;
+ base::Time time_triggered_ = base::Time::NowFromSystemTime();
+ std::string url_param;
+ if (net::GetValueForKeyInQuery(web_contents->GetURL(),
+ "url",
+ &url_param)) {
+ if (GURL(url_param).is_valid())
+ request_url = GURL(url_param);
+ }
+ std::string overridable_param;
+ if (net::GetValueForKeyInQuery(web_contents->GetURL(),
+ "overridable",
+ &overridable_param)) {
+ overridable = overridable_param == "1";
+ }
+ std::string strict_enforcement_param;
+ if (net::GetValueForKeyInQuery(web_contents->GetURL(),
+ "strict_enforcement",
+ &strict_enforcement_param)) {
+ strict_enforcement = strict_enforcement_param == "1";
+ }
+ net::SSLInfo ssl_info;
+ ssl_info.cert = ssl_info.unverified_cert = CreateFakeCert();
+ // This delegate doesn't create an interstitial.
+ int options_mask = 0;
+ if (overridable)
+ options_mask |= security_interstitials::SSLErrorUI::SOFT_OVERRIDE_ENABLED;
+ if (strict_enforcement)
+ options_mask |= security_interstitials::SSLErrorUI::STRICT_ENFORCEMENT;
+ return SSLBlockingPage::Create(
+ web_contents, cert_error, ssl_info, request_url, options_mask,
+ time_triggered_, nullptr,
+ base::Callback<void(content::CertificateRequestResultType)>());
+}
+
+BadClockBlockingPage* CreateBadClockBlockingPage(
+ content::WebContents* web_contents) {
+ // Set up a fake clock error.
+ int cert_error = net::ERR_CERT_DATE_INVALID;
+ GURL request_url("https://example.com");
+ bool overridable = false;
+ bool strict_enforcement = false;
+ std::string url_param;
+ if (net::GetValueForKeyInQuery(web_contents->GetURL(), "url", &url_param) &&
+ GURL(url_param).is_valid()) {
+ request_url = GURL(url_param);
+ }
+ std::string overridable_param;
+ if (net::GetValueForKeyInQuery(web_contents->GetURL(), "overridable",
+ &overridable_param)) {
+ overridable = overridable_param == "1";
+ }
+ std::string strict_enforcement_param;
+ if (net::GetValueForKeyInQuery(web_contents->GetURL(), "strict_enforcement",
+ &strict_enforcement_param)) {
+ strict_enforcement = strict_enforcement_param == "1";
+ }
+
+ // Determine whether to change the clock to be ahead or behind.
+ std::string clock_manipulation_param;
+ ssl_errors::ClockState clock_state = ssl_errors::CLOCK_STATE_PAST;
+ if (net::GetValueForKeyInQuery(web_contents->GetURL(), "clock_manipulation",
+ &clock_manipulation_param)) {
+ int time_offset;
+ if (base::StringToInt(clock_manipulation_param, &time_offset)) {
+ clock_state = time_offset > 0 ? ssl_errors::CLOCK_STATE_FUTURE
+ : ssl_errors::CLOCK_STATE_PAST;
+ }
+ }
+
+ net::SSLInfo ssl_info;
+ ssl_info.cert = ssl_info.unverified_cert = CreateFakeCert();
+ // This delegate doesn't create an interstitial.
+ int options_mask = 0;
+ if (overridable)
+ options_mask |= security_interstitials::SSLErrorUI::SOFT_OVERRIDE_ENABLED;
+ if (strict_enforcement)
+ options_mask |= security_interstitials::SSLErrorUI::STRICT_ENFORCEMENT;
+ return new BadClockBlockingPage(
+ web_contents, cert_error, ssl_info, request_url, base::Time::Now(),
+ clock_state, nullptr,
+ base::Callback<void(content::CertificateRequestResultType)>());
+}
+
+safe_browsing::SafeBrowsingBlockingPage* CreateSafeBrowsingBlockingPage(
+ content::WebContents* web_contents) {
+ safe_browsing::SBThreatType threat_type =
+ safe_browsing::SB_THREAT_TYPE_URL_MALWARE;
+ GURL request_url("http://example.com");
+ std::string url_param;
+ if (net::GetValueForKeyInQuery(web_contents->GetURL(),
+ "url",
+ &url_param)) {
+ if (GURL(url_param).is_valid())
+ request_url = GURL(url_param);
+ }
+ GURL main_frame_url(request_url);
+ // TODO(mattm): add flag to change main_frame_url or add dedicated flag to
+ // test subresource interstitials.
+ std::string type_param;
+ if (net::GetValueForKeyInQuery(web_contents->GetURL(),
+ "type",
+ &type_param)) {
+ // TODO(mattm): add param for SB_THREAT_TYPE_URL_UNWANTED.
+ if (type_param == "malware") {
+ threat_type = safe_browsing::SB_THREAT_TYPE_URL_MALWARE;
+ } else if (type_param == "phishing") {
+ threat_type = safe_browsing::SB_THREAT_TYPE_URL_PHISHING;
+ } else if (type_param == "clientside_malware") {
+ threat_type = safe_browsing::SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL;
+ } else if (type_param == "clientside_phishing") {
+ threat_type = safe_browsing::SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL;
+ }
+ }
+ safe_browsing::SafeBrowsingBlockingPage::UnsafeResource resource;
+ resource.url = request_url;
+ resource.is_subresource = request_url != main_frame_url;
+ resource.is_subframe = false;
+ resource.threat_type = threat_type;
+ resource.web_contents_getter =
+ security_interstitials::UnsafeResource::
+ GetWebContentsGetter(web_contents->GetRenderProcessHost()->GetID(),
+ web_contents->GetMainFrame()->GetRoutingID());
+ resource.threat_source = safe_browsing::ThreatSource::LOCAL_PVER3;
+
+ // Normally safebrowsing interstitial types which block the main page load
+ // (SB_THREAT_TYPE_URL_MALWARE, SB_THREAT_TYPE_URL_PHISHING, and
+ // SB_THREAT_TYPE_URL_UNWANTED on main-frame loads) would expect there to be a
+ // pending navigation when the SafeBrowsingBlockingPage is created. This demo
+ // creates a SafeBrowsingBlockingPage but does not actually show a real
+ // interstitial. Instead it extracts the html and displays it manually, so the
+ // parts which depend on the NavigationEntry are not hit.
+ return safe_browsing::SafeBrowsingBlockingPage::CreateBlockingPage(
+ g_browser_process->safe_browsing_service()->ui_manager().get(),
+ web_contents, main_frame_url, resource);
+}
+
+TestSafeBrowsingBlockingPageQuiet* CreateSafeBrowsingQuietBlockingPage(
+ content::WebContents* web_contents) {
+ safe_browsing::SBThreatType threat_type =
+ safe_browsing::SB_THREAT_TYPE_URL_MALWARE;
+ GURL request_url("http://example.com");
+ std::string url_param;
+ if (net::GetValueForKeyInQuery(web_contents->GetURL(), "url", &url_param)) {
+ if (GURL(url_param).is_valid())
+ request_url = GURL(url_param);
+ }
+ GURL main_frame_url(request_url);
+ std::string type_param;
+ bool is_giant_webview = false;
+ if (net::GetValueForKeyInQuery(web_contents->GetURL(), "type", &type_param)) {
+ if (type_param == "malware") {
+ threat_type = safe_browsing::SB_THREAT_TYPE_URL_MALWARE;
+ } else if (type_param == "phishing") {
+ threat_type = safe_browsing::SB_THREAT_TYPE_URL_PHISHING;
+ } else if (type_param == "giant") {
+ threat_type = safe_browsing::SB_THREAT_TYPE_URL_MALWARE;
+ is_giant_webview = true;
+ }
+ }
+ safe_browsing::SafeBrowsingBlockingPage::UnsafeResource resource;
+ resource.url = request_url;
+ resource.is_subresource = request_url != main_frame_url;
+ resource.is_subframe = false;
+ resource.threat_type = threat_type;
+ resource.web_contents_getter =
+ security_interstitials::UnsafeResource::GetWebContentsGetter(
+ web_contents->GetRenderProcessHost()->GetID(),
+ web_contents->GetMainFrame()->GetRoutingID());
+ resource.threat_source = safe_browsing::ThreatSource::LOCAL_PVER3;
+
+ // Normally safebrowsing interstitial types which block the main page load
+ // (SB_THREAT_TYPE_URL_MALWARE, SB_THREAT_TYPE_URL_PHISHING, and
+ // SB_THREAT_TYPE_URL_UNWANTED on main-frame loads) would expect there to be a
+ // pending navigation when the SafeBrowsingBlockingPage is created. This demo
+ // creates a SafeBrowsingBlockingPage but does not actually show a real
+ // interstitial. Instead it extracts the html and displays it manually, so the
+ // parts which depend on the NavigationEntry are not hit.
+ return TestSafeBrowsingBlockingPageQuiet::CreateBlockingPage(
+ g_browser_process->safe_browsing_service()->ui_manager().get(),
+ web_contents, main_frame_url, resource, is_giant_webview);
+}
+
+#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
+CaptivePortalBlockingPage* CreateCaptivePortalBlockingPage(
+ content::WebContents* web_contents) {
+ bool is_wifi_connection = false;
+ GURL landing_url("https://captive.portal/login");
+ GURL request_url("https://google.com");
+ // Not initialized to a default value, since non-empty wifi_ssid is
+ // considered a wifi connection, even if is_wifi_connection is false.
+ std::string wifi_ssid;
+
+ std::string request_url_param;
+ if (net::GetValueForKeyInQuery(web_contents->GetURL(), "url",
+ &request_url_param)) {
+ if (GURL(request_url_param).is_valid())
+ request_url = GURL(request_url_param);
+ }
+ std::string landing_url_param;
+ if (net::GetValueForKeyInQuery(web_contents->GetURL(), "landing_page",
+ &landing_url_param)) {
+ if (GURL(landing_url_param).is_valid())
+ landing_url = GURL(landing_url_param);
+ }
+ std::string wifi_connection_param;
+ if (net::GetValueForKeyInQuery(web_contents->GetURL(), "is_wifi",
+ &wifi_connection_param)) {
+ is_wifi_connection = wifi_connection_param == "1";
+ }
+ std::string wifi_ssid_param;
+ if (net::GetValueForKeyInQuery(web_contents->GetURL(), "wifi_name",
+ &wifi_ssid_param)) {
+ wifi_ssid = wifi_ssid_param;
+ }
+ net::SSLInfo ssl_info;
+ ssl_info.cert = ssl_info.unverified_cert = CreateFakeCert();
+ CaptivePortalBlockingPage* blocking_page =
+ new CaptivePortalBlockingPageWithNetInfo(
+ web_contents, request_url, landing_url, ssl_info,
+ base::Callback<void(content::CertificateRequestResultType)>(),
+ is_wifi_connection, wifi_ssid);
+ return blocking_page;
+}
+#endif
+
+} // namespace
+
+InterstitialUI::InterstitialUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::URLDataSource::Add(profile, new InterstitialHTMLSource());
+}
+
+InterstitialUI::~InterstitialUI() {
+}
+
+// InterstitialHTMLSource
+
+std::string InterstitialHTMLSource::GetMimeType(
+ const std::string& mime_type) const {
+ return "text/html";
+}
+
+std::string InterstitialHTMLSource::GetSource() const {
+ return chrome::kChromeUIInterstitialHost;
+}
+
+std::string InterstitialHTMLSource::GetContentSecurityPolicyScriptSrc() const {
+ // 'unsafe-inline' is added to script-src.
+ return "script-src chrome://resources 'self' 'unsafe-eval' 'unsafe-inline';";
+}
+
+std::string InterstitialHTMLSource::GetContentSecurityPolicyStyleSrc() const {
+ return "style-src 'self' 'unsafe-inline';";
+}
+
+std::string InterstitialHTMLSource::GetContentSecurityPolicyImgSrc() const {
+ return "img-src data:;";
+}
+
+void InterstitialHTMLSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ content::WebContents* web_contents = wc_getter.Run();
+ if (!web_contents) {
+ // When browser-side navigation is enabled, web_contents can be null if
+ // the tab is closing. Nothing to do in this case.
+ return;
+ }
+ std::unique_ptr<content::InterstitialPageDelegate> interstitial_delegate;
+ std::string html;
+ if (base::StartsWith(path, "ssl", base::CompareCase::SENSITIVE)) {
+ interstitial_delegate.reset(CreateSSLBlockingPage(web_contents));
+ } else if (base::StartsWith(path, "safebrowsing",
+ base::CompareCase::SENSITIVE)) {
+ interstitial_delegate.reset(CreateSafeBrowsingBlockingPage(web_contents));
+ } else if (base::StartsWith(path, "clock", base::CompareCase::SENSITIVE)) {
+ interstitial_delegate.reset(CreateBadClockBlockingPage(web_contents));
+ }
+#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
+ else if (base::StartsWith(path, "captiveportal",
+ base::CompareCase::SENSITIVE))
+ {
+ interstitial_delegate.reset(CreateCaptivePortalBlockingPage(web_contents));
+ }
+#endif
+ if (base::StartsWith(path, "supervised_user", base::CompareCase::SENSITIVE)) {
+ html = GetSupervisedUserInterstitialHTML(path);
+ } else if (base::StartsWith(path, "quietsafebrowsing",
+ base::CompareCase::SENSITIVE)) {
+ TestSafeBrowsingBlockingPageQuiet* blocking_page =
+ CreateSafeBrowsingQuietBlockingPage(web_contents);
+ interstitial_delegate.reset(blocking_page);
+ html = blocking_page->GetHTML();
+ } else if (interstitial_delegate.get()) {
+ html = interstitial_delegate.get()->GetHTMLContents();
+ } else {
+ html = ResourceBundle::GetSharedInstance()
+ .GetRawDataResource(IDR_SECURITY_INTERSTITIAL_UI_HTML)
+ .as_string();
+ }
+ scoped_refptr<base::RefCountedString> html_bytes = new base::RefCountedString;
+ html_bytes->data().assign(html.begin(), html.end());
+ callback.Run(html_bytes.get());
+}
+
+std::string InterstitialHTMLSource::GetSupervisedUserInterstitialHTML(
+ const std::string& path) {
+ GURL url("https://localhost/" + path);
+
+ bool allow_access_requests = true;
+ std::string allow_access_requests_string;
+ if (net::GetValueForKeyInQuery(url, "allow_access_requests",
+ &allow_access_requests_string)) {
+ allow_access_requests = allow_access_requests_string == "0";
+ }
+
+ bool is_child_account = false;
+ std::string is_child_account_string;
+ if (net::GetValueForKeyInQuery(url, "is_child_account",
+ &is_child_account_string)) {
+ is_child_account = is_child_account_string == "1";
+ }
+
+ std::string custodian;
+ net::GetValueForKeyInQuery(url, "custodian", &custodian);
+ std::string second_custodian;
+ net::GetValueForKeyInQuery(url, "second_custodian", &second_custodian);
+ std::string custodian_email;
+ net::GetValueForKeyInQuery(url, "custodian_email", &custodian_email);
+ std::string second_custodian_email;
+ net::GetValueForKeyInQuery(url, "second_custodian_email",
+ &second_custodian_email);
+ std::string profile_image_url;
+ net::GetValueForKeyInQuery(url, "profile_image_url", &profile_image_url);
+ std::string profile_image_url2;
+ net::GetValueForKeyInQuery(url, "profile_image_url2", &profile_image_url2);
+
+ supervised_user_error_page::FilteringBehaviorReason reason =
+ supervised_user_error_page::DEFAULT;
+ std::string reason_string;
+ if (net::GetValueForKeyInQuery(url, "reason", &reason_string)) {
+ if (reason_string == "safe_sites") {
+ reason = supervised_user_error_page::BLACKLIST;
+ } else if (reason_string == "manual") {
+ reason = supervised_user_error_page::MANUAL;
+ } else if (reason_string == "not_signed_in") {
+ reason = supervised_user_error_page::NOT_SIGNED_IN;
+ }
+ }
+
+ return supervised_user_error_page::BuildHtml(
+ allow_access_requests, profile_image_url, profile_image_url2, custodian,
+ custodian_email, second_custodian, second_custodian_email,
+ is_child_account, reason, g_browser_process->GetApplicationLocale());
+}
diff --git a/chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.h b/chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.h
new file mode 100644
index 00000000000..1f66c6f9e58
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/interstitials/interstitial_ui.h
@@ -0,0 +1,25 @@
+// 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_UI_WEBUI_INTERSTITIALS_INTERSTITIAL_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_INTERSTITIALS_INTERSTITIAL_UI_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// Handler for chrome://interstitials demonstration pages. This class is not
+// used in displaying any real interstitials.
+class InterstitialUI : public content::WebUIController {
+ public:
+ explicit InterstitialUI(content::WebUI* web_ui);
+
+ ~InterstitialUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InterstitialUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_INTERSTITIALS_INTERSTITIAL_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc
new file mode 100644
index 00000000000..87e4fcd4ffe
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc
@@ -0,0 +1,116 @@
+// 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/strings/utf_string_conversions.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/devtools/devtools_window.h"
+#include "chrome/browser/devtools/devtools_window_testing.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/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/web_contents.h"
+
+class InterstitialUITest : public InProcessBrowserTest {
+ public:
+ InterstitialUITest() {}
+ ~InterstitialUITest() override {}
+
+ protected:
+ void TestInterstitial(GURL url, const std::string& page_title) {
+ ui_test_utils::NavigateToURL(browser(), url);
+ EXPECT_EQ(
+ base::ASCIIToUTF16(page_title),
+ browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
+
+ // Should also be able to open and close devtools.
+ DevToolsWindow* window =
+ DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), true);
+ EXPECT_TRUE(window);
+ DevToolsWindowTesting::CloseDevToolsWindowSync(window);
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(InterstitialUITest, HomePage) {
+ TestInterstitial(
+ GURL("chrome://interstitials"),
+ "Interstitials");
+}
+
+IN_PROC_BROWSER_TEST_F(InterstitialUITest, InvalidURLShouldOpenHomePage) {
+ // Invalid path should open the main page:
+ TestInterstitial(
+ GURL("chrome://interstitials/--invalid--"),
+ "Interstitials");
+}
+
+IN_PROC_BROWSER_TEST_F(InterstitialUITest, SSLInterstitial) {
+ TestInterstitial(
+ GURL("chrome://interstitials/ssl"),
+ "Privacy error");
+}
+
+IN_PROC_BROWSER_TEST_F(InterstitialUITest, MalwareInterstitial) {
+ TestInterstitial(
+ GURL("chrome://interstitials/safebrowsing?type=malware"),
+ "Security error");
+}
+
+IN_PROC_BROWSER_TEST_F(InterstitialUITest, PhishingInterstitial) {
+ TestInterstitial(
+ GURL("chrome://interstitials/safebrowsing?type=phishing"),
+ "Security error");
+}
+
+IN_PROC_BROWSER_TEST_F(InterstitialUITest, MalwareInterstitialQuiet) {
+ TestInterstitial(
+ GURL("chrome://interstitials/quietsafebrowsing?type=malware"),
+ "Security error");
+}
+
+IN_PROC_BROWSER_TEST_F(InterstitialUITest, PhishingInterstitialQuiet) {
+ TestInterstitial(
+ GURL("chrome://interstitials/quietsafebrowsing?type=phishing"),
+ "Security error");
+}
+
+IN_PROC_BROWSER_TEST_F(InterstitialUITest, ClientsideMalwareInterstitial) {
+ TestInterstitial(
+ GURL("chrome://interstitials/safebrowsing?type=clientside_malware"),
+ "Security error");
+}
+
+IN_PROC_BROWSER_TEST_F(InterstitialUITest, ClientsidePhishingInterstitial) {
+ TestInterstitial(
+ GURL("chrome://interstitials/safebrowsing?type=clientside_phishing"),
+ "Security error");
+}
+
+IN_PROC_BROWSER_TEST_F(InterstitialUITest, CaptivePortalInterstitial) {
+ TestInterstitial(GURL("chrome://interstitials/captiveportal"),
+ "Connect to network");
+}
+
+IN_PROC_BROWSER_TEST_F(InterstitialUITest, CaptivePortalInterstitialWifi) {
+ TestInterstitial(GURL("chrome://interstitials/captiveportal?is_wifi=1"),
+ "Connect to Wi-Fi");
+}
+
+// Checks that the interstitial page uses correct web contents. If not, closing
+// the tab might result in a freed web contents pointer and cause a crash.
+// See https://crbug.com/611706 for details.
+IN_PROC_BROWSER_TEST_F(InterstitialUITest, UseCorrectWebContents) {
+ int current_tab = browser()->tab_strip_model()->active_index();
+ ui_test_utils::NavigateToURL(browser(), GURL("chrome://interstitials/ssl"));
+
+ // Duplicate the tab and close it.
+ chrome::DuplicateTab(browser());
+ EXPECT_NE(current_tab, browser()->tab_strip_model()->active_index());
+ chrome::CloseTab(browser());
+ EXPECT_EQ(current_tab, browser()->tab_strip_model()->active_index());
+
+ // Reloading the page shouldn't cause a crash.
+ chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
+}
diff --git a/chromium/chrome/browser/ui/webui/invalidations_message_handler.cc b/chromium/chrome/browser/ui/webui/invalidations_message_handler.cc
new file mode 100644
index 00000000000..74150647fd2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/invalidations_message_handler.cc
@@ -0,0 +1,128 @@
+// 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/ui/webui/invalidations_message_handler.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "chrome/browser/invalidation/profile_invalidation_provider_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/invalidation/impl/invalidation_logger.h"
+#include "components/invalidation/impl/profile_invalidation_provider.h"
+#include "components/invalidation/public/invalidation_handler.h"
+#include "components/invalidation/public/invalidation_service.h"
+#include "content/public/browser/web_ui.h"
+
+namespace invalidation {
+class InvalidationLogger;
+} // namespace invalidation
+
+namespace syncer {
+class ObjectIdInvalidationMap;
+} // namespace syncer
+
+InvalidationsMessageHandler::InvalidationsMessageHandler()
+ : logger_(NULL), weak_ptr_factory_(this) {}
+
+InvalidationsMessageHandler::~InvalidationsMessageHandler() {
+ if (logger_)
+ logger_->UnregisterObserver(this);
+}
+
+void InvalidationsMessageHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "doneLoading",
+ base::Bind(&InvalidationsMessageHandler::UIReady,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "requestDetailedStatus",
+ base::Bind(&InvalidationsMessageHandler::HandleRequestDetailedStatus,
+ base::Unretained(this)));
+}
+
+void InvalidationsMessageHandler::UIReady(const base::ListValue* args) {
+ invalidation::ProfileInvalidationProvider* invalidation_provider =
+ invalidation::ProfileInvalidationProviderFactory::GetForProfile(
+ Profile::FromWebUI(web_ui()));
+ if (invalidation_provider) {
+ logger_ = invalidation_provider->GetInvalidationService()->
+ GetInvalidationLogger();
+ }
+ if (logger_ && !logger_->IsObserverRegistered(this))
+ logger_->RegisterObserver(this);
+ UpdateContent(args);
+}
+
+void InvalidationsMessageHandler::HandleRequestDetailedStatus(
+ const base::ListValue* args) {
+ invalidation::ProfileInvalidationProvider* invalidation_provider =
+ invalidation::ProfileInvalidationProviderFactory::GetForProfile(
+ Profile::FromWebUI(web_ui()));
+ if (invalidation_provider) {
+ invalidation_provider->GetInvalidationService()->RequestDetailedStatus(
+ base::Bind(&InvalidationsMessageHandler::OnDetailedStatus,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+}
+
+void InvalidationsMessageHandler::UpdateContent(const base::ListValue* args) {
+ if (logger_)
+ logger_->EmitContent();
+}
+
+void InvalidationsMessageHandler::OnRegistrationChange(
+ const std::multiset<std::string>& registered_handlers) {
+ base::ListValue list_of_handlers;
+ for (std::multiset<std::string>::const_iterator it =
+ registered_handlers.begin();
+ it != registered_handlers.end(); ++it) {
+ list_of_handlers.AppendString(*it);
+ }
+ web_ui()->CallJavascriptFunctionUnsafe("chrome.invalidations.updateHandlers",
+ list_of_handlers);
+}
+
+void InvalidationsMessageHandler::OnStateChange(
+ const syncer::InvalidatorState& new_state,
+ const base::Time& last_changed_timestamp) {
+ std::string state(syncer::InvalidatorStateToString(new_state));
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "chrome.invalidations.updateInvalidatorState", base::Value(state),
+ base::Value(last_changed_timestamp.ToJsTime()));
+}
+
+void InvalidationsMessageHandler::OnUpdateIds(
+ const std::string& handler_name,
+ const syncer::ObjectIdCountMap& ids) {
+ base::ListValue list_of_objects;
+ for (syncer::ObjectIdCountMap::const_iterator it = ids.begin();
+ it != ids.end();
+ ++it) {
+ std::unique_ptr<base::DictionaryValue> dic(new base::DictionaryValue());
+ dic->SetString("name", (it->first).name());
+ dic->SetInteger("source", (it->first).source());
+ dic->SetInteger("totalCount", it->second);
+ list_of_objects.Append(std::move(dic));
+ }
+ web_ui()->CallJavascriptFunctionUnsafe("chrome.invalidations.updateIds",
+ base::Value(handler_name),
+ list_of_objects);
+}
+void InvalidationsMessageHandler::OnDebugMessage(
+ const base::DictionaryValue& details) {}
+
+void InvalidationsMessageHandler::OnInvalidation(
+ const syncer::ObjectIdInvalidationMap& new_invalidations) {
+ std::unique_ptr<base::ListValue> invalidations_list =
+ new_invalidations.ToValue();
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "chrome.invalidations.logInvalidations", *invalidations_list);
+}
+
+void InvalidationsMessageHandler::OnDetailedStatus(
+ const base::DictionaryValue& network_details) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "chrome.invalidations.updateDetailedStatus", network_details);
+}
diff --git a/chromium/chrome/browser/ui/webui/invalidations_message_handler.h b/chromium/chrome/browser/ui/webui/invalidations_message_handler.h
new file mode 100644
index 00000000000..8d94440be97
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/invalidations_message_handler.h
@@ -0,0 +1,65 @@
+// 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_UI_WEBUI_INVALIDATIONS_MESSAGE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_INVALIDATIONS_MESSAGE_HANDLER_H_
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "components/invalidation/impl/invalidation_logger_observer.h"
+#include "components/invalidation/public/invalidation_util.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace invalidation {
+class InvalidationLogger;
+} // namespace invalidation
+
+// The implementation for the chrome://invalidations page.
+class InvalidationsMessageHandler
+ : public content::WebUIMessageHandler,
+ public invalidation::InvalidationLoggerObserver {
+ public:
+ InvalidationsMessageHandler();
+ ~InvalidationsMessageHandler() override;
+
+ // Implementation of InvalidationLoggerObserver.
+ void OnRegistrationChange(
+ const std::multiset<std::string>& registered_handlers) override;
+ void OnStateChange(const syncer::InvalidatorState& new_state,
+ const base::Time& last_change_timestamp) override;
+ void OnUpdateIds(const std::string& handler_name,
+ const syncer::ObjectIdCountMap& ids_set) override;
+ void OnDebugMessage(const base::DictionaryValue& details) override;
+ void OnInvalidation(
+ const syncer::ObjectIdInvalidationMap& new_invalidations) override;
+ void OnDetailedStatus(const base::DictionaryValue& network_details) override;
+
+ // Implementation of WebUIMessageHandler.
+ void RegisterMessages() override;
+
+ // Triggers the logger to send the current state and objects ids.
+ void UpdateContent(const base::ListValue* args);
+
+ // Called by the javascript whenever the page is ready to receive messages.
+ void UIReady(const base::ListValue* args);
+
+ // Calls the InvalidationService for any internal details.
+ void HandleRequestDetailedStatus(const base::ListValue* args);
+
+ private:
+ // The pointer to the internal InvalidatorService InvalidationLogger.
+ // Used to get the information necessary to display to the JS and to
+ // register ourselves as Observers for any notifications.
+ invalidation::InvalidationLogger* logger_;
+
+ base::WeakPtrFactory<InvalidationsMessageHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(InvalidationsMessageHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_INVALIDATIONS_MESSAGE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/invalidations_ui.cc b/chromium/chrome/browser/ui/webui/invalidations_ui.cc
new file mode 100644
index 00000000000..ce3a01fda3f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/invalidations_ui.cc
@@ -0,0 +1,37 @@
+// 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/ui/webui/invalidations_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/invalidations_message_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/invalidations_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+content::WebUIDataSource* CreateInvalidationsHTMLSource() {
+ // This is done once per opening of the page
+ // This method does not fire when refreshing the page
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIInvalidationsHost);
+ source->AddResourcePath("about_invalidations.js", IDR_ABOUT_INVALIDATIONS_JS);
+ source->SetDefaultResource(IDR_ABOUT_INVALIDATIONS_HTML);
+ source->UseGzip(std::unordered_set<std::string>());
+ return source;
+}
+
+InvalidationsUI::InvalidationsUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ if (profile) {
+ content::WebUIDataSource::Add(profile, CreateInvalidationsHTMLSource());
+ web_ui->AddMessageHandler(base::MakeUnique<InvalidationsMessageHandler>());
+ }
+}
+
+InvalidationsUI::~InvalidationsUI() { }
+
diff --git a/chromium/chrome/browser/ui/webui/invalidations_ui.h b/chromium/chrome/browser/ui/webui/invalidations_ui.h
new file mode 100644
index 00000000000..f4a9cf405bb
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/invalidations_ui.h
@@ -0,0 +1,21 @@
+// 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_UI_WEBUI_INVALIDATIONS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_INVALIDATIONS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The implementation for the chrome://invalidations page.
+class InvalidationsUI : public content::WebUIController {
+ public:
+ explicit InvalidationsUI(content::WebUI* web_ui);
+ ~InvalidationsUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InvalidationsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_INVALIDATIONS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/large_icon_source.cc b/chromium/chrome/browser/ui/webui/large_icon_source.cc
new file mode 100644
index 00000000000..165fb78c403
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/large_icon_source.cc
@@ -0,0 +1,130 @@
+// 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.
+
+#include "chrome/browser/ui/webui/large_icon_source.h"
+
+#include <vector>
+
+#include "base/memory/ref_counted_memory.h"
+#include "chrome/browser/search/instant_io_context.h"
+#include "chrome/common/url_constants.h"
+#include "components/favicon/core/large_icon_service.h"
+#include "components/favicon_base/fallback_icon_style.h"
+#include "components/favicon_base/favicon_types.h"
+#include "components/favicon_base/large_icon_url_parser.h"
+#include "net/url_request/url_request.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/codec/png_codec.h"
+
+namespace {
+
+const int kMaxLargeIconSize = 192; // Arbitrary bound to safeguard endpoint.
+
+} // namespace
+
+LargeIconSource::LargeIconSource(favicon::LargeIconService* large_icon_service)
+ : large_icon_service_(large_icon_service) {}
+
+LargeIconSource::~LargeIconSource() {
+}
+
+std::string LargeIconSource::GetSource() const {
+ return chrome::kChromeUILargeIconHost;
+}
+
+void LargeIconSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ if (!large_icon_service_) {
+ SendNotFoundResponse(callback);
+ return;
+ }
+
+ LargeIconUrlParser parser;
+ bool success = parser.Parse(path);
+ if (!success ||
+ parser.size_in_pixels() <= 0 ||
+ parser.size_in_pixels() > kMaxLargeIconSize) {
+ SendNotFoundResponse(callback);
+ return;
+ }
+
+ GURL url(parser.url_string());
+ if (!url.is_valid()) {
+ SendNotFoundResponse(callback);
+ return;
+ }
+
+ // TODO(beaudoin): Potentially allow icon to be scaled up.
+ large_icon_service_->GetLargeIconOrFallbackStyle(
+ url,
+ parser.size_in_pixels(), // Reducing this will enable scale up.
+ parser.size_in_pixels(),
+ base::Bind(&LargeIconSource::OnLargeIconDataAvailable,
+ base::Unretained(this), callback, url,
+ parser.size_in_pixels()),
+ &cancelable_task_tracker_);
+}
+
+std::string LargeIconSource::GetMimeType(const std::string&) const {
+ // We need to explicitly return a mime type, otherwise if the user tries to
+ // drag the image they get no extension.
+ return "image/png";
+}
+
+bool LargeIconSource::AllowCaching() const {
+ return false;
+}
+
+bool LargeIconSource::ShouldReplaceExistingSource() const {
+ // Leave the existing DataSource in place, otherwise we'll drop any pending
+ // requests on the floor.
+ return false;
+}
+
+bool LargeIconSource::ShouldServiceRequest(
+ const GURL& url,
+ content::ResourceContext* resource_context,
+ int render_process_id) const {
+ if (url.SchemeIs(chrome::kChromeSearchScheme)) {
+ return InstantIOContext::ShouldServiceRequest(url, resource_context,
+ render_process_id);
+ }
+ return URLDataSource::ShouldServiceRequest(url, resource_context,
+ render_process_id);
+}
+
+void LargeIconSource::OnLargeIconDataAvailable(
+ const content::URLDataSource::GotDataCallback& callback,
+ const GURL& url,
+ int size,
+ const favicon_base::LargeIconResult& result) {
+ if (result.bitmap.is_valid()) {
+ callback.Run(result.bitmap.bitmap_data.get());
+ return;
+ }
+
+ if (!result.fallback_icon_style) {
+ SendNotFoundResponse(callback);
+ return;
+ }
+
+ // RenderFallbackIconBitmap() cannot draw fallback icons on Android. See
+ // crbug.com/580922 for details. Return a 1x1 bitmap so that JavaScript can
+ // detect that it needs to generate a fallback icon.
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(1, 1);
+ bitmap.eraseColor(result.fallback_icon_style->background_color);
+ std::vector<unsigned char> bitmap_data;
+ if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &bitmap_data))
+ bitmap_data.clear();
+
+ callback.Run(base::RefCountedBytes::TakeVector(&bitmap_data));
+}
+
+void LargeIconSource::SendNotFoundResponse(
+ const content::URLDataSource::GotDataCallback& callback) {
+ callback.Run(nullptr);
+}
diff --git a/chromium/chrome/browser/ui/webui/large_icon_source.h b/chromium/chrome/browser/ui/webui/large_icon_source.h
new file mode 100644
index 00000000000..6d48d908b87
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/large_icon_source.h
@@ -0,0 +1,76 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_LARGE_ICON_SOURCE_H_
+#define CHROME_BROWSER_UI_WEBUI_LARGE_ICON_SOURCE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "content/public/browser/url_data_source.h"
+
+namespace favicon {
+class LargeIconService;
+}
+
+namespace favicon_base {
+struct LargeIconResult;
+}
+
+// LargeIconSource services explicit chrome:// requests for large icons.
+//
+// Format:
+// chrome://large-icon/size/url
+//
+// Parameter:
+// 'size' Required (including trailing '/')
+// Positive integer to specify the large icon's size in pixels.
+// 'url' Optional
+// String to specify the page URL of the large icon.
+//
+// Example: chrome://large-icon/48/http://www.google.com/
+// This requests a 48x48 large icon for http://www.google.com.
+class LargeIconSource : public content::URLDataSource {
+ public:
+ // |large_icon_service| is owned by caller and may be null.
+ explicit LargeIconSource(favicon::LargeIconService* large_icon_service);
+
+ ~LargeIconSource() override;
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ std::string GetMimeType(const std::string&) const override;
+ bool AllowCaching() const override;
+ bool ShouldReplaceExistingSource() const override;
+ bool ShouldServiceRequest(const GURL& url,
+ content::ResourceContext* resource_context,
+ int render_process_id) const override;
+
+ private:
+ // Called with results of large icon retrieval request.
+ void OnLargeIconDataAvailable(
+ const content::URLDataSource::GotDataCallback& callback,
+ const GURL& url,
+ int size,
+ const favicon_base::LargeIconResult& bitmap_result);
+
+ // Returns null to trigger "Not Found" response.
+ void SendNotFoundResponse(
+ const content::URLDataSource::GotDataCallback& callback);
+
+ base::CancelableTaskTracker cancelable_task_tracker_;
+
+ // Owned by client.
+ favicon::LargeIconService* large_icon_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(LargeIconSource);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_LARGE_ICON_SOURCE_H_
diff --git a/chromium/chrome/browser/ui/webui/local_discovery/OWNERS b/chromium/chrome/browser/ui/webui/local_discovery/OWNERS
new file mode 100644
index 00000000000..4633094760d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/local_discovery/OWNERS
@@ -0,0 +1,4 @@
+gene@chromium.org
+vitalybuka@chromium.org
+
+# COMPONENT: Services>CloudPrint
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
new file mode 100644
index 00000000000..6cedb644387
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui.cc
@@ -0,0 +1,152 @@
+// 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/local_discovery/local_discovery_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "build/build_config.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h"
+#include "chrome/browser/ui/webui/metrics_handler.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "printing/features/features.h"
+
+namespace {
+
+content::WebUIDataSource* CreateLocalDiscoveryHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIDevicesHost);
+
+ source->SetDefaultResource(IDR_LOCAL_DISCOVERY_HTML);
+ source->AddResourcePath("local_discovery.css", IDR_LOCAL_DISCOVERY_CSS);
+ source->AddResourcePath("local_discovery.js", IDR_LOCAL_DISCOVERY_JS);
+
+ source->AddLocalizedString("serviceRegister",
+ 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("registerUser",
+ IDS_LOCAL_DISCOVERY_REGISTER_USER);
+ source->AddLocalizedString("confirmRegistration",
+ IDS_LOCAL_DISCOVERY_CONFIRM_REGISTRATION);
+ source->AddLocalizedString("addingPrinter",
+ IDS_LOCAL_DISCOVERY_ADDING_PRINTER);
+ source->AddLocalizedString("addingDevice", IDS_LOCAL_DISCOVERY_ADDING_DEVICE);
+ source->AddLocalizedString("addingError",
+ IDS_LOCAL_DISCOVERY_ERROR_OCURRED);
+ source->AddLocalizedString("addingErrorMessage",
+ IDS_LOCAL_DISCOVERY_ERROR_OCURRED_MESSAGE);
+ source->AddLocalizedString("addingCanceledMessage",
+ IDS_LOCAL_DISCOVERY_REGISTER_CANCELED_ON_PRINTER);
+ source->AddLocalizedString("addingTimeoutMessage",
+ IDS_LOCAL_DISCOVERY_REGISTER_TIMEOUT_ON_PRINTER);
+ source->AddLocalizedString("addingPrinterMessage1",
+ IDS_LOCAL_DISCOVERY_ADDING_PRINTER_MESSAGE1);
+ source->AddLocalizedString("addingPrinterMessage2",
+ IDS_LOCAL_DISCOVERY_ADDING_PRINTER_MESSAGE2);
+ source->AddLocalizedString("addingDeviceMessage1",
+ IDS_LOCAL_DISCOVERY_ADDING_DEVICE_MESSAGE1);
+ source->AddLocalizedString("addingDeviceConfirmCodeMessage",
+ IDS_LOCAL_DISCOVERY_CONFIRM_CODE_MESSAGE);
+ source->AddLocalizedString("confirmCode", IDS_LOCAL_DISCOVERY_CONFIRM_CODE);
+ source->AddLocalizedString("devicesTitle",
+ IDS_LOCAL_DISCOVERY_DEVICES_PAGE_TITLE);
+ source->AddLocalizedString("noDescriptionDevice",
+ IDS_LOCAL_DISCOVERY_NO_DESCRIPTION_DEVICE);
+ source->AddLocalizedString("noDescriptionPrinter",
+ IDS_LOCAL_DISCOVERY_NO_DESCRIPTION_PRINTER);
+ source->AddLocalizedString("printersOnNetworkZero",
+ IDS_LOCAL_DISCOVERY_PRINTERS_ON_NETWORK_ZERO);
+ source->AddLocalizedString("printersOnNetworkOne",
+ IDS_LOCAL_DISCOVERY_PRINTERS_ON_NETWORK_ONE);
+ source->AddLocalizedString("printersOnNetworkMultiple",
+ IDS_LOCAL_DISCOVERY_PRINTERS_ON_NETWORK_MULTIPLE);
+ source->AddLocalizedString("cancel", IDS_CANCEL);
+ source->AddLocalizedString("ok", IDS_OK);
+ source->AddLocalizedString("loading", IDS_LOCAL_DISCOVERY_LOADING);
+ source->AddLocalizedString("addPrinters", IDS_LOCAL_DISCOVERY_ADD_PRINTERS);
+ source->AddLocalizedString(
+ "noPrintersOnNetworkExplanation",
+ IDS_LOCAL_DISCOVERY_NO_PRINTERS_ON_NETWORK_EXPLANATION);
+ source->AddLocalizedString("cloudDevicesUnavailable",
+ IDS_LOCAL_DISCOVERY_CLOUD_DEVICES_UNAVAILABLE);
+ source->AddLocalizedString("retryLoadCloudDevices",
+ IDS_LOCAL_DISCOVERY_RETRY_LOAD_CLOUD_DEVICES);
+ source->AddLocalizedString("cloudDevicesNeedLogin",
+ IDS_LOCAL_DISCOVERY_CLOUD_DEVICES_NEED_LOGIN);
+ source->AddLocalizedString("cloudDevicesLogin",
+ IDS_LOCAL_DISCOVERY_CLOUD_DEVICES_LOGIN);
+ source->AddLocalizedString("registerNeedLogin",
+ IDS_LOCAL_DISCOVERY_REGISTER_NEED_LOGIN);
+ source->AddLocalizedString("availableDevicesTitle",
+ IDS_LOCAL_DISCOVERY_AVAILABLE_DEVICES);
+ source->AddLocalizedString("myDevicesTitle",
+ IDS_LOCAL_DISCOVERY_MY_DEVICES);
+ source->AddLocalizedString("backButton", IDS_SETTINGS_TITLE);
+
+ // Cloud print connector-related strings.
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW) && !defined(OS_CHROMEOS)
+ source->AddLocalizedString("cloudPrintConnectorEnablingButton",
+ IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_ENABLING_BUTTON);
+ source->AddLocalizedString("cloudPrintConnectorDisabledButton",
+ IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_DISABLED_BUTTON);
+ source->AddLocalizedString("cloudPrintConnectorEnabledButton",
+ IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_ENABLED_BUTTON);
+ source->AddLocalizedString("cloudPrintName",
+ IDS_GOOGLE_CLOUD_PRINT);
+ source->AddLocalizedString("titleConnector",
+ IDS_LOCAL_DISCOVERY_CONNECTOR_SECTION);
+#endif
+
+ source->SetJsonPath("strings.js");
+
+ source->DisableDenyXFrameOptions();
+
+ return source;
+}
+
+} // namespace
+
+LocalDiscoveryUI::LocalDiscoveryUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ // Set up the chrome://devices/ source.
+ content::WebUIDataSource* source = CreateLocalDiscoveryHTMLSource();
+ Browser* browser =
+ chrome::FindBrowserWithWebContents(web_ui->GetWebContents());
+ // Show a back button pointing to Settings if the browser has no location bar.
+ if (browser && browser->is_trusted_source())
+ source->AddString("backButtonURL", chrome::kChromeUISettingsURL);
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), source);
+
+ // TODO(gene): Use LocalDiscoveryUIHandler to send updated to the devices
+ // page. For example
+ web_ui->AddMessageHandler(
+ base::MakeUnique<local_discovery::LocalDiscoveryUIHandler>());
+ web_ui->AddMessageHandler(base::MakeUnique<MetricsHandler>());
+}
+
+void LocalDiscoveryUI::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterBooleanPref(
+ prefs::kLocalDiscoveryNotificationsEnabled,
+#if defined(OS_WIN)
+ false,
+#else
+ true,
+#endif
+ user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+}
diff --git a/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui.h b/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui.h
new file mode 100644
index 00000000000..9abff5fca86
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui.h
@@ -0,0 +1,21 @@
+// 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_LOCAL_DISCOVERY_LOCAL_DISCOVERY_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_LOCAL_DISCOVERY_LOCAL_DISCOVERY_UI_H_
+
+#include "base/macros.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "content/public/browser/web_ui_controller.h"
+
+class LocalDiscoveryUI : public content::WebUIController {
+ public:
+ explicit LocalDiscoveryUI(content::WebUI* web_ui);
+
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LocalDiscoveryUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_LOCAL_DISCOVERY_LOCAL_DISCOVERY_UI_H_
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
new file mode 100644
index 00000000000..9971504107a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc
@@ -0,0 +1,590 @@
+// 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 <stdint.h>
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#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/profiles/profile.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/webui/local_discovery/local_discovery_ui_handler.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "chrome/test/base/web_ui_browser_test.h"
+#include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/signin/core/browser/signin_manager_base.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request_status.h"
+#include "net/url_request/url_request_test_util.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/common/pref_names.h"
+#include "chromeos/chromeos_switches.h"
+#include "components/prefs/pref_service.h"
+#endif
+
+using testing::InvokeWithoutArgs;
+using testing::Return;
+using testing::AtLeast;
+using testing::DoDefault;
+using testing::DoAll;
+using testing::InSequence;
+using testing::StrictMock;
+using testing::AnyNumber;
+
+using testing::InvokeWithoutArgs;
+using testing::Return;
+using testing::AtLeast;
+
+namespace local_discovery {
+
+namespace {
+
+const uint8_t kQueryData[] = {
+ // Header
+ 0x00, 0x00,
+ 0x00, 0x00, // Flags not set.
+ 0x00, 0x01, // Set QDCOUNT (question count) to 1, all the
+ // rest are 0 for a query.
+ 0x00, 0x00,
+ 0x00, 0x00,
+ 0x00, 0x00,
+
+ // Question
+ 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+ 0x04, '_', 't', 'c', 'p',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+
+ 0x00, 0x0c, // QTYPE: A query.
+ 0x00, 0x01, // QCLASS: IN class. Unicast bit not set.
+};
+
+const uint8_t kAnnouncePacket[] = {
+ // Header
+ 0x00, 0x00, // ID is zeroed out
+ 0x80, 0x00, // Standard query response, no error
+ 0x00, 0x00, // No questions (for simplicity)
+ 0x00, 0x05, // 5 RR (answers)
+ 0x00, 0x00, // 0 authority RRs
+ 0x00, 0x00, // 0 additional RRs
+
+ 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+ 0x04, '_', 't', 'c', 'p',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+ 0x00, 0x0c, // TYPE is PTR.
+ 0x00, 0x01, // CLASS is IN.
+ 0x00, 0x00, // TTL (4 bytes) is 32768 second.
+ 0x10, 0x00,
+ 0x00, 0x0c, // RDLENGTH is 12 bytes.
+ 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
+ 0xc0, 0x0c,
+
+ 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
+ 0xc0, 0x0c,
+ 0x00, 0x10, // TYPE is TXT.
+ 0x00, 0x01, // CLASS is IN.
+ 0x00, 0x00, // TTL (4 bytes) is 32768 seconds.
+ 0x01, 0x00,
+ 0x00, 0x41, // RDLENGTH is 69 bytes.
+ 0x03, 'i', 'd', '=',
+ 0x10, 't', 'y', '=', 'S', 'a', 'm', 'p', 'l', 'e', ' ',
+ 'd', 'e', 'v', 'i', 'c', 'e',
+ 0x1e, 'n', 'o', 't', 'e', '=',
+ 'S', 'a', 'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', ' ',
+ 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n',
+ 0x0c, 't', 'y', 'p', 'e', '=', 'p', 'r', 'i', 'n', 't', 'e', 'r',
+
+ 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
+ 0xc0, 0x0c,
+ 0x00, 0x21, // Type is SRV
+ 0x00, 0x01, // CLASS is IN
+ 0x00, 0x00, // TTL (4 bytes) is 32768 second.
+ 0x10, 0x00,
+ 0x00, 0x17, // RDLENGTH is 23
+ 0x00, 0x00,
+ 0x00, 0x00,
+ 0x22, 0xb8, // port 8888
+ 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+
+ 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+ 0x00, 0x01, // Type is A
+ 0x00, 0x01, // CLASS is IN
+ 0x00, 0x00, // TTL (4 bytes) is 32768 second.
+ 0x10, 0x00,
+ 0x00, 0x04, // RDLENGTH is 4
+ 0x01, 0x02, 0x03, 0x04, // 1.2.3.4
+
+ 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+ 0x00, 0x1C, // Type is AAAA
+ 0x00, 0x01, // CLASS is IN
+ 0x00, 0x00, // TTL (4 bytes) is 32768 second.
+ 0x10, 0x00,
+ 0x00, 0x10, // RDLENGTH is 16
+ 0x01, 0x02, 0x03, 0x04, // 1.2.3.4
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+};
+
+
+const uint8_t kGoodbyePacket[] = {
+ // Header
+ 0x00, 0x00, // ID is zeroed out
+ 0x80, 0x00, // Standard query response, RA, no error
+ 0x00, 0x00, // No questions (for simplicity)
+ 0x00, 0x02, // 1 RR (answers)
+ 0x00, 0x00, // 0 authority RRs
+ 0x00, 0x00, // 0 additional RRs
+
+ 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+ 0x04, '_', 't', 'c', 'p',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+ 0x00, 0x0c, // TYPE is PTR.
+ 0x00, 0x01, // CLASS is IN.
+ 0x00, 0x00, // TTL (4 bytes) is 0 seconds.
+ 0x00, 0x00,
+ 0x00, 0x0c, // RDLENGTH is 12 bytes.
+ 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
+ 0xc0, 0x0c,
+
+
+ 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
+ 0xc0, 0x0c,
+ 0x00, 0x21, // Type is SRV
+ 0x00, 0x01, // CLASS is IN
+ 0x00, 0x00, // TTL (4 bytes) is 0 seconds.
+ 0x00, 0x00,
+ 0x00, 0x17, // RDLENGTH is 23
+ 0x00, 0x00,
+ 0x00, 0x00,
+ 0x22, 0xb8, // port 8888
+ 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+};
+
+const uint8_t kAnnouncePacketRegistered[] = {
+ // Header
+ 0x00, 0x00, // ID is zeroed out
+ 0x80, 0x00, // Standard query response, RA, no error
+ 0x00, 0x00, // No questions (for simplicity)
+ 0x00, 0x01, // 1 RR (answers)
+ 0x00, 0x00, // 0 authority RRs
+ 0x00, 0x00, // 0 additional RRs
+
+ 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
+ 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+ 0x04, '_', 't', 'c', 'p',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+ 0x00, 0x10, // TYPE is TXT.
+ 0x00, 0x01, // CLASS is IN.
+ 0x00, 0x00, // TTL (4 bytes) is 32768 seconds.
+ 0x01, 0x00,
+ 0x00, 0x3b, // RDLENGTH is 76 bytes.
+ 0x0a, 'i', 'd', '=', 's', 'o', 'm', 'e', '_', 'i', 'd',
+ 0x10, 't', 'y', '=', 'S', 'a', 'm', 'p', 'l', 'e', ' ',
+ 'd', 'e', 'v', 'i', 'c', 'e',
+ 0x1e, 'n', 'o', 't', 'e', '=',
+ 'S', 'a', 'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', ' ',
+ 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n',
+};
+
+const char kResponseInfo[] = "{"
+ " \"x-privet-token\" : \"MyPrivetToken\""
+ "}";
+
+const char kResponseInfoWithID[] = "{"
+ " \"x-privet-token\" : \"MyPrivetToken\","
+ " \"id\" : \"my_id\""
+ "}";
+
+const char kResponseRegisterStart[] = "{"
+ " \"action\": \"start\","
+ " \"user\": \"user@consumer.example.com\""
+ "}";
+
+const char kResponseRegisterClaimTokenNoConfirm[] = "{"
+ " \"action\": \"getClaimToken\","
+ " \"user\": \"user@consumer.example.com\","
+ " \"error\": \"pending_user_action\","
+ " \"timeout\": 1"
+ "}";
+
+const char kResponseRegisterClaimTokenConfirm[] = "{"
+ " \"action\": \"getClaimToken\","
+ " \"user\": \"user@consumer.example.com\","
+ " \"token\": \"MySampleToken\","
+ " \"claim_url\": \"http://someurl.com/\""
+ "}";
+
+const char kResponseCloudPrintConfirm[] = "{ \"success\": true }";
+
+const char kResponseRegisterComplete[] = "{"
+ " \"action\": \"complete\","
+ " \"user\": \"user@consumer.example.com\","
+ " \"device_id\": \"my_id\""
+ "}";
+
+const char kResponseGaiaToken[] = "{"
+ " \"access_token\": \"at1\","
+ " \"expires_in\": 3600,"
+ " \"token_type\": \"Bearer\""
+ "}";
+
+const char kResponseGaiaId[] = "{"
+ " \"id\": \"12345\""
+ "}";
+
+const char kURLInfo[] = "http://1.2.3.4:8888/privet/info";
+
+const char kURLRegisterStart[] =
+ "http://1.2.3.4:8888/privet/register?action=start&"
+ "user=user%40consumer.example.com";
+
+const char kURLRegisterClaimToken[] =
+ "http://1.2.3.4:8888/privet/register?action=getClaimToken&"
+ "user=user%40consumer.example.com";
+
+const char kURLCloudPrintConfirm[] =
+ "https://www.google.com/cloudprint/confirm?token=MySampleToken";
+
+const char kURLRegisterComplete[] =
+ "http://1.2.3.4:8888/privet/register?action=complete&"
+ "user=user%40consumer.example.com";
+
+const char kSampleGaiaId[] = "12345";
+const char kSampleUser[] = "user@consumer.example.com";
+
+class TestMessageLoopCondition {
+ public:
+ TestMessageLoopCondition() : signaled_(false),
+ waiting_(false) {
+ }
+
+ ~TestMessageLoopCondition() {
+ }
+
+ // Signal a waiting method that it can continue executing.
+ void Signal() {
+ signaled_ = true;
+ if (waiting_)
+ base::MessageLoop::current()->QuitWhenIdle();
+ }
+
+ // Pause execution and recursively run the message loop until |Signal()| is
+ // called. Do not pause if |Signal()| has already been called.
+ void Wait() {
+ while (!signaled_) {
+ waiting_ = true;
+ base::RunLoop().Run();
+ waiting_ = false;
+ }
+ signaled_ = false;
+ }
+
+ private:
+ bool signaled_;
+ bool waiting_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestMessageLoopCondition);
+};
+
+class MockableFakeURLFetcherCreator {
+ public:
+ MockableFakeURLFetcherCreator() {
+ }
+
+ ~MockableFakeURLFetcherCreator() {
+ }
+
+ MOCK_METHOD1(OnCreateFakeURLFetcher, void(const std::string& url));
+
+ std::unique_ptr<net::FakeURLFetcher> CreateFakeURLFetcher(
+ const GURL& url,
+ net::URLFetcherDelegate* delegate,
+ const std::string& response_data,
+ net::HttpStatusCode response_code,
+ net::URLRequestStatus::Status status) {
+ OnCreateFakeURLFetcher(url.spec());
+ return std::unique_ptr<net::FakeURLFetcher>(new net::FakeURLFetcher(
+ url, delegate, response_data, response_code, status));
+ }
+
+ net::FakeURLFetcherFactory::FakeURLFetcherCreator callback() {
+ return base::Bind(&MockableFakeURLFetcherCreator::CreateFakeURLFetcher,
+ base::Unretained(this));
+ }
+};
+
+class LocalDiscoveryUITest : public WebUIBrowserTest {
+ public:
+ LocalDiscoveryUITest() : fake_fetcher_factory_(
+ &fetcher_impl_factory_,
+ fake_url_fetcher_creator_.callback()) {
+ }
+ ~LocalDiscoveryUITest() override {
+ }
+
+ void SetUpOnMainThread() override {
+ WebUIBrowserTest::SetUpOnMainThread();
+
+ test_service_discovery_client_ = new TestServiceDiscoveryClient();
+ test_service_discovery_client_->Start();
+ EXPECT_CALL(
+ *test_service_discovery_client_.get(),
+ OnSendTo(std::string((const char*)kQueryData, sizeof(kQueryData))))
+ .Times(AtLeast(2))
+ .WillOnce(InvokeWithoutArgs(&condition_devices_listed_,
+ &TestMessageLoopCondition::Signal))
+ .WillRepeatedly(Return());
+
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetForProfile(browser()->profile());
+
+ DCHECK(signin_manager);
+ signin_manager->SetAuthenticatedAccountInfo(kSampleGaiaId, kSampleUser);
+
+ fake_fetcher_factory().SetFakeResponse(
+ GURL(kURLInfo),
+ kResponseInfo,
+ net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+
+ fake_fetcher_factory().SetFakeResponse(
+ GURL(kURLRegisterStart),
+ kResponseRegisterStart,
+ net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+
+ fake_fetcher_factory().SetFakeResponse(
+ GURL(kURLRegisterClaimToken),
+ kResponseRegisterClaimTokenNoConfirm,
+ net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+
+ fake_fetcher_factory().SetFakeResponse(
+ GURL(kURLCloudPrintConfirm),
+ kResponseCloudPrintConfirm,
+ net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+
+ fake_fetcher_factory().SetFakeResponse(
+ GURL(kURLRegisterComplete),
+ kResponseRegisterComplete,
+ net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+
+ fake_fetcher_factory().SetFakeResponse(
+ GaiaUrls::GetInstance()->oauth2_token_url(),
+ kResponseGaiaToken,
+ net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+
+ EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
+ GaiaUrls::GetInstance()->oauth2_token_url().spec()))
+ .Times(AnyNumber());
+
+ fake_fetcher_factory().SetFakeResponse(
+ GaiaUrls::GetInstance()->oauth_user_info_url(),
+ kResponseGaiaId,
+ net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+
+ EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
+ GaiaUrls::GetInstance()->oauth_user_info_url().spec()))
+ .Times(AnyNumber());
+
+ ProfileOAuth2TokenService* token_service =
+ ProfileOAuth2TokenServiceFactory::GetForProfile(browser()->profile());
+
+ token_service->UpdateCredentials(
+ signin_manager->GetAuthenticatedAccountId(), "MyFakeToken");
+
+ AddLibrary(base::FilePath(FILE_PATH_LITERAL("local_discovery_ui_test.js")));
+ }
+
+ void TearDownOnMainThread() override {
+ test_service_discovery_client_ = nullptr;
+ WebUIBrowserTest::TearDownOnMainThread();
+ }
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+#if defined(OS_CHROMEOS)
+ // On chromeos, don't sign in with the stub-user automatically. Use the
+ // kLoginUser instead.
+ command_line->AppendSwitchASCII(chromeos::switches::kLoginUser,
+ kSampleUser);
+ command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile,
+ chrome::kTestUserProfileDir);
+#endif
+ command_line->AppendSwitch(switches::kDisableDeviceDiscoveryNotifications);
+ WebUIBrowserTest::SetUpCommandLine(command_line);
+ }
+
+ void RunFor(base::TimeDelta time_period) {
+ base::CancelableCallback<void()> callback(
+ base::Bind(&base::MessageLoop::QuitWhenIdle,
+ base::Unretained(base::MessageLoop::current())));
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, callback.callback(), time_period);
+
+ base::RunLoop().Run();
+ callback.Cancel();
+ }
+
+ TestServiceDiscoveryClient* test_service_discovery_client() {
+ return test_service_discovery_client_.get();
+ }
+
+ TestMessageLoopCondition& condition_devices_listed() {
+ return condition_devices_listed_;
+ }
+
+ net::FakeURLFetcherFactory& fake_fetcher_factory() {
+ return fake_fetcher_factory_;
+ }
+
+ MockableFakeURLFetcherCreator& fake_url_fetcher_creator() {
+ return fake_url_fetcher_creator_;
+ }
+
+ private:
+ scoped_refptr<TestServiceDiscoveryClient> test_service_discovery_client_;
+ TestMessageLoopCondition condition_devices_listed_;
+
+ net::URLFetcherImplFactory fetcher_impl_factory_;
+ StrictMock<MockableFakeURLFetcherCreator> fake_url_fetcher_creator_;
+ net::FakeURLFetcherFactory fake_fetcher_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocalDiscoveryUITest);
+};
+
+IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest, EmptyTest) {
+ ui_test_utils::NavigateToURL(browser(), GURL(
+ chrome::kChromeUIDevicesURL));
+ condition_devices_listed().Wait();
+ EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices"));
+}
+
+IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest, AddRowTest) {
+ ui_test_utils::NavigateToURL(browser(), GURL(
+ chrome::kChromeUIDevicesURL));
+ condition_devices_listed().Wait();
+
+ test_service_discovery_client()->SimulateReceive(
+ kAnnouncePacket, sizeof(kAnnouncePacket));
+
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkOneDevice"));
+
+ test_service_discovery_client()->SimulateReceive(
+ kGoodbyePacket, sizeof(kGoodbyePacket));
+
+ RunFor(base::TimeDelta::FromMilliseconds(1100));
+
+ EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices"));
+}
+
+// Flaky: http://crbug.com/660669.
+IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest, RegisterTest) {
+ TestMessageLoopCondition condition_token_claimed;
+
+ ui_test_utils::NavigateToURL(browser(), GURL(
+ chrome::kChromeUIDevicesURL));
+ condition_devices_listed().Wait();
+
+ test_service_discovery_client()->SimulateReceive(
+ kAnnouncePacket, sizeof(kAnnouncePacket));
+
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkOneDevice"));
+
+ EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("registerShowOverlay"));
+
+ {
+ InSequence s;
+ EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(kURLInfo));
+ EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
+ kURLRegisterStart));
+ EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
+ kURLRegisterClaimToken))
+ .WillOnce(InvokeWithoutArgs(&condition_token_claimed,
+ &TestMessageLoopCondition::Signal));
+ }
+
+ EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("registerBegin"));
+
+ condition_token_claimed.Wait();
+
+ EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("expectPageAdding1"));
+
+ fake_fetcher_factory().SetFakeResponse(
+ GURL(kURLRegisterClaimToken),
+ kResponseRegisterClaimTokenConfirm,
+ net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+
+ fake_fetcher_factory().SetFakeResponse(
+ GURL(kURLInfo),
+ kResponseInfoWithID,
+ net::HTTP_OK,
+ net::URLRequestStatus::SUCCESS);
+
+ {
+ InSequence s;
+ EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
+ kURLRegisterClaimToken));
+ EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
+ kURLCloudPrintConfirm));
+ EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
+ kURLRegisterComplete));
+ EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(kURLInfo))
+ .WillOnce(InvokeWithoutArgs(&condition_token_claimed,
+ &TestMessageLoopCondition::Signal));
+ }
+
+ condition_token_claimed.Wait();
+
+ test_service_discovery_client()->SimulateReceive(
+ kAnnouncePacketRegistered, sizeof(kAnnouncePacketRegistered));
+
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("expectRegisterDone"));
+}
+
+} // namespace
+
+} // namespace local_discovery
diff --git a/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc b/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc
new file mode 100644
index 00000000000..c924a72f36c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc
@@ -0,0 +1,619 @@
+// 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/local_discovery/local_discovery_ui_handler.h"
+
+#include <set>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/user_metrics.h"
+#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/local_discovery/service_discovery_shared_client.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/browser/printing/cloud_print/privet_confirm_api_flow.h"
+#include "chrome/browser/printing/cloud_print/privet_constants.h"
+#include "chrome/browser/printing/cloud_print/privet_device_lister_impl.h"
+#include "chrome/browser/printing/cloud_print/privet_http_asynchronous_factory.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"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/cloud_devices/common/cloud_devices_urls.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "content/public/browser/web_ui.h"
+#include "printing/features/features.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW) && !defined(OS_CHROMEOS)
+#define CLOUD_PRINT_CONNECTOR_UI_AVAILABLE
+#endif
+
+using cloud_print::CloudPrintPrinterList;
+using cloud_print::DeviceDescription;
+using cloud_print::GCDApiFlow;
+using cloud_print::PrivetRegisterOperation;
+
+namespace local_discovery {
+
+namespace {
+
+const char kDictionaryKeyServiceName[] = "service_name";
+const char kDictionaryKeyDisplayName[] = "display_name";
+const char kDictionaryKeyDescription[] = "description";
+const char kDictionaryKeyType[] = "type";
+const char kDictionaryKeyIsWifi[] = "is_wifi";
+const char kDictionaryKeyID[] = "id";
+
+const char kKeyPrefixMDns[] = "MDns:";
+
+int g_num_visible = 0;
+
+const int kCloudDevicesPrivetVersion = 3;
+
+std::unique_ptr<base::DictionaryValue> CreateDeviceInfo(
+ const CloudPrintPrinterList::Device& description) {
+ std::unique_ptr<base::DictionaryValue> return_value(
+ new base::DictionaryValue);
+
+ return_value->SetString(kDictionaryKeyID, description.id);
+ return_value->SetString(kDictionaryKeyDisplayName, description.display_name);
+ return_value->SetString(kDictionaryKeyDescription, description.description);
+ return_value->SetString(kDictionaryKeyType, "printer");
+
+ return return_value;
+}
+
+void ReadDevicesList(const CloudPrintPrinterList::DeviceList& devices,
+ const std::set<std::string>& local_ids,
+ base::ListValue* devices_list) {
+ for (const auto& i : devices) {
+ if (base::ContainsKey(local_ids, i.id)) {
+ devices_list->Append(CreateDeviceInfo(i));
+ }
+ }
+
+ for (const auto& i : devices) {
+ if (!base::ContainsKey(local_ids, i.id)) {
+ devices_list->Append(CreateDeviceInfo(i));
+ }
+ }
+}
+
+} // namespace
+
+LocalDiscoveryUIHandler::LocalDiscoveryUIHandler()
+ : is_visible_(false),
+ failed_list_count_(0),
+ succeded_list_count_(0) {
+}
+
+LocalDiscoveryUIHandler::~LocalDiscoveryUIHandler() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetInstance()->GetForProfile(profile);
+ if (signin_manager)
+ signin_manager->RemoveObserver(this);
+ ResetCurrentRegistration();
+ SetIsVisible(false);
+}
+
+// static
+bool LocalDiscoveryUIHandler::GetHasVisible() {
+ return g_num_visible != 0;
+}
+
+void LocalDiscoveryUIHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("start", base::Bind(
+ &LocalDiscoveryUIHandler::HandleStart,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("isVisible", base::Bind(
+ &LocalDiscoveryUIHandler::HandleIsVisible,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("registerDevice", base::Bind(
+ &LocalDiscoveryUIHandler::HandleRegisterDevice,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("cancelRegistration", base::Bind(
+ &LocalDiscoveryUIHandler::HandleCancelRegistration,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "requestDeviceList",
+ base::Bind(&LocalDiscoveryUIHandler::HandleRequestDeviceList,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("openCloudPrintURL", base::Bind(
+ &LocalDiscoveryUIHandler::HandleOpenCloudPrintURL,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("showSyncUI", base::Bind(
+ &LocalDiscoveryUIHandler::HandleShowSyncUI,
+ base::Unretained(this)));
+
+ // Cloud print connector related messages
+#if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
+ web_ui()->RegisterMessageCallback(
+ "showCloudPrintSetupDialog",
+ base::Bind(&LocalDiscoveryUIHandler::ShowCloudPrintSetupDialog,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "disableCloudPrintConnector",
+ base::Bind(&LocalDiscoveryUIHandler::HandleDisableCloudPrintConnector,
+ base::Unretained(this)));
+#endif // defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
+}
+
+void LocalDiscoveryUIHandler::HandleStart(const base::ListValue* args) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+ // If |privet_lister_| is already set, it is a mock used for tests or the
+ // result of a reload.
+ if (!privet_lister_) {
+ service_discovery_client_ = ServiceDiscoverySharedClient::GetInstance();
+ privet_lister_.reset(
+ new cloud_print::PrivetDeviceListerImpl(service_discovery_client_.get(),
+ this));
+ privet_http_factory_ =
+ cloud_print::PrivetHTTPAsynchronousFactory::CreateInstance(
+ profile->GetRequestContext());
+
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetInstance()->GetForProfile(profile);
+ if (signin_manager)
+ signin_manager->AddObserver(this);
+ }
+
+ privet_lister_->Start();
+ privet_lister_->DiscoverNewDevices(false);
+
+#if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
+ StartCloudPrintConnector();
+#endif
+
+ CheckUserLoggedIn();
+}
+
+void LocalDiscoveryUIHandler::HandleIsVisible(const base::ListValue* args) {
+ bool is_visible = false;
+ bool rv = args->GetBoolean(0, &is_visible);
+ DCHECK(rv);
+ SetIsVisible(is_visible);
+}
+
+void LocalDiscoveryUIHandler::HandleRegisterDevice(
+ const base::ListValue* args) {
+ std::string device;
+ bool rv = args->GetString(0, &device);
+ DCHECK(rv);
+
+ DeviceDescriptionMap::iterator it = device_descriptions_.find(device);
+ if (it == device_descriptions_.end()) {
+ OnSetupError();
+ return;
+ }
+
+ if (it->second.version < kCloudDevicesPrivetVersion) {
+ privet_resolution_ = privet_http_factory_->CreatePrivetHTTP(device);
+ privet_resolution_->Start(
+ it->second.address,
+ base::Bind(&LocalDiscoveryUIHandler::StartRegisterHTTP,
+ base::Unretained(this)));
+ } else {
+ OnSetupError();
+ }
+}
+
+void LocalDiscoveryUIHandler::HandleCancelRegistration(
+ const base::ListValue* args) {
+ ResetCurrentRegistration();
+}
+
+void LocalDiscoveryUIHandler::HandleRequestDeviceList(
+ const base::ListValue* args) {
+ failed_list_count_ = 0;
+ succeded_list_count_ = 0;
+ cloud_devices_.clear();
+
+ cloud_print_printer_list_ = CreateApiFlow();
+
+ if (cloud_print_printer_list_) {
+ cloud_print_printer_list_->Start(
+ base::MakeUnique<CloudPrintPrinterList>(this));
+ }
+
+ CheckListingDone();
+}
+
+void LocalDiscoveryUIHandler::HandleOpenCloudPrintURL(
+ const base::ListValue* args) {
+ std::string id;
+ bool rv = args->GetString(0, &id);
+ DCHECK(rv);
+
+ Browser* browser = chrome::FindBrowserWithWebContents(
+ web_ui()->GetWebContents());
+ DCHECK(browser);
+
+ chrome::AddSelectedTabWithURL(browser,
+ cloud_devices::GetCloudPrintManageDeviceURL(id),
+ ui::PAGE_TRANSITION_FROM_API);
+}
+
+void LocalDiscoveryUIHandler::HandleShowSyncUI(
+ const base::ListValue* args) {
+ Browser* browser = chrome::FindBrowserWithWebContents(
+ web_ui()->GetWebContents());
+ DCHECK(browser);
+ chrome::ShowBrowserSignin(
+ browser, signin_metrics::AccessPoint::ACCESS_POINT_DEVICES_PAGE);
+}
+
+void LocalDiscoveryUIHandler::StartRegisterHTTP(
+ std::unique_ptr<cloud_print::PrivetHTTPClient> http_client) {
+ current_http_client_ =
+ cloud_print::PrivetV1HTTPClient::CreateDefault(std::move(http_client));
+
+ if (!current_http_client_) {
+ SendRegisterError();
+ return;
+ }
+
+ current_register_operation_ =
+ current_http_client_->CreateRegisterOperation(GetSyncAccount(), this);
+ current_register_operation_->Start();
+}
+
+void LocalDiscoveryUIHandler::OnPrivetRegisterClaimToken(
+ PrivetRegisterOperation* operation,
+ const std::string& token,
+ const GURL& url) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "local_discovery.onRegistrationConfirmedOnPrinter");
+ if (!base::ContainsKey(device_descriptions_,
+ current_http_client_->GetName())) {
+ SendRegisterError();
+ return;
+ }
+
+ confirm_api_call_flow_ = CreateApiFlow();
+ if (!confirm_api_call_flow_) {
+ SendRegisterError();
+ return;
+ }
+ confirm_api_call_flow_->Start(
+ base::MakeUnique<cloud_print::PrivetConfirmApiCallFlow>(
+ token, base::Bind(&LocalDiscoveryUIHandler::OnConfirmDone,
+ base::Unretained(this))));
+}
+
+void LocalDiscoveryUIHandler::OnPrivetRegisterError(
+ PrivetRegisterOperation* operation,
+ const std::string& action,
+ PrivetRegisterOperation::FailureReason reason,
+ int printer_http_code,
+ const base::DictionaryValue* json) {
+ std::string error;
+ if (reason == PrivetRegisterOperation::FAILURE_JSON_ERROR &&
+ json->GetString(cloud_print::kPrivetKeyError, &error)) {
+ if (error == cloud_print::kPrivetErrorTimeout) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "local_discovery.onRegistrationTimeout");
+ return;
+ }
+ if (error == cloud_print::kPrivetErrorCancel) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "local_discovery.onRegistrationCanceledPrinter");
+ return;
+ }
+ }
+
+ SendRegisterError();
+}
+
+void LocalDiscoveryUIHandler::OnPrivetRegisterDone(
+ PrivetRegisterOperation* operation,
+ const std::string& device_id) {
+ std::string name = operation->GetHTTPClient()->GetName();
+ current_register_operation_.reset();
+ current_http_client_.reset();
+ SendRegisterDone(name);
+}
+
+void LocalDiscoveryUIHandler::OnSetupError() {
+ ResetCurrentRegistration();
+ SendRegisterError();
+}
+
+void LocalDiscoveryUIHandler::OnConfirmDone(GCDApiFlow::Status status) {
+ if (status == GCDApiFlow::SUCCESS) {
+ confirm_api_call_flow_.reset();
+ current_register_operation_->CompleteRegistration();
+ } else {
+ SendRegisterError();
+ }
+}
+
+void LocalDiscoveryUIHandler::DeviceChanged(
+ const std::string& name,
+ const DeviceDescription& description) {
+ device_descriptions_[name] = description;
+
+ base::DictionaryValue info;
+
+ base::Value service_key(kKeyPrefixMDns + name);
+
+ if (description.id.empty()) {
+ info.SetString(kDictionaryKeyServiceName, name);
+ info.SetString(kDictionaryKeyDisplayName, description.name);
+ info.SetString(kDictionaryKeyDescription, description.description);
+ info.SetString(kDictionaryKeyType, description.type);
+ info.SetBoolean(kDictionaryKeyIsWifi, false);
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "local_discovery.onUnregisteredDeviceUpdate", service_key, info);
+ } else {
+ auto null_value = base::MakeUnique<base::Value>();
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "local_discovery.onUnregisteredDeviceUpdate", service_key, *null_value);
+ }
+}
+
+void LocalDiscoveryUIHandler::DeviceRemoved(const std::string& name) {
+ device_descriptions_.erase(name);
+ auto null_value = base::MakeUnique<base::Value>();
+ base::Value name_value(kKeyPrefixMDns + name);
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "local_discovery.onUnregisteredDeviceUpdate", name_value, *null_value);
+}
+
+void LocalDiscoveryUIHandler::DeviceCacheFlushed() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "local_discovery.onDeviceCacheFlushed");
+ privet_lister_->DiscoverNewDevices(false);
+}
+
+void LocalDiscoveryUIHandler::OnDeviceListReady(
+ const CloudPrintPrinterList::DeviceList& devices) {
+ cloud_devices_.insert(cloud_devices_.end(), devices.begin(), devices.end());
+ ++succeded_list_count_;
+ CheckListingDone();
+}
+
+void LocalDiscoveryUIHandler::OnDeviceListUnavailable() {
+ ++failed_list_count_;
+ CheckListingDone();
+}
+
+void LocalDiscoveryUIHandler::GoogleSigninSucceeded(
+ const std::string& account_id,
+ const std::string& username,
+ const std::string& password) {
+ CheckUserLoggedIn();
+}
+
+void LocalDiscoveryUIHandler::GoogleSignedOut(const std::string& account_id,
+ const std::string& username) {
+ CheckUserLoggedIn();
+}
+
+void LocalDiscoveryUIHandler::SendRegisterError() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "local_discovery.onRegistrationFailed");
+}
+
+void LocalDiscoveryUIHandler::SendRegisterDone(
+ const std::string& service_name) {
+ // HACK(noamsml): Generate network traffic so the Windows firewall doesn't
+ // block the printer's announcement.
+ privet_lister_->DiscoverNewDevices(false);
+
+ DeviceDescriptionMap::iterator it = device_descriptions_.find(service_name);
+
+ if (it == device_descriptions_.end()) {
+ // TODO(noamsml): Handle the case where a printer's record is not present at
+ // the end of registration.
+ SendRegisterError();
+ return;
+ }
+
+ const DeviceDescription& device = it->second;
+ base::DictionaryValue device_value;
+
+ device_value.SetString(kDictionaryKeyType, device.type);
+ device_value.SetString(kDictionaryKeyID, device.id);
+ device_value.SetString(kDictionaryKeyDisplayName, device.name);
+ device_value.SetString(kDictionaryKeyDescription, device.description);
+ device_value.SetString(kDictionaryKeyServiceName, service_name);
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "local_discovery.onRegistrationSuccess", device_value);
+}
+
+void LocalDiscoveryUIHandler::SetIsVisible(bool visible) {
+ if (visible == is_visible_)
+ return;
+
+ g_num_visible += visible ? 1 : -1;
+ is_visible_ = visible;
+}
+
+std::string LocalDiscoveryUIHandler::GetSyncAccount() const {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetForProfileIfExists(profile);
+
+ std::string email;
+ if (signin_manager)
+ email = signin_manager->GetAuthenticatedAccountInfo().email;
+ return email;
+}
+
+// TODO(noamsml): Create master object for registration flow.
+void LocalDiscoveryUIHandler::ResetCurrentRegistration() {
+ if (current_register_operation_) {
+ current_register_operation_->Cancel();
+ current_register_operation_.reset();
+ }
+
+ confirm_api_call_flow_.reset();
+ privet_resolution_.reset();
+ current_http_client_.reset();
+}
+
+void LocalDiscoveryUIHandler::CheckUserLoggedIn() {
+ base::Value logged_in_value(!GetSyncAccount().empty());
+ base::Value is_supervised_value(IsUserSupervisedOrOffTheRecord());
+ web_ui()->CallJavascriptFunctionUnsafe("local_discovery.setUserLoggedIn",
+ logged_in_value, is_supervised_value);
+}
+
+void LocalDiscoveryUIHandler::CheckListingDone() {
+ int started = cloud_print_printer_list_ ? 1 : 0;
+ if (started > failed_list_count_ + succeded_list_count_)
+ return;
+
+ if (succeded_list_count_ <= 0) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "local_discovery.onCloudDeviceListUnavailable");
+ return;
+ }
+
+ base::ListValue devices_list;
+ std::set<std::string> local_ids;
+
+ for (const auto& it : device_descriptions_)
+ local_ids.insert(it.second.id);
+
+ ReadDevicesList(cloud_devices_, local_ids, &devices_list);
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "local_discovery.onCloudDeviceListAvailable", devices_list);
+ cloud_print_printer_list_.reset();
+}
+
+std::unique_ptr<GCDApiFlow> LocalDiscoveryUIHandler::CreateApiFlow() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (!profile)
+ return std::unique_ptr<GCDApiFlow>();
+
+ ProfileOAuth2TokenService* token_service =
+ ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
+ if (!token_service)
+ return std::unique_ptr<GCDApiFlow>();
+
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetInstance()->GetForProfile(profile);
+ if (!signin_manager)
+ return std::unique_ptr<GCDApiFlow>();
+
+ return GCDApiFlow::Create(profile->GetRequestContext(),
+ token_service,
+ signin_manager->GetAuthenticatedAccountId());
+}
+
+bool LocalDiscoveryUIHandler::IsUserSupervisedOrOffTheRecord() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ return profile->IsSupervised() || profile->IsOffTheRecord();
+}
+
+#if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
+void LocalDiscoveryUIHandler::StartCloudPrintConnector() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+ base::Closure cloud_print_callback = base::Bind(
+ &LocalDiscoveryUIHandler::OnCloudPrintPrefsChanged,
+ base::Unretained(this));
+
+ if (cloud_print_connector_email_.GetPrefName().empty()) {
+ cloud_print_connector_email_.Init(
+ prefs::kCloudPrintEmail, profile->GetPrefs(), cloud_print_callback);
+ }
+
+ if (cloud_print_connector_enabled_.GetPrefName().empty()) {
+ cloud_print_connector_enabled_.Init(
+ prefs::kCloudPrintProxyEnabled, profile->GetPrefs(),
+ cloud_print_callback);
+ }
+
+ SetupCloudPrintConnectorSection();
+ RefreshCloudPrintStatusFromService();
+}
+
+void LocalDiscoveryUIHandler::OnCloudPrintPrefsChanged() {
+ SetupCloudPrintConnectorSection();
+}
+
+void LocalDiscoveryUIHandler::ShowCloudPrintSetupDialog(
+ const base::ListValue* args) {
+ base::RecordAction(base::UserMetricsAction("Options_EnableCloudPrintProxy"));
+ // Open the connector enable page in the current tab.
+ content::OpenURLParams params(cloud_devices::GetCloudPrintEnableURL(
+ GetCloudPrintProxyService()->proxy_id()),
+ content::Referrer(),
+ WindowOpenDisposition::CURRENT_TAB,
+ ui::PAGE_TRANSITION_LINK, false);
+ web_ui()->GetWebContents()->OpenURL(params);
+}
+
+void LocalDiscoveryUIHandler::HandleDisableCloudPrintConnector(
+ const base::ListValue* args) {
+ base::RecordAction(base::UserMetricsAction("Options_DisableCloudPrintProxy"));
+ GetCloudPrintProxyService()->DisableForUser();
+}
+
+void LocalDiscoveryUIHandler::SetupCloudPrintConnectorSection() {
+ bool cloud_print_connector_allowed =
+ !cloud_print_connector_enabled_.IsManaged() ||
+ cloud_print_connector_enabled_.GetValue();
+ base::Value allowed(cloud_print_connector_allowed);
+
+ std::string email;
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (profile->GetPrefs()->HasPrefPath(prefs::kCloudPrintEmail) &&
+ cloud_print_connector_allowed) {
+ email = profile->GetPrefs()->GetString(prefs::kCloudPrintEmail);
+ }
+ base::Value disabled(email.empty());
+
+ base::string16 label_str;
+ if (email.empty()) {
+ label_str = l10n_util::GetStringFUTF16(
+ IDS_LOCAL_DISCOVERY_CLOUD_PRINT_CONNECTOR_DISABLED_LABEL,
+ l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT));
+ } else {
+ label_str = l10n_util::GetStringFUTF16(
+ IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_ENABLED_LABEL,
+ l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT),
+ base::UTF8ToUTF16(email));
+ }
+ base::Value label(label_str);
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "local_discovery.setupCloudPrintConnectorSection", disabled, label,
+ allowed);
+}
+
+void LocalDiscoveryUIHandler::RefreshCloudPrintStatusFromService() {
+ auto* service = GetCloudPrintProxyService();
+ if (service)
+ service->RefreshStatusFromService();
+}
+
+CloudPrintProxyService* LocalDiscoveryUIHandler::GetCloudPrintProxyService() {
+ return CloudPrintProxyServiceFactory::GetForProfile(
+ Profile::FromWebUI(web_ui()));
+}
+#endif // defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
+
+} // namespace local_discovery
diff --git a/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h b/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h
new file mode 100644
index 00000000000..c1afc0225f8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h
@@ -0,0 +1,210 @@
+// 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_LOCAL_DISCOVERY_LOCAL_DISCOVERY_UI_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_LOCAL_DISCOVERY_LOCAL_DISCOVERY_UI_HANDLER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "chrome/browser/printing/cloud_print/cloud_print_printer_list.h"
+#include "chrome/browser/printing/cloud_print/privet_device_lister.h"
+#include "chrome/browser/printing/cloud_print/privet_http.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "printing/features/features.h"
+
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW) && !defined(OS_CHROMEOS)
+#define CLOUD_PRINT_CONNECTOR_UI_AVAILABLE
+#endif
+
+class CloudPrintProxyService;
+
+namespace cloud_print {
+class PrivetHTTPAsynchronousFactory;
+class PrivetHTTPResolution;
+class PrivetV1HTTPClient;
+}
+
+// TODO(noamsml): Factor out full registration flow into single class
+namespace local_discovery {
+
+class ServiceDiscoverySharedClient;
+
+// UI Handler for chrome://devices/
+// It listens to local discovery notifications and passes those notifications
+// into the Javascript to update the page.
+class LocalDiscoveryUIHandler
+ : public content::WebUIMessageHandler,
+ public cloud_print::PrivetRegisterOperation::Delegate,
+ public cloud_print::PrivetDeviceLister::Delegate,
+ public cloud_print::CloudPrintPrinterList::Delegate,
+ public SigninManagerBase::Observer {
+ public:
+ LocalDiscoveryUIHandler();
+ ~LocalDiscoveryUIHandler() override;
+
+ static bool GetHasVisible();
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+ // PrivetRegisterOperation::Delegate implementation.
+ void OnPrivetRegisterClaimToken(
+ cloud_print::PrivetRegisterOperation* operation,
+ const std::string& token,
+ const GURL& url) override;
+ void OnPrivetRegisterError(
+ cloud_print::PrivetRegisterOperation* operation,
+ const std::string& action,
+ cloud_print::PrivetRegisterOperation::FailureReason reason,
+ int printer_http_code,
+ const base::DictionaryValue* json) override;
+ void OnPrivetRegisterDone(cloud_print::PrivetRegisterOperation* operation,
+ const std::string& device_id) override;
+
+ // PrivetDeviceLister::Delegate implementation.
+ void DeviceChanged(
+ const std::string& name,
+ const cloud_print::DeviceDescription& description) override;
+ void DeviceRemoved(const std::string& name) override;
+ void DeviceCacheFlushed() override;
+
+ // CloudPrintPrinterList::Delegate implementation.
+ void OnDeviceListReady(
+ const cloud_print::CloudPrintPrinterList::DeviceList& devices) override;
+ void OnDeviceListUnavailable() override;
+
+ // SigninManagerBase::Observer implementation.
+ void GoogleSigninSucceeded(const std::string& account_id,
+ const std::string& username,
+ const std::string& password) override;
+ void GoogleSignedOut(const std::string& account_id,
+ const std::string& username) override;
+
+ private:
+ using DeviceDescriptionMap =
+ std::map<std::string, cloud_print::DeviceDescription>;
+ using ResultCallback = base::Callback<void(bool result)>;
+
+ // Message handlers:
+ // For when the page is ready to receive device notifications.
+ void HandleStart(const base::ListValue* args);
+
+ // For when a visibility change occurs.
+ void HandleIsVisible(const base::ListValue* args);
+
+ // For when a user choice is made.
+ void HandleRegisterDevice(const base::ListValue* args);
+
+ // For when a cancellation is made.
+ void HandleCancelRegistration(const base::ListValue* args);
+
+ // For requesting the device list.
+ void HandleRequestDeviceList(const base::ListValue* args);
+
+ // For opening URLs (relative to the Google Cloud Print base URL) in a new
+ // tab.
+ void HandleOpenCloudPrintURL(const base::ListValue* args);
+
+ // For showing sync login UI.
+ void HandleShowSyncUI(const base::ListValue* args);
+
+ // For when the IP address of the printer has been resolved for registration.
+ void StartRegisterHTTP(
+ std::unique_ptr<cloud_print::PrivetHTTPClient> http_client);
+
+ // For when the confirm operation on the cloudprint server has finished
+ // executing.
+ void OnConfirmDone(cloud_print::GCDApiFlow::Status status);
+
+ // Signal to the web interface an error has ocurred while registering.
+ void SendRegisterError();
+
+ // Singal to the web interface that registration has finished.
+ void SendRegisterDone(const std::string& service_name);
+
+ // Set the visibility of the page.
+ void SetIsVisible(bool visible);
+
+ // Get the sync account email.
+ std::string GetSyncAccount() const;
+
+ // Reset and cancel the current registration.
+ void ResetCurrentRegistration();
+
+ std::unique_ptr<cloud_print::GCDApiFlow> CreateApiFlow();
+ void OnSetupError();
+
+ // Announcement hasn't been sent for a certain time after registration
+ // finished. Consider it failed.
+ // TODO(noamsml): Re-resolve service first.
+ void OnAnnouncementTimeoutReached();
+
+ void CheckUserLoggedIn();
+
+ void CheckListingDone();
+
+ bool IsUserSupervisedOrOffTheRecord();
+
+#if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
+ void StartCloudPrintConnector();
+ void OnCloudPrintPrefsChanged();
+ void ShowCloudPrintSetupDialog(const base::ListValue* args);
+ void HandleDisableCloudPrintConnector(const base::ListValue* args);
+ void SetupCloudPrintConnectorSection();
+ void RefreshCloudPrintStatusFromService();
+ CloudPrintProxyService* GetCloudPrintProxyService();
+#endif
+
+ // A map of current device descriptions provided by the PrivetDeviceLister.
+ DeviceDescriptionMap device_descriptions_;
+
+ // The service discovery client used listen for devices on the local network.
+ scoped_refptr<ServiceDiscoverySharedClient> service_discovery_client_;
+
+ // A factory for creating the privet HTTP Client.
+ std::unique_ptr<cloud_print::PrivetHTTPAsynchronousFactory>
+ privet_http_factory_;
+
+ // An object representing the resolution process for the privet_http_factory.
+ std::unique_ptr<cloud_print::PrivetHTTPResolution> privet_resolution_;
+
+ // The current HTTP client (used for the current operation).
+ std::unique_ptr<cloud_print::PrivetV1HTTPClient> current_http_client_;
+
+ // The current register operation. Only one allowed at any time.
+ std::unique_ptr<cloud_print::PrivetRegisterOperation>
+ current_register_operation_;
+
+ // The current confirm call used during the registration flow.
+ std::unique_ptr<cloud_print::GCDApiFlow> confirm_api_call_flow_;
+
+ // The device lister used to list devices on the local network.
+ std::unique_ptr<cloud_print::PrivetDeviceLister> privet_lister_;
+
+ // Whether or not the page is marked as visible.
+ bool is_visible_;
+
+ // List of printers from cloud print.
+ std::unique_ptr<cloud_print::GCDApiFlow> cloud_print_printer_list_;
+ std::vector<cloud_print::CloudPrintPrinterList::Device> cloud_devices_;
+ int failed_list_count_;
+ int succeded_list_count_;
+
+#if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
+ StringPrefMember cloud_print_connector_email_;
+ BooleanPrefMember cloud_print_connector_enabled_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(LocalDiscoveryUIHandler);
+};
+
+#undef CLOUD_PRINT_CONNECTOR_UI_AVAILABLE
+
+} // namespace local_discovery
+
+#endif // CHROME_BROWSER_UI_WEBUI_LOCAL_DISCOVERY_LOCAL_DISCOVERY_UI_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/local_state/local_state_ui.cc b/chromium/chrome/browser/ui/webui/local_state/local_state_ui.cc
new file mode 100644
index 00000000000..1676ea7ab13
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/local_state/local_state_ui.cc
@@ -0,0 +1,125 @@
+// 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/ui/webui/local_state/local_state_ui.h"
+
+#include "base/json/json_string_value_serializer.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace {
+
+// On ChromeOS, the local state file contains some information about other
+// user accounts which we don't want to expose to other users. Use a whitelist
+// to only show variations and UMA related fields which don't contain PII.
+#if defined(OS_CHROMEOS)
+#define ENABLE_FILTERING true
+#else
+#define ENABLE_FILTERING false
+#endif // defined(OS_CHROMEOS)
+
+// UI Handler for chrome://local-state. Displays the Local State file as JSON.
+class LocalStateUIHandler : public content::WebUIMessageHandler {
+ public:
+ LocalStateUIHandler();
+ ~LocalStateUIHandler() override;
+
+ // content::WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ private:
+ // Called from JS when the page has loaded. Serializes local state prefs and
+ // sends them to the page.
+ void HandleRequestJson(const base::ListValue* args);
+
+ DISALLOW_COPY_AND_ASSIGN(LocalStateUIHandler);
+};
+
+LocalStateUIHandler::LocalStateUIHandler() {
+}
+
+LocalStateUIHandler::~LocalStateUIHandler() {
+}
+
+void LocalStateUIHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "requestJson", base::Bind(&LocalStateUIHandler::HandleRequestJson,
+ base::Unretained(this)));
+}
+
+void LocalStateUIHandler::HandleRequestJson(const base::ListValue* args) {
+ std::unique_ptr<base::DictionaryValue> local_state_values(
+ g_browser_process->local_state()->GetPreferenceValues(
+ PrefService::EXCLUDE_DEFAULTS));
+ if (ENABLE_FILTERING) {
+ std::vector<std::string> whitelisted_prefixes = {
+ "variations", "user_experience_metrics", "uninstall_metrics"};
+ internal::FilterPrefs(whitelisted_prefixes, local_state_values.get());
+ }
+ std::string json;
+ JSONStringValueSerializer serializer(&json);
+ serializer.set_pretty_print(true);
+ bool result = serializer.Serialize(*local_state_values);
+ if (!result)
+ json = "Error loading Local State file.";
+
+ web_ui()->CallJavascriptFunctionUnsafe("localState.setLocalState",
+ base::Value(json));
+}
+
+// Returns true if |pref_name| starts with one of the |valid_prefixes|.
+bool HasValidPrefix(const std::string& pref_name,
+ const std::vector<std::string> valid_prefixes) {
+ for (const std::string& prefix : valid_prefixes) {
+ if (base::StartsWith(pref_name, prefix, base::CompareCase::SENSITIVE))
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+namespace internal {
+
+void FilterPrefs(const std::vector<std::string>& valid_prefixes,
+ base::DictionaryValue* prefs) {
+ std::vector<std::string> prefs_to_remove;
+ for (base::DictionaryValue::Iterator it(*prefs); !it.IsAtEnd();
+ it.Advance()) {
+ if (!HasValidPrefix(it.key(), valid_prefixes))
+ prefs_to_remove.push_back(it.key());
+ }
+ for (const std::string& pref_to_remove : prefs_to_remove) {
+ std::unique_ptr<base::Value> removed_value;
+ bool successfully_removed = prefs->Remove(pref_to_remove, &removed_value);
+ DCHECK(successfully_removed);
+ }
+}
+
+} // namespace internal
+
+LocalStateUI::LocalStateUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ // Set up the chrome://local-state source.
+ content::WebUIDataSource* html_source =
+ content::WebUIDataSource::Create(chrome::kChromeUILocalStateHost);
+ html_source->SetDefaultResource(IDR_LOCAL_STATE_HTML);
+ html_source->AddResourcePath("local_state.js", IDR_LOCAL_STATE_JS);
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), html_source);
+ web_ui->AddMessageHandler(base::MakeUnique<LocalStateUIHandler>());
+}
+
+LocalStateUI::~LocalStateUI() {
+}
diff --git a/chromium/chrome/browser/ui/webui/local_state/local_state_ui.h b/chromium/chrome/browser/ui/webui/local_state/local_state_ui.h
new file mode 100644
index 00000000000..0a4c345c426
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/local_state/local_state_ui.h
@@ -0,0 +1,38 @@
+// 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_UI_WEBUI_LOCAL_STATE_LOCAL_STATE_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_LOCAL_STATE_LOCAL_STATE_UI_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+// Namespace for exposing the method for unit tests.
+namespace internal {
+
+// Removes elements from |prefs| where the key does not match any of the
+// prefixes in |valid_prefixes|.
+void FilterPrefs(const std::vector<std::string>& valid_prefixes,
+ base::DictionaryValue* prefs);
+
+} // namespace internal
+
+// Controller for chrome://local-state/ page.
+class LocalStateUI : public content::WebUIController {
+ public:
+ explicit LocalStateUI(content::WebUI* web_ui);
+ ~LocalStateUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LocalStateUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_LOCAL_STATE_LOCAL_STATE_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/local_state/local_state_ui_unittest.cc b/chromium/chrome/browser/ui/webui/local_state/local_state_ui_unittest.cc
new file mode 100644
index 00000000000..0715a734ad2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/local_state/local_state_ui_unittest.cc
@@ -0,0 +1,37 @@
+// 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 "chrome/browser/ui/webui/local_state/local_state_ui.h"
+
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(LocalStateUiTest, FilterPrefs) {
+ std::vector<std::string> prefixes = {"foo", "bar", "baz"};
+
+ std::vector<std::string> invalid_pref_keys = {"fo", "ar", "afoo"};
+ std::vector<std::string> valid_pref_keys = {"foo", "foom", "bar.stuff"};
+
+ std::vector<std::string> all_pref_keys = invalid_pref_keys;
+ all_pref_keys.insert(all_pref_keys.end(), valid_pref_keys.begin(),
+ valid_pref_keys.end());
+
+ base::DictionaryValue prefs;
+ for (const std::string& key : all_pref_keys) {
+ prefs.SetString(key, key + "_value");
+ }
+
+ internal::FilterPrefs(prefixes, &prefs);
+
+ for (const std::string& invalid_key : invalid_pref_keys) {
+ std::string value;
+ EXPECT_FALSE(prefs.GetString(invalid_key, &value));
+ }
+
+ for (const std::string& valid_key : valid_pref_keys) {
+ std::string value;
+ EXPECT_TRUE(prefs.GetString(valid_key, &value));
+ EXPECT_EQ(valid_key + "_value", value);
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/log_web_ui_url.cc b/chromium/chrome/browser/ui/webui/log_web_ui_url.cc
new file mode 100644
index 00000000000..fe30ba27e63
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/log_web_ui_url.cc
@@ -0,0 +1,44 @@
+// 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.
+
+#include "chrome/browser/ui/webui/log_web_ui_url.h"
+
+#include <stdint.h>
+
+#include "base/hash.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_macros.h"
+#include "chrome/common/url_constants.h"
+#include "content/public/common/url_constants.h"
+#include "extensions/features/features.h"
+#include "url/gurl.h"
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "chrome/common/extensions/extension_constants.h"
+#include "extensions/common/constants.h"
+#endif
+
+namespace webui {
+
+const char kWebUICreatedForUrl[] = "WebUI.CreatedForUrl";
+
+bool LogWebUIUrl(const GURL& web_ui_url) {
+ bool should_log = web_ui_url.SchemeIs(content::kChromeUIScheme) ||
+ web_ui_url.SchemeIs(content::kChromeDevToolsScheme);
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ if (web_ui_url.SchemeIs(extensions::kExtensionScheme))
+ should_log = web_ui_url.host() == extension_misc::kBookmarkManagerId;
+#endif
+
+ if (should_log) {
+ uint32_t hash = base::Hash(web_ui_url.GetOrigin().spec());
+ UMA_HISTOGRAM_SPARSE_SLOWLY(kWebUICreatedForUrl,
+ static_cast<base::HistogramBase::Sample>(hash));
+ }
+
+ return should_log;
+}
+
+} // namespace webui
diff --git a/chromium/chrome/browser/ui/webui/log_web_ui_url.h b/chromium/chrome/browser/ui/webui/log_web_ui_url.h
new file mode 100644
index 00000000000..5c2650a1d21
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/log_web_ui_url.h
@@ -0,0 +1,22 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_LOG_WEB_UI_URL_H_
+#define CHROME_BROWSER_UI_WEBUI_LOG_WEB_UI_URL_H_
+
+class GURL;
+
+namespace webui {
+
+// Name of histogram that WebUI URLs are logged to.
+extern const char kWebUICreatedForUrl[];
+
+// Called when WebUI objects are created to get aggregate usage data (i.e. is
+// chrome://history used more than chrome://help?). Only internal (e.g.
+// chrome://) URLs are logged. Returns whether the URL was actually logged.
+bool LogWebUIUrl(const GURL& web_ui_url);
+
+} // namespace webui
+
+#endif // CHROME_BROWSER_UI_WEBUI_LOG_WEB_UI_URL_H_
diff --git a/chromium/chrome/browser/ui/webui/log_web_ui_url_browsertest.cc b/chromium/chrome/browser/ui/webui/log_web_ui_url_browsertest.cc
new file mode 100644
index 00000000000..07c45f38eb6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/log_web_ui_url_browsertest.cc
@@ -0,0 +1,121 @@
+// 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.
+
+#include "chrome/browser/ui/webui/log_web_ui_url.h"
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/hash.h"
+#include "base/macros.h"
+#include "base/test/histogram_tester.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/common/chrome_features.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.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/common/url_constants.h"
+#include "content/public/test/browser_test_utils.h"
+#include "extensions/features/features.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+using base::Bucket;
+using testing::ElementsAre;
+
+namespace webui {
+
+class LogWebUIUrlTest : public InProcessBrowserTest {
+ public:
+ LogWebUIUrlTest() {}
+ ~LogWebUIUrlTest() override {}
+
+ std::vector<Bucket> GetSamples() {
+ return histogram_tester_.GetAllSamples(webui::kWebUICreatedForUrl);
+ }
+
+ void SetUpOnMainThread() override {
+ // Disable MD Settings to test non-MD settings page.
+ scoped_feature_list_.InitAndDisableFeature(
+ features::kMaterialDesignSettings);
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+ base::HistogramTester histogram_tester_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogWebUIUrlTest);
+};
+
+IN_PROC_BROWSER_TEST_F(LogWebUIUrlTest, TestSettingsFrame) {
+ GURL settings_frame_url(chrome::kChromeUISettingsFrameURL);
+
+ ui_test_utils::NavigateToURL(browser(), settings_frame_url);
+
+ uint32_t settings_frame_url_hash = base::Hash(settings_frame_url.spec());
+ EXPECT_THAT(GetSamples(), ElementsAre(Bucket(settings_frame_url_hash, 1)));
+
+ chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
+
+ EXPECT_THAT(GetSamples(), ElementsAre(Bucket(settings_frame_url_hash, 2)));
+}
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+IN_PROC_BROWSER_TEST_F(LogWebUIUrlTest, TestExtensionsPage) {
+ content::WebContents* tab =
+ browser()->tab_strip_model()->GetActiveWebContents();
+
+ base::string16 extension_title =
+ l10n_util::GetStringUTF16(IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE);
+
+ {
+ content::TitleWatcher title_watcher(tab, extension_title);
+ ui_test_utils::NavigateToURL(browser(),
+ GURL(chrome::kChromeUIExtensionsURL));
+ ASSERT_EQ(extension_title, title_watcher.WaitAndGetTitle());
+ }
+
+ std::string scheme(content::kChromeUIScheme);
+ GURL uber_url(scheme + "://" + chrome::kChromeUIUberHost);
+ uint32_t uber_url_hash = base::Hash(uber_url.spec());
+
+ GURL uber_frame_url(chrome::kChromeUIUberFrameURL);
+ uint32_t uber_frame_url_hash = base::Hash(uber_frame_url.spec());
+
+ GURL extensions_frame_url(chrome::kChromeUIExtensionsFrameURL);
+ uint32_t extensions_frame_url_hash = base::Hash(extensions_frame_url.spec());
+
+ EXPECT_THAT(GetSamples(), ElementsAre(Bucket(extensions_frame_url_hash, 1),
+ Bucket(uber_frame_url_hash, 1),
+ Bucket(uber_url_hash, 1)));
+
+ {
+ // Pretend a user clicked on "Settings".
+ base::string16 settings_title =
+ l10n_util::GetStringUTF16(IDS_SETTINGS_SETTINGS);
+ content::TitleWatcher title_watcher(tab, settings_title);
+ std::string javascript =
+ "uber.invokeMethodOnWindow(window, 'showPage', {pageId: 'settings'})";
+ ASSERT_TRUE(content::ExecuteScript(tab, javascript));
+ ASSERT_EQ(settings_title, title_watcher.WaitAndGetTitle());
+ }
+
+ GURL settings_frame_url(chrome::kChromeUISettingsFrameURL);
+ uint32_t settings_frame_url_hash = base::Hash(settings_frame_url.spec());
+
+ EXPECT_THAT(GetSamples(), ElementsAre(Bucket(extensions_frame_url_hash, 1),
+ Bucket(settings_frame_url_hash, 1),
+ Bucket(uber_frame_url_hash, 1),
+ Bucket(uber_url_hash, 1)));
+}
+#endif
+
+} // namespace webui
diff --git a/chromium/chrome/browser/ui/webui/log_web_ui_url_unittest.cc b/chromium/chrome/browser/ui/webui/log_web_ui_url_unittest.cc
new file mode 100644
index 00000000000..76a735fc102
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/log_web_ui_url_unittest.cc
@@ -0,0 +1,37 @@
+// 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.
+
+#include "chrome/browser/ui/webui/log_web_ui_url.h"
+
+#include "extensions/features/features.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+TEST(LogWebUIUrlTest, ValidUrls) {
+ // Typical WebUI page.
+ EXPECT_TRUE(webui::LogWebUIUrl(GURL("chrome://downloads")));
+
+ // WebUI page with a subpage.
+ EXPECT_TRUE(webui::LogWebUIUrl(GURL("chrome://settings/clearBrowserData")));
+
+ // Developer tools scheme.
+ EXPECT_TRUE(webui::LogWebUIUrl(GURL("chrome-devtools://devtools")));
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ // Bookmarks Manager (the only currently allowed extension).
+ EXPECT_TRUE(webui::LogWebUIUrl(GURL(
+ "chrome-extension://eemcgdkfndhakfknompkggombfjjjeno")));
+#endif
+}
+
+TEST(LogWebUIUrlTest, InvalidUrls) {
+ // HTTP/HTTPS/FTP/etc. schemes should be ignored.
+ EXPECT_FALSE(webui::LogWebUIUrl(GURL("http://google.com?q=pii")));
+ EXPECT_FALSE(webui::LogWebUIUrl(GURL("https://facebook.com")));
+ EXPECT_FALSE(webui::LogWebUIUrl(GURL("ftp://ftp.mysite.com")));
+
+ // Extensions other than the Bookmarks Manager should also be ignored.
+ EXPECT_FALSE(webui::LogWebUIUrl(GURL(
+ "chrome-extension://mfehgcgbbipciphmccgaenjidiccnmng")));
+}
diff --git a/chromium/chrome/browser/ui/webui/md_bookmarks/OWNERS b/chromium/chrome/browser/ui/webui/md_bookmarks/OWNERS
new file mode 100644
index 00000000000..d0663e00c74
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_bookmarks/OWNERS
@@ -0,0 +1,5 @@
+calamity@chromium.org
+dbeam@chromium.org
+tsergeant@chromium.org
+
+# COMPONENT: UI>Browser>Bookmarks
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
new file mode 100644
index 00000000000..521277b8b1a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc
@@ -0,0 +1,152 @@
+// Copyright (c) 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 "chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.h"
+
+#include <algorithm>
+
+#include "base/strings/string16.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.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"
+
+namespace {
+
+void AddLocalizedString(content::WebUIDataSource* source,
+ const std::string& message,
+ int id) {
+ base::string16 str = l10n_util::GetStringUTF16(id);
+ str.erase(std::remove(str.begin(), str.end(), '&'), str.end());
+ source->AddString(message, str);
+}
+
+content::WebUIDataSource* CreateMdBookmarksUIHTMLSource(Profile* profile) {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIBookmarksHost);
+
+ // Localized strings (alphabetical order).
+ AddLocalizedString(source, "addBookmarkTitle",
+ IDS_MD_BOOKMARK_MANAGER_ADD_BOOKMARK_TITLE);
+ AddLocalizedString(source, "addFolderTitle",
+ IDS_MD_BOOKMARK_MANAGER_ADD_FOLDER_TITLE);
+ AddLocalizedString(source, "cancel", IDS_CANCEL);
+ AddLocalizedString(source, "clearSearch",
+ IDS_MD_BOOKMARK_MANAGER_CLEAR_SEARCH);
+ AddLocalizedString(source, "delete", IDS_DELETE);
+ AddLocalizedString(source, "editBookmarkTitle", IDS_BOOKMARK_EDITOR_TITLE);
+ AddLocalizedString(source, "editDialogInvalidUrl",
+ IDS_BOOKMARK_MANAGER_INVALID_URL);
+ AddLocalizedString(source, "editDialogNameInput",
+ IDS_BOOKMARK_MANAGER_NAME_INPUT_PLACE_HOLDER);
+ AddLocalizedString(source, "editDialogUrlInput",
+ IDS_BOOKMARK_MANAGER_URL_INPUT_PLACE_HOLDER);
+ AddLocalizedString(source, "emptyList",
+ IDS_MD_BOOKMARK_MANAGER_EMPTY_LIST);
+ AddLocalizedString(source, "itemsSelected",
+ IDS_MD_BOOKMARK_MANAGER_ITEMS_SELECTED);
+ AddLocalizedString(source, "menuAddBookmark",
+ IDS_MD_BOOKMARK_MANAGER_MENU_ADD_BOOKMARK);
+ AddLocalizedString(source, "menuAddFolder",
+ IDS_MD_BOOKMARK_MANAGER_MENU_ADD_FOLDER);
+ AddLocalizedString(source, "menuCopyURL",
+ IDS_MD_BOOKMARK_MANAGER_MENU_COPY_URL);
+ AddLocalizedString(source, "menuDelete", IDS_DELETE);
+ AddLocalizedString(source, "menuEdit", IDS_EDIT);
+ AddLocalizedString(source, "menuExport", IDS_MD_BOOKMARK_MANAGER_MENU_EXPORT);
+ AddLocalizedString(source, "menuImport", IDS_MD_BOOKMARK_MANAGER_MENU_IMPORT);
+ // TODO(tsergeant): These are not the exact strings specified by UI. Reconcile
+ // the differences between these strings and the work in crbug.com/708815.
+ AddLocalizedString(source, "menuOpenAllNewTab", IDS_BOOKMARK_BAR_OPEN_ALL);
+ AddLocalizedString(source, "menuOpenAllNewWindow",
+ IDS_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW);
+ AddLocalizedString(source, "menuOpenAllIncognito",
+ IDS_BOOKMARK_BAR_OPEN_ALL_INCOGNITO);
+ AddLocalizedString(source, "menuOpenNewTab",
+ IDS_BOOKMARK_BAR_OPEN_IN_NEW_TAB);
+ AddLocalizedString(source, "menuOpenNewWindow",
+ IDS_BOOKMARK_BAR_OPEN_IN_NEW_WINDOW);
+ AddLocalizedString(source, "menuOpenIncognito",
+ IDS_BOOKMARK_BAR_OPEN_INCOGNITO);
+ AddLocalizedString(source, "menuRename", IDS_MD_BOOKMARK_MANAGER_MENU_RENAME);
+ AddLocalizedString(source, "menuSort", IDS_MD_BOOKMARK_MANAGER_MENU_SORT);
+ AddLocalizedString(source, "noSearchResults",
+ IDS_MD_BOOKMARK_MANAGER_NO_SEARCH_RESULTS);
+ AddLocalizedString(source, "renameFolderTitle",
+ IDS_MD_BOOKMARK_MANAGER_FOLDER_RENAME_TITLE);
+ AddLocalizedString(source, "searchPrompt",
+ IDS_BOOKMARK_MANAGER_SEARCH_BUTTON);
+ AddLocalizedString(source, "saveEdit", IDS_SAVE);
+ AddLocalizedString(source, "title", IDS_MD_BOOKMARK_MANAGER_TITLE);
+
+ // Resources.
+ source->AddResourcePath("actions.html", IDR_MD_BOOKMARKS_ACTIONS_HTML);
+ source->AddResourcePath("actions.js", IDR_MD_BOOKMARKS_ACTIONS_JS);
+ source->AddResourcePath("api_listener.html",
+ IDR_MD_BOOKMARKS_API_LISTENER_HTML);
+ source->AddResourcePath("api_listener.js", IDR_MD_BOOKMARKS_API_LISTENER_JS);
+ source->AddResourcePath("app.html", IDR_MD_BOOKMARKS_APP_HTML);
+ source->AddResourcePath("app.js", IDR_MD_BOOKMARKS_APP_JS);
+ source->AddResourcePath("command_manager.html",
+ IDR_MD_BOOKMARKS_COMMAND_MANAGER_HTML);
+ source->AddResourcePath("command_manager.js",
+ IDR_MD_BOOKMARKS_COMMAND_MANAGER_JS);
+ source->AddResourcePath("constants.html", IDR_MD_BOOKMARKS_CONSTANTS_HTML);
+ source->AddResourcePath("constants.js", IDR_MD_BOOKMARKS_CONSTANTS_JS);
+ source->AddResourcePath("dnd_manager.html",
+ IDR_MD_BOOKMARKS_DND_MANAGER_HTML);
+ source->AddResourcePath("dnd_manager.js", IDR_MD_BOOKMARKS_DND_MANAGER_JS);
+ source->AddResourcePath("edit_dialog.html",
+ IDR_MD_BOOKMARKS_EDIT_DIALOG_HTML);
+ source->AddResourcePath("edit_dialog.js", IDR_MD_BOOKMARKS_EDIT_DIALOG_JS);
+ source->AddResourcePath("folder_node.html",
+ IDR_MD_BOOKMARKS_FOLDER_NODE_HTML);
+ source->AddResourcePath("folder_node.js",
+ IDR_MD_BOOKMARKS_FOLDER_NODE_JS);
+ source->AddResourcePath("icons.html", IDR_MD_BOOKMARKS_ICONS_HTML);
+ source->AddResourcePath("item.html", IDR_MD_BOOKMARKS_ITEM_HTML);
+ source->AddResourcePath("item.js", IDR_MD_BOOKMARKS_ITEM_JS);
+ source->AddResourcePath("list.html", IDR_MD_BOOKMARKS_LIST_HTML);
+ source->AddResourcePath("list.js", IDR_MD_BOOKMARKS_LIST_JS);
+ source->AddResourcePath("reducers.html", IDR_MD_BOOKMARKS_REDUCERS_HTML);
+ source->AddResourcePath("reducers.js", IDR_MD_BOOKMARKS_REDUCERS_JS);
+ source->AddResourcePath("router.html", IDR_MD_BOOKMARKS_ROUTER_HTML);
+ source->AddResourcePath("router.js", IDR_MD_BOOKMARKS_ROUTER_JS);
+ source->AddResourcePath("shared_style.html",
+ IDR_MD_BOOKMARKS_SHARED_STYLE_HTML);
+ source->AddResourcePath("shared_vars.html",
+ IDR_MD_BOOKMARKS_SHARED_VARS_HTML);
+ source->AddResourcePath("store.html", IDR_MD_BOOKMARKS_STORE_HTML);
+ source->AddResourcePath("store.js", IDR_MD_BOOKMARKS_STORE_JS);
+ source->AddResourcePath("store_client.html",
+ IDR_MD_BOOKMARKS_STORE_CLIENT_HTML);
+ source->AddResourcePath("store_client.js", IDR_MD_BOOKMARKS_STORE_CLIENT_JS);
+ source->AddResourcePath("toolbar.html", IDR_MD_BOOKMARKS_TOOLBAR_HTML);
+ source->AddResourcePath("toolbar.js", IDR_MD_BOOKMARKS_TOOLBAR_JS);
+ source->AddResourcePath("util.html", IDR_MD_BOOKMARKS_UTIL_HTML);
+ source->AddResourcePath("util.js", IDR_MD_BOOKMARKS_UTIL_JS);
+ source->SetDefaultResource(IDR_MD_BOOKMARKS_BOOKMARKS_HTML);
+ source->SetJsonPath("strings.js");
+
+ return source;
+}
+
+} // namespace
+
+MdBookmarksUI::MdBookmarksUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ // Set up the chrome://bookmarks/ source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile,
+ CreateMdBookmarksUIHTMLSource(profile));
+}
+
+// static
+bool MdBookmarksUI::IsEnabled() {
+ return base::FeatureList::IsEnabled(features::kMaterialDesignBookmarks);
+}
diff --git a/chromium/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.h b/chromium/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.h
new file mode 100644
index 00000000000..b864ad6a5f8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.h
@@ -0,0 +1,21 @@
+// Copyright (c) 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MD_BOOKMARKS_MD_BOOKMARKS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_MD_BOOKMARKS_MD_BOOKMARKS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+class MdBookmarksUI : public content::WebUIController {
+ public:
+ explicit MdBookmarksUI(content::WebUI* web_ui);
+
+ static bool IsEnabled();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MdBookmarksUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_MD_BOOKMARKS_MD_BOOKMARKS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/md_downloads/OWNERS b/chromium/chrome/browser/ui/webui/md_downloads/OWNERS
new file mode 100644
index 00000000000..8eefb55c2d8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_downloads/OWNERS
@@ -0,0 +1,4 @@
+asanka@chromium.org
+dbeam@chromium.org
+
+# COMPONENT: UI>Browser>Downloads
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
new file mode 100644
index 00000000000..46a17b7c295
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker.cc
@@ -0,0 +1,423 @@
+// 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.
+
+#include "chrome/browser/ui/webui/md_downloads/downloads_list_tracker.h"
+
+#include <iterator>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/i18n/rtl.h"
+#include "base/i18n/unicodestring.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "base/value_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/download/all_download_item_notifier.h"
+#include "chrome/browser/download/download_crx_util.h"
+#include "chrome/browser/download/download_item_model.h"
+#include "chrome/browser/download/download_query.h"
+#include "chrome/browser/extensions/api/downloads/downloads_api.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/download_item.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/browser/extension_system.h"
+#include "net/base/filename_util.h"
+#include "third_party/icu/source/i18n/unicode/datefmt.h"
+#include "ui/base/l10n/time_format.h"
+
+using content::BrowserContext;
+using content::DownloadItem;
+using content::DownloadManager;
+
+using DownloadVector = DownloadManager::DownloadVector;
+
+namespace {
+
+// Returns a string constant to be used as the |danger_type| value in
+// 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) {
+ switch (danger_type) {
+ case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
+ return "DANGEROUS_FILE";
+ case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
+ return "DANGEROUS_URL";
+ case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
+ return "DANGEROUS_CONTENT";
+ case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
+ return "UNCOMMON_CONTENT";
+ case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
+ return "DANGEROUS_HOST";
+ case content::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:
+ break;
+ }
+ // Don't return a danger type string if it is NOT_DANGEROUS,
+ // MAYBE_DANGEROUS_CONTENT, or USER_VALIDATED.
+ NOTREACHED();
+ return "";
+}
+
+// TODO(dbeam): if useful elsewhere, move to base/i18n/time_formatting.h?
+base::string16 TimeFormatLongDate(const base::Time& time) {
+ std::unique_ptr<icu::DateFormat> formatter(
+ icu::DateFormat::createDateInstance(icu::DateFormat::kLong));
+ icu::UnicodeString date_string;
+ formatter->format(static_cast<UDate>(time.ToDoubleT() * 1000), date_string);
+ return base::i18n::UnicodeStringToString16(date_string);
+}
+
+} // namespace
+
+DownloadsListTracker::DownloadsListTracker(
+ DownloadManager* download_manager,
+ content::WebUI* web_ui)
+ : main_notifier_(download_manager, this),
+ web_ui_(web_ui),
+ should_show_(base::Bind(&DownloadsListTracker::ShouldShow,
+ base::Unretained(this))) {
+ Init();
+}
+
+DownloadsListTracker::~DownloadsListTracker() {}
+
+void DownloadsListTracker::Reset() {
+ if (sending_updates_)
+ web_ui_->CallJavascriptFunctionUnsafe("downloads.Manager.clearAll");
+ sent_to_page_ = 0u;
+}
+
+bool DownloadsListTracker::SetSearchTerms(const base::ListValue& search_terms) {
+ std::vector<base::string16> new_terms;
+ new_terms.resize(search_terms.GetSize());
+
+ for (size_t i = 0; i < search_terms.GetSize(); ++i)
+ search_terms.GetString(i, &new_terms[i]);
+
+ if (new_terms == search_terms_)
+ return false;
+
+ search_terms_.swap(new_terms);
+ RebuildSortedItems();
+ return true;
+}
+
+void DownloadsListTracker::StartAndSendChunk() {
+ sending_updates_ = true;
+
+ CHECK_LE(sent_to_page_, sorted_items_.size());
+
+ SortedSet::iterator it = sorted_items_.begin();
+ std::advance(it, sent_to_page_);
+
+ base::ListValue list;
+ while (it != sorted_items_.end() && list.GetSize() < chunk_size_) {
+ list.Append(CreateDownloadItemValue(*it));
+ ++it;
+ }
+
+ web_ui_->CallJavascriptFunctionUnsafe(
+ "downloads.Manager.insertItems",
+ base::Value(static_cast<int>(sent_to_page_)), list);
+
+ sent_to_page_ += list.GetSize();
+}
+
+void DownloadsListTracker::Stop() {
+ sending_updates_ = false;
+}
+
+DownloadManager* DownloadsListTracker::GetMainNotifierManager() const {
+ return main_notifier_.GetManager();
+}
+
+DownloadManager* DownloadsListTracker::GetOriginalNotifierManager() const {
+ return original_notifier_ ? original_notifier_->GetManager() : nullptr;
+}
+
+void DownloadsListTracker::OnDownloadCreated(DownloadManager* manager,
+ DownloadItem* download_item) {
+ DCHECK_EQ(0u, sorted_items_.count(download_item));
+ if (should_show_.Run(*download_item))
+ InsertItem(sorted_items_.insert(download_item).first);
+}
+
+void DownloadsListTracker::OnDownloadUpdated(DownloadManager* manager,
+ DownloadItem* download_item) {
+ auto current_position = sorted_items_.find(download_item);
+ bool is_showing = current_position != sorted_items_.end();
+ bool should_show = should_show_.Run(*download_item);
+
+ if (!is_showing && should_show)
+ InsertItem(sorted_items_.insert(download_item).first);
+ else if (is_showing && !should_show)
+ RemoveItem(current_position);
+ else if (is_showing)
+ UpdateItem(current_position);
+}
+
+void DownloadsListTracker::OnDownloadRemoved(DownloadManager* manager,
+ DownloadItem* download_item) {
+ auto current_position = sorted_items_.find(download_item);
+ if (current_position != sorted_items_.end())
+ RemoveItem(current_position);
+}
+
+DownloadsListTracker::DownloadsListTracker(
+ DownloadManager* download_manager,
+ content::WebUI* web_ui,
+ base::Callback<bool(const DownloadItem&)> should_show)
+ : main_notifier_(download_manager, this),
+ web_ui_(web_ui),
+ should_show_(should_show) {
+ Init();
+}
+
+std::unique_ptr<base::DictionaryValue>
+DownloadsListTracker::CreateDownloadItemValue(
+ content::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.
+ DownloadItemModel download_model(download_item);
+
+ // The items which are to be written into file_value are also described in
+ // chrome/browser/resources/downloads/downloads.js in @typedef for
+ // BackendDownloadObject. Please update it whenever you add or remove
+ // any keys in file_value.
+ std::unique_ptr<base::DictionaryValue> file_value(new base::DictionaryValue);
+
+ file_value->SetInteger(
+ "started", static_cast<int>(download_item->GetStartTime().ToTimeT()));
+ file_value->SetString(
+ "since_string", ui::TimeFormat::RelativeDate(
+ download_item->GetStartTime(), NULL));
+ file_value->SetString(
+ "date_string", TimeFormatLongDate(download_item->GetStartTime()));
+
+ file_value->SetString("id", base::Uint64ToString(download_item->GetId()));
+
+ base::FilePath download_path(download_item->GetTargetFilePath());
+ file_value->Set("file_path", base::CreateFilePathValue(download_path));
+ file_value->SetString("file_url",
+ net::FilePathToFileURL(download_path).spec());
+
+ extensions::DownloadedByExtension* by_ext =
+ extensions::DownloadedByExtension::Get(download_item);
+ std::string by_ext_id;
+ std::string by_ext_name;
+ if (by_ext) {
+ by_ext_id = by_ext->id();
+ // TODO(dbeam): why doesn't DownloadsByExtension::name() return a string16?
+ by_ext_name = by_ext->name();
+
+ // Lookup the extension's current name() in case the user changed their
+ // 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);
+ if (extension)
+ by_ext_name = extension->name();
+ }
+ file_value->SetString("by_ext_id", by_ext_id);
+ file_value->SetString("by_ext_name", by_ext_name);
+
+ // Keep file names as LTR. TODO(dbeam): why?
+ base::string16 file_name =
+ download_item->GetFileNameToReportUser().LossyDisplayName();
+ file_name = base::i18n::GetDisplayStringInLTRDirectionality(file_name);
+ file_value->SetString("file_name", file_name);
+ file_value->SetString("url", download_item->GetURL().spec());
+ file_value->SetInteger("total", static_cast<int>(
+ download_item->GetTotalBytes()));
+ file_value->SetBoolean("file_externally_removed",
+ download_item->GetFileExternallyRemoved());
+ file_value->SetBoolean("resume", download_item->CanResume());
+ file_value->SetBoolean("otr", IsIncognito(*download_item));
+
+ const char* danger_type = "";
+ base::string16 last_reason_text;
+ // -2 is invalid, -1 means indeterminate, and 0-100 are in-progress.
+ int percent = -2;
+ base::string16 progress_status_text;
+ bool retry = false;
+ const char* state = nullptr;
+
+ switch (download_item->GetState()) {
+ case content::DownloadItem::IN_PROGRESS: {
+ if (download_item->IsDangerous()) {
+ state = "DANGEROUS";
+ danger_type = GetDangerTypeString(download_item->GetDangerType());
+ } else if (download_item->IsPaused()) {
+ state = "PAUSED";
+ } else {
+ state = "IN_PROGRESS";
+ }
+ progress_status_text = download_model.GetTabProgressStatusText();
+ percent = download_item->PercentComplete();
+ break;
+ }
+
+ case content::DownloadItem::INTERRUPTED:
+ state = "INTERRUPTED";
+ progress_status_text = download_model.GetTabProgressStatusText();
+
+ if (download_item->CanResume())
+ percent = download_item->PercentComplete();
+
+ // TODO(asanka): last_reason_text should be set via
+ // download_model.GetInterruptReasonText(). But we are using
+ // 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()) {
+ retry = true;
+ }
+ break;
+
+ case content::DownloadItem::CANCELLED:
+ state = "CANCELLED";
+ retry = true;
+ break;
+
+ case content::DownloadItem::COMPLETE:
+ DCHECK(!download_item->IsDangerous());
+ state = "COMPLETE";
+ break;
+
+ case content::DownloadItem::MAX_DOWNLOAD_STATE:
+ NOTREACHED();
+ }
+
+ DCHECK(state);
+
+ file_value->SetString("danger_type", danger_type);
+ file_value->SetString("last_reason_text", last_reason_text);
+ file_value->SetInteger("percent", percent);
+ file_value->SetString("progress_status_text", progress_status_text);
+ file_value->SetBoolean("retry", retry);
+ file_value->SetString("state", state);
+
+ return file_value;
+}
+
+bool DownloadsListTracker::IsIncognito(const DownloadItem& item) const {
+ return GetOriginalNotifierManager() && GetMainNotifierManager() &&
+ GetMainNotifierManager()->GetDownload(item.GetId()) == &item;
+}
+
+const DownloadItem* DownloadsListTracker::GetItemForTesting(size_t index)
+ const {
+ if (index >= sorted_items_.size())
+ return nullptr;
+
+ SortedSet::iterator it = sorted_items_.begin();
+ std::advance(it, index);
+ return *it;
+}
+
+void DownloadsListTracker::SetChunkSizeForTesting(size_t chunk_size) {
+ CHECK_EQ(0u, sent_to_page_);
+ chunk_size_ = chunk_size;
+}
+
+bool DownloadsListTracker::ShouldShow(const DownloadItem& item) const {
+ return !download_crx_util::IsExtensionDownload(item) && !item.IsTemporary() &&
+ !item.IsTransient() && !item.GetFileNameToReportUser().empty() &&
+ !item.GetTargetFilePath().empty() && !item.GetURL().is_empty() &&
+ DownloadItemModel(const_cast<DownloadItem*>(&item))
+ .ShouldShowInShelf() &&
+ DownloadQuery::MatchesQuery(search_terms_, item);
+}
+
+bool DownloadsListTracker::StartTimeComparator::operator() (
+ const content::DownloadItem* a, const content::DownloadItem* b) const {
+ return a->GetStartTime() > b->GetStartTime();
+}
+
+void DownloadsListTracker::Init() {
+ Profile* profile = Profile::FromBrowserContext(
+ GetMainNotifierManager()->GetBrowserContext());
+ if (profile->IsOffTheRecord()) {
+ Profile* original_profile = profile->GetOriginalProfile();
+ original_notifier_.reset(new AllDownloadItemNotifier(
+ BrowserContext::GetDownloadManager(original_profile), this));
+ }
+
+ RebuildSortedItems();
+}
+
+void DownloadsListTracker::RebuildSortedItems() {
+ DownloadVector all_items, visible_items;
+
+ GetMainNotifierManager()->GetAllDownloads(&all_items);
+
+ if (GetOriginalNotifierManager())
+ GetOriginalNotifierManager()->GetAllDownloads(&all_items);
+
+ DownloadQuery query;
+ query.AddFilter(should_show_);
+ query.Search(all_items.begin(), all_items.end(), &visible_items);
+
+ SortedSet sorted_items(visible_items.begin(), visible_items.end());
+ sorted_items_.swap(sorted_items);
+}
+
+void DownloadsListTracker::InsertItem(const SortedSet::iterator& insert) {
+ if (!sending_updates_)
+ return;
+
+ size_t index = GetIndex(insert);
+ if (index >= chunk_size_ && index >= sent_to_page_)
+ return;
+
+ base::ListValue list;
+ list.Append(CreateDownloadItemValue(*insert));
+
+ web_ui_->CallJavascriptFunctionUnsafe("downloads.Manager.insertItems",
+ base::Value(static_cast<int>(index)),
+ list);
+
+ sent_to_page_++;
+}
+
+void DownloadsListTracker::UpdateItem(const SortedSet::iterator& update) {
+ if (!sending_updates_ || GetIndex(update) >= sent_to_page_)
+ return;
+
+ web_ui_->CallJavascriptFunctionUnsafe(
+ "downloads.Manager.updateItem",
+ base::Value(static_cast<int>(GetIndex(update))),
+ *CreateDownloadItemValue(*update));
+}
+
+size_t DownloadsListTracker::GetIndex(const SortedSet::iterator& item) const {
+ // TODO(dbeam): this could be log(N) if |item| was random access.
+ return std::distance(sorted_items_.begin(), item);
+}
+
+void DownloadsListTracker::RemoveItem(const SortedSet::iterator& remove) {
+ if (sending_updates_) {
+ size_t index = GetIndex(remove);
+ if (index < sent_to_page_) {
+ web_ui_->CallJavascriptFunctionUnsafe(
+ "downloads.Manager.removeItem", base::Value(static_cast<int>(index)));
+ sent_to_page_--;
+ }
+ }
+ sorted_items_.erase(remove);
+}
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
new file mode 100644
index 00000000000..2fd266c91d5
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker.h
@@ -0,0 +1,140 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MD_DOWNLOADS_DOWNLOADS_LIST_TRACKER_H_
+#define CHROME_BROWSER_UI_WEBUI_MD_DOWNLOADS_DOWNLOADS_LIST_TRACKER_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <set>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/download/all_download_item_notifier.h"
+#include "content/public/browser/download_item.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+namespace content {
+class DownloadManager;
+class WebUI;
+}
+
+// A class that tracks all downloads activity and keeps a sorted representation
+// of the downloads as chrome://downloads wants to display them.
+class DownloadsListTracker : public AllDownloadItemNotifier::Observer {
+ public:
+ DownloadsListTracker(content::DownloadManager* download_manager,
+ content::WebUI* web_ui);
+ ~DownloadsListTracker() override;
+
+ // Clears all downloads on the page if currently sending updates and resets
+ // chunk tracking data.
+ void Reset();
+
+ // This class only cares about downloads that match |search_terms|.
+ // An empty list shows all downloads (the default). Returns true if
+ // |search_terms| differ from the current ones.
+ bool SetSearchTerms(const base::ListValue& search_terms);
+
+ // Starts sending updates and sends a capped amount of downloads. Tracks which
+ // downloads have been sent. Re-call this to send more.
+ void StartAndSendChunk();
+
+ // Stops sending updates to the page.
+ void Stop();
+
+ content::DownloadManager* GetMainNotifierManager() const;
+ content::DownloadManager* GetOriginalNotifierManager() const;
+
+ // AllDownloadItemNotifier::Observer:
+ void OnDownloadCreated(content::DownloadManager* manager,
+ content::DownloadItem* download_item) override;
+ void OnDownloadUpdated(content::DownloadManager* manager,
+ content::DownloadItem* download_item) override;
+ void OnDownloadRemoved(content::DownloadManager* manager,
+ content::DownloadItem* download_item) override;
+
+ protected:
+ // Testing constructor.
+ DownloadsListTracker(content::DownloadManager* download_manager,
+ content::WebUI* web_ui,
+ base::Callback<bool(const content::DownloadItem&)>);
+
+ // Creates a dictionary value that's sent to the page as JSON.
+ virtual std::unique_ptr<base::DictionaryValue> CreateDownloadItemValue(
+ content::DownloadItem* item) const;
+
+ // Exposed for testing.
+ bool IsIncognito(const content::DownloadItem& item) const;
+
+ const content::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;
+ };
+ using SortedSet = std::set<content::DownloadItem*, StartTimeComparator>;
+
+ // Called by both constructors to initialize common state.
+ void Init();
+
+ // Clears and re-inserts all downloads items into |sorted_items_|.
+ void RebuildSortedItems();
+
+ // Whether |item| should show on the current page.
+ bool ShouldShow(const content::DownloadItem& item) const;
+
+ // Returns the index of |item| in |sorted_items_|.
+ size_t GetIndex(const SortedSet::iterator& item) const;
+
+ // Calls "insertItems" if sending updates and the page knows about |insert|.
+ void InsertItem(const SortedSet::iterator& insert);
+
+ // Calls "updateItem" if sending updates and the page knows about |update|.
+ void UpdateItem(const SortedSet::iterator& update);
+
+ // Removes the item that corresponds to |remove| and sends "removeItems"
+ // if sending updates.
+ void RemoveItem(const SortedSet::iterator& remove);
+
+ AllDownloadItemNotifier main_notifier_;
+ std::unique_ptr<AllDownloadItemNotifier> original_notifier_;
+
+ // The WebUI object corresponding to the page we care about.
+ content::WebUI* const web_ui_;
+
+ // 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_;
+
+ // When this is true, all changes to downloads that affect the page are sent
+ // via JavaScript.
+ bool sending_updates_ = false;
+
+ SortedSet sorted_items_;
+
+ // The number of items sent to the page so far.
+ size_t sent_to_page_ = 0u;
+
+ // The maximum number of items sent to the page at a time.
+ size_t chunk_size_ = 20u;
+
+ // Current search terms.
+ std::vector<base::string16> search_terms_;
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadsListTracker);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_MD_DOWNLOADS_DOWNLOADS_LIST_TRACKER_H_
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
new file mode 100644
index 00000000000..a2bad3f2d52
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_downloads/downloads_list_tracker_unittest.cc
@@ -0,0 +1,369 @@
+// 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.
+
+#include "chrome/browser/ui/webui/md_downloads/downloads_list_tracker.h"
+
+#include <limits.h>
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/memory/ptr_util.h"
+#include "base/time/time.h"
+#include "chrome/browser/download/download_item_model.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/mock_download_item.h"
+#include "content/public/test/mock_download_manager.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_web_ui.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::DownloadItem;
+using content::MockDownloadItem;
+using DownloadVector = std::vector<DownloadItem*>;
+using testing::_;
+using testing::Return;
+
+namespace {
+
+uint64_t GetId(const base::Value& value) {
+ const base::DictionaryValue* dict;
+ CHECK(value.GetAsDictionary(&dict));
+
+ int id;
+ CHECK(dict->GetInteger("id", &id));
+ CHECK_GE(id, 0);
+ return static_cast<uint64_t>(id);
+}
+
+std::vector<uint64_t> GetIds(const base::Value& value) {
+ std::vector<uint64_t> ids;
+
+ const base::ListValue* list;
+ if (value.GetAsList(&list)) {
+ for (const auto& list_item : *list)
+ ids.push_back(GetId(list_item));
+ } else {
+ ids.push_back(GetId(value));
+ }
+
+ return ids;
+}
+
+int GetIndex(const base::Value* value) {
+ CHECK(value);
+ int index;
+ CHECK(value->GetAsInteger(&index));
+ return index;
+}
+
+bool ShouldShowItem(const DownloadItem& item) {
+ DownloadItemModel model(const_cast<DownloadItem*>(&item));
+ return model.ShouldShowInShelf();
+}
+
+} // namespace
+
+// A test version of DownloadsListTracker.
+class TestDownloadsListTracker : public DownloadsListTracker {
+ public:
+ TestDownloadsListTracker(content::DownloadManager* manager,
+ content::WebUI* web_ui)
+ : DownloadsListTracker(manager, web_ui, base::Bind(&ShouldShowItem)) {}
+ ~TestDownloadsListTracker() override {}
+
+ using DownloadsListTracker::IsIncognito;
+ using DownloadsListTracker::GetItemForTesting;
+ using DownloadsListTracker::SetChunkSizeForTesting;
+
+ protected:
+ std::unique_ptr<base::DictionaryValue> CreateDownloadItemValue(
+ content::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());
+ return dict;
+ }
+};
+
+// A fixture to test DownloadsListTracker.
+class DownloadsListTrackerTest : public testing::Test {
+ public:
+ DownloadsListTrackerTest() {}
+
+ ~DownloadsListTrackerTest() override {
+ for (const auto& mock_item : mock_items_)
+ testing::Mock::VerifyAndClear(mock_item.get());
+ }
+
+ // testing::Test:
+ void SetUp() override {
+ ON_CALL(manager_, GetBrowserContext()).WillByDefault(Return(&profile_));
+ ON_CALL(manager_, GetAllDownloads(_)).WillByDefault(
+ testing::Invoke(this, &DownloadsListTrackerTest::GetAllDownloads));
+ }
+
+ MockDownloadItem* CreateMock(uint64_t id, const base::Time& started) {
+ MockDownloadItem* new_item = new testing::NiceMock<MockDownloadItem>();
+ mock_items_.push_back(base::WrapUnique(new_item));
+
+ ON_CALL(*new_item, GetId()).WillByDefault(Return(id));
+ ON_CALL(*new_item, GetStartTime()).WillByDefault(Return(started));
+ ON_CALL(*new_item, IsTransient()).WillByDefault(Return(false));
+
+ return new_item;
+ }
+
+ MockDownloadItem* CreateNextItem() {
+ return CreateMock(mock_items_.size(), base::Time::UnixEpoch() +
+ base::TimeDelta::FromHours(mock_items_.size()));
+ }
+
+ void CreateTracker() {
+ tracker_.reset(new TestDownloadsListTracker(manager(), web_ui()));
+ }
+
+ TestingProfile* profile() { return &profile_; }
+ content::DownloadManager* manager() { return &manager_; }
+ content::TestWebUI* web_ui() { return &web_ui_; }
+ TestDownloadsListTracker* tracker() { return tracker_.get(); }
+
+ private:
+ void GetAllDownloads(DownloadVector* result) {
+ for (const auto& mock_item : mock_items_)
+ result->push_back(mock_item.get());
+ }
+
+ // NOTE: The initialization order of these members matters.
+ content::TestBrowserThreadBundle thread_bundle_;
+ TestingProfile profile_;
+
+ testing::NiceMock<content::MockDownloadManager> manager_;
+ content::TestWebUI web_ui_;
+ std::unique_ptr<TestDownloadsListTracker> tracker_;
+
+ std::vector<std::unique_ptr<MockDownloadItem>> mock_items_;
+};
+
+TEST_F(DownloadsListTrackerTest, SetSearchTerms) {
+ CreateTracker();
+
+ const base::ListValue empty_terms;
+ EXPECT_FALSE(tracker()->SetSearchTerms(empty_terms));
+
+ base::ListValue search_terms;
+ search_terms.AppendString("search");
+ EXPECT_TRUE(tracker()->SetSearchTerms(search_terms));
+
+ EXPECT_FALSE(tracker()->SetSearchTerms(search_terms));
+
+ EXPECT_TRUE(tracker()->SetSearchTerms(empty_terms));
+
+ // Notifying the page is left up to the handler in this case.
+ EXPECT_TRUE(web_ui()->call_data().empty());
+}
+
+TEST_F(DownloadsListTrackerTest, StartCallsInsertItems) {
+ DownloadItem* first_item = CreateNextItem();
+
+ CreateTracker();
+ ASSERT_TRUE(tracker()->GetItemForTesting(0));
+ EXPECT_TRUE(web_ui()->call_data().empty());
+
+ tracker()->StartAndSendChunk();
+ ASSERT_FALSE(web_ui()->call_data().empty());
+
+ EXPECT_EQ("downloads.Manager.insertItems",
+ web_ui()->call_data()[0]->function_name());
+ EXPECT_EQ(0, GetIndex(web_ui()->call_data()[0]->arg1()));
+
+ std::vector<uint64_t> ids = GetIds(*web_ui()->call_data()[0]->arg2());
+ ASSERT_FALSE(ids.empty());
+ EXPECT_EQ(first_item->GetId(), ids[0]);
+}
+
+// The page is in a loading state until it gets an insertItems call. Ensure that
+// happens even without downloads.
+TEST_F(DownloadsListTrackerTest, EmptyGetAllItemsStillCallsInsertItems) {
+ CreateTracker();
+
+ ASSERT_FALSE(tracker()->GetItemForTesting(0));
+ ASSERT_TRUE(web_ui()->call_data().empty());
+
+ tracker()->StartAndSendChunk();
+
+ ASSERT_FALSE(web_ui()->call_data().empty());
+ EXPECT_EQ("downloads.Manager.insertItems",
+ web_ui()->call_data()[0]->function_name());
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg2());
+ EXPECT_TRUE(GetIds(*web_ui()->call_data()[0]->arg2()).empty());
+}
+
+TEST_F(DownloadsListTrackerTest, OnDownloadCreatedCallsInsertItems) {
+ CreateTracker();
+ tracker()->StartAndSendChunk();
+ web_ui()->ClearTrackedCalls();
+
+ ASSERT_FALSE(tracker()->GetItemForTesting(0));
+ DownloadItem* first_item = CreateNextItem();
+ tracker()->OnDownloadCreated(manager(), first_item);
+
+ ASSERT_FALSE(web_ui()->call_data().empty());
+ EXPECT_EQ("downloads.Manager.insertItems",
+ web_ui()->call_data()[0]->function_name());
+ EXPECT_EQ(0, GetIndex(web_ui()->call_data()[0]->arg1()));
+
+ std::vector<uint64_t> ids = GetIds(*web_ui()->call_data()[0]->arg2());
+ ASSERT_FALSE(ids.empty());
+ EXPECT_EQ(first_item->GetId(), ids[0]);
+}
+
+TEST_F(DownloadsListTrackerTest, OnDownloadRemovedCallsRemoveItem) {
+ DownloadItem* first_item = CreateNextItem();
+
+ CreateTracker();
+ tracker()->StartAndSendChunk();
+ web_ui()->ClearTrackedCalls();
+
+ EXPECT_TRUE(tracker()->GetItemForTesting(0));
+ tracker()->OnDownloadRemoved(manager(), first_item);
+ EXPECT_FALSE(tracker()->GetItemForTesting(0));
+
+ ASSERT_FALSE(web_ui()->call_data().empty());
+
+ EXPECT_EQ("downloads.Manager.removeItem",
+ web_ui()->call_data()[0]->function_name());
+ EXPECT_EQ(0, GetIndex(web_ui()->call_data()[0]->arg1()));
+}
+
+TEST_F(DownloadsListTrackerTest, OnDownloadUpdatedCallsRemoveItem) {
+ DownloadItem* first_item = CreateNextItem();
+
+ CreateTracker();
+ tracker()->StartAndSendChunk();
+ web_ui()->ClearTrackedCalls();
+
+ EXPECT_TRUE(tracker()->GetItemForTesting(0));
+
+ DownloadItemModel(first_item).SetShouldShowInShelf(false);
+ tracker()->OnDownloadUpdated(manager(), first_item);
+
+ EXPECT_FALSE(tracker()->GetItemForTesting(0));
+
+ ASSERT_FALSE(web_ui()->call_data().empty());
+
+ EXPECT_EQ("downloads.Manager.removeItem",
+ web_ui()->call_data()[0]->function_name());
+ EXPECT_EQ(0, GetIndex(web_ui()->call_data()[0]->arg1()));
+}
+
+TEST_F(DownloadsListTrackerTest, StartExcludesHiddenItems) {
+ DownloadItem* first_item = CreateNextItem();
+ DownloadItemModel(first_item).SetShouldShowInShelf(false);
+
+ CreateTracker();
+ tracker()->StartAndSendChunk();
+
+ ASSERT_FALSE(web_ui()->call_data().empty());
+
+ EXPECT_EQ("downloads.Manager.insertItems",
+ web_ui()->call_data()[0]->function_name());
+ EXPECT_TRUE(GetIds(*web_ui()->call_data()[0]->arg2()).empty());
+}
+
+TEST_F(DownloadsListTrackerTest, Incognito) {
+ testing::NiceMock<content::MockDownloadManager> incognito_manager;
+ ON_CALL(incognito_manager, GetBrowserContext()).WillByDefault(Return(
+ TestingProfile::Builder().BuildIncognito(profile())));
+
+ MockDownloadItem item;
+ EXPECT_CALL(item, GetId()).WillRepeatedly(Return(0));
+
+ ON_CALL(incognito_manager, GetDownload(0)).WillByDefault(Return(&item));
+
+ TestDownloadsListTracker tracker(&incognito_manager, web_ui());
+ EXPECT_TRUE(tracker.IsIncognito(item));
+}
+
+TEST_F(DownloadsListTrackerTest, OnlySendSomeItems) {
+ CreateNextItem();
+ CreateNextItem();
+ CreateNextItem();
+ CreateNextItem();
+ CreateNextItem();
+
+ CreateTracker();
+ tracker()->SetChunkSizeForTesting(3);
+ tracker()->StartAndSendChunk();
+
+ ASSERT_FALSE(web_ui()->call_data().empty());
+
+ EXPECT_EQ("downloads.Manager.insertItems",
+ web_ui()->call_data()[0]->function_name());
+ EXPECT_EQ(0, GetIndex(web_ui()->call_data()[0]->arg1()));
+ EXPECT_EQ(3u, GetIds(*web_ui()->call_data()[0]->arg2()).size());
+
+ tracker()->StartAndSendChunk();
+ ASSERT_GE(2u, web_ui()->call_data().size());
+
+ EXPECT_EQ("downloads.Manager.insertItems",
+ web_ui()->call_data()[1]->function_name());
+ EXPECT_EQ(3, GetIndex(web_ui()->call_data()[1]->arg1()));
+ EXPECT_EQ(2u, GetIds(*web_ui()->call_data()[1]->arg2()).size());
+}
+
+TEST_F(DownloadsListTrackerTest, IgnoreUnsentItemUpdates) {
+ DownloadItem* unsent_item = CreateNextItem();
+ CreateNextItem();
+
+ CreateTracker();
+ tracker()->SetChunkSizeForTesting(1);
+ tracker()->StartAndSendChunk();
+
+ ASSERT_FALSE(web_ui()->call_data().empty());
+
+ EXPECT_EQ("downloads.Manager.insertItems",
+ web_ui()->call_data()[0]->function_name());
+ EXPECT_EQ(1u, GetIds(*web_ui()->call_data()[0]->arg2()).size());
+
+ tracker()->OnDownloadUpdated(manager(), unsent_item);
+ EXPECT_EQ(1u, web_ui()->call_data().size());
+}
+
+TEST_F(DownloadsListTrackerTest, IgnoreUnsentItemRemovals) {
+ DownloadItem* unsent_item = CreateNextItem();
+ CreateNextItem();
+
+ CreateTracker();
+ tracker()->SetChunkSizeForTesting(1);
+ tracker()->StartAndSendChunk();
+
+ ASSERT_FALSE(web_ui()->call_data().empty());
+
+ EXPECT_EQ("downloads.Manager.insertItems",
+ web_ui()->call_data()[0]->function_name());
+ EXPECT_EQ(1u, GetIds(*web_ui()->call_data()[0]->arg2()).size());
+
+ DownloadItemModel(unsent_item).SetShouldShowInShelf(false);
+ tracker()->OnDownloadUpdated(manager(), unsent_item);
+ EXPECT_EQ(1u, web_ui()->call_data().size());
+
+ DownloadItemModel(unsent_item).SetShouldShowInShelf(true);
+ tracker()->OnDownloadUpdated(manager(), unsent_item);
+ EXPECT_EQ(1u, web_ui()->call_data().size());
+}
+
+TEST_F(DownloadsListTrackerTest, IgnoreTransientDownloads) {
+ MockDownloadItem* transient_item = CreateNextItem();
+ ON_CALL(*transient_item, IsTransient()).WillByDefault(Return(true));
+
+ CreateTracker();
+ tracker()->StartAndSendChunk();
+
+ ASSERT_FALSE(web_ui()->call_data().empty());
+ EXPECT_EQ(0u, GetIds(*web_ui()->call_data()[0]->arg2()).size());
+}
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
new file mode 100644
index 00000000000..15bbabc4372
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.cc
@@ -0,0 +1,462 @@
+// 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/ui/webui/md_downloads/md_downloads_dom_handler.h"
+
+#include <algorithm>
+#include <functional>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/i18n/rtl.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/supports_user_data.h"
+#include "base/threading/thread.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/download/download_danger_prompt.h"
+#include "chrome/browser/download/download_history.h"
+#include "chrome/browser/download/download_item_model.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/download/download_query.h"
+#include "chrome/browser/download/drag_download_item.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/fileicon_source.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "components/prefs/pref_service.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"
+#include "content/public/browser/web_ui.h"
+#include "net/base/filename_util.h"
+#include "ui/base/l10n/time_format.h"
+#include "ui/gfx/image/image.h"
+
+using content::BrowserThread;
+
+namespace {
+
+enum DownloadsDOMEvent {
+ DOWNLOADS_DOM_EVENT_GET_DOWNLOADS = 0,
+ DOWNLOADS_DOM_EVENT_OPEN_FILE = 1,
+ DOWNLOADS_DOM_EVENT_DRAG = 2,
+ DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS = 3,
+ DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS = 4,
+ DOWNLOADS_DOM_EVENT_SHOW = 5,
+ DOWNLOADS_DOM_EVENT_PAUSE = 6,
+ DOWNLOADS_DOM_EVENT_REMOVE = 7,
+ DOWNLOADS_DOM_EVENT_CANCEL = 8,
+ DOWNLOADS_DOM_EVENT_CLEAR_ALL = 9,
+ DOWNLOADS_DOM_EVENT_OPEN_FOLDER = 10,
+ DOWNLOADS_DOM_EVENT_RESUME = 11,
+ DOWNLOADS_DOM_EVENT_MAX
+};
+
+void CountDownloadsDOMEvents(DownloadsDOMEvent event) {
+ UMA_HISTOGRAM_ENUMERATION("Download.DOMEvent",
+ event,
+ DOWNLOADS_DOM_EVENT_MAX);
+}
+
+} // namespace
+
+MdDownloadsDOMHandler::MdDownloadsDOMHandler(
+ content::DownloadManager* download_manager, content::WebUI* web_ui)
+ : list_tracker_(download_manager, web_ui),
+ weak_ptr_factory_(this) {
+ // Create our fileicon data source.
+ profile_ = Profile::FromBrowserContext(download_manager->GetBrowserContext());
+ content::URLDataSource::Add(profile_, new FileIconSource());
+ CheckForRemovedFiles();
+}
+
+MdDownloadsDOMHandler::~MdDownloadsDOMHandler() {
+ FinalizeRemovals();
+}
+
+// MdDownloadsDOMHandler, public: ---------------------------------------------
+
+void MdDownloadsDOMHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("getDownloads",
+ base::Bind(&MdDownloadsDOMHandler::HandleGetDownloads,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "openFileRequiringGesture",
+ base::Bind(&MdDownloadsDOMHandler::HandleOpenFile,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback("drag",
+ base::Bind(&MdDownloadsDOMHandler::HandleDrag,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "saveDangerousRequiringGesture",
+ base::Bind(&MdDownloadsDOMHandler::HandleSaveDangerous,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback("discardDangerous",
+ base::Bind(&MdDownloadsDOMHandler::HandleDiscardDangerous,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback("show",
+ base::Bind(&MdDownloadsDOMHandler::HandleShow,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback("pause",
+ base::Bind(&MdDownloadsDOMHandler::HandlePause,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback("resume",
+ base::Bind(&MdDownloadsDOMHandler::HandleResume,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback("remove",
+ base::Bind(&MdDownloadsDOMHandler::HandleRemove,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback("undo",
+ base::Bind(&MdDownloadsDOMHandler::HandleUndo,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback("cancel",
+ base::Bind(&MdDownloadsDOMHandler::HandleCancel,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback("clearAll",
+ base::Bind(&MdDownloadsDOMHandler::HandleClearAll,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "openDownloadsFolderRequiringGesture",
+ base::Bind(&MdDownloadsDOMHandler::HandleOpenDownloadsFolder,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ Observe(GetWebUIWebContents());
+}
+
+void MdDownloadsDOMHandler::OnJavascriptDisallowed() {
+ list_tracker_.Stop();
+ list_tracker_.Reset();
+ CheckForRemovedFiles();
+}
+
+void MdDownloadsDOMHandler::RenderProcessGone(base::TerminationStatus status) {
+ // TODO(dbeam): WebUI + WebUIMessageHandler should do this automatically.
+ // http://crbug.com/610450
+ DisallowJavascript();
+}
+
+void MdDownloadsDOMHandler::HandleGetDownloads(const base::ListValue* args) {
+ AllowJavascript();
+
+ CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_GET_DOWNLOADS);
+
+ bool terms_changed = list_tracker_.SetSearchTerms(*args);
+ if (terms_changed)
+ list_tracker_.Reset();
+
+ list_tracker_.StartAndSendChunk();
+}
+
+void MdDownloadsDOMHandler::HandleOpenFile(const base::ListValue* args) {
+ CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FILE);
+ content::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);
+ if (!file)
+ return;
+
+ content::WebContents* web_contents = GetWebUIWebContents();
+ // |web_contents| is only NULL in the test.
+ if (!web_contents)
+ return;
+
+ if (file->GetState() != content::DownloadItem::COMPLETE)
+ return;
+
+ gfx::Image* icon = g_browser_process->icon_manager()->LookupIconFromFilepath(
+ file->GetTargetFilePath(), IconLoader::NORMAL);
+ gfx::NativeView view = web_contents->GetNativeView();
+ {
+ // Enable nested tasks during DnD, while |DragDownload()| blocks.
+ base::MessageLoop::ScopedNestableTaskAllower allow(
+ base::MessageLoop::current());
+ DragDownloadItem(file, icon, view);
+ }
+}
+
+void MdDownloadsDOMHandler::HandleSaveDangerous(const base::ListValue* args) {
+ CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS);
+ content::DownloadItem* file = GetDownloadByValue(args);
+ SaveDownload(file);
+}
+
+void MdDownloadsDOMHandler::SaveDownload(
+ content::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) {
+ ShowDangerPrompt(download);
+ } else {
+ // If danger type is DANGEROUS_FILE, chrome proceeds to keep this download
+ // without showing download danger prompt.
+ if (profile_) {
+ PrefService* prefs = profile_->GetPrefs();
+ if (!profile_->IsOffTheRecord() &&
+ prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) {
+ DownloadDangerPrompt::SendSafeBrowsingDownloadReport(
+ safe_browsing::ClientSafeBrowsingReportRequest::
+ DANGEROUS_DOWNLOAD_RECOVERY,
+ true, *download);
+ }
+ }
+ DangerPromptDone(download->GetId(), DownloadDangerPrompt::ACCEPT);
+ }
+}
+
+void MdDownloadsDOMHandler::HandleDiscardDangerous(
+ const base::ListValue* args) {
+ CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS);
+ RemoveDownloadInArgs(args);
+}
+
+void MdDownloadsDOMHandler::HandleShow(const base::ListValue* args) {
+ CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SHOW);
+ content::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);
+ if (file)
+ file->Pause();
+}
+
+void MdDownloadsDOMHandler::HandleResume(const base::ListValue* args) {
+ CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_RESUME);
+ content::DownloadItem* file = GetDownloadByValue(args);
+ if (file)
+ file->Resume();
+}
+
+void MdDownloadsDOMHandler::HandleRemove(const base::ListValue* args) {
+ if (!IsDeletingHistoryAllowed())
+ return;
+
+ CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_REMOVE);
+ RemoveDownloadInArgs(args);
+}
+
+void MdDownloadsDOMHandler::HandleUndo(const base::ListValue* args) {
+ // TODO(dbeam): handle more than removed downloads someday?
+ if (removals_.empty())
+ return;
+
+ const IdSet last_removed_ids = removals_.back();
+ removals_.pop_back();
+
+ const bool undoing_clear_all = last_removed_ids.size() > 1;
+ if (undoing_clear_all) {
+ list_tracker_.Reset();
+ list_tracker_.Stop();
+ }
+
+ for (auto id : last_removed_ids) {
+ content::DownloadItem* download = GetDownloadById(id);
+ if (!download)
+ continue;
+
+ DownloadItemModel model(download);
+ model.SetShouldShowInShelf(true);
+ model.SetIsBeingRevived(true);
+
+ download->UpdateObservers();
+
+ model.SetIsBeingRevived(false);
+ }
+
+ if (undoing_clear_all)
+ list_tracker_.StartAndSendChunk();
+}
+
+void MdDownloadsDOMHandler::HandleCancel(const base::ListValue* args) {
+ CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CANCEL);
+ content::DownloadItem* file = GetDownloadByValue(args);
+ if (file)
+ file->Cancel(true);
+}
+
+void MdDownloadsDOMHandler::HandleClearAll(const base::ListValue* args) {
+ if (!IsDeletingHistoryAllowed()) {
+ // This should only be reached during tests.
+ return;
+ }
+
+ CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CLEAR_ALL);
+
+ list_tracker_.Reset();
+ list_tracker_.Stop();
+
+ DownloadVector downloads;
+ if (GetMainNotifierManager())
+ GetMainNotifierManager()->GetAllDownloads(&downloads);
+ if (GetOriginalNotifierManager())
+ GetOriginalNotifierManager()->GetAllDownloads(&downloads);
+ RemoveDownloads(downloads);
+
+ list_tracker_.StartAndSendChunk();
+}
+
+void MdDownloadsDOMHandler::RemoveDownloads(const DownloadVector& to_remove) {
+ IdSet ids;
+
+ for (auto* download : to_remove) {
+ if (download->IsDangerous()) {
+ // Don't allow users to revive dangerous downloads; just nuke 'em.
+ download->Remove();
+ continue;
+ }
+
+ DownloadItemModel item_model(download);
+ if (!item_model.ShouldShowInShelf() ||
+ download->GetState() == content::DownloadItem::IN_PROGRESS) {
+ continue;
+ }
+
+ item_model.SetShouldShowInShelf(false);
+ ids.insert(download->GetId());
+ download->UpdateObservers();
+ }
+
+ if (!ids.empty())
+ removals_.push_back(ids);
+}
+
+void MdDownloadsDOMHandler::HandleOpenDownloadsFolder(
+ const base::ListValue* args) {
+ CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FOLDER);
+ content::DownloadManager* manager = GetMainNotifierManager();
+ if (manager) {
+ platform_util::OpenItem(
+ Profile::FromBrowserContext(manager->GetBrowserContext()),
+ DownloadPrefs::FromDownloadManager(manager)->DownloadPath(),
+ platform_util::OPEN_FOLDER, platform_util::OpenOperationCallback());
+ }
+}
+
+// MdDownloadsDOMHandler, private: --------------------------------------------
+
+content::DownloadManager* MdDownloadsDOMHandler::GetMainNotifierManager()
+ const {
+ return list_tracker_.GetMainNotifierManager();
+}
+
+content::DownloadManager* MdDownloadsDOMHandler::GetOriginalNotifierManager()
+ const {
+ return list_tracker_.GetOriginalNotifierManager();
+}
+
+void MdDownloadsDOMHandler::FinalizeRemovals() {
+ while (!removals_.empty()) {
+ const IdSet remove = removals_.back();
+ removals_.pop_back();
+
+ for (const auto id : remove) {
+ content::DownloadItem* download = GetDownloadById(id);
+ if (download)
+ download->Remove();
+ }
+ }
+}
+
+void MdDownloadsDOMHandler::ShowDangerPrompt(
+ content::DownloadItem* dangerous_item) {
+ DownloadDangerPrompt* danger_prompt = DownloadDangerPrompt::Create(
+ dangerous_item,
+ GetWebUIWebContents(),
+ false,
+ base::Bind(&MdDownloadsDOMHandler::DangerPromptDone,
+ weak_ptr_factory_.GetWeakPtr(), dangerous_item->GetId()));
+ // danger_prompt will delete itself.
+ DCHECK(danger_prompt);
+}
+
+void MdDownloadsDOMHandler::DangerPromptDone(
+ int download_id, DownloadDangerPrompt::Action action) {
+ if (action != DownloadDangerPrompt::ACCEPT)
+ return;
+ content::DownloadItem* item = NULL;
+ if (GetMainNotifierManager())
+ item = GetMainNotifierManager()->GetDownload(download_id);
+ if (!item && GetOriginalNotifierManager())
+ item = GetOriginalNotifierManager()->GetDownload(download_id);
+ if (!item || item->IsDone())
+ return;
+ CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS);
+ item->ValidateDangerousDownload();
+}
+
+bool MdDownloadsDOMHandler::IsDeletingHistoryAllowed() {
+ content::DownloadManager* manager = GetMainNotifierManager();
+ return manager &&
+ Profile::FromBrowserContext(manager->GetBrowserContext())->
+ GetPrefs()->GetBoolean(prefs::kAllowDeletingBrowserHistory);
+}
+
+content::DownloadItem* MdDownloadsDOMHandler::GetDownloadByValue(
+ const base::ListValue* args) {
+ std::string download_id;
+ if (!args->GetString(0, &download_id)) {
+ NOTREACHED();
+ return nullptr;
+ }
+
+ uint64_t id;
+ if (!base::StringToUint64(download_id, &id)) {
+ NOTREACHED();
+ return nullptr;
+ }
+
+ return GetDownloadById(static_cast<uint32_t>(id));
+}
+
+content::DownloadItem* MdDownloadsDOMHandler::GetDownloadById(uint32_t id) {
+ content::DownloadItem* item = NULL;
+ if (GetMainNotifierManager())
+ item = GetMainNotifierManager()->GetDownload(id);
+ if (!item && GetOriginalNotifierManager())
+ item = GetOriginalNotifierManager()->GetDownload(id);
+ return item;
+}
+
+content::WebContents* MdDownloadsDOMHandler::GetWebUIWebContents() {
+ return web_ui()->GetWebContents();
+}
+
+void MdDownloadsDOMHandler::CheckForRemovedFiles() {
+ if (GetMainNotifierManager())
+ GetMainNotifierManager()->CheckForHistoryFilesRemoval();
+ if (GetOriginalNotifierManager())
+ GetOriginalNotifierManager()->CheckForHistoryFilesRemoval();
+}
+
+void MdDownloadsDOMHandler::RemoveDownloadInArgs(const base::ListValue* args) {
+ content::DownloadItem* file = GetDownloadByValue(args);
+ if (!file)
+ return;
+
+ DownloadVector downloads;
+ downloads.push_back(file);
+ RemoveDownloads(downloads);
+}
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
new file mode 100644
index 00000000000..71cc3b47ec9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.h
@@ -0,0 +1,163 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MD_DOWNLOADS_MD_DOWNLOADS_DOM_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_MD_DOWNLOADS_MD_DOWNLOADS_DOM_HANDLER_H_
+
+#include <stdint.h>
+
+#include <set>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/download/download_danger_prompt.h"
+#include "chrome/browser/ui/webui/md_downloads/downloads_list_tracker.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace content {
+class DownloadItem;
+class DownloadManager;
+class WebContents;
+class WebUI;
+}
+
+class Profile;
+
+// The handler for Javascript messages related to the "downloads" view,
+// also observes changes to the download manager.
+class MdDownloadsDOMHandler : public content::WebContentsObserver,
+ public content::WebUIMessageHandler {
+ public:
+ MdDownloadsDOMHandler(content::DownloadManager* download_manager,
+ content::WebUI* web_ui);
+ ~MdDownloadsDOMHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptDisallowed() override;
+
+ // WebContentsObserver implementation.
+ void RenderProcessGone(base::TerminationStatus status) override;
+
+ // Callback for the "getDownloads" message.
+ void HandleGetDownloads(const base::ListValue* args);
+
+ // Callback for the "openFile" message - opens the file in the shell.
+ void HandleOpenFile(const base::ListValue* args);
+
+ // Callback for the "drag" message - initiates a file object drag.
+ void HandleDrag(const base::ListValue* args);
+
+ // Callback for the "saveDangerous" message - specifies that the user
+ // wishes to save a dangerous file.
+ void HandleSaveDangerous(const base::ListValue* args);
+
+ // Callback for the "discardDangerous" message - specifies that the user
+ // wishes to discard (remove) a dangerous file.
+ void HandleDiscardDangerous(const base::ListValue* args);
+
+ // Callback for the "show" message - shows the file in explorer.
+ void HandleShow(const base::ListValue* args);
+
+ // Callback for the "pause" message - pauses the file download.
+ void HandlePause(const base::ListValue* args);
+
+ // Callback for the "resume" message - resumes the file download.
+ void HandleResume(const base::ListValue* args);
+
+ // Callback for the "remove" message - removes the file download from shelf
+ // and list.
+ void HandleRemove(const base::ListValue* args);
+
+ // Callback for the "undo" message. Currently only undoes removals.
+ void HandleUndo(const base::ListValue* args);
+
+ // Callback for the "cancel" message - cancels the download.
+ void HandleCancel(const base::ListValue* args);
+
+ // Callback for the "clearAll" message - clears all the downloads.
+ void HandleClearAll(const base::ListValue* args);
+
+ // Callback for the "openDownloadsFolder" message - opens the downloads
+ // folder.
+ void HandleOpenDownloadsFolder(const base::ListValue* args);
+
+ protected:
+ // These methods are for mocking so that most of this class does not actually
+ // depend on WebUI. The other methods that depend on WebUI are
+ // RegisterMessages() and HandleDrag().
+ virtual content::WebContents* GetWebUIWebContents();
+
+ // Actually remove downloads with an ID in |removals_|. This cannot be undone.
+ void FinalizeRemovals();
+
+ using DownloadVector = std::vector<content::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);
+
+ private:
+ using IdSet = std::set<uint32_t>;
+
+ // Convenience method to call |main_notifier_->GetManager()| while
+ // null-checking |main_notifier_|.
+ content::DownloadManager* GetMainNotifierManager() const;
+
+ // Convenience method to call |original_notifier_->GetManager()| while
+ // null-checking |original_notifier_|.
+ content::DownloadManager* GetOriginalNotifierManager() const;
+
+ // Displays a native prompt asking the user for confirmation after accepting
+ // the dangerous download specified by |dangerous|. The function returns
+ // immediately, and will invoke DangerPromptAccepted() asynchronously if the
+ // 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);
+
+ // Conveys danger acceptance from the DownloadDangerPrompt to the
+ // DownloadItem.
+ virtual void DangerPromptDone(int download_id,
+ DownloadDangerPrompt::Action action);
+
+ // Returns true if the records of any downloaded items are allowed (and able)
+ // to be deleted.
+ bool IsDeletingHistoryAllowed();
+
+ // Returns the download that is referred to in a given value.
+ content::DownloadItem* GetDownloadByValue(const base::ListValue* args);
+
+ // Returns the download with |id| or NULL if it doesn't exist.
+ content::DownloadItem* GetDownloadById(uint32_t id);
+
+ // Removes the download specified by an ID from JavaScript in |args|.
+ void RemoveDownloadInArgs(const base::ListValue* args);
+
+ // Checks whether a download's file was removed from its original location.
+ void CheckForRemovedFiles();
+
+ DownloadsListTracker list_tracker_;
+
+ // IDs of downloads to remove when this handler gets deleted.
+ std::vector<IdSet> removals_;
+
+ // User profile that corresponds to this handler.
+ Profile* profile_;
+
+ base::WeakPtrFactory<MdDownloadsDOMHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MdDownloadsDOMHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_MD_DOWNLOADS_MD_DOWNLOADS_DOM_HANDLER_H_
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
new file mode 100644
index 00000000000..947dfe8a049
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler_unittest.cc
@@ -0,0 +1,154 @@
+// 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 "chrome/browser/ui/webui/md_downloads/md_downloads_dom_handler.h"
+
+#include <vector>
+
+#include "chrome/browser/download/download_item_model.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/mock_download_item.h"
+#include "content/public/test/mock_download_manager.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_web_ui.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class TestMdDownloadsDOMHandler : public MdDownloadsDOMHandler {
+ public:
+ explicit TestMdDownloadsDOMHandler(content::DownloadManager* download_manager,
+ content::WebUI* web_ui)
+ : MdDownloadsDOMHandler(download_manager, web_ui),
+ danger_prompt_count_(0) {}
+
+ using MdDownloadsDOMHandler::set_web_ui;
+ using MdDownloadsDOMHandler::FinalizeRemovals;
+ using MdDownloadsDOMHandler::RemoveDownloads;
+ using MdDownloadsDOMHandler::SaveDownload;
+
+ int danger_prompt_count() { return danger_prompt_count_; }
+
+ private:
+ void ShowDangerPrompt(content::DownloadItem* dangerous) override {
+ danger_prompt_count_++;
+ }
+
+ void DangerPromptDone(int download_id,
+ DownloadDangerPrompt::Action action) override {}
+
+ int danger_prompt_count_;
+};
+
+} // namespace
+
+// A fixture to test MdDownloadsDOMHandler.
+class MdDownloadsDOMHandlerTest : public testing::Test {
+ public:
+ // testing::Test:
+ void SetUp() override {
+ ON_CALL(manager_, GetBrowserContext())
+ .WillByDefault(testing::Return(&profile_));
+ }
+
+ TestingProfile* profile() { return &profile_; }
+ content::MockDownloadManager* manager() { return &manager_; }
+ content::TestWebUI* web_ui() { return &web_ui_; }
+
+ private:
+ // NOTE: The initialization order of these members matters.
+ content::TestBrowserThreadBundle thread_bundle_;
+ TestingProfile profile_;
+
+ testing::NiceMock<content::MockDownloadManager> manager_;
+ content::TestWebUI web_ui_;
+};
+
+TEST_F(MdDownloadsDOMHandlerTest, ChecksForRemovedFiles) {
+ EXPECT_CALL(*manager(), CheckForHistoryFilesRemoval());
+ TestMdDownloadsDOMHandler handler(manager(), web_ui());
+
+ testing::Mock::VerifyAndClear(manager());
+
+ EXPECT_CALL(*manager(), CheckForHistoryFilesRemoval());
+ handler.OnJavascriptDisallowed();
+}
+
+TEST_F(MdDownloadsDOMHandlerTest, HandleGetDownloads) {
+ TestMdDownloadsDOMHandler handler(manager(), web_ui());
+ handler.set_web_ui(web_ui());
+
+ base::ListValue empty_search_terms;
+ handler.HandleGetDownloads(&empty_search_terms);
+
+ EXPECT_EQ(1U, web_ui()->call_data().size());
+ EXPECT_EQ("downloads.Manager.insertItems",
+ web_ui()->call_data()[0]->function_name());
+}
+
+TEST_F(MdDownloadsDOMHandlerTest, ClearAll) {
+ std::vector<content::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));
+ downloads.push_back(&in_progress);
+
+ // Dangerous items should be removed (regardless of state).
+ testing::StrictMock<content::MockDownloadItem> dangerous;
+ EXPECT_CALL(dangerous, IsDangerous()).WillOnce(testing::Return(true));
+ EXPECT_CALL(dangerous, Remove());
+ downloads.push_back(&dangerous);
+
+ // Completed items should be marked as hidden from the shelf.
+ 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, GetId()).WillOnce(testing::Return(1));
+ EXPECT_CALL(completed, UpdateObservers());
+ downloads.push_back(&completed);
+
+ ASSERT_TRUE(DownloadItemModel(&completed).ShouldShowInShelf());
+
+ TestMdDownloadsDOMHandler handler(manager(), web_ui());
+ handler.RemoveDownloads(downloads);
+
+ // Ensure |completed| has been "soft removed" (i.e. can be revived).
+ EXPECT_FALSE(DownloadItemModel(&completed).ShouldShowInShelf());
+
+ // Make sure |completed| actually get removed when removals are "finalized".
+ EXPECT_CALL(*manager(), GetDownload(1)).WillOnce(testing::Return(&completed));
+ EXPECT_CALL(completed, Remove());
+ handler.FinalizeRemovals();
+}
+
+TEST_F(MdDownloadsDOMHandlerTest, HandleSaveDownload) {
+ // When user chooses to recover a download, download danger prompt should NOT
+ // be shown if download danger type is DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE.
+ testing::StrictMock<content::MockDownloadItem> dangerous_file_type;
+ EXPECT_CALL(dangerous_file_type, GetDangerType())
+ .WillRepeatedly(
+ testing::Return(content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE));
+ EXPECT_CALL(dangerous_file_type, GetId())
+ .WillOnce(testing::Return(uint32_t()));
+ TestMdDownloadsDOMHandler handler(manager(), web_ui());
+ EXPECT_EQ(0, handler.danger_prompt_count());
+ handler.SaveDownload(&dangerous_file_type);
+ EXPECT_EQ(0, handler.danger_prompt_count());
+
+ // For other download danger types, download danger prompt should
+ // be shown.
+ testing::StrictMock<content::MockDownloadItem> malicious_download;
+ EXPECT_CALL(malicious_download, GetDangerType())
+ .WillRepeatedly(
+ testing::Return(content::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
new file mode 100644
index 00000000000..f2da6b7eb21
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_ui.cc
@@ -0,0 +1,168 @@
+// 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/ui/webui/md_downloads/md_downloads_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/singleton.h"
+#include "base/strings/string_piece.h"
+#include "base/threading/thread.h"
+#include "base/values.h"
+#include "chrome/browser/defaults.h"
+#include "chrome/browser/profiles/profile.h"
+#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/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"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/theme_resources.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "ui/base/resource/resource_bundle.h"
+
+using content::BrowserContext;
+using content::DownloadManager;
+using content::WebContents;
+
+namespace {
+
+content::WebUIDataSource* CreateDownloadsUIHTMLSource(Profile* profile) {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIDownloadsHost);
+
+ source->AddLocalizedString("title", IDS_DOWNLOAD_TITLE);
+ source->AddLocalizedString("searchResultsFor", IDS_DOWNLOAD_SEARCHRESULTSFOR);
+ source->AddLocalizedString("downloads", IDS_DOWNLOAD_TITLE);
+
+ source->AddLocalizedString("clearAll", IDS_DOWNLOAD_LINK_CLEAR_ALL);
+ source->AddLocalizedString("clearSearch", IDS_DOWNLOAD_CLEAR_SEARCH);
+ source->AddLocalizedString("openDownloadsFolder",
+ IDS_DOWNLOAD_LINK_OPEN_DOWNLOADS_FOLDER);
+ source->AddLocalizedString("moreActions", IDS_DOWNLOAD_MORE_ACTIONS);
+ source->AddLocalizedString("search", IDS_MD_DOWNLOAD_SEARCH);
+
+
+ // No results message that shows instead of the downloads list.
+ source->AddLocalizedString("noDownloads", IDS_MD_DOWNLOAD_NO_DOWNLOADS);
+ source->AddLocalizedString("noSearchResults",
+ IDS_DOWNLOAD_NO_SEARCH_RESULTS);
+
+ // Status.
+ source->AddLocalizedString("statusCancelled", IDS_DOWNLOAD_TAB_CANCELLED);
+ source->AddLocalizedString("statusRemoved", IDS_DOWNLOAD_FILE_REMOVED);
+
+ // Dangerous file.
+ source->AddLocalizedString("dangerFileDesc",
+ IDS_BLOCK_REASON_GENERIC_DOWNLOAD);
+ source->AddLocalizedString("dangerDownloadDesc",
+ IDS_BLOCK_REASON_DANGEROUS_DOWNLOAD);
+ source->AddLocalizedString("dangerUncommonDesc",
+ IDS_BLOCK_REASON_UNCOMMON_DOWNLOAD);
+ source->AddLocalizedString("dangerSettingsDesc",
+ IDS_BLOCK_REASON_UNWANTED_DOWNLOAD);
+ source->AddLocalizedString("dangerSave", IDS_CONFIRM_DOWNLOAD);
+ source->AddLocalizedString("dangerRestore", IDS_CONFIRM_DOWNLOAD_RESTORE);
+ source->AddLocalizedString("dangerDiscard", IDS_DISCARD_DOWNLOAD);
+
+ // Controls.
+ source->AddLocalizedString("controlPause", IDS_DOWNLOAD_LINK_PAUSE);
+ if (browser_defaults::kDownloadPageHasShowInFolder)
+ source->AddLocalizedString("controlShowInFolder", IDS_DOWNLOAD_LINK_SHOW);
+ source->AddLocalizedString("controlCancel", IDS_DOWNLOAD_LINK_CANCEL);
+ source->AddLocalizedString("controlResume", IDS_DOWNLOAD_LINK_RESUME);
+ source->AddLocalizedString("controlRemoveFromList",
+ IDS_DOWNLOAD_LINK_REMOVE);
+ source->AddLocalizedString("controlRetry", IDS_MD_DOWNLOAD_LINK_RETRY);
+ source->AddLocalizedString("controlledByUrl",
+ IDS_DOWNLOAD_BY_EXTENSION_URL);
+
+ PrefService* prefs = profile->GetPrefs();
+ source->AddBoolean("allowDeletingHistory",
+ prefs->GetBoolean(prefs::kAllowDeletingBrowserHistory) &&
+ !profile->IsSupervised());
+
+ source->AddLocalizedString("inIncognito", IDS_DOWNLOAD_IN_INCOGNITO);
+
+ source->AddResourcePath("1x/incognito_marker.png",
+ IDR_MD_DOWNLOADS_1X_INCOGNITO_MARKER_PNG);
+ source->AddResourcePath("2x/incognito_marker.png",
+ IDR_MD_DOWNLOADS_2X_INCOGNITO_MARKER_PNG);
+ source->AddResourcePath("1x/no_downloads.png",
+ IDR_MD_DOWNLOADS_1X_NO_DOWNLOADS_PNG);
+ source->AddResourcePath("2x/no_downloads.png",
+ IDR_MD_DOWNLOADS_2X_NO_DOWNLOADS_PNG);
+
+#if BUILDFLAG(USE_VULCANIZE)
+ std::unordered_set<std::string> exclude_from_gzip;
+ exclude_from_gzip.insert("1x/incognito_marker.png");
+ exclude_from_gzip.insert("2x/incognito_marker.png");
+ exclude_from_gzip.insert("1x/no_downloads.png");
+ exclude_from_gzip.insert("2x/no_downloads.png");
+ source->UseGzip(exclude_from_gzip);
+
+ source->AddResourcePath("crisper.js", IDR_MD_DOWNLOADS_CRISPER_JS);
+ source->SetDefaultResource(IDR_MD_DOWNLOADS_VULCANIZED_HTML);
+#else
+ source->AddResourcePath("action_service.html",
+ IDR_MD_DOWNLOADS_ACTION_SERVICE_HTML);
+ source->AddResourcePath("action_service.js",
+ IDR_MD_DOWNLOADS_ACTION_SERVICE_JS);
+ source->AddResourcePath("constants.html", IDR_MD_DOWNLOADS_CONSTANTS_HTML);
+ source->AddResourcePath("constants.js", IDR_MD_DOWNLOADS_CONSTANTS_JS);
+ source->AddResourcePath("downloads.js", IDR_MD_DOWNLOADS_DOWNLOADS_JS);
+ source->AddResourcePath("i18n_setup.html", IDR_MD_DOWNLOADS_I18N_SETUP_HTML);
+ source->AddResourcePath("icons.html", IDR_MD_DOWNLOADS_ICONS_HTML);
+ source->AddResourcePath("item.html", IDR_MD_DOWNLOADS_ITEM_HTML);
+ source->AddResourcePath("item.js", IDR_MD_DOWNLOADS_ITEM_JS);
+ source->AddResourcePath("manager.html", IDR_MD_DOWNLOADS_MANAGER_HTML);
+ source->AddResourcePath("manager.js", IDR_MD_DOWNLOADS_MANAGER_JS);
+ source->AddResourcePath("toolbar.html", IDR_MD_DOWNLOADS_TOOLBAR_HTML);
+ source->AddResourcePath("toolbar.js", IDR_MD_DOWNLOADS_TOOLBAR_JS);
+ source->SetDefaultResource(IDR_MD_DOWNLOADS_DOWNLOADS_HTML);
+#endif
+
+ source->SetJsonPath("strings.js");
+
+ return source;
+}
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// MdDownloadsUI
+//
+///////////////////////////////////////////////////////////////////////////////
+
+MdDownloadsUI::MdDownloadsUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ DownloadManager* dlm = BrowserContext::GetDownloadManager(profile);
+
+ web_ui->AddMessageHandler(
+ base::MakeUnique<MdDownloadsDOMHandler>(dlm, web_ui));
+ web_ui->AddMessageHandler(base::MakeUnique<MetricsHandler>());
+
+ // Set up the chrome://downloads/ source.
+ content::WebUIDataSource* source = CreateDownloadsUIHTMLSource(profile);
+ content::WebUIDataSource::Add(profile, source);
+ ThemeSource* theme = new ThemeSource(profile);
+ content::URLDataSource::Add(profile, theme);
+}
+
+// static
+base::RefCountedMemory* MdDownloadsUI::GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor) {
+ return ResourceBundle::GetSharedInstance().
+ LoadDataResourceBytesForScale(IDR_DOWNLOADS_FAVICON, scale_factor);
+}
diff --git a/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_ui.h b/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_ui.h
new file mode 100644
index 00000000000..37ce107a7b3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_downloads/md_downloads_ui.h
@@ -0,0 +1,27 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MD_DOWNLOADS_MD_DOWNLOADS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_MD_DOWNLOADS_MD_DOWNLOADS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "ui/base/layout.h"
+
+namespace base {
+class RefCountedMemory;
+}
+
+class MdDownloadsUI : public content::WebUIController {
+ public:
+ explicit MdDownloadsUI(content::WebUI* web_ui);
+
+ static base::RefCountedMemory* GetFaviconResourceBytes(
+ ui::ScaleFactor scale_factor);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MdDownloadsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_MD_DOWNLOADS_MD_DOWNLOADS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/md_feedback/OWNERS b/chromium/chrome/browser/ui/webui/md_feedback/OWNERS
new file mode 100644
index 00000000000..02b5b4649de
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_feedback/OWNERS
@@ -0,0 +1 @@
+apacible@chromium.org
diff --git a/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_dialog_controller.cc b/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_dialog_controller.cc
new file mode 100644
index 00000000000..613da1bf3be
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_dialog_controller.cc
@@ -0,0 +1,84 @@
+// 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 "chrome/browser/ui/webui/md_feedback/md_feedback_dialog_controller.h"
+
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/web_dialogs/web_dialog_delegate.h"
+#include "url/gurl.h"
+
+namespace {
+
+// The WebDialogDelegate that specifies what the MD Feedback dialog will look
+// like.
+class MdFeedbackDialogDelegate : public ui::WebDialogDelegate {
+ public:
+ MdFeedbackDialogDelegate() {}
+ ~MdFeedbackDialogDelegate() override {}
+
+ ui::ModalType GetDialogModalType() const override {
+ return ui::MODAL_TYPE_SYSTEM;
+ }
+
+ base::string16 GetDialogTitle() const override {
+ return l10n_util::GetStringUTF16(IDS_MD_FEEDBACK_DIALOG_TITLE);
+ }
+
+ GURL GetDialogContentURL() const override {
+ return GURL(chrome::kChromeUIFeedbackURL);
+ }
+
+ void GetWebUIMessageHandlers(
+ std::vector<content::WebUIMessageHandler*>* handlers) const override {}
+
+ void GetDialogSize(gfx::Size* size) const override {
+ // TODO(apacible): Update when WebUI sizing behavior is finalized.
+ size->SetSize(400, 600);
+ }
+
+ std::string GetDialogArgs() const override {
+ return std::string();
+ }
+
+ void OnDialogClosed(const std::string& json_retval) override {
+ delete this;
+ }
+
+ void OnCloseContents(
+ content::WebContents* source, bool* out_close_dialog) override {}
+
+ bool ShouldShowDialogTitle() const override {
+ return false;
+ }
+
+ bool HandleContextMenu(const content::ContextMenuParams& params) override {
+ // Do not show the contextual menu.
+ return true;
+ }
+};
+
+} // namespace
+
+// static
+MdFeedbackDialogController* MdFeedbackDialogController::GetInstance() {
+ return base::Singleton<MdFeedbackDialogController>::get();
+}
+
+void MdFeedbackDialogController::Show(
+ content::BrowserContext* browser_context) {
+ // TODO(apacible): Platform dependent implementations.
+#if !defined(OS_MACOSX)
+ // TODO(apacible): If a feedback dialog is already open, bring that dialog
+ // to the front rather than creating a new dialog.
+ chrome::ShowWebDialog(nullptr, browser_context,
+ new MdFeedbackDialogDelegate());
+#endif // !defined(OS_MACOSX)
+}
+
+MdFeedbackDialogController::MdFeedbackDialogController() {}
diff --git a/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_dialog_controller.h b/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_dialog_controller.h
new file mode 100644
index 00000000000..1251ba97637
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_dialog_controller.h
@@ -0,0 +1,31 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MD_FEEDBACK_MD_FEEDBACK_DIALOG_CONTROLLER_H_
+#define CHROME_BROWSER_UI_WEBUI_MD_FEEDBACK_MD_FEEDBACK_DIALOG_CONTROLLER_H_
+
+#include "base/memory/singleton.h"
+#include "content/public/browser/browser_context.h"
+
+// Manages MD Feedback dialog creation, destruction, and showing a constrained
+// web dialog with the feedback contents. This controller is implemented as a
+// singleton.
+class MdFeedbackDialogController {
+ public:
+ static MdFeedbackDialogController* GetInstance();
+
+ // Show the feedback dialog. This WebUI dialog is not anchored to the browser
+ // window but tied to a profile.
+ void Show(content::BrowserContext* browser_context);
+
+ private:
+ friend struct base::DefaultSingletonTraits<MdFeedbackDialogController>;
+
+ MdFeedbackDialogController();
+ ~MdFeedbackDialogController() = default;
+
+ DISALLOW_COPY_AND_ASSIGN(MdFeedbackDialogController);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_MD_FEEDBACK_MD_FEEDBACK_DIALOG_CONTROLLER_H_
diff --git a/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_ui.cc b/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_ui.cc
new file mode 100644
index 00000000000..786e9911275
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_ui.cc
@@ -0,0 +1,80 @@
+// 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 "chrome/browser/ui/webui/md_feedback/md_feedback_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/ui/webui/md_feedback/md_feedback_webui_message_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+#if !defined(OS_MACOSX)
+#include "chrome/browser/ui/browser_dialogs.h"
+#endif
+
+namespace {
+
+content::WebUIDataSource* CreateMdFeedbackUIHTMLSource(Profile* profile) {
+ content::WebUIDataSource* html_source =
+ content::WebUIDataSource::Create(chrome::kChromeUIFeedbackHost);
+
+ // General strings.
+ html_source->AddLocalizedString("headingText",
+ IDS_MD_FEEDBACK_HEADING);
+#if defined(GOOGLE_CHROME_BUILD)
+ html_source->AddLocalizedString("privacyNote",
+ IDS_MD_FEEDBACK_PRIVACY_NOTE);
+#endif
+ html_source->AddLocalizedString("additionalInfoLabel",
+ IDS_MD_FEEDBACK_ADDITIONAL_INFO_LABEL);
+ // Input labels.
+ html_source->AddLocalizedString("emailLabel",
+ IDS_MD_FEEDBACK_USER_EMAIL_LABEL);
+ html_source->AddLocalizedString("openEndedLabel",
+ IDS_MD_FEEDBACK_OPEN_ENDED_LABEL);
+ html_source->AddLocalizedString("urlLabel",
+ IDS_MD_FEEDBACK_URL_LABEL);
+
+ // Buttons.
+ html_source->AddLocalizedString("cancelButton",
+ IDS_MD_FEEDBACK_CANCEL_BUTTON);
+ html_source->AddLocalizedString("sendReportButton",
+ IDS_MD_FEEDBACK_SEND_REPORT_BUTTON);
+
+ // Checkbox labels.
+ html_source->AddLocalizedString("includeScreenshotLabel",
+ IDS_MD_FEEDBACK_SCREENSHOT_LABEL);
+ html_source->AddLocalizedString("sendSystemInfoLabel",
+ IDS_MD_FEEDBACK_SEND_SYSTEM_INFO_LABEL);
+
+ // File resources.
+ html_source->AddResourcePath("feedback.js", IDR_MD_FEEDBACK_FEEDBACK_JS);
+
+ // Polymer resources.
+ html_source->AddResourcePath("feedback_container.html",
+ IDR_MD_FEEDBACK_FEEDBACK_CONTAINER_HTML);
+ html_source->AddResourcePath("feedback_container.js",
+ IDR_MD_FEEDBACK_FEEDBACK_CONTAINER_JS);
+
+ html_source->SetJsonPath("strings.js");
+ html_source->SetDefaultResource(IDR_MD_FEEDBACK_FEEDBACK_HTML);
+ return html_source;
+}
+
+} // namespace
+
+MdFeedbackUI::MdFeedbackUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui), profile_(Profile::FromWebUI(web_ui)) {
+ // Set up the chrome://feedback data html_source.
+ content::WebUIDataSource* html_source =
+ CreateMdFeedbackUIHTMLSource(profile_);
+ content::WebUIDataSource::Add(profile_, html_source);
+
+ web_ui->AddMessageHandler(
+ base::MakeUnique<MdFeedbackWebUIMessageHandler>(this));
+}
+
+MdFeedbackUI::~MdFeedbackUI() {}
diff --git a/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_ui.h b/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_ui.h
new file mode 100644
index 00000000000..feb25cf2571
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_ui.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MD_FEEDBACK_MD_FEEDBACK_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_MD_FEEDBACK_MD_FEEDBACK_UI_H_
+
+#include "base/macros.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace content {
+class BrowserContext;
+} // namespace content
+
+// The WebUI for chrome://feedback.
+class MdFeedbackUI : public content::WebUIController {
+ public:
+ explicit MdFeedbackUI(content::WebUI* web_ui);
+ ~MdFeedbackUI() override;
+
+ Profile* profile() { return profile_; }
+
+ private:
+ Profile* profile_;
+
+ DISALLOW_COPY_AND_ASSIGN(MdFeedbackUI);
+};
+
+// Create and show a web dialog with the MD feedback UI.
+// |browser_context| Is used to constructed the HTML dialog's WebContents.
+void ShowFeedbackWebDialog(
+ content::BrowserContext* browser_context);
+
+#endif // CHROME_BROWSER_UI_WEBUI_MD_FEEDBACK_MD_FEEDBACK_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_webui_message_handler.cc b/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_webui_message_handler.cc
new file mode 100644
index 00000000000..4f37a971713
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_webui_message_handler.cc
@@ -0,0 +1,67 @@
+// 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 "chrome/browser/ui/webui/md_feedback/md_feedback_webui_message_handler.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "chrome/browser/feedback/feedback_dialog_utils.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.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/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/md_feedback/md_feedback_ui.h"
+#include "components/signin/core/browser/signin_manager.h"
+
+namespace {
+
+// Message names.
+constexpr char kRequestData[] = "requestData";
+
+// JS function names.
+constexpr char kSetData[] = "Feedback.UI.setData";
+
+} // namespace
+
+MdFeedbackWebUIMessageHandler::MdFeedbackWebUIMessageHandler(
+ MdFeedbackUI* md_feedback_ui)
+ : md_feedback_ui_(md_feedback_ui) {
+ DCHECK(md_feedback_ui_);
+}
+
+MdFeedbackWebUIMessageHandler::~MdFeedbackWebUIMessageHandler() {}
+
+void MdFeedbackWebUIMessageHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ kRequestData, base::Bind(&MdFeedbackWebUIMessageHandler::OnRequestData,
+ base::Unretained(this)));
+}
+
+void MdFeedbackWebUIMessageHandler::OnRequestData(const base::ListValue* args) {
+ DVLOG(1) << "OnRequestData";
+ base::DictionaryValue data;
+
+ Profile* profile = md_feedback_ui_->profile();
+ // Do not launch feedback on an OTR profile.
+ profile = profile->GetOriginalProfile();
+ DCHECK(profile);
+
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetForProfile(profile);
+ DCHECK(signin_manager);
+ data.SetString("email", signin_manager->GetAuthenticatedAccountInfo().email);
+
+ GURL page_url = GURL();
+ Browser* browser = chrome::FindBrowserWithProfile(profile);
+ if (browser) {
+ page_url = chrome::GetTargetTabUrl(
+ browser->session_id().id(), browser->tab_strip_model()->active_index());
+ }
+
+ data.SetString("url", page_url.spec());
+
+ web_ui()->CallJavascriptFunctionUnsafe(kSetData, data);
+}
diff --git a/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_webui_message_handler.h b/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_webui_message_handler.h
new file mode 100644
index 00000000000..6b17db1efb1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_feedback/md_feedback_webui_message_handler.h
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MD_FEEDBACK_MD_FEEDBACK_WEBUI_MESSAGE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_MD_FEEDBACK_MD_FEEDBACK_WEBUI_MESSAGE_HANDLER_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+class MdFeedbackUI;
+
+// The handler for Javascript messages related to the feedback dialog.
+class MdFeedbackWebUIMessageHandler : public content::WebUIMessageHandler {
+ public:
+ explicit MdFeedbackWebUIMessageHandler(MdFeedbackUI* md_feedback_ui);
+ ~MdFeedbackWebUIMessageHandler() override;
+
+ private:
+ // WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ // Handler for a JavaScript message that retrieves data (e.g. user email) to
+ // populate the feedback form.
+ void OnRequestData(const base::ListValue* args);
+
+ MdFeedbackUI* md_feedback_ui_; // Not owned.
+
+ DISALLOW_COPY_AND_ASSIGN(MdFeedbackWebUIMessageHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_MD_FEEDBACK_MD_FEEDBACK_WEBUI_MESSAGE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/md_history_ui.cc b/chromium/chrome/browser/ui/webui/md_history_ui.cc
new file mode 100644
index 00000000000..381cde7e1f4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_history_ui.cc
@@ -0,0 +1,245 @@
+// 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.
+
+#include "chrome/browser/ui/webui/md_history_ui.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/ui/webui/browsing_history_handler.h"
+#include "chrome/browser/ui/webui/foreign_session_handler.h"
+#include "chrome/browser/ui/webui/history_login_handler.h"
+#include "chrome/browser/ui/webui/metrics_handler.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/locale_settings.h"
+#include "chrome/grit/theme_resources.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
+#include "components/search/search.h"
+#include "components/signin/core/browser/signin_manager.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"
+
+namespace {
+
+constexpr char kIsUserSignedInKey[] = "isUserSignedIn";
+constexpr char kShowMenuPromoKey[] = "showMenuPromo";
+
+bool IsUserSignedIn(Profile* profile) {
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetForProfile(profile);
+ return signin_manager && signin_manager->IsAuthenticated();
+}
+
+bool MenuPromoShown(Profile* profile) {
+ return profile->GetPrefs()->GetBoolean(prefs::kMdHistoryMenuPromoShown);
+}
+
+content::WebUIDataSource* CreateMdHistoryUIHTMLSource(Profile* profile,
+ bool use_test_title) {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIHistoryHost);
+
+ // Localized strings (alphabetical order).
+ source->AddLocalizedString("bookmarked", IDS_HISTORY_ENTRY_BOOKMARKED);
+ source->AddLocalizedString("cancel", IDS_CANCEL);
+ source->AddLocalizedString("clearBrowsingData",
+ IDS_CLEAR_BROWSING_DATA_TITLE);
+ source->AddLocalizedString("clearSearch", IDS_MD_HISTORY_CLEAR_SEARCH);
+ source->AddLocalizedString("closeMenuPromo", IDS_MD_HISTORY_CLOSE_MENU_PROMO);
+ source->AddLocalizedString("collapseSessionButton",
+ IDS_HISTORY_OTHER_SESSIONS_COLLAPSE_SESSION);
+ source->AddLocalizedString("delete", IDS_MD_HISTORY_DELETE);
+ source->AddLocalizedString("deleteConfirm",
+ IDS_HISTORY_DELETE_PRIOR_VISITS_CONFIRM_BUTTON);
+ source->AddLocalizedString("deleteSession",
+ IDS_HISTORY_OTHER_SESSIONS_HIDE_FOR_NOW);
+ source->AddLocalizedString("deleteWarning",
+ IDS_HISTORY_DELETE_PRIOR_VISITS_WARNING);
+ source->AddLocalizedString("entrySummary", IDS_HISTORY_ENTRY_SUMMARY);
+ source->AddLocalizedString("expandSessionButton",
+ IDS_HISTORY_OTHER_SESSIONS_EXPAND_SESSION);
+ source->AddLocalizedString("foundSearchResults",
+ IDS_HISTORY_FOUND_SEARCH_RESULTS);
+ source->AddLocalizedString("hasSyncedResults",
+ IDS_MD_HISTORY_HAS_SYNCED_RESULTS);
+ source->AddLocalizedString("hasSyncedResultsDescription",
+ IDS_MD_HISTORY_HAS_SYNCED_RESULTS_DESCRIPTION);
+ source->AddLocalizedString("historyMenuButton",
+ IDS_MD_HISTORY_HISTORY_MENU_DESCRIPTION);
+ source->AddLocalizedString("historyMenuItem",
+ IDS_MD_HISTORY_HISTORY_MENU_ITEM);
+ source->AddLocalizedString("itemsSelected", IDS_MD_HISTORY_ITEMS_SELECTED);
+ source->AddLocalizedString("loading", IDS_HISTORY_LOADING);
+ source->AddLocalizedString("menuPromo", IDS_MD_HISTORY_MENU_PROMO);
+ source->AddLocalizedString("moreActionsButton",
+ IDS_HISTORY_ACTION_MENU_DESCRIPTION);
+ source->AddLocalizedString("moreFromSite", IDS_HISTORY_MORE_FROM_SITE);
+ source->AddLocalizedString("openAll", IDS_HISTORY_OTHER_SESSIONS_OPEN_ALL);
+ source->AddLocalizedString("openTabsMenuItem",
+ IDS_MD_HISTORY_OPEN_TABS_MENU_ITEM);
+ source->AddLocalizedString("noResults", IDS_HISTORY_NO_RESULTS);
+ source->AddLocalizedString("noSearchResults", IDS_HISTORY_NO_SEARCH_RESULTS);
+ source->AddLocalizedString("noSyncedResults",
+ IDS_MD_HISTORY_NO_SYNCED_RESULTS);
+ source->AddLocalizedString("removeBookmark", IDS_HISTORY_REMOVE_BOOKMARK);
+ source->AddLocalizedString("removeFromHistory", IDS_HISTORY_REMOVE_PAGE);
+ source->AddLocalizedString("removeSelected",
+ IDS_HISTORY_REMOVE_SELECTED_ITEMS);
+ source->AddLocalizedString("searchPrompt", IDS_MD_HISTORY_SEARCH_PROMPT);
+ source->AddLocalizedString("searchResult", IDS_HISTORY_SEARCH_RESULT);
+ source->AddLocalizedString("searchResults", IDS_HISTORY_SEARCH_RESULTS);
+ source->AddLocalizedString("signInButton", IDS_MD_HISTORY_SIGN_IN_BUTTON);
+ source->AddLocalizedString("signInPromo", IDS_MD_HISTORY_SIGN_IN_PROMO);
+ source->AddLocalizedString("signInPromoDesc",
+ IDS_MD_HISTORY_SIGN_IN_PROMO_DESC);
+ if (use_test_title)
+ source->AddString("title", "MD History");
+ else
+ source->AddLocalizedString("title", IDS_HISTORY_TITLE);
+
+ source->AddString(
+ "sidebarFooter",
+ l10n_util::GetStringFUTF16(
+ IDS_HISTORY_OTHER_FORMS_OF_HISTORY,
+ l10n_util::GetStringUTF16(
+ IDS_SETTINGS_CLEAR_DATA_WEB_HISTORY_URL_IN_HISTORY)));
+
+ PrefService* prefs = profile->GetPrefs();
+ bool allow_deleting_history =
+ prefs->GetBoolean(prefs::kAllowDeletingBrowserHistory);
+ source->AddBoolean("allowDeletingHistory", allow_deleting_history);
+
+ source->AddBoolean(kShowMenuPromoKey, !MenuPromoShown(profile));
+ source->AddBoolean("isGuestSession", profile->IsGuestSession());
+
+ source->AddBoolean(kIsUserSignedInKey, IsUserSignedIn(profile));
+
+ struct UncompressedResource {
+ const char* path;
+ int idr;
+ };
+ const UncompressedResource uncompressed_resources[] = {
+ {"constants.html", IDR_MD_HISTORY_CONSTANTS_HTML},
+ {"constants.js", IDR_MD_HISTORY_CONSTANTS_JS},
+ {"history.js", IDR_MD_HISTORY_HISTORY_JS},
+ {"images/100/sign_in_promo.jpg",
+ IDR_MD_HISTORY_IMAGES_100_SIGN_IN_PROMO_JPG},
+ {"images/200/sign_in_promo.jpg",
+ IDR_MD_HISTORY_IMAGES_200_SIGN_IN_PROMO_JPG},
+#if !BUILDFLAG(USE_VULCANIZE)
+ {"app.html", IDR_MD_HISTORY_APP_HTML},
+ {"app.js", IDR_MD_HISTORY_APP_JS},
+ {"browser_service.html", IDR_MD_HISTORY_BROWSER_SERVICE_HTML},
+ {"browser_service.js", IDR_MD_HISTORY_BROWSER_SERVICE_JS},
+ {"history_item.html", IDR_MD_HISTORY_HISTORY_ITEM_HTML},
+ {"history_item.js", IDR_MD_HISTORY_HISTORY_ITEM_JS},
+ {"history_list.html", IDR_MD_HISTORY_HISTORY_LIST_HTML},
+ {"history_list.js", IDR_MD_HISTORY_HISTORY_LIST_JS},
+ {"history_toolbar.html", IDR_MD_HISTORY_HISTORY_TOOLBAR_HTML},
+ {"history_toolbar.js", IDR_MD_HISTORY_HISTORY_TOOLBAR_JS},
+ {"icons.html", IDR_MD_HISTORY_ICONS_HTML},
+ {"lazy_load.html", IDR_MD_HISTORY_LAZY_LOAD_HTML},
+ {"query_manager.html", IDR_MD_HISTORY_QUERY_MANAGER_HTML},
+ {"query_manager.js", IDR_MD_HISTORY_QUERY_MANAGER_JS},
+ {"router.html", IDR_MD_HISTORY_ROUTER_HTML},
+ {"router.js", IDR_MD_HISTORY_ROUTER_JS},
+ {"searched_label.html", IDR_MD_HISTORY_SEARCHED_LABEL_HTML},
+ {"searched_label.js", IDR_MD_HISTORY_SEARCHED_LABEL_JS},
+ {"shared_style.html", IDR_MD_HISTORY_SHARED_STYLE_HTML},
+ {"shared_vars.html", IDR_MD_HISTORY_SHARED_VARS_HTML},
+ {"side_bar.html", IDR_MD_HISTORY_SIDE_BAR_HTML},
+ {"side_bar.js", IDR_MD_HISTORY_SIDE_BAR_JS},
+ {"synced_device_card.html", IDR_MD_HISTORY_SYNCED_DEVICE_CARD_HTML},
+ {"synced_device_card.js", IDR_MD_HISTORY_SYNCED_DEVICE_CARD_JS},
+ {"synced_device_manager.html", IDR_MD_HISTORY_SYNCED_DEVICE_MANAGER_HTML},
+ {"synced_device_manager.js", IDR_MD_HISTORY_SYNCED_DEVICE_MANAGER_JS},
+#endif
+ };
+
+ std::unordered_set<std::string> exclude_from_gzip;
+ for (size_t i = 0; i < arraysize(uncompressed_resources); ++i) {
+ const UncompressedResource& resource = uncompressed_resources[i];
+ source->AddResourcePath(resource.path, resource.idr);
+ exclude_from_gzip.insert(resource.path);
+ }
+ source->UseGzip(exclude_from_gzip);
+
+#if BUILDFLAG(USE_VULCANIZE)
+ source->AddResourcePath("app.html",
+ IDR_MD_HISTORY_APP_VULCANIZED_HTML);
+ source->AddResourcePath("app.crisper.js",
+ IDR_MD_HISTORY_APP_CRISPER_JS);
+ source->AddResourcePath("lazy_load.html",
+ IDR_MD_HISTORY_LAZY_LOAD_VULCANIZED_HTML);
+ source->AddResourcePath("lazy_load.crisper.js",
+ IDR_MD_HISTORY_LAZY_LOAD_CRISPER_JS);
+#endif
+
+ source->SetDefaultResource(IDR_MD_HISTORY_HISTORY_HTML);
+ source->SetJsonPath("strings.js");
+
+ return source;
+}
+
+} // namespace
+
+bool MdHistoryUI::use_test_title_ = false;
+
+MdHistoryUI::MdHistoryUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource* data_source =
+ CreateMdHistoryUIHTMLSource(profile, use_test_title_);
+ content::WebUIDataSource::Add(profile, data_source);
+
+ web_ui->AddMessageHandler(base::MakeUnique<BrowsingHistoryHandler>());
+ web_ui->AddMessageHandler(base::MakeUnique<MetricsHandler>());
+
+ if (search::IsInstantExtendedAPIEnabled()) {
+ web_ui->AddMessageHandler(
+ base::MakeUnique<browser_sync::ForeignSessionHandler>());
+ web_ui->AddMessageHandler(base::MakeUnique<HistoryLoginHandler>(
+ base::Bind(&MdHistoryUI::UpdateDataSource, base::Unretained(this))));
+ }
+
+ web_ui->RegisterMessageCallback("menuPromoShown",
+ base::Bind(&MdHistoryUI::HandleMenuPromoShown, base::Unretained(this)));
+}
+
+MdHistoryUI::~MdHistoryUI() {}
+
+void MdHistoryUI::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterBooleanPref(prefs::kMdHistoryMenuPromoShown, false,
+ user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+}
+
+void MdHistoryUI::UpdateDataSource() {
+ CHECK(web_ui());
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+ std::unique_ptr<base::DictionaryValue> update(new base::DictionaryValue);
+ update->SetBoolean(kIsUserSignedInKey, IsUserSignedIn(profile));
+ update->SetBoolean(kShowMenuPromoKey, !MenuPromoShown(profile));
+
+ content::WebUIDataSource::Update(profile, chrome::kChromeUIHistoryHost,
+ std::move(update));
+}
+
+void MdHistoryUI::HandleMenuPromoShown(const base::ListValue* args) {
+ Profile::FromWebUI(web_ui())->GetPrefs()->SetBoolean(
+ prefs::kMdHistoryMenuPromoShown, true);
+ UpdateDataSource();
+}
diff --git a/chromium/chrome/browser/ui/webui/md_history_ui.h b/chromium/chrome/browser/ui/webui/md_history_ui.h
new file mode 100644
index 00000000000..709cef0d22e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/md_history_ui.h
@@ -0,0 +1,40 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MD_HISTORY_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_MD_HISTORY_UI_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+class MdHistoryUI : public content::WebUIController {
+ public:
+ explicit MdHistoryUI(content::WebUI* web_ui);
+ ~MdHistoryUI() override;
+
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(ContinueWhereILeftOffTest, MDHistoryUpgrade);
+
+ static bool use_test_title_;
+
+ void UpdateDataSource();
+
+ // Handler for the "menuPromoShown" message from the page. No arguments.
+ void HandleMenuPromoShown(const base::ListValue* args);
+
+ DISALLOW_COPY_AND_ASSIGN(MdHistoryUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_MD_HISTORY_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/media/webrtc_logs_ui.cc b/chromium/chrome/browser/ui/webui/media/webrtc_logs_ui.cc
new file mode 100644
index 00000000000..5a3355fbab2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media/webrtc_logs_ui.cc
@@ -0,0 +1,225 @@
+// 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/media/webrtc_logs_ui.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/i18n/time_formatting.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#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"
+#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
+#include "components/upload_list/upload_list.h"
+#include "components/version_info/version_info.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#endif
+
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace {
+
+content::WebUIDataSource* CreateWebRtcLogsUIHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIWebRtcLogsHost);
+
+ source->AddLocalizedString("webrtcLogsTitle", IDS_WEBRTC_LOGS_TITLE);
+ source->AddLocalizedString("webrtcLogCountFormat",
+ IDS_WEBRTC_LOGS_LOG_COUNT_BANNER_FORMAT);
+ source->AddLocalizedString("webrtcLogHeaderFormat",
+ IDS_WEBRTC_LOGS_LOG_HEADER_FORMAT);
+ source->AddLocalizedString("webrtcLogLocalFileLabelFormat",
+ IDS_WEBRTC_LOGS_LOG_LOCAL_FILE_LABEL_FORMAT);
+ source->AddLocalizedString("noLocalLogFileMessage",
+ IDS_WEBRTC_LOGS_NO_LOCAL_LOG_FILE_MESSAGE);
+ source->AddLocalizedString("webrtcLogUploadTimeFormat",
+ IDS_WEBRTC_LOGS_LOG_UPLOAD_TIME_FORMAT);
+ source->AddLocalizedString("webrtcLogReportIdFormat",
+ IDS_WEBRTC_LOGS_LOG_REPORT_ID_FORMAT);
+ source->AddLocalizedString("bugLinkText", IDS_WEBRTC_LOGS_BUG_LINK_LABEL);
+ source->AddLocalizedString("webrtcLogNotUploadedMessage",
+ IDS_WEBRTC_LOGS_LOG_NOT_UPLOADED_MESSAGE);
+ source->AddLocalizedString("noLogsMessage",
+ IDS_WEBRTC_LOGS_NO_LOGS_MESSAGE);
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("webrtc_logs.js", IDR_WEBRTC_LOGS_JS);
+ source->SetDefaultResource(IDR_WEBRTC_LOGS_HTML);
+ return source;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// WebRtcLogsDOMHandler
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// The handler for Javascript messages for the chrome://webrtc-logs/ page.
+class WebRtcLogsDOMHandler : public WebUIMessageHandler,
+ public UploadList::Delegate {
+ public:
+ explicit WebRtcLogsDOMHandler(Profile* profile);
+ ~WebRtcLogsDOMHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // UploadList::Delegate implemenation.
+ void OnUploadListAvailable() override;
+
+ private:
+ // Asynchronously fetches the list of upload WebRTC logs. Called from JS.
+ void HandleRequestWebRtcLogs(const base::ListValue* args);
+
+ // Sends the recently uploaded logs list JS.
+ void UpdateUI();
+
+ // Loads, parses and stores the list of uploaded WebRTC logs.
+ scoped_refptr<UploadList> upload_list_;
+
+ // The directory where the logs are stored.
+ base::FilePath log_dir_;
+
+ // Set when |upload_list_| has finished populating the list of logs.
+ bool list_available_;
+
+ // Set when the webpage wants to update the list (on the webpage) but
+ // |upload_list_| hasn't finished populating the list of logs yet.
+ bool js_request_pending_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebRtcLogsDOMHandler);
+};
+
+WebRtcLogsDOMHandler::WebRtcLogsDOMHandler(Profile* profile)
+ : log_dir_(
+ WebRtcLogList::GetWebRtcLogDirectoryForProfile(profile->GetPath())),
+ list_available_(false),
+ js_request_pending_(false) {
+ upload_list_ = WebRtcLogList::CreateWebRtcLogList(this, profile);
+}
+
+WebRtcLogsDOMHandler::~WebRtcLogsDOMHandler() {
+ upload_list_->ClearDelegate();
+}
+
+void WebRtcLogsDOMHandler::RegisterMessages() {
+ upload_list_->LoadUploadListAsynchronously();
+
+ web_ui()->RegisterMessageCallback("requestWebRtcLogsList",
+ base::Bind(&WebRtcLogsDOMHandler::HandleRequestWebRtcLogs,
+ base::Unretained(this)));
+}
+
+void WebRtcLogsDOMHandler::HandleRequestWebRtcLogs(
+ const base::ListValue* args) {
+ if (list_available_)
+ UpdateUI();
+ else
+ js_request_pending_ = true;
+}
+
+void WebRtcLogsDOMHandler::OnUploadListAvailable() {
+ list_available_ = true;
+ if (js_request_pending_)
+ UpdateUI();
+}
+
+void WebRtcLogsDOMHandler::UpdateUI() {
+ std::vector<UploadList::UploadInfo> uploads;
+ upload_list_->GetUploads(50, &uploads);
+
+ base::ListValue upload_list;
+ for (std::vector<UploadList::UploadInfo>::iterator i = uploads.begin();
+ i != uploads.end();
+ ++i) {
+ std::unique_ptr<base::DictionaryValue> upload(new base::DictionaryValue());
+ upload->SetString("id", i->upload_id);
+
+ base::string16 value_w;
+ if (!i->upload_time.is_null())
+ value_w = base::TimeFormatFriendlyDateAndTime(i->upload_time);
+ upload->SetString("upload_time", value_w);
+
+ base::FilePath::StringType value;
+ if (!i->local_id.empty())
+ value = log_dir_.AppendASCII(i->local_id)
+ .AddExtension(FILE_PATH_LITERAL(".gz")).value();
+ upload->SetString("local_file", value);
+
+ // In october 2015, capture time was added to the log list, previously the
+ // local ID was used as capture time. The local ID has however changed so
+ // that it might not be a time. We fall back on the local ID if it traslates
+ // to a time within reasonable bounds, otherwise we fall back on the upload
+ // time.
+ // TODO(grunell): Use |capture_time| only.
+ if (!i->capture_time.is_null()) {
+ value_w = base::TimeFormatFriendlyDateAndTime(i->capture_time);
+ } else {
+ // Fall back on local ID as time. We need to check that it's within
+ // resonable bounds, since the ID may not represent time. Check between
+ // 2012 when the feature was introduced and now.
+ double seconds_since_epoch;
+ if (base::StringToDouble(i->local_id, &seconds_since_epoch)) {
+ base::Time capture_time = base::Time::FromDoubleT(seconds_since_epoch);
+ const base::Time::Exploded lower_limit = {2012, 1, 0, 1, 0, 0, 0, 0};
+ base::Time out_time;
+ bool conversion_success =
+ base::Time::FromUTCExploded(lower_limit, &out_time);
+ DCHECK(conversion_success);
+ if (capture_time > out_time && capture_time < base::Time::Now()) {
+ value_w = base::TimeFormatFriendlyDateAndTime(capture_time);
+ }
+ }
+ }
+ // If we haven't set |value_w| above, we fall back on the upload time, which
+ // was already in the variable. In case it's empty set the string to
+ // inform that the time is unknown.
+ if (value_w.empty())
+ value_w = base::string16(base::ASCIIToUTF16("(unknown time)"));
+ upload->SetString("capture_time", value_w);
+
+ upload_list.Append(std::move(upload));
+ }
+
+ base::Value version(version_info::GetVersionNumber());
+
+ web_ui()->CallJavascriptFunctionUnsafe("updateWebRtcLogsList", upload_list,
+ version);
+}
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// WebRtcLogsUI
+//
+///////////////////////////////////////////////////////////////////////////////
+
+WebRtcLogsUI::WebRtcLogsUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ web_ui->AddMessageHandler(base::MakeUnique<WebRtcLogsDOMHandler>(profile));
+
+ // Set up the chrome://webrtc-logs/ source.
+ content::WebUIDataSource::Add(profile, CreateWebRtcLogsUIHTMLSource());
+}
diff --git a/chromium/chrome/browser/ui/webui/media/webrtc_logs_ui.h b/chromium/chrome/browser/ui/webui/media/webrtc_logs_ui.h
new file mode 100644
index 00000000000..a7e55990c70
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media/webrtc_logs_ui.h
@@ -0,0 +1,21 @@
+// 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_MEDIA_WEBRTC_LOGS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_MEDIA_WEBRTC_LOGS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "ui/base/layout.h"
+
+// The WebUI handler for chrome://webrtc-logs.
+class WebRtcLogsUI : public content::WebUIController {
+ public:
+ explicit WebRtcLogsUI(content::WebUI* web_ui);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WebRtcLogsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_MEDIA_WEBRTC_LOGS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/media_router/OWNERS b/chromium/chrome/browser/ui/webui/media_router/OWNERS
new file mode 100644
index 00000000000..37a19f6307e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/OWNERS
@@ -0,0 +1,8 @@
+apacible@chromium.org
+imcheng@chromium.org
+mfoltz@chromium.org
+
+# Fallback OWNER, if others are not available.
+wez@chromium.org
+
+# COMPONENT: Internals>Cast>UI
diff --git a/chromium/chrome/browser/ui/webui/media_router/README b/chromium/chrome/browser/ui/webui/media_router/README
new file mode 100644
index 00000000000..dd2bafeb157
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/README
@@ -0,0 +1,4 @@
+The Chrome Media Router allows applications to share media with connected
+screens.
+
+Design doc: http://www.chromium.org/developers/design-documents/media-router \ No newline at end of file
diff --git a/chromium/chrome/browser/ui/webui/media_router/cast_modes_with_media_sources.cc b/chromium/chrome/browser/ui/webui/media_router/cast_modes_with_media_sources.cc
new file mode 100644
index 00000000000..09aa90a0d7b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/cast_modes_with_media_sources.cc
@@ -0,0 +1,49 @@
+// 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 "base/stl_util.h"
+#include "chrome/browser/ui/webui/media_router/cast_modes_with_media_sources.h"
+
+namespace media_router {
+
+CastModesWithMediaSources::CastModesWithMediaSources() {}
+CastModesWithMediaSources::CastModesWithMediaSources(
+ CastModesWithMediaSources&& other) = default;
+CastModesWithMediaSources::~CastModesWithMediaSources() {}
+
+void CastModesWithMediaSources::AddSource(MediaCastMode cast_mode,
+ const MediaSource& source) {
+ cast_modes_[cast_mode].insert(source);
+}
+
+void CastModesWithMediaSources::RemoveSource(MediaCastMode cast_mode,
+ const MediaSource& source) {
+ const auto& cast_mode_it = cast_modes_.find(cast_mode);
+ if (cast_mode_it != cast_modes_.end()) {
+ auto& sources_for_cast_mode = cast_mode_it->second;
+ sources_for_cast_mode.erase(source);
+ if (sources_for_cast_mode.empty())
+ cast_modes_.erase(cast_mode);
+ }
+}
+
+bool CastModesWithMediaSources::HasSource(MediaCastMode cast_mode,
+ const MediaSource& source) const {
+ return base::ContainsKey(cast_modes_, cast_mode)
+ ? base::ContainsKey(cast_modes_.at(cast_mode), source)
+ : false;
+}
+
+CastModeSet CastModesWithMediaSources::GetCastModes() const {
+ CastModeSet cast_mode_set;
+ for (const auto& cast_mode_pair : cast_modes_)
+ cast_mode_set.insert(cast_mode_pair.first);
+ return cast_mode_set;
+}
+
+bool CastModesWithMediaSources::IsEmpty() const {
+ return cast_modes_.empty();
+}
+
+} // namespace media_router
diff --git a/chromium/chrome/browser/ui/webui/media_router/cast_modes_with_media_sources.h b/chromium/chrome/browser/ui/webui/media_router/cast_modes_with_media_sources.h
new file mode 100644
index 00000000000..d6fb466101f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/cast_modes_with_media_sources.h
@@ -0,0 +1,49 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_CAST_MODES_WITH_MEDIA_SOURCES_H_
+#define CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_CAST_MODES_WITH_MEDIA_SOURCES_H_
+
+#include <map>
+#include <unordered_set>
+
+#include "chrome/browser/ui/webui/media_router/media_cast_mode.h"
+#include "chrome/common/media_router/media_source.h"
+
+namespace media_router {
+
+// Contains information on cast modes and the sources associated with them.
+// Each cast mode contained has at least one source.
+class CastModesWithMediaSources {
+ public:
+ CastModesWithMediaSources();
+ CastModesWithMediaSources(CastModesWithMediaSources&& other);
+ ~CastModesWithMediaSources();
+
+ // Adds a source for the cast mode.
+ void AddSource(MediaCastMode cast_mode, const MediaSource& source);
+
+ // Removes a source from the cast mode. The cast mode will also get removed if
+ // it has no other sources. This is a no-op if the source is not found.
+ void RemoveSource(MediaCastMode cast_mode, const MediaSource& source);
+
+ // Returns true if the source for the cast mode is contained.
+ bool HasSource(MediaCastMode cast_mode, const MediaSource& source) const;
+
+ // Returns a set of all the cast modes contained.
+ CastModeSet GetCastModes() const;
+
+ // Returns true if there are no cast modes contained.
+ bool IsEmpty() const;
+
+ private:
+ std::map<MediaCastMode, std::unordered_set<MediaSource, MediaSource::Hash>>
+ cast_modes_;
+
+ DISALLOW_COPY_AND_ASSIGN(CastModesWithMediaSources);
+};
+
+} // namespace media_router
+
+#endif // CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_CAST_MODES_WITH_MEDIA_SOURCES_H_
diff --git a/chromium/chrome/browser/ui/webui/media_router/cast_modes_with_media_sources_unittest.cc b/chromium/chrome/browser/ui/webui/media_router/cast_modes_with_media_sources_unittest.cc
new file mode 100644
index 00000000000..a056b7a83c9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/cast_modes_with_media_sources_unittest.cc
@@ -0,0 +1,78 @@
+// 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 "chrome/browser/ui/webui/media_router/cast_modes_with_media_sources.h"
+
+#include "chrome/common/media_router/media_source_helper.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace media_router {
+
+TEST(MediaRouterCastModesWithMediaSourcesTest, AddAndRemoveSources) {
+ const MediaSource defaultSource1(MediaSourceForPresentationUrl(
+ GURL("http://www.example.com/presentation.html")));
+ const MediaSource defaultSource2(MediaSourceForPresentationUrl(
+ GURL("http://www.example.net/presentation.html")));
+ const MediaSource tabSourceA(MediaSourceForTab(123));
+ const CastModeSet castModeSetEmpty;
+ const CastModeSet castModeSetDefault({MediaCastMode::DEFAULT});
+ const CastModeSet castModeSetTab({MediaCastMode::TAB_MIRROR});
+ const CastModeSet castModeSetDefaultAndTab(
+ {MediaCastMode::DEFAULT, MediaCastMode::TAB_MIRROR});
+
+ CastModesWithMediaSources sources;
+ EXPECT_TRUE(sources.IsEmpty());
+ EXPECT_EQ(sources.GetCastModes(), castModeSetEmpty);
+
+ // After the below addition, |sources| should contain:
+ // [Default: 1]
+ sources.AddSource(MediaCastMode::DEFAULT, defaultSource1);
+ EXPECT_TRUE(sources.HasSource(MediaCastMode::DEFAULT, defaultSource1));
+ EXPECT_FALSE(sources.HasSource(MediaCastMode::DEFAULT, defaultSource2));
+ EXPECT_FALSE(sources.HasSource(MediaCastMode::TAB_MIRROR, defaultSource1));
+ EXPECT_FALSE(sources.IsEmpty());
+ EXPECT_EQ(sources.GetCastModes(), castModeSetDefault);
+
+ // Trying to remove non-existing sources should be no-op.
+ sources.RemoveSource(MediaCastMode::DEFAULT, defaultSource2);
+ sources.RemoveSource(MediaCastMode::TAB_MIRROR, defaultSource1);
+ sources.RemoveSource(MediaCastMode::TAB_MIRROR, tabSourceA);
+ EXPECT_TRUE(sources.HasSource(MediaCastMode::DEFAULT, defaultSource1));
+ EXPECT_EQ(sources.GetCastModes(), castModeSetDefault);
+
+ // [Default: 1; Tab: A]
+ sources.AddSource(MediaCastMode::TAB_MIRROR, tabSourceA);
+ EXPECT_TRUE(sources.HasSource(MediaCastMode::DEFAULT, defaultSource1));
+ EXPECT_TRUE(sources.HasSource(MediaCastMode::TAB_MIRROR, tabSourceA));
+ EXPECT_EQ(sources.GetCastModes(), castModeSetDefaultAndTab);
+
+ // [Default: 1,2; Tab: A]
+ sources.AddSource(MediaCastMode::DEFAULT, defaultSource2);
+ EXPECT_TRUE(sources.HasSource(MediaCastMode::DEFAULT, defaultSource2));
+ EXPECT_EQ(sources.GetCastModes(), castModeSetDefaultAndTab);
+
+ // [Default: 2; Tab: A]
+ sources.RemoveSource(MediaCastMode::DEFAULT, defaultSource1);
+ EXPECT_FALSE(sources.HasSource(MediaCastMode::DEFAULT, defaultSource1));
+ EXPECT_TRUE(sources.HasSource(MediaCastMode::DEFAULT, defaultSource2));
+ EXPECT_EQ(sources.GetCastModes(), castModeSetDefaultAndTab);
+
+ // [Tab: A]
+ sources.RemoveSource(MediaCastMode::DEFAULT, defaultSource2);
+ EXPECT_FALSE(sources.HasSource(MediaCastMode::DEFAULT, defaultSource1));
+ EXPECT_FALSE(sources.IsEmpty());
+ EXPECT_EQ(sources.GetCastModes(), castModeSetTab);
+
+ // []
+ sources.RemoveSource(MediaCastMode::TAB_MIRROR, tabSourceA);
+ EXPECT_FALSE(sources.HasSource(MediaCastMode::DEFAULT, defaultSource1));
+ EXPECT_FALSE(sources.HasSource(MediaCastMode::DEFAULT, defaultSource2));
+ EXPECT_FALSE(sources.HasSource(MediaCastMode::TAB_MIRROR, tabSourceA));
+ EXPECT_TRUE(sources.IsEmpty());
+ EXPECT_EQ(sources.GetCastModes(), castModeSetEmpty);
+}
+
+} // namespace media_router
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_cast_mode.cc b/chromium/chrome/browser/ui/webui/media_router/media_cast_mode.cc
new file mode 100644
index 00000000000..f742fa7012d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_cast_mode.cc
@@ -0,0 +1,44 @@
+// 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.
+
+#include "chrome/browser/ui/webui/media_router/media_cast_mode.h"
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace media_router {
+
+std::string MediaCastModeToDescription(
+ MediaCastMode mode, const std::string& host) {
+ switch (mode) {
+ case MediaCastMode::DEFAULT:
+ return l10n_util::GetStringFUTF8(
+ IDS_MEDIA_ROUTER_DEFAULT_CAST_MODE,
+ base::UTF8ToUTF16(host));
+ case MediaCastMode::TAB_MIRROR:
+ return l10n_util::GetStringUTF8(
+ IDS_MEDIA_ROUTER_TAB_MIRROR_CAST_MODE);
+ case MediaCastMode::DESKTOP_MIRROR:
+ return l10n_util::GetStringUTF8(
+ IDS_MEDIA_ROUTER_DESKTOP_MIRROR_CAST_MODE);
+ default:
+ NOTREACHED();
+ return "";
+ }
+}
+
+bool IsValidCastModeNum(int cast_mode_num) {
+ switch (cast_mode_num) {
+ case MediaCastMode::DEFAULT:
+ case MediaCastMode::TAB_MIRROR:
+ case MediaCastMode::DESKTOP_MIRROR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace media_router
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_cast_mode.h b/chromium/chrome/browser/ui/webui/media_router/media_cast_mode.h
new file mode 100644
index 00000000000..9a3a7baa7fd
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_cast_mode.h
@@ -0,0 +1,43 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_CAST_MODE_H_
+#define CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_CAST_MODE_H_
+
+#include <set>
+#include <string>
+
+namespace media_router {
+
+// A cast mode represents one way that the current WebContents (i.e., tab) may
+// be presented to a media sink. These must be declared in the priority order
+// returned by GetPreferredCastMode.
+enum MediaCastMode {
+ // A presentation URL provided by the WebContents via the Presentation API.
+ // This can be set by the default presentation URL (for top level browsing
+ // contexts) or presentation URLs passed with a PresentationRequest (for top
+ // level and nested browsing contexts).
+ // TODO(mfoltz): More accurately named PRESENTATION
+ DEFAULT = 0x1,
+ // Capture the rendered WebContents and stream it to a media sink. Always
+ // available.
+ TAB_MIRROR = 0x2,
+ // Capture the entire desktop and stream it to a media sink. Always
+ // available.
+ DESKTOP_MIRROR = 0x4,
+};
+
+using CastModeSet = std::set<MediaCastMode>;
+
+// Returns a localized description string for |mode| and |host|
+// (e.g. google.com).
+std::string MediaCastModeToDescription(MediaCastMode mode,
+ const std::string& host);
+
+// Returns true if |cast_mode_num| is a valid MediaCastMode, false otherwise.
+bool IsValidCastModeNum(int cast_mode_num);
+
+} // namespace media_router
+
+#endif // CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_CAST_MODE_H_
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_cast_mode_unittest.cc b/chromium/chrome/browser/ui/webui/media_router/media_cast_mode_unittest.cc
new file mode 100644
index 00000000000..574cdadc2aa
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_cast_mode_unittest.cc
@@ -0,0 +1,32 @@
+// 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.
+
+#include "chrome/browser/ui/webui/media_router/media_cast_mode.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Not;
+using testing::HasSubstr;
+
+namespace media_router {
+
+TEST(MediaCastModeTest, MediaCastModeToDescription) {
+ EXPECT_FALSE(MediaCastModeToDescription(MediaCastMode::DEFAULT, "youtube.com")
+ .empty());
+ EXPECT_FALSE(
+ MediaCastModeToDescription(MediaCastMode::TAB_MIRROR, "").empty());
+ EXPECT_FALSE(
+ MediaCastModeToDescription(MediaCastMode::DESKTOP_MIRROR, "").empty());
+}
+
+TEST(MediaCastModeTest, IsValidCastModeNum) {
+ EXPECT_TRUE(IsValidCastModeNum(MediaCastMode::DEFAULT));
+ EXPECT_TRUE(IsValidCastModeNum(MediaCastMode::TAB_MIRROR));
+ EXPECT_TRUE(IsValidCastModeNum(MediaCastMode::DESKTOP_MIRROR));
+ EXPECT_FALSE(IsValidCastModeNum(-1));
+ EXPECT_FALSE(IsValidCastModeNum(666));
+}
+
+} // namespace media_router
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl.cc b/chromium/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl.cc
new file mode 100644
index 00000000000..83da136340b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl.cc
@@ -0,0 +1,342 @@
+// 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.
+
+#include "chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
+#include "chrome/browser/media/router/media_router_ui_service.h"
+#include "chrome/browser/media/router/presentation_service_delegate_impl.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/toolbar/media_router_action.h"
+#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
+#include "chrome/browser/ui/webui/media_router/media_router_ui.h"
+#include "chrome/common/url_constants.h"
+#include "components/guest_view/browser/guest_view_base.h"
+#include "components/web_modal/web_contents_modal_dialog_host.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_details.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "ui/web_dialogs/web_dialog_delegate.h"
+#include "ui/web_dialogs/web_dialog_web_contents_delegate.h"
+#include "url/gurl.h"
+
+DEFINE_WEB_CONTENTS_USER_DATA_KEY(
+ media_router::MediaRouterDialogControllerImpl);
+
+using content::LoadCommittedDetails;
+using content::NavigationController;
+using content::WebContents;
+using content::WebUIMessageHandler;
+using ui::WebDialogDelegate;
+
+namespace media_router {
+
+namespace {
+
+constexpr const int kMaxHeight = 2000;
+constexpr const int kMinHeight = 80;
+constexpr const int kWidth = 340;
+
+// WebDialogDelegate that specifies what the Media Router dialog
+// will look like.
+class MediaRouterDialogDelegate : public WebDialogDelegate {
+ public:
+ explicit MediaRouterDialogDelegate(
+ const base::WeakPtr<MediaRouterDialogControllerImpl>& controller)
+ : controller_(controller) {}
+ ~MediaRouterDialogDelegate() override {}
+
+ // WebDialogDelegate implementation.
+ ui::ModalType GetDialogModalType() const override {
+ // Not used, returning dummy value.
+ return ui::MODAL_TYPE_WINDOW;
+ }
+
+ base::string16 GetDialogTitle() const override {
+ return base::string16();
+ }
+
+ GURL GetDialogContentURL() const override {
+ return GURL(chrome::kChromeUIMediaRouterURL);
+ }
+
+ void GetWebUIMessageHandlers(
+ std::vector<WebUIMessageHandler*>* handlers) const override {
+ // MediaRouterUI adds its own message handlers.
+ }
+
+ void GetDialogSize(gfx::Size* size) const override {
+ DCHECK(size);
+ // We set the dialog width if it's not set, so that the dialog is
+ // center-aligned horizontally when it appears.
+ if (size->width() != kWidth)
+ size->set_width(kWidth);
+ // GetDialogSize() is called when the browser window resizes. We may want to
+ // update the maximum height of the dialog and scale the WebUI to the new
+ // height. |size| is not set because the dialog is auto-resizeable.
+ controller_->UpdateMaxDialogSize();
+ }
+
+ std::string GetDialogArgs() const override {
+ return std::string();
+ }
+
+ void OnDialogClosed(const std::string& json_retval) override {
+ // We don't delete |this| here because this class is owned
+ // by ConstrainedWebDialogDelegate.
+ }
+
+ void OnCloseContents(WebContents* source, bool* out_close_dialog) override {
+ if (out_close_dialog)
+ *out_close_dialog = true;
+ }
+
+ bool ShouldShowDialogTitle() const override {
+ return false;
+ }
+
+ private:
+ base::WeakPtr<MediaRouterAction> action_;
+ base::WeakPtr<MediaRouterDialogControllerImpl> controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaRouterDialogDelegate);
+};
+
+MediaRouterActionController* GetActionController(WebContents* web_contents) {
+ Profile* profile =
+ Profile::FromBrowserContext(web_contents->GetBrowserContext());
+ return MediaRouterUIService::Get(profile)->action_controller();
+}
+
+} // namespace
+
+// static
+MediaRouterDialogControllerImpl*
+MediaRouterDialogControllerImpl::GetOrCreateForWebContents(
+ WebContents* web_contents) {
+ DCHECK(web_contents);
+ // This call does nothing if the controller already exists.
+ MediaRouterDialogControllerImpl::CreateForWebContents(web_contents);
+ return MediaRouterDialogControllerImpl::FromWebContents(web_contents);
+}
+
+class MediaRouterDialogControllerImpl::DialogWebContentsObserver
+ : public content::WebContentsObserver {
+ public:
+ DialogWebContentsObserver(
+ WebContents* web_contents,
+ MediaRouterDialogControllerImpl* dialog_controller)
+ : content::WebContentsObserver(web_contents),
+ dialog_controller_(dialog_controller) {
+ }
+
+ private:
+ void WebContentsDestroyed() override {
+ // The dialog is already closed. No need to call Close() again.
+ // NOTE: |this| is deleted after Reset() returns.
+ dialog_controller_->Reset();
+ }
+
+ void NavigationEntryCommitted(const LoadCommittedDetails& load_details)
+ override {
+ dialog_controller_->OnDialogNavigated(load_details);
+ }
+
+ void RenderProcessGone(base::TerminationStatus status) override {
+ // NOTE: |this| is deleted after CloseMediaRouterDialog() returns.
+ dialog_controller_->CloseMediaRouterDialog();
+ }
+
+ MediaRouterDialogControllerImpl* const dialog_controller_;
+};
+
+MediaRouterDialogControllerImpl::MediaRouterDialogControllerImpl(
+ WebContents* web_contents)
+ : MediaRouterDialogController(web_contents),
+ media_router_dialog_pending_(false),
+ action_controller_(GetActionController(web_contents)),
+ weak_ptr_factory_(this) {
+ DCHECK(action_controller_);
+}
+
+MediaRouterDialogControllerImpl::~MediaRouterDialogControllerImpl() {
+ Reset();
+}
+
+WebContents* MediaRouterDialogControllerImpl::GetMediaRouterDialog() const {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ return dialog_observer_.get() ? dialog_observer_->web_contents() : nullptr;
+}
+
+void MediaRouterDialogControllerImpl::SetMediaRouterAction(
+ const base::WeakPtr<MediaRouterAction>& action) {
+ action_ = action;
+}
+
+bool MediaRouterDialogControllerImpl::IsShowingMediaRouterDialog() const {
+ return GetMediaRouterDialog() != nullptr;
+}
+
+void MediaRouterDialogControllerImpl::UpdateMaxDialogSize() {
+ WebContents* media_router_dialog = GetMediaRouterDialog();
+ if (!media_router_dialog)
+ return;
+
+ content::WebUI* web_ui = media_router_dialog->GetWebUI();
+ if (web_ui) {
+ MediaRouterUI* media_router_ui =
+ static_cast<MediaRouterUI*>(web_ui->GetController());
+ if (media_router_ui) {
+ Browser* browser = chrome::FindBrowserWithWebContents(initiator());
+ web_modal::WebContentsModalDialogHost* host = nullptr;
+ if (browser)
+ host = browser->window()->GetWebContentsModalDialogHost();
+
+ gfx::Size maxSize = host ?
+ host->GetMaximumDialogSize() :
+ initiator()->GetContainerBounds().size();
+
+ // The max height of the dialog should be 90% of the browser window
+ // height. The width stays fixed.
+ maxSize.Enlarge(0, -0.1 * maxSize.height());
+ media_router_ui->UpdateMaxDialogHeight(maxSize.height());
+ }
+ }
+}
+
+void MediaRouterDialogControllerImpl::CloseMediaRouterDialog() {
+ WebContents* media_router_dialog = GetMediaRouterDialog();
+ if (!media_router_dialog)
+ return;
+
+ content::WebUI* web_ui = media_router_dialog->GetWebUI();
+ if (web_ui) {
+ MediaRouterUI* media_router_ui =
+ static_cast<MediaRouterUI*>(web_ui->GetController());
+ if (media_router_ui)
+ media_router_ui->Close();
+ }
+}
+
+void MediaRouterDialogControllerImpl::CreateMediaRouterDialog() {
+ DCHECK(!dialog_observer_.get());
+
+ base::Time dialog_creation_time = base::Time::Now();
+ TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("media_router", "UI", initiator());
+
+ Profile* profile =
+ Profile::FromBrowserContext(initiator()->GetBrowserContext());
+ DCHECK(profile);
+
+ // |web_dialog_delegate|'s owner is |constrained_delegate|.
+ // |constrained_delegate| is owned by the parent |views::View|.
+ WebDialogDelegate* web_dialog_delegate =
+ new MediaRouterDialogDelegate(weak_ptr_factory_.GetWeakPtr());
+
+ // |ShowConstrainedWebDialogWithAutoResize()| will end up creating
+ // ConstrainedWebDialogDelegateViewViews containing a WebContents containing
+ // the MediaRouterUI, using the provided |web_dialog_delegate|. Then, the
+ // view is shown as a modal dialog constrained to the |initiator| WebContents.
+ // The dialog will resize between the given minimum and maximum size bounds
+ // based on the currently rendered contents.
+ ConstrainedWebDialogDelegate* constrained_delegate =
+ ShowConstrainedWebDialogWithAutoResize(
+ profile, web_dialog_delegate, initiator(),
+ gfx::Size(kWidth, kMinHeight), gfx::Size(kWidth, kMaxHeight));
+
+ WebContents* media_router_dialog = constrained_delegate->GetWebContents();
+ TRACE_EVENT_NESTABLE_ASYNC_INSTANT1("media_router", "UI", initiator(),
+ "WebContents created",
+ media_router_dialog);
+
+ // |media_router_ui| is created when |constrained_delegate| is created.
+ // For tests, GetWebUI() returns a nullptr.
+ if (media_router_dialog->GetWebUI()) {
+ MediaRouterUI* media_router_ui = static_cast<MediaRouterUI*>(
+ media_router_dialog->GetWebUI()->GetController());
+ DCHECK(media_router_ui);
+ media_router_ui->SetUIInitializationTimer(dialog_creation_time);
+ }
+
+ media_router_dialog_pending_ = true;
+
+ dialog_observer_.reset(new DialogWebContentsObserver(
+ media_router_dialog, this));
+
+ // The |action_controller_| must be notified after |action_| to avoid a UI
+ // bug in which the drop shadow is drawn in an incorrect position.
+ if (action_)
+ action_->OnDialogShown();
+ action_controller_->OnDialogShown();
+}
+
+void MediaRouterDialogControllerImpl::Reset() {
+ if (IsShowingMediaRouterDialog()) {
+ if (action_)
+ action_->OnDialogHidden();
+ action_controller_->OnDialogHidden();
+ }
+ MediaRouterDialogController::Reset();
+ dialog_observer_.reset();
+}
+
+void MediaRouterDialogControllerImpl::OnDialogNavigated(
+ const content::LoadCommittedDetails& details) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ WebContents* media_router_dialog = GetMediaRouterDialog();
+ CHECK(media_router_dialog);
+ ui::PageTransition transition_type = details.entry->GetTransitionType();
+ content::NavigationType nav_type = details.type;
+
+ // New |media_router_dialog| is created.
+ DCHECK(media_router_dialog_pending_);
+ DCHECK(ui::PageTransitionCoreTypeIs(transition_type,
+ ui::PAGE_TRANSITION_AUTO_TOPLEVEL) &&
+ nav_type == content::NAVIGATION_TYPE_NEW_PAGE)
+ << "transition_type: " << transition_type << ", "
+ << "nav_type: " << nav_type;
+
+ media_router_dialog_pending_ = false;
+
+ PopulateDialog(media_router_dialog);
+}
+
+void MediaRouterDialogControllerImpl::PopulateDialog(
+ content::WebContents* media_router_dialog) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(media_router_dialog);
+ if (!initiator() || !media_router_dialog->GetWebUI()) {
+ Reset();
+ return;
+ }
+
+ MediaRouterUI* media_router_ui = static_cast<MediaRouterUI*>(
+ media_router_dialog->GetWebUI()->GetController());
+ DCHECK(media_router_ui);
+
+ std::unique_ptr<CreatePresentationConnectionRequest>
+ create_connection_request(TakeCreateConnectionRequest());
+ PresentationServiceDelegateImpl* delegate =
+ PresentationServiceDelegateImpl::FromWebContents(initiator());
+ if (!create_connection_request.get()) {
+ media_router_ui->InitWithDefaultMediaSource(initiator(), delegate);
+ } else {
+ media_router_ui->InitWithPresentationSessionRequest(
+ initiator(), delegate, std::move(create_connection_request));
+ }
+}
+
+} // namespace media_router
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl.h b/chromium/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl.h
new file mode 100644
index 00000000000..1d21c0886b9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl.h
@@ -0,0 +1,92 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_DIALOG_CONTROLLER_IMPL_H_
+#define CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_DIALOG_CONTROLLER_IMPL_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/media/router/media_router_dialog_controller.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+FORWARD_DECLARE_TEST(MediaRouterActionUnitTest, IconPressedState);
+
+class MediaRouterAction;
+class MediaRouterActionController;
+
+namespace media_router {
+
+// A desktop implementation of MediaRouterDialogController.
+// This class is not thread safe and must be called on the UI thread.
+class MediaRouterDialogControllerImpl :
+ public content::WebContentsUserData<MediaRouterDialogControllerImpl>,
+ public MediaRouterDialogController {
+ public:
+ ~MediaRouterDialogControllerImpl() override;
+
+ static MediaRouterDialogControllerImpl* GetOrCreateForWebContents(
+ content::WebContents* web_contents);
+
+ // Returns the media router dialog WebContents.
+ // Returns nullptr if there is no dialog.
+ content::WebContents* GetMediaRouterDialog() const;
+
+ // Sets the action to notify when a dialog gets shown or hidden.
+ void SetMediaRouterAction(const base::WeakPtr<MediaRouterAction>& action);
+
+ // MediaRouterDialogController:
+ bool IsShowingMediaRouterDialog() const override;
+
+ void UpdateMaxDialogSize();
+
+ MediaRouterAction* action() { return action_.get(); }
+
+ private:
+ class DialogWebContentsObserver;
+ friend class content::WebContentsUserData<MediaRouterDialogControllerImpl>;
+ FRIEND_TEST_ALL_PREFIXES(::MediaRouterActionUnitTest, IconPressedState);
+
+ // Use MediaRouterDialogControllerImpl::CreateForWebContents() to create an
+ // instance.
+ explicit MediaRouterDialogControllerImpl(content::WebContents* web_contents);
+
+ // MediaRouterDialogController:
+ void CreateMediaRouterDialog() override;
+ void CloseMediaRouterDialog() override;
+ void Reset() override;
+
+ // Invoked when the dialog WebContents has navigated.
+ void OnDialogNavigated(const content::LoadCommittedDetails& details);
+
+ void PopulateDialog(content::WebContents* media_router_dialog);
+
+ std::unique_ptr<DialogWebContentsObserver> dialog_observer_;
+
+ // True if the controller is waiting for a new media router dialog to be
+ // created.
+ bool media_router_dialog_pending_;
+
+ // |action_| refers to the MediaRouterAction on the toolbar, rather than
+ // overflow menu. A MediaRouterAction is always created for the toolbar
+ // first. Any subsequent creations for the overflow menu will not be set as
+ // |action_|.
+ // The lifetime of |action_| is dependent on the creation and destruction of
+ // a browser window. The overflow menu's MediaRouterAction is only created
+ // when the overflow menu is opened and destroyed when the menu is closed.
+ base::WeakPtr<MediaRouterAction> action_;
+
+ // |action_controller_| is responsible for showing and hiding the toolbar
+ // action. It's owned by MediaRouterUIService, which outlives |this|.
+ MediaRouterActionController* action_controller_;
+
+ base::WeakPtrFactory<MediaRouterDialogControllerImpl> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaRouterDialogControllerImpl);
+};
+
+} // namespace media_router
+
+#endif // CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_DIALOG_CONTROLLER_IMPL_H_
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl_browsertest.cc b/chromium/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl_browsertest.cc
new file mode 100644
index 00000000000..202d76bfa20
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl_browsertest.cc
@@ -0,0 +1,140 @@
+// 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.
+
+#include "base/macros.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl.h"
+#include "chrome/browser/ui/webui/media_router/media_router_ui.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/common/url_constants.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+
+using content::WebContents;
+using content::TestNavigationObserver;
+
+namespace media_router {
+
+class MediaRouterDialogControllerBrowserTest : public InProcessBrowserTest {
+ public:
+ MediaRouterDialogControllerBrowserTest()
+ : dialog_controller_(nullptr),
+ initiator_(nullptr),
+ media_router_dialog_(nullptr) {}
+ ~MediaRouterDialogControllerBrowserTest() override {}
+
+ protected:
+ void SetUpOnMainThread() override {
+ // Start with one window with one tab.
+ EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
+ EXPECT_EQ(1, browser()->tab_strip_model()->count());
+
+ initiator_ = browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(initiator_);
+ MediaRouterDialogControllerImpl::CreateForWebContents(initiator_);
+ dialog_controller_ =
+ MediaRouterDialogControllerImpl::FromWebContents(initiator_);
+ ASSERT_TRUE(dialog_controller_);
+
+ // Get the media router dialog for the initiator.
+ dialog_controller_->ShowMediaRouterDialog();
+ media_router_dialog_ = dialog_controller_->GetMediaRouterDialog();
+ ASSERT_TRUE(media_router_dialog_);
+ }
+
+ MediaRouterDialogControllerImpl* dialog_controller_;
+ WebContents* initiator_;
+ WebContents* media_router_dialog_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MediaRouterDialogControllerBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(MediaRouterDialogControllerBrowserTest, ShowDialog) {
+ // Waits for the dialog to initialize.
+ TestNavigationObserver nav_observer(media_router_dialog_);
+ nav_observer.Wait();
+
+ // New media router dialog is a constrained window, so the number of
+ // tabs is still 1.
+ EXPECT_EQ(1, browser()->tab_strip_model()->count());
+ EXPECT_NE(initiator_, media_router_dialog_);
+ EXPECT_EQ(media_router_dialog_, dialog_controller_->GetMediaRouterDialog());
+
+ content::WebUI* web_ui = media_router_dialog_->GetWebUI();
+ ASSERT_TRUE(web_ui);
+ MediaRouterUI* media_router_ui =
+ static_cast<MediaRouterUI*>(web_ui->GetController());
+ ASSERT_TRUE(media_router_ui);
+}
+
+IN_PROC_BROWSER_TEST_F(MediaRouterDialogControllerBrowserTest, Navigate) {
+ {
+ // Wait for the dialog to initialize.
+ TestNavigationObserver nav_observer(media_router_dialog_);
+ nav_observer.Wait();
+ }
+
+ // New media router dialog is a constrained window, so the number of
+ // tabs is still 1.
+ EXPECT_EQ(1, browser()->tab_strip_model()->count());
+ EXPECT_EQ(media_router_dialog_, dialog_controller_->GetMediaRouterDialog());
+
+ {
+ // Navigate to another URL and block until the dialog WebContents has been
+ // destroyed.
+ content::WebContentsDestroyedWatcher dialog_watcher(media_router_dialog_);
+ ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
+ dialog_watcher.Wait();
+ }
+
+ // Verify that dialog has been removed.
+ EXPECT_FALSE(dialog_controller_->GetMediaRouterDialog());
+
+ // Open the dialog again.
+ EXPECT_TRUE(dialog_controller_->ShowMediaRouterDialog());
+ media_router_dialog_ = dialog_controller_->GetMediaRouterDialog();
+ ASSERT_TRUE(media_router_dialog_);
+
+ {
+ // Wait for the dialog to initialize.
+ TestNavigationObserver nav_observer(media_router_dialog_);
+ nav_observer.Wait();
+
+ // Refresh and block until dialog WebContents has been destroyed.
+ content::WebContentsDestroyedWatcher dialog_watcher(media_router_dialog_);
+ chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
+ dialog_watcher.Wait();
+ }
+
+ // Verify that dialog has been removed again.
+ EXPECT_FALSE(dialog_controller_->GetMediaRouterDialog());
+}
+
+IN_PROC_BROWSER_TEST_F(MediaRouterDialogControllerBrowserTest,
+ RenderProcessHost) {
+ // New media router dialog is a constrained window, so the number of
+ // tabs is still 1.
+ EXPECT_EQ(1, browser()->tab_strip_model()->count());
+ EXPECT_EQ(media_router_dialog_, dialog_controller_->GetMediaRouterDialog());
+
+ // Crash initiator_'s renderer process.
+ content::WebContentsDestroyedWatcher dialog_watcher(media_router_dialog_);
+ content::RenderProcessHostWatcher rph_watcher(initiator_,
+ content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+
+ ui_test_utils::NavigateToURL(browser(), GURL(content::kChromeUICrashURL));
+
+ // Blocks until the dialog WebContents has been destroyed.
+ rph_watcher.Wait();
+ dialog_watcher.Wait();
+
+ // Entry has been removed.
+ EXPECT_FALSE(dialog_controller_->GetMediaRouterDialog());
+}
+
+} // namespace media_router
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl_unittest.cc b/chromium/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl_unittest.cc
new file mode 100644
index 00000000000..5be13b0bf19
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl_unittest.cc
@@ -0,0 +1,274 @@
+// 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.
+
+#include <vector>
+
+#include "base/macros.h"
+#include "chrome/browser/media/router/media_router_ui_service.h"
+#include "chrome/browser/media/router/test_helper.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/toolbar/mock_media_router_action_controller.h"
+#include "chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl.h"
+#include "chrome/browser/ui/webui/media_router/media_router_ui.h"
+#include "chrome/browser/ui/webui/media_router/media_router_web_ui_test.h"
+#include "content/public/test/test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using content::WebContents;
+
+namespace media_router {
+
+class MediaRouterDialogControllerImplTest : public MediaRouterWebUITest {
+ public:
+ MediaRouterDialogControllerImplTest() : MediaRouterWebUITest(true) {}
+ ~MediaRouterDialogControllerImplTest() override {}
+
+ void OpenMediaRouterDialog();
+
+ MOCK_METHOD2(PresentationSuccessCallback,
+ void(const content::PresentationInfo&, const MediaRoute&));
+ MOCK_METHOD1(PresentationErrorCallback,
+ void(const content::PresentationError& error));
+
+ protected:
+ WebContents* initiator_ = nullptr;
+ MediaRouterDialogControllerImpl* dialog_controller_ = nullptr;
+ WebContents* media_router_dialog_ = nullptr;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MediaRouterDialogControllerImplTest);
+};
+
+void MediaRouterDialogControllerImplTest::OpenMediaRouterDialog() {
+ // Start with one window with one tab.
+ EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
+ EXPECT_EQ(0, browser()->tab_strip_model()->count());
+ chrome::NewTab(browser());
+ EXPECT_EQ(1, browser()->tab_strip_model()->count());
+
+ // Create a reference to initiator contents.
+ initiator_ = browser()->tab_strip_model()->GetActiveWebContents();
+
+ dialog_controller_ =
+ MediaRouterDialogControllerImpl::GetOrCreateForWebContents(initiator_);
+ ASSERT_TRUE(dialog_controller_);
+
+ // Get the media router dialog for the initiator.
+ dialog_controller_->ShowMediaRouterDialog();
+ media_router_dialog_ = dialog_controller_->GetMediaRouterDialog();
+ ASSERT_TRUE(media_router_dialog_);
+
+ // New media router dialog is a constrained window, so the number of
+ // tabs is still 1.
+ EXPECT_EQ(1, browser()->tab_strip_model()->count());
+ EXPECT_NE(initiator_, media_router_dialog_);
+ EXPECT_EQ(media_router_dialog_, dialog_controller_->GetMediaRouterDialog());
+}
+
+// Create/Get a media router dialog for initiator.
+TEST_F(MediaRouterDialogControllerImplTest, ShowMediaRouterDialog) {
+ OpenMediaRouterDialog();
+
+ // Show media router dialog for the same initiator again.
+ dialog_controller_->ShowMediaRouterDialog();
+ WebContents* same_media_router_dialog =
+ dialog_controller_->GetMediaRouterDialog();
+
+ // Tab count remains the same.
+ EXPECT_EQ(1, browser()->tab_strip_model()->count());
+
+ // Media router dialog already exists. Calling |ShowMediaRouterDialog| again
+ // should not have created a new media router dialog.
+ EXPECT_EQ(media_router_dialog_, same_media_router_dialog);
+}
+
+// Tests multiple media router dialogs exist in the same browser for different
+// initiators. If a dialog already exists for an initiator, that initiator
+// gets focused.
+TEST_F(MediaRouterDialogControllerImplTest, MultipleMediaRouterDialogs) {
+ // Let's start with one window and two tabs.
+ EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
+ TabStripModel* tab_strip_model = browser()->tab_strip_model();
+ ASSERT_TRUE(tab_strip_model);
+
+ EXPECT_EQ(0, tab_strip_model->count());
+
+ // Create some new initiators.
+ chrome::NewTab(browser());
+ WebContents* web_contents_1 = tab_strip_model->GetActiveWebContents();
+ ASSERT_TRUE(web_contents_1);
+
+ chrome::NewTab(browser());
+ WebContents* web_contents_2 = tab_strip_model->GetActiveWebContents();
+ ASSERT_TRUE(web_contents_2);
+ EXPECT_EQ(2, tab_strip_model->count());
+
+
+ // Create media router dialog for |web_contents_1|.
+ MediaRouterDialogControllerImpl* dialog_controller_1 =
+ MediaRouterDialogControllerImpl::GetOrCreateForWebContents(
+ web_contents_1);
+ ASSERT_TRUE(dialog_controller_1);
+
+ dialog_controller_1->ShowMediaRouterDialog();
+ WebContents* media_router_dialog_1 =
+ dialog_controller_1->GetMediaRouterDialog();
+
+ ASSERT_TRUE(media_router_dialog_1);
+
+ EXPECT_NE(web_contents_1, media_router_dialog_1);
+ EXPECT_EQ(2, tab_strip_model->count());
+
+ // Create media router dialog for |web_contents_2|.
+ MediaRouterDialogControllerImpl* dialog_controller_2 =
+ MediaRouterDialogControllerImpl::GetOrCreateForWebContents(
+ web_contents_2);
+ ASSERT_TRUE(dialog_controller_2);
+
+ dialog_controller_2->ShowMediaRouterDialog();
+ WebContents* media_router_dialog_2 =
+ dialog_controller_2->GetMediaRouterDialog();
+ ASSERT_TRUE(media_router_dialog_2);
+
+ EXPECT_NE(web_contents_2, media_router_dialog_2);
+ EXPECT_NE(media_router_dialog_1, media_router_dialog_2);
+
+ // 2 initiators and 2 dialogs exist in the same browser. The dialogs are
+ // constrained in their respective initiators.
+ EXPECT_EQ(2, tab_strip_model->count());
+
+ int tab_1_index = tab_strip_model->GetIndexOfWebContents(web_contents_1);
+ int tab_2_index = tab_strip_model->GetIndexOfWebContents(web_contents_2);
+ int media_router_dialog_1_index =
+ tab_strip_model->GetIndexOfWebContents(media_router_dialog_1);
+ int media_router_dialog_2_index =
+ tab_strip_model->GetIndexOfWebContents(media_router_dialog_2);
+
+ // Constrained dialogs are not in the TabStripModel.
+ EXPECT_EQ(-1, media_router_dialog_1_index);
+ EXPECT_EQ(-1, media_router_dialog_2_index);
+
+ // Since |media_router_dialog_2_index| was the most recently created dialog,
+ // its initiator should have focus.
+ EXPECT_EQ(tab_2_index, tab_strip_model->active_index());
+
+ // When we get the media router dialog for |web_contents_1|,
+ // |media_router_dialog_1| is activated and focused.
+ dialog_controller_1->ShowMediaRouterDialog();
+ EXPECT_EQ(tab_1_index, tab_strip_model->active_index());
+
+ // When we get the media router dialog for |web_contents_2|,
+ // |media_router_dialog_2| is activated and focused.
+ dialog_controller_2->ShowMediaRouterDialog();
+ EXPECT_EQ(tab_2_index, tab_strip_model->active_index());
+}
+
+TEST_F(MediaRouterDialogControllerImplTest, CloseDialogFromWebUI) {
+ OpenMediaRouterDialog();
+
+ // Close the dialog.
+ content::WebContentsDestroyedWatcher watcher(media_router_dialog_);
+
+ content::WebUI* web_ui = media_router_dialog_->GetWebUI();
+ ASSERT_TRUE(web_ui);
+ MediaRouterUI* media_router_ui =
+ static_cast<MediaRouterUI*>(web_ui->GetController());
+ ASSERT_TRUE(media_router_ui);
+ media_router_ui->Close();
+
+ // Blocks until the media router dialog WebContents has been destroyed.
+ watcher.Wait();
+
+ // Still 1 tab in the browser.
+ EXPECT_EQ(1, browser()->tab_strip_model()->count());
+
+ // Entry has been removed.
+ EXPECT_FALSE(dialog_controller_->GetMediaRouterDialog());
+
+ // Show the media router dialog again, creating a new one.
+ dialog_controller_->ShowMediaRouterDialog();
+ WebContents* media_router_dialog_2 =
+ dialog_controller_->GetMediaRouterDialog();
+
+ // Still 1 tab in the browser.
+ EXPECT_EQ(1, browser()->tab_strip_model()->count());
+
+ EXPECT_EQ(media_router_dialog_2, dialog_controller_->GetMediaRouterDialog());
+}
+
+TEST_F(MediaRouterDialogControllerImplTest, CloseDialogFromDialogController) {
+ OpenMediaRouterDialog();
+
+ // Close the dialog.
+ content::WebContentsDestroyedWatcher watcher(media_router_dialog_);
+
+ dialog_controller_->HideMediaRouterDialog();
+
+ // Blocks until the media router dialog WebContents has been destroyed.
+ watcher.Wait();
+
+ // Still 1 tab in the browser.
+ EXPECT_EQ(1, browser()->tab_strip_model()->count());
+
+ // Entry has been removed.
+ EXPECT_FALSE(dialog_controller_->GetMediaRouterDialog());
+}
+
+TEST_F(MediaRouterDialogControllerImplTest, CloseInitiator) {
+ OpenMediaRouterDialog();
+
+ // Close the initiator. This should also close the dialog WebContents.
+ content::WebContentsDestroyedWatcher initiator_watcher(initiator_);
+ content::WebContentsDestroyedWatcher dialog_watcher(media_router_dialog_);
+
+ int initiator_index = browser()->tab_strip_model()
+ ->GetIndexOfWebContents(initiator_);
+ EXPECT_NE(-1, initiator_index);
+ EXPECT_TRUE(browser()->tab_strip_model()->CloseWebContentsAt(
+ initiator_index, TabStripModel::CLOSE_NONE));
+
+ // Blocks until the initiator WebContents has been destroyed.
+ initiator_watcher.Wait();
+ dialog_watcher.Wait();
+
+ // No tabs in the browser.
+ EXPECT_EQ(0, browser()->tab_strip_model()->count());
+
+ // The dialog controller is deleted when the WebContents is closed/destroyed.
+}
+
+TEST_F(MediaRouterDialogControllerImplTest, NotifyActionController) {
+ MockMediaRouterActionController* action_controller =
+ static_cast<MockMediaRouterActionController*>(
+ MediaRouterUIService::Get(browser()->profile())->action_controller());
+ ASSERT_TRUE(action_controller);
+
+ EXPECT_CALL(*action_controller, OnDialogShown());
+ OpenMediaRouterDialog();
+ EXPECT_CALL(*action_controller, OnDialogHidden());
+ dialog_controller_->HideMediaRouterDialog();
+
+ EXPECT_CALL(*action_controller, OnDialogShown());
+ dialog_controller_->ShowMediaRouterDialogForPresentation(
+ std::unique_ptr<CreatePresentationConnectionRequest>(
+ new CreatePresentationConnectionRequest(
+ RenderFrameHostId(1, 2),
+ {GURL("http://test.com"), GURL("http://test2.com")},
+ url::Origin(GURL("http://example.com")),
+ base::Bind(&MediaRouterDialogControllerImplTest::
+ PresentationSuccessCallback,
+ base::Unretained(this)),
+ base::Bind(&MediaRouterDialogControllerImplTest::
+ PresentationErrorCallback,
+ base::Unretained(this)))));
+
+ // When |dialog_controller_| is destroyed with its dialog open,
+ // |action_controller| should be notified.
+ EXPECT_CALL(*action_controller, OnDialogHidden());
+ EXPECT_CALL(*this, PresentationErrorCallback(testing::_));
+}
+
+} // namespace media_router
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
new file mode 100644
index 00000000000..6345cbec2b1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.cc
@@ -0,0 +1,83 @@
+// 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.
+
+#include "chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.h"
+
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace {
+
+// Note that media_router.html contains a <script> tag which imports a script
+// of the following name. These names must be kept in sync.
+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("backButtonTitle",
+ IDS_MEDIA_ROUTER_BACK_BUTTON_TITLE);
+ html_source->AddLocalizedString("closeButtonTitle",
+ IDS_MEDIA_ROUTER_CLOSE_BUTTON_TITLE);
+ html_source->AddLocalizedString("searchButtonTitle",
+ IDS_MEDIA_ROUTER_SEARCH_BUTTON_TITLE);
+ html_source->AddLocalizedString("viewCastModeListButtonTitle",
+ IDS_MEDIA_ROUTER_VIEW_CAST_MODE_LIST_BUTTON_TITLE);
+ html_source->AddLocalizedString("viewDeviceListButtonTitle",
+ IDS_MEDIA_ROUTER_VIEW_DEVICE_LIST_BUTTON_TITLE);
+}
+
+void AddRouteDetailsStrings(content::WebUIDataSource* html_source) {
+ html_source->AddLocalizedString("castingActivityStatus",
+ IDS_MEDIA_ROUTER_CASTING_ACTIVITY_STATUS);
+ html_source->AddLocalizedString("stopCastingButtonText",
+ IDS_MEDIA_ROUTER_STOP_CASTING_BUTTON);
+ html_source->AddLocalizedString("startCastingButtonText",
+ IDS_MEDIA_ROUTER_START_CASTING_BUTTON);
+}
+
+void AddIssuesStrings(content::WebUIDataSource* html_source) {
+ html_source->AddLocalizedString("dismissButton",
+ IDS_MEDIA_ROUTER_DISMISS_BUTTON);
+ html_source->AddLocalizedString("issueHeaderText",
+ IDS_MEDIA_ROUTER_ISSUE_HEADER);
+}
+
+void AddMediaRouterContainerStrings(content::WebUIDataSource* html_source) {
+ html_source->AddLocalizedString("firstRunFlowButtonText",
+ IDS_MEDIA_ROUTER_FIRST_RUN_FLOW_BUTTON);
+ html_source->AddLocalizedString("firstRunFlowText",
+ IDS_MEDIA_ROUTER_FIRST_RUN_FLOW_TEXT);
+ html_source->AddLocalizedString("firstRunFlowTitle",
+ IDS_MEDIA_ROUTER_FIRST_RUN_FLOW_TITLE);
+ html_source->AddLocalizedString("firstRunFlowCloudPrefText",
+ IDS_MEDIA_ROUTER_FIRST_RUN_FLOW_CLOUD_PREF_TEXT);
+ html_source->AddLocalizedString("autoCastMode",
+ IDS_MEDIA_ROUTER_AUTO_CAST_MODE);
+ html_source->AddLocalizedString("destinationMissingText",
+ IDS_MEDIA_ROUTER_DESTINATION_MISSING);
+ html_source->AddLocalizedString("searchInputLabel",
+ IDS_MEDIA_ROUTER_SEARCH_LABEL);
+ html_source->AddLocalizedString("searchNoMatchesText",
+ IDS_MEDIA_ROUTER_SEARCH_NO_MATCHES);
+ html_source->AddLocalizedString("selectCastModeHeaderText",
+ IDS_MEDIA_ROUTER_SELECT_CAST_MODE_HEADER);
+ html_source->AddLocalizedString("shareYourScreenSubheadingText",
+ IDS_MEDIA_ROUTER_SHARE_YOUR_SCREEN_SUBHEADING);
+}
+
+} // namespace
+
+namespace media_router {
+
+void AddLocalizedStrings(content::WebUIDataSource* html_source) {
+ AddMediaRouterStrings(html_source);
+ AddRouteDetailsStrings(html_source);
+ AddIssuesStrings(html_source);
+ AddMediaRouterContainerStrings(html_source);
+ html_source->SetJsonPath(kLocalizedStringsFile);
+}
+
+} // namespace media_router
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.h b/chromium/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.h
new file mode 100644
index 00000000000..e700a0ed6e3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_LOCALIZED_STRINGS_PROVIDER_H_
+#define CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_LOCALIZED_STRINGS_PROVIDER_H_
+
+namespace content {
+class WebUIDataSource;
+}
+
+namespace media_router {
+
+// Adds the strings needed by Media Router to |html_source|. This function
+// causes |html_source| to expose a strings.js file from its source which
+// contains a mapping from string's name to its translated value.
+void AddLocalizedStrings(content::WebUIDataSource* html_source);
+
+} // namespace media_router
+
+#endif // CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_LOCALIZED_STRINGS_PROVIDER_H_
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_resources_provider.cc b/chromium/chrome/browser/ui/webui/media_router/media_router_resources_provider.cc
new file mode 100644
index 00000000000..6450288a4c6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_resources_provider.cc
@@ -0,0 +1,91 @@
+// 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.
+
+#include "chrome/browser/ui/webui/media_router/media_router_resources_provider.h"
+
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace {
+
+void AddMainWebResources(content::WebUIDataSource* html_source) {
+ html_source->AddResourcePath("media_router.js", IDR_MEDIA_ROUTER_JS);
+ html_source->AddResourcePath("media_router_common.css",
+ IDR_MEDIA_ROUTER_COMMON_CSS);
+ html_source->AddResourcePath("media_router.css",
+ IDR_MEDIA_ROUTER_CSS);
+ html_source->AddResourcePath("media_router_data.js",
+ IDR_MEDIA_ROUTER_DATA_JS);
+ html_source->AddResourcePath("media_router_ui_interface.js",
+ IDR_MEDIA_ROUTER_UI_INTERFACE_JS);
+}
+
+void AddPolymerElements(content::WebUIDataSource* html_source) {
+ html_source->AddResourcePath(
+ "icons/media_router_icons.html",
+ IDR_MEDIA_ROUTER_ICONS_HTML);
+ html_source->AddResourcePath(
+ "elements/issue_banner/issue_banner.css",
+ IDR_ISSUE_BANNER_CSS);
+ html_source->AddResourcePath(
+ "elements/issue_banner/issue_banner.html",
+ IDR_ISSUE_BANNER_HTML);
+ html_source->AddResourcePath(
+ "elements/issue_banner/issue_banner.js",
+ IDR_ISSUE_BANNER_JS);
+ html_source->AddResourcePath(
+ "elements/media_router_container/media_router_container.css",
+ IDR_MEDIA_ROUTER_CONTAINER_CSS);
+ html_source->AddResourcePath(
+ "elements/media_router_container/media_router_container.html",
+ IDR_MEDIA_ROUTER_CONTAINER_HTML);
+ html_source->AddResourcePath(
+ "elements/media_router_container/media_router_container.js",
+ IDR_MEDIA_ROUTER_CONTAINER_JS);
+ html_source->AddResourcePath(
+ "elements/media_router_header/media_router_header.css",
+ IDR_MEDIA_ROUTER_HEADER_CSS);
+ html_source->AddResourcePath(
+ "elements/media_router_header/media_router_header.html",
+ IDR_MEDIA_ROUTER_HEADER_HTML);
+ html_source->AddResourcePath(
+ "elements/media_router_header/media_router_header.js",
+ IDR_MEDIA_ROUTER_HEADER_JS);
+ html_source->AddResourcePath(
+ "elements/media_router_search_highlighter/"
+ "media_router_search_highlighter.css",
+ IDR_MEDIA_ROUTER_SEARCH_HIGHLIGHTER_CSS);
+ html_source->AddResourcePath(
+ "elements/media_router_search_highlighter/"
+ "media_router_search_highlighter.html",
+ IDR_MEDIA_ROUTER_SEARCH_HIGHLIGHTER_HTML);
+ html_source->AddResourcePath(
+ "elements/media_router_search_highlighter/"
+ "media_router_search_highlighter.js",
+ IDR_MEDIA_ROUTER_SEARCH_HIGHLIGHTER_JS);
+ html_source->AddResourcePath(
+ "elements/route_details/route_details.css",
+ IDR_ROUTE_DETAILS_CSS);
+ html_source->AddResourcePath(
+ "elements/route_details/route_details.html",
+ IDR_ROUTE_DETAILS_HTML);
+ html_source->AddResourcePath(
+ "elements/route_details/route_details.js",
+ IDR_ROUTE_DETAILS_JS);
+ html_source->AddResourcePath(
+ "elements/media_router_container/pseudo_sink_search_state.js",
+ IDR_PSEUDO_SINK_SEARCH_STATE_JS);
+}
+
+} // namespace
+
+namespace media_router {
+
+void AddMediaRouterUIResources(content::WebUIDataSource* html_source) {
+ AddMainWebResources(html_source);
+ AddPolymerElements(html_source);
+ html_source->SetDefaultResource(IDR_MEDIA_ROUTER_HTML);
+}
+
+} // namespace media_router
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_resources_provider.h b/chromium/chrome/browser/ui/webui/media_router/media_router_resources_provider.h
new file mode 100644
index 00000000000..1c62084f4c4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_resources_provider.h
@@ -0,0 +1,19 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_RESOURCES_PROVIDER_H_
+#define CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_RESOURCES_PROVIDER_H_
+
+namespace content {
+class WebUIDataSource;
+}
+
+namespace media_router {
+
+// Adds the resources needed by Media Router to |html_source|.
+void AddMediaRouterUIResources(content::WebUIDataSource* html_source);
+
+} // namespace media_router
+
+#endif // CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_RESOURCES_PROVIDER_H_
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
new file mode 100644
index 00000000000..044db042dda
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_ui.cc
@@ -0,0 +1,838 @@
+// 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.
+
+#include "chrome/browser/ui/webui/media_router/media_router_ui.h"
+
+#include <algorithm>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+#include "base/guid.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/media/router/create_presentation_connection_request.h"
+#include "chrome/browser/media/router/issues_observer.h"
+#include "chrome/browser/media/router/media_router.h"
+#include "chrome/browser/media/router/media_router_factory.h"
+#include "chrome/browser/media/router/media_router_metrics.h"
+#include "chrome/browser/media/router/media_routes_observer.h"
+#include "chrome/browser/media/router/media_sinks_observer.h"
+#include "chrome/browser/media/router/mojo/media_router_mojo_impl.h"
+#include "chrome/browser/media/router/presentation_service_delegate_impl.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sessions/session_tab_helper.h"
+#include "chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.h"
+#include "chrome/browser/ui/webui/media_router/media_router_resources_provider.h"
+#include "chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/media_router/issue.h"
+#include "chrome/common/media_router/media_route.h"
+#include "chrome/common/media_router/media_sink.h"
+#include "chrome/common/media_router/media_source.h"
+#include "chrome/common/media_router/media_source_helper.h"
+#include "chrome/common/media_router/route_request_result.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/constants.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.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"
+
+namespace media_router {
+
+namespace {
+
+// The amount of time to wait for a response when creating a new route.
+const int kCreateRouteTimeoutSeconds = 20;
+const int kCreateRouteTimeoutSecondsForTab = 60;
+const int kCreateRouteTimeoutSecondsForDesktop = 120;
+
+std::string GetHostFromURL(const GURL& gurl) {
+ if (gurl.is_empty()) return std::string();
+ std::string host = gurl.host();
+ if (base::StartsWith(host, "www.", base::CompareCase::INSENSITIVE_ASCII))
+ host = host.substr(4);
+ return host;
+}
+
+std::string TruncateHost(const std::string& host) {
+ const std::string truncated =
+ net::registry_controlled_domains::GetDomainAndRegistry(
+ host, net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
+ // The truncation will be empty in some scenarios (e.g. host is
+ // simply an IP address). Fail gracefully.
+ return truncated.empty() ? host : truncated;
+}
+
+base::TimeDelta GetRouteRequestTimeout(MediaCastMode cast_mode) {
+ switch (cast_mode) {
+ case DEFAULT:
+ return base::TimeDelta::FromSeconds(kCreateRouteTimeoutSeconds);
+ case TAB_MIRROR:
+ return base::TimeDelta::FromSeconds(kCreateRouteTimeoutSecondsForTab);
+ case DESKTOP_MIRROR:
+ return base::TimeDelta::FromSeconds(kCreateRouteTimeoutSecondsForDesktop);
+ default:
+ NOTREACHED();
+ return base::TimeDelta();
+ }
+}
+
+// Returns the first source in |sources| that can be connected to by using the
+// "Cast" button in the dialog, or an empty source if there is none. This is
+// 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);
+ return source_it != sources.end() ? *source_it : MediaSource("");
+}
+
+} // namespace
+
+// static
+std::string MediaRouterUI::GetExtensionName(
+ const GURL& gurl, extensions::ExtensionRegistry* registry) {
+ if (gurl.is_empty() || !registry) return std::string();
+
+ const extensions::Extension* extension =
+ registry->enabled_extensions().GetExtensionOrAppByURL(gurl);
+
+ return extension ? extension->name() : std::string();
+}
+
+// This class calls to refresh the UI when the highest priority issue is
+// updated.
+class MediaRouterUI::UIIssuesObserver : public IssuesObserver {
+ public:
+ UIIssuesObserver(MediaRouter* router, MediaRouterUI* ui)
+ : IssuesObserver(router), ui_(ui) {
+ DCHECK(ui);
+ }
+
+ ~UIIssuesObserver() override {}
+
+ // IssuesObserver implementation.
+ void OnIssue(const Issue& issue) override { ui_->SetIssue(issue); }
+ void OnIssuesCleared() override { ui_->ClearIssue(); }
+
+ private:
+ // Reference back to the owning MediaRouterUI instance.
+ MediaRouterUI* ui_;
+
+ DISALLOW_COPY_AND_ASSIGN(UIIssuesObserver);
+};
+
+MediaRouterUI::UIMediaRoutesObserver::UIMediaRoutesObserver(
+ MediaRouter* router,
+ const MediaSource::Id& source_id,
+ const RoutesUpdatedCallback& callback)
+ : MediaRoutesObserver(router, source_id), callback_(callback) {
+ DCHECK(!callback_.is_null());
+}
+
+MediaRouterUI::UIMediaRoutesObserver::~UIMediaRoutesObserver() {}
+
+void MediaRouterUI::UIMediaRoutesObserver::OnRoutesUpdated(
+ const std::vector<MediaRoute>& routes,
+ const std::vector<MediaRoute::Id>& joinable_route_ids) {
+ callback_.Run(routes, joinable_route_ids);
+}
+
+MediaRouterUI::UIMediaRouteControllerObserver::UIMediaRouteControllerObserver(
+ MediaRouterUI* ui,
+ scoped_refptr<MediaRouteController> controller)
+ : MediaRouteController::Observer(std::move(controller)), ui_(ui) {
+ if (controller_->current_media_status())
+ OnMediaStatusUpdated(controller_->current_media_status().value());
+}
+
+MediaRouterUI::UIMediaRouteControllerObserver::
+ ~UIMediaRouteControllerObserver() {}
+
+void MediaRouterUI::UIMediaRouteControllerObserver::OnMediaStatusUpdated(
+ const MediaStatus& status) {
+ ui_->UpdateMediaRouteStatus(status);
+}
+
+void MediaRouterUI::UIMediaRouteControllerObserver::OnControllerInvalidated() {
+ ui_->OnRouteControllerInvalidated();
+}
+
+MediaRouterUI::MediaRouterUI(content::WebUI* web_ui)
+ : ConstrainedWebDialogUI(web_ui),
+ ui_initialized_(false),
+ current_route_request_id_(-1),
+ route_request_counter_(0),
+ initiator_(nullptr),
+ router_(nullptr),
+ weak_factory_(this) {
+ auto handler = base::MakeUnique<MediaRouterWebUIMessageHandler>(this);
+ handler_ = handler.get();
+
+ // Create a WebUIDataSource containing the chrome://media-router page's
+ // content.
+ std::unique_ptr<content::WebUIDataSource> html_source(
+ content::WebUIDataSource::Create(chrome::kChromeUIMediaRouterHost));
+
+ content::WebContents* wc = web_ui->GetWebContents();
+ DCHECK(wc);
+
+ router_ =
+ MediaRouterFactory::GetApiForBrowserContext(wc->GetBrowserContext());
+
+ // Allows UI to load extensionview.
+ // TODO(haibinlu): limit object-src to current extension once crbug/514866
+ // is fixed.
+ html_source->OverrideContentSecurityPolicyObjectSrc("object-src chrome:;");
+
+ AddLocalizedStrings(html_source.get());
+ AddMediaRouterUIResources(html_source.get());
+ // Ownership of |html_source| is transferred to the BrowserContext.
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui),
+ html_source.release());
+
+ web_ui->AddMessageHandler(std::move(handler));
+}
+
+MediaRouterUI::~MediaRouterUI() {
+ if (query_result_manager_.get()) query_result_manager_->RemoveObserver(this);
+ if (presentation_service_delegate_.get())
+ presentation_service_delegate_->RemoveDefaultPresentationRequestObserver(
+ this);
+ // If |create_session_request_| still exists, then it means presentation route
+ // request was never attempted.
+ if (create_session_request_) {
+ bool presentation_sinks_available = std::any_of(
+ sinks_.begin(), sinks_.end(), [](const MediaSinkWithCastModes& sink) {
+ return base::ContainsValue(sink.cast_modes, MediaCastMode::DEFAULT);
+ });
+ if (presentation_sinks_available) {
+ create_session_request_->InvokeErrorCallback(content::PresentationError(
+ content::PRESENTATION_ERROR_PRESENTATION_REQUEST_CANCELLED,
+ "Dialog closed."));
+ } else {
+ create_session_request_->InvokeErrorCallback(content::PresentationError(
+ content::PRESENTATION_ERROR_NO_AVAILABLE_SCREENS,
+ "No screens found."));
+ }
+ }
+}
+
+void MediaRouterUI::InitWithDefaultMediaSource(
+ content::WebContents* initiator,
+ PresentationServiceDelegateImpl* delegate) {
+ DCHECK(initiator);
+ DCHECK(!presentation_service_delegate_);
+ DCHECK(!query_result_manager_.get());
+
+ InitCommon(initiator);
+ if (delegate) {
+ presentation_service_delegate_ = delegate->GetWeakPtr();
+ presentation_service_delegate_->AddDefaultPresentationRequestObserver(this);
+ }
+
+ if (delegate && delegate->HasDefaultPresentationRequest()) {
+ OnDefaultPresentationChanged(delegate->GetDefaultPresentationRequest());
+ } else {
+ // Register for MediaRoute updates without a media source.
+ routes_observer_.reset(new UIMediaRoutesObserver(
+ router_, MediaSource::Id(),
+ base::Bind(&MediaRouterUI::OnRoutesUpdated, base::Unretained(this))));
+ }
+}
+
+void MediaRouterUI::InitWithPresentationSessionRequest(
+ content::WebContents* initiator,
+ PresentationServiceDelegateImpl* delegate,
+ std::unique_ptr<CreatePresentationConnectionRequest>
+ create_session_request) {
+ DCHECK(initiator);
+ DCHECK(delegate);
+ DCHECK(create_session_request);
+ DCHECK(!create_session_request_);
+ DCHECK(!query_result_manager_);
+
+ create_session_request_ = std::move(create_session_request);
+ presentation_service_delegate_ = delegate->GetWeakPtr();
+
+ InitCommon(initiator);
+ OnDefaultPresentationChanged(
+ create_session_request_->presentation_request());
+}
+
+void MediaRouterUI::InitCommon(content::WebContents* initiator) {
+ DCHECK(initiator);
+ DCHECK(router_);
+
+ TRACE_EVENT_NESTABLE_ASYNC_INSTANT1("media_router", "UI", initiator,
+ "MediaRouterUI::InitCommon", this);
+
+ // Presentation requests from content must show the origin requesting
+ // presentation: crbug.com/704964
+ if (create_session_request_)
+ forced_cast_mode_ = MediaCastMode::DEFAULT;
+
+ router_->OnUserGesture();
+
+ // Create |collator_| before |query_result_manager_| so that |collator_| is
+ // already set up when we get a callback from |query_result_manager_|.
+ UErrorCode error = U_ZERO_ERROR;
+ const std::string& locale = g_browser_process->GetApplicationLocale();
+ collator_.reset(
+ icu::Collator::createInstance(icu::Locale(locale.c_str()), error));
+ if (U_FAILURE(error)) {
+ DLOG(ERROR) << "Failed to create collator for locale " << locale;
+ collator_.reset();
+ }
+
+ query_result_manager_.reset(new QueryResultManager(router_));
+ query_result_manager_->AddObserver(this);
+
+ // Use a placeholder URL as origin for mirroring.
+ url::Origin origin{GURL(chrome::kChromeUIMediaRouterURL)};
+
+ // Desktop mirror mode is always available.
+ query_result_manager_->SetSourcesForCastMode(
+ MediaCastMode::DESKTOP_MIRROR, {MediaSourceForDesktop()}, origin);
+ initiator_ = initiator;
+ SessionID::id_type tab_id = SessionTabHelper::IdForTab(initiator);
+ if (tab_id != -1) {
+ MediaSource mirroring_source(MediaSourceForTab(tab_id));
+ query_result_manager_->SetSourcesForCastMode(MediaCastMode::TAB_MIRROR,
+ {mirroring_source}, origin);
+ }
+
+ UpdateCastModes();
+
+ // Get the current list of media routes, so that the WebUI will have routes
+ // information at initialization.
+ OnRoutesUpdated(router_->GetCurrentRoutes(), std::vector<MediaRoute::Id>());
+}
+
+void MediaRouterUI::InitForTest(
+ MediaRouter* router,
+ content::WebContents* initiator,
+ MediaRouterWebUIMessageHandler* handler,
+ std::unique_ptr<CreatePresentationConnectionRequest>
+ create_session_request) {
+ router_ = router;
+ handler_ = handler;
+ create_session_request_ = std::move(create_session_request);
+ InitCommon(initiator);
+ if (create_session_request_) {
+ OnDefaultPresentationChanged(
+ create_session_request_->presentation_request());
+ }
+}
+
+void MediaRouterUI::OnDefaultPresentationChanged(
+ const PresentationRequest& presentation_request) {
+ std::vector<MediaSource> sources = presentation_request.GetMediaSources();
+ presentation_request_.reset(new PresentationRequest(presentation_request));
+ query_result_manager_->SetSourcesForCastMode(
+ MediaCastMode::DEFAULT, sources, presentation_request_->frame_origin());
+ // Register for MediaRoute updates. NOTE(mfoltz): If there are multiple
+ // sources that can be connected to via the dialog, this will break. We will
+ // need to observe multiple sources (keyed by sinks) in that case. As this is
+ // Cast-specific for the forseeable future, it may be simpler to plumb a new
+ // observer API for this case.
+ const MediaSource source_for_route_observer =
+ GetSourceForRouteObserver(sources);
+ routes_observer_.reset(new UIMediaRoutesObserver(
+ router_, source_for_route_observer.id(),
+ base::Bind(&MediaRouterUI::OnRoutesUpdated, base::Unretained(this))));
+
+ UpdateCastModes();
+}
+
+void MediaRouterUI::OnDefaultPresentationRemoved() {
+ presentation_request_.reset();
+ query_result_manager_->RemoveSourcesForCastMode(MediaCastMode::DEFAULT);
+
+ // This should not be set if the dialog was initiated with a default
+ // presentation request from the top level frame. However, clear it just to
+ // be safe.
+ forced_cast_mode_ = base::nullopt;
+
+ // Register for MediaRoute updates without a media source.
+ routes_observer_.reset(new UIMediaRoutesObserver(
+ router_, MediaSource::Id(),
+ base::Bind(&MediaRouterUI::OnRoutesUpdated, base::Unretained(this))));
+ UpdateCastModes();
+}
+
+void MediaRouterUI::UpdateCastModes() {
+ // Gets updated cast modes from |query_result_manager_| and forwards it to UI.
+ cast_modes_ = query_result_manager_->GetSupportedCastModes();
+ if (ui_initialized_) {
+ handler_->UpdateCastModes(cast_modes_, GetPresentationRequestSourceName(),
+ forced_cast_mode());
+ }
+}
+
+void MediaRouterUI::UpdateRoutesToCastModesMapping() {
+ std::unordered_map<MediaSource::Id, MediaCastMode> available_source_map;
+ for (const auto& cast_mode : cast_modes_) {
+ for (const auto& source :
+ query_result_manager_->GetSourcesForCastMode(cast_mode)) {
+ available_source_map.insert(std::make_pair(source.id(), cast_mode));
+ }
+ }
+
+ routes_and_cast_modes_.clear();
+ for (const auto& route : routes_) {
+ auto source_entry = available_source_map.find(route.media_source().id());
+ if (source_entry != available_source_map.end()) {
+ routes_and_cast_modes_.insert(
+ std::make_pair(route.media_route_id(), source_entry->second));
+ }
+ }
+}
+
+void MediaRouterUI::Close() {
+ ConstrainedWebDialogDelegate* delegate = GetConstrainedDelegate();
+ if (delegate) {
+ delegate->GetWebDialogDelegate()->OnDialogClosed(std::string());
+ delegate->OnDialogCloseFromWebUI();
+ }
+}
+
+void MediaRouterUI::UIInitialized() {
+ TRACE_EVENT_NESTABLE_ASYNC_END0("media_router", "UI", initiator_);
+ ui_initialized_ = true;
+
+ // Register for Issue updates.
+ issues_observer_.reset(new UIIssuesObserver(router_, this));
+ issues_observer_->Init();
+}
+
+bool MediaRouterUI::CreateRoute(const MediaSink::Id& sink_id,
+ MediaCastMode cast_mode) {
+ MediaSource::Id source_id;
+ url::Origin origin;
+ std::vector<MediaRouteResponseCallback> route_response_callbacks;
+ base::TimeDelta timeout;
+ bool incognito;
+ if (!SetRouteParameters(sink_id, cast_mode, &source_id, &origin,
+ &route_response_callbacks, &timeout, &incognito)) {
+ SendIssueForUnableToCast(cast_mode);
+ return false;
+ }
+ router_->CreateRoute(source_id, sink_id, origin, initiator_,
+ route_response_callbacks, timeout, incognito);
+ return true;
+}
+
+bool MediaRouterUI::SetRouteParameters(
+ const MediaSink::Id& sink_id,
+ MediaCastMode cast_mode,
+ MediaSource::Id* source_id,
+ url::Origin* origin,
+ std::vector<MediaRouteResponseCallback>* route_response_callbacks,
+ base::TimeDelta* timeout,
+ bool* incognito) {
+ DCHECK(query_result_manager_.get());
+ DCHECK(initiator_);
+
+ // Note that there is a rarely-encountered bug, where the MediaCastMode to
+ // MediaSource mapping could have been updated, between when the user clicked
+ // on the UI to start a create route request, and when this function is
+ // called. However, since the user does not have visibility into the
+ // MediaSource, and that it occurs very rarely in practice, we leave it as-is
+ // for now.
+ std::unique_ptr<MediaSource> source =
+ query_result_manager_->GetSourceForCastModeAndSink(cast_mode, sink_id);
+ if (!source) {
+ LOG(ERROR) << "No corresponding MediaSource for cast mode "
+ << static_cast<int>(cast_mode) << " and sink " << sink_id;
+ return false;
+ }
+ *source_id = source->id();
+
+ bool for_default_source = cast_mode == MediaCastMode::DEFAULT;
+ if (for_default_source && !presentation_request_) {
+ DLOG(ERROR) << "Requested to create a route for presentation, but "
+ << "presentation request is missing.";
+ return false;
+ }
+
+ current_route_request_id_ = ++route_request_counter_;
+ *origin = for_default_source
+ ? presentation_request_->frame_origin()
+ : url::Origin(GURL(chrome::kChromeUIMediaRouterURL));
+ DVLOG(1) << "DoCreateRoute: origin: " << *origin;
+
+ // There are 3 cases. In cases (1) and (3) the MediaRouterUI will need to be
+ // notified. In case (2) the dialog will be closed.
+ // (1) Non-presentation route request (e.g., mirroring). No additional
+ // notification necessary.
+ // (2) Presentation route request for a Presentation API startSession call.
+ // The startSession (CreatePresentationConnectionRequest) will need to be
+ // answered with the route response.
+ // (3) Browser-initiated presentation route request. If successful,
+ // PresentationServiceDelegateImpl will have to be notified. Note that we
+ // treat subsequent route requests from a Presentation API-initiated dialogs
+ // as browser-initiated.
+ if (!for_default_source || !create_session_request_) {
+ route_response_callbacks->push_back(base::Bind(
+ &MediaRouterUI::OnRouteResponseReceived, weak_factory_.GetWeakPtr(),
+ current_route_request_id_, sink_id, cast_mode,
+ base::UTF8ToUTF16(GetTruncatedPresentationRequestSourceName())));
+ }
+ if (for_default_source) {
+ if (create_session_request_) {
+ // |create_session_request_| will be nullptr after this call, as the
+ // object will be transferred to the callback.
+ route_response_callbacks->push_back(
+ base::Bind(&CreatePresentationConnectionRequest::HandleRouteResponse,
+ base::Passed(&create_session_request_)));
+ route_response_callbacks->push_back(
+ base::Bind(&MediaRouterUI::HandleCreateSessionRequestRouteResponse,
+ weak_factory_.GetWeakPtr()));
+ } else if (presentation_service_delegate_) {
+ route_response_callbacks->push_back(
+ base::Bind(&PresentationServiceDelegateImpl::OnRouteResponse,
+ presentation_service_delegate_, *presentation_request_));
+ }
+ }
+
+ *timeout = GetRouteRequestTimeout(cast_mode);
+ *incognito = Profile::FromWebUI(web_ui())->IsOffTheRecord();
+
+ return true;
+}
+
+bool MediaRouterUI::ConnectRoute(const MediaSink::Id& sink_id,
+ const MediaRoute::Id& route_id) {
+ MediaSource::Id source_id;
+ url::Origin origin;
+ std::vector<MediaRouteResponseCallback> route_response_callbacks;
+ base::TimeDelta timeout;
+ bool incognito;
+ if (!SetRouteParameters(sink_id, MediaCastMode::DEFAULT, &source_id, &origin,
+ &route_response_callbacks, &timeout, &incognito)) {
+ SendIssueForUnableToCast(MediaCastMode::DEFAULT);
+ return false;
+ }
+ router_->ConnectRouteByRouteId(source_id, route_id, origin, initiator_,
+ route_response_callbacks, timeout, incognito);
+ return true;
+}
+
+void MediaRouterUI::CloseRoute(const MediaRoute::Id& route_id) {
+ router_->TerminateRoute(route_id);
+}
+
+void MediaRouterUI::AddIssue(const IssueInfo& issue) {
+ router_->AddIssue(issue);
+}
+
+void MediaRouterUI::ClearIssue(const Issue::Id& issue_id) {
+ router_->ClearIssue(issue_id);
+}
+
+void MediaRouterUI::SearchSinksAndCreateRoute(
+ const MediaSink::Id& sink_id,
+ const std::string& search_criteria,
+ const std::string& domain,
+ MediaCastMode cast_mode) {
+ std::unique_ptr<MediaSource> source =
+ query_result_manager_->GetSourceForCastModeAndSink(cast_mode, sink_id);
+ const std::string source_id = source ? source->id() : "";
+
+ // 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));
+}
+
+bool MediaRouterUI::UserSelectedTabMirroringForCurrentOrigin() const {
+ const base::ListValue* origins =
+ Profile::FromWebUI(web_ui())->GetPrefs()->GetList(
+ prefs::kMediaRouterTabMirroringSources);
+ return origins->Find(base::Value(GetSerializedInitiatorOrigin())) !=
+ origins->end();
+}
+
+void MediaRouterUI::RecordCastModeSelection(MediaCastMode cast_mode) {
+ ListPrefUpdate update(Profile::FromWebUI(web_ui())->GetPrefs(),
+ prefs::kMediaRouterTabMirroringSources);
+
+ switch (cast_mode) {
+ case MediaCastMode::DEFAULT:
+ update->Remove(base::Value(GetSerializedInitiatorOrigin()), nullptr);
+ break;
+ case MediaCastMode::TAB_MIRROR:
+ update->AppendIfNotPresent(
+ base::MakeUnique<base::Value>(GetSerializedInitiatorOrigin()));
+ break;
+ case MediaCastMode::DESKTOP_MIRROR:
+ // Desktop mirroring isn't domain-specific, so we don't record the
+ // selection.
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+void MediaRouterUI::OnResultsUpdated(
+ const std::vector<MediaSinkWithCastModes>& sinks) {
+ sinks_ = sinks;
+
+ const icu::Collator* collator_ptr = collator_.get();
+ std::sort(sinks_.begin(), sinks_.end(),
+ [collator_ptr](const MediaSinkWithCastModes& sink1,
+ const MediaSinkWithCastModes& sink2) {
+ return sink1.sink.CompareUsingCollator(sink2.sink, collator_ptr);
+ });
+
+ if (ui_initialized_)
+ handler_->UpdateSinks(sinks_);
+}
+
+void MediaRouterUI::SetIssue(const Issue& issue) {
+ if (ui_initialized_)
+ handler_->UpdateIssue(issue);
+}
+
+void MediaRouterUI::ClearIssue() {
+ if (ui_initialized_)
+ handler_->ClearIssue();
+}
+
+void MediaRouterUI::OnRoutesUpdated(
+ const std::vector<MediaRoute>& routes,
+ const std::vector<MediaRoute::Id>& joinable_route_ids) {
+ routes_.clear();
+ joinable_route_ids_.clear();
+
+ for (const MediaRoute& route : routes) {
+ if (route.for_display()) {
+#ifndef NDEBUG
+ for (const MediaRoute& existing_route : routes_) {
+ if (existing_route.media_sink_id() == route.media_sink_id()) {
+ DVLOG(2) << "Received another route for display with the same sink"
+ << " id as an existing route. " << route.media_route_id()
+ << " has the same sink id as "
+ << existing_route.media_sink_id() << ".";
+ }
+ }
+#endif
+ if (base::ContainsValue(joinable_route_ids, route.media_route_id())) {
+ joinable_route_ids_.push_back(route.media_route_id());
+ }
+
+ routes_.push_back(route);
+ }
+ }
+ UpdateRoutesToCastModesMapping();
+
+ if (ui_initialized_)
+ handler_->UpdateRoutes(routes_, joinable_route_ids_,
+ routes_and_cast_modes_);
+}
+
+void MediaRouterUI::OnRouteResponseReceived(
+ int route_request_id,
+ const MediaSink::Id& sink_id,
+ MediaCastMode cast_mode,
+ const base::string16& presentation_request_source_name,
+ const RouteRequestResult& result) {
+ DVLOG(1) << "OnRouteResponseReceived";
+ // If we receive a new route that we aren't expecting, do nothing.
+ if (route_request_id != current_route_request_id_) return;
+
+ const MediaRoute* route = result.route();
+ if (!route) {
+ // The provider will handle sending an issue for a failed route request.
+ DVLOG(1) << "MediaRouteResponse returned error: " << result.error();
+ }
+
+ handler_->OnCreateRouteResponseReceived(sink_id, route);
+ current_route_request_id_ = -1;
+
+ if (result.result_code() == RouteRequestResult::TIMED_OUT)
+ SendIssueForRouteTimeout(cast_mode, presentation_request_source_name);
+}
+
+void MediaRouterUI::HandleCreateSessionRequestRouteResponse(
+ const RouteRequestResult&) {
+ Close();
+}
+
+void MediaRouterUI::OnSearchSinkResponseReceived(
+ MediaCastMode cast_mode,
+ const MediaSink::Id& found_sink_id) {
+ DVLOG(1) << "OnSearchSinkResponseReceived";
+ handler_->ReturnSearchResult(found_sink_id);
+
+ MediaSource::Id source_id;
+ url::Origin origin;
+ std::vector<MediaRouteResponseCallback> route_response_callbacks;
+ base::TimeDelta timeout;
+ bool incognito;
+ if (!SetRouteParameters(found_sink_id, cast_mode, &source_id, &origin,
+ &route_response_callbacks, &timeout, &incognito)) {
+ SendIssueForUnableToCast(cast_mode);
+ return;
+ }
+ router_->CreateRoute(source_id, found_sink_id, origin, initiator_,
+ route_response_callbacks, timeout, incognito);
+}
+
+void MediaRouterUI::SendIssueForRouteTimeout(
+ MediaCastMode cast_mode,
+ const base::string16& presentation_request_source_name) {
+ std::string issue_title;
+ switch (cast_mode) {
+ case DEFAULT:
+ DLOG_IF(ERROR, presentation_request_source_name.empty())
+ << "Empty presentation request source name.";
+ issue_title =
+ l10n_util::GetStringFUTF8(IDS_MEDIA_ROUTER_ISSUE_CREATE_ROUTE_TIMEOUT,
+ presentation_request_source_name);
+ break;
+ case TAB_MIRROR:
+ issue_title = l10n_util::GetStringUTF8(
+ IDS_MEDIA_ROUTER_ISSUE_CREATE_ROUTE_TIMEOUT_FOR_TAB);
+ break;
+ case DESKTOP_MIRROR:
+ issue_title = l10n_util::GetStringUTF8(
+ IDS_MEDIA_ROUTER_ISSUE_CREATE_ROUTE_TIMEOUT_FOR_DESKTOP);
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ AddIssue(IssueInfo(issue_title, IssueInfo::Action::DISMISS,
+ IssueInfo::Severity::NOTIFICATION));
+}
+
+void MediaRouterUI::SendIssueForUnableToCast(MediaCastMode cast_mode) {
+ // For a generic error, claim a tab error unless it was specifically desktop
+ // mirroring.
+ std::string issue_title =
+ (cast_mode == MediaCastMode::DESKTOP_MIRROR)
+ ? l10n_util::GetStringUTF8(
+ IDS_MEDIA_ROUTER_ISSUE_UNABLE_TO_CAST_DESKTOP)
+ : l10n_util::GetStringUTF8(
+ IDS_MEDIA_ROUTER_ISSUE_CREATE_ROUTE_TIMEOUT_FOR_TAB);
+ AddIssue(IssueInfo(issue_title, IssueInfo::Action::DISMISS,
+ IssueInfo::Severity::WARNING));
+}
+
+GURL MediaRouterUI::GetFrameURL() const {
+ return presentation_request_ ? presentation_request_->frame_origin().GetURL()
+ : GURL();
+}
+
+std::string MediaRouterUI::GetPresentationRequestSourceName() const {
+ GURL gurl = GetFrameURL();
+ return gurl.SchemeIs(extensions::kExtensionScheme)
+ ? GetExtensionName(gurl, extensions::ExtensionRegistry::Get(
+ Profile::FromWebUI(web_ui())))
+ : GetHostFromURL(gurl);
+}
+
+std::string MediaRouterUI::GetTruncatedPresentationRequestSourceName() const {
+ GURL gurl = GetFrameURL();
+ return gurl.SchemeIs(extensions::kExtensionScheme)
+ ? GetExtensionName(gurl, extensions::ExtensionRegistry::Get(
+ Profile::FromWebUI(web_ui())))
+ : TruncateHost(GetHostFromURL(gurl));
+}
+
+const std::set<MediaCastMode>& MediaRouterUI::cast_modes() const {
+ return cast_modes_;
+}
+
+const std::string& MediaRouterUI::GetRouteProviderExtensionId() const {
+ // TODO(crbug.com/597778): remove reference to MediaRouterMojoImpl
+ return static_cast<MediaRouterMojoImpl*>(router_)
+ ->media_route_provider_extension_id();
+}
+
+void MediaRouterUI::SetUIInitializationTimer(const base::Time& start_time) {
+ DCHECK(!start_time.is_null());
+ start_time_ = start_time;
+}
+
+void MediaRouterUI::OnUIInitiallyLoaded() {
+ if (!start_time_.is_null()) {
+ MediaRouterMetrics::RecordMediaRouterDialogPaint(
+ base::Time::Now() - start_time_);
+ }
+}
+
+void MediaRouterUI::OnUIInitialDataReceived() {
+ if (!start_time_.is_null()) {
+ MediaRouterMetrics::RecordMediaRouterDialogLoaded(
+ base::Time::Now() - start_time_);
+ start_time_ = base::Time();
+ }
+}
+
+void MediaRouterUI::UpdateMaxDialogHeight(int height) {
+ handler_->UpdateMaxDialogHeight(height);
+}
+
+const MediaRouteController* MediaRouterUI::GetMediaRouteController() const {
+ return route_controller_observer_
+ ? route_controller_observer_->controller().get()
+ : nullptr;
+}
+
+void MediaRouterUI::OnMediaControllerUIAvailable(
+ const MediaRoute::Id& route_id) {
+ scoped_refptr<MediaRouteController> controller =
+ router_->GetRouteController(route_id);
+ if (!controller) {
+ DVLOG(1) << "Requested a route controller with an invalid route ID.";
+ return;
+ }
+ DVLOG_IF(1, route_controller_observer_)
+ << "Route controller observer unexpectedly exists.";
+ route_controller_observer_ =
+ base::MakeUnique<UIMediaRouteControllerObserver>(this, controller);
+}
+
+void MediaRouterUI::OnMediaControllerUIClosed() {
+ route_controller_observer_.reset();
+}
+
+void MediaRouterUI::OnRouteControllerInvalidated() {
+ route_controller_observer_.reset();
+ handler_->OnRouteControllerInvalidated();
+}
+
+void MediaRouterUI::UpdateMediaRouteStatus(const MediaStatus& status) {
+ handler_->UpdateMediaRouteStatus(status);
+}
+
+std::string MediaRouterUI::GetSerializedInitiatorOrigin() const {
+ url::Origin origin = initiator_
+ ? url::Origin(initiator_->GetLastCommittedURL())
+ : url::Origin();
+ return origin.Serialize();
+}
+
+} // 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
new file mode 100644
index 00000000000..ea9474e6c49
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_ui.h
@@ -0,0 +1,411 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_UI_H_
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/media/router/mojo/media_route_controller.h"
+#include "chrome/browser/media/router/presentation_service_delegate_impl.h"
+#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
+#include "chrome/browser/ui/webui/media_router/media_cast_mode.h"
+#include "chrome/browser/ui/webui/media_router/media_sink_with_cast_modes.h"
+#include "chrome/browser/ui/webui/media_router/query_result_manager.h"
+#include "chrome/common/media_router/issue.h"
+#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 "url/gurl.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace extensions {
+class ExtensionRegistry;
+}
+
+namespace U_ICU_NAMESPACE {
+class Collator;
+}
+
+namespace media_router {
+
+class CreatePresentationConnectionRequest;
+class IssuesObserver;
+class MediaRoute;
+class MediaRouter;
+class MediaRoutesObserver;
+class MediaRouterWebUIMessageHandler;
+class MediaSink;
+class RouteRequestResult;
+
+// Implements the chrome://media-router user interface.
+class MediaRouterUI : public ConstrainedWebDialogUI,
+ public QueryResultManager::Observer,
+ public PresentationServiceDelegateImpl::
+ DefaultPresentationRequestObserver {
+ public:
+ // |web_ui| owns this object and is used to initialize the base class.
+ explicit MediaRouterUI(content::WebUI* web_ui);
+ ~MediaRouterUI() override;
+
+ // Initializes internal state (e.g. starts listening for MediaSinks) for
+ // targeting the default MediaSource (if any) of the initiator tab that owns
+ // |initiator|: Reference to the WebContents that initiated the dialog.
+ // Must not be null.
+ // |delegate|, as well as mirroring sources of that tab.
+ // The contents of the UI will change as the default MediaSource changes.
+ // If there is a default MediaSource, then DEFAULT MediaCastMode will be
+ // added to |cast_modes_|.
+ // Init* methods can only be called once.
+ // |delegate|: PresentationServiceDelegateImpl of the initiator tab.
+ // Must not be null.
+ // TODO(imcheng): Replace use of impl with an intermediate abstract
+ // interface.
+ void InitWithDefaultMediaSource(content::WebContents* initiator,
+ PresentationServiceDelegateImpl* delegate);
+
+ // Initializes internal state targeting the presentation specified in
+ // |request|. Also sets up mirroring sources based on |initiator|.
+ // This is different from |InitWithDefaultMediaSource| in that it does not
+ // listen for default media source changes, as the UI is fixed to the source
+ // in |request|.
+ // Init* methods can only be called once.
+ // |initiator|: Reference to the WebContents that initiated the dialog.
+ // Must not be null.
+ // |delegate|: PresentationServiceDelegateImpl of the initiator tab.
+ // Must not be null.
+ // |presentation_request|: The presentation request. This instance will take
+ // ownership of it. Must not be null.
+ void InitWithPresentationSessionRequest(
+ content::WebContents* initiator,
+ PresentationServiceDelegateImpl* delegate,
+ std::unique_ptr<CreatePresentationConnectionRequest>
+ presentation_request);
+
+ // Closes the media router UI.
+ void Close();
+
+ // Notifies this instance that the UI has been initialized.
+ virtual void UIInitialized();
+
+ // Requests a route be created from the source mapped to
+ // |cast_mode|, to the sink given by |sink_id|.
+ // Returns true if a route request is successfully submitted.
+ // |OnRouteResponseReceived()| will be invoked when the route request
+ // completes.
+ bool CreateRoute(const MediaSink::Id& sink_id, MediaCastMode cast_mode);
+
+ // Calls MediaRouter to join the given route.
+ bool ConnectRoute(const MediaSink::Id& sink_id,
+ const MediaRoute::Id& route_id);
+
+ // Calls MediaRouter to close the given route.
+ void CloseRoute(const MediaRoute::Id& route_id);
+
+ // Calls MediaRouter to add the given issue.
+ void AddIssue(const IssueInfo& issue);
+
+ // Calls MediaRouter to clear the given issue.
+ void ClearIssue(const Issue::Id& issue_id);
+
+ // Calls MediaRouter to search route providers for sinks matching
+ // |search_criteria| with the source that is currently associated with
+ // |cast_mode|. The user's domain |domain| is also used.
+ void SearchSinksAndCreateRoute(const MediaSink::Id& sink_id,
+ const std::string& search_criteria,
+ const std::string& domain,
+ MediaCastMode cast_mode);
+
+ // Returns true if the cast mode last chosen for the current origin is tab
+ // mirroring.
+ virtual bool UserSelectedTabMirroringForCurrentOrigin() const;
+
+ // Records the cast mode selection for the current origin, unless the cast
+ // mode is MediaCastMode::DESKTOP_MIRROR.
+ virtual void RecordCastModeSelection(MediaCastMode cast_mode);
+
+ // Returns the hostname of the default source'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_;
+ }
+ virtual const std::set<MediaCastMode>& cast_modes() const;
+ const std::unordered_map<MediaRoute::Id, MediaCastMode>&
+ routes_and_cast_modes() const {
+ return routes_and_cast_modes_;
+ }
+ const content::WebContents* initiator() const { return initiator_; }
+ const base::Optional<MediaCastMode>& forced_cast_mode() const {
+ return forced_cast_mode_;
+ }
+
+ virtual const std::string& GetRouteProviderExtensionId() const;
+
+ // Called to track UI metrics.
+ void SetUIInitializationTimer(const base::Time& start_time);
+ void OnUIInitiallyLoaded();
+ void OnUIInitialDataReceived();
+
+ void UpdateMaxDialogHeight(int height);
+
+ // Gets the route controller currently in use by the UI. Returns a nullptr if
+ // none is in use.
+ virtual const MediaRouteController* GetMediaRouteController() const;
+
+ // Called when a media controller UI surface is created. Creates an observer
+ // for the MediaRouteController for |route_id| to listen for media status
+ // updates.
+ virtual void OnMediaControllerUIAvailable(const MediaRoute::Id& route_id);
+ // Called when a media controller UI surface is closed. Resets the observer
+ // for MediaRouteController.
+ virtual void OnMediaControllerUIClosed();
+
+ void InitForTest(MediaRouter* router,
+ content::WebContents* initiator,
+ MediaRouterWebUIMessageHandler* handler,
+ std::unique_ptr<CreatePresentationConnectionRequest>
+ create_session_request);
+
+ private:
+ friend class MediaRouterUITest;
+
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, SortedSinks);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, SortSinksByIconType);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, FilterNonDisplayRoutes);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, FilterNonDisplayJoinableRoutes);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest,
+ UIMediaRoutesObserverAssignsCurrentCastModes);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest,
+ UIMediaRoutesObserverSkipsUnavailableCastModes);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, GetExtensionNameExtensionPresent);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest,
+ GetExtensionNameEmptyWhenNotInstalled);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest,
+ GetExtensionNameEmptyWhenNotExtensionURL);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest,
+ RouteCreationTimeoutForPresentation);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, RouteRequestFromIncognito);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, OpenAndCloseUIDetailsView);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, SendMediaCommands);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, SendMediaStatusUpdate);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterUITest, SendInitialMediaStatusUpdate);
+
+ class UIIssuesObserver;
+
+ class UIMediaRoutesObserver : public MediaRoutesObserver {
+ public:
+ using RoutesUpdatedCallback =
+ base::Callback<void(const std::vector<MediaRoute>&,
+ const std::vector<MediaRoute::Id>&)>;
+ UIMediaRoutesObserver(MediaRouter* router,
+ const MediaSource::Id& source_id,
+ const RoutesUpdatedCallback& callback);
+ ~UIMediaRoutesObserver() override;
+
+ // MediaRoutesObserver
+ void OnRoutesUpdated(
+ const std::vector<MediaRoute>& routes,
+ const std::vector<MediaRoute::Id>& joinable_route_ids) override;
+
+ private:
+ // Callback to the owning MediaRouterUI instance.
+ RoutesUpdatedCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(UIMediaRoutesObserver);
+ };
+
+ class UIMediaRouteControllerObserver : public MediaRouteController::Observer {
+ public:
+ explicit UIMediaRouteControllerObserver(
+ MediaRouterUI* ui,
+ scoped_refptr<MediaRouteController> controller);
+ ~UIMediaRouteControllerObserver() override;
+
+ // MediaRouteController::Observer
+ void OnMediaStatusUpdated(const MediaStatus& status) override;
+ void OnControllerInvalidated() override;
+
+ private:
+ MediaRouterUI* ui_;
+
+ DISALLOW_COPY_AND_ASSIGN(UIMediaRouteControllerObserver);
+ };
+
+ static std::string GetExtensionName(const GURL& url,
+ extensions::ExtensionRegistry* registry);
+
+ // QueryResultManager::Observer
+ void OnResultsUpdated(
+ const std::vector<MediaSinkWithCastModes>& sinks) override;
+
+ // Called by |issues_observer_| when the top issue has changed.
+ // If the UI is already initialized, notifies |handler_| to update the UI.
+ // Ignored if the UI is not yet initialized.
+ void SetIssue(const Issue& issue);
+ void ClearIssue();
+
+ // Called by |routes_observer_| when the set of active routes has changed.
+ void OnRoutesUpdated(const std::vector<MediaRoute>& routes,
+ const std::vector<MediaRoute::Id>& joinable_route_ids);
+
+ // Callback passed to MediaRouter to receive response to route creation
+ // requests.
+ void OnRouteResponseReceived(
+ int route_request_id,
+ const MediaSink::Id& sink_id,
+ MediaCastMode cast_mode,
+ const base::string16& presentation_request_source_name,
+ const RouteRequestResult& result);
+
+ // Closes the dialog after receiving a route response when using
+ // |create_session_request_|. This prevents the dialog from trying to use the
+ // same presentation request again.
+ void HandleCreateSessionRequestRouteResponse(const RouteRequestResult&);
+
+ // Callback passed to MediaRouter to receive the sink ID of the sink found by
+ // SearchSinksAndCreateRoute().
+ void OnSearchSinkResponseReceived(MediaCastMode cast_mode,
+ const MediaSink::Id& found_sink_id);
+
+ // Creates and sends an issue if route creation timed out.
+ void SendIssueForRouteTimeout(
+ MediaCastMode cast_mode,
+ const base::string16& presentation_request_source_name);
+
+ // Creates and sends an issue if casting fails for any other reason.
+ void SendIssueForUnableToCast(MediaCastMode cast_mode);
+
+ // Initializes the dialog with mirroring sources derived from |initiator|.
+ void InitCommon(content::WebContents* initiator);
+
+ // PresentationServiceDelegateImpl::DefaultPresentationObserver
+ void OnDefaultPresentationChanged(
+ const PresentationRequest& presentation_request) override;
+ void OnDefaultPresentationRemoved() override;
+
+ // Populates common route-related parameters for CreateRoute(),
+ // ConnectRoute(), and SearchSinksAndCreateRoute().
+ bool SetRouteParameters(
+ const MediaSink::Id& sink_id,
+ MediaCastMode cast_mode,
+ MediaSource::Id* source_id,
+ url::Origin* origin,
+ std::vector<MediaRouteResponseCallback>* route_response_callbacks,
+ base::TimeDelta* timeout,
+ bool* incognito);
+
+ // Updates the set of supported cast modes and sends the updated set to
+ // |handler_|.
+ void UpdateCastModes();
+
+ // Updates the routes-to-cast-modes mapping in |routes_and_cast_modes_| to
+ // match the value of |routes_|.
+ void UpdateRoutesToCastModesMapping();
+
+ // Returns the default presentation request's frame URL if there is one.
+ // Otherwise returns an empty GURL.
+ GURL GetFrameURL() const;
+
+ // Returns the serialized origin for |initiator_|, or the serialization of an
+ // opaque origin ("null") if |initiator_| is not set.
+ std::string GetSerializedInitiatorOrigin() const;
+
+ // Destroys the route controller observer. Called when the route controller is
+ // invalidated.
+ void OnRouteControllerInvalidated();
+
+ // Called by the internal route controller observer. Notifies the message
+ // handler of a media status update for the route currently shown in the UI.
+ void UpdateMediaRouteStatus(const MediaStatus& status);
+
+ // Owned by the |web_ui| passed in the ctor, and guaranteed to be deleted
+ // only after it has deleted |this|.
+ MediaRouterWebUIMessageHandler* handler_ = nullptr;
+
+ // These are non-null while this instance is registered to receive
+ // updates from them.
+ std::unique_ptr<IssuesObserver> issues_observer_;
+ std::unique_ptr<MediaRoutesObserver> routes_observer_;
+
+ // Set to true by |handler_| when the UI has been initialized.
+ bool ui_initialized_;
+
+ // Set to -1 if not tracking a pending route request.
+ int current_route_request_id_;
+
+ // Sequential counter for route requests. Used to update
+ // |current_route_request_id_| when there is a new route request.
+ int route_request_counter_;
+
+ // Used for locale-aware sorting of sinks by name. Set during |InitCommon()|
+ // using the current locale. Set to null
+ std::unique_ptr<icu::Collator> collator_;
+
+ std::vector<MediaSinkWithCastModes> sinks_;
+ std::vector<MediaRoute> routes_;
+ std::vector<MediaRoute::Id> joinable_route_ids_;
+ CastModeSet cast_modes_;
+ std::unordered_map<MediaRoute::Id, MediaCastMode> routes_and_cast_modes_;
+
+ std::unique_ptr<QueryResultManager> query_result_manager_;
+
+ // If set, then the result of the next presentation route request will
+ // be handled by this object.
+ std::unique_ptr<CreatePresentationConnectionRequest> create_session_request_;
+
+ // Set to the presentation request corresponding to the presentation cast
+ // mode, if supported. Otherwise set to nullptr.
+ std::unique_ptr<PresentationRequest> presentation_request_;
+
+ // It's possible for PresentationServiceDelegateImpl to be destroyed before
+ // this class.
+ // (e.g. if a tab with the UI open is closed, then the tab WebContents will
+ // be destroyed first momentarily before the UI WebContents).
+ // Holding a WeakPtr to PresentationServiceDelegateImpl is the cleanest way to
+ // handle this.
+ // TODO(imcheng): hold a weak ptr to an abstract type instead.
+ base::WeakPtr<PresentationServiceDelegateImpl> presentation_service_delegate_;
+
+ content::WebContents* initiator_;
+
+ // Pointer to the MediaRouter for this instance's BrowserContext.
+ MediaRouter* router_;
+
+ // The start time for UI initialization metrics timer. When a dialog has been
+ // been painted and initialized with initial data, this should be cleared.
+ base::Time start_time_;
+
+ // The observer for the route controller. Notifies |handler_| of media status
+ // updates.
+ std::unique_ptr<UIMediaRouteControllerObserver> route_controller_observer_;
+
+ // If set, a cast mode that is required to be shown first.
+ base::Optional<MediaCastMode> forced_cast_mode_;
+
+ // 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_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaRouterUI);
+};
+
+} // namespace media_router
+
+#endif // CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_UI_H_
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
new file mode 100644
index 00000000000..28861553fa8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
@@ -0,0 +1,745 @@
+// 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.
+
+#include "chrome/browser/ui/webui/media_router/media_router_ui.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/media/router/create_presentation_connection_request.h"
+#include "chrome/browser/media/router/mock_media_router.h"
+#include "chrome/browser/media/router/mojo/media_router_mojo_test.h"
+#include "chrome/browser/media/router/test_helper.h"
+#include "chrome/browser/sessions/session_tab_helper.h"
+#include "chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h"
+#include "chrome/common/media_router/media_route.h"
+#include "chrome/common/media_router/media_source_helper.h"
+#include "chrome/common/media_router/route_request_result.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_web_ui.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions/common/test_util.h"
+#include "extensions/common/value_builder.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using content::WebContents;
+using testing::_;
+using testing::AnyNumber;
+using testing::Invoke;
+using testing::Mock;
+using testing::Return;
+using testing::SaveArg;
+
+namespace media_router {
+
+class MockMediaRouterWebUIMessageHandler
+ : public MediaRouterWebUIMessageHandler {
+ public:
+ explicit MockMediaRouterWebUIMessageHandler(MediaRouterUI* media_router_ui)
+ : MediaRouterWebUIMessageHandler(media_router_ui) {}
+ ~MockMediaRouterWebUIMessageHandler() override {}
+
+ MOCK_METHOD1(UpdateMediaRouteStatus, void(const MediaStatus& status));
+ MOCK_METHOD3(UpdateCastModes,
+ void(const CastModeSet& cast_modes,
+ const std::string& source_host,
+ base::Optional<MediaCastMode> forced_cast_mode));
+};
+
+class PresentationRequestCallbacks {
+ public:
+ PresentationRequestCallbacks() {}
+
+ explicit PresentationRequestCallbacks(
+ const content::PresentationError& expected_error)
+ : expected_error_(expected_error) {}
+
+ void Success(const content::PresentationInfo&, const MediaRoute&) {}
+
+ void Error(const content::PresentationError& error) {
+ EXPECT_EQ(expected_error_.error_type, error.error_type);
+ EXPECT_EQ(expected_error_.message, error.message);
+ }
+
+ private:
+ content::PresentationError expected_error_;
+};
+
+class MediaRouterUITest : public ChromeRenderViewHostTestHarness {
+ public:
+ MediaRouterUITest() {
+ ON_CALL(mock_router_, GetCurrentRoutes())
+ .WillByDefault(Return(std::vector<MediaRoute>()));
+ }
+
+ void TearDown() override {
+ EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_))
+ .Times(AnyNumber());
+ EXPECT_CALL(mock_router_, UnregisterMediaRoutesObserver(_))
+ .Times(AnyNumber());
+ web_ui_contents_.reset();
+ create_session_request_.reset();
+ media_router_ui_.reset();
+ message_handler_.reset();
+ ChromeRenderViewHostTestHarness::TearDown();
+ }
+
+ void CreateMediaRouterUIForURL(Profile* profile, const GURL& url) {
+ web_contents()->GetController().LoadURL(url, content::Referrer(),
+ ui::PAGE_TRANSITION_LINK, "");
+ content::RenderFrameHostTester::CommitPendingLoad(
+ &web_contents()->GetController());
+ CreateMediaRouterUI(profile);
+ }
+
+ void CreateMediaRouterUI(Profile* profile) {
+ SessionTabHelper::CreateForWebContents(web_contents());
+ web_ui_contents_.reset(
+ WebContents::Create(WebContents::CreateParams(profile)));
+ web_ui_.set_web_contents(web_ui_contents_.get());
+ media_router_ui_ = base::MakeUnique<MediaRouterUI>(&web_ui_);
+ message_handler_ = base::MakeUnique<MockMediaRouterWebUIMessageHandler>(
+ media_router_ui_.get());
+ EXPECT_CALL(mock_router_, RegisterMediaSinksObserver(_))
+ .WillRepeatedly(Invoke([this](MediaSinksObserver* observer) {
+ this->media_sinks_observers_.push_back(observer);
+ return true;
+ }));
+ EXPECT_CALL(mock_router_, RegisterMediaRoutesObserver(_))
+ .Times(AnyNumber());
+ media_router_ui_->InitForTest(&mock_router_, web_contents(),
+ message_handler_.get(),
+ std::move(create_session_request_));
+ message_handler_->SetWebUIForTest(&web_ui_);
+ }
+
+ MediaSink CreateSinkCompatibleWithAllSources() {
+ MediaSink sink("sinkId", "sinkName", MediaSink::GENERIC);
+ for (auto* observer : media_sinks_observers_)
+ observer->OnSinksUpdated({sink}, std::vector<url::Origin>());
+ return sink;
+ }
+
+ scoped_refptr<MockMediaRouteController> CreateMediaRouteController(
+ const MediaRoute::Id& route_id) {
+ mojom::MediaControllerPtr mojo_media_controller;
+ mojo::MakeRequest(&mojo_media_controller);
+ return scoped_refptr<MockMediaRouteController>(new MockMediaRouteController(
+ route_id, std::move(mojo_media_controller), &mock_router_));
+ }
+
+ // Notifies MediaRouterUI that a route details view has been opened. Expects
+ // MediaRouterUI to request a MediaRouteController, and gives it a mock
+ // controller. Returns a reference to the mock controller.
+ scoped_refptr<MockMediaRouteController> OpenUIDetailsView(
+ const MediaRoute::Id& route_id) {
+ auto controller = CreateMediaRouteController(route_id);
+ MediaSource media_source("mediaSource");
+ MediaRoute route(route_id, media_source, "sinkId", "", true, "", true);
+
+ media_router_ui_->OnRoutesUpdated({route}, std::vector<MediaRoute::Id>());
+ EXPECT_CALL(mock_router_, GetRouteController(route_id))
+ .WillOnce(Return(controller));
+ media_router_ui_->OnMediaControllerUIAvailable(route_id);
+
+ return controller;
+ }
+
+ protected:
+ MockMediaRouter mock_router_;
+ content::TestWebUI web_ui_;
+ std::unique_ptr<WebContents> web_ui_contents_;
+ std::unique_ptr<CreatePresentationConnectionRequest> create_session_request_;
+ std::unique_ptr<MediaRouterUI> media_router_ui_;
+ std::unique_ptr<MockMediaRouterWebUIMessageHandler> message_handler_;
+ std::vector<MediaSinksObserver*> media_sinks_observers_;
+};
+
+TEST_F(MediaRouterUITest, RouteCreationTimeoutForTab) {
+ CreateMediaRouterUI(profile());
+ std::vector<MediaRouteResponseCallback> callbacks;
+ EXPECT_CALL(
+ mock_router_,
+ CreateRoute(_, _, _, _, _, base::TimeDelta::FromSeconds(60), false))
+ .WillOnce(SaveArg<4>(&callbacks));
+ media_router_ui_->CreateRoute(CreateSinkCompatibleWithAllSources().id(),
+ MediaCastMode::TAB_MIRROR);
+
+ std::string expected_title = l10n_util::GetStringUTF8(
+ IDS_MEDIA_ROUTER_ISSUE_CREATE_ROUTE_TIMEOUT_FOR_TAB);
+ EXPECT_CALL(mock_router_, AddIssue(IssueTitleEquals(expected_title)));
+ std::unique_ptr<RouteRequestResult> result =
+ RouteRequestResult::FromError("Timed out", RouteRequestResult::TIMED_OUT);
+ for (const auto& callback : callbacks)
+ callback.Run(*result);
+}
+
+TEST_F(MediaRouterUITest, RouteCreationTimeoutForDesktop) {
+ CreateMediaRouterUI(profile());
+ std::vector<MediaRouteResponseCallback> callbacks;
+ EXPECT_CALL(
+ mock_router_,
+ CreateRoute(_, _, _, _, _, base::TimeDelta::FromSeconds(120), false))
+ .WillOnce(SaveArg<4>(&callbacks));
+ media_router_ui_->CreateRoute(CreateSinkCompatibleWithAllSources().id(),
+ MediaCastMode::DESKTOP_MIRROR);
+
+ std::string expected_title = l10n_util::GetStringUTF8(
+ IDS_MEDIA_ROUTER_ISSUE_CREATE_ROUTE_TIMEOUT_FOR_DESKTOP);
+ EXPECT_CALL(mock_router_, AddIssue(IssueTitleEquals(expected_title)));
+ std::unique_ptr<RouteRequestResult> result =
+ RouteRequestResult::FromError("Timed out", RouteRequestResult::TIMED_OUT);
+ for (const auto& callback : callbacks)
+ callback.Run(*result);
+}
+
+TEST_F(MediaRouterUITest, RouteCreationTimeoutForPresentation) {
+ CreateMediaRouterUI(profile());
+ PresentationRequest presentation_request(
+ RenderFrameHostId(0, 0), {GURL("https://presentationurl.com")},
+ url::Origin(GURL("https://frameurl.fakeurl")));
+ media_router_ui_->OnDefaultPresentationChanged(presentation_request);
+ std::vector<MediaRouteResponseCallback> callbacks;
+ EXPECT_CALL(
+ mock_router_,
+ CreateRoute(_, _, _, _, _, base::TimeDelta::FromSeconds(20), false))
+ .WillOnce(SaveArg<4>(&callbacks));
+ media_router_ui_->CreateRoute(CreateSinkCompatibleWithAllSources().id(),
+ MediaCastMode::DEFAULT);
+
+ std::string expected_title =
+ l10n_util::GetStringFUTF8(IDS_MEDIA_ROUTER_ISSUE_CREATE_ROUTE_TIMEOUT,
+ base::UTF8ToUTF16("frameurl.fakeurl"));
+ EXPECT_CALL(mock_router_, AddIssue(IssueTitleEquals(expected_title)));
+ std::unique_ptr<RouteRequestResult> result =
+ RouteRequestResult::FromError("Timed out", RouteRequestResult::TIMED_OUT);
+ for (const auto& callback : callbacks)
+ callback.Run(*result);
+}
+
+TEST_F(MediaRouterUITest, RouteCreationParametersCantBeCreated) {
+ CreateMediaRouterUI(profile());
+ MediaSinkSearchResponseCallback sink_callback;
+ EXPECT_CALL(mock_router_, SearchSinks(_, _, _, _, _))
+ .WillOnce(SaveArg<4>(&sink_callback));
+
+ // Use DEFAULT mode without setting a PresentationRequest.
+ media_router_ui_->SearchSinksAndCreateRoute("sinkId", "search input",
+ "domain", MediaCastMode::DEFAULT);
+ std::string expected_title = l10n_util::GetStringUTF8(
+ IDS_MEDIA_ROUTER_ISSUE_CREATE_ROUTE_TIMEOUT_FOR_TAB);
+ EXPECT_CALL(mock_router_, AddIssue(IssueTitleEquals(expected_title)));
+ sink_callback.Run("foundSinkId");
+}
+
+TEST_F(MediaRouterUITest, RouteRequestFromIncognito) {
+ CreateMediaRouterUI(profile()->GetOffTheRecordProfile());
+
+ PresentationRequest presentation_request(
+ RenderFrameHostId(0, 0), {GURL("https://foo.url.com/")},
+ url::Origin(GURL("https://frameUrl")));
+ media_router_ui_->OnDefaultPresentationChanged(presentation_request);
+
+ EXPECT_CALL(
+ mock_router_,
+ CreateRoute(_, _, _, _, _, base::TimeDelta::FromSeconds(20), true));
+ media_router_ui_->CreateRoute(CreateSinkCompatibleWithAllSources().id(),
+ MediaCastMode::DEFAULT);
+}
+
+TEST_F(MediaRouterUITest, SortedSinks) {
+ CreateMediaRouterUI(profile());
+ std::vector<MediaSinkWithCastModes> unsorted_sinks;
+ std::string sink_id1("sink3");
+ std::string sink_name1("B sink");
+ MediaSinkWithCastModes sink1(
+ MediaSink(sink_id1, sink_name1, MediaSink::IconType::CAST));
+ unsorted_sinks.push_back(sink1);
+
+ std::string sink_id2("sink1");
+ std::string sink_name2("A sink");
+ MediaSinkWithCastModes sink2(
+ MediaSink(sink_id2, sink_name2, MediaSink::IconType::CAST));
+ unsorted_sinks.push_back(sink2);
+
+ std::string sink_id3("sink2");
+ std::string sink_name3("B sink");
+ MediaSinkWithCastModes sink3(
+ MediaSink(sink_id3, sink_name3, MediaSink::IconType::CAST));
+ unsorted_sinks.push_back(sink3);
+
+ // Sorted order is 2, 3, 1.
+ media_router_ui_->OnResultsUpdated(unsorted_sinks);
+ const auto& sorted_sinks = media_router_ui_->sinks_;
+ EXPECT_EQ(sink_name2, sorted_sinks[0].sink.name());
+ EXPECT_EQ(sink_id3, sorted_sinks[1].sink.id());
+ EXPECT_EQ(sink_id1, sorted_sinks[2].sink.id());
+}
+
+TEST_F(MediaRouterUITest, SortSinksByIconType) {
+ CreateMediaRouterUI(profile());
+ std::vector<MediaSinkWithCastModes> unsorted_sinks;
+
+ MediaSinkWithCastModes sink1(
+ MediaSink("id1", "sink", MediaSink::IconType::HANGOUT));
+ unsorted_sinks.push_back(sink1);
+ MediaSinkWithCastModes sink2(
+ MediaSink("id2", "B sink", MediaSink::IconType::CAST_AUDIO_GROUP));
+ unsorted_sinks.push_back(sink2);
+ MediaSinkWithCastModes sink3(
+ MediaSink("id3", "sink", MediaSink::IconType::GENERIC));
+ unsorted_sinks.push_back(sink3);
+ MediaSinkWithCastModes sink4(
+ MediaSink("id4", "A sink", MediaSink::IconType::CAST_AUDIO_GROUP));
+ unsorted_sinks.push_back(sink4);
+ MediaSinkWithCastModes sink5(
+ MediaSink("id5", "sink", MediaSink::IconType::CAST_AUDIO));
+ unsorted_sinks.push_back(sink5);
+ MediaSinkWithCastModes sink6(
+ MediaSink("id6", "sink", MediaSink::IconType::CAST));
+ unsorted_sinks.push_back(sink6);
+
+ // Sorted order is CAST, CAST_AUDIO_GROUP "A", CAST_AUDIO_GROUP "B",
+ // CAST_AUDIO, HANGOUT, GENERIC.
+ media_router_ui_->OnResultsUpdated(unsorted_sinks);
+ const auto& sorted_sinks = media_router_ui_->sinks_;
+ EXPECT_EQ(sink6.sink.id(), sorted_sinks[0].sink.id());
+ EXPECT_EQ(sink4.sink.id(), sorted_sinks[1].sink.id());
+ EXPECT_EQ(sink2.sink.id(), sorted_sinks[2].sink.id());
+ EXPECT_EQ(sink5.sink.id(), sorted_sinks[3].sink.id());
+ EXPECT_EQ(sink1.sink.id(), sorted_sinks[4].sink.id());
+ EXPECT_EQ(sink3.sink.id(), sorted_sinks[5].sink.id());
+}
+
+TEST_F(MediaRouterUITest, FilterNonDisplayRoutes) {
+ CreateMediaRouterUI(profile());
+
+ MediaSource media_source("mediaSource");
+ MediaRoute display_route_1("routeId1", media_source, "sinkId1", "desc 1",
+ true, "", true);
+ MediaRoute non_display_route_1("routeId2", media_source, "sinkId2", "desc 2",
+ true, "", false);
+ MediaRoute display_route_2("routeId3", media_source, "sinkId2", "desc 2",
+ true, "", true);
+ std::vector<MediaRoute> routes;
+ routes.push_back(display_route_1);
+ routes.push_back(non_display_route_1);
+ routes.push_back(display_route_2);
+
+ media_router_ui_->OnRoutesUpdated(routes, std::vector<MediaRoute::Id>());
+ ASSERT_EQ(2u, media_router_ui_->routes_.size());
+ EXPECT_TRUE(display_route_1.Equals(media_router_ui_->routes_[0]));
+ EXPECT_TRUE(media_router_ui_->routes_[0].for_display());
+ EXPECT_TRUE(display_route_2.Equals(media_router_ui_->routes_[1]));
+ EXPECT_TRUE(media_router_ui_->routes_[1].for_display());
+}
+
+TEST_F(MediaRouterUITest, FilterNonDisplayJoinableRoutes) {
+ CreateMediaRouterUI(profile());
+
+ MediaSource media_source("mediaSource");
+ MediaRoute display_route_1("routeId1", media_source, "sinkId1", "desc 1",
+ true, "", true);
+ MediaRoute non_display_route_1("routeId2", media_source, "sinkId2", "desc 2",
+ true, "", false);
+ MediaRoute display_route_2("routeId3", media_source, "sinkId2", "desc 2",
+ true, "", true);
+ std::vector<MediaRoute> routes;
+ routes.push_back(display_route_1);
+ routes.push_back(non_display_route_1);
+ routes.push_back(display_route_2);
+
+ std::vector<MediaRoute::Id> joinable_route_ids;
+ joinable_route_ids.push_back("routeId1");
+ joinable_route_ids.push_back("routeId2");
+ joinable_route_ids.push_back("routeId3");
+
+ media_router_ui_->OnRoutesUpdated(routes, joinable_route_ids);
+ ASSERT_EQ(2u, media_router_ui_->joinable_route_ids_.size());
+ EXPECT_EQ(display_route_1.media_route_id(),
+ media_router_ui_->joinable_route_ids_[0]);
+ EXPECT_EQ(display_route_2.media_route_id(),
+ media_router_ui_->joinable_route_ids_[1]);
+}
+
+TEST_F(MediaRouterUITest, UIMediaRoutesObserverAssignsCurrentCastModes) {
+ CreateMediaRouterUI(profile());
+ SessionID::id_type tab_id = SessionTabHelper::IdForTab(web_contents());
+ MediaSource media_source_1(MediaSourceForTab(tab_id));
+ MediaSource media_source_2("mediaSource");
+ MediaSource media_source_3(MediaSourceForDesktop());
+ std::unique_ptr<MediaRouterUI::UIMediaRoutesObserver> observer(
+ new MediaRouterUI::UIMediaRoutesObserver(
+ &mock_router_, MediaSource::Id(),
+ base::Bind(&MediaRouterUI::OnRoutesUpdated,
+ base::Unretained(media_router_ui_.get()))));
+
+ MediaRoute display_route_1("routeId1", media_source_1, "sinkId1", "desc 1",
+ true, "", true);
+ MediaRoute non_display_route_1("routeId2", media_source_2, "sinkId2",
+ "desc 2", true, "", false);
+ MediaRoute display_route_2("routeId3", media_source_3, "sinkId2", "desc 2",
+ true, "", true);
+ std::vector<MediaRoute> routes;
+ routes.push_back(display_route_1);
+ routes.push_back(non_display_route_1);
+ routes.push_back(display_route_2);
+
+ observer->OnRoutesUpdated(routes, std::vector<MediaRoute::Id>());
+
+ const auto& filtered_routes = media_router_ui_->routes();
+ ASSERT_EQ(2u, filtered_routes.size());
+ EXPECT_TRUE(display_route_1.Equals(filtered_routes[0]));
+ EXPECT_TRUE(filtered_routes[0].for_display());
+ EXPECT_TRUE(display_route_2.Equals(filtered_routes[1]));
+ EXPECT_TRUE(filtered_routes[1].for_display());
+
+ const auto& current_cast_modes = media_router_ui_->routes_and_cast_modes();
+ ASSERT_EQ(2u, current_cast_modes.size());
+ auto cast_mode_entry =
+ current_cast_modes.find(display_route_1.media_route_id());
+ EXPECT_NE(end(current_cast_modes), cast_mode_entry);
+ EXPECT_EQ(MediaCastMode::TAB_MIRROR, cast_mode_entry->second);
+ cast_mode_entry =
+ current_cast_modes.find(non_display_route_1.media_route_id());
+ EXPECT_EQ(end(current_cast_modes), cast_mode_entry);
+ cast_mode_entry = current_cast_modes.find(display_route_2.media_route_id());
+ EXPECT_NE(end(current_cast_modes), cast_mode_entry);
+ EXPECT_EQ(MediaCastMode::DESKTOP_MIRROR, cast_mode_entry->second);
+
+ EXPECT_CALL(mock_router_, UnregisterMediaRoutesObserver(_)).Times(1);
+ observer.reset();
+}
+
+TEST_F(MediaRouterUITest, UIMediaRoutesObserverSkipsUnavailableCastModes) {
+ CreateMediaRouterUI(profile());
+ MediaSource media_source_1("mediaSource1");
+ MediaSource media_source_2("mediaSource2");
+ MediaSource media_source_3(MediaSourceForDesktop());
+ std::unique_ptr<MediaRouterUI::UIMediaRoutesObserver> observer(
+ new MediaRouterUI::UIMediaRoutesObserver(
+ &mock_router_, MediaSource::Id(),
+ base::Bind(&MediaRouterUI::OnRoutesUpdated,
+ base::Unretained(media_router_ui_.get()))));
+
+ MediaRoute display_route_1("routeId1", media_source_1, "sinkId1", "desc 1",
+ true, "", true);
+ MediaRoute non_display_route_1("routeId2", media_source_2, "sinkId2",
+ "desc 2", true, "", false);
+ MediaRoute display_route_2("routeId3", media_source_3, "sinkId2", "desc 2",
+ true, "", true);
+ std::vector<MediaRoute> routes;
+ routes.push_back(display_route_1);
+ routes.push_back(non_display_route_1);
+ routes.push_back(display_route_2);
+
+ observer->OnRoutesUpdated(routes, std::vector<MediaRoute::Id>());
+
+ const auto& filtered_routes = media_router_ui_->routes();
+ ASSERT_EQ(2u, filtered_routes.size());
+ EXPECT_TRUE(display_route_1.Equals(filtered_routes[0]));
+ EXPECT_TRUE(filtered_routes[0].for_display());
+ EXPECT_TRUE(display_route_2.Equals(filtered_routes[1]));
+ EXPECT_TRUE(filtered_routes[1].for_display());
+
+ const auto& current_cast_modes = media_router_ui_->routes_and_cast_modes();
+ ASSERT_EQ(1u, current_cast_modes.size());
+ auto cast_mode_entry =
+ current_cast_modes.find(display_route_1.media_route_id());
+ // No observer for source "mediaSource1" means no cast mode for this route.
+ EXPECT_EQ(end(current_cast_modes), cast_mode_entry);
+ cast_mode_entry =
+ current_cast_modes.find(non_display_route_1.media_route_id());
+ EXPECT_EQ(end(current_cast_modes), cast_mode_entry);
+ cast_mode_entry = current_cast_modes.find(display_route_2.media_route_id());
+ EXPECT_NE(end(current_cast_modes), cast_mode_entry);
+ EXPECT_EQ(MediaCastMode::DESKTOP_MIRROR, cast_mode_entry->second);
+
+ EXPECT_CALL(mock_router_, UnregisterMediaRoutesObserver(_)).Times(1);
+ observer.reset();
+}
+
+TEST_F(MediaRouterUITest, GetExtensionNameExtensionPresent) {
+ std::string id = "extensionid";
+ GURL url = GURL("chrome-extension://" + id);
+ std::unique_ptr<extensions::ExtensionRegistry> registry =
+ base::MakeUnique<extensions::ExtensionRegistry>(nullptr);
+ scoped_refptr<extensions::Extension> app =
+ extensions::test_util::BuildApp(extensions::ExtensionBuilder())
+ .MergeManifest(extensions::DictionaryBuilder()
+ .Set("name", "test app name")
+ .Build())
+ .SetID(id)
+ .Build();
+
+ ASSERT_TRUE(registry->AddEnabled(app));
+ EXPECT_EQ("test app name",
+ MediaRouterUI::GetExtensionName(url, registry.get()));
+}
+
+TEST_F(MediaRouterUITest, GetExtensionNameEmptyWhenNotInstalled) {
+ std::string id = "extensionid";
+ GURL url = GURL("chrome-extension://" + id);
+ std::unique_ptr<extensions::ExtensionRegistry> registry =
+ base::MakeUnique<extensions::ExtensionRegistry>(nullptr);
+
+ EXPECT_EQ("", MediaRouterUI::GetExtensionName(url, registry.get()));
+}
+
+TEST_F(MediaRouterUITest, GetExtensionNameEmptyWhenNotExtensionURL) {
+ GURL url = GURL("https://www.google.com");
+ std::unique_ptr<extensions::ExtensionRegistry> registry =
+ base::MakeUnique<extensions::ExtensionRegistry>(nullptr);
+
+ EXPECT_EQ("", MediaRouterUI::GetExtensionName(url, registry.get()));
+}
+
+TEST_F(MediaRouterUITest, NotFoundErrorOnCloseWithNoSinks) {
+ content::PresentationError expected_error(
+ content::PresentationErrorType::PRESENTATION_ERROR_NO_AVAILABLE_SCREENS,
+ "No screens found.");
+ PresentationRequestCallbacks request_callbacks(expected_error);
+ create_session_request_.reset(new CreatePresentationConnectionRequest(
+ RenderFrameHostId(0, 0),
+ {GURL("http://google.com/presentation"),
+ GURL("http://google.com/presentation2")},
+ url::Origin(GURL("http://google.com")),
+ base::Bind(&PresentationRequestCallbacks::Success,
+ base::Unretained(&request_callbacks)),
+ base::Bind(&PresentationRequestCallbacks::Error,
+ base::Unretained(&request_callbacks))));
+ CreateMediaRouterUI(profile());
+ // Destroying the UI should return the expected error from above to the error
+ // callback.
+ media_router_ui_.reset();
+}
+
+TEST_F(MediaRouterUITest, NotFoundErrorOnCloseWithNoCompatibleSinks) {
+ content::PresentationError expected_error(
+ content::PresentationErrorType::PRESENTATION_ERROR_NO_AVAILABLE_SCREENS,
+ "No screens found.");
+ PresentationRequestCallbacks request_callbacks(expected_error);
+ GURL presentation_url("http://google.com/presentation");
+ create_session_request_.reset(new CreatePresentationConnectionRequest(
+ RenderFrameHostId(0, 0), {presentation_url},
+ url::Origin(GURL("http://google.com")),
+ base::Bind(&PresentationRequestCallbacks::Success,
+ base::Unretained(&request_callbacks)),
+ base::Bind(&PresentationRequestCallbacks::Error,
+ base::Unretained(&request_callbacks))));
+ CreateMediaRouterUI(profile());
+
+ // Send a sink to the UI that is compatible with sources other than the
+ // presentation url to cause a NotFoundError.
+ std::vector<MediaSink> sinks;
+ sinks.emplace_back("sink id", "sink name", MediaSink::GENERIC);
+ std::vector<url::Origin> origins;
+ for (auto* observer : media_sinks_observers_) {
+ if (observer->source().id() != presentation_url.spec()) {
+ observer->OnSinksUpdated(sinks, origins);
+ }
+ }
+ // Destroying the UI should return the expected error from above to the error
+ // callback.
+ media_router_ui_.reset();
+}
+
+TEST_F(MediaRouterUITest, AbortErrorOnClose) {
+ content::PresentationError expected_error(
+ content::PresentationErrorType::
+ PRESENTATION_ERROR_PRESENTATION_REQUEST_CANCELLED,
+ "Dialog closed.");
+ PresentationRequestCallbacks request_callbacks(expected_error);
+ GURL presentation_url("http://google.com/presentation");
+ create_session_request_.reset(new CreatePresentationConnectionRequest(
+ RenderFrameHostId(0, 0), {presentation_url},
+ url::Origin(GURL("http://google.com")),
+ base::Bind(&PresentationRequestCallbacks::Success,
+ base::Unretained(&request_callbacks)),
+ base::Bind(&PresentationRequestCallbacks::Error,
+ base::Unretained(&request_callbacks))));
+ CreateMediaRouterUI(profile());
+
+ // Send a sink to the UI that is compatible with the presentation url to avoid
+ // a NotFoundError.
+ std::vector<MediaSink> sinks;
+ sinks.emplace_back("sink id", "sink name", MediaSink::GENERIC);
+ std::vector<url::Origin> origins;
+ MediaSource::Id presentation_source_id =
+ MediaSourceForPresentationUrl(presentation_url).id();
+ for (auto* observer : media_sinks_observers_) {
+ if (observer->source().id() == presentation_source_id) {
+ observer->OnSinksUpdated(sinks, origins);
+ }
+ }
+ // Destroying the UI should return the expected error from above to the error
+ // callback.
+ media_router_ui_.reset();
+}
+
+TEST_F(MediaRouterUITest, RecordCastModeSelections) {
+ const GURL url_1a = GURL("https://www.example.com/watch?v=AAAA");
+ const GURL url_1b = GURL("https://www.example.com/watch?v=BBBB");
+ const GURL url_2 = GURL("https://example2.com/0000");
+ const GURL url_3 = GURL("https://www3.example.com/index.html");
+
+ CreateMediaRouterUIForURL(profile(), url_1a);
+ EXPECT_FALSE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
+ media_router_ui_->RecordCastModeSelection(MediaCastMode::TAB_MIRROR);
+ EXPECT_TRUE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
+
+ CreateMediaRouterUIForURL(profile(), url_2);
+ EXPECT_FALSE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
+
+ CreateMediaRouterUIForURL(profile(), url_1b);
+ // |url_1a| and |url_1b| have the same origin, so the selection made for
+ // |url_1a| should be retrieved.
+ EXPECT_TRUE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
+ media_router_ui_->RecordCastModeSelection(MediaCastMode::DEFAULT);
+ EXPECT_FALSE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
+
+ media_router_ui_->RecordCastModeSelection(MediaCastMode::TAB_MIRROR);
+ CreateMediaRouterUIForURL(profile(), url_3);
+ // |url_1a| and |url_3| have the same domain "example.com" but different
+ // origins, so their preferences should be separate.
+ EXPECT_FALSE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
+}
+
+TEST_F(MediaRouterUITest, RecordCastModeSelectionsInIncognito) {
+ const GURL url = GURL("https://www.example.com/watch?v=AAAA");
+
+ CreateMediaRouterUIForURL(profile()->GetOffTheRecordProfile(), url);
+ EXPECT_FALSE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
+ media_router_ui_->RecordCastModeSelection(MediaCastMode::TAB_MIRROR);
+ EXPECT_TRUE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
+
+ // Selections recorded in incognito shouldn't be retrieved in the regular
+ // profile.
+ CreateMediaRouterUIForURL(profile(), url);
+ EXPECT_FALSE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
+}
+
+TEST_F(MediaRouterUITest, RecordDesktopMirroringCastModeSelection) {
+ const GURL url = GURL("https://www.example.com/watch?v=AAAA");
+ CreateMediaRouterUIForURL(profile(), url);
+
+ EXPECT_FALSE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
+ media_router_ui_->RecordCastModeSelection(MediaCastMode::DESKTOP_MIRROR);
+ // Selecting desktop mirroring should not change the recorded preferences.
+ EXPECT_FALSE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
+
+ media_router_ui_->RecordCastModeSelection(MediaCastMode::TAB_MIRROR);
+ EXPECT_TRUE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
+ media_router_ui_->RecordCastModeSelection(MediaCastMode::DESKTOP_MIRROR);
+ // Selecting desktop mirroring should not change the recorded preferences.
+ EXPECT_TRUE(media_router_ui_->UserSelectedTabMirroringForCurrentOrigin());
+}
+
+TEST_F(MediaRouterUITest, OpenAndCloseUIDetailsView) {
+ const std::string route_id = "routeId";
+ CreateMediaRouterUI(profile());
+ OpenUIDetailsView(route_id);
+
+ // When the route details view is closed, the route controller observer should
+ // be destroyed, also triggering the destruction of the controller.
+ EXPECT_CALL(mock_router_, DetachRouteController(route_id, _));
+ media_router_ui_->OnMediaControllerUIClosed();
+
+ EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_router_));
+}
+
+TEST_F(MediaRouterUITest, SendMediaStatusUpdate) {
+ MediaStatus status;
+ status.title = "test title";
+ CreateMediaRouterUI(profile());
+ scoped_refptr<MockMediaRouteController> controller =
+ OpenUIDetailsView("routeId");
+
+ // The route controller observer held by MediaRouterUI should send the status
+ // update to the message handler.
+ EXPECT_CALL(*message_handler_, UpdateMediaRouteStatus(status));
+ controller->OnMediaStatusUpdated(status);
+}
+
+TEST_F(MediaRouterUITest, SendInitialMediaStatusUpdate) {
+ MediaStatus status;
+ status.title = "test title";
+ std::string route_id = "routeId";
+ auto controller = CreateMediaRouteController(route_id);
+ controller->OnMediaStatusUpdated(status);
+
+ CreateMediaRouterUI(profile());
+ MediaSource media_source("mediaSource");
+ MediaRoute route(route_id, media_source, "sinkId", "", true, "", true);
+ media_router_ui_->OnRoutesUpdated({route}, std::vector<MediaRoute::Id>());
+
+ // If the controller has already received a media status update, MediaRouterUI
+ // should be notified with it when it starts observing the controller.
+ EXPECT_CALL(mock_router_, GetRouteController(route_id))
+ .WillOnce(Return(controller));
+ EXPECT_CALL(*message_handler_, UpdateMediaRouteStatus(status));
+ media_router_ui_->OnMediaControllerUIAvailable(route_id);
+}
+
+TEST_F(MediaRouterUITest, SetsForcedCastModeWithPresentationURLs) {
+ content::PresentationError expected_error(
+ content::PresentationErrorType::PRESENTATION_ERROR_NO_AVAILABLE_SCREENS,
+ "No screens found.");
+ PresentationRequestCallbacks request_callbacks(expected_error);
+ create_session_request_.reset(new CreatePresentationConnectionRequest(
+ RenderFrameHostId(0, 0),
+ {GURL("http://google.com/presentation"),
+ GURL("http://google.com/presentation2")},
+ url::Origin(GURL("http://google.com")),
+ base::Bind(&PresentationRequestCallbacks::Success,
+ base::Unretained(&request_callbacks)),
+ base::Bind(&PresentationRequestCallbacks::Error,
+ base::Unretained(&request_callbacks))));
+
+ SessionTabHelper::CreateForWebContents(web_contents());
+ web_ui_contents_.reset(
+ WebContents::Create(WebContents::CreateParams(profile())));
+ web_ui_.set_web_contents(web_ui_contents_.get());
+ media_router_ui_ = base::MakeUnique<MediaRouterUI>(&web_ui_);
+ message_handler_ = base::MakeUnique<MockMediaRouterWebUIMessageHandler>(
+ media_router_ui_.get());
+ message_handler_->SetWebUIForTest(&web_ui_);
+ EXPECT_CALL(mock_router_, RegisterMediaSinksObserver(_))
+ .WillRepeatedly(Invoke([this](MediaSinksObserver* observer) {
+ this->media_sinks_observers_.push_back(observer);
+ 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});
+ EXPECT_CALL(
+ *message_handler_,
+ UpdateCastModes(expected_modes, "",
+ base::Optional<MediaCastMode>(MediaCastMode::DEFAULT)));
+ expected_modes.insert(MediaCastMode::DEFAULT);
+ EXPECT_CALL(
+ *message_handler_,
+ UpdateCastModes(expected_modes, "google.com",
+ base::Optional<MediaCastMode>(MediaCastMode::DEFAULT)));
+ media_router_ui_->UIInitialized();
+ media_router_ui_->InitForTest(&mock_router_, web_contents(),
+ message_handler_.get(),
+ std::move(create_session_request_));
+ // |media_router_ui_| takes ownership of |request_callbacks|.
+ media_router_ui_.reset();
+}
+
+} // 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
new file mode 100644
index 00000000000..7ddebdb29e1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_web_ui_test.cc
@@ -0,0 +1,62 @@
+// 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 "chrome/browser/ui/webui/media_router/media_router_web_ui_test.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/media/router/media_router_ui_service.h"
+#include "chrome/browser/media/router/media_router_ui_service_factory.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"
+#include "chrome/test/base/dialog_test_browser_window.h"
+
+class MockMediaRouterUIService : public media_router::MediaRouterUIService {
+ public:
+ explicit MockMediaRouterUIService(Profile* profile)
+ : media_router::MediaRouterUIService(profile),
+ action_controller_(profile) {}
+ ~MockMediaRouterUIService() override {}
+
+ MediaRouterActionController* action_controller() override {
+ return &action_controller_;
+ }
+
+ private:
+ MockMediaRouterActionController action_controller_;
+};
+
+std::unique_ptr<KeyedService> BuildMockMediaRouterUIService(
+ content::BrowserContext* context) {
+ return base::MakeUnique<MockMediaRouterUIService>(
+ static_cast<Profile*>(context));
+}
+
+std::unique_ptr<KeyedService> BuildToolbarActionsModel(
+ content::BrowserContext* context) {
+ return base::MakeUnique<ToolbarActionsModel>(static_cast<Profile*>(context),
+ nullptr);
+}
+
+MediaRouterWebUITest::MediaRouterWebUITest() : MediaRouterWebUITest(false) {}
+MediaRouterWebUITest::MediaRouterWebUITest(bool require_mock_ui_service)
+ : require_mock_ui_service_(require_mock_ui_service) {}
+
+MediaRouterWebUITest::~MediaRouterWebUITest() {}
+
+TestingProfile* MediaRouterWebUITest::CreateProfile() {
+ TestingProfile::Builder builder;
+ if (require_mock_ui_service_) {
+ builder.AddTestingFactory(
+ media_router::MediaRouterUIServiceFactory::GetInstance(),
+ BuildMockMediaRouterUIService);
+ builder.AddTestingFactory(ToolbarActionsModelFactory::GetInstance(),
+ BuildToolbarActionsModel);
+ }
+ return builder.Build().release();
+}
+
+BrowserWindow* MediaRouterWebUITest::CreateBrowserWindow() {
+ return new DialogTestBrowserWindow;
+}
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_web_ui_test.h b/chromium/chrome/browser/ui/webui/media_router/media_router_web_ui_test.h
new file mode 100644
index 00000000000..1e406183224
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_web_ui_test.h
@@ -0,0 +1,30 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_WEB_UI_TEST_H_
+#define CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_WEB_UI_TEST_H_
+
+#include "chrome/test/base/browser_with_test_window_test.h"
+
+class MediaRouterWebUITest : public BrowserWithTestWindowTest {
+ public:
+ // |require_mock_ui_service_| defaults to false in the default ctor.
+ MediaRouterWebUITest();
+ explicit MediaRouterWebUITest(bool require_mock_ui_service);
+ ~MediaRouterWebUITest() override;
+
+ protected:
+ // BrowserWithTestWindowTest:
+ TestingProfile* CreateProfile() override;
+ BrowserWindow* CreateBrowserWindow() override;
+
+ private:
+ // When this is set to true, MockMediaRouterUIService is instantiated.
+ // Otherwise, no MediaRouterUIService is instantiated.
+ bool require_mock_ui_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaRouterWebUITest);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_WEB_UI_TEST_H_
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
new file mode 100644
index 00000000000..b4da44a3602
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc
@@ -0,0 +1,1057 @@
+// 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.
+
+#include "chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "chrome/browser/media/router/media_router_metrics.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/ui/webui/media_router/media_cast_mode.h"
+#include "chrome/browser/ui/webui/media_router/media_router_ui.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/common/constants.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace media_router {
+
+namespace {
+
+const char kCastLearnMorePageUrl[] =
+ "https://www.google.com/chrome/devices/chromecast/learn.html";
+const char kHelpPageUrlPrefix[] =
+ "https://support.google.com/chromecast/answer/%d";
+
+// Message names.
+const char kRequestInitialData[] = "requestInitialData";
+const char kCreateRoute[] = "requestRoute";
+const char kAcknowledgeFirstRunFlow[] = "acknowledgeFirstRunFlow";
+const char kActOnIssue[] = "actOnIssue";
+const char kCloseRoute[] = "closeRoute";
+const char kJoinRoute[] = "joinRoute";
+const char kCloseDialog[] = "closeDialog";
+const char kReportBlur[] = "reportBlur";
+const char kReportClickedSinkIndex[] = "reportClickedSinkIndex";
+const char kReportFilter[] = "reportFilter";
+const char kReportInitialAction[] = "reportInitialAction";
+const char kReportInitialState[] = "reportInitialState";
+const char kReportNavigateToView[] = "reportNavigateToView";
+const char kReportRouteCreationOutcome[] = "reportRouteCreationOutcome";
+const char kReportRouteCreation[] = "reportRouteCreation";
+const char kReportSelectedCastMode[] = "reportSelectedCastMode";
+const char kReportSinkCount[] = "reportSinkCount";
+const char kReportTimeToClickSink[] = "reportTimeToClickSink";
+const char kReportTimeToInitialActionClose[] = "reportTimeToInitialActionClose";
+const char kSearchSinksAndCreateRoute[] = "searchSinksAndCreateRoute";
+const char kOnInitialDataReceived[] = "onInitialDataReceived";
+const char kOnMediaControllerAvailable[] = "onMediaControllerAvailable";
+const char kOnMediaControllerClosed[] = "onMediaControllerClosed";
+const char kPauseCurrentMedia[] = "pauseCurrentMedia";
+const char kPlayCurrentMedia[] = "playCurrentMedia";
+const char kSeekCurrentMedia[] = "seekCurrentMedia";
+const char kSetCurrentMediaMute[] = "setCurrentMediaMute";
+const char kSetCurrentMediaVolume[] = "setCurrentMediaVolume";
+
+// JS function names.
+const char kSetInitialData[] = "media_router.ui.setInitialData";
+const char kOnCreateRouteResponseReceived[] =
+ "media_router.ui.onCreateRouteResponseReceived";
+const char kOnRouteControllerInvalidated[] =
+ "media_router.ui.onRouteControllerInvalidated";
+const char kReceiveSearchResult[] = "media_router.ui.receiveSearchResult";
+const char kSetFirstRunFlowData[] = "media_router.ui.setFirstRunFlowData";
+const char kSetIssue[] = "media_router.ui.setIssue";
+const char kSetSinkListAndIdentity[] = "media_router.ui.setSinkListAndIdentity";
+const char kSetRouteList[] = "media_router.ui.setRouteList";
+const char kSetCastModeList[] = "media_router.ui.setCastModeList";
+const char kUpdateMaxHeight[] = "media_router.ui.updateMaxHeight";
+const char kUpdateRouteStatus[] = "media_router.ui.updateRouteStatus";
+const char kWindowOpen[] = "window.open";
+
+std::unique_ptr<base::DictionaryValue> SinksAndIdentityToValue(
+ const std::vector<MediaSinkWithCastModes>& sinks,
+ const AccountInfo& account_info) {
+ std::unique_ptr<base::DictionaryValue> sink_list_and_identity(
+ new base::DictionaryValue);
+ bool show_email = false;
+ bool show_domain = false;
+ std::string user_domain;
+ if (account_info.IsValid()) {
+ user_domain = account_info.hosted_domain;
+ sink_list_and_identity->SetString("userEmail", account_info.email);
+ }
+
+ std::unique_ptr<base::ListValue> sinks_val(new base::ListValue);
+
+ for (const MediaSinkWithCastModes& sink_with_cast_modes : sinks) {
+ std::unique_ptr<base::DictionaryValue> sink_val(new base::DictionaryValue);
+
+ const MediaSink& sink = sink_with_cast_modes.sink;
+ sink_val->SetString("id", sink.id());
+ sink_val->SetString("name", sink.name());
+ sink_val->SetInteger("iconType", sink.icon_type());
+ if (sink.description())
+ sink_val->SetString("description", *sink.description());
+
+ bool is_pseudo_sink =
+ base::StartsWith(sink.id(), "pseudo:", base::CompareCase::SENSITIVE);
+ if (!user_domain.empty() && sink.domain() && !sink.domain()->empty()) {
+ std::string domain = *sink.domain();
+ // Convert default domains to user domain
+ if (domain == "default") {
+ domain = user_domain;
+ if (domain == Profile::kNoHostedDomainFound) {
+ // Default domain will be empty for non-dasher accounts.
+ domain.clear();
+ }
+ }
+
+ sink_val->SetString("domain", domain);
+
+ show_email = show_email || !is_pseudo_sink;
+ if (!domain.empty() && domain != user_domain) {
+ show_domain = true;
+ }
+ }
+
+ int cast_mode_bits = 0;
+ for (MediaCastMode cast_mode : sink_with_cast_modes.cast_modes)
+ cast_mode_bits |= cast_mode;
+
+ sink_val->SetInteger("castModes", cast_mode_bits);
+ sink_val->SetBoolean("isPseudoSink", is_pseudo_sink);
+ sinks_val->Append(std::move(sink_val));
+ }
+
+ sink_list_and_identity->Set("sinks", std::move(sinks_val));
+ sink_list_and_identity->SetBoolean("showEmail", show_email);
+ sink_list_and_identity->SetBoolean("showDomain", show_domain);
+ return sink_list_and_identity;
+}
+
+std::unique_ptr<base::DictionaryValue> RouteToValue(
+ const MediaRoute& route,
+ bool can_join,
+ const std::string& extension_id,
+ bool incognito,
+ int current_cast_mode) {
+ std::unique_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue);
+ dictionary->SetString("id", route.media_route_id());
+ dictionary->SetString("sinkId", route.media_sink_id());
+ dictionary->SetString("description", route.description());
+ dictionary->SetBoolean("isLocal", route.is_local());
+ dictionary->SetBoolean("canJoin", can_join);
+ if (current_cast_mode > 0) {
+ dictionary->SetInteger("currentCastMode", current_cast_mode);
+ }
+
+ const std::string& custom_path = route.custom_controller_path();
+ if (!incognito && !custom_path.empty()) {
+ std::string full_custom_controller_path = base::StringPrintf("%s://%s/%s",
+ extensions::kExtensionScheme, extension_id.c_str(),
+ custom_path.c_str());
+ DCHECK(GURL(full_custom_controller_path).is_valid());
+ dictionary->SetString("customControllerPath",
+ full_custom_controller_path);
+ }
+
+ return dictionary;
+}
+
+std::unique_ptr<base::ListValue> CastModesToValue(
+ const CastModeSet& cast_modes,
+ const std::string& source_host,
+ base::Optional<MediaCastMode> forced_cast_mode) {
+ std::unique_ptr<base::ListValue> value(new base::ListValue);
+
+ for (const MediaCastMode& cast_mode : cast_modes) {
+ std::unique_ptr<base::DictionaryValue> cast_mode_val(
+ new base::DictionaryValue);
+ cast_mode_val->SetInteger("type", cast_mode);
+ cast_mode_val->SetString(
+ "description", MediaCastModeToDescription(cast_mode, source_host));
+ cast_mode_val->SetString("host", source_host);
+ cast_mode_val->SetBoolean(
+ "isForced", forced_cast_mode && forced_cast_mode == cast_mode);
+ value->Append(std::move(cast_mode_val));
+ }
+
+ return value;
+}
+
+// Returns an Issue dictionary created from |issue| that can be used in WebUI.
+std::unique_ptr<base::DictionaryValue> IssueToValue(const Issue& issue) {
+ const IssueInfo& issue_info = issue.info();
+ std::unique_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue);
+ dictionary->SetInteger("id", issue.id());
+ dictionary->SetString("title", issue_info.title);
+ dictionary->SetString("message", issue_info.message);
+ dictionary->SetInteger("defaultActionType",
+ static_cast<int>(issue_info.default_action));
+ if (!issue_info.secondary_actions.empty()) {
+ DCHECK_EQ(1u, issue_info.secondary_actions.size());
+ dictionary->SetInteger("secondaryActionType",
+ static_cast<int>(issue_info.secondary_actions[0]));
+ }
+ if (!issue_info.route_id.empty())
+ dictionary->SetString("routeId", issue_info.route_id);
+ dictionary->SetBoolean("isBlocking", issue_info.is_blocking);
+ if (issue_info.help_page_id > 0)
+ dictionary->SetInteger("helpPageId", issue_info.help_page_id);
+
+ return dictionary;
+}
+
+bool IsValidIssueActionTypeNum(int issue_action_type_num) {
+ return issue_action_type_num >= 0 &&
+ issue_action_type_num <=
+ static_cast<int>(IssueInfo::Action::NUM_VALUES);
+}
+
+// Composes a "learn more" URL. The URL depends on template arguments in |args|.
+// Returns an empty string if |args| is invalid.
+std::string GetLearnMoreUrl(const base::DictionaryValue* args) {
+ // TODO(imcheng): The template arguments for determining the learn more URL
+ // should come from the Issue object in the browser, not from WebUI.
+ int help_page_id = -1;
+ if (!args->GetInteger("helpPageId", &help_page_id) || help_page_id < 0) {
+ DVLOG(1) << "Invalid help page id.";
+ return std::string();
+ }
+
+ std::string help_url = base::StringPrintf(kHelpPageUrlPrefix, help_page_id);
+ if (!GURL(help_url).is_valid()) {
+ DVLOG(1) << "Error: URL is invalid and cannot be opened.";
+ return std::string();
+ }
+ return help_url;
+}
+
+} // namespace
+
+MediaRouterWebUIMessageHandler::MediaRouterWebUIMessageHandler(
+ MediaRouterUI* media_router_ui)
+ : incognito_(
+ Profile::FromWebUI(media_router_ui->web_ui())->IsOffTheRecord()),
+ dialog_closing_(false),
+ media_router_ui_(media_router_ui) {}
+
+MediaRouterWebUIMessageHandler::~MediaRouterWebUIMessageHandler() {
+}
+
+void MediaRouterWebUIMessageHandler::UpdateSinks(
+ const std::vector<MediaSinkWithCastModes>& sinks) {
+ DVLOG(2) << "UpdateSinks";
+ std::unique_ptr<base::DictionaryValue> sinks_and_identity_val(
+ SinksAndIdentityToValue(sinks, GetAccountInfo()));
+ web_ui()->CallJavascriptFunctionUnsafe(kSetSinkListAndIdentity,
+ *sinks_and_identity_val);
+}
+
+void MediaRouterWebUIMessageHandler::UpdateRoutes(
+ const std::vector<MediaRoute>& routes,
+ const std::vector<MediaRoute::Id>& joinable_route_ids,
+ const std::unordered_map<MediaRoute::Id, MediaCastMode>&
+ current_cast_modes) {
+ std::unique_ptr<base::ListValue> routes_val(
+ RoutesToValue(routes, joinable_route_ids, current_cast_modes));
+ web_ui()->CallJavascriptFunctionUnsafe(kSetRouteList, *routes_val);
+}
+
+void MediaRouterWebUIMessageHandler::UpdateCastModes(
+ const CastModeSet& cast_modes,
+ const std::string& source_host,
+ base::Optional<MediaCastMode> forced_cast_mode) {
+ DVLOG(2) << "UpdateCastModes";
+ std::unique_ptr<base::ListValue> cast_modes_val(
+ CastModesToValue(cast_modes, source_host, forced_cast_mode));
+ web_ui()->CallJavascriptFunctionUnsafe(kSetCastModeList, *cast_modes_val);
+}
+
+void MediaRouterWebUIMessageHandler::OnCreateRouteResponseReceived(
+ const MediaSink::Id& sink_id,
+ const MediaRoute* route) {
+ DVLOG(2) << "OnCreateRouteResponseReceived";
+ if (route) {
+ int current_cast_mode = CurrentCastModeForRouteId(
+ route->media_route_id(), media_router_ui_->routes_and_cast_modes());
+ std::unique_ptr<base::DictionaryValue> route_value(RouteToValue(
+ *route, false, media_router_ui_->GetRouteProviderExtensionId(),
+ incognito_, current_cast_mode));
+ web_ui()->CallJavascriptFunctionUnsafe(kOnCreateRouteResponseReceived,
+ base::Value(sink_id), *route_value,
+ base::Value(route->for_display()));
+ } else {
+ web_ui()->CallJavascriptFunctionUnsafe(kOnCreateRouteResponseReceived,
+ base::Value(sink_id), base::Value(),
+ base::Value(false));
+ }
+}
+
+void MediaRouterWebUIMessageHandler::ReturnSearchResult(
+ const std::string& sink_id) {
+ DVLOG(2) << "ReturnSearchResult";
+ web_ui()->CallJavascriptFunctionUnsafe(kReceiveSearchResult,
+ base::Value(sink_id));
+}
+
+void MediaRouterWebUIMessageHandler::UpdateIssue(const Issue& issue) {
+ DVLOG(2) << "UpdateIssue";
+ web_ui()->CallJavascriptFunctionUnsafe(kSetIssue, *IssueToValue(issue));
+}
+
+void MediaRouterWebUIMessageHandler::ClearIssue() {
+ DVLOG(2) << "ClearIssue";
+ web_ui()->CallJavascriptFunctionUnsafe(kSetIssue, base::Value());
+}
+
+void MediaRouterWebUIMessageHandler::UpdateMaxDialogHeight(int height) {
+ DVLOG(2) << "UpdateMaxDialogHeight";
+ web_ui()->CallJavascriptFunctionUnsafe(kUpdateMaxHeight, base::Value(height));
+}
+
+void MediaRouterWebUIMessageHandler::UpdateMediaRouteStatus(
+ const MediaStatus& status) {
+ current_media_status_ = base::make_optional<MediaStatus>(MediaStatus(status));
+
+ base::DictionaryValue status_value;
+ status_value.SetString("title", status.title);
+ status_value.SetString("description", status.description);
+ status_value.SetBoolean("canPlayPause", status.can_play_pause);
+ status_value.SetBoolean("canMute", status.can_mute);
+ status_value.SetBoolean("canSetVolume", status.can_set_volume);
+ status_value.SetBoolean("canSeek", status.can_seek);
+ status_value.SetBoolean("isPaused", status.is_paused);
+ status_value.SetBoolean("isMuted", status.is_muted);
+ status_value.SetInteger("duration", status.duration.InSeconds());
+ status_value.SetInteger("currentTime", status.current_time.InSeconds());
+ status_value.SetDouble("volume", status.volume);
+ web_ui()->CallJavascriptFunctionUnsafe(kUpdateRouteStatus,
+ std::move(status_value));
+}
+
+void MediaRouterWebUIMessageHandler::OnRouteControllerInvalidated() {
+ web_ui()->CallJavascriptFunctionUnsafe(kOnRouteControllerInvalidated);
+}
+
+void MediaRouterWebUIMessageHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ kRequestInitialData,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnRequestInitialData,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kCreateRoute,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnCreateRoute,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kAcknowledgeFirstRunFlow,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnAcknowledgeFirstRunFlow,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kActOnIssue,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnActOnIssue,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kCloseRoute,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnCloseRoute,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kJoinRoute,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnJoinRoute,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kCloseDialog,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnCloseDialog,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kReportBlur,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnReportBlur,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kReportClickedSinkIndex,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnReportClickedSinkIndex,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kReportFilter,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnReportFilter,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kReportInitialState,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnReportInitialState,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kReportInitialAction,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnReportInitialAction,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kReportRouteCreation,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnReportRouteCreation,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kReportRouteCreationOutcome,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnReportRouteCreationOutcome,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kReportSelectedCastMode,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnReportSelectedCastMode,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kReportNavigateToView,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnReportNavigateToView,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kReportSinkCount,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnReportSinkCount,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kReportTimeToClickSink,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnReportTimeToClickSink,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kReportTimeToInitialActionClose,
+ base::Bind(
+ &MediaRouterWebUIMessageHandler::OnReportTimeToInitialActionClose,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kSearchSinksAndCreateRoute,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnSearchSinksAndCreateRoute,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kOnInitialDataReceived,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnInitialDataReceived,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kOnMediaControllerAvailable,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnMediaControllerAvailable,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kOnMediaControllerClosed,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnMediaControllerClosed,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kPauseCurrentMedia,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnPauseCurrentMedia,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kPlayCurrentMedia,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnPlayCurrentMedia,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kSeekCurrentMedia,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnSeekCurrentMedia,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kSetCurrentMediaMute,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnSetCurrentMediaMute,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kSetCurrentMediaVolume,
+ base::Bind(&MediaRouterWebUIMessageHandler::OnSetCurrentMediaVolume,
+ base::Unretained(this)));
+}
+
+void MediaRouterWebUIMessageHandler::OnRequestInitialData(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnRequestInitialData";
+ media_router_ui_->OnUIInitiallyLoaded();
+ base::DictionaryValue initial_data;
+
+ // "No Cast devices found?" Chromecast help center page.
+ initial_data.SetString("deviceMissingUrl",
+ base::StringPrintf(kHelpPageUrlPrefix, 3249268));
+
+ std::unique_ptr<base::DictionaryValue> sinks_and_identity(
+ SinksAndIdentityToValue(media_router_ui_->sinks(), GetAccountInfo()));
+ initial_data.Set("sinksAndIdentity", std::move(sinks_and_identity));
+
+ std::unique_ptr<base::ListValue> routes(RoutesToValue(
+ media_router_ui_->routes(), media_router_ui_->joinable_route_ids(),
+ media_router_ui_->routes_and_cast_modes()));
+ initial_data.Set("routes", std::move(routes));
+
+ const std::set<MediaCastMode> cast_modes = media_router_ui_->cast_modes();
+ std::unique_ptr<base::ListValue> cast_modes_list(CastModesToValue(
+ cast_modes, media_router_ui_->GetPresentationRequestSourceName(),
+ media_router_ui_->forced_cast_mode()));
+ initial_data.Set("castModes", std::move(cast_modes_list));
+
+ // If the cast mode last chosen for the current origin is tab mirroring,
+ // that should be the cast mode initially selected in the dialog. Otherwise
+ // the initial cast mode should be chosen automatically by the dialog.
+ bool use_tab_mirroring =
+ base::ContainsKey(cast_modes, MediaCastMode::TAB_MIRROR) &&
+ media_router_ui_->UserSelectedTabMirroringForCurrentOrigin();
+ initial_data.SetBoolean("useTabMirroring", use_tab_mirroring);
+
+ initial_data.SetBoolean(
+ "useNewRouteControls",
+ base::FeatureList::IsEnabled(features::kMediaRouterUIRouteController));
+
+ web_ui()->CallJavascriptFunctionUnsafe(kSetInitialData, initial_data);
+ media_router_ui_->UIInitialized();
+}
+
+void MediaRouterWebUIMessageHandler::OnCreateRoute(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnCreateRoute";
+ const base::DictionaryValue* args_dict = nullptr;
+ std::string sink_id;
+ int cast_mode_num = -1;
+ if (!args->GetDictionary(0, &args_dict) ||
+ !args_dict->GetString("sinkId", &sink_id) ||
+ !args_dict->GetInteger("selectedCastMode", &cast_mode_num)) {
+ DVLOG(1) << "Unable to extract args.";
+ return;
+ }
+
+ if (sink_id.empty()) {
+ DVLOG(1) << "Media Route UI did not respond with a "
+ << "valid sink ID. Aborting.";
+ return;
+ }
+
+ if (!IsValidCastModeNum(cast_mode_num)) {
+ // TODO(imcheng): Record error condition with UMA.
+ DVLOG(1) << "Invalid cast mode: " << cast_mode_num << ". Aborting.";
+ return;
+ }
+
+ MediaRouterUI* media_router_ui =
+ static_cast<MediaRouterUI*>(web_ui()->GetController());
+ if (media_router_ui->HasPendingRouteRequest()) {
+ DVLOG(1) << "UI already has pending route request. Ignoring.";
+ IssueInfo issue(
+ l10n_util::GetStringUTF8(IDS_MEDIA_ROUTER_ISSUE_PENDING_ROUTE),
+ IssueInfo::Action::DISMISS, IssueInfo::Severity::NOTIFICATION);
+ media_router_ui_->AddIssue(issue);
+ return;
+ }
+
+ DVLOG(2) << __func__ << ": sink id: " << sink_id
+ << ", cast mode: " << cast_mode_num;
+
+ // TODO(haibinlu): Pass additional parameters into the CreateRoute request,
+ // e.g. low-fps-mirror, user-override. (crbug.com/490364)
+ if (!media_router_ui->CreateRoute(
+ sink_id, static_cast<MediaCastMode>(cast_mode_num))) {
+ DVLOG(1) << "Error initiating route request.";
+ }
+}
+
+void MediaRouterWebUIMessageHandler::OnAcknowledgeFirstRunFlow(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnAcknowledgeFirstRunFlow";
+ Profile::FromWebUI(web_ui())->GetPrefs()->SetBoolean(
+ prefs::kMediaRouterFirstRunFlowAcknowledged, true);
+
+ bool enabled_cloud_services = false;
+ // Do not set the relevant cloud services prefs if the user was not shown
+ // the cloud services prompt.
+ if (!args->GetBoolean(0, &enabled_cloud_services)) {
+ DVLOG(1) << "User was not shown the enable cloud services prompt.";
+ return;
+ }
+
+ PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
+ pref_service->SetBoolean(prefs::kMediaRouterEnableCloudServices,
+ enabled_cloud_services);
+ pref_service->SetBoolean(prefs::kMediaRouterCloudServicesPrefSet, true);
+}
+
+void MediaRouterWebUIMessageHandler::OnActOnIssue(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnActOnIssue";
+ const base::DictionaryValue* args_dict = nullptr;
+ Issue::Id issue_id;
+ int action_type_num = -1;
+ if (!args->GetDictionary(0, &args_dict) ||
+ !args_dict->GetInteger("issueId", &issue_id) ||
+ !args_dict->GetInteger("actionType", &action_type_num)) {
+ DVLOG(1) << "Unable to extract args.";
+ return;
+ }
+ if (!IsValidIssueActionTypeNum(action_type_num)) {
+ DVLOG(1) << "Invalid action type: " << action_type_num;
+ return;
+ }
+ IssueInfo::Action action_type =
+ static_cast<IssueInfo::Action>(action_type_num);
+ if (ActOnIssueType(action_type, args_dict))
+ DVLOG(1) << "ActOnIssueType failed for Issue ID " << issue_id;
+ media_router_ui_->ClearIssue(issue_id);
+}
+
+void MediaRouterWebUIMessageHandler::OnJoinRoute(const base::ListValue* args) {
+ DVLOG(1) << "OnJoinRoute";
+ const base::DictionaryValue* args_dict = nullptr;
+ std::string route_id;
+ std::string sink_id;
+ if (!args->GetDictionary(0, &args_dict) ||
+ !args_dict->GetString("sinkId", &sink_id) ||
+ !args_dict->GetString("routeId", &route_id)) {
+ DVLOG(1) << "Unable to extract args.";
+ return;
+ }
+
+ if (sink_id.empty()) {
+ DVLOG(1) << "Media Route UI did not respond with a "
+ << "valid sink ID. Aborting.";
+ return;
+ }
+
+ if (route_id.empty()) {
+ DVLOG(1) << "Media Route UI did not respond with a "
+ << "valid route ID. Aborting.";
+ return;
+ }
+
+ MediaRouterUI* media_router_ui =
+ static_cast<MediaRouterUI*>(web_ui()->GetController());
+ if (media_router_ui->HasPendingRouteRequest()) {
+ DVLOG(1) << "UI already has pending route request. Ignoring.";
+ IssueInfo issue(
+ l10n_util::GetStringUTF8(IDS_MEDIA_ROUTER_ISSUE_PENDING_ROUTE),
+ IssueInfo::Action::DISMISS, IssueInfo::Severity::NOTIFICATION);
+ media_router_ui_->AddIssue(issue);
+ return;
+ }
+
+ if (!media_router_ui_->ConnectRoute(sink_id, route_id)) {
+ DVLOG(1) << "Error initiating route join request.";
+ }
+}
+
+void MediaRouterWebUIMessageHandler::OnCloseRoute(const base::ListValue* args) {
+ DVLOG(1) << "OnCloseRoute";
+ const base::DictionaryValue* args_dict = nullptr;
+ std::string route_id;
+ bool is_local = false;
+ if (!args->GetDictionary(0, &args_dict) ||
+ !args_dict->GetString("routeId", &route_id) ||
+ !args_dict->GetBoolean("isLocal", &is_local)) {
+ DVLOG(1) << "Unable to extract args.";
+ return;
+ }
+ media_router_ui_->CloseRoute(route_id);
+ UMA_HISTOGRAM_BOOLEAN("MediaRouter.Ui.Action.StopRoute", !is_local);
+}
+
+void MediaRouterWebUIMessageHandler::OnCloseDialog(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnCloseDialog";
+ if (dialog_closing_)
+ return;
+
+ bool used_esc_to_close_dialog = false;
+ if (!args->GetBoolean(0, &used_esc_to_close_dialog)) {
+ DVLOG(1) << "Unable to extract args.";
+ return;
+ }
+
+ if (used_esc_to_close_dialog) {
+ base::RecordAction(base::UserMetricsAction(
+ "MediaRouter_Ui_Dialog_ESCToClose"));
+ }
+
+ dialog_closing_ = true;
+ media_router_ui_->Close();
+}
+
+void MediaRouterWebUIMessageHandler::OnReportBlur(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnReportBlur";
+ base::RecordAction(base::UserMetricsAction("MediaRouter_Ui_Dialog_Blur"));
+}
+
+void MediaRouterWebUIMessageHandler::OnReportClickedSinkIndex(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnReportClickedSinkIndex";
+ int index;
+ if (!args->GetInteger(0, &index)) {
+ DVLOG(1) << "Unable to extract args.";
+ return;
+ }
+ UMA_HISTOGRAM_SPARSE_SLOWLY("MediaRouter.Ui.Action.StartLocalPosition",
+ std::min(index, 100));
+}
+
+void MediaRouterWebUIMessageHandler::OnReportFilter(const base::ListValue*) {
+ DVLOG(1) << "OnReportFilter";
+ base::RecordAction(base::UserMetricsAction("MediaRouter_Ui_Action_Filter"));
+}
+
+void MediaRouterWebUIMessageHandler::OnReportInitialAction(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnReportInitialAction";
+ int action;
+ if (!args->GetInteger(0, &action)) {
+ DVLOG(1) << "Unable to extract args.";
+ return;
+ }
+ media_router::MediaRouterMetrics::RecordMediaRouterInitialUserAction(
+ static_cast<MediaRouterUserAction>(action));
+}
+
+void MediaRouterWebUIMessageHandler::OnReportInitialState(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnReportInitialState";
+ std::string initial_view;
+ if (!args->GetString(0, &initial_view)) {
+ DVLOG(1) << "Unable to extract args.";
+ return;
+ }
+ bool sink_list_state = initial_view == "sink-list";
+ DCHECK(sink_list_state || (initial_view == "route-details"));
+ UMA_HISTOGRAM_BOOLEAN("MediaRouter.Ui.InitialState", sink_list_state);
+}
+
+void MediaRouterWebUIMessageHandler::OnReportNavigateToView(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnReportNavigateToView";
+ std::string view;
+ if (!args->GetString(0, &view)) {
+ DVLOG(1) << "Unable to extract args.";
+ return;
+ }
+
+ if (view == "cast-mode-list") {
+ base::RecordAction(base::UserMetricsAction(
+ "MediaRouter_Ui_Navigate_SinkListToSource"));
+ } else if (view == "route-details") {
+ base::RecordAction(base::UserMetricsAction(
+ "MediaRouter_Ui_Navigate_SinkListToRouteDetails"));
+ } else if (view == "sink-list") {
+ base::RecordAction(base::UserMetricsAction(
+ "MediaRouter_Ui_Navigate_RouteDetailsToSinkList"));
+ }
+}
+
+void MediaRouterWebUIMessageHandler::OnReportRouteCreation(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnReportRouteCreation";
+ bool route_created_successfully;
+ if (!args->GetBoolean(0, &route_created_successfully)) {
+ DVLOG(1) << "Unable to extract args.";
+ return;
+ }
+
+ UMA_HISTOGRAM_BOOLEAN("MediaRouter.Ui.Action.StartLocalSessionSuccessful",
+ route_created_successfully);
+}
+
+void MediaRouterWebUIMessageHandler::OnReportRouteCreationOutcome(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnReportRouteCreationOutcome";
+ int outcome;
+ if (!args->GetInteger(0, &outcome)) {
+ DVLOG(1) << "Unable to extract args.";
+ return;
+ }
+
+ media_router::MediaRouterMetrics::RecordRouteCreationOutcome(
+ static_cast<MediaRouterRouteCreationOutcome>(outcome));
+}
+
+void MediaRouterWebUIMessageHandler::OnReportSelectedCastMode(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnReportSelectedCastMode";
+ int cast_mode_type;
+ if (!args->GetInteger(0, &cast_mode_type)) {
+ DVLOG(1) << "Unable to extract args.";
+ return;
+ }
+ DCHECK(IsValidCastModeNum(cast_mode_type));
+ UMA_HISTOGRAM_SPARSE_SLOWLY("MediaRouter.Ui.Navigate.SourceSelection",
+ cast_mode_type);
+ media_router_ui_->RecordCastModeSelection(
+ static_cast<MediaCastMode>(cast_mode_type));
+}
+
+void MediaRouterWebUIMessageHandler::OnReportSinkCount(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnReportSinkCount";
+ int sink_count;
+ if (!args->GetInteger(0, &sink_count)) {
+ DVLOG(1) << "Unable to extract args.";
+ return;
+ }
+ UMA_HISTOGRAM_COUNTS_100("MediaRouter.Ui.Device.Count", sink_count);
+}
+
+void MediaRouterWebUIMessageHandler::OnReportTimeToClickSink(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnReportTimeToClickSink";
+ double time_to_click;
+ if (!args->GetDouble(0, &time_to_click)) {
+ DVLOG(1) << "Unable to extract args.";
+ return;
+ }
+ UMA_HISTOGRAM_TIMES("MediaRouter.Ui.Action.StartLocal.Latency",
+ base::TimeDelta::FromMillisecondsD(time_to_click));
+}
+
+void MediaRouterWebUIMessageHandler::OnReportTimeToInitialActionClose(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnReportTimeToInitialActionClose";
+ double time_to_close;
+ if (!args->GetDouble(0, &time_to_close)) {
+ VLOG(0) << "Unable to extract args.";
+ return;
+ }
+ UMA_HISTOGRAM_TIMES("MediaRouter.Ui.Action.CloseLatency",
+ base::TimeDelta::FromMillisecondsD(time_to_close));
+}
+
+void MediaRouterWebUIMessageHandler::OnSearchSinksAndCreateRoute(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnSearchSinksAndCreateRoute";
+ const base::DictionaryValue* args_dict = nullptr;
+ std::string sink_id;
+ std::string search_criteria;
+ std::string domain;
+ int cast_mode_num = -1;
+ if (!args->GetDictionary(0, &args_dict) ||
+ !args_dict->GetString("sinkId", &sink_id) ||
+ !args_dict->GetString("searchCriteria", &search_criteria) ||
+ !args_dict->GetString("domain", &domain) ||
+ !args_dict->GetInteger("selectedCastMode", &cast_mode_num)) {
+ DVLOG(1) << "Unable to extract args";
+ return;
+ }
+
+ if (search_criteria.empty()) {
+ DVLOG(1) << "Media Router UI did not provide valid search criteria. "
+ "Aborting.";
+ return;
+ }
+
+ if (!IsValidCastModeNum(cast_mode_num)) {
+ DVLOG(1) << "Invalid cast mode: " << cast_mode_num << ". Aborting.";
+ return;
+ }
+
+ media_router_ui_->SearchSinksAndCreateRoute(
+ sink_id, search_criteria, domain,
+ static_cast<MediaCastMode>(cast_mode_num));
+}
+
+void MediaRouterWebUIMessageHandler::OnInitialDataReceived(
+ const base::ListValue* args) {
+ DVLOG(1) << "OnInitialDataReceived";
+ media_router_ui_->OnUIInitialDataReceived();
+ MaybeUpdateFirstRunFlowData();
+}
+
+void MediaRouterWebUIMessageHandler::OnMediaControllerAvailable(
+ const base::ListValue* args) {
+ const base::DictionaryValue* args_dict = nullptr;
+ std::string route_id;
+ if (!args->GetDictionary(0, &args_dict) ||
+ !args_dict->GetString("routeId", &route_id)) {
+ DVLOG(1) << "Unable to extract media route ID";
+ return;
+ }
+ media_router_ui_->OnMediaControllerUIAvailable(route_id);
+}
+
+void MediaRouterWebUIMessageHandler::OnMediaControllerClosed(
+ const base::ListValue* args) {
+ current_media_status_.reset();
+ media_router_ui_->OnMediaControllerUIClosed();
+}
+
+void MediaRouterWebUIMessageHandler::OnPauseCurrentMedia(
+ const base::ListValue* args) {
+ const MediaRouteController* route_controller =
+ media_router_ui_->GetMediaRouteController();
+ if (route_controller)
+ route_controller->Pause();
+}
+
+void MediaRouterWebUIMessageHandler::OnPlayCurrentMedia(
+ const base::ListValue* args) {
+ const MediaRouteController* route_controller =
+ media_router_ui_->GetMediaRouteController();
+ if (route_controller)
+ route_controller->Play();
+}
+
+void MediaRouterWebUIMessageHandler::OnSeekCurrentMedia(
+ const base::ListValue* args) {
+ const base::DictionaryValue* args_dict = nullptr;
+ int time;
+ if (!args->GetDictionary(0, &args_dict) ||
+ !args_dict->GetInteger("time", &time)) {
+ DVLOG(1) << "Unable to extract time";
+ return;
+ }
+ base::TimeDelta time_delta = base::TimeDelta::FromSeconds(time);
+ const MediaRouteController* route_controller =
+ media_router_ui_->GetMediaRouteController();
+ if (route_controller && current_media_status_ &&
+ time_delta >= base::TimeDelta() &&
+ time_delta <= current_media_status_->duration) {
+ route_controller->Seek(time_delta);
+ }
+}
+
+void MediaRouterWebUIMessageHandler::OnSetCurrentMediaMute(
+ const base::ListValue* args) {
+ const base::DictionaryValue* args_dict = nullptr;
+ bool mute;
+ if (!args->GetDictionary(0, &args_dict) ||
+ !args_dict->GetBoolean("mute", &mute)) {
+ DVLOG(1) << "Unable to extract mute";
+ return;
+ }
+ const MediaRouteController* route_controller =
+ media_router_ui_->GetMediaRouteController();
+ if (route_controller)
+ route_controller->SetMute(mute);
+}
+
+void MediaRouterWebUIMessageHandler::OnSetCurrentMediaVolume(
+ const base::ListValue* args) {
+ const base::DictionaryValue* args_dict = nullptr;
+ double volume;
+ if (!args->GetDictionary(0, &args_dict) ||
+ !args_dict->GetDouble("volume", &volume)) {
+ DVLOG(1) << "Unable to extract volume";
+ return;
+ }
+ const MediaRouteController* route_controller =
+ media_router_ui_->GetMediaRouteController();
+ if (route_controller && volume >= 0 && volume <= 1)
+ route_controller->SetVolume(volume);
+}
+
+bool MediaRouterWebUIMessageHandler::ActOnIssueType(
+ IssueInfo::Action action_type,
+ const base::DictionaryValue* args) {
+ if (action_type == IssueInfo::Action::LEARN_MORE) {
+ std::string learn_more_url = GetLearnMoreUrl(args);
+ if (learn_more_url.empty())
+ return false;
+ std::unique_ptr<base::ListValue> open_args(new base::ListValue);
+ open_args->AppendString(learn_more_url);
+ web_ui()->CallJavascriptFunctionUnsafe(kWindowOpen, *open_args);
+ return true;
+ } else {
+ // Do nothing; no other issue action types require any other action.
+ return true;
+ }
+}
+
+void MediaRouterWebUIMessageHandler::MaybeUpdateFirstRunFlowData() {
+ base::DictionaryValue first_run_flow_data;
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ PrefService* pref_service = profile->GetPrefs();
+
+ bool first_run_flow_acknowledged =
+ pref_service->GetBoolean(prefs::kMediaRouterFirstRunFlowAcknowledged);
+ bool show_cloud_pref = false;
+ // Cloud services preference is shown if user is logged in. If the user
+ // enables sync after acknowledging the first run flow, this is treated as
+ // the user opting into Google services, including cloud services, if the
+ // browser is a Chrome branded build.
+ if (!pref_service->GetBoolean(prefs::kMediaRouterCloudServicesPrefSet)) {
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetForProfile(profile);
+ if (signin_manager && signin_manager->IsAuthenticated()) {
+ // If the user had previously acknowledged the first run flow without
+ // being shown the cloud services option, and is now logged in with sync
+ // enabled, turn on cloud services.
+ if (first_run_flow_acknowledged &&
+ ProfileSyncServiceFactory::GetForProfile(profile)->IsSyncActive()) {
+ pref_service->SetBoolean(prefs::kMediaRouterEnableCloudServices, true);
+ pref_service->SetBoolean(prefs::kMediaRouterCloudServicesPrefSet,
+ true);
+ // Return early since the first run flow won't be surfaced.
+ return;
+ }
+
+ show_cloud_pref = true;
+ // "Casting to a Hangout from Chrome" Chromecast help center page.
+ first_run_flow_data.SetString("firstRunFlowCloudPrefLearnMoreUrl",
+ base::StringPrintf(kHelpPageUrlPrefix, 6320939));
+ }
+ }
+
+ // Return early if the first run flow won't be surfaced.
+ if (first_run_flow_acknowledged && !show_cloud_pref)
+ return;
+
+ // General Chromecast learn more page.
+ first_run_flow_data.SetString("firstRunFlowLearnMoreUrl",
+ kCastLearnMorePageUrl);
+ first_run_flow_data.SetBoolean("wasFirstRunFlowAcknowledged",
+ first_run_flow_acknowledged);
+ first_run_flow_data.SetBoolean("showFirstRunFlowCloudPref", show_cloud_pref);
+ web_ui()->CallJavascriptFunctionUnsafe(kSetFirstRunFlowData,
+ first_run_flow_data);
+}
+
+AccountInfo MediaRouterWebUIMessageHandler::GetAccountInfo() {
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetForProfile(Profile::FromWebUI(web_ui()));
+ return signin_manager ? signin_manager->GetAuthenticatedAccountInfo()
+ : AccountInfo();
+}
+
+int MediaRouterWebUIMessageHandler::CurrentCastModeForRouteId(
+ const MediaRoute::Id& route_id,
+ const std::unordered_map<MediaRoute::Id, MediaCastMode>& current_cast_modes)
+ const {
+ auto current_cast_mode_entry = current_cast_modes.find(route_id);
+ int current_cast_mode = current_cast_mode_entry != current_cast_modes.end()
+ ? current_cast_mode_entry->second
+ : -1;
+ return current_cast_mode;
+}
+
+std::unique_ptr<base::ListValue> MediaRouterWebUIMessageHandler::RoutesToValue(
+ const std::vector<MediaRoute>& routes,
+ const std::vector<MediaRoute::Id>& joinable_route_ids,
+ const std::unordered_map<MediaRoute::Id, MediaCastMode>& current_cast_modes)
+ const {
+ std::unique_ptr<base::ListValue> value(new base::ListValue);
+ const std::string& extension_id =
+ media_router_ui_->GetRouteProviderExtensionId();
+
+ for (const MediaRoute& route : routes) {
+ bool can_join =
+ base::ContainsValue(joinable_route_ids, route.media_route_id());
+ int current_cast_mode = CurrentCastModeForRouteId(route.media_route_id(),
+ current_cast_modes);
+ std::unique_ptr<base::DictionaryValue> route_val(RouteToValue(
+ route, can_join, extension_id, incognito_, current_cast_mode));
+ value->Append(std::move(route_val));
+ }
+
+ return value;
+}
+
+void MediaRouterWebUIMessageHandler::SetWebUIForTest(content::WebUI* web_ui) {
+ set_web_ui(web_ui);
+}
+
+} // namespace media_router
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
new file mode 100644
index 00000000000..ef4030b5d1c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h
@@ -0,0 +1,168 @@
+// 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.
+
+#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 <unordered_map>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "chrome/browser/ui/webui/media_router/media_cast_mode.h"
+#include "chrome/browser/ui/webui/media_router/media_sink_with_cast_modes.h"
+#include "chrome/common/media_router/issue.h"
+#include "chrome/common/media_router/media_status.h"
+#include "components/signin/core/browser/account_info.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+} // namespace base
+
+namespace content {
+class WebUI;
+}
+
+namespace media_router {
+
+class Issue;
+class MediaRoute;
+class MediaRouterUI;
+
+// The handler for Javascript messages related to the media router dialog.
+class MediaRouterWebUIMessageHandler : public content::WebUIMessageHandler {
+ public:
+ explicit MediaRouterWebUIMessageHandler(MediaRouterUI* media_router_ui);
+ ~MediaRouterWebUIMessageHandler() override;
+
+ // Methods to update the status displayed by the dialog.
+ 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>&
+ current_cast_modes);
+ // Overridden in tests.
+ virtual void UpdateCastModes(const CastModeSet& cast_modes,
+ const std::string& source_host,
+ base::Optional<MediaCastMode> forced_cast_mode);
+ void OnCreateRouteResponseReceived(const MediaSink::Id& sink_id,
+ const MediaRoute* route);
+ void ReturnSearchResult(const std::string& sink_id);
+
+ void UpdateIssue(const Issue& issue);
+ void ClearIssue();
+
+ // Updates the maximum dialog height to allow the WebUI properly scale when
+ // the browser window changes.
+ void UpdateMaxDialogHeight(int height);
+
+ // Notifies the WebUI with an updated MediaStatus. Overridden in tests.
+ virtual void UpdateMediaRouteStatus(const MediaStatus& status);
+
+ // Notifies the WebUI that the controller for the selected route has been
+ // invalidated.
+ void OnRouteControllerInvalidated();
+
+ void SetWebUIForTest(content::WebUI* webui);
+ void set_incognito_for_test(bool incognito) { incognito_ = incognito; }
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterWebUIMessageHandlerTest,
+ RecordCastModeSelection);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterWebUIMessageHandlerTest,
+ RetrieveCastModeSelection);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterWebUIMessageHandlerTest,
+ OnRouteDetailsOpenedAndClosed);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterWebUIMessageHandlerTest,
+ OnMediaCommandsReceived);
+ FRIEND_TEST_ALL_PREFIXES(MediaRouterWebUIMessageHandlerTest,
+ OnInvalidMediaCommandsReceived);
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // Handlers for JavaScript messages.
+ // See media_router_ui_interface.js for documentation on parameters.
+ void OnRequestInitialData(const base::ListValue* args);
+ void OnCreateRoute(const base::ListValue* args);
+ void OnAcknowledgeFirstRunFlow(const base::ListValue* args);
+ void OnActOnIssue(const base::ListValue* args);
+ void OnCloseRoute(const base::ListValue* args);
+ void OnJoinRoute(const base::ListValue* args);
+ void OnCloseDialog(const base::ListValue* args);
+ void OnReportBlur(const base::ListValue* args);
+ void OnReportClickedSinkIndex(const base::ListValue* args);
+ void OnReportFilter(const base::ListValue* args);
+ void OnReportInitialAction(const base::ListValue* args);
+ void OnReportInitialState(const base::ListValue* args);
+ void OnReportNavigateToView(const base::ListValue* args);
+ void OnReportRouteCreation(const base::ListValue* args);
+ void OnReportRouteCreationOutcome(const base::ListValue* args);
+ void OnReportSelectedCastMode(const base::ListValue* args);
+ void OnReportSinkCount(const base::ListValue* args);
+ void OnReportTimeToClickSink(const base::ListValue* args);
+ void OnReportTimeToInitialActionClose(const base::ListValue* args);
+ void OnMediaControllerAvailable(const base::ListValue* args);
+ void OnMediaControllerClosed(const base::ListValue* args);
+ void OnSearchSinksAndCreateRoute(const base::ListValue* args);
+ void OnInitialDataReceived(const base::ListValue* args);
+
+ // Handlers for JavaScript messages to control the media.
+ void OnPlayCurrentMedia(const base::ListValue* args);
+ void OnPauseCurrentMedia(const base::ListValue* args);
+ void OnSeekCurrentMedia(const base::ListValue* args);
+ void OnSetCurrentMediaMute(const base::ListValue* args);
+ void OnSetCurrentMediaVolume(const base::ListValue* args);
+
+ // Performs an action for an Issue of |type|.
+ // |args| contains additional parameter that varies based on |type|.
+ // Returns |true| if the action was successfully performed.
+ bool ActOnIssueType(IssueInfo::Action type,
+ const base::DictionaryValue* args);
+
+ // May update the first run flow related properties in the WebUI. This is
+ // called after the initial data is received to avoid unnecessary work when
+ // initializing the WebUI.
+ void MaybeUpdateFirstRunFlowData();
+
+ // Returns the current cast mode for the route with ID |route_id| or -1 if the
+ // route has no current cast mode.
+ int CurrentCastModeForRouteId(
+ const MediaRoute::Id& route_id,
+ const std::unordered_map<MediaRoute::Id, MediaCastMode>&
+ current_cast_modes) const;
+
+ // Converts |routes| and |joinable_route_ids| into base::ListValue that can be
+ // passed to WebUI.
+ std::unique_ptr<base::ListValue> RoutesToValue(
+ const std::vector<MediaRoute>& routes,
+ const std::vector<MediaRoute::Id>& joinable_route_ids,
+ const std::unordered_map<MediaRoute::Id, MediaCastMode>&
+ current_cast_modes) const;
+
+ // Retrieve the account info for email and domain of signed in users. This is
+ // used when updating sinks to determine if identity should be displayed.
+ // Marked virtual for tests.
+ virtual AccountInfo GetAccountInfo();
+
+ // |true| if the associated Profile is incognito.
+ bool incognito_;
+
+ // Keeps track of whether a command to close the dialog has been issued.
+ bool dialog_closing_;
+
+ // The media status currently shown in the UI.
+ base::Optional<MediaStatus> current_media_status_;
+
+ MediaRouterUI* media_router_ui_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaRouterWebUIMessageHandler);
+};
+
+} // namespace media_router
+
+#endif // CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_ROUTER_WEBUI_MESSAGE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler_unittest.cc b/chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler_unittest.cc
new file mode 100644
index 00000000000..484b987f00d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_router_webui_message_handler_unittest.cc
@@ -0,0 +1,687 @@
+// 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.
+
+#include "chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h"
+#include "base/macros.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/media/router/mock_media_router.h"
+#include "chrome/browser/media/router/mojo/media_router_mojo_test.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/media_router/media_router_ui.h"
+#include "chrome/browser/ui/webui/media_router/media_router_web_ui_test.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/test/test_web_ui.h"
+#include "extensions/common/constants.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Return;
+using testing::ReturnRef;
+
+namespace media_router {
+
+namespace {
+
+const char kProviderExtensionIdForTesting[] = "test_id";
+const char kControllerPathForTesting[] = "test_path";
+const char kUserEmailForTesting[] = "nobody@example.com";
+const char kUserDomainForTesting[] = "example.com";
+
+bool GetBooleanFromDict(const base::DictionaryValue* dict,
+ const std::string& key) {
+ bool value = false;
+ EXPECT_TRUE(dict->GetBoolean(key, &value));
+ return value;
+}
+
+double GetDoubleFromDict(const base::DictionaryValue* dict,
+ const std::string& key) {
+ double value = 0;
+ EXPECT_TRUE(dict->GetDouble(key, &value));
+ return value;
+}
+
+int GetIntegerFromDict(const base::DictionaryValue* dict,
+ const std::string& key) {
+ int value = 0;
+ EXPECT_TRUE(dict->GetInteger(key, &value));
+ return value;
+}
+
+std::string GetStringFromDict(const base::DictionaryValue* dict,
+ const std::string& key) {
+ std::string value;
+ EXPECT_TRUE(dict->GetString(key, &value));
+ return value;
+}
+
+// Creates a local route for display.
+MediaRoute CreateRoute() {
+ MediaRoute::Id route_id("routeId123");
+ MediaSink::Id sink_id("sinkId123");
+ MediaSink sink(sink_id, "The sink", MediaSink::IconType::CAST);
+ std::string description("This is a route");
+ bool is_local = true;
+ bool is_for_display = true;
+ MediaRoute route(route_id, MediaSource("mediaSource"), sink_id, description,
+ is_local, kControllerPathForTesting, is_for_display);
+
+ return route;
+}
+
+MediaSinkWithCastModes CreateMediaSinkWithCastMode(const std::string& sink_id,
+ MediaCastMode cast_mode) {
+ std::string sink_name("The sink");
+ MediaSinkWithCastModes media_sink_with_cast_modes(
+ MediaSink(sink_id, sink_name, MediaSink::IconType::CAST));
+ media_sink_with_cast_modes.cast_modes.insert(cast_mode);
+
+ return media_sink_with_cast_modes;
+}
+
+} // namespace
+
+class MockMediaRouterUI : public MediaRouterUI {
+ public:
+ explicit MockMediaRouterUI(content::WebUI* web_ui)
+ : MediaRouterUI(web_ui) {}
+ ~MockMediaRouterUI() {}
+
+ MOCK_METHOD0(UIInitialized, void());
+ MOCK_CONST_METHOD0(UserSelectedTabMirroringForCurrentOrigin, bool());
+ MOCK_METHOD1(RecordCastModeSelection, void(MediaCastMode cast_mode));
+ MOCK_CONST_METHOD0(cast_modes, const std::set<MediaCastMode>&());
+ MOCK_CONST_METHOD0(GetRouteProviderExtensionId, const std::string&());
+ MOCK_METHOD1(OnMediaControllerUIAvailable,
+ void(const MediaRoute::Id& route_id));
+ MOCK_METHOD0(OnMediaControllerUIClosed, void());
+ MOCK_METHOD0(PlayRoute, void());
+ MOCK_METHOD0(PauseRoute, void());
+ MOCK_METHOD1(SeekRoute, void(base::TimeDelta time));
+ MOCK_METHOD1(SetRouteMute, void(bool mute));
+ MOCK_METHOD1(SetRouteVolume, void(float volume));
+ MOCK_CONST_METHOD0(GetMediaRouteController, const MediaRouteController*());
+};
+
+class TestMediaRouterWebUIMessageHandler
+ : public MediaRouterWebUIMessageHandler {
+ public:
+ explicit TestMediaRouterWebUIMessageHandler(MediaRouterUI* media_router_ui)
+ : MediaRouterWebUIMessageHandler(media_router_ui),
+ email_(kUserEmailForTesting),
+ domain_(kUserDomainForTesting) {}
+ ~TestMediaRouterWebUIMessageHandler() override = default;
+
+ AccountInfo GetAccountInfo() override {
+ AccountInfo info = AccountInfo();
+ info.account_id = info.gaia = info.email = email_;
+ info.hosted_domain = domain_;
+ info.full_name = info.given_name = "name";
+ info.locale = "locale";
+ info.picture_url = "picture";
+
+ return info;
+ }
+
+ void SetEmailAndDomain(const std::string& email, const std::string& domain) {
+ email_ = email;
+ domain_ = domain;
+ }
+
+ private:
+ std::string email_;
+ std::string domain_;
+};
+
+class MediaRouterWebUIMessageHandlerTest : public MediaRouterWebUITest {
+ public:
+ MediaRouterWebUIMessageHandlerTest()
+ : web_ui_(base::MakeUnique<content::TestWebUI>()),
+ provider_extension_id_(kProviderExtensionIdForTesting) {}
+ ~MediaRouterWebUIMessageHandlerTest() override {}
+
+ // BrowserWithTestWindowTest:
+ void SetUp() override {
+ BrowserWithTestWindowTest::SetUp();
+ chrome::NewTab(browser());
+ web_ui_->set_web_contents(
+ browser()->tab_strip_model()->GetActiveWebContents());
+ mock_media_router_ui_ = base::MakeUnique<MockMediaRouterUI>(web_ui_.get());
+ handler_ = base::MakeUnique<TestMediaRouterWebUIMessageHandler>(
+ mock_media_router_ui_.get());
+ handler_->SetWebUIForTest(web_ui_.get());
+ }
+
+ void TearDown() override {
+ handler_.reset();
+ mock_media_router_ui_.reset();
+ web_ui_.reset();
+ BrowserWithTestWindowTest::TearDown();
+ }
+
+ const std::string& provider_extension_id() const {
+ return provider_extension_id_;
+ }
+
+ // Gets the call data for the function call made to |web_ui_|. There needs
+ // to be one call made, and its function name must be |function_name|.
+ const base::Value* GetCallData(const std::string& function_name) {
+ CHECK(1u == web_ui_->call_data().size());
+ const content::TestWebUI::CallData& call_data = *web_ui_->call_data()[0];
+ CHECK(function_name == call_data.function_name());
+ return call_data.arg1();
+ }
+
+ // Gets the dictionary passed into a call to the |web_ui_| as the argument.
+ // There needs to be one call made, and its function name must be
+ // |function_name|.
+ const base::DictionaryValue* ExtractDictFromCallArg(
+ const std::string& function_name) {
+ const base::DictionaryValue* dict_value = nullptr;
+ CHECK(GetCallData(function_name)->GetAsDictionary(&dict_value));
+ return dict_value;
+ }
+
+ // Gets the list passed into a call to the |web_ui_| as the argument.
+ // There needs to be one call made, and its function name must be
+ // |function_name|.
+ const base::ListValue* ExtractListFromCallArg(
+ const std::string& function_name) {
+ const base::ListValue* list_value = nullptr;
+ CHECK(GetCallData(function_name)->GetAsList(&list_value));
+ return list_value;
+ }
+
+ // Gets the first element of the list passed in as the argument to a call to
+ // the |web_ui_| as a dictionary. There needs to be one call made, and its
+ // function name must be |function_name|.
+ const base::DictionaryValue* ExtractDictFromListFromCallArg(
+ const std::string& function_name) {
+ const base::ListValue* list_value = nullptr;
+ CHECK(GetCallData(function_name)->GetAsList(&list_value));
+ const base::DictionaryValue* dict_value = nullptr;
+ CHECK(list_value->GetDictionary(0, &dict_value));
+ return dict_value;
+ }
+
+ protected:
+ std::unique_ptr<content::TestWebUI> web_ui_;
+ std::unique_ptr<MockMediaRouterUI> mock_media_router_ui_;
+ std::unique_ptr<TestMediaRouterWebUIMessageHandler> handler_;
+ const std::string provider_extension_id_;
+};
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, UpdateSinks) {
+ MediaSink::Id sink_id("sinkId123");
+ MediaSinkWithCastModes media_sink_with_cast_modes =
+ CreateMediaSinkWithCastMode(sink_id, MediaCastMode::TAB_MIRROR);
+
+ handler_->UpdateSinks({media_sink_with_cast_modes});
+ const base::DictionaryValue* sinks_with_identity_value =
+ ExtractDictFromCallArg("media_router.ui.setSinkListAndIdentity");
+
+ // Email is not displayed if there is no sinks with domain.
+ EXPECT_FALSE(GetBooleanFromDict(sinks_with_identity_value, "showEmail"));
+
+ // Domain is not displayed if there is no sinks with domain.
+ EXPECT_FALSE(GetBooleanFromDict(sinks_with_identity_value, "showDomain"));
+
+ const base::ListValue* sinks_list_value = nullptr;
+ ASSERT_TRUE(sinks_with_identity_value->GetList("sinks", &sinks_list_value));
+ const base::DictionaryValue* sink_value = nullptr;
+ ASSERT_TRUE(sinks_list_value->GetDictionary(0, &sink_value));
+
+ EXPECT_EQ(sink_id, GetStringFromDict(sink_value, "id"));
+ EXPECT_EQ(media_sink_with_cast_modes.sink.name(),
+ GetStringFromDict(sink_value, "name"));
+ EXPECT_EQ(static_cast<int>(MediaCastMode::TAB_MIRROR),
+ GetIntegerFromDict(sink_value, "castModes"));
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, UpdateSinksWithIdentity) {
+ MediaSinkWithCastModes media_sink_with_cast_modes =
+ CreateMediaSinkWithCastMode("sinkId123", MediaCastMode::TAB_MIRROR);
+ media_sink_with_cast_modes.sink.set_domain(kUserDomainForTesting);
+
+ handler_->UpdateSinks({media_sink_with_cast_modes});
+ const base::DictionaryValue* sinks_with_identity_value =
+ ExtractDictFromCallArg("media_router.ui.setSinkListAndIdentity");
+
+ EXPECT_TRUE(GetBooleanFromDict(sinks_with_identity_value, "showEmail"));
+ // Sink domain is not displayed if it matches user domain.
+ EXPECT_FALSE(GetBooleanFromDict(sinks_with_identity_value, "showDomain"));
+ EXPECT_EQ(kUserEmailForTesting,
+ GetStringFromDict(sinks_with_identity_value, "userEmail"));
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest,
+ UpdateSinksWithIdentityAndPseudoSink) {
+ MediaSinkWithCastModes media_sink_with_cast_modes =
+ CreateMediaSinkWithCastMode("pseudo:sinkId1", MediaCastMode::TAB_MIRROR);
+ media_sink_with_cast_modes.sink.set_domain(kUserDomainForTesting);
+
+ handler_->UpdateSinks({media_sink_with_cast_modes});
+ const base::DictionaryValue* sinks_with_identity_value =
+ ExtractDictFromCallArg("media_router.ui.setSinkListAndIdentity");
+
+ EXPECT_FALSE(GetBooleanFromDict(sinks_with_identity_value, "showEmail"));
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, UpdateSinksWithIdentityAndDomain) {
+ MediaSinkWithCastModes media_sink_with_cast_modes =
+ CreateMediaSinkWithCastMode("sinkId123", MediaCastMode::TAB_MIRROR);
+ std::string domain_name("google.com");
+ media_sink_with_cast_modes.sink.set_domain(domain_name);
+
+ handler_->UpdateSinks({media_sink_with_cast_modes});
+ const base::DictionaryValue* sinks_with_identity_value =
+ ExtractDictFromCallArg("media_router.ui.setSinkListAndIdentity");
+
+ // Domain is displayed for sinks with domains that are not the user domain.
+ EXPECT_TRUE(GetBooleanFromDict(sinks_with_identity_value, "showDomain"));
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, UpdateSinksWithNoDomain) {
+ MediaSinkWithCastModes media_sink_with_cast_modes =
+ CreateMediaSinkWithCastMode("sinkId123", MediaCastMode::TAB_MIRROR);
+ std::string user_email("nobody@gmail.com");
+ std::string user_domain("NO_HOSTED_DOMAIN");
+ std::string domain_name("default");
+ media_sink_with_cast_modes.sink.set_domain(domain_name);
+
+ handler_->SetEmailAndDomain(user_email, user_domain);
+ handler_->UpdateSinks({media_sink_with_cast_modes});
+ const base::DictionaryValue* sinks_with_identity_value =
+ ExtractDictFromCallArg("media_router.ui.setSinkListAndIdentity");
+
+ const base::ListValue* sinks_list_value = nullptr;
+ ASSERT_TRUE(sinks_with_identity_value->GetList("sinks", &sinks_list_value));
+ const base::DictionaryValue* sink_value = nullptr;
+ ASSERT_TRUE(sinks_list_value->GetDictionary(0, &sink_value));
+
+ // Domain should not be shown if there were only default sink domains.
+ EXPECT_FALSE(GetBooleanFromDict(sinks_with_identity_value, "showDomain"));
+
+ // Sink domain should be empty if user has no hosted domain.
+ EXPECT_EQ(std::string(), GetStringFromDict(sink_value, "domain"));
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, UpdateSinksWithDefaultDomain) {
+ MediaSinkWithCastModes media_sink_with_cast_modes =
+ CreateMediaSinkWithCastMode("sinkId123", MediaCastMode::TAB_MIRROR);
+ std::string domain_name("default");
+ media_sink_with_cast_modes.sink.set_domain(domain_name);
+
+ handler_->UpdateSinks({media_sink_with_cast_modes});
+ const base::DictionaryValue* sinks_with_identity_value =
+ ExtractDictFromCallArg("media_router.ui.setSinkListAndIdentity");
+
+ const base::ListValue* sinks_list_value = nullptr;
+ ASSERT_TRUE(sinks_with_identity_value->GetList("sinks", &sinks_list_value));
+ const base::DictionaryValue* sink_value = nullptr;
+ ASSERT_TRUE(sinks_list_value->GetDictionary(0, &sink_value));
+
+ // Domain should not be shown if there were only default sink domains.
+ EXPECT_FALSE(GetBooleanFromDict(sinks_with_identity_value, "showDomain"));
+
+ // Sink domain should be updated from 'default' to user domain.
+ EXPECT_EQ(kUserDomainForTesting, GetStringFromDict(sink_value, "domain"));
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, UpdateRoutes) {
+ const MediaRoute route = CreateRoute();
+ std::vector<MediaRoute::Id> joinable_route_ids = {route.media_route_id()};
+ std::unordered_map<MediaRoute::Id, MediaCastMode> current_cast_modes;
+ current_cast_modes.insert(
+ std::make_pair(route.media_route_id(), MediaCastMode::DEFAULT));
+
+ EXPECT_CALL(*mock_media_router_ui_, GetRouteProviderExtensionId()).WillOnce(
+ ReturnRef(provider_extension_id()));
+ handler_->UpdateRoutes({route}, joinable_route_ids, current_cast_modes);
+ const base::DictionaryValue* route_value =
+ ExtractDictFromListFromCallArg("media_router.ui.setRouteList");
+
+ EXPECT_EQ(route.media_route_id(), GetStringFromDict(route_value, "id"));
+ EXPECT_EQ(route.media_sink_id(), GetStringFromDict(route_value, "sinkId"));
+ EXPECT_EQ(route.description(), GetStringFromDict(route_value, "description"));
+ EXPECT_EQ(route.is_local(), GetBooleanFromDict(route_value, "isLocal"));
+ EXPECT_TRUE(GetBooleanFromDict(route_value, "canJoin"));
+ EXPECT_EQ(MediaCastMode::DEFAULT,
+ GetIntegerFromDict(route_value, "currentCastMode"));
+ std::string expected_path = base::StringPrintf("%s://%s/%s",
+ extensions::kExtensionScheme,
+ kProviderExtensionIdForTesting,
+ kControllerPathForTesting);
+ EXPECT_EQ(expected_path,
+ GetStringFromDict(route_value, "customControllerPath"));
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, UpdateRoutesIncognito) {
+ handler_->set_incognito_for_test(true);
+ const MediaRoute route = CreateRoute();
+
+ EXPECT_CALL(*mock_media_router_ui_, GetRouteProviderExtensionId())
+ .WillOnce(ReturnRef(provider_extension_id()));
+ handler_->UpdateRoutes({route}, std::vector<MediaRoute::Id>(),
+ std::unordered_map<MediaRoute::Id, MediaCastMode>());
+ const base::DictionaryValue* route_value =
+ ExtractDictFromListFromCallArg("media_router.ui.setRouteList");
+
+ EXPECT_EQ(route.media_route_id(), GetStringFromDict(route_value, "id"));
+ EXPECT_EQ(route.media_sink_id(), GetStringFromDict(route_value, "sinkId"));
+ EXPECT_EQ(route.description(), GetStringFromDict(route_value, "description"));
+ EXPECT_EQ(route.is_local(), GetBooleanFromDict(route_value, "isLocal"));
+ EXPECT_FALSE(GetBooleanFromDict(route_value, "canJoin"));
+
+ int actual_current_cast_mode = -1;
+ EXPECT_FALSE(
+ route_value->GetInteger("currentCastMode", &actual_current_cast_mode));
+ std::string custom_controller_path;
+ EXPECT_FALSE(
+ route_value->GetString("customControllerPath", &custom_controller_path));
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, SetCastModesList) {
+ CastModeSet cast_modes({MediaCastMode::DEFAULT, MediaCastMode::TAB_MIRROR,
+ MediaCastMode::DESKTOP_MIRROR});
+ handler_->UpdateCastModes(cast_modes, "www.host.com", MediaCastMode::DEFAULT);
+ const base::ListValue* set_cast_mode_list =
+ ExtractListFromCallArg("media_router.ui.setCastModeList");
+
+ const base::DictionaryValue* cast_mode = nullptr;
+ size_t index = 0;
+ for (auto i = cast_modes.begin(); i != cast_modes.end(); i++) {
+ CHECK(set_cast_mode_list->GetDictionary(index++, &cast_mode));
+ EXPECT_EQ(static_cast<int>(*i), GetIntegerFromDict(cast_mode, "type"));
+ EXPECT_EQ(MediaCastModeToDescription(*i, "www.host.com"),
+ GetStringFromDict(cast_mode, "description"));
+ EXPECT_EQ("www.host.com", GetStringFromDict(cast_mode, "host"));
+ EXPECT_EQ(*i == MediaCastMode::DEFAULT,
+ GetBooleanFromDict(cast_mode, "isForced"));
+ }
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, UpdateMediaRouteStatus) {
+ MediaStatus status;
+ status.title = "test title";
+ status.description = "test description";
+ status.can_play_pause = true;
+ status.can_set_volume = true;
+ status.is_paused = true;
+ status.duration = base::TimeDelta::FromSeconds(90);
+ status.current_time = base::TimeDelta::FromSeconds(80);
+ status.volume = 0.9;
+
+ handler_->UpdateMediaRouteStatus(status);
+ const base::DictionaryValue* status_value =
+ ExtractDictFromCallArg("media_router.ui.updateRouteStatus");
+
+ EXPECT_EQ(status.title, GetStringFromDict(status_value, "title"));
+ EXPECT_EQ(status.description, GetStringFromDict(status_value, "description"));
+ EXPECT_EQ(status.can_play_pause,
+ GetBooleanFromDict(status_value, "canPlayPause"));
+ EXPECT_EQ(status.can_mute, GetBooleanFromDict(status_value, "canMute"));
+ EXPECT_EQ(status.can_set_volume,
+ GetBooleanFromDict(status_value, "canSetVolume"));
+ EXPECT_EQ(status.can_seek, GetBooleanFromDict(status_value, "canSeek"));
+ EXPECT_EQ(status.is_paused, GetBooleanFromDict(status_value, "isPaused"));
+ EXPECT_EQ(status.is_muted, GetBooleanFromDict(status_value, "isMuted"));
+ EXPECT_EQ(status.duration.InSeconds(),
+ GetIntegerFromDict(status_value, "duration"));
+ EXPECT_EQ(status.current_time.InSeconds(),
+ GetIntegerFromDict(status_value, "currentTime"));
+ EXPECT_EQ(status.volume, GetDoubleFromDict(status_value, "volume"));
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, OnCreateRouteResponseReceived) {
+ MediaRoute route = CreateRoute();
+ bool incognito = false;
+ route.set_incognito(incognito);
+
+ EXPECT_CALL(*mock_media_router_ui_, GetRouteProviderExtensionId())
+ .WillOnce(ReturnRef(provider_extension_id()));
+ handler_->OnCreateRouteResponseReceived(route.media_sink_id(), &route);
+
+ const content::TestWebUI::CallData& call_data = *web_ui_->call_data()[0];
+ EXPECT_EQ("media_router.ui.onCreateRouteResponseReceived",
+ call_data.function_name());
+ std::string sink_id_value;
+ ASSERT_TRUE(call_data.arg1()->GetAsString(&sink_id_value));
+ EXPECT_EQ(route.media_sink_id(), sink_id_value);
+
+ const base::DictionaryValue* route_value = nullptr;
+ ASSERT_TRUE(call_data.arg2()->GetAsDictionary(&route_value));
+ EXPECT_EQ(route.media_route_id(), GetStringFromDict(route_value, "id"));
+ EXPECT_EQ(route.media_sink_id(), GetStringFromDict(route_value, "sinkId"));
+ EXPECT_EQ(route.description(), GetStringFromDict(route_value, "description"));
+ EXPECT_EQ(route.is_local(), GetBooleanFromDict(route_value, "isLocal"));
+ std::string expected_path = base::StringPrintf(
+ "%s://%s/%s", extensions::kExtensionScheme,
+ kProviderExtensionIdForTesting, kControllerPathForTesting);
+ EXPECT_EQ(expected_path,
+ GetStringFromDict(route_value, "customControllerPath"));
+
+ bool route_for_display = false;
+ ASSERT_TRUE(call_data.arg3()->GetAsBoolean(&route_for_display));
+ EXPECT_TRUE(route_for_display);
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest,
+ OnCreateRouteResponseReceivedIncognito) {
+ handler_->set_incognito_for_test(true);
+ MediaRoute route = CreateRoute();
+ bool incognito = true;
+ route.set_incognito(incognito);
+
+ EXPECT_CALL(*mock_media_router_ui_, GetRouteProviderExtensionId()).WillOnce(
+ ReturnRef(provider_extension_id()));
+ handler_->OnCreateRouteResponseReceived(route.media_sink_id(), &route);
+
+ const content::TestWebUI::CallData& call_data = *web_ui_->call_data()[0];
+ EXPECT_EQ("media_router.ui.onCreateRouteResponseReceived",
+ call_data.function_name());
+ std::string sink_id_value;
+ ASSERT_TRUE(call_data.arg1()->GetAsString(&sink_id_value));
+ EXPECT_EQ(route.media_sink_id(), sink_id_value);
+
+ const base::DictionaryValue* route_value = nullptr;
+ ASSERT_TRUE(call_data.arg2()->GetAsDictionary(&route_value));
+ EXPECT_EQ(route.media_route_id(), GetStringFromDict(route_value, "id"));
+ EXPECT_EQ(route.media_sink_id(), GetStringFromDict(route_value, "sinkId"));
+ EXPECT_EQ(route.description(), GetStringFromDict(route_value, "description"));
+ EXPECT_EQ(route.is_local(), GetBooleanFromDict(route_value, "isLocal"));
+
+ std::string actual_path;
+ EXPECT_FALSE(route_value->GetString("customControllerPath", &actual_path));
+
+ bool route_for_display = false;
+ ASSERT_TRUE(call_data.arg3()->GetAsBoolean(&route_for_display));
+ EXPECT_TRUE(route_for_display);
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, UpdateIssue) {
+ std::string issue_title("An issue");
+ std::string issue_message("This is an issue");
+ IssueInfo::Action default_action = IssueInfo::Action::LEARN_MORE;
+ std::vector<IssueInfo::Action> secondary_actions;
+ secondary_actions.push_back(IssueInfo::Action::DISMISS);
+ MediaRoute::Id route_id("routeId123");
+ IssueInfo info(issue_title, default_action, IssueInfo::Severity::FATAL);
+ info.message = issue_message;
+ info.secondary_actions = secondary_actions;
+ info.route_id = route_id;
+ Issue issue(info);
+ const Issue::Id& issue_id = issue.id();
+
+ handler_->UpdateIssue(issue);
+ const base::DictionaryValue* issue_value =
+ ExtractDictFromCallArg("media_router.ui.setIssue");
+
+ // Initialized to invalid issue id.
+ EXPECT_EQ(issue_id, GetIntegerFromDict(issue_value, "id"));
+ EXPECT_EQ(issue_title, GetStringFromDict(issue_value, "title"));
+ EXPECT_EQ(issue_message, GetStringFromDict(issue_value, "message"));
+
+ // Initialized to invalid action type.
+ EXPECT_EQ(static_cast<int>(default_action),
+ GetIntegerFromDict(issue_value, "defaultActionType"));
+ EXPECT_EQ(static_cast<int>(secondary_actions[0]),
+ GetIntegerFromDict(issue_value, "secondaryActionType"));
+ EXPECT_EQ(route_id, GetStringFromDict(issue_value, "routeId"));
+
+ // The issue is blocking since it is FATAL.
+ EXPECT_TRUE(GetBooleanFromDict(issue_value, "isBlocking"));
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, RecordCastModeSelection) {
+ base::ListValue args;
+ args.AppendInteger(MediaCastMode::DEFAULT);
+ EXPECT_CALL(*mock_media_router_ui_,
+ RecordCastModeSelection(MediaCastMode::DEFAULT))
+ .Times(1);
+ handler_->OnReportSelectedCastMode(&args);
+
+ args.Clear();
+ args.AppendInteger(MediaCastMode::TAB_MIRROR);
+ EXPECT_CALL(*mock_media_router_ui_,
+ RecordCastModeSelection(MediaCastMode::TAB_MIRROR))
+ .Times(1);
+ handler_->OnReportSelectedCastMode(&args);
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, RetrieveCastModeSelection) {
+ base::ListValue args;
+ std::set<MediaCastMode> cast_modes = {MediaCastMode::TAB_MIRROR};
+ EXPECT_CALL(*mock_media_router_ui_, GetRouteProviderExtensionId())
+ .WillRepeatedly(ReturnRef(provider_extension_id()));
+ EXPECT_CALL(*mock_media_router_ui_, cast_modes())
+ .WillRepeatedly(ReturnRef(cast_modes));
+
+ EXPECT_CALL(*mock_media_router_ui_,
+ UserSelectedTabMirroringForCurrentOrigin())
+ .WillOnce(Return(true));
+ handler_->OnRequestInitialData(&args);
+ const content::TestWebUI::CallData& call_data1 = *web_ui_->call_data()[0];
+ ASSERT_EQ("media_router.ui.setInitialData", call_data1.function_name());
+ const base::DictionaryValue* initial_data = nullptr;
+ ASSERT_TRUE(call_data1.arg1()->GetAsDictionary(&initial_data));
+ EXPECT_TRUE(GetBooleanFromDict(initial_data, "useTabMirroring"));
+
+ EXPECT_CALL(*mock_media_router_ui_,
+ UserSelectedTabMirroringForCurrentOrigin())
+ .WillOnce(Return(false));
+ handler_->OnRequestInitialData(&args);
+ const content::TestWebUI::CallData& call_data2 = *web_ui_->call_data()[1];
+ ASSERT_EQ("media_router.ui.setInitialData", call_data2.function_name());
+ ASSERT_TRUE(call_data2.arg1()->GetAsDictionary(&initial_data));
+ EXPECT_FALSE(GetBooleanFromDict(initial_data, "useTabMirroring"));
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, OnRouteDetailsOpenedAndClosed) {
+ const std::string route_id = "routeId123";
+ base::ListValue args_list;
+ base::DictionaryValue* args;
+ args_list.Append(base::MakeUnique<base::DictionaryValue>());
+ args_list.GetDictionary(0, &args);
+ args->SetString("routeId", route_id);
+
+ EXPECT_CALL(*mock_media_router_ui_, OnMediaControllerUIAvailable(route_id));
+ handler_->OnMediaControllerAvailable(&args_list);
+
+ args_list.Clear();
+ EXPECT_CALL(*mock_media_router_ui_, OnMediaControllerUIClosed());
+ handler_->OnMediaControllerClosed(&args_list);
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, OnMediaCommandsReceived) {
+ mojom::MediaControllerPtr mojo_media_controller;
+ mojo::MakeRequest(&mojo_media_controller);
+ MockMediaRouter media_router;
+ scoped_refptr<MockMediaRouteController> controller =
+ new MockMediaRouteController("routeId", std::move(mojo_media_controller),
+ &media_router);
+ EXPECT_CALL(*mock_media_router_ui_, GetMediaRouteController())
+ .WillRepeatedly(Return(controller.get()));
+ MediaStatus status;
+ status.duration = base::TimeDelta::FromSeconds(100);
+ handler_->UpdateMediaRouteStatus(status);
+
+ base::ListValue args_list;
+
+ EXPECT_CALL(*controller, Play());
+ handler_->OnPlayCurrentMedia(&args_list);
+
+ EXPECT_CALL(*controller, Pause());
+ handler_->OnPauseCurrentMedia(&args_list);
+
+ base::DictionaryValue* args;
+ args_list.Append(base::MakeUnique<base::DictionaryValue>());
+ args_list.GetDictionary(0, &args);
+
+ const int time = 50;
+ args->SetInteger("time", time);
+ EXPECT_CALL(*controller, Seek(base::TimeDelta::FromSeconds(time)));
+ handler_->OnSeekCurrentMedia(&args_list);
+
+ args->Clear();
+ args->SetBoolean("mute", true);
+ EXPECT_CALL(*controller, SetMute(true));
+ handler_->OnSetCurrentMediaMute(&args_list);
+
+ const double volume = 0.4;
+ args->Clear();
+ args->SetDouble("volume", volume);
+ EXPECT_CALL(*controller, SetVolume(volume));
+ handler_->OnSetCurrentMediaVolume(&args_list);
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, OnInvalidMediaCommandsReceived) {
+ mojom::MediaControllerPtr mojo_media_controller;
+ mojo::MakeRequest(&mojo_media_controller);
+ MockMediaRouter media_router;
+ scoped_refptr<MockMediaRouteController> controller =
+ new MockMediaRouteController("routeId", std::move(mojo_media_controller),
+ &media_router);
+ EXPECT_CALL(*mock_media_router_ui_, GetMediaRouteController())
+ .WillRepeatedly(Return(controller.get()));
+
+ MediaStatus status;
+ status.duration = base::TimeDelta::FromSeconds(100);
+ handler_->UpdateMediaRouteStatus(status);
+
+ EXPECT_CALL(*controller, Seek(_)).Times(0);
+ EXPECT_CALL(*controller, SetVolume(_)).Times(0);
+
+ base::ListValue args_list;
+
+ base::DictionaryValue* args;
+ args_list.Append(base::MakeUnique<base::DictionaryValue>());
+ args_list.GetDictionary(0, &args);
+
+ // Seek positions greater than the duration or negative should be ignored.
+ args->SetInteger("time", 101);
+ handler_->OnSeekCurrentMedia(&args_list);
+ args->SetInteger("time", -10);
+ handler_->OnSeekCurrentMedia(&args_list);
+
+ args->Clear();
+
+ // Volumes outside of the [0, 1] range should be ignored.
+ args->SetDouble("volume", 1.5);
+ handler_->OnSetCurrentMediaVolume(&args_list);
+ args->SetDouble("volume", 1.5);
+ handler_->OnSetCurrentMediaVolume(&args_list);
+}
+
+TEST_F(MediaRouterWebUIMessageHandlerTest, OnRouteControllerInvalidated) {
+ handler_->OnRouteControllerInvalidated();
+ EXPECT_EQ(1u, web_ui_->call_data().size());
+ const content::TestWebUI::CallData& call_data = *web_ui_->call_data()[0];
+ EXPECT_EQ("media_router.ui.onRouteControllerInvalidated",
+ call_data.function_name());
+}
+
+} // namespace media_router
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_sink_with_cast_modes.cc b/chromium/chrome/browser/ui/webui/media_router/media_sink_with_cast_modes.cc
new file mode 100644
index 00000000000..0c26af3430b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_sink_with_cast_modes.cc
@@ -0,0 +1,23 @@
+// 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.
+
+#include "chrome/browser/ui/webui/media_router/media_sink_with_cast_modes.h"
+
+namespace media_router {
+
+MediaSinkWithCastModes::MediaSinkWithCastModes(const MediaSink& sink)
+ : sink(sink) {
+}
+
+MediaSinkWithCastModes::MediaSinkWithCastModes(
+ const MediaSinkWithCastModes& other) = default;
+
+MediaSinkWithCastModes::~MediaSinkWithCastModes() {
+}
+
+bool MediaSinkWithCastModes::Equals(const MediaSinkWithCastModes& other) const {
+ return sink.Equals(other.sink) && cast_modes == other.cast_modes;
+}
+
+} // namespace media_router
diff --git a/chromium/chrome/browser/ui/webui/media_router/media_sink_with_cast_modes.h b/chromium/chrome/browser/ui/webui/media_router/media_sink_with_cast_modes.h
new file mode 100644
index 00000000000..7006f68e34a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/media_sink_with_cast_modes.h
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_SINK_WITH_CAST_MODES_H_
+#define CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_SINK_WITH_CAST_MODES_H_
+
+#include <set>
+
+#include "chrome/browser/ui/webui/media_router/media_cast_mode.h"
+#include "chrome/common/media_router/media_sink.h"
+
+namespace media_router {
+
+// Contains information on a MediaSink and the set of cast modes it is
+// compatible with. This should be interpreted under the context of a
+// QueryResultManager which contains a mapping from MediaCastMode to
+// MediaSource.
+struct MediaSinkWithCastModes {
+ explicit MediaSinkWithCastModes(const MediaSink& sink);
+ MediaSinkWithCastModes(const MediaSinkWithCastModes& other);
+ ~MediaSinkWithCastModes();
+
+ MediaSink sink;
+ CastModeSet cast_modes;
+
+ bool Equals(const MediaSinkWithCastModes& other) const;
+};
+
+} // namespace media_router
+
+#endif // CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_MEDIA_SINK_WITH_CAST_MODES_H_
diff --git a/chromium/chrome/browser/ui/webui/media_router/query_result_manager.cc b/chromium/chrome/browser/ui/webui/media_router/query_result_manager.cc
new file mode 100644
index 00000000000..2bd508abbc4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/query_result_manager.cc
@@ -0,0 +1,239 @@
+// 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.
+
+#include "chrome/browser/ui/webui/media_router/query_result_manager.h"
+
+#include <utility>
+
+#include "base/containers/hash_tables.h"
+#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
+#include "chrome/browser/media/router/media_router.h"
+#include "chrome/browser/media/router/media_sinks_observer.h"
+#include "content/public/browser/browser_thread.h"
+#include "url/origin.h"
+
+namespace media_router {
+
+// MediaSinkObserver that propagates results back to |result_manager|.
+// An instance of this class is associated with each registered MediaSource.
+class QueryResultManager::MediaSourceMediaSinksObserver
+ : public MediaSinksObserver {
+ public:
+ MediaSourceMediaSinksObserver(MediaCastMode cast_mode,
+ const MediaSource& source,
+ const url::Origin& origin,
+ MediaRouter* router,
+ QueryResultManager* result_manager)
+ : MediaSinksObserver(router, source, origin),
+ cast_mode_(cast_mode),
+ source_(source),
+ result_manager_(result_manager) {
+ DCHECK(result_manager);
+ }
+
+ ~MediaSourceMediaSinksObserver() override {}
+
+ // MediaSinksObserver
+ void OnSinksReceived(const std::vector<MediaSink>& result) override {
+ latest_sink_ids_.clear();
+ for (const MediaSink& sink : result) {
+ latest_sink_ids_.push_back(sink.id());
+ }
+ result_manager_->SetSinksCompatibleWithSource(cast_mode_, source_, result);
+ result_manager_->NotifyOnResultsUpdated();
+ }
+
+ // Returns the most recent sink IDs that were passed to |OnSinksReceived|.
+ void GetLatestSinkIds(std::vector<MediaSink::Id>* sink_ids) const {
+ DCHECK(sink_ids);
+ *sink_ids = latest_sink_ids_;
+ }
+
+ MediaCastMode cast_mode() const { return cast_mode_; }
+
+ private:
+ const MediaCastMode cast_mode_;
+ const MediaSource source_;
+ std::vector<MediaSink::Id> latest_sink_ids_;
+ QueryResultManager* const result_manager_;
+};
+
+QueryResultManager::QueryResultManager(MediaRouter* router) : router_(router) {
+ DCHECK(router_);
+}
+
+QueryResultManager::~QueryResultManager() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+}
+
+void QueryResultManager::AddObserver(Observer* observer) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(observer);
+ observers_.AddObserver(observer);
+}
+
+void QueryResultManager::RemoveObserver(Observer* observer) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(observer);
+ observers_.RemoveObserver(observer);
+}
+
+void QueryResultManager::SetSourcesForCastMode(
+ MediaCastMode cast_mode,
+ const std::vector<MediaSource>& sources,
+ const url::Origin& origin) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ if (sources.empty()) {
+ LOG(WARNING) << "SetSourcesForCastMode called with empty sources for "
+ << cast_mode;
+ return;
+ }
+ if (!AreSourcesValidForCastMode(cast_mode, sources)) {
+ LOG(WARNING) << "SetSourcesForCastMode called with invalid sources for "
+ << cast_mode;
+ return;
+ }
+
+ RemoveOldSourcesForCastMode(cast_mode, sources);
+ AddObserversForCastMode(cast_mode, sources, origin);
+ cast_mode_sources_[cast_mode] = sources;
+ NotifyOnResultsUpdated();
+}
+
+void QueryResultManager::RemoveSourcesForCastMode(MediaCastMode cast_mode) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ RemoveOldSourcesForCastMode(cast_mode, std::vector<MediaSource>());
+ cast_mode_sources_.erase(cast_mode);
+ NotifyOnResultsUpdated();
+}
+
+CastModeSet QueryResultManager::GetSupportedCastModes() const {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ CastModeSet modes;
+ for (const auto& cast_mode_pair : cast_mode_sources_)
+ modes.insert(cast_mode_pair.first);
+
+ return modes;
+}
+
+std::unique_ptr<MediaSource> QueryResultManager::GetSourceForCastModeAndSink(
+ MediaCastMode cast_mode,
+ MediaSink::Id sink_id) const {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ for (const auto& sink_pair : all_sinks_) {
+ if (sink_pair.first.id() == sink_id) {
+ return GetHighestPrioritySourceForCastModeAndSink(cast_mode,
+ sink_pair.second);
+ }
+ }
+ return std::unique_ptr<MediaSource>();
+}
+
+std::vector<MediaSource> QueryResultManager::GetSourcesForCastMode(
+ MediaCastMode cast_mode) const {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ const auto& cast_mode_it = cast_mode_sources_.find(cast_mode);
+ return cast_mode_it == cast_mode_sources_.end() ? std::vector<MediaSource>()
+ : cast_mode_it->second;
+}
+
+void QueryResultManager::RemoveOldSourcesForCastMode(
+ MediaCastMode cast_mode,
+ const std::vector<MediaSource>& new_sources) {
+ const auto& cast_mode_it = cast_mode_sources_.find(cast_mode);
+ if (cast_mode_it == cast_mode_sources_.end())
+ return;
+
+ for (const MediaSource& source : cast_mode_it->second) {
+ if (!base::ContainsValue(new_sources, source)) {
+ sinks_observers_.erase(source);
+ SetSinksCompatibleWithSource(cast_mode, source, std::vector<MediaSink>());
+ }
+ }
+}
+
+void QueryResultManager::AddObserversForCastMode(
+ MediaCastMode cast_mode,
+ const std::vector<MediaSource>& sources,
+ const url::Origin& origin) {
+ for (const MediaSource& source : sources) {
+ if (!base::ContainsKey(sinks_observers_, source)) {
+ std::unique_ptr<MediaSourceMediaSinksObserver> observer(
+ new MediaSourceMediaSinksObserver(cast_mode, source, origin, router_,
+ this));
+ observer->Init();
+ sinks_observers_[source] = std::move(observer);
+ }
+ }
+}
+
+void QueryResultManager::SetSinksCompatibleWithSource(
+ MediaCastMode cast_mode,
+ const MediaSource& source,
+ const std::vector<MediaSink>& new_sinks) {
+ base::hash_set<MediaSink::Id> new_sink_ids;
+ for (const MediaSink& sink : new_sinks)
+ new_sink_ids.insert(sink.id());
+
+ // (1) Iterate through current sink set, remove cast mode from those that
+ // do not appear in latest result.
+ for (auto it = all_sinks_.begin(); it != all_sinks_.end(); /*no-op*/) {
+ const MediaSink& sink = it->first;
+ CastModesWithMediaSources& sources_for_sink = it->second;
+ if (!base::ContainsKey(new_sink_ids, sink.id()))
+ sources_for_sink.RemoveSource(cast_mode, source);
+ if (sources_for_sink.IsEmpty())
+ all_sinks_.erase(it++);
+ else
+ ++it;
+ }
+
+ // (2) Add / update sinks with latest result.
+ for (const MediaSink& sink : new_sinks)
+ all_sinks_[sink].AddSource(cast_mode, source);
+}
+
+std::unique_ptr<MediaSource>
+QueryResultManager::GetHighestPrioritySourceForCastModeAndSink(
+ MediaCastMode cast_mode,
+ const CastModesWithMediaSources& sources_for_sink) const {
+ const auto& cast_mode_it = cast_mode_sources_.find(cast_mode);
+ if (cast_mode_it == cast_mode_sources_.end())
+ return std::unique_ptr<MediaSource>();
+
+ for (const MediaSource& source : cast_mode_it->second) {
+ if (sources_for_sink.HasSource(cast_mode, source))
+ return base::MakeUnique<MediaSource>(source.id());
+ }
+ return std::unique_ptr<MediaSource>();
+}
+
+bool QueryResultManager::AreSourcesValidForCastMode(
+ MediaCastMode cast_mode,
+ const std::vector<MediaSource>& sources) const {
+ const auto& cast_mode_it = cast_mode_sources_.find(cast_mode);
+ bool has_cast_mode = cast_mode_it != cast_mode_sources_.end();
+ // If a source has already been registered, then it must be associated with
+ // |cast_mode|.
+ return std::find_if(
+ sources.begin(), sources.end(), [=](const MediaSource& source) {
+ return base::ContainsKey(sinks_observers_, source) &&
+ (!has_cast_mode ||
+ !base::ContainsValue(cast_mode_it->second, source));
+ }) == sources.end();
+}
+
+void QueryResultManager::NotifyOnResultsUpdated() {
+ std::vector<MediaSinkWithCastModes> sinks;
+ for (const auto& sink_pair : all_sinks_) {
+ MediaSinkWithCastModes sink_with_cast_modes(sink_pair.first);
+ sink_with_cast_modes.cast_modes = sink_pair.second.GetCastModes();
+ sinks.push_back(sink_with_cast_modes);
+ }
+ for (QueryResultManager::Observer& observer : observers_)
+ observer.OnResultsUpdated(sinks);
+}
+
+} // namespace media_router
diff --git a/chromium/chrome/browser/ui/webui/media_router/query_result_manager.h b/chromium/chrome/browser/ui/webui/media_router/query_result_manager.h
new file mode 100644
index 00000000000..a1ab37e98ad
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/query_result_manager.h
@@ -0,0 +1,181 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_QUERY_RESULT_MANAGER_H_
+#define CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_QUERY_RESULT_MANAGER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <unordered_set>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "chrome/browser/media/router/media_routes_observer.h"
+#include "chrome/browser/ui/webui/media_router/cast_modes_with_media_sources.h"
+#include "chrome/browser/ui/webui/media_router/media_cast_mode.h"
+#include "chrome/browser/ui/webui/media_router/media_sink_with_cast_modes.h"
+#include "chrome/common/media_router/media_sink.h"
+#include "chrome/common/media_router/media_source.h"
+
+namespace url {
+class Origin;
+} // namespace url
+
+namespace media_router {
+
+class MediaRouter;
+class MediaSinksObserver;
+
+// The Media Router dialog allows the user to initiate casting using one of
+// several actions (each represented by a cast mode). Each cast mode is
+// associated with a vector of media sources. This class allows the dialog to
+// receive lists of MediaSinks compatible with the cast modes available through
+// the dialog.
+//
+// Typical use:
+//
+// url::Origin origin{GURL("https://origin.com")};
+// QueryResultManager::Observer* observer = ...;
+// QueryResultManager result_manager(router);
+// result_manager.AddObserver(observer);
+// result_manager.SetSourcesForCastMode(MediaCastMode::DEFAULT,
+// {MediaSourceForPresentationUrl("http://google.com")}, origin);
+// result_manager.SetSourcesForCastMode(MediaCastMode::TAB_MIRROR,
+// {MediaSourceForTab(123)}, origin);
+// ...
+// [Updates will be received by observer via OnResultsUpdated()]
+// ...
+// [When info on MediaSource is needed, i.e. when requesting route for a mode]
+// CastModeSet cast_modes = result_manager.GetSupportedCastModes();
+// [Logic to select a MediaCastMode from the set]
+// std::unique_ptr<MediaSource> source =
+// result_manager.GetSourceForCastModeAndSink(
+// MediaCastMode::TAB_MIRROR, sink_of_interest);
+// if (source) {
+// ...
+// }
+//
+// Not thread-safe. Must be used on the UI thread.
+class QueryResultManager {
+ public:
+ class Observer {
+ public:
+ virtual ~Observer() {}
+
+ // Updated results have been received.
+ // |sinks|: List of sinks and the cast modes they are compatible with.
+ virtual void OnResultsUpdated(
+ const std::vector<MediaSinkWithCastModes>& sinks) = 0;
+ };
+
+ explicit QueryResultManager(MediaRouter* media_router);
+ ~QueryResultManager();
+
+ // Adds/removes an observer that is notified with query results.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ // Requests a list of MediaSinks compatible with |sources| for |cast_mode|
+ // from |origin|. |sources| should be in descending order of priority.
+ // Results are sent to all observers registered with AddObserver().
+ //
+ // Starts new queries in the Media Router for sources that we have no existing
+ // queries for, and stops queries for sources no longer associated with any
+ // cast mode.
+ //
+ // If |sources| is empty or contains a source that has already been registered
+ // with another cast mode, no new queries are begun.
+ void SetSourcesForCastMode(MediaCastMode cast_mode,
+ const std::vector<MediaSource>& sources,
+ const url::Origin& origin);
+
+ // Stops notifying observers for |cast_mode|, and removes it from the set of
+ // supported cast modes.
+ void RemoveSourcesForCastMode(MediaCastMode cast_mode);
+
+ // Gets the set of cast modes that are being actively queried.
+ CastModeSet GetSupportedCastModes() const;
+
+ // Gets the highest-priority source for the cast mode that is supported by
+ // the sink. Returns an empty unique_ptr if there isn't any.
+ std::unique_ptr<MediaSource> GetSourceForCastModeAndSink(
+ MediaCastMode cast_mode,
+ MediaSink::Id sink_id) const;
+
+ // Returns all the sources registered for |cast_mode|. Returns an empty
+ // vector if there is none.
+ std::vector<MediaSource> GetSourcesForCastMode(MediaCastMode cast_mode) const;
+
+ private:
+ class MediaSourceMediaSinksObserver;
+
+ FRIEND_TEST_ALL_PREFIXES(QueryResultManagerTest, Observers);
+ FRIEND_TEST_ALL_PREFIXES(QueryResultManagerTest, StartRoutesDiscovery);
+ FRIEND_TEST_ALL_PREFIXES(QueryResultManagerTest, MultipleQueries);
+ FRIEND_TEST_ALL_PREFIXES(QueryResultManagerTest, MultipleUrls);
+ FRIEND_TEST_ALL_PREFIXES(QueryResultManagerTest, AddInvalidSource);
+
+ // Stops and destroys the MediaSinksObservers for media sources that
+ // |cast_mode| used to support, but isn't in |new_sources|, and disassociates
+ // them from sinks.
+ void RemoveOldSourcesForCastMode(MediaCastMode cast_mode,
+ const std::vector<MediaSource>& new_sources);
+
+ // Creates observers and starts queries for each source in |sources| that
+ // doesn't already have an associated observer.
+ void AddObserversForCastMode(MediaCastMode cast_mode,
+ const std::vector<MediaSource>& sources,
+ const url::Origin& origin);
+
+ // Modifies the set of sinks compatible with |cast_mode| and |source|
+ // to |new_sinks|.
+ void SetSinksCompatibleWithSource(MediaCastMode cast_mode,
+ const MediaSource& source,
+ const std::vector<MediaSink>& new_sinks);
+
+ // Returns the highest-priority source for |cast_mode| contained in
+ // |sources_for_sink|. Returns an empty unique_ptr if none exists.
+ std::unique_ptr<MediaSource> GetHighestPrioritySourceForCastModeAndSink(
+ MediaCastMode cast_mode,
+ const CastModesWithMediaSources& sources_for_sink) const;
+
+ // Returns true if every source in |sources| is either not registered yet, or
+ // associated with |cast_mode|. This check prevents a source from being
+ // associated with two cast modes.
+ bool AreSourcesValidForCastMode(
+ MediaCastMode cast_mode,
+ const std::vector<MediaSource>& sources) const;
+
+ // Notifies observers that results have been updated.
+ void NotifyOnResultsUpdated();
+
+ // MediaSinksObservers that listen for compatible MediaSink updates.
+ // Each observer is associated with a MediaSource. Results received by
+ // observers are propagated back to this class.
+ std::unordered_map<MediaSource,
+ std::unique_ptr<MediaSinksObserver>,
+ MediaSource::Hash>
+ sinks_observers_;
+
+ // Holds registrations of MediaSources for cast modes.
+ std::map<MediaCastMode, std::vector<MediaSource>> cast_mode_sources_;
+
+ // Holds all known sinks along with the cast modes and sources they support.
+ std::map<MediaSink, CastModesWithMediaSources, MediaSink::Compare> all_sinks_;
+
+ // Registered observers.
+ base::ObserverList<Observer> observers_;
+
+ // Not owned by this object.
+ MediaRouter* const router_;
+
+ DISALLOW_COPY_AND_ASSIGN(QueryResultManager);
+};
+
+} // namespace media_router
+
+#endif // CHROME_BROWSER_UI_WEBUI_MEDIA_ROUTER_QUERY_RESULT_MANAGER_H_
diff --git a/chromium/chrome/browser/ui/webui/media_router/query_result_manager_unittest.cc b/chromium/chrome/browser/ui/webui/media_router/query_result_manager_unittest.cc
new file mode 100644
index 00000000000..571a83b88d3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/media_router/query_result_manager_unittest.cc
@@ -0,0 +1,408 @@
+// 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.
+
+#include "chrome/browser/ui/webui/media_router/query_result_manager.h"
+
+#include "base/bind.h"
+#include "base/containers/hash_tables.h"
+#include "base/json/json_writer.h"
+#include "base/macros.h"
+#include "chrome/browser/media/router/media_sinks_observer.h"
+#include "chrome/browser/media/router/mock_media_router.h"
+#include "chrome/common/media_router/media_source_helper.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using testing::Eq;
+using testing::IsEmpty;
+using testing::Eq;
+using testing::Mock;
+using testing::Return;
+using testing::_;
+
+namespace media_router {
+
+namespace {
+
+const char kOrigin[] = "https://origin.com";
+
+class MockObserver : public QueryResultManager::Observer {
+ public:
+ MOCK_METHOD1(OnResultsUpdated, void(
+ const std::vector<MediaSinkWithCastModes>& sinks));
+};
+
+} // namespace
+
+class QueryResultManagerTest : public ::testing::Test {
+ public:
+ QueryResultManagerTest()
+ : mock_router_(), query_result_manager_(&mock_router_) {
+ }
+
+ void DiscoverSinks(MediaCastMode cast_mode, const MediaSource& source) {
+ EXPECT_CALL(mock_router_, RegisterMediaSinksObserver(_))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_observer_, OnResultsUpdated(_)).Times(1);
+ query_result_manager_.SetSourcesForCastMode(cast_mode, {source},
+ url::Origin(GURL(kOrigin)));
+ }
+
+ bool IsDefaultSourceForSink(const MediaSource* source,
+ const MediaSink& sink) {
+ return IsPreferredSourceForSink(MediaCastMode::DEFAULT, source, sink);
+ }
+
+ bool IsTabSourceForSink(const MediaSource* source, const MediaSink& sink) {
+ return IsPreferredSourceForSink(MediaCastMode::TAB_MIRROR, source, sink);
+ }
+
+ bool IsPreferredSourceForSink(MediaCastMode cast_mode,
+ const MediaSource* source,
+ const MediaSink& sink) {
+ std::unique_ptr<MediaSource> default_source =
+ query_result_manager_.GetSourceForCastModeAndSink(cast_mode, sink.id());
+ return (!(default_source || source)) ||
+ (default_source && source && *default_source.get() == *source);
+ }
+
+ content::TestBrowserThreadBundle thread_bundle_;
+ MockMediaRouter mock_router_;
+ QueryResultManager query_result_manager_;
+ MockObserver mock_observer_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QueryResultManagerTest);
+};
+
+MATCHER_P(VectorEquals, expected, "") {
+ if (expected.size() != arg.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < expected.size(); ++i) {
+ if (!expected[i].Equals(arg[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+TEST_F(QueryResultManagerTest, Observers) {
+ MockObserver ob1;
+ MockObserver ob2;
+ query_result_manager_.AddObserver(&ob1);
+ query_result_manager_.AddObserver(&ob2);
+
+ EXPECT_CALL(ob1, OnResultsUpdated(_)).Times(1);
+ EXPECT_CALL(ob2, OnResultsUpdated(_)).Times(1);
+ query_result_manager_.NotifyOnResultsUpdated();
+
+ query_result_manager_.RemoveObserver(&ob2);
+ EXPECT_CALL(ob1, OnResultsUpdated(_)).Times(1);
+ query_result_manager_.NotifyOnResultsUpdated();
+
+ query_result_manager_.RemoveObserver(&ob1);
+ query_result_manager_.NotifyOnResultsUpdated();
+}
+
+TEST_F(QueryResultManagerTest, StartStopSinksQuery) {
+ CastModeSet cast_modes = query_result_manager_.GetSupportedCastModes();
+ EXPECT_TRUE(cast_modes.empty());
+ std::vector<MediaSource> actual_sources =
+ query_result_manager_.GetSourcesForCastMode(MediaCastMode::DEFAULT);
+ EXPECT_EQ(0u, actual_sources.size());
+
+ MediaSource source(MediaSourceForPresentationUrl(GURL("http://foo.com")));
+ EXPECT_CALL(mock_router_, RegisterMediaSinksObserver(_))
+ .WillOnce(Return(true));
+ query_result_manager_.SetSourcesForCastMode(MediaCastMode::DEFAULT, {source},
+ url::Origin(GURL(kOrigin)));
+
+ cast_modes = query_result_manager_.GetSupportedCastModes();
+ EXPECT_EQ(1u, cast_modes.size());
+ EXPECT_TRUE(base::ContainsKey(cast_modes, MediaCastMode::DEFAULT));
+ actual_sources =
+ query_result_manager_.GetSourcesForCastMode(MediaCastMode::DEFAULT);
+ EXPECT_EQ(1u, actual_sources.size());
+ EXPECT_EQ(source, actual_sources[0]);
+
+ // Register a different set of sources for the same cast mode.
+ MediaSource another_source(
+ MediaSourceForPresentationUrl(GURL("http://bar.com")));
+ EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_)).Times(1);
+ EXPECT_CALL(mock_router_, RegisterMediaSinksObserver(_))
+ .WillOnce(Return(true));
+ query_result_manager_.SetSourcesForCastMode(
+ MediaCastMode::DEFAULT, {another_source}, url::Origin(GURL(kOrigin)));
+
+ cast_modes = query_result_manager_.GetSupportedCastModes();
+ EXPECT_EQ(1u, cast_modes.size());
+ EXPECT_TRUE(base::ContainsKey(cast_modes, MediaCastMode::DEFAULT));
+ actual_sources =
+ query_result_manager_.GetSourcesForCastMode(MediaCastMode::DEFAULT);
+ EXPECT_EQ(1u, actual_sources.size());
+ EXPECT_EQ(another_source, actual_sources[0]);
+
+ EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_)).Times(1);
+ query_result_manager_.RemoveSourcesForCastMode(MediaCastMode::DEFAULT);
+
+ cast_modes = query_result_manager_.GetSupportedCastModes();
+ EXPECT_TRUE(cast_modes.empty());
+ actual_sources =
+ query_result_manager_.GetSourcesForCastMode(MediaCastMode::DEFAULT);
+ EXPECT_EQ(0u, actual_sources.size());
+}
+
+TEST_F(QueryResultManagerTest, MultipleQueries) {
+ MediaSink sink1("sinkId1", "Sink 1", MediaSink::IconType::CAST);
+ MediaSink sink2("sinkId2", "Sink 2", MediaSink::IconType::CAST);
+ MediaSink sink3("sinkId3", "Sink 3", MediaSink::IconType::CAST);
+ MediaSink sink4("sinkId4", "Sink 4", MediaSink::IconType::CAST);
+ MediaSink sink5("sinkId5", "Sink 5", MediaSink::IconType::CAST);
+ MediaSource default_source1 =
+ MediaSourceForPresentationUrl(GURL("http://bar.com"));
+ MediaSource default_source2 =
+ MediaSourceForPresentationUrl(GURL("http://baz.com"));
+ MediaSource tab_source = MediaSourceForTab(123);
+
+ query_result_manager_.AddObserver(&mock_observer_);
+ DiscoverSinks(MediaCastMode::DEFAULT, default_source1);
+ DiscoverSinks(MediaCastMode::TAB_MIRROR, tab_source);
+
+ // Scenario (results in this order):
+ // Action: DEFAULT -> [1, 2, 3]
+ // Expected result:
+ // Sinks: [1 -> {DEFAULT}, 2 -> {DEFAULT}, 3 -> {DEFAULT}]
+ std::vector<MediaSinkWithCastModes> expected_sinks;
+ expected_sinks.push_back(MediaSinkWithCastModes(sink1));
+ expected_sinks.back().cast_modes.insert(MediaCastMode::DEFAULT);
+ expected_sinks.push_back(MediaSinkWithCastModes(sink2));
+ expected_sinks.back().cast_modes.insert(MediaCastMode::DEFAULT);
+ expected_sinks.push_back(MediaSinkWithCastModes(sink3));
+ expected_sinks.back().cast_modes.insert(MediaCastMode::DEFAULT);
+
+ const auto& sinks_observers = query_result_manager_.sinks_observers_;
+ auto sinks_observer_it = sinks_observers.find(default_source1);
+ ASSERT_TRUE(sinks_observer_it != sinks_observers.end());
+ ASSERT_TRUE(sinks_observer_it->second.get());
+
+ std::vector<MediaSink> sinks_query_result;
+ sinks_query_result.push_back(sink1);
+ sinks_query_result.push_back(sink2);
+ sinks_query_result.push_back(sink3);
+ EXPECT_CALL(mock_observer_,
+ OnResultsUpdated(VectorEquals(expected_sinks))).Times(1);
+ sinks_observer_it->second->OnSinksUpdated(sinks_query_result,
+ std::vector<url::Origin>());
+
+ // Action: TAB_MIRROR -> [2, 3, 4]
+ // Expected result:
+ // Sinks: [1 -> {DEFAULT}, 2 -> {DEFAULT, TAB_MIRROR},
+ // 3 -> {DEFAULT, TAB_MIRROR}, 4 -> {TAB_MIRROR}]
+ expected_sinks.clear();
+ expected_sinks.push_back(MediaSinkWithCastModes(sink1));
+ expected_sinks.back().cast_modes.insert(MediaCastMode::DEFAULT);
+ expected_sinks.push_back(MediaSinkWithCastModes(sink2));
+ expected_sinks.back().cast_modes.insert(MediaCastMode::DEFAULT);
+ expected_sinks.back().cast_modes.insert(MediaCastMode::TAB_MIRROR);
+ expected_sinks.push_back(MediaSinkWithCastModes(sink3));
+ expected_sinks.back().cast_modes.insert(MediaCastMode::DEFAULT);
+ expected_sinks.back().cast_modes.insert(MediaCastMode::TAB_MIRROR);
+ expected_sinks.push_back(MediaSinkWithCastModes(sink4));
+ expected_sinks.back().cast_modes.insert(MediaCastMode::TAB_MIRROR);
+
+ sinks_query_result.clear();
+ sinks_query_result.push_back(sink2);
+ sinks_query_result.push_back(sink3);
+ sinks_query_result.push_back(sink4);
+
+ sinks_observer_it = sinks_observers.find(tab_source);
+ ASSERT_TRUE(sinks_observer_it != sinks_observers.end());
+ ASSERT_TRUE(sinks_observer_it->second.get());
+ EXPECT_CALL(mock_observer_,
+ OnResultsUpdated(VectorEquals(expected_sinks))).Times(1);
+ sinks_observer_it->second->OnSinksUpdated(sinks_query_result,
+ {url::Origin(GURL(kOrigin))});
+
+ // Action: Update default presentation URL
+ // Expected result:
+ // Sinks: [2 -> {TAB_MIRROR}, 3 -> {TAB_MIRROR}, 4 -> {TAB_MIRROR}]
+ expected_sinks.clear();
+ expected_sinks.push_back(MediaSinkWithCastModes(sink2));
+ expected_sinks.back().cast_modes.insert(MediaCastMode::TAB_MIRROR);
+ expected_sinks.push_back(MediaSinkWithCastModes(sink3));
+ expected_sinks.back().cast_modes.insert(MediaCastMode::TAB_MIRROR);
+ expected_sinks.push_back(MediaSinkWithCastModes(sink4));
+ expected_sinks.back().cast_modes.insert(MediaCastMode::TAB_MIRROR);
+
+ // The observer for the old source will be unregistered.
+ EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_)).Times(1);
+ // The observer for the new source will be registered.
+ EXPECT_CALL(mock_router_, RegisterMediaSinksObserver(_))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_observer_,
+ OnResultsUpdated(VectorEquals(expected_sinks))).Times(1);
+ query_result_manager_.SetSourcesForCastMode(
+ MediaCastMode::DEFAULT, {default_source2}, url::Origin(GURL(kOrigin)));
+
+ // Action: DEFAULT -> [1], origins don't match
+ // Expected result: [2 -> {TAB_MIRROR}, 3 -> {TAB_MIRROR}, 4 -> {TAB_MIRROR}]
+ // (No change)
+ sinks_query_result.clear();
+ sinks_query_result.push_back(sink1);
+ sinks_observer_it = sinks_observers.find(default_source2);
+ ASSERT_TRUE(sinks_observer_it != sinks_observers.end());
+ ASSERT_TRUE(sinks_observer_it->second.get());
+ EXPECT_CALL(mock_observer_, OnResultsUpdated(VectorEquals(expected_sinks)))
+ .Times(1);
+ sinks_observer_it->second->OnSinksUpdated(
+ sinks_query_result, {url::Origin(GURL("https://differentOrigin.com"))});
+
+ // Action: Remove TAB_MIRROR observer
+ // Expected result:
+ // Sinks: []
+ expected_sinks.clear();
+ EXPECT_CALL(mock_observer_,
+ OnResultsUpdated(VectorEquals(expected_sinks))).Times(1);
+ EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_)).Times(1);
+ query_result_manager_.RemoveSourcesForCastMode(MediaCastMode::TAB_MIRROR);
+
+ // Remaining observers: DEFAULT observer, which will be removed on destruction
+ EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_)).Times(1);
+}
+
+TEST_F(QueryResultManagerTest, MultipleUrls) {
+ const MediaSink sink1("sinkId1", "Sink 1", MediaSink::IconType::CAST);
+ const MediaSink sink2("sinkId2", "Sink 2", MediaSink::IconType::CAST);
+ const MediaSink sink3("sinkId3", "Sink 3", MediaSink::IconType::CAST);
+ const MediaSink sink4("sinkId4", "Sink 4", MediaSink::IconType::CAST);
+ const MediaSource source_a(
+ MediaSourceForPresentationUrl(GURL("http://urlA.com")));
+ const MediaSource source_b(
+ MediaSourceForPresentationUrl(GURL("http://urlB.com")));
+ const MediaSource source_c(
+ MediaSourceForPresentationUrl(GURL("http://urlC.com")));
+ const MediaSource source_tab(MediaSourceForTab(1));
+ // The sources are in decreasing order of priority.
+ const std::vector<MediaSource> default_sources = {source_a, source_b,
+ source_c};
+ const std::vector<MediaSource> tab_sources = {source_tab};
+ const auto& sinks_observers = query_result_manager_.sinks_observers_;
+
+ // There should be one MediaSinksObserver per source.
+ EXPECT_CALL(mock_router_, RegisterMediaSinksObserver(_))
+ .Times(4)
+ .WillRepeatedly(Return(true));
+ query_result_manager_.SetSourcesForCastMode(
+ MediaCastMode::DEFAULT, default_sources, url::Origin(GURL(kOrigin)));
+ query_result_manager_.SetSourcesForCastMode(
+ MediaCastMode::TAB_MIRROR, tab_sources, url::Origin(GURL(kOrigin)));
+
+ // Scenario (results in this order):
+ // Action: URL_B -> [2, 4]
+ // Expected result:
+ // Sinks: [1 -> {},
+ // 2 -> {URL_B},
+ // 3 -> {},
+ // 4 -> {URL_B}]
+ auto sinks_observer_it = sinks_observers.find(source_b);
+ ASSERT_TRUE(sinks_observer_it != sinks_observers.end());
+ ASSERT_TRUE(sinks_observer_it->second.get());
+
+ auto& source_b_observer = sinks_observer_it->second;
+ source_b_observer->OnSinksUpdated({sink2, sink4}, std::vector<url::Origin>());
+ EXPECT_TRUE(IsDefaultSourceForSink(nullptr, sink1));
+ EXPECT_TRUE(IsDefaultSourceForSink(&source_b, sink2));
+ EXPECT_TRUE(IsDefaultSourceForSink(nullptr, sink3));
+ EXPECT_TRUE(IsDefaultSourceForSink(&source_b, sink4));
+
+ // Action: URL_C -> [1, 2, 3]
+ // Expected result:
+ // Sinks: [1 -> {URL_C},
+ // 2 -> {URL_B, URL_C},
+ // 3 -> {URL_C},
+ // 4 -> {URL_B}]
+ sinks_observer_it = sinks_observers.find(source_c);
+ ASSERT_TRUE(sinks_observer_it != sinks_observers.end());
+ ASSERT_TRUE(sinks_observer_it->second.get());
+
+ auto& source_c_observer = sinks_observer_it->second;
+ source_c_observer->OnSinksUpdated({sink1, sink2, sink3},
+ std::vector<url::Origin>());
+ EXPECT_TRUE(IsDefaultSourceForSink(&source_c, sink1));
+ EXPECT_TRUE(IsDefaultSourceForSink(&source_b, sink2));
+ EXPECT_TRUE(IsDefaultSourceForSink(&source_c, sink3));
+ EXPECT_TRUE(IsDefaultSourceForSink(&source_b, sink4));
+
+ // Action: URL_A -> [2, 3, 4]
+ // Expected result:
+ // Sinks: [1 -> {URL_C},
+ // 2 -> {URL_A, URL_B, URL_C},
+ // 3 -> {URL_A, URL_C},
+ // 4 -> {URL_A, URL_B}]
+ sinks_observer_it = sinks_observers.find(source_a);
+ ASSERT_TRUE(sinks_observer_it != sinks_observers.end());
+ ASSERT_TRUE(sinks_observer_it->second.get());
+
+ auto& source_a_observer = sinks_observer_it->second;
+ source_a_observer->OnSinksUpdated({sink2, sink3, sink4},
+ std::vector<url::Origin>());
+ EXPECT_TRUE(IsDefaultSourceForSink(&source_c, sink1));
+ EXPECT_TRUE(IsDefaultSourceForSink(&source_a, sink2));
+ EXPECT_TRUE(IsDefaultSourceForSink(&source_a, sink3));
+ EXPECT_TRUE(IsDefaultSourceForSink(&source_a, sink4));
+
+ // Action: TAB -> [1, 2]
+ // Expected result:
+ // Sinks: [1 -> {URL_C, TAB},
+ // 2 -> {URL_A, URL_B, URL_C, TAB},
+ // 3 -> {URL_A, URL_C},
+ // 4 -> {URL_A, URL_B}]
+ sinks_observer_it = sinks_observers.find(source_tab);
+ ASSERT_TRUE(sinks_observer_it != sinks_observers.end());
+ ASSERT_TRUE(sinks_observer_it->second.get());
+
+ auto& source_tab_observer = sinks_observer_it->second;
+ source_tab_observer->OnSinksUpdated({sink1, sink2},
+ std::vector<url::Origin>());
+ EXPECT_TRUE(IsDefaultSourceForSink(&source_c, sink1));
+ EXPECT_TRUE(IsDefaultSourceForSink(&source_a, sink2));
+ EXPECT_TRUE(IsDefaultSourceForSink(&source_a, sink3));
+ EXPECT_TRUE(IsDefaultSourceForSink(&source_a, sink4));
+ EXPECT_TRUE(IsTabSourceForSink(&source_tab, sink1));
+ EXPECT_TRUE(IsTabSourceForSink(&source_tab, sink2));
+ EXPECT_TRUE(IsTabSourceForSink(nullptr, sink3));
+ EXPECT_TRUE(IsTabSourceForSink(nullptr, sink4));
+
+ // The observers for the four sources should get unregistered.
+ EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_)).Times(4);
+ query_result_manager_.RemoveSourcesForCastMode(MediaCastMode::DEFAULT);
+ query_result_manager_.RemoveSourcesForCastMode(MediaCastMode::TAB_MIRROR);
+}
+
+TEST_F(QueryResultManagerTest, AddInvalidSource) {
+ const MediaSource source(
+ MediaSourceForPresentationUrl(GURL("http://url.com")));
+
+ EXPECT_CALL(mock_router_, RegisterMediaSinksObserver(_))
+ .Times(1)
+ .WillRepeatedly(Return(true));
+ query_result_manager_.SetSourcesForCastMode(MediaCastMode::DEFAULT, {source},
+ url::Origin(GURL(kOrigin)));
+ // |source| has already been registered with the default cast mode, so it
+ // shouldn't get registered with tab mirroring.
+ query_result_manager_.SetSourcesForCastMode(
+ MediaCastMode::TAB_MIRROR, {source}, url::Origin(GURL(kOrigin)));
+
+ const auto& cast_mode_sources = query_result_manager_.cast_mode_sources_;
+ const auto& default_sources = cast_mode_sources.at(MediaCastMode::DEFAULT);
+ EXPECT_TRUE(base::ContainsKey(cast_mode_sources, MediaCastMode::DEFAULT));
+ EXPECT_EQ(default_sources.size(), 1u);
+ EXPECT_EQ(default_sources.at(0), source);
+ EXPECT_FALSE(base::ContainsKey(cast_mode_sources, MediaCastMode::TAB_MIRROR));
+}
+
+} // namespace media_router
diff --git a/chromium/chrome/browser/ui/webui/metrics_handler.cc b/chromium/chrome/browser/ui/webui/metrics_handler.cc
new file mode 100644
index 00000000000..21fcb2cb0bb
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/metrics_handler.cc
@@ -0,0 +1,143 @@
+// 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/ui/webui/metrics_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.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 "build/build_config.h"
+#include "chrome/browser/ui/tab_contents/core_tab_helper.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+
+using base::ListValue;
+using base::UserMetricsAction;
+using content::WebContents;
+
+MetricsHandler::MetricsHandler() {}
+MetricsHandler::~MetricsHandler() {}
+
+void MetricsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "metricsHandler:recordAction",
+ base::Bind(&MetricsHandler::HandleRecordAction, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "metricsHandler:recordInHistogram",
+ base::Bind(&MetricsHandler::HandleRecordInHistogram,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "metricsHandler:recordBooleanHistogram",
+ base::Bind(&MetricsHandler::HandleRecordBooleanHistogram,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "metricsHandler:recordTime",
+ base::Bind(&MetricsHandler::HandleRecordTime, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "metricsHandler:logEventTime",
+ base::Bind(&MetricsHandler::HandleLogEventTime, base::Unretained(this)));
+}
+
+void MetricsHandler::HandleRecordAction(const base::ListValue* args) {
+ std::string string_action = base::UTF16ToUTF8(ExtractStringValue(args));
+ base::RecordComputedAction(string_action);
+}
+
+void MetricsHandler::HandleRecordInHistogram(const base::ListValue* args) {
+ std::string histogram_name;
+ double value;
+ double boundary_value;
+ if (!args->GetString(0, &histogram_name) ||
+ !args->GetDouble(1, &value) ||
+ !args->GetDouble(2, &boundary_value)) {
+ NOTREACHED();
+ return;
+ }
+
+ int int_value = static_cast<int>(value);
+ int int_boundary_value = static_cast<int>(boundary_value);
+ if (int_boundary_value >= 4000 ||
+ int_value > int_boundary_value ||
+ int_value < 0) {
+ NOTREACHED();
+ return;
+ }
+
+ int bucket_count = int_boundary_value;
+ while (bucket_count >= 100) {
+ bucket_count /= 10;
+ }
+
+ // As |histogram_name| may change between calls, the UMA_HISTOGRAM_ENUMERATION
+ // macro cannot be used here.
+ base::HistogramBase* counter =
+ base::LinearHistogram::FactoryGet(
+ histogram_name, 1, int_boundary_value, bucket_count + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+ counter->Add(int_value);
+}
+
+void MetricsHandler::HandleRecordBooleanHistogram(const base::ListValue* args) {
+ std::string histogram_name;
+ bool value;
+ if (!args->GetString(0, &histogram_name) || !args->GetBoolean(1, &value)) {
+ NOTREACHED();
+ return;
+ }
+
+ base::HistogramBase* counter = base::BooleanHistogram::FactoryGet(
+ histogram_name, base::HistogramBase::kUmaTargetedHistogramFlag);
+ counter->AddBoolean(value);
+}
+
+void MetricsHandler::HandleRecordTime(const base::ListValue* args) {
+ std::string histogram_name;
+ double value;
+
+ if (!args->GetString(0, &histogram_name) ||
+ !args->GetDouble(1, &value) ||
+ value < 0) {
+ NOTREACHED();
+ return;
+ }
+
+ base::TimeDelta time_value = base::TimeDelta::FromMilliseconds(value);
+
+ base::HistogramBase* counter = base::Histogram::FactoryTimeGet(
+ histogram_name, base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromSeconds(10), 50,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+ counter->AddTime(time_value);
+}
+
+void MetricsHandler::HandleLogEventTime(const base::ListValue* args) {
+ std::string event_name = base::UTF16ToUTF8(ExtractStringValue(args));
+ WebContents* tab = web_ui()->GetWebContents();
+
+ // Not all new tab pages get timed. In those cases, we don't have a
+ // new_tab_start_time_.
+ CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(tab);
+ if (core_tab_helper->new_tab_start_time().is_null())
+ return;
+
+ base::TimeDelta duration =
+ base::TimeTicks::Now() - core_tab_helper->new_tab_start_time();
+
+ if (event_name == "Tab.NewTabScriptStart") {
+ UMA_HISTOGRAM_TIMES("Tab.NewTabScriptStart", duration);
+ } else if (event_name == "Tab.NewTabDOMContentLoaded") {
+ UMA_HISTOGRAM_TIMES("Tab.NewTabDOMContentLoaded", duration);
+ } else if (event_name == "Tab.NewTabOnload") {
+ UMA_HISTOGRAM_TIMES("Tab.NewTabOnload", duration);
+ // The new tab page has finished loading; reset it.
+ CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(tab);
+ core_tab_helper->set_new_tab_start_time(base::TimeTicks());
+ } else {
+ NOTREACHED();
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/metrics_handler.h b/chromium/chrome/browser/ui/webui/metrics_handler.h
new file mode 100644
index 00000000000..79f94f715ce
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/metrics_handler.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2011 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_METRICS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_METRICS_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// MetricsHandler
+
+// Let the page contents record UMA actions. Only use when you can't do it from
+// C++. For example, we currently use it to let the NTP log the position of the
+// Most Visited or Bookmark the user clicked on, as we don't get that
+// information through RequestOpenURL. You will need to update the metrics
+// dashboard with the action names you use, as our processor won't catch that
+// information (treat it as RecordComputedMetrics)
+
+namespace base {
+class ListValue;
+}
+
+class MetricsHandler : public content::WebUIMessageHandler {
+ public:
+ MetricsHandler();
+ ~MetricsHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // Callback for the "metricsHandler:recordAction" message. This records a
+ // user action.
+ void HandleRecordAction(const base::ListValue* args);
+
+ // TODO(dbeam): http://crbug.com/104338
+
+ // Callback for the "metricsHandler:recordInHistogram" message. This records
+ // into a histogram. |args| contains the histogram name, the value to record,
+ // and the maximum allowed value, which can be at most 4000. The histogram
+ // will use at most 100 buckets, one for each 1, 10, or 100 different values,
+ // depending on the maximum value.
+ void HandleRecordInHistogram(const base::ListValue* args);
+
+ // Callback for the "metricsHandler:recordBooleanHistogram" message. This
+ // records into a boolean histogram. |args| contains the histogram name, and
+ // the value to record.
+ void HandleRecordBooleanHistogram(const base::ListValue* args);
+
+ // Record a millisecond time value in a histogram, similar to
+ // UMA_HISTOGRAM_TIMES. Handles times between 1ms and 10sec. |args|
+ // contains the histogram name and a value in milliseconds.
+ void HandleRecordTime(const base::ListValue* args);
+
+ // Callback for the "metricsHandler:logEventTime" message.
+ void HandleLogEventTime(const base::ListValue* args);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MetricsHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_METRICS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/mojo_web_ui_controller.cc b/chromium/chrome/browser/ui/webui/mojo_web_ui_controller.cc
new file mode 100644
index 00000000000..fc51c20e93a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/mojo_web_ui_controller.cc
@@ -0,0 +1,23 @@
+// 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/ui/webui/mojo_web_ui_controller.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/mojo_web_ui_handler.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/common/bindings_policy.h"
+#include "mojo/public/cpp/system/core.h"
+
+MojoWebUIControllerBase::MojoWebUIControllerBase(content::WebUI* contents)
+ : WebUIController(contents) {}
+
+MojoWebUIControllerBase::~MojoWebUIControllerBase() {
+}
+
+void MojoWebUIControllerBase::RenderFrameCreated(
+ content::RenderFrameHost* render_frame_host) {
+ render_frame_host->AllowBindings(content::BINDINGS_POLICY_WEB_UI);
+}
diff --git a/chromium/chrome/browser/ui/webui/mojo_web_ui_controller.h b/chromium/chrome/browser/ui/webui/mojo_web_ui_controller.h
new file mode 100644
index 00000000000..75109db2423
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/mojo_web_ui_controller.h
@@ -0,0 +1,71 @@
+// 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_UI_WEBUI_MOJO_WEB_UI_CONTROLLER_H_
+#define CHROME_BROWSER_UI_WEBUI_MOJO_WEB_UI_CONTROLLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/webui/mojo_web_ui_handler.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "mojo/public/cpp/system/core.h"
+#include "services/service_manager/public/cpp/bind_source_info.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+
+class MojoWebUIControllerBase : public content::WebUIController {
+ public:
+ explicit MojoWebUIControllerBase(content::WebUI* contents);
+ ~MojoWebUIControllerBase() override;
+
+ // WebUIController overrides:
+ void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MojoWebUIControllerBase);
+};
+
+// MojoWebUIController is intended for web ui pages that use mojo. It is
+// expected that subclasses will do two things:
+// . In the constructor invoke AddMojoResourcePath() to register the bindings
+// files, eg:
+// AddMojoResourcePath("chrome/browser/ui/webui/omnibox/omnibox.mojom",
+// IDR_OMNIBOX_MOJO_JS);
+// . Override BindUIHandler() to create and bind the implementation of the
+// bindings.
+template <typename Interface>
+class MojoWebUIController : public MojoWebUIControllerBase {
+ public:
+ explicit MojoWebUIController(content::WebUI* contents)
+ : MojoWebUIControllerBase(contents), weak_factory_(this) {}
+ ~MojoWebUIController() override {}
+
+ void RenderFrameCreated(
+ content::RenderFrameHost* render_frame_host) override {
+ MojoWebUIControllerBase::RenderFrameCreated(render_frame_host);
+
+ // Right now, this is expected to be called only for main frames.
+ DCHECK(!render_frame_host->GetParent());
+ render_frame_host->GetInterfaceRegistry()->
+ AddInterface<Interface>(
+ base::Bind(&MojoWebUIController::BindUIHandler,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ protected:
+ // Invoked to create the specific bindings implementation.
+ virtual void BindUIHandler(const service_manager::BindSourceInfo& source_info,
+ mojo::InterfaceRequest<Interface> request) = 0;
+
+ private:
+ base::WeakPtrFactory<MojoWebUIController> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MojoWebUIController);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_MOJO_WEB_UI_CONTROLLER_H_
diff --git a/chromium/chrome/browser/ui/webui/mojo_web_ui_handler.h b/chromium/chrome/browser/ui/webui/mojo_web_ui_handler.h
new file mode 100644
index 00000000000..22d2ef02c28
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/mojo_web_ui_handler.h
@@ -0,0 +1,16 @@
+// 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_UI_WEBUI_MOJO_WEB_UI_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_MOJO_WEB_UI_HANDLER_H_
+
+// Bindings implementations must subclass this. Used so that MojoWebUIController
+// can own the binding implementation.
+class MojoWebUIHandler {
+ public:
+ MojoWebUIHandler() {}
+ virtual ~MojoWebUIHandler() {}
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_MOJO_WEB_UI_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/nacl_ui.cc b/chromium/chrome/browser/ui/webui/nacl_ui.cc
new file mode 100644
index 00000000000..22a2027b181
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/nacl_ui.cc
@@ -0,0 +1,397 @@
+// 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/ui/webui/nacl_ui.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/metrics/user_metrics.h"
+#include "base/path_service.h"
+#include "base/strings/string16.h"
+#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"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/version_info/version_info.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/plugin_service.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "content/public/common/webplugininfo.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
+using base::ASCIIToUTF16;
+using base::UserMetricsAction;
+using content::BrowserThread;
+using content::PluginService;
+using content::WebUIMessageHandler;
+
+namespace {
+
+content::WebUIDataSource* CreateNaClUIHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUINaClHost);
+
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("about_nacl.css", IDR_ABOUT_NACL_CSS);
+ source->AddResourcePath("about_nacl.js", IDR_ABOUT_NACL_JS);
+ source->SetDefaultResource(IDR_ABOUT_NACL_HTML);
+ return source;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// NaClDomHandler
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// The handler for JavaScript messages for the about:flags page.
+class NaClDomHandler : public WebUIMessageHandler {
+ public:
+ NaClDomHandler();
+ ~NaClDomHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ // Callback for the "requestNaClInfo" message.
+ void HandleRequestNaClInfo(const base::ListValue* args);
+
+ // Callback for the NaCl plugin information.
+ void OnGotPlugins(const std::vector<content::WebPluginInfo>& plugins);
+
+ // A helper callback that receives the result of checking if PNaCl path
+ // exists and checking the PNaCl |version|. |is_valid| is true if the PNaCl
+ // path that was returned by PathService is valid, and false otherwise.
+ void DidCheckPathAndVersion(const std::string* version, bool is_valid);
+
+ // Called when enough information is gathered to return data back to the page.
+ void MaybeRespondToPage();
+
+ // Helper for MaybeRespondToPage -- called after enough information
+ // is gathered.
+ void PopulatePageInformation(base::DictionaryValue* naclInfo);
+
+ // Returns whether the specified plugin is enabled.
+ bool isPluginEnabled(size_t plugin_index);
+
+ // Adds information regarding the operating system and chrome version to list.
+ void AddOperatingSystemInfo(base::ListValue* list);
+
+ // Adds the list of plugins for NaCl to list.
+ void AddPluginList(base::ListValue* list);
+
+ // Adds the information relevant to PNaCl (e.g., enablement, paths, version)
+ // to the list.
+ void AddPnaclInfo(base::ListValue* list);
+
+ // Adds the information relevant to NaCl to list.
+ void AddNaClInfo(base::ListValue* list);
+
+ // Whether the page has requested data.
+ bool page_has_requested_data_;
+
+ // Whether the plugin information is ready.
+ bool has_plugin_info_;
+
+ // Whether PNaCl path was validated. PathService can return a path
+ // that does not exists, so it needs to be validated.
+ bool pnacl_path_validated_;
+ bool pnacl_path_exists_;
+ std::string pnacl_version_string_;
+
+ // Factory for the creating refs in callbacks.
+ base::WeakPtrFactory<NaClDomHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(NaClDomHandler);
+};
+
+NaClDomHandler::NaClDomHandler()
+ : page_has_requested_data_(false),
+ has_plugin_info_(false),
+ pnacl_path_validated_(false),
+ pnacl_path_exists_(false),
+ weak_ptr_factory_(this) {
+ PluginService::GetInstance()->GetPlugins(base::Bind(
+ &NaClDomHandler::OnGotPlugins, weak_ptr_factory_.GetWeakPtr()));
+}
+
+NaClDomHandler::~NaClDomHandler() {
+}
+
+void NaClDomHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "requestNaClInfo",
+ base::Bind(&NaClDomHandler::HandleRequestNaClInfo,
+ base::Unretained(this)));
+}
+
+// Helper functions for collecting a list of key-value pairs that will
+// be displayed.
+void AddPair(base::ListValue* list,
+ const base::string16& key,
+ const base::string16& value) {
+ std::unique_ptr<base::DictionaryValue> results(new base::DictionaryValue());
+ results->SetString("key", key);
+ results->SetString("value", value);
+ list->Append(std::move(results));
+}
+
+// Generate an empty data-pair which acts as a line break.
+void AddLineBreak(base::ListValue* list) {
+ AddPair(list, ASCIIToUTF16(""), ASCIIToUTF16(""));
+}
+
+bool NaClDomHandler::isPluginEnabled(size_t plugin_index) {
+ std::vector<content::WebPluginInfo> info_array;
+ PluginService::GetInstance()->GetPluginInfoArray(
+ GURL(), "application/x-nacl", false, &info_array, NULL);
+ PluginPrefs* plugin_prefs =
+ PluginPrefs::GetForProfile(Profile::FromWebUI(web_ui())).get();
+ return (!info_array.empty() &&
+ plugin_prefs->IsPluginEnabled(info_array[plugin_index]));
+}
+
+void NaClDomHandler::AddOperatingSystemInfo(base::ListValue* list) {
+ // Obtain the Chrome version info.
+ AddPair(list,
+ l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
+ ASCIIToUTF16(version_info::GetVersionNumber() + " (" +
+ chrome::GetChannelString() + ")"));
+
+ // OS version information.
+ // TODO(jvoung): refactor this to share the extra windows labeling
+ // with about:flash, or something.
+ std::string os_label = version_info::GetOSType();
+#if defined(OS_WIN)
+ base::win::OSInfo* os = base::win::OSInfo::GetInstance();
+ switch (os->version()) {
+ case base::win::VERSION_XP: os_label += " XP"; break;
+ case base::win::VERSION_SERVER_2003:
+ os_label += " Server 2003 or XP Pro 64 bit";
+ break;
+ case base::win::VERSION_VISTA: os_label += " Vista or Server 2008"; break;
+ case base::win::VERSION_WIN7: os_label += " 7 or Server 2008 R2"; break;
+ case base::win::VERSION_WIN8: os_label += " 8 or Server 2012"; break;
+ default: os_label += " UNKNOWN"; break;
+ }
+ os_label += " SP" + base::IntToString(os->service_pack().major);
+ if (os->service_pack().minor > 0)
+ os_label += "." + base::IntToString(os->service_pack().minor);
+ if (os->architecture() == base::win::OSInfo::X64_ARCHITECTURE)
+ os_label += " 64 bit";
+#endif
+ AddPair(list, l10n_util::GetStringUTF16(IDS_VERSION_UI_OS),
+ ASCIIToUTF16(os_label));
+ AddLineBreak(list);
+}
+
+void NaClDomHandler::AddPluginList(base::ListValue* list) {
+ // Obtain the version of the NaCl plugin.
+ std::vector<content::WebPluginInfo> info_array;
+ PluginService::GetInstance()->GetPluginInfoArray(
+ GURL(), "application/x-nacl", false, &info_array, NULL);
+ base::string16 nacl_version;
+ base::string16 nacl_key = ASCIIToUTF16("NaCl plugin");
+ if (info_array.empty()) {
+ AddPair(list, nacl_key, ASCIIToUTF16("Disabled"));
+ } else {
+ // Only the 0th plugin is used.
+ nacl_version = info_array[0].version + ASCIIToUTF16(" ") +
+ info_array[0].path.LossyDisplayName();
+ if (!isPluginEnabled(0)) {
+ nacl_version += ASCIIToUTF16(" (Disabled in profile prefs)");
+ }
+
+ AddPair(list, nacl_key, nacl_version);
+
+ // Mark the rest as not used.
+ for (size_t i = 1; i < info_array.size(); ++i) {
+ nacl_version = info_array[i].version + ASCIIToUTF16(" ") +
+ info_array[i].path.LossyDisplayName();
+ nacl_version += ASCIIToUTF16(" (not used)");
+ if (!isPluginEnabled(i)) {
+ nacl_version += ASCIIToUTF16(" (Disabled in profile prefs)");
+ }
+ AddPair(list, nacl_key, nacl_version);
+ }
+ }
+ AddLineBreak(list);
+}
+
+void NaClDomHandler::AddPnaclInfo(base::ListValue* list) {
+ // Display whether PNaCl is enabled.
+ base::string16 pnacl_enabled_string = ASCIIToUTF16("Enabled");
+ if (!isPluginEnabled(0)) {
+ pnacl_enabled_string = ASCIIToUTF16("Disabled in profile prefs");
+ }
+ AddPair(list,
+ ASCIIToUTF16("Portable Native Client (PNaCl)"),
+ pnacl_enabled_string);
+
+ // Obtain the version of the PNaCl translator.
+ base::FilePath pnacl_path;
+ bool got_path = PathService::Get(chrome::DIR_PNACL_COMPONENT, &pnacl_path);
+ if (!got_path || pnacl_path.empty() || !pnacl_path_exists_) {
+ AddPair(list,
+ ASCIIToUTF16("PNaCl translator"),
+ ASCIIToUTF16("Not installed"));
+ } else {
+ AddPair(list,
+ ASCIIToUTF16("PNaCl translator path"),
+ pnacl_path.LossyDisplayName());
+ AddPair(list,
+ ASCIIToUTF16("PNaCl translator version"),
+ ASCIIToUTF16(pnacl_version_string_));
+ }
+ AddLineBreak(list);
+}
+
+void NaClDomHandler::AddNaClInfo(base::ListValue* list) {
+ base::string16 nacl_enabled_string = ASCIIToUTF16("Disabled");
+ if (isPluginEnabled(0) &&
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableNaCl)) {
+ nacl_enabled_string = ASCIIToUTF16("Enabled by flag '--enable-nacl'");
+ }
+ AddPair(list,
+ ASCIIToUTF16("Native Client (non-portable, outside web store)"),
+ nacl_enabled_string);
+ AddLineBreak(list);
+}
+
+void NaClDomHandler::HandleRequestNaClInfo(const base::ListValue* args) {
+ page_has_requested_data_ = true;
+ // Force re-validation of PNaCl's path in the next call to
+ // MaybeRespondToPage(), in case PNaCl went from not-installed
+ // to installed since the request.
+ pnacl_path_validated_ = false;
+ MaybeRespondToPage();
+}
+
+void NaClDomHandler::OnGotPlugins(
+ const std::vector<content::WebPluginInfo>& plugins) {
+ has_plugin_info_ = true;
+ MaybeRespondToPage();
+}
+
+void NaClDomHandler::PopulatePageInformation(base::DictionaryValue* naclInfo) {
+ DCHECK(pnacl_path_validated_);
+ // Store Key-Value pairs of about-information.
+ std::unique_ptr<base::ListValue> list(new base::ListValue());
+ // Display the operating system and chrome version information.
+ AddOperatingSystemInfo(list.get());
+ // Display the list of plugins serving NaCl.
+ AddPluginList(list.get());
+ // Display information relevant to PNaCl.
+ AddPnaclInfo(list.get());
+ // Display information relevant to NaCl (non-portable.
+ AddNaClInfo(list.get());
+ // naclInfo will take ownership of list, and clean it up on destruction.
+ naclInfo->Set("naclInfo", std::move(list));
+}
+
+void NaClDomHandler::DidCheckPathAndVersion(const std::string* version,
+ bool is_valid) {
+ pnacl_path_validated_ = true;
+ pnacl_path_exists_ = is_valid;
+ pnacl_version_string_ = *version;
+ MaybeRespondToPage();
+}
+
+void CheckVersion(const base::FilePath& pnacl_path, std::string* version) {
+ base::FilePath pnacl_json_path =
+ pnacl_path.AppendASCII("pnacl_public_pnacl_json");
+ JSONFileValueDeserializer deserializer(pnacl_json_path);
+ std::string error;
+ std::unique_ptr<base::Value> root = deserializer.Deserialize(NULL, &error);
+ if (!root || !root->IsType(base::Value::Type::DICTIONARY))
+ return;
+
+ // Now try to get the field. This may leave version empty if the
+ // the "get" fails (no key, or wrong type).
+ static_cast<base::DictionaryValue*>(root.get())->GetStringASCII(
+ "pnacl-version", version);
+}
+
+bool CheckPathAndVersion(std::string* version) {
+ base::FilePath pnacl_path;
+ bool got_path = PathService::Get(chrome::DIR_PNACL_COMPONENT, &pnacl_path);
+ if (got_path && !pnacl_path.empty() && base::PathExists(pnacl_path)) {
+ CheckVersion(pnacl_path, version);
+ return true;
+ }
+ return false;
+}
+
+void NaClDomHandler::MaybeRespondToPage() {
+ // Don't reply until everything is ready. The page will show a 'loading'
+ // message until then.
+ if (!page_has_requested_data_ || !has_plugin_info_)
+ return;
+
+ if (!pnacl_path_validated_) {
+ std::string* version_string = new std::string;
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&CheckPathAndVersion, version_string),
+ base::Bind(&NaClDomHandler::DidCheckPathAndVersion,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Owned(version_string)));
+ return;
+ }
+
+ base::DictionaryValue naclInfo;
+ PopulatePageInformation(&naclInfo);
+ web_ui()->CallJavascriptFunctionUnsafe("nacl.returnNaClInfo", naclInfo);
+}
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// NaClUI
+//
+///////////////////////////////////////////////////////////////////////////////
+
+NaClUI::NaClUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ base::RecordAction(UserMetricsAction("ViewAboutNaCl"));
+
+ web_ui->AddMessageHandler(base::MakeUnique<NaClDomHandler>());
+
+ // Set up the about:nacl source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreateNaClUIHTMLSource());
+}
diff --git a/chromium/chrome/browser/ui/webui/nacl_ui.h b/chromium/chrome/browser/ui/webui/nacl_ui.h
new file mode 100644
index 00000000000..472b68c36bf
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/nacl_ui.h
@@ -0,0 +1,20 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_NACL_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_NACL_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The Web UI handler for about:nacl.
+class NaClUI : public content::WebUIController {
+ public:
+ explicit NaClUI(content::WebUI* web_ui);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NaClUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_NACL_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/net_export_ui.cc b/chromium/chrome/browser/ui/webui/net_export_ui.cc
new file mode 100644
index 00000000000..be80d206317
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/net_export_ui.cc
@@ -0,0 +1,416 @@
+// 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/ui/webui/net_export_ui.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/scoped_observer.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/io_thread.h"
+#include "chrome/browser/net/net_export_helper.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/chrome_select_file_policy.h"
+#include "chrome/common/url_constants.h"
+#include "components/grit/components_resources.h"
+#include "components/net_log/chrome_net_log.h"
+#include "components/net_log/net_export_ui_constants.h"
+#include "components/net_log/net_log_file_writer.h"
+#include "content/public/browser/browser_thread.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"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "extensions/features/features.h"
+#include "net/log/net_log_capture_mode.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+
+#if defined(OS_ANDROID)
+#include "chrome/browser/android/intent_helper.h"
+#endif
+
+using content::BrowserThread;
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace {
+
+class ProxyScriptFetcherContextGetter : public net::URLRequestContextGetter {
+ public:
+ explicit ProxyScriptFetcherContextGetter(IOThread* io_thread)
+ : io_thread_(io_thread) {}
+
+ net::URLRequestContext* GetURLRequestContext() override {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ DCHECK(io_thread_->globals()->proxy_script_fetcher_context.get());
+ return io_thread_->globals()->proxy_script_fetcher_context.get();
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
+ const override {
+ return BrowserThread::GetTaskRunnerForThread(BrowserThread::IO);
+ }
+
+ protected:
+ ~ProxyScriptFetcherContextGetter() override {}
+
+ private:
+ IOThread* const io_thread_; // Owned by BrowserProcess.
+};
+
+// May only be accessed on the UI thread
+base::LazyInstance<base::FilePath>::Leaky
+ last_save_dir = LAZY_INSTANCE_INITIALIZER;
+
+content::WebUIDataSource* CreateNetExportHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUINetExportHost);
+
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath(net_log::kNetExportUIJS, IDR_NET_LOG_NET_EXPORT_JS);
+ source->SetDefaultResource(IDR_NET_LOG_NET_EXPORT_HTML);
+ return source;
+}
+
+void SetIfNotNull(base::DictionaryValue* dict,
+ const base::StringPiece& path,
+ std::unique_ptr<base::Value> in_value) {
+ if (in_value) {
+ dict->Set(path, std::move(in_value));
+ }
+}
+
+// This class receives javascript messages from the renderer.
+// Note that the WebUI infrastructure runs on the UI thread, therefore all of
+// this class's public methods are expected to run on the UI thread.
+class NetExportMessageHandler
+ : public WebUIMessageHandler,
+ public base::SupportsWeakPtr<NetExportMessageHandler>,
+ public ui::SelectFileDialog::Listener,
+ public net_log::NetLogFileWriter::StateObserver {
+ public:
+ NetExportMessageHandler();
+ ~NetExportMessageHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // Messages
+ void OnEnableNotifyUIWithState(const base::ListValue* list);
+ void OnStartNetLog(const base::ListValue* list);
+ void OnStopNetLog(const base::ListValue* list);
+ void OnSendNetLog(const base::ListValue* list);
+ void OnShowFile(const base::ListValue* list);
+
+ // ui::SelectFileDialog::Listener implementation.
+ void FileSelected(const base::FilePath& path,
+ int index,
+ void* params) override;
+ void FileSelectionCanceled(void* params) override;
+
+ // net_log::NetLogFileWriter::StateObserver implementation.
+ void OnNewState(const base::DictionaryValue& state) override;
+
+ private:
+ using URLRequestContextGetterList =
+ std::vector<scoped_refptr<net::URLRequestContextGetter>>;
+
+ // Send NetLog data via email.
+ static void SendEmail(const base::FilePath& file_to_send);
+
+ // Reveal |path| in the shell on desktop platforms.
+ void ShowFileInShell(const base::FilePath& path);
+
+ // chrome://net-export can be used on both mobile and desktop platforms.
+ // On mobile a user cannot pick where their NetLog file is saved to.
+ // Instead, everything is saved on the user's temp directory. Thus the
+ // mobile user has the UI available to send their NetLog file as an
+ // email while the desktop user, who gets to choose their NetLog file's
+ // location, does not. Furthermore, since every time a user starts logging
+ // to a new NetLog file on mobile platforms it overwrites the previous
+ // NetLog file, a warning message appears on the Start Logging button
+ // that informs the user of this. This does not exist on the desktop
+ // UI.
+ static bool UsingMobileUI();
+
+ // Calls NetExportView.onExportNetLogInfoChanged JavaScript function in the
+ // renderer, passing in |file_writer_state|.
+ void NotifyUIWithState(std::unique_ptr<base::DictionaryValue> state);
+
+ // Opens the SelectFileDialog UI with the default path to save a
+ // NetLog file.
+ void ShowSelectFileDialog(const base::FilePath& default_path);
+
+ // Returns a list of context getters used to retrieve ongoing events when
+ // logging starts so that net log entries can be added for those events.
+ URLRequestContextGetterList GetURLRequestContexts() const;
+
+ // Cache of g_browser_process->net_log()->net_log_file_writer(). This
+ // is owned by ChromeNetLog which is owned by BrowserProcessImpl.
+ net_log::NetLogFileWriter* file_writer_;
+
+ ScopedObserver<net_log::NetLogFileWriter,
+ net_log::NetLogFileWriter::StateObserver>
+ state_observer_manager_;
+
+ // The capture mode the user chose in the UI when logging started is cached
+ // here and is read after a file path is chosen in the save dialog.
+ // Its value is only valid while the save dialog is open on the desktop UI.
+ net::NetLogCaptureMode capture_mode_;
+
+ scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
+
+ base::WeakPtrFactory<NetExportMessageHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetExportMessageHandler);
+};
+
+NetExportMessageHandler::NetExportMessageHandler()
+ : file_writer_(g_browser_process->net_log()->net_log_file_writer()),
+ state_observer_manager_(this),
+ weak_ptr_factory_(this) {
+ file_writer_->Initialize(
+ BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE_USER_BLOCKING),
+ BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
+}
+
+NetExportMessageHandler::~NetExportMessageHandler() {
+ // There may be a pending file dialog, it needs to be told that the user
+ // has gone away so that it doesn't try to call back.
+ if (select_file_dialog_)
+ select_file_dialog_->ListenerDestroyed();
+
+ file_writer_->StopNetLog(nullptr, nullptr);
+}
+
+void NetExportMessageHandler::RegisterMessages() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ web_ui()->RegisterMessageCallback(
+ net_log::kEnableNotifyUIWithStateHandler,
+ base::Bind(&NetExportMessageHandler::OnEnableNotifyUIWithState,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ net_log::kStartNetLogHandler,
+ base::Bind(&NetExportMessageHandler::OnStartNetLog,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ net_log::kStopNetLogHandler,
+ base::Bind(&NetExportMessageHandler::OnStopNetLog,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ net_log::kSendNetLogHandler,
+ base::Bind(&NetExportMessageHandler::OnSendNetLog,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ net_log::kShowFile,
+ base::Bind(&NetExportMessageHandler::OnShowFile, base::Unretained(this)));
+}
+
+// The net-export UI is not notified of state changes until this function runs.
+// After this function, NotifyUIWithState() will be called on all |file_writer_|
+// state changes.
+void NetExportMessageHandler::OnEnableNotifyUIWithState(
+ const base::ListValue* list) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (!state_observer_manager_.IsObservingSources()) {
+ state_observer_manager_.Add(file_writer_);
+ }
+ NotifyUIWithState(file_writer_->GetState());
+}
+
+void NetExportMessageHandler::OnStartNetLog(const base::ListValue* list) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ std::string capture_mode_string;
+ bool result = list->GetString(0, &capture_mode_string);
+ DCHECK(result);
+
+ capture_mode_ =
+ net_log::NetLogFileWriter::CaptureModeFromString(capture_mode_string);
+
+ if (UsingMobileUI()) {
+ file_writer_->StartNetLog(base::FilePath(), capture_mode_,
+ GetURLRequestContexts());
+ } else {
+ base::FilePath initial_dir = last_save_dir.Pointer()->empty() ?
+ DownloadPrefs::FromBrowserContext(
+ web_ui()->GetWebContents()->GetBrowserContext())->DownloadPath() :
+ *last_save_dir.Pointer();
+ base::FilePath initial_path =
+ initial_dir.Append(FILE_PATH_LITERAL("chrome-net-export-log.json"));
+ ShowSelectFileDialog(initial_path);
+ }
+}
+
+void NetExportMessageHandler::OnStopNetLog(const base::ListValue* list) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ std::unique_ptr<base::DictionaryValue> ui_thread_polled_data(
+ new base::DictionaryValue());
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ SetIfNotNull(ui_thread_polled_data.get(), "dataReductionProxyInfo",
+ chrome_browser_net::GetDataReductionProxyInfo(profile));
+ SetIfNotNull(ui_thread_polled_data.get(), "historicNetworkStats",
+ chrome_browser_net::GetHistoricNetworkStats(profile));
+ SetIfNotNull(ui_thread_polled_data.get(), "prerenderInfo",
+ chrome_browser_net::GetPrerenderInfo(profile));
+ SetIfNotNull(ui_thread_polled_data.get(), "sessionNetworkStats",
+ chrome_browser_net::GetSessionNetworkStats(profile));
+ SetIfNotNull(ui_thread_polled_data.get(), "extensionInfo",
+ chrome_browser_net::GetExtensionInfo(profile));
+#if defined(OS_WIN)
+ SetIfNotNull(ui_thread_polled_data.get(), "serviceProviders",
+ chrome_browser_net::GetWindowsServiceProviders());
+#endif
+
+ file_writer_->StopNetLog(std::move(ui_thread_polled_data),
+ Profile::FromWebUI(web_ui())->GetRequestContext());
+}
+
+void NetExportMessageHandler::OnSendNetLog(const base::ListValue* list) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ file_writer_->GetFilePathToCompletedLog(
+ base::Bind(&NetExportMessageHandler::SendEmail));
+}
+
+void NetExportMessageHandler::OnShowFile(const base::ListValue* list) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ file_writer_->GetFilePathToCompletedLog(
+ base::Bind(&NetExportMessageHandler::ShowFileInShell, AsWeakPtr()));
+}
+
+void NetExportMessageHandler::FileSelected(const base::FilePath& path,
+ int index,
+ void* params) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(select_file_dialog_);
+ *last_save_dir.Pointer() = path.DirName();
+
+ file_writer_->StartNetLog(path, capture_mode_, GetURLRequestContexts());
+
+ // IMPORTANT: resetting the dialog may lead to the deletion of |path|, so keep
+ // this line last.
+ select_file_dialog_ = nullptr;
+}
+
+void NetExportMessageHandler::FileSelectionCanceled(void* params) {
+ DCHECK(select_file_dialog_);
+ select_file_dialog_ = nullptr;
+}
+
+void NetExportMessageHandler::OnNewState(const base::DictionaryValue& state) {
+ NotifyUIWithState(state.CreateDeepCopy());
+}
+
+// static
+void NetExportMessageHandler::SendEmail(const base::FilePath& file_to_send) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+#if defined(OS_ANDROID)
+ if (file_to_send.empty())
+ return;
+ std::string email;
+ std::string subject = "net_internals_log";
+ std::string title = "Issue number: ";
+ std::string body =
+ "Please add some informative text about the network issues.";
+ base::FilePath::StringType file_to_attach(file_to_send.value());
+ chrome::android::SendEmail(
+ base::UTF8ToUTF16(email), base::UTF8ToUTF16(subject),
+ base::UTF8ToUTF16(body), base::UTF8ToUTF16(title),
+ base::UTF8ToUTF16(file_to_attach));
+#endif
+}
+
+void NetExportMessageHandler::ShowFileInShell(const base::FilePath& path) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (path.empty())
+ return;
+
+ // (The |profile| parameter is relevant for Chrome OS)
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+ platform_util::ShowItemInFolder(profile, path);
+}
+
+// static
+bool NetExportMessageHandler::UsingMobileUI() {
+#if defined(OS_ANDROID) || defined(OS_IOS)
+ return true;
+#else
+ return false;
+#endif
+}
+
+void NetExportMessageHandler::NotifyUIWithState(
+ std::unique_ptr<base::DictionaryValue> state) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(web_ui());
+ web_ui()->CallJavascriptFunctionUnsafe(net_log::kOnExportNetLogInfoChanged,
+ *state);
+}
+
+void NetExportMessageHandler::ShowSelectFileDialog(
+ const base::FilePath& default_path) {
+ // User may have clicked more than once before the save dialog appears.
+ // This prevents creating more than one save dialog.
+ if (select_file_dialog_)
+ return;
+
+ WebContents* webcontents = web_ui()->GetWebContents();
+
+ select_file_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(webcontents));
+ ui::SelectFileDialog::FileTypeInfo file_type_info;
+ file_type_info.extensions = {{FILE_PATH_LITERAL("json")}};
+ gfx::NativeWindow owning_window = webcontents->GetTopLevelNativeWindow();
+ select_file_dialog_->SelectFile(
+ ui::SelectFileDialog::SELECT_SAVEAS_FILE, base::string16(), default_path,
+ &file_type_info, 0, base::FilePath::StringType(), owning_window, nullptr);
+}
+
+NetExportMessageHandler::URLRequestContextGetterList
+NetExportMessageHandler::GetURLRequestContexts() const {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ URLRequestContextGetterList context_getters;
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+ context_getters.push_back(profile->GetRequestContext());
+ context_getters.push_back(
+ content::BrowserContext::GetDefaultStoragePartition(profile)
+ ->GetMediaURLRequestContext());
+ context_getters.push_back(
+ g_browser_process->io_thread()->system_url_request_context_getter());
+ context_getters.push_back(
+ new ProxyScriptFetcherContextGetter(g_browser_process->io_thread()));
+
+ return context_getters;
+}
+
+} // namespace
+
+NetExportUI::NetExportUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<NetExportMessageHandler>());
+
+ // Set up the chrome://net-export/ source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreateNetExportHTMLSource());
+}
diff --git a/chromium/chrome/browser/ui/webui/net_export_ui.h b/chromium/chrome/browser/ui/webui/net_export_ui.h
new file mode 100644
index 00000000000..cb58382957c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/net_export_ui.h
@@ -0,0 +1,20 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_NET_EXPORT_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_NET_EXPORT_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The C++ back-end for the chrome://net-export webui page.
+class NetExportUI : public content::WebUIController {
+ public:
+ explicit NetExportUI(content::WebUI* web_ui);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NetExportUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_NET_EXPORT_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/net_internals/DEPS b/chromium/chrome/browser/ui/webui/net_internals/DEPS
new file mode 100644
index 00000000000..5f9d86d4586
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/net_internals/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+components/onc",
+ "+components/user_manager",
+]
diff --git a/chromium/chrome/browser/ui/webui/net_internals/OWNERS b/chromium/chrome/browser/ui/webui/net_internals/OWNERS
new file mode 100644
index 00000000000..ff5c63b6e7f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/net_internals/OWNERS
@@ -0,0 +1,4 @@
+eroman@chromium.org
+mmenke@chromium.org
+
+# COMPONENT: Internals>Network>Logging
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
new file mode 100644
index 00000000000..2fa6e2ae757
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
@@ -0,0 +1,1059 @@
+// 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/ui/webui/net_internals/net_internals_ui.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browsing_data/browsing_data_helper.h"
+#include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/io_thread.h"
+#include "chrome/browser/net/chrome_network_delegate.h"
+#include "chrome/browser/net/net_export_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/net_internals_resources.h"
+#include "components/net_log/chrome_net_log.h"
+#include "components/onc/onc_constants.h"
+#include "components/prefs/pref_member.h"
+#include "components/url_formatter/url_fixer.h"
+#include "components/version_info/version_info.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/browsing_data_remover.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/resource_dispatcher_host.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "net/base/net_errors.h"
+#include "net/disk_cache/disk_cache.h"
+#include "net/dns/host_cache.h"
+#include "net/dns/host_resolver.h"
+#include "net/http/http_cache.h"
+#include "net/http/http_network_layer.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_server_properties.h"
+#include "net/http/http_stream_factory.h"
+#include "net/http/transport_security_state.h"
+#include "net/log/net_log.h"
+#include "net/log/net_log_capture_mode.h"
+#include "net/log/net_log_entry.h"
+#include "net/log/net_log_util.h"
+#include "net/log/write_to_file_net_log_observer.h"
+#include "net/proxy/proxy_service.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/system_logs/debug_log_writer.h"
+#include "chrome/browser/net/nss_context.h"
+#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_utils.h"
+#include "components/user_manager/user.h"
+#endif
+
+using base::Value;
+using content::BrowserThread;
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace {
+
+// Delay between when an event occurs and when it is passed to the Javascript
+// page. All events that occur during this period are grouped together and
+// sent to the page at once, which reduces context switching and CPU usage.
+const int kNetLogEventDelayMilliseconds = 100;
+
+// Returns the HostCache for |context|'s primary HostResolver, or NULL if
+// there is none.
+net::HostCache* GetHostResolverCache(net::URLRequestContext* context) {
+ return context->host_resolver()->GetHostCache();
+}
+
+std::string HashesToBase64String(const net::HashValueVector& hashes) {
+ std::string str;
+ for (size_t i = 0; i != hashes.size(); ++i) {
+ if (i != 0)
+ str += ",";
+ str += hashes[i].ToString();
+ }
+ return str;
+}
+
+bool Base64StringToHashes(const std::string& hashes_str,
+ net::HashValueVector* hashes) {
+ hashes->clear();
+ std::vector<std::string> vector_hash_str = base::SplitString(
+ hashes_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+ for (size_t i = 0; i != vector_hash_str.size(); ++i) {
+ std::string hash_str;
+ base::RemoveChars(vector_hash_str[i], " \t\r\n", &hash_str);
+ net::HashValue hash;
+ // Skip past unrecognized hash algos
+ // But return false on malformatted input
+ if (hash_str.empty())
+ return false;
+ if (hash_str.compare(0, 5, "sha1/") != 0 &&
+ hash_str.compare(0, 7, "sha256/") != 0) {
+ continue;
+ }
+ if (!hash.FromString(hash_str))
+ return false;
+ hashes->push_back(hash);
+ }
+ return true;
+}
+
+// Returns the http network session for |context| if there is one.
+// Otherwise, returns NULL.
+net::HttpNetworkSession* GetHttpNetworkSession(
+ net::URLRequestContext* context) {
+ if (!context->http_transaction_factory())
+ return nullptr;
+ return context->http_transaction_factory()->GetSession();
+}
+
+content::WebUIDataSource* CreateNetInternalsHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUINetInternalsHost);
+
+ source->SetDefaultResource(IDR_NET_INTERNALS_INDEX_HTML);
+ source->AddResourcePath("index.js", IDR_NET_INTERNALS_INDEX_JS);
+ source->SetJsonPath("strings.js");
+ source->UseGzip(std::unordered_set<std::string>());
+ return source;
+}
+
+// This class receives javascript messages from the renderer.
+// Note that the WebUI infrastructure runs on the UI thread, therefore all of
+// this class's methods are expected to run on the UI thread.
+//
+// Since the network code we want to run lives on the IO thread, we proxy
+// almost everything over to NetInternalsMessageHandler::IOThreadImpl, which
+// runs on the IO thread.
+//
+// TODO(eroman): Can we start on the IO thread to begin with?
+class NetInternalsMessageHandler
+ : public WebUIMessageHandler,
+ public base::SupportsWeakPtr<NetInternalsMessageHandler> {
+ public:
+ NetInternalsMessageHandler();
+ ~NetInternalsMessageHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // Calls g_browser.receive in the renderer, passing in |command| and |arg|.
+ // If the renderer is displaying a log file, the message will be ignored.
+ void SendJavascriptCommand(const std::string& command,
+ std::unique_ptr<base::Value> arg);
+
+ // Javascript message handlers.
+ void OnRendererReady(const base::ListValue* list);
+ void OnClearBrowserCache(const base::ListValue* list);
+ void OnGetPrerenderInfo(const base::ListValue* list);
+ void OnGetHistoricNetworkStats(const base::ListValue* list);
+ void OnGetSessionNetworkStats(const base::ListValue* list);
+ void OnGetExtensionInfo(const base::ListValue* list);
+ void OnGetDataReductionProxyInfo(const base::ListValue* list);
+#if defined(OS_CHROMEOS)
+ void OnImportONCFile(const base::ListValue* list);
+ void OnStoreDebugLogs(const base::ListValue* list);
+ void OnStoreDebugLogsCompleted(const base::FilePath& log_path,
+ bool succeeded);
+ void OnSetNetworkDebugMode(const base::ListValue* list);
+ void OnSetNetworkDebugModeCompleted(const std::string& subsystem,
+ bool succeeded);
+
+ // Callback to |GetNSSCertDatabaseForProfile| used to retrieve the database
+ // to which user's ONC defined certificates should be imported.
+ // It parses and imports |onc_blob|.
+ void ImportONCFileToNSSDB(const std::string& onc_blob,
+ const std::string& passcode,
+ net::NSSCertDatabase* nssdb);
+
+ // Called back by the CertificateImporter when a certificate import finished.
+ // |previous_error| contains earlier errors during this import.
+ void OnCertificatesImported(
+ const std::string& previous_error,
+ bool success,
+ const net::CertificateList& onc_trusted_certificates);
+#endif
+
+ private:
+ class IOThreadImpl;
+
+ // This is the "real" message handler, which lives on the IO thread.
+ scoped_refptr<IOThreadImpl> proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetInternalsMessageHandler);
+};
+
+// This class is the "real" message handler. It is allocated and destroyed on
+// the UI thread. With the exception of OnAddEntry, OnWebUIDeleted, and
+// SendJavascriptCommand, its methods are all expected to be called from the IO
+// thread. OnAddEntry and SendJavascriptCommand can be called from any thread,
+// and OnWebUIDeleted can only be called from the UI thread.
+class NetInternalsMessageHandler::IOThreadImpl
+ : public base::RefCountedThreadSafe<
+ NetInternalsMessageHandler::IOThreadImpl,
+ BrowserThread::DeleteOnUIThread>,
+ public net::NetLog::ThreadSafeObserver {
+ public:
+ // Type for methods that can be used as MessageHandler callbacks.
+ typedef void (IOThreadImpl::*MessageHandler)(const base::ListValue*);
+
+ // Creates a proxy for |handler| that will live on the IO thread.
+ // |handler| is a weak pointer, since it is possible for the
+ // WebUIMessageHandler to be deleted on the UI thread while we were executing
+ // on the IO thread. |io_thread| is the global IOThread (it is passed in as
+ // an argument since we need to grab it from the UI thread).
+ IOThreadImpl(
+ const base::WeakPtr<NetInternalsMessageHandler>& handler,
+ IOThread* io_thread,
+ net::URLRequestContextGetter* main_context_getter);
+
+ // Called on UI thread just after creation, to add a ContextGetter to
+ // |context_getters_|.
+ void AddRequestContextGetter(net::URLRequestContextGetter* context_getter);
+
+ // Helper method to enable a callback that will be executed on the IO thread.
+ static void CallbackHelper(MessageHandler method,
+ scoped_refptr<IOThreadImpl> io_thread,
+ const base::ListValue* list);
+
+ // Called once the WebUI has been deleted (i.e. renderer went away), on the
+ // IO thread.
+ void Detach();
+
+ // Called when the WebUI is deleted. Prevents calling Javascript functions
+ // afterwards. Called on UI thread.
+ void OnWebUIDeleted();
+
+ //--------------------------------
+ // Javascript message handlers:
+ //--------------------------------
+
+ void OnRendererReady(const base::ListValue* list);
+
+ void OnGetNetInfo(const base::ListValue* list);
+ void OnReloadProxySettings(const base::ListValue* list);
+ void OnClearBadProxies(const base::ListValue* list);
+ void OnClearHostResolverCache(const base::ListValue* list);
+ void OnHSTSQuery(const base::ListValue* list);
+ void OnHSTSAdd(const base::ListValue* list);
+ void OnHSTSDelete(const base::ListValue* list);
+ void OnCloseIdleSockets(const base::ListValue* list);
+ void OnFlushSocketPools(const base::ListValue* list);
+#if defined(OS_WIN)
+ void OnGetServiceProviders(const base::ListValue* list);
+#endif
+ void OnSetCaptureMode(const base::ListValue* list);
+
+ // NetLog::ThreadSafeObserver implementation:
+ void OnAddEntry(const net::NetLogEntry& entry) override;
+
+ // Helper that calls g_browser.receive in the renderer, passing in |command|
+ // and |arg|. If the renderer is displaying a log file, the message will be
+ // ignored. Note that this can be called from any thread.
+ void SendJavascriptCommand(const std::string& command,
+ std::unique_ptr<base::Value> arg);
+
+ private:
+ friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
+ friend class base::DeleteHelper<IOThreadImpl>;
+
+ using ContextGetterList =
+ std::vector<scoped_refptr<net::URLRequestContextGetter>>;
+
+ ~IOThreadImpl() override;
+
+ // Adds |entry| to the queue of pending log entries to be sent to the page via
+ // Javascript. Must be called on the IO Thread. Also creates a delayed task
+ // that will call PostPendingEntries, if there isn't one already.
+ void AddEntryToQueue(std::unique_ptr<base::Value> entry);
+
+ // Sends all pending entries to the page via Javascript, and clears the list
+ // of pending entries. Sending multiple entries at once results in a
+ // significant reduction of CPU usage when a lot of events are happening.
+ // Must be called on the IO Thread.
+ void PostPendingEntries();
+
+ // Adds entries with the states of ongoing URL requests.
+ void PrePopulateEventList();
+
+ net::URLRequestContext* GetMainContext() {
+ return main_context_getter_->GetURLRequestContext();
+ }
+
+ // |info_sources| is an or'd together list of the net::NetInfoSources to
+ // send information about. Information is sent to Javascript in the form of
+ // a single dictionary with information about all requests sources.
+ void SendNetInfo(int info_sources);
+
+ // Pointer to the UI-thread message handler. Only access this from
+ // the UI thread.
+ base::WeakPtr<NetInternalsMessageHandler> handler_;
+
+ // The global IOThread, which contains the global NetLog to observer.
+ IOThread* io_thread_;
+
+ // The main URLRequestContextGetter for the tab's profile.
+ scoped_refptr<net::URLRequestContextGetter> main_context_getter_;
+
+ // True if the Web UI has been deleted. This is used to prevent calling
+ // Javascript functions after the Web UI is destroyed. On refresh, the
+ // messages can end up being sent to the refreshed page, causing duplicate
+ // or partial entries.
+ //
+ // This is only read and written to on the UI thread.
+ bool was_webui_deleted_;
+
+ // Log entries that have yet to be passed along to Javascript page. Non-NULL
+ // when and only when there is a pending delayed task to call
+ // PostPendingEntries. Read and written to exclusively on the IO Thread.
+ std::unique_ptr<base::ListValue> pending_entries_;
+
+ // Used for getting current status of URLRequests when net-internals is
+ // opened. |main_context_getter_| is automatically added on construction.
+ // Duplicates are allowed.
+ ContextGetterList context_getters_;
+
+ DISALLOW_COPY_AND_ASSIGN(IOThreadImpl);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// NetInternalsMessageHandler
+//
+////////////////////////////////////////////////////////////////////////////////
+
+NetInternalsMessageHandler::NetInternalsMessageHandler() {}
+
+NetInternalsMessageHandler::~NetInternalsMessageHandler() {
+ if (proxy_) {
+ proxy_->OnWebUIDeleted();
+ // Notify the handler on the IO thread that the renderer is gone.
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&IOThreadImpl::Detach, proxy_));
+ }
+}
+
+void NetInternalsMessageHandler::RegisterMessages() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+ proxy_ = new IOThreadImpl(this->AsWeakPtr(), g_browser_process->io_thread(),
+ profile->GetRequestContext());
+ proxy_->AddRequestContextGetter(
+ content::BrowserContext::GetDefaultStoragePartition(profile)->
+ GetMediaURLRequestContext());
+
+ web_ui()->RegisterMessageCallback(
+ "notifyReady",
+ base::Bind(&NetInternalsMessageHandler::OnRendererReady,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getNetInfo",
+ base::Bind(&IOThreadImpl::CallbackHelper,
+ &IOThreadImpl::OnGetNetInfo, proxy_));
+ web_ui()->RegisterMessageCallback(
+ "reloadProxySettings",
+ base::Bind(&IOThreadImpl::CallbackHelper,
+ &IOThreadImpl::OnReloadProxySettings, proxy_));
+ web_ui()->RegisterMessageCallback(
+ "clearBadProxies",
+ base::Bind(&IOThreadImpl::CallbackHelper,
+ &IOThreadImpl::OnClearBadProxies, proxy_));
+ web_ui()->RegisterMessageCallback(
+ "clearHostResolverCache",
+ base::Bind(&IOThreadImpl::CallbackHelper,
+ &IOThreadImpl::OnClearHostResolverCache, proxy_));
+ web_ui()->RegisterMessageCallback(
+ "hstsQuery",
+ base::Bind(&IOThreadImpl::CallbackHelper,
+ &IOThreadImpl::OnHSTSQuery, proxy_));
+ web_ui()->RegisterMessageCallback(
+ "hstsAdd",
+ base::Bind(&IOThreadImpl::CallbackHelper,
+ &IOThreadImpl::OnHSTSAdd, proxy_));
+ web_ui()->RegisterMessageCallback(
+ "hstsDelete",
+ base::Bind(&IOThreadImpl::CallbackHelper,
+ &IOThreadImpl::OnHSTSDelete, proxy_));
+ web_ui()->RegisterMessageCallback(
+ "closeIdleSockets",
+ base::Bind(&IOThreadImpl::CallbackHelper,
+ &IOThreadImpl::OnCloseIdleSockets, proxy_));
+ web_ui()->RegisterMessageCallback(
+ "flushSocketPools",
+ base::Bind(&IOThreadImpl::CallbackHelper,
+ &IOThreadImpl::OnFlushSocketPools, proxy_));
+#if defined(OS_WIN)
+ web_ui()->RegisterMessageCallback(
+ "getServiceProviders",
+ base::Bind(&IOThreadImpl::CallbackHelper,
+ &IOThreadImpl::OnGetServiceProviders, proxy_));
+#endif
+
+ web_ui()->RegisterMessageCallback(
+ "setCaptureMode", base::Bind(&IOThreadImpl::CallbackHelper,
+ &IOThreadImpl::OnSetCaptureMode, proxy_));
+ web_ui()->RegisterMessageCallback(
+ "clearBrowserCache",
+ base::Bind(&NetInternalsMessageHandler::OnClearBrowserCache,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getPrerenderInfo",
+ base::Bind(&NetInternalsMessageHandler::OnGetPrerenderInfo,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getHistoricNetworkStats",
+ base::Bind(&NetInternalsMessageHandler::OnGetHistoricNetworkStats,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getSessionNetworkStats",
+ base::Bind(&NetInternalsMessageHandler::OnGetSessionNetworkStats,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getExtensionInfo",
+ base::Bind(&NetInternalsMessageHandler::OnGetExtensionInfo,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getDataReductionProxyInfo",
+ base::Bind(&NetInternalsMessageHandler::OnGetDataReductionProxyInfo,
+ base::Unretained(this)));
+#if defined(OS_CHROMEOS)
+ web_ui()->RegisterMessageCallback(
+ "importONCFile",
+ base::Bind(&NetInternalsMessageHandler::OnImportONCFile,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "storeDebugLogs",
+ base::Bind(&NetInternalsMessageHandler::OnStoreDebugLogs,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setNetworkDebugMode",
+ base::Bind(&NetInternalsMessageHandler::OnSetNetworkDebugMode,
+ base::Unretained(this)));
+#endif
+}
+
+void NetInternalsMessageHandler::SendJavascriptCommand(
+ const std::string& command,
+ std::unique_ptr<base::Value> arg) {
+ std::unique_ptr<base::Value> command_value(new base::Value(command));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (arg) {
+ web_ui()->CallJavascriptFunctionUnsafe("g_browser.receive",
+ *command_value.get(), *arg.get());
+ } else {
+ web_ui()->CallJavascriptFunctionUnsafe("g_browser.receive",
+ *command_value.get());
+ }
+}
+
+void NetInternalsMessageHandler::OnRendererReady(const base::ListValue* list) {
+ IOThreadImpl::CallbackHelper(&IOThreadImpl::OnRendererReady, proxy_, list);
+}
+
+void NetInternalsMessageHandler::OnClearBrowserCache(
+ const base::ListValue* list) {
+ content::BrowsingDataRemover* remover =
+ Profile::GetBrowsingDataRemover(Profile::FromWebUI(web_ui()));
+ remover->Remove(base::Time(), base::Time::Max(),
+ content::BrowsingDataRemover::DATA_TYPE_CACHE,
+ content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB);
+ // BrowsingDataRemover deletes itself.
+}
+
+void NetInternalsMessageHandler::OnGetPrerenderInfo(
+ const base::ListValue* list) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ SendJavascriptCommand(
+ "receivedPrerenderInfo",
+ chrome_browser_net::GetPrerenderInfo(Profile::FromWebUI(web_ui())));
+}
+
+void NetInternalsMessageHandler::OnGetHistoricNetworkStats(
+ const base::ListValue* list) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ SendJavascriptCommand("receivedHistoricNetworkStats",
+ chrome_browser_net::GetHistoricNetworkStats(
+ Profile::FromWebUI(web_ui())));
+}
+
+void NetInternalsMessageHandler::OnGetSessionNetworkStats(
+ const base::ListValue* list) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ SendJavascriptCommand(
+ "receivedSessionNetworkStats",
+ chrome_browser_net::GetSessionNetworkStats(Profile::FromWebUI(web_ui())));
+}
+
+void NetInternalsMessageHandler::OnGetExtensionInfo(
+ const base::ListValue* list) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ SendJavascriptCommand(
+ "receivedExtensionInfo",
+ chrome_browser_net::GetExtensionInfo(Profile::FromWebUI(web_ui())));
+}
+
+void NetInternalsMessageHandler::OnGetDataReductionProxyInfo(
+ const base::ListValue* list) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ SendJavascriptCommand("receivedDataReductionProxyInfo",
+ chrome_browser_net::GetDataReductionProxyInfo(
+ Profile::FromWebUI(web_ui())));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// NetInternalsMessageHandler::IOThreadImpl
+//
+////////////////////////////////////////////////////////////////////////////////
+
+NetInternalsMessageHandler::IOThreadImpl::IOThreadImpl(
+ const base::WeakPtr<NetInternalsMessageHandler>& handler,
+ IOThread* io_thread,
+ net::URLRequestContextGetter* main_context_getter)
+ : handler_(handler),
+ io_thread_(io_thread),
+ main_context_getter_(main_context_getter),
+ was_webui_deleted_(false) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ AddRequestContextGetter(main_context_getter);
+}
+
+NetInternalsMessageHandler::IOThreadImpl::~IOThreadImpl() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::AddRequestContextGetter(
+ net::URLRequestContextGetter* context_getter) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ context_getters_.push_back(context_getter);
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::CallbackHelper(
+ MessageHandler method,
+ scoped_refptr<IOThreadImpl> io_thread,
+ const base::ListValue* list) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ // We need to make a copy of the value in order to pass it over to the IO
+ // thread. |list_copy| will be deleted when the task is destroyed. The called
+ // |method| cannot take ownership of |list_copy|.
+ base::ListValue* list_copy =
+ (list && list->GetSize()) ? list->DeepCopy() : nullptr;
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::BindOnce(method, io_thread, base::Owned(list_copy)));
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::Detach() {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ // Unregister with network stack to observe events.
+ if (net_log())
+ net_log()->DeprecatedRemoveObserver(this);
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::OnWebUIDeleted() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ was_webui_deleted_ = true;
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::OnRendererReady(
+ const base::ListValue* list) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ // If currently watching the NetLog, temporarily stop watching it and flush
+ // pending events, so they won't appear before the status events created for
+ // currently active network objects below.
+ if (net_log()) {
+ net_log()->DeprecatedRemoveObserver(this);
+ PostPendingEntries();
+ }
+
+ SendJavascriptCommand(
+ "receivedConstants",
+ net_log::ChromeNetLog::GetConstants(
+ base::CommandLine::ForCurrentProcess()->GetCommandLineString(),
+ chrome::GetChannelString()));
+
+ PrePopulateEventList();
+
+ // Register with network stack to observe events.
+ io_thread_->net_log()->DeprecatedAddObserver(
+ this, net::NetLogCaptureMode::IncludeCookiesAndCredentials());
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::OnGetNetInfo(
+ const base::ListValue* list) {
+ DCHECK(list);
+ int info_sources;
+ if (!list->GetInteger(0, &info_sources))
+ return;
+ SendNetInfo(info_sources);
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::OnReloadProxySettings(
+ const base::ListValue* list) {
+ DCHECK(!list);
+ GetMainContext()->proxy_service()->ForceReloadProxyConfig();
+
+ // Cause the renderer to be notified of the new values.
+ SendNetInfo(net::NET_INFO_PROXY_SETTINGS);
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::OnClearBadProxies(
+ const base::ListValue* list) {
+ DCHECK(!list);
+ GetMainContext()->proxy_service()->ClearBadProxiesCache();
+
+ // Cause the renderer to be notified of the new values.
+ SendNetInfo(net::NET_INFO_BAD_PROXIES);
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::OnClearHostResolverCache(
+ const base::ListValue* list) {
+ DCHECK(!list);
+ net::HostCache* cache = GetHostResolverCache(GetMainContext());
+
+ if (cache)
+ cache->clear();
+
+ // Cause the renderer to be notified of the new values.
+ SendNetInfo(net::NET_INFO_HOST_RESOLVER);
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::OnHSTSQuery(
+ const base::ListValue* list) {
+ // |list| should be: [<domain to query>].
+ std::string domain;
+ CHECK(list->GetString(0, &domain));
+ auto result = base::MakeUnique<base::DictionaryValue>();
+
+ if (base::IsStringASCII(domain)) {
+ net::TransportSecurityState* transport_security_state =
+ GetMainContext()->transport_security_state();
+ if (transport_security_state) {
+ net::TransportSecurityState::STSState static_sts_state;
+ net::TransportSecurityState::PKPState static_pkp_state;
+ const bool found_static = transport_security_state->GetStaticDomainState(
+ domain, &static_sts_state, &static_pkp_state);
+ if (found_static) {
+ result->SetInteger("static_upgrade_mode",
+ static_cast<int>(static_sts_state.upgrade_mode));
+ result->SetBoolean("static_sts_include_subdomains",
+ static_sts_state.include_subdomains);
+ result->SetDouble("static_sts_observed",
+ static_sts_state.last_observed.ToDoubleT());
+ result->SetDouble("static_sts_expiry",
+ static_sts_state.expiry.ToDoubleT());
+ result->SetBoolean("static_pkp_include_subdomains",
+ static_pkp_state.include_subdomains);
+ result->SetDouble("static_pkp_observed",
+ static_pkp_state.last_observed.ToDoubleT());
+ result->SetDouble("static_pkp_expiry",
+ static_pkp_state.expiry.ToDoubleT());
+ result->SetString("static_spki_hashes",
+ HashesToBase64String(static_pkp_state.spki_hashes));
+ result->SetString("static_sts_domain", static_sts_state.domain);
+ result->SetString("static_pkp_domain", static_pkp_state.domain);
+ }
+
+ net::TransportSecurityState::STSState dynamic_sts_state;
+ net::TransportSecurityState::PKPState dynamic_pkp_state;
+ const bool found_sts_dynamic =
+ transport_security_state->GetDynamicSTSState(domain,
+ &dynamic_sts_state);
+
+ const bool found_pkp_dynamic =
+ transport_security_state->GetDynamicPKPState(domain,
+ &dynamic_pkp_state);
+ if (found_sts_dynamic) {
+ result->SetInteger("dynamic_upgrade_mode",
+ static_cast<int>(dynamic_sts_state.upgrade_mode));
+ result->SetBoolean("dynamic_sts_include_subdomains",
+ dynamic_sts_state.include_subdomains);
+ result->SetDouble("dynamic_sts_observed",
+ dynamic_sts_state.last_observed.ToDoubleT());
+ result->SetDouble("dynamic_sts_expiry",
+ dynamic_sts_state.expiry.ToDoubleT());
+ result->SetString("dynamic_sts_domain", dynamic_sts_state.domain);
+ }
+
+ if (found_pkp_dynamic) {
+ result->SetBoolean("dynamic_pkp_include_subdomains",
+ dynamic_pkp_state.include_subdomains);
+ result->SetDouble("dynamic_pkp_observed",
+ dynamic_pkp_state.last_observed.ToDoubleT());
+ result->SetDouble("dynamic_pkp_expiry",
+ dynamic_pkp_state.expiry.ToDoubleT());
+ result->SetString("dynamic_spki_hashes",
+ HashesToBase64String(dynamic_pkp_state.spki_hashes));
+ result->SetString("dynamic_pkp_domain", dynamic_pkp_state.domain);
+ }
+
+ result->SetBoolean(
+ "result", found_static || found_sts_dynamic || found_pkp_dynamic);
+ } else {
+ result->SetString("error", "no TransportSecurityState active");
+ }
+ } else {
+ result->SetString("error", "non-ASCII domain name");
+ }
+
+ SendJavascriptCommand("receivedHSTSResult", std::move(result));
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::OnHSTSAdd(
+ const base::ListValue* list) {
+ // |list| should be: [<domain to query>, <STS include subdomains>, <PKP
+ // include subdomains>, <key pins>].
+ std::string domain;
+ CHECK(list->GetString(0, &domain));
+ if (!base::IsStringASCII(domain)) {
+ // Silently fail. The user will get a helpful error if they query for the
+ // name.
+ return;
+ }
+ bool sts_include_subdomains;
+ CHECK(list->GetBoolean(1, &sts_include_subdomains));
+ bool pkp_include_subdomains;
+ CHECK(list->GetBoolean(2, &pkp_include_subdomains));
+ std::string hashes_str;
+ CHECK(list->GetString(3, &hashes_str));
+
+ net::TransportSecurityState* transport_security_state =
+ GetMainContext()->transport_security_state();
+ if (!transport_security_state)
+ return;
+
+ base::Time expiry = base::Time::Now() + base::TimeDelta::FromDays(1000);
+ net::HashValueVector hashes;
+ if (!hashes_str.empty()) {
+ if (!Base64StringToHashes(hashes_str, &hashes))
+ return;
+ }
+
+ transport_security_state->AddHSTS(domain, expiry, sts_include_subdomains);
+ transport_security_state->AddHPKP(domain, expiry, pkp_include_subdomains,
+ hashes, GURL());
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::OnHSTSDelete(
+ const base::ListValue* list) {
+ // |list| should be: [<domain to query>].
+ std::string domain;
+ CHECK(list->GetString(0, &domain));
+ if (!base::IsStringASCII(domain)) {
+ // There cannot be a unicode entry in the HSTS set.
+ return;
+ }
+ net::TransportSecurityState* transport_security_state =
+ GetMainContext()->transport_security_state();
+ if (!transport_security_state)
+ return;
+
+ transport_security_state->DeleteDynamicDataForHost(domain);
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::OnFlushSocketPools(
+ const base::ListValue* list) {
+ DCHECK(!list);
+ net::HttpNetworkSession* http_network_session =
+ GetHttpNetworkSession(GetMainContext());
+
+ if (http_network_session)
+ http_network_session->CloseAllConnections();
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::OnCloseIdleSockets(
+ const base::ListValue* list) {
+ DCHECK(!list);
+ net::HttpNetworkSession* http_network_session =
+ GetHttpNetworkSession(GetMainContext());
+
+ if (http_network_session)
+ http_network_session->CloseIdleConnections();
+}
+
+#if defined(OS_WIN)
+void NetInternalsMessageHandler::IOThreadImpl::OnGetServiceProviders(
+ const base::ListValue* list) {
+ DCHECK(!list);
+ SendJavascriptCommand("receivedServiceProviders",
+ chrome_browser_net::GetWindowsServiceProviders());
+}
+#endif
+
+#if defined(OS_CHROMEOS)
+void NetInternalsMessageHandler::ImportONCFileToNSSDB(
+ const std::string& onc_blob,
+ const std::string& passcode,
+ net::NSSCertDatabase* nssdb) {
+ const user_manager::User* user =
+ chromeos::ProfileHelper::Get()->GetUserByProfile(
+ Profile::FromWebUI(web_ui()));
+
+ if (!user) {
+ std::string error = "User not found.";
+ SendJavascriptCommand("receivedONCFileParse",
+ base::MakeUnique<base::Value>(error));
+ return;
+ }
+
+ std::string error;
+ onc::ONCSource onc_source = onc::ONC_SOURCE_USER_IMPORT;
+ base::ListValue network_configs;
+ base::DictionaryValue global_network_config;
+ base::ListValue certificates;
+ if (!chromeos::onc::ParseAndValidateOncForImport(onc_blob,
+ onc_source,
+ passcode,
+ &network_configs,
+ &global_network_config,
+ &certificates)) {
+ error = "Errors occurred during the ONC parsing. ";
+ }
+
+ std::string network_error;
+ chromeos::onc::ImportNetworksForUser(user, network_configs, &network_error);
+ if (!network_error.empty())
+ error += network_error;
+
+ chromeos::onc::CertificateImporterImpl cert_importer(
+ BrowserThread::GetTaskRunnerForThread(BrowserThread::IO), nssdb);
+ cert_importer.ImportCertificates(
+ certificates,
+ onc_source,
+ base::Bind(&NetInternalsMessageHandler::OnCertificatesImported,
+ AsWeakPtr(),
+ error));
+}
+
+void NetInternalsMessageHandler::OnCertificatesImported(
+ const std::string& previous_error,
+ bool success,
+ const net::CertificateList& /* unused onc_trusted_certificates */) {
+ std::string error = previous_error;
+ if (!success)
+ error += "Some certificates couldn't be imported. ";
+
+ SendJavascriptCommand("receivedONCFileParse",
+ base::MakeUnique<base::Value>(error));
+}
+
+void NetInternalsMessageHandler::OnImportONCFile(
+ const base::ListValue* list) {
+ std::string onc_blob;
+ std::string passcode;
+ if (list->GetSize() != 2 ||
+ !list->GetString(0, &onc_blob) ||
+ !list->GetString(1, &passcode)) {
+ NOTREACHED();
+ }
+
+ GetNSSCertDatabaseForProfile(
+ Profile::FromWebUI(web_ui()),
+ base::Bind(&NetInternalsMessageHandler::ImportONCFileToNSSDB, AsWeakPtr(),
+ onc_blob, passcode));
+}
+
+void NetInternalsMessageHandler::OnStoreDebugLogs(const base::ListValue* list) {
+ DCHECK(list);
+
+ SendJavascriptCommand("receivedStoreDebugLogs",
+ base::MakeUnique<base::Value>("Creating log file..."));
+ Profile* profile = Profile::FromWebUI(web_ui());
+ const DownloadPrefs* const prefs = DownloadPrefs::FromBrowserContext(profile);
+ base::FilePath path = prefs->DownloadPath();
+ if (file_manager::util::IsUnderNonNativeLocalPath(profile, path))
+ path = prefs->GetDefaultDownloadDirectoryForProfile();
+ chromeos::DebugLogWriter::StoreLogs(
+ path,
+ true, // should_compress
+ base::Bind(&NetInternalsMessageHandler::OnStoreDebugLogsCompleted,
+ AsWeakPtr()));
+}
+
+void NetInternalsMessageHandler::OnStoreDebugLogsCompleted(
+ const base::FilePath& log_path, bool succeeded) {
+ std::string status;
+ if (succeeded)
+ status = "Created log file: " + log_path.BaseName().AsUTF8Unsafe();
+ else
+ status = "Failed to create log file";
+ SendJavascriptCommand("receivedStoreDebugLogs",
+ base::MakeUnique<base::Value>(status));
+}
+
+void NetInternalsMessageHandler::OnSetNetworkDebugMode(
+ const base::ListValue* list) {
+ std::string subsystem;
+ if (list->GetSize() != 1 || !list->GetString(0, &subsystem))
+ NOTREACHED();
+ chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->
+ SetDebugMode(
+ subsystem,
+ base::Bind(
+ &NetInternalsMessageHandler::OnSetNetworkDebugModeCompleted,
+ AsWeakPtr(),
+ subsystem));
+}
+
+void NetInternalsMessageHandler::OnSetNetworkDebugModeCompleted(
+ const std::string& subsystem,
+ bool succeeded) {
+ std::string status = succeeded ? "Debug mode is changed to "
+ : "Failed to change debug mode to ";
+ status += subsystem;
+ SendJavascriptCommand("receivedSetNetworkDebugMode",
+ base::MakeUnique<base::Value>(status));
+}
+#endif // defined(OS_CHROMEOS)
+
+void NetInternalsMessageHandler::IOThreadImpl::OnSetCaptureMode(
+ const base::ListValue* list) {
+ std::string capture_mode_string;
+ if (!list->GetString(0, &capture_mode_string)) {
+ NOTREACHED();
+ return;
+ }
+
+ // Convert the string to a NetLogCaptureMode.
+ net::NetLogCaptureMode mode;
+ if (capture_mode_string == "IncludeSocketBytes") {
+ mode = net::NetLogCaptureMode::IncludeSocketBytes();
+ } else if (capture_mode_string == "IncludeCookiesAndCredentials") {
+ mode = net::NetLogCaptureMode::IncludeCookiesAndCredentials();
+ } else {
+ NOTREACHED();
+ }
+
+ net_log()->SetObserverCaptureMode(this, mode);
+}
+
+// Note that unlike other methods of IOThreadImpl, this function
+// 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())));
+}
+
+// Note that this can be called from ANY THREAD.
+void NetInternalsMessageHandler::IOThreadImpl::SendJavascriptCommand(
+ const std::string& command,
+ std::unique_ptr<base::Value> arg) {
+ if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ if (handler_ && !was_webui_deleted_) {
+ // We check |handler_| in case it was deleted on the UI thread earlier
+ // while we were running on the IO thread.
+ handler_->SendJavascriptCommand(command, std::move(arg));
+ }
+ return;
+ }
+
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::BindOnce(&IOThreadImpl::SendJavascriptCommand,
+ this, command, base::Passed(&arg)));
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::AddEntryToQueue(
+ std::unique_ptr<base::Value> entry) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (!pending_entries_) {
+ pending_entries_.reset(new base::ListValue());
+ BrowserThread::PostDelayedTask(
+ BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&IOThreadImpl::PostPendingEntries, this),
+ base::TimeDelta::FromMilliseconds(kNetLogEventDelayMilliseconds));
+ }
+ pending_entries_->Append(std::move(entry));
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::PostPendingEntries() {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (pending_entries_)
+ SendJavascriptCommand("receivedLogEntries", std::move(pending_entries_));
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::PrePopulateEventList() {
+ // Using a set removes any duplicates.
+ std::set<net::URLRequestContext*> contexts;
+ for (const auto& getter : context_getters_)
+ contexts.insert(getter->GetURLRequestContext());
+ contexts.insert(io_thread_->globals()->proxy_script_fetcher_context.get());
+ contexts.insert(io_thread_->globals()->system_request_context.get());
+
+ // Add entries for ongoing network objects.
+ CreateNetLogEntriesForActiveObjects(contexts, this);
+}
+
+void NetInternalsMessageHandler::IOThreadImpl::SendNetInfo(int info_sources) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ SendJavascriptCommand("receivedNetInfo",
+ net::GetNetInfo(GetMainContext(), info_sources));
+}
+
+} // namespace
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// NetInternalsUI
+//
+////////////////////////////////////////////////////////////////////////////////
+
+NetInternalsUI::NetInternalsUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<NetInternalsMessageHandler>());
+
+ // Set up the chrome://net-internals/ source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreateNetInternalsHTMLSource());
+}
diff --git a/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui.h b/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui.h
new file mode 100644
index 00000000000..9349ef0c185
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui.h
@@ -0,0 +1,19 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_NET_INTERNALS_NET_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_NET_INTERNALS_NET_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+class NetInternalsUI : public content::WebUIController {
+ public:
+ explicit NetInternalsUI(content::WebUI* web_ui);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NetInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_NET_INTERNALS_NET_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc
new file mode 100644
index 00000000000..7ab14c99ba1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc
@@ -0,0 +1,389 @@
+// 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/ui/webui/net_internals/net_internals_ui_browsertest.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/io_thread.h"
+#include "chrome/browser/prerender/prerender_manager.h"
+#include "chrome/browser/prerender/prerender_manager_factory.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/net_internals/net_internals_ui.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/net_log/chrome_net_log.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "net/base/address_list.h"
+#include "net/base/net_errors.h"
+#include "net/base/network_change_notifier.h"
+#include "net/dns/host_cache.h"
+#include "net/dns/host_resolver.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_transaction_factory.h"
+#include "net/log/net_log.h"
+#include "net/log/net_log_event_type.h"
+#include "net/log/net_log_source_type.h"
+#include "net/log/net_log_with_source.h"
+#include "net/log/write_to_file_net_log_observer.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/request_handler_util.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using content::BrowserThread;
+using content::WebUIMessageHandler;
+
+namespace {
+
+// Called on IO thread. Adds an entry to the cache for the specified hostname.
+// Either |net_error| must be net::OK, or |address| must be NULL.
+void AddCacheEntryOnIOThread(net::URLRequestContextGetter* context_getter,
+ const std::string& hostname,
+ const std::string& ip_literal,
+ int net_error,
+ int expire_days_from_now) {
+ ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ net::URLRequestContext* context = context_getter->GetURLRequestContext();
+ net::HostCache* cache = context->host_resolver()->GetHostCache();
+ ASSERT_TRUE(cache);
+
+ net::HostCache::Key key(hostname, net::ADDRESS_FAMILY_UNSPECIFIED, 0);
+ base::TimeDelta ttl = base::TimeDelta::FromDays(expire_days_from_now);
+
+ net::AddressList address_list;
+ if (net_error == net::OK) {
+ // If |net_error| does not indicate an error, convert |ip_literal| to a
+ // net::AddressList, so it can be used with the cache.
+ int rv = net::ParseAddressList(ip_literal, hostname, &address_list);
+ ASSERT_EQ(net::OK, rv);
+ } else {
+ ASSERT_TRUE(ip_literal.empty());
+ }
+
+ // Add entry to the cache.
+ cache->Set(net::HostCache::Key(hostname, net::ADDRESS_FAMILY_UNSPECIFIED, 0),
+ net::HostCache::Entry(net_error, address_list),
+ base::TimeTicks::Now(),
+ ttl);
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// NetInternalsTest::MessageHandler
+////////////////////////////////////////////////////////////////////////////////
+
+// Class to handle messages from the renderer needed by certain tests.
+class NetInternalsTest::MessageHandler : public content::WebUIMessageHandler {
+ public:
+ explicit MessageHandler(NetInternalsTest* net_internals_test);
+
+ private:
+ void RegisterMessages() override;
+
+ // Runs NetInternalsTest.callback with the given value.
+ void RunJavascriptCallback(base::Value* value);
+
+ // Takes a string and provides the corresponding URL from the test server,
+ // which must already have been started.
+ void GetTestServerURL(const base::ListValue* list_value);
+
+ // Called on UI thread. Adds an entry to the cache for the specified
+ // hostname by posting a task to the IO thread. Takes the host name,
+ // ip address, net error code, and expiration time in days from now
+ // as parameters. If the error code indicates failure, the ip address
+ // must be an empty string.
+ void AddCacheEntry(const base::ListValue* list_value);
+
+ // Simulates a network change.
+ void ChangeNetwork(const base::ListValue* list_value);
+
+ // Opens the given URL in a new tab.
+ void LoadPage(const base::ListValue* list_value);
+
+ // Opens a page in a new tab that prerenders the given URL.
+ void PrerenderPage(const base::ListValue* list_value);
+
+ // Navigates to the prerender in the background tab. This assumes that
+ // there is a "Click()" function in the background tab which will navigate
+ // there, and that the background tab exists at slot 1.
+ void NavigateToPrerender(const base::ListValue* list_value);
+
+ // Creates an incognito browser. Once creation is complete, passes a
+ // message to the Javascript test harness.
+ void CreateIncognitoBrowser(const base::ListValue* list_value);
+
+ // Closes an incognito browser created with CreateIncognitoBrowser.
+ void CloseIncognitoBrowser(const base::ListValue* list_value);
+
+ // Creates a simple log using WriteToFileNetLogObserver, and returns it to
+ // the Javascript callback.
+ void GetNetLogFileContents(const base::ListValue* list_value);
+
+ // Changes the data reduction proxy mode. A boolean is assumed to exist at
+ // index 0 which enables the proxy is set to true.
+ void EnableDataReductionProxy(const base::ListValue* list_value);
+
+ Browser* browser() { return net_internals_test_->browser(); }
+
+ NetInternalsTest* net_internals_test_;
+ Browser* incognito_browser_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageHandler);
+};
+
+NetInternalsTest::MessageHandler::MessageHandler(
+ NetInternalsTest* net_internals_test)
+ : net_internals_test_(net_internals_test),
+ incognito_browser_(NULL) {
+}
+
+void NetInternalsTest::MessageHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("getTestServerURL",
+ base::Bind(&NetInternalsTest::MessageHandler::GetTestServerURL,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("addCacheEntry",
+ base::Bind(&NetInternalsTest::MessageHandler::AddCacheEntry,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "changeNetwork",
+ base::Bind(&NetInternalsTest::MessageHandler::ChangeNetwork,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("loadPage",
+ base::Bind(&NetInternalsTest::MessageHandler::LoadPage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("prerenderPage",
+ base::Bind(&NetInternalsTest::MessageHandler::PrerenderPage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("navigateToPrerender",
+ base::Bind(&NetInternalsTest::MessageHandler::NavigateToPrerender,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("createIncognitoBrowser",
+ base::Bind(&NetInternalsTest::MessageHandler::CreateIncognitoBrowser,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("closeIncognitoBrowser",
+ base::Bind(&NetInternalsTest::MessageHandler::CloseIncognitoBrowser,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("getNetLogFileContents",
+ base::Bind(
+ &NetInternalsTest::MessageHandler::GetNetLogFileContents,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("enableDataReductionProxy",
+ base::Bind(
+ &NetInternalsTest::MessageHandler::EnableDataReductionProxy,
+ base::Unretained(this)));
+}
+
+void NetInternalsTest::MessageHandler::RunJavascriptCallback(
+ base::Value* value) {
+ web_ui()->CallJavascriptFunctionUnsafe("NetInternalsTest.callback", *value);
+}
+
+void NetInternalsTest::MessageHandler::GetTestServerURL(
+ const base::ListValue* list_value) {
+ ASSERT_TRUE(net_internals_test_->StartTestServer());
+ std::string path;
+ ASSERT_TRUE(list_value->GetString(0, &path));
+ GURL url = net_internals_test_->embedded_test_server()->GetURL(path);
+ std::unique_ptr<base::Value> url_value(new base::Value(url.spec()));
+ RunJavascriptCallback(url_value.get());
+}
+
+void NetInternalsTest::MessageHandler::AddCacheEntry(
+ const base::ListValue* list_value) {
+ std::string hostname;
+ std::string ip_literal;
+ double net_error;
+ double expire_days_from_now;
+ ASSERT_TRUE(list_value->GetString(0, &hostname));
+ ASSERT_TRUE(list_value->GetString(1, &ip_literal));
+ ASSERT_TRUE(list_value->GetDouble(2, &net_error));
+ ASSERT_TRUE(list_value->GetDouble(3, &expire_days_from_now));
+ ASSERT_TRUE(browser());
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::BindOnce(
+ &AddCacheEntryOnIOThread,
+ base::RetainedRef(browser()->profile()->GetRequestContext()),
+ hostname, ip_literal, static_cast<int>(net_error),
+ static_cast<int>(expire_days_from_now)));
+}
+
+void NetInternalsTest::MessageHandler::ChangeNetwork(
+ const base::ListValue* list_value) {
+ net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+}
+
+void NetInternalsTest::MessageHandler::LoadPage(
+ const base::ListValue* list_value) {
+ std::string url;
+ ASSERT_TRUE(list_value->GetString(0, &url));
+ LOG(WARNING) << "url: [" << url << "]";
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL(url), WindowOpenDisposition::NEW_BACKGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_NONE);
+}
+
+void NetInternalsTest::MessageHandler::PrerenderPage(
+ const base::ListValue* list_value) {
+ std::string prerender_url;
+ ASSERT_TRUE(list_value->GetString(0, &prerender_url));
+ GURL loader_url =
+ net_internals_test_->CreatePrerenderLoaderUrl(GURL(prerender_url));
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL(loader_url), WindowOpenDisposition::NEW_BACKGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_NONE);
+}
+
+void NetInternalsTest::MessageHandler::NavigateToPrerender(
+ const base::ListValue* list_value) {
+ std::string url;
+ ASSERT_TRUE(list_value->GetString(0, &url));
+ content::RenderFrameHost* frame =
+ browser()->tab_strip_model()->GetWebContentsAt(1)->GetMainFrame();
+ frame->ExecuteJavaScriptForTests(
+ base::ASCIIToUTF16(base::StringPrintf("Click('%s')", url.c_str())));
+}
+
+void NetInternalsTest::MessageHandler::CreateIncognitoBrowser(
+ const base::ListValue* list_value) {
+ ASSERT_FALSE(incognito_browser_);
+ incognito_browser_ = net_internals_test_->CreateIncognitoBrowser();
+
+ // Tell the test harness that creation is complete.
+ base::Value command_value("onIncognitoBrowserCreatedForTest");
+ web_ui()->CallJavascriptFunctionUnsafe("g_browser.receive", command_value);
+}
+
+void NetInternalsTest::MessageHandler::CloseIncognitoBrowser(
+ const base::ListValue* list_value) {
+ ASSERT_TRUE(incognito_browser_);
+ incognito_browser_->tab_strip_model()->CloseAllTabs();
+ // Closing all a Browser's tabs will ultimately result in its destruction,
+ // thought it may not have been destroyed yet.
+ incognito_browser_ = NULL;
+}
+
+void NetInternalsTest::MessageHandler::GetNetLogFileContents(
+ const base::ListValue* list_value) {
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+ base::ScopedTempDir temp_directory;
+ ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
+ base::FilePath temp_file;
+ ASSERT_TRUE(
+ base::CreateTemporaryFileInDir(temp_directory.GetPath(), &temp_file));
+ base::ScopedFILE temp_file_handle(base::OpenFile(temp_file, "w"));
+ ASSERT_TRUE(temp_file_handle);
+
+ std::unique_ptr<base::Value> constants(net_log::ChromeNetLog::GetConstants(
+ base::CommandLine::ForCurrentProcess()->GetCommandLineString(),
+ chrome::GetChannelString()));
+ std::unique_ptr<net::WriteToFileNetLogObserver> net_log_logger(
+ new net::WriteToFileNetLogObserver());
+ net_log_logger->StartObserving(g_browser_process->net_log(),
+ std::move(temp_file_handle), constants.get(),
+ nullptr);
+ g_browser_process->net_log()->AddGlobalEntry(
+ net::NetLogEventType::NETWORK_IP_ADDRESSES_CHANGED);
+ net::NetLogWithSource net_log_with_source = net::NetLogWithSource::Make(
+ g_browser_process->net_log(), net::NetLogSourceType::URL_REQUEST);
+ net_log_with_source.BeginEvent(net::NetLogEventType::REQUEST_ALIVE);
+ net_log_logger->StopObserving(nullptr);
+ net_log_logger.reset();
+
+ std::string log_contents;
+ ASSERT_TRUE(base::ReadFileToString(temp_file, &log_contents));
+ ASSERT_GT(log_contents.length(), 0u);
+
+ std::unique_ptr<base::Value> log_contents_value(
+ new base::Value(log_contents));
+ RunJavascriptCallback(log_contents_value.get());
+}
+
+void NetInternalsTest::MessageHandler::EnableDataReductionProxy(
+ const base::ListValue* list_value) {
+ bool enable;
+ ASSERT_TRUE(list_value->GetBoolean(0, &enable));
+ browser()->profile()->GetPrefs()->SetBoolean(
+ prefs::kDataSaverEnabled, enable);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NetInternalsTest
+////////////////////////////////////////////////////////////////////////////////
+
+NetInternalsTest::NetInternalsTest()
+ : test_server_started_(false) {
+ message_handler_.reset(new MessageHandler(this));
+}
+
+NetInternalsTest::~NetInternalsTest() {
+}
+
+void NetInternalsTest::SetUpOnMainThread() {
+ WebUIBrowserTest::SetUpOnMainThread();
+ // Needed to test the prerender view.
+ prerender::PrerenderManager::SetMode(
+ prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
+ // Increase the memory allowed in a prerendered page above normal settings,
+ // as debug builds use more memory and often go over the usual limit.
+ Profile* profile = browser()->profile();
+ prerender::PrerenderManager* prerender_manager =
+ prerender::PrerenderManagerFactory::GetForBrowserContext(profile);
+ prerender_manager->mutable_config().max_bytes = 1000 * 1024 * 1024;
+
+ // Sample domain for SDCH-view test. Dictionaries for localhost/127.0.0.1
+ // are forbidden.
+ host_resolver()->AddRule("testdomain.com", "127.0.0.1");
+ host_resolver()->AddRule("sub.testdomain.com", "127.0.0.1");
+}
+
+content::WebUIMessageHandler* NetInternalsTest::GetMockMessageHandler() {
+ return message_handler_.get();
+}
+
+GURL NetInternalsTest::CreatePrerenderLoaderUrl(
+ const GURL& prerender_url) {
+ EXPECT_TRUE(StartTestServer());
+ base::StringPairs replacement_text;
+ replacement_text.push_back(
+ make_pair("REPLACE_WITH_PRERENDER_URL", prerender_url.spec()));
+ std::string replacement_path;
+ net::test_server::GetFilePathWithReplacements(
+ "/prerender/prerender_loader.html", replacement_text, &replacement_path);
+ GURL url_loader = embedded_test_server()->GetURL(replacement_path);
+ return url_loader;
+}
+
+bool NetInternalsTest::StartTestServer() {
+ if (test_server_started_)
+ return true;
+ test_server_started_ = embedded_test_server()->Start();
+
+ return test_server_started_;
+}
diff --git a/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.h b/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.h
new file mode 100644
index 00000000000..fcae5dae948
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.h
@@ -0,0 +1,43 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_NET_INTERNALS_NET_INTERNALS_UI_BROWSERTEST_H_
+#define CHROME_BROWSER_UI_WEBUI_NET_INTERNALS_NET_INTERNALS_UI_BROWSERTEST_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/test/base/web_ui_browser_test.h"
+
+class GURL;
+
+class NetInternalsTest : public WebUIBrowserTest {
+ protected:
+ NetInternalsTest();
+ ~NetInternalsTest() override;
+
+ private:
+ class MessageHandler;
+
+ // InProcessBrowserTest overrides.
+ void SetUpOnMainThread() override;
+
+ // WebUIBrowserTest implementation.
+ content::WebUIMessageHandler* GetMockMessageHandler() override;
+
+ GURL CreatePrerenderLoaderUrl(const GURL& prerender_url);
+
+ // Attempts to start the test server. Returns true on success or if the
+ // TestServer is already started.
+ bool StartTestServer();
+
+ std::unique_ptr<MessageHandler> message_handler_;
+
+ // True if the test server has already been successfully started.
+ bool test_server_started_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetInternalsTest);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_NET_INTERNALS_NET_INTERNALS_UI_BROWSERTEST_H_
diff --git a/chromium/chrome/browser/ui/webui/ntp/OWNERS b/chromium/chrome/browser/ui/webui/ntp/OWNERS
new file mode 100644
index 00000000000..df619791e08
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ntp/OWNERS
@@ -0,0 +1,5 @@
+estade@chromium.org
+dbeam@chromium.org
+
+# TEAM: ntp-dev@chromium.org
+# COMPONENT: UI>Browser>NewTabPage
diff --git a/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
new file mode 100644
index 00000000000..0b2364d867e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
@@ -0,0 +1,892 @@
+// 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/ui/webui/ntp/app_launcher_handler.h"
+
+#include <stddef.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "apps/metrics_names.h"
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/i18n/rtl.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/crx_installer.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_ui_util.h"
+#include "chrome/browser/extensions/launch_util.h"
+#include "chrome/browser/favicon/favicon_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/app_list_util.h"
+#include "chrome/browser/ui/apps/app_info_dialog.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/extensions/app_launch_params.h"
+#include "chrome/browser/ui/extensions/application_launch.h"
+#include "chrome/browser/ui/extensions/extension_enable_flow.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/extensions/extension_basic_info.h"
+#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
+#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
+#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"
+#include "chrome/grit/generated_resources.h"
+#include "components/favicon_base/favicon_types.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/common/favicon_url.h"
+#include "extensions/browser/app_sorting.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/management_policy.h"
+#include "extensions/browser/pref_names.h"
+#include "extensions/browser/uninstall_reason.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_icon_set.h"
+#include "extensions/common/extension_set.h"
+#include "net/base/url_util.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "url/gurl.h"
+
+using content::WebContents;
+using extensions::AppSorting;
+using extensions::CrxInstaller;
+using extensions::Extension;
+using extensions::ExtensionPrefs;
+using extensions::ExtensionRegistry;
+using extensions::ExtensionSet;
+using extensions::ExtensionSystem;
+
+namespace {
+
+// The purpose of this enum is to track which page on the NTP is showing.
+// The lower 10 bits of kNtpShownPage are used for the index within the page
+// group, and the rest of the bits are used for the page group ID (defined
+// here).
+static const int kPageIdOffset = 10;
+enum {
+ INDEX_MASK = (1 << kPageIdOffset) - 1,
+ APPS_PAGE_ID = 2 << kPageIdOffset,
+};
+
+void RecordAppLauncherPromoHistogram(
+ apps::AppLauncherPromoHistogramValues value) {
+ DCHECK_LT(value, apps::APP_LAUNCHER_PROMO_MAX);
+ UMA_HISTOGRAM_ENUMERATION(
+ "Apps.AppLauncherPromo", value, apps::APP_LAUNCHER_PROMO_MAX);
+}
+
+} // namespace
+
+AppLauncherHandler::AppInstallInfo::AppInstallInfo() {}
+
+AppLauncherHandler::AppInstallInfo::~AppInstallInfo() {}
+
+AppLauncherHandler::AppLauncherHandler(ExtensionService* extension_service)
+ : extension_service_(extension_service),
+ ignore_changes_(false),
+ attempted_bookmark_app_install_(false),
+ has_loaded_apps_(false) {
+ if (IsAppLauncherEnabled())
+ RecordAppLauncherPromoHistogram(apps::APP_LAUNCHER_PROMO_ALREADY_INSTALLED);
+ else if (ShouldShowAppLauncherPromo())
+ RecordAppLauncherPromoHistogram(apps::APP_LAUNCHER_PROMO_SHOWN);
+}
+
+AppLauncherHandler::~AppLauncherHandler() {
+ ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))->RemoveObserver(this);
+}
+
+void AppLauncherHandler::CreateAppInfo(
+ const Extension* extension,
+ ExtensionService* service,
+ base::DictionaryValue* value) {
+ // The items which are to be written into |value| are also described in
+ // chrome/browser/resources/ntp4/page_list_view.js in @typedef for AppInfo.
+ // Please update it whenever you add or remove any keys here.
+ value->Clear();
+
+ // Communicate the kiosk flag so the apps page can disable showing the
+ // context menu in kiosk mode.
+ value->SetBoolean(
+ "kioskMode",
+ base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode));
+
+ // The Extension class 'helpfully' wraps bidi control characters that
+ // impede our ability to determine directionality.
+ base::string16 short_name = base::UTF8ToUTF16(extension->short_name());
+ base::i18n::UnadjustStringForLocaleDirection(&short_name);
+ NewTabUI::SetUrlTitleAndDirection(
+ value,
+ short_name,
+ extensions::AppLaunchInfo::GetFullLaunchURL(extension));
+
+ base::string16 name = base::UTF8ToUTF16(extension->name());
+ base::i18n::UnadjustStringForLocaleDirection(&name);
+ NewTabUI::SetFullNameAndDirection(name, value);
+
+ bool enabled =
+ service->IsExtensionEnabled(extension->id()) &&
+ !extensions::ExtensionRegistry::Get(service->GetBrowserContext())
+ ->GetExtensionById(extension->id(),
+ extensions::ExtensionRegistry::TERMINATED);
+ extensions::GetExtensionBasicInfo(extension, enabled, value);
+
+ value->SetBoolean("mayDisable", extensions::ExtensionSystem::Get(
+ service->profile())->management_policy()->UserMayModifySettings(
+ extension, NULL));
+
+ // Instead of setting grayscale here, we do it in apps_page.js.
+ GURL icon = extensions::ExtensionIconSource::GetIconURL(
+ extension, extension_misc::EXTENSION_ICON_LARGE,
+ ExtensionIconSet::MATCH_BIGGER, false);
+ DCHECK_NE(GURL(), icon);
+ value->SetString("icon", icon.spec());
+ value->SetInteger("launch_container",
+ extensions::AppLaunchInfo::GetLaunchContainer(extension));
+ ExtensionPrefs* prefs = ExtensionPrefs::Get(service->profile());
+ value->SetInteger("launch_type", extensions::GetLaunchType(prefs, extension));
+ value->SetBoolean("is_component",
+ extension->location() == extensions::Manifest::COMPONENT);
+ value->SetBoolean("is_webstore",
+ extension->id() == extensions::kWebStoreAppId);
+
+ AppSorting* sorting = ExtensionSystem::Get(service->profile())->app_sorting();
+ syncer::StringOrdinal page_ordinal = sorting->GetPageOrdinal(extension->id());
+ if (!page_ordinal.IsValid()) {
+ // Make sure every app has a page ordinal (some predate the page ordinal).
+ // The webstore app should be on the first page.
+ page_ordinal = extension->id() == extensions::kWebStoreAppId ?
+ sorting->CreateFirstAppPageOrdinal() :
+ sorting->GetNaturalAppPageOrdinal();
+ sorting->SetPageOrdinal(extension->id(), page_ordinal);
+ }
+ value->SetInteger("page_index",
+ sorting->PageStringOrdinalAsInteger(page_ordinal));
+
+ syncer::StringOrdinal app_launch_ordinal =
+ sorting->GetAppLaunchOrdinal(extension->id());
+ if (!app_launch_ordinal.IsValid()) {
+ // Make sure every app has a launch ordinal (some predate the launch
+ // ordinal). The webstore's app launch ordinal is always set to the first
+ // position.
+ app_launch_ordinal = extension->id() == extensions::kWebStoreAppId ?
+ sorting->CreateFirstAppLaunchOrdinal(page_ordinal) :
+ sorting->CreateNextAppLaunchOrdinal(page_ordinal);
+ sorting->SetAppLaunchOrdinal(extension->id(), app_launch_ordinal);
+ }
+ value->SetString("app_launch_ordinal", app_launch_ordinal.ToInternalValue());
+}
+
+// static
+void AppLauncherHandler::GetLocalizedValues(Profile* profile,
+ base::DictionaryValue* values) {
+ PrefService* prefs = profile->GetPrefs();
+ int shown_page = prefs->GetInteger(prefs::kNtpShownPage);
+ values->SetInteger("shown_page_index", shown_page & INDEX_MASK);
+}
+
+// static
+void AppLauncherHandler::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterIntegerPref(prefs::kNtpShownPage, APPS_PAGE_ID);
+}
+
+void AppLauncherHandler::RegisterMessages() {
+ registrar_.Add(this, chrome::NOTIFICATION_APP_INSTALLED_TO_NTP,
+ content::Source<WebContents>(web_ui()->GetWebContents()));
+
+ // Some tests don't have a local state.
+#if BUILDFLAG(ENABLE_APP_LIST)
+ if (g_browser_process->local_state()) {
+ local_state_pref_change_registrar_.Init(g_browser_process->local_state());
+ local_state_pref_change_registrar_.Add(
+ prefs::kShowAppLauncherPromo,
+ base::Bind(&AppLauncherHandler::OnLocalStatePreferenceChanged,
+ base::Unretained(this)));
+ }
+#endif
+ web_ui()->RegisterMessageCallback("getApps",
+ base::Bind(&AppLauncherHandler::HandleGetApps,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("launchApp",
+ base::Bind(&AppLauncherHandler::HandleLaunchApp,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("setLaunchType",
+ base::Bind(&AppLauncherHandler::HandleSetLaunchType,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("uninstallApp",
+ base::Bind(&AppLauncherHandler::HandleUninstallApp,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("createAppShortcut",
+ base::Bind(&AppLauncherHandler::HandleCreateAppShortcut,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("showAppInfo",
+ base::Bind(&AppLauncherHandler::HandleShowAppInfo,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("reorderApps",
+ base::Bind(&AppLauncherHandler::HandleReorderApps,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("setPageIndex",
+ base::Bind(&AppLauncherHandler::HandleSetPageIndex,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("saveAppPageName",
+ base::Bind(&AppLauncherHandler::HandleSaveAppPageName,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("generateAppForLink",
+ base::Bind(&AppLauncherHandler::HandleGenerateAppForLink,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("stopShowingAppLauncherPromo",
+ base::Bind(&AppLauncherHandler::HandleStopShowingAppLauncherPromo,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("onLearnMore",
+ base::Bind(&AppLauncherHandler::HandleOnLearnMore,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("pageSelected",
+ base::Bind(&AppLauncherHandler::HandlePageSelected,
+ base::Unretained(this)));
+}
+
+void AppLauncherHandler::Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ if (type == chrome::NOTIFICATION_APP_INSTALLED_TO_NTP) {
+ highlight_app_id_ = *content::Details<const std::string>(details).ptr();
+ if (has_loaded_apps_)
+ SetAppToBeHighlighted();
+ return;
+ }
+
+ if (ignore_changes_ || !has_loaded_apps_)
+ return;
+
+ switch (type) {
+ case chrome::NOTIFICATION_APP_LAUNCHER_REORDERED: {
+ const std::string* id =
+ content::Details<const std::string>(details).ptr();
+ if (id) {
+ const Extension* extension =
+ extension_service_->GetInstalledExtension(*id);
+ if (!extension) {
+ // Extension could still be downloading or installing.
+ return;
+ }
+
+ base::DictionaryValue app_info;
+ CreateAppInfo(extension,
+ extension_service_,
+ &app_info);
+ web_ui()->CallJavascriptFunctionUnsafe("ntp.appMoved", app_info);
+ } else {
+ HandleGetApps(NULL);
+ }
+ break;
+ }
+ case extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR: {
+ CrxInstaller* crx_installer = content::Source<CrxInstaller>(source).ptr();
+ if (!Profile::FromWebUI(web_ui())->IsSameProfile(
+ crx_installer->profile())) {
+ return;
+ }
+ // Fall through.
+ }
+ case extensions::NOTIFICATION_EXTENSION_LOAD_ERROR: {
+ attempted_bookmark_app_install_ = false;
+ break;
+ }
+ default:
+ NOTREACHED();
+ }
+}
+
+void AppLauncherHandler::OnExtensionLoaded(
+ content::BrowserContext* browser_context,
+ const Extension* extension) {
+ if (!ShouldShow(extension))
+ return;
+
+ std::unique_ptr<base::DictionaryValue> app_info(GetAppInfo(extension));
+ if (!app_info.get())
+ return;
+
+ visible_apps_.insert(extension->id());
+ ExtensionPrefs* prefs = ExtensionPrefs::Get(extension_service_->profile());
+ base::Value highlight(prefs->IsFromBookmark(extension->id()) &&
+ attempted_bookmark_app_install_);
+ attempted_bookmark_app_install_ = false;
+ web_ui()->CallJavascriptFunctionUnsafe("ntp.appAdded", *app_info, highlight);
+}
+
+void AppLauncherHandler::OnExtensionUnloaded(
+ content::BrowserContext* browser_context,
+ const Extension* extension,
+ extensions::UnloadedExtensionReason reason) {
+ AppRemoved(extension, false);
+}
+
+void AppLauncherHandler::OnExtensionUninstalled(
+ content::BrowserContext* browser_context,
+ const Extension* extension,
+ extensions::UninstallReason reason) {
+ AppRemoved(extension, true);
+}
+
+void AppLauncherHandler::FillAppDictionary(base::DictionaryValue* dictionary) {
+ // CreateAppInfo and ClearOrdinals can change the extension prefs.
+ base::AutoReset<bool> auto_reset(&ignore_changes_, true);
+
+ auto installed_extensions = base::MakeUnique<base::ListValue>();
+ Profile* profile = Profile::FromWebUI(web_ui());
+ PrefService* prefs = profile->GetPrefs();
+
+ for (std::set<std::string>::iterator it = visible_apps_.begin();
+ it != visible_apps_.end(); ++it) {
+ const Extension* extension = extension_service_->GetInstalledExtension(*it);
+ if (extension && extensions::ui_util::ShouldDisplayInNewTabPage(
+ extension, profile)) {
+ installed_extensions->Append(GetAppInfo(extension));
+ }
+ }
+
+ dictionary->Set("apps", std::move(installed_extensions));
+
+ const base::ListValue* app_page_names =
+ prefs->GetList(prefs::kNtpAppPageNames);
+ if (!app_page_names || !app_page_names->GetSize()) {
+ ListPrefUpdate update(prefs, prefs::kNtpAppPageNames);
+ base::ListValue* list = update.Get();
+ list->Set(0, base::MakeUnique<base::Value>(
+ l10n_util::GetStringUTF16(IDS_APP_DEFAULT_PAGE_NAME)));
+ dictionary->Set("appPageNames", base::MakeUnique<base::Value>(*list));
+ } else {
+ dictionary->Set("appPageNames",
+ base::MakeUnique<base::Value>(*app_page_names));
+ }
+}
+
+std::unique_ptr<base::DictionaryValue> AppLauncherHandler::GetAppInfo(
+ const Extension* extension) {
+ std::unique_ptr<base::DictionaryValue> app_info(new base::DictionaryValue());
+ // CreateAppInfo can change the extension prefs.
+ base::AutoReset<bool> auto_reset(&ignore_changes_, true);
+ CreateAppInfo(extension, extension_service_, app_info.get());
+ return app_info;
+}
+
+void AppLauncherHandler::HandleGetApps(const base::ListValue* args) {
+ base::DictionaryValue dictionary;
+
+ // Tell the client whether to show the promo for this view. We don't do this
+ // in the case of PREF_CHANGED because:
+ //
+ // a) At that point in time, depending on the pref that changed, it can look
+ // like the set of apps installed has changed, and we will mark the promo
+ // expired.
+ // b) Conceptually, it doesn't really make sense to count a
+ // prefchange-triggered refresh as a promo 'view'.
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+ // The first time we load the apps we must add all current app to the list
+ // of apps visible on the NTP.
+ if (!has_loaded_apps_) {
+ ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
+ const ExtensionSet& enabled_set = registry->enabled_extensions();
+ for (extensions::ExtensionSet::const_iterator it = enabled_set.begin();
+ it != enabled_set.end(); ++it) {
+ visible_apps_.insert((*it)->id());
+ }
+
+ const ExtensionSet& disabled_set = registry->disabled_extensions();
+ for (ExtensionSet::const_iterator it = disabled_set.begin();
+ it != disabled_set.end(); ++it) {
+ visible_apps_.insert((*it)->id());
+ }
+
+ const ExtensionSet& terminated_set = registry->terminated_extensions();
+ for (ExtensionSet::const_iterator it = terminated_set.begin();
+ it != terminated_set.end(); ++it) {
+ visible_apps_.insert((*it)->id());
+ }
+ }
+
+ SetAppToBeHighlighted();
+ FillAppDictionary(&dictionary);
+ web_ui()->CallJavascriptFunctionUnsafe("ntp.getAppsCallback", dictionary);
+
+ // First time we get here we set up the observer so that we can tell update
+ // the apps as they change.
+ if (!has_loaded_apps_) {
+ base::Closure callback = base::Bind(
+ &AppLauncherHandler::OnExtensionPreferenceChanged,
+ base::Unretained(this));
+ extension_pref_change_registrar_.Init(
+ ExtensionPrefs::Get(profile)->pref_service());
+ extension_pref_change_registrar_.Add(
+ extensions::pref_names::kExtensions, callback);
+ extension_pref_change_registrar_.Add(prefs::kNtpAppPageNames, callback);
+
+ ExtensionRegistry::Get(profile)->AddObserver(this);
+ registrar_.Add(this, chrome::NOTIFICATION_APP_LAUNCHER_REORDERED,
+ content::Source<AppSorting>(
+ ExtensionSystem::Get(profile)->app_sorting()));
+ registrar_.Add(this,
+ extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR,
+ content::Source<CrxInstaller>(NULL));
+ registrar_.Add(this,
+ extensions::NOTIFICATION_EXTENSION_LOAD_ERROR,
+ content::Source<Profile>(profile));
+ }
+
+ has_loaded_apps_ = true;
+}
+
+void AppLauncherHandler::HandleLaunchApp(const base::ListValue* args) {
+ std::string extension_id;
+ CHECK(args->GetString(0, &extension_id));
+ double source = -1.0;
+ CHECK(args->GetDouble(1, &source));
+ GURL override_url;
+
+ extension_misc::AppLaunchBucket launch_bucket =
+ static_cast<extension_misc::AppLaunchBucket>(
+ static_cast<int>(source));
+ CHECK(launch_bucket >= 0 &&
+ launch_bucket < extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
+
+ const Extension* extension =
+ extension_service_->GetExtensionById(extension_id, false);
+
+ // Prompt the user to re-enable the application if disabled.
+ if (!extension) {
+ PromptToEnableApp(extension_id);
+ return;
+ }
+
+ Profile* profile = extension_service_->profile();
+
+ WindowOpenDisposition disposition =
+ args->GetSize() > 3 ? webui::GetDispositionFromClick(args, 3)
+ : WindowOpenDisposition::CURRENT_TAB;
+ if (extension_id != extensions::kWebStoreAppId) {
+ CHECK_NE(launch_bucket, extension_misc::APP_LAUNCH_BUCKET_INVALID);
+ extensions::RecordAppLaunchType(launch_bucket, extension->GetType());
+ } else {
+ extensions::RecordWebStoreLaunch();
+
+ if (args->GetSize() > 2) {
+ std::string source_value;
+ CHECK(args->GetString(2, &source_value));
+ if (!source_value.empty()) {
+ override_url = net::AppendQueryParameter(
+ extensions::AppLaunchInfo::GetFullLaunchURL(extension),
+ extension_urls::kWebstoreSourceField, source_value);
+ }
+ }
+ }
+
+ if (disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
+ disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB ||
+ disposition == WindowOpenDisposition::NEW_WINDOW) {
+ // TODO(jamescook): Proper support for background tabs.
+ AppLaunchParams params(profile, extension,
+ disposition == WindowOpenDisposition::NEW_WINDOW
+ ? extensions::LAUNCH_CONTAINER_WINDOW
+ : extensions::LAUNCH_CONTAINER_TAB,
+ disposition, extensions::SOURCE_NEW_TAB_PAGE);
+ params.override_url = override_url;
+ OpenApplication(params);
+ } else {
+ // To give a more "launchy" experience when using the NTP launcher, we close
+ // it automatically.
+ Browser* browser = chrome::FindBrowserWithWebContents(
+ web_ui()->GetWebContents());
+ WebContents* old_contents = NULL;
+ if (browser)
+ old_contents = browser->tab_strip_model()->GetActiveWebContents();
+
+ AppLaunchParams params = CreateAppLaunchParamsUserContainer(
+ profile, extension,
+ old_contents ? WindowOpenDisposition::CURRENT_TAB
+ : WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ extensions::SOURCE_NEW_TAB_PAGE);
+ params.override_url = override_url;
+ WebContents* new_contents = OpenApplication(params);
+
+ // This will also destroy the handler, so do not perform any actions after.
+ if (new_contents != old_contents && browser &&
+ browser->tab_strip_model()->count() > 1) {
+ chrome::CloseWebContents(browser, old_contents, true);
+ }
+ }
+}
+
+void AppLauncherHandler::HandleSetLaunchType(const base::ListValue* args) {
+ std::string extension_id;
+ double launch_type;
+ CHECK(args->GetString(0, &extension_id));
+ CHECK(args->GetDouble(1, &launch_type));
+
+ const Extension* extension =
+ extension_service_->GetExtensionById(extension_id, true);
+ if (!extension)
+ return;
+
+ // Don't update the page; it already knows about the launch type change.
+ base::AutoReset<bool> auto_reset(&ignore_changes_, true);
+
+ extensions::SetLaunchType(
+ Profile::FromWebUI(web_ui()), extension_id,
+ static_cast<extensions::LaunchType>(static_cast<int>(launch_type)));
+}
+
+void AppLauncherHandler::HandleUninstallApp(const base::ListValue* args) {
+ std::string extension_id;
+ CHECK(args->GetString(0, &extension_id));
+
+ const Extension* extension = extension_service_->GetInstalledExtension(
+ extension_id);
+ if (!extension)
+ return;
+
+ if (!extensions::ExtensionSystem::Get(extension_service_->profile())->
+ management_policy()->UserMayModifySettings(extension, NULL)) {
+ LOG(ERROR) << "Attempt to uninstall an extension that is non-usermanagable "
+ << "was made. Extension id : " << extension->id();
+ return;
+ }
+ if (!extension_id_prompting_.empty())
+ return; // Only one prompt at a time.
+
+ extension_id_prompting_ = extension_id;
+
+ bool dont_confirm = false;
+ if (args->GetBoolean(1, &dont_confirm) && dont_confirm) {
+ base::AutoReset<bool> auto_reset(&ignore_changes_, true);
+ // Do the uninstall work here.
+ extension_service_->UninstallExtension(
+ extension_id_prompting_, extensions::UNINSTALL_REASON_USER_INITIATED,
+ base::Bind(&base::DoNothing), nullptr);
+ CleanupAfterUninstall();
+ } else {
+ GetExtensionUninstallDialog()->ConfirmUninstall(
+ extension, extensions::UNINSTALL_REASON_USER_INITIATED,
+ extensions::UNINSTALL_SOURCE_CHROME_APPS_PAGE);
+ }
+}
+
+void AppLauncherHandler::HandleCreateAppShortcut(const base::ListValue* args) {
+ std::string extension_id;
+ CHECK(args->GetString(0, &extension_id));
+
+ const Extension* extension =
+ extension_service_->GetExtensionById(extension_id, true);
+ if (!extension)
+ return;
+
+ Browser* browser = chrome::FindBrowserWithWebContents(
+ web_ui()->GetWebContents());
+ chrome::ShowCreateChromeAppShortcutsDialog(
+ browser->window()->GetNativeWindow(), browser->profile(), extension,
+ base::Callback<void(bool)>());
+}
+
+void AppLauncherHandler::HandleShowAppInfo(const base::ListValue* args) {
+ std::string extension_id;
+ CHECK(args->GetString(0, &extension_id));
+
+ const Extension* extension =
+ extension_service_->GetExtensionById(extension_id, true);
+ if (!extension)
+ return;
+
+ UMA_HISTOGRAM_ENUMERATION("Apps.AppInfoDialog.Launches",
+ AppInfoLaunchSource::FROM_APPS_PAGE,
+ AppInfoLaunchSource::NUM_LAUNCH_SOURCES);
+
+ ShowAppInfoInNativeDialog(
+ web_ui()->GetWebContents(), GetAppInfoNativeDialogSize(),
+ Profile::FromWebUI(web_ui()), extension, base::Closure());
+}
+
+void AppLauncherHandler::HandleReorderApps(const base::ListValue* args) {
+ CHECK(args->GetSize() == 2);
+
+ std::string dragged_app_id;
+ const base::ListValue* app_order;
+ CHECK(args->GetString(0, &dragged_app_id));
+ CHECK(args->GetList(1, &app_order));
+
+ std::string predecessor_to_moved_ext;
+ std::string successor_to_moved_ext;
+ for (size_t i = 0; i < app_order->GetSize(); ++i) {
+ std::string value;
+ if (app_order->GetString(i, &value) && value == dragged_app_id) {
+ if (i > 0)
+ CHECK(app_order->GetString(i - 1, &predecessor_to_moved_ext));
+ if (i + 1 < app_order->GetSize())
+ CHECK(app_order->GetString(i + 1, &successor_to_moved_ext));
+ break;
+ }
+ }
+
+ // Don't update the page; it already knows the apps have been reordered.
+ base::AutoReset<bool> auto_reset(&ignore_changes_, true);
+ ExtensionPrefs* extension_prefs =
+ ExtensionPrefs::Get(extension_service_->GetBrowserContext());
+ extension_prefs->SetAppDraggedByUser(dragged_app_id);
+ ExtensionSystem::Get(extension_service_->GetBrowserContext())
+ ->app_sorting()
+ ->OnExtensionMoved(dragged_app_id, predecessor_to_moved_ext,
+ successor_to_moved_ext);
+}
+
+void AppLauncherHandler::HandleSetPageIndex(const base::ListValue* args) {
+ AppSorting* app_sorting =
+ ExtensionSystem::Get(extension_service_->profile())->app_sorting();
+ std::string extension_id;
+ double page_index;
+ CHECK(args->GetString(0, &extension_id));
+ CHECK(args->GetDouble(1, &page_index));
+ const syncer::StringOrdinal& page_ordinal =
+ app_sorting->PageIntegerAsStringOrdinal(static_cast<size_t>(page_index));
+
+ // Don't update the page; it already knows the apps have been reordered.
+ base::AutoReset<bool> auto_reset(&ignore_changes_, true);
+ app_sorting->SetPageOrdinal(extension_id, page_ordinal);
+}
+
+void AppLauncherHandler::HandleSaveAppPageName(const base::ListValue* args) {
+ std::string name;
+ CHECK(args->GetString(0, &name));
+
+ double page_index;
+ CHECK(args->GetDouble(1, &page_index));
+
+ base::AutoReset<bool> auto_reset(&ignore_changes_, true);
+ PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+ ListPrefUpdate update(prefs, prefs::kNtpAppPageNames);
+ base::ListValue* list = update.Get();
+ list->Set(static_cast<size_t>(page_index),
+ base::MakeUnique<base::Value>(name));
+}
+
+void AppLauncherHandler::HandleGenerateAppForLink(const base::ListValue* args) {
+ std::string url;
+ CHECK(args->GetString(0, &url));
+ GURL launch_url(url);
+
+ base::string16 title;
+ CHECK(args->GetString(1, &title));
+
+ double page_index;
+ CHECK(args->GetDouble(2, &page_index));
+ AppSorting* app_sorting =
+ ExtensionSystem::Get(extension_service_->profile())->app_sorting();
+ const syncer::StringOrdinal& page_ordinal =
+ app_sorting->PageIntegerAsStringOrdinal(static_cast<size_t>(page_index));
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ favicon::FaviconService* favicon_service =
+ FaviconServiceFactory::GetForProfile(profile,
+ ServiceAccessType::EXPLICIT_ACCESS);
+ if (!favicon_service) {
+ LOG(ERROR) << "No favicon service";
+ return;
+ }
+
+ std::unique_ptr<AppInstallInfo> install_info(new AppInstallInfo());
+ install_info->title = base::CollapseWhitespace(
+ title, /* trim_sequences_with_line_breaks */ false);
+ install_info->app_url = launch_url;
+ install_info->page_ordinal = page_ordinal;
+
+ favicon_service->GetFaviconImageForPageURL(
+ launch_url,
+ base::Bind(&AppLauncherHandler::OnFaviconForApp,
+ base::Unretained(this),
+ base::Passed(&install_info)),
+ &cancelable_task_tracker_);
+}
+
+void AppLauncherHandler::HandleStopShowingAppLauncherPromo(
+ const base::ListValue* args) {
+#if BUILDFLAG(ENABLE_APP_LIST)
+ g_browser_process->local_state()->SetBoolean(
+ prefs::kShowAppLauncherPromo, false);
+ RecordAppLauncherPromoHistogram(apps::APP_LAUNCHER_PROMO_DISMISSED);
+#endif
+}
+
+void AppLauncherHandler::HandleOnLearnMore(const base::ListValue* args) {
+ RecordAppLauncherPromoHistogram(apps::APP_LAUNCHER_PROMO_LEARN_MORE);
+}
+
+void AppLauncherHandler::HandlePageSelected(const base::ListValue* args) {
+ double index_double;
+ CHECK(args->GetDouble(0, &index_double));
+ int index = static_cast<int>(index_double);
+
+ PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+ prefs->SetInteger(prefs::kNtpShownPage, APPS_PAGE_ID | index);
+}
+
+void AppLauncherHandler::OnFaviconForApp(
+ std::unique_ptr<AppInstallInfo> install_info,
+ const favicon_base::FaviconImageResult& image_result) {
+ std::unique_ptr<WebApplicationInfo> web_app(new WebApplicationInfo());
+ web_app->title = install_info->title;
+ web_app->app_url = install_info->app_url;
+
+ if (!image_result.image.IsEmpty()) {
+ WebApplicationInfo::IconInfo icon;
+ icon.data = image_result.image.AsBitmap();
+ icon.width = icon.data.width();
+ icon.height = icon.data.height();
+ web_app->icons.push_back(icon);
+ }
+
+ scoped_refptr<CrxInstaller> installer(
+ CrxInstaller::CreateSilent(extension_service_));
+ installer->set_error_on_unsupported_requirements(true);
+ installer->set_page_ordinal(install_info->page_ordinal);
+ installer->InstallWebApp(*web_app);
+ attempted_bookmark_app_install_ = true;
+}
+
+void AppLauncherHandler::SetAppToBeHighlighted() {
+ if (highlight_app_id_.empty())
+ return;
+
+ base::Value app_id(highlight_app_id_);
+ web_ui()->CallJavascriptFunctionUnsafe("ntp.setAppToBeHighlighted", app_id);
+ highlight_app_id_.clear();
+}
+
+void AppLauncherHandler::OnExtensionPreferenceChanged() {
+ base::DictionaryValue dictionary;
+ FillAppDictionary(&dictionary);
+ web_ui()->CallJavascriptFunctionUnsafe("ntp.appsPrefChangeCallback",
+ dictionary);
+}
+
+void AppLauncherHandler::OnLocalStatePreferenceChanged() {
+#if BUILDFLAG(ENABLE_APP_LIST)
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ntp.appLauncherPromoPrefChangeCallback",
+ base::Value(g_browser_process->local_state()->GetBoolean(
+ prefs::kShowAppLauncherPromo)));
+#endif
+}
+
+void AppLauncherHandler::CleanupAfterUninstall() {
+ extension_id_prompting_.clear();
+}
+
+void AppLauncherHandler::PromptToEnableApp(const std::string& extension_id) {
+ if (!extension_id_prompting_.empty())
+ return; // Only one prompt at a time.
+
+ extension_id_prompting_ = extension_id;
+ extension_enable_flow_.reset(new ExtensionEnableFlow(
+ Profile::FromWebUI(web_ui()), extension_id, this));
+ extension_enable_flow_->StartForWebContents(web_ui()->GetWebContents());
+}
+
+void AppLauncherHandler::OnExtensionUninstallDialogClosed(
+ bool did_start_uninstall,
+ const base::string16& error) {
+ CleanupAfterUninstall();
+}
+
+void AppLauncherHandler::ExtensionEnableFlowFinished() {
+ DCHECK_EQ(extension_id_prompting_, extension_enable_flow_->extension_id());
+
+ // We bounce this off the NTP so the browser can update the apps icon.
+ // If we don't launch the app asynchronously, then the app's disabled
+ // icon disappears but isn't replaced by the enabled icon, making a poor
+ // visual experience.
+ base::Value app_id(extension_id_prompting_);
+ web_ui()->CallJavascriptFunctionUnsafe("ntp.launchAppAfterEnable", app_id);
+
+ extension_enable_flow_.reset();
+ extension_id_prompting_ = "";
+}
+
+void AppLauncherHandler::ExtensionEnableFlowAborted(bool user_initiated) {
+ DCHECK_EQ(extension_id_prompting_, extension_enable_flow_->extension_id());
+
+ // We record the histograms here because ExtensionUninstallCanceled is also
+ // called when the extension uninstall dialog is canceled.
+ const Extension* extension =
+ extension_service_->GetExtensionById(extension_id_prompting_, true);
+ std::string histogram_name = user_initiated ? "ReEnableCancel"
+ : "ReEnableAbort";
+ ExtensionService::RecordPermissionMessagesHistogram(
+ extension, histogram_name.c_str());
+
+ extension_enable_flow_.reset();
+ CleanupAfterUninstall();
+}
+
+extensions::ExtensionUninstallDialog*
+AppLauncherHandler::GetExtensionUninstallDialog() {
+ if (!extension_uninstall_dialog_.get()) {
+ Browser* browser = chrome::FindBrowserWithWebContents(
+ web_ui()->GetWebContents());
+ extension_uninstall_dialog_.reset(
+ extensions::ExtensionUninstallDialog::Create(
+ extension_service_->profile(),
+ browser->window()->GetNativeWindow(),
+ this));
+ }
+ return extension_uninstall_dialog_.get();
+}
+
+void AppLauncherHandler::AppRemoved(const Extension* extension,
+ bool is_uninstall) {
+ if (!ShouldShow(extension))
+ return;
+
+ std::unique_ptr<base::DictionaryValue> app_info(GetAppInfo(extension));
+ if (!app_info.get())
+ return;
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ntp.appRemoved", *app_info, base::Value(is_uninstall),
+ base::Value(!extension_id_prompting_.empty()));
+}
+
+bool AppLauncherHandler::ShouldShow(const Extension* extension) const {
+ if (ignore_changes_ || !has_loaded_apps_ || !extension->is_app())
+ return false;
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ return extensions::ui_util::ShouldDisplayInNewTabPage(extension, profile);
+}
diff --git a/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.h b/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.h
new file mode 100644
index 00000000000..e808463991e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ntp/app_launcher_handler.h
@@ -0,0 +1,237 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_NTP_APP_LAUNCHER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_NTP_APP_LAUNCHER_HANDLER_H_
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "apps/metrics_names.h"
+#include "base/macros.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "chrome/browser/extensions/extension_uninstall_dialog.h"
+#include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "components/favicon/core/favicon_service.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/sync/model/string_ordinal.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "extensions/browser/extension_registry_observer.h"
+#include "extensions/common/extension.h"
+
+class ExtensionEnableFlow;
+class ExtensionService;
+class PrefChangeRegistrar;
+class Profile;
+
+namespace favicon_base {
+struct FaviconImageResult;
+}
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+// The handler for Javascript messages related to the "apps" view.
+class AppLauncherHandler
+ : public content::WebUIMessageHandler,
+ public extensions::ExtensionUninstallDialog::Delegate,
+ public ExtensionEnableFlowDelegate,
+ public content::NotificationObserver,
+ public extensions::ExtensionRegistryObserver {
+ public:
+ explicit AppLauncherHandler(ExtensionService* extension_service);
+ ~AppLauncherHandler() override;
+
+ // Populate a dictionary with the information from an extension.
+ static void CreateAppInfo(
+ const extensions::Extension* extension,
+ ExtensionService* service,
+ base::DictionaryValue* value);
+
+ // Registers values (strings etc.) for the page.
+ static void GetLocalizedValues(Profile* profile,
+ base::DictionaryValue* values);
+
+ // Register per-profile preferences.
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ // WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ // content::NotificationObserver:
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ // extensions::ExtensionRegistryObserver:
+ void OnExtensionLoaded(content::BrowserContext* browser_context,
+ const extensions::Extension* extension) override;
+ void OnExtensionUnloaded(content::BrowserContext* browser_context,
+ const extensions::Extension* extension,
+ extensions::UnloadedExtensionReason reason) override;
+ void OnExtensionUninstalled(content::BrowserContext* browser_context,
+ const extensions::Extension* extension,
+ extensions::UninstallReason reason) override;
+
+ // Populate the given dictionary with all installed app info.
+ void FillAppDictionary(base::DictionaryValue* value);
+
+ // Create a dictionary value for the given extension. May return null, e.g. if
+ // the given extension is not an app.
+ std::unique_ptr<base::DictionaryValue> GetAppInfo(
+ const extensions::Extension* extension);
+
+ // Populate the given dictionary with the web store promo content.
+ void FillPromoDictionary(base::DictionaryValue* value);
+
+ // Handles the "launchApp" message with unused |args|.
+ void HandleGetApps(const base::ListValue* args);
+
+ // Handles the "launchApp" message with |args| containing [extension_id,
+ // source] with optional [url, disposition], |disposition| defaulting to
+ // CURRENT_TAB.
+ void HandleLaunchApp(const base::ListValue* args);
+
+ // Handles the "setLaunchType" message with args containing [extension_id,
+ // launch_type].
+ void HandleSetLaunchType(const base::ListValue* args);
+
+ // Handles the "uninstallApp" message with |args| containing [extension_id]
+ // and an optional bool to not confirm the uninstall when true, defaults to
+ // false.
+ void HandleUninstallApp(const base::ListValue* args);
+
+ // Handles the "createAppShortcut" message with |args| containing
+ // [extension_id].
+ void HandleCreateAppShortcut(const base::ListValue* args);
+
+ // Handles the "showAppInfo" message with |args| containing [extension_id].
+ void HandleShowAppInfo(const base::ListValue* args);
+
+ // Handles the "reorderApps" message with |args| containing [dragged_app_id,
+ // app_order].
+ void HandleReorderApps(const base::ListValue* args);
+
+ // Handles the "setPageIndex" message with |args| containing [extension_id,
+ // page_index].
+ void HandleSetPageIndex(const base::ListValue* args);
+
+ // Handles "saveAppPageName" message with |args| containing [name,
+ // page_index].
+ void HandleSaveAppPageName(const base::ListValue* args);
+
+ // Handles "generateAppForLink" message with |args| containing [url, title,
+ // page_index].
+ void HandleGenerateAppForLink(const base::ListValue* args);
+
+ // Handles "stopShowingAppLauncherPromo" message with unused |args|.
+ void HandleStopShowingAppLauncherPromo(const base::ListValue* args);
+
+ // Handles "learnMore" message with unused |args|.
+ void HandleOnLearnMore(const base::ListValue* args);
+
+ // Handles "pageSelected" message with |args| containing [page_index].
+ void HandlePageSelected(const base::ListValue* args);
+
+ private:
+ struct AppInstallInfo {
+ AppInstallInfo();
+ ~AppInstallInfo();
+
+ base::string16 title;
+ GURL app_url;
+ syncer::StringOrdinal page_ordinal;
+ };
+
+ // Reset some instance flags we use to track the currently uninstalling app.
+ void CleanupAfterUninstall();
+
+ // Prompts the user to re-enable the app for |extension_id|.
+ void PromptToEnableApp(const std::string& extension_id);
+
+ // ExtensionUninstallDialog::Delegate:
+ void OnExtensionUninstallDialogClosed(bool did_start_uninstall,
+ const base::string16& error) override;
+
+ // ExtensionEnableFlowDelegate:
+ void ExtensionEnableFlowFinished() override;
+ void ExtensionEnableFlowAborted(bool user_initiated) override;
+
+ // Returns the ExtensionUninstallDialog object for this class, creating it if
+ // needed.
+ extensions::ExtensionUninstallDialog* GetExtensionUninstallDialog();
+
+ // Continuation for installing a bookmark app after favicon lookup.
+ void OnFaviconForApp(std::unique_ptr<AppInstallInfo> install_info,
+ const favicon_base::FaviconImageResult& image_result);
+
+ // Sends |highlight_app_id_| to the js.
+ void SetAppToBeHighlighted();
+
+ void OnExtensionPreferenceChanged();
+
+ void OnLocalStatePreferenceChanged();
+
+ // Called when an app is removed (unloaded or uninstalled). Updates the UI.
+ void AppRemoved(const extensions::Extension* extension, bool is_uninstall);
+
+ // True if the extension should be displayed.
+ bool ShouldShow(const extensions::Extension* extension) const;
+
+ // The apps are represented in the extensions model, which
+ // outlives us since it's owned by our containing profile.
+ ExtensionService* const extension_service_;
+
+ // We monitor changes to the extension system so that we can reload the apps
+ // when necessary.
+ content::NotificationRegistrar registrar_;
+
+ // Monitor extension preference changes so that the Web UI can be notified.
+ PrefChangeRegistrar extension_pref_change_registrar_;
+
+ // Monitor the local state pref to control the app launcher promo.
+ PrefChangeRegistrar local_state_pref_change_registrar_;
+
+ // Used to show confirmation UI for uninstalling extensions in incognito mode.
+ std::unique_ptr<extensions::ExtensionUninstallDialog>
+ extension_uninstall_dialog_;
+
+ // Used to show confirmation UI for enabling extensions.
+ std::unique_ptr<ExtensionEnableFlow> extension_enable_flow_;
+
+ // The ids of apps to show on the NTP.
+ std::set<std::string> visible_apps_;
+
+ // The id of the extension we are prompting the user about (either enable or
+ // uninstall).
+ std::string extension_id_prompting_;
+
+ // When true, we ignore changes to the underlying data rather than immediately
+ // refreshing. This is useful when making many batch updates to avoid flicker.
+ bool ignore_changes_;
+
+ // When true, we have attempted to install a bookmark app, and are still
+ // waiting to hear about success or failure from the extensions system.
+ bool attempted_bookmark_app_install_;
+
+ // True if we have executed HandleGetApps() at least once.
+ bool has_loaded_apps_;
+
+ // The ID of the app to be highlighted on the NTP (i.e. shown on the page
+ // and pulsed). This is done for new installs. The actual higlighting occurs
+ // when the app is added to the page (via getAppsCallback or appAdded).
+ std::string highlight_app_id_;
+
+ // Used for favicon loading tasks.
+ base::CancelableTaskTracker cancelable_task_tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppLauncherHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_NTP_APP_LAUNCHER_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.cc b/chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.cc
new file mode 100644
index 00000000000..70497099008
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.cc
@@ -0,0 +1,41 @@
+// 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/ui/webui/ntp/app_resource_cache_factory.h"
+
+#include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/themes/theme_service_factory.h"
+#include "chrome/browser/ui/webui/ntp/ntp_resource_cache.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+
+// static
+NTPResourceCache* AppResourceCacheFactory::GetForProfile(Profile* profile) {
+ return static_cast<NTPResourceCache*>(
+ GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+// static
+AppResourceCacheFactory* AppResourceCacheFactory::GetInstance() {
+ return base::Singleton<AppResourceCacheFactory>::get();
+}
+
+AppResourceCacheFactory::AppResourceCacheFactory()
+ : BrowserContextKeyedServiceFactory(
+ "AppResourceCache",
+ BrowserContextDependencyManager::GetInstance()) {
+ DependsOn(ThemeServiceFactory::GetInstance());
+}
+
+AppResourceCacheFactory::~AppResourceCacheFactory() {}
+
+KeyedService* AppResourceCacheFactory::BuildServiceInstanceFor(
+ content::BrowserContext* profile) const {
+ return new NTPResourceCache(static_cast<Profile*>(profile));
+}
+
+content::BrowserContext* AppResourceCacheFactory::GetBrowserContextToUse(
+ content::BrowserContext* context) const {
+ return chrome::GetBrowserContextRedirectedInIncognito(context);
+}
diff --git a/chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.h b/chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.h
new file mode 100644
index 00000000000..fc4b5f96d3c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ntp/app_resource_cache_factory.h
@@ -0,0 +1,36 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_NTP_APP_RESOURCE_CACHE_FACTORY_H_
+#define CHROME_BROWSER_UI_WEBUI_NTP_APP_RESOURCE_CACHE_FACTORY_H_
+
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class NTPResourceCache;
+class Profile;
+
+// Singleton that owns NTPResourceCaches used by the apps launcher page and
+// associates them with Profiles. Listens for the Profile's destruction
+// notification and cleans up the associated ThemeService.
+class AppResourceCacheFactory : public BrowserContextKeyedServiceFactory {
+ public:
+ static NTPResourceCache* GetForProfile(Profile* profile);
+
+ static AppResourceCacheFactory* GetInstance();
+
+ private:
+ friend struct base::DefaultSingletonTraits<AppResourceCacheFactory>;
+
+ AppResourceCacheFactory();
+ ~AppResourceCacheFactory() override;
+
+ // BrowserContextKeyedServiceFactory:
+ KeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* profile) const override;
+ content::BrowserContext* GetBrowserContextToUse(
+ content::BrowserContext* context) const override;
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_NTP_APP_RESOURCE_CACHE_FACTORY_H_
diff --git a/chromium/chrome/browser/ui/webui/ntp/core_app_launcher_handler.cc b/chromium/chrome/browser/ui/webui/ntp/core_app_launcher_handler.cc
new file mode 100644
index 00000000000..42573605dd9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ntp/core_app_launcher_handler.cc
@@ -0,0 +1,70 @@
+// 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/ntp/core_app_launcher_handler.h"
+
+#include "base/bind.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/extensions/extension_metrics.h"
+#include "chrome/common/pref_names.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/extension_set.h"
+#include "extensions/common/manifest.h"
+#include "net/base/escape.h"
+
+namespace {
+const net::UnescapeRule::Type kUnescapeRules =
+ net::UnescapeRule::NORMAL | net::UnescapeRule::PATH_SEPARATORS |
+ net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS;
+}
+
+CoreAppLauncherHandler::CoreAppLauncherHandler() {}
+
+CoreAppLauncherHandler::~CoreAppLauncherHandler() {}
+
+// static
+void CoreAppLauncherHandler::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterListPref(prefs::kNtpAppPageNames,
+ user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+}
+
+void CoreAppLauncherHandler::HandleRecordAppLaunchByUrl(
+ const base::ListValue* args) {
+ std::string url;
+ CHECK(args->GetString(0, &url));
+ double source;
+ CHECK(args->GetDouble(1, &source));
+
+ extension_misc::AppLaunchBucket bucket =
+ static_cast<extension_misc::AppLaunchBucket>(static_cast<int>(source));
+ CHECK(source < extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
+
+ RecordAppLaunchByUrl(Profile::FromWebUI(web_ui()), url, bucket);
+}
+
+void CoreAppLauncherHandler::RecordAppLaunchByUrl(
+ Profile* profile,
+ std::string escaped_url,
+ extension_misc::AppLaunchBucket bucket) {
+ CHECK(bucket != extension_misc::APP_LAUNCH_BUCKET_INVALID);
+
+ GURL url(net::UnescapeURLComponent(escaped_url, kUnescapeRules));
+ if (!extensions::ExtensionRegistry::Get(profile)
+ ->enabled_extensions().GetAppByURL(url)) {
+ return;
+ }
+
+ extensions::RecordAppLaunchType(bucket,
+ extensions::Manifest::TYPE_HOSTED_APP);
+}
+
+void CoreAppLauncherHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("recordAppLaunchByURL",
+ base::Bind(&CoreAppLauncherHandler::HandleRecordAppLaunchByUrl,
+ base::Unretained(this)));
+}
diff --git a/chromium/chrome/browser/ui/webui/ntp/core_app_launcher_handler.h b/chromium/chrome/browser/ui/webui/ntp/core_app_launcher_handler.h
new file mode 100644
index 00000000000..fd1cb0dfe3d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ntp/core_app_launcher_handler.h
@@ -0,0 +1,49 @@
+// 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_NTP_CORE_APP_LAUNCHER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_NTP_CORE_APP_LAUNCHER_HANDLER_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "extensions/common/extension.h"
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+class Profile;
+
+class CoreAppLauncherHandler : public content::WebUIMessageHandler {
+ public:
+ CoreAppLauncherHandler();
+ ~CoreAppLauncherHandler() override;
+
+ // Register app launcher preferences.
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ private:
+ // Callback for the "recordAppLaunchByUrl" message. Takes an escaped URL and
+ // a launch source(integer), and if the URL represents an app, records the
+ // action for UMA.
+ void HandleRecordAppLaunchByUrl(const base::ListValue* args);
+
+ // Records an app launch in the corresponding |bucket| of the app launch
+ // histogram if the |escaped_url| corresponds to an installed app.
+ void RecordAppLaunchByUrl(Profile* profile,
+ std::string escaped_url,
+ extension_misc::AppLaunchBucket bucket);
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ DISALLOW_COPY_AND_ASSIGN(CoreAppLauncherHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_NTP_CORE_APP_LAUNCHER_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/ntp/new_tab_ui.cc b/chromium/chrome/browser/ui/webui/ntp/new_tab_ui.cc
new file mode 100644
index 00000000000..fbd429bc253
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ntp/new_tab_ui.cc
@@ -0,0 +1,214 @@
+// 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/ui/webui/ntp/new_tab_ui.h"
+
+#include <memory>
+#include <string>
+
+#include "base/i18n/rtl.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
+#include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
+#include "chrome/browser/ui/webui/ntp/ntp_resource_cache.h"
+#include "chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.h"
+#include "chrome/browser/ui/webui/theme_handler.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "components/bookmarks/common/bookmark_pref_names.h"
+#include "components/prefs/pref_service.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/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+namespace {
+
+// Strings sent to the page via jstemplates used to set the direction of the
+// HTML document based on locale.
+const char kRTLHtmlTextDirection[] = "rtl";
+const char kLTRHtmlTextDirection[] = "ltr";
+
+const char* GetHtmlTextDirection(const base::string16& text) {
+ if (base::i18n::IsRTL() && base::i18n::StringContainsStrongRTLChars(text))
+ return kRTLHtmlTextDirection;
+ else
+ return kLTRHtmlTextDirection;
+}
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// NewTabUI
+
+NewTabUI::NewTabUI(content::WebUI* web_ui) : content::WebUIController(web_ui) {
+ web_ui->OverrideTitle(l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
+
+ Profile* profile = GetProfile();
+
+ if (!profile->IsGuestSession())
+ web_ui->AddMessageHandler(base::MakeUnique<ThemeHandler>());
+
+ // content::URLDataSource assumes the ownership of the html source.
+ content::URLDataSource::Add(
+ profile, new NewTabHTMLSource(profile->GetOriginalProfile()));
+
+ pref_change_registrar_.Init(profile->GetPrefs());
+ pref_change_registrar_.Add(bookmarks::prefs::kShowBookmarkBar,
+ base::Bind(&NewTabUI::OnShowBookmarkBarChanged,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(
+ prefs::kWebKitDefaultFontSize,
+ base::Bind(&NewTabUI::OnDefaultFontSizeChanged, base::Unretained(this)));
+}
+
+NewTabUI::~NewTabUI() {}
+
+void NewTabUI::OnShowBookmarkBarChanged() {
+ base::Value attached(
+ GetProfile()->GetPrefs()->GetBoolean(bookmarks::prefs::kShowBookmarkBar)
+ ? "true"
+ : "false");
+ web_ui()->CallJavascriptFunctionUnsafe("ntp.setBookmarkBarAttached",
+ attached);
+}
+
+void NewTabUI::OnDefaultFontSizeChanged() {
+ web_ui()->CallJavascriptFunctionUnsafe("ntp.defaultFontSizeChanged");
+}
+
+// static
+void NewTabUI::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ CoreAppLauncherHandler::RegisterProfilePrefs(registry);
+ AppLauncherHandler::RegisterProfilePrefs(registry);
+}
+
+// static
+bool NewTabUI::IsNewTab(const GURL& url) {
+ return url.GetOrigin() == GURL(chrome::kChromeUINewTabURL).GetOrigin();
+}
+
+// static
+void NewTabUI::SetUrlTitleAndDirection(base::DictionaryValue* dictionary,
+ const base::string16& title,
+ const GURL& gurl) {
+ dictionary->SetString("url", gurl.spec());
+
+ bool using_url_as_the_title = false;
+ base::string16 title_to_set(title);
+ if (title_to_set.empty()) {
+ using_url_as_the_title = true;
+ title_to_set = base::UTF8ToUTF16(gurl.spec());
+ }
+
+ // We set the "dir" attribute of the title, so that in RTL locales, a LTR
+ // title is rendered left-to-right and truncated from the right. For example,
+ // the title of http://msdn.microsoft.com/en-us/default.aspx is "MSDN:
+ // Microsoft developer network". In RTL locales, in the [New Tab] page, if
+ // the "dir" of this title is not specified, it takes Chrome UI's
+ // directionality. So the title will be truncated as "soft developer
+ // network". Setting the "dir" attribute as "ltr" renders the truncated title
+ // as "MSDN: Microsoft D...". As another example, the title of
+ // http://yahoo.com is "Yahoo!". In RTL locales, in the [New Tab] page, the
+ // title will be rendered as "!Yahoo" if its "dir" attribute is not set to
+ // "ltr".
+ std::string direction;
+ if (using_url_as_the_title)
+ direction = kLTRHtmlTextDirection;
+ else
+ direction = GetHtmlTextDirection(title);
+
+ dictionary->SetString("title", title_to_set);
+ dictionary->SetString("direction", direction);
+}
+
+// static
+void NewTabUI::SetFullNameAndDirection(const base::string16& full_name,
+ base::DictionaryValue* dictionary) {
+ dictionary->SetString("full_name", full_name);
+ dictionary->SetString("full_name_direction", GetHtmlTextDirection(full_name));
+}
+
+Profile* NewTabUI::GetProfile() const {
+ return Profile::FromWebUI(web_ui());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// NewTabHTMLSource
+
+NewTabUI::NewTabHTMLSource::NewTabHTMLSource(Profile* profile)
+ : profile_(profile) {
+}
+
+std::string NewTabUI::NewTabHTMLSource::GetSource() const {
+ return chrome::kChromeUINewTabHost;
+}
+
+void NewTabUI::NewTabHTMLSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ if (!path.empty() && path[0] != '#') {
+ // A path under new-tab was requested; it's likely a bad relative
+ // URL from the new tab page, but in any case it's an error.
+ NOTREACHED() << path << " should not have been requested on the NTP";
+ callback.Run(NULL);
+ return;
+ }
+
+ content::WebContents* web_contents = wc_getter.Run();
+ content::RenderProcessHost* render_host =
+ web_contents ? web_contents->GetRenderProcessHost() : nullptr;
+ NTPResourceCache::WindowType win_type = NTPResourceCache::GetWindowType(
+ profile_, render_host);
+ scoped_refptr<base::RefCountedMemory> html_bytes(
+ NTPResourceCacheFactory::GetForProfile(profile_)->
+ GetNewTabHTML(win_type));
+
+ callback.Run(html_bytes.get());
+}
+
+std::string NewTabUI::NewTabHTMLSource::GetMimeType(const std::string& resource)
+ const {
+ return "text/html";
+}
+
+bool NewTabUI::NewTabHTMLSource::ShouldReplaceExistingSource() const {
+ return false;
+}
+
+std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyScriptSrc()
+ const {
+ // 'unsafe-inline' and google resources are added to script-src.
+ return "script-src chrome://resources 'self' 'unsafe-eval' 'unsafe-inline' "
+ "*.google.com *.gstatic.com;";
+}
+
+std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyStyleSrc()
+ const {
+ return "style-src 'self' chrome://resources 'unsafe-inline' chrome://theme;";
+}
+
+std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyImgSrc()
+ const {
+ return "img-src chrome-search://thumb chrome-search://thumb2 "
+ "chrome-search://theme chrome://theme data:;";
+}
+
+std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyChildSrc()
+ const {
+ return "child-src chrome-search://most-visited;";
+}
+
+NewTabUI::NewTabHTMLSource::~NewTabHTMLSource() {}
diff --git a/chromium/chrome/browser/ui/webui/ntp/new_tab_ui.h b/chromium/chrome/browser/ui/webui/ntp/new_tab_ui.h
new file mode 100644
index 00000000000..e3bf3c65dd7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ntp/new_tab_ui.h
@@ -0,0 +1,86 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_NTP_NEW_TAB_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_NTP_NEW_TAB_UI_H_
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_ui_controller.h"
+
+class GURL;
+class Profile;
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+// The WebUIController used for the incognito and guest mode New Tab page.
+class NewTabUI : public content::WebUIController {
+ public:
+ explicit NewTabUI(content::WebUI* web_ui);
+ ~NewTabUI() override;
+
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ // Checks whether the given URL points to an NTP WebUI. Note that this only
+ // applies to incognito and guest mode NTPs - you probably want to check
+ // search::NavEntryIsInstantNTP too!
+ static bool IsNewTab(const GURL& url);
+
+ // TODO(dbeam): why are these static |Set*()| methods on NewTabUI?
+
+ // Adds "url", "title", and "direction" keys on incoming dictionary, setting
+ // title as the url as a fallback on empty title.
+ static void SetUrlTitleAndDirection(base::DictionaryValue* dictionary,
+ const base::string16& title,
+ const GURL& gurl);
+
+ // Adds "full_name" and "full_name_direction" keys on incoming dictionary.
+ static void SetFullNameAndDirection(const base::string16& full_name,
+ base::DictionaryValue* dictionary);
+
+ private:
+ class NewTabHTMLSource : public content::URLDataSource {
+ public:
+ explicit NewTabHTMLSource(Profile* profile);
+ ~NewTabHTMLSource() override;
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ std::string GetMimeType(const std::string&) const override;
+ bool ShouldReplaceExistingSource() const override;
+ std::string GetContentSecurityPolicyScriptSrc() const override;
+ std::string GetContentSecurityPolicyStyleSrc() const override;
+ std::string GetContentSecurityPolicyImgSrc() const override;
+ std::string GetContentSecurityPolicyChildSrc() const override;
+
+ private:
+ // Pointer back to the original profile.
+ Profile* profile_;
+
+ DISALLOW_COPY_AND_ASSIGN(NewTabHTMLSource);
+ };
+
+ void OnShowBookmarkBarChanged();
+ void OnDefaultFontSizeChanged();
+
+ Profile* GetProfile() const;
+
+ PrefChangeRegistrar pref_change_registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(NewTabUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_NTP_NEW_TAB_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/ntp/new_tab_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/ntp/new_tab_ui_browsertest.cc
new file mode 100644
index 00000000000..0e019c935b0
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ntp/new_tab_ui_browsertest.cc
@@ -0,0 +1,97 @@
+// 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 <stddef.h>
+
+#include "base/command_line.h"
+#include "base/logging.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/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 "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "url/gurl.h"
+
+using content::OpenURLParams;
+using content::Referrer;
+
+namespace {
+
+static bool had_console_errors = false;
+
+bool HandleMessage(int severity,
+ const char* file,
+ int line,
+ size_t message_start,
+ const std::string& str) {
+ if (severity == logging::LOG_ERROR && file && file == std::string("CONSOLE"))
+ had_console_errors = true;
+ return false;
+}
+
+} // namespace
+
+class NewTabUIBrowserTest : public InProcessBrowserTest {
+ public:
+ NewTabUIBrowserTest() {
+ logging::SetLogMessageHandler(&HandleMessage);
+ }
+
+ ~NewTabUIBrowserTest() override { logging::SetLogMessageHandler(NULL); }
+
+ void TearDown() override {
+ InProcessBrowserTest::TearDown();
+ ASSERT_FALSE(had_console_errors);
+ }
+};
+
+// Navigate to incognito NTP. Fails if there are console errors.
+IN_PROC_BROWSER_TEST_F(NewTabUIBrowserTest, ShowIncognito) {
+ ui_test_utils::NavigateToURL(CreateIncognitoBrowser(),
+ GURL(chrome::kChromeUINewTabURL));
+}
+
+class NewTabUIProcessPerTabTest : public NewTabUIBrowserTest {
+ public:
+ NewTabUIProcessPerTabTest() {}
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ command_line->AppendSwitch(switches::kProcessPerTab);
+ }
+};
+
+// Navigates away from NTP before it commits, in process-per-tab mode.
+// Ensures that we don't load the normal page in the NTP process (and thus
+// crash), as in http://crbug.com/69224.
+// If this flakes, use http://crbug.com/87200
+IN_PROC_BROWSER_TEST_F(NewTabUIProcessPerTabTest, NavBeforeNTPCommits) {
+ // Bring up a new tab page.
+ ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
+
+ // Navigate to chrome://hang/ to stall the process.
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL(content::kChromeUIHangURL),
+ WindowOpenDisposition::CURRENT_TAB, 0);
+
+ // Visit a normal URL in another NTP that hasn't committed.
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL(chrome::kChromeUINewTabURL),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB, 0);
+
+ // We don't use ui_test_utils::NavigateToURLWithDisposition because that waits
+ // for current loading to stop.
+ content::TestNavigationObserver observer(
+ browser()->tab_strip_model()->GetActiveWebContents());
+ browser()->OpenURL(OpenURLParams(
+ GURL("data:text/html,hello world"), Referrer(),
+ WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false));
+ observer.Wait();
+}
diff --git a/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc b/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
new file mode 100644
index 00000000000..444478cf76f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
@@ -0,0 +1,604 @@
+// Copyright 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/ui/webui/ntp/ntp_resource_cache.h"
+
+#include <string>
+
+#include "base/feature_list.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/themes/theme_properties.h"
+#include "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/themes/theme_service_factory.h"
+#include "chrome/browser/ui/apps/app_info_dialog.h"
+#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/chrome_features.h"
+#include "chrome/common/features.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/locale_settings.h"
+#include "chrome/grit/theme_resources.h"
+#include "components/bookmarks/common/bookmark_pref_names.h"
+#include "components/google/core/browser/google_util.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/render_process_host.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_urls.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/template_expressions.h"
+#include "ui/base/theme_provider.h"
+#include "ui/base/webui/jstemplate_builder.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "ui/gfx/animation/animation.h"
+#include "ui/gfx/color_utils.h"
+
+#if defined(OS_CHROMEOS)
+#include "ash/strings/grit/ash_strings.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#endif
+
+#if defined(OS_MACOSX)
+#include "chrome/browser/platform_util.h"
+#endif
+
+using content::BrowserThread;
+
+namespace {
+
+// The URL for the the Learn More page shown on incognito new tab.
+const char kLearnMoreIncognitoUrl[] =
+#if defined(OS_CHROMEOS)
+ "https://support.google.com/chromebook/?p=incognito";
+#else
+ "https://support.google.com/chrome/?p=incognito";
+#endif
+
+// The URL for the Learn More page shown on guest session new tab.
+const char kLearnMoreGuestSessionUrl[] =
+#if defined(OS_CHROMEOS)
+ "https://support.google.com/chromebook/?p=chromebook_guest";
+#else
+ "https://support.google.com/chrome/?p=ui_guest";
+#endif
+
+SkColor GetThemeColor(const ui::ThemeProvider& tp, int id) {
+ SkColor color = tp.GetColor(id);
+ // If web contents are being inverted because the system is in high-contrast
+ // mode, any system theme colors we use must be inverted too to cancel out.
+ return color_utils::IsInvertedColorScheme() ?
+ color_utils::InvertColor(color) : color;
+}
+
+// Get the CSS string for the background position on the new tab page for the
+// states when the bar is attached or detached.
+std::string GetNewTabBackgroundCSS(const ui::ThemeProvider& theme_provider,
+ bool bar_attached) {
+ // TODO(glen): This is a quick workaround to hide the notused.png image when
+ // no image is provided - we don't have time right now to figure out why
+ // this is painting as white.
+ // http://crbug.com/17593
+ if (!theme_provider.HasCustomImage(IDR_THEME_NTP_BACKGROUND)) {
+ return "-64px";
+ }
+
+ int alignment = theme_provider.GetDisplayProperty(
+ ThemeProperties::NTP_BACKGROUND_ALIGNMENT);
+
+ if (bar_attached)
+ return ThemeProperties::AlignmentToString(alignment);
+
+ if (alignment & ThemeProperties::ALIGN_TOP) {
+ // The bar is detached, so we must offset the background by the bar size
+ // if it's a top-aligned bar.
+ int offset = chrome::kNTPBookmarkBarHeight;
+
+ if (alignment & ThemeProperties::ALIGN_LEFT)
+ return "left " + base::IntToString(-offset) + "px";
+ else if (alignment & ThemeProperties::ALIGN_RIGHT)
+ return "right " + base::IntToString(-offset) + "px";
+ return "center " + base::IntToString(-offset) + "px";
+ }
+
+ return ThemeProperties::AlignmentToString(alignment);
+}
+
+// How the background image on the new tab page should be tiled (see tiling
+// masks in theme_service.h).
+std::string GetNewTabBackgroundTilingCSS(
+ const ui::ThemeProvider& theme_provider) {
+ int repeat_mode =
+ theme_provider.GetDisplayProperty(ThemeProperties::NTP_BACKGROUND_TILING);
+ return ThemeProperties::TilingToString(repeat_mode);
+}
+
+bool ShouldShowApps() {
+// Ash shows apps in app list thus should not show apps page in NTP4.
+#if defined(USE_ASH)
+ return false;
+#else
+ return true;
+#endif
+}
+
+} // namespace
+
+NTPResourceCache::NTPResourceCache(Profile* profile)
+ : profile_(profile), is_swipe_tracking_from_scroll_events_enabled_(false) {
+ registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
+ content::Source<ThemeService>(
+ ThemeServiceFactory::GetForProfile(profile)));
+
+ base::Closure callback = base::Bind(&NTPResourceCache::OnPreferenceChanged,
+ base::Unretained(this));
+
+ // Watch for pref changes that cause us to need to invalidate the HTML cache.
+ profile_pref_change_registrar_.Init(profile_->GetPrefs());
+ profile_pref_change_registrar_.Add(bookmarks::prefs::kShowBookmarkBar,
+ callback);
+ profile_pref_change_registrar_.Add(prefs::kNtpShownPage, callback);
+ profile_pref_change_registrar_.Add(prefs::kSignInPromoShowNTPBubble,
+ callback);
+ profile_pref_change_registrar_.Add(prefs::kHideWebStoreIcon, callback);
+
+ // Some tests don't have a local state.
+#if BUILDFLAG(ENABLE_APP_LIST)
+ if (g_browser_process->local_state()) {
+ local_state_pref_change_registrar_.Init(g_browser_process->local_state());
+ local_state_pref_change_registrar_.Add(prefs::kShowAppLauncherPromo,
+ callback);
+ local_state_pref_change_registrar_.Add(
+ prefs::kAppLauncherHasBeenEnabled, callback);
+ }
+#endif
+}
+
+NTPResourceCache::~NTPResourceCache() {}
+
+bool NTPResourceCache::NewTabHTMLNeedsRefresh() {
+#if defined(OS_MACOSX)
+ // Invalidate if the current value is different from the cached value.
+ bool is_enabled = platform_util::IsSwipeTrackingFromScrollEventsEnabled();
+ if (is_enabled != is_swipe_tracking_from_scroll_events_enabled_) {
+ is_swipe_tracking_from_scroll_events_enabled_ = is_enabled;
+ return true;
+ }
+#endif
+ return false;
+}
+
+NTPResourceCache::WindowType NTPResourceCache::GetWindowType(
+ Profile* profile, content::RenderProcessHost* render_host) {
+ if (profile->IsGuestSession()) {
+ return GUEST;
+ } else if (render_host) {
+ // Sometimes the |profile| is the parent (non-incognito) version of the user
+ // so we check the |render_host| if it is provided.
+ if (render_host->GetBrowserContext()->IsOffTheRecord())
+ return INCOGNITO;
+ } else if (profile->IsOffTheRecord()) {
+ return INCOGNITO;
+ }
+ return NORMAL;
+}
+
+base::RefCountedMemory* NTPResourceCache::GetNewTabHTML(WindowType win_type) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (win_type == GUEST) {
+ if (!new_tab_guest_html_)
+ CreateNewTabGuestHTML();
+ return new_tab_guest_html_.get();
+ }
+
+ if (win_type == INCOGNITO) {
+ if (!new_tab_incognito_html_)
+ CreateNewTabIncognitoHTML();
+ return new_tab_incognito_html_.get();
+ }
+
+ // Refresh the cached HTML if necessary.
+ // NOTE: NewTabHTMLNeedsRefresh() must be called every time the new tab
+ // HTML is fetched, because it needs to initialize cached values.
+ if (NewTabHTMLNeedsRefresh() || !new_tab_html_)
+ CreateNewTabHTML();
+ return new_tab_html_.get();
+}
+
+base::RefCountedMemory* NTPResourceCache::GetNewTabCSS(WindowType win_type) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ // Guest mode doesn't have theme-related CSS.
+ if (win_type == GUEST)
+ return nullptr;
+
+ if (win_type == INCOGNITO) {
+ if (!new_tab_incognito_css_)
+ CreateNewTabIncognitoCSS();
+ return new_tab_incognito_css_.get();
+ }
+
+ if (!new_tab_css_)
+ CreateNewTabCSS();
+ return new_tab_css_.get();
+}
+
+void NTPResourceCache::Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK_EQ(chrome::NOTIFICATION_BROWSER_THEME_CHANGED, type);
+
+ // Invalidate the cache.
+ Invalidate();
+}
+
+void NTPResourceCache::OnPreferenceChanged() {
+ // A change occurred to one of the preferences we care about, so flush the
+ // cache.
+ new_tab_incognito_html_ = nullptr;
+ new_tab_html_ = nullptr;
+ new_tab_css_ = nullptr;
+}
+
+// TODO(dbeam): why must Invalidate() and OnPreferenceChanged() both exist?
+void NTPResourceCache::Invalidate() {
+ new_tab_incognito_html_ = nullptr;
+ new_tab_html_ = nullptr;
+ new_tab_incognito_css_ = nullptr;
+ new_tab_css_ = nullptr;
+}
+
+void NTPResourceCache::CreateNewTabIncognitoHTML() {
+ const bool is_md_incognito_ntp_enabled =
+ base::FeatureList::IsEnabled(features::kMaterialDesignIncognitoNTP);
+
+ ui::TemplateReplacements replacements;
+ // Note: there's specific rules in CSS that look for this attribute's content
+ // being equal to "true" as a string.
+ replacements["bookmarkbarattached"] =
+ profile_->GetPrefs()->GetBoolean(bookmarks::prefs::kShowBookmarkBar)
+ ? "true"
+ : "false";
+
+ if (is_md_incognito_ntp_enabled) {
+ replacements["incognitoTabDescription"] =
+ l10n_util::GetStringUTF8(IDS_NEW_TAB_OTR_SUBTITLE);
+ replacements["incognitoTabHeading"] =
+ l10n_util::GetStringUTF8(IDS_NEW_TAB_OTR_TITLE);
+ replacements["incognitoTabWarning"] =
+ l10n_util::GetStringUTF8(IDS_NEW_TAB_OTR_VISIBLE);
+ replacements["learnMore"] =
+ l10n_util::GetStringUTF8(IDS_NEW_TAB_OTR_LEARN_MORE_LINK);
+ replacements["incognitoTabFeatures"] =
+ l10n_util::GetStringUTF8(IDS_NEW_TAB_OTR_NOT_SAVED);
+ } else {
+ replacements["incognitoTabDescription"] =
+ l10n_util::GetStringUTF8(IDS_NEW_TAB_OTR_DESCRIPTION);
+ replacements["incognitoTabHeading"] =
+ l10n_util::GetStringUTF8(IDS_NEW_TAB_OTR_HEADING);
+ replacements["incognitoTabWarning"] =
+ l10n_util::GetStringUTF8(IDS_NEW_TAB_OTR_MESSAGE_WARNING);
+ replacements["learnMore"] =
+ l10n_util::GetStringUTF8(IDS_NEW_TAB_OTR_LEARN_MORE_LINK);
+ }
+ replacements["learnMoreLink"] = kLearnMoreIncognitoUrl;
+ replacements["title"] = l10n_util::GetStringUTF8(IDS_NEW_TAB_TITLE);
+
+ const ui::ThemeProvider& tp =
+ ThemeService::GetThemeProviderForProfile(profile_);
+ replacements["hasCustomBackground"] =
+ tp.HasCustomImage(IDR_THEME_NTP_BACKGROUND) ? "true" : "false";
+
+ const std::string& app_locale = g_browser_process->GetApplicationLocale();
+ webui::SetLoadTimeDataDefaults(app_locale, &replacements);
+
+ static const base::StringPiece incognito_tab_html(
+ ResourceBundle::GetSharedInstance().GetRawDataResource(
+ is_md_incognito_ntp_enabled ? IDR_MD_INCOGNITO_TAB_HTML
+ : IDR_INCOGNITO_TAB_HTML));
+
+ std::string full_html =
+ ui::ReplaceTemplateExpressions(incognito_tab_html, replacements);
+
+ new_tab_incognito_html_ = base::RefCountedString::TakeString(&full_html);
+}
+
+void NTPResourceCache::CreateNewTabGuestHTML() {
+ base::DictionaryValue localized_strings;
+ localized_strings.SetString("title",
+ l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
+ const char* guest_tab_link = kLearnMoreGuestSessionUrl;
+ 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;
+
+#if defined(OS_CHROMEOS)
+ guest_tab_ids = IDR_GUEST_SESSION_TAB_HTML;
+
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ std::string enterprise_domain = connector->GetEnterpriseDomain();
+
+ // TODO(jamescook): What about Active Directory managed devices?
+ if (!enterprise_domain.empty()) {
+ // Device is enterprise enrolled.
+ localized_strings.SetString("enterpriseInfoVisible", "true");
+ base::string16 enterprise_info =
+ l10n_util::GetStringFUTF16(IDS_ASH_ENTERPRISE_DEVICE_MANAGED_BY,
+ base::UTF8ToUTF16(enterprise_domain));
+ localized_strings.SetString("enterpriseInfoMessage", enterprise_info);
+ localized_strings.SetString("enterpriseLearnMore",
+ l10n_util::GetStringUTF16(IDS_LEARN_MORE));
+ localized_strings.SetString("enterpriseInfoHintLink",
+ chrome::kLearnMoreEnterpriseURL);
+ } else {
+ localized_strings.SetString("enterpriseInfoVisible", "false");
+ }
+#endif
+
+ localized_strings.SetString("guestTabDescription",
+ l10n_util::GetStringUTF16(guest_tab_description_ids));
+ localized_strings.SetString("guestTabHeading",
+ l10n_util::GetStringUTF16(guest_tab_heading_ids));
+ localized_strings.SetString("learnMore",
+ l10n_util::GetStringUTF16(guest_tab_link_ids));
+ localized_strings.SetString("learnMoreLink", guest_tab_link);
+
+ const std::string& app_locale = g_browser_process->GetApplicationLocale();
+ webui::SetLoadTimeDataDefaults(app_locale, &localized_strings);
+
+ static const base::StringPiece guest_tab_html(
+ ResourceBundle::GetSharedInstance().GetRawDataResource(guest_tab_ids));
+
+#if defined(OS_CHROMEOS)
+ // TODO(dbeam): convert c/b/resources/chromeos/guest_session_tab.html from
+ // i18n-* to $i18n{}.
+ std::string full_html = webui::GetI18nTemplateHtml(
+ guest_tab_html, &localized_strings);
+#else
+ ui::TemplateReplacements replacements;
+ ui::TemplateReplacementsFromDictionaryValue(localized_strings, &replacements);
+ std::string full_html =
+ ui::ReplaceTemplateExpressions(guest_tab_html, replacements);
+#endif
+
+ new_tab_guest_html_ = base::RefCountedString::TakeString(&full_html);
+}
+
+void NTPResourceCache::CreateNewTabHTML() {
+ // TODO(estade): these strings should be defined in their relevant handlers
+ // (in GetLocalizedValues) and should have more legible names.
+ // Show the profile name in the title and most visited labels if the current
+ // profile is not the default.
+ PrefService* prefs = profile_->GetPrefs();
+ base::DictionaryValue load_time_data;
+ load_time_data.SetString(
+ "bookmarkbarattached",
+ prefs->GetBoolean(bookmarks::prefs::kShowBookmarkBar) ? "true" : "false");
+ load_time_data.SetBoolean("showAppLauncherPromo", false);
+ load_time_data.SetString("title",
+ l10n_util::GetStringUTF16(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));
+ load_time_data.SetString("attributionintro",
+ l10n_util::GetStringUTF16(IDS_NEW_TAB_ATTRIBUTION_INTRO));
+ load_time_data.SetString("appuninstall",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_UNINSTALL));
+ load_time_data.SetString("appoptions",
+ l10n_util::GetStringUTF16(IDS_NEW_TAB_APP_OPTIONS));
+ load_time_data.SetString("appdetails",
+ l10n_util::GetStringUTF16(IDS_NEW_TAB_APP_DETAILS));
+ load_time_data.SetString("appinfodialog",
+ l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_SHOW_INFO));
+ load_time_data.SetString("appcreateshortcut",
+ l10n_util::GetStringUTF16(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));
+ load_time_data.SetString("syncLinkText",
+ l10n_util::GetStringUTF16(IDS_SYNC_ADVANCED_OPTIONS));
+ load_time_data.SetBoolean("shouldShowSyncLogin",
+ AppLauncherLoginHandler::ShouldShow(profile_));
+ load_time_data.SetString("learnMore",
+ l10n_util::GetStringUTF16(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("appsPromoTitle",
+ l10n_util::GetStringUTF16(IDS_NEW_TAB_PAGE_APPS_PROMO_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.
+ load_time_data.SetBoolean("isSwipeTrackingFromScrollEventsEnabled",
+ is_swipe_tracking_from_scroll_events_enabled_);
+
+ load_time_data.SetBoolean("showApps", ShouldShowApps());
+ load_time_data.SetBoolean("showWebStoreIcon",
+ !prefs->GetBoolean(prefs::kHideWebStoreIcon));
+
+ load_time_data.SetBoolean("enableNewBookmarkApps",
+ extensions::util::IsNewBookmarkAppsEnabled());
+
+ load_time_data.SetBoolean("canHostedAppsOpenInWindows",
+ extensions::util::CanHostedAppsOpenInWindows());
+
+ load_time_data.SetBoolean("canShowAppInfoDialog",
+ CanShowAppInfoDialog());
+
+ AppLauncherHandler::GetLocalizedValues(profile_, &load_time_data);
+ AppLauncherLoginHandler::GetLocalizedValues(profile_, &load_time_data);
+
+ webui::SetLoadTimeDataDefaults(app_locale, &load_time_data);
+
+ // Control fade and resize animations.
+ load_time_data.SetBoolean("anim",
+ gfx::Animation::ShouldRenderRichAnimation());
+
+ load_time_data.SetBoolean(
+ "isUserSignedIn",
+ SigninManagerFactory::GetForProfile(profile_)->IsAuthenticated());
+
+ // Load the new tab page appropriate for this build.
+ base::StringPiece new_tab_html(ResourceBundle::GetSharedInstance().
+ GetRawDataResource(IDR_NEW_TAB_4_HTML));
+ std::string full_html =
+ webui::GetI18nTemplateHtml(new_tab_html, &load_time_data);
+ new_tab_html_ = base::RefCountedString::TakeString(&full_html);
+}
+
+void NTPResourceCache::CreateNewTabIncognitoCSS() {
+ // TODO(estade): this returns a subtly incorrect theme provider because
+ // |profile_| is actually not the incognito profile. See crbug.com/568388
+ const ui::ThemeProvider& tp =
+ ThemeService::GetThemeProviderForProfile(profile_);
+
+ // Get our theme colors
+ SkColor color_background =
+ tp.HasCustomImage(IDR_THEME_NTP_BACKGROUND)
+ ? GetThemeColor(tp, ThemeProperties::COLOR_NTP_BACKGROUND)
+ : ThemeProperties::GetDefaultColor(
+ ThemeProperties::COLOR_NTP_BACKGROUND, true /* incognito */);
+
+ // Generate the replacements.
+ ui::TemplateReplacements substitutions;
+
+ // Cache-buster for background.
+ substitutions["themeId"] =
+ profile_->GetPrefs()->GetString(prefs::kCurrentThemeID);
+
+ // Colors.
+ substitutions["colorBackground"] =
+ color_utils::SkColorToRgbaString(color_background);
+ substitutions["backgroundBarDetached"] = GetNewTabBackgroundCSS(tp, false);
+ substitutions["backgroundBarAttached"] = GetNewTabBackgroundCSS(tp, true);
+ substitutions["backgroundTiling"] = GetNewTabBackgroundTilingCSS(tp);
+
+ // Get our template.
+ static const base::StringPiece new_tab_theme_css(
+ ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_NEW_INCOGNITO_TAB_THEME_CSS));
+
+ // Create the string from our template and the replacements.
+ std::string full_css =
+ ui::ReplaceTemplateExpressions(new_tab_theme_css, substitutions);
+
+ new_tab_incognito_css_ = base::RefCountedString::TakeString(&full_css);
+}
+
+void NTPResourceCache::CreateNewTabCSS() {
+ const ui::ThemeProvider& tp =
+ ThemeService::GetThemeProviderForProfile(profile_);
+
+ // Get our theme colors
+ SkColor color_background =
+ GetThemeColor(tp, ThemeProperties::COLOR_NTP_BACKGROUND);
+ SkColor color_text = GetThemeColor(tp, ThemeProperties::COLOR_NTP_TEXT);
+ SkColor color_text_light =
+ GetThemeColor(tp, ThemeProperties::COLOR_NTP_TEXT_LIGHT);
+
+ SkColor color_header =
+ GetThemeColor(tp, ThemeProperties::COLOR_NTP_HEADER);
+ // Generate a lighter color for the header gradients.
+ color_utils::HSL header_lighter;
+ color_utils::SkColorToHSL(color_header, &header_lighter);
+ header_lighter.l += (1 - header_lighter.l) * 0.33;
+
+ // Generate section border color from the header color. See
+ // BookmarkBarView::Paint for how we do this for the bookmark bar
+ // borders.
+ SkColor color_section_border =
+ SkColorSetARGB(80,
+ SkColorGetR(color_header),
+ SkColorGetG(color_header),
+ SkColorGetB(color_header));
+
+ // Generate the replacements.
+ ui::TemplateReplacements substitutions;
+
+ // Cache-buster for background.
+ substitutions["themeId"] =
+ profile_->GetPrefs()->GetString(prefs::kCurrentThemeID);
+
+ // Colors.
+ substitutions["colorBackground"] =
+ color_utils::SkColorToRgbaString(color_background);
+ substitutions["backgroundBarDetached"] = GetNewTabBackgroundCSS(tp, false);
+ substitutions["backgroundBarAttached"] = GetNewTabBackgroundCSS(tp, true);
+ substitutions["backgroundTiling"] = GetNewTabBackgroundTilingCSS(tp);
+ substitutions["colorTextRgba"] = color_utils::SkColorToRgbaString(color_text);
+ substitutions["colorTextLight"] =
+ color_utils::SkColorToRgbaString(color_text_light);
+ substitutions["colorSectionBorder"] =
+ color_utils::SkColorToRgbString(color_section_border);
+ substitutions["colorText"] = color_utils::SkColorToRgbString(color_text);
+
+ // For themes that right-align the background, we flip the attribution to the
+ // left to avoid conflicts.
+ int alignment =
+ tp.GetDisplayProperty(ThemeProperties::NTP_BACKGROUND_ALIGNMENT);
+ if (alignment & ThemeProperties::ALIGN_RIGHT) {
+ substitutions["leftAlignAttribution"] = "0";
+ substitutions["rightAlignAttribution"] = "auto";
+ substitutions["textAlignAttribution"] = "right";
+ } else {
+ substitutions["leftAlignAttribution"] = "auto";
+ substitutions["rightAlignAttribution"] = "0";
+ substitutions["textAlignAttribution"] = "left";
+ }
+
+ substitutions["displayAttribution"] =
+ tp.HasCustomImage(IDR_THEME_NTP_ATTRIBUTION) ? "inline" : "none";
+
+ // Get our template.
+ static const base::StringPiece new_tab_theme_css(
+ ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_NEW_TAB_4_THEME_CSS));
+
+ // Create the string from our template and the replacements.
+ std::string css_string =
+ ui::ReplaceTemplateExpressions(new_tab_theme_css, substitutions);
+ new_tab_css_ = base::RefCountedString::TakeString(&css_string);
+}
diff --git a/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache.h b/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache.h
new file mode 100644
index 00000000000..827b2eeacd3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache.h
@@ -0,0 +1,90 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_NTP_NTP_RESOURCE_CACHE_H_
+#define CHROME_BROWSER_UI_WEBUI_NTP_NTP_RESOURCE_CACHE_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+class Profile;
+
+namespace base {
+class RefCountedMemory;
+}
+
+namespace content {
+class RenderProcessHost;
+}
+
+// This class keeps a cache of NTP resources (HTML and CSS) so we don't have to
+// regenerate them all the time.
+// Note: This is only used for incognito and guest mode NTPs (NewTabUI), as well
+// as for (non-incognito) app launcher pages (AppLauncherPageUI).
+class NTPResourceCache : public content::NotificationObserver,
+ public KeyedService {
+ public:
+ enum WindowType {
+ NORMAL,
+ INCOGNITO,
+ GUEST,
+ };
+
+ explicit NTPResourceCache(Profile* profile);
+ ~NTPResourceCache() override;
+
+ base::RefCountedMemory* GetNewTabHTML(WindowType win_type);
+ base::RefCountedMemory* GetNewTabCSS(WindowType win_type);
+
+ // content::NotificationObserver interface.
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ static WindowType GetWindowType(
+ Profile* profile, content::RenderProcessHost* render_host);
+
+ private:
+ void OnPreferenceChanged();
+
+ // Invalidates the NTPResourceCache.
+ void Invalidate();
+
+ // Helper to determine if the resource cache for the main (not incognito or
+ // guest) HTML should be invalidated.
+ // This is called on every page load, and can be used to check values that
+ // don't generate a notification when changed (e.g., system preferences).
+ bool NewTabHTMLNeedsRefresh();
+
+ void CreateNewTabHTML();
+ void CreateNewTabCSS();
+
+ void CreateNewTabIncognitoHTML();
+ void CreateNewTabIncognitoCSS();
+
+ void CreateNewTabGuestHTML();
+
+ Profile* profile_;
+
+ scoped_refptr<base::RefCountedMemory> new_tab_html_;
+ scoped_refptr<base::RefCountedMemory> new_tab_css_;
+ scoped_refptr<base::RefCountedMemory> new_tab_guest_html_;
+ scoped_refptr<base::RefCountedMemory> new_tab_incognito_html_;
+ scoped_refptr<base::RefCountedMemory> new_tab_incognito_css_;
+ content::NotificationRegistrar registrar_;
+ PrefChangeRegistrar profile_pref_change_registrar_;
+ PrefChangeRegistrar local_state_pref_change_registrar_;
+
+ // Set based on platform_util::IsSwipeTrackingFromScrollEventsEnabled.
+ bool is_swipe_tracking_from_scroll_events_enabled_;
+
+ DISALLOW_COPY_AND_ASSIGN(NTPResourceCache);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_NTP_NTP_RESOURCE_CACHE_H_
diff --git a/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.cc b/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.cc
new file mode 100644
index 00000000000..1f9b0d1002e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.cc
@@ -0,0 +1,43 @@
+// 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/ui/webui/ntp/ntp_resource_cache_factory.h"
+
+#include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/themes/theme_service_factory.h"
+#include "chrome/browser/ui/webui/ntp/ntp_resource_cache.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+
+// static
+NTPResourceCache* NTPResourceCacheFactory::GetForProfile(Profile* profile) {
+ return static_cast<NTPResourceCache*>(
+ GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+// static
+NTPResourceCacheFactory* NTPResourceCacheFactory::GetInstance() {
+ return base::Singleton<NTPResourceCacheFactory>::get();
+}
+
+NTPResourceCacheFactory::NTPResourceCacheFactory()
+ : BrowserContextKeyedServiceFactory(
+ "NTPResourceCache",
+ BrowserContextDependencyManager::GetInstance()) {
+ DependsOn(SigninManagerFactory::GetInstance());
+ DependsOn(ThemeServiceFactory::GetInstance());
+}
+
+NTPResourceCacheFactory::~NTPResourceCacheFactory() {}
+
+KeyedService* NTPResourceCacheFactory::BuildServiceInstanceFor(
+ content::BrowserContext* profile) const {
+ return new NTPResourceCache(static_cast<Profile*>(profile));
+}
+
+content::BrowserContext* NTPResourceCacheFactory::GetBrowserContextToUse(
+ content::BrowserContext* context) const {
+ return chrome::GetBrowserContextRedirectedInIncognito(context);
+}
diff --git a/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.h b/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.h
new file mode 100644
index 00000000000..ecb7dc03967
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2011 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_NTP_NTP_RESOURCE_CACHE_FACTORY_H_
+#define CHROME_BROWSER_UI_WEBUI_NTP_NTP_RESOURCE_CACHE_FACTORY_H_
+
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class NTPResourceCache;
+class Profile;
+
+// Singleton that owns the NTPResourceCaches used by the NTP and associates them
+// with Profiles. Listens for the Profile's destruction notification and cleans
+// up the associated ThemeService.
+class NTPResourceCacheFactory : public BrowserContextKeyedServiceFactory {
+ public:
+ static NTPResourceCache* GetForProfile(Profile* profile);
+
+ static NTPResourceCacheFactory* GetInstance();
+
+ private:
+ friend struct base::DefaultSingletonTraits<NTPResourceCacheFactory>;
+
+ NTPResourceCacheFactory();
+ ~NTPResourceCacheFactory() override;
+
+ // BrowserContextKeyedServiceFactory:
+ KeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* profile) const override;
+ content::BrowserContext* GetBrowserContextToUse(
+ content::BrowserContext* context) const override;
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_NTP_NTP_RESOURCE_CACHE_FACTORY_H_
diff --git a/chromium/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc b/chromium/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc
new file mode 100644
index 00000000000..5f679da3960
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ntp_tiles_internals_ui.cc
@@ -0,0 +1,131 @@
+// 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 "chrome/browser/ui/webui/ntp_tiles_internals_ui.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/favicon/favicon_service_factory.h"
+#include "chrome/browser/history/top_sites_factory.h"
+#include "chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search/suggestions/image_decoder_impl.h"
+#include "chrome/browser/search/suggestions/suggestions_service_factory.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/common/url_constants.h"
+#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"
+#include "components/ntp_tiles/webui/ntp_tiles_internals_message_handler_client.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 "content/public/browser/web_ui_message_handler.h"
+
+namespace {
+
+// The implementation for the chrome://ntp-tiles-internals page.
+class ChromeNTPTilesInternalsMessageHandlerClient
+ : public content::WebUIMessageHandler,
+ public ntp_tiles::NTPTilesInternalsMessageHandlerClient {
+ public:
+ ChromeNTPTilesInternalsMessageHandlerClient() {}
+
+ private:
+ // content::WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ // ntp_tiles::NTPTilesInternalsMessageHandlerClient
+ bool SupportsNTPTiles() override;
+ bool DoesSourceExist(ntp_tiles::TileSource source) override;
+ std::unique_ptr<ntp_tiles::MostVisitedSites> MakeMostVisitedSites() override;
+ PrefService* GetPrefs() override;
+ void RegisterMessageCallback(
+ const std::string& message,
+ const base::Callback<void(const base::ListValue*)>& callback) override;
+ void CallJavascriptFunctionVector(
+ const std::string& name,
+ const std::vector<const base::Value*>& values) override;
+
+ ntp_tiles::NTPTilesInternalsMessageHandler handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeNTPTilesInternalsMessageHandlerClient);
+};
+
+void ChromeNTPTilesInternalsMessageHandlerClient::RegisterMessages() {
+ handler_.RegisterMessages(this);
+}
+
+bool ChromeNTPTilesInternalsMessageHandlerClient::SupportsNTPTiles() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ return !(profile->IsGuestSession() || profile->IsOffTheRecord());
+}
+
+bool ChromeNTPTilesInternalsMessageHandlerClient::DoesSourceExist(
+ ntp_tiles::TileSource source) {
+ switch (source) {
+ case ntp_tiles::TileSource::TOP_SITES:
+ case ntp_tiles::TileSource::SUGGESTIONS_SERVICE:
+ case ntp_tiles::TileSource::WHITELIST:
+ case ntp_tiles::TileSource::HOMEPAGE:
+ return true;
+ case ntp_tiles::TileSource::POPULAR:
+#if defined(OS_ANDROID)
+ return true;
+#else
+ return false;
+#endif
+ }
+ NOTREACHED();
+ return false;
+}
+
+std::unique_ptr<ntp_tiles::MostVisitedSites>
+ChromeNTPTilesInternalsMessageHandlerClient::MakeMostVisitedSites() {
+ return ChromeMostVisitedSitesFactory::NewForProfile(
+ Profile::FromWebUI(web_ui()));
+}
+
+PrefService* ChromeNTPTilesInternalsMessageHandlerClient::GetPrefs() {
+ return Profile::FromWebUI(web_ui())->GetPrefs();
+}
+
+void ChromeNTPTilesInternalsMessageHandlerClient::RegisterMessageCallback(
+ const std::string& message,
+ const base::Callback<void(const base::ListValue*)>& callback) {
+ web_ui()->RegisterMessageCallback(message, callback);
+}
+
+void ChromeNTPTilesInternalsMessageHandlerClient::CallJavascriptFunctionVector(
+ const std::string& name,
+ const std::vector<const base::Value*>& values) {
+ web_ui()->CallJavascriptFunctionUnsafe(name, values);
+}
+
+content::WebUIDataSource* CreateNTPTilesInternalsHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUINTPTilesInternalsHost);
+
+ source->AddResourcePath("ntp_tiles_internals.js", IDR_NTP_TILES_INTERNALS_JS);
+ source->AddResourcePath("ntp_tiles_internals.css",
+ IDR_NTP_TILES_INTERNALS_CSS);
+ source->SetDefaultResource(IDR_NTP_TILES_INTERNALS_HTML);
+ return source;
+}
+
+} // namespace
+
+NTPTilesInternalsUI::NTPTilesInternalsUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui),
+ CreateNTPTilesInternalsHTMLSource());
+ web_ui->AddMessageHandler(
+ base::MakeUnique<ChromeNTPTilesInternalsMessageHandlerClient>());
+}
+
+NTPTilesInternalsUI::~NTPTilesInternalsUI() {}
diff --git a/chromium/chrome/browser/ui/webui/ntp_tiles_internals_ui.h b/chromium/chrome/browser/ui/webui/ntp_tiles_internals_ui.h
new file mode 100644
index 00000000000..1d1a363fccc
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/ntp_tiles_internals_ui.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_NTP_TILES_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_NTP_TILES_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The implementation for the chrome://ntp-tiles-internals page.
+class NTPTilesInternalsUI : public content::WebUIController {
+ public:
+ explicit NTPTilesInternalsUI(content::WebUI* web_ui);
+ ~NTPTilesInternalsUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NTPTilesInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_NTP_TILES_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/offline/offline_internals_ui.cc b/chromium/chrome/browser/ui/webui/offline/offline_internals_ui.cc
new file mode 100644
index 00000000000..073519e5e4e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/offline/offline_internals_ui.cc
@@ -0,0 +1,43 @@
+// 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 "chrome/browser/ui/webui/offline/offline_internals_ui.h"
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+OfflineInternalsUI::OfflineInternalsUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ // chrome://offline-internals source.
+ content::WebUIDataSource* html_source =
+ content::WebUIDataSource::Create(chrome::kChromeUIOfflineInternalsHost);
+
+ // Required resources.
+ html_source->SetJsonPath("strings.js");
+ html_source->AddResourcePath("offline_internals.css",
+ IDR_OFFLINE_INTERNALS_CSS);
+ html_source->AddResourcePath("offline_internals.js",
+ IDR_OFFLINE_INTERNALS_JS);
+ html_source->AddResourcePath("offline_internals_browser_proxy.js",
+ IDR_OFFLINE_INTERNALS_BROWSER_PROXY_JS);
+ html_source->SetDefaultResource(IDR_OFFLINE_INTERNALS_HTML);
+ html_source->UseGzip(std::unordered_set<std::string>());
+
+ Profile* profile = Profile::FromWebUI(web_ui);
+ html_source->AddBoolean("isIncognito", profile->IsOffTheRecord());
+
+ content::WebUIDataSource::Add(profile, html_source);
+
+ web_ui->AddMessageHandler(
+ base::MakeUnique<offline_internals::OfflineInternalsUIMessageHandler>());
+}
+
+OfflineInternalsUI::~OfflineInternalsUI() {}
diff --git a/chromium/chrome/browser/ui/webui/offline/offline_internals_ui.h b/chromium/chrome/browser/ui/webui/offline/offline_internals_ui.h
new file mode 100644
index 00000000000..0afcec231bc
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/offline/offline_internals_ui.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OFFLINE_OFFLINE_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_OFFLINE_OFFLINE_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The WebUI for chrome://offline-internals.
+class OfflineInternalsUI : public content::WebUIController {
+ public:
+ explicit OfflineInternalsUI(content::WebUI* web_ui);
+ ~OfflineInternalsUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OfflineInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_OFFLINE_OFFLINE_INTERNALS_UI_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
new file mode 100644
index 00000000000..2d7be60d63f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
@@ -0,0 +1,386 @@
+// 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 "chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/guid.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/android/offline_pages/offline_page_model_factory.h"
+#include "chrome/browser/android/offline_pages/prefetch/prefetch_background_task.h"
+#include "chrome/browser/android/offline_pages/request_coordinator_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/offline_pages/core/client_namespace_constants.h"
+#include "content/public/browser/web_ui.h"
+#include "net/base/network_change_notifier.h"
+
+namespace offline_internals {
+
+OfflineInternalsUIMessageHandler::OfflineInternalsUIMessageHandler()
+ : offline_page_model_(nullptr),
+ request_coordinator_(nullptr),
+ weak_ptr_factory_(this) {}
+
+OfflineInternalsUIMessageHandler::~OfflineInternalsUIMessageHandler() {}
+
+std::string OfflineInternalsUIMessageHandler::GetStringFromDeletePageResult(
+ offline_pages::DeletePageResult value) {
+ switch (value) {
+ case offline_pages::DeletePageResult::SUCCESS:
+ return "Success";
+ case offline_pages::DeletePageResult::CANCELLED:
+ return "Cancelled";
+ case offline_pages::DeletePageResult::STORE_FAILURE:
+ return "Store failure";
+ case offline_pages::DeletePageResult::DEVICE_FAILURE:
+ return "Device failure";
+ case offline_pages::DeletePageResult::NOT_FOUND:
+ return "Not found";
+ case offline_pages::DeletePageResult::RESULT_COUNT:
+ break;
+ }
+ NOTREACHED();
+ return "Unknown";
+}
+
+std::string OfflineInternalsUIMessageHandler::GetStringFromDeleteRequestResults(
+ const offline_pages::MultipleItemStatuses& results) {
+ // If any requests failed, return "failure", else "success".
+ for (const auto& result : results) {
+ if (result.second == offline_pages::ItemActionStatus::STORE_ERROR)
+ return "Store failure, could not delete one or more requests";
+ }
+
+ return "Success";
+}
+
+std::string OfflineInternalsUIMessageHandler::GetStringFromSavePageStatus() {
+ return "Available";
+}
+
+void OfflineInternalsUIMessageHandler::HandleDeleteSelectedPages(
+ const base::ListValue* args) {
+ std::string callback_id;
+ CHECK(args->GetString(0, &callback_id));
+
+ std::vector<int64_t> offline_ids;
+ const base::ListValue* offline_ids_from_arg;
+ args->GetList(1, &offline_ids_from_arg);
+
+ for (size_t i = 0; i < offline_ids_from_arg->GetSize(); i++) {
+ std::string value;
+ offline_ids_from_arg->GetString(i, &value);
+ int64_t int_value;
+ base::StringToInt64(value, &int_value);
+ offline_ids.push_back(int_value);
+ }
+
+ offline_page_model_->DeletePagesByOfflineId(
+ offline_ids,
+ base::Bind(&OfflineInternalsUIMessageHandler::HandleDeletedPagesCallback,
+ weak_ptr_factory_.GetWeakPtr(), callback_id));
+}
+
+void OfflineInternalsUIMessageHandler::HandleDeleteSelectedRequests(
+ const base::ListValue* args) {
+ std::string callback_id;
+ CHECK(args->GetString(0, &callback_id));
+
+ std::vector<int64_t> offline_ids;
+ const base::ListValue* offline_ids_from_arg = nullptr;
+ args->GetList(1, &offline_ids_from_arg);
+
+ for (size_t i = 0; i < offline_ids_from_arg->GetSize(); i++) {
+ std::string value;
+ offline_ids_from_arg->GetString(i, &value);
+ int64_t int_value;
+ base::StringToInt64(value, &int_value);
+ offline_ids.push_back(int_value);
+ }
+
+ // Call RequestCoordinator to delete them
+ if (request_coordinator_) {
+ request_coordinator_->RemoveRequests(
+ offline_ids,
+ base::Bind(
+ &OfflineInternalsUIMessageHandler::HandleDeletedRequestsCallback,
+ weak_ptr_factory_.GetWeakPtr(), callback_id));
+ }
+}
+
+void OfflineInternalsUIMessageHandler::HandleDeletedPagesCallback(
+ std::string callback_id,
+ offline_pages::DeletePageResult result) {
+ ResolveJavascriptCallback(base::Value(callback_id),
+ base::Value(GetStringFromDeletePageResult(result)));
+}
+
+void OfflineInternalsUIMessageHandler::HandleDeletedRequestsCallback(
+ std::string callback_id,
+ const offline_pages::MultipleItemStatuses& results) {
+ ResolveJavascriptCallback(
+ base::Value(callback_id),
+ base::Value(GetStringFromDeleteRequestResults(results)));
+}
+
+void OfflineInternalsUIMessageHandler::HandleStoredPagesCallback(
+ std::string callback_id,
+ const offline_pages::MultipleOfflinePageItemResult& pages) {
+ base::ListValue results;
+
+ for (const auto& page : pages) {
+ auto offline_page = base::MakeUnique<base::DictionaryValue>();
+ offline_page->SetString("onlineUrl", page.url.spec());
+ offline_page->SetString("namespace", page.client_id.name_space);
+ offline_page->SetDouble("size", page.file_size);
+ offline_page->SetString("id", std::to_string(page.offline_id));
+ offline_page->SetString("filePath", page.file_path.MaybeAsASCII());
+ offline_page->SetDouble("creationTime", page.creation_time.ToJsTime());
+ offline_page->SetDouble("lastAccessTime", page.last_access_time.ToJsTime());
+ offline_page->SetInteger("accessCount", page.access_count);
+ offline_page->SetString("originalUrl", page.original_url.spec());
+ results.Append(std::move(offline_page));
+ }
+ ResolveJavascriptCallback(base::Value(callback_id), results);
+}
+
+void OfflineInternalsUIMessageHandler::HandleRequestQueueCallback(
+ std::string callback_id,
+ offline_pages::GetRequestsResult result,
+ std::vector<std::unique_ptr<offline_pages::SavePageRequest>> requests) {
+ base::ListValue save_page_requests;
+ if (result == offline_pages::GetRequestsResult::SUCCESS) {
+ for (const auto& request : requests) {
+ auto save_page_request = base::MakeUnique<base::DictionaryValue>();
+ save_page_request->SetString("onlineUrl", request->url().spec());
+ save_page_request->SetDouble("creationTime",
+ request->creation_time().ToJsTime());
+ save_page_request->SetString("status", GetStringFromSavePageStatus());
+ save_page_request->SetString("namespace",
+ request->client_id().name_space);
+ save_page_request->SetDouble("lastAttempt",
+ request->last_attempt_time().ToJsTime());
+ save_page_request->SetString("id", std::to_string(request->request_id()));
+ save_page_request->SetString("originalUrl",
+ request->original_url().spec());
+ save_page_requests.Append(std::move(save_page_request));
+ }
+ }
+ ResolveJavascriptCallback(base::Value(callback_id), save_page_requests);
+}
+
+void OfflineInternalsUIMessageHandler::HandleGetRequestQueue(
+ const base::ListValue* args) {
+ AllowJavascript();
+ std::string callback_id;
+ CHECK(args->GetString(0, &callback_id));
+
+ if (request_coordinator_) {
+ request_coordinator_->queue()->GetRequests(base::Bind(
+ &OfflineInternalsUIMessageHandler::HandleRequestQueueCallback,
+ weak_ptr_factory_.GetWeakPtr(), callback_id));
+ } else {
+ base::ListValue results;
+ ResolveJavascriptCallback(base::Value(callback_id), results);
+ }
+}
+
+void OfflineInternalsUIMessageHandler::HandleGetStoredPages(
+ const base::ListValue* args) {
+ AllowJavascript();
+ std::string callback_id;
+ CHECK(args->GetString(0, &callback_id));
+
+ if (offline_page_model_) {
+ offline_page_model_->GetAllPages(
+ base::Bind(&OfflineInternalsUIMessageHandler::HandleStoredPagesCallback,
+ weak_ptr_factory_.GetWeakPtr(), callback_id));
+ } else {
+ base::ListValue results;
+ ResolveJavascriptCallback(base::Value(callback_id), results);
+ }
+}
+
+void OfflineInternalsUIMessageHandler::HandleSetRecordPageModel(
+ const base::ListValue* args) {
+ AllowJavascript();
+ bool should_record;
+ CHECK(args->GetBoolean(0, &should_record));
+ if (offline_page_model_)
+ offline_page_model_->GetLogger()->SetIsLogging(should_record);
+}
+
+void OfflineInternalsUIMessageHandler::HandleGetNetworkStatus(
+ const base::ListValue* args) {
+ AllowJavascript();
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+
+ ResolveJavascriptCallback(
+ *callback_id,
+ base::Value(net::NetworkChangeNotifier::IsOffline() ? "Offline"
+ : "Online"));
+}
+
+void OfflineInternalsUIMessageHandler::HandleScheduleNwake(
+ const base::ListValue* args) {
+ AllowJavascript();
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+
+ offline_pages::PrefetchBackgroundTask::Schedule();
+
+ ResolveJavascriptCallback(*callback_id, base::Value("Scheduled."));
+}
+
+void OfflineInternalsUIMessageHandler::HandleCancelNwake(
+ const base::ListValue* args) {
+ AllowJavascript();
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+
+ offline_pages::PrefetchBackgroundTask::Cancel();
+
+ ResolveJavascriptCallback(*callback_id, base::Value("Cancelled."));
+}
+
+void OfflineInternalsUIMessageHandler::HandleSetRecordRequestQueue(
+ const base::ListValue* args) {
+ AllowJavascript();
+ bool should_record;
+ CHECK(args->GetBoolean(0, &should_record));
+ if (request_coordinator_)
+ request_coordinator_->GetLogger()->SetIsLogging(should_record);
+}
+
+void OfflineInternalsUIMessageHandler::HandleGetLoggingState(
+ const base::ListValue* args) {
+ AllowJavascript();
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+
+ base::DictionaryValue result;
+ result.SetBoolean("modelIsLogging",
+ offline_page_model_
+ ? offline_page_model_->GetLogger()->GetIsLogging()
+ : false);
+ result.SetBoolean("queueIsLogging",
+ request_coordinator_
+ ? request_coordinator_->GetLogger()->GetIsLogging()
+ : false);
+ ResolveJavascriptCallback(*callback_id, result);
+}
+
+void OfflineInternalsUIMessageHandler::HandleGetEventLogs(
+ const base::ListValue* args) {
+ AllowJavascript();
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+
+ std::vector<std::string> logs;
+ if (offline_page_model_)
+ offline_page_model_->GetLogger()->GetLogs(&logs);
+ if (request_coordinator_)
+ request_coordinator_->GetLogger()->GetLogs(&logs);
+ std::sort(logs.begin(), logs.end());
+
+ base::ListValue result;
+ result.AppendStrings(logs);
+
+ ResolveJavascriptCallback(*callback_id, result);
+}
+
+void OfflineInternalsUIMessageHandler::HandleAddToRequestQueue(
+ const base::ListValue* args) {
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+
+ if (request_coordinator_) {
+ std::string url;
+ CHECK(args->GetString(1, &url));
+
+ // To be visible in Downloads UI, these items need a well-formed GUID
+ // and AsyncNamespace in their ClientId.
+ std::ostringstream id_stream;
+ id_stream << base::GenerateGUID();
+
+ offline_pages::RequestCoordinator::SavePageLaterParams params;
+ params.url = GURL(url);
+ params.client_id = offline_pages::ClientId(offline_pages::kAsyncNamespace,
+ id_stream.str());
+ ResolveJavascriptCallback(
+ *callback_id,
+ base::Value(request_coordinator_->SavePageLater(params) > 0));
+ } else {
+ ResolveJavascriptCallback(*callback_id, base::Value(false));
+ }
+}
+
+void OfflineInternalsUIMessageHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "deleteSelectedPages",
+ base::Bind(&OfflineInternalsUIMessageHandler::HandleDeleteSelectedPages,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "deleteSelectedRequests",
+ base::Bind(
+ &OfflineInternalsUIMessageHandler::HandleDeleteSelectedRequests,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "getRequestQueue",
+ base::Bind(&OfflineInternalsUIMessageHandler::HandleGetRequestQueue,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "getStoredPages",
+ base::Bind(&OfflineInternalsUIMessageHandler::HandleGetStoredPages,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "getEventLogs",
+ base::Bind(&OfflineInternalsUIMessageHandler::HandleGetEventLogs,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "setRecordRequestQueue",
+ base::Bind(&OfflineInternalsUIMessageHandler::HandleSetRecordRequestQueue,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "setRecordPageModel",
+ base::Bind(&OfflineInternalsUIMessageHandler::HandleSetRecordPageModel,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "getLoggingState",
+ base::Bind(&OfflineInternalsUIMessageHandler::HandleGetLoggingState,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "addToRequestQueue",
+ base::Bind(&OfflineInternalsUIMessageHandler::HandleAddToRequestQueue,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "getNetworkStatus",
+ base::Bind(&OfflineInternalsUIMessageHandler::HandleGetNetworkStatus,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "scheduleNwake",
+ base::Bind(&OfflineInternalsUIMessageHandler::HandleScheduleNwake,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "cancelNwake",
+ base::Bind(&OfflineInternalsUIMessageHandler::HandleCancelNwake,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ // Get the offline page model associated with this web ui.
+ Profile* profile = Profile::FromWebUI(web_ui());
+ offline_page_model_ =
+ offline_pages::OfflinePageModelFactory::GetForBrowserContext(profile);
+ request_coordinator_ =
+ offline_pages::RequestCoordinatorFactory::GetForBrowserContext(profile);
+}
+
+} // namespace offline_internals
diff --git a/chromium/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h b/chromium/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h
new file mode 100644
index 00000000000..2dba260b69e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h
@@ -0,0 +1,113 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OFFLINE_OFFLINE_INTERNALS_UI_MESSAGE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OFFLINE_OFFLINE_INTERNALS_UI_MESSAGE_HANDLER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/offline_page_model.h"
+#include "components/offline_pages/core/offline_store_types.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace offline_pages {
+enum class GetRequestsResult;
+}
+
+namespace offline_internals {
+
+// Class acting as a controller of the chrome://offline-internals WebUI.
+class OfflineInternalsUIMessageHandler : public content::WebUIMessageHandler {
+ public:
+ OfflineInternalsUIMessageHandler();
+ ~OfflineInternalsUIMessageHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ // Delete selected list of page ids from the store.
+ void HandleDeleteSelectedPages(const base::ListValue* args);
+
+ // Delete selected list of requests from the request queue.
+ void HandleDeleteSelectedRequests(const base::ListValue* args);
+
+ // Load Request Queue info.
+ void HandleGetRequestQueue(const base::ListValue* args);
+
+ // Load Stored pages info.
+ void HandleGetStoredPages(const base::ListValue* args);
+
+ // Set whether to record offline page model events.
+ void HandleSetRecordPageModel(const base::ListValue* args);
+
+ // Set whether to record request queue events.
+ void HandleSetRecordRequestQueue(const base::ListValue* args);
+
+ // Load both Page Model and Request Queue event logs.
+ void HandleGetEventLogs(const base::ListValue* args);
+
+ // Load whether logs are being recorded.
+ void HandleGetLoggingState(const base::ListValue* args);
+
+ // Adds a url to the background loader queue.
+ void HandleAddToRequestQueue(const base::ListValue* args);
+
+ // Load whether device is currently offline.
+ void HandleGetNetworkStatus(const base::ListValue* args);
+
+ // Schedules an NWake signal.
+ void HandleScheduleNwake(const base::ListValue* args);
+
+ // Cancels an NWake signal.
+ void HandleCancelNwake(const base::ListValue* args);
+
+ // Callback for async GetAllPages calls.
+ void HandleStoredPagesCallback(
+ std::string callback_id,
+ const offline_pages::MultipleOfflinePageItemResult& pages);
+
+ // Callback for async GetRequests calls.
+ void HandleRequestQueueCallback(
+ std::string callback_id,
+ offline_pages::GetRequestsResult result,
+ std::vector<std::unique_ptr<offline_pages::SavePageRequest>> requests);
+
+ // Callback for DeletePage/DeleteAllPages calls.
+ void HandleDeletedPagesCallback(std::string callback_id,
+ const offline_pages::DeletePageResult result);
+
+ // Callback for DeleteRequest/DeleteAllRequests calls.
+ void HandleDeletedRequestsCallback(
+ std::string callback_id,
+ const offline_pages::MultipleItemStatuses& results);
+
+ // Turns a DeletePageResult enum into logical string.
+ std::string GetStringFromDeletePageResult(
+ offline_pages::DeletePageResult value);
+
+ // Summarizes the MultipleItemStatuses vector with a string.
+ std::string GetStringFromDeleteRequestResults(
+ const offline_pages::MultipleItemStatuses& result);
+
+ // Turns a SavePageRequest::Status into logical string.
+ std::string GetStringFromSavePageStatus();
+
+ // Offline page model to call methods on.
+ offline_pages::OfflinePageModel* offline_page_model_;
+
+ // Request coordinator for background offline actions.
+ offline_pages::RequestCoordinator* request_coordinator_;
+
+ // Factory for creating references in callbacks.
+ base::WeakPtrFactory<OfflineInternalsUIMessageHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(OfflineInternalsUIMessageHandler);
+};
+
+} // namespace offline_internals
+
+#endif // CHROME_BROWSER_UI_WEBUI_OFFLINE_OFFLINE_INTERNALS_UI_MESSAGE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc b/chromium/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc
new file mode 100644
index 00000000000..1be9a7b7202
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/omnibox/omnibox_page_handler.cc
@@ -0,0 +1,215 @@
+// 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/ui/webui/omnibox/omnibox_page_handler.h"
+
+#include <stddef.h>
+
+#include <string>
+#include <utility>
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h"
+#include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
+#include "chrome/browser/bookmarks/bookmark_model_factory.h"
+#include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search/search.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/history/core/browser/url_database.h"
+#include "components/metrics/proto/omnibox_event.pb.h"
+#include "components/omnibox/browser/autocomplete_classifier.h"
+#include "components/omnibox/browser/autocomplete_controller.h"
+#include "components/omnibox/browser/autocomplete_match.h"
+#include "components/omnibox/browser/autocomplete_provider.h"
+#include "components/search_engines/template_url.h"
+#include "content/public/browser/web_ui.h"
+
+using bookmarks::BookmarkModel;
+
+namespace mojo {
+
+template <>
+struct TypeConverter<std::vector<mojom::AutocompleteAdditionalInfoPtr>,
+ AutocompleteMatch::AdditionalInfo> {
+ static std::vector<mojom::AutocompleteAdditionalInfoPtr> Convert(
+ const AutocompleteMatch::AdditionalInfo& input) {
+ std::vector<mojom::AutocompleteAdditionalInfoPtr> array(input.size());
+ size_t index = 0;
+ for (AutocompleteMatch::AdditionalInfo::const_iterator i = input.begin();
+ i != input.end(); ++i, index++) {
+ mojom::AutocompleteAdditionalInfoPtr item(
+ mojom::AutocompleteAdditionalInfo::New());
+ item->key = i->first;
+ item->value = i->second;
+ array[index] = std::move(item);
+ }
+ return array;
+ }
+};
+
+template <>
+struct TypeConverter<mojom::AutocompleteMatchPtr, AutocompleteMatch> {
+ static mojom::AutocompleteMatchPtr Convert(const AutocompleteMatch& input) {
+ mojom::AutocompleteMatchPtr result(mojom::AutocompleteMatch::New());
+ if (input.provider != NULL) {
+ result->provider_name = std::string(input.provider->GetName());
+ result->provider_done = input.provider->done();
+ }
+ result->relevance = input.relevance;
+ result->deletable = input.deletable;
+ result->fill_into_edit = base::UTF16ToUTF8(input.fill_into_edit);
+ result->inline_autocompletion =
+ base::UTF16ToUTF8(input.inline_autocompletion);
+ result->destination_url = input.destination_url.spec();
+ result->contents = base::UTF16ToUTF8(input.contents);
+ // At this time, we're not bothering to send along the long vector that
+ // represent contents classification. i.e., for each character, what
+ // type of text it is.
+ result->description = base::UTF16ToUTF8(input.description);
+ // At this time, we're not bothering to send along the long vector that
+ // represents description classification. i.e., for each character, what
+ // type of text it is.
+ result->transition = input.transition;
+ result->allowed_to_be_default_match = input.allowed_to_be_default_match;
+ result->type = AutocompleteMatchType::ToString(input.type);
+ if (input.associated_keyword.get() != NULL) {
+ result->associated_keyword =
+ base::UTF16ToUTF8(input.associated_keyword->keyword);
+ }
+ result->keyword = base::UTF16ToUTF8(input.keyword);
+ result->duplicates = static_cast<int32_t>(input.duplicate_matches.size());
+ result->from_previous = input.from_previous;
+
+ result->additional_info =
+ mojo::ConvertTo<std::vector<mojom::AutocompleteAdditionalInfoPtr>>(
+ input.additional_info);
+ return result;
+ }
+};
+
+template <>
+struct TypeConverter<mojom::AutocompleteResultsForProviderPtr,
+ scoped_refptr<AutocompleteProvider>> {
+ static mojom::AutocompleteResultsForProviderPtr Convert(
+ const scoped_refptr<AutocompleteProvider>& input) {
+ mojom::AutocompleteResultsForProviderPtr result(
+ mojom::AutocompleteResultsForProvider::New());
+ result->provider_name = input->GetName();
+ result->results = mojo::ConvertTo<std::vector<mojom::AutocompleteMatchPtr>>(
+ input->matches());
+ return result;
+ }
+};
+
+} // namespace mojo
+
+OmniboxPageHandler::OmniboxPageHandler(
+ Profile* profile,
+ mojo::InterfaceRequest<mojom::OmniboxPageHandler> request)
+ : profile_(profile), binding_(this, std::move(request)) {
+ ResetController();
+}
+
+OmniboxPageHandler::~OmniboxPageHandler() {}
+
+void OmniboxPageHandler::OnResultChanged(bool default_match_changed) {
+ mojom::OmniboxResultPtr result(mojom::OmniboxResult::New());
+ result->done = controller_->done();
+ result->time_since_omnibox_started_ms =
+ (base::Time::Now() - time_omnibox_started_).InMilliseconds();
+ const base::string16 host =
+ input_.text().substr(input_.parts().host.begin, input_.parts().host.len);
+ result->host = base::UTF16ToUTF8(host);
+ bool is_typed_host;
+ if (!LookupIsTypedHost(host, &is_typed_host))
+ is_typed_host = false;
+ result->is_typed_host = is_typed_host;
+
+ {
+ // Copy to an ACMatches to make conversion easier. Since this isn't
+ // performance critical we don't worry about the cost here.
+ ACMatches matches(controller_->result().begin(),
+ controller_->result().end());
+ result->combined_results =
+ mojo::ConvertTo<std::vector<mojom::AutocompleteMatchPtr>>(matches);
+ }
+ result->results_by_provider =
+ mojo::ConvertTo<std::vector<mojom::AutocompleteResultsForProviderPtr>>(
+ controller_->providers());
+
+ // Fill AutocompleteMatch::starred.
+ BookmarkModel* bookmark_model =
+ BookmarkModelFactory::GetForBrowserContext(profile_);
+ if (bookmark_model) {
+ for (size_t i = 0; i < result->combined_results.size(); ++i) {
+ result->combined_results[i]->starred = bookmark_model->IsBookmarked(
+ GURL(result->combined_results[i]->destination_url));
+ }
+ for (size_t i = 0; i < result->results_by_provider.size(); ++i) {
+ const mojom::AutocompleteResultsForProvider& result_by_provider =
+ *result->results_by_provider[i];
+ for (size_t j = 0; j < result_by_provider.results.size(); ++j) {
+ result_by_provider.results[j]->starred = bookmark_model->IsBookmarked(
+ GURL(result_by_provider.results[j]->destination_url));
+ }
+ }
+ }
+
+ page_->HandleNewAutocompleteResult(std::move(result));
+}
+
+bool OmniboxPageHandler::LookupIsTypedHost(const base::string16& host,
+ bool* is_typed_host) const {
+ history::HistoryService* const history_service =
+ HistoryServiceFactory::GetForProfile(profile_,
+ ServiceAccessType::EXPLICIT_ACCESS);
+ if (!history_service)
+ return false;
+ history::URLDatabase* url_db = history_service->InMemoryDatabase();
+ if (!url_db)
+ return false;
+ *is_typed_host = url_db->IsTypedHost(base::UTF16ToUTF8(host));
+ return true;
+}
+
+void OmniboxPageHandler::SetClientPage(mojom::OmniboxPagePtr page) {
+ page_ = std::move(page);
+}
+
+void OmniboxPageHandler::StartOmniboxQuery(const std::string& input_string,
+ int32_t cursor_position,
+ bool prevent_inline_autocomplete,
+ bool prefer_keyword,
+ int32_t page_classification) {
+ // Reset the controller. If we don't do this, then the
+ // AutocompleteController might inappropriately set its |minimal_changes|
+ // variable (or something else) and some providers will short-circuit
+ // important logic and return stale results. In short, we want the
+ // actual results to not depend on the state of the previous request.
+ ResetController();
+ time_omnibox_started_ = base::Time::Now();
+ input_ = AutocompleteInput(
+ base::UTF8ToUTF16(input_string), cursor_position, std::string(), GURL(),
+ base::string16(),
+ static_cast<metrics::OmniboxEventProto::PageClassification>(
+ page_classification),
+ prevent_inline_autocomplete, prefer_keyword, true, true, false,
+ ChromeAutocompleteSchemeClassifier(profile_));
+ controller_->Start(input_);
+}
+
+void OmniboxPageHandler::ResetController() {
+ controller_.reset(new AutocompleteController(
+ base::MakeUnique<ChromeAutocompleteProviderClient>(profile_), this,
+ AutocompleteClassifier::DefaultOmniboxProviders()));
+}
diff --git a/chromium/chrome/browser/ui/webui/omnibox/omnibox_page_handler.h b/chromium/chrome/browser/ui/webui/omnibox/omnibox_page_handler.h
new file mode 100644
index 00000000000..3633baebd73
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/omnibox/omnibox_page_handler.h
@@ -0,0 +1,82 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OMNIBOX_OMNIBOX_PAGE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OMNIBOX_OMNIBOX_PAGE_HANDLER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "chrome/browser/ui/webui/mojo_web_ui_handler.h"
+#include "chrome/browser/ui/webui/omnibox/omnibox.mojom.h"
+#include "components/omnibox/browser/autocomplete_controller_delegate.h"
+#include "components/omnibox/browser/autocomplete_input.h"
+#include "components/omnibox/browser/autocomplete_match.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+class AutocompleteController;
+class Profile;
+
+// Implementation of mojo::OmniboxPageHandler. StartOmniboxQuery() calls to a
+// private AutocompleteController. It also listens for updates from the
+// AutocompleteController to OnResultChanged() and passes those results to
+// the OmniboxPage.
+class OmniboxPageHandler : public AutocompleteControllerDelegate,
+ public mojom::OmniboxPageHandler,
+ public MojoWebUIHandler {
+ public:
+ // OmniboxPageHandler is deleted when the supplied pipe is destroyed.
+ OmniboxPageHandler(Profile* profile,
+ mojo::InterfaceRequest<mojom::OmniboxPageHandler> request);
+ ~OmniboxPageHandler() override;
+
+ // AutocompleteControllerDelegate overrides:
+ void OnResultChanged(bool default_match_changed) override;
+
+ // mojom::OmniboxPageHandler overrides:
+ void SetClientPage(mojom::OmniboxPagePtr page) override;
+ void StartOmniboxQuery(const std::string& input_string,
+ int32_t cursor_position,
+ bool prevent_inline_autocomplete,
+ bool prefer_keyword,
+ int32_t page_classification) override;
+
+ private:
+ // Looks up whether the hostname is a typed host (i.e., has received
+ // typed visits). Return true if the lookup succeeded; if so, the
+ // value of |is_typed_host| is set appropriately.
+ bool LookupIsTypedHost(const base::string16& host, bool* is_typed_host) const;
+
+ // Re-initializes the AutocompleteController in preparation for the
+ // next query.
+ void ResetController();
+
+ // The omnibox AutocompleteController that collects/sorts/dup-
+ // eliminates the results as they come in.
+ std::unique_ptr<AutocompleteController> controller_;
+
+ // Time the user's input was sent to the omnibox to start searching.
+ // Needed because we also pass timing information in the object we
+ // hand back to the javascript.
+ base::Time time_omnibox_started_;
+
+ // The input used when starting the AutocompleteController.
+ AutocompleteInput input_;
+
+ // Handle back to the page by which we can pass results.
+ mojom::OmniboxPagePtr page_;
+
+ // The Profile* handed to us in our constructor.
+ Profile* profile_;
+
+ mojo::Binding<mojom::OmniboxPageHandler> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(OmniboxPageHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_OMNIBOX_OMNIBOX_PAGE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/omnibox/omnibox_ui.cc b/chromium/chrome/browser/ui/webui/omnibox/omnibox_ui.cc
new file mode 100644
index 00000000000..439115ef720
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/omnibox/omnibox_ui.cc
@@ -0,0 +1,37 @@
+// 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/ui/webui/omnibox/omnibox_ui.h"
+
+#include <utility>
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/omnibox/omnibox_page_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+OmniboxUI::OmniboxUI(content::WebUI* web_ui) : MojoWebUIController(web_ui) {
+ // Set up the chrome://omnibox/ source.
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIOmniboxHost);
+ source->AddResourcePath("omnibox.css", IDR_OMNIBOX_CSS);
+ source->AddResourcePath("omnibox.js", IDR_OMNIBOX_JS);
+ source->AddResourcePath("chrome/browser/ui/webui/omnibox/omnibox.mojom",
+ IDR_OMNIBOX_MOJO_JS);
+ source->SetDefaultResource(IDR_OMNIBOX_HTML);
+
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), source);
+}
+
+OmniboxUI::~OmniboxUI() {}
+
+void OmniboxUI::BindUIHandler(
+ const service_manager::BindSourceInfo& source_info,
+ mojom::OmniboxPageHandlerRequest request) {
+ omnibox_handler_.reset(
+ new OmniboxPageHandler(Profile::FromWebUI(web_ui()), std::move(request)));
+}
diff --git a/chromium/chrome/browser/ui/webui/omnibox/omnibox_ui.h b/chromium/chrome/browser/ui/webui/omnibox/omnibox_ui.h
new file mode 100644
index 00000000000..a5a11a3295e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/omnibox/omnibox_ui.h
@@ -0,0 +1,30 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OMNIBOX_OMNIBOX_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_OMNIBOX_OMNIBOX_UI_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/mojo_web_ui_controller.h"
+#include "chrome/browser/ui/webui/omnibox/omnibox.mojom.h"
+
+class OmniboxPageHandler;
+
+// The UI for chrome://omnibox/
+class OmniboxUI : public MojoWebUIController<mojom::OmniboxPageHandler> {
+ public:
+ explicit OmniboxUI(content::WebUI* contents);
+ ~OmniboxUI() override;
+
+ private:
+ // MojoWebUIController overrides:
+ void BindUIHandler(const service_manager::BindSourceInfo& source_info,
+ mojom::OmniboxPageHandlerRequest request) override;
+
+ std::unique_ptr<OmniboxPageHandler> omnibox_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(OmniboxUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_OMNIBOX_OMNIBOX_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/options/DEPS b/chromium/chrome/browser/ui/webui/options/DEPS
new file mode 100644
index 00000000000..0cb6537cd9d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+components/user_manager",
+ "+third_party/libaddressinput", # for third_party/libaddressinput/messages.h
+]
diff --git a/chromium/chrome/browser/ui/webui/options/OWNERS b/chromium/chrome/browser/ui/webui/options/OWNERS
new file mode 100644
index 00000000000..5fb82a30bed
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/OWNERS
@@ -0,0 +1,8 @@
+# This UI is deprecated. See chrome/browser/ui/webui/options/ instead.
+set noparent
+
+dbeam@chromium.org
+stevenjb@chromium.org
+
+per-file sync_setup_handler*=atwilson@chromium.org
+per-file sync_setup_handler*=rogerta@chromium.org
diff --git a/chromium/chrome/browser/ui/webui/options/autofill_options_browsertest.js b/chromium/chrome/browser/ui/webui/options/autofill_options_browsertest.js
new file mode 100644
index 00000000000..79109af9407
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/autofill_options_browsertest.js
@@ -0,0 +1,174 @@
+// 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.
+
+GEN_INCLUDE(['options_browsertest_base.js']);
+
+/**
+ * Returns the HTML element for the |field|.
+ * @param {string} field The field name for the element.
+ * @return {HTMLElement} The HTML element.
+ */
+function getField(field) {
+ return document.querySelector(
+ '#autofill-edit-address-overlay [field=' + field + ']');
+}
+
+/**
+ * Returns the size of the |list|.
+ * @param {HTMLElement} list The list to check.
+ * @return {number} The size of the list.
+ */
+function getListSize(list) {
+ // Remove 1 for placeholder input field.
+ return list.items.length - 1;
+}
+
+/**
+ * TestFixture for autofill options WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function AutofillOptionsWebUITest() {}
+
+AutofillOptionsWebUITest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /**
+ * Browse to autofill options.
+ * @override
+ */
+ browsePreload: 'chrome://settings-frame/autofill',
+};
+
+// TODO(crbug.com/617066) Flakes on Win.
+GEN('#if defined(OS_WIN)');
+GEN('#define MAYBE_testOpenAutofillOptions ' +
+ 'DISABLED_testOpenAutofillOptions');
+GEN('#else');
+GEN('#define MAYBE_testOpenAutofillOptions testOpenAutofillOptions');
+GEN('#endif');
+// Test opening the autofill options has correct location.
+TEST_F('AutofillOptionsWebUITest', 'MAYBE_testOpenAutofillOptions',
+ function() {
+ assertEquals(this.browsePreload, document.location.href);
+});
+
+/**
+ * TestFixture for autofill edit address overlay WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function AutofillEditAddressWebUITest() {}
+
+AutofillEditAddressWebUITest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /** @override */
+ browsePreload: 'chrome://settings-frame/autofillEditAddress',
+};
+
+TEST_F('AutofillEditAddressWebUITest', 'testInitialFormLayout', function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ var fields = ['country', 'phone', 'email', 'fullName', 'city'];
+ for (field in fields) {
+ assertEquals('', getField(fields[field]).value, 'Field: ' + fields[field]);
+ }
+
+ testDone();
+});
+
+TEST_F('AutofillEditAddressWebUITest', 'testLoadAddress', function() {
+ // http://crbug.com/434502
+ // Accessibility failure was originally (and consistently) seen on Mac OS and
+ // Chromium OS. Disabling for all OSs because of a flake in Windows. There is
+ // a possibility for flake in linux too.
+ this.disableAccessibilityChecks();
+
+ assertEquals(this.browsePreload, document.location.href);
+
+ var testAddress = {
+ guid: 'GUID Value',
+ fullName: 'Full Name 1',
+ companyName: 'Company Name Value',
+ addrLines: 'First Line Value\nSecond Line Value',
+ dependentLocality: 'Dependent Locality Value',
+ city: 'City Value',
+ state: 'State Value',
+ postalCode: 'Postal Code Value',
+ sortingCode: 'Sorting Code Value',
+ country: 'CH',
+ phone: '123',
+ email: 'a@b.c',
+ languageCode: 'de',
+ components: [[
+ {field: 'postalCode', length: 'short'},
+ {field: 'sortingCode', length: 'short'},
+ {field: 'dependentLocality', length: 'short'},
+ {field: 'city', length: 'short'},
+ {field: 'state', length: 'short'},
+ {field: 'addrLines', length: 'long'},
+ {field: 'companyName', length: 'long'},
+ {field: 'country', length: 'long'},
+ {field: 'fullName', length: 'long', placeholder: 'Add name'}
+ ]]
+ };
+ AutofillEditAddressOverlay.loadAddress(testAddress);
+
+ var overlay = AutofillEditAddressOverlay.getInstance();
+ assertEquals(testAddress.guid, overlay.guid_);
+ assertEquals(testAddress.languageCode, overlay.languageCode_);
+
+ var inputs = ['companyName', 'dependentLocality', 'city', 'state',
+ 'postalCode', 'sortingCode', 'fullName', 'email', 'phone'];
+ for (var i in inputs) {
+ var field = getField(inputs[i]);
+ assertEquals(testAddress[inputs[i]], field.value);
+ assertTrue(field instanceof HTMLInputElement);
+ }
+
+ var addrLines = getField('addrLines');
+ assertEquals(testAddress.addrLines, addrLines.value);
+ assertTrue(addrLines instanceof HTMLTextAreaElement);
+
+ var country = getField('country');
+ assertEquals(testAddress.country, country.value);
+ assertTrue(country instanceof HTMLSelectElement);
+});
+
+TEST_F('AutofillEditAddressWebUITest', 'testLoadAddressComponents', function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ var testInput = {
+ languageCode: 'fr',
+ components: [[{field: 'city'}],
+ [{field: 'state'}]]
+ };
+ AutofillEditAddressOverlay.loadAddressComponents(testInput);
+
+ assertEquals('fr', AutofillEditAddressOverlay.getInstance().languageCode_);
+ expectEquals(2, $('autofill-edit-address-fields').children.length);
+});
+
+TEST_F('AutofillEditAddressWebUITest', 'testFieldValuesSaved', function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ AutofillEditAddressOverlay.loadAddressComponents({
+ languageCode: 'en',
+ components: [[{field: 'city'}]]
+ });
+ getField('city').value = 'New York';
+
+ AutofillEditAddressOverlay.loadAddressComponents({
+ languageCode: 'en',
+ components: [[{field: 'state'}]]
+ });
+ assertEquals(null, getField('city'));
+
+ AutofillEditAddressOverlay.loadAddressComponents({
+ languageCode: 'en',
+ components: [[{field: 'city'}]]
+ });
+ assertEquals('New York', getField('city').value);
+});
diff --git a/chromium/chrome/browser/ui/webui/options/autofill_options_handler.cc b/chromium/chrome/browser/ui/webui/options/autofill_options_handler.cc
new file mode 100644
index 00000000000..f4480a23c69
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/autofill_options_handler.cc
@@ -0,0 +1,507 @@
+// 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/ui/webui/options/autofill_options_handler.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/guid.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/autofill/personal_data_manager_factory.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/autofill/core/browser/autofill_address_util.h"
+#include "components/autofill/core/browser/autofill_data_util.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/country_combobox_model.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/payments/payments_service_url.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/phone_number_i18n.h"
+#include "components/autofill/core/common/autofill_constants.h"
+#include "components/autofill/core/common/autofill_switches.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_ui.h"
+#include "third_party/libaddressinput/messages.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/webui/web_ui_util.h"
+
+using autofill::AutofillType;
+using autofill::AutofillProfile;
+using autofill::CreditCard;
+using autofill::PersonalDataManager;
+
+namespace {
+
+static const char kComponents[] = "components";
+static const char kLanguageCode[] = "languageCode";
+
+std::unique_ptr<base::DictionaryValue> CreditCardToDictionary(
+ const CreditCard& card) {
+ std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
+ value->SetString("guid", card.guid());
+ std::pair<base::string16, base::string16> label_pieces = card.LabelPieces();
+ value->SetString("label", label_pieces.first);
+ value->SetString("sublabel", label_pieces.second);
+ value->SetBoolean("isLocal", card.record_type() == CreditCard::LOCAL_CARD);
+ value->SetBoolean("isCached",
+ card.record_type() == CreditCard::FULL_SERVER_CARD);
+ return value;
+}
+
+} // namespace
+
+namespace options {
+
+AutofillOptionsHandler::AutofillOptionsHandler() : personal_data_(nullptr) {}
+
+AutofillOptionsHandler::~AutofillOptionsHandler() {
+ if (personal_data_)
+ personal_data_->RemoveObserver(this);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// OptionsPageUIHandler implementation:
+void AutofillOptionsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ static OptionsStringResource resources[] = {
+ { "autofillAddresses", IDS_AUTOFILL_ADDRESSES_GROUP_NAME },
+ { "autofillCreditCards", IDS_AUTOFILL_CREDITCARDS_GROUP_NAME },
+ { "autofillAddAddress", IDS_AUTOFILL_ADD_ADDRESS_BUTTON },
+ { "autofillAddCreditCard", IDS_AUTOFILL_ADD_CREDITCARD_BUTTON },
+ { "autofillEditProfileButton", IDS_AUTOFILL_EDIT_PROFILE_BUTTON },
+ { "autofillFromGoogleAccount", IDS_AUTOFILL_FROM_GOOGLE_ACCOUNT },
+ { "autofillDescribeLocalCopy", IDS_AUTOFILL_DESCRIBE_LOCAL_COPY },
+ { "autofillClearLocalCopyButton", IDS_AUTOFILL_CLEAR_LOCAL_COPY_BUTTON },
+ { "helpButton", IDS_AUTOFILL_HELP_LABEL },
+ { "addAddressTitle", IDS_AUTOFILL_ADD_ADDRESS_CAPTION },
+ { "editAddressTitle", IDS_AUTOFILL_EDIT_ADDRESS_CAPTION },
+ { "addCreditCardTitle", IDS_AUTOFILL_ADD_CREDITCARD_CAPTION },
+ { "editCreditCardTitle", IDS_AUTOFILL_EDIT_CREDITCARD_CAPTION },
+ };
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+ RegisterTitle(localized_strings, "autofillOptionsPage",
+ IDS_AUTOFILL_OPTIONS_TITLE);
+
+ localized_strings->SetString("helpUrl", autofill::kHelpURL);
+
+ personal_data_ = autofill::PersonalDataManagerFactory::GetForProfile(
+ Profile::FromWebUI(web_ui()));
+
+ SetAddressOverlayStrings(localized_strings);
+ SetCreditCardOverlayStrings(localized_strings);
+
+ localized_strings->SetString(
+ "paymentsManageAddressesUrl",
+ autofill::payments::GetManageAddressesUrl(0).spec());
+ localized_strings->SetString(
+ "paymentsManageInstrumentsUrl",
+ autofill::payments::GetManageInstrumentsUrl(0).spec());
+}
+
+void AutofillOptionsHandler::InitializeHandler() {
+ // personal_data_ is NULL in guest mode on Chrome OS.
+ if (personal_data_)
+ personal_data_->AddObserver(this);
+}
+
+void AutofillOptionsHandler::InitializePage() {
+ if (personal_data_)
+ LoadAutofillData();
+}
+
+void AutofillOptionsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "removeData",
+ base::Bind(&AutofillOptionsHandler::RemoveData,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "loadAddressEditor",
+ base::Bind(&AutofillOptionsHandler::LoadAddressEditor,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "loadAddressEditorComponents",
+ base::Bind(&AutofillOptionsHandler::LoadAddressEditorComponents,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "loadCreditCardEditor",
+ base::Bind(&AutofillOptionsHandler::LoadCreditCardEditor,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setAddress",
+ base::Bind(&AutofillOptionsHandler::SetAddress, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setCreditCard",
+ base::Bind(&AutofillOptionsHandler::SetCreditCard,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "clearLocalCardCopy",
+ base::Bind(&AutofillOptionsHandler::RemaskServerCard,
+ base::Unretained(this)));
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// PersonalDataManagerObserver implementation:
+void AutofillOptionsHandler::OnPersonalDataChanged() {
+ LoadAutofillData();
+}
+
+void AutofillOptionsHandler::SetAddressOverlayStrings(
+ base::DictionaryValue* localized_strings) {
+ localized_strings->SetString("autofillEditAddressTitle",
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_EDIT_ADDRESS_CAPTION));
+ localized_strings->SetString("autofillCountryLabel",
+ l10n_util::GetStringUTF16(IDS_LIBADDRESSINPUT_COUNTRY_OR_REGION_LABEL));
+ localized_strings->SetString("autofillPhoneLabel",
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_PHONE));
+ localized_strings->SetString("autofillEmailLabel",
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_EMAIL));
+ SetCountryData(*personal_data_, localized_strings,
+ g_browser_process->GetApplicationLocale());
+}
+
+void AutofillOptionsHandler::SetCreditCardOverlayStrings(
+ base::DictionaryValue* localized_strings) {
+ localized_strings->SetString("autofillEditCreditCardTitle",
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_EDIT_CREDITCARD_CAPTION));
+ localized_strings->SetString("nameOnCardLabel",
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_NAME_ON_CARD));
+ localized_strings->SetString("creditCardNumberLabel",
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_CREDIT_CARD_NUMBER));
+ localized_strings->SetString("creditCardExpirationDateLabel",
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_EXPIRATION_DATE));
+}
+
+void AutofillOptionsHandler::LoadAutofillData() {
+ if (!IsPersonalDataLoaded())
+ return;
+
+ const std::vector<AutofillProfile*>& profiles = personal_data_->GetProfiles();
+ std::vector<base::string16> labels;
+ AutofillProfile::CreateDifferentiatingLabels(
+ profiles,
+ g_browser_process->GetApplicationLocale(),
+ &labels);
+ DCHECK_EQ(labels.size(), profiles.size());
+
+ base::ListValue addresses;
+ for (size_t i = 0; i < profiles.size(); ++i) {
+ // Skip showing auxiliary profiles (e.g. Mac Contacts) for now.
+ if (profiles[i]->record_type() == AutofillProfile::AUXILIARY_PROFILE)
+ continue;
+
+ base::string16 separator =
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR);
+ std::vector<base::string16> label_parts = base::SplitStringUsingSubstr(
+ labels[i], separator, base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+ std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
+ value->SetString("guid", profiles[i]->guid());
+ value->SetString("label", label_parts[0]);
+ value->SetString("sublabel", labels[i].substr(label_parts[0].size()));
+ value->SetBoolean("isLocal", profiles[i]->record_type() ==
+ AutofillProfile::LOCAL_PROFILE);
+ addresses.Append(std::move(value));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("AutofillOptions.setAddressList",
+ addresses);
+
+ base::ListValue credit_cards;
+ const std::vector<CreditCard*>& cards = personal_data_->GetCreditCards();
+ for (const CreditCard* card : cards) {
+ credit_cards.Append(CreditCardToDictionary(*card));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("AutofillOptions.setCreditCardList",
+ credit_cards);
+}
+
+void AutofillOptionsHandler::RemoveData(const base::ListValue* args) {
+ DCHECK(IsPersonalDataLoaded());
+
+ std::string guid;
+ if (!args->GetString(0, &guid)) {
+ NOTREACHED();
+ return;
+ }
+
+ personal_data_->RemoveByGUID(guid);
+}
+
+void AutofillOptionsHandler::LoadAddressEditor(const base::ListValue* args) {
+ DCHECK(IsPersonalDataLoaded());
+
+ std::string guid;
+ if (!args->GetString(0, &guid)) {
+ NOTREACHED();
+ return;
+ }
+
+ const AutofillProfile* prior_profile = personal_data_->GetProfileByGUID(guid);
+ if (!prior_profile) {
+ // There is a race where a user can click once on the close button and
+ // quickly click again on the list item before the item is removed (since
+ // the list is not updated until the model tells the list an item has been
+ // removed). This will activate the editor for a profile that has been
+ // removed. Do nothing in that case.
+ return;
+ }
+
+ base::DictionaryValue address;
+ AutofillProfileToDictionary(*prior_profile, &address);
+
+ web_ui()->CallJavascriptFunctionUnsafe("AutofillOptions.editAddress",
+ address);
+}
+
+void AutofillOptionsHandler::LoadAddressEditorComponents(
+ const base::ListValue* args) {
+ std::string country_code;
+ if (!args->GetString(0, &country_code)) {
+ NOTREACHED();
+ return;
+ }
+
+ base::DictionaryValue input;
+ std::unique_ptr<base::ListValue> components(new base::ListValue);
+ std::string language_code;
+ autofill::GetAddressComponents(country_code,
+ g_browser_process->GetApplicationLocale(),
+ components.get(), &language_code);
+ input.Set(kComponents, std::move(components));
+ input.SetString(kLanguageCode, language_code);
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "AutofillEditAddressOverlay.loadAddressComponents", input);
+}
+
+void AutofillOptionsHandler::LoadCreditCardEditor(const base::ListValue* args) {
+ DCHECK(IsPersonalDataLoaded());
+
+ std::string guid;
+ if (!args->GetString(0, &guid)) {
+ NOTREACHED();
+ return;
+ }
+
+ CreditCard* credit_card = personal_data_->GetCreditCardByGUID(guid);
+ if (!credit_card) {
+ // There is a race where a user can click once on the close button and
+ // quickly click again on the list item before the item is removed (since
+ // the list is not updated until the model tells the list an item has been
+ // removed). This will activate the editor for a profile that has been
+ // removed. Do nothing in that case.
+ return;
+ }
+
+ base::DictionaryValue credit_card_data;
+ credit_card_data.SetString("guid", credit_card->guid());
+ credit_card_data.SetString(
+ "nameOnCard", credit_card->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL));
+ credit_card_data.SetString(
+ "creditCardNumber",
+ credit_card->GetRawInfo(autofill::CREDIT_CARD_NUMBER));
+ credit_card_data.SetString(
+ "expirationMonth",
+ credit_card->GetRawInfo(autofill::CREDIT_CARD_EXP_MONTH));
+ credit_card_data.SetString(
+ "expirationYear",
+ credit_card->GetRawInfo(autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR));
+
+ web_ui()->CallJavascriptFunctionUnsafe("AutofillOptions.editCreditCard",
+ credit_card_data);
+}
+
+void AutofillOptionsHandler::SetAddress(const base::ListValue* args) {
+ if (!IsPersonalDataLoaded())
+ return;
+
+ int arg_counter = 0;
+ std::string guid;
+ if (!args->GetString(arg_counter++, &guid)) {
+ NOTREACHED();
+ return;
+ }
+
+ AutofillProfile profile(guid, autofill::kSettingsOrigin);
+
+ base::string16 full_name;
+ if (args->GetString(arg_counter++, &full_name)) {
+ // Although First/Middle/Last are not displayed on the form, we transfer
+ // this information when they match the full name in the old version of the
+ // profile, if one exists. This is because it may not be possible later to
+ // correctly tokenize the concatenated full name; e.g., when the last name
+ // contains a space, the first word would be treated as a middle name.
+ const AutofillProfile* prior_profile =
+ base::IsValidGUID(profile.guid())
+ ? personal_data_->GetProfileByGUID(guid)
+ : nullptr;
+
+ if (prior_profile && autofill::data_util::ProfileMatchesFullName(
+ full_name, *prior_profile)) {
+ profile.SetRawInfo(autofill::NAME_FULL, full_name);
+
+ profile.SetRawInfo(autofill::NAME_FIRST,
+ prior_profile->GetRawInfo(autofill::NAME_FIRST));
+ profile.SetRawInfo(autofill::NAME_MIDDLE,
+ prior_profile->GetRawInfo(autofill::NAME_MIDDLE));
+ profile.SetRawInfo(autofill::NAME_LAST,
+ prior_profile->GetRawInfo(autofill::NAME_LAST));
+ } else {
+ // In contrast to SetRawInfo, SetInfo will naively attempt to populate the
+ // First/Middle/Last fields by tokenization.
+ profile.SetInfo(AutofillType(autofill::NAME_FULL), full_name,
+ g_browser_process->GetApplicationLocale());
+ }
+ }
+
+ base::string16 value;
+ if (args->GetString(arg_counter++, &value))
+ profile.SetRawInfo(autofill::COMPANY_NAME, value);
+
+ if (args->GetString(arg_counter++, &value))
+ profile.SetRawInfo(autofill::ADDRESS_HOME_STREET_ADDRESS, value);
+
+ if (args->GetString(arg_counter++, &value))
+ profile.SetRawInfo(autofill::ADDRESS_HOME_DEPENDENT_LOCALITY, value);
+
+ if (args->GetString(arg_counter++, &value))
+ profile.SetRawInfo(autofill::ADDRESS_HOME_CITY, value);
+
+ if (args->GetString(arg_counter++, &value))
+ profile.SetRawInfo(autofill::ADDRESS_HOME_STATE, value);
+
+ if (args->GetString(arg_counter++, &value))
+ profile.SetRawInfo(autofill::ADDRESS_HOME_ZIP, value);
+
+ if (args->GetString(arg_counter++, &value))
+ profile.SetRawInfo(autofill::ADDRESS_HOME_SORTING_CODE, value);
+
+ if (args->GetString(arg_counter++, &value))
+ profile.SetRawInfo(autofill::ADDRESS_HOME_COUNTRY, value);
+
+ if (args->GetString(arg_counter++, &value))
+ profile.SetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER, value);
+
+ if (args->GetString(arg_counter++, &value))
+ profile.SetRawInfo(autofill::EMAIL_ADDRESS, value);
+
+ if (args->GetString(arg_counter++, &value))
+ profile.set_language_code(base::UTF16ToUTF8(value));
+
+ if (!base::IsValidGUID(profile.guid())) {
+ profile.set_guid(base::GenerateGUID());
+ personal_data_->AddProfile(profile);
+ } else {
+ personal_data_->UpdateProfile(profile);
+ }
+}
+
+void AutofillOptionsHandler::SetCreditCard(const base::ListValue* args) {
+ if (!IsPersonalDataLoaded())
+ return;
+
+ std::string guid;
+ if (!args->GetString(0, &guid)) {
+ NOTREACHED();
+ return;
+ }
+
+ CreditCard credit_card(guid, autofill::kSettingsOrigin);
+
+ base::string16 value;
+ if (args->GetString(1, &value))
+ credit_card.SetRawInfo(autofill::CREDIT_CARD_NAME_FULL, value);
+
+ if (args->GetString(2, &value))
+ credit_card.SetRawInfo(autofill::CREDIT_CARD_NUMBER, value);
+
+ if (args->GetString(3, &value))
+ credit_card.SetRawInfo(autofill::CREDIT_CARD_EXP_MONTH, value);
+
+ if (args->GetString(4, &value))
+ credit_card.SetRawInfo(autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR, value);
+
+ if (!base::IsValidGUID(credit_card.guid())) {
+ credit_card.set_guid(base::GenerateGUID());
+ personal_data_->AddCreditCard(credit_card);
+ } else {
+ personal_data_->UpdateCreditCard(credit_card);
+ }
+}
+
+void AutofillOptionsHandler::RemaskServerCard(const base::ListValue* args) {
+ std::string guid;
+ if (!args->GetString(0, &guid)) {
+ NOTREACHED();
+ return;
+ }
+
+ personal_data_->ResetFullServerCard(guid);
+}
+
+bool AutofillOptionsHandler::IsPersonalDataLoaded() const {
+ return personal_data_ && personal_data_->IsDataLoaded();
+}
+
+// static
+void AutofillOptionsHandler::AutofillProfileToDictionary(
+ const autofill::AutofillProfile& profile,
+ base::DictionaryValue* address) {
+ address->SetString("guid", profile.guid());
+ address->SetString(
+ autofill::kFullNameField,
+ profile.GetInfo(AutofillType(autofill::NAME_FULL),
+ g_browser_process->GetApplicationLocale()));
+ address->SetString(autofill::kCompanyNameField,
+ profile.GetRawInfo(autofill::COMPANY_NAME));
+ address->SetString(autofill::kAddressLineField,
+ profile.GetRawInfo(autofill::ADDRESS_HOME_STREET_ADDRESS));
+ address->SetString(autofill::kCityField,
+ profile.GetRawInfo(autofill::ADDRESS_HOME_CITY));
+ address->SetString(autofill::kStateField,
+ profile.GetRawInfo(autofill::ADDRESS_HOME_STATE));
+ address->SetString(
+ autofill::kDependentLocalityField,
+ profile.GetRawInfo(autofill::ADDRESS_HOME_DEPENDENT_LOCALITY));
+ address->SetString(autofill::kSortingCodeField,
+ profile.GetRawInfo(autofill::ADDRESS_HOME_SORTING_CODE));
+ address->SetString(autofill::kPostalCodeField,
+ profile.GetRawInfo(autofill::ADDRESS_HOME_ZIP));
+ address->SetString(autofill::kCountryField,
+ profile.GetRawInfo(autofill::ADDRESS_HOME_COUNTRY));
+ address->SetString("phone",
+ profile.GetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER));
+ address->SetString("email", profile.GetRawInfo(autofill::EMAIL_ADDRESS));
+ address->SetString(kLanguageCode, profile.language_code());
+
+ std::unique_ptr<base::ListValue> components(new base::ListValue);
+ autofill::GetAddressComponents(
+ base::UTF16ToUTF8(profile.GetRawInfo(autofill::ADDRESS_HOME_COUNTRY)),
+ profile.language_code(), components.get(), nullptr);
+ address->Set(kComponents, std::move(components));
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/autofill_options_handler.h b/chromium/chrome/browser/ui/webui/options/autofill_options_handler.h
new file mode 100644
index 00000000000..ef00b3ec45b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/autofill_options_handler.h
@@ -0,0 +1,114 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_AUTOFILL_OPTIONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_AUTOFILL_OPTIONS_HANDLER_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "components/autofill/core/browser/personal_data_manager_observer.h"
+
+namespace autofill {
+class AutofillProfile;
+class PersonalDataManager;
+} // namespace autofill
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+namespace options {
+
+class AutofillOptionsHandler : public OptionsPageUIHandler,
+ public autofill::PersonalDataManagerObserver {
+ public:
+ AutofillOptionsHandler();
+ ~AutofillOptionsHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializeHandler() override;
+ void InitializePage() override;
+ void RegisterMessages() override;
+
+ // PersonalDataManagerObserver implementation.
+ void OnPersonalDataChanged() override;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(AutofillOptionsHandlerTest, AddressToDictionary);
+
+ // Loads the strings for the address and credit card overlays.
+ void SetAddressOverlayStrings(base::DictionaryValue* localized_strings);
+ void SetCreditCardOverlayStrings(base::DictionaryValue* localized_strings);
+
+ // Loads Autofill addresses and credit cards using the PersonalDataManager.
+ void LoadAutofillData();
+
+ // Removes data from the PersonalDataManager.
+ // |args| - A string, the GUID of the address or credit card to remove.
+ void RemoveData(const base::ListValue* args);
+
+ // Requests profile data for a specific address. Calls into WebUI with the
+ // loaded profile data to open the address editor.
+ // |args| - A string, the GUID of the address to load.
+ void LoadAddressEditor(const base::ListValue* args);
+
+ // Requests input form layout information for a specific country code. Calls
+ // into WebUI with the layout information.
+ // |args| - A string, the country code to load.
+ void LoadAddressEditorComponents(const base::ListValue* args);
+
+ // Requests profile data for a specific credit card. Calls into WebUI with the
+ // loaded profile data to open the credit card editor.
+ // |args| - A string, the GUID of the credit card to load.
+ void LoadCreditCardEditor(const base::ListValue* args);
+
+ // Adds or updates an address, depending on the GUID of the profile. If the
+ // GUID is empty, a new address is added to the WebDatabase; otherwise, the
+ // address with the matching GUID is updated. Called from WebUI.
+ // |args| - an array containing the GUID of the address followed by the
+ // address data.
+ void SetAddress(const base::ListValue* args);
+
+ // Adds or updates a credit card, depending on the GUID of the profile. If the
+ // GUID is empty, a new credit card is added to the WebDatabase; otherwise,
+ // the credit card with the matching GUID is updated. Called from WebUI.
+ // |args| - an array containing the GUID of the credit card followed by the
+ // credit card data.
+ void SetCreditCard(const base::ListValue* args);
+
+ // Validates a list of phone numbers. The resulting validated list of
+ // numbers is then sent back to the WebUI.
+ // |args| - an array containing the index of the modified or added number, the
+ // array of numbers, and the country code string set on the profile.
+ void ValidatePhoneNumbers(const base::ListValue* args);
+
+ // Resets the masked state on the unmasked Wallet card described by the GUID
+ // in args[0].
+ void RemaskServerCard(const base::ListValue* args);
+
+ // Returns true if |personal_data_| is non-null and loaded.
+ bool IsPersonalDataLoaded() const;
+
+ // Fills in |address| with the data format that the options js expects.
+ static void AutofillProfileToDictionary(
+ const autofill::AutofillProfile& profile,
+ base::DictionaryValue* address);
+
+ // The personal data manager, used to load Autofill profiles and credit cards.
+ // Unowned pointer, may not be NULL.
+ autofill::PersonalDataManager* personal_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillOptionsHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_AUTOFILL_OPTIONS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/autofill_options_handler_unittest.cc b/chromium/chrome/browser/ui/webui/options/autofill_options_handler_unittest.cc
new file mode 100644
index 00000000000..a00cc4cb3d7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/autofill_options_handler_unittest.cc
@@ -0,0 +1,62 @@
+// 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/ui/webui/options/autofill_options_handler.h"
+
+#include "base/values.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace options {
+
+TEST(AutofillOptionsHandlerTest, AddressToDictionary) {
+ autofill::AutofillProfile profile;
+ autofill::test::SetProfileInfoWithGuid(&profile,
+ "guid",
+ "First",
+ "Middle",
+ "Last",
+ "fml@example.com",
+ "Acme inc",
+ "123 Main",
+ "Apt 2",
+ "Laredo",
+ "TX",
+ "77300",
+ "US",
+ "832-555-1000");
+
+ base::DictionaryValue dictionary;
+ options::AutofillOptionsHandler::AutofillProfileToDictionary(profile,
+ &dictionary);
+
+ std::string value;
+ EXPECT_TRUE(dictionary.GetString("addrLines", &value));
+ EXPECT_EQ("123 Main\nApt 2", value);
+ EXPECT_TRUE(dictionary.GetString("city", &value));
+ EXPECT_EQ("Laredo", value);
+ EXPECT_TRUE(dictionary.GetString("country", &value));
+ EXPECT_EQ("US", value);
+ EXPECT_TRUE(dictionary.GetString("dependentLocality", &value));
+ EXPECT_EQ("", value);
+ EXPECT_TRUE(dictionary.GetString("guid", &value));
+ EXPECT_EQ("guid", value);
+ EXPECT_TRUE(dictionary.GetString("languageCode", &value));
+ EXPECT_EQ("", value);
+ EXPECT_TRUE(dictionary.GetString("postalCode", &value));
+ EXPECT_EQ("77300", value);
+ EXPECT_TRUE(dictionary.GetString("sortingCode", &value));
+ EXPECT_EQ("", value);
+ EXPECT_TRUE(dictionary.GetString("state", &value));
+ EXPECT_EQ("TX", value);
+ EXPECT_TRUE(dictionary.GetString("email", &value));
+ EXPECT_EQ("fml@example.com", value);
+ EXPECT_TRUE(dictionary.GetString("fullName", &value));
+ EXPECT_EQ("First Middle Last", value);
+ EXPECT_TRUE(dictionary.GetString("phone", &value));
+ EXPECT_EQ("832-555-1000", value);
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/automatic_settings_reset_handler.cc b/chromium/chrome/browser/ui/webui/options/automatic_settings_reset_handler.cc
new file mode 100644
index 00000000000..f6bf319a8e8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/automatic_settings_reset_handler.cc
@@ -0,0 +1,71 @@
+// 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/ui/webui/options/automatic_settings_reset_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "chrome/browser/prefs/chrome_pref_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_ui.h"
+
+namespace {
+
+void OnDismissedAutomaticSettingsResetBanner(Profile* profile,
+ const base::ListValue* value) {
+ chrome_prefs::ClearResetTime(profile);
+}
+
+} // namespace
+
+namespace options {
+
+AutomaticSettingsResetHandler::AutomaticSettingsResetHandler() {}
+AutomaticSettingsResetHandler::~AutomaticSettingsResetHandler() {}
+
+void AutomaticSettingsResetHandler::InitializePage() {
+ static const int kBannerShowTimeInDays = 5;
+
+ const base::Time then =
+ chrome_prefs::GetResetTime(Profile::FromWebUI(web_ui()));
+ if (!then.is_null()) {
+ const base::Time now = base::Time::Now();
+ if ((now - then).InDays() < kBannerShowTimeInDays)
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "AutomaticSettingsResetBanner.show");
+ }
+}
+
+void AutomaticSettingsResetHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ static const OptionsStringResource resources[] = {
+ { "automaticSettingsResetBannerResetButtonText",
+ IDS_AUTOMATIC_SETTINGS_RESET_BANNER_RESET_BUTTON_TEXT },
+ { "automaticSettingsResetBannerText",
+ IDS_AUTOMATIC_SETTINGS_RESET_BANNER_TEXT },
+ { "automaticSettingsResetLearnMoreUrl",
+ IDS_LEARN_MORE },
+ };
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+ localized_strings->SetString(
+ "automaticSettingsResetLearnMoreUrl",
+ chrome::kAutomaticSettingsResetLearnMoreURL);
+}
+
+void AutomaticSettingsResetHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "onDismissedAutomaticSettingsResetBanner",
+ base::Bind(&OnDismissedAutomaticSettingsResetBanner,
+ Profile::FromWebUI(web_ui())));
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/automatic_settings_reset_handler.h b/chromium/chrome/browser/ui/webui/options/automatic_settings_reset_handler.h
new file mode 100644
index 00000000000..1b4baed4b69
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/automatic_settings_reset_handler.h
@@ -0,0 +1,34 @@
+// 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_UI_WEBUI_OPTIONS_AUTOMATIC_SETTINGS_RESET_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_AUTOMATIC_SETTINGS_RESET_HANDLER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+namespace options {
+
+// Handler for the banner that displays a settings reset event at the top of the
+// settings page.
+class AutomaticSettingsResetHandler : public OptionsPageUIHandler {
+ public:
+ AutomaticSettingsResetHandler();
+ ~AutomaticSettingsResetHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializePage() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutomaticSettingsResetHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_AUTOMATIC_SETTINGS_RESET_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/browser_options_browsertest.js b/chromium/chrome/browser/ui/webui/options/browser_options_browsertest.js
new file mode 100644
index 00000000000..b5713f60ca6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/browser_options_browsertest.js
@@ -0,0 +1,131 @@
+// 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.
+
+GEN_INCLUDE(['options_browsertest_base.js']);
+
+/**
+ * TestFixture for browser options WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function BrowserOptionsWebUITest() {}
+
+BrowserOptionsWebUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /** @override */
+ browsePreload: 'chrome://chrome/settings/',
+};
+
+// Test opening the browser options has correct location.
+// Times out on Mac debug only. See http://crbug.com/121030
+GEN('#if defined(OS_MACOSX) && !defined(NDEBUG)');
+GEN('#define MAYBE_testOpenBrowserOptions ' +
+ 'DISABLED_testOpenBrowserOptions');
+GEN('#else');
+GEN('#define MAYBE_testOpenBrowserOptions testOpenBrowserOptions');
+GEN('#endif // defined(OS_MACOSX)');
+TEST_F('BrowserOptionsWebUITest', 'MAYBE_testOpenBrowserOptions', function() {
+ assertEquals(this.browsePreload, document.location.href);
+ expectFalse($('navigation').classList.contains('background'));
+});
+
+/**
+ * TestFixture for the uber page when the browser options page has an overlay.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function BrowserOptionsOverlayWebUITest() {}
+
+BrowserOptionsOverlayWebUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /** @override */
+ browsePreload: 'chrome://chrome/settings/autofill',
+
+ /** @override */
+ isAsync: true,
+};
+
+TEST_F('BrowserOptionsOverlayWebUITest', 'testNavigationInBackground',
+ function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ if ($('navigation').classList.contains('background')) {
+ testDone();
+ return;
+ }
+
+ // Wait for the message to be posted to the Uber page.
+ window.addEventListener('message', function(e) {
+ if (e.data.method == 'beginInterceptingEvents') {
+ window.setTimeout(function() {
+ assertTrue($('navigation').classList.contains('background'));
+ testDone();
+ });
+ }
+ });
+});
+
+/**
+ * @extends {testing.Test}
+ * @constructor
+ */
+function BrowserOptionsFrameWebUITest() {}
+
+BrowserOptionsFrameWebUITest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /** @override */
+ browsePreload: 'chrome://settings-frame/',
+
+ /** @override */
+ setUp: function() {
+ OptionsBrowsertestBase.prototype.setUp.call(this);
+
+ // Enable when failure is resolved.
+ // AX_TEXT_04: http://crbug.com/570721
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'linkWithUnclearPurpose',
+ '#sync-overview > A');
+
+ // Enable when failure is resolved.
+ // AX_ARIA_10: http://crbug.com/570723
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'unsupportedAriaAttribute',
+ '#profiles-list');
+ },
+};
+
+TEST_F('BrowserOptionsFrameWebUITest', 'testAdvancedSettingsHiddenByDefault',
+ function() {
+ assertEquals(this.browsePreload, document.location.href);
+ expectTrue($('advanced-settings').hidden);
+});
+
+/**
+ * @extends {testing.Test}
+ * @constructor
+ */
+function AdvancedSettingsWebUITest() {}
+
+AdvancedSettingsWebUITest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /** @override */
+ browsePreload: 'chrome://settings-frame/autofill',
+};
+
+// TODO(crbug.com/617066) Flakes on Win.
+GEN('#if defined(OS_WIN)');
+GEN('#define MAYBE_testAdvancedSettingsShown ' +
+ 'DISABLED_testAdvancedSettingsShown');
+GEN('#else');
+GEN('#define MAYBE_testAdvancedSettingsShown testAdvancedSettingsShown');
+GEN('#endif');
+TEST_F('AdvancedSettingsWebUITest', 'MAYBE_testAdvancedSettingsShown',
+ function() {
+ assertEquals(this.browsePreload, document.location.href);
+ expectFalse($('advanced-settings').hidden);
+});
diff --git a/chromium/chrome/browser/ui/webui/options/browser_options_handler.cc b/chromium/chrome/browser/ui/webui/options/browser_options_handler.cc
new file mode 100644
index 00000000000..61ce8bf577c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/browser_options_handler.cc
@@ -0,0 +1,2356 @@
+// 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/ui/webui/options/browser_options_handler.h"
+
+#include <stddef.h>
+
+#include <set>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/i18n/number_formatting.h"
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/value_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/custom_home_pages_table_model.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/extensions/settings_api_helpers.h"
+#include "chrome/browser/gpu/gpu_mode_manager.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
+#include "chrome/browser/metrics/metrics_reporting_state.h"
+#include "chrome/browser/net/prediction_options.h"
+#include "chrome/browser/prefs/session_startup_pref.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/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_avatar_icon_util.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_metrics.h"
+#include "chrome/browser/profiles/profile_shortcut_manager.h"
+#include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/profiles/profiles_state.h"
+#include "chrome/browser/search/hotword_audio_history_handler.h"
+#include "chrome/browser/search/hotword_service.h"
+#include "chrome/browser/search/hotword_service_factory.h"
+#include "chrome/browser/search/search.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/browser/signin/easy_unlock_service.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/signin_ui_util.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/sync/sync_ui_util.h"
+#include "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/themes/theme_service_factory.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/chrome_select_file_policy.h"
+#include "chrome/browser/ui/passwords/manage_passwords_view_utils_desktop.h"
+#include "chrome/browser/ui/webui/favicon_source.h"
+#include "chrome/browser/ui/webui/profile_helper.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/features.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/locale_settings.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/metrics/metrics_pref_names.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/core/common/policy_service.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "components/proximity_auth/switches.h"
+#include "components/proxy_config/proxy_config_pref_names.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/search_engines/template_url.h"
+#include "components/search_engines/template_url_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/signin/core/common/profile_management_switches.h"
+#include "components/signin/core/common/signin_pref_names.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/user_manager/user_type.h"
+#include "components/zoom/page_zoom.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/browser_url_handler.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/page_zoom.h"
+#include "extensions/browser/extension_registry.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "printing/features/features.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/webui/web_ui_util.h"
+
+#if !defined(OS_CHROMEOS)
+#include "chrome/browser/ui/startup/default_browser_prompt.h"
+#include "chrome/browser/ui/webui/settings_utils.h"
+#endif
+
+#if defined(OS_CHROMEOS)
+#include "ash/accessibility_types.h" // nogncheck
+#include "ash/shell.h" // nogncheck
+#include "ash/system/devicetype_utils.h" // nogncheck
+#include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/chromeos/accessibility/accessibility_util.h"
+#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h"
+#include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
+#include "chrome/browser/chromeos/net/wake_on_wifi_manager.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/reset/metrics.h"
+#include "chrome/browser/chromeos/system/timezone_util.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/policy/profile_policy_connector_factory.h"
+#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
+#include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/power_manager_client.h"
+#include "components/arc/arc_util.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
+#include "ui/gfx/image/image_skia.h"
+#endif // defined(OS_CHROMEOS)
+
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+#include "chrome/browser/printing/cloud_print/privet_notifications.h"
+#endif
+
+using base::UserMetricsAction;
+using content::BrowserContext;
+using content::BrowserThread;
+using content::DownloadManager;
+using content::OpenURLParams;
+using content::Referrer;
+using extensions::Extension;
+using extensions::ExtensionRegistry;
+
+namespace {
+
+void AppendExtensionData(const std::string& key,
+ const Extension* extension,
+ base::DictionaryValue* dict) {
+ std::unique_ptr<base::DictionaryValue> details(new base::DictionaryValue);
+ details->SetString("id", extension ? extension->id() : std::string());
+ details->SetString("name", extension ? extension->name() : std::string());
+ dict->Set(key, std::move(details));
+}
+
+#if !defined(OS_CHROMEOS)
+bool IsDisabledByPolicy(const BooleanPrefMember& pref) {
+ return pref.IsManaged() && !pref.GetValue();
+}
+#endif // !defined(OS_CHROMEOS)
+
+std::string GetSyncErrorAction(sync_ui_util::ActionType action_type) {
+ switch (action_type) {
+ case sync_ui_util::REAUTHENTICATE:
+ return "reauthenticate";
+ case sync_ui_util::SIGNOUT_AND_SIGNIN:
+ return "signOutAndSignIn";
+ case sync_ui_util::UPGRADE_CLIENT:
+ return "upgradeClient";
+ case sync_ui_util::ENTER_PASSPHRASE:
+ return "enterPassphrase";
+ default:
+ return "noAction";
+ }
+}
+
+#if defined(OS_CHROMEOS)
+bool g_enable_polymer_preload = true;
+#endif // defined(OS_CHROMEOS)
+} // namespace
+
+namespace options {
+
+BrowserOptionsHandler::BrowserOptionsHandler()
+ : page_initialized_(false),
+ template_url_service_(NULL),
+ cloud_print_mdns_ui_enabled_(false),
+#if defined(OS_CHROMEOS)
+ enable_factory_reset_(false),
+#endif // defined(OS_CHROMEOS)
+ signin_observer_(this),
+ weak_ptr_factory_(this) {
+#if !defined(OS_CHROMEOS)
+ // The worker pointer is reference counted. While it is running, the
+ // message loops of the FILE and UI thread will hold references to it
+ // and it will be automatically freed once all its tasks have finished.
+ default_browser_worker_ = new shell_integration::DefaultBrowserWorker(
+ base::Bind(&BrowserOptionsHandler::OnDefaultBrowserWorkerFinished,
+ weak_ptr_factory_.GetWeakPtr()));
+#endif // !defined(OS_CHROMEOS)
+
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+ cloud_print_mdns_ui_enabled_ = true;
+#endif
+}
+
+BrowserOptionsHandler::~BrowserOptionsHandler() {
+ browser_sync::ProfileSyncService* sync_service(
+ ProfileSyncServiceFactory::GetInstance()->GetForProfile(
+ Profile::FromWebUI(web_ui())));
+ if (sync_service)
+ sync_service->RemoveObserver(this);
+
+ if (template_url_service_)
+ template_url_service_->RemoveObserver(this);
+ // There may be pending file dialogs, we need to tell them that we've gone
+ // away so they don't try and call back to us.
+ if (select_folder_dialog_.get())
+ select_folder_dialog_->ListenerDestroyed();
+
+ g_browser_process->policy_service()->RemoveObserver(
+ policy::POLICY_DOMAIN_CHROME, this);
+}
+
+void BrowserOptionsHandler::GetLocalizedValues(base::DictionaryValue* values) {
+ DCHECK(values);
+
+#if defined(OS_CHROMEOS)
+ const int device_type_resource_id = ash::GetChromeOSDeviceTypeResourceId();
+ const int enable_logging_resource_id =
+ IDS_OPTIONS_ENABLE_LOGGING_DIAGNOSTIC_AND_USAGE_DATA;
+#else
+ const int device_type_resource_id = IDS_EASY_UNLOCK_GENERIC_DEVICE_TYPE;
+ const int enable_logging_resource_id = IDS_OPTIONS_ENABLE_LOGGING;
+#endif // defined(OS_CHROMEOS)
+
+ static OptionsStringResource resources[] = {
+ // Please keep these in alphabetical order.
+ {"accessibilityFeaturesLink", IDS_OPTIONS_ACCESSIBILITY_FEATURES_LINK},
+ {"accessibilityTitle", IDS_OPTIONS_SETTINGS_SECTION_TITLE_ACCESSIBILITY},
+ {"advancedSectionTitleCertificates",
+ IDS_OPTIONS_ADVANCED_SECTION_TITLE_CERTIFICATES},
+ {"advancedSectionTitleCloudPrint", IDS_GOOGLE_CLOUD_PRINT},
+ {"advancedSectionTitleContent", IDS_OPTIONS_ADVANCED_SECTION_TITLE_CONTENT},
+ {"advancedSectionTitleLanguages",
+ IDS_OPTIONS_ADVANCED_SECTION_TITLE_LANGUAGES},
+ {"advancedSectionTitleNetwork", IDS_OPTIONS_ADVANCED_SECTION_TITLE_NETWORK},
+ {"advancedSectionTitlePrivacy", IDS_OPTIONS_ADVANCED_SECTION_TITLE_PRIVACY},
+ {"advancedSectionTitleSystem", IDS_OPTIONS_ADVANCED_SECTION_TITLE_SYSTEM},
+ {"autoOpenFileTypesInfo", IDS_OPTIONS_OPEN_FILE_TYPES_AUTOMATICALLY},
+ {"autoOpenFileTypesResetToDefault",
+ IDS_OPTIONS_AUTOOPENFILETYPES_RESETTODEFAULT},
+ {"autofillEnabled", IDS_OPTIONS_AUTOFILL_ENABLE},
+ {"autologinEnabled", IDS_OPTIONS_PASSWORDS_AUTOLOGIN},
+ {"certificatesManageButton", IDS_OPTIONS_CERTIFICATES_MANAGE_BUTTON},
+ {"changeHomePage", IDS_OPTIONS_CHANGE_HOME_PAGE},
+ {"childLabel", IDS_PROFILES_LIST_CHILD_LABEL},
+ {"currentUserOnly", IDS_OPTIONS_CURRENT_USER_ONLY},
+ {"customizeSync", IDS_OPTIONS_CUSTOMIZE_SYNC_BUTTON_LABEL},
+ {"defaultBrowserUnknown", IDS_OPTIONS_DEFAULTBROWSER_UNKNOWN,
+ IDS_PRODUCT_NAME},
+ {"defaultBrowserUseAsDefault", IDS_OPTIONS_DEFAULTBROWSER_USEASDEFAULT},
+ {"defaultFontSizeLabel", IDS_OPTIONS_DEFAULT_FONT_SIZE_LABEL},
+ {"defaultSearchManageEngines", IDS_OPTIONS_DEFAULTSEARCH_MANAGE_ENGINES},
+ {"defaultZoomFactorLabel", IDS_OPTIONS_DEFAULT_ZOOM_LEVEL_LABEL},
+ {"disableWebServices", IDS_OPTIONS_DISABLE_WEB_SERVICES},
+ {"doNotTrack", IDS_OPTIONS_ENABLE_DO_NOT_TRACK},
+ {"doNotTrackConfirmDisable",
+ IDS_OPTIONS_ENABLE_DO_NOT_TRACK_BUBBLE_DISABLE},
+ {"doNotTrackConfirmEnable", IDS_OPTIONS_ENABLE_DO_NOT_TRACK_BUBBLE_ENABLE},
+ {"doNotTrackConfirmMessage", IDS_OPTIONS_ENABLE_DO_NOT_TRACK_BUBBLE_TEXT},
+ {"downloadLocationAskForSaveLocation",
+ IDS_OPTIONS_DOWNLOADLOCATION_ASKFORSAVELOCATION},
+ {"downloadLocationBrowseTitle", IDS_OPTIONS_DOWNLOADLOCATION_BROWSE_TITLE},
+ {"downloadLocationChangeButton",
+ IDS_OPTIONS_DOWNLOADLOCATION_CHANGE_BUTTON},
+ {"downloadLocationGroupName", IDS_OPTIONS_DOWNLOADLOCATION_GROUP_NAME},
+ {"easyUnlockDescription", IDS_OPTIONS_EASY_UNLOCK_DESCRIPTION,
+ device_type_resource_id},
+ {"easyUnlockRequireProximityLabel",
+ IDS_OPTIONS_EASY_UNLOCK_REQUIRE_PROXIMITY_LABEL, device_type_resource_id},
+ {"easyUnlockSectionTitle", IDS_OPTIONS_EASY_UNLOCK_SECTION_TITLE},
+ {"easyUnlockSetupButton", IDS_OPTIONS_EASY_UNLOCK_SETUP_BUTTON},
+ {"easyUnlockSetupIntro", IDS_OPTIONS_EASY_UNLOCK_SETUP_INTRO,
+ device_type_resource_id},
+ {"enableLogging", enable_logging_resource_id},
+ {"extensionControlled", IDS_OPTIONS_TAB_EXTENSION_CONTROLLED},
+ {"extensionDisable", IDS_OPTIONS_TAB_EXTENSION_CONTROLLED_DISABLE},
+ {"fontSettingsCustomizeFontsButton",
+ IDS_OPTIONS_FONTSETTINGS_CUSTOMIZE_FONTS_BUTTON},
+ {"fontSizeLabelCustom", IDS_OPTIONS_FONT_SIZE_LABEL_CUSTOM},
+ {"fontSizeLabelLarge", IDS_OPTIONS_FONT_SIZE_LABEL_LARGE},
+ {"fontSizeLabelMedium", IDS_OPTIONS_FONT_SIZE_LABEL_MEDIUM},
+ {"fontSizeLabelSmall", IDS_OPTIONS_FONT_SIZE_LABEL_SMALL},
+ {"fontSizeLabelVeryLarge", IDS_OPTIONS_FONT_SIZE_LABEL_VERY_LARGE},
+ {"fontSizeLabelVerySmall", IDS_OPTIONS_FONT_SIZE_LABEL_VERY_SMALL},
+ {"googleNowLauncherEnable", IDS_OPTIONS_ENABLE_GOOGLE_NOW},
+ {"hideAdvancedSettings", IDS_SETTINGS_HIDE_ADVANCED_SETTINGS},
+ {"homePageNtp", IDS_OPTIONS_HOMEPAGE_NTP},
+ {"homePageShowHomeButton", IDS_OPTIONS_TOOLBAR_SHOW_HOME_BUTTON},
+ {"homePageUseNewTab", IDS_OPTIONS_HOMEPAGE_USE_NEWTAB},
+ {"homePageUseURL", IDS_OPTIONS_HOMEPAGE_USE_URL},
+ {"hotwordAlwaysOnAudioHistoryDescription",
+ IDS_HOTWORD_ALWAYS_ON_AUDIO_HISTORY_DESCRIPTION},
+ {"hotwordAlwaysOnDesc", IDS_HOTWORD_SEARCH_ALWAYS_ON_DESCRIPTION},
+ {"hotwordAudioHistoryManage", IDS_HOTWORD_AUDIO_HISTORY_MANAGE_LINK},
+ {"hotwordAudioLoggingEnable", IDS_HOTWORD_AUDIO_LOGGING_ENABLE},
+ {"hotwordConfirmDisable", IDS_HOTWORD_CONFIRM_BUBBLE_DISABLE},
+ {"hotwordConfirmEnable", IDS_HOTWORD_CONFIRM_BUBBLE_ENABLE},
+ {"hotwordConfirmMessage", IDS_HOTWORD_SEARCH_PREF_DESCRIPTION},
+ {"hotwordNoDSPDesc", IDS_HOTWORD_SEARCH_NO_DSP_DESCRIPTION},
+ {"hotwordRetrainLink", IDS_HOTWORD_RETRAIN_LINK},
+ {"hotwordSearchEnable", IDS_HOTWORD_SEARCH_PREF_CHKBOX},
+ {"importData", IDS_OPTIONS_IMPORT_DATA_BUTTON},
+ {"improveBrowsingExperience", IDS_OPTIONS_IMPROVE_BROWSING_EXPERIENCE},
+ {"languageAndSpellCheckSettingsButton",
+ IDS_OPTIONS_SETTINGS_LANGUAGE_AND_INPUT_SETTINGS},
+#if defined(OS_CHROMEOS)
+ {"languageSectionLabel", IDS_OPTIONS_ADVANCED_LANGUAGE_LABEL,
+ IDS_SHORT_PRODUCT_OS_NAME},
+#else
+ {"languageSectionLabel", IDS_OPTIONS_ADVANCED_LANGUAGE_LABEL,
+ IDS_SHORT_PRODUCT_NAME},
+#endif
+ {"linkDoctorPref", IDS_OPTIONS_LINKDOCTOR_PREF},
+ {"manageAutofillSettings", IDS_OPTIONS_MANAGE_AUTOFILL_SETTINGS_LINK},
+ {"manageLanguages", IDS_OPTIONS_TRANSLATE_MANAGE_LANGUAGES},
+ {"managePasswords", IDS_OPTIONS_PASSWORDS_MANAGE_PASSWORDS_LINK},
+ {"metricsReportingResetRestart", IDS_OPTIONS_ENABLE_LOGGING_RESTART},
+ {"networkPredictionEnabledDescription",
+ IDS_NETWORK_PREDICTION_ENABLED_DESCRIPTION},
+ {"passwordManagerEnabled", GetPasswordManagerSettingsStringId(
+ ProfileSyncServiceFactory::GetForProfile(
+ Profile::FromWebUI(web_ui())))},
+ {"passwordsAndAutofillGroupName",
+ IDS_OPTIONS_PASSWORDS_AND_FORMS_GROUP_NAME},
+ {"privacyClearDataButton", IDS_OPTIONS_PRIVACY_CLEAR_DATA_BUTTON},
+ {"privacyContentSettingsButton",
+ IDS_OPTIONS_PRIVACY_CONTENT_SETTINGS_BUTTON},
+ {"profileAddPersonEnable", IDS_PROFILE_ADD_PERSON_ENABLE},
+ {"profileBrowserGuestEnable", IDS_PROFILE_BROWSER_GUEST_ENABLE},
+ {"profilesCreate", IDS_PROFILES_CREATE_BUTTON_LABEL},
+ {"profilesDelete", IDS_PROFILES_DELETE_BUTTON_LABEL},
+ {"profilesDeleteSingle", IDS_PROFILES_DELETE_SINGLE_BUTTON_LABEL},
+ {"profilesListItemCurrent", IDS_PROFILES_LIST_ITEM_CURRENT},
+ {"profilesManage", IDS_PROFILES_MANAGE_BUTTON_LABEL},
+ {"profilesSingleUser", IDS_PROFILES_SINGLE_USER_MESSAGE, IDS_PRODUCT_NAME},
+ {"proxiesLabelExtension", IDS_OPTIONS_EXTENSION_PROXIES_LABEL},
+ {"proxiesLabelSystem", IDS_OPTIONS_SYSTEM_PROXIES_LABEL, IDS_PRODUCT_NAME},
+ {"resetProfileSettings", IDS_RESET_PROFILE_SETTINGS_BUTTON},
+ {"resetProfileSettingsDescription", IDS_RESET_PROFILE_SETTINGS_DESCRIPTION},
+ {"resetProfileSettingsSectionTitle",
+ IDS_RESET_PROFILE_SETTINGS_SECTION_TITLE},
+ {"safeBrowsingEnableProtection", IDS_OPTIONS_SAFEBROWSING_ENABLEPROTECTION},
+ {"sectionTitleAppearance", IDS_APPEARANCE_GROUP_NAME},
+ {"sectionTitleDefaultBrowser", IDS_OPTIONS_DEFAULTBROWSER_GROUP_NAME},
+ {"sectionTitleProxy", IDS_OPTIONS_PROXY_GROUP_NAME},
+ {"sectionTitleSearch", IDS_OPTIONS_DEFAULTSEARCH_GROUP_NAME},
+ {"sectionTitleStartup", IDS_OPTIONS_STARTUP_GROUP_NAME},
+ {"sectionTitleSync", IDS_SYNC_OPTIONS_GROUP_NAME},
+ {"sectionTitleUsers", IDS_PROFILES_OPTIONS_GROUP_NAME},
+ {"settingsTitle", IDS_SETTINGS_TITLE},
+ {"showAdvancedSettings", IDS_SETTINGS_SHOW_ADVANCED_SETTINGS},
+ {"spellingConfirmDisable", IDS_CONTENT_CONTEXT_SPELLING_BUBBLE_DISABLE},
+ {"spellingConfirmEnable", IDS_CONTENT_CONTEXT_SPELLING_BUBBLE_ENABLE},
+ {"spellingConfirmMessage", IDS_CONTENT_CONTEXT_SPELLING_BUBBLE_TEXT},
+ {"spellingPref", IDS_OPTIONS_SPELLING_PREF},
+ {"startupRestoreLastSession", IDS_OPTIONS_STARTUP_RESTORE_LAST_SESSION},
+ {"startupSetPages", IDS_OPTIONS_STARTUP_SET_PAGES},
+ {"startupShowNewTab", IDS_OPTIONS_STARTUP_SHOW_NEWTAB},
+ {"startupShowPages", IDS_OPTIONS_STARTUP_SHOW_PAGES},
+ {"suggestPref", IDS_OPTIONS_SUGGEST_PREF},
+ {"supervisedUserLabel", IDS_PROFILES_LIST_LEGACY_SUPERVISED_USER_LABEL},
+ {"syncButtonTextInProgress", IDS_SYNC_NTP_SETUP_IN_PROGRESS},
+ {"syncButtonTextSignIn", IDS_SYNC_START_SYNC_BUTTON_LABEL,
+ IDS_SHORT_PRODUCT_NAME},
+ {"syncButtonTextStop", IDS_SYNC_STOP_SYNCING_BUTTON_LABEL},
+ {"syncOverview", IDS_SYNC_OVERVIEW},
+ {"tabsToLinksPref", IDS_OPTIONS_TABS_TO_LINKS_PREF},
+ {"themesGallery", IDS_THEMES_GALLERY_BUTTON},
+ {"themesGalleryURL", IDS_THEMES_GALLERY_URL},
+ {"themesReset", IDS_THEMES_RESET_BUTTON},
+ {"toolbarShowBookmarksBar", IDS_OPTIONS_TOOLBAR_SHOW_BOOKMARKS_BAR},
+ {"toolbarShowHomeButton", IDS_OPTIONS_TOOLBAR_SHOW_HOME_BUTTON},
+ {"translateEnableTranslate", IDS_OPTIONS_TRANSLATE_ENABLE_TRANSLATE},
+#if defined(OS_CHROMEOS)
+ {"accessibilityAlwaysShowMenu",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SHOULD_ALWAYS_SHOW_MENU},
+ {"accessibilityAutoclick",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_AUTOCLICK_DESCRIPTION},
+ {"accessibilityAutoclickDropdown",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_AUTOCLICK_DROPDOWN_DESCRIPTION},
+ {"accessibilityCaretHighlight",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_CARET_HIGHLIGHT_DESCRIPTION},
+ {"accessibilityCursorHighlight",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_CURSOR_HIGHLIGHT_DESCRIPTION},
+ {"accessibilityExplanation",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_EXPLANATION},
+ {"accessibilityFocusHighlight",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_FOCUS_HIGHLIGHT_DESCRIPTION},
+ {"accessibilityHighContrast",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_HIGH_CONTRAST_DESCRIPTION},
+ {"accessibilityLargeCursor",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_LARGE_CURSOR_DESCRIPTION},
+ {"accessibilityScreenMagnifier",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SCREEN_MAGNIFIER_DESCRIPTION},
+ {"accessibilityScreenMagnifierCenterFocus",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SCREEN_MAGNIFIER_CENTER_FOCUS},
+ {"accessibilityScreenMagnifierFull",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SCREEN_MAGNIFIER_FULL},
+ {"accessibilityScreenMagnifierOff",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SCREEN_MAGNIFIER_OFF},
+ {"accessibilityScreenMagnifierPartial",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SCREEN_MAGNIFIER_PARTIAL},
+ {"accessibilitySelectToSpeak",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_DESCRIPTION},
+ {"accessibilitySettings", IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SETTINGS},
+ {"accessibilitySpokenFeedback",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SPOKEN_FEEDBACK_DESCRIPTION},
+ {"accessibilityStickyKeys",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_STICKY_KEYS_DESCRIPTION},
+ {"accessibilitySwitchAccess",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SWITCH_ACCESS_DESCRIPTION},
+ {"accessibilityTalkBackSettings",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_TALKBACK_SETTINGS},
+ {"accessibilityTapDragging",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_TOUCHPAD_TAP_DRAGGING_DESCRIPTION},
+ {"accessibilityVirtualKeyboard",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_VIRTUAL_KEYBOARD_DESCRIPTION},
+ {"accessibilityMonoAudio",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_MONO_AUDIO_DESCRIPTION},
+ {"advancedSectionTitleCupsPrint",
+ IDS_OPTIONS_ADVANCED_SECTION_TITLE_CUPS_PRINT},
+ {"androidAppsTitle", IDS_OPTIONS_ARC_TITLE},
+ {"androidAppsEnabled", IDS_OPTIONS_ARC_ENABLE},
+ {"androidAppsSettingsLabel", IDS_OPTIONS_ARC_MANAGE_APPS},
+ {"arcOptOutConfirmOverlayTabTitle", IDS_ARC_OPT_OUT_TAB_TITLE},
+ {"arcOptOutDialogHeader", IDS_ARC_OPT_OUT_DIALOG_HEADER},
+ {"arcOptOutDialogDescription", IDS_ARC_OPT_OUT_DIALOG_DESCRIPTION},
+ {"arcOptOutDialogButtonConfirmDisable",
+ IDS_ARC_OPT_OUT_DIALOG_BUTTON_CONFIRM_DISABLE},
+ {"arcOptOutDialogButtonCancel", IDS_ARC_OPT_OUT_DIALOG_BUTTON_CANCEL},
+ {"autoclickDelayExtremelyShort",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_AUTOCLICK_DELAY_EXTREMELY_SHORT},
+ {"autoclickDelayLong",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_AUTOCLICK_DELAY_LONG},
+ {"autoclickDelayShort",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_AUTOCLICK_DELAY_SHORT},
+ {"autoclickDelayVeryLong",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_AUTOCLICK_DELAY_VERY_LONG},
+ {"autoclickDelayVeryShort",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_AUTOCLICK_DELAY_VERY_SHORT},
+ {"changePicture", IDS_OPTIONS_CHANGE_PICTURE},
+ {"changePictureCaption", IDS_OPTIONS_CHANGE_PICTURE_CAPTION},
+ {"cupsPrintOptionLabel", IDS_OPTIONS_ADVANCED_SECTION_CUPS_PRINT_LABEL},
+ {"cupsPrintersManageButton",
+ IDS_OPTIONS_ADVANCED_SECTION_CUPS_PRINT_MANAGE_BUTTON},
+ {"datetimeTitle", IDS_OPTIONS_SETTINGS_SECTION_TITLE_DATETIME},
+ {"deviceGroupDescription", IDS_OPTIONS_DEVICE_GROUP_DESCRIPTION},
+ {"deviceGroupPointer", IDS_OPTIONS_DEVICE_GROUP_POINTER_SECTION},
+ {"disableGData", IDS_OPTIONS_DISABLE_GDATA},
+ {"displayOptions", IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_BUTTON_LABEL},
+ {"enableContentProtectionAttestation",
+ IDS_OPTIONS_ENABLE_CONTENT_PROTECTION_ATTESTATION},
+ {"manageScreenlock", IDS_OPTIONS_MANAGE_SCREENLOCKER},
+ {"factoryResetDataRestart", IDS_RELAUNCH_BUTTON},
+ {"factoryResetDescription", IDS_OPTIONS_FACTORY_RESET_DESCRIPTION,
+ IDS_SHORT_PRODUCT_NAME},
+ {"factoryResetHeading", IDS_OPTIONS_FACTORY_RESET_HEADING},
+ {"factoryResetHelpUrl", IDS_FACTORY_RESET_HELP_URL},
+ {"factoryResetRestart", IDS_OPTIONS_FACTORY_RESET_BUTTON},
+ {"factoryResetTitle", IDS_OPTIONS_FACTORY_RESET},
+ {"factoryResetWarning", IDS_OPTIONS_FACTORY_RESET_WARNING},
+ {"internetOptionsButtonTitle", IDS_OPTIONS_INTERNET_OPTIONS_BUTTON_TITLE},
+ {"keyboardSettingsButtonTitle",
+ IDS_OPTIONS_DEVICE_GROUP_KEYBOARD_SETTINGS_BUTTON_TITLE},
+ {"manageAccountsButtonTitle", IDS_OPTIONS_ACCOUNTS_BUTTON_TITLE},
+ {"mouseSpeed", IDS_OPTIONS_SETTINGS_MOUSE_SPEED_DESCRIPTION},
+ {"noPointingDevices", IDS_OPTIONS_NO_POINTING_DEVICES},
+ {"confirm", IDS_CONFIRM},
+ {"configureFingerprintTitle", IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_TITLE},
+ {"configureFingerprintInstructionLocateScannerStep",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_LOCATE_SCANNER},
+ {"configureFingerprintInstructionMoveFingerStep",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_MOVE_FINGER},
+ {"configureFingerprintInstructionReadyStep",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_READY},
+ {"configureFingerprintLiftFinger",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_LIFT_FINGER},
+ {"configureFingerprintPartialData",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_PARTIAL_DATA},
+ {"configureFingerprintInsufficientData",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSUFFICIENT_DATA},
+ {"configureFingerprintSensorDirty",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_SENSOR_DIRTY},
+ {"configureFingerprintTooSlow",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_FINGER_TOO_SLOW},
+ {"configureFingerprintTooFast",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_FINGER_TOO_FAST},
+ {"configureFingerprintImmobile",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_FINGER_IMMOBILE},
+ {"configureFingerprintCancelButton",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_CANCEL_BUTTON},
+ {"configureFingerprintDoneButton",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_DONE_BUTTON},
+ {"configureFingerprintAddAnotherButton",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_ADD_ANOTHER_BUTTON},
+ {"configurePinChoosePinTitle",
+ IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_CHOOSE_PIN_TITLE},
+ {"configurePinConfirmPinTitle",
+ IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_CONFIRM_PIN_TITLE},
+ {"configurePinContinueButton",
+ IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_CONTINUE_BUTTON},
+ {"configurePinMismatched", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_MISMATCHED},
+ {"configurePinTooShort", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_TOO_SHORT},
+ {"configurePinTooLong", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_TOO_LONG},
+ {"configurePinWeakPin", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_WEAK_PIN},
+ {"lockScreenAddFingerprint",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_ADD_FINGERPRINT_BUTTON},
+ {"lockScreenChangePinButton",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_CHANGE_PIN_BUTTON},
+ {"lockScreenEditFingerprints",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_EDIT_FINGERPRINTS},
+ {"lockScreenEditFingerprintsDescription",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_EDIT_FINGERPRINTS_DESCRIPTION},
+ {"lockScreenSetupFingerprintButton",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_FINGERPRINT_SETUP_BUTTON},
+ {"lockScreenNumberFingerprints",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_NUM_FINGERPRINTS},
+ {"lockScreenFingerprintEnable",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_ENABLE_FINGERPRINT_CHECKBOX_LABEL},
+ {"lockScreenFingerprintNewName",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_NEW_FINGERPRINT_DEFAULT_NAME},
+ {"lockScreenFingerprintTitle",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_FINGERPRINT_SUBPAGE_TITLE},
+ {"lockScreenFingerprintWarning",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_FINGERPRINT_LESS_SECURE},
+ {"lockScreenNone", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_NONE},
+ {"lockScreenOptions", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_OPTIONS},
+ {"lockScreenPasswordOnly", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PASSWORD_ONLY},
+ {"lockScreenPinOrPassword",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PIN_OR_PASSWORD},
+ {"lockScreenRegisteredFingerprints",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_REGISTERED_FINGERPRINTS_LABEL},
+ {"lockScreenSetupPinButton",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_SETUP_PIN_BUTTON},
+ {"lockScreenTitle", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_TITLE},
+ {"passwordPromptEnterPassword",
+ IDS_SETTINGS_PEOPLE_PASSWORD_PROMPT_ENTER_PASSWORD},
+ {"passwordPromptInvalidPassword",
+ IDS_SETTINGS_PEOPLE_PASSWORD_PROMPT_INVALID_PASSWORD},
+ {"passwordPromptPasswordLabel",
+ IDS_SETTINGS_PEOPLE_PASSWORD_PROMPT_PASSWORD_LABEL},
+ {"passwordPromptTitle", IDS_SETTINGS_PEOPLE_PASSWORD_PROMPT_TITLE},
+ {"pinKeyboardPlaceholderPin", IDS_PIN_KEYBOARD_HINT_TEXT_PIN},
+ {"pinKeyboardPlaceholderPinPassword",
+ IDS_PIN_KEYBOARD_HINT_TEXT_PIN_PASSWORD},
+ {"pinKeyboardDeleteAccessibleName",
+ IDS_LOGIN_POD_PASSWORD_FIELD_ACCESSIBLE_NAME},
+ {"powerSettingsButton", IDS_OPTIONS_DEVICE_GROUP_POWER_SETTINGS_BUTTON},
+ {"resolveTimezoneByGeoLocation",
+ IDS_OPTIONS_RESOLVE_TIMEZONE_BY_GEOLOCATION_DESCRIPTION},
+ {"sectionTitleDevice", IDS_OPTIONS_DEVICE_GROUP_NAME},
+ {"sectionTitleInternet", IDS_OPTIONS_INTERNET_OPTIONS_GROUP_LABEL},
+ {"storageManagerButtonTitle",
+ IDS_OPTIONS_DEVICE_GROUP_STORAGE_MANAGER_BUTTON_TITLE},
+ {"syncButtonTextStart", IDS_SYNC_SETUP_BUTTON_LABEL},
+ {"thirdPartyImeConfirmDisable", IDS_CANCEL},
+ {"thirdPartyImeConfirmEnable", IDS_OK},
+ {"thirdPartyImeConfirmMessage",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_THIRD_PARTY_WARNING_MESSAGE},
+ {"timezone", IDS_OPTIONS_SETTINGS_TIMEZONE_DESCRIPTION},
+ {"touchpadSpeed", IDS_OPTIONS_SETTINGS_TOUCHPAD_SPEED_DESCRIPTION},
+ {"use24HourClock", IDS_OPTIONS_SETTINGS_USE_24HOUR_CLOCK_DESCRIPTION},
+ {"wakeOnWifiLabel", IDS_OPTIONS_SETTINGS_WAKE_ON_WIFI_DESCRIPTION},
+#else
+ {"gpuModeCheckbox", IDS_OPTIONS_SYSTEM_ENABLE_HARDWARE_ACCELERATION_MODE},
+ {"gpuModeResetRestart",
+ IDS_OPTIONS_SYSTEM_ENABLE_HARDWARE_ACCELERATION_MODE_RESTART},
+ {"proxiesConfigureButton", IDS_OPTIONS_PROXIES_CONFIGURE_BUTTON},
+ {"syncButtonTextStart", IDS_SYNC_SETUP_BUTTON_LABEL},
+#endif // defined(OS_CHROMEOS)
+
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+ {"showWindowDecorations", IDS_SHOW_WINDOW_DECORATIONS},
+ {"themesNativeButton", IDS_THEMES_GTK_BUTTON},
+ {"themesSetClassic", IDS_THEMES_SET_CLASSIC},
+#else
+ {"themes", IDS_THEMES_GROUP_NAME},
+#endif // defined(OS_LINUX) && !defined(OS_CHROMEOS)
+
+#if defined(OS_CHROMEOS)
+ {"setWallpaper", IDS_SET_WALLPAPER_BUTTON},
+#endif // defined(OS_CHROMEOS)
+
+#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
+ {"backgroundModeCheckbox", IDS_OPTIONS_SYSTEM_ENABLE_BACKGROUND_MODE},
+#endif // defined(OS_MACOSX) && !defined(OS_CHROMEOS)
+
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+ {"cloudPrintDevicesPageButton", IDS_LOCAL_DISCOVERY_DEVICES_PAGE_BUTTON},
+ {"cloudPrintEnableNotificationsLabel",
+ IDS_LOCAL_DISCOVERY_NOTIFICATIONS_ENABLE_CHECKBOX_LABEL},
+#endif
+ };
+
+ RegisterStrings(values, resources, arraysize(resources));
+ RegisterTitle(values, "doNotTrackConfirmOverlay",
+ IDS_OPTIONS_ENABLE_DO_NOT_TRACK_BUBBLE_TITLE);
+ RegisterTitle(values, "spellingConfirmOverlay",
+ IDS_CONTENT_CONTEXT_SPELLING_ASK_GOOGLE);
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
+ RegisterCloudPrintValues(values);
+#endif
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ values->SetString(
+ "safeBrowsingEnableExtendedReporting",
+ l10n_util::GetStringUTF16(safe_browsing::ChooseOptInTextResource(
+ *profile->GetPrefs(),
+ IDS_OPTIONS_SAFEBROWSING_ENABLE_EXTENDED_REPORTING,
+ IDS_OPTIONS_SAFEBROWSING_ENABLE_SCOUT_REPORTING)));
+ values->SetString("syncLearnMoreURL", chrome::kSyncLearnMoreURL);
+ base::string16 omnibox_url = base::ASCIIToUTF16(chrome::kOmniboxLearnMoreURL);
+ values->SetString(
+ "defaultSearchGroupLabel",
+ l10n_util::GetStringFUTF16(IDS_SEARCH_PREF_EXPLANATION, omnibox_url));
+ values->SetString("hotwordLearnMoreURL", chrome::kHotwordLearnMoreURL);
+ RegisterTitle(values, "hotwordConfirmOverlay",
+ IDS_HOTWORD_CONFIRM_BUBBLE_TITLE);
+ values->SetString("hotwordManageAudioHistoryURL",
+ chrome::kManageAudioHistoryURL);
+ base::string16 supervised_user_dashboard =
+ base::ASCIIToUTF16(chrome::kLegacySupervisedUserManagementURL);
+ values->SetString("profilesSupervisedDashboardTip",
+ l10n_util::GetStringFUTF16(
+ IDS_PROFILES_LEGACY_SUPERVISED_USER_DASHBOARD_TIP,
+ supervised_user_dashboard));
+
+#if defined(OS_CHROMEOS)
+ std::string username = profile->GetProfileUserName();
+ if (username.empty()) {
+ const user_manager::User* user =
+ chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
+ if (user && (user->GetType() != user_manager::USER_TYPE_GUEST))
+ username = user->GetAccountId().GetUserEmail();
+ }
+ if (!username.empty())
+ username = gaia::SanitizeEmail(gaia::CanonicalizeEmail(username));
+
+ values->SetString("username", username);
+#endif
+ // Pass along sync status early so it will be available during page init.
+ values->Set("syncData", GetSyncStateDictionary());
+
+ values->SetString("privacyLearnMoreURL", chrome::kPrivacyLearnMoreURL);
+
+ values->SetString("doNotTrackLearnMoreURL", chrome::kDoNotTrackLearnMoreURL);
+
+ values->SetBoolean(
+ "metricsReportingEnabledAtStart",
+ ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled());
+
+#if defined(OS_CHROMEOS)
+ // TODO(pastarmovj): replace this with a call to the CrosSettings list
+ // handling functionality to come.
+ values->Set("timezoneList", chromeos::system::GetTimezoneList());
+
+ values->SetString("accessibilityLearnMoreURL",
+ chrome::kChromeAccessibilityHelpURL);
+
+ std::string settings_url = std::string("chrome-extension://") +
+ extension_misc::kChromeVoxExtensionId +
+ chrome::kChromeAccessibilitySettingsURL;
+
+ values->SetString("accessibilitySettingsURL",
+ settings_url);
+
+ values->SetString("contentProtectionAttestationLearnMoreURL",
+ chrome::kAttestationForContentProtectionLearnMoreURL);
+
+ // Creates magnifierList.
+ std::unique_ptr<base::ListValue> magnifier_list(new base::ListValue);
+
+ std::unique_ptr<base::ListValue> option_full(new base::ListValue);
+ option_full->AppendInteger(ash::MAGNIFIER_FULL);
+ option_full->AppendString(l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SCREEN_MAGNIFIER_FULL));
+ magnifier_list->Append(std::move(option_full));
+
+ std::unique_ptr<base::ListValue> option_partial(new base::ListValue);
+ option_partial->AppendInteger(ash::MAGNIFIER_PARTIAL);
+ option_partial->AppendString(l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SCREEN_MAGNIFIER_PARTIAL));
+ magnifier_list->Append(std::move(option_partial));
+
+ values->Set("magnifierList", std::move(magnifier_list));
+ values->SetBoolean("enablePolymerPreload", g_enable_polymer_preload);
+#endif // defined(OS_CHROMEOS)
+
+#if defined(OS_MACOSX)
+ values->SetString("macPasswordsWarning",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_PASSWORDS_MAC_WARNING));
+ values->SetBoolean("multiple_profiles",
+ g_browser_process->profile_manager()->GetNumberOfProfiles() > 1);
+#endif
+
+ if (ShouldShowMultiProfilesUserList())
+ values->Set("profilesInfo", GetProfilesInfoList());
+
+ // Profile deletion is not allowed for any users in ChromeOS.
+ bool allow_deletion = true;
+#if defined(OS_CHROMEOS)
+ allow_deletion = allow_deletion && !ash::Shell::HasInstance();
+#endif
+ values->SetBoolean("allowProfileDeletion", allow_deletion);
+ values->SetBoolean("profileIsGuest", profile->IsOffTheRecord());
+ values->SetBoolean("profileIsSupervised", profile->IsSupervised());
+
+#if !defined(OS_CHROMEOS)
+ values->SetBoolean(
+ "gpuEnabledAtStart",
+ g_browser_process->gpu_mode_manager()->initial_gpu_mode_pref());
+#endif
+
+#if defined(OS_CHROMEOS)
+ values->SetBoolean("cupsPrintDisabled",
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ ::switches::kDisableNativeCups));
+ values->SetString("cupsPrintLearnMoreURL", chrome::kCrosPrintingLearnMoreURL);
+#endif // defined(OS_CHROMEOS)
+
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+ values->SetBoolean("cloudPrintHideNotificationsCheckbox",
+ !cloud_print::PrivetNotificationService::IsEnabled());
+#endif
+
+ values->SetBoolean("cloudPrintShowMDnsOptions",
+ cloud_print_mdns_ui_enabled_);
+
+ values->SetString("cloudPrintLearnMoreURL", chrome::kCloudPrintLearnMoreURL);
+
+ values->SetString("languagesLearnMoreURL",
+ chrome::kLanguageSettingsLearnMoreUrl);
+
+ values->SetBoolean(
+ "easyUnlockAllowed", EasyUnlockService::Get(profile)->IsAllowed());
+ values->SetString("easyUnlockLearnMoreURL", chrome::kEasyUnlockLearnMoreUrl);
+ values->SetBoolean("easyUnlockProximityDetectionAllowed",
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ proximity_auth::switches::kEnableProximityDetection));
+
+#if defined(OS_CHROMEOS)
+ RegisterTitle(values, "thirdPartyImeConfirmOverlay",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_THIRD_PARTY_WARNING_TITLE);
+ values->SetBoolean("usingNewProfilesUI", false);
+#else
+ values->SetBoolean("usingNewProfilesUI", true);
+#endif
+
+ values->SetBoolean("showSetDefault", ShouldShowSetDefaultBrowser());
+
+ values->SetBoolean("allowAdvancedSettings", ShouldAllowAdvancedSettings());
+
+#if defined(OS_CHROMEOS)
+ values->SetBoolean(
+ "showWakeOnWifi",
+ chromeos::WakeOnWifiManager::Get()->WakeOnWifiSupported() &&
+ chromeos::switches::WakeOnWifiEnabled());
+ values->SetBoolean("enableTimeZoneTrackingOption",
+ !chromeos::system::HasSystemTimezonePolicy());
+ values->SetBoolean("resolveTimezoneByGeolocationInitialValue",
+ profile->GetPrefs()->GetBoolean(prefs::kResolveTimezoneByGeolocation));
+ values->SetBoolean("enableLanguageOptionsImeMenu",
+ base::FeatureList::IsEnabled(features::kOptInImeMenu));
+ values->SetBoolean(
+ "enableExperimentalAccessibilityFeatures",
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kEnableExperimentalAccessibilityFeatures));
+
+ chromeos::CrosSettings* cros_settings = chromeos::CrosSettings::Get();
+ bool allow_bluetooth = true;
+ cros_settings->GetBoolean(chromeos::kAllowBluetooth, &allow_bluetooth);
+ values->SetBoolean("allowBluetooth", allow_bluetooth);
+
+ values->SetBoolean("showQuickUnlockSettings",
+ chromeos::quick_unlock::IsPinEnabled(profile->GetPrefs()));
+ values->SetBoolean("fingerprintUnlockEnabled",
+ chromeos::quick_unlock::IsFingerprintEnabled());
+ values->SetBoolean("quickUnlockEnabled",
+ chromeos::quick_unlock::IsPinEnabled(profile->GetPrefs()));
+ if (chromeos::quick_unlock::IsPinEnabled(profile->GetPrefs())) {
+ values->SetString(
+ "enableScreenlock",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_ENABLE_SCREENLOCKER_CHECKBOX_WITH_QUICK_UNLOCK));
+ } else {
+ values->SetString(
+ "enableScreenlock",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_ENABLE_SCREENLOCKER_CHECKBOX));
+ }
+ // Format numbers to be used on the pin keyboard.
+ for (int j = 0; j <= 9; ++j) {
+ values->SetString("pinKeyboard" + base::IntToString(j),
+ base::FormatNumber(int64_t{j}));
+ }
+#endif
+}
+
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
+void BrowserOptionsHandler::RegisterCloudPrintValues(
+ base::DictionaryValue* values) {
+ values->SetString("cloudPrintOptionLabel",
+ l10n_util::GetStringFUTF16(
+ IDS_CLOUD_PRINT_CHROMEOS_OPTION_LABEL,
+ l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT)));
+}
+#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
+
+void BrowserOptionsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "setDefaultSearchEngine",
+ base::Bind(&BrowserOptionsHandler::SetDefaultSearchEngine,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "deleteProfile",
+ base::Bind(&BrowserOptionsHandler::DeleteProfile,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "themesReset",
+ base::Bind(&BrowserOptionsHandler::ThemesReset,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "requestProfilesInfo",
+ base::Bind(&BrowserOptionsHandler::HandleRequestProfilesInfo,
+ base::Unretained(this)));
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+ web_ui()->RegisterMessageCallback(
+ "themesSetNative",
+ base::Bind(&BrowserOptionsHandler::ThemesSetNative,
+ base::Unretained(this)));
+#endif
+ web_ui()->RegisterMessageCallback(
+ "selectDownloadLocation",
+ base::Bind(&BrowserOptionsHandler::HandleSelectDownloadLocation,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "autoOpenFileTypesAction",
+ base::Bind(&BrowserOptionsHandler::HandleAutoOpenButton,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "defaultFontSizeAction",
+ base::Bind(&BrowserOptionsHandler::HandleDefaultFontSize,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "defaultZoomFactorAction",
+ base::Bind(&BrowserOptionsHandler::HandleDefaultZoomFactor,
+ base::Unretained(this)));
+#if defined(OS_WIN) || defined(OS_MACOSX)
+ web_ui()->RegisterMessageCallback(
+ "showManageSSLCertificates",
+ base::Bind(&BrowserOptionsHandler::ShowManageSSLCertificates,
+ base::Unretained(this)));
+#endif
+#if defined(OS_CHROMEOS)
+ web_ui()->RegisterMessageCallback(
+ "openWallpaperManager",
+ base::Bind(&BrowserOptionsHandler::HandleOpenWallpaperManager,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "virtualKeyboardChange",
+ base::Bind(&BrowserOptionsHandler::VirtualKeyboardChangeCallback,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "onPowerwashDialogShow",
+ base::Bind(&BrowserOptionsHandler::OnPowerwashDialogShow,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "performFactoryResetRestart",
+ base::Bind(&BrowserOptionsHandler::PerformFactoryResetRestart,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "showAndroidAppsSettings",
+ base::Bind(&BrowserOptionsHandler::ShowAndroidAppsSettings,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "showPlayStoreApps",
+ base::Bind(&BrowserOptionsHandler::ShowPlayStoreApps,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "showAccessibilityTalkBackSettings",
+ base::Bind(&BrowserOptionsHandler::ShowAccessibilityTalkBackSettings,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "showCupsPrintDevicesPage",
+ base::Bind(&BrowserOptionsHandler::ShowCupsPrintDevicesPage,
+ base::Unretained(this)));
+#else
+ web_ui()->RegisterMessageCallback(
+ "becomeDefaultBrowser",
+ base::Bind(&BrowserOptionsHandler::BecomeDefaultBrowser,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "restartBrowser",
+ base::Bind(&BrowserOptionsHandler::HandleRestartBrowser,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "showNetworkProxySettings",
+ base::Bind(&BrowserOptionsHandler::ShowNetworkProxySettings,
+ base::Unretained(this)));
+#endif // defined(OS_CHROMEOS)
+
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+ if (cloud_print_mdns_ui_enabled_) {
+ web_ui()->RegisterMessageCallback(
+ "showCloudPrintDevicesPage",
+ base::Bind(&BrowserOptionsHandler::ShowCloudPrintDevicesPage,
+ base::Unretained(this)));
+ }
+#endif
+ web_ui()->RegisterMessageCallback(
+ "requestGoogleNowAvailable",
+ base::Bind(&BrowserOptionsHandler::HandleRequestGoogleNowAvailable,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "requestHotwordAvailable",
+ base::Bind(&BrowserOptionsHandler::HandleRequestHotwordAvailable,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "launchHotwordAudioVerificationApp",
+ base::Bind(
+ &BrowserOptionsHandler::HandleLaunchHotwordAudioVerificationApp,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "launchEasyUnlockSetup",
+ base::Bind(&BrowserOptionsHandler::HandleLaunchEasyUnlockSetup,
+ base::Unretained(this)));
+#if defined(OS_WIN)
+ web_ui()->RegisterMessageCallback(
+ "refreshExtensionControlIndicators",
+ base::Bind(
+ &BrowserOptionsHandler::HandleRefreshExtensionControlIndicators,
+ base::Unretained(this)));
+#endif // defined(OS_WIN)
+ web_ui()->RegisterMessageCallback("metricsReportingCheckboxChanged",
+ base::Bind(&BrowserOptionsHandler::HandleMetricsReportingChange,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "safeBrowsingExtendedReportingAction",
+ base::Bind(&BrowserOptionsHandler::HandleSafeBrowsingExtendedReporting,
+ base::Unretained(this)));
+}
+
+void BrowserOptionsHandler::Uninitialize() {
+ registrar_.RemoveAll();
+ g_browser_process->profile_manager()->
+ GetProfileAttributesStorage().RemoveObserver(this);
+#if defined(OS_WIN)
+ ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))->RemoveObserver(this);
+#endif
+#if defined(OS_CHROMEOS)
+ ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(
+ Profile::FromWebUI(web_ui()));
+ if (arc_prefs)
+ arc_prefs->RemoveObserver(this);
+ user_manager::UserManager::Get()->RemoveObserver(this);
+#endif
+}
+
+void BrowserOptionsHandler::OnStateChanged(syncer::SyncService* sync) {
+ UpdateSyncState();
+}
+
+void BrowserOptionsHandler::GoogleSigninSucceeded(const std::string& account_id,
+ const std::string& username,
+ const std::string& password) {
+ UpdateSyncState();
+}
+
+void BrowserOptionsHandler::GoogleSignedOut(const std::string& account_id,
+ const std::string& username) {
+ UpdateSyncState();
+}
+
+void BrowserOptionsHandler::PageLoadStarted() {
+ page_initialized_ = false;
+}
+
+void BrowserOptionsHandler::InitializeHandler() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ PrefService* prefs = profile->GetPrefs();
+ ChromeZoomLevelPrefs* zoom_level_prefs = profile->GetZoomLevelPrefs();
+ // Only regular profiles are able to edit default zoom level, or delete per-
+ // host zoom levels, via the settings menu. We only require a zoom_level_prefs
+ // if the profile is able to change these preference types.
+ DCHECK(zoom_level_prefs ||
+ profile->GetProfileType() != Profile::REGULAR_PROFILE);
+ if (zoom_level_prefs) {
+ default_zoom_level_subscription_ =
+ zoom_level_prefs->RegisterDefaultZoomLevelCallback(
+ base::Bind(&BrowserOptionsHandler::SetupPageZoomSelector,
+ base::Unretained(this)));
+ }
+
+ g_browser_process->policy_service()->AddObserver(
+ policy::POLICY_DOMAIN_CHROME, this);
+
+ g_browser_process->profile_manager()->
+ GetProfileAttributesStorage().AddObserver(this);
+
+ browser_sync::ProfileSyncService* sync_service(
+ ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile));
+ // TODO(blundell): Use a ScopedObserver to observe the PSS so that cleanup on
+ // destruction is automatic.
+ if (sync_service)
+ sync_service->AddObserver(this);
+
+ SigninManagerBase* signin_manager(
+ SigninManagerFactory::GetInstance()->GetForProfile(profile));
+ if (signin_manager)
+ signin_observer_.Add(signin_manager);
+
+ // Create our favicon data source.
+ content::URLDataSource::Add(profile, new FaviconSource(profile));
+
+#if !defined(OS_CHROMEOS)
+ default_browser_policy_.Init(
+ prefs::kDefaultBrowserSettingEnabled,
+ g_browser_process->local_state(),
+ base::Bind(&BrowserOptionsHandler::UpdateDefaultBrowserState,
+ base::Unretained(this)));
+#endif
+
+#if defined(OS_CHROMEOS)
+ user_manager::UserManager::Get()->AddObserver(this);
+#endif
+ registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
+ content::Source<ThemeService>(
+ ThemeServiceFactory::GetForProfile(profile)));
+ registrar_.Add(this, chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED,
+ content::Source<Profile>(profile));
+ AddTemplateUrlServiceObserver();
+
+#if defined(OS_WIN)
+ ExtensionRegistry::Get(profile)->AddObserver(this);
+#endif
+
+ // No preferences below this point may be modified by guest profiles.
+ if (profile->IsGuestSession())
+ return;
+
+ auto_open_files_.Init(
+ prefs::kDownloadExtensionsToOpen, prefs,
+ base::Bind(&BrowserOptionsHandler::SetupAutoOpenFileTypes,
+ base::Unretained(this)));
+ profile_pref_registrar_.Init(prefs);
+ profile_pref_registrar_.Add(
+ prefs::kNetworkPredictionOptions,
+ base::Bind(&BrowserOptionsHandler::SetupNetworkPredictionControl,
+ base::Unretained(this)));
+ profile_pref_registrar_.Add(
+ prefs::kWebKitDefaultFontSize,
+ base::Bind(&BrowserOptionsHandler::SetupFontSizeSelector,
+ base::Unretained(this)));
+ profile_pref_registrar_.Add(
+ prefs::kWebKitDefaultFixedFontSize,
+ base::Bind(&BrowserOptionsHandler::SetupFontSizeSelector,
+ base::Unretained(this)));
+ profile_pref_registrar_.Add(
+ prefs::kSupervisedUsers,
+ base::Bind(&BrowserOptionsHandler::SetupManagingSupervisedUsers,
+ base::Unretained(this)));
+ profile_pref_registrar_.Add(
+ prefs::kSigninAllowed,
+ base::Bind(&BrowserOptionsHandler::OnSigninAllowedPrefChange,
+ base::Unretained(this)));
+ profile_pref_registrar_.Add(
+ prefs::kEasyUnlockPairing,
+ base::Bind(&BrowserOptionsHandler::SetupEasyUnlock,
+ base::Unretained(this)));
+
+#if defined(OS_WIN)
+ profile_pref_registrar_.Add(
+ prefs::kURLsToRestoreOnStartup,
+ base::Bind(&BrowserOptionsHandler::SetupExtensionControlledIndicators,
+ base::Unretained(this)));
+ profile_pref_registrar_.Add(
+ prefs::kHomePage,
+ base::Bind(&BrowserOptionsHandler::SetupExtensionControlledIndicators,
+ base::Unretained(this)));
+#endif // defined(OS_WIN)
+
+#if defined(OS_CHROMEOS)
+ if (!policy_registrar_) {
+ policy_registrar_.reset(new policy::PolicyChangeRegistrar(
+ policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile)
+ ->policy_service(),
+ policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string())));
+ policy_registrar_->Observe(
+ policy::key::kUserAvatarImage,
+ base::Bind(&BrowserOptionsHandler::OnUserImagePolicyChanged,
+ base::Unretained(this)));
+ policy_registrar_->Observe(
+ policy::key::kWallpaperImage,
+ base::Bind(&BrowserOptionsHandler::OnWallpaperPolicyChanged,
+ base::Unretained(this)));
+ }
+ system_timezone_policy_observer_ =
+ chromeos::CrosSettings::Get()->AddSettingsObserver(
+ chromeos::kSystemTimezonePolicy,
+ base::Bind(&BrowserOptionsHandler::OnSystemTimezonePolicyChanged,
+ weak_ptr_factory_.GetWeakPtr()));
+ local_state_pref_change_registrar_.Init(g_browser_process->local_state());
+ local_state_pref_change_registrar_.Add(
+ prefs::kSystemTimezoneAutomaticDetectionPolicy,
+ base::Bind(&BrowserOptionsHandler::
+ OnSystemTimezoneAutomaticDetectionPolicyChanged,
+ base::Unretained(this)));
+ ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(profile);
+ if (arc_prefs)
+ arc_prefs->AddObserver(this);
+#else // !defined(OS_CHROMEOS)
+ profile_pref_registrar_.Add(
+ proxy_config::prefs::kProxy,
+ base::Bind(&BrowserOptionsHandler::SetupProxySettingsSection,
+ base::Unretained(this)));
+#endif // !defined(OS_CHROMEOS)
+
+ profile_pref_registrar_.Add(
+ prefs::kSafeBrowsingExtendedReportingEnabled,
+ base::Bind(&BrowserOptionsHandler::SetupSafeBrowsingExtendedReporting,
+ base::Unretained(this)));
+ profile_pref_registrar_.Add(
+ prefs::kSafeBrowsingScoutReportingEnabled,
+ base::Bind(&BrowserOptionsHandler::SetupSafeBrowsingExtendedReporting,
+ base::Unretained(this)));
+}
+
+void BrowserOptionsHandler::InitializePage() {
+ page_initialized_ = true;
+
+ OnTemplateURLServiceChanged();
+
+ ObserveThemeChanged();
+ UpdateSyncState();
+#if !defined(OS_CHROMEOS)
+ UpdateDefaultBrowserState();
+#endif
+
+ SetupMetricsReportingSettingVisibility();
+ SetupMetricsReportingCheckbox();
+ SetupNetworkPredictionControl();
+ SetupFontSizeSelector();
+ SetupPageZoomSelector();
+ SetupAutoOpenFileTypes();
+ SetupProxySettingsSection();
+ SetupManagingSupervisedUsers();
+ SetupEasyUnlock();
+ SetupExtensionControlledIndicators();
+ SetupSafeBrowsingExtendedReporting();
+
+#if defined(OS_CHROMEOS)
+ SetupAccessibilityFeatures();
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ enable_factory_reset_ = !connector->IsEnterpriseManaged() &&
+ !user_manager::UserManager::Get()->IsLoggedInAsGuest() &&
+ !user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser();
+ if (enable_factory_reset_) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.enableFactoryResetSection");
+ }
+
+ Profile* const profile = Profile::FromWebUI(web_ui());
+ user_manager::User const* const user =
+ chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
+
+ OnAccountPictureManagedChanged(
+ policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile)
+ ->policy_service()
+ ->GetPolicies(policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME,
+ std::string()))
+ .Get(policy::key::kUserAvatarImage));
+
+ OnWallpaperManagedChanged(
+ chromeos::WallpaperManager::Get()->IsPolicyControlled(
+ user->GetAccountId()));
+
+ if (arc::IsArcAllowedForProfile(profile) &&
+ !arc::IsArcOptInVerificationDisabled()) {
+ base::Value is_play_store_enabled(
+ arc::IsArcPlayStoreEnabledForProfile(profile));
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.showAndroidAppsSection", is_play_store_enabled);
+ // Get the initial state of Android Settings app readiness.
+ std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
+ ArcAppListPrefs::Get(profile)->GetApp(arc::kSettingsAppId);
+ if (app_info && app_info->ready)
+ UpdateAndroidSettingsAppState(app_info->ready);
+ }
+
+ OnSystemTimezoneAutomaticDetectionPolicyChanged();
+#endif
+}
+
+bool BrowserOptionsHandler::ShouldShowSetDefaultBrowser() {
+#if defined(OS_CHROMEOS)
+ // We're always the default browser on ChromeOS.
+ return false;
+#else
+ return !Profile::FromWebUI(web_ui())->IsGuestSession();
+#endif
+}
+
+bool BrowserOptionsHandler::ShouldShowMultiProfilesUserList() {
+#if defined(OS_CHROMEOS)
+ // On Chrome OS we use different UI for multi-profiles.
+ return false;
+#else
+ if (Profile::FromWebUI(web_ui())->IsGuestSession())
+ return false;
+ return profiles::IsMultipleProfilesEnabled();
+#endif
+}
+
+bool BrowserOptionsHandler::ShouldAllowAdvancedSettings() {
+#if defined(OS_CHROMEOS)
+ // ChromeOS handles guest-mode restrictions in a different manner.
+ return true;
+#else
+ return !Profile::FromWebUI(web_ui())->IsGuestSession();
+#endif
+}
+
+#if !defined(OS_CHROMEOS)
+
+void BrowserOptionsHandler::UpdateDefaultBrowserState() {
+ default_browser_worker_->StartCheckIsDefault();
+}
+
+void BrowserOptionsHandler::BecomeDefaultBrowser(const base::ListValue* args) {
+ // If the default browser setting is managed then we should not be able to
+ // call this function.
+ if (IsDisabledByPolicy(default_browser_policy_))
+ return;
+
+ base::RecordAction(UserMetricsAction("Options_SetAsDefaultBrowser"));
+ UMA_HISTOGRAM_COUNTS("Settings.StartSetAsDefault", true);
+
+ // Callback takes care of updating UI.
+ default_browser_worker_->StartSetAsDefault();
+
+ // If the user attempted to make Chrome the default browser, notify
+ // them when this changes.
+ chrome::ResetDefaultBrowserPrompt(Profile::FromWebUI(web_ui()));
+}
+
+void BrowserOptionsHandler::OnDefaultBrowserWorkerFinished(
+ shell_integration::DefaultWebClientState state) {
+ int status_string_id;
+
+ if (state == shell_integration::IS_DEFAULT) {
+ status_string_id = IDS_OPTIONS_DEFAULTBROWSER_DEFAULT;
+ // Notify the user in the future if Chrome ceases to be the user's chosen
+ // default browser.
+ chrome::ResetDefaultBrowserPrompt(Profile::FromWebUI(web_ui()));
+ } else if (state == shell_integration::NOT_DEFAULT) {
+ if (shell_integration::CanSetAsDefaultBrowser()) {
+ status_string_id = IDS_OPTIONS_DEFAULTBROWSER_NOTDEFAULT;
+ } else {
+ status_string_id = IDS_OPTIONS_DEFAULTBROWSER_SXS;
+ }
+ } else if (state == shell_integration::UNKNOWN_DEFAULT) {
+ status_string_id = IDS_OPTIONS_DEFAULTBROWSER_UNKNOWN;
+ } else {
+ NOTREACHED();
+ return;
+ }
+
+ SetDefaultBrowserUIString(status_string_id);
+}
+
+void BrowserOptionsHandler::SetDefaultBrowserUIString(int status_string_id) {
+ base::Value status_string(l10n_util::GetStringFUTF16(
+ status_string_id, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
+
+ base::Value is_default(status_string_id ==
+ IDS_OPTIONS_DEFAULTBROWSER_DEFAULT);
+
+ base::Value can_be_default(
+ !IsDisabledByPolicy(default_browser_policy_) &&
+ (status_string_id == IDS_OPTIONS_DEFAULTBROWSER_DEFAULT ||
+ status_string_id == IDS_OPTIONS_DEFAULTBROWSER_NOTDEFAULT));
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.updateDefaultBrowserState", status_string, is_default,
+ can_be_default);
+}
+#endif // !defined(OS_CHROMEOS)
+
+void BrowserOptionsHandler::OnTemplateURLServiceChanged() {
+ if (!template_url_service_ || !template_url_service_->loaded())
+ return;
+
+ const TemplateURL* default_url =
+ template_url_service_->GetDefaultSearchProvider();
+
+ int default_index = -1;
+ base::ListValue search_engines;
+ TemplateURLService::TemplateURLVector model_urls(
+ template_url_service_->GetTemplateURLs());
+ for (size_t i = 0; i < model_urls.size(); ++i) {
+ TemplateURL* t_url = model_urls[i];
+ if (!template_url_service_->ShowInDefaultList(t_url))
+ continue;
+
+ std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
+ entry->SetString("name", t_url->short_name());
+ entry->SetInteger("index", i);
+ search_engines.Append(std::move(entry));
+ if (t_url == default_url)
+ default_index = i;
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.updateSearchEngines", search_engines,
+ base::Value(default_index),
+ base::Value(template_url_service_->is_default_search_managed() ||
+ template_url_service_->IsExtensionControlledDefaultSearch()));
+
+ SetupExtensionControlledIndicators();
+
+ HandleRequestHotwordAvailable(nullptr);
+ HandleRequestGoogleNowAvailable(nullptr);
+}
+
+void BrowserOptionsHandler::SetDefaultSearchEngine(
+ const base::ListValue* args) {
+ int selected_index = -1;
+ if (!ExtractIntegerValue(args, &selected_index)) {
+ NOTREACHED();
+ return;
+ }
+
+ TemplateURLService::TemplateURLVector model_urls(
+ template_url_service_->GetTemplateURLs());
+ if (selected_index >= 0 &&
+ selected_index < static_cast<int>(model_urls.size()))
+ template_url_service_->SetUserSelectedDefaultSearchProvider(
+ model_urls[selected_index]);
+
+ base::RecordAction(UserMetricsAction("Options_SearchEngineChanged"));
+}
+
+void BrowserOptionsHandler::AddTemplateUrlServiceObserver() {
+ template_url_service_ =
+ TemplateURLServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()));
+ if (template_url_service_) {
+ template_url_service_->Load();
+ template_url_service_->AddObserver(this);
+ }
+}
+
+void BrowserOptionsHandler::OnExtensionLoaded(
+ content::BrowserContext* browser_context,
+ const Extension* extension) {
+ SetupExtensionControlledIndicators();
+}
+
+void BrowserOptionsHandler::OnExtensionUnloaded(
+ content::BrowserContext* browser_context,
+ const Extension* extension,
+ extensions::UnloadedExtensionReason reason) {
+ SetupExtensionControlledIndicators();
+}
+
+void BrowserOptionsHandler::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ // Notifications are used to update the UI dynamically when settings change in
+ // the background. If the UI is currently being loaded, no dynamic updates are
+ // possible (as the DOM and JS are not fully loaded) or necessary (as
+ // InitializePage() will update the UI at the end of the load).
+ if (!page_initialized_)
+ return;
+
+ switch (type) {
+ case chrome::NOTIFICATION_BROWSER_THEME_CHANGED:
+ ObserveThemeChanged();
+ break;
+ case chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED:
+ // Update our sync/signin status display.
+ UpdateSyncState();
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
+void BrowserOptionsHandler::OnProfileAdded(const base::FilePath& profile_path) {
+ SendProfilesInfo();
+}
+
+void BrowserOptionsHandler::OnProfileWasRemoved(
+ const base::FilePath& profile_path,
+ const base::string16& profile_name) {
+ SendProfilesInfo();
+}
+
+void BrowserOptionsHandler::OnProfileNameChanged(
+ const base::FilePath& profile_path,
+ const base::string16& old_profile_name) {
+ SendProfilesInfo();
+}
+
+void BrowserOptionsHandler::OnProfileAvatarChanged(
+ const base::FilePath& profile_path) {
+ SendProfilesInfo();
+}
+
+std::unique_ptr<base::ListValue> BrowserOptionsHandler::GetProfilesInfoList() {
+ std::vector<ProfileAttributesEntry*> entries =
+ g_browser_process->profile_manager()->
+ GetProfileAttributesStorage().GetAllProfilesAttributesSortedByName();
+ std::unique_ptr<base::ListValue> profile_info_list(new base::ListValue);
+ base::FilePath current_profile_path =
+ web_ui()->GetWebContents()->GetBrowserContext()->GetPath();
+
+ for (const ProfileAttributesEntry* entry : entries) {
+ // The items in |profile_value| are also described in
+ // chrome/browser/resources/options/browser_options.js in a @typedef for
+ // Profile. Please update it whenever you add or remove any keys here.
+ std::unique_ptr<base::DictionaryValue> profile_value(
+ new base::DictionaryValue());
+ profile_value->SetString("name", entry->GetName());
+ base::FilePath profile_path = entry->GetPath();
+ profile_value->Set("filePath", base::CreateFilePathValue(profile_path));
+ profile_value->SetBoolean("isCurrentProfile",
+ profile_path == current_profile_path);
+ profile_value->SetBoolean("isSupervised", entry->IsSupervised());
+ profile_value->SetBoolean("isChild", entry->IsChild());
+
+ if (entry->IsUsingGAIAPicture() && entry->GetGAIAPicture()) {
+ gfx::Image icon = profiles::GetAvatarIconForWebUI(entry->GetAvatarIcon(),
+ true);
+ profile_value->SetString("iconURL",
+ webui::GetBitmapDataUrl(icon.AsBitmap()));
+ } else {
+ size_t icon_index = entry->GetAvatarIconIndex();
+ profile_value->SetString("iconURL",
+ profiles::GetDefaultAvatarIconUrl(icon_index));
+ }
+
+ profile_info_list->Append(std::move(profile_value));
+ }
+
+ return profile_info_list;
+}
+
+void BrowserOptionsHandler::SendProfilesInfo() {
+ if (!ShouldShowMultiProfilesUserList())
+ return;
+ web_ui()->CallJavascriptFunctionUnsafe("BrowserOptions.setProfilesInfo",
+ *GetProfilesInfoList());
+}
+
+void BrowserOptionsHandler::DeleteProfile(const base::ListValue* args) {
+ DCHECK(args);
+ const base::Value* file_path_value;
+ if (!args->Get(0, &file_path_value)) {
+ NOTREACHED();
+ return;
+ }
+
+ base::FilePath file_path;
+ if (!base::GetValueAsFilePath(*file_path_value, &file_path)) {
+ NOTREACHED();
+ return;
+ }
+
+ webui::DeleteProfileAtPath(file_path,
+ web_ui(),
+ ProfileMetrics::DELETE_PROFILE_SETTINGS);
+}
+
+void BrowserOptionsHandler::ObserveThemeChanged() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ ThemeService* theme_service = ThemeServiceFactory::GetForProfile(profile);
+ bool is_system_theme = false;
+
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+ bool profile_is_supervised = profile->IsSupervised();
+ is_system_theme = theme_service->UsingSystemTheme();
+ base::Value native_theme_enabled(!is_system_theme && !profile_is_supervised);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setNativeThemeButtonEnabled", native_theme_enabled);
+#endif
+
+ bool is_classic_theme = !is_system_theme &&
+ theme_service->UsingDefaultTheme();
+ base::Value enabled(!is_classic_theme);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setThemesResetButtonEnabled", enabled);
+}
+
+void BrowserOptionsHandler::ThemesReset(const base::ListValue* args) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ base::RecordAction(UserMetricsAction("Options_ThemesReset"));
+ ThemeServiceFactory::GetForProfile(profile)->UseDefaultTheme();
+}
+
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+void BrowserOptionsHandler::ThemesSetNative(const base::ListValue* args) {
+ base::RecordAction(UserMetricsAction("Options_GtkThemeSet"));
+ Profile* profile = Profile::FromWebUI(web_ui());
+ ThemeServiceFactory::GetForProfile(profile)->UseSystemTheme();
+}
+#endif
+
+#if defined(OS_CHROMEOS)
+void BrowserOptionsHandler::UpdateAccountPicture() {
+ std::string email = user_manager::UserManager::Get()
+ ->GetActiveUser()
+ ->GetAccountId()
+ .GetUserEmail();
+ if (!email.empty()) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.updateAccountPicture");
+ base::Value email_value(email);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.updateAccountPicture", email_value);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "AccountsOptions.getInstance().updateAccountPicture", email_value);
+ }
+}
+
+void BrowserOptionsHandler::OnAccountPictureManagedChanged(bool managed) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setAccountPictureManaged", base::Value(managed));
+}
+
+void BrowserOptionsHandler::OnWallpaperManagedChanged(bool managed) {
+ web_ui()->CallJavascriptFunctionUnsafe("BrowserOptions.setWallpaperManaged",
+ base::Value(managed));
+}
+
+void BrowserOptionsHandler::OnSystemTimezonePolicyChanged() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setSystemTimezoneManaged",
+ base::Value(chromeos::system::HasSystemTimezonePolicy()));
+}
+
+void BrowserOptionsHandler::OnSystemTimezoneAutomaticDetectionPolicyChanged() {
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kDisableSystemTimezoneAutomaticDetectionPolicy)) {
+ return;
+ }
+
+ PrefService* prefs = g_browser_process->local_state();
+ const bool is_managed = prefs->IsManagedPreference(
+ prefs::kSystemTimezoneAutomaticDetectionPolicy);
+ const int value =
+ prefs->GetInteger(prefs::kSystemTimezoneAutomaticDetectionPolicy);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setSystemTimezoneAutomaticDetectionManaged",
+ base::Value(is_managed), base::Value(value));
+}
+#endif
+
+std::unique_ptr<base::DictionaryValue>
+BrowserOptionsHandler::GetSyncStateDictionary() {
+ // The items which are to be written into |sync_status| are also described in
+ // chrome/browser/resources/options/browser_options.js in @typedef
+ // for SyncStatus. Please update it whenever you add or remove any keys here.
+ std::unique_ptr<base::DictionaryValue> sync_status(new base::DictionaryValue);
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (profile->IsGuestSession()) {
+ // Cannot display signin status when running in guest mode on chromeos
+ // because there is no SigninManager.
+ sync_status->SetBoolean("signinAllowed", false);
+ return sync_status;
+ }
+
+ sync_status->SetBoolean("supervisedUser", profile->IsSupervised());
+ sync_status->SetBoolean("childUser", profile->IsChild());
+
+ bool signout_prohibited = false;
+#if !defined(OS_CHROMEOS)
+ // Signout is not allowed if the user has policy (crbug.com/172204).
+ signout_prohibited =
+ SigninManagerFactory::GetForProfile(profile)->IsSignoutProhibited();
+#endif
+
+ browser_sync::ProfileSyncService* service =
+ ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
+ SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile);
+ DCHECK(signin);
+ sync_status->SetBoolean("signoutAllowed", !signout_prohibited);
+ sync_status->SetBoolean("signinAllowed", signin->IsSigninAllowed());
+ sync_status->SetBoolean("syncSystemEnabled", (service != NULL));
+ sync_status->SetBoolean("setupCompleted",
+ service && service->IsFirstSetupComplete());
+ sync_status->SetBoolean("setupInProgress",
+ service && !service->IsManaged() && service->IsFirstSetupInProgress());
+
+ base::string16 status_label;
+ base::string16 link_label;
+ sync_ui_util::ActionType action_type = sync_ui_util::NO_ACTION;
+ bool status_has_error =
+ sync_ui_util::GetStatusLabels(profile, service, *signin,
+ sync_ui_util::WITH_HTML, &status_label,
+ &link_label, &action_type) ==
+ sync_ui_util::SYNC_ERROR;
+ sync_status->SetString("statusText", status_label);
+ sync_status->SetString("actionLinkText", link_label);
+ sync_status->SetBoolean("hasError", status_has_error);
+ sync_status->SetString("statusAction", GetSyncErrorAction(action_type));
+ sync_status->SetBoolean("managed", service && service->IsManaged());
+ sync_status->SetBoolean("signedIn", signin->IsAuthenticated());
+ sync_status->SetString("accountInfo",
+ l10n_util::GetStringFUTF16(
+ IDS_SYNC_ACCOUNT_INFO,
+ signin_ui_util::GetAuthenticatedUsername(signin)));
+ sync_status->SetBoolean("hasUnrecoverableError",
+ service && service->HasUnrecoverableError());
+
+ return sync_status;
+}
+
+void BrowserOptionsHandler::HandleSelectDownloadLocation(
+ const base::ListValue* args) {
+ PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
+ select_folder_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
+ ui::SelectFileDialog::FileTypeInfo info;
+ info.allowed_paths = ui::SelectFileDialog::FileTypeInfo::NATIVE_OR_DRIVE_PATH;
+ select_folder_dialog_->SelectFile(
+ ui::SelectFileDialog::SELECT_FOLDER,
+ l10n_util::GetStringUTF16(IDS_OPTIONS_DOWNLOADLOCATION_BROWSE_TITLE),
+ pref_service->GetFilePath(prefs::kDownloadDefaultDirectory),
+ &info,
+ 0,
+ base::FilePath::StringType(),
+ web_ui()->GetWebContents()->GetTopLevelNativeWindow(),
+ NULL);
+}
+
+void BrowserOptionsHandler::FileSelected(const base::FilePath& path, int index,
+ void* params) {
+ base::RecordAction(UserMetricsAction("Options_SetDownloadDirectory"));
+ PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
+ pref_service->SetFilePath(prefs::kDownloadDefaultDirectory, path);
+ pref_service->SetFilePath(prefs::kSaveFileDefaultDirectory, path);
+}
+
+#if defined(OS_CHROMEOS)
+void BrowserOptionsHandler::TouchpadExists(bool exists) {
+ base::Value val(exists);
+ web_ui()->CallJavascriptFunctionUnsafe("BrowserOptions.showTouchpadControls",
+ val);
+}
+
+void BrowserOptionsHandler::MouseExists(bool exists) {
+ base::Value val(exists);
+ web_ui()->CallJavascriptFunctionUnsafe("BrowserOptions.showMouseControls",
+ val);
+}
+
+void BrowserOptionsHandler::OnUserImagePolicyChanged(
+ const base::Value* previous_policy,
+ const base::Value* current_policy) {
+ const bool had_policy = previous_policy;
+ const bool has_policy = current_policy;
+ if (had_policy != has_policy)
+ OnAccountPictureManagedChanged(has_policy);
+}
+
+void BrowserOptionsHandler::OnWallpaperPolicyChanged(
+ const base::Value* previous_policy,
+ const base::Value* current_policy) {
+ const bool had_policy = previous_policy;
+ const bool has_policy = current_policy;
+ if (had_policy != has_policy)
+ OnWallpaperManagedChanged(has_policy);
+}
+
+void BrowserOptionsHandler::OnPowerwashDialogShow(
+ const base::ListValue* args) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "Reset.ChromeOS.PowerwashDialogShown",
+ chromeos::reset::DIALOG_FROM_OPTIONS,
+ chromeos::reset::DIALOG_VIEW_TYPE_SIZE);
+}
+
+#endif // defined(OS_CHROMEOS)
+
+void BrowserOptionsHandler::UpdateSyncState() {
+ web_ui()->CallJavascriptFunctionUnsafe("BrowserOptions.updateSyncState",
+ *GetSyncStateDictionary());
+
+ // A change in sign-in state also affects how hotwording and audio history are
+ // displayed. Hide all hotwording and re-display properly.
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setAllHotwordSectionsVisible", base::Value(false));
+ HandleRequestHotwordAvailable(nullptr);
+}
+
+void BrowserOptionsHandler::OnSigninAllowedPrefChange() {
+ UpdateSyncState();
+}
+
+void BrowserOptionsHandler::HandleAutoOpenButton(const base::ListValue* args) {
+ base::RecordAction(UserMetricsAction("Options_ResetAutoOpenFiles"));
+ DownloadManager* manager = BrowserContext::GetDownloadManager(
+ web_ui()->GetWebContents()->GetBrowserContext());
+ DownloadPrefs::FromDownloadManager(manager)->ResetAutoOpen();
+}
+
+void BrowserOptionsHandler::HandleDefaultFontSize(const base::ListValue* args) {
+ int font_size;
+ if (ExtractIntegerValue(args, &font_size)) {
+ if (font_size > 0) {
+ PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
+ pref_service->SetInteger(prefs::kWebKitDefaultFontSize, font_size);
+ SetupFontSizeSelector();
+ }
+ }
+}
+
+void BrowserOptionsHandler::HandleDefaultZoomFactor(
+ const base::ListValue* args) {
+ double zoom_factor;
+ if (ExtractDoubleValue(args, &zoom_factor)) {
+ ChromeZoomLevelPrefs* zoom_level_prefs =
+ Profile::FromWebUI(web_ui())->GetZoomLevelPrefs();
+ DCHECK(zoom_level_prefs);
+ zoom_level_prefs->SetDefaultZoomLevelPref(
+ content::ZoomFactorToZoomLevel(zoom_factor));
+ }
+}
+
+void BrowserOptionsHandler::HandleRestartBrowser(const base::ListValue* args) {
+ chrome::AttemptRestart();
+}
+
+void BrowserOptionsHandler::HandleRequestProfilesInfo(
+ const base::ListValue* args) {
+ SendProfilesInfo();
+}
+
+#if !defined(OS_CHROMEOS)
+void BrowserOptionsHandler::ShowNetworkProxySettings(
+ const base::ListValue* args) {
+ base::RecordAction(UserMetricsAction("Options_ShowProxySettings"));
+ settings_utils::ShowNetworkProxySettings(web_ui()->GetWebContents());
+}
+#endif
+
+#if defined(OS_WIN) || defined(OS_MACOSX)
+void BrowserOptionsHandler::ShowManageSSLCertificates(
+ const base::ListValue* args) {
+ base::RecordAction(UserMetricsAction("Options_ManageSSLCertificates"));
+ settings_utils::ShowManageSSLCertificates(web_ui()->GetWebContents());
+}
+#endif
+
+#if defined(OS_CHROMEOS)
+void BrowserOptionsHandler::ShowCupsPrintDevicesPage(
+ const base::ListValue* args) {
+ // Navigate in current tab to CUPS printers management page.
+ OpenURLParams params(GURL(chrome::kChromeUIMdCupsSettingsURL), Referrer(),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui::PAGE_TRANSITION_LINK, false);
+ web_ui()->GetWebContents()->OpenURL(params);
+}
+#endif // defined(OS_CHROMEOS)
+
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+void BrowserOptionsHandler::ShowCloudPrintDevicesPage(
+ const base::ListValue* args) {
+ base::RecordAction(UserMetricsAction("Options_CloudPrintDevicesPage"));
+ // Navigate in current tab to devices page.
+ OpenURLParams params(GURL(chrome::kChromeUIDevicesURL), Referrer(),
+ WindowOpenDisposition::CURRENT_TAB,
+ ui::PAGE_TRANSITION_LINK, false);
+ web_ui()->GetWebContents()->OpenURL(params);
+}
+#endif
+
+void BrowserOptionsHandler::SetHotwordAudioHistorySectionVisible(
+ const base::string16& audio_history_state,
+ bool success, bool logging_enabled) {
+ bool visible = logging_enabled && success;
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setAudioHistorySectionVisible", base::Value(visible),
+ base::Value(audio_history_state));
+}
+
+void BrowserOptionsHandler::HandleRequestGoogleNowAvailable(
+ const base::ListValue* args) {
+ bool is_search_provider_google = false;
+ if (template_url_service_ && template_url_service_->loaded()) {
+ const TemplateURL* default_url =
+ template_url_service_->GetDefaultSearchProvider();
+ if (default_url && default_url->HasGoogleBaseURLs(
+ template_url_service_->search_terms_data())) {
+ is_search_provider_google = true;
+ }
+ }
+
+ std::string group = base::FieldTrialList::FindFullName("GoogleNowExtension");
+ bool has_field_trial = !group.empty() && group != "Disabled";
+
+ bool should_show = is_search_provider_google && has_field_trial;
+ web_ui()->CallJavascriptFunctionUnsafe("BrowserOptions.setNowSectionVisible",
+ base::Value(should_show));
+}
+
+void BrowserOptionsHandler::HandleRequestHotwordAvailable(
+ const base::ListValue* args) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+ bool is_search_provider_google = false;
+ // The check for default search provider is only valid if the
+ // |template_url_service_| has loaded already.
+ if (template_url_service_ && template_url_service_->loaded()) {
+ const TemplateURL* default_url =
+ template_url_service_->GetDefaultSearchProvider();
+ if (default_url && default_url->HasGoogleBaseURLs(
+ template_url_service_->search_terms_data())) {
+ is_search_provider_google = true;
+ } else {
+ // If the user has chosen a default search provide other than Google, turn
+ // off hotwording since other providers don't provide that functionality.
+ HotwordService* hotword_service =
+ HotwordServiceFactory::GetForProfile(profile);
+ if (hotword_service)
+ hotword_service->DisableHotwordPreferences();
+ }
+ }
+
+ // |is_search_provider_google| may be false because |template_url_service_|
+ // does not exist yet or because the user selected a different search
+ // provider. In either case it does not make sense to show the hotwording
+ // options.
+ if (!is_search_provider_google) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setAllHotwordSectionsVisible", base::Value(false));
+ return;
+ }
+
+ // Don't need to check the field trial here since |IsHotwordAllowed| also
+ // checks it.
+ if (HotwordServiceFactory::IsHotwordAllowed(profile)) {
+ // Update the current error value.
+ HotwordServiceFactory::IsServiceAvailable(profile);
+ int error = HotwordServiceFactory::GetCurrentError(profile);
+
+ std::string function_name;
+ bool always_on = false;
+ SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile);
+ bool authenticated = signin && signin->IsAuthenticated();
+ if (HotwordServiceFactory::IsAlwaysOnAvailable() && authenticated) {
+ function_name = "BrowserOptions.showHotwordAlwaysOnSection";
+ always_on = true;
+ // Show the retrain link if always-on is enabled.
+ if (profile->GetPrefs()->GetBoolean(
+ prefs::kHotwordAlwaysOnSearchEnabled)) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setHotwordRetrainLinkVisible", base::Value(true));
+ }
+ } else {
+ function_name = "BrowserOptions.showHotwordNoDspSection";
+ }
+
+ // Audio history should be displayed if it's enabled regardless of the
+ // hotword error state if the user is signed in. If the user is not signed
+ // in, audio history is meaningless. This is only displayed if always-on
+ // hotwording is available.
+ if (authenticated && always_on) {
+ std::string user_display_name =
+ signin->GetAuthenticatedAccountInfo().email;
+ DCHECK(!user_display_name.empty());
+ base::string16 audio_history_state =
+ l10n_util::GetStringFUTF16(IDS_HOTWORD_AUDIO_HISTORY_ENABLED,
+ base::ASCIIToUTF16(user_display_name));
+ HotwordService* hotword_service =
+ HotwordServiceFactory::GetForProfile(profile);
+ if (hotword_service) {
+ hotword_service->GetAudioHistoryHandler()->GetAudioHistoryEnabled(
+ base::Bind(
+ &BrowserOptionsHandler::SetHotwordAudioHistorySectionVisible,
+ weak_ptr_factory_.GetWeakPtr(),
+ audio_history_state));
+ }
+ }
+
+ if (!error) {
+ web_ui()->CallJavascriptFunctionUnsafe(function_name);
+ } else {
+ base::string16 hotword_help_url =
+ base::ASCIIToUTF16(chrome::kHotwordLearnMoreURL);
+ base::Value error_message(l10n_util::GetStringUTF16(error));
+ if (error == IDS_HOTWORD_GENERIC_ERROR_MESSAGE) {
+ error_message =
+ base::Value(l10n_util::GetStringFUTF16(error, hotword_help_url));
+ }
+ web_ui()->CallJavascriptFunctionUnsafe(function_name, error_message);
+ }
+ }
+}
+
+void BrowserOptionsHandler::HandleLaunchHotwordAudioVerificationApp(
+ const base::ListValue* args) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+ bool retrain = false;
+ bool success = args->GetBoolean(0, &retrain);
+ DCHECK(success);
+ HotwordService::LaunchMode launch_mode =
+ HotwordService::HOTWORD_AND_AUDIO_HISTORY;
+
+ if (retrain) {
+ DCHECK(profile->GetPrefs()->GetBoolean(
+ prefs::kHotwordAlwaysOnSearchEnabled));
+ DCHECK(profile->GetPrefs()->GetBoolean(
+ prefs::kHotwordAudioLoggingEnabled));
+
+ launch_mode = HotwordService::RETRAIN;
+ } else if (profile->GetPrefs()->GetBoolean(
+ prefs::kHotwordAudioLoggingEnabled)) {
+ DCHECK(!profile->GetPrefs()->GetBoolean(
+ prefs::kHotwordAlwaysOnSearchEnabled));
+ launch_mode = HotwordService::HOTWORD_ONLY;
+ } else {
+ DCHECK(!profile->GetPrefs()->GetBoolean(
+ prefs::kHotwordAlwaysOnSearchEnabled));
+ }
+
+ HotwordService* hotword_service =
+ HotwordServiceFactory::GetForProfile(profile);
+ if (!hotword_service)
+ return;
+
+ hotword_service->OptIntoHotwording(launch_mode);
+}
+
+void BrowserOptionsHandler::HandleLaunchEasyUnlockSetup(
+ const base::ListValue* args) {
+ EasyUnlockService::Get(Profile::FromWebUI(web_ui()))->LaunchSetup();
+}
+
+void BrowserOptionsHandler::HandleRefreshExtensionControlIndicators(
+ const base::ListValue* args) {
+ SetupExtensionControlledIndicators();
+}
+
+#if defined(OS_CHROMEOS)
+void BrowserOptionsHandler::HandleOpenWallpaperManager(
+ const base::ListValue* args) {
+ chromeos::WallpaperManager::Get()->Open();
+}
+
+void BrowserOptionsHandler::VirtualKeyboardChangeCallback(
+ const base::ListValue* args) {
+ bool enabled = false;
+ args->GetBoolean(0, &enabled);
+
+ chromeos::accessibility::EnableVirtualKeyboard(enabled);
+}
+
+void BrowserOptionsHandler::PerformFactoryResetRestart(
+ const base::ListValue* args) {
+ if (!enable_factory_reset_)
+ return;
+
+ PrefService* prefs = g_browser_process->local_state();
+ prefs->SetBoolean(prefs::kFactoryResetRequested, true);
+ prefs->CommitPendingWrite();
+
+ // Perform sign out. Current chrome process will then terminate, new one will
+ // be launched (as if it was a restart).
+ chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
+}
+
+void BrowserOptionsHandler::OnAppRegistered(
+ const std::string& app_id,
+ const ArcAppListPrefs::AppInfo& app_info) {
+ OnAppReadyChanged(app_id, app_info.ready);
+}
+
+void BrowserOptionsHandler::OnAppRemoved(const std::string& app_id) {
+ OnAppReadyChanged(app_id, false);
+}
+
+void BrowserOptionsHandler::OnAppReadyChanged(
+ const std::string& app_id,
+ bool ready) {
+ if (app_id == arc::kSettingsAppId) {
+ UpdateAndroidSettingsAppState(ready);
+ }
+}
+
+void BrowserOptionsHandler::OnUserImageChanged(const user_manager::User& user) {
+ UpdateAccountPicture();
+}
+
+void BrowserOptionsHandler::UpdateAndroidSettingsAppState(bool visible) {
+ base::Value is_visible(visible);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setAndroidAppsSettingsVisibility", is_visible);
+}
+
+void BrowserOptionsHandler::ShowAndroidAppsSettings(
+ const base::ListValue* args) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ // Settings in secondary profile cannot access ARC.
+ if (!arc::IsArcAllowedForProfile(profile)) {
+ LOG(ERROR) << "Settings can't be invoked for non-primary profile";
+ return;
+ }
+
+ // We only care whether the event came from a keyboard or non-keyboard
+ // (mouse/touch). Set the default flags in such a way that it would appear
+ // that it came from a mouse by default.
+ bool activated_from_keyboard = false;
+ args->GetBoolean(0, &activated_from_keyboard);
+ int flags = activated_from_keyboard ? ui::EF_NONE : ui::EF_LEFT_MOUSE_BUTTON;
+
+ arc::LaunchAndroidSettingsApp(profile, flags);
+}
+
+void BrowserOptionsHandler::ShowPlayStoreApps(const base::ListValue* args) {
+ std::string apps_url;
+ args->GetString(0, &apps_url);
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (!arc::IsArcAllowedForProfile(profile)) {
+ VLOG(1) << "ARC is not enabled for this profile";
+ return;
+ }
+
+ arc::LaunchPlayStoreWithUrl(apps_url);
+}
+
+void BrowserOptionsHandler::ShowAccessibilityTalkBackSettings(
+ const base::ListValue *args) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ // Settings in secondary profile cannot access ARC.
+ if (!arc::IsArcAllowedForProfile(profile)) {
+ LOG(WARNING) << "Settings can't be invoked for non-primary profile";
+ return;
+ }
+
+ arc::ShowTalkBackSettings();
+}
+
+void BrowserOptionsHandler::SetupAccessibilityFeatures() {
+ PrefService* pref_service = g_browser_process->local_state();
+ base::Value virtual_keyboard_enabled(
+ pref_service->GetBoolean(prefs::kAccessibilityVirtualKeyboardEnabled));
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setVirtualKeyboardCheckboxState",
+ virtual_keyboard_enabled);
+}
+#endif
+
+void BrowserOptionsHandler::SetupMetricsReportingSettingVisibility() {
+#if defined(GOOGLE_CHROME_BUILD)
+ // Don't show the reporting setting if we are in the guest mode.
+ if (Profile::FromWebUI(web_ui())->IsGuestSession()) {
+ base::Value visible(false);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setMetricsReportingSettingVisibility", visible);
+ }
+#endif
+}
+
+void BrowserOptionsHandler::SetupNetworkPredictionControl() {
+ PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
+
+ base::DictionaryValue dict;
+ dict.SetInteger("value",
+ pref_service->GetInteger(prefs::kNetworkPredictionOptions));
+ dict.SetBoolean("disabled",
+ !pref_service->IsUserModifiablePreference(
+ prefs::kNetworkPredictionOptions));
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setNetworkPredictionValue", dict);
+}
+
+void BrowserOptionsHandler::SetupFontSizeSelector() {
+ PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
+ const PrefService::Preference* default_font_size =
+ pref_service->FindPreference(prefs::kWebKitDefaultFontSize);
+ const PrefService::Preference* default_fixed_font_size =
+ pref_service->FindPreference(prefs::kWebKitDefaultFixedFontSize);
+
+ base::DictionaryValue dict;
+ dict.SetInteger("value",
+ pref_service->GetInteger(prefs::kWebKitDefaultFontSize));
+
+ // The font size control displays the value of the default font size, but
+ // setting it alters both the default font size and the default fixed font
+ // size. So it must be disabled when either of those prefs is not user
+ // modifiable.
+ dict.SetBoolean("disabled",
+ !default_font_size->IsUserModifiable() ||
+ !default_fixed_font_size->IsUserModifiable());
+
+ // This is a simplified version of CoreOptionsHandler::CreateValueForPref,
+ // adapted to consider two prefs. It may be better to refactor
+ // CreateValueForPref so it can be called from here.
+ if (default_font_size->IsManaged() || default_fixed_font_size->IsManaged()) {
+ dict.SetString("controlledBy", "policy");
+ } else if (default_font_size->IsExtensionControlled() ||
+ default_fixed_font_size->IsExtensionControlled()) {
+ dict.SetString("controlledBy", "extension");
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("BrowserOptions.setFontSize", dict);
+}
+
+void BrowserOptionsHandler::SetupPageZoomSelector() {
+ double default_zoom_level =
+ content::HostZoomMap::GetDefaultForBrowserContext(
+ Profile::FromWebUI(web_ui()))->GetDefaultZoomLevel();
+ double default_zoom_factor =
+ content::ZoomLevelToZoomFactor(default_zoom_level);
+
+ // Generate a vector of zoom factors from an array of known presets along with
+ // the default factor added if necessary.
+ std::vector<double> zoom_factors =
+ zoom::PageZoom::PresetZoomFactors(default_zoom_factor);
+
+ // Iterate through the zoom factors and and build the contents of the
+ // selector that will be sent to the javascript handler.
+ // Each item in the list has the following parameters:
+ // 1. Title (string).
+ // 2. Value (double).
+ // 3. Is selected? (bool).
+ base::ListValue zoom_factors_value;
+ for (std::vector<double>::const_iterator i = zoom_factors.begin();
+ i != zoom_factors.end(); ++i) {
+ std::unique_ptr<base::ListValue> option(new base::ListValue());
+ double factor = *i;
+ int percent = static_cast<int>(factor * 100 + 0.5);
+ option->AppendString(base::FormatPercent(percent));
+ option->AppendDouble(factor);
+ bool selected = content::ZoomValuesEqual(factor, default_zoom_factor);
+ option->AppendBoolean(selected);
+ zoom_factors_value.Append(std::move(option));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("BrowserOptions.setupPageZoomSelector",
+ zoom_factors_value);
+}
+
+void BrowserOptionsHandler::SetupAutoOpenFileTypes() {
+ // Set the hidden state for the AutoOpenFileTypesResetToDefault button.
+ // We show the button if the user has any auto-open file types registered.
+ DownloadManager* manager = BrowserContext::GetDownloadManager(
+ web_ui()->GetWebContents()->GetBrowserContext());
+ bool display = DownloadPrefs::FromDownloadManager(manager)->IsAutoOpenUsed();
+ base::Value value(display);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setAutoOpenFileTypesDisplayed", value);
+}
+
+void BrowserOptionsHandler::SetupProxySettingsSection() {
+#if !defined(OS_CHROMEOS)
+ PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
+ const PrefService::Preference* proxy_config =
+ pref_service->FindPreference(proxy_config::prefs::kProxy);
+ bool is_extension_controlled = (proxy_config &&
+ proxy_config->IsExtensionControlled());
+
+ base::Value disabled(proxy_config && !proxy_config->IsUserModifiable());
+ base::Value extension_controlled(is_extension_controlled);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setupProxySettingsButton", disabled,
+ extension_controlled);
+
+#if defined(OS_WIN)
+ SetupExtensionControlledIndicators();
+#endif // defined(OS_WIN)
+
+#endif // !defined(OS_CHROMEOS)
+}
+
+void BrowserOptionsHandler::SetupManagingSupervisedUsers() {
+ bool has_users = !Profile::FromWebUI(web_ui())->
+ GetPrefs()->GetDictionary(prefs::kSupervisedUsers)->empty();
+ base::Value has_users_value(has_users);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.updateManagesSupervisedUsers", has_users_value);
+}
+
+void BrowserOptionsHandler::SetupEasyUnlock() {
+ base::Value is_enabled(
+ EasyUnlockService::Get(Profile::FromWebUI(web_ui()))->IsEnabled());
+ web_ui()->CallJavascriptFunctionUnsafe("BrowserOptions.updateEasyUnlock",
+ is_enabled);
+}
+
+void BrowserOptionsHandler::SetupExtensionControlledIndicators() {
+ base::DictionaryValue extension_controlled;
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+ // Check if an extension is overriding the Search Engine.
+ const extensions::Extension* extension =
+ extensions::GetExtensionOverridingSearchEngine(profile);
+ AppendExtensionData("searchEngine", extension, &extension_controlled);
+
+ // Check if an extension is overriding the Home page.
+ extension = extensions::GetExtensionOverridingHomepage(profile);
+ AppendExtensionData("homePage", extension, &extension_controlled);
+
+ // Check if an extension is overriding the Startup pages.
+ extension = extensions::GetExtensionOverridingStartupPages(profile);
+ AppendExtensionData("startUpPage", extension, &extension_controlled);
+
+ // Check if an extension is overriding the NTP page.
+ extension = extensions::GetExtensionOverridingNewTabPage(profile);
+ AppendExtensionData("newTabPage", extension, &extension_controlled);
+
+ // Check if an extension is overwriting the proxy setting.
+ extension = extensions::GetExtensionOverridingProxy(profile);
+ AppendExtensionData("proxy", extension, &extension_controlled);
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.toggleExtensionIndicators", extension_controlled);
+}
+
+void BrowserOptionsHandler::SetupMetricsReportingCheckbox() {
+// As the metrics and crash reporting checkbox only exists for official builds
+// it doesn't need to be set up for non-official builds.
+#if defined(GOOGLE_CHROME_BUILD)
+ bool checked =
+ ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled();
+ bool policy_managed = IsMetricsReportingPolicyManaged();
+ bool owner_managed = false;
+#if defined(OS_CHROMEOS)
+ owner_managed = !IsDeviceOwnerProfile();
+#endif // defined(OS_CHROMEOS)
+ SetMetricsReportingCheckbox(checked, policy_managed, owner_managed);
+#endif // defined(GOOGLE_CHROME_BUILD)
+}
+
+void BrowserOptionsHandler::HandleMetricsReportingChange(
+ const base::ListValue* args) {
+ bool enable;
+ if (!args->GetBoolean(0, &enable))
+ return;
+ // Decline the change if current user shouldn't be able to change metrics
+ // reporting.
+ if (!IsDeviceOwnerProfile() || IsMetricsReportingPolicyManaged()) {
+ NotifyUIOfMetricsReportingChange(
+ ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled());
+ return;
+ }
+
+// For Chrome OS updating device settings will notify an observer to update
+// metrics pref, however we still need to call
+// |ChangeMetricsReportingStateWithReply| with a proper callback so that UI gets
+// updated in case of failure to update the metrics pref.
+// TODO(gayane): Don't call |ChangeMetricsReportingStateWithReply| twice so that
+// metrics service pref changes only as a result of device settings change for
+// Chrome OS .crbug.com/552550.
+#if defined(OS_CHROMEOS)
+ chromeos::CrosSettings::Get()->SetBoolean(chromeos::kStatsReportingPref,
+ enable);
+#endif // defined(OS_CHROMEOS)
+ ChangeMetricsReportingStateWithReply(
+ enable,
+ base::Bind(&BrowserOptionsHandler::NotifyUIOfMetricsReportingChange,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void BrowserOptionsHandler::NotifyUIOfMetricsReportingChange(bool enabled) {
+ SetMetricsReportingCheckbox(enabled, IsMetricsReportingPolicyManaged(),
+ !IsDeviceOwnerProfile());
+}
+
+void BrowserOptionsHandler::SetMetricsReportingCheckbox(bool checked,
+ bool policy_managed,
+ bool owner_managed) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setMetricsReportingCheckboxState", base::Value(checked),
+ base::Value(policy_managed), base::Value(owner_managed));
+}
+
+void BrowserOptionsHandler::SetupSafeBrowsingExtendedReporting() {
+ base::Value is_enabled(safe_browsing::IsExtendedReportingEnabled(
+ *Profile::FromWebUI(web_ui())->GetPrefs()));
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setExtendedReportingEnabledCheckboxState", is_enabled);
+}
+
+void BrowserOptionsHandler::HandleSafeBrowsingExtendedReporting(
+ const base::ListValue* args) {
+ bool checked;
+ if (args->GetBoolean(0, &checked)) {
+ safe_browsing::SetExtendedReportingPrefAndMetric(
+ Profile::FromWebUI(web_ui())->GetPrefs(), checked,
+ safe_browsing::SBER_OPTIN_SITE_CHROME_SETTINGS);
+ }
+}
+
+void BrowserOptionsHandler::OnPolicyUpdated(const policy::PolicyNamespace& ns,
+ const policy::PolicyMap& previous,
+ const policy::PolicyMap& current) {
+ std::set<std::string> different_keys;
+ current.GetDifferingKeys(previous, &different_keys);
+ if (base::ContainsKey(different_keys, policy::key::kMetricsReportingEnabled))
+ SetupMetricsReportingCheckbox();
+}
+
+#if defined(OS_CHROMEOS)
+// static
+void BrowserOptionsHandler::DisablePolymerPreloadForTesting() {
+ g_enable_polymer_preload = false;
+}
+#endif // defined(OS_CHROMEOS)
+
+bool BrowserOptionsHandler::IsDeviceOwnerProfile() {
+#if defined(OS_CHROMEOS)
+ return chromeos::ProfileHelper::IsOwnerProfile(Profile::FromWebUI(web_ui()));
+#else
+ return true;
+#endif
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/browser_options_handler.h b/chromium/chrome/browser/ui/webui/options/browser_options_handler.h
new file mode 100644
index 00000000000..2d117952b04
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/browser_options_handler.h
@@ -0,0 +1,465 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_BROWSER_OPTIONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_BROWSER_OPTIONS_HANDLER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
+#include "build/build_config.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "chrome/browser/ui/zoom/chrome_zoom_level_prefs.h"
+#include "chrome/common/features.h"
+#include "components/policy/core/common/policy_service.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_member.h"
+#include "components/search_engines/template_url_service_observer.h"
+#include "components/signin/core/browser/signin_manager_base.h"
+#include "components/signin/core/common/signin_pref_names.h"
+#include "components/sync/driver/sync_service_observer.h"
+#include "content/public/browser/notification_observer.h"
+#include "extensions/browser/extension_registry_observer.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "printing/features/features.h"
+#include "ui/base/models/table_model_observer.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/chromeos/system/pointer_device_observer.h"
+#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
+#include "components/user_manager/user_manager.h"
+#else // defined(OS_CHROMEOS)
+#include "chrome/browser/shell_integration.h"
+#endif // !defined(OS_CHROMEOS)
+
+class TemplateURLService;
+
+namespace base {
+class Value;
+}
+
+namespace policy {
+class PolicyChangeRegistrar;
+}
+
+namespace options {
+
+// Chrome browser options page UI handler.
+class BrowserOptionsHandler
+ : public OptionsPageUIHandler,
+ public ProfileAttributesStorage::Observer,
+ public syncer::SyncServiceObserver,
+ public SigninManagerBase::Observer,
+ public ui::SelectFileDialog::Listener,
+#if defined(OS_CHROMEOS)
+ public chromeos::system::PointerDeviceObserver::Observer,
+ public ArcAppListPrefs::Observer,
+ public user_manager::UserManager::Observer,
+#endif
+ public TemplateURLServiceObserver,
+ public extensions::ExtensionRegistryObserver,
+ public content::NotificationObserver,
+ public policy::PolicyService::Observer {
+ public:
+ BrowserOptionsHandler();
+ ~BrowserOptionsHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* values) override;
+ void PageLoadStarted() override;
+ void InitializeHandler() override;
+ void InitializePage() override;
+ void RegisterMessages() override;
+ void Uninitialize() override;
+
+ // syncer::SyncServiceObserver implementation.
+ void OnStateChanged(syncer::SyncService* sync) override;
+
+ // SigninManagerBase::Observer implementation.
+ void GoogleSigninSucceeded(const std::string& account_id,
+ const std::string& username,
+ const std::string& password) override;
+ void GoogleSignedOut(const std::string& account_id,
+ const std::string& username) override;
+
+ // TemplateURLServiceObserver implementation.
+ void OnTemplateURLServiceChanged() override;
+
+ // extensions::ExtensionRegistryObserver:
+ void OnExtensionLoaded(content::BrowserContext* browser_context,
+ const extensions::Extension* extension) override;
+ void OnExtensionUnloaded(content::BrowserContext* browser_context,
+ const extensions::Extension* extension,
+ extensions::UnloadedExtensionReason reason) override;
+
+ // policy::PolicyService::Observer:
+ void OnPolicyUpdated(const policy::PolicyNamespace& ns,
+ const policy::PolicyMap& previous,
+ const policy::PolicyMap& current) override;
+#if defined(OS_CHROMEOS)
+ static void DisablePolymerPreloadForTesting();
+#endif // defined(OS_CHROMEOS)
+
+ private:
+ // content::NotificationObserver implementation.
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ // ProfileAttributesStorage::Observer implementation.
+ void OnProfileAdded(const base::FilePath& profile_path) override;
+ void OnProfileWasRemoved(const base::FilePath& profile_path,
+ const base::string16& profile_name) override;
+ void OnProfileNameChanged(const base::FilePath& profile_path,
+ const base::string16& old_profile_name) override;
+ void OnProfileAvatarChanged(const base::FilePath& profile_path) override;
+
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW) && !defined(OS_CHROMEOS)
+ void OnCloudPrintPrefsChanged();
+#endif
+
+ // SelectFileDialog::Listener implementation
+ void FileSelected(const base::FilePath& path,
+ int index,
+ void* params) override;
+
+#if defined(OS_CHROMEOS)
+ // PointerDeviceObserver::Observer implementation.
+ void TouchpadExists(bool exists) override;
+ void MouseExists(bool exists) override;
+
+ // Will be called when the policy::key::kUserAvatarImage policy changes.
+ void OnUserImagePolicyChanged(const base::Value* previous_policy,
+ const base::Value* current_policy);
+
+ // Will be called when the policy::key::kWallpaperImage policy changes.
+ void OnWallpaperPolicyChanged(const base::Value* previous_policy,
+ const base::Value* current_policy);
+
+ // Will be called when powerwash dialog is shown.
+ void OnPowerwashDialogShow(const base::ListValue* args);
+
+ // ArcAppListPrefs::Observer overrides.
+ void OnAppReadyChanged(const std::string& app_id, bool ready) override;
+ void OnAppRemoved(const std::string& app_id) override;
+ void OnAppRegistered(const std::string& app_id,
+ const ArcAppListPrefs::AppInfo& app_info) override;
+
+ // user_manager::UserManager::Observer overrides.
+ void OnUserImageChanged(const user_manager::User& user) override;
+#endif
+
+ void UpdateSyncState();
+
+ // Will be called when the kSigninAllowed pref has changed.
+ void OnSigninAllowedPrefChange();
+
+ // Sets the search engine at the given index to be default. Called from WebUI.
+ void SetDefaultSearchEngine(const base::ListValue* args);
+
+ // Returns if the "make Chrome default browser" button should be shown.
+ bool ShouldShowSetDefaultBrowser();
+
+ // Returns if profiles list should be shown on settings page.
+ bool ShouldShowMultiProfilesUserList();
+
+ // Returns if access to advanced settings should be allowed.
+ bool ShouldAllowAdvancedSettings();
+
+#if !defined(OS_CHROMEOS)
+ // Gets the current default browser state, and asynchronously reports it to
+ // the WebUI page.
+ void UpdateDefaultBrowserState();
+
+ // Makes this the default browser. Called from WebUI.
+ void BecomeDefaultBrowser(const base::ListValue* args);
+
+ // Receives the default browser state when the worker is done.
+ void OnDefaultBrowserWorkerFinished(
+ shell_integration::DefaultWebClientState state);
+
+ // Updates the UI with the given state for the default browser.
+ void SetDefaultBrowserUIString(int status_string_id);
+#endif // !defined(OS_CHROMEOS)
+
+ // Loads the possible default search engine list and reports it to the WebUI.
+ void AddTemplateUrlServiceObserver();
+
+ // Creates a list of dictionaries where each dictionary is of the form:
+ // profileInfo = {
+ // name: "Profile Name",
+ // iconURL: "chrome://path/to/icon/image",
+ // filePath: "/path/to/profile/data/on/disk",
+ // isCurrentProfile: false
+ // };
+ std::unique_ptr<base::ListValue> GetProfilesInfoList();
+
+ // Sends an array of Profile objects to javascript.
+ void SendProfilesInfo();
+
+ // Deletes the given profile. Expects one argument:
+ // 0: profile file path (string)
+ void DeleteProfile(const base::ListValue* args);
+
+ void ObserveThemeChanged();
+ void ThemesReset(const base::ListValue* args);
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+ void ThemesSetNative(const base::ListValue* args);
+#endif
+
+#if defined(OS_CHROMEOS)
+ void UpdateAccountPicture();
+
+ // Updates the UI, allowing the user to change the avatar image if |managed|
+ // is |false| and preventing the user from changing the avatar image if
+ // |managed| is |true|.
+ void OnAccountPictureManagedChanged(bool managed);
+
+ // Updates the UI, allowing the user to change the wallpaper if |managed| is
+ // |false| and preventing the user from changing the wallpaper if |managed| is
+ // |true|.
+ void OnWallpaperManagedChanged(bool managed);
+
+ // Updates the UI, allowing the user to change the system time zone if
+ // kSystemTimezonePolicy is set, and preventing the user from changing the
+ // system time zone if kSystemTimezonePolicy is not set.
+ void OnSystemTimezonePolicyChanged();
+
+ // Updates the UI, preventing the user from changing timezone or timezone
+ // detection settings if kSystemTimezoneAutomaticDetectionPolicy is set, and
+ // allowing the user to update these settings if
+ // kSystemTimezoneAutomaticDetectionPolicy is not set.
+ void OnSystemTimezoneAutomaticDetectionPolicyChanged();
+#endif
+
+ // Callback for the "selectDownloadLocation" message. This will prompt the
+ // user for a destination folder using platform-specific APIs.
+ void HandleSelectDownloadLocation(const base::ListValue* args);
+
+ // Callback for the "autoOpenFileTypesResetToDefault" message. This will
+ // remove all auto-open file-type settings.
+ void HandleAutoOpenButton(const base::ListValue* args);
+
+ // Callback for the "defaultFontSizeAction" message. This is called if the
+ // user changes the default font size. |args| is an array that contains
+ // one item, the font size as a numeric value.
+ void HandleDefaultFontSize(const base::ListValue* args);
+
+ // Callback for the "defaultZoomFactorAction" message. This is called if the
+ // user changes the default zoom factor. |args| is an array that contains
+ // one item, the zoom factor as a numeric value.
+ void HandleDefaultZoomFactor(const base::ListValue* args);
+
+ // Callback for the "Use SSL 3.0" checkbox. This is called if the user toggles
+ // the "Use SSL 3.0" checkbox.
+ void HandleUseSSL3Checkbox(const base::ListValue* args);
+
+ // Callback for the "Use TLS 1.0" checkbox. This is called if the user toggles
+ // the "Use TLS 1.0" checkbox.
+ void HandleUseTLS1Checkbox(const base::ListValue* args);
+
+ // Callback for the "restartBrowser" message. Restores all tabs on restart.
+ void HandleRestartBrowser(const base::ListValue* args);
+
+ // Callback for "requestProfilesInfo" message.
+ void HandleRequestProfilesInfo(const base::ListValue* args);
+
+#if !defined(OS_CHROMEOS)
+ // Callback for the "showNetworkProxySettings" message. This will invoke
+ // an appropriate dialog for configuring proxy settings.
+ void ShowNetworkProxySettings(const base::ListValue* args);
+#endif
+
+#if !defined(USE_NSS_CERTS)
+ // Callback for the "showManageSSLCertificates" message. This will invoke
+ // an appropriate certificate management action based on the platform.
+ void ShowManageSSLCertificates(const base::ListValue* args);
+#endif
+
+#if defined(OS_CHROMEOS)
+ void ShowCupsPrintDevicesPage(const base::ListValue* args);
+#endif // defined(OS_CHROMEOS)
+
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+ void ShowCloudPrintDevicesPage(const base::ListValue* args);
+#endif
+
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
+ // Register localized values used by Cloud Print
+ void RegisterCloudPrintValues(base::DictionaryValue* values);
+#endif
+
+ // Check if hotword is available. If it is, tell the javascript to show
+ // the hotword section of the settings page.
+ void SendHotwordAvailable();
+
+ // Callback that updates the visibility of the audio history upon completion
+ // of a call to the server to the get the current value.
+ void SetHotwordAudioHistorySectionVisible(
+ const base::string16& audio_history_state,
+ bool success,
+ bool logging_enabled);
+
+ // Callback for "requestHotwordAvailable" message.
+ void HandleRequestHotwordAvailable(const base::ListValue* args);
+
+ // Callback for "launchHotwordAudioVerificationApp" message.
+ void HandleLaunchHotwordAudioVerificationApp(const base::ListValue* args);
+
+ // Callback for "requestGoogleNowAvailable" message.
+ void HandleRequestGoogleNowAvailable(const base::ListValue* args);
+
+ // Callback for "launchEasyUnlockSetup" message.
+ void HandleLaunchEasyUnlockSetup(const base::ListValue* args);
+
+ // Callback for "refreshExtensionControlIndicators" message.
+ void HandleRefreshExtensionControlIndicators(const base::ListValue* args);
+
+#if defined(OS_CHROMEOS)
+ // Opens the wallpaper manager component extension.
+ void HandleOpenWallpaperManager(const base::ListValue* args);
+
+ // Called when the accessibility checkbox values are changed.
+ // |args| will contain the checkbox checked state as a string
+ // ("true" or "false").
+ void VirtualKeyboardChangeCallback(const base::ListValue* args);
+
+ // Called when the user confirmed factory reset. Chrome will
+ // initiate asynchronous file operation and then log out.
+ void PerformFactoryResetRestart(const base::ListValue* args);
+
+ // Update visibility of Android apps settings section.
+ void UpdateAndroidSettingsAppState(bool visible);
+
+ // Called to show Android apps settings.
+ void ShowAndroidAppsSettings(const base::ListValue* args);
+
+ // Called to show apps based on a url for the Play Store.
+ void ShowPlayStoreApps(const base::ListValue* args);
+
+ // Called to show TalkBack settings.
+ void ShowAccessibilityTalkBackSettings(const base::ListValue *args);
+#endif
+
+ // Setup the visibility for the metrics reporting setting.
+ void SetupMetricsReportingSettingVisibility();
+
+ // Update value of predictive network actions UI element.
+ void SetupNetworkPredictionControl();
+
+ // Setup the font size selector control.
+ void SetupFontSizeSelector();
+
+ // Setup the page zoom selector control.
+ void SetupPageZoomSelector();
+
+ // Setup the visibility of the reset button.
+ void SetupAutoOpenFileTypes();
+
+ // Setup the proxy settings section UI.
+ void SetupProxySettingsSection();
+
+ // Setup the UI specific to managing supervised users.
+ void SetupManagingSupervisedUsers();
+
+ // Setup the UI for Easy Unlock.
+ void SetupEasyUnlock();
+
+ // Setup the UI for showing which settings are extension controlled.
+ void SetupExtensionControlledIndicators();
+
+ // Setup the value and the disabled property for metrics reporting for (except
+ // Android).
+ void SetupMetricsReportingCheckbox();
+
+ // Called when the MetricsReportingEnabled checkbox values are changed.
+ // |args| will contain the checkbox checked state as a boolean.
+ void HandleMetricsReportingChange(const base::ListValue* args);
+
+ // Notifies the result of MetricsReportingEnabled change to Javascript layer.
+ void NotifyUIOfMetricsReportingChange(bool enabled);
+
+ // Calls a Javascript function to set the state of MetricsReporting checkbox.
+ void SetMetricsReportingCheckbox(bool checked,
+ bool policy_managed,
+ bool owner_managed);
+
+#if defined(OS_CHROMEOS)
+ // Setup the accessibility features for ChromeOS.
+ void SetupAccessibilityFeatures();
+#endif
+
+ // Update the Extended Reporting Enabled checkbox based on the current state
+ // of the opt-in.
+ void SetupSafeBrowsingExtendedReporting();
+
+ // Callback for "safeBrowsingExtentedReportingAction" message. This is called
+ // if the user checks or unchecks the Extended Reporting checkbox. |args| is
+ // an array that contains one item, the checked state of the checkbox.
+ void HandleSafeBrowsingExtendedReporting(const base::ListValue* args);
+
+ // Returns a newly created dictionary with a number of properties that
+ // correspond to the status of sync.
+ std::unique_ptr<base::DictionaryValue> GetSyncStateDictionary();
+
+ // Checks whether on Chrome OS the current user is the device owner. Returns
+ // true on other platforms.
+ bool IsDeviceOwnerProfile();
+
+#if !defined(OS_CHROMEOS)
+ scoped_refptr<shell_integration::DefaultBrowserWorker>
+ default_browser_worker_;
+ BooleanPrefMember default_browser_policy_;
+#endif
+
+ bool page_initialized_;
+
+ StringPrefMember homepage_;
+
+ TemplateURLService* template_url_service_; // Weak.
+
+ scoped_refptr<ui::SelectFileDialog> select_folder_dialog_;
+
+ bool cloud_print_mdns_ui_enabled_;
+
+ StringPrefMember auto_open_files_;
+
+ std::unique_ptr<ChromeZoomLevelPrefs::DefaultZoomLevelSubscription>
+ default_zoom_level_subscription_;
+
+ PrefChangeRegistrar profile_pref_registrar_;
+#if defined(OS_CHROMEOS)
+ std::unique_ptr<policy::PolicyChangeRegistrar> policy_registrar_;
+
+ // Whether factory reset can be performed.
+ bool enable_factory_reset_;
+
+ PrefChangeRegistrar local_state_pref_change_registrar_;
+
+ std::unique_ptr<chromeos::CrosSettings::ObserverSubscription>
+ system_timezone_policy_observer_;
+#endif
+
+ ScopedObserver<SigninManagerBase, SigninManagerBase::Observer>
+ signin_observer_;
+
+ // Used to get WeakPtr to self for use on the UI thread.
+ base::WeakPtrFactory<BrowserOptionsHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserOptionsHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_BROWSER_OPTIONS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/certificate_manager_browsertest.cc b/chromium/chrome/browser/ui/webui/options/certificate_manager_browsertest.cc
new file mode 100644
index 00000000000..ed0ae533f40
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/certificate_manager_browsertest.cc
@@ -0,0 +1,108 @@
+// 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 "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/options/options_ui_browsertest.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/common/external_data_fetcher.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/policy_constants.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
+#include "chromeos/network/onc/onc_test_utils.h"
+#endif
+
+using testing::Return;
+using testing::_;
+
+class CertificateManagerBrowserTest : public options::OptionsUIBrowserTest {
+ public:
+ CertificateManagerBrowserTest() {}
+ ~CertificateManagerBrowserTest() override {}
+
+ protected:
+ void SetUpInProcessBrowserTestFixture() override {
+ options::OptionsUIBrowserTest::SetUpInProcessBrowserTestFixture();
+#if defined(OS_CHROMEOS)
+ device_policy_test_helper_.MarkAsEnterpriseOwned();
+#endif
+ // Setup the policy provider for injecting certs through ONC policy.
+ EXPECT_CALL(provider_, IsInitializationComplete(_))
+ .WillRepeatedly(Return(true));
+ policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
+ }
+
+#if defined(OS_CHROMEOS)
+ void LoadONCPolicy(const std::string& filename) {
+ const std::string& user_policy_blob =
+ chromeos::onc::test_utils::ReadTestData(filename);
+ policy::PolicyMap policy;
+ policy.Set(policy::key::kOpenNetworkConfiguration,
+ policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
+ policy::POLICY_SOURCE_CLOUD,
+ base::MakeUnique<base::Value>(user_policy_blob), nullptr);
+ provider_.UpdateChromePolicy(policy);
+ content::RunAllPendingInMessageLoop();
+ }
+#endif
+
+ void ClickElement(const std::string& selector) {
+ EXPECT_TRUE(content::ExecuteScript(
+ GetSettingsFrame(),
+ "document.querySelector(\"" + selector + "\").click()"));
+ }
+
+ bool HasElement(const std::string& selector) {
+ bool result;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+ GetSettingsFrame(),
+ "window.domAutomationController.send("
+ " !!document.querySelector('" + selector + "'));",
+ &result));
+ return result;
+ }
+
+ policy::MockConfigurationPolicyProvider provider_;
+#if defined(OS_CHROMEOS)
+ policy::DevicePolicyCrosTestHelper device_policy_test_helper_;
+#endif
+};
+
+#if defined(OS_CHROMEOS)
+// Ensure policy-installed certificates without web trust do not display
+// the managed setting indicator (only on Chrome OS).
+IN_PROC_BROWSER_TEST_F(CertificateManagerBrowserTest,
+ PolicyCertificateWithoutWebTrustHasNoIndicator) {
+ LoadONCPolicy("certificate-authority.onc");
+ NavigateToSettings();
+ ClickElement("#certificatesManageButton");
+ ClickElement("#ca-certs-nav-tab");
+ EXPECT_FALSE(HasElement(".cert-policy"));
+}
+#endif
+
+#if defined(OS_CHROMEOS)
+// Ensure policy-installed certificates with web trust display the
+// managed setting indicator (only on Chrome OS).
+IN_PROC_BROWSER_TEST_F(CertificateManagerBrowserTest,
+ PolicyCertificateWithWebTrustHasIndicator) {
+ LoadONCPolicy("certificate-web-authority.onc");
+ NavigateToSettings();
+ ClickElement("#certificatesManageButton");
+ ClickElement("#ca-certs-nav-tab");
+ EXPECT_TRUE(HasElement(".cert-policy"));
+}
+#endif
diff --git a/chromium/chrome/browser/ui/webui/options/certificate_manager_browsertest.js b/chromium/chrome/browser/ui/webui/options/certificate_manager_browsertest.js
new file mode 100644
index 00000000000..cb9e3d81840
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/certificate_manager_browsertest.js
@@ -0,0 +1,405 @@
+// 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.
+
+// Mac and Windows go to native certificate manager, and certificate manager
+// isn't implemented if OpenSSL is used.
+GEN('#if defined(USE_NSS_CERTS)');
+
+GEN_INCLUDE(['options_browsertest_base.js']);
+
+/**
+ * URL of the Certificates dialog in the Settings page.
+ * @const
+ */
+var CERTIFICATE_MANAGER_SETTINGS_PAGE_URL =
+ 'chrome://settings-frame/certificates';
+
+// Standalone certificate manager dialog page is implemented only in Chrome OS.
+GEN('#if defined(OS_CHROMEOS)');
+
+/**
+ * URL of the standalone certificate manager dialog page.
+ * @const
+ */
+var CERTIFICATE_MANAGER_STANDALONE_PAGE_URL = 'chrome://certificate-manager/';
+
+GEN('#endif // defined(OS_CHROMEOS)');
+
+/**
+ * TestFixture for certificate manager WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function CertificateManagerWebUIBaseTest() {}
+
+CertificateManagerWebUIBaseTest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /** @override */
+ preLoad: function() {
+ // We can't check cr.isChromeOS in the preLoad since "cr" doesn't exist yet.
+ // This is copied from ui/webui/resources/js/cr.js, maybe
+ // there's a better way to do this.
+ this.isChromeOS = /CrOS/.test(navigator.userAgent);
+
+ this.makeAndRegisterMockHandler(
+ [
+ 'editCaCertificateTrust',
+ 'exportPersonalCertificate',
+ 'importPersonalCertificate',
+ 'importCaCertificate',
+ 'exportCertificate',
+ 'deleteCertificate',
+ 'populateCertificateManager',
+ 'viewCertificate',
+ ]);
+ },
+
+ /** @override */
+ setUp: function() {
+ OptionsBrowsertestBase.prototype.setUp.call(this);
+
+ var ariaRoleNotScopedSelectors = [
+ '#tree-item-autogen-id-0',
+ '#tree-item-autogen-id-1',
+ '#tree-item-autogen-id-2',
+ '#tree-item-autogen-id-3',
+ '#tree-item-autogen-id-4',
+ ];
+
+ // Enable when failure is resolved.
+ // AX_ARIA_09: http://crbug.com/570567
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'ariaRoleNotScoped',
+ ariaRoleNotScopedSelectors);
+
+ // Enable when failure is resolved.
+ // AX_ARIA_10: http://crbug.com/570566
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'unsupportedAriaAttribute',
+ '#caCertsTab-tree');
+
+ var focusableElementNotVisibleAndNotAriaHiddenSelectors = [
+ '#personalCertsTab-tree',
+ '#personalCertsTab-import',
+ '#personalCertsTab-import-and-bind',
+ '#certificate-confirm',
+ ];
+
+ // Enable when failure is resolved.
+ // AX_FOCUS_01: http://crbug.com/570568
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'focusableElementNotVisibleAndNotAriaHidden',
+ focusableElementNotVisibleAndNotAriaHiddenSelectors);
+ },
+};
+
+/**
+ * TestFixture for certificate manager WebUI testing.
+ * @extends {CertificateManagerWebUIBaseTest}
+ * @constructor
+ */
+function CertificateManagerWebUIUnpopulatedTest() {}
+
+CertificateManagerWebUIUnpopulatedTest.prototype = {
+ __proto__: CertificateManagerWebUIBaseTest.prototype,
+
+ /**
+ * Browse to the certificate manager dialog in the Settings page.
+ */
+ browsePreload: CERTIFICATE_MANAGER_SETTINGS_PAGE_URL,
+
+ /** @override */
+ preLoad: function() {
+ CertificateManagerWebUIBaseTest.prototype.preLoad.call(this);
+
+ // We expect the populateCertificateManager callback, but do not reply to
+ // it. This simulates what will be displayed if retrieving the cert list
+ // from NSS is slow.
+ this.mockHandler.expects(once()).populateCertificateManager();
+ },
+};
+
+// Test opening the certificate manager has correct location and buttons have
+// correct initial states when onPopulateTree has not been called.
+TEST_F('CertificateManagerWebUIUnpopulatedTest',
+ 'testUnpopulatedCertificateManager', function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ // All buttons should be disabled to start.
+ expectTrue($('personalCertsTab-view').disabled);
+ expectTrue($('personalCertsTab-backup').disabled);
+ expectTrue($('personalCertsTab-delete').disabled);
+ expectTrue($('personalCertsTab-import').disabled);
+ if (this.isChromeOS)
+ expectTrue($('personalCertsTab-import-and-bind').disabled);
+
+ expectTrue($('serverCertsTab-view').disabled);
+ expectTrue($('serverCertsTab-export').disabled);
+ expectTrue($('serverCertsTab-delete').disabled);
+ expectTrue($('serverCertsTab-import').disabled);
+
+ expectTrue($('caCertsTab-view').disabled);
+ expectTrue($('caCertsTab-edit').disabled);
+ expectTrue($('caCertsTab-export').disabled);
+ expectTrue($('caCertsTab-delete').disabled);
+ expectTrue($('caCertsTab-import').disabled);
+
+ expectTrue($('otherCertsTab-view').disabled);
+ expectTrue($('otherCertsTab-export').disabled);
+ expectTrue($('otherCertsTab-delete').disabled);
+
+ Mock4JS.verifyAllMocks();
+
+ // If user database is not available, import buttons should be disabled.
+ CertificateManager.onModelReady(false /* userDbAvailable*/,
+ false /* tpmAvailable */);
+
+ expectTrue($('personalCertsTab-import').disabled);
+ expectTrue($('serverCertsTab-import').disabled);
+ expectTrue($('caCertsTab-import').disabled);
+
+ // Once we get the onModelReady call, the import buttons should be enabled,
+ // others should still be disabled.
+ CertificateManager.onModelReady(true /* userDbAvailable*/,
+ false /* tpmAvailable */);
+
+ expectTrue($('personalCertsTab-view').disabled);
+ expectTrue($('personalCertsTab-backup').disabled);
+ expectTrue($('personalCertsTab-delete').disabled);
+ expectFalse($('personalCertsTab-import').disabled);
+
+ expectTrue($('serverCertsTab-view').disabled);
+ expectTrue($('serverCertsTab-export').disabled);
+ expectTrue($('serverCertsTab-delete').disabled);
+ expectFalse($('serverCertsTab-import').disabled);
+
+ expectTrue($('caCertsTab-view').disabled);
+ expectTrue($('caCertsTab-edit').disabled);
+ expectTrue($('caCertsTab-export').disabled);
+ expectTrue($('caCertsTab-delete').disabled);
+ expectFalse($('caCertsTab-import').disabled);
+
+ expectTrue($('otherCertsTab-view').disabled);
+ expectTrue($('otherCertsTab-export').disabled);
+ expectTrue($('otherCertsTab-delete').disabled);
+
+ // On ChromeOS, the import and bind button should only be enabled if TPM is
+ // present.
+ if (this.isChromeOS) {
+ expectTrue($('personalCertsTab-import-and-bind').disabled);
+ CertificateManager.onModelReady(true /* userDbAvailable*/,
+ true /* tpmAvailable */);
+ expectFalse($('personalCertsTab-import-and-bind').disabled);
+ }
+});
+
+/**
+ * TestFixture for certificate manager WebUI testing.
+ * @extends {CertificateManagerWebUIBaseTest}
+ * @constructor
+ */
+function CertificateManagerWebUITest() {}
+
+CertificateManagerWebUITest.prototype = {
+ __proto__: CertificateManagerWebUIBaseTest.prototype,
+
+ /** @override */
+ preLoad: function() {
+ CertificateManagerWebUIBaseTest.prototype.preLoad.call(this);
+
+ var tpmAvailable = this.isChromeOS;
+ var userDbAvailable = true;
+ this.mockHandler.expects(once()).populateCertificateManager().will(
+ callFunction(function() {
+ CertificateManager.onModelReady(userDbAvailable, tpmAvailable);
+
+ [['personalCertsTab-tree',
+ [{'id': 'o1',
+ 'name': 'org1',
+ 'subnodes': [{ 'id': 'c1',
+ 'name': 'cert1',
+ 'readonly': false,
+ 'untrusted': false,
+ 'extractable': true }],
+ }],
+ ],
+ ['caCertsTab-tree',
+ [{'id': 'o2',
+ 'name': 'org2',
+ 'subnodes': [{ 'id': 'ca_cert0',
+ 'name': 'ca_cert0',
+ 'readonly': false,
+ 'untrusted': false,
+ 'extractable': true,
+ 'policy': false },
+ { 'id': 'ca_cert1',
+ 'name': 'ca_cert1',
+ 'readonly': false,
+ 'untrusted': false,
+ 'extractable': true,
+ 'policy': true },
+ { 'id': 'ca_cert2',
+ 'name': 'ca_cert2',
+ 'readonly': false,
+ 'untrusted': false,
+ 'extractable': true,
+ 'policy': false }],
+ }],
+ ]
+ ].forEach(CertificateManager.onPopulateTree)}));
+ },
+};
+
+/**
+ * TestFixture for testing certificate manager WebUI in the Settings page.
+ * @extends {CertificateManagerWebUITest}
+ * @constructor
+ */
+function CertificateManagerSettingsWebUITest() {}
+
+CertificateManagerSettingsWebUITest.prototype = {
+ __proto__: CertificateManagerWebUITest.prototype,
+
+ /**
+ * Browse to the certificate manager dialog in the Settings page.
+ */
+ browsePreload: CERTIFICATE_MANAGER_SETTINGS_PAGE_URL,
+};
+
+TEST_F('CertificateManagerSettingsWebUITest',
+ 'testViewAndDeleteCert', function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ this.mockHandler.expects(once()).viewCertificate(['c1']);
+
+ expectTrue($('personalCertsTab-view').disabled);
+ expectTrue($('personalCertsTab-backup').disabled);
+ expectTrue($('personalCertsTab-delete').disabled);
+ expectFalse($('personalCertsTab-import').disabled);
+ if (this.isChromeOS)
+ expectFalse($('personalCertsTab-import-and-bind').disabled);
+
+ var personalCerts = $('personalCertsTab');
+
+ // Click on the first folder.
+ personalCerts.querySelector('div.tree-item').click();
+ // Buttons should still be in same state.
+ expectTrue($('personalCertsTab-view').disabled);
+ expectTrue($('personalCertsTab-backup').disabled);
+ expectTrue($('personalCertsTab-delete').disabled);
+ expectFalse($('personalCertsTab-import').disabled);
+ if (this.isChromeOS)
+ expectFalse($('personalCertsTab-import-and-bind').disabled);
+
+ // Click on the first cert.
+ personalCerts.querySelector('div.tree-item div.tree-item').click();
+ // Buttons should now allow you to act on the cert.
+ expectFalse($('personalCertsTab-view').disabled);
+ expectFalse($('personalCertsTab-backup').disabled);
+ expectFalse($('personalCertsTab-delete').disabled);
+ expectFalse($('personalCertsTab-import').disabled);
+ if (this.isChromeOS)
+ expectFalse($('personalCertsTab-import-and-bind').disabled);
+
+ // Click on the view button.
+ $('personalCertsTab-view').click();
+
+ Mock4JS.verifyAllMocks();
+
+ this.mockHandler.expects(once()).deleteCertificate(['c1']).will(callFunction(
+ function() {
+ CertificateManager.onPopulateTree(['personalCertsTab-tree', []]);
+ }));
+
+ // Click on the delete button.
+ $('personalCertsTab-delete').click();
+
+ // Click on the cancel button to verify the confirmation overlay closes.
+ $('alertOverlayCancel').click();
+ expectTrue($('alertOverlay').parentNode.classList.contains('transparent'));
+
+ // Click on the delete button.
+ $('personalCertsTab-delete').click();
+
+ // Click on the ok button in the confirmation overlay.
+ $('alertOverlayOk').click();
+ expectTrue($('alertOverlay').parentNode.classList.contains('transparent'));
+
+ // Context sensitive buttons should be disabled.
+ expectTrue($('personalCertsTab-view').disabled);
+ expectTrue($('personalCertsTab-backup').disabled);
+ expectTrue($('personalCertsTab-delete').disabled);
+ expectFalse($('personalCertsTab-import').disabled);
+ if (this.isChromeOS)
+ expectFalse($('personalCertsTab-import-and-bind').disabled);
+ // Tree should be empty.
+ expectTrue(personalCerts.querySelector('div.tree-item') === null);
+});
+
+// Ensure certificate objects with the 'policy' property set have
+// the cert-policy CSS class appended.
+TEST_F('CertificateManagerSettingsWebUITest',
+ 'testPolicyInstalledCertificate', function() {
+ // Click on the first folder and get the certificates.
+ var caCertsTab = $('caCertsTab');
+ caCertsTab.querySelector('div.tree-item').click();
+ var certs = caCertsTab.querySelectorAll('div.tree-item div.tree-item');
+
+ // First cert shouldn't show the controlled setting badge, and the
+ // edit and delete buttons should be enabled.
+ var cert0 = certs[0];
+ expectEquals('ca_cert0', cert0.data.name);
+ expectEquals(null, cert0.querySelector('.cert-policy'));
+ cert0.click();
+ expectFalse($('caCertsTab-edit').disabled);
+ expectFalse($('caCertsTab-delete').disabled);
+
+ // But the second should show the controlled setting badge, and the
+ // edit and delete buttons should be disabled.
+ var cert1 = certs[1];
+ expectEquals('ca_cert1', cert1.data.name);
+ expectNotEquals(null, cert1.querySelector('.cert-policy'));
+ cert1.click();
+ expectTrue($('caCertsTab-edit').disabled);
+ expectTrue($('caCertsTab-delete').disabled);
+});
+
+// Standalone certificate manager dialog page is implemented only in Chrome OS.
+GEN('#if defined(OS_CHROMEOS)');
+
+/**
+ * TestFixture for testing standalone certificate manager WebUI.
+ * @extends {CertificateManagerWebUITest}
+ * @constructor
+ */
+function CertificateManagerStandaloneWebUITest() {}
+
+CertificateManagerStandaloneWebUITest.prototype = {
+ __proto__: CertificateManagerWebUITest.prototype,
+
+ /**
+ * Browse to the certificate manager page.
+ */
+ browsePreload: CERTIFICATE_MANAGER_STANDALONE_PAGE_URL,
+};
+
+// Ensure that the standalone certificate manager page loads and displays the
+// ceertificates correctly.
+TEST_F('CertificateManagerStandaloneWebUITest', 'testCertsDisplaying',
+ function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ // Click on the first folder and get the certificates.
+ var caCertsTab = $('caCertsTab');
+ caCertsTab.querySelector('div.tree-item').click();
+ var certs = caCertsTab.querySelectorAll('div.tree-item div.tree-item');
+
+ // There should be exactly three certificates displayed.
+ expectEquals(certs.length, 3);
+});
+
+GEN('#endif // defined(OS_CHROMEOS)');
+
+GEN('#endif // defined(USE_NSS_CERTS)');
diff --git a/chromium/chrome/browser/ui/webui/options/certificate_manager_handler.cc b/chromium/chrome/browser/ui/webui/options/certificate_manager_handler.cc
new file mode 100644
index 00000000000..91f5f9f50d1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/certificate_manager_handler.cc
@@ -0,0 +1,1239 @@
+// 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/ui/webui/options/certificate_manager_handler.h"
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_util.h" // for FileAccessProvider
+#include "base/i18n/string_compare.h"
+#include "base/id_map.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/posix/safe_strerror.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/certificate_viewer.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/certificate_dialogs.h"
+#include "chrome/browser/ui/chrome_select_file_policy.h"
+#include "chrome/browser/ui/crypto_module_password_dialog_nss.h"
+#include "chrome/browser/ui/webui/certificate_viewer_webui.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "net/base/net_errors.h"
+#include "net/cert/x509_certificate.h"
+#include "net/der/input.h"
+#include "net/der/parser.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/policy/user_network_configuration_updater.h"
+#include "chrome/browser/chromeos/policy/user_network_configuration_updater_factory.h"
+#endif
+
+using base::UTF8ToUTF16;
+using content::BrowserThread;
+
+namespace {
+
+static const char kKeyId[] = "id";
+static const char kSubNodesId[] = "subnodes";
+static const char kNameId[] = "name";
+static const char kReadOnlyId[] = "readonly";
+static const char kUntrustedId[] = "untrusted";
+static const char kExtractableId[] = "extractable";
+static const char kErrorId[] = "error";
+static const char kPolicyTrustedId[] = "policy";
+
+// Enumeration of different callers of SelectFile. (Start counting at 1 so
+// if SelectFile is accidentally called with params=NULL it won't match any.)
+enum {
+ EXPORT_PERSONAL_FILE_SELECTED = 1,
+ IMPORT_PERSONAL_FILE_SELECTED,
+ IMPORT_SERVER_FILE_SELECTED,
+ IMPORT_CA_FILE_SELECTED,
+};
+
+std::string OrgNameToId(const std::string& org) {
+ return "org-" + org;
+}
+
+bool CallbackArgsToBool(const base::ListValue* args, int index, bool* result) {
+ std::string string_value;
+ if (!args->GetString(index, &string_value))
+ return false;
+
+ *result = string_value[0] == 't';
+ return true;
+}
+
+struct DictionaryIdComparator {
+ explicit DictionaryIdComparator(icu::Collator* collator)
+ : collator_(collator) {
+ }
+
+ bool operator()(const base::Value& a, const base::Value& b) const {
+ const base::DictionaryValue* a_dict;
+ bool a_is_dictionary = a.GetAsDictionary(&a_dict);
+ DCHECK(a_is_dictionary);
+ const base::DictionaryValue* b_dict;
+ bool b_is_dictionary = b.GetAsDictionary(&b_dict);
+ DCHECK(b_is_dictionary);
+ base::string16 a_str;
+ base::string16 b_str;
+ a_dict->GetString(kNameId, &a_str);
+ b_dict->GetString(kNameId, &b_str);
+ if (collator_ == NULL)
+ return a_str < b_str;
+ return base::i18n::CompareString16WithCollator(*collator_, a_str, b_str) ==
+ UCOL_LESS;
+ }
+
+ icu::Collator* collator_;
+};
+
+std::string NetErrorToString(int net_error) {
+ switch (net_error) {
+ // TODO(mattm): handle more cases.
+ case net::ERR_IMPORT_CA_CERT_NOT_CA:
+ return l10n_util::GetStringUTF8(IDS_CERT_MANAGER_ERROR_NOT_CA);
+ case net::ERR_IMPORT_CERT_ALREADY_EXISTS:
+ return l10n_util::GetStringUTF8(
+ IDS_CERT_MANAGER_ERROR_CERT_ALREADY_EXISTS);
+ default:
+ return l10n_util::GetStringUTF8(IDS_CERT_MANAGER_UNKNOWN_ERROR);
+ }
+}
+
+// Struct to bind the Equals member function to an object for use in find_if.
+struct CertEquals {
+ explicit CertEquals(const net::X509Certificate* cert) : cert_(cert) {}
+ bool operator()(const scoped_refptr<net::X509Certificate> cert) const {
+ return cert_->Equals(cert.get());
+ }
+ const net::X509Certificate* cert_;
+};
+
+// Determine whether a certificate was stored with web trust by a policy.
+bool IsPolicyInstalledWithWebTrust(
+ const net::CertificateList& web_trust_certs,
+ net::X509Certificate* cert) {
+ return std::find_if(web_trust_certs.begin(), web_trust_certs.end(),
+ CertEquals(cert)) != web_trust_certs.end();
+}
+
+#if defined(OS_CHROMEOS)
+void ShowCertificateViewerModalDialog(content::WebContents* web_contents,
+ gfx::NativeWindow parent,
+ net::X509Certificate* cert) {
+ CertificateViewerModalDialog* dialog = new CertificateViewerModalDialog(cert);
+ dialog->Show(web_contents, parent);
+}
+#endif
+
+// Determine if |data| could be a PFX Protocol Data Unit.
+// This only does the minimum parsing necessary to distinguish a PFX file from a
+// DER encoded Certificate.
+//
+// From RFC 7292 section 4:
+// PFX ::= SEQUENCE {
+// version INTEGER {v3(3)}(v3,...),
+// authSafe ContentInfo,
+// macData MacData OPTIONAL
+// }
+// From RFC 5280 section 4.1:
+// Certificate ::= SEQUENCE {
+// tbsCertificate TBSCertificate,
+// signatureAlgorithm AlgorithmIdentifier,
+// signatureValue BIT STRING }
+//
+// Certificate must be DER encoded, while PFX may be BER encoded.
+// Therefore PFX can be distingushed by checking if the file starts with an
+// indefinite SEQUENCE, or a definite SEQUENCE { INTEGER, ... }.
+bool CouldBePFX(const std::string& data) {
+ if (data.size() < 4)
+ return false;
+
+ // Indefinite length SEQUENCE.
+ if (data[0] == 0x30 && static_cast<uint8_t>(data[1]) == 0x80)
+ return true;
+
+ // If the SEQUENCE is definite length, it can be parsed through the version
+ // tag using DER parser, since INTEGER must be definite length, even in BER.
+ net::der::Parser parser((net::der::Input(&data)));
+ net::der::Parser sequence_parser;
+ if (!parser.ReadSequence(&sequence_parser))
+ return false;
+ if (!sequence_parser.SkipTag(net::der::kInteger))
+ return false;
+ return true;
+}
+
+} // namespace
+
+namespace options {
+
+///////////////////////////////////////////////////////////////////////////////
+// CertIdMap
+
+class CertIdMap {
+ public:
+ CertIdMap() {}
+ ~CertIdMap() {}
+
+ std::string CertToId(net::X509Certificate* cert);
+ net::X509Certificate* IdToCert(const std::string& id);
+ net::X509Certificate* CallbackArgsToCert(const base::ListValue* args);
+
+ private:
+ typedef std::map<net::X509Certificate*, int32_t> CertMap;
+
+ // Creates an ID for cert and looks up the cert for an ID.
+ IDMap<net::X509Certificate*> id_map_;
+
+ // Finds the ID for a cert.
+ CertMap cert_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(CertIdMap);
+};
+
+std::string CertIdMap::CertToId(net::X509Certificate* cert) {
+ CertMap::const_iterator iter = cert_map_.find(cert);
+ if (iter != cert_map_.end())
+ return base::IntToString(iter->second);
+
+ int32_t new_id = id_map_.Add(cert);
+ cert_map_[cert] = new_id;
+ return base::IntToString(new_id);
+}
+
+net::X509Certificate* CertIdMap::IdToCert(const std::string& id) {
+ int32_t cert_id = 0;
+ if (!base::StringToInt(id, &cert_id))
+ return NULL;
+
+ return id_map_.Lookup(cert_id);
+}
+
+net::X509Certificate* CertIdMap::CallbackArgsToCert(
+ const base::ListValue* args) {
+ std::string node_id;
+ if (!args->GetString(0, &node_id))
+ return NULL;
+
+ net::X509Certificate* cert = IdToCert(node_id);
+ if (!cert) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ return cert;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FileAccessProvider
+
+// TODO(mattm): Move to some shared location?
+class FileAccessProvider
+ : public base::RefCountedThreadSafe<FileAccessProvider> {
+ public:
+ // The first parameter is 0 on success or errno on failure. The second
+ // parameter is read result.
+ typedef base::Callback<void(const int*, const std::string*)> ReadCallback;
+
+ // The first parameter is 0 on success or errno on failure. The second
+ // parameter is the number of bytes written on success.
+ typedef base::Callback<void(const int*, const int*)> WriteCallback;
+
+ base::CancelableTaskTracker::TaskId StartRead(
+ const base::FilePath& path,
+ const ReadCallback& callback,
+ base::CancelableTaskTracker* tracker);
+ base::CancelableTaskTracker::TaskId StartWrite(
+ const base::FilePath& path,
+ const std::string& data,
+ const WriteCallback& callback,
+ base::CancelableTaskTracker* tracker);
+
+ private:
+ friend class base::RefCountedThreadSafe<FileAccessProvider>;
+ virtual ~FileAccessProvider() {}
+
+ // Reads file at |path|. |saved_errno| is 0 on success or errno on failure.
+ // When success, |data| has file content.
+ void DoRead(const base::FilePath& path,
+ int* saved_errno,
+ std::string* data);
+ // Writes data to file at |path|. |saved_errno| is 0 on success or errno on
+ // failure. When success, |bytes_written| has number of bytes written.
+ void DoWrite(const base::FilePath& path,
+ const std::string& data,
+ int* saved_errno,
+ int* bytes_written);
+};
+
+base::CancelableTaskTracker::TaskId FileAccessProvider::StartRead(
+ const base::FilePath& path,
+ const ReadCallback& callback,
+ base::CancelableTaskTracker* tracker) {
+ // Owned by reply callback posted below.
+ int* saved_errno = new int(0);
+ std::string* data = new std::string();
+
+ // Post task to file thread to read file.
+ return tracker->PostTaskAndReply(
+ BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get(),
+ FROM_HERE,
+ base::Bind(&FileAccessProvider::DoRead, this, path, saved_errno, data),
+ base::Bind(callback, base::Owned(saved_errno), base::Owned(data)));
+}
+
+base::CancelableTaskTracker::TaskId FileAccessProvider::StartWrite(
+ const base::FilePath& path,
+ const std::string& data,
+ const WriteCallback& callback,
+ base::CancelableTaskTracker* tracker) {
+ // Owned by reply callback posted below.
+ int* saved_errno = new int(0);
+ int* bytes_written = new int(0);
+
+ // Post task to file thread to write file.
+ return tracker->PostTaskAndReply(
+ BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get(),
+ FROM_HERE, base::Bind(&FileAccessProvider::DoWrite, this, path, data,
+ saved_errno, bytes_written),
+ base::Bind(callback, base::Owned(saved_errno),
+ base::Owned(bytes_written)));
+}
+
+void FileAccessProvider::DoRead(const base::FilePath& path,
+ int* saved_errno,
+ std::string* data) {
+ bool success = base::ReadFileToString(path, data);
+ *saved_errno = success ? 0 : errno;
+}
+
+void FileAccessProvider::DoWrite(const base::FilePath& path,
+ const std::string& data,
+ int* saved_errno,
+ int* bytes_written) {
+ *bytes_written = base::WriteFile(path, data.data(), data.size());
+ *saved_errno = *bytes_written >= 0 ? 0 : errno;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CertificateManagerHandler
+
+CertificateManagerHandler::CertificateManagerHandler(
+ bool show_certs_in_modal_dialog)
+ : show_certs_in_modal_dialog_(show_certs_in_modal_dialog),
+ requested_certificate_manager_model_(false),
+ use_hardware_backed_(false),
+ file_access_provider_(new FileAccessProvider()),
+ cert_id_map_(new CertIdMap),
+ weak_ptr_factory_(this) {}
+
+CertificateManagerHandler::~CertificateManagerHandler() {
+}
+
+void CertificateManagerHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ RegisterTitle(localized_strings, "certificateManagerPage",
+ IDS_CERTIFICATE_MANAGER_TITLE);
+
+ // Tabs.
+ localized_strings->SetString("personalCertsTabTitle",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_PERSONAL_CERTS_TAB_LABEL));
+ localized_strings->SetString("serverCertsTabTitle",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_SERVER_CERTS_TAB_LABEL));
+ localized_strings->SetString("caCertsTabTitle",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_CERT_AUTHORITIES_TAB_LABEL));
+ localized_strings->SetString("otherCertsTabTitle",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_OTHER_TAB_LABEL));
+
+ // Tab descriptions.
+ localized_strings->SetString("personalCertsTabDescription",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_USER_TREE_DESCRIPTION));
+ localized_strings->SetString("serverCertsTabDescription",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_SERVER_TREE_DESCRIPTION));
+ localized_strings->SetString("caCertsTabDescription",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_AUTHORITIES_TREE_DESCRIPTION));
+ localized_strings->SetString("otherCertsTabDescription",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_OTHER_TREE_DESCRIPTION));
+
+ // Buttons.
+ localized_strings->SetString("view_certificate",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_VIEW_CERT_BUTTON));
+ localized_strings->SetString("import_certificate",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_IMPORT_BUTTON));
+ localized_strings->SetString("export_certificate",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_EXPORT_BUTTON));
+ localized_strings->SetString("edit_certificate",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_EDIT_BUTTON));
+ localized_strings->SetString("delete_certificate",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_BUTTON));
+
+ // Certificate Delete overlay strings.
+ localized_strings->SetString("personalCertsTabDeleteConfirm",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_USER_FORMAT));
+ localized_strings->SetString("personalCertsTabDeleteImpact",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_USER_DESCRIPTION));
+ localized_strings->SetString("serverCertsTabDeleteConfirm",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_SERVER_FORMAT));
+ localized_strings->SetString("serverCertsTabDeleteImpact",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_SERVER_DESCRIPTION));
+ localized_strings->SetString("caCertsTabDeleteConfirm",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_CA_FORMAT));
+ localized_strings->SetString("caCertsTabDeleteImpact",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_CA_DESCRIPTION));
+ localized_strings->SetString("otherCertsTabDeleteConfirm",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_DELETE_OTHER_FORMAT));
+ localized_strings->SetString("otherCertsTabDeleteImpact", std::string());
+
+ // Certificate Restore overlay strings.
+ localized_strings->SetString("certificateRestorePasswordDescription",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_RESTORE_PASSWORD_DESC));
+ localized_strings->SetString("certificatePasswordLabel",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_PASSWORD_LABEL));
+
+ // Personal Certificate Export overlay strings.
+ localized_strings->SetString("certificateExportPasswordDescription",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_EXPORT_PASSWORD_DESC));
+ localized_strings->SetString("certificateExportPasswordHelp",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_EXPORT_PASSWORD_HELP));
+ localized_strings->SetString("certificateConfirmPasswordLabel",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_CONFIRM_PASSWORD_LABEL));
+
+ // Edit CA Trust & Import CA overlay strings.
+ localized_strings->SetString("certificateEditCaTitle",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_EDIT_CA_TITLE));
+ localized_strings->SetString("certificateEditTrustLabel",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_EDIT_TRUST_LABEL));
+ localized_strings->SetString("certificateEditCaTrustDescriptionFormat",
+ l10n_util::GetStringUTF16(
+ IDS_CERT_MANAGER_EDIT_CA_TRUST_DESCRIPTION_FORMAT));
+ localized_strings->SetString("certificateImportCaDescriptionFormat",
+ l10n_util::GetStringUTF16(
+ IDS_CERT_MANAGER_IMPORT_CA_DESCRIPTION_FORMAT));
+ localized_strings->SetString("certificateCaTrustSSLLabel",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_EDIT_CA_TRUST_SSL_LABEL));
+ localized_strings->SetString("certificateCaTrustEmailLabel",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_EDIT_CA_TRUST_EMAIL_LABEL));
+ localized_strings->SetString("certificateCaTrustObjSignLabel",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_EDIT_CA_TRUST_OBJSIGN_LABEL));
+ localized_strings->SetString("certificateImportErrorFormat",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_IMPORT_ERROR_FORMAT));
+
+ // Badges next to certificates
+ localized_strings->SetString("badgeCertUntrusted",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_UNTRUSTED));
+ localized_strings->SetString("certPolicyInstalled",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_POLICY_INSTALLED));
+
+#if defined(OS_CHROMEOS)
+ localized_strings->SetString("importAndBindCertificate",
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_IMPORT_AND_BIND_BUTTON));
+#endif // defined(OS_CHROMEOS)
+}
+
+void CertificateManagerHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "viewCertificate",
+ base::Bind(&CertificateManagerHandler::View, base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "getCaCertificateTrust",
+ base::Bind(&CertificateManagerHandler::GetCATrust,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "editCaCertificateTrust",
+ base::Bind(&CertificateManagerHandler::EditCATrust,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "editServerCertificate",
+ base::Bind(&CertificateManagerHandler::EditServer,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "cancelImportExportCertificate",
+ base::Bind(&CertificateManagerHandler::CancelImportExportProcess,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "exportPersonalCertificate",
+ base::Bind(&CertificateManagerHandler::ExportPersonal,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "exportAllPersonalCertificates",
+ base::Bind(&CertificateManagerHandler::ExportAllPersonal,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "exportPersonalCertificatePasswordSelected",
+ base::Bind(&CertificateManagerHandler::ExportPersonalPasswordSelected,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "importPersonalCertificate",
+ base::Bind(&CertificateManagerHandler::StartImportPersonal,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "importPersonalCertificatePasswordSelected",
+ base::Bind(&CertificateManagerHandler::ImportPersonalPasswordSelected,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "importCaCertificate",
+ base::Bind(&CertificateManagerHandler::ImportCA,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "importCaCertificateTrustSelected",
+ base::Bind(&CertificateManagerHandler::ImportCATrustSelected,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "importServerCertificate",
+ base::Bind(&CertificateManagerHandler::ImportServer,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "exportCertificate",
+ base::Bind(&CertificateManagerHandler::Export,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "deleteCertificate",
+ base::Bind(&CertificateManagerHandler::Delete,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "populateCertificateManager",
+ base::Bind(&CertificateManagerHandler::Populate,
+ base::Unretained(this)));
+}
+
+void CertificateManagerHandler::CertificatesRefreshed() {
+ net::CertificateList web_trusted_certs;
+#if defined(OS_CHROMEOS)
+ policy::UserNetworkConfigurationUpdater* service =
+ policy::UserNetworkConfigurationUpdaterFactory::GetForProfile(
+ Profile::FromWebUI(web_ui()));
+ if (service)
+ service->GetWebTrustedCertificates(&web_trusted_certs);
+#endif
+ PopulateTree("personalCertsTab", net::USER_CERT, web_trusted_certs);
+ PopulateTree("serverCertsTab", net::SERVER_CERT, web_trusted_certs);
+ PopulateTree("caCertsTab", net::CA_CERT, web_trusted_certs);
+ PopulateTree("otherCertsTab", net::OTHER_CERT, web_trusted_certs);
+}
+
+void CertificateManagerHandler::FileSelected(const base::FilePath& path,
+ int index,
+ void* params) {
+ switch (reinterpret_cast<intptr_t>(params)) {
+ case EXPORT_PERSONAL_FILE_SELECTED:
+ ExportPersonalFileSelected(path);
+ break;
+ case IMPORT_PERSONAL_FILE_SELECTED:
+ ImportPersonalFileSelected(path);
+ break;
+ case IMPORT_SERVER_FILE_SELECTED:
+ ImportServerFileSelected(path);
+ break;
+ case IMPORT_CA_FILE_SELECTED:
+ ImportCAFileSelected(path);
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
+void CertificateManagerHandler::FileSelectionCanceled(void* params) {
+ switch (reinterpret_cast<intptr_t>(params)) {
+ case EXPORT_PERSONAL_FILE_SELECTED:
+ case IMPORT_PERSONAL_FILE_SELECTED:
+ case IMPORT_SERVER_FILE_SELECTED:
+ case IMPORT_CA_FILE_SELECTED:
+ ImportExportCleanup();
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
+void CertificateManagerHandler::View(const base::ListValue* args) {
+ net::X509Certificate* cert = cert_id_map_->CallbackArgsToCert(args);
+ if (!cert)
+ return;
+#if defined(OS_CHROMEOS)
+ if (show_certs_in_modal_dialog_) {
+ ShowCertificateViewerModalDialog(web_ui()->GetWebContents(),
+ GetParentWindow(),
+ cert);
+ return;
+ }
+#endif
+ ShowCertificateViewer(web_ui()->GetWebContents(), GetParentWindow(), cert);
+}
+
+void CertificateManagerHandler::GetCATrust(const base::ListValue* args) {
+ net::X509Certificate* cert = cert_id_map_->CallbackArgsToCert(args);
+ if (!cert) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "CertificateEditCaTrustOverlay.dismiss");
+ return;
+ }
+
+ net::NSSCertDatabase::TrustBits trust_bits =
+ certificate_manager_model_->cert_db()->GetCertTrust(cert, net::CA_CERT);
+ base::Value ssl_value(
+ static_cast<bool>(trust_bits & net::NSSCertDatabase::TRUSTED_SSL));
+ base::Value email_value(
+ static_cast<bool>(trust_bits & net::NSSCertDatabase::TRUSTED_EMAIL));
+ base::Value obj_sign_value(
+ static_cast<bool>(trust_bits & net::NSSCertDatabase::TRUSTED_OBJ_SIGN));
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "CertificateEditCaTrustOverlay.populateTrust", ssl_value, email_value,
+ obj_sign_value);
+}
+
+void CertificateManagerHandler::EditCATrust(const base::ListValue* args) {
+ net::X509Certificate* cert = cert_id_map_->CallbackArgsToCert(args);
+ bool fail = !cert;
+ bool trust_ssl = false;
+ bool trust_email = false;
+ bool trust_obj_sign = false;
+ fail |= !CallbackArgsToBool(args, 1, &trust_ssl);
+ fail |= !CallbackArgsToBool(args, 2, &trust_email);
+ fail |= !CallbackArgsToBool(args, 3, &trust_obj_sign);
+ if (fail) {
+ LOG(ERROR) << "EditCATrust args fail";
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "CertificateEditCaTrustOverlay.dismiss");
+ return;
+ }
+
+ bool result = certificate_manager_model_->SetCertTrust(
+ cert,
+ net::CA_CERT,
+ trust_ssl * net::NSSCertDatabase::TRUSTED_SSL +
+ trust_email * net::NSSCertDatabase::TRUSTED_EMAIL +
+ trust_obj_sign * net::NSSCertDatabase::TRUSTED_OBJ_SIGN);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "CertificateEditCaTrustOverlay.dismiss");
+ if (!result) {
+ // TODO(mattm): better error messages?
+ ShowError(
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_SET_TRUST_ERROR_TITLE),
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_UNKNOWN_ERROR));
+ }
+}
+
+void CertificateManagerHandler::EditServer(const base::ListValue* args) {
+ NOTIMPLEMENTED();
+}
+
+void CertificateManagerHandler::ExportPersonal(const base::ListValue* args) {
+ net::X509Certificate* cert = cert_id_map_->CallbackArgsToCert(args);
+ if (!cert)
+ return;
+
+ selected_cert_list_.push_back(cert);
+
+ ui::SelectFileDialog::FileTypeInfo file_type_info;
+ file_type_info.extensions.resize(1);
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("p12"));
+ file_type_info.extension_description_overrides.push_back(
+ l10n_util::GetStringUTF16(IDS_CERT_MANAGER_PKCS12_FILES));
+ file_type_info.include_all_files = true;
+ select_file_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
+ select_file_dialog_->SelectFile(
+ ui::SelectFileDialog::SELECT_SAVEAS_FILE, base::string16(),
+ base::FilePath(), &file_type_info, 1, FILE_PATH_LITERAL("p12"),
+ GetParentWindow(),
+ reinterpret_cast<void*>(EXPORT_PERSONAL_FILE_SELECTED));
+}
+
+void CertificateManagerHandler::ExportAllPersonal(const base::ListValue* args) {
+ NOTIMPLEMENTED();
+}
+
+void CertificateManagerHandler::ExportPersonalFileSelected(
+ const base::FilePath& path) {
+ file_path_ = path;
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "CertificateManager.exportPersonalAskPassword");
+}
+
+void CertificateManagerHandler::ExportPersonalPasswordSelected(
+ const base::ListValue* args) {
+ if (!args->GetString(0, &password_)) {
+ web_ui()->CallJavascriptFunctionUnsafe("CertificateRestoreOverlay.dismiss");
+ ImportExportCleanup();
+ return;
+ }
+
+ // Currently, we don't support exporting more than one at a time. If we do,
+ // this would need to either change this to use UnlockSlotsIfNecessary or
+ // change UnlockCertSlotIfNecessary to take a CertificateList.
+ DCHECK_EQ(selected_cert_list_.size(), 1U);
+
+ // TODO(mattm): do something smarter about non-extractable keys
+ chrome::UnlockCertSlotIfNecessary(
+ selected_cert_list_[0].get(),
+ chrome::kCryptoModulePasswordCertExport,
+ net::HostPortPair(), // unused.
+ GetParentWindow(),
+ base::Bind(&CertificateManagerHandler::ExportPersonalSlotsUnlocked,
+ base::Unretained(this)));
+}
+
+void CertificateManagerHandler::ExportPersonalSlotsUnlocked() {
+ std::string output;
+ int num_exported = certificate_manager_model_->cert_db()->ExportToPKCS12(
+ selected_cert_list_,
+ password_,
+ &output);
+ if (!num_exported) {
+ web_ui()->CallJavascriptFunctionUnsafe("CertificateRestoreOverlay.dismiss");
+ ShowError(
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_PKCS12_EXPORT_ERROR_TITLE),
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_UNKNOWN_ERROR));
+ ImportExportCleanup();
+ return;
+ }
+ file_access_provider_->StartWrite(
+ file_path_,
+ output,
+ base::Bind(&CertificateManagerHandler::ExportPersonalFileWritten,
+ base::Unretained(this)),
+ &tracker_);
+}
+
+void CertificateManagerHandler::ExportPersonalFileWritten(
+ const int* write_errno, const int* bytes_written) {
+ web_ui()->CallJavascriptFunctionUnsafe("CertificateRestoreOverlay.dismiss");
+ ImportExportCleanup();
+ if (*write_errno) {
+ ShowError(
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_PKCS12_EXPORT_ERROR_TITLE),
+ l10n_util::GetStringFUTF8(IDS_CERT_MANAGER_WRITE_ERROR_FORMAT,
+ UTF8ToUTF16(
+ base::safe_strerror(*write_errno))));
+ }
+}
+
+void CertificateManagerHandler::StartImportPersonal(
+ const base::ListValue* args) {
+ ui::SelectFileDialog::FileTypeInfo file_type_info;
+ if (!args->GetBoolean(0, &use_hardware_backed_)) {
+ // Unable to retrieve the hardware backed attribute from the args,
+ // so bail.
+ web_ui()->CallJavascriptFunctionUnsafe("CertificateRestoreOverlay.dismiss");
+ ImportExportCleanup();
+ return;
+ }
+ file_type_info.extensions.resize(1);
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("p12"));
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pfx"));
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("crt"));
+ file_type_info.extension_description_overrides.push_back(
+ l10n_util::GetStringUTF16(IDS_CERT_USAGE_SSL_CLIENT));
+ file_type_info.include_all_files = true;
+ select_file_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
+ select_file_dialog_->SelectFile(
+ ui::SelectFileDialog::SELECT_OPEN_FILE, base::string16(),
+ base::FilePath(), &file_type_info, 1, FILE_PATH_LITERAL("p12"),
+ GetParentWindow(),
+ reinterpret_cast<void*>(IMPORT_PERSONAL_FILE_SELECTED));
+}
+
+void CertificateManagerHandler::ImportPersonalFileSelected(
+ const base::FilePath& path) {
+ file_access_provider_->StartRead(
+ path, base::Bind(&CertificateManagerHandler::ImportPersonalFileRead,
+ base::Unretained(this)),
+ &tracker_);
+}
+
+void CertificateManagerHandler::ImportPersonalFileRead(
+ const int* read_errno, const std::string* data) {
+ if (*read_errno) {
+ ImportExportCleanup();
+ web_ui()->CallJavascriptFunctionUnsafe("CertificateRestoreOverlay.dismiss");
+ ShowError(
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringFUTF8(IDS_CERT_MANAGER_READ_ERROR_FORMAT,
+ UTF8ToUTF16(
+ base::safe_strerror(*read_errno))));
+ return;
+ }
+
+ file_data_ = *data;
+
+ if (CouldBePFX(file_data_)) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "CertificateManager.importPersonalAskPassword");
+ return;
+ }
+
+ // Non .p12/.pfx files are assumed to be single/chain certificates without
+ // private key data. The default extension according to spec is '.crt',
+ // however other extensions are also used in some places to represent these
+ // certificates.
+ int result = certificate_manager_model_->ImportUserCert(file_data_);
+ ImportExportCleanup();
+ web_ui()->CallJavascriptFunctionUnsafe("CertificateRestoreOverlay.dismiss");
+ int string_id;
+ switch (result) {
+ case net::OK:
+ return;
+ case net::ERR_NO_PRIVATE_KEY_FOR_CERT:
+ string_id = IDS_CERT_MANAGER_IMPORT_MISSING_KEY;
+ break;
+ case net::ERR_CERT_INVALID:
+ string_id = IDS_CERT_MANAGER_IMPORT_INVALID_FILE;
+ break;
+ default:
+ string_id = IDS_CERT_MANAGER_UNKNOWN_ERROR;
+ break;
+ }
+ ShowError(
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringUTF8(string_id));
+}
+
+void CertificateManagerHandler::ImportPersonalPasswordSelected(
+ const base::ListValue* args) {
+ if (!args->GetString(0, &password_)) {
+ web_ui()->CallJavascriptFunctionUnsafe("CertificateRestoreOverlay.dismiss");
+ ImportExportCleanup();
+ return;
+ }
+
+ if (use_hardware_backed_) {
+ slot_ = certificate_manager_model_->cert_db()->GetPrivateSlot();
+ } else {
+ slot_ = certificate_manager_model_->cert_db()->GetPublicSlot();
+ }
+
+ std::vector<crypto::ScopedPK11Slot> modules;
+ modules.push_back(crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot_.get())));
+ chrome::UnlockSlotsIfNecessary(
+ std::move(modules), chrome::kCryptoModulePasswordCertImport,
+ net::HostPortPair(), // unused.
+ GetParentWindow(),
+ base::Bind(&CertificateManagerHandler::ImportPersonalSlotUnlocked,
+ base::Unretained(this)));
+}
+
+void CertificateManagerHandler::ImportPersonalSlotUnlocked() {
+ // Determine if the private key should be unextractable after the import.
+ // We do this by checking the value of |use_hardware_backed_| which is set
+ // to true if importing into a hardware module. Currently, this only happens
+ // for Chrome OS when the "Import and Bind" option is chosen.
+ bool is_extractable = !use_hardware_backed_;
+ int result = certificate_manager_model_->ImportFromPKCS12(
+ slot_.get(), file_data_, password_, is_extractable);
+ ImportExportCleanup();
+ web_ui()->CallJavascriptFunctionUnsafe("CertificateRestoreOverlay.dismiss");
+ int string_id;
+ switch (result) {
+ case net::OK:
+ return;
+ case net::ERR_PKCS12_IMPORT_BAD_PASSWORD:
+ // TODO(mattm): if the error was a bad password, we should reshow the
+ // password dialog after the user dismisses the error dialog.
+ string_id = IDS_CERT_MANAGER_BAD_PASSWORD;
+ break;
+ case net::ERR_PKCS12_IMPORT_INVALID_MAC:
+ string_id = IDS_CERT_MANAGER_IMPORT_INVALID_MAC;
+ break;
+ case net::ERR_PKCS12_IMPORT_INVALID_FILE:
+ string_id = IDS_CERT_MANAGER_IMPORT_INVALID_FILE;
+ break;
+ case net::ERR_PKCS12_IMPORT_UNSUPPORTED:
+ string_id = IDS_CERT_MANAGER_IMPORT_UNSUPPORTED;
+ break;
+ default:
+ string_id = IDS_CERT_MANAGER_UNKNOWN_ERROR;
+ break;
+ }
+ ShowError(
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringUTF8(string_id));
+}
+
+void CertificateManagerHandler::CancelImportExportProcess(
+ const base::ListValue* args) {
+ ImportExportCleanup();
+}
+
+void CertificateManagerHandler::ImportExportCleanup() {
+ file_path_.clear();
+ password_.clear();
+ file_data_.clear();
+ use_hardware_backed_ = false;
+ selected_cert_list_.clear();
+ slot_.reset();
+ tracker_.TryCancelAll();
+
+ // There may be pending file dialogs, we need to tell them that we've gone
+ // away so they don't try and call back to us.
+ if (select_file_dialog_.get())
+ select_file_dialog_->ListenerDestroyed();
+ select_file_dialog_ = NULL;
+}
+
+void CertificateManagerHandler::ImportServer(const base::ListValue* args) {
+ select_file_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
+ ShowCertSelectFileDialog(
+ select_file_dialog_.get(),
+ ui::SelectFileDialog::SELECT_OPEN_FILE,
+ base::FilePath(),
+ GetParentWindow(),
+ reinterpret_cast<void*>(IMPORT_SERVER_FILE_SELECTED));
+}
+
+void CertificateManagerHandler::ImportServerFileSelected(
+ const base::FilePath& path) {
+ file_access_provider_->StartRead(
+ path, base::Bind(&CertificateManagerHandler::ImportServerFileRead,
+ base::Unretained(this)),
+ &tracker_);
+}
+
+void CertificateManagerHandler::ImportServerFileRead(const int* read_errno,
+ const std::string* data) {
+ if (*read_errno) {
+ ImportExportCleanup();
+ ShowError(
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_SERVER_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringFUTF8(IDS_CERT_MANAGER_READ_ERROR_FORMAT,
+ UTF8ToUTF16(
+ base::safe_strerror(*read_errno))));
+ return;
+ }
+
+ selected_cert_list_ = net::X509Certificate::CreateCertificateListFromBytes(
+ data->data(), data->size(), net::X509Certificate::FORMAT_AUTO);
+ if (selected_cert_list_.empty()) {
+ ImportExportCleanup();
+ ShowError(
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_SERVER_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_CERT_PARSE_ERROR));
+ return;
+ }
+
+ net::NSSCertDatabase::ImportCertFailureList not_imported;
+ // TODO(mattm): Add UI for trust. http://crbug.com/76274
+ bool result = certificate_manager_model_->ImportServerCert(
+ selected_cert_list_,
+ net::NSSCertDatabase::TRUST_DEFAULT,
+ &not_imported);
+ if (!result) {
+ ShowError(
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_SERVER_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_UNKNOWN_ERROR));
+ } else if (!not_imported.empty()) {
+ ShowImportErrors(
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_SERVER_IMPORT_ERROR_TITLE),
+ not_imported);
+ }
+ ImportExportCleanup();
+}
+
+void CertificateManagerHandler::ImportCA(const base::ListValue* args) {
+ select_file_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
+ ShowCertSelectFileDialog(select_file_dialog_.get(),
+ ui::SelectFileDialog::SELECT_OPEN_FILE,
+ base::FilePath(),
+ GetParentWindow(),
+ reinterpret_cast<void*>(IMPORT_CA_FILE_SELECTED));
+}
+
+void CertificateManagerHandler::ImportCAFileSelected(
+ const base::FilePath& path) {
+ file_access_provider_->StartRead(
+ path, base::Bind(&CertificateManagerHandler::ImportCAFileRead,
+ base::Unretained(this)),
+ &tracker_);
+}
+
+void CertificateManagerHandler::ImportCAFileRead(const int* read_errno,
+ const std::string* data) {
+ if (*read_errno) {
+ ImportExportCleanup();
+ ShowError(
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_CA_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringFUTF8(IDS_CERT_MANAGER_READ_ERROR_FORMAT,
+ UTF8ToUTF16(
+ base::safe_strerror(*read_errno))));
+ return;
+ }
+
+ selected_cert_list_ = net::X509Certificate::CreateCertificateListFromBytes(
+ data->data(), data->size(), net::X509Certificate::FORMAT_AUTO);
+ if (selected_cert_list_.empty()) {
+ ImportExportCleanup();
+ ShowError(
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_CA_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_CERT_PARSE_ERROR));
+ return;
+ }
+
+ scoped_refptr<net::X509Certificate> root_cert =
+ certificate_manager_model_->cert_db()->FindRootInList(
+ selected_cert_list_);
+
+ // TODO(mattm): check here if root_cert is not a CA cert and show error.
+
+ base::Value cert_name(root_cert->subject().GetDisplayName());
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "CertificateEditCaTrustOverlay.showImport", cert_name);
+}
+
+void CertificateManagerHandler::ImportCATrustSelected(
+ const base::ListValue* args) {
+ bool fail = false;
+ bool trust_ssl = false;
+ bool trust_email = false;
+ bool trust_obj_sign = false;
+ fail |= !CallbackArgsToBool(args, 0, &trust_ssl);
+ fail |= !CallbackArgsToBool(args, 1, &trust_email);
+ fail |= !CallbackArgsToBool(args, 2, &trust_obj_sign);
+ if (fail) {
+ LOG(ERROR) << "ImportCATrustSelected args fail";
+ ImportExportCleanup();
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "CertificateEditCaTrustOverlay.dismiss");
+ return;
+ }
+
+ // TODO(mattm): add UI for setting explicit distrust, too.
+ // http://crbug.com/128411
+ net::NSSCertDatabase::ImportCertFailureList not_imported;
+ bool result = certificate_manager_model_->ImportCACerts(
+ selected_cert_list_,
+ trust_ssl * net::NSSCertDatabase::TRUSTED_SSL +
+ trust_email * net::NSSCertDatabase::TRUSTED_EMAIL +
+ trust_obj_sign * net::NSSCertDatabase::TRUSTED_OBJ_SIGN,
+ &not_imported);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "CertificateEditCaTrustOverlay.dismiss");
+ if (!result) {
+ ShowError(
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_CA_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_UNKNOWN_ERROR));
+ } else if (!not_imported.empty()) {
+ ShowImportErrors(
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_CA_IMPORT_ERROR_TITLE),
+ not_imported);
+ }
+ ImportExportCleanup();
+}
+
+void CertificateManagerHandler::Export(const base::ListValue* args) {
+ net::X509Certificate* cert = cert_id_map_->CallbackArgsToCert(args);
+ if (!cert)
+ return;
+ ShowCertExportDialog(web_ui()->GetWebContents(), GetParentWindow(), cert);
+}
+
+void CertificateManagerHandler::Delete(const base::ListValue* args) {
+ net::X509Certificate* cert = cert_id_map_->CallbackArgsToCert(args);
+ if (!cert)
+ return;
+ bool result = certificate_manager_model_->Delete(cert);
+ if (!result) {
+ // TODO(mattm): better error messages?
+ ShowError(
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_DELETE_CERT_ERROR_TITLE),
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_UNKNOWN_ERROR));
+ }
+}
+
+void CertificateManagerHandler::OnCertificateManagerModelCreated(
+ std::unique_ptr<CertificateManagerModel> model) {
+ certificate_manager_model_ = std::move(model);
+ CertificateManagerModelReady();
+}
+
+void CertificateManagerHandler::CertificateManagerModelReady() {
+ base::Value user_db_available_value(
+ certificate_manager_model_->is_user_db_available());
+ base::Value tpm_available_value(
+ certificate_manager_model_->is_tpm_available());
+ web_ui()->CallJavascriptFunctionUnsafe("CertificateManager.onModelReady",
+ user_db_available_value,
+ tpm_available_value);
+ certificate_manager_model_->Refresh();
+}
+
+void CertificateManagerHandler::Populate(const base::ListValue* args) {
+ if (certificate_manager_model_) {
+ // Already have a model, the webui must be re-loading. Just re-run the
+ // webui initialization.
+ CertificateManagerModelReady();
+ return;
+ }
+
+ if (!requested_certificate_manager_model_) {
+ // Request that a model be created.
+ CertificateManagerModel::Create(
+ Profile::FromWebUI(web_ui()),
+ this,
+ base::Bind(&CertificateManagerHandler::OnCertificateManagerModelCreated,
+ weak_ptr_factory_.GetWeakPtr()));
+ requested_certificate_manager_model_ = true;
+ return;
+ }
+
+ // We are already waiting for a CertificateManagerModel to be created, no need
+ // to do anything.
+}
+
+void CertificateManagerHandler::PopulateTree(
+ const std::string& tab_name,
+ net::CertType type,
+ const net::CertificateList& web_trust_certs) {
+ const std::string tree_name = tab_name + "-tree";
+
+ std::unique_ptr<icu::Collator> collator;
+ UErrorCode error = U_ZERO_ERROR;
+ collator.reset(
+ icu::Collator::createInstance(
+ icu::Locale(g_browser_process->GetApplicationLocale().c_str()),
+ error));
+ if (U_FAILURE(error))
+ collator.reset(NULL);
+ DictionaryIdComparator comparator(collator.get());
+ CertificateManagerModel::OrgGroupingMap map;
+
+ certificate_manager_model_->FilterAndBuildOrgGroupingMap(type, &map);
+
+ {
+ std::unique_ptr<base::ListValue> nodes(new base::ListValue);
+ for (CertificateManagerModel::OrgGroupingMap::iterator i = map.begin();
+ i != map.end(); ++i) {
+ // Populate first level (org name).
+ std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+ dict->SetString(kKeyId, OrgNameToId(i->first));
+ dict->SetString(kNameId, i->first);
+
+ // Populate second level (certs).
+ auto subnodes = base::MakeUnique<base::ListValue>();
+ for (net::CertificateList::const_iterator org_cert_it = i->second.begin();
+ org_cert_it != i->second.end(); ++org_cert_it) {
+ std::unique_ptr<base::DictionaryValue> cert_dict(
+ new base::DictionaryValue);
+ net::X509Certificate* cert = org_cert_it->get();
+ cert_dict->SetString(kKeyId, cert_id_map_->CertToId(cert));
+ cert_dict->SetString(kNameId, certificate_manager_model_->GetColumnText(
+ *cert, CertificateManagerModel::COL_SUBJECT_NAME));
+ cert_dict->SetBoolean(
+ kReadOnlyId,
+ certificate_manager_model_->cert_db()->IsReadOnly(cert));
+ // Policy-installed certificates with web trust are trusted.
+ bool policy_trusted =
+ IsPolicyInstalledWithWebTrust(web_trust_certs, cert);
+ cert_dict->SetBoolean(
+ kUntrustedId,
+ !policy_trusted &&
+ certificate_manager_model_->cert_db()->IsUntrusted(cert));
+ cert_dict->SetBoolean(kPolicyTrustedId, policy_trusted);
+ // TODO(hshi): This should be determined by testing for PKCS #11
+ // CKA_EXTRACTABLE attribute. We may need to use the NSS function
+ // PK11_ReadRawAttribute to do that.
+ cert_dict->SetBoolean(
+ kExtractableId,
+ !certificate_manager_model_->IsHardwareBacked(cert));
+ // TODO(mattm): Other columns.
+ subnodes->Append(std::move(cert_dict));
+ }
+ std::sort(subnodes->begin(), subnodes->end(), comparator);
+
+ dict->Set(kSubNodesId, std::move(subnodes));
+ nodes->Append(std::move(dict));
+ }
+ std::sort(nodes->begin(), nodes->end(), comparator);
+
+ base::ListValue args;
+ args.AppendString(tree_name);
+ args.Append(std::move(nodes));
+ web_ui()->CallJavascriptFunctionUnsafe("CertificateManager.onPopulateTree",
+ args);
+ }
+}
+
+void CertificateManagerHandler::ShowError(const std::string& title,
+ const std::string& error) const {
+ auto title_value = base::MakeUnique<base::Value>(title);
+ auto error_value = base::MakeUnique<base::Value>(error);
+ auto ok_title_value =
+ base::MakeUnique<base::Value>(l10n_util::GetStringUTF8(IDS_OK));
+ auto cancel_title_value = base::MakeUnique<base::Value>();
+ auto ok_callback_value = base::MakeUnique<base::Value>();
+ auto cancel_callback_value = base::MakeUnique<base::Value>();
+ std::vector<const base::Value*> args = {
+ title_value.get(), error_value.get(),
+ ok_title_value.get(), cancel_title_value.get(),
+ ok_callback_value.get(), cancel_callback_value.get()};
+ web_ui()->CallJavascriptFunctionUnsafe("AlertOverlay.show", args);
+}
+
+void CertificateManagerHandler::ShowImportErrors(
+ const std::string& title,
+ const net::NSSCertDatabase::ImportCertFailureList& not_imported) const {
+ std::string error;
+ if (selected_cert_list_.size() == 1)
+ error = l10n_util::GetStringUTF8(
+ IDS_CERT_MANAGER_IMPORT_SINGLE_NOT_IMPORTED);
+ else if (not_imported.size() == selected_cert_list_.size())
+ error = l10n_util::GetStringUTF8(IDS_CERT_MANAGER_IMPORT_ALL_NOT_IMPORTED);
+ else
+ error = l10n_util::GetStringUTF8(IDS_CERT_MANAGER_IMPORT_SOME_NOT_IMPORTED);
+
+ base::ListValue cert_error_list;
+ for (size_t i = 0; i < not_imported.size(); ++i) {
+ const net::NSSCertDatabase::ImportCertFailure& failure = not_imported[i];
+ std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+ dict->SetString(kNameId, failure.certificate->subject().GetDisplayName());
+ dict->SetString(kErrorId, NetErrorToString(failure.net_error));
+ cert_error_list.Append(std::move(dict));
+ }
+
+ base::Value title_value(title);
+ base::Value error_value(error);
+ web_ui()->CallJavascriptFunctionUnsafe("CertificateImportErrorOverlay.show",
+ title_value, error_value,
+ cert_error_list);
+}
+
+gfx::NativeWindow CertificateManagerHandler::GetParentWindow() const {
+ return web_ui()->GetWebContents()->GetTopLevelNativeWindow();
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/certificate_manager_handler.h b/chromium/chrome/browser/ui/webui/options/certificate_manager_handler.h
new file mode 100644
index 00000000000..392790bc1ba
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/certificate_manager_handler.h
@@ -0,0 +1,194 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CERTIFICATE_MANAGER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CERTIFICATE_MANAGER_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "chrome/browser/certificate_manager_model.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "net/cert/nss_cert_database.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+
+namespace options {
+
+class CertIdMap;
+class FileAccessProvider;
+
+class CertificateManagerHandler
+ : public OptionsPageUIHandler,
+ public CertificateManagerModel::Observer,
+ public ui::SelectFileDialog::Listener {
+ public:
+ explicit CertificateManagerHandler(bool show_certs_in_modal_dialog);
+ ~CertificateManagerHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void RegisterMessages() override;
+
+ // CertificateManagerModel::Observer implementation.
+ void CertificatesRefreshed() override;
+
+ // SelectFileDialog::Listener implementation.
+ void FileSelected(const base::FilePath& path,
+ int index,
+ void* params) override;
+ void FileSelectionCanceled(void* params) override;
+
+ private:
+ // View certificate.
+ void View(const base::ListValue* args);
+
+ // Edit server certificate trust values.
+ void EditServer(const base::ListValue* args);
+
+ // Edit certificate authority trust values. The sequence goes like:
+ // 1. user clicks edit button -> CertificateEditCaTrustOverlay.show ->
+ // GetCATrust -> CertificateEditCaTrustOverlay.populateTrust
+ // 2. user clicks ok -> EditCATrust -> CertificateEditCaTrustOverlay.dismiss
+ void GetCATrust(const base::ListValue* args);
+ void EditCATrust(const base::ListValue* args);
+
+ // Cleanup state stored during import or export process.
+ void CancelImportExportProcess(const base::ListValue* args);
+ void ImportExportCleanup();
+
+ // Export to PKCS #12 file. The sequence goes like:
+ // 1a. user click on export button -> ExportPersonal -> launches file
+ // selector
+ // 1b. user click on export all button -> ExportAllPersonal -> launches file
+ // selector
+ // 2. user selects file -> ExportPersonalFileSelected -> launches password
+ // dialog
+ // 3. user enters password -> ExportPersonalPasswordSelected -> unlock slots
+ // 4. slots unlocked -> ExportPersonalSlotsUnlocked -> exports to memory
+ // buffer -> starts async write operation
+ // 5. write finishes (or fails) -> ExportPersonalFileWritten
+ void ExportPersonal(const base::ListValue* args);
+ void ExportAllPersonal(const base::ListValue* args);
+ void ExportPersonalFileSelected(const base::FilePath& path);
+ void ExportPersonalPasswordSelected(const base::ListValue* args);
+ void ExportPersonalSlotsUnlocked();
+ void ExportPersonalFileWritten(const int* write_errno,
+ const int* bytes_written);
+
+ // Import from PKCS #12 or cert file. The sequence goes like:
+ // 1. user click on import button -> StartImportPersonal -> launches file
+ // selector
+ // 2. user selects file -> ImportPersonalFileSelected -> starts async
+ // read operation
+ // 3. read operation completes -> ImportPersonalFileRead ->
+ // If file is PFX -> launches password dialog, goto step 4
+ // Else -> import as certificate, goto step 6
+ // 4. user enters password -> ImportPersonalPasswordSelected -> unlock slot
+ // 5. slot unlocked -> ImportPersonalSlotUnlocked attempts to
+ // import with previously entered password
+ // 6a. if import succeeds -> ImportExportCleanup
+ // 6b. if import fails -> show error, ImportExportCleanup
+ // TODO(mattm): allow retrying with different password
+ void StartImportPersonal(const base::ListValue* args);
+ void ImportPersonalFileSelected(const base::FilePath& path);
+ void ImportPersonalFileRead(const int* read_errno, const std::string* data);
+ void ImportPersonalPasswordSelected(const base::ListValue* args);
+ void ImportPersonalSlotUnlocked();
+
+ // Import Server certificates from file. Sequence goes like:
+ // 1. user clicks on import button -> ImportServer -> launches file selector
+ // 2. user selects file -> ImportServerFileSelected -> starts async read
+ // 3. read completes -> ImportServerFileRead -> parse certs -> attempt import
+ // 4a. if import succeeds -> ImportExportCleanup
+ // 4b. if import fails -> show error, ImportExportCleanup
+ void ImportServer(const base::ListValue* args);
+ void ImportServerFileSelected(const base::FilePath& path);
+ void ImportServerFileRead(const int* read_errno, const std::string* data);
+
+ // Import Certificate Authorities from file. Sequence goes like:
+ // 1. user clicks on import button -> ImportCA -> launches file selector
+ // 2. user selects file -> ImportCAFileSelected -> starts async read
+ // 3. read completes -> ImportCAFileRead -> parse certs ->
+ // CertificateEditCaTrustOverlay.showImport
+ // 4. user clicks ok -> ImportCATrustSelected -> attempt import
+ // 5a. if import succeeds -> ImportExportCleanup
+ // 5b. if import fails -> show error, ImportExportCleanup
+ void ImportCA(const base::ListValue* args);
+ void ImportCAFileSelected(const base::FilePath& path);
+ void ImportCAFileRead(const int* read_errno, const std::string* data);
+ void ImportCATrustSelected(const base::ListValue* args);
+
+ // Export a certificate.
+ void Export(const base::ListValue* args);
+
+ // Delete certificate and private key (if any).
+ void Delete(const base::ListValue* args);
+
+ // Model initialization methods.
+ void OnCertificateManagerModelCreated(
+ std::unique_ptr<CertificateManagerModel> model);
+ void CertificateManagerModelReady();
+
+ // Populate the trees in all the tabs.
+ void Populate(const base::ListValue* args);
+
+ // Populate the given tab's tree.
+ void PopulateTree(const std::string& tab_name,
+ net::CertType type,
+ const net::CertificateList& web_trust_certs);
+
+ // Populate the tree after retrieving the list of policy-installed
+ // web-trusted certificates.
+ void OnPolicyWebTrustCertsRetrieved(
+ const net::CertificateList& web_trust_certs);
+
+ // Display a WebUI error message box.
+ void ShowError(const std::string& title, const std::string& error) const;
+
+ // Display a WebUI error message box for import failures.
+ // Depends on |selected_cert_list_| being set to the imports that we
+ // attempted to import.
+ void ShowImportErrors(
+ const std::string& title,
+ const net::NSSCertDatabase::ImportCertFailureList& not_imported) const;
+
+ gfx::NativeWindow GetParentWindow() const;
+
+ // True if certificate viewer should be shown in modal instead of constrianed
+ // dialog.
+ bool show_certs_in_modal_dialog_;
+ // The Certificates Manager model
+ bool requested_certificate_manager_model_;
+ std::unique_ptr<CertificateManagerModel> certificate_manager_model_;
+
+ // For multi-step import or export processes, we need to store the path,
+ // password, etc the user chose while we wait for them to enter a password,
+ // wait for file to be read, etc.
+ base::FilePath file_path_;
+ base::string16 password_;
+ bool use_hardware_backed_;
+ std::string file_data_;
+ net::CertificateList selected_cert_list_;
+ scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
+ crypto::ScopedPK11Slot slot_;
+
+ // Used in reading and writing certificate files.
+ base::CancelableTaskTracker tracker_;
+ scoped_refptr<FileAccessProvider> file_access_provider_;
+
+ std::unique_ptr<CertIdMap> cert_id_map_;
+
+ base::WeakPtrFactory<CertificateManagerHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CertificateManagerHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CERTIFICATE_MANAGER_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/DEPS b/chromium/chrome/browser/ui/webui/options/chromeos/DEPS
new file mode 100644
index 00000000000..273d26becd6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+components/onc",
+]
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/OWNERS b/chromium/chrome/browser/ui/webui/options/chromeos/OWNERS
new file mode 100644
index 00000000000..fff9165e69d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/OWNERS
@@ -0,0 +1,6 @@
+# This UI is deprecated. See chrome/browser/ui/webui/settings/chromeos/ instead.
+achuith@chromium.org
+stevenjb@chromium.org
+
+# Display options.
+mukai@chromium.org
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/accounts_options_browsertest.cc b/chromium/chrome/browser/ui/webui/options/chromeos/accounts_options_browsertest.cc
new file mode 100644
index 00000000000..34ea81b1ced
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/accounts_options_browsertest.cc
@@ -0,0 +1,166 @@
+// 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 <stddef.h>
+
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/login/login_manager_test.h"
+#include "chrome/browser/chromeos/login/startup_utils.h"
+#include "chrome/browser/chromeos/login/ui/user_adding_screen.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "chromeos/settings/cros_settings_names.h"
+#include "components/prefs/pref_service.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+
+namespace chromeos {
+
+namespace {
+
+const char* kTestUsers[] = { "test-user1@gmail.com", "test-user2@gmail.com" };
+
+} // namespace
+
+class AccountsOptionsTest : public LoginManagerTest {
+ public:
+ AccountsOptionsTest()
+ : LoginManagerTest(false),
+ stub_settings_provider_(base::MakeUnique<StubCrosSettingsProvider>()),
+ stub_settings_provider_ptr_(static_cast<StubCrosSettingsProvider*>(
+ stub_settings_provider_.get())) {
+ stub_settings_provider_->Set(kDeviceOwner, base::Value(kTestUsers[0]));
+ for (size_t i = 0; i < arraysize(kTestUsers); ++i) {
+ test_users_.push_back(AccountId::FromUserEmail(kTestUsers[i]));
+ }
+ }
+
+ ~AccountsOptionsTest() override {}
+
+ void SetUpOnMainThread() override {
+ LoginManagerTest::SetUpOnMainThread();
+ CrosSettings* settings = CrosSettings::Get();
+ CrosSettingsProvider* device_settings_provider =
+ settings->GetProvider(kDeviceOwner);
+ device_settings_provider_ =
+ settings->RemoveSettingsProvider(device_settings_provider);
+ settings->AddSettingsProvider(std::move(stub_settings_provider_));
+
+ // Notify ChromeUserManager of ownership change.
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_OWNERSHIP_STATUS_CHANGED,
+ content::Source<AccountsOptionsTest>(this),
+ content::NotificationService::NoDetails());
+ }
+
+ void TearDownOnMainThread() override {
+ CrosSettings* settings = CrosSettings::Get();
+ stub_settings_provider_ =
+ settings->RemoveSettingsProvider(stub_settings_provider_ptr_);
+ settings->AddSettingsProvider(std::move(device_settings_provider_));
+ LoginManagerTest::TearDownOnMainThread();
+ }
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ LoginManagerTest::SetUpCommandLine(command_line);
+ }
+
+ protected:
+ void CheckAccountsUI(const user_manager::User* user, bool is_owner) {
+ Profile* profile = ProfileHelper::Get()->GetProfileByUserUnsafe(user);
+
+ ui_test_utils::BrowserAddedObserver observer;
+ Browser* browser = CreateBrowser(profile);
+ observer.WaitForSingleNewBrowser();
+
+ ui_test_utils::NavigateToURL(browser,
+ GURL("chrome://settings-frame/accounts"));
+ content::WebContents* contents =
+ browser->tab_strip_model()->GetActiveWebContents();
+
+ bool warning_visible;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+ contents,
+ "var e = document.getElementById('ownerOnlyWarning');"
+ "var visible = e.offsetWidth > 0 && e.offsetHeight > 0;"
+ "window.domAutomationController.send(visible);",
+ &warning_visible));
+ EXPECT_EQ(is_owner, !warning_visible);
+
+ bool guest_option_enabled;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+ contents,
+ "var e = document.getElementById('allowBwsiCheck');"
+ "window.domAutomationController.send(!e.disabled);",
+ &guest_option_enabled));
+ EXPECT_EQ(is_owner, guest_option_enabled);
+
+ bool supervised_users_enabled;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+ contents,
+ "var e = document.getElementById('allowSupervisedCheck');"
+ "window.domAutomationController.send(!e.disabled);",
+ &supervised_users_enabled));
+ ASSERT_EQ(is_owner, supervised_users_enabled);
+
+ bool user_pods_enabled;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+ contents,
+ "var e = document.getElementById('showUserNamesCheck');"
+ "window.domAutomationController.send(!e.disabled);",
+ &user_pods_enabled));
+ EXPECT_EQ(is_owner, user_pods_enabled);
+
+ bool whitelist_enabled;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+ contents,
+ "var e = document.getElementById('useWhitelistCheck');"
+ "window.domAutomationController.send(!e.disabled);",
+ &whitelist_enabled));
+ EXPECT_EQ(is_owner, whitelist_enabled);
+ }
+
+ std::unique_ptr<CrosSettingsProvider> stub_settings_provider_;
+ StubCrosSettingsProvider* stub_settings_provider_ptr_;
+ std::unique_ptr<CrosSettingsProvider> device_settings_provider_;
+ std::vector<AccountId> test_users_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AccountsOptionsTest);
+};
+
+IN_PROC_BROWSER_TEST_F(AccountsOptionsTest, PRE_MultiProfilesAccountsOptions) {
+ RegisterUser(test_users_[0].GetUserEmail());
+ RegisterUser(test_users_[1].GetUserEmail());
+ StartupUtils::MarkOobeCompleted();
+}
+
+IN_PROC_BROWSER_TEST_F(AccountsOptionsTest, MultiProfilesAccountsOptions) {
+ LoginUser(test_users_[0].GetUserEmail());
+ UserAddingScreen::Get()->Start();
+ content::RunAllPendingInMessageLoop();
+ AddUser(test_users_[1].GetUserEmail());
+
+ user_manager::UserManager* manager = user_manager::UserManager::Get();
+ ASSERT_EQ(2u, manager->GetLoggedInUsers().size());
+
+ CheckAccountsUI(manager->FindUser(test_users_[0]), true /* is_owner */);
+ CheckAccountsUI(manager->FindUser(test_users_[1]), false /* is_owner */);
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/accounts_options_browsertest.js b/chromium/chrome/browser/ui/webui/options/chromeos/accounts_options_browsertest.js
new file mode 100644
index 00000000000..381aead5130
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/accounts_options_browsertest.js
@@ -0,0 +1,42 @@
+// 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.
+
+GEN_INCLUDE(['../options_browsertest_base.js']);
+
+function AccountsOptionsWebUITest() {}
+
+AccountsOptionsWebUITest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /**
+ * Browse to accounts options.
+ */
+ browsePreload: 'chrome://settings-frame/accounts',
+};
+
+function createEnterKeyboardEvent(type) {
+ return new KeyboardEvent(type, {
+ 'bubbles': true,
+ 'cancelable': true,
+ 'key': 'Enter'
+ });
+}
+
+TEST_F('AccountsOptionsWebUITest', 'testNoCloseOnEnter', function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ var inputField = $('userNameEdit');
+ var accountsOptionsPage = AccountsOptions.getInstance();
+
+ // Overlay is visible.
+ assertTrue(accountsOptionsPage.visible);
+
+ // Simulate pressing the enter key in the edit field.
+ inputField.dispatchEvent(createEnterKeyboardEvent('keydown'));
+ inputField.dispatchEvent(createEnterKeyboardEvent('keypress'));
+ inputField.dispatchEvent(createEnterKeyboardEvent('keyup'));
+
+ // Verify the overlay is still visible.
+ assertTrue(accountsOptionsPage.visible);
+});
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/accounts_options_handler.cc b/chromium/chrome/browser/ui/webui/options/chromeos/accounts_options_handler.cc
new file mode 100644
index 00000000000..dfca5fd225a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/accounts_options_handler.cc
@@ -0,0 +1,182 @@
+// 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/ui/webui/options/chromeos/accounts_options_handler.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_metrics.h"
+#include "chrome/browser/ui/webui/chromeos/ui_account_tweaks.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/settings/cros_settings_names.h"
+#include "components/prefs/pref_service.h"
+#include "components/user_manager/user_manager.h"
+#include "components/user_manager/user_names.h"
+#include "content/public/browser/web_ui.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace chromeos {
+
+namespace {
+
+// Adds specified user to the whitelist. Returns false if that user is already
+// in the whitelist.
+bool WhitelistUser(OwnerSettingsServiceChromeOS* service,
+ const std::string& username) {
+ if (CrosSettings::Get()->FindEmailInList(kAccountsPrefUsers, username, NULL))
+ return false;
+ if (service) {
+ base::Value username_value(username);
+ service->AppendToList(kAccountsPrefUsers, username_value);
+ }
+ return true;
+}
+
+} // namespace
+
+namespace options {
+
+AccountsOptionsHandler::AccountsOptionsHandler() {
+}
+
+AccountsOptionsHandler::~AccountsOptionsHandler() {
+}
+
+void AccountsOptionsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("whitelistUser",
+ base::Bind(&AccountsOptionsHandler::HandleWhitelistUser,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("unwhitelistUser",
+ base::Bind(&AccountsOptionsHandler::HandleUnwhitelistUser,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("updateWhitelist",
+ base::Bind(&AccountsOptionsHandler::HandleUpdateWhitelist,
+ base::Unretained(this)));
+}
+
+void AccountsOptionsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ RegisterTitle(localized_strings, "accountsPage",
+ IDS_OPTIONS_ACCOUNTS_TAB_LABEL);
+
+ localized_strings->SetString("allow_BWSI", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_ACCOUNTS_ALLOW_BWSI_DESCRIPTION));
+ localized_strings->SetString(
+ "allow_supervised_users",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_ACCOUNTS_ENABLE_SUPERVISED_USERS));
+ localized_strings->SetString("use_whitelist", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_ACCOUNTS_USE_WHITELIST_DESCRIPTION));
+ localized_strings->SetString("show_user_on_signin", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_ACCOUNTS_SHOW_USER_NAMES_ON_SINGIN_DESCRIPTION));
+ localized_strings->SetString("username_edit_hint", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_ACCOUNTS_USERNAME_EDIT_HINT));
+ localized_strings->SetString("username_format", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_ACCOUNTS_USERNAME_FORMAT));
+ localized_strings->SetString("add_users", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_ACCOUNTS_ADD_USERS));
+ localized_strings->SetString("owner_only", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_ACCOUNTS_OWNER_ONLY));
+
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ localized_strings->SetBoolean("whitelist_is_managed",
+ connector->IsEnterpriseManaged());
+
+ AddAccountUITweaksLocalizedValues(localized_strings,
+ Profile::FromWebUI(web_ui()));
+}
+
+void AccountsOptionsHandler::HandleWhitelistUser(const base::ListValue* args) {
+ std::string typed_email;
+ std::string name;
+ if (!args->GetString(0, &typed_email) ||
+ !args->GetString(1, &name)) {
+ return;
+ }
+
+ if (OwnerSettingsServiceChromeOS* service =
+ OwnerSettingsServiceChromeOS::FromWebUI(web_ui())) {
+ WhitelistUser(service, gaia::CanonicalizeEmail(typed_email));
+ }
+}
+
+void AccountsOptionsHandler::HandleUnwhitelistUser(
+ const base::ListValue* args) {
+ std::string email;
+ if (!args->GetString(0, &email)) {
+ return;
+ }
+
+ ProfileMetrics::LogProfileDeleteUser(ProfileMetrics::DELETE_PROFILE_SETTINGS);
+
+ base::Value canonical_email(gaia::CanonicalizeEmail(email));
+ if (OwnerSettingsServiceChromeOS* service =
+ OwnerSettingsServiceChromeOS::FromWebUI(web_ui())) {
+ service->RemoveFromList(kAccountsPrefUsers, canonical_email);
+ }
+ user_manager::UserManager::Get()->RemoveUser(AccountId::FromUserEmail(email),
+ nullptr);
+}
+
+void AccountsOptionsHandler::HandleUpdateWhitelist(
+ const base::ListValue* args) {
+ DCHECK(args && args->empty());
+
+ // Creates one list to set. This is needed because user white list update is
+ // asynchronous and sequential. Before previous write comes back, cached list
+ // is stale and should not be used for appending. See http://crbug.com/127215
+ std::unique_ptr<base::ListValue> new_list;
+
+ CrosSettings* cros_settings = CrosSettings::Get();
+ const base::ListValue* existing = NULL;
+ if (cros_settings->GetList(kAccountsPrefUsers, &existing) && existing)
+ new_list.reset(existing->DeepCopy());
+ else
+ new_list.reset(new base::ListValue);
+
+ // Remove all supervised users. On the next step only supervised users present
+ // on the device will be added back. Thus not present SU are removed.
+ // No need to remove usual users as they can simply login back.
+ for (size_t i = 0; i < new_list->GetSize(); ++i) {
+ std::string whitelisted_user;
+ new_list->GetString(i, &whitelisted_user);
+ if (gaia::ExtractDomainName(whitelisted_user) ==
+ user_manager::kSupervisedUserDomain) {
+ new_list->Remove(i, NULL);
+ --i;
+ }
+ }
+
+ const user_manager::UserList& users =
+ user_manager::UserManager::Get()->GetUsers();
+ for (const auto* user : users) {
+ new_list->AppendIfNotPresent(
+ base::MakeUnique<base::Value>(user->GetAccountId().GetUserEmail()));
+ }
+
+ if (OwnerSettingsServiceChromeOS* service =
+ OwnerSettingsServiceChromeOS::FromWebUI(web_ui())) {
+ service->Set(kAccountsPrefUsers, *new_list.get());
+ }
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/accounts_options_handler.h b/chromium/chrome/browser/ui/webui/options/chromeos/accounts_options_handler.h
new file mode 100644
index 00000000000..83212bbcc8d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/accounts_options_handler.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_ACCOUNTS_OPTIONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_ACCOUNTS_OPTIONS_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+namespace chromeos {
+namespace options {
+
+// ChromeOS accounts options page handler.
+class AccountsOptionsHandler : public ::options::OptionsPageUIHandler {
+ public:
+ AccountsOptionsHandler();
+ ~AccountsOptionsHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+
+ private:
+ // Javascript callbacks to update whitelist/unwhitelist user.
+ void HandleWhitelistUser(const base::ListValue* args);
+ void HandleUnwhitelistUser(const base::ListValue* args);
+
+ // Javascript callback to update the white list: auto add existing users,
+ // remove not present supervised users.
+ void HandleUpdateWhitelist(const base::ListValue* args);
+
+ DISALLOW_COPY_AND_ASSIGN(AccountsOptionsHandler);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_ACCOUNTS_OPTIONS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/bluetooth_options_browsertest.js b/chromium/chrome/browser/ui/webui/options/chromeos/bluetooth_options_browsertest.js
new file mode 100644
index 00000000000..38e4c9bc063
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/bluetooth_options_browsertest.js
@@ -0,0 +1,440 @@
+// 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.
+
+GEN('#if defined(OS_CHROMEOS)');
+
+GEN_INCLUDE(['../options_browsertest_base.js']);
+
+function BluetoothWebUITestAsync() {}
+
+BluetoothWebUITestAsync.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /** @override */
+ isAsync: true,
+
+ /**
+ * Start tests from the main-settings page.
+ */
+ browsePreload: 'chrome://settings-frame/',
+
+ // These entries match the fake entries in FakeBluetoothDeviceClient.
+ fakePairedDevice: {
+ address: '00:11:22:33:44:55',
+ connectable: true,
+ connected: false,
+ name: 'Fake Device (name)',
+ paired: true
+ },
+
+ fakePairedDevice2: {
+ address: '20:7D:74:00:00:04',
+ connectable: false,
+ connected: false,
+ name: 'Paired Unconnectable Device (name)',
+ paired: true
+ },
+
+ fakeUnpairedDevice: {
+ address: '28:CF:DA:00:00:00',
+ connectable: true,
+ connected: false,
+ name: 'Bluetooth 2.0 Mouse',
+ paired: false
+ },
+
+ fakeUnpairedDevice2: {
+ address: '00:24:BE:00:00:00',
+ connectable: true,
+ connected: false,
+ name: 'PIN Device',
+ paired: false
+ },
+
+ /** @override */
+ setUp: function() {
+ OptionsBrowsertestBase.prototype.setUp.call(this);
+
+ var unsupportedAriaAttributeSelectors = [
+ '#bluetooth-paired-devices-list',
+ '#bluetooth-unpaired-devices-list',
+ ];
+
+ // Enable when failure is resolved.
+ // AX_ARIA_10: http://crbug.com/570564
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'unsupportedAriaAttribute',
+ unsupportedAriaAttributeSelectors);
+ },
+
+ /**
+ * Retrieves the list item associated with a Bluetooth device.
+ * @param {!Element} listElement Element containing a list of devices.
+ * @param {string} deviceName The name of the device.
+ * @return {Element|undefined} List item matching the device name.
+ */
+ getElementForDevice: function(listElement, deviceName) {
+ var items = listElement.querySelectorAll('.bluetooth-device');
+ for (var i = 0; i < items.length; i++) {
+ var candidate = items[i];
+ var name = candidate.data.name;
+ if (name == deviceName)
+ return candidate;
+ }
+ return undefined;
+ },
+
+ /**
+ * Selects a bluetooth device from the list with the matching address.
+ * @param {!Element} listElement A list of Bluetooth devices.
+ * @param {string} address Device address.
+ */
+ selectDevice: function(listElement, address) {
+ listElement.setSelectedDevice_(address);
+ cr.dispatchSimpleEvent(listElement, 'change');
+ },
+
+ /**
+ * Fake input of a pincode or passkey.
+ * @param {!Element} element Text input field.
+ * @param {string} text New value for the input field.
+ */
+ fakeInput: function(element, text) {
+ element.value = text;
+ cr.dispatchSimpleEvent(element, 'input');
+ },
+};
+
+TEST_F('BluetoothWebUITestAsync', 'testEnableBluetooth', function() {
+ assertEquals(this.browsePreload, document.location.href);
+ expectFalse($('enable-bluetooth').checked);
+ expectTrue($('bluetooth-paired-devices-list').parentNode.hidden);
+
+ $('enable-bluetooth').click();
+
+ // The UI may not be updated until all callbacks have been handled, so
+ // send a new request that will get processed after any currently pending
+ // callbacks.
+ chrome.bluetooth.getAdapterState(function(state) {
+ expectTrue(state.powered);
+ expectFalse($('bluetooth-paired-devices-list').parentNode.hidden);
+ testDone();
+ }.bind(this));
+});
+
+// TODO(crbug.com/603499) Test is flaky.
+TEST_F('BluetoothWebUITestAsync', 'DISABLED_testAddDevice', function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ // Enable bluetooth.
+ $('enable-bluetooth').click();
+
+ // Wait for the UI to process any pending messages.
+ window.setTimeout(function() {
+ // Wait for fake bluetooth impl to send any updates.
+ chrome.bluetooth.getAdapterState(function(state) {
+ var pairedDeviceList = $('bluetooth-paired-devices-list');
+ var unpairedDeviceList = $('bluetooth-unpaired-devices-list');
+
+ // Verify that devices are in the correct list.
+ var index = pairedDeviceList.find(this.fakePairedDevice.address);
+ expectEquals(1, index);
+ index = pairedDeviceList.find(this.fakePairedDevice2.address);
+ expectEquals(0, index);
+ index = pairedDeviceList.find(this.fakeUnpairedDevice.address);
+ expectEquals(undefined, index);
+ expectTrue(!!this.getElementForDevice(pairedDeviceList,
+ this.fakePairedDevice.name));
+ expectFalse(!!this.getElementForDevice(unpairedDeviceList,
+ this.fakePairedDevice.name));
+
+ // Test clicking on the 'Add a device' button. This should send a
+ // startDiscovering request.
+ $('bluetooth-add-device').click();
+ expectFalse($('bluetooth-options').hidden);
+
+ // Wait for fake bluetooth impl to send any updates.
+ chrome.bluetooth.getAdapterState(function(state) {
+ expectTrue(state.discovering);
+ expectFalse(unpairedDeviceList.parentNode.hidden);
+
+ index = unpairedDeviceList.find(this.fakeUnpairedDevice.address);
+ expectEquals(0, index);
+
+ var connectButton = $('bluetooth-add-device-apply-button');
+ expectTrue(connectButton.disabled);
+ expectFalse($('bluetooth-add-device-cancel-button').disabled);
+
+ // Test selecting an element and clicking on the connect button.
+ this.selectDevice(unpairedDeviceList, this.fakeUnpairedDevice.address);
+ expectFalse(connectButton.disabled);
+ connectButton.click();
+
+ // Wait for fake bluetooth impl to send any updates.
+ chrome.bluetooth.getAdapterState(function(state) {
+ // Verify that the pairing UI is shown.
+ expectFalse($('bluetooth-pairing').hidden);
+ testDone();
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+});
+
+TEST_F('BluetoothWebUITestAsync', 'testDevicePairing', function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ // Enable bluetooth.
+ $('enable-bluetooth').click();
+
+ // Wait for the UI to process any pending messages.
+ window.setTimeout(function() {
+ // Wait for fake bluetooth impl to send any updates.
+ chrome.bluetooth.getAdapterState(function(state) {
+ var pairedDeviceList = $('bluetooth-paired-devices-list');
+ var unpairedDeviceList = $('bluetooth-unpaired-devices-list');
+
+ $('bluetooth-add-device').click();
+
+ // Wait for fake bluetooth impl to send any updates.
+ chrome.bluetooth.getAdapterState(function(state) {
+ expectFalse(unpairedDeviceList.parentNode.hidden);
+
+ // Test selecting an element and clicking on the connect button.
+ var index = unpairedDeviceList.find(this.fakeUnpairedDevice2.address);
+ expectNotEquals(undefined, index);
+ this.selectDevice(unpairedDeviceList, this.fakeUnpairedDevice2.address);
+ var connectButton = $('bluetooth-add-device-apply-button');
+ expectFalse(connectButton.disabled);
+ connectButton.click();
+
+ // Wait for fake bluetooth impl to send any updates.
+ chrome.bluetooth.getAdapterState(function(state) {
+ // Verify that the pairing UI is shown.
+ expectFalse($('bluetooth-pairing').hidden);
+ expectTrue($('bluetooth-pairing-passkey-display').hidden);
+ expectTrue($('bluetooth-pairing-passkey-entry').hidden);
+ expectFalse($('bluetooth-pairing-pincode-entry').hidden);
+
+ var pincode = '123456';
+ this.fakeInput($('bluetooth-pincode'), pincode);
+ $('bluetooth-pair-device-connect-button').click();
+
+ // Wait for fake bluetooth impl to send any updates.
+ chrome.bluetooth.getAdapterState(function(state) {
+ expectTrue($('bluetooth-pairing-pincode-entry').hidden);
+ testDone();
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+});
+
+// TODO(crbug.com/608126) Test is flaky.
+TEST_F('BluetoothWebUITestAsync', 'DISABLED_testConnect', function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ // Enable bluetooth.
+ $('enable-bluetooth').click();
+
+ // Wait for the UI to process any pending messages.
+ window.setTimeout(function() {
+ // Wait for fake bluetooth impl to send any updates.
+ chrome.bluetooth.getAdapterState(function(state) {
+ var pairedDeviceList = $('bluetooth-paired-devices-list');
+ var element = this.getElementForDevice(
+ pairedDeviceList, this.fakePairedDevice.name);
+ assertTrue(!!element, this.fakePairedDevice.name);
+ expectFalse(!!element.getAttribute('connected'));
+
+ var connectButton = $('bluetooth-reconnect-device');
+ expectTrue(connectButton.disabled);
+
+ // Simulate connecting to a previously paired device.
+ this.selectDevice(pairedDeviceList, this.fakePairedDevice.address);
+ expectFalse(connectButton.disabled);
+ connectButton.click();
+
+ // Call bluetooth.getAdapterState to ensure that all state has been
+ // updated.
+ chrome.bluetooth.getAdapterState(function(state) {
+ element = this.getElementForDevice(
+ pairedDeviceList, this.fakePairedDevice.name);
+ expectTrue(!!element.getAttribute('connected'));
+ var deleteButton = element.querySelector('.row-delete-button');
+ expectTrue(!!deleteButton);
+ testDone();
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+});
+
+TEST_F('BluetoothWebUITestAsync', 'testDisconnect', function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ // Enable bluetooth.
+ $('enable-bluetooth').click();
+
+ // Wait for the UI to process any pending messages.
+ window.setTimeout(function() {
+ // Wait for fake bluetooth impl to send any updates.
+ chrome.bluetooth.getAdapterState(function(state) {
+ var pairedDeviceList = $('bluetooth-paired-devices-list');
+
+ // First connect to the device so that the fake implementation state is
+ // connected.
+ chrome.bluetoothPrivate.connect(
+ this.fakePairedDevice.address, function(result) {
+ assertEquals(
+ chrome.bluetoothPrivate.ConnectResultType.SUCCESS, result);
+
+ var element = this.getElementForDevice(
+ pairedDeviceList, this.fakePairedDevice.name);
+ assertTrue(!!element, this.fakePairedDevice.name);
+ expectTrue(!!element.getAttribute('connected'));
+
+ // Simulate disconnecting from a connected device.
+ var button = element.querySelector('.row-delete-button');
+ button.click();
+
+ // Wait for fake bluetooth impl to send any updates.
+ chrome.bluetooth.getAdapterState(function(state) {
+ element = this.getElementForDevice(
+ pairedDeviceList, this.fakePairedDevice.name);
+ expectFalse(!!element.getAttribute('connected'));
+ button = element.querySelector('.row-delete-button');
+ expectTrue(!!button);
+ testDone();
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+});
+
+// TODO(crbug.com/605090): Disabled because of flakiness.
+TEST_F('BluetoothWebUITestAsync', 'DISABLED_testForget', function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ // Enable bluetooth.
+ $('enable-bluetooth').click();
+
+ // Wait for the UI to process any pending messages.
+ window.setTimeout(function() {
+ // Wait for fake bluetooth impl to send any updates.
+ chrome.bluetooth.getAdapterState(function(state) {
+ var pairedDeviceList = $('bluetooth-paired-devices-list');
+
+ var element = this.getElementForDevice(pairedDeviceList,
+ this.fakePairedDevice.name);
+ var button = element.querySelector('.row-delete-button');
+ button.click();
+
+ // Wait for fake bluetooth impl to send any updates.
+ chrome.bluetooth.getAdapterState(function(state) {
+ expectFalse(!!this.getElementForDevice(pairedDeviceList,
+ this.fakePairedDevice.name));
+ testDone();
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+});
+
+
+TEST_F('BluetoothWebUITestAsync', 'testMaliciousInput', function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ var maliciousStrings = [
+ '<SCRIPT>alert(1)</SCRIPT>',
+ '>\'>\\"><SCRIPT>alert(1)</SCRIPT>',
+ '<IMG SRC=\\"javascript:alert(1)\\">',
+ '<A HREF=\\"data:text/html;base64,' +
+ 'PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pgo=\\">..</A>',
+ '<div>',
+ '<textarea>',
+ '<style>',
+ '[0xC0][0xBC]SCRIPT[0xC0][0xBE]alert(1)[0xC0][0xBC]/SCRIPT[0xC0][0xBE]',
+ '+ADw-SCRIPT+AD4-alert(1)+ADw-/SCRIPT+AD4-',
+ '&#<script>alert(1)</script>;',
+ '<!-- Hello -- world > <SCRIPT>alert(1)</SCRIPT> -->',
+ '<!<!-- Hello world > <SCRIPT>alert(1)</SCRIPT> -->',
+ '\x3CSCRIPT\x3Ealert(1)\x3C/SCRIPT\x3E',
+ '<IMG SRC=\\"j[0x00]avascript:alert(1)\\">',
+ '<BASE HREF=\\"javascript:1;/**/\\"><IMG SRC=\\"alert(1)\\">',
+ 'javascript:alert(1);',
+ ' xss_injection=\\"\\" ',
+ '\\" xss_injection=\\"',
+ '\' xss_injection=\'',
+ '<!--',
+ '\'',
+ '\\"'
+ ];
+
+ var fakeEvent = {
+ device: {
+ address: '28:CF:DA:00:00:00',
+ connectable: true,
+ connected: false,
+ name: 'Bluetooth 2.0 Mouse',
+ paired: false
+ },
+ pairing: 'bluetoothStartConnecting'
+ };
+
+ var nodeCount = function(node) {
+ if (node.getAttribute)
+ assertFalse(!!node.getAttribute('xss_injection'));
+ var length = node.childNodes.length;
+ var tally = length;
+ for (var i = 0; i < length; i++) {
+ tally += nodeCount(node.childNodes[i]);
+ }
+ return tally;
+ };
+
+ // Enable bluetooth.
+ $('enable-bluetooth').click();
+
+ // Wait for the UI to process any pending messages.
+ window.setTimeout(function() {
+ // Wait for fake bluetooth impl to send any updates.
+ chrome.bluetooth.getAdapterState(function(state) {
+ var unpairedDeviceList = $('bluetooth-unpaired-devices-list');
+ var pairDeviceDialog = $('bluetooth-pairing');
+
+ // Show the pairing dialog.
+ $('bluetooth-add-device').click();
+ BluetoothPairing.showDialog(fakeEvent);
+
+ // Wait for fake bluetooth impl to send any updates.
+ chrome.bluetooth.getAdapterState(function(state) {
+ expectFalse(unpairedDeviceList.parentNode.hidden);
+
+ // Determine the expected sizes.
+ var unpairedDeviceListSize = nodeCount(unpairedDeviceList);
+ var pairDeviceDialogSize = nodeCount(pairDeviceDialog);
+
+ // Ensure that updating the device with a malicious name does not
+ // corrupt the structure of the document. Tests the unpaired device
+ // list and bluetooth pairing dialog.
+ for (var i = 0; i < maliciousStrings.length; i++) {
+ fakeEvent.device.name = maliciousStrings[i];
+ BluetoothPairing.showDialog(fakeEvent);
+ assertEquals(unpairedDeviceListSize, nodeCount(unpairedDeviceList));
+ var element = this.getElementForDevice(
+ unpairedDeviceList, fakeEvent.device.name);
+ assertTrue(!!element, fakeEvent.device.name);
+ var label = element.querySelector('.bluetooth-device-label');
+ assertTrue(!!label);
+ assertEquals(maliciousStrings[i], label.textContent);
+ assertEquals(pairDeviceDialogSize, nodeCount(pairDeviceDialog));
+ }
+
+ testDone();
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+});
+
+GEN('#endif');
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/bluetooth_options_handler.cc b/chromium/chrome/browser/ui/webui/options/chromeos/bluetooth_options_handler.cc
new file mode 100644
index 00000000000..ddff2c18054
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/bluetooth_options_handler.cc
@@ -0,0 +1,104 @@
+// 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/ui/webui/options/chromeos/bluetooth_options_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+namespace options {
+
+BluetoothOptionsHandler::BluetoothOptionsHandler() {
+}
+
+BluetoothOptionsHandler::~BluetoothOptionsHandler() {
+}
+
+void BluetoothOptionsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ static OptionsStringResource resources[] = {
+ { "bluetooth", IDS_OPTIONS_SETTINGS_SECTION_TITLE_BLUETOOTH },
+ { "disableBluetooth", IDS_OPTIONS_SETTINGS_BLUETOOTH_DISABLE },
+ { "enableBluetooth", IDS_OPTIONS_SETTINGS_BLUETOOTH_ENABLE },
+ { "addBluetoothDevice", IDS_OPTIONS_SETTINGS_ADD_BLUETOOTH_DEVICE },
+ { "bluetoothAddDeviceTitle",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_ADD_DEVICE_TITLE },
+ { "bluetoothOptionsPageTabTitle",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_ADD_DEVICE_TITLE },
+ { "bluetoothNoDevices", IDS_OPTIONS_SETTINGS_BLUETOOTH_NO_DEVICES },
+ { "bluetoothNoDevicesFound",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_NO_DEVICES_FOUND },
+ { "bluetoothScanning", IDS_OPTIONS_SETTINGS_BLUETOOTH_SCANNING },
+ { "bluetoothScanStopped", IDS_OPTIONS_SETTINGS_BLUETOOTH_SCAN_STOPPED },
+ { "bluetoothDeviceConnecting", IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECTING },
+ { "bluetoothConnectDevice", IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT },
+ { "bluetoothDisconnectDevice", IDS_OPTIONS_SETTINGS_BLUETOOTH_DISCONNECT },
+ { "bluetoothForgetDevice", IDS_OPTIONS_SETTINGS_BLUETOOTH_FORGET },
+ { "bluetoothCancel", IDS_OPTIONS_SETTINGS_BLUETOOTH_CANCEL },
+ { "bluetoothEnterKey", IDS_OPTIONS_SETTINGS_BLUETOOTH_ENTER_KEY },
+ { "bluetoothDismissError", IDS_OPTIONS_SETTINGS_BLUETOOTH_DISMISS_ERROR },
+
+ // Device connecting and pairing.
+ { "bluetoothStartConnecting",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_START_CONNECTING },
+ { "bluetoothAcceptPasskey",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_ACCEPT_PASSKEY },
+ { "bluetoothRejectPasskey",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_REJECT_PASSKEY },
+ { "bluetoothEnterPinCode",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_ENTER_PIN_CODE_REQUEST },
+ { "bluetoothEnterPasskey",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_ENTER_PASSKEY_REQUEST },
+ { "bluetoothRemotePinCode",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_REMOTE_PIN_CODE_REQUEST },
+ { "bluetoothRemotePasskey",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_REMOTE_PASSKEY_REQUEST },
+ { "bluetoothConfirmPasskey",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_CONFIRM_PASSKEY_REQUEST },
+
+ // Error messages.
+ { "bluetoothStartDiscoveryFailed",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_START_DISCOVERY_FAILED },
+ { "bluetoothStopDiscoveryFailed",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_STOP_DISCOVERY_FAILED },
+ { "bluetoothChangePowerFailed",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_CHANGE_POWER_FAILED },
+ { "bluetoothConnectUnknownError",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT_UNKNOWN_ERROR },
+ { "bluetoothConnectInProgress",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT_IN_PROGRESS },
+ { "bluetoothConnectFailed",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT_FAILED },
+ { "bluetoothConnectAuthFailed",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT_AUTH_FAILED },
+ { "bluetoothConnectAuthCanceled",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT_AUTH_CANCELED },
+ { "bluetoothConnectAuthRejected",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT_AUTH_REJECTED },
+ { "bluetoothConnectAuthTimeout",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT_AUTH_TIMEOUT },
+ { "bluetoothConnectUnsupportedDevice",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT_UNSUPPORTED_DEVICE },
+ { "bluetoothDisconnectFailed",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_DISCONNECT_FAILED },
+ { "bluetoothForgetFailed",
+ IDS_OPTIONS_SETTINGS_BLUETOOTH_FORGET_FAILED }};
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/bluetooth_options_handler.h b/chromium/chrome/browser/ui/webui/options/chromeos/bluetooth_options_handler.h
new file mode 100644
index 00000000000..dfe8753008f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/bluetooth_options_handler.h
@@ -0,0 +1,40 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_BLUETOOTH_OPTIONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_BLUETOOTH_OPTIONS_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace chromeos {
+namespace options {
+
+// Handler for Bluetooth options on the system options page.
+class BluetoothOptionsHandler : public ::options::OptionsPageUIHandler {
+ public:
+ BluetoothOptionsHandler();
+ ~BluetoothOptionsHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BluetoothOptionsHandler);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_BLUETOOTH_OPTIONS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc b/chromium/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc
new file mode 100644
index 00000000000..e5661d299db
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc
@@ -0,0 +1,478 @@
+// 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/ui/webui/options/chromeos/change_picture_options_handler.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
+#include "chrome/browser/chromeos/camera_presence_notifier.h"
+#include "chrome/browser/chromeos/login/users/avatar/user_image_manager.h"
+#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
+#include "chrome/browser/chromeos/login/users/default_user_image/default_user_images.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/chrome_select_file_policy.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/audio/chromeos_sounds.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_image/user_image.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/common/url_constants.h"
+#include "media/audio/sounds/sounds_manager.h"
+#include "net/base/data_url.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "ui/views/widget/widget.h"
+#include "url/gurl.h"
+
+using content::BrowserThread;
+
+namespace chromeos {
+namespace options {
+
+namespace {
+
+// Returns info about extensions for files we support as user images.
+ui::SelectFileDialog::FileTypeInfo GetUserImageFileTypeInfo() {
+ ui::SelectFileDialog::FileTypeInfo file_type_info;
+ file_type_info.extensions.resize(1);
+
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("bmp"));
+
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("jpg"));
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("jpeg"));
+
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("png"));
+
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("tif"));
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("tiff"));
+
+ file_type_info.extension_description_overrides.resize(1);
+ file_type_info.extension_description_overrides[0] =
+ l10n_util::GetStringUTF16(IDS_IMAGE_FILES);
+
+ return file_type_info;
+}
+
+// Time histogram suffix for profile image download.
+const char kProfileDownloadReason[] = "Preferences";
+
+} // namespace
+
+ChangePictureOptionsHandler::ChangePictureOptionsHandler()
+ : previous_image_url_(url::kAboutBlankURL),
+ previous_image_index_(user_manager::User::USER_IMAGE_INVALID) {
+ user_manager::UserManager::Get()->AddObserver(this);
+
+ ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+ media::SoundsManager* manager = media::SoundsManager::Get();
+ manager->Initialize(SOUND_OBJECT_DELETE,
+ bundle.GetRawDataResource(IDR_SOUND_OBJECT_DELETE_WAV));
+ manager->Initialize(SOUND_CAMERA_SNAP,
+ bundle.GetRawDataResource(IDR_SOUND_CAMERA_SNAP_WAV));
+}
+
+ChangePictureOptionsHandler::~ChangePictureOptionsHandler() {
+ user_manager::UserManager::Get()->RemoveObserver(this);
+ CameraPresenceNotifier::GetInstance()->RemoveObserver(this);
+ if (select_file_dialog_.get())
+ select_file_dialog_->ListenerDestroyed();
+}
+
+void ChangePictureOptionsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+ localized_strings->SetString("changePicturePage",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_DIALOG_TITLE));
+ localized_strings->SetString("changePicturePageDescription",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_DIALOG_TEXT));
+ localized_strings->SetString("takePhoto",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_TAKE_PHOTO));
+ localized_strings->SetString("discardPhoto",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_DISCARD_PHOTO));
+ localized_strings->SetString("flipPhoto",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_FLIP_PHOTO));
+ localized_strings->SetString("chooseFile",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_CHOOSE_FILE));
+ localized_strings->SetString("profilePhoto",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_PROFILE_PHOTO));
+ localized_strings->SetString("profilePhotoLoading",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_CHANGE_PICTURE_PROFILE_LOADING_PHOTO));
+ localized_strings->SetString("previewAltText",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_PREVIEW_ALT));
+ localized_strings->SetString("authorCredit",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_SET_WALLPAPER_AUTHOR_TEXT));
+ localized_strings->SetString("photoFromCamera",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_CHANGE_PICTURE_PHOTO_FROM_CAMERA));
+ localized_strings->SetString("photoFlippedAccessibleText",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_PHOTO_FLIP_ACCESSIBLE_TEXT));
+ localized_strings->SetString("photoFlippedBackAccessibleText",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_PHOTO_FLIPBACK_ACCESSIBLE_TEXT));
+ localized_strings->SetString("photoCaptureAccessibleText",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_PHOTO_CAPTURE_ACCESSIBLE_TEXT));
+ localized_strings->SetString("photoDiscardAccessibleText",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_PHOTO_DISCARD_ACCESSIBLE_TEXT));
+}
+
+void ChangePictureOptionsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("chooseFile",
+ base::Bind(&ChangePictureOptionsHandler::HandleChooseFile,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("takePhoto",
+ base::Bind(&ChangePictureOptionsHandler::HandleTakePhoto,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("photoTaken",
+ base::Bind(&ChangePictureOptionsHandler::HandlePhotoTaken,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("discardPhoto",
+ base::Bind(&ChangePictureOptionsHandler::HandleDiscardPhoto,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("onChangePicturePageShown",
+ base::Bind(&ChangePictureOptionsHandler::HandlePageShown,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("onChangePicturePageHidden",
+ base::Bind(&ChangePictureOptionsHandler::HandlePageHidden,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("onChangePicturePageInitialized",
+ base::Bind(&ChangePictureOptionsHandler::HandlePageInitialized,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("selectImage",
+ base::Bind(&ChangePictureOptionsHandler::HandleSelectImage,
+ base::Unretained(this)));
+}
+
+void ChangePictureOptionsHandler::SendDefaultImages() {
+ base::ListValue image_urls;
+ for (int i = default_user_image::kFirstDefaultImageIndex;
+ i < default_user_image::kDefaultImagesCount; ++i) {
+ std::unique_ptr<base::DictionaryValue> image_data(
+ new base::DictionaryValue);
+ image_data->SetString("url", default_user_image::GetDefaultImageUrl(i));
+ image_data->SetString("author",
+ l10n_util::GetStringUTF16(
+ default_user_image::kDefaultImageAuthorIDs[i]));
+ image_data->SetString("website",
+ l10n_util::GetStringUTF16(
+ default_user_image::kDefaultImageWebsiteIDs[i]));
+ image_data->SetString("title",
+ default_user_image::GetDefaultImageDescription(i));
+ image_urls.Append(std::move(image_data));
+ }
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ChangePictureOptions.setDefaultImages", image_urls);
+}
+
+void ChangePictureOptionsHandler::HandleChooseFile(
+ const base::ListValue* args) {
+ DCHECK(args && args->empty());
+ select_file_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
+
+ base::FilePath downloads_path;
+ if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &downloads_path)) {
+ NOTREACHED();
+ return;
+ }
+
+ // Static so we initialize it only once.
+ CR_DEFINE_STATIC_LOCAL(ui::SelectFileDialog::FileTypeInfo, file_type_info,
+ (GetUserImageFileTypeInfo()));
+
+ select_file_dialog_->SelectFile(
+ ui::SelectFileDialog::SELECT_OPEN_FILE,
+ l10n_util::GetStringUTF16(IDS_DOWNLOAD_TITLE),
+ downloads_path,
+ &file_type_info,
+ 0,
+ FILE_PATH_LITERAL(""),
+ GetBrowserWindow(),
+ NULL);
+}
+
+void ChangePictureOptionsHandler::HandleTakePhoto(
+ const base::ListValue* args) {
+ DCHECK(args->empty());
+ AccessibilityManager::Get()->PlayEarcon(
+ SOUND_CAMERA_SNAP, PlaySoundOption::SPOKEN_FEEDBACK_ENABLED);
+}
+
+void ChangePictureOptionsHandler::HandleDiscardPhoto(
+ const base::ListValue* args) {
+ DCHECK(args->empty());
+ AccessibilityManager::Get()->PlayEarcon(
+ SOUND_OBJECT_DELETE, PlaySoundOption::SPOKEN_FEEDBACK_ENABLED);
+}
+
+void ChangePictureOptionsHandler::HandlePhotoTaken(
+ const base::ListValue* args) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ std::string image_url;
+ if (!args || args->GetSize() != 1 || !args->GetString(0, &image_url))
+ NOTREACHED();
+ DCHECK(!image_url.empty());
+
+ std::string mime_type, charset, raw_data;
+ if (!net::DataURL::Parse(GURL(image_url), &mime_type, &charset, &raw_data))
+ NOTREACHED();
+ DCHECK_EQ("image/png", mime_type);
+
+ user_photo_ = gfx::ImageSkia();
+ user_photo_data_url_ = image_url;
+
+ ImageDecoder::Cancel(this);
+ ImageDecoder::Start(this, raw_data);
+}
+
+void ChangePictureOptionsHandler::HandlePageInitialized(
+ const base::ListValue* args) {
+ DCHECK(args && args->empty());
+ SendDefaultImages();
+}
+
+void ChangePictureOptionsHandler::HandlePageShown(const base::ListValue* args) {
+ DCHECK(args && args->empty());
+ SendSelectedImage();
+ UpdateProfileImage();
+ CameraPresenceNotifier::GetInstance()->AddObserver(this);
+}
+
+void ChangePictureOptionsHandler::HandlePageHidden(
+ const base::ListValue* args) {
+ CameraPresenceNotifier::GetInstance()->RemoveObserver(this);
+}
+
+void ChangePictureOptionsHandler::SendSelectedImage() {
+ const user_manager::User* user = GetUser();
+ DCHECK(user->GetAccountId().is_valid());
+
+ previous_image_index_ = user->image_index();
+ switch (previous_image_index_) {
+ case user_manager::User::USER_IMAGE_EXTERNAL: {
+ // User has image from camera/file, record it and add to the image list.
+ previous_image_ = user->GetImage();
+ SendOldImage(webui::GetBitmapDataUrl(*previous_image_.bitmap()));
+ break;
+ }
+ case user_manager::User::USER_IMAGE_PROFILE: {
+ // User has their Profile image as the current image.
+ SendProfileImage(user->GetImage(), true);
+ break;
+ }
+ default: {
+ DCHECK(previous_image_index_ >= 0 &&
+ previous_image_index_ < default_user_image::kDefaultImagesCount);
+ if (previous_image_index_ >=
+ default_user_image::kFirstDefaultImageIndex) {
+ // User has image from the current set of default images.
+ base::Value image_url(
+ default_user_image::GetDefaultImageUrl(previous_image_index_));
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ChangePictureOptions.setSelectedImage", image_url);
+ } else {
+ // User has an old default image, so present it in the same manner as a
+ // previous image from file.
+ SendOldImage(
+ default_user_image::GetDefaultImageUrl(previous_image_index_));
+ }
+ }
+ }
+}
+
+void ChangePictureOptionsHandler::SendProfileImage(const gfx::ImageSkia& image,
+ bool should_select) {
+ base::Value data_url(webui::GetBitmapDataUrl(*image.bitmap()));
+ base::Value select(should_select);
+ web_ui()->CallJavascriptFunctionUnsafe("ChangePictureOptions.setProfileImage",
+ data_url, select);
+}
+
+void ChangePictureOptionsHandler::UpdateProfileImage() {
+ UserImageManager* user_image_manager =
+ ChromeUserManager::Get()->GetUserImageManager(GetUser()->GetAccountId());
+ // If we have a downloaded profile image and haven't sent it in
+ // |SendSelectedImage|, send it now (without selecting).
+ if (previous_image_index_ != user_manager::User::USER_IMAGE_PROFILE &&
+ !user_image_manager->DownloadedProfileImage().isNull())
+ SendProfileImage(user_image_manager->DownloadedProfileImage(), false);
+
+ user_image_manager->DownloadProfileImage(kProfileDownloadReason);
+}
+
+void ChangePictureOptionsHandler::SendOldImage(const std::string& image_url) {
+ previous_image_url_ = image_url;
+ base::Value url(image_url);
+ web_ui()->CallJavascriptFunctionUnsafe("ChangePictureOptions.setOldImage",
+ url);
+}
+
+void ChangePictureOptionsHandler::HandleSelectImage(
+ const base::ListValue* args) {
+ std::string image_url;
+ std::string image_type;
+ if (!args ||
+ args->GetSize() != 2 ||
+ !args->GetString(0, &image_url) ||
+ !args->GetString(1, &image_type)) {
+ NOTREACHED();
+ return;
+ }
+ DCHECK(!image_url.empty());
+ DCHECK(!image_type.empty());
+
+ UserImageManager* user_image_manager =
+ ChromeUserManager::Get()->GetUserImageManager(GetUser()->GetAccountId());
+ int image_index = user_manager::User::USER_IMAGE_INVALID;
+ bool waiting_for_camera_photo = false;
+
+ if (image_type == "old") {
+ // Previous image (from camera or manually uploaded) re-selected.
+ DCHECK(!previous_image_.isNull());
+ user_image_manager->SaveUserImage(
+ user_manager::UserImage::CreateAndEncode(
+ previous_image_, user_manager::UserImage::FORMAT_JPEG));
+
+ UMA_HISTOGRAM_EXACT_LINEAR("UserImage.ChangeChoice",
+ default_user_image::kHistogramImageOld,
+ default_user_image::kHistogramImagesCount);
+ VLOG(1) << "Selected old user image";
+ } else if (image_type == "default" &&
+ default_user_image::IsDefaultImageUrl(image_url, &image_index)) {
+ // One of the default user images.
+ user_image_manager->SaveUserDefaultImageIndex(image_index);
+
+ UMA_HISTOGRAM_EXACT_LINEAR(
+ "UserImage.ChangeChoice",
+ default_user_image::GetDefaultImageHistogramValue(image_index),
+ default_user_image::kHistogramImagesCount);
+ VLOG(1) << "Selected default user image: " << image_index;
+ } else if (image_type == "camera") {
+ // Camera image is selected.
+ if (user_photo_.isNull()) {
+ waiting_for_camera_photo = true;
+ VLOG(1) << "Still waiting for camera image to decode";
+ } else {
+ SetImageFromCamera(user_photo_);
+ }
+ } else if (image_type == "profile") {
+ // Profile image selected. Could be previous (old) user image.
+ user_image_manager->SaveUserImageFromProfileImage();
+
+ if (previous_image_index_ == user_manager::User::USER_IMAGE_PROFILE) {
+ UMA_HISTOGRAM_EXACT_LINEAR("UserImage.ChangeChoice",
+ default_user_image::kHistogramImageOld,
+ default_user_image::kHistogramImagesCount);
+ VLOG(1) << "Selected old (profile) user image";
+ } else {
+ UMA_HISTOGRAM_EXACT_LINEAR("UserImage.ChangeChoice",
+ default_user_image::kHistogramImageFromProfile,
+ default_user_image::kHistogramImagesCount);
+ VLOG(1) << "Selected profile image";
+ }
+ } else {
+ NOTREACHED() << "Unexpected image type: " << image_type;
+ }
+
+ // Ignore the result of the previous decoding if it's no longer needed.
+ if (!waiting_for_camera_photo)
+ ImageDecoder::Cancel(this);
+}
+
+void ChangePictureOptionsHandler::FileSelected(const base::FilePath& path,
+ int index,
+ void* params) {
+ ChromeUserManager::Get()
+ ->GetUserImageManager(GetUser()->GetAccountId())
+ ->SaveUserImageFromFile(path);
+ UMA_HISTOGRAM_EXACT_LINEAR("UserImage.ChangeChoice",
+ default_user_image::kHistogramImageFromFile,
+ default_user_image::kHistogramImagesCount);
+ VLOG(1) << "Selected image from file";
+}
+
+void ChangePictureOptionsHandler::SetImageFromCamera(
+ const gfx::ImageSkia& photo) {
+ ChromeUserManager::Get()
+ ->GetUserImageManager(GetUser()->GetAccountId())
+ ->SaveUserImage(user_manager::UserImage::CreateAndEncode(
+ photo, user_manager::UserImage::FORMAT_JPEG));
+ UMA_HISTOGRAM_EXACT_LINEAR("UserImage.ChangeChoice",
+ default_user_image::kHistogramImageFromCamera,
+ default_user_image::kHistogramImagesCount);
+ VLOG(1) << "Selected camera photo";
+}
+
+void ChangePictureOptionsHandler::SetCameraPresent(bool present) {
+ base::Value present_value(present);
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ChangePictureOptions.setCameraPresent", present_value);
+}
+
+void ChangePictureOptionsHandler::OnCameraPresenceCheckDone(
+ bool is_camera_present) {
+ SetCameraPresent(is_camera_present);
+}
+
+void ChangePictureOptionsHandler::OnUserImageChanged(
+ const user_manager::User& user) {
+ // Not initialized yet.
+ if (previous_image_index_ == user_manager::User::USER_IMAGE_INVALID)
+ return;
+ SendSelectedImage();
+}
+
+void ChangePictureOptionsHandler::OnUserProfileImageUpdated(
+ const user_manager::User& user,
+ const gfx::ImageSkia& profile_image) {
+ // User profile image has been updated.
+ SendProfileImage(profile_image, false);
+}
+
+gfx::NativeWindow ChangePictureOptionsHandler::GetBrowserWindow() const {
+ Browser* browser =
+ chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
+ return browser->window()->GetNativeWindow();
+}
+
+void ChangePictureOptionsHandler::OnImageDecoded(
+ const SkBitmap& decoded_image) {
+ user_photo_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
+ SetImageFromCamera(user_photo_);
+}
+
+void ChangePictureOptionsHandler::OnDecodeImageFailed() {
+ NOTREACHED() << "Failed to decode PNG image from WebUI";
+}
+
+const user_manager::User* ChangePictureOptionsHandler::GetUser() const {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ const user_manager::User* user =
+ ProfileHelper::Get()->GetUserByProfile(profile);
+ if (!user)
+ return user_manager::UserManager::Get()->GetActiveUser();
+ return user;
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.h b/chromium/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.h
new file mode 100644
index 00000000000..625ce3b945d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.h
@@ -0,0 +1,145 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_CHANGE_PICTURE_OPTIONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_CHANGE_PICTURE_OPTIONS_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/camera_presence_notifier.h"
+#include "chrome/browser/image_decoder.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "components/user_manager/user_manager.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+namespace user_manager {
+class User;
+}
+
+namespace chromeos {
+
+namespace options {
+
+// ChromeOS user image options page UI handler.
+class ChangePictureOptionsHandler : public ::options::OptionsPageUIHandler,
+ public ui::SelectFileDialog::Listener,
+ public user_manager::UserManager::Observer,
+ public ImageDecoder::ImageRequest,
+ public CameraPresenceNotifier::Observer {
+ public:
+ ChangePictureOptionsHandler();
+ ~ChangePictureOptionsHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // CameraPresenceNotifier::Observer implementation:
+ void OnCameraPresenceCheckDone(bool is_camera_present) override;
+
+ private:
+ // Sends list of available default images to the page.
+ void SendDefaultImages();
+
+ // Sends current selection to the page.
+ void SendSelectedImage();
+
+ // Sends the profile image to the page. If |should_select| is true then
+ // the profile image element is selected.
+ void SendProfileImage(const gfx::ImageSkia& image, bool should_select);
+
+ // Starts profile image update and shows the last downloaded profile image,
+ // if any, on the page. Shouldn't be called before |SendProfileImage|.
+ void UpdateProfileImage();
+
+ // Sends previous user image to the page.
+ void SendOldImage(const std::string& image_url);
+
+ // Starts camera presence check.
+ void CheckCameraPresence();
+
+ // Updates UI with camera presence state.
+ void SetCameraPresent(bool present);
+
+ // Opens a file selection dialog to choose user image from file.
+ void HandleChooseFile(const base::ListValue* args);
+
+ // Handles 'take-photo' button click.
+ void HandleTakePhoto(const base::ListValue* args);
+
+ // Handles photo taken with WebRTC UI.
+ void HandlePhotoTaken(const base::ListValue* args);
+
+ // Handles 'discard-photo' button click.
+ void HandleDiscardPhoto(const base::ListValue* args);
+
+ // Gets the list of available user images and sends it to the page.
+ void HandleGetAvailableImages(const base::ListValue* args);
+
+ // Handles page initialized event.
+ void HandlePageInitialized(const base::ListValue* args);
+
+ // Handles page shown event.
+ void HandlePageShown(const base::ListValue* args);
+
+ // Handles page hidden event.
+ void HandlePageHidden(const base::ListValue* args);
+
+ // Selects one of the available images as user's.
+ void HandleSelectImage(const base::ListValue* args);
+
+ // SelectFileDialog::Delegate implementation.
+ void FileSelected(const base::FilePath& path,
+ int index,
+ void* params) override;
+
+ // user_manager::UserManager::Observer implementation.
+ void OnUserImageChanged(const user_manager::User& user) override;
+ void OnUserProfileImageUpdated(const user_manager::User& user,
+ const gfx::ImageSkia& profile_image) override;
+
+ // Sets user image to photo taken from camera.
+ void SetImageFromCamera(const gfx::ImageSkia& photo);
+
+ // Returns handle to browser window or NULL if it can't be found.
+ gfx::NativeWindow GetBrowserWindow() const;
+
+ // Overriden from ImageDecoder::ImageRequest:
+ void OnImageDecoded(const SkBitmap& decoded_image) override;
+ void OnDecodeImageFailed() override;
+
+ // Returns user related to current WebUI. If this user doesn't exist,
+ // returns active user.
+ const user_manager::User* GetUser() const;
+
+ scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
+
+ // Previous user image from camera/file and its data URL.
+ gfx::ImageSkia previous_image_;
+ std::string previous_image_url_;
+
+ // Index of the previous user image.
+ int previous_image_index_;
+
+ // Last user photo, if taken.
+ gfx::ImageSkia user_photo_;
+
+ // Data URL for |user_photo_|.
+ std::string user_photo_data_url_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChangePictureOptionsHandler);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_CHANGE_PICTURE_OPTIONS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.cc b/chromium/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.cc
new file mode 100644
index 00000000000..b21df5fcf9d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.cc
@@ -0,0 +1,427 @@
+// 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/ui/webui/options/chromeos/core_chromeos_options_handler.h"
+
+#include <stddef.h>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/sys_info.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
+#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/chromeos/proxy_cros_settings_parser.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/session_controller_client.h"
+#include "chrome/browser/ui/webui/chromeos/ui_account_tweaks.h"
+#include "chrome/browser/ui/webui/options/chromeos/accounts_options_handler.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/proxy/ui_proxy_config_service.h"
+#include "components/onc/onc_pref_names.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/proxy_config/proxy_config_pref_names.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace chromeos {
+namespace options {
+
+namespace {
+
+// List of settings that should be changeable by all users.
+const char* kNonPrivilegedSettings[] = {
+ kSystemTimezone
+};
+
+// List of settings that should only be changeable by the primary user.
+const char* kPrimaryUserSettings[] = {
+ prefs::kWakeOnWifiDarkConnect,
+};
+
+// Returns true if |pref| can be controlled (e.g. by policy or owner).
+bool IsSettingPrivileged(const std::string& pref) {
+ const char** end = kNonPrivilegedSettings + arraysize(kNonPrivilegedSettings);
+ return std::find(kNonPrivilegedSettings, end, pref) == end;
+}
+
+// Returns true if |pref| is shared (controlled by the primary user).
+bool IsSettingShared(const std::string& pref) {
+ const char** end = kPrimaryUserSettings + arraysize(kPrimaryUserSettings);
+ return std::find(kPrimaryUserSettings, end, pref) != end;
+}
+
+// Creates a user info dictionary to be stored in the |ListValue| that is
+// passed to Javascript for the |kAccountsPrefUsers| preference.
+std::unique_ptr<base::DictionaryValue> CreateUserInfo(
+ const std::string& username,
+ const std::string& display_email,
+ const std::string& display_name) {
+ auto user_dict = base::MakeUnique<base::DictionaryValue>();
+ user_dict->SetString("username", username);
+ user_dict->SetString("name", display_email);
+ user_dict->SetString("email", display_name);
+
+ const bool is_owner =
+ user_manager::UserManager::Get()->GetOwnerAccountId().GetUserEmail() ==
+ username;
+ user_dict->SetBoolean("owner", is_owner);
+ return user_dict;
+}
+
+// This function decorates the bare list of emails with some more information
+// needed by the UI to properly display the Accounts page.
+std::unique_ptr<base::Value> CreateUsersWhitelist(
+ const base::Value* pref_value) {
+ const base::ListValue* list_value =
+ static_cast<const base::ListValue*>(pref_value);
+ auto user_list = base::MakeUnique<base::ListValue>();
+ user_manager::UserManager* user_manager = user_manager::UserManager::Get();
+
+ for (base::ListValue::const_iterator i = list_value->begin();
+ i != list_value->end(); ++i) {
+ std::string email;
+ if (i->GetAsString(&email)) {
+ // Translate email to the display email.
+ const std::string display_email =
+ user_manager->GetUserDisplayEmail(AccountId::FromUserEmail(email));
+ // TODO(ivankr): fetch display name for existing users.
+ user_list->Append(CreateUserInfo(email, display_email, std::string()));
+ }
+ }
+ return std::move(user_list);
+}
+
+// Checks whether this is a secondary user in a multi-profile session.
+bool IsSecondaryUser(Profile* profile) {
+ user_manager::UserManager* user_manager = user_manager::UserManager::Get();
+ const user_manager::User* user =
+ ProfileHelper::Get()->GetUserByProfile(profile);
+ return user &&
+ user->GetAccountId() != user_manager->GetPrimaryUser()->GetAccountId();
+}
+
+const char kSelectNetworkMessage[] = "selectNetwork";
+
+UIProxyConfigService* GetUiProxyConfigService() {
+ return NetworkHandler::Get()->ui_proxy_config_service();
+}
+
+} // namespace
+
+CoreChromeOSOptionsHandler::CoreChromeOSOptionsHandler() {
+ notification_registrar_.Add(this,
+ chrome::NOTIFICATION_OWNERSHIP_STATUS_CHANGED,
+ content::NotificationService::AllSources());
+}
+
+CoreChromeOSOptionsHandler::~CoreChromeOSOptionsHandler() {
+}
+
+void CoreChromeOSOptionsHandler::RegisterMessages() {
+ CoreOptionsHandler::RegisterMessages();
+ web_ui()->RegisterMessageCallback(
+ kSelectNetworkMessage,
+ base::Bind(&CoreChromeOSOptionsHandler::SelectNetworkCallback,
+ base::Unretained(this)));
+}
+
+void CoreChromeOSOptionsHandler::InitializeHandler() {
+ // This function is both called on the initial page load and on each reload.
+ CoreOptionsHandler::InitializeHandler();
+
+ if (!ProfileHelper::IsSigninProfile(Profile::FromWebUI(web_ui())))
+ ObservePref(onc::prefs::kOpenNetworkConfiguration);
+ ObservePref(proxy_config::prefs::kProxy);
+ ObservePref(onc::prefs::kDeviceOpenNetworkConfiguration);
+}
+
+void CoreChromeOSOptionsHandler::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK_EQ(chrome::NOTIFICATION_OWNERSHIP_STATUS_CHANGED, type);
+
+ // Finish this asynchronously because the notification has to tricle in to all
+ // Chrome components before we can reliably read the status on the other end.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&CoreChromeOSOptionsHandler::NotifyOwnershipChanged,
+ base::Unretained(this)));
+}
+
+void CoreChromeOSOptionsHandler::NotifyOwnershipChanged() {
+ for (auto it : pref_subscription_map_)
+ NotifySettingsChanged(it.first);
+}
+
+std::unique_ptr<base::Value> CoreChromeOSOptionsHandler::FetchPref(
+ const std::string& pref_name) {
+ if (proxy_cros_settings_parser::IsProxyPref(pref_name)) {
+ std::unique_ptr<base::Value> value;
+ proxy_cros_settings_parser::GetProxyPrefValue(
+ network_guid_, pref_name, GetUiProxyConfigService(), &value);
+ return value;
+ }
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (!CrosSettings::IsCrosSettings(pref_name)) {
+ std::string controlling_pref =
+ pref_name == proxy_config::prefs::kUseSharedProxies
+ ? proxy_config::prefs::kProxy
+ : std::string();
+ std::unique_ptr<base::Value> value =
+ CreateValueForPref(pref_name, controlling_pref);
+ if (!IsSettingShared(pref_name) || !IsSecondaryUser(profile))
+ return value;
+ base::DictionaryValue* dict;
+ if (!value->GetAsDictionary(&dict) || dict->HasKey("controlledBy"))
+ return value;
+ Profile* primary_profile = ProfileHelper::Get()->GetProfileByUser(
+ user_manager::UserManager::Get()->GetPrimaryUser());
+ if (!primary_profile)
+ return value;
+ dict->SetString("controlledBy", "shared");
+ dict->SetBoolean("disabled", true);
+ dict->SetBoolean("value", primary_profile->GetPrefs()->GetBoolean(
+ pref_name));
+ return value;
+ }
+
+ const base::Value* pref_value = CrosSettings::Get()->GetPref(pref_name);
+ if (!pref_value)
+ return base::MakeUnique<base::Value>();
+
+ // Decorate pref value as CoreOptionsHandler::CreateValueForPref() does.
+ // TODO(estade): seems that this should replicate CreateValueForPref less.
+ auto dict = base::MakeUnique<base::DictionaryValue>();
+ if (pref_name == kAccountsPrefUsers)
+ dict->Set("value", CreateUsersWhitelist(pref_value));
+ else
+ dict->Set("value", base::MakeUnique<base::Value>(*pref_value));
+
+ std::string controlled_by;
+ if (IsSettingPrivileged(pref_name)) {
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ if (connector->IsEnterpriseManaged())
+ controlled_by = "policy";
+ else if (!ProfileHelper::IsOwnerProfile(profile))
+ controlled_by = "owner";
+ }
+ dict->SetBoolean("disabled", !controlled_by.empty());
+ if (!controlled_by.empty())
+ dict->SetString("controlledBy", controlled_by);
+ return std::move(dict);
+}
+
+void CoreChromeOSOptionsHandler::ObservePref(const std::string& pref_name) {
+ if (proxy_cros_settings_parser::IsProxyPref(pref_name)) {
+ // We observe those all the time.
+ return;
+ }
+ if (!CrosSettings::IsCrosSettings(pref_name))
+ return ::options::CoreOptionsHandler::ObservePref(pref_name);
+
+ linked_ptr<CrosSettings::ObserverSubscription> subscription(
+ CrosSettings::Get()->AddSettingsObserver(
+ pref_name.c_str(),
+ base::Bind(&CoreChromeOSOptionsHandler::NotifySettingsChanged,
+ base::Unretained(this),
+ pref_name)).release());
+ pref_subscription_map_.insert(make_pair(pref_name, subscription));
+}
+
+void CoreChromeOSOptionsHandler::SetPref(const std::string& pref_name,
+ const base::Value* value,
+ const std::string& metric) {
+ if (proxy_cros_settings_parser::IsProxyPref(pref_name)) {
+ proxy_cros_settings_parser::SetProxyPrefValue(
+ network_guid_, pref_name, value, GetUiProxyConfigService());
+ base::Value proxy_type(pref_name);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.internet.DetailsInternetPage.updateProxySettings", proxy_type);
+ ProcessUserMetric(value, metric);
+ return;
+ }
+ if (!CrosSettings::IsCrosSettings(pref_name))
+ return ::options::CoreOptionsHandler::SetPref(pref_name, value, metric);
+ OwnerSettingsServiceChromeOS* service =
+ OwnerSettingsServiceChromeOS::FromWebUI(web_ui());
+ if (service && service->HandlesSetting(pref_name))
+ service->Set(pref_name, *value);
+ else
+ CrosSettings::Get()->Set(pref_name, *value);
+
+ ProcessUserMetric(value, metric);
+}
+
+void CoreChromeOSOptionsHandler::StopObservingPref(const std::string& path) {
+ if (proxy_cros_settings_parser::IsProxyPref(path))
+ return; // We unregister those in the destructor.
+ // Unregister this instance from observing prefs of chrome os settings.
+ if (CrosSettings::IsCrosSettings(path))
+ pref_subscription_map_.erase(path);
+ else // Call base class to handle regular preferences.
+ ::options::CoreOptionsHandler::StopObservingPref(path);
+}
+
+std::unique_ptr<base::Value> CoreChromeOSOptionsHandler::CreateValueForPref(
+ const std::string& pref_name,
+ const std::string& controlling_pref_name) {
+ // The screen lock setting is shared if multiple users are logged in and at
+ // least one has chosen to require passwords.
+ if (pref_name == prefs::kEnableAutoScreenLock &&
+ user_manager::UserManager::Get()->GetLoggedInUsers().size() > 1 &&
+ controlling_pref_name.empty()) {
+ PrefService* user_prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+ const PrefService::Preference* pref =
+ user_prefs->FindPreference(prefs::kEnableAutoScreenLock);
+
+ if (pref && pref->IsUserModifiable() &&
+ SessionControllerClient::ShouldLockScreenAutomatically()) {
+ bool screen_lock = false;
+ bool success = pref->GetValue()->GetAsBoolean(&screen_lock);
+ DCHECK(success);
+ if (!screen_lock) {
+ // Screen lock is enabled for the session, but not in the user's
+ // preferences. Show the user's value in the checkbox, but indicate
+ // that the password requirement is enabled by some other user.
+ auto dict = base::MakeUnique<base::DictionaryValue>();
+ dict->Set("value", base::MakeUnique<base::Value>(*pref->GetValue()));
+ dict->SetString("controlledBy", "shared");
+ return std::move(dict);
+ }
+ }
+ }
+
+ return CoreOptionsHandler::CreateValueForPref(pref_name,
+ controlling_pref_name);
+}
+
+void CoreChromeOSOptionsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+ CoreOptionsHandler::GetLocalizedValues(localized_strings);
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ AddAccountUITweaksLocalizedValues(localized_strings, profile);
+
+ user_manager::UserManager* user_manager = user_manager::UserManager::Get();
+
+ if (IsSecondaryUser(profile)) {
+ const std::string& primary_email =
+ user_manager->GetPrimaryUser()->GetAccountId().GetUserEmail();
+
+ // Set secondaryUser to show the shared icon by the network section header.
+ localized_strings->SetBoolean("secondaryUser", true);
+ localized_strings->SetString("secondaryUserBannerText",
+ l10n_util::GetStringFUTF16(
+ IDS_OPTIONS_SETTINGS_SECONDARY_USER_BANNER,
+ base::ASCIIToUTF16(primary_email)));
+ localized_strings->SetString("controlledSettingShared",
+ l10n_util::GetStringFUTF16(
+ IDS_OPTIONS_CONTROLLED_SETTING_SHARED,
+ base::ASCIIToUTF16(primary_email)));
+ localized_strings->SetString("controlledSettingsShared",
+ l10n_util::GetStringFUTF16(
+ IDS_OPTIONS_CONTROLLED_SETTINGS_SHARED,
+ base::ASCIIToUTF16(primary_email)));
+ } else {
+ localized_strings->SetBoolean("secondaryUser", false);
+ localized_strings->SetString("secondaryUserBannerText", base::string16());
+ localized_strings->SetString("controlledSettingShared", base::string16());
+ localized_strings->SetString("controlledSettingsShared", base::string16());
+ }
+
+ // Screen lock icon can show up as primary or secondary user.
+ localized_strings->SetString("screenLockShared",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_CONTROLLED_SETTING_SHARED_SCREEN_LOCK));
+
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ if (connector->IsEnterpriseManaged()) {
+ // Managed machines have no "owner".
+ localized_strings->SetString("controlledSettingOwner", base::string16());
+ } else {
+ localized_strings->SetString(
+ "controlledSettingOwner",
+ l10n_util::GetStringFUTF16(
+ IDS_OPTIONS_CONTROLLED_SETTING_OWNER,
+ base::ASCIIToUTF16(
+ user_manager->GetOwnerAccountId().GetUserEmail())));
+ }
+}
+
+void CoreChromeOSOptionsHandler::SelectNetworkCallback(
+ const base::ListValue* args) {
+ if (args->GetSize() != 1 || !args->GetString(0, &network_guid_)) {
+ NOTREACHED();
+ return;
+ }
+ NotifyProxyPrefsChanged();
+}
+
+void CoreChromeOSOptionsHandler::OnPreferenceChanged(
+ PrefService* service,
+ const std::string& pref_name) {
+ // Redetermine the current proxy settings and notify the UI if any of these
+ // preferences change.
+ if (pref_name == onc::prefs::kOpenNetworkConfiguration ||
+ pref_name == onc::prefs::kDeviceOpenNetworkConfiguration ||
+ pref_name == proxy_config::prefs::kProxy) {
+ NotifyProxyPrefsChanged();
+ return;
+ }
+ if (pref_name == proxy_config::prefs::kUseSharedProxies) {
+ // kProxy controls kUseSharedProxies and decides if it's managed by
+ // policy/extension.
+ NotifyPrefChanged(proxy_config::prefs::kUseSharedProxies,
+ proxy_config::prefs::kProxy);
+ return;
+ }
+ ::options::CoreOptionsHandler::OnPreferenceChanged(service, pref_name);
+}
+
+void CoreChromeOSOptionsHandler::NotifySettingsChanged(
+ const std::string& setting_name) {
+ DCHECK(CrosSettings::Get()->IsCrosSettings(setting_name));
+ std::unique_ptr<base::Value> value(FetchPref(setting_name));
+ if (!value.get())
+ NOTREACHED();
+ DispatchPrefChangeNotification(setting_name, std::move(value));
+}
+
+void CoreChromeOSOptionsHandler::NotifyProxyPrefsChanged() {
+ GetUiProxyConfigService()->UpdateFromPrefs(network_guid_);
+ for (size_t i = 0; i < proxy_cros_settings_parser::kProxySettingsCount; ++i) {
+ std::unique_ptr<base::Value> value;
+ proxy_cros_settings_parser::GetProxyPrefValue(
+ network_guid_, proxy_cros_settings_parser::kProxySettings[i],
+ GetUiProxyConfigService(), &value);
+ DCHECK(value);
+ DispatchPrefChangeNotification(
+ proxy_cros_settings_parser::kProxySettings[i], std::move(value));
+ }
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.h b/chromium/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.h
new file mode 100644
index 00000000000..0bc9e74e00a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.h
@@ -0,0 +1,81 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_CORE_CHROMEOS_OPTIONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_CORE_CHROMEOS_OPTIONS_HANDLER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/linked_ptr.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/ui/webui/options/core_options_handler.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+namespace chromeos {
+namespace options {
+
+// CoreChromeOSOptionsHandler handles ChromeOS settings.
+class CoreChromeOSOptionsHandler : public ::options::CoreOptionsHandler,
+ public content::NotificationObserver {
+ public:
+ CoreChromeOSOptionsHandler();
+ ~CoreChromeOSOptionsHandler() override;
+
+ // ::CoreOptionsHandler overrides
+ void RegisterMessages() override;
+ std::unique_ptr<base::Value> FetchPref(const std::string& pref_name) override;
+ void InitializeHandler() override;
+ void ObservePref(const std::string& pref_name) override;
+ void SetPref(const std::string& pref_name,
+ const base::Value* value,
+ const std::string& metric) override;
+ void StopObservingPref(const std::string& path) override;
+ std::unique_ptr<base::Value> CreateValueForPref(
+ const std::string& pref_name,
+ const std::string& controlling_pref_name) override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+
+ // content::NotificationObserver implementation.
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ private:
+ void OnPreferenceChanged(PrefService* service,
+ const std::string& pref_name) override;
+
+ // Called from Javascript to select the network to show proxy settings
+ // for. Triggers pref notifications about the updated proxy settings.
+ void SelectNetworkCallback(const base::ListValue* args);
+
+ // Notifies registered JS callbacks on ChromeOS setting change.
+ void NotifySettingsChanged(const std::string& setting_name);
+ void NotifyProxyPrefsChanged();
+
+ // Called on changes to the ownership status. Needed to update the interface
+ // in case it has been shown before ownership has been fully established.
+ void NotifyOwnershipChanged();
+
+ typedef std::map<std::string, linked_ptr<CrosSettings::ObserverSubscription> >
+ SubscriptionMap;
+ SubscriptionMap pref_subscription_map_;
+
+ content::NotificationRegistrar notification_registrar_;
+
+ // Currently selected network id.
+ std::string network_guid_;
+
+ DISALLOW_COPY_AND_ASSIGN(CoreChromeOSOptionsHandler);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_CORE_CHROMEOS_OPTIONS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler.cc b/chromium/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler.cc
new file mode 100644
index 00000000000..1c938f22190
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler.cc
@@ -0,0 +1,279 @@
+// 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/ui/webui/options/chromeos/cros_language_options_handler.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/chromeos/input_method/input_method_util.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/chromeos/login/l10n_util.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/user_manager/user_manager.h"
+#include "components/user_manager/user_type.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/manifest_url_handlers.h"
+#include "ui/base/ime/chromeos/component_extension_ime_manager.h"
+#include "ui/base/ime/chromeos/extension_ime_util.h"
+#include "ui/base/ime/chromeos/input_method_manager.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using base::UserMetricsAction;
+
+namespace chromeos {
+namespace options {
+
+CrosLanguageOptionsHandler::CrosLanguageOptionsHandler() {
+}
+
+CrosLanguageOptionsHandler::~CrosLanguageOptionsHandler() {
+}
+
+void CrosLanguageOptionsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ ::options::LanguageOptionsHandlerCommon::GetLocalizedValues(
+ localized_strings);
+
+ RegisterTitle(localized_strings, "languagePage",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_AND_INPUT_DIALOG_TITLE);
+ localized_strings->SetString("okButton", l10n_util::GetStringUTF16(IDS_OK));
+ localized_strings->SetString("configure",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_LANGUAGES_CONFIGURE));
+ localized_strings->SetString("inputMethod",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_LANGUAGES_INPUT_METHOD));
+ localized_strings->SetString("pleaseAddAnotherInputMethod",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_PLEASE_ADD_ANOTHER_INPUT_METHOD));
+ localized_strings->SetString("inputMethodInstructions",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_INPUT_METHOD_INSTRUCTIONS));
+ localized_strings->SetString("switchInputMethodsHint",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_SWITCH_INPUT_METHODS_HINT));
+ localized_strings->SetString("selectPreviousInputMethodHint",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_SELECT_PREVIOUS_INPUT_METHOD_HINT));
+ localized_strings->SetString("restartButton",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_SIGN_OUT_BUTTON));
+ localized_strings->SetString("extensionImeLable",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_INPUT_METHOD_EXTENSION_IME));
+ localized_strings->SetString("extensionImeDescription",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_INPUT_METHOD_EXTENSION_DESCRIPTION));
+ localized_strings->SetString("noInputMethods",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_NO_INPUT_METHODS));
+ localized_strings->SetString("activateImeMenu",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_ACTIVATE_IME_MENU));
+
+ // GetSupportedInputMethods() never returns NULL.
+ localized_strings->Set("languageList", GetAcceptLanguageList());
+ localized_strings->Set("inputMethodList", GetInputMethodList());
+
+ input_method::InputMethodManager* manager =
+ input_method::InputMethodManager::Get();
+ input_method::InputMethodDescriptors ext_ime_descriptors;
+ manager->GetActiveIMEState()->GetInputMethodExtensions(&ext_ime_descriptors);
+
+ std::unique_ptr<base::ListValue> ext_ime_list =
+ ConvertInputMethodDescriptorsToIMEList(ext_ime_descriptors);
+ AddImeProvider(ext_ime_list.get());
+ localized_strings->Set("extensionImeList", std::move(ext_ime_list));
+
+ ComponentExtensionIMEManager* component_extension_manager =
+ input_method::InputMethodManager::Get()
+ ->GetComponentExtensionIMEManager();
+ localized_strings->Set(
+ "componentExtensionImeList",
+ ConvertInputMethodDescriptorsToIMEList(
+ component_extension_manager->GetAllIMEAsInputMethodDescriptor()));
+}
+
+void CrosLanguageOptionsHandler::RegisterMessages() {
+ ::options::LanguageOptionsHandlerCommon::RegisterMessages();
+
+ web_ui()->RegisterMessageCallback("inputMethodDisable",
+ base::Bind(&CrosLanguageOptionsHandler::InputMethodDisableCallback,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("inputMethodEnable",
+ base::Bind(&CrosLanguageOptionsHandler::InputMethodEnableCallback,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("inputMethodOptionsOpen",
+ base::Bind(&CrosLanguageOptionsHandler::InputMethodOptionsOpenCallback,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("uiLanguageRestart",
+ base::Bind(&CrosLanguageOptionsHandler::RestartCallback,
+ base::Unretained(this)));
+}
+
+// static
+std::unique_ptr<base::ListValue>
+CrosLanguageOptionsHandler::GetInputMethodList() {
+ input_method::InputMethodManager* manager =
+ input_method::InputMethodManager::Get();
+ // GetSupportedInputMethods() never return NULL.
+ std::unique_ptr<input_method::InputMethodDescriptors> descriptors(
+ manager->GetSupportedInputMethods());
+
+ auto input_method_list = base::MakeUnique<base::ListValue>();
+
+ for (size_t i = 0; i < descriptors->size(); ++i) {
+ const input_method::InputMethodDescriptor& descriptor =
+ (*descriptors)[i];
+ const std::string display_name =
+ manager->GetInputMethodUtil()->GetInputMethodDisplayNameFromId(
+ descriptor.id());
+ auto dictionary = base::MakeUnique<base::DictionaryValue>();
+ dictionary->SetString("id", descriptor.id());
+ dictionary->SetString("displayName", display_name);
+
+ // One input method can be associated with multiple languages, hence
+ // we use a dictionary here.
+ auto languages = base::MakeUnique<base::DictionaryValue>();
+ for (size_t i = 0; i < descriptor.language_codes().size(); ++i) {
+ languages->SetBoolean(descriptor.language_codes().at(i), true);
+ }
+ dictionary->Set("languageCodeSet", std::move(languages));
+
+ input_method_list->Append(std::move(dictionary));
+ }
+
+ return input_method_list;
+}
+
+std::unique_ptr<base::ListValue>
+CrosLanguageOptionsHandler::ConvertInputMethodDescriptorsToIMEList(
+ const input_method::InputMethodDescriptors& descriptors) {
+ input_method::InputMethodUtil* util =
+ input_method::InputMethodManager::Get()->GetInputMethodUtil();
+ std::unique_ptr<base::ListValue> ime_ids_list(new base::ListValue());
+ for (size_t i = 0; i < descriptors.size(); ++i) {
+ const input_method::InputMethodDescriptor& descriptor = descriptors[i];
+ std::unique_ptr<base::DictionaryValue> dictionary(
+ new base::DictionaryValue());
+ dictionary->SetString("id", descriptor.id());
+ dictionary->SetString(
+ "displayName", util->GetLocalizedDisplayName(descriptor));
+ dictionary->SetString("optionsPage", descriptor.options_page_url().spec());
+ std::unique_ptr<base::DictionaryValue> language_codes(
+ new base::DictionaryValue());
+ for (size_t i = 0; i < descriptor.language_codes().size(); ++i)
+ language_codes->SetBoolean(descriptor.language_codes().at(i), true);
+ dictionary->Set("languageCodeSet", std::move(language_codes));
+ ime_ids_list->Append(std::move(dictionary));
+ }
+ return ime_ids_list;
+}
+
+void CrosLanguageOptionsHandler::SetApplicationLocale(
+ const std::string& language_code) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ user_manager::UserManager* user_manager = user_manager::UserManager::Get();
+
+ // Secondary users and public session users cannot change the locale.
+ const user_manager::User* user =
+ ProfileHelper::Get()->GetUserByProfile(profile);
+ if (user &&
+ user->GetAccountId() == user_manager->GetPrimaryUser()->GetAccountId() &&
+ user->GetType() != user_manager::USER_TYPE_PUBLIC_ACCOUNT) {
+ profile->ChangeAppLocale(language_code,
+ Profile::APP_LOCALE_CHANGED_VIA_SETTINGS);
+ }
+}
+
+void CrosLanguageOptionsHandler::RestartCallback(const base::ListValue* args) {
+ base::RecordAction(UserMetricsAction("LanguageOptions_SignOut"));
+ chrome::AttemptUserExit();
+}
+
+void CrosLanguageOptionsHandler::InputMethodDisableCallback(
+ const base::ListValue* args) {
+ const std::string input_method_id =
+ base::UTF16ToASCII(ExtractStringValue(args));
+ const std::string action = base::StringPrintf(
+ "LanguageOptions_DisableInputMethod_%s", input_method_id.c_str());
+ base::RecordComputedAction(action);
+}
+
+void CrosLanguageOptionsHandler::InputMethodEnableCallback(
+ const base::ListValue* args) {
+ const std::string input_method_id =
+ base::UTF16ToASCII(ExtractStringValue(args));
+ const std::string action = base::StringPrintf(
+ "LanguageOptions_EnableInputMethod_%s", input_method_id.c_str());
+ base::RecordComputedAction(action);
+}
+
+void CrosLanguageOptionsHandler::InputMethodOptionsOpenCallback(
+ const base::ListValue* args) {
+ const std::string input_method_id =
+ base::UTF16ToASCII(ExtractStringValue(args));
+ const std::string extension_id =
+ extension_ime_util::GetExtensionIDFromInputMethodID(input_method_id);
+ if (extension_id.empty())
+ return;
+
+ const input_method::InputMethodDescriptor* ime =
+ input_method::InputMethodManager::Get()
+ ->GetActiveIMEState()
+ ->GetInputMethodFromId(input_method_id);
+ if (!ime)
+ return;
+
+ Browser* browser = chrome::FindBrowserWithWebContents(
+ web_ui()->GetWebContents());
+ content::OpenURLParams params(ime->options_page_url(), content::Referrer(),
+ WindowOpenDisposition::SINGLETON_TAB,
+ ui::PAGE_TRANSITION_LINK, false);
+ browser->OpenURL(params);
+}
+
+void CrosLanguageOptionsHandler::AddImeProvider(base::ListValue* list) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ const extensions::ExtensionSet& enabled_extensions =
+ extensions::ExtensionRegistry::Get(profile)->enabled_extensions();
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ base::DictionaryValue* entry;
+ list->GetDictionary(i, &entry);
+
+ std::string input_method_id;
+ entry->GetString("id", &input_method_id);
+
+ std::string extension_id =
+ extension_ime_util::GetExtensionIDFromInputMethodID(input_method_id);
+ const extensions::Extension* extension =
+ enabled_extensions.GetByID(extension_id);
+ if (extension)
+ entry->SetString("extensionName", extension->name());
+ }
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler.h b/chromium/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler.h
new file mode 100644
index 00000000000..0a657c14e9a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler.h
@@ -0,0 +1,86 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_CROS_LANGUAGE_OPTIONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_CROS_LANGUAGE_OPTIONS_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/language_options_handler.h"
+#include "ui/base/ime/chromeos/component_extension_ime_manager.h"
+#include "ui/base/ime/chromeos/input_method_descriptor.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace chromeos {
+namespace options {
+
+// Language options page UI handler for Chrome OS. For non-Chrome OS,
+// see LanguageOptionsHnadler.
+class CrosLanguageOptionsHandler
+ : public ::options::LanguageOptionsHandlerCommon {
+ public:
+ CrosLanguageOptionsHandler();
+ ~CrosLanguageOptionsHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+
+ // DOMMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // The following static methods are public for ease of testing.
+
+ // Gets the list of supported input methods.
+ // The return value will look like:
+ // [{'id': 'pinyin', 'displayName': 'Pinyin',
+ // 'languageCodeSet': {'zh-CW': true}}, ...]
+ //
+ // Note that true in languageCodeSet does not mean anything. We just use
+ // the dictionary as a set.
+ static std::unique_ptr<base::ListValue> GetInputMethodList();
+
+ // Converts input method descriptors to the list of input methods.
+ // The return value will look like:
+ // [{'id': '_ext_ime_nejguenhnsnjnwychcnsdsdjketest',
+ // 'displayName': 'Sample IME'}, ...]
+ static std::unique_ptr<base::ListValue>
+ ConvertInputMethodDescriptorsToIMEList(
+ const input_method::InputMethodDescriptors& descriptors);
+
+ private:
+ // LanguageOptionsHandlerCommon implementation.
+ void SetApplicationLocale(const std::string& language_code) override;
+
+ // Called when the sign-out button is clicked.
+ void RestartCallback(const base::ListValue* args);
+
+ // Called when the input method is disabled.
+ // |args| will contain the input method ID as string (ex. "mozc").
+ void InputMethodDisableCallback(const base::ListValue* args);
+
+ // Called when the input method is enabled.
+ // |args| will contain the input method ID as string (ex. "mozc").
+ void InputMethodEnableCallback(const base::ListValue* args);
+
+ // Called when the input method options page is opened.
+ // |args| will contain the input method ID as string (ex. "mozc").
+ void InputMethodOptionsOpenCallback(const base::ListValue* args);
+
+ // Adds the name of the extension that provides the IME to each entry in the
+ // |list| of extension IMEs.
+ void AddImeProvider(base::ListValue* list);
+
+ DISALLOW_COPY_AND_ASSIGN(CrosLanguageOptionsHandler);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_CROS_LANGUAGE_OPTIONS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler_unittest.cc b/chromium/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler_unittest.cc
new file mode 100644
index 00000000000..74dbe665a63
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler_unittest.cc
@@ -0,0 +1,92 @@
+// 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/ui/webui/options/chromeos/cros_language_options_handler.h"
+
+#include <memory>
+
+#include "base/values.h"
+#include "chrome/browser/chromeos/input_method/input_method_configuration.h"
+#include "chrome/browser/ui/webui/chromeos/login/l10n_util_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+namespace options {
+
+class CrosLanguageOptionsHandlerTest : public testing::Test {
+ public:
+ CrosLanguageOptionsHandlerTest()
+ : input_manager_(new MockInputMethodManagerWithInputMethods) {
+ chromeos::input_method::InitializeForTesting(input_manager_);
+ }
+
+ ~CrosLanguageOptionsHandlerTest() override {
+ chromeos::input_method::Shutdown();
+ }
+
+ // testing::Test:
+ void SetUp() override {
+ input_manager_->AddInputMethod("xkb:us::eng", "us", "en-US");
+ input_manager_->AddInputMethod("xkb:fr::fra", "fr", "fr");
+ input_manager_->AddInputMethod("xkb:be::fra", "be", "fr");
+ input_manager_->AddInputMethod("xkb:is::ice", "is", "is");
+ }
+
+ private:
+ MockInputMethodManagerWithInputMethods* input_manager_;
+};
+
+TEST_F(CrosLanguageOptionsHandlerTest, GetInputMethodList) {
+ std::unique_ptr<base::ListValue> list(
+ CrosLanguageOptionsHandler::GetInputMethodList());
+ ASSERT_EQ(4U, list->GetSize());
+
+ base::DictionaryValue* entry = NULL;
+ base::DictionaryValue *language_code_set = NULL;
+ std::string input_method_id;
+ std::string display_name;
+ std::string language_code;
+
+ // As shown below, the list should be input method ids should appear in
+ // the same order of the descriptors.
+ ASSERT_TRUE(list->GetDictionary(0, &entry));
+ ASSERT_TRUE(entry->GetString("id", &input_method_id));
+ ASSERT_TRUE(entry->GetString("displayName", &display_name));
+ ASSERT_TRUE(entry->GetDictionary("languageCodeSet", &language_code_set));
+ EXPECT_EQ("xkb:us::eng", input_method_id);
+ // Commented out as it depends on translation in generated_resources.grd
+ // (i.e. makes the test fragile).
+ // EXPECT_EQ("English (USA) keyboard layout", display_name);
+ ASSERT_TRUE(language_code_set->HasKey("en-US"));
+
+ ASSERT_TRUE(list->GetDictionary(1, &entry));
+ ASSERT_TRUE(entry->GetString("id", &input_method_id));
+ ASSERT_TRUE(entry->GetString("displayName", &display_name));
+ ASSERT_TRUE(entry->GetDictionary("languageCodeSet", &language_code_set));
+ EXPECT_EQ("xkb:fr::fra", input_method_id);
+ // Commented out. See above.
+ // EXPECT_EQ("French keyboard layout", display_name);
+ ASSERT_TRUE(language_code_set->HasKey("fr"));
+
+ ASSERT_TRUE(list->GetDictionary(2, &entry));
+ ASSERT_TRUE(entry->GetString("id", &input_method_id));
+ ASSERT_TRUE(entry->GetString("displayName", &display_name));
+ ASSERT_TRUE(entry->GetDictionary("languageCodeSet", &language_code_set));
+ EXPECT_EQ("xkb:be::fra", input_method_id);
+ // Commented out. See above.
+ // EXPECT_EQ("Belgian keyboard layout", display_name);
+ ASSERT_TRUE(language_code_set->HasKey("fr"));
+
+ ASSERT_TRUE(list->GetDictionary(3, &entry));
+ ASSERT_TRUE(entry->GetString("id", &input_method_id));
+ ASSERT_TRUE(entry->GetString("displayName", &display_name));
+ ASSERT_TRUE(entry->GetDictionary("languageCodeSet", &language_code_set));
+ EXPECT_EQ("xkb:is::ice", input_method_id);
+ // Commented out. See above.
+ // EXPECT_EQ("Japanese input method (for US keyboard)", display_name);
+ ASSERT_TRUE(language_code_set->HasKey("is"));
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/date_time_options_browsertest.js b/chromium/chrome/browser/ui/webui/options/chromeos/date_time_options_browsertest.js
new file mode 100644
index 00000000000..4f0845beb37
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/date_time_options_browsertest.js
@@ -0,0 +1,38 @@
+// 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.
+
+GEN_INCLUDE(['../options_browsertest_base.js']);
+
+GEN('#if defined(OS_CHROMEOS)');
+
+/**
+ * DateTimeOptionsWebUITest tests the date and time section of the options page.
+ * @constructor
+ * @extends {testing.Test}
+ */
+function DateTimeOptionsWebUITest() {}
+
+DateTimeOptionsWebUITest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /**
+ * Browse to date/time options.
+ * @override
+ */
+ browsePreload: 'chrome://settings-frame/search#date',
+};
+
+TEST_F('DateTimeOptionsWebUITest', 'testShowSetTimeButton', function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ // Show button.
+ BrowserOptions.setCanSetTime(true);
+ expectFalse($('set-time').hidden);
+
+ // Hide button.
+ BrowserOptions.setCanSetTime(false);
+ expectTrue($('set-time').hidden);
+});
+
+GEN('#endif');
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/date_time_options_handler.cc b/chromium/chrome/browser/ui/webui/options/chromeos/date_time_options_handler.cc
new file mode 100644
index 00000000000..c26fe069f5a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/date_time_options_handler.cc
@@ -0,0 +1,77 @@
+// 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/ui/webui/options/chromeos/date_time_options_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/set_time_dialog.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/system_clock_client.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace chromeos {
+namespace options {
+
+DateTimeOptionsHandler::DateTimeOptionsHandler()
+ : can_set_time_(false), page_initialized_(false) {
+}
+
+DateTimeOptionsHandler::~DateTimeOptionsHandler() {
+ DBusThreadManager::Get()->GetSystemClockClient()->RemoveObserver(this);
+}
+
+void DateTimeOptionsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ localized_strings->SetString(
+ "setTimeButton",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_SET_TIME_BUTTON));
+}
+
+void DateTimeOptionsHandler::InitializeHandler() {
+ SystemClockClient* system_clock_client =
+ DBusThreadManager::Get()->GetSystemClockClient();
+ system_clock_client->AddObserver(this);
+
+ can_set_time_ = system_clock_client->CanSetTime();
+ SystemClockCanSetTimeChanged(can_set_time_);
+}
+
+void DateTimeOptionsHandler::InitializePage() {
+ page_initialized_ = true;
+ SystemClockCanSetTimeChanged(can_set_time_);
+}
+
+void DateTimeOptionsHandler::RegisterMessages() {
+ // Callback for set time button.
+ web_ui()->RegisterMessageCallback(
+ "showSetTime",
+ base::Bind(&DateTimeOptionsHandler::HandleShowSetTime,
+ base::Unretained(this)));
+}
+
+void DateTimeOptionsHandler::SystemClockCanSetTimeChanged(bool can_set_time) {
+ if (page_initialized_) {
+ web_ui()->CallJavascriptFunctionUnsafe("BrowserOptions.setCanSetTime",
+ base::Value(can_set_time));
+ }
+ can_set_time_ = can_set_time;
+}
+
+void DateTimeOptionsHandler::HandleShowSetTime(const base::ListValue* args) {
+ // Make sure the clock status hasn't changed since the button was clicked.
+ if (can_set_time_) {
+ SetTimeDialog::ShowDialogInParent(
+ web_ui()->GetWebContents()->GetTopLevelNativeWindow());
+ }
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/date_time_options_handler.h b/chromium/chrome/browser/ui/webui/options/chromeos/date_time_options_handler.h
new file mode 100644
index 00000000000..8313d8a83ab
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/date_time_options_handler.h
@@ -0,0 +1,47 @@
+// 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_UI_WEBUI_OPTIONS_CHROMEOS_DATE_TIME_OPTIONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_DATE_TIME_OPTIONS_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "chromeos/dbus/system_clock_client.h"
+
+namespace chromeos {
+namespace options {
+
+// Chrome OS handler for the set date/time link in the Advanced settings page.
+class DateTimeOptionsHandler : public ::options::OptionsPageUIHandler,
+ public SystemClockClient::Observer {
+ public:
+ DateTimeOptionsHandler();
+ ~DateTimeOptionsHandler() override;
+
+ // OptionsPageUIHandler:
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializeHandler() override;
+ void InitializePage() override;
+ void RegisterMessages() override;
+
+ private:
+ // SystemClockClient::Observer:
+ void SystemClockCanSetTimeChanged(bool can_set_time) override;
+
+ // Callback for the "showSetTime" message to show the set time dialog. No
+ // arguments are expected.
+ void HandleShowSetTime(const base::ListValue* args);
+
+ // Only expose the button and dialog if the system time can be set.
+ bool can_set_time_;
+ bool page_initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(DateTimeOptionsHandler);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_DATE_TIME_OPTIONS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc b/chromium/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc
new file mode 100644
index 00000000000..f18f3b2131e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc
@@ -0,0 +1,581 @@
+// 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/ui/webui/options/chromeos/display_options_handler.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "ash/display/display_configuration_controller.h"
+#include "ash/display/resolution_notification_controller.h"
+#include "ash/display/window_tree_host_manager.h"
+#include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/display/display_preferences.h"
+#include "chrome/browser/ui/ash/ash_util.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/display/display.h"
+#include "ui/display/display_layout.h"
+#include "ui/display/display_layout_builder.h"
+#include "ui/display/manager/display_manager.h"
+#include "ui/display/screen.h"
+#include "ui/display/types/display_constants.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size_conversions.h"
+
+namespace chromeos {
+namespace options {
+namespace {
+
+display::DisplayManager* GetDisplayManager() {
+ return ash::Shell::Get()->display_manager();
+}
+
+ash::DisplayConfigurationController* GetDisplayConfigurationController() {
+ return ash::Shell::Get()->display_configuration_controller();
+}
+
+int64_t GetDisplayIdFromValue(const base::Value* arg) {
+ std::string id_value;
+ if (!arg->GetAsString(&id_value))
+ return display::kInvalidDisplayId;
+ int64_t display_id = display::kInvalidDisplayId;
+ if (!base::StringToInt64(id_value, &display_id))
+ return display::kInvalidDisplayId;
+ return display_id;
+}
+
+int64_t GetDisplayIdFromArgs(const base::ListValue* args) {
+ const base::Value* arg;
+ if (!args->Get(0, &arg)) {
+ LOG(ERROR) << "No display id arg";
+ return display::kInvalidDisplayId;
+ }
+ int64_t display_id = GetDisplayIdFromValue(arg);
+ if (display_id == display::kInvalidDisplayId)
+ LOG(ERROR) << "Invalid display id: " << *arg;
+ return display_id;
+}
+
+int64_t GetDisplayIdFromDictionary(const base::DictionaryValue* dictionary,
+ const std::string& key) {
+ const base::Value* arg;
+ if (!dictionary->Get(key, &arg))
+ return display::kInvalidDisplayId;
+ return GetDisplayIdFromValue(arg);
+}
+
+base::string16 GetColorProfileName(display::ColorCalibrationProfile profile) {
+ switch (profile) {
+ case display::COLOR_PROFILE_STANDARD:
+ return l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_STANDARD);
+ case display::COLOR_PROFILE_DYNAMIC:
+ return l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_DYNAMIC);
+ case display::COLOR_PROFILE_MOVIE:
+ return l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_MOVIE);
+ case display::COLOR_PROFILE_READING:
+ return l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE_READING);
+ case display::NUM_COLOR_PROFILES:
+ break;
+ }
+
+ NOTREACHED();
+ return base::string16();
+}
+
+int GetIntOrDouble(const base::DictionaryValue* dict,
+ const std::string& field) {
+ double double_result = 0;
+ if (dict->GetDouble(field, &double_result))
+ return static_cast<int>(double_result);
+
+ int result = 0;
+ dict->GetInteger(field, &result);
+ return result;
+}
+
+bool GetFloat(const base::DictionaryValue* dict,
+ const std::string& field,
+ float* result) {
+ double double_result = 0;
+ if (dict->GetDouble(field, &double_result)) {
+ *result = static_cast<float>(double_result);
+ return true;
+ }
+ return false;
+}
+
+scoped_refptr<display::ManagedDisplayMode> ConvertValueToManagedDisplayMode(
+ const base::DictionaryValue* dict) {
+ scoped_refptr<display::ManagedDisplayMode> mode;
+
+ gfx::Size size;
+ size.set_width(GetIntOrDouble(dict, "originalWidth"));
+ size.set_height(GetIntOrDouble(dict, "originalHeight"));
+
+ if (size.IsEmpty()) {
+ LOG(ERROR) << "missing width or height.";
+ return mode;
+ }
+
+ float refresh_rate, ui_scale, device_scale_factor;
+ if (!GetFloat(dict, "refreshRate", &refresh_rate)) {
+ LOG(ERROR) << "missing refreshRate.";
+ return mode;
+ }
+ if (!GetFloat(dict, "scale", &ui_scale)) {
+ LOG(ERROR) << "missing ui-scale.";
+ return mode;
+ }
+ if (!GetFloat(dict, "deviceScaleFactor", &device_scale_factor)) {
+ LOG(ERROR) << "missing deviceScaleFactor.";
+ return mode;
+ }
+
+ // Used to select the actual mode.
+ mode = new display::ManagedDisplayMode(
+ size, refresh_rate, false /* interlaced */, false /* native */, ui_scale,
+ device_scale_factor);
+ return mode;
+}
+
+std::unique_ptr<base::DictionaryValue> ConvertDisplayModeToValue(
+ int64_t display_id,
+ const scoped_refptr<display::ManagedDisplayMode>& mode) {
+ bool is_internal = display::Display::HasInternalDisplay() &&
+ display::Display::InternalDisplayId() == display_id;
+ auto result = base::MakeUnique<base::DictionaryValue>();
+ gfx::Size size_dip = mode->GetSizeInDIP(is_internal);
+ result->SetInteger("width", size_dip.width());
+ result->SetInteger("height", size_dip.height());
+ result->SetInteger("originalWidth", mode->size().width());
+ result->SetInteger("originalHeight", mode->size().height());
+ result->SetDouble("deviceScaleFactor", mode->device_scale_factor());
+ result->SetDouble("scale", mode->ui_scale());
+ result->SetDouble("refreshRate", mode->refresh_rate());
+ result->SetBoolean("isBest",
+ is_internal ? (mode->ui_scale() == 1.0f) : mode->native());
+ result->SetBoolean("isNative", mode->native());
+ result->SetBoolean(
+ "selected",
+ mode->IsEquivalent(
+ GetDisplayManager()->GetActiveModeForDisplayId(display_id)));
+ return result;
+}
+
+std::unique_ptr<base::DictionaryValue> ConvertBoundsToValue(
+ const gfx::Rect& bounds) {
+ auto result = base::MakeUnique<base::DictionaryValue>();
+ result->SetInteger("left", bounds.x());
+ result->SetInteger("top", bounds.y());
+ result->SetInteger("width", bounds.width());
+ result->SetInteger("height", bounds.height());
+ return result;
+}
+
+} // namespace
+
+DisplayOptionsHandler::DisplayOptionsHandler() {
+ // TODO(mash) Support Chrome display settings in Mash. crbug.com/548429
+ if (!ash_util::IsRunningInMash())
+ ash::Shell::Get()->window_tree_host_manager()->AddObserver(this);
+}
+
+DisplayOptionsHandler::~DisplayOptionsHandler() {
+ // TODO(mash) Support Chrome display settings in Mash. crbug.com/548429
+ if (!ash_util::IsRunningInMash())
+ ash::Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
+}
+
+void DisplayOptionsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+ RegisterTitle(localized_strings, "displayOptionsPage",
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_TAB_TITLE);
+
+ localized_strings->SetString(
+ "selectedDisplayTitleOptions", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OPTIONS));
+ localized_strings->SetString(
+ "selectedDisplayTitleResolution", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION));
+ localized_strings->SetString(
+ "selectedDisplayTitleOrientation", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_ORIENTATION));
+ localized_strings->SetString(
+ "selectedDisplayTitleOverscan", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_OVERSCAN));
+
+ localized_strings->SetString("extendedMode", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_EXTENDED_MODE_LABEL));
+ localized_strings->SetString("mirroringMode", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_MIRRORING_MODE_LABEL));
+ localized_strings->SetString("mirroringDisplay", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_MIRRORING_DISPLAY_NAME));
+ localized_strings->SetString("setPrimary", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_SET_PRIMARY));
+ localized_strings->SetString("annotateBest", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION_ANNOTATION_BEST));
+ localized_strings->SetString("annotateNative", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_RESOLUTION_ANNOTATION_NATIVE));
+ localized_strings->SetString("orientation0", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_STANDARD_ORIENTATION));
+ localized_strings->SetString("orientation90", l10n_util::GetStringUTF16(
+ IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90));
+ localized_strings->SetString("orientation180", l10n_util::GetStringUTF16(
+ IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180));
+ localized_strings->SetString("orientation270", l10n_util::GetStringUTF16(
+ IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270));
+ localized_strings->SetString(
+ "startCalibratingOverscan", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_START_CALIBRATING_OVERSCAN));
+ localized_strings->SetString(
+ "selectedDisplayColorProfile", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_COLOR_PROFILE));
+ localized_strings->SetString(
+ "enableUnifiedDesktop",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OPTIONS_ENABLE_UNIFIED_DESKTOP));
+}
+
+void DisplayOptionsHandler::InitializePage() {
+ DCHECK(web_ui());
+ UpdateDisplaySettingsEnabled();
+}
+
+void DisplayOptionsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "getDisplayInfo",
+ base::Bind(&DisplayOptionsHandler::HandleDisplayInfo,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setMirroring",
+ base::Bind(&DisplayOptionsHandler::HandleMirroring,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setPrimary",
+ base::Bind(&DisplayOptionsHandler::HandleSetPrimary,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setDisplayLayout",
+ base::Bind(&DisplayOptionsHandler::HandleSetDisplayLayout,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setDisplayMode",
+ base::Bind(&DisplayOptionsHandler::HandleSetDisplayMode,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setRotation",
+ base::Bind(&DisplayOptionsHandler::HandleSetRotation,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setColorProfile",
+ base::Bind(&DisplayOptionsHandler::HandleSetColorProfile,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setUnifiedDesktopEnabled",
+ base::Bind(&DisplayOptionsHandler::HandleSetUnifiedDesktopEnabled,
+ base::Unretained(this)));
+}
+
+void DisplayOptionsHandler::OnDisplayConfigurationChanging() {
+}
+
+void DisplayOptionsHandler::OnDisplayConfigurationChanged() {
+ UpdateDisplaySettingsEnabled();
+ SendAllDisplayInfo();
+}
+
+void DisplayOptionsHandler::SendAllDisplayInfo() {
+ display::DisplayManager* display_manager = GetDisplayManager();
+
+ std::vector<display::Display> displays;
+ for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i)
+ displays.push_back(display_manager->GetDisplayAt(i));
+
+ display::DisplayManager::MultiDisplayMode display_mode;
+ if (display_manager->IsInMirrorMode())
+ display_mode = display::DisplayManager::MIRRORING;
+ else if (display_manager->IsInUnifiedMode())
+ display_mode = display::DisplayManager::UNIFIED;
+ else
+ display_mode = display::DisplayManager::EXTENDED;
+ base::Value mode(static_cast<int>(display_mode));
+
+ int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
+ std::unique_ptr<base::ListValue> js_displays(new base::ListValue);
+ for (const display::Display& display : displays) {
+ const display::ManagedDisplayInfo& display_info =
+ display_manager->GetDisplayInfo(display.id());
+ auto js_display = base::MakeUnique<base::DictionaryValue>();
+ js_display->SetString("id", base::Int64ToString(display.id()));
+ js_display->SetString("name",
+ display_manager->GetDisplayNameForId(display.id()));
+ js_display->Set("bounds", ConvertBoundsToValue(display.bounds()));
+ js_display->SetBoolean("isPrimary", display.id() == primary_id);
+ js_display->SetBoolean("isInternal", display.IsInternal());
+ js_display->SetInteger("rotation", display.RotationAsDegree());
+
+ auto js_resolutions = base::MakeUnique<base::ListValue>();
+ for (const scoped_refptr<display::ManagedDisplayMode>& display_mode :
+ display_info.display_modes()) {
+ js_resolutions->Append(
+ ConvertDisplayModeToValue(display.id(), display_mode));
+ }
+ js_display->Set("resolutions", std::move(js_resolutions));
+
+ js_display->SetInteger("colorProfileId", display_info.color_profile());
+ auto available_color_profiles = base::MakeUnique<base::ListValue>();
+ for (const auto& color_profile : display_info.available_color_profiles()) {
+ const base::string16 profile_name = GetColorProfileName(color_profile);
+ if (profile_name.empty())
+ continue;
+ auto color_profile_dict = base::MakeUnique<base::DictionaryValue>();
+ color_profile_dict->SetInteger("profileId", color_profile);
+ color_profile_dict->SetString("name", profile_name);
+ available_color_profiles->Append(std::move(color_profile_dict));
+ }
+ js_display->Set("availableColorProfiles",
+ std::move(available_color_profiles));
+
+ if (display_manager->GetNumDisplays() > 1) {
+ // The settings UI must use the resolved display layout to show the
+ // actual applied layout.
+ const display::DisplayPlacement placement =
+ display_manager->GetCurrentResolvedDisplayLayout().FindPlacementById(
+ display.id());
+ if (placement.display_id != display::kInvalidDisplayId) {
+ js_display->SetString(
+ "parentId", base::Int64ToString(placement.parent_display_id));
+ js_display->SetInteger("layoutType", placement.position);
+ js_display->SetInteger("offset", placement.offset);
+ }
+ }
+
+ js_displays->Append(std::move(js_display));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.DisplayOptions.setDisplayInfo", mode, *js_displays);
+}
+
+void DisplayOptionsHandler::UpdateDisplaySettingsEnabled() {
+ // TODO(mash) Support Chrome display settings in Mash. crbug.com/548429
+ if (ash_util::IsRunningInMash())
+ return;
+
+ display::DisplayManager* display_manager = GetDisplayManager();
+ bool disable_multi_display_layout =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kDisableMultiDisplayLayout);
+ bool ui_enabled = display_manager->num_connected_displays() <= 2 ||
+ !disable_multi_display_layout;
+ bool unified_enabled = display_manager->unified_desktop_enabled();
+ bool mirrored_enabled = display_manager->num_connected_displays() == 2;
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.BrowserOptions.enableDisplaySettings", base::Value(ui_enabled),
+ base::Value(unified_enabled), base::Value(mirrored_enabled));
+}
+
+void DisplayOptionsHandler::HandleDisplayInfo(
+ const base::ListValue* unused_args) {
+ SendAllDisplayInfo();
+}
+
+void DisplayOptionsHandler::HandleMirroring(const base::ListValue* args) {
+ DCHECK(!args->empty());
+ bool is_mirroring = false;
+ if (!args->GetBoolean(0, &is_mirroring))
+ NOTREACHED();
+ base::RecordAction(base::UserMetricsAction("Options_DisplayToggleMirroring"));
+ GetDisplayConfigurationController()->SetMirrorMode(is_mirroring);
+}
+
+void DisplayOptionsHandler::HandleSetPrimary(const base::ListValue* args) {
+ DCHECK(!args->empty());
+ int64_t display_id = GetDisplayIdFromArgs(args);
+ if (display_id == display::kInvalidDisplayId)
+ return;
+
+ base::RecordAction(base::UserMetricsAction("Options_DisplaySetPrimary"));
+ GetDisplayConfigurationController()->SetPrimaryDisplayId(display_id);
+}
+
+void DisplayOptionsHandler::HandleSetDisplayLayout(
+ const base::ListValue* args) {
+ const base::ListValue* layouts = nullptr;
+ if (!args->GetList(0, &layouts))
+ NOTREACHED();
+ base::RecordAction(base::UserMetricsAction("Options_DisplayRearrange"));
+
+ display::DisplayManager* display_manager = GetDisplayManager();
+ display::DisplayLayoutBuilder builder(
+ display_manager->GetCurrentDisplayLayout());
+ builder.ClearPlacements();
+ for (const auto& layout : *layouts) {
+ const base::DictionaryValue* dictionary;
+ if (!layout.GetAsDictionary(&dictionary)) {
+ LOG(ERROR) << "Invalid layout dictionary: " << *dictionary;
+ continue;
+ }
+
+ int64_t parent_id = GetDisplayIdFromDictionary(dictionary, "parentId");
+ if (parent_id == display::kInvalidDisplayId)
+ continue; // No placement for root (primary) display.
+
+ int64_t display_id = GetDisplayIdFromDictionary(dictionary, "id");
+ if (display_id == display::kInvalidDisplayId) {
+ LOG(ERROR) << "Invalud display id in layout dictionary: " << *dictionary;
+ continue;
+ }
+
+ int position = 0;
+ dictionary->GetInteger("layoutType", &position);
+ int offset = 0;
+ dictionary->GetInteger("offset", &offset);
+
+ builder.AddDisplayPlacement(
+ display_id, parent_id,
+ static_cast<display::DisplayPlacement::Position>(position), offset);
+ }
+ std::unique_ptr<display::DisplayLayout> layout = builder.Build();
+ if (!display::DisplayLayout::Validate(
+ display_manager->GetCurrentDisplayIdList(), *layout)) {
+ LOG(ERROR) << "Invalid layout: " << layout->ToString();
+ return;
+ }
+
+ VLOG(1) << "Updating display layout: " << layout->ToString();
+ GetDisplayConfigurationController()->SetDisplayLayout(std::move(layout));
+}
+
+void DisplayOptionsHandler::HandleSetDisplayMode(const base::ListValue* args) {
+ DCHECK(!args->empty());
+
+ int64_t display_id = GetDisplayIdFromArgs(args);
+ if (display_id == display::kInvalidDisplayId)
+ return;
+
+ const base::DictionaryValue* mode_data = nullptr;
+ if (!args->GetDictionary(1, &mode_data)) {
+ LOG(ERROR) << "Failed to get mode data";
+ return;
+ }
+
+ scoped_refptr<display::ManagedDisplayMode> mode =
+ ConvertValueToManagedDisplayMode(mode_data);
+ if (!mode)
+ return;
+
+ base::RecordAction(base::UserMetricsAction("Options_DisplaySetResolution"));
+ display::DisplayManager* display_manager = GetDisplayManager();
+ scoped_refptr<display::ManagedDisplayMode> current_mode =
+ display_manager->GetActiveModeForDisplayId(display_id);
+
+ if (mode->IsEquivalent(current_mode)) {
+ LOG(ERROR) << "New display mode matches current mode.";
+ return;
+ }
+
+ if (!ash::Shell::Get()
+ ->resolution_notification_controller()
+ ->PrepareNotificationAndSetDisplayMode(
+ display_id, current_mode, mode,
+ base::Bind(&chromeos::StoreDisplayPrefs))) {
+ LOG(ERROR) << "Unable to set display mode for: " << display_id
+ << " Mode: " << *mode_data;
+ }
+}
+
+void DisplayOptionsHandler::HandleSetRotation(const base::ListValue* args) {
+ DCHECK(!args->empty());
+
+ int64_t display_id = GetDisplayIdFromArgs(args);
+ if (display_id == display::kInvalidDisplayId)
+ return;
+
+ int rotation_value = 0;
+ if (!args->GetInteger(1, &rotation_value)) {
+ LOG(ERROR) << "Can't parse rotation: " << args;
+ return;
+ }
+ display::Display::Rotation new_rotation = display::Display::ROTATE_0;
+ if (rotation_value == 90)
+ new_rotation = display::Display::ROTATE_90;
+ else if (rotation_value == 180)
+ new_rotation = display::Display::ROTATE_180;
+ else if (rotation_value == 270)
+ new_rotation = display::Display::ROTATE_270;
+ else if (rotation_value != 0)
+ LOG(ERROR) << "Invalid rotation: " << rotation_value << " Falls back to 0";
+
+ base::RecordAction(base::UserMetricsAction("Options_DisplaySetOrientation"));
+ GetDisplayConfigurationController()->SetDisplayRotation(
+ display_id, new_rotation, display::Display::ROTATION_SOURCE_USER);
+}
+
+void DisplayOptionsHandler::HandleSetColorProfile(const base::ListValue* args) {
+ DCHECK(!args->empty());
+ int64_t display_id = GetDisplayIdFromArgs(args);
+ if (display_id == display::kInvalidDisplayId)
+ return;
+
+ std::string profile_value;
+ if (!args->GetString(1, &profile_value)) {
+ LOG(ERROR) << "Invalid profile_value";
+ return;
+ }
+
+ int profile_id;
+ if (!base::StringToInt(profile_value, &profile_id)) {
+ LOG(ERROR) << "Invalid profile: " << profile_value;
+ return;
+ }
+
+ if (profile_id < display::COLOR_PROFILE_STANDARD ||
+ profile_id > display::COLOR_PROFILE_READING) {
+ LOG(ERROR) << "Invalid profile_id: " << profile_id;
+ return;
+ }
+
+ base::RecordAction(base::UserMetricsAction("Options_DisplaySetColorProfile"));
+ GetDisplayManager()->SetColorCalibrationProfile(
+ display_id, static_cast<display::ColorCalibrationProfile>(profile_id));
+
+ SendAllDisplayInfo();
+}
+
+void DisplayOptionsHandler::HandleSetUnifiedDesktopEnabled(
+ const base::ListValue* args) {
+ DCHECK(GetDisplayManager()->unified_desktop_enabled());
+ bool enable = false;
+ if (!args->GetBoolean(0, &enable))
+ NOTREACHED();
+
+ GetDisplayManager()->SetDefaultMultiDisplayModeForCurrentDisplays(
+ enable ? display::DisplayManager::UNIFIED
+ : display::DisplayManager::EXTENDED);
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/display_options_handler.h b/chromium/chrome/browser/ui/webui/options/chromeos/display_options_handler.h
new file mode 100644
index 00000000000..3f93b9a64b6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/display_options_handler.h
@@ -0,0 +1,63 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_DISPLAY_OPTIONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_DISPLAY_OPTIONS_HANDLER_H_
+
+#include <vector>
+
+#include "ash/display/window_tree_host_manager.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+namespace chromeos {
+namespace options {
+
+// Display options overlay page UI handler.
+class DisplayOptionsHandler : public ::options::OptionsPageUIHandler,
+ public ash::WindowTreeHostManager::Observer {
+ public:
+ DisplayOptionsHandler();
+ ~DisplayOptionsHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializePage() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // ash::WindowTreeHostManager::Observer implementation.
+ void OnDisplayConfigurationChanging() override;
+ void OnDisplayConfigurationChanged() override;
+
+ private:
+ // Sends all of the current display information to the web_ui of options page.
+ void SendAllDisplayInfo();
+
+ // Enables or disables the display settings UI.
+ void UpdateDisplaySettingsEnabled();
+
+ // Handlers of JS messages.
+ void HandleDisplayInfo(const base::ListValue* unused_args);
+ void HandleMirroring(const base::ListValue* args);
+ void HandleSetPrimary(const base::ListValue* args);
+ void HandleSetDisplayLayout(const base::ListValue* args);
+ void HandleSetDisplayMode(const base::ListValue* args);
+ void HandleSetRotation(const base::ListValue* args);
+ void HandleSetColorProfile(const base::ListValue* args);
+ void HandleSetUnifiedDesktopEnabled(const base::ListValue* args);
+
+ DISALLOW_COPY_AND_ASSIGN(DisplayOptionsHandler);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_DISPLAY_OPTIONS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/display_overscan_handler.cc b/chromium/chrome/browser/ui/webui/options/chromeos/display_overscan_handler.cc
new file mode 100644
index 00000000000..9616d7e97d0
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/display_overscan_handler.cc
@@ -0,0 +1,211 @@
+// 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/ui/webui/options/chromeos/display_overscan_handler.h"
+
+#include <string>
+
+#include "ash/display/window_tree_host_manager.h"
+#include "ash/shell.h"
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/display/overscan_calibrator.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/display/display.h"
+#include "ui/display/manager/display_manager.h"
+#include "ui/display/screen.h"
+#include "ui/display/types/display_constants.h"
+
+namespace chromeos {
+namespace options {
+namespace {
+
+// The value for the orientation of overscan operations.
+const char kOrientationHorizontal[] = "horizontal";
+const char kOrientationVertical[] = "vertical";
+
+}
+
+DisplayOverscanHandler::DisplayOverscanHandler() {
+ display::Screen::GetScreen()->AddObserver(this);
+}
+
+DisplayOverscanHandler::~DisplayOverscanHandler() {
+ display::Screen::GetScreen()->RemoveObserver(this);
+}
+
+void DisplayOverscanHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ RegisterTitle(localized_strings, "displayOverscanPage",
+ IDS_OPTIONS_SETTINGS_DISPLAY_OVERSCAN_TAB_TITLE);
+ localized_strings->SetString("shrinkAndExpand", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OVERSCAN_SHRINK_EXPAND));
+ localized_strings->SetString("move", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OVERSCAN_MOVE));
+ localized_strings->SetString("overscanReset", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OVERSCAN_RESET_BUTTON_LABEL));
+ localized_strings->SetString("overscanOK", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OVERSCAN_OK_BUTTON_LABEL));
+ localized_strings->SetString("overscanCancel", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_DISPLAY_OVERSCAN_CANCEL_BUTTON_LABEL));
+}
+
+void DisplayOverscanHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "start",
+ base::Bind(&DisplayOverscanHandler::HandleStart,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "commit",
+ base::Bind(&DisplayOverscanHandler::HandleCommit,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "reset",
+ base::Bind(&DisplayOverscanHandler::HandleReset,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "cancel",
+ base::Bind(&DisplayOverscanHandler::HandleCancel,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "move",
+ base::Bind(&DisplayOverscanHandler::HandleMove,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "resize",
+ base::Bind(&DisplayOverscanHandler::HandleResize,
+ base::Unretained(this)));
+}
+
+void DisplayOverscanHandler::OnDisplayAdded(
+ const display::Display& new_display) {
+ if (!overscan_calibrator_)
+ return;
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.DisplayOverscan.onOverscanCanceled");
+}
+
+void DisplayOverscanHandler::OnDisplayRemoved(
+ const display::Display& old_display) {
+ if (!overscan_calibrator_)
+ return;
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.DisplayOverscan.onOverscanCanceled");
+}
+
+void DisplayOverscanHandler::OnDisplayMetricsChanged(const display::Display&,
+ uint32_t) {}
+
+void DisplayOverscanHandler::HandleStart(const base::ListValue* args) {
+ int64_t display_id = display::kInvalidDisplayId;
+ std::string id_value;
+ if (!args->GetString(0, &id_value)) {
+ LOG(ERROR) << "Can't find ID";
+ return;
+ }
+
+ if (!base::StringToInt64(id_value, &display_id) ||
+ display_id == display::kInvalidDisplayId) {
+ LOG(ERROR) << "Invalid parameter: " << id_value;
+ return;
+ }
+
+ const display::Display& display =
+ ash::Shell::Get()->display_manager()->GetDisplayForId(display_id);
+ DCHECK(display.is_valid());
+ if (!display.is_valid())
+ return;
+
+ ash::WindowTreeHostManager* window_tree_host_manager =
+ ash::Shell::Get()->window_tree_host_manager();
+ overscan_calibrator_.reset(new OverscanCalibrator(
+ display, window_tree_host_manager->GetOverscanInsets(display_id)));
+}
+
+void DisplayOverscanHandler::HandleCommit(const base::ListValue* unused_args) {
+ if (overscan_calibrator_.get()) {
+ overscan_calibrator_->Commit();
+ overscan_calibrator_.reset();
+ }
+}
+
+void DisplayOverscanHandler::HandleReset(const base::ListValue* unused_args) {
+ if (overscan_calibrator_.get())
+ overscan_calibrator_->Reset();
+}
+
+void DisplayOverscanHandler::HandleCancel(const base::ListValue* unused_args) {
+ overscan_calibrator_.reset();
+}
+
+void DisplayOverscanHandler::HandleMove(const base::ListValue* args) {
+ std::string orientation;
+ double length;
+ if (!args->GetString(0, &orientation)) {
+ LOG(ERROR) << "The first argument must be orientation";
+ return;
+ }
+ if (!args->GetDouble(1, &length)) {
+ LOG(ERROR) << "The second argument must be a numeric";
+ return;
+ }
+
+ if (!overscan_calibrator_.get())
+ return;
+
+ gfx::Insets insets = overscan_calibrator_->insets();
+ if (orientation == kOrientationHorizontal) {
+ insets.Set(insets.top(), insets.left() + length,
+ insets.bottom(), insets.right() - length);
+ } else if (orientation == kOrientationVertical) {
+ insets.Set(insets.top() + length, insets.left(),
+ insets.bottom() - length, insets.right());
+ } else {
+ LOG(ERROR) << "The orientation must be '" << kOrientationHorizontal
+ << "' or '" << kOrientationVertical << "': "
+ << orientation;
+ return;
+ }
+ overscan_calibrator_->UpdateInsets(insets);
+}
+
+void DisplayOverscanHandler::HandleResize(const base::ListValue* args) {
+ std::string orientation;
+ double length;
+ if (!args->GetString(0, &orientation)) {
+ LOG(ERROR) << "The first argument must be orientation";
+ return;
+ }
+ if (!args->GetDouble(1, &length)) {
+ LOG(ERROR) << "The second argument must be a numeric";
+ return;
+ }
+
+ if (!overscan_calibrator_.get())
+ return;
+
+ gfx::Insets insets = overscan_calibrator_->insets();
+ if (orientation == kOrientationHorizontal) {
+ insets.Set(insets.top(), insets.left() + length,
+ insets.bottom(), insets.right() + length);
+ } else if (orientation == kOrientationVertical) {
+ insets.Set(insets.top() + length, insets.left(),
+ insets.bottom() + length, insets.right());
+ } else {
+ LOG(ERROR) << "The orientation must be '" << kOrientationHorizontal
+ << "' or '" << kOrientationVertical << "': "
+ << orientation;
+ return;
+ }
+ overscan_calibrator_->UpdateInsets(insets);
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/display_overscan_handler.h b/chromium/chrome/browser/ui/webui/options/chromeos/display_overscan_handler.h
new file mode 100644
index 00000000000..d46e527515c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/display_overscan_handler.h
@@ -0,0 +1,62 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_DISPLAY_OVERSCAN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_DISPLAY_OVERSCAN_HANDLER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "ui/display/display_observer.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+namespace chromeos {
+class OverscanCalibrator;
+
+namespace options {
+
+// Display options overlay page UI handler.
+class DisplayOverscanHandler : public ::options::OptionsPageUIHandler,
+ public display::DisplayObserver {
+ public:
+ DisplayOverscanHandler();
+ ~DisplayOverscanHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // display::DisplayObserver implementation.
+ void OnDisplayAdded(const display::Display& new_display) override;
+ void OnDisplayRemoved(const display::Display& old_display) override;
+ void OnDisplayMetricsChanged(const display::Display& display,
+ uint32_t metrics) override;
+
+ private:
+ // Handlers of JS messages.
+ void HandleStart(const base::ListValue* args);
+ void HandleCommit(const base::ListValue* unused_args);
+ void HandleReset(const base::ListValue* unused_args);
+ void HandleCancel(const base::ListValue* unused_args);
+ void HandleMove(const base::ListValue* args);
+ void HandleResize(const base::ListValue* args);
+
+ std::unique_ptr<OverscanCalibrator> overscan_calibrator_;
+
+ DISALLOW_COPY_AND_ASSIGN(DisplayOverscanHandler);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_DISPLAY_OVERSCAN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/guest_mode_options_browsertest.cc b/chromium/chrome/browser/ui/webui/options/chromeos/guest_mode_options_browsertest.cc
new file mode 100644
index 00000000000..d2bdaa8dec6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/guest_mode_options_browsertest.cc
@@ -0,0 +1,28 @@
+// 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 "chrome/browser/ui/webui/options/chromeos/guest_mode_options_browsertest.h"
+
+#include "base/command_line.h"
+#include "chrome/browser/ui/webui/options/options_ui_browsertest.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chromeos/chromeos_switches.h"
+#include "components/signin/core/account_id/account_id.h"
+#include "components/user_manager/user_names.h"
+
+GuestModeOptionsUIBrowserTest::GuestModeOptionsUIBrowserTest() {}
+
+GuestModeOptionsUIBrowserTest::~GuestModeOptionsUIBrowserTest() {}
+
+void GuestModeOptionsUIBrowserTest::SetUpCommandLine(
+ base::CommandLine* command_line) {
+ command_line->AppendSwitch(chromeos::switches::kGuestSession);
+ command_line->AppendSwitchASCII(
+ chromeos::switches::kLoginUser,
+ user_manager::GuestAccountId().GetUserEmail());
+ command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile,
+ TestingProfile::kTestUserProfileDir);
+ command_line->AppendSwitch(switches::kIncognito);
+}
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/guest_mode_options_browsertest.h b/chromium/chrome/browser/ui/webui/options/chromeos/guest_mode_options_browsertest.h
new file mode 100644
index 00000000000..33ccde9a7cc
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/guest_mode_options_browsertest.h
@@ -0,0 +1,27 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_GUEST_MODE_OPTIONS_BROWSERTEST_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_GUEST_MODE_OPTIONS_BROWSERTEST_H_
+
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/test/base/web_ui_browser_test.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+// This is a helper class used by guest_mode_options_browsertest.js to enable
+// guest mode.
+class GuestModeOptionsUIBrowserTest : public WebUIBrowserTest {
+ public:
+ GuestModeOptionsUIBrowserTest();
+ ~GuestModeOptionsUIBrowserTest() override;
+
+ private:
+ void SetUpCommandLine(base::CommandLine* command_line) override;
+
+ DISALLOW_COPY_AND_ASSIGN(GuestModeOptionsUIBrowserTest);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_GUEST_MODE_OPTIONS_BROWSERTEST_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/guest_mode_options_browsertest.js b/chromium/chrome/browser/ui/webui/options/chromeos/guest_mode_options_browsertest.js
new file mode 100644
index 00000000000..f4a0d0eb76c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/guest_mode_options_browsertest.js
@@ -0,0 +1,80 @@
+// 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.
+
+GEN_INCLUDE(['../options_browsertest_base.js']);
+GEN('#include "chrome/browser/ui/webui/options/chromeos/' +
+ 'guest_mode_options_browsertest.h"');
+
+/**
+ * TestFixture for guest mode options WebUI testing.
+ * @extends {OptionsBrowsertestBase}
+ * @constructor
+ */
+function GuestModeOptionsUIBrowserTest() {}
+
+GuestModeOptionsUIBrowserTest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /** @override */
+ browsePreload: 'chrome://settings-frame',
+
+ /** @override */
+ typedefCppFixture: 'GuestModeOptionsUIBrowserTest',
+
+ /**
+ * Returns the element that sets a given preference, failing if no such
+ * element is found.
+ * @param {string} pref Name of the preference.
+ * @return {!HTMLElement} The element controlling the preference.
+ */
+ getControlForPref: function(pref) {
+ var control = document.querySelector('[pref="' + pref + '"]');
+ assertTrue(!!control);
+ return control;
+ },
+
+ /** @param {!HTMLElement} el */
+ expectHidden: function(el) {
+ expectFalse(el.offsetHeight > 0 && el.offsetWidth > 0);
+ },
+};
+
+/** Test sections that should be hidden in guest mode. */
+TEST_F('GuestModeOptionsUIBrowserTest', 'testSections', function() {
+ this.expectHidden($('startup-section'));
+ this.expectHidden($('appearance-section'));
+ this.expectHidden($('android-apps-section'));
+ this.expectHidden($('sync-users-section'));
+ this.expectHidden($('easy-unlock-section'));
+ this.expectHidden($('reset-profile-settings-section'));
+});
+
+/** Test controls that should be disabled in guest mode. */
+TEST_F('GuestModeOptionsUIBrowserTest', 'testControls', function() {
+ // Appearance section.
+ var setWallpaper = $('set-wallpaper');
+ expectTrue(setWallpaper.disabled);
+
+ // Passwords and autofill section.
+ expectTrue($('autofill-enabled').disabled);
+
+ // Date and time section.
+ expectTrue($('timezone-value-select').disabled);
+ expectFalse($('resolve-timezone-by-geolocation').disabled);
+ expectFalse($('use-24hour-clock').disabled);
+
+ // Privacy section.
+ expectTrue(this.getControlForPref('search.suggest_enabled').disabled);
+ expectTrue($('networkPredictionOptions').disabled);
+
+ // Web content section.
+ expectTrue($('defaultZoomFactor').disabled);
+
+ // Downloads section.
+ expectTrue(this.getControlForPref('gdata.disabled').disabled);
+
+ // Content settings overlay.
+ expectTrue(this.getControlForPref('settings.privacy.drm_enabled').disabled);
+ expectTrue($('protected-content-exceptions').disabled);
+});
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/guest_mode_options_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/options/chromeos/guest_mode_options_ui_browsertest.cc
new file mode 100644
index 00000000000..3d7a39ccd1c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/guest_mode_options_ui_browsertest.cc
@@ -0,0 +1,38 @@
+// 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 "base/command_line.h"
+#include "chrome/browser/ui/webui/options/options_ui_browsertest.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chromeos/chromeos_switches.h"
+#include "components/signin/core/account_id/account_id.h"
+#include "components/user_manager/user_names.h"
+
+namespace {
+
+// Same as OptionsUIBrowserTest but launches with Guest mode command line
+// switches.
+class GuestModeOptionsBrowserTest : public options::OptionsUIBrowserTest {
+ public:
+ GuestModeOptionsBrowserTest() : OptionsUIBrowserTest() {}
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ command_line->AppendSwitch(chromeos::switches::kGuestSession);
+ command_line->AppendSwitchASCII(
+ chromeos::switches::kLoginUser,
+ user_manager::GuestAccountId().GetUserEmail());
+ command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile,
+ TestingProfile::kTestUserProfileDir);
+ command_line->AppendSwitch(switches::kIncognito);
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(GuestModeOptionsBrowserTest, LoadOptionsByURL) {
+ NavigateToSettings();
+ VerifyTitle();
+ VerifyNavbar();
+}
+
+} // namespace
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc b/chromium/chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc
new file mode 100644
index 00000000000..9933d21f4bc
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/internet_options_handler.cc
@@ -0,0 +1,253 @@
+// 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/ui/webui/options/chromeos/internet_options_handler.h"
+
+#include <ctype.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/enrollment_dialog_view.h"
+#include "chrome/browser/chromeos/mobile_config.h"
+#include "chrome/browser/chromeos/options/network_config_view.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/chromeos/sim_dialog_delegate.h"
+#include "chrome/browser/chromeos/ui/choose_mobile_network_dialog.h"
+#include "chrome/browser/chromeos/ui/mobile_config_ui.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/chromeos/mobile_setup_dialog.h"
+#include "chrome/browser/ui/webui/options/chromeos/internet_options_handler_strings.h"
+#include "chromeos/login/login_state.h"
+#include "chromeos/network/managed_network_configuration_handler.h"
+#include "chromeos/network/network_connect.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "components/onc/onc_constants.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/browser/api/vpn_provider/vpn_service.h"
+#include "extensions/browser/api/vpn_provider/vpn_service_factory.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+#include "ui/base/webui/web_ui_util.h"
+
+namespace chromeos {
+namespace options {
+
+namespace {
+
+const char kAllowOnlyPolicyNetworksToConnect[] =
+ "allowOnlyPolicyNetworksToConnect";
+
+// Keys for the initial "localized" dictionary values.
+const char kLoggedInAsOwnerKey[] = "loggedInAsOwner";
+
+// JS methods to show additional UI.
+const char kShowMorePlanInfoMessage[] = "showMorePlanInfo";
+const char kSimOperationMessage[] = "simOperation";
+
+// TODO(stevenjb): Deprecate these and integrate with settings Web UI.
+const char kAddVPNConnectionMessage[] = "addVPNConnection";
+const char kAddNonVPNConnectionMessage[] = "addNonVPNConnection";
+const char kConfigureNetworkMessage[] = "configureNetwork";
+
+// These are strings used to communicate with JavaScript.
+const char kTagSimOpChangePin[] = "changePin";
+const char kTagSimOpConfigure[] = "configure";
+const char kTagSimOpSetLocked[] = "setLocked";
+const char kTagSimOpSetUnlocked[] = "setUnlocked";
+const char kTagSimOpUnlock[] = "unlock";
+
+Profile* GetProfileForPrimaryUser() {
+ return chromeos::ProfileHelper::Get()->GetProfileByUser(
+ user_manager::UserManager::Get()->GetPrimaryUser());
+}
+
+} // namespace
+
+InternetOptionsHandler::InternetOptionsHandler()
+ : weak_factory_(this) {
+}
+
+InternetOptionsHandler::~InternetOptionsHandler() {
+}
+
+void InternetOptionsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+ internet_options_strings::RegisterLocalizedStrings(localized_strings);
+
+ // TODO(stevenjb): Find a better way to populate initial data.
+ std::string owner;
+ chromeos::CrosSettings::Get()->GetString(chromeos::kDeviceOwner, &owner);
+ bool logged_in_as_owner = LoginState::Get()->GetLoggedInUserType() ==
+ LoginState::LOGGED_IN_USER_OWNER;
+ localized_strings->SetBoolean(kLoggedInAsOwnerKey, logged_in_as_owner);
+
+ // TODO(fqj/stevenjb): Make this a property of networkingPrivate
+ const base::DictionaryValue* global_network_config =
+ NetworkHandler::Get()
+ ->managed_network_configuration_handler()
+ ->GetGlobalConfigFromPolicy(
+ std::string() /* no user hash, device policy*/);
+ if (global_network_config) {
+ bool only_policy_connect = false;
+ global_network_config->GetBooleanWithoutPathExpansion(
+ ::onc::global_network_config::kAllowOnlyPolicyNetworksToConnect,
+ &only_policy_connect);
+ localized_strings->SetBoolean(kAllowOnlyPolicyNetworksToConnect,
+ only_policy_connect);
+ }
+}
+
+void InternetOptionsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(kAddVPNConnectionMessage,
+ base::Bind(&InternetOptionsHandler::AddVPNConnection,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kAddNonVPNConnectionMessage,
+ base::Bind(&InternetOptionsHandler::AddNonVPNConnection,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kConfigureNetworkMessage,
+ base::Bind(&InternetOptionsHandler::ConfigureNetwork,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kShowMorePlanInfoMessage,
+ base::Bind(&InternetOptionsHandler::ShowMorePlanInfoCallback,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kSimOperationMessage,
+ base::Bind(&InternetOptionsHandler::SimOperationCallback,
+ base::Unretained(this)));
+}
+
+void InternetOptionsHandler::ShowMorePlanInfoCallback(
+ const base::ListValue* args) {
+ if (!web_ui())
+ return;
+ std::string guid;
+ if (args->GetSize() != 1 || !args->GetString(0, &guid)) {
+ NOTREACHED();
+ return;
+ }
+ NetworkConnect::Get()->ShowMobileSetup(guid);
+}
+
+void InternetOptionsHandler::SimOperationCallback(const base::ListValue* args) {
+ std::string operation;
+ if (args->GetSize() != 1 || !args->GetString(0, &operation)) {
+ NOTREACHED();
+ return;
+ }
+ if (operation == kTagSimOpConfigure) {
+ mobile_config_ui::DisplayConfigDialog();
+ return;
+ }
+ // 1. Bring up SIM unlock dialog, pass new RequirePin setting in URL.
+ // 2. Dialog will ask for current PIN in any case.
+ // 3. If card is locked it will first call PIN unlock operation
+ // 4. Then it will call Set RequirePin, passing the same PIN.
+ // 5. The dialog may change device properties, in which case
+ // DevicePropertiesUpdated() will get called which will update the UI.
+ SimDialogDelegate::SimDialogMode mode;
+ if (operation == kTagSimOpSetLocked) {
+ mode = SimDialogDelegate::SIM_DIALOG_SET_LOCK_ON;
+ } else if (operation == kTagSimOpSetUnlocked) {
+ mode = SimDialogDelegate::SIM_DIALOG_SET_LOCK_OFF;
+ } else if (operation == kTagSimOpUnlock) {
+ mode = SimDialogDelegate::SIM_DIALOG_UNLOCK;
+ } else if (operation == kTagSimOpChangePin) {
+ mode = SimDialogDelegate::SIM_DIALOG_CHANGE_PIN;
+ } else {
+ NOTREACHED();
+ return;
+ }
+ SimDialogDelegate::ShowDialog(GetNativeWindow(), mode);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+gfx::NativeWindow InternetOptionsHandler::GetNativeWindow() const {
+ return web_ui()->GetWebContents()->GetTopLevelNativeWindow();
+}
+
+const PrefService* InternetOptionsHandler::GetPrefs() const {
+ return Profile::FromWebUI(web_ui())->GetPrefs();
+}
+
+
+void InternetOptionsHandler::AddVPNConnection(const base::ListValue* args) {
+ if (args->empty()) {
+ // Show the "add network" dialog for the built-in OpenVPN/L2TP provider.
+ NetworkConfigView::ShowForType(shill::kTypeVPN, GetNativeWindow());
+ return;
+ }
+
+ std::string extension_id;
+ if (args->GetSize() != 1 || !args->GetString(0, &extension_id)) {
+ NOTREACHED();
+ return;
+ }
+
+ // Request that the third-party VPN provider identified by |provider_id|
+ // show its "add network" dialog.
+ chromeos::VpnServiceFactory::GetForBrowserContext(
+ GetProfileForPrimaryUser())->SendShowAddDialogToExtension(extension_id);
+}
+
+void InternetOptionsHandler::AddNonVPNConnection(const base::ListValue* args) {
+ std::string onc_type;
+ if (args->GetSize() != 1 || !args->GetString(0, &onc_type)) {
+ NOTREACHED();
+ return;
+ }
+ if (onc_type == ::onc::network_type::kWiFi) {
+ NetworkConfigView::ShowForType(shill::kTypeWifi, GetNativeWindow());
+ } else if (onc_type == ::onc::network_type::kCellular) {
+ ChooseMobileNetworkDialog::ShowDialog(GetNativeWindow());
+ } else {
+ LOG(ERROR) << "Unsupported type for AddConnection";
+ }
+}
+
+void InternetOptionsHandler::ConfigureNetwork(const base::ListValue* args) {
+ std::string guid;
+ if (args->GetSize() < 1 || !args->GetString(0, &guid)) {
+ NOTREACHED();
+ return;
+ }
+ bool force_show = false;
+ if (args->GetSize() >= 2)
+ args->GetBoolean(1, &force_show);
+
+ const NetworkState* network =
+ NetworkHandler::Get()->network_state_handler()->GetNetworkStateFromGuid(
+ guid);
+ if (!network)
+ return;
+
+ if (network->type() == shill::kTypeVPN &&
+ network->vpn_provider_type() == shill::kProviderThirdPartyVpn) {
+ // Request that the third-party VPN provider used by the |network| show a
+ // configuration dialog for it.
+ VpnServiceFactory::GetForBrowserContext(GetProfileForPrimaryUser())
+ ->SendShowConfigureDialogToExtension(
+ network->third_party_vpn_provider_extension_id(), network->name());
+ return;
+ }
+
+ // If a network is not connectable, show the enrollment dialog if available.
+ if (!force_show && !network->connectable() &&
+ enrollment::CreateEnrollmentDialog(guid, GetNativeWindow())) {
+ return;
+ }
+
+ NetworkConfigView::ShowForNetworkId(guid, GetNativeWindow());
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/internet_options_handler.h b/chromium/chrome/browser/ui/webui/options/chromeos/internet_options_handler.h
new file mode 100644
index 00000000000..383b977db54
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/internet_options_handler.h
@@ -0,0 +1,60 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_INTERNET_OPTIONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_INTERNET_OPTIONS_HANDLER_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "ui/gfx/native_widget_types.h"
+
+class PrefService;
+
+namespace chromeos {
+namespace options {
+
+// ChromeOS internet options page UI handler.
+class InternetOptionsHandler : public ::options::OptionsPageUIHandler {
+ public:
+ InternetOptionsHandler();
+ ~InternetOptionsHandler() override;
+
+ private:
+ // OptionsPageUIHandler
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+
+ // WebUIMessageHandler (from OptionsPageUIHandler)
+ void RegisterMessages() override;
+
+ // Callbacks to set network state properties.
+ void ShowMorePlanInfoCallback(const base::ListValue* args);
+ void SimOperationCallback(const base::ListValue* args);
+
+ // Gets the native window for hosting dialogs, etc.
+ gfx::NativeWindow GetNativeWindow() const;
+
+ // Gets the user PrefService associated with the WebUI.
+ const PrefService* GetPrefs() const;
+
+ // Additional callbacks for managing networks.
+ void AddVPNConnection(const base::ListValue* args);
+ void AddNonVPNConnection(const base::ListValue* args);
+ void ConfigureNetwork(const base::ListValue* args);
+
+ // Weak pointer factory so we can start connections at a later time
+ // without worrying that they will actually try to happen after the lifetime
+ // of this object.
+ base::WeakPtrFactory<InternetOptionsHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(InternetOptionsHandler);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_INTERNET_OPTIONS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/internet_options_handler_strings.cc b/chromium/chrome/browser/ui/webui/options/chromeos/internet_options_handler_strings.cc
new file mode 100644
index 00000000000..6a89972c128
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/internet_options_handler_strings.cc
@@ -0,0 +1,207 @@
+// Copyright (c) 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/ui/webui/options/chromeos/internet_options_handler_strings.h"
+
+#include <stddef.h>
+
+#include "ash/strings/grit/ash_strings.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace chromeos {
+namespace internet_options_strings {
+
+namespace {
+
+struct StringResource {
+ const char* name;
+ int id;
+};
+
+StringResource kStringResources[] = {
+ // Main settings page.
+ {"ethernetName", IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET},
+ {"ethernetTitle", IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET},
+ {"wifiTitle", IDS_OPTIONS_SETTINGS_SECTION_TITLE_WIFI_NETWORK},
+ {"wimaxTitle", IDS_OPTIONS_SETTINGS_SECTION_TITLE_WIMAX_NETWORK},
+ {"cellularTitle", IDS_OPTIONS_SETTINGS_SECTION_TITLE_CELLULAR_NETWORK},
+ {"vpnTitle", IDS_OPTIONS_SETTINGS_SECTION_TITLE_PRIVATE_NETWORK},
+ {"vpnNameTemplate",
+ IDS_OPTIONS_SETTINGS_SECTION_THIRD_PARTY_VPN_NAME_TEMPLATE},
+ {"defaultThirdPartyProviderName",
+ IDS_OPTIONS_SETTINGS_SECTION_DEFAULT_THIRD_PARTY_PROVIDER_NAME},
+ {"vpnBuiltInProvider", IDS_ASH_STATUS_TRAY_VPN_BUILT_IN_PROVIDER},
+ {"joinOtherNetwork", IDS_OPTIONS_SETTINGS_NETWORK_OTHER},
+ {"networkDisabled", IDS_OPTIONS_SETTINGS_NETWORK_DISABLED},
+ {"networkProhibited", IDS_OPTIONS_SETTINGS_NETWORK_PROHIBITED},
+ {"turnOffWifi", IDS_OPTIONS_SETTINGS_NETWORK_DISABLE_WIFI},
+ {"turnOffWimax", IDS_OPTIONS_SETTINGS_NETWORK_DISABLE_WIMAX},
+ {"turnOffCellular", IDS_OPTIONS_SETTINGS_NETWORK_DISABLE_CELLULAR},
+ {"disconnectNetwork", IDS_OPTIONS_SETTINGS_DISCONNECT},
+ {"preferredNetworks", IDS_OPTIONS_SETTINGS_PREFERRED_NETWORKS_LABEL},
+ {"preferredNetworksPage", IDS_OPTIONS_SETTINGS_PREFERRED_NETWORKS_TITLE},
+ {"useSharedProxies", IDS_OPTIONS_SETTINGS_USE_SHARED_PROXIES},
+ {"addConnectionTitle", IDS_OPTIONS_SETTINGS_SECTION_TITLE_ADD_CONNECTION},
+ {"addConnectionWifi", IDS_OPTIONS_SETTINGS_ADD_CONNECTION_WIFI},
+ {"addConnectionVPNTemplate", IDS_OPTIONS_SETTINGS_ADD_VPN_TEMPLATE},
+ {"otherCellularNetworks", IDS_OPTIONS_SETTINGS_OTHER_CELLULAR_NETWORKS},
+ {"enableDataRoaming", IDS_OPTIONS_SETTINGS_ENABLE_DATA_ROAMING},
+ {"disableDataRoaming", IDS_OPTIONS_SETTINGS_DISABLE_DATA_ROAMING},
+ {"dataRoamingDisableToggleTooltip",
+ IDS_OPTIONS_SETTINGS_TOGGLE_DATA_ROAMING_RESTRICTION},
+ {"prohibitedNetwork", IDS_OPTIONS_SETTINGS_PROHIBITED_NETWORK},
+ {"prohibitedNetworkOther", IDS_OPTIONS_SETTINGS_PROHIBITED_NETWORK_OTHER},
+
+ // ONC network states. Format is 'Onc' + key + value.
+ // Note: '.' must be replaced with '-', e.g. VPN.Type -> OncVPN-Type
+ {"OncCellular-ActivationStateActivated",
+ IDS_CHROMEOS_NETWORK_ACTIVATION_STATE_ACTIVATED},
+ {"OncCellular-ActivationStateActivating",
+ IDS_CHROMEOS_NETWORK_ACTIVATION_STATE_ACTIVATING},
+ {"OncCellular-ActivationStateNotActivated",
+ IDS_CHROMEOS_NETWORK_ACTIVATION_STATE_NOT_ACTIVATED},
+ {"OncCellular-ActivationStatePartiallyActivated",
+ IDS_CHROMEOS_NETWORK_ACTIVATION_STATE_PARTIALLY_ACTIVATED},
+ {"OncConnectionStateConnected", IDS_CHROMEOS_NETWORK_STATE_CONNECTED},
+ {"OncConnectionStateConnecting", IDS_CHROMEOS_NETWORK_STATE_CONNECTING},
+ {"OncConnectionStateNotConnected",
+ IDS_CHROMEOS_NETWORK_STATE_NOT_CONNECTED},
+ {"OncCellular-RoamingStateHome", IDS_CHROMEOS_NETWORK_ROAMING_STATE_HOME},
+ {"OncCellular-RoamingStateRoaming",
+ IDS_CHROMEOS_NETWORK_ROAMING_STATE_ROAMING},
+ {"OncTypeCellular", IDS_NETWORK_TYPE_CELLULAR},
+ {"OncTypeEthernet", IDS_NETWORK_TYPE_ETHERNET},
+ {"OncTypeWiFi", IDS_NETWORK_TYPE_WIFI},
+ {"OncTypeWimax", IDS_NETWORK_TYPE_WIMAX},
+ {"OncVPN-TypeL2TP-IPsecCert",
+ IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_L2TP_IPSEC_USER_CERT},
+ {"OncVPN-TypeL2TP-IPsecPSK",
+ IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_L2TP_IPSEC_PSK},
+ {"OncVPN-TypeOpenVPN", IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_OPEN_VPN},
+ {"OncVPN-TypeThirdPartyVPN",
+ IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_THIRD_PARTY_VPN},
+
+ // Internet details dialog.
+ {"restrictedNo", IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL},
+ {"restrictedYes", IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL},
+ {"managedNetwork", IDS_OPTIONS_SETTINGS_MANAGED_NETWORK},
+ {"wifiNetworkTabLabel", IDS_OPTIONS_SETTINGS_INTERNET_TAB_CONNECTION},
+ {"vpnTabLabel", IDS_OPTIONS_SETTINGS_INTERNET_TAB_VPN},
+ {"cellularConnTabLabel", IDS_OPTIONS_SETTINGS_INTERNET_TAB_CONNECTION},
+ {"cellularDeviceTabLabel", IDS_OPTIONS_SETTINGS_INTERNET_TAB_DEVICE},
+ {"networkTabLabel", IDS_OPTIONS_SETTINGS_INTERNET_TAB_NETWORK},
+ {"securityTabLabel", IDS_OPTIONS_SETTINGS_INTERNET_TAB_SECURITY},
+ {"proxyTabLabel", IDS_OPTIONS_SETTINGS_INTERNET_TAB_PROXY},
+ {"connectionState", IDS_OPTIONS_SETTINGS_INTERNET_CONNECTION_STATE},
+ // TODO(stevenjb): Rename the IDS constant when we redesign the UI.
+ {"restrictedConnectivity",
+ IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_RESTRICTED_POOL},
+ {"inetAddress", IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_ADDRESS},
+ {"ipv6Address", IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_ADDRESS_IPV6},
+ {"inetNetmask", IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SUBNETMASK},
+ {"inetGateway", IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_GATEWAY},
+ {"inetNameServers", IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_DNSSERVER},
+ {"ipAutomaticConfiguration",
+ IDS_OPTIONS_SETTINGS_INTERNET_IP_AUTOMATIC_CONFIGURATION},
+ {"automaticNameServers",
+ IDS_OPTIONS_SETTINGS_INTERNET_AUTOMATIC_NAME_SERVERS},
+ {"userNameServer1", IDS_OPTIONS_SETTINGS_INTERNET_USER_NAME_SERVER_1},
+ {"userNameServer2", IDS_OPTIONS_SETTINGS_INTERNET_USER_NAME_SERVER_2},
+ {"userNameServer3", IDS_OPTIONS_SETTINGS_INTERNET_USER_NAME_SERVER_3},
+ {"userNameServer4", IDS_OPTIONS_SETTINGS_INTERNET_USER_NAME_SERVER_4},
+ {"googleNameServers", IDS_OPTIONS_SETTINGS_INTERNET_GOOGLE_NAME_SERVERS},
+ {"userNameServers", IDS_OPTIONS_SETTINGS_INTERNET_USER_NAME_SERVERS},
+ {"hardwareAddress", IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_HARDWARE_ADDRESS},
+ {"detailsInternetDismiss", IDS_CLOSE},
+ {"activateButton", IDS_OPTIONS_SETTINGS_ACTIVATE},
+ {"connectButton", IDS_OPTIONS_SETTINGS_CONNECT},
+ {"configureButton", IDS_OPTIONS_SETTINGS_CONFIGURE},
+ {"disconnectButton", IDS_OPTIONS_SETTINGS_DISCONNECT},
+ {"viewAccountButton", IDS_STATUSBAR_NETWORK_VIEW_ACCOUNT},
+ {"wimaxConnTabLabel", IDS_OPTIONS_SETTINGS_INTERNET_TAB_WIMAX},
+
+ // Wifi Tab.
+ {"inetSsid", IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_NETWORK_ID},
+ {"inetBssid", IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_NETWORK_BSSID},
+ {"inetEncryption",
+ IDS_OPTIONS_SETTIGNS_INTERNET_OPTIONS_NETWORK_ENCRYPTION},
+ {"inetFrequency", IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_NETWORK_FREQUENCY},
+ {"inetFrequencyFormat",
+ IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_NETWORK_FREQUENCY_MHZ},
+ {"inetSignalStrength",
+ IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_NETWORK_STRENGTH},
+ {"inetSignalStrengthFormat",
+ IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_NETWORK_STRENGTH_PERCENTAGE},
+ {"inetPassProtected", IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_NET_PROTECTED},
+ {"inetNetworkShared", IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_NETWORK_SHARED},
+ {"inetPreferredNetwork",
+ IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PREFER_NETWORK},
+ {"inetAutoConnectNetwork",
+ IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_AUTO_CONNECT},
+
+ // VPN Tab.
+ {"inetServiceName", IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_SERVICE_NAME},
+ {"inetServerHostname",
+ IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_SERVER_HOSTNAME},
+ {"inetProviderType",
+ IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_PROVIDER_TYPE},
+ {"inetProviderName",
+ IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_PROVIDER_NAME},
+ {"inetUsername", IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USERNAME},
+
+ // Cellular Tab.
+ {"serviceName", IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_SERVICE_NAME},
+ {"networkTechnology",
+ IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_NETWORK_TECHNOLOGY},
+ {"operatorName", IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_OPERATOR},
+ {"operatorCode", IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_OPERATOR_CODE},
+ {"activationState",
+ IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_ACTIVATION_STATE},
+ {"roamingState", IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_ROAMING_STATE},
+ {"errorState", IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_ERROR_STATE},
+ {"cellularManufacturer",
+ IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_MANUFACTURER},
+ {"modelId", IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_MODEL_ID},
+ {"firmwareRevision",
+ IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_FIRMWARE_REVISION},
+ {"hardwareRevision",
+ IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_HARDWARE_REVISION},
+ {"prlVersion", IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_PRL_VERSION},
+ {"cellularApnLabel", IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_APN},
+ {"cellularApnOther", IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_APN_OTHER},
+ {"cellularApnUsername",
+ IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_APN_USERNAME},
+ {"cellularApnPassword",
+ IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_APN_PASSWORD},
+ {"cellularApnUseDefault", IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_APN_CLEAR},
+ {"cellularApnSet", IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_APN_SET},
+ {"cellularApnCancel", IDS_CANCEL},
+
+ // Security Tab.
+ {"lockSimCard", IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_LOCK_SIM_CARD},
+ {"changePinButton",
+ IDS_OPTIONS_SETTINGS_INTERNET_CELLULAR_CHANGE_PIN_BUTTON},
+
+ // Proxy Tab.
+ {"webProxyAutoDiscoveryUrl", IDS_PROXY_WEB_PROXY_AUTO_DISCOVERY},
+};
+
+const size_t kStringResourcesLength = arraysize(kStringResources);
+
+} // namespace
+
+void RegisterLocalizedStrings(base::DictionaryValue* localized_strings) {
+ for (size_t i = 0; i < kStringResourcesLength; ++i) {
+ localized_strings->SetString(
+ kStringResources[i].name,
+ l10n_util::GetStringUTF16(kStringResources[i].id));
+ }
+}
+
+} // namespace internet_options_strings
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/internet_options_handler_strings.h b/chromium/chrome/browser/ui/webui/options/chromeos/internet_options_handler_strings.h
new file mode 100644
index 00000000000..2983f548e1b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/internet_options_handler_strings.h
@@ -0,0 +1,22 @@
+// Copyright (c) 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_UI_WEBUI_OPTIONS_CHROMEOS_INTERNET_OPTIONS_HANDLER_STRINGS_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_INTERNET_OPTIONS_HANDLER_STRINGS_H_
+
+#include <string>
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace chromeos {
+namespace internet_options_strings {
+
+void RegisterLocalizedStrings(base::DictionaryValue* localized_strings);
+
+} // namespace internet_options_strings
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_INTERNET_OPTIONS_HANDLER_STRINGS_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/keyboard_handler.cc b/chromium/chrome/browser/ui/webui/options/chromeos/keyboard_handler.cc
new file mode 100644
index 00000000000..1d6882b886d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/keyboard_handler.cc
@@ -0,0 +1,191 @@
+// 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/ui/webui/options/chromeos/keyboard_handler.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "ash/new_window_controller.h"
+#include "ash/shell.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/ime/chromeos/ime_keyboard.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/events/devices/input_device_manager.h"
+
+namespace {
+const struct ModifierKeysSelectItem {
+ int message_id;
+ chromeos::input_method::ModifierKey value;
+} kModifierKeysSelectItems[] = {
+ { IDS_OPTIONS_SETTINGS_LANGUAGES_KEY_SEARCH,
+ chromeos::input_method::kSearchKey },
+ { IDS_OPTIONS_SETTINGS_LANGUAGES_KEY_LEFT_CTRL,
+ chromeos::input_method::kControlKey },
+ { IDS_OPTIONS_SETTINGS_LANGUAGES_KEY_LEFT_ALT,
+ chromeos::input_method::kAltKey },
+ { IDS_OPTIONS_SETTINGS_LANGUAGES_KEY_VOID,
+ chromeos::input_method::kVoidKey },
+ { IDS_OPTIONS_SETTINGS_LANGUAGES_KEY_CAPS_LOCK,
+ chromeos::input_method::kCapsLockKey },
+ { IDS_OPTIONS_SETTINGS_LANGUAGES_KEY_ESCAPE,
+ chromeos::input_method::kEscapeKey },
+ { IDS_OPTIONS_SETTINGS_LANGUAGES_KEY_BACKSPACE,
+ chromeos::input_method::kBackspaceKey },
+};
+
+const char* kDataValuesNames[] = {
+ "remapSearchKeyToValue",
+ "remapControlKeyToValue",
+ "remapAltKeyToValue",
+ "remapCapsLockKeyToValue",
+ "remapDiamondKeyToValue",
+ "remapEscapeKeyToValue",
+ "remapBackspaceKeyToValue",
+};
+
+bool HasExternalKeyboard() {
+ for (const ui::InputDevice& keyboard :
+ ui::InputDeviceManager::GetInstance()->GetKeyboardDevices()) {
+ if (keyboard.type == ui::InputDeviceType::INPUT_DEVICE_EXTERNAL)
+ return true;
+ }
+
+ return false;
+}
+} // namespace
+
+namespace chromeos {
+namespace options {
+
+KeyboardHandler::KeyboardHandler() {
+ ui::InputDeviceManager::GetInstance()->AddObserver(this);
+}
+
+KeyboardHandler::~KeyboardHandler() {
+ ui::InputDeviceManager::GetInstance()->RemoveObserver(this);
+}
+
+void KeyboardHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+ RegisterTitle(localized_strings, "keyboardOverlay",
+ IDS_OPTIONS_KEYBOARD_OVERLAY_TITLE);
+
+ localized_strings->SetString("remapSearchKeyToContent",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_KEY_SEARCH_LABEL));
+ localized_strings->SetString("remapControlKeyToContent",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_KEY_LEFT_CTRL_LABEL));
+ localized_strings->SetString("remapAltKeyToContent",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_KEY_LEFT_ALT_LABEL));
+ localized_strings->SetString("remapCapsLockKeyToContent",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_KEY_CAPS_LOCK_LABEL));
+ localized_strings->SetString("remapDiamondKeyToContent",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_KEY_DIAMOND_KEY_LABEL));
+ localized_strings->SetString("remapBackspaceKeyToContent",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_KEY_BACKSPACE_KEY_LABEL));
+ localized_strings->SetString("remapEscapeKeyToContent",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_KEY_ESCAPE_KEY_LABEL));
+ localized_strings->SetString("sendFunctionKeys",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_SEND_FUNCTION_KEYS));
+ localized_strings->SetString("sendFunctionKeysDescription",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_SEND_FUNCTION_KEYS_DESCRIPTION));
+ localized_strings->SetString("enableAutoRepeat",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_AUTO_REPEAT_ENABLE));
+ localized_strings->SetString("autoRepeatDelay",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_AUTO_REPEAT_DELAY));
+ localized_strings->SetString("autoRepeatDelayLong",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_AUTO_REPEAT_DELAY_LONG));
+ localized_strings->SetString("autoRepeatDelayShort",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_AUTO_REPEAT_DELAY_SHORT));
+ localized_strings->SetString("autoRepeatRate",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_AUTO_REPEAT_RATE));
+ localized_strings->SetString("autoRepeatRateSlow",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_AUTO_REPEAT_RATE_SLOW));
+ localized_strings->SetString("autoRepeatRateFast",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_AUTO_REPEAT_RATE_FAST));
+ localized_strings->SetString("changeLanguageAndInputSettings",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_CHANGE_LANGUAGE_AND_INPUT_SETTINGS));
+ localized_strings->SetString("showKeyboardShortcuts",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_SHOW_KEYBOARD_SHORTCUTS));
+
+ for (size_t i = 0; i < arraysize(kDataValuesNames); ++i) {
+ auto list_value = base::MakeUnique<base::ListValue>();
+ for (size_t j = 0; j < arraysize(kModifierKeysSelectItems); ++j) {
+ const input_method::ModifierKey value =
+ kModifierKeysSelectItems[j].value;
+ const int message_id = kModifierKeysSelectItems[j].message_id;
+ auto option = base::MakeUnique<base::ListValue>();
+ option->AppendInteger(value);
+ option->AppendString(l10n_util::GetStringUTF16(message_id));
+ list_value->Append(std::move(option));
+ }
+ localized_strings->Set(kDataValuesNames[i], std::move(list_value));
+ }
+}
+
+void KeyboardHandler::InitializePage() {
+ bool has_diamond_key = base::CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kHasChromeOSDiamondKey);
+ const base::Value show_diamond_key_options(has_diamond_key);
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.KeyboardOverlay.showDiamondKeyOptions",
+ show_diamond_key_options);
+
+ UpdateCapsLockOptions();
+}
+
+void KeyboardHandler::RegisterMessages() {
+ // Callback to show keyboard overlay.
+ web_ui()->RegisterMessageCallback(
+ "showKeyboardShortcuts",
+ base::Bind(&KeyboardHandler::HandleShowKeyboardShortcuts,
+ base::Unretained(this)));
+}
+
+void KeyboardHandler::OnKeyboardDeviceConfigurationChanged() {
+ UpdateCapsLockOptions();
+}
+
+void KeyboardHandler::HandleShowKeyboardShortcuts(const base::ListValue* args) {
+ ash::Shell::Get()->new_window_controller()->ShowKeyboardOverlay();
+}
+
+void KeyboardHandler::UpdateCapsLockOptions() const {
+ const base::Value show_caps_lock_options(HasExternalKeyboard());
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.KeyboardOverlay.showCapsLockOptions", show_caps_lock_options);
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/keyboard_handler.h b/chromium/chrome/browser/ui/webui/options/chromeos/keyboard_handler.h
new file mode 100644
index 00000000000..8f6501a0e81
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/keyboard_handler.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_KEYBOARD_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_KEYBOARD_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "components/prefs/pref_member.h"
+#include "ui/events/devices/input_device_event_observer.h"
+
+namespace chromeos {
+namespace options {
+
+// Customize modifier keys overlay page UI handler.
+class KeyboardHandler
+ : public ::options::OptionsPageUIHandler,
+ public ui::InputDeviceEventObserver {
+ public:
+ KeyboardHandler();
+ ~KeyboardHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializePage() override;
+ void RegisterMessages() override;
+
+ // ui::InputDeviceEventObserver:
+ void OnKeyboardDeviceConfigurationChanged() override;
+
+ private:
+ // Show the keyboard shortcuts overlay from the options page.
+ void HandleShowKeyboardShortcuts(const base::ListValue* args);
+
+ // Shows or hides the CapsLock options based on whether or not there is an
+ // external keyboard connected.
+ void UpdateCapsLockOptions() const;
+
+ DISALLOW_COPY_AND_ASSIGN(KeyboardHandler);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_KEYBOARD_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.cc b/chromium/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.cc
new file mode 100644
index 00000000000..8d2c9a2c80e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.cc
@@ -0,0 +1,152 @@
+// Copyright (c) 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 "chrome/browser/ui/webui/options/chromeos/options_stylus_handler.h"
+
+#include "ash/system/palette/palette_utils.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/events/devices/input_device_manager.h"
+
+namespace chromeos {
+namespace options {
+
+namespace {
+
+// Keys in objects passed to updateNoteTakingApps_.
+constexpr char kAppNameKey[] = "name";
+constexpr char kAppIdKey[] = "id";
+constexpr char kAppPreferredKey[] = "preferred";
+
+} // namespace
+
+OptionsStylusHandler::OptionsStylusHandler() : weak_ptr_factory_(this) {
+ NoteTakingHelper::Get()->AddObserver(this);
+ ui::InputDeviceManager::GetInstance()->AddObserver(this);
+}
+
+OptionsStylusHandler::~OptionsStylusHandler() {
+ ui::InputDeviceManager::GetInstance()->RemoveObserver(this);
+ NoteTakingHelper::Get()->RemoveObserver(this);
+}
+
+void OptionsStylusHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ RegisterTitle(localized_strings, "stylusOverlay", IDS_SETTINGS_STYLUS_TITLE);
+
+ localized_strings->SetString(
+ "stylusSettingsButtonTitle",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_DEVICE_GROUP_STYLUS_SETTINGS_BUTTON));
+ localized_strings->SetString(
+ "stylusAutoOpenStylusTools",
+ l10n_util::GetStringUTF16(IDS_SETTINGS_STYLUS_AUTO_OPEN_STYLUS_TOOLS));
+ localized_strings->SetString(
+ "stylusEnableStylusTools",
+ l10n_util::GetStringUTF16(IDS_SETTINGS_STYLUS_ENABLE_STYLUS_TOOLS));
+ localized_strings->SetString(
+ "stylusFindMoreApps",
+ l10n_util::GetStringUTF16(IDS_SETTINGS_STYLUS_FIND_MORE_APPS_PRIMARY));
+ localized_strings->SetString(
+ "stylusNoteTakingApp",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_STYLUS_NOTE_TAKING_APP_LABEL));
+ localized_strings->SetString(
+ "stylusNoteTakingAppNoneAvailable",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_STYLUS_NOTE_TAKING_APP_NONE_AVAILABLE));
+ localized_strings->SetString(
+ "stylusNoteTakingAppWaitingForAndroid",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_STYLUS_NOTE_TAKING_APP_WAITING_FOR_ANDROID));
+}
+
+void OptionsStylusHandler::InitializePage() {
+ UpdateNoteTakingApps();
+}
+
+void OptionsStylusHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "setPreferredNoteTakingApp",
+ base::Bind(&OptionsStylusHandler::SetPreferredNoteTakingApp,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "requestStylusHardwareState",
+ base::Bind(&OptionsStylusHandler::RequestStylusHardwareState,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void OptionsStylusHandler::OnAvailableNoteTakingAppsUpdated() {
+ UpdateNoteTakingApps();
+}
+
+void OptionsStylusHandler::OnDeviceListsComplete() {
+ SendHasStylus();
+}
+
+void OptionsStylusHandler::RequestStylusHardwareState(
+ const base::ListValue* args) {
+ if (ui::InputDeviceManager::GetInstance()->AreDeviceListsComplete())
+ SendHasStylus();
+}
+
+void OptionsStylusHandler::SendHasStylus() {
+ DCHECK(ui::InputDeviceManager::GetInstance()->AreDeviceListsComplete());
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.setStylusInputStatus",
+ base::Value(ash::palette_utils::HasStylusInput()));
+}
+
+void OptionsStylusHandler::UpdateNoteTakingApps() {
+ bool waiting_for_android = false;
+ note_taking_app_ids_.clear();
+ base::ListValue apps_list;
+
+ NoteTakingHelper* helper = NoteTakingHelper::Get();
+ if (helper->play_store_enabled() && !helper->android_apps_received()) {
+ // If Play Store is enabled but not ready yet, let the JS know so it can
+ // disable the menu and display an explanatory message.
+ waiting_for_android = true;
+ } else {
+ for (const NoteTakingAppInfo& info :
+ NoteTakingHelper::Get()->GetAvailableApps(
+ Profile::FromWebUI(web_ui()))) {
+ std::unique_ptr<base::DictionaryValue> dict(
+ new base::DictionaryValue());
+ dict->SetString(kAppNameKey, info.name);
+ dict->SetString(kAppIdKey, info.app_id);
+ dict->SetBoolean(kAppPreferredKey, info.preferred);
+ apps_list.Append(std::move(dict));
+
+ note_taking_app_ids_.insert(info.app_id);
+ }
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("StylusOverlay.updateNoteTakingApps",
+ apps_list,
+ base::Value(waiting_for_android));
+}
+
+void OptionsStylusHandler::SetPreferredNoteTakingApp(
+ const base::ListValue* args) {
+ std::string app_id;
+ CHECK(args->GetString(0, &app_id));
+
+ // Sanity check: make sure that the ID we got back from WebUI is in the
+ // currently-available set.
+ if (!note_taking_app_ids_.count(app_id)) {
+ LOG(ERROR) << "Got unknown note-taking-app ID \"" << app_id << "\"";
+ return;
+ }
+
+ NoteTakingHelper::Get()->SetPreferredApp(Profile::FromWebUI(web_ui()),
+ app_id);
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.h b/chromium/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.h
new file mode 100644
index 00000000000..6e729c69959
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/options_stylus_handler.h
@@ -0,0 +1,69 @@
+// Copyright (c) 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_OPTIONS_STYLUS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_OPTIONS_STYLUS_HANDLER_H_
+
+#include <set>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/note_taking_helper.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "ui/events/devices/input_device_event_observer.h"
+
+namespace base {
+class ListValue;
+} // namespace base
+
+namespace chromeos {
+namespace options {
+
+// Stylus-specific options C++ code.
+class OptionsStylusHandler : public ::options::OptionsPageUIHandler,
+ public NoteTakingHelper::Observer,
+ public ui::InputDeviceEventObserver {
+ public:
+ OptionsStylusHandler();
+ ~OptionsStylusHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializePage() override;
+ void RegisterMessages() override;
+
+ // NoteTakingHelper::Observer implementation.
+ void OnAvailableNoteTakingAppsUpdated() override;
+
+ // ui::InputDeviceEventObserver implementation:
+ void OnDeviceListsComplete() override;
+
+ private:
+ // Called from WebUI when the page is initialized to determine if stylus
+ // settings should be shown.
+ void RequestStylusHardwareState(const base::ListValue* args);
+
+ // Sends if there is a stylus device to WebUI.
+ void SendHasStylus();
+
+ // Updates the note-taking app menu in the stylus overlay to display the
+ // currently-available set of apps.
+ void UpdateNoteTakingApps();
+
+ // Called from WebUI when the user selects a note-taking app.
+ void SetPreferredNoteTakingApp(const base::ListValue* args);
+
+ // IDs of available note-taking apps.
+ std::set<std::string> note_taking_app_ids_;
+
+ base::WeakPtrFactory<OptionsStylusHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(OptionsStylusHandler);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_OPTIONS_STYLUS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/pointer_handler.cc b/chromium/chrome/browser/ui/webui/options/chromeos/pointer_handler.cc
new file mode 100644
index 00000000000..a4ee792b43f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/pointer_handler.cc
@@ -0,0 +1,87 @@
+// 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/ui/webui/options/chromeos/pointer_handler.h"
+
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace chromeos {
+namespace options {
+
+PointerHandler::PointerHandler()
+ : has_touchpad_(false),
+ has_mouse_(false) {
+}
+
+PointerHandler::~PointerHandler() {
+}
+
+void PointerHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ static OptionsStringResource resources[] = {
+ { "pointerOverlayTitleTouchpadOnly",
+ IDS_OPTIONS_POINTER_TOUCHPAD_OVERLAY_TITLE },
+ { "pointerOverlayTitleMouseOnly",
+ IDS_OPTIONS_POINTER_MOUSE_OVERLAY_TITLE },
+ { "pointerOverlayTitleTouchpadMouse",
+ IDS_OPTIONS_POINTER_TOUCHPAD_MOUSE_OVERLAY_TITLE },
+ { "pointerOverlaySectionTitleTouchpad",
+ IDS_OPTIONS_POINTER_OVERLAY_SECTION_TITLE_TOUCHPAD },
+ { "pointerOverlaySectionTitleMouse",
+ IDS_OPTIONS_POINTER_OVERLAY_SECTION_TITLE_MOUSE },
+ { "enableTapToClick",
+ IDS_OPTIONS_SETTINGS_TAP_TO_CLICK_ENABLED_DESCRIPTION },
+ { "primaryMouseRight",
+ IDS_OPTIONS_SETTINGS_PRIMARY_MOUSE_RIGHT_DESCRIPTION },
+ { "traditionalScroll",
+ IDS_OPTIONS_SETTINGS_TRADITIONAL_SCROLL_DESCRIPTION },
+ };
+
+ localized_strings->SetString("naturalScroll",
+ l10n_util::GetStringFUTF16(
+ IDS_OPTIONS_SETTINGS_NATURAL_SCROLL_DESCRIPTION,
+ base::ASCIIToUTF16(chrome::kNaturalScrollHelpURL)));
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+}
+
+
+void PointerHandler::TouchpadExists(bool exists) {
+ has_touchpad_ = exists;
+ base::Value val(exists);
+ web_ui()->CallJavascriptFunctionUnsafe("PointerOverlay.showTouchpadControls",
+ val);
+ UpdateTitle();
+}
+
+void PointerHandler::MouseExists(bool exists) {
+ has_mouse_ = exists;
+ base::Value val(exists);
+ web_ui()->CallJavascriptFunctionUnsafe("PointerOverlay.showMouseControls",
+ val);
+ UpdateTitle();
+}
+
+void PointerHandler::UpdateTitle() {
+ std::string label;
+ if (has_touchpad_) {
+ label = has_mouse_ ? "pointerOverlayTitleTouchpadMouse" :
+ "pointerOverlayTitleTouchpadOnly";
+ } else {
+ label = has_mouse_ ? "pointerOverlayTitleMouseOnly" : "";
+ }
+ base::Value val(label);
+ web_ui()->CallJavascriptFunctionUnsafe("PointerOverlay.setTitle", val);
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/pointer_handler.h b/chromium/chrome/browser/ui/webui/options/chromeos/pointer_handler.h
new file mode 100644
index 00000000000..d0d4c48e4bd
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/pointer_handler.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_POINTER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_POINTER_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/system/pointer_device_observer.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "components/prefs/pref_member.h"
+
+namespace chromeos {
+namespace options {
+
+// Pointer settings overlay page UI handler.
+class PointerHandler
+ : public ::options::OptionsPageUIHandler,
+ public chromeos::system::PointerDeviceObserver::Observer {
+ public:
+ PointerHandler();
+ ~PointerHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+
+ private:
+ // PointerDeviceObserver implementation.
+ void TouchpadExists(bool exists) override;
+ void MouseExists(bool exists) override;
+
+ // Set the title dynamically based on whether a touchpad and/or mouse is
+ // detected.
+ void UpdateTitle();
+
+ bool has_touchpad_;
+ bool has_mouse_;
+
+ DISALLOW_COPY_AND_ASSIGN(PointerHandler);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_POINTER_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/power_handler.cc b/chromium/chrome/browser/ui/webui/options/chromeos/power_handler.cc
new file mode 100644
index 00000000000..8cd2cb22176
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/power_handler.cc
@@ -0,0 +1,174 @@
+// 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/ui/webui/options/chromeos/power_handler.h"
+
+#include <utility>
+
+#include "ash/resources/grit/ash_resources.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/ui/ash/ash_util.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/time_format.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/web_ui_util.h"
+
+using ash::PowerStatus;
+
+namespace chromeos {
+namespace options {
+
+PowerHandler::PowerHandler() {
+ // TODO(mash): Support Chrome power settings in Mash. crbug.com/644348
+ this->show_power_status_ = !ash_util::IsRunningInMash() &&
+ (switches::PowerOverlayEnabled() ||
+ (PowerStatus::Get()->IsBatteryPresent() &&
+ PowerStatus::Get()->SupportsDualRoleDevices()));
+}
+
+PowerHandler::~PowerHandler() {
+ if (this->show_power_status_)
+ PowerStatus::Get()->RemoveObserver(this);
+}
+
+void PowerHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+ RegisterTitle(localized_strings, "powerOverlay",
+ IDS_OPTIONS_POWER_OVERLAY_TITLE);
+ localized_strings->SetString(
+ "batteryStatusLabel",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_BATTERY_STATUS_LABEL));
+ localized_strings->SetBoolean(
+ "showPowerStatus", this->show_power_status_);
+ localized_strings->SetString(
+ "powerSourceLabel",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_POWER_SOURCE_LABEL));
+ localized_strings->SetString(
+ "powerSourceBattery",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_POWER_SOURCE_BATTERY));
+ localized_strings->SetString(
+ "powerSourceAcAdapter",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_POWER_SOURCE_AC_ADAPTER));
+ localized_strings->SetString(
+ "powerSourceLowPowerCharger",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_POWER_SOURCE_LOW_POWER_CHARGER));
+ localized_strings->SetString(
+ "calculatingPower",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_POWER_OVERLAY_CALCULATING));
+}
+
+void PowerHandler::InitializePage() {
+ if (this->show_power_status_)
+ PowerStatus::Get()->RequestStatusUpdate();
+}
+
+void PowerHandler::RegisterMessages() {
+ if (this->show_power_status_)
+ PowerStatus::Get()->AddObserver(this);
+ // Callback to fetch the power info.
+ web_ui()->RegisterMessageCallback(
+ "updatePowerStatus",
+ base::Bind(&PowerHandler::UpdatePowerStatus, base::Unretained(this)));
+ // Callback to set the power source.
+ web_ui()->RegisterMessageCallback(
+ "setPowerSource",
+ base::Bind(&PowerHandler::SetPowerSource, base::Unretained(this)));
+}
+
+void PowerHandler::OnPowerStatusChanged() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.PowerOverlay.setBatteryStatusText",
+ base::Value(GetStatusValue()));
+ UpdatePowerSources();
+}
+
+base::string16 PowerHandler::GetStatusValue() const {
+ PowerStatus* status = PowerStatus::Get();
+ if (!status->IsBatteryPresent())
+ return base::string16();
+
+ bool charging = status->IsBatteryCharging();
+ bool calculating = status->IsBatteryTimeBeingCalculated();
+ int percent = status->GetRoundedBatteryPercent();
+ base::TimeDelta time_left;
+ bool show_time = false;
+
+ if (!calculating) {
+ time_left = charging ? status->GetBatteryTimeToFull() :
+ status->GetBatteryTimeToEmpty();
+ show_time = PowerStatus::ShouldDisplayBatteryTime(time_left);
+ }
+
+ if (!show_time) {
+ return l10n_util::GetStringFUTF16(IDS_OPTIONS_BATTERY_STATUS_SHORT,
+ base::IntToString16(percent));
+ } else {
+ int hour = 0;
+ int min = 0;
+ PowerStatus::SplitTimeIntoHoursAndMinutes(time_left, &hour, &min);
+
+ base::string16 time_text;
+ if (hour == 0 || min == 0) {
+ // Display only one unit ("2 hours" or "10 minutes").
+ time_text = ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
+ ui::TimeFormat::LENGTH_LONG,
+ time_left);
+ } else {
+ time_text = ui::TimeFormat::Detailed(ui::TimeFormat::FORMAT_DURATION,
+ ui::TimeFormat::LENGTH_LONG,
+ -1, // force hour and minute output
+ time_left);
+ }
+
+ return l10n_util::GetStringFUTF16(
+ charging ? IDS_OPTIONS_BATTERY_STATUS_CHARGING :
+ IDS_OPTIONS_BATTERY_STATUS,
+ base::IntToString16(percent),
+ time_text);
+ }
+}
+
+void PowerHandler::UpdatePowerStatus(const base::ListValue* args) {
+ PowerStatus::Get()->RequestStatusUpdate();
+}
+
+void PowerHandler::SetPowerSource(const base::ListValue* args) {
+ std::string id;
+ if (!args->GetString(0, &id)) {
+ NOTREACHED();
+ return;
+ }
+ PowerStatus::Get()->SetPowerSource(id);
+}
+
+void PowerHandler::UpdatePowerSources() {
+ PowerStatus* status = PowerStatus::Get();
+
+ base::ListValue sources_list;
+ for (const auto& source : status->GetPowerSources()) {
+ std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+ dict->SetString("id", source.id);
+ dict->SetInteger("type", source.type);
+ dict->SetString("description",
+ l10n_util::GetStringUTF16(source.description_id));
+ sources_list.Append(std::move(dict));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.PowerOverlay.setPowerSources", sources_list,
+ base::Value(status->GetCurrentPowerSourceID()),
+ base::Value(status->IsUsbChargerConnected()),
+ base::Value(status->IsBatteryTimeBeingCalculated()));
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/power_handler.h b/chromium/chrome/browser/ui/webui/options/chromeos/power_handler.h
new file mode 100644
index 00000000000..843749b8e08
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/power_handler.h
@@ -0,0 +1,54 @@
+// 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_UI_WEBUI_OPTIONS_CHROMEOS_POWER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_POWER_HANDLER_H_
+
+#include "ash/system/power/power_status.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+namespace chromeos {
+namespace options {
+
+// Shows battery status and power settings.
+class PowerHandler : public ::options::OptionsPageUIHandler,
+ public ash::PowerStatus::Observer {
+ public:
+ PowerHandler();
+ ~PowerHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializePage() override;
+ void RegisterMessages() override;
+
+ // ash::PowerStatus::Observer implementation.
+ void OnPowerStatusChanged() override;
+
+private:
+ // Gets the battery status text, showing the percentage and time remaining if
+ // calculated. Returns an empty string if there is no battery.
+ base::string16 GetStatusValue() const;
+
+ // Handler to request updating the power status.
+ void UpdatePowerStatus(const base::ListValue* args);
+
+ // Handler to change the power source.
+ void SetPowerSource(const base::ListValue* args);
+
+ // Updates the UI with the latest power source information.
+ void UpdatePowerSources();
+
+ // Whether the UI should show the power status.
+ bool show_power_status_;
+
+ DISALLOW_COPY_AND_ASSIGN(PowerHandler);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_POWER_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/power_overlay_browsertest.js b/chromium/chrome/browser/ui/webui/options/chromeos/power_overlay_browsertest.js
new file mode 100644
index 00000000000..d49131c5f80
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/power_overlay_browsertest.js
@@ -0,0 +1,175 @@
+// 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.
+
+GEN_INCLUDE(['../options_browsertest_base.js']);
+
+function PowerOverlayWebUITest() {}
+
+PowerOverlayWebUITest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ browsePreload: 'chrome://settings-frame/',
+
+ commandLineSwitches: [{
+ switchName: 'enable-power-overlay',
+ }],
+
+ /** @override */
+ preLoad: function() {
+ this.makeAndRegisterMockHandler([
+ 'updatePowerStatus',
+ 'setPowerSource',
+ ]);
+ this.mockHandler.expects(atLeastOnce()).updatePowerStatus();
+ },
+
+ /**
+ * Sets power sources using a deep copy of |sources|.
+ * @param {Array<Object>} sources
+ * @param {string} sourceId
+ * @param {bool} isUsbCharger
+ * @param {bool} isCalculating
+ */
+ setPowerSources: function(sources, sourceId, isUsbCharger, isCalculating) {
+ var sourcesCopy = sources.map(function(source) {
+ return Object.assign({}, source);
+ });
+ options.PowerOverlay.setPowerSources(
+ sourcesCopy, sourceId, isUsbCharger, isCalculating);
+ },
+
+ /**
+ * Simulates the user selecting a power source, verifying that the overlay
+ * calls setPowerSource.
+ * @param {string} sourceId
+ */
+ selectPowerSource: function(sourceId) {
+ this.mockHandler.expects(once()).setPowerSource(eq(sourceId));
+ $('power-source-dropdown').value = sourceId;
+ expectTrue(cr.dispatchSimpleEvent($('power-source-dropdown'), 'change'));
+ },
+
+ /**
+ * Checks that the sources dropdown is visible.
+ * @param {string} sourceId The ID of the source that should be selected.
+ */
+ checkSource: function(sourceId) {
+ expectTrue($('power-source-charger').hidden);
+ expectFalse($('power-sources').hidden);
+ expectEquals(sourceId, $('power-source-dropdown').value);
+ },
+
+ checkNoSources: function() {
+ expectTrue($('power-source-charger').hidden);
+ expectTrue($('power-sources').hidden);
+ },
+
+ checkDedicatedCharger: function() {
+ expectFalse($('power-source-charger').hidden);
+ expectTrue($('power-sources').hidden);
+ },
+};
+
+TEST_F('PowerOverlayWebUITest', 'testNoPowerSources', function() {
+ assertEquals(this.browsePreload, document.location.href);
+ this.mockHandler.expects(never()).setPowerSource();
+ $('power-settings-link').click();
+
+ // This should be the initial state.
+ this.checkNoSources();
+
+ // Setting an empty sources list shouldn't change the state.
+ this.setPowerSources([], '', false, false);
+ this.checkNoSources();
+});
+
+TEST_F('PowerOverlayWebUITest', 'testDedicatedCharger', function() {
+ assertEquals(this.browsePreload, document.location.href);
+ this.mockHandler.expects(never()).setPowerSource();
+ $('power-settings-link').click();
+
+ var fakeSources = [{
+ id: 'source1',
+ description: 'Left port',
+ type: options.PowerStatusDeviceType.DEDICATED_CHARGER,
+ }];
+
+ this.setPowerSources(fakeSources, 'source1', false, false);
+ this.checkDedicatedCharger();
+
+ // Remove the charger.
+ this.setPowerSources([], '');
+ this.checkNoSources();
+
+ // Set a low-powered charger.
+ this.setPowerSources(fakeSources, 'source1', true, false);
+ this.checkDedicatedCharger();
+});
+
+TEST_F('PowerOverlayWebUITest', 'testSingleSource', function() {
+ assertEquals(this.browsePreload, document.location.href);
+ $('power-settings-link').click();
+
+ var fakeSources = [{
+ id: 'source1',
+ description: 'Left port',
+ type: options.PowerStatusDeviceType.DUAL_ROLE_USB,
+ }];
+
+ this.setPowerSources(fakeSources, '', false, false);
+ this.checkSource('');
+
+ this.selectPowerSource('source1');
+ this.checkSource('source1');
+
+ // Remove the device.
+ this.setPowerSources([], '', false, false);
+ this.checkNoSources();
+});
+
+TEST_F('PowerOverlayWebUITest', 'testMultipleSources', function() {
+ assertEquals(this.browsePreload, document.location.href);
+ $('power-settings-link').click();
+
+ var fakeSources = [{
+ id: 'source1',
+ description: 'Left port',
+ type: options.PowerStatusDeviceType.DUAL_ROLE_USB,
+ }, {
+ id: 'source2',
+ description: 'Right port',
+ type: options.PowerStatusDeviceType.DUAL_ROLE_USB,
+ }, {
+ id: 'source3',
+ description: 'Front port',
+ type: options.PowerStatusDeviceType.DUAL_ROLE_USB,
+ }, {
+ id: 'source4',
+ description: 'Rear port',
+ type: options.PowerStatusDeviceType.DUAL_ROLE_USB,
+ }];
+
+ // Use a dual-role device.
+ this.setPowerSources(fakeSources, 'source2', false, false);
+ this.checkSource('source2');
+
+ // Use a USB charger.
+ this.setPowerSources(fakeSources, 'source3', true, false);
+ this.checkSource('source3');
+
+ // Remove the currently used device.
+ fakeSources.splice(2, 1);
+ this.setPowerSources(fakeSources, 'source4', false, false);
+ this.checkSource('source4');
+
+ // Do not charge (use battery).
+ this.setPowerSources(fakeSources, '', false, false);
+ this.checkSource('');
+
+ // The user selects a device.
+ this.selectPowerSource('source1');
+
+ // The user selects the battery.
+ this.selectPowerSource('');
+});
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/proxy_handler.cc b/chromium/chrome/browser/ui/webui/options/chromeos/proxy_handler.cc
new file mode 100644
index 00000000000..72c066e765e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/proxy_handler.cc
@@ -0,0 +1,77 @@
+// 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/ui/webui/options/chromeos/proxy_handler.h"
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/system/input_device_settings.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/locale_settings.h"
+#include "chromeos/chromeos_constants.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace chromeos {
+namespace options {
+
+ProxyHandler::ProxyHandler() {
+}
+
+ProxyHandler::~ProxyHandler() {
+}
+
+void ProxyHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ // Proxy page - ChromeOS
+ static OptionsStringResource resources[] = {
+ { "proxyPage", IDS_OPTIONS_PROXY_TAB_LABEL },
+ { "proxyDirectInternetConnection", IDS_PROXY_DIRECT_CONNECTION },
+ { "proxyManual", IDS_PROXY_MANUAL_CONFIG },
+ { "sameProxyProtocols", IDS_PROXY_SAME_FORALL },
+ { "httpProxy", IDS_PROXY_HTTP_PROXY },
+ { "secureHttpProxy", IDS_PROXY_HTTP_SECURE_HTTP_PROXY },
+ { "ftpProxy", IDS_PROXY_FTP_PROXY },
+ { "socksHost", IDS_PROXY_SOCKS_HOST },
+ { "proxyAutomatic", IDS_PROXY_AUTOMATIC },
+ { "proxyUseConfigUrl", IDS_PROXY_USE_AUTOCONFIG_URL },
+ { "addHost", IDS_PROXY_ADD_HOST },
+ { "removeHost", IDS_PROXY_REMOVE_HOST },
+ { "proxyPort", IDS_PROXY_PORT },
+ { "proxyBypass", IDS_PROXY_BYPASS },
+ { "proxyBannerPolicy", IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PROXY_POLICY },
+ { "proxyBannerExtension",
+ IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PROXY_EXTENSION },
+ { "proxyBannerOther",
+ IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PROXY_OTHER_PRECEDE }
+ };
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+
+ localized_strings->SetString("proxyBannerShared",
+ l10n_util::GetStringFUTF16(
+ IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_PROXY_ENABLE_SHARED_HINT,
+ l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_USE_SHARED_PROXIES)));
+}
+
+void ProxyHandler::InitializePage() {
+ ::options::OptionsPageUIHandler::InitializePage();
+
+ bool keyboard_driven_oobe =
+ system::InputDeviceSettings::Get()->ForceKeyboardDrivenUINavigation();
+ if (keyboard_driven_oobe) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "DetailsInternetPage.initializeKeyboardFlow");
+ }
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/proxy_handler.h b/chromium/chrome/browser/ui/webui/options/chromeos/proxy_handler.h
new file mode 100644
index 00000000000..428020799ba
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/proxy_handler.h
@@ -0,0 +1,33 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_PROXY_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_PROXY_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+namespace chromeos {
+namespace options {
+
+// ChromeOS proxy options page UI handler.
+class ProxyHandler : public ::options::OptionsPageUIHandler {
+ public:
+ explicit ProxyHandler();
+ ~ProxyHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializePage() override;
+
+ private:
+
+ DISALLOW_COPY_AND_ASSIGN(ProxyHandler);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_PROXY_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/shared_options_browsertest.cc b/chromium/chrome/browser/ui/webui/options/chromeos/shared_options_browsertest.cc
new file mode 100644
index 00000000000..7e7b7fa708f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/shared_options_browsertest.cc
@@ -0,0 +1,467 @@
+// 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 <stddef.h>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/login/login_manager_test.h"
+#include "chrome/browser/chromeos/login/startup_utils.h"
+#include "chrome/browser/chromeos/login/ui/user_adding_screen.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/signin_manager_factory.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/common/pref_names.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "chromeos/settings/cros_settings_names.h"
+#if defined(GOOGLE_CHROME_BUILD)
+#include "components/spellcheck/browser/pref_names.h"
+#endif
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+
+namespace chromeos {
+
+namespace {
+
+// Because policy is not needed in this test it is better to use e-mails that
+// are definitely not enterprise. This lets us to avoid faking of policy fetch
+// procedure.
+const char* kTestOwner = "test-owner@gmail.com";
+const char* kTestNonOwner = "test-user1@gmail.com";
+
+const char* kKnownSettings[] = {
+ kDeviceOwner,
+ kAccountsPrefAllowGuest,
+ kAccountsPrefAllowNewUser,
+ kAccountsPrefDeviceLocalAccounts,
+ kAccountsPrefShowUserNamesOnSignIn,
+ kAccountsPrefSupervisedUsersEnabled,
+};
+
+// Stub settings provider that only handles the settings we need to control.
+// StubCrosSettingsProvider handles more settings but leaves many of them unset
+// which the Settings page doesn't expect.
+class StubAccountSettingsProvider : public StubCrosSettingsProvider {
+ public:
+ StubAccountSettingsProvider() {
+ }
+
+ ~StubAccountSettingsProvider() override {}
+
+ // StubCrosSettingsProvider implementation.
+ bool HandlesSetting(const std::string& path) const override {
+ const char** end = kKnownSettings + arraysize(kKnownSettings);
+ return std::find(kKnownSettings, end, path) != end;
+ }
+};
+
+struct PrefTest {
+ const char* pref_name;
+ bool owner_only;
+ bool indicator;
+};
+
+const PrefTest kPrefTests[] = {
+ { kSystemTimezone, false, false },
+ { prefs::kUse24HourClock, false, false },
+ { kAttestationForContentProtectionEnabled, true, true },
+ { kAccountsPrefAllowGuest, true, false },
+ { kAccountsPrefAllowNewUser, true, false },
+ { kAccountsPrefShowUserNamesOnSignIn, true, false },
+ { kAccountsPrefSupervisedUsersEnabled, true, false },
+#if defined(GOOGLE_CHROME_BUILD)
+ { kStatsReportingPref, true, true },
+ { spellcheck::prefs::kSpellCheckUseSpellingService, false, false },
+#endif
+};
+
+} // namespace
+
+class SharedOptionsTest : public LoginManagerTest {
+ public:
+ SharedOptionsTest()
+ : LoginManagerTest(false),
+ stub_settings_provider_(base::MakeUnique<StubCrosSettingsProvider>()),
+ stub_settings_provider_ptr_(static_cast<StubCrosSettingsProvider*>(
+ stub_settings_provider_.get())),
+ test_owner_account_id_(AccountId::FromUserEmail(kTestOwner)),
+ test_non_owner_account_id_(AccountId::FromUserEmail(kTestNonOwner)) {
+ stub_settings_provider_->Set(kDeviceOwner, base::Value(kTestOwner));
+ }
+
+ ~SharedOptionsTest() override {}
+
+ void SetUpOnMainThread() override {
+ LoginManagerTest::SetUpOnMainThread();
+
+ CrosSettings* settings = CrosSettings::Get();
+
+ // Add the stub settings provider, moving the device settings provider
+ // behind it so our stub takes precedence.
+ std::unique_ptr<CrosSettingsProvider> device_settings_provider =
+ settings->RemoveSettingsProvider(settings->GetProvider(kDeviceOwner));
+ settings->AddSettingsProvider(std::move(stub_settings_provider_));
+ settings->AddSettingsProvider(std::move(device_settings_provider));
+
+ // Notify ChromeUserManager of ownership change.
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_OWNERSHIP_STATUS_CHANGED,
+ content::Source<SharedOptionsTest>(this),
+ content::NotificationService::NoDetails());
+ }
+
+ void TearDownOnMainThread() override {
+ CrosSettings* settings = CrosSettings::Get();
+ settings->RemoveSettingsProvider(stub_settings_provider_ptr_);
+ LoginManagerTest::TearDownOnMainThread();
+ }
+
+ protected:
+ void CheckOptionsUI(const user_manager::User* user,
+ bool is_owner,
+ bool is_primary) {
+ ASSERT_NE(nullptr, user);
+ Browser* browser = CreateBrowserForUser(user);
+ content::WebContents* contents =
+ browser->tab_strip_model()->GetActiveWebContents();
+
+ for (size_t i = 0; i < sizeof(kPrefTests) / sizeof(kPrefTests[0]); i++) {
+ bool disabled = !is_owner && kPrefTests[i].owner_only;
+ if (strcmp(kPrefTests[i].pref_name, kSystemTimezone) == 0) {
+ disabled = ProfileHelper::Get()
+ ->GetProfileByUser(user)
+ ->GetPrefs()
+ ->GetBoolean(prefs::kResolveTimezoneByGeolocation);
+ }
+
+ CheckPreference(
+ contents, kPrefTests[i].pref_name, disabled,
+ !is_owner && kPrefTests[i].indicator ? "owner" : std::string());
+ }
+ CheckBanner(contents, is_primary);
+ CheckSharedSections(contents, is_primary);
+ CheckAccountsOverlay(contents, is_owner);
+ }
+
+ // Creates a browser and navigates to the Settings page.
+ Browser* CreateBrowserForUser(const user_manager::User* user) {
+ Profile* profile = ProfileHelper::Get()->GetProfileByUser(user);
+ SigninManagerFactory::GetForProfile(profile)->SetAuthenticatedAccountInfo(
+ GetGaiaIDForUserID(user->GetAccountId().GetUserEmail()),
+ user->GetAccountId().GetUserEmail());
+
+ ui_test_utils::BrowserAddedObserver observer;
+ Browser* browser = CreateBrowser(profile);
+ observer.WaitForSingleNewBrowser();
+
+ ui_test_utils::NavigateToURL(browser,
+ GURL("chrome://settings-frame"));
+ return browser;
+ }
+
+ // Verifies a preference's disabled state and controlled-by indicator.
+ void CheckPreference(content::WebContents* contents,
+ std::string pref_name,
+ bool disabled,
+ std::string controlled_by) {
+ bool success;
+ std::string js_expression = base::StringPrintf(
+ "var prefSelector = '[pref=\"%s\"]';"
+ "var controlledBy = '%s';"
+ "var input = document.querySelector("
+ " 'input' + prefSelector + ', select' + prefSelector);"
+ "var success = false;"
+ "if (input) {"
+ " success = input.disabled == %d;"
+ " var indicator = input.parentNode.parentNode.querySelector("
+ " '.controlled-setting-indicator');"
+ " if (controlledBy) {"
+ " success = success && indicator &&"
+ " indicator.getAttribute('controlled-by') == controlledBy;"
+ " } else {"
+ " success = success && (!indicator ||"
+ " !indicator.hasAttribute('controlled-by') ||"
+ " indicator.getAttribute('controlled-by') == '')"
+ " }"
+ "}"
+ "window.domAutomationController.send(!!success);",
+ pref_name.c_str(), controlled_by.c_str(), disabled);
+ ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+ contents, js_expression, &success));
+ EXPECT_TRUE(success);
+ }
+
+ // Verifies a checkbox's disabled state, controlled-by indicator and value.
+ void CheckBooleanPreference(content::WebContents* contents,
+ std::string pref_name,
+ bool disabled,
+ std::string controlled_by,
+ bool expected_value) {
+ CheckPreference(contents, pref_name, disabled, controlled_by);
+ bool actual_value;
+ std::string js_expression = base::StringPrintf(
+ "window.domAutomationController.send(document.querySelector('"
+ " input[type=\"checkbox\"][pref=\"%s\"]').checked);",
+ pref_name.c_str());
+ ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+ contents, js_expression, &actual_value));
+ EXPECT_EQ(expected_value, actual_value);
+ }
+
+ // Verifies that the shared settings banner is visible only for
+ // secondary users.
+ void CheckBanner(content::WebContents* contents,
+ bool is_primary) {
+ bool banner_visible;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+ contents,
+ "var e = $('secondary-user-banner');"
+ "window.domAutomationController.send(e && !e.hidden);",
+ &banner_visible));
+ EXPECT_EQ(!is_primary, banner_visible);
+ }
+
+ // Verifies that sections of shared settings have the appropriate indicator.
+ void CheckSharedSections(content::WebContents* contents,
+ bool is_primary) {
+ // This only applies to the Internet options section.
+ std::string controlled_by;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ contents,
+ "var e = document.querySelector("
+ " '#network-section-header span.controlled-setting-indicator');"
+ "if (!e || !e.getAttribute('controlled-by')) {"
+ " window.domAutomationController.send('');"
+ "} else {"
+ " window.domAutomationController.send("
+ " e.getAttribute('controlled-by'));"
+ "}",
+ &controlled_by));
+ EXPECT_EQ(!is_primary ? "shared" : std::string(), controlled_by);
+ }
+
+ // Checks the Accounts header and non-checkbox inputs.
+ void CheckAccountsOverlay(content::WebContents* contents, bool is_owner) {
+ // Set cros.accounts.allowGuest to false so we can test the accounts list.
+ // This has to be done after the PRE_* test or we can't add the owner.
+ stub_settings_provider_ptr_->Set(kAccountsPrefAllowNewUser,
+ base::Value(false));
+
+ bool success;
+ std::string js_expression = base::StringPrintf(
+ "var controlled = %d;"
+ "var warning = $('ownerOnlyWarning');"
+ "var userList = $('userList');"
+ "var input = $('userNameEdit');"
+ "var success;"
+ "if (controlled)"
+ " success = warning && !warning.hidden && userList.disabled &&"
+ " input.disabled;"
+ "else"
+ " success = (!warning || warning.hidden) && !userList.disabled &&"
+ " !input.disabled;"
+ "window.domAutomationController.send(!!success);",
+ !is_owner);
+ ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+ contents, js_expression, &success));
+ EXPECT_TRUE(success) << "Accounts overlay incorrect for " <<
+ (is_owner ? "owner." : "non-owner.");
+ }
+
+ std::unique_ptr<CrosSettingsProvider> stub_settings_provider_;
+ StubCrosSettingsProvider* stub_settings_provider_ptr_;
+
+ const AccountId test_owner_account_id_;
+ const AccountId test_non_owner_account_id_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SharedOptionsTest);
+};
+
+IN_PROC_BROWSER_TEST_F(SharedOptionsTest, PRE_SharedOptions) {
+ RegisterUser(test_owner_account_id_.GetUserEmail());
+ RegisterUser(test_non_owner_account_id_.GetUserEmail());
+ StartupUtils::MarkOobeCompleted();
+}
+
+IN_PROC_BROWSER_TEST_F(SharedOptionsTest, SharedOptions) {
+ // Log in the owner first, then add a secondary user.
+ LoginUser(test_owner_account_id_.GetUserEmail());
+ UserAddingScreen::Get()->Start();
+ content::RunAllPendingInMessageLoop();
+ AddUser(test_non_owner_account_id_.GetUserEmail());
+
+ user_manager::UserManager* manager = user_manager::UserManager::Get();
+ ASSERT_EQ(2u, manager->GetLoggedInUsers().size());
+ {
+ SCOPED_TRACE("Checking settings for owner, primary user.");
+ CheckOptionsUI(manager->FindUser(manager->GetOwnerAccountId()), true, true);
+ }
+ {
+ SCOPED_TRACE("Checking settings for non-owner, secondary user.");
+ CheckOptionsUI(manager->FindUser(test_non_owner_account_id_), false, false);
+ }
+ // TODO(michaelpg): Add tests for non-primary owner and primary non-owner
+ // when the owner-only multiprofile restriction is removed, probably M38.
+}
+
+IN_PROC_BROWSER_TEST_F(SharedOptionsTest, PRE_ScreenLockPreferencePrimary) {
+ RegisterUser(test_owner_account_id_.GetUserEmail());
+ RegisterUser(test_non_owner_account_id_.GetUserEmail());
+ StartupUtils::MarkOobeCompleted();
+}
+
+// Tests the shared setting indicator for the primary user's auto-lock setting
+// when the secondary user has enabled or disabled their preference.
+// (The checkbox is unset if the current user's preference is false, but if any
+// other signed-in user has enabled this preference, the shared setting
+// indicator explains this.)
+IN_PROC_BROWSER_TEST_F(SharedOptionsTest, ScreenLockPreferencePrimary) {
+ LoginUser(test_owner_account_id_.GetUserEmail());
+ UserAddingScreen::Get()->Start();
+ content::RunAllPendingInMessageLoop();
+ AddUser(test_non_owner_account_id_.GetUserEmail());
+
+ user_manager::UserManager* manager = user_manager::UserManager::Get();
+ const user_manager::User* user1 = manager->FindUser(test_owner_account_id_);
+ const user_manager::User* user2 =
+ manager->FindUser(test_non_owner_account_id_);
+
+ PrefService* prefs1 =
+ ProfileHelper::Get()->GetProfileByUser(user1)->GetPrefs();
+ PrefService* prefs2 =
+ ProfileHelper::Get()->GetProfileByUser(user2)->GetPrefs();
+
+ // Set both users' preference to false, then change the secondary user's to
+ // true. We'll do the opposite in the next test. Doesn't provide 100% coverage
+ // but reloading the settings page is super slow on debug builds.
+ prefs1->SetBoolean(prefs::kEnableAutoScreenLock, false);
+ prefs2->SetBoolean(prefs::kEnableAutoScreenLock, false);
+
+ Browser* browser = CreateBrowserForUser(user1);
+ content::WebContents* contents =
+ browser->tab_strip_model()->GetActiveWebContents();
+
+ bool disabled = false;
+ bool expected_value;
+ std::string empty_controlled;
+ std::string shared_controlled("shared");
+
+ {
+ SCOPED_TRACE("Screen lock false for both users");
+ expected_value = false;
+ CheckBooleanPreference(contents, prefs::kEnableAutoScreenLock, disabled,
+ empty_controlled, expected_value);
+ }
+
+ // Set the secondary user's preference to true, and reload the primary user's
+ // browser to see the updated controlled-by indicator.
+ prefs2->SetBoolean(prefs::kEnableAutoScreenLock, true);
+ chrome::Reload(browser, WindowOpenDisposition::CURRENT_TAB);
+ content::WaitForLoadStop(contents);
+ {
+ SCOPED_TRACE("Screen lock false for primary user");
+ expected_value = false;
+ CheckBooleanPreference(contents, prefs::kEnableAutoScreenLock, disabled,
+ shared_controlled, expected_value);
+ }
+
+ // Set the preference to true for the primary user and check that the
+ // indicator disappears.
+ prefs1->SetBoolean(prefs::kEnableAutoScreenLock, true);
+ {
+ SCOPED_TRACE("Screen lock true for both users");
+ expected_value = true;
+ CheckBooleanPreference(contents, prefs::kEnableAutoScreenLock, disabled,
+ empty_controlled, expected_value);
+ }
+}
+
+IN_PROC_BROWSER_TEST_F(SharedOptionsTest, PRE_ScreenLockPreferenceSecondary) {
+ RegisterUser(test_owner_account_id_.GetUserEmail());
+ RegisterUser(test_non_owner_account_id_.GetUserEmail());
+ StartupUtils::MarkOobeCompleted();
+}
+
+// Tests the shared setting indicator for the secondary user's auto-lock setting
+// when the primary user has enabled or disabled their preference.
+// (The checkbox is unset if the current user's preference is false, but if any
+// other signed-in user has enabled this preference, the shared setting
+// indicator explains this.)
+IN_PROC_BROWSER_TEST_F(SharedOptionsTest, ScreenLockPreferenceSecondary) {
+ LoginUser(test_owner_account_id_.GetUserEmail());
+ UserAddingScreen::Get()->Start();
+ content::RunAllPendingInMessageLoop();
+ AddUser(test_non_owner_account_id_.GetUserEmail());
+
+ user_manager::UserManager* manager = user_manager::UserManager::Get();
+ const user_manager::User* user1 = manager->FindUser(test_owner_account_id_);
+ const user_manager::User* user2 =
+ manager->FindUser(test_non_owner_account_id_);
+
+ PrefService* prefs1 =
+ ProfileHelper::Get()->GetProfileByUser(user1)->GetPrefs();
+ PrefService* prefs2 =
+ ProfileHelper::Get()->GetProfileByUser(user2)->GetPrefs();
+
+ // Set both users' preference to true, then change the secondary user's to
+ // false.
+ prefs1->SetBoolean(prefs::kEnableAutoScreenLock, true);
+ prefs2->SetBoolean(prefs::kEnableAutoScreenLock, true);
+
+ Browser* browser = CreateBrowserForUser(user2);
+ content::WebContents* contents =
+ browser->tab_strip_model()->GetActiveWebContents();
+
+ bool disabled = false;
+ bool expected_value;
+ std::string empty_controlled;
+ std::string shared_controlled("shared");
+
+ {
+ SCOPED_TRACE("Screen lock true for both users");
+ expected_value = true;
+ CheckBooleanPreference(contents, prefs::kEnableAutoScreenLock, disabled,
+ empty_controlled, expected_value);
+ }
+
+ // Set the secondary user's preference to false and check that the
+ // controlled-by indicator is shown.
+ prefs2->SetBoolean(prefs::kEnableAutoScreenLock, false);
+ {
+ SCOPED_TRACE("Screen lock false for secondary user");
+ expected_value = false;
+ CheckBooleanPreference(contents, prefs::kEnableAutoScreenLock, disabled,
+ shared_controlled, expected_value);
+ }
+
+ // Set the preference to false for the primary user and check that the
+ // indicator disappears.
+ prefs1->SetBoolean(prefs::kEnableAutoScreenLock, false);
+ chrome::Reload(browser, WindowOpenDisposition::CURRENT_TAB);
+ content::WaitForLoadStop(contents);
+ {
+ SCOPED_TRACE("Screen lock false for both users");
+ expected_value = false;
+ CheckBooleanPreference(contents, prefs::kEnableAutoScreenLock, disabled,
+ empty_controlled, expected_value);
+ }
+}
+
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/stats_options_handler.cc b/chromium/chrome/browser/ui/webui/options/chromeos/stats_options_handler.cc
new file mode 100644
index 00000000000..93048cbafeb
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/stats_options_handler.cc
@@ -0,0 +1,46 @@
+// 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/ui/webui/options/chromeos/stats_options_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "content/public/browser/web_ui.h"
+
+using base::UserMetricsAction;
+
+namespace chromeos {
+namespace options {
+
+StatsOptionsHandler::StatsOptionsHandler() {
+}
+
+// OptionsPageUIHandler implementation.
+void StatsOptionsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+}
+
+// WebUIMessageHandler implementation.
+void StatsOptionsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("metricsReportingCheckboxAction",
+ base::Bind(&StatsOptionsHandler::HandleMetricsReportingCheckbox,
+ base::Unretained(this)));
+}
+
+void StatsOptionsHandler::HandleMetricsReportingCheckbox(
+ const base::ListValue* args) {
+#if defined(GOOGLE_CHROME_BUILD)
+ const std::string checked_str = base::UTF16ToUTF8(ExtractStringValue(args));
+ const bool enabled = (checked_str == "true");
+ base::RecordAction(
+ enabled ? UserMetricsAction("Options_MetricsReportingCheckbox_Enable")
+ : UserMetricsAction("Options_MetricsReportingCheckbox_Disable"));
+#endif
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/stats_options_handler.h b/chromium/chrome/browser/ui/webui/options/chromeos/stats_options_handler.h
new file mode 100644
index 00000000000..8c95f11229c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/stats_options_handler.h
@@ -0,0 +1,38 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_STATS_OPTIONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_STATS_OPTIONS_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+namespace chromeos {
+namespace options {
+
+// ChromeOS handler for "Stats/crash reporting to Google" option of the Advanced
+// settings page. This handler does only ChromeOS-specific actions while default
+// code is in Chrome's AdvancedOptionsHandler
+// (chrome/browser/webui/advanced_options_handler.cc).
+class StatsOptionsHandler : public ::options::OptionsPageUIHandler {
+ public:
+ StatsOptionsHandler();
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ void HandleMetricsReportingCheckbox(const base::ListValue* args);
+
+ DISALLOW_COPY_AND_ASSIGN(StatsOptionsHandler);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_STATS_OPTIONS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/storage_manager_handler.cc b/chromium/chrome/browser/ui/webui/options/chromeos/storage_manager_handler.cc
new file mode 100644
index 00000000000..c6e0c9f4d74
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/storage_manager_handler.cc
@@ -0,0 +1,450 @@
+// 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 "chrome/browser/ui/webui/options/chromeos/storage_manager_handler.h"
+
+#include <algorithm>
+#include <numeric>
+#include <string>
+
+#include "base/files/file_util.h"
+#include "base/sys_info.h"
+#include "base/task_scheduler/post_task.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browsing_data/browsing_data_appcache_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_cache_storage_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_channel_id_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_cookie_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_database_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_file_system_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_flash_lso_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_service_worker_helper.h"
+#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/drive/file_system_util.h"
+#include "chrome/browser/chromeos/file_manager/path_util.h"
+#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 "components/arc/arc_util.h"
+#include "components/browsing_data/content/conditional_cache_counting_helper.h"
+#include "components/drive/chromeos/file_system_interface.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/text/bytes_formatting.h"
+
+namespace chromeos {
+namespace options {
+namespace {
+
+void GetSizeStatAsync(const base::FilePath& mount_path,
+ int64_t* total_size,
+ int64_t* available_size) {
+ int64_t size = base::SysInfo::AmountOfTotalDiskSpace(mount_path);
+ if (size >= 0)
+ *total_size = size;
+ size = base::SysInfo::AmountOfFreeDiskSpace(mount_path);
+ if (size >= 0)
+ *available_size = size;
+}
+
+// Threshold to show a message indicating space is critically low (512 MB).
+const int64_t kSpaceCriticallyLowBytes = 512 * 1024 * 1024;
+
+// Threshold to show a message indicating space is low (1 GB).
+const int64_t kSpaceLowBytes = 1 * 1024 * 1024 * 1024;
+
+} // namespace
+
+StorageManagerHandler::StorageManagerHandler()
+ : browser_cache_size_(-1),
+ has_browser_cache_size_(false),
+ browser_site_data_size_(-1),
+ has_browser_site_data_size_(false),
+ updating_downloads_size_(false),
+ updating_drive_cache_size_(false),
+ updating_browsing_data_size_(false),
+ updating_arc_size_(false),
+ updating_other_users_size_(false),
+ weak_ptr_factory_(this) {
+}
+
+StorageManagerHandler::~StorageManagerHandler() {
+}
+
+void StorageManagerHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ RegisterTitle(localized_strings, "storageManagerPage",
+ IDS_OPTIONS_SETTINGS_STORAGE_MANAGER_TAB_TITLE);
+ RegisterTitle(localized_strings, "storageClearDriveCachePage",
+ IDS_OPTIONS_SETTINGS_STORAGE_CLEAR_DRIVE_CACHE_TAB_TITLE);
+
+ localized_strings->SetString(
+ "storageItemLabelCapacity", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_ITEM_LABEL_CAPACITY));
+ localized_strings->SetString(
+ "storageItemLabelInUse", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_ITEM_LABEL_IN_USE));
+ localized_strings->SetString(
+ "storageItemLabelAvailable", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_ITEM_LABEL_AVAILABLE));
+ localized_strings->SetString(
+ "storageSubitemLabelDownloads", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SUBITEM_LABEL_DOWNLOADS));
+ localized_strings->SetString(
+ "storageSubitemLabelDriveCache", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SUBITEM_LABEL_DRIVE_CACHE));
+ localized_strings->SetString(
+ "storageSubitemLabelBrowsingData", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SUBITEM_LABEL_BROWSING_DATA));
+ localized_strings->SetString(
+ "storageSubitemLabelOtherUsers", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SUBITEM_LABEL_OTHER_USERS));
+ localized_strings->SetString(
+ "storageSubitemLabelArc", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SUBITEM_LABEL_ARC));
+ localized_strings->SetString(
+ "storageSizeCalculating", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SIZE_CALCULATING));
+ localized_strings->SetString(
+ "storageClearDriveCacheDialogTitle", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_CLEAR_DRIVE_CACHE_DIALOG_TITLE));
+ localized_strings->SetString(
+ "storageClearDriveCacheDescription", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_CLEAR_DRIVE_CACHE_DESCRIPTION));
+ localized_strings->SetString(
+ "storageDeleteAllButtonTitle", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_DELETE_ALL_BUTTON_TITLE));
+ localized_strings->SetString(
+ "storageLowMessageTitle", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SPACE_LOW_MESSAGE_TITLE));
+ localized_strings->SetString(
+ "storageLowMessageLine1", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SPACE_LOW_MESSAGE_LINE_1));
+ localized_strings->SetString(
+ "storageLowMessageLine2", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SPACE_LOW_MESSAGE_LINE_2));
+ localized_strings->SetString(
+ "storageCriticallyLowMessageTitle", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SPACE_CRITICALLY_LOW_MESSAGE_TITLE));
+ localized_strings->SetString(
+ "storageCriticallyLowMessageLine1", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SPACE_CRITICALLY_LOW_MESSAGE_LINE_1));
+ localized_strings->SetString(
+ "storageCriticallyLowMessageLine2", l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SPACE_CRITICALLY_LOW_MESSAGE_LINE_2));
+}
+
+void StorageManagerHandler::InitializePage() {
+ DCHECK(web_ui());
+}
+
+void StorageManagerHandler::RegisterMessages() {
+ DCHECK(web_ui());
+
+ web_ui()->RegisterMessageCallback(
+ "updateStorageInfo",
+ base::Bind(&StorageManagerHandler::HandleUpdateStorageInfo,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "openDownloads",
+ base::Bind(&StorageManagerHandler::HandleOpenDownloads,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "openArcStorage",
+ base::Bind(&StorageManagerHandler::HandleOpenArcStorage,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "clearDriveCache",
+ base::Bind(&StorageManagerHandler::HandleClearDriveCache,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void StorageManagerHandler::HandleUpdateStorageInfo(
+ const base::ListValue* unused_args) {
+ UpdateSizeStat();
+ UpdateDownloadsSize();
+ UpdateDriveCacheSize();
+ UpdateBrowsingDataSize();
+ UpdateOtherUsersSize();
+ UpdateArcSize();
+}
+
+void StorageManagerHandler::HandleOpenDownloads(
+ const base::ListValue* unused_args) {
+ Profile* const profile = Profile::FromWebUI(web_ui());
+ const base::FilePath downloads_path =
+ file_manager::util::GetDownloadsFolderForProfile(profile);
+ platform_util::OpenItem(
+ profile,
+ downloads_path,
+ platform_util::OPEN_FOLDER,
+ platform_util::OpenOperationCallback());
+}
+
+void StorageManagerHandler::HandleOpenArcStorage(
+ const base::ListValue* unused_args) {
+ arc::ArcStorageManager::Get()->OpenPrivateVolumeSettings();
+}
+
+void StorageManagerHandler::HandleClearDriveCache(
+ const base::ListValue* unused_args) {
+ drive::FileSystemInterface* const file_system =
+ drive::util::GetFileSystemByProfile(Profile::FromWebUI(web_ui()));
+ file_system->FreeDiskSpaceIfNeededFor(
+ std::numeric_limits<int64_t>::max(), // Removes as much as possible.
+ base::Bind(&StorageManagerHandler::OnClearDriveCacheDone,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void StorageManagerHandler::UpdateSizeStat() {
+ Profile* const profile = Profile::FromWebUI(web_ui());
+ const base::FilePath downloads_path =
+ file_manager::util::GetDownloadsFolderForProfile(profile);
+
+ int64_t* total_size = new int64_t(0);
+ int64_t* available_size = new int64_t(0);
+ base::PostTaskWithTraitsAndReply(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&GetSizeStatAsync, downloads_path, total_size, available_size),
+ base::Bind(&StorageManagerHandler::OnGetSizeStat,
+ weak_ptr_factory_.GetWeakPtr(), base::Owned(total_size),
+ base::Owned(available_size)));
+}
+
+void StorageManagerHandler::OnGetSizeStat(int64_t* total_size,
+ int64_t* available_size) {
+ int64_t used_size = *total_size - *available_size;
+ base::DictionaryValue size_stat;
+ size_stat.SetString("totalSize", ui::FormatBytes(*total_size));
+ size_stat.SetString("availableSize", ui::FormatBytes(*available_size));
+ size_stat.SetString("usedSize", ui::FormatBytes(used_size));
+ size_stat.SetDouble("usedRatio",
+ static_cast<double>(used_size) / *total_size);
+ int storage_space_state = STORAGE_SPACE_NORMAL;
+ if (*available_size < kSpaceCriticallyLowBytes)
+ storage_space_state = STORAGE_SPACE_CRITICALLY_LOW;
+ else if (*available_size < kSpaceLowBytes)
+ storage_space_state = STORAGE_SPACE_LOW;
+ size_stat.SetInteger("spaceState", storage_space_state);
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.StorageManager.setSizeStat", size_stat);
+}
+
+void StorageManagerHandler::UpdateDownloadsSize() {
+ if (updating_downloads_size_)
+ return;
+ updating_downloads_size_ = true;
+
+ Profile* const profile = Profile::FromWebUI(web_ui());
+ const base::FilePath downloads_path =
+ file_manager::util::GetDownloadsFolderForProfile(profile);
+
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&base::ComputeDirectorySize, downloads_path),
+ base::Bind(&StorageManagerHandler::OnGetDownloadsSize,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void StorageManagerHandler::OnGetDownloadsSize(int64_t size) {
+ updating_downloads_size_ = false;
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.StorageManager.setDownloadsSize",
+ base::Value(ui::FormatBytes(size)));
+}
+
+void StorageManagerHandler::UpdateDriveCacheSize() {
+ if (updating_drive_cache_size_)
+ return;
+
+ drive::FileSystemInterface* const file_system =
+ drive::util::GetFileSystemByProfile(Profile::FromWebUI(web_ui()));
+ if (!file_system)
+ return;
+
+ // Shows the item "Offline cache" and start calculating size.
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.StorageManager.showDriveCacheItem");
+ updating_drive_cache_size_ = true;
+ file_system->CalculateCacheSize(
+ base::Bind(&StorageManagerHandler::OnGetDriveCacheSize,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void StorageManagerHandler::OnGetDriveCacheSize(int64_t size) {
+ updating_drive_cache_size_ = false;
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.StorageManager.setDriveCacheSize",
+ base::Value(ui::FormatBytes(size)));
+}
+
+void StorageManagerHandler::UpdateBrowsingDataSize() {
+ if (updating_browsing_data_size_)
+ return;
+ updating_browsing_data_size_ = true;
+
+ has_browser_cache_size_ = false;
+ has_browser_site_data_size_ = false;
+ Profile* const profile = Profile::FromWebUI(web_ui());
+ // Fetch the size of http cache in browsing data.
+ // ConditionalCacheCountingHelper deletes itself when it is done.
+ browsing_data::ConditionalCacheCountingHelper::CreateForRange(
+ content::BrowserContext::GetDefaultStoragePartition(profile),
+ base::Time(), base::Time::Max())
+ ->CountAndDestroySelfWhenFinished(
+ base::Bind(&StorageManagerHandler::OnGetCacheSize,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ // Fetch the size of site data in browsing data.
+ if (!site_data_size_collector_.get()) {
+ content::StoragePartition* storage_partition =
+ content::BrowserContext::GetDefaultStoragePartition(profile);
+ site_data_size_collector_.reset(new SiteDataSizeCollector(
+ storage_partition->GetPath(),
+ new BrowsingDataCookieHelper(profile->GetRequestContext()),
+ new BrowsingDataDatabaseHelper(profile),
+ new BrowsingDataLocalStorageHelper(profile),
+ new BrowsingDataAppCacheHelper(profile),
+ new BrowsingDataIndexedDBHelper(
+ storage_partition->GetIndexedDBContext()),
+ BrowsingDataFileSystemHelper::Create(
+ storage_partition->GetFileSystemContext()),
+ BrowsingDataChannelIDHelper::Create(profile->GetRequestContext()),
+ new BrowsingDataServiceWorkerHelper(
+ storage_partition->GetServiceWorkerContext()),
+ new BrowsingDataCacheStorageHelper(
+ storage_partition->GetCacheStorageContext()),
+ BrowsingDataFlashLSOHelper::Create(profile)));
+ }
+ site_data_size_collector_->Fetch(
+ base::Bind(&StorageManagerHandler::OnGetBrowsingDataSize,
+ weak_ptr_factory_.GetWeakPtr(), true));
+}
+
+void StorageManagerHandler::OnGetCacheSize(bool is_upper_limit, int64_t size) {
+ DCHECK(!is_upper_limit);
+ OnGetBrowsingDataSize(false, size);
+}
+
+void StorageManagerHandler::OnGetBrowsingDataSize(bool is_site_data,
+ int64_t size) {
+ if (is_site_data) {
+ has_browser_site_data_size_ = true;
+ browser_site_data_size_ = size;
+ } else {
+ has_browser_cache_size_ = true;
+ browser_cache_size_ = size;
+ }
+ if (has_browser_cache_size_ && has_browser_site_data_size_) {
+ base::string16 size_string;
+ if (browser_cache_size_ >= 0 && browser_site_data_size_ >= 0) {
+ size_string = ui::FormatBytes(
+ browser_site_data_size_ + browser_cache_size_);
+ } else {
+ size_string = l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SIZE_UNKNOWN);
+ }
+ updating_browsing_data_size_ = false;
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.StorageManager.setBrowsingDataSize", base::Value(size_string));
+ }
+}
+
+void StorageManagerHandler::UpdateOtherUsersSize() {
+ if (updating_other_users_size_)
+ return;
+ updating_other_users_size_ = true;
+
+ other_users_.clear();
+ user_sizes_.clear();
+ const user_manager::UserList& users =
+ user_manager::UserManager::Get()->GetUsers();
+ for (auto* user : users) {
+ if (user->is_active())
+ continue;
+ other_users_.push_back(user);
+ cryptohome::HomedirMethods::GetInstance()->GetAccountDiskUsage(
+ cryptohome::Identification(user->GetAccountId()),
+ base::Bind(&StorageManagerHandler::OnGetOtherUserSize,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+ // We should show "0 B" if there is no other user.
+ if (other_users_.empty()) {
+ updating_other_users_size_ = false;
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.StorageManager.setOtherUsersSize",
+ base::Value(ui::FormatBytes(0)));
+ }
+}
+
+void StorageManagerHandler::OnGetOtherUserSize(bool success, int64_t size) {
+ user_sizes_.push_back(success ? size : -1);
+ if (user_sizes_.size() == other_users_.size()) {
+ base::string16 size_string;
+ // If all the requests succeed, shows the total bytes in the UI.
+ if (std::count(user_sizes_.begin(), user_sizes_.end(), -1) == 0) {
+ size_string = ui::FormatBytes(
+ std::accumulate(user_sizes_.begin(), user_sizes_.end(), 0LL));
+ } else {
+ size_string = l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SIZE_UNKNOWN);
+ }
+ updating_other_users_size_ = false;
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.StorageManager.setOtherUsersSize", base::Value(size_string));
+ }
+}
+
+void StorageManagerHandler::UpdateArcSize() {
+ if (updating_arc_size_)
+ return;
+ updating_arc_size_ = true;
+
+ Profile* const profile = Profile::FromWebUI(web_ui());
+ if (!arc::IsArcPlayStoreEnabledForProfile(profile) ||
+ arc::IsArcOptInVerificationDisabled()) {
+ return;
+ }
+
+ // Shows the item "Android apps and cache" and start calculating size.
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.StorageManager.showArcItem");
+ bool success = arc::ArcStorageManager::Get()->GetApplicationsSize(
+ base::Bind(&StorageManagerHandler::OnGetArcSize,
+ weak_ptr_factory_.GetWeakPtr()));
+ if (!success)
+ updating_arc_size_ = false;
+}
+
+void StorageManagerHandler::OnGetArcSize(bool succeeded,
+ arc::mojom::ApplicationsSizePtr size) {
+ base::string16 size_string;
+ if (succeeded) {
+ uint64_t total_bytes = size->total_code_bytes +
+ size->total_data_bytes +
+ size->total_cache_bytes;
+ size_string = ui::FormatBytes(total_bytes);
+ } else {
+ size_string = l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SIZE_UNKNOWN);
+ }
+ updating_arc_size_ = false;
+ web_ui()->CallJavascriptFunctionUnsafe("options.StorageManager.setArcSize",
+ base::Value(size_string));
+}
+
+void StorageManagerHandler::OnClearDriveCacheDone(bool success) {
+ UpdateDriveCacheSize();
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/storage_manager_handler.h b/chromium/chrome/browser/ui/webui/options/chromeos/storage_manager_handler.h
new file mode 100644
index 00000000000..052524e3518
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/storage_manager_handler.h
@@ -0,0 +1,130 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_STORAGE_MANAGER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_STORAGE_MANAGER_HANDLER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/browsing_data/site_data_size_collector.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "components/arc/storage_manager/arc_storage_manager.h"
+#include "components/user_manager/user.h"
+
+namespace chromeos {
+namespace options {
+
+// Storage manager overlay page UI handler.
+class StorageManagerHandler : public ::options::OptionsPageUIHandler {
+ public:
+ // Enumeration for device state about remaining space. These values must be
+ // kept in sync with options.StorageSpaceState in JS code.
+ enum StorageSpaceState {
+ STORAGE_SPACE_NORMAL = 0,
+ STORAGE_SPACE_LOW = 1,
+ STORAGE_SPACE_CRITICALLY_LOW = 2,
+ };
+
+ StorageManagerHandler();
+ ~StorageManagerHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializePage() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ // Handlers of JS messages.
+ void HandleUpdateStorageInfo(const base::ListValue* unused_args);
+ void HandleOpenDownloads(const base::ListValue* unused_args);
+ void HandleOpenArcStorage(const base::ListValue* unused_args);
+ void HandleClearDriveCache(const base::ListValue* unused_args);
+
+ // Requests updating disk space information.
+ void UpdateSizeStat();
+
+ // Callback to update the UI about disk space information.
+ void OnGetSizeStat(int64_t* total_size, int64_t* available_size);
+
+ // Requests updating the size of Downloads directory.
+ void UpdateDownloadsSize();
+
+ // Callback to update the UI about the size of Downloads directory.
+ void OnGetDownloadsSize(int64_t size);
+
+ // Requests updating the size of Drive Cache.
+ void UpdateDriveCacheSize();
+
+ // Callback to update the UI about the size of Drive Cache.
+ void OnGetDriveCacheSize(int64_t size);
+
+ // Requests updating the size of browsing data.
+ void UpdateBrowsingDataSize();
+
+ // Callback to receive the cache size.
+ void OnGetCacheSize(bool is_upper_limit, int64_t size);
+
+ // Callback to update the UI about the size of browsing data.
+ void OnGetBrowsingDataSize(bool is_site_data, int64_t size);
+
+ // Requests updating the total size of other users' data.
+ void UpdateOtherUsersSize();
+
+ // Callback to save the fetched user sizes and update the UI.
+ void OnGetOtherUserSize(bool success, int64_t size);
+
+ // Requests updating the space size used by Android apps and cache.
+ void UpdateArcSize();
+
+ // Callback to update the UI about Android apps and cache.
+ void OnGetArcSize(bool succeeded, arc::mojom::ApplicationsSizePtr size);
+
+ // Callback called when clearing Drive cache is done.
+ void OnClearDriveCacheDone(bool success);
+
+ // Total size of cache data in browsing data.
+ int64_t browser_cache_size_;
+
+ // True if we have already received the size of http cache.
+ bool has_browser_cache_size_;
+
+ // Total size of site data in browsing data.
+ int64_t browser_site_data_size_;
+
+ // True if we have already received the size of site data.
+ bool has_browser_site_data_size_;
+
+ // The list of other users whose directory sizes will be accumulated as the
+ // size of "Other users".
+ user_manager::UserList other_users_;
+
+ // Fetched sizes of user directories.
+ std::vector<int64_t> user_sizes_;
+
+ // Helper to compute the total size of all types of site date.
+ std::unique_ptr<SiteDataSizeCollector> site_data_size_collector_;
+
+ // Flags indicating fetch operations for storage sizes are ongoing.
+ bool updating_downloads_size_;
+ bool updating_drive_cache_size_;
+ bool updating_browsing_data_size_;
+ bool updating_arc_size_;
+ bool updating_other_users_size_;
+
+ base::WeakPtrFactory<StorageManagerHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(StorageManagerHandler);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_STORAGE_MANAGER_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/user_image_source.cc b/chromium/chrome/browser/ui/webui/options/chromeos/user_image_source.cc
new file mode 100644
index 00000000000..baad28fd5a1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/user_image_source.cc
@@ -0,0 +1,114 @@
+// 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/ui/webui/options/chromeos/user_image_source.h"
+
+#include "base/memory/ref_counted_memory.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_split.h"
+#include "chrome/browser/chromeos/login/users/default_user_image/default_user_images.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/theme_resources.h"
+#include "components/signin/core/account_id/account_id.h"
+#include "components/user_manager/known_user.h"
+#include "components/user_manager/user_manager.h"
+#include "net/base/escape.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/chromeos/resources/grit/ui_chromeos_resources.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "url/third_party/mozilla/url_parse.h"
+
+namespace {
+
+// Parses the user image URL, which looks like
+// "chrome://userimage/serialized-user-id?key1=value1&...&key_n=value_n",
+// to user email.
+void ParseRequest(const GURL& url, std::string* email) {
+ DCHECK(url.is_valid());
+ const std::string serialized_account_id = net::UnescapeURLComponent(
+ url.path().substr(1),
+ net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS |
+ net::UnescapeRule::PATH_SEPARATORS | net::UnescapeRule::SPACES);
+ AccountId account_id(EmptyAccountId());
+ const bool status =
+ AccountId::Deserialize(serialized_account_id, &account_id);
+ // TODO(alemate): DCHECK(status) - should happen after options page is
+ // migrated.
+ if (!status) {
+ LOG(WARNING) << "Failed to deserialize account_id.";
+ account_id = user_manager::known_user::GetAccountId(
+ serialized_account_id, std::string() /* id */, AccountType::UNKNOWN);
+ }
+ *email = account_id.GetUserEmail();
+}
+
+scoped_refptr<base::RefCountedMemory> GetUserImageInternal(
+ const AccountId& account_id) {
+ const user_manager::User* user =
+ user_manager::UserManager::Get()->FindUser(account_id);
+
+ // Always use the 100% scaling. These source images are 256x256, and are
+ // downscaled to ~64x64 for use in WebUI pages. Therefore, they are big enough
+ // for device scale factors up to 4. We do not use SCALE_FACTOR_NONE, as we
+ // specifically want 100% scale images to not transmit more data than needed.
+ if (user) {
+ if (user->has_image_bytes())
+ return user->image_bytes();
+ if (user->image_is_stub()) {
+ return ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
+ IDR_PROFILE_PICTURE_LOADING, ui::SCALE_FACTOR_100P);
+ }
+ if (user->HasDefaultImage()) {
+ return ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
+ chromeos::default_user_image::kDefaultImageResourceIDs
+ [user->image_index()],
+ ui::SCALE_FACTOR_100P);
+ }
+ NOTREACHED() << "User with custom image missing data bytes";
+ } else {
+ LOG(ERROR) << "User not found: " << account_id.GetUserEmail();
+ }
+ return ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
+ IDR_LOGIN_DEFAULT_USER, ui::SCALE_FACTOR_100P);
+}
+
+} // namespace
+
+namespace chromeos {
+namespace options {
+
+// Static.
+scoped_refptr<base::RefCountedMemory> UserImageSource::GetUserImage(
+ const AccountId& account_id) {
+ return GetUserImageInternal(account_id);
+}
+
+UserImageSource::UserImageSource() {
+}
+
+UserImageSource::~UserImageSource() {}
+
+std::string UserImageSource::GetSource() const {
+ return chrome::kChromeUIUserImageHost;
+}
+
+void UserImageSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ std::string email;
+ GURL url(chrome::kChromeUIUserImageURL + path);
+ ParseRequest(url, &email);
+ const AccountId account_id(AccountId::FromUserEmail(email));
+ callback.Run(GetUserImageInternal(account_id));
+}
+
+std::string UserImageSource::GetMimeType(const std::string& path) const {
+ // We need to explicitly return a mime type, otherwise if the user tries to
+ // drag the image they get no extension.
+ return "image/png";
+}
+
+} // namespace options
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/options/chromeos/user_image_source.h b/chromium/chrome/browser/ui/webui/options/chromeos/user_image_source.h
new file mode 100644
index 00000000000..6724abe2a0f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/chromeos/user_image_source.h
@@ -0,0 +1,55 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_USER_IMAGE_SOURCE_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_USER_IMAGE_SOURCE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "content/public/browser/url_data_source.h"
+#include "ui/base/layout.h"
+
+class AccountId;
+
+namespace base {
+class RefCountedMemory;
+}
+
+namespace chromeos {
+namespace options {
+
+// UserImageSource is the data source that serves user images for users that
+// have it.
+class UserImageSource : public content::URLDataSource {
+ public:
+ UserImageSource();
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ std::string GetMimeType(const std::string& path) const override;
+
+ // Returns PNG encoded image for user with specified |account_id|. If there's
+ // no user with such an id, returns the first default image. Always returns
+ // the 100%-scale asset.
+ static scoped_refptr<base::RefCountedMemory> GetUserImage(
+ const AccountId& account_id);
+
+ private:
+ ~UserImageSource() override;
+
+ DISALLOW_COPY_AND_ASSIGN(UserImageSource);
+};
+
+} // namespace options
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_USER_IMAGE_SOURCE_H_
diff --git a/chromium/chrome/browser/ui/webui/options/clear_browser_data_browsertest.cc b/chromium/chrome/browser/ui/webui/options/clear_browser_data_browsertest.cc
new file mode 100644
index 00000000000..78063231dd4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/clear_browser_data_browsertest.cc
@@ -0,0 +1,115 @@
+// 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 <stddef.h>
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/webui/options/options_ui_browsertest.h"
+#include "chrome/common/url_constants.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browsing_data_remover.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/browsing_data_remover_test_util.h"
+
+namespace options {
+
+class ClearBrowserDataBrowserTest : public OptionsUIBrowserTest {
+ protected:
+ void ClickElement(const std::string& selector) {
+ bool element_enabled = false;
+ ASSERT_NO_FATAL_FAILURE(GetElementEnabledState(selector, &element_enabled));
+ ASSERT_TRUE(element_enabled);
+ ASSERT_TRUE(content::ExecuteScript(
+ GetSettingsFrame(),
+ "document.querySelector('" + selector + "').click();"));
+ }
+
+ bool IsElementEnabled(const std::string& selector) {
+ bool element_enabled = false;
+ GetElementEnabledState(selector, &element_enabled);
+ return element_enabled;
+ }
+
+ private:
+ void GetElementEnabledState(
+ const std::string& selector,
+ bool* enabled) {
+ ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+ GetSettingsFrame(),
+ "window.domAutomationController.send(!document.querySelector('" +
+ selector + "').disabled);",
+ enabled));
+ }
+};
+
+// http://crbug.com/458684
+#if defined(OS_WIN) || defined(OS_MACOSX)
+#define MAYBE_CommitButtonDisabledWhileDeletionInProgress \
+ DISABLED_CommitButtonDisabledWhileDeletionInProgress
+#else
+#define MAYBE_CommitButtonDisabledWhileDeletionInProgress \
+ CommitButtonDisabledWhileDeletionInProgress
+#endif
+
+IN_PROC_BROWSER_TEST_F(ClearBrowserDataBrowserTest,
+ MAYBE_CommitButtonDisabledWhileDeletionInProgress) {
+ const char kCommitButtonId[] = "#clear-browser-data-commit";
+ content::BrowsingDataRemoverCompletionInhibitor completion_inhibitor(
+ content::BrowserContext::GetBrowsingDataRemover(browser()->profile()));
+
+ // Navigate to the Clear Browsing Data dialog to ensure that the commit button
+ // is initially enabled, usable, and gets disabled after having been pressed.
+ NavigateToSettingsSubpage(chrome::kClearBrowserDataSubPage);
+ ASSERT_NO_FATAL_FAILURE(ClickElement(kCommitButtonId));
+ EXPECT_FALSE(IsElementEnabled(kCommitButtonId));
+
+ completion_inhibitor.BlockUntilNearCompletion();
+
+ // Simulate a reload while the previous removal is still running, and verify
+ // that the button is still disabled.
+ NavigateToSettingsSubpage(chrome::kClearBrowserDataSubPage);
+ EXPECT_FALSE(IsElementEnabled(kCommitButtonId));
+
+ completion_inhibitor.ContinueToCompletion();
+
+ // However, the button should be enabled again once the process has finished.
+ NavigateToSettingsSubpage(chrome::kClearBrowserDataSubPage);
+ EXPECT_TRUE(IsElementEnabled(kCommitButtonId));
+}
+
+IN_PROC_BROWSER_TEST_F(ClearBrowserDataBrowserTest,
+ CommitButtonDisabledWhenNoDataTypesSelected) {
+ const char kCommitButtonId[] = "#clear-browser-data-commit";
+ const char* kDataTypes[] = {"browser.clear_data.browsing_history",
+ "browser.clear_data.download_history",
+ "browser.clear_data.cache",
+ "browser.clear_data.cookies",
+ "browser.clear_data.passwords",
+ "browser.clear_data.form_data",
+ "browser.clear_data.hosted_apps_data",
+ "browser.clear_data.media_licenses"};
+
+ PrefService* prefs = browser()->profile()->GetPrefs();
+ for (size_t i = 0; i < arraysize(kDataTypes); ++i) {
+ prefs->SetBoolean(kDataTypes[i], false);
+ }
+
+ // Navigate to the Clear Browsing Data dialog to ensure that the commit button
+ // is disabled if clearing is not requested for any of the data types.
+ NavigateToSettingsSubpage(chrome::kClearBrowserDataSubPage);
+ EXPECT_FALSE(IsElementEnabled(kCommitButtonId));
+
+ // However, expect the commit button to be re-enabled if any of the data types
+ // gets selected to be cleared.
+ for (size_t i = 0; i < arraysize(kDataTypes); ++i) {
+ prefs->SetBoolean(kDataTypes[i], true);
+ EXPECT_TRUE(IsElementEnabled(kCommitButtonId));
+ prefs->SetBoolean(kDataTypes[i], false);
+ }
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/clear_browser_data_handler.cc b/chromium/chrome/browser/ui/webui/options/clear_browser_data_handler.cc
new file mode 100644
index 00000000000..0c3e07d5b2e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/clear_browser_data_handler.cc
@@ -0,0 +1,441 @@
+// 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/ui/webui/options/clear_browser_data_handler.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browsing_data/browsing_data_counter_factory.h"
+#include "chrome/browser/browsing_data/browsing_data_counter_utils.h"
+#include "chrome/browser/browsing_data/browsing_data_helper.h"
+#include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
+#include "chrome/browser/history/web_history_service_factory.h"
+#include "chrome/browser/prefs/incognito_mode_prefs.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/ui/accelerator_utils.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/locale_settings.h"
+#include "components/browsing_data/core/counters/browsing_data_counter.h"
+#include "components/browsing_data/core/history_notice_utils.h"
+#include "components/browsing_data/core/pref_names.h"
+#include "components/google/core/browser/google_util.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browsing_data_remover.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+
+namespace {
+
+const char kClearBrowsingDataLearnMoreUrl[] =
+ "https://support.google.com/chrome/?p=settings_clear_browsing_data";
+
+const char kMyActivityUrlInFooter[] =
+ "https://history.google.com/history/?utm_source=chrome_cbd";
+
+const char kMyActivityUrlInDialog[] =
+ "https://history.google.com/history/?utm_source=chrome_n";
+
+const int kMaxTimesHistoryNoticeShown = 1;
+
+const char* kCounterPrefs[] = {
+ browsing_data::prefs::kDeleteBrowsingHistory,
+ browsing_data::prefs::kDeleteCache,
+ browsing_data::prefs::kDeleteFormData,
+ browsing_data::prefs::kDeleteMediaLicenses,
+ browsing_data::prefs::kDeletePasswords,
+};
+
+} // namespace
+
+namespace options {
+
+ClearBrowserDataHandler::ClearBrowserDataHandler()
+ : remover_(nullptr),
+ sync_service_(nullptr),
+ should_show_history_notice_(false),
+ should_show_history_deletion_dialog_(false),
+ weak_ptr_factory_(this) {
+}
+
+ClearBrowserDataHandler::~ClearBrowserDataHandler() {
+ if (remover_)
+ remover_->RemoveObserver(this);
+ if (sync_service_)
+ sync_service_->RemoveObserver(this);
+}
+
+void ClearBrowserDataHandler::InitializeHandler() {
+ PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+ clear_plugin_lso_data_enabled_.Init(prefs::kClearPluginLSODataEnabled, prefs);
+ allow_deleting_browser_history_.Init(
+ prefs::kAllowDeletingBrowserHistory,
+ prefs,
+ base::Bind(&ClearBrowserDataHandler::OnBrowsingHistoryPrefChanged,
+ base::Unretained(this)));
+
+ if (AreCountersEnabled()) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ for (const std::string& pref : kCounterPrefs) {
+ AddCounter(
+ BrowsingDataCounterFactory::GetForProfileAndPref(profile, pref));
+ }
+
+ sync_service_ = ProfileSyncServiceFactory::GetForProfile(profile);
+ if (sync_service_)
+ sync_service_->AddObserver(this);
+ }
+}
+
+void ClearBrowserDataHandler::InitializePage() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ClearBrowserDataOverlay.createFooter", base::Value(AreCountersEnabled()),
+ base::Value(sync_service_ && sync_service_->IsSyncActive()),
+ base::Value(should_show_history_notice_));
+ RefreshHistoryNotice();
+ UpdateInfoBannerVisibility();
+ OnBrowsingHistoryPrefChanged();
+ bool removal_in_progress = !!remover_;
+ web_ui()->CallJavascriptFunctionUnsafe("ClearBrowserDataOverlay.setClearing",
+ base::Value(removal_in_progress));
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ClearBrowserDataOverlay.markInitializationComplete");
+}
+
+void ClearBrowserDataHandler::UpdateInfoBannerVisibility() {
+ base::string16 text;
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ auto availability = IncognitoModePrefs::GetAvailability(profile->GetPrefs());
+ if (availability == IncognitoModePrefs::ENABLED) {
+ base::Time last_clear_time = base::Time::FromInternalValue(
+ profile->GetPrefs()->GetInt64(
+ browsing_data::prefs::kLastClearBrowsingDataTime));
+
+ const base::TimeDelta since_clear = base::Time::Now() - last_clear_time;
+ if (since_clear < base::TimeDelta::FromHours(base::Time::kHoursPerDay)) {
+ ui::Accelerator acc = chrome::GetPrimaryChromeAcceleratorForCommandId(
+ IDC_NEW_INCOGNITO_WINDOW);
+ DCHECK_NE(ui::VKEY_UNKNOWN, acc.key_code());
+ text = l10n_util::GetStringFUTF16(IDS_CLEAR_BROWSING_DATA_INFO_BAR_TEXT,
+ acc.GetShortcutText());
+ }
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ClearBrowserDataOverlay.setBannerText", base::Value(text));
+}
+
+void ClearBrowserDataHandler::OnPageOpened(const base::ListValue* value) {
+ for (const auto& counter : counters_) {
+ DCHECK(AreCountersEnabled());
+ counter->Restart();
+ }
+}
+
+void ClearBrowserDataHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ static OptionsStringResource resources[] = {
+ {"clearBrowserDataLabel", IDS_CLEAR_BROWSING_DATA_LABEL},
+ {"clearBrowserDataSyncWarning", IDS_CLEAR_BROWSING_DATA_SYNCED_DELETION},
+ {"clearBrowserDataSupportString",
+ AreCountersEnabled() ? IDS_CLEAR_BROWSING_DATA_SOME_STUFF_REMAINS_SIMPLE
+ : IDS_CLEAR_BROWSING_DATA_SOME_STUFF_REMAINS},
+ {"clearBrowserDataHistoryNoticeTitle",
+ IDS_CLEAR_BROWSING_DATA_HISTORY_NOTICE_TITLE},
+ {"clearBrowserDataHistoryNoticeOk",
+ IDS_CLEAR_BROWSING_DATA_HISTORY_NOTICE_OK},
+ {"deleteBrowsingHistoryCheckbox", IDS_DEL_BROWSING_HISTORY_CHKBOX},
+ {"deleteDownloadHistoryCheckbox", IDS_DEL_DOWNLOAD_HISTORY_CHKBOX},
+ {"deleteCacheCheckbox", IDS_DEL_CACHE_CHKBOX},
+ {"deleteCookiesCheckbox", IDS_DEL_COOKIES_CHKBOX},
+ {"deleteCookiesFlashCheckbox", IDS_DEL_COOKIES_FLASH_CHKBOX},
+ {"deletePasswordsCheckbox", IDS_DEL_PASSWORDS_CHKBOX},
+ {"deleteFormDataCheckbox", IDS_DEL_FORM_DATA_CHKBOX},
+ {"deleteHostedAppsDataCheckbox", IDS_DEL_HOSTED_APPS_DATA_CHKBOX},
+ {"deleteMediaLicensesCheckbox", IDS_DEL_MEDIA_LICENSES_CHKBOX},
+ {"clearBrowserDataCommit", IDS_CLEAR_BROWSING_DATA_COMMIT},
+ {"flashStorageUrl", IDS_FLASH_STORAGE_URL},
+ };
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+ RegisterTitle(localized_strings, "clearBrowserDataOverlay",
+ IDS_CLEAR_BROWSING_DATA_TITLE);
+ localized_strings->SetString("clearBrowsingDataLearnMoreUrl",
+ kClearBrowsingDataLearnMoreUrl);
+ localized_strings->SetString(
+ "clearBrowserDataHistoryFooter",
+ l10n_util::GetStringFUTF16(
+ IDS_CLEAR_BROWSING_DATA_HISTORY_FOOTER,
+ base::ASCIIToUTF16(kMyActivityUrlInFooter)));
+ localized_strings->SetString(
+ "clearBrowserDataHistoryNotice",
+ l10n_util::GetStringFUTF16(
+ IDS_CLEAR_BROWSING_DATA_HISTORY_NOTICE,
+ base::ASCIIToUTF16(kMyActivityUrlInDialog)));
+
+ auto time_list = base::MakeUnique<base::ListValue>();
+ for (int i = 0; i < 5; i++) {
+ base::string16 label_string;
+ switch (i) {
+ case 0:
+ label_string = l10n_util::GetStringUTF16(IDS_CLEAR_DATA_HOUR);
+ break;
+ case 1:
+ label_string = l10n_util::GetStringUTF16(IDS_CLEAR_DATA_DAY);
+ break;
+ case 2:
+ label_string = l10n_util::GetStringUTF16(IDS_CLEAR_DATA_WEEK);
+ break;
+ case 3:
+ label_string = l10n_util::GetStringUTF16(IDS_CLEAR_DATA_4WEEKS);
+ break;
+ case 4:
+ label_string = l10n_util::GetStringUTF16(IDS_CLEAR_DATA_EVERYTHING);
+ break;
+ }
+ std::unique_ptr<base::ListValue> option(new base::ListValue());
+ option->AppendInteger(i);
+ option->AppendString(label_string);
+ time_list->Append(std::move(option));
+ }
+ localized_strings->Set("clearBrowserDataTimeList", std::move(time_list));
+ localized_strings->SetBoolean("showDeleteBrowsingHistoryCheckboxes",
+ !Profile::FromWebUI(web_ui())->IsSupervised());
+}
+
+void ClearBrowserDataHandler::RegisterMessages() {
+ // Setup handlers specific to this panel.
+ web_ui()->RegisterMessageCallback("performClearBrowserData",
+ base::Bind(&ClearBrowserDataHandler::HandleClearBrowserData,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("openedClearBrowserData",
+ base::Bind(&ClearBrowserDataHandler::OnPageOpened,
+ base::Unretained(this)));
+}
+
+void ClearBrowserDataHandler::HandleClearBrowserData(
+ const base::ListValue* value) {
+ // We should never be called when the previous clearing has not yet finished.
+ CHECK(!remover_);
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ PrefService* prefs = profile->GetPrefs();
+
+ int site_data_mask = ChromeBrowsingDataRemoverDelegate::DATA_TYPE_SITE_DATA;
+ // Don't try to clear LSO data if it's not supported.
+ if (!*clear_plugin_lso_data_enabled_)
+ site_data_mask &= ~ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PLUGIN_DATA;
+
+ int remove_mask = 0;
+ int origin_mask = 0;
+ if (prefs->GetBoolean(browsing_data::prefs::kDeleteBrowsingHistory) &&
+ *allow_deleting_browser_history_) {
+ remove_mask |= ChromeBrowsingDataRemoverDelegate::DATA_TYPE_HISTORY;
+ }
+ if (prefs->GetBoolean(browsing_data::prefs::kDeleteDownloadHistory) &&
+ *allow_deleting_browser_history_) {
+ remove_mask |= content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS;
+ }
+ if (prefs->GetBoolean(browsing_data::prefs::kDeleteCache))
+ remove_mask |= content::BrowsingDataRemover::DATA_TYPE_CACHE;
+ if (prefs->GetBoolean(browsing_data::prefs::kDeleteCookies)) {
+ remove_mask |= site_data_mask;
+ origin_mask |= content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB;
+ }
+ if (prefs->GetBoolean(browsing_data::prefs::kDeletePasswords))
+ remove_mask |= ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PASSWORDS;
+ if (prefs->GetBoolean(browsing_data::prefs::kDeleteFormData))
+ remove_mask |= ChromeBrowsingDataRemoverDelegate::DATA_TYPE_FORM_DATA;
+ if (prefs->GetBoolean(browsing_data::prefs::kDeleteMediaLicenses))
+ remove_mask |= content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES;
+ if (prefs->GetBoolean(browsing_data::prefs::kDeleteHostedAppsData)) {
+ remove_mask |= site_data_mask;
+ origin_mask |= content::BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB;
+ }
+
+ // Record the deletion of cookies and cache.
+ content::BrowsingDataRemover::CookieOrCacheDeletionChoice choice =
+ content::BrowsingDataRemover::NEITHER_COOKIES_NOR_CACHE;
+ if (prefs->GetBoolean(browsing_data::prefs::kDeleteCookies)) {
+ choice = prefs->GetBoolean(browsing_data::prefs::kDeleteCache)
+ ? content::BrowsingDataRemover::BOTH_COOKIES_AND_CACHE
+ : content::BrowsingDataRemover::ONLY_COOKIES;
+ } else if (prefs->GetBoolean(browsing_data::prefs::kDeleteCache)) {
+ choice = content::BrowsingDataRemover::ONLY_CACHE;
+ }
+ UMA_HISTOGRAM_ENUMERATION(
+ "History.ClearBrowsingData.UserDeletedCookieOrCacheFromDialog", choice,
+ content::BrowsingDataRemover::MAX_CHOICE_VALUE);
+
+ // Record the circumstances under which passwords are deleted.
+ if (prefs->GetBoolean(browsing_data::prefs::kDeletePasswords)) {
+ static const char* other_types[] = {
+ browsing_data::prefs::kDeleteBrowsingHistory,
+ browsing_data::prefs::kDeleteDownloadHistory,
+ browsing_data::prefs::kDeleteCache,
+ browsing_data::prefs::kDeleteCookies,
+ browsing_data::prefs::kDeleteFormData,
+ browsing_data::prefs::kDeleteHostedAppsData,
+ browsing_data::prefs::kDeleteMediaLicenses,
+ };
+ static size_t num_other_types = arraysize(other_types);
+ int checked_other_types = std::count_if(
+ other_types,
+ other_types + num_other_types,
+ [prefs](const std::string& pref) { return prefs->GetBoolean(pref); });
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "History.ClearBrowsingData.PasswordsDeletion.AdditionalDatatypesCount",
+ checked_other_types);
+ }
+
+ remover_ = content::BrowserContext::GetBrowsingDataRemover(profile);
+ remover_->AddObserver(this);
+ int period_selected =
+ prefs->GetInteger(browsing_data::prefs::kDeleteTimePeriod);
+ browsing_data::TimePeriod time_period =
+ static_cast<browsing_data::TimePeriod>(period_selected);
+ browsing_data::RecordDeletionForPeriod(time_period);
+ remover_->RemoveAndReply(
+ browsing_data::CalculateBeginDeleteTime(time_period),
+ browsing_data::CalculateEndDeleteTime(time_period),
+ remove_mask, origin_mask, this);
+
+ // Store the clear browsing data time. Next time the clear browsing data
+ // dialog is open, this time is used to decide whether to display an info
+ // banner or not.
+ prefs->SetInt64(browsing_data::prefs::kLastClearBrowsingDataTime,
+ base::Time::Now().ToInternalValue());
+}
+
+void ClearBrowserDataHandler::OnBrowsingDataRemoverDone() {
+ remover_->RemoveObserver(this);
+ remover_ = nullptr;
+
+ PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+ int notice_shown_times = prefs->GetInteger(
+ browsing_data::prefs::kClearBrowsingDataHistoryNoticeShownTimes);
+
+ // When the deletion is complete, we might show an additional dialog with
+ // a notice about other forms of browsing history. This is the case if
+ const bool show_notice =
+ // 1. The dialog is relevant for the user.
+ should_show_history_deletion_dialog_ &&
+ // 2. The selected data types contained browsing history.
+ prefs->GetBoolean(browsing_data::prefs::kDeleteBrowsingHistory) &&
+ // 3. The notice has been shown less than |kMaxTimesHistoryNoticeShown|.
+ notice_shown_times < kMaxTimesHistoryNoticeShown;
+
+ if (show_notice) {
+ // Increment the preference.
+ prefs->SetInteger(
+ browsing_data::prefs::kClearBrowsingDataHistoryNoticeShownTimes,
+ notice_shown_times + 1);
+ }
+
+ UMA_HISTOGRAM_BOOLEAN(
+ "History.ClearBrowsingData.ShownHistoryNoticeAfterClearing", show_notice);
+
+ web_ui()->CallJavascriptFunctionUnsafe("ClearBrowserDataOverlay.doneClearing",
+ base::Value(show_notice));
+}
+
+void ClearBrowserDataHandler::OnBrowsingHistoryPrefChanged() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ClearBrowserDataOverlay.setAllowDeletingHistory",
+ base::Value(*allow_deleting_browser_history_));
+}
+
+void ClearBrowserDataHandler::AddCounter(
+ std::unique_ptr<browsing_data::BrowsingDataCounter> counter) {
+ DCHECK(AreCountersEnabled());
+
+ counter->Init(Profile::FromWebUI(web_ui())->GetPrefs(),
+ browsing_data::ClearBrowsingDataTab::ADVANCED,
+ base::Bind(&ClearBrowserDataHandler::UpdateCounterText,
+ base::Unretained(this)));
+ counters_.push_back(std::move(counter));
+}
+
+void ClearBrowserDataHandler::UpdateCounterText(
+ std::unique_ptr<browsing_data::BrowsingDataCounter::Result> result) {
+ DCHECK(AreCountersEnabled());
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ClearBrowserDataOverlay.updateCounter",
+ base::Value(result->source()->GetPrefName()),
+ base::Value(GetChromeCounterTextFromResult(result.get())));
+}
+
+void ClearBrowserDataHandler::OnStateChanged(syncer::SyncService* sync) {
+ UpdateSyncState();
+}
+
+void ClearBrowserDataHandler::UpdateSyncState() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ClearBrowserDataOverlay.updateSyncWarningAndHistoryFooter",
+ base::Value(sync_service_ && sync_service_->IsSyncActive()),
+ base::Value(should_show_history_notice_));
+}
+
+void ClearBrowserDataHandler::RefreshHistoryNotice() {
+ browsing_data::ShouldShowNoticeAboutOtherFormsOfBrowsingHistory(
+ sync_service_,
+ WebHistoryServiceFactory::GetForProfile(Profile::FromWebUI(web_ui())),
+ base::Bind(&ClearBrowserDataHandler::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.
+ int notice_shown_times = Profile::FromWebUI(web_ui())->GetPrefs()->GetInteger(
+ browsing_data::prefs::kClearBrowsingDataHistoryNoticeShownTimes);
+
+ if (notice_shown_times < kMaxTimesHistoryNoticeShown) {
+ browsing_data::ShouldPopupDialogAboutOtherFormsOfBrowsingHistory(
+ sync_service_,
+ WebHistoryServiceFactory::GetForProfile(Profile::FromWebUI(web_ui())),
+ chrome::GetChannel(),
+ base::Bind(&ClearBrowserDataHandler::UpdateHistoryDeletionDialog,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+}
+
+void ClearBrowserDataHandler::UpdateHistoryNotice(bool show) {
+ should_show_history_notice_ = show;
+ UpdateSyncState();
+
+ UMA_HISTOGRAM_BOOLEAN(
+ "History.ClearBrowsingData.HistoryNoticeShownInFooterWhenUpdated",
+ should_show_history_notice_);
+}
+
+void ClearBrowserDataHandler::UpdateHistoryDeletionDialog(bool show) {
+ // This is used by OnBrowsingDataRemoverDone (when the deletion finishes).
+ should_show_history_deletion_dialog_ = show;
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/clear_browser_data_handler.h b/chromium/chrome/browser/ui/webui/options/clear_browser_data_handler.h
new file mode 100644
index 00000000000..8c6a259f66c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/clear_browser_data_handler.h
@@ -0,0 +1,108 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CLEAR_BROWSER_DATA_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CLEAR_BROWSER_DATA_HANDLER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/browsing_data/core/counters/browsing_data_counter.h"
+#include "components/prefs/pref_member.h"
+#include "content/public/browser/browsing_data_remover.h"
+
+namespace options {
+
+// Clear browser data handler page UI handler.
+class ClearBrowserDataHandler : public OptionsPageUIHandler,
+ public content::BrowsingDataRemover::Observer,
+ public syncer::SyncServiceObserver {
+ public:
+ ClearBrowserDataHandler();
+ ~ClearBrowserDataHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializeHandler() override;
+ void InitializePage() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ void UpdateInfoBannerVisibility();
+
+ private:
+ // Javascript callback for when the CBD dialog is opened. The caller does
+ // not provide any parameters, so |value| is unused.
+ void OnPageOpened(const base::ListValue* value);
+
+ // Javascript callback to start clearing data.
+ void HandleClearBrowserData(const base::ListValue* value);
+
+ // BrowsingDataRemover::Observer implementation.
+ // Closes the dialog once all requested data has been removed.
+ void OnBrowsingDataRemoverDone() override;
+
+ // Updates UI when the pref to allow clearing history changes.
+ virtual void OnBrowsingHistoryPrefChanged();
+
+ // Adds a |counter| for browsing data.
+ void AddCounter(std::unique_ptr<browsing_data::BrowsingDataCounter> counter);
+
+ // Updates a counter in the UI according to the |result|.
+ void UpdateCounterText(
+ std::unique_ptr<browsing_data::BrowsingDataCounter::Result> result);
+
+ // Implementation of SyncServiceObserver.
+ void OnStateChanged(syncer::SyncService* sync) override;
+
+ // Updates the support string at the bottom of the dialog.
+ void UpdateSyncState();
+
+ // Finds out whether we should show a notice informing the user about other
+ // forms of browsing history. Responds with an asynchronous callback to
+ // |UpdateHistoryNotice|.
+ void RefreshHistoryNotice();
+
+ // Shows or hides the notice about other forms of browsing history.
+ void UpdateHistoryNotice(bool show);
+
+ // Remembers whether we should popup a dialog about other forms of browsing
+ // history when the user deletes the history for the first time.
+ void UpdateHistoryDeletionDialog(bool show);
+
+ // If non-null it means removal is in progress.
+ content::BrowsingDataRemover* remover_;
+
+ // Keeps track of whether clearing LSO data is supported.
+ BooleanPrefMember clear_plugin_lso_data_enabled_;
+
+ // Keeps track of whether deleting browsing history and downloads is allowed.
+ BooleanPrefMember allow_deleting_browser_history_;
+
+ // Counters that calculate the data volume for some of the data types.
+ std::vector<std::unique_ptr<browsing_data::BrowsingDataCounter>> counters_;
+
+ // Informs us whether the user is syncing their data.
+ browser_sync::ProfileSyncService* sync_service_;
+
+ // Whether we should show a notice about other forms of browsing history.
+ bool should_show_history_notice_;
+
+ // Whether we should popup a dialog about other forms of browsing history
+ // when the user deletes their browsing history for the first time.
+ bool should_show_history_deletion_dialog_;
+
+ // A weak pointer factory for asynchronous calls referencing this class.
+ base::WeakPtrFactory<ClearBrowserDataHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClearBrowserDataHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CLEAR_BROWSER_DATA_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/content_options_browsertest.js b/chromium/chrome/browser/ui/webui/options/content_options_browsertest.js
new file mode 100644
index 00000000000..863fb28f880
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/content_options_browsertest.js
@@ -0,0 +1,27 @@
+// 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.
+
+GEN_INCLUDE(['options_browsertest_base.js']);
+
+/**
+ * TestFixture for content options WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function ContentOptionsWebUITest() {}
+
+ContentOptionsWebUITest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /**
+ * Browse to content options.
+ * @override
+ */
+ browsePreload: 'chrome://settings-frame/content',
+};
+
+// Test opening the content options has correct location.
+TEST_F('ContentOptionsWebUITest', 'testOpenContentOptions', function() {
+ assertEquals(this.browsePreload, document.location.href);
+});
diff --git a/chromium/chrome/browser/ui/webui/options/content_settings_exception_area_browsertest.cc b/chromium/chrome/browser/ui/webui/options/content_settings_exception_area_browsertest.cc
new file mode 100644
index 00000000000..e149f7e45ef
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/content_settings_exception_area_browsertest.cc
@@ -0,0 +1,24 @@
+// 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.
+
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/webui/options/options_ui_browsertest.h"
+
+typedef options::OptionsUIBrowserTest ContentSettingsExceptionsAreaBrowserTest;
+
+// Test that an incognito window can be opened while the exceptions page is
+// open. If this test fails it could indicate that a new content setting has
+// been added but is not being dealt with correctly by the content settings
+// handling WebUI code.
+#if defined(OS_MACOSX) && defined(ADDRESS_SANITIZER)
+// Flaky on ASAN on Mac. See https://crbug.com/674497.
+#define MAYBE_OpenIncognitoWindow DISABLED_OpenIncognitoWindow
+#else
+#define MAYBE_OpenIncognitoWindow OpenIncognitoWindow
+#endif
+IN_PROC_BROWSER_TEST_F(ContentSettingsExceptionsAreaBrowserTest,
+ MAYBE_OpenIncognitoWindow) {
+ NavigateToSettingsSubpage("contentExceptions");
+ chrome::NewIncognitoWindow(browser());
+}
diff --git a/chromium/chrome/browser/ui/webui/options/content_settings_exception_area_browsertest.js b/chromium/chrome/browser/ui/webui/options/content_settings_exception_area_browsertest.js
new file mode 100644
index 00000000000..7cfc1edc633
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/content_settings_exception_area_browsertest.js
@@ -0,0 +1,109 @@
+// 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.
+
+GEN_INCLUDE(['options_browsertest_base.js']);
+
+/**
+ * TestFixture for content settings exception area WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function ContentSettingsExceptionAreaWebUITest() {}
+
+ContentSettingsExceptionAreaWebUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /** @override */
+ browsePreload: 'chrome://settings-frame/contentExceptions',
+};
+
+// See crbug.com/579666 for OS_LINUX and crbug.com/588586 for Windows and
+// crbug.com/718947 for Mac.
+GEN('#if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_WIN) || ' +
+ 'defined(OS_MACOSX)');
+GEN('#define MAYBE_testOpenContentSettingsExceptionArea ' +
+ 'DISABLED_testOpenContentSettingsExceptionArea');
+GEN('#else');
+GEN('#define MAYBE_testOpenContentSettingsExceptionArea ' +
+ 'testOpenContentSettingsExceptionArea');
+GEN('#endif // defined(OS_CHROMEOS) || defined(OS_LINUX)');
+// Test opening the content settings exception area has correct location.
+TEST_F('ContentSettingsExceptionAreaWebUITest',
+ 'MAYBE_testOpenContentSettingsExceptionArea', function() {
+ assertEquals(this.browsePreload, document.location.href);
+});
+
+/**
+ * A class to asynchronously test the content settings exception area dialog.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function ContentSettingsExceptionsAreaAsyncWebUITest() {}
+
+ContentSettingsExceptionsAreaAsyncWebUITest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /** @override */
+ browsePreload: 'chrome://settings-frame/contentExceptions',
+
+ /** @override */
+ isAsync: true,
+
+ /** @override */
+ setUp: function() {
+ OptionsBrowsertestBase.prototype.setUp.call(this);
+
+ // Enable when failure is resolved.
+ // AX_TEXT_01: http://crbug.com/570562
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'controlsWithoutLabel',
+ '#content-settings-exceptions-area > .content-area > *');
+
+ // Enable when failure is resolved.
+ // AX_TEXT_04: http://crbug.com/570563
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'linkWithUnclearPurpose',
+ '#content-settings-exceptions-area > .action-area > *');
+ },
+};
+
+// Adds and removes a location content setting exception.
+TEST_F('ContentSettingsExceptionsAreaAsyncWebUITest',
+ 'testAddRemoveLocationExceptions', function() {
+ assertEquals(this.browsePreload, document.location.href);
+
+ /** @const */ var origin = 'http://google.com:80';
+ /** @const */ var setExceptions = ContentSettings.setExceptions;
+
+ var list = ContentSettings.getExceptionsList('cookies', 'normal');
+ assertEquals(1, list.items.length);
+
+ var setExceptionsCounter = 0;
+ var setExceptionsCallback = function() {
+ setExceptionsCounter++;
+ if (setExceptionsCounter == 1) {
+ // The first item is now the exception (edit items are always last).
+ expectEquals('block', list.dataModel.item(0).setting);
+ expectEquals(origin, list.dataModel.item(0).origin);
+
+ // Delete the item and verify it worked.
+ list.deleteItemAtIndex(0);
+ } else if (setExceptionsCounter == 2) {
+ // Verify the item was deleted, restore the original method, and finish.
+ expectEquals(1, list.items.length);
+ ContentSettings.setExceptions = setExceptions;
+ testDone();
+ }
+ };
+
+ // NOTE: if this test doesn't succeed, |ContentSettings.setExceptions| may not
+ // be restored to its original method. I know no easy way to fix this.
+ ContentSettings.setExceptions = function() {
+ setExceptions.apply(ContentSettings, arguments);
+ setExceptionsCallback();
+ };
+
+ // Add an item to the location exception area to start the test.
+ list.items[0].finishEdit(origin, 'block');
+});
diff --git a/chromium/chrome/browser/ui/webui/options/content_settings_handler.cc b/chromium/chrome/browser/ui/webui/options/content_settings_handler.cc
new file mode 100644
index 00000000000..b72dfe7301f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/content_settings_handler.cc
@@ -0,0 +1,1494 @@
+// 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/ui/webui/options/content_settings_handler.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/i18n/number_formatting.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/user_metrics.h"
+#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/content_settings/web_site_settings_uma_util.h"
+#include "chrome/browser/custom_handlers/protocol_handler_registry.h"
+#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
+#include "chrome/browser/extensions/extension_special_storage_policy.h"
+#include "chrome/browser/notifications/desktop_notification_profile_util.h"
+#include "chrome/browser/permissions/chooser_context_base.h"
+#include "chrome/browser/permissions/permission_uma_util.h"
+#include "chrome/browser/permissions/permission_util.h"
+#include "chrome/browser/plugins/plugin_utils.h"
+#include "chrome/browser/plugins/plugins_field_trial.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
+#include "chrome/browser/ui/webui/site_settings_helper.h"
+#include "chrome/browser/usb/usb_chooser_context.h"
+#include "chrome/browser/usb/usb_chooser_context_factory.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/chrome_switches.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/grit/generated_resources.h"
+#include "chrome/grit/locale_settings.h"
+#include "components/content_settings/core/browser/content_settings_details.h"
+#include "components/content_settings/core/browser/content_settings_utils.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/browser/website_settings_info.h"
+#include "components/content_settings/core/browser/website_settings_registry.h"
+#include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/content_settings_pattern.h"
+#include "components/google/core/browser/google_util.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/common/profile_management_switches.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/page_zoom.h"
+#include "content/public/common/url_constants.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension_set.h"
+#include "extensions/common/permissions/api_permission.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "ppapi/features/features.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if defined(OS_CHROMEOS)
+#include "components/user_manager/user_manager.h"
+#endif
+
+using base::UserMetricsAction;
+using content_settings::ContentSettingToString;
+using content_settings::ContentSettingFromString;
+using extensions::APIPermission;
+
+namespace options {
+
+namespace {
+
+struct ContentSettingWithExceptions {
+ ContentSettingWithExceptions(bool otr, UserMetricsAction action)
+ : has_otr_exceptions(otr), uma(action) {}
+ bool has_otr_exceptions;
+ UserMetricsAction uma;
+};
+
+// The AppFilter is used in AddExceptionsGrantedByHostedApps() to choose
+// extensions which should have their extent displayed.
+typedef bool (*AppFilter)(const extensions::Extension& app,
+ content::BrowserContext* profile);
+
+const char kAppName[] = "appName";
+const char kAppId[] = "appId";
+const char kZoom[] = "zoom";
+
+// A pseudo content type. We use it to display data like a content setting even
+// though it is not a real content setting.
+const char kZoomContentType[] = "zoomlevels";
+
+// Maps from a content settings type to a content setting with exceptions
+// struct.
+typedef std::map<ContentSettingsType, ContentSettingWithExceptions>
+ ExceptionsInfoMap;
+
+const ExceptionsInfoMap& GetExceptionsInfoMap() {
+ CR_DEFINE_STATIC_LOCAL(ExceptionsInfoMap, exceptions_info_map, ());
+ if (exceptions_info_map.empty()) {
+ // With OTR exceptions.
+ exceptions_info_map.insert(std::make_pair(
+ CONTENT_SETTINGS_TYPE_COOKIES,
+ ContentSettingWithExceptions(
+ true, UserMetricsAction("Options_DefaultCookieSettingChanged"))));
+ exceptions_info_map.insert(std::make_pair(
+ CONTENT_SETTINGS_TYPE_IMAGES,
+ ContentSettingWithExceptions(
+ true, UserMetricsAction("Options_DefaultImagesSettingChanged"))));
+ exceptions_info_map.insert(std::make_pair(
+ CONTENT_SETTINGS_TYPE_JAVASCRIPT,
+ ContentSettingWithExceptions(
+ true,
+ UserMetricsAction("Options_DefaultJavaScriptSettingChanged"))));
+ exceptions_info_map.insert(std::make_pair(
+ CONTENT_SETTINGS_TYPE_PLUGINS,
+ ContentSettingWithExceptions(
+ true, UserMetricsAction("Options_DefaultPluginsSettingChanged"))));
+ exceptions_info_map.insert(std::make_pair(
+ CONTENT_SETTINGS_TYPE_POPUPS,
+ ContentSettingWithExceptions(
+ true, UserMetricsAction("Options_DefaultPopupsSettingChanged"))));
+ exceptions_info_map.insert(std::make_pair(
+ CONTENT_SETTINGS_TYPE_PPAPI_BROKER,
+ ContentSettingWithExceptions(
+ true,
+ UserMetricsAction("Options_DefaultPPAPIBrokerSettingChanged"))));
+#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
+ exceptions_info_map.insert(std::make_pair(
+ CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER,
+ ContentSettingWithExceptions(
+ true,
+ UserMetricsAction(
+ "Options_DefaultProtectedMediaIdentifierSettingChanged"))));
+#endif
+
+ // Without OTR exceptions.
+ exceptions_info_map.insert(std::make_pair(
+ CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+ ContentSettingWithExceptions(
+ false,
+ UserMetricsAction("Options_DefaultNotificationsSettingChanged"))));
+ exceptions_info_map.insert(std::make_pair(
+ CONTENT_SETTINGS_TYPE_GEOLOCATION,
+ ContentSettingWithExceptions(
+ false,
+ UserMetricsAction("Options_DefaultGeolocationSettingChanged"))));
+ exceptions_info_map.insert(std::make_pair(
+ CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
+ ContentSettingWithExceptions(
+ false,
+ UserMetricsAction("Options_DefaultMediaStreamMicSettingChanged"))));
+ exceptions_info_map.insert(std::make_pair(
+ CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
+ ContentSettingWithExceptions(
+ false, UserMetricsAction(
+ "Options_DefaultMediaStreamCameraSettingChanged"))));
+ exceptions_info_map.insert(std::make_pair(
+ CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS,
+ ContentSettingWithExceptions(
+ false, UserMetricsAction(
+ "Options_DefaultMultipleAutomaticDLSettingChange"))));
+ exceptions_info_map.insert(std::make_pair(
+ CONTENT_SETTINGS_TYPE_MIDI_SYSEX,
+ ContentSettingWithExceptions(
+ false,
+ UserMetricsAction("Options_DefaultMIDISysExSettingChanged"))));
+ exceptions_info_map.insert(std::make_pair(
+ CONTENT_SETTINGS_TYPE_BACKGROUND_SYNC,
+ ContentSettingWithExceptions(
+ false,
+ UserMetricsAction("Options_DefaultBackgroundSyncSettingChanged"))));
+ }
+
+ return exceptions_info_map;
+}
+
+content::BrowserContext* GetBrowserContext(content::WebUI* web_ui) {
+ return web_ui->GetWebContents()->GetBrowserContext();
+}
+
+// Create a DictionaryValue* that will act as a data source for a single row
+// in the Geolocation exceptions table.
+std::unique_ptr<base::DictionaryValue> GetGeolocationExceptionForPage(
+ const ContentSettingsPattern& origin,
+ const ContentSettingsPattern& embedding_origin,
+ ContentSetting setting) {
+ base::DictionaryValue* exception = new base::DictionaryValue();
+
+ std::string setting_string =
+ content_settings::ContentSettingToString(setting);
+ DCHECK(!setting_string.empty());
+
+ exception->SetString(site_settings::kSetting, setting_string);
+ exception->SetString(site_settings::kOrigin, origin.ToString());
+ exception->SetString(
+ site_settings::kEmbeddingOrigin, embedding_origin.ToString());
+ return base::WrapUnique(exception);
+}
+
+// Create a DictionaryValue* that will act as a data source for a single row
+// in the desktop notifications exceptions table.
+std::unique_ptr<base::DictionaryValue> GetNotificationExceptionForPage(
+ const ContentSettingsPattern& primary_pattern,
+ const ContentSettingsPattern& secondary_pattern,
+ ContentSetting setting,
+ const std::string& provider_name) {
+ std::string embedding_origin;
+ if (secondary_pattern != ContentSettingsPattern::Wildcard())
+ embedding_origin = secondary_pattern.ToString();
+
+ base::DictionaryValue* exception = new base::DictionaryValue();
+
+ std::string setting_string =
+ content_settings::ContentSettingToString(setting);
+ DCHECK(!setting_string.empty());
+
+ exception->SetString(site_settings::kSetting, setting_string);
+ exception->SetString(site_settings::kOrigin, primary_pattern.ToString());
+ exception->SetString(site_settings::kEmbeddingOrigin, embedding_origin);
+ exception->SetString(site_settings::kSource, provider_name);
+ return base::WrapUnique(exception);
+}
+
+// Returns true whenever the |extension| is hosted and has |permission|.
+// Must have the AppFilter signature.
+template <APIPermission::ID permission>
+bool HostedAppHasPermission(const extensions::Extension& extension,
+ content::BrowserContext* /* context */) {
+ return extension.is_hosted_app() &&
+ extension.permissions_data()->HasAPIPermission(permission);
+}
+
+// Add an "Allow"-entry to the list of |exceptions| for a |url_pattern| from
+// the web extent of a hosted |app|.
+void AddExceptionForHostedApp(const std::string& url_pattern,
+ const extensions::Extension& app, base::ListValue* exceptions) {
+ std::unique_ptr<base::DictionaryValue> exception(new base::DictionaryValue());
+
+ std::string setting_string =
+ content_settings::ContentSettingToString(CONTENT_SETTING_ALLOW);
+ DCHECK(!setting_string.empty());
+
+ exception->SetString(site_settings::kSetting, setting_string);
+ exception->SetString(site_settings::kOrigin, url_pattern);
+ exception->SetString(site_settings::kEmbeddingOrigin, url_pattern);
+ exception->SetString(site_settings::kSource, "HostedApp");
+ exception->SetString(kAppName, app.name());
+ exception->SetString(kAppId, app.id());
+ exceptions->Append(std::move(exception));
+}
+
+// Asks the |profile| for hosted apps which have the |permission| set, and
+// adds their web extent and launch URL to the |exceptions| list.
+void AddExceptionsGrantedByHostedApps(content::BrowserContext* context,
+ AppFilter app_filter,
+ base::ListValue* exceptions) {
+ const extensions::ExtensionSet& extensions =
+ extensions::ExtensionRegistry::Get(context)->enabled_extensions();
+ for (extensions::ExtensionSet::const_iterator extension = extensions.begin();
+ extension != extensions.end(); ++extension) {
+ if (!app_filter(*extension->get(), context))
+ continue;
+
+ 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) {
+ std::string url_pattern = pattern->GetAsString();
+ AddExceptionForHostedApp(url_pattern, *extension->get(), exceptions);
+ }
+ // Retrieve the launch URL.
+ GURL launch_url =
+ extensions::AppLaunchInfo::GetLaunchWebURL(extension->get());
+ // Skip adding the launch URL if it is part of the web extent.
+ if (web_extent.MatchesURL(launch_url))
+ continue;
+ AddExceptionForHostedApp(launch_url.spec(), *extension->get(), exceptions);
+ }
+}
+
+} // namespace
+
+ContentSettingsHandler::MediaSettingsInfo::MediaSettingsInfo() {
+}
+
+ContentSettingsHandler::MediaSettingsInfo::~MediaSettingsInfo() {
+}
+
+ContentSettingsHandler::MediaSettingsInfo::ForFlash::ForFlash()
+ : default_setting(CONTENT_SETTING_DEFAULT),
+ initialized(false),
+ last_refresh_request_id(0) {
+}
+
+ContentSettingsHandler::MediaSettingsInfo::ForFlash::~ForFlash() {
+}
+
+ContentSettingsHandler::MediaSettingsInfo::ForFlash&
+ ContentSettingsHandler::MediaSettingsInfo::forFlash() {
+ return flash_settings_;
+}
+
+ContentSettingsHandler::MediaSettingsInfo::ForOneType&
+ ContentSettingsHandler::MediaSettingsInfo::forType(
+ ContentSettingsType type) {
+ if (type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC)
+ return mic_settings_;
+ else if (type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA)
+ return camera_settings_;
+
+ NOTREACHED();
+ return mic_settings_;
+}
+
+ContentSettingsHandler::MediaSettingsInfo::ForOneType::ForOneType()
+ : show_flash_default_link(false),
+ show_flash_exceptions_link(false),
+ default_setting(CONTENT_SETTING_DEFAULT),
+ policy_disable(false),
+ default_setting_initialized(false),
+ exceptions_initialized(false) {
+}
+
+ContentSettingsHandler::MediaSettingsInfo::ForOneType::~ForOneType() {
+}
+
+ContentSettingsHandler::ContentSettingsHandler() : observer_(this) {
+}
+
+ContentSettingsHandler::~ContentSettingsHandler() {
+}
+
+void ContentSettingsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ static OptionsStringResource resources[] = {
+ {"allowException", IDS_EXCEPTIONS_ALLOW_BUTTON},
+ {"blockException", IDS_EXCEPTIONS_BLOCK_BUTTON},
+ {"sessionException", IDS_EXCEPTIONS_SESSION_ONLY_BUTTON},
+ {"askException", IDS_EXCEPTIONS_ASK_BUTTON},
+ {"otrExceptionsExplanation", IDS_EXCEPTIONS_OTR_LABEL},
+ {"addNewExceptionInstructions", IDS_EXCEPTIONS_ADD_NEW_INSTRUCTIONS},
+ {"manageExceptions", IDS_EXCEPTIONS_MANAGE},
+ {"manageHandlers", IDS_HANDLERS_MANAGE},
+ {"exceptionPatternHeader", IDS_EXCEPTIONS_PATTERN_HEADER},
+ {"exceptionBehaviorHeader", IDS_EXCEPTIONS_ACTION_HEADER},
+ {"exceptionUsbDeviceHeader", IDS_EXCEPTIONS_USB_DEVICE_HEADER},
+ {"exceptionZoomHeader", IDS_EXCEPTIONS_ZOOM_HEADER},
+ {"embeddedOnHost", IDS_EXCEPTIONS_GEOLOCATION_EMBEDDED_ON_HOST},
+ // Cookies filter.
+ {"cookiesTabLabel", IDS_COOKIES_TAB_LABEL},
+ {"cookiesHeader", IDS_COOKIES_HEADER},
+ {"cookiesAllow", IDS_COOKIES_ALLOW_RADIO},
+ {"cookiesBlock", IDS_COOKIES_BLOCK_RADIO},
+ {"cookiesSessionOnly", IDS_COOKIES_SESSION_ONLY_RADIO},
+ {"cookiesBlock3rdParty", IDS_COOKIES_BLOCK_3RDPARTY_CHKBOX},
+ {"cookiesShowCookies", IDS_COOKIES_SHOW_COOKIES_BUTTON},
+ {"flashStorageSettings", IDS_FLASH_STORAGE_SETTINGS},
+ {"flashStorageUrl", IDS_FLASH_STORAGE_URL},
+#if BUILDFLAG(ENABLE_GOOGLE_NOW)
+ {"googleGeolocationAccessEnable",
+ IDS_GEOLOCATION_GOOGLE_ACCESS_ENABLE_CHKBOX},
+#endif
+ // Image filter.
+ {"imagesTabLabel", IDS_IMAGES_TAB_LABEL},
+ {"imagesHeader", IDS_IMAGES_HEADER},
+ {"imagesAllow", IDS_IMAGES_LOAD_RADIO},
+ {"imagesBlock", IDS_IMAGES_NOLOAD_RADIO},
+ // JavaScript filter.
+ {"javascriptTabLabel", IDS_JAVASCRIPT_TAB_LABEL},
+ {"javascriptHeader", IDS_JAVASCRIPT_HEADER},
+ {"javascriptAllow", IDS_JS_ALLOW_RADIO},
+ {"javascriptBlock", IDS_JS_DONOTALLOW_RADIO},
+ // Plugins filter.
+ {"pluginsTabLabel", IDS_FLASH_TAB_LABEL},
+ {"pluginsHeader", IDS_FLASH_HEADER},
+ {"pluginsAllow", IDS_FLASH_ALLOW_RADIO},
+ {"pluginsBlock", IDS_FLASH_BLOCK_RADIO},
+ // Pop-ups filter.
+ {"popupsTabLabel", IDS_POPUP_TAB_LABEL},
+ {"popupsHeader", IDS_POPUP_HEADER},
+ {"popupsAllow", IDS_POPUP_ALLOW_RADIO},
+ {"popupsBlock", IDS_POPUP_BLOCK_RADIO},
+ // Location filter.
+ {"locationTabLabel", IDS_GEOLOCATION_TAB_LABEL},
+ {"locationHeader", IDS_GEOLOCATION_HEADER},
+ {"locationAllow", IDS_GEOLOCATION_ALLOW_RADIO},
+ {"locationAsk", IDS_GEOLOCATION_ASK_RADIO},
+ {"locationBlock", IDS_GEOLOCATION_BLOCK_RADIO},
+ {"setBy", IDS_GEOLOCATION_SET_BY_HOVER},
+ // Notifications filter.
+ {"notificationsTabLabel", IDS_NOTIFICATIONS_TAB_LABEL},
+ {"notificationsHeader", IDS_NOTIFICATIONS_HEADER},
+ {"notificationsAllow", IDS_NOTIFICATIONS_ALLOW_RADIO},
+ {"notificationsAsk", IDS_NOTIFICATIONS_ASK_RADIO},
+ {"notificationsBlock", IDS_NOTIFICATIONS_BLOCK_RADIO},
+ // Protected Content filter
+ {"protectedContentTabLabel", IDS_PROTECTED_CONTENT_TAB_LABEL},
+ {"protectedContentEnableCheckbox", IDS_PROTECTED_CONTENT_ENABLE_CHECKBOX},
+#if defined(OS_CHROMEOS) || defined(OS_WIN)
+ {"protectedContentInfo", IDS_PROTECTED_CONTENT_INFO},
+ {"protectedContentEnableIdentifiersCheckbox",
+ IDS_PROTECTED_CONTENT_ENABLE_IDENTIFIERS_CHECKBOX},
+ {"protectedContentHeader", IDS_PROTECTED_CONTENT_HEADER},
+#endif // defined(OS_CHROMEOS) || defined(OS_WIN)
+ // Microphone filter.
+ {"mediaStreamMicTabLabel", IDS_MEDIA_STREAM_MIC_TAB_LABEL},
+ {"mediaStreamMicHeader", IDS_MEDIA_STREAM_MIC_HEADER},
+ {"mediaStreamMicAsk", IDS_MEDIA_STREAM_ASK_AUDIO_ONLY_RADIO},
+ {"mediaStreamMicBlock", IDS_MEDIA_STREAM_BLOCK_AUDIO_ONLY_RADIO},
+ // Camera filter.
+ {"mediaStreamCameraTabLabel", IDS_MEDIA_STREAM_CAMERA_TAB_LABEL},
+ {"mediaStreamCameraHeader", IDS_MEDIA_STREAM_CAMERA_HEADER},
+ {"mediaStreamCameraAsk", IDS_MEDIA_STREAM_ASK_VIDEO_ONLY_RADIO},
+ {"mediaStreamCameraBlock", IDS_MEDIA_STREAM_BLOCK_VIDEO_ONLY_RADIO},
+ // Flash media settings.
+ {"mediaPepperFlashMicDefaultDivergedLabel",
+ IDS_MEDIA_PEPPER_FLASH_MIC_DEFAULT_DIVERGED_LABEL},
+ {"mediaPepperFlashCameraDefaultDivergedLabel",
+ IDS_MEDIA_PEPPER_FLASH_CAMERA_DEFAULT_DIVERGED_LABEL},
+ {"mediaPepperFlashMicExceptionsDivergedLabel",
+ IDS_MEDIA_PEPPER_FLASH_MIC_EXCEPTIONS_DIVERGED_LABEL},
+ {"mediaPepperFlashCameraExceptionsDivergedLabel",
+ IDS_MEDIA_PEPPER_FLASH_CAMERA_EXCEPTIONS_DIVERGED_LABEL},
+ {"mediaPepperFlashChangeLink", IDS_MEDIA_PEPPER_FLASH_CHANGE_LINK},
+ {"mediaPepperFlashGlobalPrivacyURL", IDS_FLASH_GLOBAL_PRIVACY_URL},
+ {"mediaPepperFlashWebsitePrivacyURL", IDS_FLASH_WEBSITE_PRIVACY_URL},
+ // PPAPI broker filter.
+ {"ppapiBrokerHeader", IDS_PPAPI_BROKER_HEADER},
+ {"ppapiBrokerTabLabel", IDS_PPAPI_BROKER_TAB_LABEL},
+ {"ppapiBrokerAllow", IDS_PPAPI_BROKER_ALLOW_RADIO},
+ {"ppapiBrokerAsk", IDS_PPAPI_BROKER_ASK_RADIO},
+ {"ppapiBrokerBlock", IDS_PPAPI_BROKER_BLOCK_RADIO},
+ // Multiple automatic downloads.
+ {"multipleAutomaticDownloadsTabLabel", IDS_AUTOMATIC_DOWNLOADS_TAB_LABEL},
+ {"multipleAutomaticDownloadsHeader", IDS_AUTOMATIC_DOWNLOADS_TAB_LABEL},
+ {"multipleAutomaticDownloadsAllow", IDS_AUTOMATIC_DOWNLOADS_ALLOW_RADIO},
+ {"multipleAutomaticDownloadsAsk", IDS_AUTOMATIC_DOWNLOADS_ASK_RADIO},
+ {"multipleAutomaticDownloadsBlock", IDS_AUTOMATIC_DOWNLOADS_BLOCK_RADIO},
+ // MIDI system exclusive messages.
+ {"midiSysexHeader", IDS_MIDI_SYSEX_TAB_LABEL},
+ {"midiSysExAllow", IDS_MIDI_SYSEX_ALLOW_RADIO},
+ {"midiSysExAsk", IDS_MIDI_SYSEX_ASK_RADIO},
+ {"midiSysExBlock", IDS_MIDI_SYSEX_BLOCK_RADIO},
+ // Push messaging strings.
+ {"pushMessagingHeader", IDS_PUSH_MESSAGES_TAB_LABEL},
+ {"pushMessagingAllow", IDS_PUSH_MESSSAGING_ALLOW_RADIO},
+ {"pushMessagingAsk", IDS_PUSH_MESSSAGING_ASK_RADIO},
+ {"pushMessagingBlock", IDS_PUSH_MESSSAGING_BLOCK_RADIO},
+ // USB devices.
+ {"usbDevicesHeader", IDS_USB_DEVICES_HEADER_AND_TAB_LABEL},
+ {"usbDevicesManage", IDS_USB_DEVICES_MANAGE_BUTTON},
+ // Background sync.
+ {"backgroundSyncHeader", IDS_BACKGROUND_SYNC_HEADER},
+ {"backgroundSyncAllow", IDS_BACKGROUND_SYNC_ALLOW_RADIO},
+ {"backgroundSyncBlock", IDS_BACKGROUND_SYNC_BLOCK_RADIO},
+ // Zoom levels.
+ {"zoomlevelsHeader", IDS_ZOOMLEVELS_HEADER_AND_TAB_LABEL},
+ {"zoomLevelsManage", IDS_ZOOMLEVELS_MANAGE_BUTTON},
+ // PDF Plugin filter.
+ {"pdfTabLabel", IDS_PDF_TAB_LABEL},
+ {"pdfEnable", IDS_PDF_ENABLE_CHECKBOX},
+ };
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+
+ // TODO(tommycli): When the HTML5 By Default feature flag is on, we want to
+ // display strings that begin with "Ask...", even though the setting remains
+ // DETECT. Once this feature is finalized, then we migrate the setting to ASK.
+ Profile* profile = Profile::FromWebUI(web_ui());
+ bool is_hbd = PluginUtils::ShouldPreferHtmlOverPlugins(
+ HostContentSettingsMapFactory::GetForProfile(profile));
+ static OptionsStringResource flash_strings[] = {
+ {"pluginsDetectImportantContent",
+ is_hbd ? IDS_FLASH_ASK_RECOMMENDED_RADIO
+ : IDS_FLASH_DETECT_RECOMMENDED_RADIO},
+ {"detectException",
+ is_hbd ? IDS_EXCEPTIONS_ASK_BUTTON
+ : IDS_EXCEPTIONS_DETECT_IMPORTANT_CONTENT_BUTTON},
+ };
+ RegisterStrings(localized_strings, flash_strings, arraysize(flash_strings));
+
+ PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+ const base::Value* default_pref = prefs->GetDefaultPrefValue(
+ content_settings::WebsiteSettingsRegistry::GetInstance()
+ ->Get(CONTENT_SETTINGS_TYPE_PLUGINS)
+ ->default_value_pref_name());
+
+ int default_value = CONTENT_SETTING_DEFAULT;
+ bool success = default_pref->GetAsInteger(&default_value);
+ DCHECK(success);
+ DCHECK_NE(CONTENT_SETTING_DEFAULT, default_value);
+
+ RegisterTitle(localized_strings, "contentSettingsPage",
+ IDS_CONTENT_SETTINGS_TITLE);
+
+ // Register titles for each of the individual settings whose exception
+ // dialogs will be processed by |ContentSettingsHandler|.
+ RegisterTitle(localized_strings, "cookies",
+ IDS_COOKIES_TAB_LABEL);
+ RegisterTitle(localized_strings, "images",
+ IDS_IMAGES_TAB_LABEL);
+ RegisterTitle(localized_strings, "javascript",
+ IDS_JAVASCRIPT_TAB_LABEL);
+ RegisterTitle(localized_strings, "plugins", IDS_FLASH_TAB_LABEL);
+ RegisterTitle(localized_strings, "popups",
+ IDS_POPUP_TAB_LABEL);
+ RegisterTitle(localized_strings, "location",
+ IDS_GEOLOCATION_TAB_LABEL);
+ RegisterTitle(localized_strings, "notifications",
+ IDS_NOTIFICATIONS_TAB_LABEL);
+#if defined(OS_CHROMEOS)
+ RegisterTitle(localized_strings, "protectedContent",
+ IDS_PROTECTED_CONTENT_TAB_LABEL);
+#endif
+ RegisterTitle(localized_strings, "media-stream-mic",
+ IDS_MEDIA_STREAM_MIC_TAB_LABEL);
+ RegisterTitle(localized_strings, "media-stream-camera",
+ IDS_MEDIA_STREAM_CAMERA_TAB_LABEL);
+ RegisterTitle(localized_strings, "ppapi-broker",
+ IDS_PPAPI_BROKER_TAB_LABEL);
+ RegisterTitle(localized_strings, "multiple-automatic-downloads",
+ IDS_AUTOMATIC_DOWNLOADS_TAB_LABEL);
+ RegisterTitle(localized_strings, "midi-sysex",
+ IDS_MIDI_SYSEX_TAB_LABEL);
+ RegisterTitle(localized_strings, "usb-devices",
+ IDS_USB_DEVICES_HEADER_AND_TAB_LABEL);
+ RegisterTitle(localized_strings, "background-sync",
+ IDS_BACKGROUND_SYNC_HEADER);
+ RegisterTitle(localized_strings, "zoomlevels",
+ IDS_ZOOMLEVELS_HEADER_AND_TAB_LABEL);
+
+ localized_strings->SetString("exceptionsLearnMoreUrl",
+ chrome::kContentSettingsExceptionsLearnMoreURL);
+}
+
+void ContentSettingsHandler::InitializeHandler() {
+ notification_registrar_.Add(
+ this, chrome::NOTIFICATION_PROFILE_CREATED,
+ content::NotificationService::AllSources());
+ notification_registrar_.Add(
+ this, chrome::NOTIFICATION_PROFILE_DESTROYED,
+ content::NotificationService::AllSources());
+
+ content::BrowserContext* context = GetBrowserContext(web_ui());
+ notification_registrar_.Add(
+ this, chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED,
+ content::Source<content::BrowserContext>(context));
+
+ PrefService* prefs = user_prefs::UserPrefs::Get(context);
+ pref_change_registrar_.Init(prefs);
+ pref_change_registrar_.Add(
+ prefs::kPepperFlashSettingsEnabled,
+ base::Bind(&ContentSettingsHandler::OnPepperFlashPrefChanged,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(
+ prefs::kAudioCaptureAllowed,
+ base::Bind(&ContentSettingsHandler::UpdateSettingDefaultFromModel,
+ base::Unretained(this),
+ CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC));
+ pref_change_registrar_.Add(
+ prefs::kAudioCaptureAllowedUrls,
+ base::Bind(&ContentSettingsHandler::UpdateExceptionsViewFromModel,
+ base::Unretained(this),
+ CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC));
+ pref_change_registrar_.Add(
+ prefs::kVideoCaptureAllowed,
+ base::Bind(&ContentSettingsHandler::UpdateSettingDefaultFromModel,
+ base::Unretained(this),
+ CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA));
+ pref_change_registrar_.Add(
+ prefs::kVideoCaptureAllowedUrls,
+ base::Bind(&ContentSettingsHandler::UpdateExceptionsViewFromModel,
+ base::Unretained(this),
+ CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA));
+ pref_change_registrar_.Add(
+ prefs::kEnableDRM,
+ base::Bind(
+ &ContentSettingsHandler::UpdateProtectedContentExceptionsButton,
+ base::Unretained(this)));
+
+ // Here we only subscribe to the HostZoomMap for the default storage partition
+ // since we don't allow the user to manage the zoom levels for apps.
+ // We're only interested in zoom-levels that are persisted, since the user
+ // is given the opportunity to view/delete these in the content-settings page.
+ host_zoom_map_subscription_ =
+ content::HostZoomMap::GetDefaultForBrowserContext(context)
+ ->AddZoomLevelChangedCallback(
+ base::Bind(&ContentSettingsHandler::OnZoomLevelChanged,
+ base::Unretained(this)));
+
+ flash_settings_manager_.reset(new PepperFlashSettingsManager(this, context));
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ observer_.Add(HostContentSettingsMapFactory::GetForProfile(profile));
+ if (profile->HasOffTheRecordProfile()) {
+ auto* map = HostContentSettingsMapFactory::GetForProfile(
+ profile->GetOffTheRecordProfile());
+ if (!observer_.IsObserving(map))
+ observer_.Add(map);
+ }
+}
+
+void ContentSettingsHandler::InitializePage() {
+ media_settings_.reset(new MediaSettingsInfo());
+ RefreshFlashMediaSettings();
+
+ UpdateHandlersEnabledRadios();
+ UpdateAllExceptionsViewsFromModel();
+ UpdateAllChooserExceptionsViewsFromModel();
+ UpdateProtectedContentExceptionsButton();
+}
+
+void ContentSettingsHandler::OnContentSettingChanged(
+ const ContentSettingsPattern& primary_pattern,
+ const ContentSettingsPattern& secondary_pattern,
+ ContentSettingsType content_type,
+ std::string resource_identifier) {
+ const ContentSettingsDetails details(
+ primary_pattern, secondary_pattern, content_type, resource_identifier);
+ // TODO(estade): we pretend update_all() is always true.
+ if (details.update_all_types()) {
+ UpdateAllExceptionsViewsFromModel();
+ UpdateAllChooserExceptionsViewsFromModel();
+ } else {
+ if (base::ContainsKey(GetExceptionsInfoMap(), details.type()))
+ UpdateExceptionsViewFromModel(details.type());
+ }
+}
+
+void ContentSettingsHandler::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ switch (type) {
+ case chrome::NOTIFICATION_PROFILE_DESTROYED: {
+ Profile* profile = content::Source<Profile>(source).ptr();
+ HostContentSettingsMap* settings_map =
+ HostContentSettingsMapFactory::GetForProfile(profile);
+ if (profile->IsOffTheRecord() &&
+ observer_.IsObserving(settings_map)) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ContentSettingsExceptionsArea.OTRProfileDestroyed");
+ observer_.Remove(settings_map);
+ }
+ break;
+ }
+
+ case chrome::NOTIFICATION_PROFILE_CREATED: {
+ Profile* profile = content::Source<Profile>(source).ptr();
+ if (profile->IsOffTheRecord()) {
+ UpdateAllOTRExceptionsViewsFromModel();
+ UpdateAllOTRChooserExceptionsViewsFromModel();
+ observer_.Add(HostContentSettingsMapFactory::GetForProfile(profile));
+ }
+ break;
+ }
+
+ case chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED: {
+ UpdateHandlersEnabledRadios();
+ break;
+ }
+ }
+}
+
+void ContentSettingsHandler::OnGetPermissionSettingsCompleted(
+ uint32_t request_id,
+ bool success,
+ PP_Flash_BrowserOperations_Permission default_permission,
+ const ppapi::FlashSiteSettings& sites) {
+ MediaSettingsInfo::ForFlash& settings = media_settings_->forFlash();
+ if (success && request_id == settings.last_refresh_request_id) {
+ settings.initialized = true;
+ settings.default_setting =
+ PepperFlashContentSettingsUtils::FlashPermissionToContentSetting(
+ default_permission);
+ PepperFlashContentSettingsUtils::FlashSiteSettingsToMediaExceptions(
+ sites, &settings.exceptions);
+ PepperFlashContentSettingsUtils::SortMediaExceptions(
+ &settings.exceptions);
+
+ UpdateFlashMediaLinksVisibility(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC);
+ UpdateFlashMediaLinksVisibility(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
+ }
+}
+
+void ContentSettingsHandler::UpdateSettingDefaultFromModel(
+ ContentSettingsType type) {
+ std::string provider_id;
+ HostContentSettingsMap* host_content_settings_map =
+ HostContentSettingsMapFactory::GetForProfile(GetProfile());
+ ContentSetting default_setting =
+ host_content_settings_map->GetDefaultContentSetting(type, &provider_id);
+
+#if BUILDFLAG(ENABLE_PLUGINS)
+ default_setting = PluginsFieldTrial::EffectiveContentSetting(
+ host_content_settings_map, type, default_setting);
+#endif
+
+ // Camera and microphone default content settings cannot be set by the policy.
+ // However, the policy can disable them. Treat this case visually in the same
+ // way as if the policy set the default setting to BLOCK. Furthermore, compare
+ // the settings with Flash settings and show links to the Flash settings site
+ // if they differ.
+ if (type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC ||
+ type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA) {
+ UpdateMediaSettingsFromPrefs(type);
+ if (media_settings_->forType(type).policy_disable) {
+ default_setting = CONTENT_SETTING_BLOCK;
+ provider_id = site_settings::kPolicyProviderId;
+ }
+ }
+
+ base::DictionaryValue filter_settings;
+
+ std::string setting_string =
+ content_settings::ContentSettingToString(default_setting);
+ DCHECK(!setting_string.empty());
+
+ filter_settings.SetString(
+ site_settings::ContentSettingsTypeToGroupName(type) + ".value",
+ setting_string);
+ filter_settings.SetString(
+ site_settings::ContentSettingsTypeToGroupName(type) + ".managedBy",
+ provider_id);
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ContentSettings.setContentFilterSettingsValue", filter_settings);
+}
+
+void ContentSettingsHandler::UpdateMediaSettingsFromPrefs(
+ ContentSettingsType type) {
+ PrefService* prefs = user_prefs::UserPrefs::Get(GetBrowserContext(web_ui()));
+ HostContentSettingsMap* settings_map =
+ HostContentSettingsMapFactory::GetForProfile(GetProfile());
+ MediaSettingsInfo::ForOneType& settings = media_settings_->forType(type);
+ std::string policy_pref = (type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC)
+ ? prefs::kAudioCaptureAllowed
+ : prefs::kVideoCaptureAllowed;
+
+ settings.policy_disable = !prefs->GetBoolean(policy_pref) &&
+ prefs->IsManagedPreference(policy_pref);
+ settings.default_setting =
+ settings_map->GetDefaultContentSetting(type, nullptr);
+ settings.default_setting_initialized = true;
+
+ UpdateFlashMediaLinksVisibility(type);
+ UpdateMediaDeviceDropdownVisibility(type);
+}
+
+void ContentSettingsHandler::UpdateHandlersEnabledRadios() {
+ base::Value handlers_enabled(GetProtocolHandlerRegistry()->enabled());
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ContentSettings.updateHandlersEnabledRadios", handlers_enabled);
+}
+
+void ContentSettingsHandler::UpdateAllExceptionsViewsFromModel() {
+ const ExceptionsInfoMap& exceptions_info_map = GetExceptionsInfoMap();
+ for (const auto& exceptions_info_pair : exceptions_info_map)
+ UpdateExceptionsViewFromModel(exceptions_info_pair.first);
+
+ // Zoom levels are not actually a content type so we need to handle them
+ // separately.
+ UpdateZoomLevelsExceptionsView();
+}
+
+void ContentSettingsHandler::UpdateAllOTRExceptionsViewsFromModel() {
+ const ExceptionsInfoMap& exceptions_info_map = GetExceptionsInfoMap();
+ for (const auto& exceptions_info_pair : exceptions_info_map) {
+ if (exceptions_info_pair.second.has_otr_exceptions) {
+ UpdateExceptionsViewFromOTRHostContentSettingsMap(
+ exceptions_info_pair.first);
+ }
+ }
+}
+
+void ContentSettingsHandler::UpdateExceptionsViewFromModel(
+ ContentSettingsType type) {
+ if (type == CONTENT_SETTINGS_TYPE_GEOLOCATION) {
+ UpdateGeolocationExceptionsView();
+ } else if (type == CONTENT_SETTINGS_TYPE_NOTIFICATIONS) {
+ UpdateNotificationExceptionsView();
+ } else if (type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC ||
+ type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA) {
+ CompareMediaExceptionsWithFlash(type);
+ UpdateExceptionsViewFromHostContentSettingsMap(type);
+ } else if (type == CONTENT_SETTINGS_TYPE_MIDI_SYSEX) {
+ UpdateMIDISysExExceptionsView();
+ } else {
+ UpdateExceptionsViewFromHostContentSettingsMap(type);
+ }
+}
+
+// TODO(estade): merge with GetExceptionsFromHostContentSettingsMap.
+void ContentSettingsHandler::UpdateGeolocationExceptionsView() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ HostContentSettingsMap* map =
+ HostContentSettingsMapFactory::GetForProfile(profile);
+
+ ContentSettingsForOneType all_settings;
+ map->GetSettingsForOneType(
+ CONTENT_SETTINGS_TYPE_GEOLOCATION,
+ std::string(),
+ &all_settings);
+
+ // Group geolocation settings by primary_pattern.
+ site_settings::AllPatternsSettings all_patterns_settings;
+ for (ContentSettingsForOneType::iterator i = all_settings.begin();
+ i != all_settings.end(); ++i) {
+ // Don't add default settings.
+ if (i->primary_pattern == ContentSettingsPattern::Wildcard() &&
+ i->secondary_pattern == ContentSettingsPattern::Wildcard() &&
+ i
+ ->source != site_settings::kPreferencesSource) {
+ continue;
+ }
+ all_patterns_settings[std::make_pair(i->primary_pattern, i->source)]
+ [i->secondary_pattern] = i->setting;
+ }
+
+ base::ListValue exceptions;
+ AddExceptionsGrantedByHostedApps(
+ profile,
+ HostedAppHasPermission<APIPermission::kGeolocation>,
+ &exceptions);
+
+ for (site_settings::AllPatternsSettings::iterator i =
+ all_patterns_settings.begin(); i != all_patterns_settings.end(); ++i) {
+ const ContentSettingsPattern& primary_pattern = i->first.first;
+ const site_settings::OnePatternSettings& one_settings = i->second;
+
+ site_settings::OnePatternSettings::const_iterator parent =
+ one_settings.find(primary_pattern);
+
+ // Add the "parent" entry for the non-embedded setting.
+ ContentSetting parent_setting =
+ parent == one_settings.end() ? CONTENT_SETTING_DEFAULT : parent->second;
+ exceptions.Append(GetGeolocationExceptionForPage(primary_pattern,
+ primary_pattern,
+ parent_setting));
+
+ // Add the "children" for any embedded settings.
+ for (site_settings::OnePatternSettings::const_iterator j =
+ one_settings.begin();
+ j != one_settings.end();
+ ++j) {
+ // Skip the non-embedded setting which we already added above.
+ if (j == parent)
+ continue;
+
+ exceptions.Append(GetGeolocationExceptionForPage(
+ primary_pattern, j->first, j->second));
+ }
+ }
+
+ base::Value type_string(site_settings::ContentSettingsTypeToGroupName(
+ CONTENT_SETTINGS_TYPE_GEOLOCATION));
+ web_ui()->CallJavascriptFunctionUnsafe("ContentSettings.setExceptions",
+ type_string, exceptions);
+
+ // This is mainly here to keep this function ideologically parallel to
+ // UpdateExceptionsViewFromHostContentSettingsMap().
+ UpdateSettingDefaultFromModel(CONTENT_SETTINGS_TYPE_GEOLOCATION);
+}
+
+void ContentSettingsHandler::UpdateNotificationExceptionsView() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ ContentSettingsForOneType settings;
+ DesktopNotificationProfileUtil::GetNotificationsSettings(profile, &settings);
+
+ base::ListValue exceptions;
+ AddExceptionsGrantedByHostedApps(
+ profile,
+ HostedAppHasPermission<APIPermission::kNotifications>,
+ &exceptions);
+
+ for (ContentSettingsForOneType::const_iterator i =
+ settings.begin();
+ i != settings.end();
+ ++i) {
+ // Don't add default settings.
+ if (i->primary_pattern == ContentSettingsPattern::Wildcard() &&
+ i->secondary_pattern == ContentSettingsPattern::Wildcard() &&
+ i
+ ->source != site_settings::kPreferencesSource) {
+ continue;
+ }
+
+ exceptions.Append(
+ GetNotificationExceptionForPage(i->primary_pattern,
+ i->secondary_pattern,
+ i->setting,
+ i->source));
+ }
+
+ base::Value type_string(site_settings::ContentSettingsTypeToGroupName(
+ CONTENT_SETTINGS_TYPE_NOTIFICATIONS));
+ web_ui()->CallJavascriptFunctionUnsafe("ContentSettings.setExceptions",
+ type_string, exceptions);
+
+ // This is mainly here to keep this function ideologically parallel to
+ // UpdateExceptionsViewFromHostContentSettingsMap().
+ UpdateSettingDefaultFromModel(CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
+}
+
+void ContentSettingsHandler::CompareMediaExceptionsWithFlash(
+ ContentSettingsType type) {
+ MediaSettingsInfo::ForOneType& settings = media_settings_->forType(type);
+ HostContentSettingsMap* settings_map =
+ HostContentSettingsMapFactory::GetForProfile(GetProfile());
+ const auto* extension_registry =
+ extensions::ExtensionRegistry::Get(GetProfile());
+ base::ListValue exceptions;
+ site_settings::GetExceptionsFromHostContentSettingsMap(
+ settings_map, type, extension_registry, web_ui(), /*incognito=*/false,
+ /*filter=*/nullptr, &exceptions);
+
+ settings.exceptions.clear();
+ for (base::ListValue::const_iterator entry = exceptions.begin();
+ entry != exceptions.end(); ++entry) {
+ const base::DictionaryValue* dict = nullptr;
+ bool valid_dict = entry->GetAsDictionary(&dict);
+ DCHECK(valid_dict);
+
+ std::string origin;
+ std::string setting;
+ dict->GetString(site_settings::kOrigin, &origin);
+ dict->GetString(site_settings::kSetting, &setting);
+
+ ContentSetting setting_type;
+ bool result =
+ content_settings::ContentSettingFromString(setting, &setting_type);
+ DCHECK(result);
+
+ settings.exceptions.push_back(MediaException(
+ ContentSettingsPattern::FromString(origin),
+ setting_type));
+ }
+
+ PepperFlashContentSettingsUtils::SortMediaExceptions(
+ &settings.exceptions);
+
+ settings.exceptions_initialized = true;
+ UpdateFlashMediaLinksVisibility(type);
+}
+
+void ContentSettingsHandler::UpdateMIDISysExExceptionsView() {
+ UpdateSettingDefaultFromModel(CONTENT_SETTINGS_TYPE_MIDI_SYSEX);
+ UpdateExceptionsViewFromHostContentSettingsMap(
+ CONTENT_SETTINGS_TYPE_MIDI_SYSEX);
+}
+
+void ContentSettingsHandler::UpdateAllChooserExceptionsViewsFromModel() {
+ for (const site_settings::ChooserTypeNameEntry& chooser_type :
+ site_settings::kChooserTypeGroupNames)
+ UpdateChooserExceptionsViewFromModel(chooser_type);
+}
+
+void ContentSettingsHandler::UpdateAllOTRChooserExceptionsViewsFromModel() {
+ for (const site_settings::ChooserTypeNameEntry& chooser_type :
+ site_settings::kChooserTypeGroupNames)
+ UpdateOTRChooserExceptionsViewFromModel(chooser_type);
+}
+
+void ContentSettingsHandler::UpdateChooserExceptionsViewFromModel(
+ const site_settings::ChooserTypeNameEntry& chooser_type) {
+ base::ListValue exceptions;
+ site_settings::GetChooserExceptionsFromProfile(
+ Profile::FromWebUI(web_ui()), false, chooser_type, &exceptions);
+ base::Value type_string(chooser_type.name);
+ web_ui()->CallJavascriptFunctionUnsafe("ContentSettings.setExceptions",
+ type_string, exceptions);
+
+ UpdateOTRChooserExceptionsViewFromModel(chooser_type);
+}
+
+void ContentSettingsHandler::UpdateOTRChooserExceptionsViewFromModel(
+ const site_settings::ChooserTypeNameEntry& chooser_type) {
+ if (!Profile::FromWebUI(web_ui())->HasOffTheRecordProfile())
+ return;
+
+ base::ListValue exceptions;
+ site_settings::GetChooserExceptionsFromProfile(
+ Profile::FromWebUI(web_ui()), true, chooser_type, &exceptions);
+ base::Value type_string(chooser_type.name);
+ web_ui()->CallJavascriptFunctionUnsafe("ContentSettings.setOTRExceptions",
+ type_string, exceptions);
+}
+
+void ContentSettingsHandler::UpdateZoomLevelsExceptionsView() {
+ base::ListValue zoom_levels_exceptions;
+
+ content::HostZoomMap* host_zoom_map =
+ content::HostZoomMap::GetDefaultForBrowserContext(
+ GetBrowserContext(web_ui()));
+ content::HostZoomMap::ZoomLevelVector zoom_levels(
+ host_zoom_map->GetAllZoomLevels());
+
+ // Sort ZoomLevelChanges by host and scheme
+ // (a.com < http://a.com < https://a.com < b.com).
+ std::sort(zoom_levels.begin(), zoom_levels.end(),
+ [](const content::HostZoomMap::ZoomLevelChange& a,
+ const content::HostZoomMap::ZoomLevelChange& b) {
+ return a.host == b.host ? a.scheme < b.scheme : a.host < b.host;
+ });
+
+ for (content::HostZoomMap::ZoomLevelVector::const_iterator i =
+ zoom_levels.begin();
+ i != zoom_levels.end();
+ ++i) {
+ std::unique_ptr<base::DictionaryValue> exception(new base::DictionaryValue);
+ switch (i->mode) {
+ case content::HostZoomMap::ZOOM_CHANGED_FOR_HOST: {
+ exception->SetString(site_settings::kOrigin, i->host);
+ std::string host = i->host;
+ if (host == content::kUnreachableWebDataURL) {
+ host =
+ l10n_util::GetStringUTF8(IDS_ZOOMLEVELS_CHROME_ERROR_PAGES_LABEL);
+ }
+ exception->SetString(site_settings::kOrigin, host);
+ break;
+ }
+ case content::HostZoomMap::ZOOM_CHANGED_FOR_SCHEME_AND_HOST:
+ // These are not stored in preferences and get cleared on next browser
+ // start. Therefore, we don't care for them.
+ continue;
+ case content::HostZoomMap::PAGE_SCALE_IS_ONE_CHANGED:
+ continue;
+ case content::HostZoomMap::ZOOM_CHANGED_TEMPORARY_ZOOM:
+ NOTREACHED();
+ }
+
+ std::string setting_string =
+ content_settings::ContentSettingToString(CONTENT_SETTING_DEFAULT);
+ DCHECK(!setting_string.empty());
+
+ exception->SetString(site_settings::kSetting, setting_string);
+
+ // Calculate the zoom percent from the factor. Round up to the nearest whole
+ // number.
+ int zoom_percent = static_cast<int>(
+ content::ZoomLevelToZoomFactor(i->zoom_level) * 100 + 0.5);
+ exception->SetString(kZoom, base::FormatPercent(zoom_percent));
+ exception->SetString(
+ site_settings::kSource, site_settings::kPreferencesSource);
+ // Append the new entry to the list and map.
+ zoom_levels_exceptions.Append(std::move(exception));
+ }
+
+ base::Value type_string(kZoomContentType);
+ web_ui()->CallJavascriptFunctionUnsafe("ContentSettings.setExceptions",
+ type_string, zoom_levels_exceptions);
+}
+
+void ContentSettingsHandler::UpdateExceptionsViewFromHostContentSettingsMap(
+ ContentSettingsType type) {
+ base::ListValue exceptions;
+ HostContentSettingsMap* settings_map =
+ HostContentSettingsMapFactory::GetForProfile(GetProfile());
+ const auto* extension_registry =
+ extensions::ExtensionRegistry::Get(GetProfile());
+ site_settings::GetExceptionsFromHostContentSettingsMap(
+ settings_map, type, extension_registry, web_ui(), /*incognito=*/false,
+ /*filter=*/nullptr, &exceptions);
+ base::Value type_string(site_settings::ContentSettingsTypeToGroupName(type));
+ web_ui()->CallJavascriptFunctionUnsafe("ContentSettings.setExceptions",
+ type_string, exceptions);
+
+ UpdateExceptionsViewFromOTRHostContentSettingsMap(type);
+
+#if defined(OS_CHROMEOS)
+ // Also the default for protected contents is managed in another place.
+ if (type == CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER)
+ return;
+#endif
+
+ // The default may also have changed (we won't get a separate notification).
+ // If it hasn't changed, this call will be harmless.
+ UpdateSettingDefaultFromModel(type);
+}
+
+void ContentSettingsHandler::UpdateExceptionsViewFromOTRHostContentSettingsMap(
+ ContentSettingsType type) {
+ const HostContentSettingsMap* otr_settings_map =
+ HostContentSettingsMapFactory::GetForProfile(GetOTRProfile());
+ if (!otr_settings_map)
+ return;
+ const auto* extension_registry =
+ extensions::ExtensionRegistry::Get(GetOTRProfile());
+ base::ListValue exceptions;
+ site_settings::GetExceptionsFromHostContentSettingsMap(
+ otr_settings_map, type, extension_registry, web_ui(), /*incognito=*/true,
+ /*filter=*/nullptr, &exceptions);
+ base::Value type_string(site_settings::ContentSettingsTypeToGroupName(type));
+ web_ui()->CallJavascriptFunctionUnsafe("ContentSettings.setOTRExceptions",
+ type_string, exceptions);
+}
+
+void ContentSettingsHandler::RemoveExceptionFromHostContentSettingsMap(
+ const base::ListValue* args,
+ ContentSettingsType type) {
+ std::string mode;
+ bool rv = args->GetString(1, &mode);
+ DCHECK(rv);
+
+ std::string pattern;
+ rv = args->GetString(2, &pattern);
+ DCHECK(rv);
+
+ // The fourth argument to this handler is optional.
+ std::string secondary_pattern_string;
+ if (args->GetSize() >= 4U) {
+ rv = args->GetString(3, &secondary_pattern_string);
+ DCHECK(rv);
+ }
+
+ Profile* profile = mode == "normal" ? GetProfile() : GetOTRProfile();
+ if (!profile)
+ return;
+
+ HostContentSettingsMap* settings_map =
+ HostContentSettingsMapFactory::GetForProfile(profile);
+ ContentSettingsPattern primary_pattern =
+ ContentSettingsPattern::FromString(pattern);
+ ContentSettingsPattern secondary_pattern =
+ secondary_pattern_string.empty()
+ ? ContentSettingsPattern::Wildcard()
+ : ContentSettingsPattern::FromString(secondary_pattern_string);
+ PermissionUtil::ScopedRevocationReporter scoped_revocation_reporter(
+ profile, primary_pattern, secondary_pattern, type,
+ PermissionSourceUI::SITE_SETTINGS);
+
+ settings_map->SetContentSettingCustomScope(
+ primary_pattern, secondary_pattern, type, std::string(),
+ CONTENT_SETTING_DEFAULT);
+}
+
+void ContentSettingsHandler::RemoveZoomLevelException(
+ const base::ListValue* args) {
+ std::string mode;
+ bool rv = args->GetString(1, &mode);
+ DCHECK(rv);
+
+ std::string pattern;
+ rv = args->GetString(2, &pattern);
+ DCHECK(rv);
+
+ if (pattern ==
+ l10n_util::GetStringUTF8(IDS_ZOOMLEVELS_CHROME_ERROR_PAGES_LABEL)) {
+ pattern = content::kUnreachableWebDataURL;
+ }
+
+ content::HostZoomMap* host_zoom_map;
+ host_zoom_map =
+ content::HostZoomMap::GetDefaultForBrowserContext(
+ GetBrowserContext(web_ui()));
+ double default_level = host_zoom_map->GetDefaultZoomLevel();
+ host_zoom_map->SetZoomLevelForHost(pattern, default_level);
+}
+
+void ContentSettingsHandler::RemoveChooserException(
+ const site_settings::ChooserTypeNameEntry* chooser_type,
+ const base::ListValue* args) {
+ std::string mode;
+ bool rv = args->GetString(1, &mode);
+ DCHECK(rv);
+
+ std::string requesting_origin_string;
+ rv = args->GetString(2, &requesting_origin_string);
+ DCHECK(rv);
+ GURL requesting_origin(requesting_origin_string);
+ DCHECK(requesting_origin.is_valid());
+
+ std::string embedding_origin_string;
+ rv = args->GetString(3, &embedding_origin_string);
+ DCHECK(rv);
+ GURL embedding_origin(embedding_origin_string);
+ DCHECK(embedding_origin.is_valid());
+
+ const base::DictionaryValue* object = nullptr;
+ rv = args->GetDictionary(4, &object);
+ DCHECK(rv);
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (mode != "normal")
+ profile = profile->GetOffTheRecordProfile();
+
+ ChooserContextBase* chooser_context = chooser_type->get_context(profile);
+ chooser_context->RevokeObjectPermission(requesting_origin, embedding_origin,
+ *object);
+ UpdateChooserExceptionsViewFromModel(*chooser_type);
+ // TODO(reillyg): Create metrics for revocations. crbug.com/556845
+}
+
+void ContentSettingsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("setContentFilter",
+ base::Bind(&ContentSettingsHandler::SetContentFilter,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("removeException",
+ base::Bind(&ContentSettingsHandler::RemoveException,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("setException",
+ base::Bind(&ContentSettingsHandler::SetException,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("checkExceptionPatternValidity",
+ base::Bind(&ContentSettingsHandler::CheckExceptionPatternValidity,
+ base::Unretained(this)));
+}
+
+void ContentSettingsHandler::SetContentFilter(const base::ListValue* args) {
+ DCHECK_EQ(2U, args->GetSize());
+ std::string group, setting;
+ if (!(args->GetString(0, &group) &&
+ args->GetString(1, &setting))) {
+ NOTREACHED();
+ return;
+ }
+
+ ContentSetting default_setting;
+ bool result =
+ content_settings::ContentSettingFromString(setting, &default_setting);
+ DCHECK(result);
+
+ ContentSettingsType content_type =
+ site_settings::ContentSettingsTypeFromGroupName(group);
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+#if defined(OS_CHROMEOS)
+ // ChromeOS special case : in Guest mode settings are opened in Incognito
+ // mode, so we need original profile to actually modify settings.
+ if (user_manager::UserManager::Get()->IsLoggedInAsGuest())
+ profile = profile->GetOriginalProfile();
+#endif
+
+ HostContentSettingsMap* map =
+ HostContentSettingsMapFactory::GetForProfile(profile);
+ map->SetDefaultContentSetting(content_type, default_setting);
+
+ const ExceptionsInfoMap& exceptions_info_map = GetExceptionsInfoMap();
+ const auto& it = exceptions_info_map.find(content_type);
+ if (it != exceptions_info_map.end())
+ base::RecordAction(it->second.uma);
+}
+
+void ContentSettingsHandler::RemoveException(const base::ListValue* args) {
+ std::string type_string;
+ CHECK(args->GetString(0, &type_string));
+
+ // Zoom levels are no actual content type so we need to handle them
+ // separately. They would not be recognized by
+ // ContentSettingsTypeFromGroupName.
+ if (type_string == kZoomContentType) {
+ RemoveZoomLevelException(args);
+ return;
+ }
+
+ const site_settings::ChooserTypeNameEntry* chooser_type =
+ site_settings::ChooserTypeFromGroupName(type_string);
+ if (chooser_type) {
+ RemoveChooserException(chooser_type, args);
+ return;
+ }
+
+ ContentSettingsType type =
+ site_settings::ContentSettingsTypeFromGroupName(type_string);
+ RemoveExceptionFromHostContentSettingsMap(args, type);
+
+ WebSiteSettingsUmaUtil::LogPermissionChange(
+ type, ContentSetting::CONTENT_SETTING_DEFAULT);
+}
+
+void ContentSettingsHandler::SetException(const base::ListValue* args) {
+ std::string type_string;
+ CHECK(args->GetString(0, &type_string));
+ std::string mode;
+ CHECK(args->GetString(1, &mode));
+ std::string pattern;
+ CHECK(args->GetString(2, &pattern));
+ std::string setting;
+ CHECK(args->GetString(3, &setting));
+
+ ContentSettingsType type =
+ site_settings::ContentSettingsTypeFromGroupName(type_string);
+
+ DCHECK(type != CONTENT_SETTINGS_TYPE_GEOLOCATION &&
+ type != CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC &&
+ type != CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
+
+ Profile* profile = mode == "normal" ? GetProfile() : GetOTRProfile();
+
+ // The profile could be nullptr if the mode was OTR but the OTR profile
+ // got destroyed before we received this message.
+ if (!profile)
+ return;
+
+ ContentSetting setting_type;
+ bool result =
+ content_settings::ContentSettingFromString(setting, &setting_type);
+ DCHECK(result);
+
+ HostContentSettingsMap* settings_map =
+ HostContentSettingsMapFactory::GetForProfile(profile);
+
+ PermissionUtil::ScopedRevocationReporter scoped_revocation_reporter(
+ profile, ContentSettingsPattern::FromString(pattern),
+ ContentSettingsPattern::Wildcard(), type,
+ PermissionSourceUI::SITE_SETTINGS);
+
+ settings_map->SetContentSettingCustomScope(
+ ContentSettingsPattern::FromString(pattern),
+ ContentSettingsPattern::Wildcard(), type, std::string(), setting_type);
+ WebSiteSettingsUmaUtil::LogPermissionChange(type, setting_type);
+}
+
+void ContentSettingsHandler::CheckExceptionPatternValidity(
+ const base::ListValue* args) {
+ std::string type_string;
+ CHECK(args->GetString(0, &type_string));
+ std::string mode_string;
+ CHECK(args->GetString(1, &mode_string));
+ std::string pattern_string;
+ CHECK(args->GetString(2, &pattern_string));
+
+ ContentSettingsPattern pattern =
+ ContentSettingsPattern::FromString(pattern_string);
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ContentSettings.patternValidityCheckComplete", base::Value(type_string),
+ base::Value(mode_string), base::Value(pattern_string),
+ base::Value(pattern.IsValid()));
+}
+
+Profile* ContentSettingsHandler::GetProfile() {
+ return Profile::FromWebUI(web_ui());
+}
+
+ProtocolHandlerRegistry* ContentSettingsHandler::GetProtocolHandlerRegistry() {
+ return ProtocolHandlerRegistryFactory::GetForBrowserContext(
+ GetBrowserContext(web_ui()));
+}
+
+Profile* ContentSettingsHandler::GetOTRProfile() {
+ return GetProfile()->HasOffTheRecordProfile()
+ ? GetProfile()->GetOffTheRecordProfile()
+ : nullptr;
+}
+
+void ContentSettingsHandler::RefreshFlashMediaSettings() {
+ MediaSettingsInfo::ForFlash& settings = media_settings_->forFlash();
+ settings.initialized = false;
+
+ settings.last_refresh_request_id =
+ flash_settings_manager_->GetPermissionSettings(
+ PP_FLASH_BROWSEROPERATIONS_SETTINGTYPE_CAMERAMIC);
+}
+
+void ContentSettingsHandler::OnPepperFlashPrefChanged() {
+ ShowFlashMediaLink(
+ DEFAULT_SETTING, CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, false);
+ ShowFlashMediaLink(
+ DEFAULT_SETTING, CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, false);
+ ShowFlashMediaLink(
+ EXCEPTIONS, CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, false);
+ ShowFlashMediaLink(
+ EXCEPTIONS, CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, false);
+
+ PrefService* prefs = user_prefs::UserPrefs::Get(GetBrowserContext(web_ui()));
+ if (prefs->GetBoolean(prefs::kPepperFlashSettingsEnabled))
+ RefreshFlashMediaSettings();
+ else
+ media_settings_->forFlash().initialized = false;
+}
+
+void ContentSettingsHandler::OnZoomLevelChanged(
+ const content::HostZoomMap::ZoomLevelChange& change) {
+ UpdateZoomLevelsExceptionsView();
+}
+
+void ContentSettingsHandler::ShowFlashMediaLink(
+ LinkType link_type, ContentSettingsType content_type, bool show) {
+ MediaSettingsInfo::ForOneType& settings =
+ media_settings_->forType(content_type);
+
+ bool& show_link = link_type == DEFAULT_SETTING ?
+ settings.show_flash_default_link :
+ settings.show_flash_exceptions_link;
+
+ if (show_link != show) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ContentSettings.showMediaPepperFlashLink",
+ base::Value(link_type == DEFAULT_SETTING ? "default" : "exceptions"),
+ base::Value(content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
+ ? "mic"
+ : "camera"),
+ base::Value(show));
+ show_link = show;
+ }
+}
+
+void ContentSettingsHandler::UpdateFlashMediaLinksVisibility(
+ ContentSettingsType type) {
+ MediaSettingsInfo::ForOneType& settings = media_settings_->forType(type);
+ MediaSettingsInfo::ForFlash& flash_settings = media_settings_->forFlash();
+
+ if (!flash_settings.initialized)
+ return;
+
+ // We handle four cases - default settings and exceptions for microphone
+ // and camera. We use the following criteria to determine whether to show
+ // the links.
+ //
+ // 1. Flash won't send us notifications when its settings get changed, which
+ // means the Flash settings in |media_settings_| may be out-dated, especially
+ // after we show links to change Flash settings.
+ // In order to avoid confusion, we won't hide the links once they are showed.
+ // One exception is that we will hide them when Pepper Flash is disabled
+ // (handled in OnPepperFlashPrefChanged()).
+ //
+ // 2. If audio or video capture are disabled by policy, the respective link
+ // shouldn't be showed. Flash conforms to the policy in this case because
+ // it cannot open those devices.
+ //
+ // 3. Otherwise, we show the link if the corresponding setting is different
+ // in HostContentSettingsMap than it is in Flash.
+ if (settings.policy_disable)
+ return;
+
+ if (settings.default_setting_initialized &&
+ !settings.show_flash_default_link &&
+ (flash_settings.default_setting !=
+ settings.default_setting)) {
+ ShowFlashMediaLink(DEFAULT_SETTING, type, true);
+ }
+
+ if (settings.exceptions_initialized &&
+ !settings.show_flash_exceptions_link &&
+ !PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
+ settings.default_setting,
+ settings.exceptions,
+ flash_settings.default_setting,
+ flash_settings.exceptions)) {
+ ShowFlashMediaLink(EXCEPTIONS, type, true);
+ }
+}
+
+void ContentSettingsHandler::UpdateMediaDeviceDropdownVisibility(
+ ContentSettingsType type) {
+ MediaSettingsInfo::ForOneType& settings = media_settings_->forType(type);
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ContentSettings.setDevicesMenuVisibility",
+ base::Value(site_settings::ContentSettingsTypeToGroupName(type)),
+ base::Value(!settings.policy_disable));
+}
+
+void ContentSettingsHandler::UpdateProtectedContentExceptionsButton() {
+#if defined(OS_CHROMEOS)
+ // Guests cannot modify exceptions. UIAccountTweaks will disabled the button.
+ if (user_manager::UserManager::Get()->IsLoggedInAsGuest())
+ return;
+#endif
+
+ // Exceptions apply only when the feature is enabled.
+ PrefService* prefs = user_prefs::UserPrefs::Get(GetBrowserContext(web_ui()));
+ bool enable_exceptions = prefs->GetBoolean(prefs::kEnableDRM);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ContentSettings.enableProtectedContentExceptions",
+ base::Value(enable_exceptions));
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/content_settings_handler.h b/chromium/chrome/browser/ui/webui/options/content_settings_handler.h
new file mode 100644
index 00000000000..768ff702a69
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/content_settings_handler.h
@@ -0,0 +1,285 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CONTENT_SETTINGS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CONTENT_SETTINGS_HANDLER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "base/values.h"
+#include "chrome/browser/pepper_flash_settings_manager.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "chrome/browser/ui/webui/options/pepper_flash_content_settings_utils.h"
+#include "components/content_settings/core/browser/content_settings_observer.h"
+#include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/content_settings_types.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "content/public/browser/host_zoom_map.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+class HostContentSettingsMap;
+class Profile;
+class ProtocolHandlerRegistry;
+
+namespace site_settings {
+struct ChooserTypeNameEntry;
+}
+
+namespace options {
+
+class ContentSettingsHandler : public OptionsPageUIHandler,
+ public content_settings::Observer,
+ public content::NotificationObserver,
+ public PepperFlashSettingsManager::Client {
+ public:
+ ContentSettingsHandler();
+ ~ContentSettingsHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializeHandler() override;
+ void InitializePage() override;
+ void RegisterMessages() override;
+
+ // content_settings::Observer implementation.
+ void OnContentSettingChanged(const ContentSettingsPattern& primary_pattern,
+ const ContentSettingsPattern& secondary_pattern,
+ ContentSettingsType content_type,
+ std::string resource_identifier) override;
+
+ // content::NotificationObserver implementation.
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ // PepperFlashSettingsManager::Client implementation.
+ void OnGetPermissionSettingsCompleted(
+ uint32_t request_id,
+ bool success,
+ PP_Flash_BrowserOperations_Permission default_permission,
+ const ppapi::FlashSiteSettings& sites) override;
+
+ private:
+ // Used to determine whether we should show links to Flash camera and
+ // microphone settings.
+ class MediaSettingsInfo {
+ public:
+ MediaSettingsInfo();
+ ~MediaSettingsInfo();
+
+ // Cached Pepper Flash settings.
+ struct ForFlash {
+ ForFlash();
+ ~ForFlash();
+
+ ContentSetting default_setting;
+ MediaExceptions exceptions;
+ bool initialized;
+ uint32_t last_refresh_request_id;
+ };
+
+ struct ForOneType {
+ ForOneType();
+ ~ForOneType();
+
+ // Whether the links to Flash settings pages are showed.
+ bool show_flash_default_link;
+ bool show_flash_exceptions_link;
+
+ // Cached Chrome media settings.
+ ContentSetting default_setting;
+ bool policy_disable;
+ bool default_setting_initialized;
+ MediaExceptions exceptions;
+ bool exceptions_initialized;
+ };
+
+ ForOneType& forType(ContentSettingsType type);
+ ForFlash& forFlash();
+
+ private:
+ ForOneType mic_settings_;
+ ForOneType camera_settings_;
+ ForFlash flash_settings_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaSettingsInfo);
+ };
+
+ // Used by ShowFlashMediaLink() to specify which link to show/hide.
+ enum LinkType {
+ DEFAULT_SETTING = 0,
+ EXCEPTIONS,
+ };
+
+ // Functions that call into the page -----------------------------------------
+
+ // Updates the page with the default settings (allow, ask, block, etc.)
+ void UpdateSettingDefaultFromModel(ContentSettingsType type);
+
+ // Compares the microphone or camera |type| default settings with Flash
+ // and updates the Flash links' visibility accordingly.
+ void UpdateMediaSettingsFromPrefs(ContentSettingsType type);
+
+ // Clobbers and rebuilds the specific content setting type exceptions table.
+ void UpdateExceptionsViewFromModel(ContentSettingsType type);
+
+ // Clobbers and rebuilds the specific content setting type exceptions
+ // OTR table.
+ void UpdateOTRExceptionsViewFromModel(ContentSettingsType type);
+
+ // Clobbers and rebuilds all the exceptions tables in the page (both normal
+ // and OTR tables).
+ void UpdateAllExceptionsViewsFromModel();
+
+ // As above, but only OTR tables.
+ void UpdateAllOTRExceptionsViewsFromModel();
+
+ // Clobbers and rebuilds just the geolocation exception table.
+ void UpdateGeolocationExceptionsView();
+
+ // Clobbers and rebuilds just the desktop notification exception table.
+ void UpdateNotificationExceptionsView();
+
+ // Compares the exceptions of the camera or microphone |type| with its Flash
+ // counterparts and updates the Flash links' visibility accordingly.
+ void CompareMediaExceptionsWithFlash(ContentSettingsType type);
+
+ // Clobbers and rebuilds just the MIDI SysEx exception table.
+ void UpdateMIDISysExExceptionsView();
+
+ // Clobbers and rebuilds all chooser-based exception tables.
+ void UpdateAllChooserExceptionsViewsFromModel();
+
+ // As above, but only OTR tables.
+ void UpdateAllOTRChooserExceptionsViewsFromModel();
+
+ // Clobbers and rebuilds the exception table for a particular chooser-based
+ // permission.
+ void UpdateChooserExceptionsViewFromModel(
+ const site_settings::ChooserTypeNameEntry& chooser_type);
+
+ // As above, but only OTR tables.
+ void UpdateOTRChooserExceptionsViewFromModel(
+ const site_settings::ChooserTypeNameEntry& chooser_type);
+
+ // Modifies the zoom level exceptions list to display correct chrome
+ // signin page entry. When the legacy (non-WebView-based) signin page
+ // goes away, this function can be removed.
+ void AdjustZoomLevelsListForSigninPageIfNecessary(
+ content::HostZoomMap::ZoomLevelVector* zoom_levels);
+
+ // Clobbers and rebuilds just the zoom levels exception table.
+ void UpdateZoomLevelsExceptionsView();
+
+ // Clobbers and rebuilds an exception table that's managed by the host content
+ // settings map.
+ void UpdateExceptionsViewFromHostContentSettingsMap(ContentSettingsType type);
+
+ // As above, but acts on the OTR table for the content setting type.
+ void UpdateExceptionsViewFromOTRHostContentSettingsMap(
+ ContentSettingsType type);
+
+ // Updates the radio buttons for enabling / disabling handlers.
+ void UpdateHandlersEnabledRadios();
+
+ // Removes one geolocation exception. |args| contains the parameters passed to
+ // RemoveException().
+ void RemoveGeolocationException(const base::ListValue* args);
+
+ // Removes one notification exception. |args| contains the parameters passed
+ // to RemoveException().
+ void RemoveNotificationException(const base::ListValue* args);
+
+ // Removes one exception of |type| from the host content settings map. |args|
+ // contains the parameters passed to RemoveException().
+ void RemoveExceptionFromHostContentSettingsMap(
+ const base::ListValue* args,
+ ContentSettingsType type);
+
+ // Removes one zoom level exception. |args| contains the parameters passed to
+ // RemoveException().
+ void RemoveZoomLevelException(const base::ListValue* args);
+
+ // Removes one exception for a chooser-based permission. |args| contains the
+ // parameters passed to RemoveException().
+ void RemoveChooserException(
+ const site_settings::ChooserTypeNameEntry* chooser_type,
+ const base::ListValue* args);
+
+ // Callbacks used by the page ------------------------------------------------
+
+ // Sets the default value for a specific content type. |args| includes the
+ // content type and a string describing the new default the user has
+ // chosen.
+ void SetContentFilter(const base::ListValue* args);
+
+ // Removes the given row from the table. The first entry in |args| is the
+ // content type, and the rest of the arguments depend on the content type
+ // to be removed.
+ void RemoveException(const base::ListValue* args);
+
+ // Changes the value of an exception. Called after the user is done editing an
+ // exception.
+ void SetException(const base::ListValue* args);
+
+ // Called to decide whether a given pattern is valid, or if it should be
+ // rejected. Called while the user is editing an exception pattern.
+ void CheckExceptionPatternValidity(const base::ListValue* args);
+
+ // Utility functions ---------------------------------------------------------
+
+ // Applies content settings whitelists to reduce breakage / user confusion.
+ void ApplyWhitelist(ContentSettingsType content_type,
+ ContentSetting default_setting);
+
+ // Gets the normal profile.
+ Profile* GetProfile();
+
+ // Gets the incognito profile, or nullptr if there is no active incognito
+ // session.
+ Profile* GetOTRProfile();
+
+ // Gets the ProtocolHandlerRegistry for the normal profile.
+ ProtocolHandlerRegistry* GetProtocolHandlerRegistry();
+
+ void RefreshFlashMediaSettings();
+
+ void OnPepperFlashPrefChanged();
+
+ // content::HostZoomMap subscription.
+ void OnZoomLevelChanged(const content::HostZoomMap::ZoomLevelChange& change);
+
+ void ShowFlashMediaLink(
+ LinkType link_type, ContentSettingsType content_type, bool show);
+
+ void UpdateFlashMediaLinksVisibility(ContentSettingsType type);
+
+ void UpdateMediaDeviceDropdownVisibility(ContentSettingsType type);
+
+ void UpdateProtectedContentExceptionsButton();
+
+ // Member variables ---------------------------------------------------------
+
+ content::NotificationRegistrar notification_registrar_;
+ PrefChangeRegistrar pref_change_registrar_;
+ std::unique_ptr<PepperFlashSettingsManager> flash_settings_manager_;
+ std::unique_ptr<MediaSettingsInfo> media_settings_;
+ std::unique_ptr<content::HostZoomMap::Subscription>
+ host_zoom_map_subscription_;
+ std::unique_ptr<content::HostZoomMap::Subscription>
+ signin_host_zoom_map_subscription_;
+ ScopedObserver<HostContentSettingsMap, content_settings::Observer> observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContentSettingsHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CONTENT_SETTINGS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/cookies_view_browsertest.js b/chromium/chrome/browser/ui/webui/options/cookies_view_browsertest.js
new file mode 100644
index 00000000000..e2c094d2ca7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/cookies_view_browsertest.js
@@ -0,0 +1,60 @@
+// 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.
+
+GEN_INCLUDE(['options_browsertest_base.js']);
+
+/**
+ * TestFixture for cookies view WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function CookiesViewWebUITest() {}
+
+CookiesViewWebUITest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /**
+ * Browse to the cookies view.
+ */
+ browsePreload: 'chrome://settings-frame/cookies',
+
+ /** @override */
+ setUp: function() {
+ OptionsBrowsertestBase.prototype.setUp.call(this);
+
+ // Enable when failure is resolved.
+ // AX_TEXT_01: http://crbug.com/570560
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'controlsWithoutLabel',
+ '#cookies-view-page > .content-area.cookies-list-content-area > *');
+
+ var requiredOwnedAriaRoleMissingSelectors = [
+ '#default-search-engine-list',
+ '#other-search-engine-list',
+ ];
+
+ // Enable when failure is resolved.
+ // AX_ARIA_08: http://crbug.com/605689
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'requiredOwnedAriaRoleMissing',
+ requiredOwnedAriaRoleMissingSelectors);
+ },
+};
+
+// Test opening the cookies view has correct location.
+TEST_F('CookiesViewWebUITest', 'testOpenCookiesView', function() {
+ assertEquals(this.browsePreload, document.location.href);
+});
+
+TEST_F('CookiesViewWebUITest', 'testNoCloseOnSearchEnter', function() {
+ var cookiesView = CookiesView.getInstance();
+ assertTrue(cookiesView.visible);
+ var searchBox = cookiesView.pageDiv.querySelector('.cookies-search-box');
+ searchBox.dispatchEvent(new KeyboardEvent('keydown', {
+ 'bubbles': true,
+ 'cancelable': true,
+ 'key': 'Enter'
+ }));
+ assertTrue(cookiesView.visible);
+});
diff --git a/chromium/chrome/browser/ui/webui/options/cookies_view_handler.cc b/chromium/chrome/browser/ui/webui/options/cookies_view_handler.cc
new file mode 100644
index 00000000000..7b1a4bb686f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/cookies_view_handler.cc
@@ -0,0 +1,297 @@
+// 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/ui/webui/options/cookies_view_handler.h"
+
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browsing_data/browsing_data_appcache_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_cache_storage_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_channel_id_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_cookie_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_database_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_file_system_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_flash_lso_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_media_license_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_quota_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_service_worker_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/cookies_tree_model_util.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_ui.h"
+
+namespace storage {
+class FileSystemContext;
+}
+
+namespace options {
+
+CookiesViewHandler::CookiesViewHandler()
+ : batch_update_(false),
+ model_util_(new CookiesTreeModelUtil) {
+}
+
+CookiesViewHandler::~CookiesViewHandler() {
+}
+
+void CookiesViewHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ static OptionsStringResource resources[] = {
+ {"label_cookie_name", IDS_COOKIES_COOKIE_NAME_LABEL},
+ {"label_cookie_content", IDS_COOKIES_COOKIE_CONTENT_LABEL},
+ {"label_cookie_domain", IDS_COOKIES_COOKIE_DOMAIN_LABEL},
+ {"label_cookie_path", IDS_COOKIES_COOKIE_PATH_LABEL},
+ {"label_cookie_send_for", IDS_COOKIES_COOKIE_SENDFOR_LABEL},
+ {"label_cookie_accessible_to_script",
+ IDS_COOKIES_COOKIE_ACCESSIBLE_TO_SCRIPT_LABEL},
+ {"label_cookie_created", IDS_COOKIES_COOKIE_CREATED_LABEL},
+ {"label_cookie_expires", IDS_COOKIES_COOKIE_EXPIRES_LABEL},
+ {"label_webdb_desc", IDS_COOKIES_WEB_DATABASE_DESCRIPTION_LABEL},
+ {"label_local_storage_size",
+ IDS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
+ {"label_local_storage_last_modified",
+ IDS_COOKIES_LOCAL_STORAGE_LAST_MODIFIED_LABEL},
+ {"label_local_storage_origin", IDS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
+ {"label_indexed_db_size", IDS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
+ {"label_indexed_db_last_modified",
+ IDS_COOKIES_LOCAL_STORAGE_LAST_MODIFIED_LABEL},
+ {"label_indexed_db_origin", IDS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
+ {"label_service_worker_origin", IDS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
+ {"label_service_worker_size",
+ IDS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
+ {"label_service_worker_scopes", IDS_COOKIES_SERVICE_WORKER_SCOPES_LABEL},
+ {"label_cache_storage_origin", IDS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
+ {"label_cache_storage_size",
+ IDS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
+ {"label_cache_storage_last_modified",
+ IDS_COOKIES_LOCAL_STORAGE_LAST_MODIFIED_LABEL},
+ {"label_app_cache_manifest",
+ IDS_COOKIES_APPLICATION_CACHE_MANIFEST_LABEL},
+ {"label_cookie_last_accessed", IDS_COOKIES_LAST_ACCESSED_LABEL},
+ {"cookie_domain", IDS_COOKIES_DOMAIN_COLUMN_HEADER},
+ {"cookie_local_data", IDS_COOKIES_DATA_COLUMN_HEADER},
+ {"cookie_singular", IDS_COOKIES_SINGLE_COOKIE},
+ {"cookie_plural", IDS_COOKIES_PLURAL_COOKIES},
+ {"cookie_database_storage", IDS_COOKIES_DATABASE_STORAGE},
+ {"cookie_indexed_db", IDS_COOKIES_INDEXED_DB},
+ {"cookie_local_storage", IDS_COOKIES_LOCAL_STORAGE},
+ {"cookie_app_cache", IDS_COOKIES_APPLICATION_CACHE},
+ {"cookie_service_worker", IDS_COOKIES_SERVICE_WORKER},
+ {"cookie_cache_storage", IDS_COOKIES_CACHE_STORAGE},
+ {"cookie_flash_lso", IDS_COOKIES_FLASH_LSO},
+ {"search_cookies", IDS_COOKIES_SEARCH_COOKIES},
+ {"remove_cookie", IDS_COOKIES_REMOVE_LABEL},
+ {"remove_all_cookie", IDS_COOKIES_REMOVE_ALL_LABEL},
+ {"remove_all_shown_cookie", IDS_COOKIES_REMOVE_ALL_SHOWN_LABEL},
+ {"cookie_file_system", IDS_COOKIES_FILE_SYSTEM},
+ {"label_file_system_origin", IDS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
+ {"label_file_system_temporary_usage",
+ IDS_COOKIES_FILE_SYSTEM_TEMPORARY_USAGE_LABEL},
+ {"label_file_system_persistent_usage",
+ IDS_COOKIES_FILE_SYSTEM_PERSISTENT_USAGE_LABEL},
+ {"cookie_channel_id", IDS_COOKIES_CHANNEL_ID},
+ {"label_channel_id_server_id", IDS_COOKIES_CHANNEL_ID_ORIGIN_LABEL},
+ {"label_channel_id_type", IDS_COOKIES_CHANNEL_ID_TYPE_LABEL},
+ {"label_channel_id_created", IDS_COOKIES_CHANNEL_ID_CREATED_LABEL},
+ {"label_channel_id_expires", IDS_COOKIES_CHANNEL_ID_EXPIRES_LABEL},
+ {"label_protected_by_apps",
+ IDS_GEOLOCATION_SET_BY_HOVER}, // TODO(bauerb): Use a better string
+ {"cookie_media_license", IDS_COOKIES_MEDIA_LICENSE},
+ {"label_media_license_origin", IDS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
+ {"label_media_license_size",
+ IDS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
+ {"label_media_license_last_modified",
+ IDS_COOKIES_LOCAL_STORAGE_LAST_MODIFIED_LABEL},
+ };
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+ RegisterTitle(localized_strings, "cookiesViewPage",
+ IDS_COOKIES_WEBSITE_PERMISSIONS_WINDOW_TITLE);
+}
+
+void CookiesViewHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("updateCookieSearchResults",
+ base::Bind(&CookiesViewHandler::UpdateSearchResults,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("removeAllCookies",
+ base::Bind(&CookiesViewHandler::RemoveAll,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("removeCookie",
+ base::Bind(&CookiesViewHandler::Remove,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("loadCookie",
+ base::Bind(&CookiesViewHandler::LoadChildren,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("reloadCookies",
+ base::Bind(&CookiesViewHandler::ReloadCookies,
+ base::Unretained(this)));
+}
+
+void CookiesViewHandler::TreeNodesAdded(ui::TreeModel* model,
+ ui::TreeModelNode* parent,
+ int start,
+ int count) {
+ // Skip if there is a batch update in progress.
+ if (batch_update_)
+ return;
+
+ CookiesTreeModel* tree_model = static_cast<CookiesTreeModel*>(model);
+ CookieTreeNode* parent_node = tree_model->AsNode(parent);
+
+ std::unique_ptr<base::ListValue> children(new base::ListValue);
+ model_util_->GetChildNodeList(
+ parent_node, start, count, /*include_quota_nodes=*/true, children.get());
+
+ base::ListValue args;
+ if (parent == tree_model->GetRoot())
+ args.Append(base::MakeUnique<base::Value>());
+ else
+ args.AppendString(model_util_->GetTreeNodeId(parent_node));
+ args.AppendInteger(start);
+ args.Append(std::move(children));
+ web_ui()->CallJavascriptFunctionUnsafe("CookiesView.onTreeItemAdded", args);
+}
+
+void CookiesViewHandler::TreeNodesRemoved(ui::TreeModel* model,
+ ui::TreeModelNode* parent,
+ int start,
+ int count) {
+ // Skip if there is a batch update in progress.
+ if (batch_update_)
+ return;
+
+ CookiesTreeModel* tree_model = static_cast<CookiesTreeModel*>(model);
+
+ base::ListValue args;
+ if (parent == tree_model->GetRoot())
+ args.Append(base::MakeUnique<base::Value>());
+ else
+ args.AppendString(model_util_->GetTreeNodeId(tree_model->AsNode(parent)));
+ args.AppendInteger(start);
+ args.AppendInteger(count);
+ web_ui()->CallJavascriptFunctionUnsafe("CookiesView.onTreeItemRemoved", args);
+}
+
+void CookiesViewHandler::TreeModelBeginBatch(CookiesTreeModel* model) {
+ DCHECK(!batch_update_); // There should be no nested batch begin.
+ batch_update_ = true;
+}
+
+void CookiesViewHandler::TreeModelEndBatch(CookiesTreeModel* model) {
+ DCHECK(batch_update_);
+ batch_update_ = false;
+
+ SendChildren(model->GetRoot());
+}
+
+void CookiesViewHandler::EnsureCookiesTreeModelCreated() {
+ if (!cookies_tree_model_.get()) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ content::StoragePartition* storage_partition =
+ content::BrowserContext::GetDefaultStoragePartition(profile);
+ content::IndexedDBContext* indexed_db_context =
+ storage_partition->GetIndexedDBContext();
+ content::ServiceWorkerContext* service_worker_context =
+ storage_partition->GetServiceWorkerContext();
+ content::CacheStorageContext* cache_storage_context =
+ storage_partition->GetCacheStorageContext();
+ storage::FileSystemContext* file_system_context =
+ storage_partition->GetFileSystemContext();
+ LocalDataContainer* container = new LocalDataContainer(
+ new BrowsingDataCookieHelper(profile->GetRequestContext()),
+ new BrowsingDataDatabaseHelper(profile),
+ new BrowsingDataLocalStorageHelper(profile), NULL,
+ new BrowsingDataAppCacheHelper(profile),
+ new BrowsingDataIndexedDBHelper(indexed_db_context),
+ BrowsingDataFileSystemHelper::Create(file_system_context),
+ BrowsingDataQuotaHelper::Create(profile),
+ BrowsingDataChannelIDHelper::Create(profile->GetRequestContext()),
+ new BrowsingDataServiceWorkerHelper(service_worker_context),
+ new BrowsingDataCacheStorageHelper(cache_storage_context),
+ BrowsingDataFlashLSOHelper::Create(profile),
+ BrowsingDataMediaLicenseHelper::Create(file_system_context));
+ cookies_tree_model_.reset(new CookiesTreeModel(
+ container, profile->GetExtensionSpecialStoragePolicy()));
+ cookies_tree_model_->AddCookiesTreeObserver(this);
+ }
+}
+
+void CookiesViewHandler::UpdateSearchResults(const base::ListValue* args) {
+ base::string16 query;
+ if (!args->GetString(0, &query))
+ return;
+
+ EnsureCookiesTreeModelCreated();
+
+ cookies_tree_model_->UpdateSearchResults(query);
+}
+
+void CookiesViewHandler::RemoveAll(const base::ListValue* args) {
+ EnsureCookiesTreeModelCreated();
+ cookies_tree_model_->DeleteAllStoredObjects();
+}
+
+void CookiesViewHandler::Remove(const base::ListValue* args) {
+ std::string node_path;
+ if (!args->GetString(0, &node_path))
+ return;
+
+ EnsureCookiesTreeModelCreated();
+
+ const CookieTreeNode* node = model_util_->GetTreeNodeFromPath(
+ cookies_tree_model_->GetRoot(), node_path);
+ if (node)
+ cookies_tree_model_->DeleteCookieNode(const_cast<CookieTreeNode*>(node));
+}
+
+void CookiesViewHandler::LoadChildren(const base::ListValue* args) {
+ std::string node_path;
+ if (!args->GetString(0, &node_path))
+ return;
+
+ EnsureCookiesTreeModelCreated();
+
+ const CookieTreeNode* node = model_util_->GetTreeNodeFromPath(
+ cookies_tree_model_->GetRoot(), node_path);
+ if (node)
+ SendChildren(node);
+}
+
+void CookiesViewHandler::SendChildren(const CookieTreeNode* parent) {
+ std::unique_ptr<base::ListValue> children(new base::ListValue);
+ model_util_->GetChildNodeList(parent, /*start=*/0, parent->child_count(),
+ /*include_quota_nodes=*/true, children.get());
+
+ base::ListValue args;
+ if (parent == cookies_tree_model_->GetRoot())
+ args.Append(base::MakeUnique<base::Value>());
+ else
+ args.AppendString(model_util_->GetTreeNodeId(parent));
+ args.Append(std::move(children));
+
+ web_ui()->CallJavascriptFunctionUnsafe("CookiesView.loadChildren", args);
+}
+
+void CookiesViewHandler::ReloadCookies(const base::ListValue* args) {
+ cookies_tree_model_.reset();
+
+ EnsureCookiesTreeModelCreated();
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/cookies_view_handler.h b/chromium/chrome/browser/ui/webui/options/cookies_view_handler.h
new file mode 100644
index 00000000000..4e440ae49c1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/cookies_view_handler.h
@@ -0,0 +1,81 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_COOKIES_VIEW_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_COOKIES_VIEW_HANDLER_H_
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/browsing_data/cookies_tree_model.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+class CookiesTreeModelUtil;
+
+namespace options {
+
+class CookiesViewHandler : public OptionsPageUIHandler,
+ public CookiesTreeModel::Observer {
+ public:
+ CookiesViewHandler();
+ ~CookiesViewHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void RegisterMessages() override;
+
+ // CookiesTreeModel::Observer implementation.
+ void TreeNodesAdded(ui::TreeModel* model,
+ ui::TreeModelNode* parent,
+ int start,
+ int count) override;
+ void TreeNodesRemoved(ui::TreeModel* model,
+ ui::TreeModelNode* parent,
+ int start,
+ int count) override;
+ void TreeNodeChanged(ui::TreeModel* model, ui::TreeModelNode* node) override {
+ }
+ void TreeModelBeginBatch(CookiesTreeModel* model) override;
+ void TreeModelEndBatch(CookiesTreeModel* model) override;
+
+ private:
+ // Creates the CookiesTreeModel if neccessary.
+ void EnsureCookiesTreeModelCreated();
+
+ // Updates search filter for cookies tree model.
+ void UpdateSearchResults(const base::ListValue* args);
+
+ // Remove all sites data.
+ void RemoveAll(const base::ListValue* args);
+
+ // Remove selected sites data.
+ void Remove(const base::ListValue* args);
+
+ // Get the tree node using the tree path info in |args| and call
+ // SendChildren to pass back children nodes data to WebUI.
+ void LoadChildren(const base::ListValue* args);
+
+ // Get children nodes data and pass it to 'CookiesView.loadChildren' to
+ // update the WebUI.
+ void SendChildren(const CookieTreeNode* parent);
+
+ // Reloads the CookiesTreeModel and passes the nodes to
+ // 'CookiesView.loadChildren' to update the WebUI.
+ void ReloadCookies(const base::ListValue* args);
+
+ // The Cookies Tree model
+ std::unique_ptr<CookiesTreeModel> cookies_tree_model_;
+
+ // Flag to indicate whether there is a batch update in progress.
+ bool batch_update_;
+
+ std::unique_ptr<CookiesTreeModelUtil> model_util_;
+
+ DISALLOW_COPY_AND_ASSIGN(CookiesViewHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_COOKIES_VIEW_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/core_options_handler.cc b/chromium/chrome/browser/ui/webui/options/core_options_handler.cc
new file mode 100644
index 00000000000..ec45725bbe2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/core_options_handler.cc
@@ -0,0 +1,665 @@
+// 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/ui/webui/options/core_options_handler.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/locale_settings.h"
+#include "components/proxy_config/proxy_config_pref_names.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/url_formatter/url_fixer.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/browser/extension_pref_value_map.h"
+#include "extensions/browser/extension_pref_value_map_factory.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+using base::UserMetricsAction;
+
+namespace options {
+
+namespace {
+
+// Whether "controlledBy" property of pref value sent to options web UI needs to
+// be set to "extension" when the preference is controlled by an extension.
+bool CanSetExtensionControlledPrefValue(
+ const PrefService::Preference* preference) {
+#if defined(OS_WIN)
+ // These have more obvious UI than the standard one for extension controlled
+ // values (an extension puzzle piece) on the settings page. To avoiding
+ // showing the extension puzzle piece for these settings, their "controlledBy"
+ // value should never be set to "extension".
+ return preference->name() != prefs::kURLsToRestoreOnStartup &&
+ preference->name() != prefs::kRestoreOnStartup &&
+ preference->name() != prefs::kHomePage &&
+ preference->name() != prefs::kHomePageIsNewTabPage;
+#else
+ return true;
+#endif
+}
+
+} // namespace
+
+CoreOptionsHandler::CoreOptionsHandler()
+ : handlers_host_(NULL) {
+}
+
+CoreOptionsHandler::~CoreOptionsHandler() {}
+
+void CoreOptionsHandler::InitializeHandler() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+ plugin_status_pref_setter_.Init(
+ profile,
+ base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
+ base::Unretained(this),
+ profile->GetPrefs()));
+
+ pref_change_filters_[prefs::kBrowserGuestModeEnabled] =
+ base::Bind(&CoreOptionsHandler::IsUserUnsupervised,
+ base::Unretained(this));
+ pref_change_filters_[prefs::kBrowserAddPersonEnabled] =
+ base::Bind(&CoreOptionsHandler::IsUserUnsupervised,
+ base::Unretained(this));
+}
+
+void CoreOptionsHandler::InitializePage() {
+ UpdateClearPluginLSOData();
+ UpdatePepperFlashSettingsEnabled();
+}
+
+void CoreOptionsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ GetStaticLocalizedValues(localized_strings);
+}
+
+void CoreOptionsHandler::GetStaticLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+ // Main
+ localized_strings->SetString("optionsPageTitle",
+ l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE));
+
+ // Controlled settings bubble.
+ localized_strings->SetString("controlledSettingPolicy",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_POLICY));
+ localized_strings->SetString("controlledSettingExtension",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_EXTENSION));
+ localized_strings->SetString("controlledSettingExtensionWithName",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_CONTROLLED_SETTING_EXTENSION_WITH_NAME));
+ localized_strings->SetString("controlledSettingManageExtension",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_CONTROLLED_SETTING_MANAGE_EXTENSION));
+ localized_strings->SetString("controlledSettingDisableExtension",
+ l10n_util::GetStringUTF16(IDS_EXTENSIONS_DISABLE));
+ localized_strings->SetString("controlledSettingRecommended",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_RECOMMENDED));
+ localized_strings->SetString("controlledSettingHasRecommendation",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_CONTROLLED_SETTING_HAS_RECOMMENDATION));
+ localized_strings->SetString("controlledSettingFollowRecommendation",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_CONTROLLED_SETTING_FOLLOW_RECOMMENDATION));
+ localized_strings->SetString("controlledSettingsPolicy",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTINGS_POLICY));
+ localized_strings->SetString("controlledSettingsExtension",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTINGS_EXTENSION));
+ localized_strings->SetString("controlledSettingsExtensionWithName",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_CONTROLLED_SETTINGS_EXTENSION_WITH_NAME));
+
+ // Search
+ RegisterTitle(localized_strings, "searchPage", IDS_OPTIONS_SEARCH_PAGE_TITLE);
+ localized_strings->SetString("searchPlaceholder",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PLACEHOLDER));
+ localized_strings->SetString("searchPageNoMatches",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PAGE_NO_MATCHES));
+ localized_strings->SetString("searchPageHelpLabel",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PAGE_HELP_LABEL));
+ localized_strings->SetString("searchPageHelpTitle",
+ l10n_util::GetStringFUTF16(IDS_OPTIONS_SEARCH_PAGE_HELP_TITLE,
+ l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
+ localized_strings->SetString("searchPageHelpURL",
+ chrome::kSettingsSearchHelpURL);
+
+ // About
+ localized_strings->SetString("aboutButton",
+ l10n_util::GetStringUTF16(IDS_ABOUT_BUTTON));
+
+ // Common
+ localized_strings->SetString("ok",
+ l10n_util::GetStringUTF16(IDS_OK));
+ localized_strings->SetString("cancel",
+ l10n_util::GetStringUTF16(IDS_CANCEL));
+ localized_strings->SetString("learnMore",
+ l10n_util::GetStringUTF16(IDS_LEARN_MORE));
+ localized_strings->SetString("close",
+ l10n_util::GetStringUTF16(IDS_CLOSE));
+ localized_strings->SetString("done",
+ l10n_util::GetStringUTF16(IDS_DONE));
+ localized_strings->SetString("deletableItemDeleteButtonTitle",
+ l10n_util::GetStringUTF16(IDS_OPTIONS_DELETABLE_ITEM_DELETE_BUTTON));
+}
+
+void CoreOptionsHandler::Uninitialize() {
+ std::string last_pref;
+ for (PreferenceCallbackMap::const_iterator iter = pref_callback_map_.begin();
+ iter != pref_callback_map_.end();
+ ++iter) {
+ if (last_pref != iter->first) {
+ StopObservingPref(iter->first);
+ last_pref = iter->first;
+ }
+ }
+}
+
+void CoreOptionsHandler::OnPreferenceChanged(PrefService* service,
+ const std::string& pref_name) {
+ if (pref_name == prefs::kClearPluginLSODataEnabled) {
+ // This preference is stored in Local State, not in the user preferences.
+ UpdateClearPluginLSOData();
+ return;
+ }
+ if (pref_name == prefs::kPepperFlashSettingsEnabled) {
+ UpdatePepperFlashSettingsEnabled();
+ return;
+ }
+ NotifyPrefChanged(pref_name, std::string());
+}
+
+void CoreOptionsHandler::RegisterMessages() {
+ registrar_.Init(Profile::FromWebUI(web_ui())->GetPrefs());
+ local_state_registrar_.Init(g_browser_process->local_state());
+
+ web_ui()->RegisterMessageCallback("coreOptionsInitialize",
+ base::Bind(&CoreOptionsHandler::HandleInitialize,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("onFinishedLoadingOptions",
+ base::Bind(&CoreOptionsHandler::OnFinishedLoading,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("fetchPrefs",
+ base::Bind(&CoreOptionsHandler::HandleFetchPrefs,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("observePrefs",
+ base::Bind(&CoreOptionsHandler::HandleObservePrefs,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("setBooleanPref",
+ base::Bind(&CoreOptionsHandler::HandleSetBooleanPref,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("setIntegerPref",
+ base::Bind(&CoreOptionsHandler::HandleSetIntegerPref,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("setDoublePref",
+ base::Bind(&CoreOptionsHandler::HandleSetDoublePref,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("setStringPref",
+ base::Bind(&CoreOptionsHandler::HandleSetStringPref,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("setURLPref",
+ base::Bind(&CoreOptionsHandler::HandleSetURLPref,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("setListPref",
+ base::Bind(&CoreOptionsHandler::HandleSetListPref,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("clearPref",
+ base::Bind(&CoreOptionsHandler::HandleClearPref,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("coreOptionsUserMetricsAction",
+ base::Bind(&CoreOptionsHandler::HandleUserMetricsAction,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("disableExtension",
+ base::Bind(&CoreOptionsHandler::HandleDisableExtension,
+ base::Unretained(this)));
+}
+
+void CoreOptionsHandler::HandleInitialize(const base::ListValue* args) {
+ DCHECK(handlers_host_);
+ handlers_host_->InitializeHandlers();
+}
+
+void CoreOptionsHandler::OnFinishedLoading(const base::ListValue* args) {
+ DCHECK(handlers_host_);
+ handlers_host_->OnFinishedLoading();
+}
+
+std::unique_ptr<base::Value> CoreOptionsHandler::FetchPref(
+ const std::string& pref_name) {
+ return CreateValueForPref(pref_name, std::string());
+}
+
+void CoreOptionsHandler::ObservePref(const std::string& pref_name) {
+ if (g_browser_process->local_state()->FindPreference(pref_name)) {
+ local_state_registrar_.Add(
+ pref_name,
+ base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
+ base::Unretained(this),
+ local_state_registrar_.prefs()));
+ }
+ // TODO(pneubeck): change this to if/else once kProxy is only used as a user
+ // pref. Currently, it is both a user and a local state pref.
+ if (Profile::FromWebUI(web_ui())->GetPrefs()->FindPreference(pref_name)) {
+ registrar_.Add(
+ pref_name,
+ base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
+ base::Unretained(this),
+ registrar_.prefs()));
+ }
+}
+
+void CoreOptionsHandler::StopObservingPref(const std::string& pref_name) {
+ if (g_browser_process->local_state()->FindPreference(pref_name))
+ local_state_registrar_.Remove(pref_name);
+ else
+ registrar_.Remove(pref_name);
+}
+
+void CoreOptionsHandler::SetPref(const std::string& pref_name,
+ const base::Value* value,
+ const std::string& metric) {
+ PrefService* pref_service = FindServiceForPref(pref_name);
+ PrefChangeFilterMap::iterator iter = pref_change_filters_.find(pref_name);
+ if (iter != pref_change_filters_.end()) {
+ // Also check if the pref is user modifiable (don't even try to run the
+ // filter function if the user is not allowed to change the pref).
+ const PrefService::Preference* pref =
+ pref_service->FindPreference(pref_name);
+ if ((pref && !pref->IsUserModifiable()) || !iter->second.Run(value)) {
+ // Reject the change; remind the page of the true value.
+ NotifyPrefChanged(pref_name, std::string());
+ return;
+ }
+ }
+
+ switch (value->GetType()) {
+ case base::Value::Type::BOOLEAN:
+ case base::Value::Type::INTEGER:
+ case base::Value::Type::DOUBLE:
+ case base::Value::Type::STRING:
+ case base::Value::Type::LIST:
+ pref_service->Set(pref_name, *value);
+ break;
+
+ default:
+ NOTREACHED();
+ return;
+ }
+
+ ProcessUserMetric(value, metric);
+}
+
+void CoreOptionsHandler::ClearPref(const std::string& pref_name,
+ const std::string& metric) {
+ PrefService* pref_service = FindServiceForPref(pref_name);
+ pref_service->ClearPref(pref_name);
+
+ if (!metric.empty())
+ base::RecordComputedAction(metric);
+}
+
+void CoreOptionsHandler::ProcessUserMetric(const base::Value* value,
+ const std::string& metric) {
+ if (metric.empty())
+ return;
+
+ std::string metric_string = metric;
+ if (value->IsType(base::Value::Type::BOOLEAN)) {
+ bool bool_value;
+ CHECK(value->GetAsBoolean(&bool_value));
+ metric_string += bool_value ? "_Enable" : "_Disable";
+ }
+
+ base::RecordComputedAction(metric_string);
+}
+
+void CoreOptionsHandler::NotifyPrefChanged(
+ const std::string& pref_name,
+ const std::string& controlling_pref_name) {
+ std::unique_ptr<base::Value> value(
+ CreateValueForPref(pref_name, controlling_pref_name));
+ DispatchPrefChangeNotification(pref_name, std::move(value));
+}
+
+void CoreOptionsHandler::DispatchPrefChangeNotification(
+ const std::string& name,
+ std::unique_ptr<base::Value> value) {
+ std::pair<PreferenceCallbackMap::const_iterator,
+ PreferenceCallbackMap::const_iterator> range =
+ pref_callback_map_.equal_range(name);
+ base::ListValue result_value;
+ result_value.AppendString(name);
+ result_value.Append(std::move(value));
+ for (PreferenceCallbackMap::const_iterator iter = range.first;
+ iter != range.second; ++iter) {
+ const std::string& callback_function = iter->second;
+ web_ui()->CallJavascriptFunctionUnsafe(callback_function, result_value);
+ }
+}
+
+std::unique_ptr<base::Value> CoreOptionsHandler::CreateValueForPref(
+ const std::string& pref_name,
+ const std::string& controlling_pref_name) {
+ const PrefService* pref_service = FindServiceForPref(pref_name);
+ const PrefService::Preference* pref =
+ pref_service->FindPreference(pref_name);
+ if (!pref) {
+ NOTREACHED();
+ return base::MakeUnique<base::Value>();
+ }
+ const PrefService::Preference* controlling_pref =
+ pref_service->FindPreference(controlling_pref_name);
+ if (!controlling_pref)
+ controlling_pref = pref;
+
+ auto dict = base::MakeUnique<base::DictionaryValue>();
+ dict->Set("value", base::MakeUnique<base::Value>(*pref->GetValue()));
+ if (controlling_pref->IsManaged()) {
+ dict->SetString("controlledBy", "policy");
+ } else if (controlling_pref->IsExtensionControlled() &&
+ CanSetExtensionControlledPrefValue(controlling_pref)) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ ExtensionPrefValueMap* extension_pref_value_map =
+ ExtensionPrefValueMapFactory::GetForBrowserContext(profile);
+ std::string extension_id =
+ extension_pref_value_map->GetExtensionControllingPref(
+ controlling_pref->name());
+
+ const extensions::Extension* extension =
+ extensions::ExtensionRegistry::Get(profile)->GetExtensionById(
+ extension_id, extensions::ExtensionRegistry::EVERYTHING);
+ if (extension) {
+ dict->SetString("controlledBy", "extension");
+ dict->Set("extension", extensions::util::GetExtensionInfo(extension));
+ }
+ } else if (controlling_pref->IsRecommended()) {
+ dict->SetString("controlledBy", "recommended");
+ }
+
+ const base::Value* recommended_value =
+ controlling_pref->GetRecommendedValue();
+ if (recommended_value)
+ dict->Set("recommendedValue",
+ base::MakeUnique<base::Value>(*recommended_value));
+ dict->SetBoolean("disabled", !controlling_pref->IsUserModifiable());
+ return std::move(dict);
+}
+
+PrefService* CoreOptionsHandler::FindServiceForPref(
+ const std::string& pref_name) {
+ // Proxy is a peculiar case: on ChromeOS, settings exist in both user
+ // prefs and local state, but chrome://settings should affect only user prefs.
+ // Elsewhere the proxy settings are stored in local state.
+ // See http://crbug.com/157147
+ PrefService* user_prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+ if (pref_name == proxy_config::prefs::kProxy)
+#if defined(OS_CHROMEOS)
+ return user_prefs;
+#else
+ return g_browser_process->local_state();
+#endif
+
+ // Find which PrefService contains the given pref. Pref names should not
+ // be duplicated across services, however if they are, prefer the user's
+ // prefs.
+ if (user_prefs->FindPreference(pref_name))
+ return user_prefs;
+
+ if (g_browser_process->local_state()->FindPreference(pref_name))
+ return g_browser_process->local_state();
+
+ return user_prefs;
+}
+
+void CoreOptionsHandler::HandleFetchPrefs(const base::ListValue* args) {
+ // First param is name of callback function, so, there needs to be at least
+ // one more element for the actual preference identifier.
+ DCHECK_GE(static_cast<int>(args->GetSize()), 2);
+
+ // Get callback JS function name.
+ const base::Value* callback;
+ if (!args->Get(0, &callback) || !callback->IsType(base::Value::Type::STRING))
+ return;
+
+ base::string16 callback_function;
+ if (!callback->GetAsString(&callback_function))
+ return;
+
+ // Get the list of name for prefs to build the response dictionary.
+ base::DictionaryValue result_value;
+ const base::Value* list_member;
+
+ for (size_t i = 1; i < args->GetSize(); i++) {
+ if (!args->Get(i, &list_member))
+ break;
+
+ if (!list_member->IsType(base::Value::Type::STRING))
+ continue;
+
+ std::string pref_name;
+ if (!list_member->GetAsString(&pref_name))
+ continue;
+
+ result_value.Set(pref_name, FetchPref(pref_name));
+ }
+ web_ui()->CallJavascriptFunctionUnsafe(base::UTF16ToASCII(callback_function),
+ result_value);
+}
+
+void CoreOptionsHandler::HandleObservePrefs(const base::ListValue* args) {
+ // First param is name is JS callback function name, the rest are pref
+ // identifiers that we are observing.
+ DCHECK_GE(static_cast<int>(args->GetSize()), 2);
+
+ // Get preference change callback function name.
+ std::string callback_func_name;
+ if (!args->GetString(0, &callback_func_name))
+ return;
+
+ // Get all other parameters - pref identifiers.
+ for (size_t i = 1; i < args->GetSize(); i++) {
+ const base::Value* list_member;
+ if (!args->Get(i, &list_member))
+ break;
+
+ // Just ignore bad pref identifiers for now.
+ std::string pref_name;
+ if (!list_member->IsType(base::Value::Type::STRING) ||
+ !list_member->GetAsString(&pref_name))
+ continue;
+
+ if (pref_callback_map_.find(pref_name) == pref_callback_map_.end())
+ ObservePref(pref_name);
+
+ pref_callback_map_.insert(
+ PreferenceCallbackMap::value_type(pref_name, callback_func_name));
+ }
+}
+
+void CoreOptionsHandler::HandleSetBooleanPref(const base::ListValue* args) {
+ HandleSetPref(args, TYPE_BOOLEAN);
+}
+
+void CoreOptionsHandler::HandleSetIntegerPref(const base::ListValue* args) {
+ HandleSetPref(args, TYPE_INTEGER);
+}
+
+void CoreOptionsHandler::HandleSetDoublePref(const base::ListValue* args) {
+ HandleSetPref(args, TYPE_DOUBLE);
+}
+
+void CoreOptionsHandler::HandleSetStringPref(const base::ListValue* args) {
+ HandleSetPref(args, TYPE_STRING);
+}
+
+void CoreOptionsHandler::HandleSetURLPref(const base::ListValue* args) {
+ HandleSetPref(args, TYPE_URL);
+}
+
+void CoreOptionsHandler::HandleSetListPref(const base::ListValue* args) {
+ HandleSetPref(args, TYPE_LIST);
+}
+
+void CoreOptionsHandler::HandleSetPref(const base::ListValue* args,
+ PrefType type) {
+ DCHECK_GT(static_cast<int>(args->GetSize()), 1);
+
+ std::string pref_name;
+ if (!args->GetString(0, &pref_name))
+ return;
+
+ const base::Value* value;
+ if (!args->Get(1, &value))
+ return;
+
+ std::unique_ptr<base::Value> temp_value;
+
+ switch (type) {
+ case TYPE_BOOLEAN:
+ if (!value->IsType(base::Value::Type::BOOLEAN)) {
+ NOTREACHED();
+ return;
+ }
+ break;
+ case TYPE_INTEGER: {
+ // In JS all numbers are doubles.
+ double double_value;
+ if (!value->GetAsDouble(&double_value)) {
+ NOTREACHED();
+ return;
+ }
+ int int_value = static_cast<int>(double_value);
+ temp_value.reset(new base::Value(int_value));
+ value = temp_value.get();
+ break;
+ }
+ case TYPE_DOUBLE:
+ if (!value->IsType(base::Value::Type::DOUBLE)) {
+ NOTREACHED();
+ return;
+ }
+ break;
+ case TYPE_STRING:
+ if (!value->IsType(base::Value::Type::STRING)) {
+ NOTREACHED();
+ return;
+ }
+ break;
+ case TYPE_URL: {
+ std::string original;
+ if (!value->GetAsString(&original)) {
+ NOTREACHED();
+ return;
+ }
+ GURL fixed = url_formatter::FixupURL(original, std::string());
+ temp_value.reset(new base::Value(fixed.spec()));
+ value = temp_value.get();
+ break;
+ }
+ case TYPE_LIST: {
+ // In case we have a List pref we got a JSON string.
+ std::string json_string;
+ if (!value->GetAsString(&json_string)) {
+ NOTREACHED();
+ return;
+ }
+ temp_value = base::JSONReader::Read(json_string);
+ value = temp_value.get();
+ if (!value || !value->IsType(base::Value::Type::LIST)) {
+ NOTREACHED();
+ return;
+ }
+ break;
+ }
+ default:
+ NOTREACHED();
+ }
+
+ std::string metric;
+ if (args->GetSize() > 2 && !args->GetString(2, &metric))
+ LOG(WARNING) << "Invalid metric parameter: " << pref_name;
+ SetPref(pref_name, value, metric);
+}
+
+void CoreOptionsHandler::HandleClearPref(const base::ListValue* args) {
+ DCHECK_GT(static_cast<int>(args->GetSize()), 0);
+
+ std::string pref_name;
+ if (!args->GetString(0, &pref_name))
+ return;
+
+ std::string metric;
+ if (args->GetSize() > 1) {
+ if (!args->GetString(1, &metric))
+ NOTREACHED();
+ }
+
+ ClearPref(pref_name, metric);
+}
+
+void CoreOptionsHandler::HandleUserMetricsAction(const base::ListValue* args) {
+ std::string metric = base::UTF16ToUTF8(ExtractStringValue(args));
+ if (!metric.empty())
+ base::RecordComputedAction(metric);
+}
+
+void CoreOptionsHandler::HandleDisableExtension(const base::ListValue* args) {
+ std::string extension_id;
+ if (args->GetString(0, &extension_id)) {
+ ExtensionService* extension_service = extensions::ExtensionSystem::Get(
+ Profile::FromWebUI(web_ui()))->extension_service();
+ DCHECK(extension_service);
+ extension_service->DisableExtension(
+ extension_id, extensions::Extension::DISABLE_USER_ACTION);
+ } else {
+ NOTREACHED();
+ }
+}
+
+void CoreOptionsHandler::UpdateClearPluginLSOData() {
+ base::Value enabled(plugin_status_pref_setter_.IsClearPluginLSODataEnabled());
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.OptionsPage.setClearPluginLSODataEnabled", enabled);
+}
+
+void CoreOptionsHandler::UpdatePepperFlashSettingsEnabled() {
+ base::Value enabled(
+ plugin_status_pref_setter_.IsPepperFlashSettingsEnabled());
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.OptionsPage.setPepperFlashSettingsEnabled", enabled);
+}
+
+bool CoreOptionsHandler::IsUserUnsupervised(const base::Value* to_value) {
+ return !Profile::FromWebUI(web_ui())->IsSupervised();
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/core_options_handler.h b/chromium/chrome/browser/ui/webui/options/core_options_handler.h
new file mode 100644
index 00000000000..1ff2c16f664
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/core_options_handler.h
@@ -0,0 +1,191 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CORE_OPTIONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CORE_OPTIONS_HANDLER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "chrome/browser/plugins/plugin_status_pref_setter.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_service.h"
+
+namespace options {
+
+// Core options UI handler.
+// Handles resource and JS calls common to all options sub-pages.
+class CoreOptionsHandler : public OptionsPageUIHandler {
+ public:
+ CoreOptionsHandler();
+ ~CoreOptionsHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializeHandler() override;
+ void InitializePage() override;
+ void Uninitialize() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ void set_handlers_host(OptionsPageUIHandlerHost* handlers_host) {
+ handlers_host_ = handlers_host;
+ }
+
+ // Adds localized strings to |localized_strings|.
+ static void GetStaticLocalizedValues(
+ base::DictionaryValue* localized_strings);
+
+ protected:
+ // Fetches a pref value of given |pref_name|.
+ // Note that caller owns the returned Value.
+ virtual std::unique_ptr<base::Value> FetchPref(const std::string& pref_name);
+
+ // Observes a pref of given |pref_name|.
+ virtual void ObservePref(const std::string& pref_name);
+
+ // Stops observing given preference identified by |pref_name|.
+ virtual void StopObservingPref(const std::string& pref_name);
+
+ // Sets a pref |value| to given |pref_name|.
+ virtual void SetPref(const std::string& pref_name,
+ const base::Value* value,
+ const std::string& metric);
+
+ // Clears pref value for given |pref_name|.
+ void ClearPref(const std::string& pref_name, const std::string& metric);
+
+ // Records a user metric action for the given value.
+ void ProcessUserMetric(const base::Value* value,
+ const std::string& metric);
+
+ // Virtual dispatch is needed as handling of some prefs may be
+ // finessed in subclasses. The PrefService pointer is included
+ // so that subclasses can know whether the observed pref is from the
+ // local state or not.
+ virtual void OnPreferenceChanged(PrefService* service,
+ const std::string& pref_name);
+
+ // Notifies registered JS callbacks on change in |pref_name| preference.
+ // |controlling_pref_name| controls if |pref_name| is managed by
+ // policy/extension; empty |controlling_pref_name| indicates no other pref is
+ // controlling |pref_name|.
+ void NotifyPrefChanged(const std::string& pref_name,
+ const std::string& controlling_pref_name);
+
+ // Calls JS callbacks to report a change in the value of the |name|
+ // preference. |value| is the new value for |name|. Called from
+ // Notify*Changed methods to fire off the notifications.
+ void DispatchPrefChangeNotification(const std::string& name,
+ std::unique_ptr<base::Value> value);
+
+ // Creates dictionary value for the pref described by |pref_name|.
+ // If |controlling_pref| is not empty, it describes the pref that manages
+ // |pref| via policy or extension.
+ virtual std::unique_ptr<base::Value> CreateValueForPref(
+ const std::string& pref_name,
+ const std::string& controlling_pref_name);
+
+ typedef std::multimap<std::string, std::string> PreferenceCallbackMap;
+ PreferenceCallbackMap pref_callback_map_;
+
+ private:
+ // Type of preference value received from the page. This doesn't map 1:1 to
+ // Value::Type, since a TYPE_STRING can require custom processing.
+ enum PrefType {
+ TYPE_BOOLEAN = 0,
+ TYPE_INTEGER,
+ TYPE_DOUBLE,
+ TYPE_STRING,
+ TYPE_URL,
+ TYPE_LIST,
+ };
+
+ // Finds the pref service that holds the given pref. If the pref is not found,
+ // it will return user prefs.
+ PrefService* FindServiceForPref(const std::string& pref_name);
+
+ // Callback for the "coreOptionsInitialize" message. This message will
+ // trigger the Initialize() method of all other handlers so that final
+ // setup can be performed before the page is shown.
+ void HandleInitialize(const base::ListValue* args);
+
+ // Callback for the "onFinishedLoadingOptions" message. This message is sent
+ // when the load() handler for the options frame, along with all asynchronous
+ // calls it has spawned, have finished running.
+ void OnFinishedLoading(const base::ListValue* args);
+
+ // Callback for the "fetchPrefs" message. This message accepts the list of
+ // preference names passed as the |args| parameter (ListValue). It passes
+ // results dictionary of preference values by calling prefsFetched() JS method
+ // on the page.
+ void HandleFetchPrefs(const base::ListValue* args);
+
+ // Callback for the "observePrefs" message. This message initiates
+ // notification observing for given array of preference names.
+ void HandleObservePrefs(const base::ListValue* args);
+
+ // Callbacks for the "set<type>Pref" message. This message saves the new
+ // preference value. |args| is an array of parameters as follows:
+ // item 0 - name of the preference.
+ // item 1 - the value of the preference in string form.
+ // item 2 - name of the metric identifier (optional).
+ void HandleSetBooleanPref(const base::ListValue* args);
+ void HandleSetIntegerPref(const base::ListValue* args);
+ void HandleSetDoublePref(const base::ListValue* args);
+ void HandleSetStringPref(const base::ListValue* args);
+ void HandleSetURLPref(const base::ListValue* args);
+ void HandleSetListPref(const base::ListValue* args);
+
+ void HandleSetPref(const base::ListValue* args, PrefType type);
+
+ // Callback for the "clearPref" message. This message clears a preference
+ // value. |args| is an array of parameters as follows:
+ // item 0 - name of the preference.
+ // item 1 - name of the metric identifier (optional).
+ void HandleClearPref(const base::ListValue* args);
+
+ // Callback for the "coreOptionsUserMetricsAction" message. This records
+ // an action that should be tracked if metrics recording is enabled. |args|
+ // is an array that contains a single item, the name of the metric identifier.
+ void HandleUserMetricsAction(const base::ListValue* args);
+
+ // Callback for the "disableExtension" message. The extension ID string is the
+ // only argument in the |args| list.
+ void HandleDisableExtension(const base::ListValue* args);
+
+ void UpdateClearPluginLSOData();
+ void UpdatePepperFlashSettingsEnabled();
+
+ // Checks that the current profile is not supervised. Used as a pref filter.
+ bool IsUserUnsupervised(const base::Value* to_value);
+
+ OptionsPageUIHandlerHost* handlers_host_;
+ // This registrar keeps track of user prefs.
+ PrefChangeRegistrar registrar_;
+ // This registrar keeps track of local state.
+ PrefChangeRegistrar local_state_registrar_;
+
+ PluginStatusPrefSetter plugin_status_pref_setter_;
+
+ // This maps pref names to filter functions. The callbacks should take the
+ // value that the user has attempted to set for the pref, and should return
+ // true if that value may be applied. If the return value is false, the
+ // change will be ignored.
+ typedef std::map<std::string, base::Callback<bool(const base::Value*)> >
+ PrefChangeFilterMap;
+ PrefChangeFilterMap pref_change_filters_;
+
+ DISALLOW_COPY_AND_ASSIGN(CoreOptionsHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CORE_OPTIONS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/create_profile_handler.cc b/chromium/chrome/browser/ui/webui/options/create_profile_handler.cc
new file mode 100644
index 00000000000..f820a9943e0
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/create_profile_handler.cc
@@ -0,0 +1,483 @@
+// 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/options/create_profile_handler.h"
+
+#include <stddef.h>
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_util.h"
+#include "base/value_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_avatar_icon_util.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_metrics.h"
+#include "chrome/browser/profiles/profiles_state.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/ui/webui/profile_helper.h"
+#include "chrome/common/features.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+#include "chrome/browser/supervised_user/legacy/supervised_user_registration_utility.h"
+#include "chrome/browser/supervised_user/legacy/supervised_user_sync_service.h"
+#include "chrome/browser/supervised_user/legacy/supervised_user_sync_service_factory.h"
+#include "chrome/browser/supervised_user/supervised_user_service.h"
+#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
+#endif
+
+namespace options {
+
+CreateProfileHandler::CreateProfileHandler()
+ : profile_creation_type_(NO_CREATION_IN_PROGRESS),
+ weak_ptr_factory_(this) {
+}
+
+CreateProfileHandler::~CreateProfileHandler() {
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ // Cancellation is only supported for supervised users.
+ CancelProfileRegistration(false);
+#endif
+}
+
+void CreateProfileHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+}
+
+void CreateProfileHandler::RegisterMessages() {
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ // Cancellation is only supported for supervised users.
+ web_ui()->RegisterMessageCallback(
+ "cancelCreateProfile",
+ base::Bind(&CreateProfileHandler::HandleCancelProfileCreation,
+ base::Unretained(this)));
+#endif
+ web_ui()->RegisterMessageCallback(
+ "createProfile",
+ base::Bind(&CreateProfileHandler::CreateProfile,
+ base::Unretained(this)));
+}
+
+void CreateProfileHandler::CreateProfile(const base::ListValue* args) {
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ // This handler could have been called for a supervised user, for example
+ // because the user fiddled with the web inspector. Silently return.
+ if (Profile::FromWebUI(web_ui())->IsSupervised())
+ return;
+#endif
+
+ if (!profiles::IsMultipleProfilesEnabled())
+ return;
+
+ // We can have only one in progress profile creation
+ // at any given moment, if new ones are initiated just
+ // ignore them until we are done with the old one.
+ if (profile_creation_type_ != NO_CREATION_IN_PROGRESS)
+ return;
+
+ profile_creation_type_ = NON_SUPERVISED_PROFILE_CREATION;
+
+ DCHECK(profile_path_being_created_.empty());
+ profile_creation_start_time_ = base::TimeTicks::Now();
+
+ base::string16 name;
+ std::string icon_url;
+ bool create_shortcut = false;
+ if (args->GetString(0, &name) && args->GetString(1, &icon_url)) {
+ DCHECK(base::IsStringASCII(icon_url));
+ base::TrimWhitespace(name, base::TRIM_ALL, &name);
+ CHECK(!name.empty());
+#ifndef NDEBUG
+ size_t icon_index;
+ DCHECK(profiles::IsDefaultAvatarIconUrl(icon_url, &icon_index));
+#endif
+ args->GetBoolean(2, &create_shortcut);
+ }
+ std::string supervised_user_id;
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ if (!ProcessSupervisedCreateProfileArgs(args, &supervised_user_id))
+ return;
+#endif
+
+ ProfileMetrics::LogProfileAddNewUser(ProfileMetrics::ADD_NEW_USER_DIALOG);
+
+ profile_path_being_created_ = ProfileManager::CreateMultiProfileAsync(
+ name, icon_url, base::Bind(&CreateProfileHandler::OnProfileCreated,
+ weak_ptr_factory_.GetWeakPtr(),
+ create_shortcut, supervised_user_id),
+ supervised_user_id);
+}
+
+void CreateProfileHandler::OnProfileCreated(
+ bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* profile,
+ Profile::CreateStatus status) {
+ if (status != Profile::CREATE_STATUS_CREATED)
+ RecordProfileCreationMetrics(status);
+
+ switch (status) {
+ case Profile::CREATE_STATUS_LOCAL_FAIL: {
+ ShowProfileCreationError(profile, GetProfileCreationErrorMessageLocal());
+ break;
+ }
+ case Profile::CREATE_STATUS_CREATED: {
+ // Do nothing for an intermediate status.
+ break;
+ }
+ case Profile::CREATE_STATUS_INITIALIZED: {
+ HandleProfileCreationSuccess(create_shortcut, supervised_user_id,
+ profile);
+ break;
+ }
+ // User-initiated cancellation is handled in CancelProfileRegistration and
+ // does not call this callback.
+ case Profile::CREATE_STATUS_CANCELED:
+ // Supervised user registration errors are handled in
+ // OnSupervisedUserRegistered().
+ case Profile::CREATE_STATUS_REMOTE_FAIL:
+ case Profile::MAX_CREATE_STATUS: {
+ NOTREACHED();
+ break;
+ }
+ }
+}
+
+void CreateProfileHandler::HandleProfileCreationSuccess(
+ bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* profile) {
+ switch (profile_creation_type_) {
+ case NON_SUPERVISED_PROFILE_CREATION: {
+ DCHECK(supervised_user_id.empty());
+ CreateShortcutAndShowSuccess(create_shortcut, profile);
+ break;
+ }
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ case SUPERVISED_PROFILE_CREATION:
+ case SUPERVISED_PROFILE_IMPORT:
+ RegisterSupervisedUser(create_shortcut, supervised_user_id, profile);
+ break;
+#endif
+ case NO_CREATION_IN_PROGRESS:
+ NOTREACHED();
+ break;
+ }
+}
+
+void CreateProfileHandler::CreateShortcutAndShowSuccess(bool create_shortcut,
+ Profile* profile) {
+ if (create_shortcut) {
+ ProfileShortcutManager* shortcut_manager =
+ g_browser_process->profile_manager()->profile_shortcut_manager();
+
+ if (shortcut_manager)
+ shortcut_manager->CreateProfileShortcut(profile->GetPath());
+ }
+
+ DCHECK_EQ(profile_path_being_created_.value(), profile->GetPath().value());
+ profile_path_being_created_.clear();
+ DCHECK_NE(NO_CREATION_IN_PROGRESS, profile_creation_type_);
+ base::DictionaryValue dict;
+ dict.SetString("name",
+ profile->GetPrefs()->GetString(prefs::kProfileName));
+ dict.Set("filePath", base::CreateFilePathValue(profile->GetPath()));
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ bool is_supervised =
+ profile_creation_type_ == SUPERVISED_PROFILE_CREATION ||
+ profile_creation_type_ == SUPERVISED_PROFILE_IMPORT;
+ dict.SetBoolean("isSupervised", is_supervised);
+#endif
+ web_ui()->CallJavascriptFunctionUnsafe(
+ GetJavascriptMethodName(PROFILE_CREATION_SUCCESS), dict);
+
+ // If the new profile is a supervised user, instead of opening a new window
+ // right away, a confirmation overlay will be shown by JS from the creation
+ // dialog. If we are importing an existing supervised profile or creating a
+ // new non-supervised user profile we don't show any confirmation, so open
+ // the new window now.
+ bool should_open_new_window = true;
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ if (profile_creation_type_ == SUPERVISED_PROFILE_CREATION)
+ should_open_new_window = false;
+#endif
+
+ if (should_open_new_window) {
+ // Opening the new window must be the last action, after all callbacks
+ // have been run, to give them a chance to initialize the profile.
+ webui::OpenNewWindowForProfile(profile);
+ }
+ profile_creation_type_ = NO_CREATION_IN_PROGRESS;
+}
+
+void CreateProfileHandler::ShowProfileCreationError(
+ Profile* profile,
+ const base::string16& error) {
+ DCHECK_NE(NO_CREATION_IN_PROGRESS, profile_creation_type_);
+ profile_creation_type_ = NO_CREATION_IN_PROGRESS;
+ profile_path_being_created_.clear();
+ web_ui()->CallJavascriptFunctionUnsafe(
+ GetJavascriptMethodName(PROFILE_CREATION_ERROR), base::Value(error));
+ // The ProfileManager calls us back with a NULL profile in some cases.
+ if (profile) {
+ webui::DeleteProfileAtPath(profile->GetPath(),
+ web_ui(),
+ ProfileMetrics::DELETE_PROFILE_SETTINGS);
+ }
+}
+
+void CreateProfileHandler::RecordProfileCreationMetrics(
+ Profile::CreateStatus status) {
+ UMA_HISTOGRAM_ENUMERATION("Profile.CreateResult",
+ status,
+ Profile::MAX_CREATE_STATUS);
+ UMA_HISTOGRAM_MEDIUM_TIMES(
+ "Profile.CreateTimeNoTimeout",
+ base::TimeTicks::Now() - profile_creation_start_time_);
+}
+
+base::string16 CreateProfileHandler::GetProfileCreationErrorMessageLocal()
+ const {
+ int message_id = IDS_PROFILES_CREATE_LOCAL_ERROR;
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ // Local errors can occur during supervised profile import.
+ if (profile_creation_type_ == SUPERVISED_PROFILE_IMPORT)
+ message_id = IDS_LEGACY_SUPERVISED_USER_IMPORT_LOCAL_ERROR;
+#endif
+ return l10n_util::GetStringUTF16(message_id);
+}
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+base::string16 CreateProfileHandler::GetProfileCreationErrorMessageRemote()
+ const {
+ return l10n_util::GetStringUTF16(
+ profile_creation_type_ == SUPERVISED_PROFILE_IMPORT ?
+ IDS_LEGACY_SUPERVISED_USER_IMPORT_REMOTE_ERROR :
+ IDS_PROFILES_CREATE_REMOTE_ERROR);
+}
+
+base::string16 CreateProfileHandler::GetProfileCreationErrorMessageSignin()
+ const {
+ return l10n_util::GetStringUTF16(
+ profile_creation_type_ == SUPERVISED_PROFILE_IMPORT ?
+ IDS_LEGACY_SUPERVISED_USER_IMPORT_SIGN_IN_ERROR :
+ IDS_PROFILES_CREATE_SIGN_IN_ERROR);
+}
+#endif
+
+std::string CreateProfileHandler::GetJavascriptMethodName(
+ ProfileCreationStatus status) const {
+ switch (profile_creation_type_) {
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ case SUPERVISED_PROFILE_IMPORT:
+ switch (status) {
+ case PROFILE_CREATION_SUCCESS:
+ return "BrowserOptions.showSupervisedUserImportSuccess";
+ case PROFILE_CREATION_ERROR:
+ return "BrowserOptions.showSupervisedUserImportError";
+ }
+ break;
+#endif
+ default:
+ switch (status) {
+ case PROFILE_CREATION_SUCCESS:
+ return "BrowserOptions.showCreateProfileSuccess";
+ case PROFILE_CREATION_ERROR:
+ return "BrowserOptions.showCreateProfileError";
+ }
+ break;
+ }
+
+ NOTREACHED();
+ return std::string();
+}
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+bool CreateProfileHandler::ProcessSupervisedCreateProfileArgs(
+ const base::ListValue* args, std::string* supervised_user_id) {
+ bool supervised_user = false;
+ if (args->GetSize() >= 5) {
+ bool success = args->GetBoolean(3, &supervised_user);
+ DCHECK(success);
+
+ success = args->GetString(4, supervised_user_id);
+ DCHECK(success);
+ }
+
+ if (supervised_user) {
+ if (!IsValidExistingSupervisedUserId(*supervised_user_id))
+ return false;
+
+ profile_creation_type_ = SUPERVISED_PROFILE_IMPORT;
+ if (supervised_user_id->empty()) {
+ profile_creation_type_ = SUPERVISED_PROFILE_CREATION;
+ *supervised_user_id =
+ SupervisedUserRegistrationUtility::GenerateNewSupervisedUserId();
+
+ // If sync is not yet fully initialized, the creation may take extra time,
+ // so show a message. Import doesn't wait for an acknowledgment, so it
+ // won't have the same potential delay.
+ browser_sync::ProfileSyncService* sync_service =
+ ProfileSyncServiceFactory::GetInstance()->GetForProfile(
+ Profile::FromWebUI(web_ui()));
+ browser_sync::ProfileSyncService::SyncStatusSummary status =
+ sync_service->QuerySyncStatusSummary();
+ if (status ==
+ browser_sync::ProfileSyncService::DATATYPES_NOT_INITIALIZED) {
+ ShowProfileCreationWarning(l10n_util::GetStringUTF16(
+ IDS_PROFILES_CREATE_SUPERVISED_JUST_SIGNED_IN));
+ }
+ }
+ }
+ return true;
+}
+
+void CreateProfileHandler::HandleCancelProfileCreation(
+ const base::ListValue* args) {
+ CancelProfileRegistration(true);
+}
+
+// Non-supervised user creation cannot be canceled. (Creating a non-supervised
+// profile shouldn't take significant time, and it can easily be deleted
+// afterward.)
+void CreateProfileHandler::CancelProfileRegistration(bool user_initiated) {
+ if (profile_path_being_created_.empty())
+ return;
+
+ ProfileManager* manager = g_browser_process->profile_manager();
+ Profile* new_profile = manager->GetProfileByPath(profile_path_being_created_);
+ if (!new_profile || !new_profile->IsSupervised())
+ return;
+
+ DCHECK(supervised_user_registration_utility_.get());
+ supervised_user_registration_utility_.reset();
+
+ if (user_initiated) {
+ UMA_HISTOGRAM_MEDIUM_TIMES(
+ "Profile.CreateTimeCanceledNoTimeout",
+ base::TimeTicks::Now() - profile_creation_start_time_);
+ RecordProfileCreationMetrics(Profile::CREATE_STATUS_CANCELED);
+ }
+
+ DCHECK_NE(NO_CREATION_IN_PROGRESS, profile_creation_type_);
+ profile_creation_type_ = NO_CREATION_IN_PROGRESS;
+
+ // Cancelling registration means the callback passed into
+ // 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);
+}
+
+void CreateProfileHandler::RegisterSupervisedUser(
+ bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* new_profile) {
+ DCHECK_EQ(profile_path_being_created_.value(),
+ new_profile->GetPath().value());
+
+ SupervisedUserService* supervised_user_service =
+ SupervisedUserServiceFactory::GetForProfile(new_profile);
+
+ // Register the supervised user using the profile of the custodian.
+ supervised_user_registration_utility_ =
+ SupervisedUserRegistrationUtility::Create(Profile::FromWebUI(web_ui()));
+ supervised_user_service->RegisterAndInitSync(
+ supervised_user_registration_utility_.get(),
+ Profile::FromWebUI(web_ui()),
+ supervised_user_id,
+ base::Bind(&CreateProfileHandler::OnSupervisedUserRegistered,
+ weak_ptr_factory_.GetWeakPtr(),
+ create_shortcut,
+ new_profile));
+}
+
+void CreateProfileHandler::OnSupervisedUserRegistered(
+ bool create_shortcut,
+ Profile* profile,
+ const GoogleServiceAuthError& error) {
+ GoogleServiceAuthError::State state = error.state();
+ RecordSupervisedProfileCreationMetrics(state);
+ if (state == GoogleServiceAuthError::NONE) {
+ CreateShortcutAndShowSuccess(create_shortcut, profile);
+ return;
+ }
+
+ base::string16 error_msg;
+ if (state == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS ||
+ state == GoogleServiceAuthError::USER_NOT_SIGNED_UP ||
+ state == GoogleServiceAuthError::ACCOUNT_DELETED ||
+ state == GoogleServiceAuthError::ACCOUNT_DISABLED) {
+ error_msg = GetProfileCreationErrorMessageSignin();
+ } else {
+ error_msg = GetProfileCreationErrorMessageRemote();
+ }
+ ShowProfileCreationError(profile, error_msg);
+}
+
+void CreateProfileHandler::ShowProfileCreationWarning(
+ const base::string16& warning) {
+ DCHECK_EQ(SUPERVISED_PROFILE_CREATION, profile_creation_type_);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.showCreateProfileWarning", base::Value(warning));
+}
+
+void CreateProfileHandler::RecordSupervisedProfileCreationMetrics(
+ GoogleServiceAuthError::State error_state) {
+ if (profile_creation_type_ == SUPERVISED_PROFILE_CREATION) {
+ UMA_HISTOGRAM_ENUMERATION("Profile.SupervisedProfileCreateError",
+ error_state,
+ GoogleServiceAuthError::NUM_STATES);
+ UMA_HISTOGRAM_MEDIUM_TIMES(
+ "Profile.SupervisedProfileTotalCreateTime",
+ base::TimeTicks::Now() - profile_creation_start_time_);
+ } else {
+ DCHECK_EQ(SUPERVISED_PROFILE_IMPORT, profile_creation_type_);
+ UMA_HISTOGRAM_ENUMERATION("Profile.SupervisedProfileImportError",
+ error_state,
+ GoogleServiceAuthError::NUM_STATES);
+ UMA_HISTOGRAM_MEDIUM_TIMES(
+ "Profile.SupervisedProfileTotalImportTime",
+ base::TimeTicks::Now() - profile_creation_start_time_);
+ }
+}
+
+bool CreateProfileHandler::IsValidExistingSupervisedUserId(
+ const std::string& existing_supervised_user_id) const {
+ if (existing_supervised_user_id.empty())
+ return true;
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ const base::DictionaryValue* dict =
+ SupervisedUserSyncServiceFactory::GetForProfile(profile)->
+ GetSupervisedUsers();
+ if (!dict->HasKey(existing_supervised_user_id))
+ return false;
+
+ // Check if this supervised user already exists on this machine.
+ std::vector<ProfileAttributesEntry*> entries =
+ g_browser_process->profile_manager()->
+ GetProfileAttributesStorage().GetAllProfilesAttributes();
+ for (const ProfileAttributesEntry* entry : entries) {
+ if (existing_supervised_user_id == entry->GetSupervisedUserId())
+ return false;
+ }
+ return true;
+}
+#endif
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/create_profile_handler.h b/chromium/chrome/browser/ui/webui/options/create_profile_handler.h
new file mode 100644
index 00000000000..29795ca6856
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/create_profile_handler.h
@@ -0,0 +1,171 @@
+// 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_OPTIONS_CREATE_PROFILE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CREATE_PROFILE_HANDLER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "chrome/common/features.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+class SupervisedUserRegistrationUtility;
+#endif
+
+namespace options {
+
+// Handler for the 'create profile' overlay.
+class CreateProfileHandler: public OptionsPageUIHandler {
+ public:
+ CreateProfileHandler();
+ ~CreateProfileHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ // Represents the final profile creation status. It is used to map
+ // the status to the javascript method to be called.
+ enum ProfileCreationStatus {
+ PROFILE_CREATION_SUCCESS,
+ PROFILE_CREATION_ERROR,
+ };
+
+ // Represents the type of the in progress profile creation operation.
+ // It is used to map the type of the profile creation operation to the
+ // correct UMA metric name.
+ enum ProfileCreationOperationType {
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ SUPERVISED_PROFILE_CREATION,
+ SUPERVISED_PROFILE_IMPORT,
+#endif
+ NON_SUPERVISED_PROFILE_CREATION,
+ NO_CREATION_IN_PROGRESS
+ };
+
+ // Asynchronously creates and initializes a new profile.
+ // The arguments are as follows:
+ // 0: name (string)
+ // 1: icon (string)
+ // 2: a flag stating whether we should create a profile desktop shortcut
+ // (optional, boolean)
+ // 3: a flag stating whether the user should be supervised
+ // (optional, boolean)
+ // 4: a string representing the supervised user ID.
+ void CreateProfile(const base::ListValue* args);
+
+ // If a local error occurs during profile creation, then show an appropriate
+ // error message. However, if profile creation succeeded and the
+ // profile being created/imported is a supervised user profile,
+ // then proceed with the registration step. Otherwise, update the UI
+ // as the final task after a new profile has been created.
+ void OnProfileCreated(bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* profile,
+ Profile::CreateStatus status);
+
+ void HandleProfileCreationSuccess(bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* profile);
+
+ // Creates desktop shortcut and updates the UI to indicate success
+ // when creating a profile.
+ void CreateShortcutAndShowSuccess(bool create_shortcut, Profile* profile);
+
+ // Updates the UI to show an error when creating a profile.
+ void ShowProfileCreationError(Profile* profile, const base::string16& error);
+
+ // Updates the UI to show a non-fatal warning when creating a profile.
+ void ShowProfileCreationWarning(const base::string16& warning);
+
+ // Records UMA histograms relevant to profile creation.
+ void RecordProfileCreationMetrics(Profile::CreateStatus status);
+
+ base::string16 GetProfileCreationErrorMessageLocal() const;
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ // The following error messages only apply to supervised profiles.
+ base::string16 GetProfileCreationErrorMessageRemote() const;
+ base::string16 GetProfileCreationErrorMessageSignin() const;
+#endif
+
+ std::string GetJavascriptMethodName(ProfileCreationStatus status) const;
+
+ // Used to allow cancelling a profile creation (particularly a supervised-user
+ // registration) in progress. Set when profile creation is begun, and
+ // cleared when all the callbacks have been run and creation is complete.
+ base::FilePath profile_path_being_created_;
+
+ // Used to track how long profile creation takes.
+ base::TimeTicks profile_creation_start_time_;
+
+ // Indicates the type of the in progress profile creation operation.
+ // The value is only relevant while we are creating/importing a profile.
+ ProfileCreationOperationType profile_creation_type_;
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ // Extracts the supervised user ID from the args passed into CreateProfile,
+ // sets |profile_creation_type_| if necessary, and returns true if the
+ // supervised user id specified in |args| are valid.
+ bool ProcessSupervisedCreateProfileArgs(const base::ListValue* args,
+ std::string* supervised_user_id);
+
+ // Cancels creation of a supervised-user profile currently in progress, as
+ // indicated by profile_path_being_created_, removing the object and files
+ // and canceling supervised-user registration. This is the handler for the
+ // "cancelCreateProfile" message. |args| is not used.
+ void HandleCancelProfileCreation(const base::ListValue* args);
+
+ // Internal implementation. This may safely be called whether profile creation
+ // or registration is in progress or not. |user_initiated| should be true if
+ // the cancellation was deliberately requested by the user, and false if it
+ // was caused implicitly, e.g. by shutting down the browser.
+ void CancelProfileRegistration(bool user_initiated);
+
+ // After a new supervised-user profile has been created, registers the user
+ // with the management server.
+ void RegisterSupervisedUser(bool create_shortcut,
+ const std::string& managed_user_id,
+ Profile* new_profile);
+
+ // Called back with the result of the supervised user registration.
+ void OnSupervisedUserRegistered(bool create_shortcut,
+ Profile* profile,
+ const GoogleServiceAuthError& error);
+
+ // Records UMA histograms relevant to supervised user profiles
+ // creation and registration.
+ void RecordSupervisedProfileCreationMetrics(
+ GoogleServiceAuthError::State error_state);
+
+ bool IsValidExistingSupervisedUserId(
+ const std::string& existing_supervised_user_id) const;
+
+ std::unique_ptr<SupervisedUserRegistrationUtility>
+ supervised_user_registration_utility_;
+#endif
+
+ base::WeakPtrFactory<CreateProfileHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CreateProfileHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CREATE_PROFILE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/easy_unlock_handler.cc b/chromium/chrome/browser/ui/webui/options/easy_unlock_handler.cc
new file mode 100644
index 00000000000..ff3cf2ddc27
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/easy_unlock_handler.cc
@@ -0,0 +1,110 @@
+// 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/ui/webui/options/easy_unlock_handler.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/easy_unlock_service.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_ui.h"
+
+namespace options {
+
+EasyUnlockHandler::EasyUnlockHandler() {
+}
+
+EasyUnlockHandler::~EasyUnlockHandler() {
+ EasyUnlockService::Get(Profile::FromWebUI(web_ui()))->RemoveObserver(this);
+}
+
+void EasyUnlockHandler::GetLocalizedValues(base::DictionaryValue* values) {
+ static OptionsStringResource resources[] = {
+ {"easyUnlockTurnOffButton", IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_BUTTON},
+ {"easyUnlockTurnOffTitle", IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_TITLE},
+ {"easyUnlockTurnOffDescription",
+ IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_DESCRIPTION},
+ {"easyUnlockTurnOffOfflineTitle",
+ IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_OFFLINE_TITLE},
+ {"easyUnlockTurnOffOfflineMessage",
+ IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_OFFLINE_MESSAGE},
+ {"easyUnlockTurnOffErrorTitle",
+ IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_ERROR_TITLE},
+ {"easyUnlockTurnOffErrorMessage",
+ IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_ERROR_MESSAGE},
+ {"easyUnlockTurnOffRetryButton",
+ IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_RETRY_BUTTON},
+ };
+
+ RegisterStrings(values, resources, arraysize(resources));
+}
+
+void EasyUnlockHandler::InitializeHandler() {
+ EasyUnlockService::Get(Profile::FromWebUI(web_ui()))->AddObserver(this);
+}
+
+void EasyUnlockHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "easyUnlockGetTurnOffFlowStatus",
+ base::Bind(&EasyUnlockHandler::HandleGetTurnOffFlowStatus,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "easyUnlockRequestTurnOff",
+ base::Bind(&EasyUnlockHandler::HandleRequestTurnOff,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "easyUnlockTurnOffOverlayDismissed",
+ base::Bind(&EasyUnlockHandler::HandlePageDismissed,
+ base::Unretained(this)));
+}
+
+void EasyUnlockHandler::OnTurnOffOperationStatusChanged() {
+ SendTurnOffOperationStatus();
+}
+
+void EasyUnlockHandler::SendTurnOffOperationStatus() {
+ EasyUnlockService::TurnOffFlowStatus status =
+ EasyUnlockService::Get(Profile::FromWebUI(web_ui()))
+ ->GetTurnOffFlowStatus();
+
+ // Translate status into JS UI state string. Note the translated string
+ // should match UIState defined in easy_unlock_turn_off_overlay.js.
+ std::string status_string;
+ switch (status) {
+ case EasyUnlockService::IDLE:
+ status_string = "idle";
+ break;
+ case EasyUnlockService::PENDING:
+ status_string = "pending";
+ break;
+ case EasyUnlockService::FAIL:
+ status_string = "server-error";
+ break;
+ default:
+ LOG(ERROR) << "Unknown Easy unlock turn-off operation status: " << status;
+ status_string = "idle";
+ break;
+ }
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "EasyUnlockTurnOffOverlay.updateUIState", base::Value(status_string));
+}
+
+void EasyUnlockHandler::HandleGetTurnOffFlowStatus(
+ const base::ListValue* args) {
+ SendTurnOffOperationStatus();
+}
+
+void EasyUnlockHandler::HandleRequestTurnOff(const base::ListValue* args) {
+ EasyUnlockService::Get(Profile::FromWebUI(web_ui()))->RunTurnOffFlow();
+}
+
+void EasyUnlockHandler::HandlePageDismissed(const base::ListValue* args) {
+ EasyUnlockService::Get(Profile::FromWebUI(web_ui()))->ResetTurnOffFlow();
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/easy_unlock_handler.h b/chromium/chrome/browser/ui/webui/options/easy_unlock_handler.h
new file mode 100644
index 00000000000..c8feb5f59b4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/easy_unlock_handler.h
@@ -0,0 +1,43 @@
+// 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_UI_WEBUI_OPTIONS_EASY_UNLOCK_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_EASY_UNLOCK_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/signin/easy_unlock_service_observer.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+namespace options {
+
+class EasyUnlockHandler : public OptionsPageUIHandler,
+ public EasyUnlockServiceObserver {
+ public:
+ EasyUnlockHandler();
+ ~EasyUnlockHandler() override;
+
+ // OptionsPageUIHandler
+ void InitializeHandler() override;
+ void GetLocalizedValues(base::DictionaryValue* values) override;
+
+ // WebUIMessageHandler
+ void RegisterMessages() override;
+
+ // EasyUnlockServiceObserver
+ void OnTurnOffOperationStatusChanged() override;
+
+ private:
+ void SendTurnOffOperationStatus();
+
+ // JS callbacks.
+ void HandleGetTurnOffFlowStatus(const base::ListValue* args);
+ void HandleRequestTurnOff(const base::ListValue* args);
+ void HandlePageDismissed(const base::ListValue* args);
+
+ DISALLOW_COPY_AND_ASSIGN(EasyUnlockHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_EASY_UNLOCK_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/edit_dictionary_browsertest.js b/chromium/chrome/browser/ui/webui/options/edit_dictionary_browsertest.js
new file mode 100644
index 00000000000..5bcb0e0b18a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/edit_dictionary_browsertest.js
@@ -0,0 +1,166 @@
+// 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.
+
+GEN_INCLUDE(['options_browsertest_base.js']);
+
+/**
+ * TestFixture for EditDictionaryOverlay WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function EditDictionaryWebUITest() {}
+
+EditDictionaryWebUITest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /**
+ * Browse to the edit dictionary page & call our preLoad().
+ */
+ browsePreload: 'chrome://settings-frame/editDictionary',
+
+ /**
+ * Register a mock dictionary handler.
+ */
+ preLoad: function() {
+ this.makeAndRegisterMockHandler(
+ ['refreshDictionaryWords',
+ 'addDictionaryWord',
+ 'removeDictionaryWord',
+ ]);
+ this.mockHandler.stubs().refreshDictionaryWords().
+ will(callFunction(function() {
+ EditDictionaryOverlay.setWordList([]);
+ }));
+ this.mockHandler.stubs().addDictionaryWord(ANYTHING);
+ this.mockHandler.stubs().removeDictionaryWord(ANYTHING);
+ },
+
+ /** @override */
+ setUp: function() {
+ OptionsBrowsertestBase.prototype.setUp.call(this);
+
+ // Enable when failure is resolved.
+ // AX_TEXT_01: http://crbug.com/570556
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'controlsWithoutLabel',
+ '#language-dictionary-overlay-word-list > .deletable-item > *');
+
+ var unsupportedAriaAttributeSelectors = [
+ '#language-dictionary-overlay-word-list',
+ '#language-options-list',
+ ];
+
+ // Enable when failure is resolved.
+ // AX_ARIA_10: http://crbug.com/570559
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'unsupportedAriaAttribute',
+ unsupportedAriaAttributeSelectors);
+ },
+};
+
+// Verify that users can add and remove words in the dictionary.
+TEST_F('EditDictionaryWebUITest', 'testAddRemoveWords', function() {
+ var testWord = 'foo';
+ $('language-dictionary-overlay-word-list').querySelector('input').value =
+ testWord;
+
+ this.mockHandler.expects(once()).addDictionaryWord([testWord]).
+ will(callFunction(function() {
+ EditDictionaryOverlay.setWordList([testWord]);
+ }));
+ var addWordItem = EditDictionaryOverlay.getWordListForTesting().items[0];
+ addWordItem.onEditCommitted_({currentTarget: addWordItem});
+
+ this.mockHandler.expects(once()).removeDictionaryWord([testWord]).
+ will(callFunction(function() {
+ EditDictionaryOverlay.setWordList([]);
+ }));
+ EditDictionaryOverlay.getWordListForTesting().deleteItemAtIndex(0);
+});
+
+// Verify that users can search words in the dictionary.
+TEST_F('EditDictionaryWebUITest', 'testSearch', function() {
+ EditDictionaryOverlay.setWordList(['foo', 'bar']);
+ expectEquals(3, EditDictionaryOverlay.getWordListForTesting().items.length);
+
+ /**
+ * @param {Element} el The element to dispatch an event on.
+ * @param {string} value The text of the search event.
+ */
+ var fakeSearchEvent = function(el, value) {
+ el.value = value;
+ cr.dispatchSimpleEvent(el, 'search');
+ };
+ var searchField = $('language-dictionary-overlay-search-field');
+ fakeSearchEvent(searchField, 'foo');
+ expectEquals(2, EditDictionaryOverlay.getWordListForTesting().items.length);
+
+ fakeSearchEvent(searchField, '');
+ expectEquals(3, EditDictionaryOverlay.getWordListForTesting().items.length);
+});
+
+TEST_F('EditDictionaryWebUITest', 'testNoCloseOnSearchEnter', function() {
+ var editDictionaryPage = EditDictionaryOverlay.getInstance();
+ assertTrue(editDictionaryPage.visible);
+ var searchField = $('language-dictionary-overlay-search-field');
+ searchField.dispatchEvent(new KeyboardEvent('keydown', {
+ 'bubbles': true,
+ 'cancelable': true,
+ 'key': 'Enter'
+ }));
+ assertTrue(editDictionaryPage.visible);
+});
+
+// Verify that dictionary shows newly added words that arrived in a
+// notification, but ignores duplicate add notifications.
+TEST_F('EditDictionaryWebUITest', 'testAddNotification', function() {
+ // Begin with an empty dictionary.
+ EditDictionaryOverlay.setWordList([]);
+ expectEquals(1, EditDictionaryOverlay.getWordListForTesting().items.length);
+
+ // User adds word 'foo'.
+ EditDictionaryOverlay.getWordListForTesting().addDictionaryWord_('foo');
+ expectEquals(2, EditDictionaryOverlay.getWordListForTesting().items.length);
+
+ // Backend notifies UI that the word 'foo' has been added. UI ignores this
+ // notification, because the word is displayed immediately after user added
+ // it.
+ EditDictionaryOverlay.updateWords(['foo'], []);
+ expectEquals(2, EditDictionaryOverlay.getWordListForTesting().items.length);
+
+ // Backend notifies UI that the words 'bar' and 'baz' were added. UI shows
+ // these new words.
+ EditDictionaryOverlay.updateWords(['bar', 'baz'], []);
+ expectEquals(4, EditDictionaryOverlay.getWordListForTesting().items.length);
+});
+
+// Verify that dictionary hides newly removed words that arrived in a
+// notification, but ignores duplicate remove notifications.
+// TODO(crbug.com/631940): Flaky on Win 7.
+GEN('#if defined(OS_WIN)');
+GEN('#define MAYBE_testRemoveNotification DISABLED_testRemoveNotification');
+GEN('#else');
+GEN('#define MAYBE_testRemoveNotification testRemoveNotification');
+GEN('#endif // defined(OS_WIN)');
+TEST_F('EditDictionaryWebUITest', 'MAYBE_testRemoveNotification', function() {
+ // Begin with a dictionary with words 'foo', 'bar', 'baz', and 'baz'. The
+ // second instance of 'baz' appears because the user added the word twice.
+ // The backend keeps only one copy of the word.
+ EditDictionaryOverlay.setWordList(['foo', 'bar', 'baz', 'baz']);
+ expectEquals(5, EditDictionaryOverlay.getWordListForTesting().items.length);
+
+ // User deletes the second instance of 'baz'.
+ EditDictionaryOverlay.getWordListForTesting().deleteItemAtIndex(3);
+ expectEquals(4, EditDictionaryOverlay.getWordListForTesting().items.length);
+
+ // Backend notifies UI that the word 'baz' has been removed. UI ignores this
+ // notification.
+ EditDictionaryOverlay.updateWords([], ['baz']);
+ expectEquals(4, EditDictionaryOverlay.getWordListForTesting().items.length);
+
+ // Backend notifies UI that words 'foo' and 'bar' have been removed. UI
+ // removes these words.
+ EditDictionaryOverlay.updateWords([], ['foo', 'bar']);
+ expectEquals(2, EditDictionaryOverlay.getWordListForTesting().items.length);
+});
diff --git a/chromium/chrome/browser/ui/webui/options/font_settings_browsertest.js b/chromium/chrome/browser/ui/webui/options/font_settings_browsertest.js
new file mode 100644
index 00000000000..6e419a207cf
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/font_settings_browsertest.js
@@ -0,0 +1,66 @@
+// 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.
+
+GEN_INCLUDE(['options_browsertest_base.js']);
+
+/**
+ * TestFixture for font settings WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function FontSettingsWebUITest() {}
+
+FontSettingsWebUITest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /**
+ * Browse to the font settings page.
+ */
+ browsePreload: 'chrome://settings-frame/fonts',
+
+ /** @override */
+ preLoad: function() {
+ this.makeAndRegisterMockHandler(['openAdvancedFontSettingsOptions']);
+ },
+
+ /** @override */
+ setUp: function() {
+ OptionsBrowsertestBase.prototype.setUp.call(this);
+
+ var controlsWithoutLabelSelectors = [
+ '#standard-font-size',
+ '#minimum-font-size',
+ ];
+
+ // Enable when failure is resolved.
+ // AX_TEXT_01: http://crbug.com/570555
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'controlsWithoutLabel',
+ controlsWithoutLabelSelectors);
+ },
+};
+
+// Test opening font settings has correct location.
+TEST_F('FontSettingsWebUITest', 'testOpenFontSettings', function() {
+ assertEquals(this.browsePreload, document.location.href);
+});
+
+// Test setup of the Advanced Font Settings links.
+TEST_F('FontSettingsWebUITest', 'testAdvancedFontSettingsLink', function() {
+ var installElement = $('advanced-font-settings-install');
+ var optionsElement = $('advanced-font-settings-options');
+ var expectedUrl = 'https://chrome.google.com/webstore/detail/' +
+ 'caclkomlalccbpcdllchkeecicepbmbm';
+
+ FontSettings.notifyAdvancedFontSettingsAvailability(false);
+ assertFalse(installElement.hidden);
+ assertEquals(expectedUrl, installElement.querySelector('a').href);
+ assertTrue(optionsElement.hidden);
+
+ FontSettings.notifyAdvancedFontSettingsAvailability(true);
+ assertTrue(installElement.hidden);
+ assertFalse(optionsElement.hidden);
+ this.mockHandler.expects(once()).openAdvancedFontSettingsOptions();
+ optionsElement.click();
+});
diff --git a/chromium/chrome/browser/ui/webui/options/font_settings_handler.cc b/chromium/chrome/browser/ui/webui/options/font_settings_handler.cc
new file mode 100644
index 00000000000..f5a045c438c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/font_settings_handler.cc
@@ -0,0 +1,277 @@
+// 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/ui/webui/options/font_settings_handler.h"
+
+#include <stddef.h>
+
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/i18n/rtl.h"
+#include "base/macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/webui/options/font_settings_utils.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/font_list_async.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_urls.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+namespace {
+
+const char kAdvancedFontSettingsExtensionId[] =
+ "caclkomlalccbpcdllchkeecicepbmbm";
+
+} // namespace
+
+
+namespace options {
+
+FontSettingsHandler::FontSettingsHandler()
+ : extension_registry_observer_(this),
+ weak_ptr_factory_(this) {
+}
+
+FontSettingsHandler::~FontSettingsHandler() {
+}
+
+void FontSettingsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ static OptionsStringResource resources[] = {
+ { "fontSettingsStandard",
+ IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_STANDARD_LABEL },
+ { "fontSettingsSerif",
+ IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_SERIF_LABEL },
+ { "fontSettingsSansSerif",
+ IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_SANS_SERIF_LABEL },
+ { "fontSettingsFixedWidth",
+ IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_FIXED_WIDTH_LABEL },
+ { "fontSettingsMinimumSize",
+ IDS_FONT_LANGUAGE_SETTING_MINIMUM_FONT_SIZE_TITLE },
+ { "fontSettings",
+ IDS_FONT_LANGUAGE_SETTING_FONT_SUB_DIALOG_TITLE },
+ { "fontSettingsSizeTiny",
+ IDS_FONT_LANGUAGE_SETTING_FONT_SIZE_TINY },
+ { "fontSettingsSizeHuge",
+ IDS_FONT_LANGUAGE_SETTING_FONT_SIZE_HUGE },
+ { "fontSettingsLoremIpsum",
+ IDS_FONT_LANGUAGE_SETTING_LOREM_IPSUM },
+ { "advancedFontSettingsOptions",
+ IDS_FONT_LANGUAGE_SETTING_ADVANCED_FONT_SETTINGS_OPTIONS }
+ };
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+ RegisterTitle(localized_strings, "fontSettingsPage",
+ IDS_FONT_LANGUAGE_SETTING_FONT_TAB_TITLE);
+ localized_strings->SetString("fontSettingsPlaceholder",
+ l10n_util::GetStringUTF16(
+ IDS_FONT_LANGUAGE_SETTING_PLACEHOLDER));
+
+ GURL install_url(extension_urls::GetWebstoreItemDetailURLPrefix());
+ localized_strings->SetString("advancedFontSettingsInstall",
+ l10n_util::GetStringFUTF16(
+ IDS_FONT_LANGUAGE_SETTING_ADVANCED_FONT_SETTINGS_INSTALL,
+ base::UTF8ToUTF16(
+ install_url.Resolve(kAdvancedFontSettingsExtensionId).spec())));
+}
+
+void FontSettingsHandler::InitializeHandler() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ extension_registry_observer_.Add(extensions::ExtensionRegistry::Get(profile));
+}
+
+void FontSettingsHandler::InitializePage() {
+ DCHECK(web_ui());
+ SetUpStandardFontSample();
+ SetUpSerifFontSample();
+ SetUpSansSerifFontSample();
+ SetUpFixedFontSample();
+ SetUpMinimumFontSample();
+ NotifyAdvancedFontSettingsAvailability();
+}
+
+void FontSettingsHandler::RegisterMessages() {
+ // Perform validation for saved fonts.
+ PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
+ FontSettingsUtilities::ValidateSavedFonts(pref_service);
+
+ // Register for preferences that we need to observe manually.
+ standard_font_.Init(prefs::kWebKitStandardFontFamily,
+ pref_service,
+ base::Bind(&FontSettingsHandler::SetUpStandardFontSample,
+ base::Unretained(this)));
+ serif_font_.Init(prefs::kWebKitSerifFontFamily,
+ pref_service,
+ base::Bind(&FontSettingsHandler::SetUpSerifFontSample,
+ base::Unretained(this)));
+ sans_serif_font_.Init(
+ prefs::kWebKitSansSerifFontFamily,
+ pref_service,
+ base::Bind(&FontSettingsHandler::SetUpSansSerifFontSample,
+ base::Unretained(this)));
+
+ base::Closure callback = base::Bind(
+ &FontSettingsHandler::SetUpFixedFontSample, base::Unretained(this));
+
+ fixed_font_.Init(prefs::kWebKitFixedFontFamily, pref_service, callback);
+ default_fixed_font_size_.Init(prefs::kWebKitDefaultFixedFontSize,
+ pref_service, callback);
+ default_font_size_.Init(
+ prefs::kWebKitDefaultFontSize,
+ pref_service,
+ base::Bind(&FontSettingsHandler::OnWebKitDefaultFontSizeChanged,
+ base::Unretained(this)));
+ minimum_font_size_.Init(
+ prefs::kWebKitMinimumFontSize,
+ pref_service,
+ base::Bind(&FontSettingsHandler::SetUpMinimumFontSample,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback("fetchFontsData",
+ base::Bind(&FontSettingsHandler::HandleFetchFontsData,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("openAdvancedFontSettingsOptions",
+ base::Bind(&FontSettingsHandler::HandleOpenAdvancedFontSettingsOptions,
+ base::Unretained(this)));
+}
+
+void FontSettingsHandler::OnExtensionLoaded(
+ content::BrowserContext* browser_context,
+ const extensions::Extension* extension) {
+ NotifyAdvancedFontSettingsAvailability();
+}
+
+void FontSettingsHandler::OnExtensionUnloaded(
+ content::BrowserContext* browser_context,
+ const extensions::Extension* extension,
+ extensions::UnloadedExtensionReason reason) {
+ NotifyAdvancedFontSettingsAvailability();
+}
+
+void FontSettingsHandler::HandleFetchFontsData(const base::ListValue* args) {
+ content::GetFontListAsync(
+ base::Bind(&FontSettingsHandler::FontsListHasLoaded,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void FontSettingsHandler::FontsListHasLoaded(
+ std::unique_ptr<base::ListValue> list) {
+ // Selects the directionality for the fonts in the given list.
+ for (size_t i = 0; i < list->GetSize(); i++) {
+ base::ListValue* font;
+ bool has_font = list->GetList(i, &font);
+ DCHECK(has_font);
+ base::string16 value;
+ bool has_value = font->GetString(1, &value);
+ DCHECK(has_value);
+ bool has_rtl_chars = base::i18n::StringContainsStrongRTLChars(value);
+ font->AppendString(has_rtl_chars ? "rtl" : "ltr");
+ }
+
+ base::ListValue selected_values;
+ selected_values.AppendString(FontSettingsUtilities::MaybeGetLocalizedFontName(
+ standard_font_.GetValue()));
+ selected_values.AppendString(
+ FontSettingsUtilities::MaybeGetLocalizedFontName(serif_font_.GetValue()));
+ selected_values.AppendString(FontSettingsUtilities::MaybeGetLocalizedFontName(
+ sans_serif_font_.GetValue()));
+ selected_values.AppendString(
+ FontSettingsUtilities::MaybeGetLocalizedFontName(fixed_font_.GetValue()));
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "FontSettings.setFontsData", *list.get(), selected_values);
+}
+
+void FontSettingsHandler::SetUpStandardFontSample() {
+ base::Value font_value(
+ FontSettingsUtilities::ResolveFontList(standard_font_.GetValue()));
+ base::Value size_value(default_font_size_.GetValue());
+ web_ui()->CallJavascriptFunctionUnsafe("FontSettings.setUpStandardFontSample",
+ font_value, size_value);
+}
+
+void FontSettingsHandler::SetUpSerifFontSample() {
+ base::Value font_value(
+ FontSettingsUtilities::ResolveFontList(serif_font_.GetValue()));
+ base::Value size_value(default_font_size_.GetValue());
+ web_ui()->CallJavascriptFunctionUnsafe("FontSettings.setUpSerifFontSample",
+ font_value, size_value);
+}
+
+void FontSettingsHandler::SetUpSansSerifFontSample() {
+ base::Value font_value(
+ FontSettingsUtilities::ResolveFontList(sans_serif_font_.GetValue()));
+ base::Value size_value(default_font_size_.GetValue());
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "FontSettings.setUpSansSerifFontSample", font_value, size_value);
+}
+
+void FontSettingsHandler::SetUpFixedFontSample() {
+ base::Value font_value(
+ FontSettingsUtilities::ResolveFontList(fixed_font_.GetValue()));
+ base::Value size_value(default_fixed_font_size_.GetValue());
+ web_ui()->CallJavascriptFunctionUnsafe("FontSettings.setUpFixedFontSample",
+ font_value, size_value);
+}
+
+void FontSettingsHandler::SetUpMinimumFontSample() {
+ base::Value size_value(minimum_font_size_.GetValue());
+ web_ui()->CallJavascriptFunctionUnsafe("FontSettings.setUpMinimumFontSample",
+ size_value);
+}
+
+const extensions::Extension*
+FontSettingsHandler::GetAdvancedFontSettingsExtension() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ ExtensionService* service =
+ extensions::ExtensionSystem::Get(profile)->extension_service();
+ if (!service->IsExtensionEnabled(kAdvancedFontSettingsExtensionId))
+ return NULL;
+ return service->GetInstalledExtension(kAdvancedFontSettingsExtensionId);
+}
+
+void FontSettingsHandler::NotifyAdvancedFontSettingsAvailability() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "FontSettings.notifyAdvancedFontSettingsAvailability",
+ base::Value(GetAdvancedFontSettingsExtension() != NULL));
+}
+
+void FontSettingsHandler::HandleOpenAdvancedFontSettingsOptions(
+ const base::ListValue* args) {
+ const extensions::Extension* extension = GetAdvancedFontSettingsExtension();
+ if (!extension)
+ return;
+ extensions::ExtensionTabUtil::OpenOptionsPage(extension,
+ chrome::FindBrowserWithWebContents(web_ui()->GetWebContents()));
+}
+
+void FontSettingsHandler::OnWebKitDefaultFontSizeChanged() {
+ SetUpStandardFontSample();
+ SetUpSerifFontSample();
+ SetUpSansSerifFontSample();
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/font_settings_handler.h b/chromium/chrome/browser/ui/webui/options/font_settings_handler.h
new file mode 100644
index 00000000000..adcd8e8b2df
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/font_settings_handler.h
@@ -0,0 +1,91 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_FONT_SETTINGS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_FONT_SETTINGS_HANDLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "components/prefs/pref_member.h"
+#include "extensions/browser/extension_registry_observer.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace extensions {
+class Extension;
+class ExtensionRegistry;
+}
+
+namespace options {
+
+// Font settings overlay page UI handler.
+class FontSettingsHandler : public OptionsPageUIHandler,
+ public extensions::ExtensionRegistryObserver {
+ public:
+ FontSettingsHandler();
+ ~FontSettingsHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializeHandler() override;
+ void InitializePage() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // ExtensionRegistryObserver implementation.
+ void OnExtensionLoaded(content::BrowserContext* browser_context,
+ const extensions::Extension* extension) override;
+ void OnExtensionUnloaded(content::BrowserContext* browser_context,
+ const extensions::Extension* extension,
+ extensions::UnloadedExtensionReason reason) override;
+
+ private:
+ void HandleFetchFontsData(const base::ListValue* args);
+
+ void FontsListHasLoaded(std::unique_ptr<base::ListValue> list);
+
+ void SetUpStandardFontSample();
+ void SetUpSerifFontSample();
+ void SetUpSansSerifFontSample();
+ void SetUpFixedFontSample();
+ void SetUpMinimumFontSample();
+
+ // Returns the Advanced Font Settings Extension if it's installed and enabled,
+ // or NULL otherwise.
+ const extensions::Extension* GetAdvancedFontSettingsExtension();
+ // Notifies the web UI about whether the Advanced Font Settings Extension is
+ // installed and enabled.
+ void NotifyAdvancedFontSettingsAvailability();
+ // Opens the options page of the Advanced Font Settings Extension.
+ void HandleOpenAdvancedFontSettingsOptions(const base::ListValue* args);
+
+ void OnWebKitDefaultFontSizeChanged();
+
+ StringPrefMember standard_font_;
+ StringPrefMember serif_font_;
+ StringPrefMember sans_serif_font_;
+ StringPrefMember fixed_font_;
+ IntegerPrefMember default_font_size_;
+ IntegerPrefMember default_fixed_font_size_;
+ IntegerPrefMember minimum_font_size_;
+
+ ScopedObserver<extensions::ExtensionRegistry,
+ extensions::ExtensionRegistryObserver>
+ extension_registry_observer_;
+
+ base::WeakPtrFactory<FontSettingsHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FontSettingsHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_FONT_SETTINGS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/font_settings_utils.cc b/chromium/chrome/browser/ui/webui/options/font_settings_utils.cc
new file mode 100644
index 00000000000..516b138c93e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/font_settings_utils.cc
@@ -0,0 +1,25 @@
+// Copyright (c) 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 "chrome/browser/ui/webui/options/font_settings_utils.h"
+
+#include "ui/gfx/font_list.h"
+
+namespace options {
+
+std::string FontSettingsUtilities::ResolveFontList(
+ const std::string& font_name_or_list) {
+ if (!font_name_or_list.empty() && font_name_or_list[0] == ',')
+ return gfx::FontList::FirstAvailableOrFirst(font_name_or_list);
+ return font_name_or_list;
+}
+
+#if !defined(OS_WIN)
+std::string FontSettingsUtilities::MaybeGetLocalizedFontName(
+ const std::string& font_name_or_list) {
+ return ResolveFontList(font_name_or_list);
+}
+#endif
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/font_settings_utils.h b/chromium/chrome/browser/ui/webui/options/font_settings_utils.h
new file mode 100644
index 00000000000..35801bb471f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/font_settings_utils.h
@@ -0,0 +1,41 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_FONT_SETTINGS_UTILS_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_FONT_SETTINGS_UTILS_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+class PrefService;
+
+namespace options {
+
+// Chrome advanced options utility methods.
+class FontSettingsUtilities {
+ public:
+ static void ValidateSavedFonts(PrefService* prefs);
+
+ // When |font_name_or_list| starts with ",", it is a list of font names
+ // separated by "," and this function returns the first available font name.
+ // Otherwise returns |font_name_or_list| as is.
+ // Unlike gfx::FontList, this function picks one font, and character-level
+ // fallback is handled in CSS.
+ static std::string ResolveFontList(const std::string& font_name_or_list);
+
+ // Returns the localized name of a font so that settings can find it within
+ // the list of system fonts. On Windows, the list of system fonts has names
+ // only for the system locale, but the pref value may be in the English name.
+ // For example, "MS Gothic" becomes "MS ゴシック" on localized Windows.
+ static std::string MaybeGetLocalizedFontName(
+ const std::string& font_name_or_list);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(FontSettingsUtilities);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_FONT_SETTINGS_UTILS_H_
diff --git a/chromium/chrome/browser/ui/webui/options/font_settings_utils_linux.cc b/chromium/chrome/browser/ui/webui/options/font_settings_utils_linux.cc
new file mode 100644
index 00000000000..1576d03be1f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/font_settings_utils_linux.cc
@@ -0,0 +1,14 @@
+// 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/ui/webui/options/font_settings_utils.h"
+
+namespace options {
+
+// static
+void FontSettingsUtilities::ValidateSavedFonts(PrefService* prefs) {
+ // Nothing to do for X11.
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/font_settings_utils_mac.mm b/chromium/chrome/browser/ui/webui/options/font_settings_utils_mac.mm
new file mode 100644
index 00000000000..85c220f9399
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/font_settings_utils_mac.mm
@@ -0,0 +1,42 @@
+// 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/ui/webui/options/font_settings_utils.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/values.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+
+namespace options {
+
+static void ValidateFontFamily(PrefService* prefs,
+ const char* family_pref_name) {
+ // The native font settings dialog saved fonts by the font name, rather
+ // than the family name. This worked for the old dialog since
+ // -[NSFont fontWithName:size] accepted a font or family name, but the
+ // behavior was technically wrong. Since we really need the family name for
+ // the dom-ui options window, we will fix the saved preference if necessary.
+ NSString *family_name =
+ base::SysUTF8ToNSString(prefs->GetString(family_pref_name));
+ NSFont *font = [NSFont fontWithName:family_name
+ size:[NSFont systemFontSize]];
+ if (font &&
+ [[font familyName] caseInsensitiveCompare:family_name] != NSOrderedSame) {
+ std::string new_family_name = base::SysNSStringToUTF8([font familyName]);
+ prefs->SetString(family_pref_name, new_family_name);
+ }
+}
+
+// static
+void FontSettingsUtilities::ValidateSavedFonts(PrefService* prefs) {
+ ValidateFontFamily(prefs, prefs::kWebKitSerifFontFamily);
+ ValidateFontFamily(prefs, prefs::kWebKitSansSerifFontFamily);
+ ValidateFontFamily(prefs, prefs::kWebKitFixedFontFamily);
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/font_settings_utils_unittest.cc b/chromium/chrome/browser/ui/webui/options/font_settings_utils_unittest.cc
new file mode 100644
index 00000000000..2c274d1029d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/font_settings_utils_unittest.cc
@@ -0,0 +1,30 @@
+// 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 "chrome/browser/ui/webui/options/font_settings_utils.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace options {
+
+TEST(FontSettingsUtilitiesTest, ResolveFontList) {
+ EXPECT_TRUE(FontSettingsUtilities::ResolveFontList("").empty());
+
+ // Returns the first available font if starts with ",".
+ EXPECT_EQ("Arial",
+ FontSettingsUtilities::ResolveFontList(",not exist, Arial"));
+
+ // Returns the first font if no fonts are available.
+ EXPECT_EQ("not exist",
+ FontSettingsUtilities::ResolveFontList(",not exist, not exist 2"));
+
+ // Otherwise returns any strings as they were set.
+ std::string non_lists[] = {
+ "Arial", "not exist", "not exist, Arial",
+ };
+ for (const std::string& name : non_lists)
+ EXPECT_EQ(name, FontSettingsUtilities::ResolveFontList(name));
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/font_settings_utils_win.cc b/chromium/chrome/browser/ui/webui/options/font_settings_utils_win.cc
new file mode 100644
index 00000000000..aa3dd7941ee
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/font_settings_utils_win.cc
@@ -0,0 +1,27 @@
+// 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/ui/webui/options/font_settings_utils.h"
+
+#include "ui/gfx/font.h"
+#include "ui/gfx/platform_font_win.h"
+
+namespace options {
+
+// static
+void FontSettingsUtilities::ValidateSavedFonts(PrefService* prefs) {
+ // Nothing to do for Windows.
+}
+
+std::string FontSettingsUtilities::MaybeGetLocalizedFontName(
+ const std::string& font_name_or_list) {
+ std::string font_name = ResolveFontList(font_name_or_list);
+ if (font_name.empty())
+ return font_name;
+ gfx::Font font(font_name, 12); // dummy font size
+ return static_cast<gfx::PlatformFontWin*>(font.platform_font())
+ ->GetLocalizedFontName();
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/geolocation_options_handler.cc b/chromium/chrome/browser/ui/webui/options/geolocation_options_handler.cc
new file mode 100644
index 00000000000..05a04ebbf02
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/geolocation_options_handler.cc
@@ -0,0 +1,40 @@
+// 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/options/geolocation_options_handler.h"
+
+#include "base/command_line.h"
+#include "base/metrics/field_trial.h"
+#include "chrome/common/chrome_switches.h"
+#include "content/public/browser/web_ui.h"
+
+namespace options {
+
+GeolocationOptionsHandler::GeolocationOptionsHandler() {}
+
+GeolocationOptionsHandler::~GeolocationOptionsHandler() {}
+
+void GeolocationOptionsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+}
+
+void GeolocationOptionsHandler::InitializePage() {
+ DCHECK(web_ui());
+
+ const char kEnablePrefix[] = "Enable";
+ const char kFieldTrialName[] = "GoogleNow";
+ std::string enable_prefix(kEnablePrefix);
+ std::string field_trial_result =
+ base::FieldTrialList::FindFullName(kFieldTrialName);
+ if (field_trial_result.compare(
+ 0,
+ enable_prefix.length(),
+ enable_prefix) == 0) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "GeolocationOptions.showGeolocationOption");
+ }
+}
+
+} // namespace options
+
diff --git a/chromium/chrome/browser/ui/webui/options/geolocation_options_handler.h b/chromium/chrome/browser/ui/webui/options/geolocation_options_handler.h
new file mode 100644
index 00000000000..aeb4e7640c1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/geolocation_options_handler.h
@@ -0,0 +1,31 @@
+// 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_OPTIONS_GEOLOCATION_OPTIONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_GEOLOCATION_OPTIONS_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "components/prefs/pref_member.h"
+
+namespace options {
+
+// Handles processing of the geolocation options on settings page load.
+class GeolocationOptionsHandler : public OptionsPageUIHandler {
+ public:
+ GeolocationOptionsHandler();
+ ~GeolocationOptionsHandler() override;
+
+ // OptionsPageUIHandler implementation
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializePage() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GeolocationOptionsHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_GEOLOCATION_OPTIONS_HANDLER_H_
+
diff --git a/chromium/chrome/browser/ui/webui/options/handler_options_handler.cc b/chromium/chrome/browser/ui/webui/options/handler_options_handler.cc
new file mode 100644
index 00000000000..73b3fdea0a3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/handler_options_handler.cc
@@ -0,0 +1,224 @@
+// 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/ui/webui/options/handler_options_handler.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/google/core/browser/google_util.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_ui.h"
+
+namespace options {
+
+HandlerOptionsHandler::HandlerOptionsHandler() {
+}
+
+HandlerOptionsHandler::~HandlerOptionsHandler() {
+}
+
+void HandlerOptionsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ static OptionsStringResource resources[] = {
+ { "handlersTabLabel", IDS_HANDLERS_TAB_LABEL },
+ { "handlersAllow", IDS_HANDLERS_ALLOW_RADIO },
+ { "handlersBlock", IDS_HANDLERS_DONOTALLOW_RADIO },
+ { "handlersTypeColumnHeader", IDS_HANDLERS_TYPE_COLUMN_HEADER },
+ { "handlersSiteColumnHeader", IDS_HANDLERS_SITE_COLUMN_HEADER },
+ { "handlersRemoveLink", IDS_HANDLERS_REMOVE_HANDLER_LINK },
+ { "handlersNoneHandler", IDS_HANDLERS_NONE_HANDLER },
+ { "handlersActiveHeading", IDS_HANDLERS_ACTIVE_HEADING },
+ { "handlersIgnoredHeading", IDS_HANDLERS_IGNORED_HEADING },
+ };
+ RegisterTitle(localized_strings, "handlersPage",
+ IDS_HANDLER_OPTIONS_WINDOW_TITLE);
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+
+ localized_strings->SetString("handlersLearnMoreUrl",
+ chrome::kLearnMoreRegisterProtocolHandlerURL);
+}
+
+void HandlerOptionsHandler::InitializeHandler() {
+ notification_registrar_.Add(
+ this, chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED,
+ content::Source<Profile>(Profile::FromWebUI(web_ui())));
+}
+
+void HandlerOptionsHandler::InitializePage() {
+ UpdateHandlerList();
+}
+
+void HandlerOptionsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("clearDefault",
+ base::Bind(&HandlerOptionsHandler::ClearDefault,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("removeHandler",
+ base::Bind(&HandlerOptionsHandler::RemoveHandler,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("setHandlersEnabled",
+ base::Bind(&HandlerOptionsHandler::SetHandlersEnabled,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("setDefault",
+ base::Bind(&HandlerOptionsHandler::SetDefault,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("removeIgnoredHandler",
+ base::Bind(&HandlerOptionsHandler::RemoveIgnoredHandler,
+ base::Unretained(this)));
+}
+
+ProtocolHandlerRegistry* HandlerOptionsHandler::GetProtocolHandlerRegistry() {
+ return ProtocolHandlerRegistryFactory::GetForBrowserContext(
+ Profile::FromWebUI(web_ui()));
+}
+
+static void GetHandlersAsListValue(
+ const ProtocolHandlerRegistry::ProtocolHandlerList& handlers,
+ base::ListValue* handler_list) {
+ ProtocolHandlerRegistry::ProtocolHandlerList::const_iterator handler;
+ for (handler = handlers.begin(); handler != handlers.end(); ++handler) {
+ std::unique_ptr<base::ListValue> handler_value(new base::ListValue());
+ handler_value->AppendString(handler->protocol());
+ handler_value->AppendString(handler->url().spec());
+ handler_value->AppendString(handler->url().host());
+ handler_list->Append(std::move(handler_value));
+ }
+}
+
+void HandlerOptionsHandler::GetHandlersForProtocol(
+ const std::string& protocol,
+ base::DictionaryValue* handlers_value) {
+ ProtocolHandlerRegistry* registry = GetProtocolHandlerRegistry();
+ // The items which are to be written into |handlers_value| are also described
+ // in chrome/browser/resources/options/handler_options.js in @typedef
+ // for Handlers. Please update them whenever you add or remove any keys here.
+ 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 = base::MakeUnique<base::ListValue>();
+ GetHandlersAsListValue(registry->GetHandlersFor(protocol),
+ handlers_list.get());
+ handlers_value->Set("handlers", std::move(handlers_list));
+}
+
+void HandlerOptionsHandler::GetIgnoredHandlers(base::ListValue* handlers) {
+ ProtocolHandlerRegistry* registry = GetProtocolHandlerRegistry();
+ ProtocolHandlerRegistry::ProtocolHandlerList ignored_handlers =
+ registry->GetIgnoredHandlers();
+ return GetHandlersAsListValue(ignored_handlers, handlers);
+}
+
+void HandlerOptionsHandler::UpdateHandlerList() {
+ ProtocolHandlerRegistry* registry = GetProtocolHandlerRegistry();
+ std::vector<std::string> protocols;
+ registry->GetRegisteredProtocols(&protocols);
+
+ base::ListValue handlers;
+ for (std::vector<std::string>::iterator protocol = protocols.begin();
+ protocol != protocols.end(); protocol++) {
+ std::unique_ptr<base::DictionaryValue> handler_value(
+ new base::DictionaryValue());
+ GetHandlersForProtocol(*protocol, handler_value.get());
+ handlers.Append(std::move(handler_value));
+ }
+
+ std::unique_ptr<base::ListValue> ignored_handlers(new base::ListValue());
+ GetIgnoredHandlers(ignored_handlers.get());
+ web_ui()->CallJavascriptFunctionUnsafe("HandlerOptions.setHandlers",
+ handlers);
+ web_ui()->CallJavascriptFunctionUnsafe("HandlerOptions.setIgnoredHandlers",
+ *ignored_handlers);
+}
+
+void HandlerOptionsHandler::RemoveHandler(const base::ListValue* args) {
+ const base::ListValue* list;
+ if (!args->GetList(0, &list)) {
+ NOTREACHED();
+ return;
+ }
+
+ ProtocolHandler handler(ParseHandlerFromArgs(list));
+ GetProtocolHandlerRegistry()->RemoveHandler(handler);
+
+ // No need to call UpdateHandlerList() - we should receive a notification
+ // that the ProtocolHandlerRegistry has changed and we will update the view
+ // then.
+}
+
+void HandlerOptionsHandler::RemoveIgnoredHandler(const base::ListValue* args) {
+ const base::ListValue* list;
+ if (!args->GetList(0, &list)) {
+ NOTREACHED();
+ return;
+ }
+
+ ProtocolHandler handler(ParseHandlerFromArgs(list));
+ GetProtocolHandlerRegistry()->RemoveIgnoredHandler(handler);
+}
+
+void HandlerOptionsHandler::SetHandlersEnabled(const base::ListValue* args) {
+ bool enabled = true;
+ CHECK(args->GetBoolean(0, &enabled));
+ if (enabled)
+ GetProtocolHandlerRegistry()->Enable();
+ else
+ GetProtocolHandlerRegistry()->Disable();
+}
+
+void HandlerOptionsHandler::ClearDefault(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 HandlerOptionsHandler::SetDefault(const base::ListValue* args) {
+ const base::ListValue* list;
+ CHECK(args->GetList(0, &list));
+ const ProtocolHandler& handler(ParseHandlerFromArgs(list));
+ CHECK(!handler.IsEmpty());
+ GetProtocolHandlerRegistry()->OnAcceptRegisterProtocolHandler(handler);
+}
+
+ProtocolHandler HandlerOptionsHandler::ParseHandlerFromArgs(
+ const base::ListValue* args) const {
+ base::string16 protocol;
+ base::string16 url;
+ bool ok = args->GetString(0, &protocol) && args->GetString(1, &url);
+ if (!ok)
+ return ProtocolHandler::EmptyProtocolHandler();
+ return ProtocolHandler::CreateProtocolHandler(base::UTF16ToUTF8(protocol),
+ GURL(base::UTF16ToUTF8(url)));
+}
+
+void HandlerOptionsHandler::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK_EQ(chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED, type);
+ UpdateHandlerList();
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/handler_options_handler.h b/chromium/chrome/browser/ui/webui/options/handler_options_handler.h
new file mode 100644
index 00000000000..f1b49acbabc
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/handler_options_handler.h
@@ -0,0 +1,90 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_HANDLER_OPTIONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_HANDLER_OPTIONS_HANDLER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/custom_handlers/protocol_handler_registry.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "chrome/common/custom_handlers/protocol_handler.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// HandlerOptionsHandler
+
+// Listen for changes to protocol handlers (i.e. registerProtocolHandler()).
+// This get triggered whenever a user allows a specific website or application
+// to handle clicks on a link with a specified protocol (i.e. mailto: -> Gmail).
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace options {
+
+class HandlerOptionsHandler : public OptionsPageUIHandler,
+ public content::NotificationObserver {
+ public:
+ HandlerOptionsHandler();
+ ~HandlerOptionsHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializeHandler() override;
+ void InitializePage() override;
+ void RegisterMessages() override;
+
+ // content::NotificationObserver implementation.
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ private:
+ // Called when the user toggles whether custom handlers are enabled.
+ void SetHandlersEnabled(const base::ListValue* args);
+
+ // Called when the user sets a new default handler for a protocol.
+ void SetDefault(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 ClearDefault(const base::ListValue* args);
+
+ // Parses a ProtocolHandler out of the arguments passed back from the view.
+ // |args| is a list of [protocol, url, title].
+ ProtocolHandler ParseHandlerFromArgs(const base::ListValue* args) const;
+
+ // Returns a JSON object describing the set of protocol handlers for the
+ // given protocol.
+ void GetHandlersForProtocol(const std::string& protocol,
+ base::DictionaryValue* value);
+
+ // Returns a JSON list of the ignored protocol handlers.
+ void GetIgnoredHandlers(base::ListValue* handlers);
+
+ // Called when the JS PasswordManager object is initialized.
+ void UpdateHandlerList();
+
+ // Remove a handler.
+ // |args| is a list of [protocol, url, title].
+ void RemoveHandler(const base::ListValue* args);
+
+ // Remove an ignored handler.
+ // |args| is a list of [protocol, url, title].
+ void RemoveIgnoredHandler(const base::ListValue* args);
+
+ ProtocolHandlerRegistry* GetProtocolHandlerRegistry();
+
+ content::NotificationRegistrar notification_registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandlerOptionsHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_HANDLER_OPTIONS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/help_overlay_handler.cc b/chromium/chrome/browser/ui/webui/options/help_overlay_handler.cc
new file mode 100644
index 00000000000..fafc0fc6220
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/help_overlay_handler.cc
@@ -0,0 +1,34 @@
+// 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/ui/webui/options/help_overlay_handler.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/ui/webui/help/help_handler.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_ui.h"
+
+namespace options {
+
+HelpOverlayHandler::HelpOverlayHandler() {
+}
+
+HelpOverlayHandler::~HelpOverlayHandler() {
+}
+
+void HelpOverlayHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ if (::switches::SettingsWindowEnabled())
+ RegisterTitle(localized_strings, "aboutOverlay", IDS_ABOUT_TITLE);
+ HelpHandler::GetLocalizedValues(localized_strings);
+}
+
+void HelpOverlayHandler::RegisterMessages() {
+ if (::switches::SettingsWindowEnabled())
+ web_ui()->AddMessageHandler(base::MakeUnique<HelpHandler>());
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/help_overlay_handler.h b/chromium/chrome/browser/ui/webui/options/help_overlay_handler.h
new file mode 100644
index 00000000000..08b9973ad42
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/help_overlay_handler.h
@@ -0,0 +1,32 @@
+// 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_UI_WEBUI_OPTIONS_HELP_OVERLAY_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_HELP_OVERLAY_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+namespace options {
+
+// Help UI page handler to support About in Settings when using Settings in a
+// window. Defers to ::HelpHandler.
+class HelpOverlayHandler : public ::options::OptionsPageUIHandler {
+ public:
+ HelpOverlayHandler();
+ ~HelpOverlayHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HelpOverlayHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_HELP_OVERLAY_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/home_page_overlay_handler.cc b/chromium/chrome/browser/ui/webui/options/home_page_overlay_handler.cc
new file mode 100644
index 00000000000..e062ed3b876
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/home_page_overlay_handler.cc
@@ -0,0 +1,71 @@
+// 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/ui/webui/options/home_page_overlay_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h"
+#include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/metrics/proto/omnibox_event.pb.h"
+#include "components/omnibox/browser/autocomplete_classifier.h"
+#include "components/omnibox/browser/autocomplete_controller.h"
+#include "components/omnibox/browser/autocomplete_input.h"
+#include "components/omnibox/browser/autocomplete_result.h"
+#include "content/public/browser/web_ui.h"
+
+namespace options {
+
+HomePageOverlayHandler::HomePageOverlayHandler() {
+}
+
+HomePageOverlayHandler::~HomePageOverlayHandler() {
+}
+
+void HomePageOverlayHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "requestAutocompleteSuggestionsForHomePage",
+ base::Bind(&HomePageOverlayHandler::RequestAutocompleteSuggestions,
+ base::Unretained(this)));
+}
+
+void HomePageOverlayHandler::InitializeHandler() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ autocomplete_controller_.reset(new AutocompleteController(
+ base::MakeUnique<ChromeAutocompleteProviderClient>(profile), this,
+ AutocompleteClassifier::DefaultOmniboxProviders()));
+}
+
+void HomePageOverlayHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ RegisterTitle(localized_strings, "homePageOverlay",
+ IDS_OPTIONS_HOMEPAGE_TITLE);
+}
+
+void HomePageOverlayHandler::RequestAutocompleteSuggestions(
+ const base::ListValue* args) {
+ base::string16 input;
+ CHECK_EQ(args->GetSize(), 1U);
+ CHECK(args->GetString(0, &input));
+
+ autocomplete_controller_->Start(AutocompleteInput(
+ input, base::string16::npos, std::string(), GURL(), base::string16(),
+ metrics::OmniboxEventProto::INVALID_SPEC, true, false, false, true, false,
+ ChromeAutocompleteSchemeClassifier(Profile::FromWebUI(web_ui()))));
+}
+
+void HomePageOverlayHandler::OnResultChanged(bool default_match_changed) {
+ const AutocompleteResult& result = autocomplete_controller_->result();
+ base::ListValue suggestions;
+ OptionsUI::ProcessAutocompleteSuggestions(result, &suggestions);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "HomePageOverlay.updateAutocompleteSuggestions", suggestions);
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/home_page_overlay_handler.h b/chromium/chrome/browser/ui/webui/options/home_page_overlay_handler.h
new file mode 100644
index 00000000000..852e3f9731b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/home_page_overlay_handler.h
@@ -0,0 +1,48 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_HOME_PAGE_OVERLAY_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_HOME_PAGE_OVERLAY_HANDLER_H_
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "components/omnibox/browser/autocomplete_controller_delegate.h"
+
+class AutocompleteController;
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+namespace options {
+
+class HomePageOverlayHandler : public OptionsPageUIHandler,
+ public AutocompleteControllerDelegate {
+ public:
+ HomePageOverlayHandler();
+ ~HomePageOverlayHandler() override;
+
+ // OptionsPageUIHandler implementation
+ void GetLocalizedValues(base::DictionaryValue*) override;
+ void InitializeHandler() override;
+ void RegisterMessages() override;
+
+ // AutocompleteControllerDelegate implementation.
+ void OnResultChanged(bool default_match_changed) override;
+
+ private:
+ void RequestAutocompleteSuggestions(const base::ListValue* args);
+
+ std::unique_ptr<AutocompleteController> autocomplete_controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(HomePageOverlayHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_HOME_PAGE_OVERLAY_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/import_data_handler.cc b/chromium/chrome/browser/ui/webui/options/import_data_handler.cc
new file mode 100644
index 00000000000..0b49c9824fd
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/import_data_handler.cc
@@ -0,0 +1,272 @@
+// 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/ui/webui/options/import_data_handler.h"
+
+#include <stddef.h>
+
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/importer/external_process_importer_host.h"
+#include "chrome/browser/importer/importer_list.h"
+#include "chrome/browser/importer/importer_uma.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/chrome_select_file_policy.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_ui.h"
+
+using content::BrowserThread;
+
+namespace options {
+
+ImportDataHandler::ImportDataHandler()
+ : importer_host_(NULL),
+ import_did_succeed_(false) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+ImportDataHandler::~ImportDataHandler() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if (importer_host_)
+ importer_host_->set_observer(NULL);
+
+ if (select_file_dialog_.get())
+ select_file_dialog_->ListenerDestroyed();
+}
+
+void ImportDataHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(localized_strings);
+
+ static OptionsStringResource resources[] = {
+ {"importFromLabel", IDS_IMPORT_FROM_LABEL},
+ {"importLoading", IDS_IMPORT_LOADING_PROFILES},
+ {"importDescription", IDS_IMPORT_ITEMS_LABEL},
+ {"importHistory", IDS_IMPORT_HISTORY_CHKBOX},
+ {"importFavorites", IDS_IMPORT_FAVORITES_CHKBOX},
+ {"importSearch", IDS_IMPORT_SEARCH_ENGINES_CHKBOX},
+ {"importPasswords", IDS_IMPORT_PASSWORDS_CHKBOX},
+ {"importAutofillFormData", IDS_IMPORT_AUTOFILL_FORM_DATA_CHKBOX},
+ {"importChooseFile", IDS_IMPORT_CHOOSE_FILE},
+ {"importCommit", IDS_IMPORT_COMMIT},
+ {"noProfileFound", IDS_IMPORT_NO_PROFILE_FOUND},
+ {"importSucceeded", IDS_IMPORT_SUCCEEDED},
+ {"findYourImportedBookmarks", IDS_IMPORT_FIND_YOUR_BOOKMARKS},
+ };
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+ RegisterTitle(localized_strings, "importDataOverlay",
+ IDS_IMPORT_SETTINGS_TITLE);
+}
+
+void ImportDataHandler::InitializeHandler() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ importer_list_.reset(new ImporterList());
+ importer_list_->DetectSourceProfiles(
+ g_browser_process->GetApplicationLocale(),
+ true, // include_interactive_profiles?
+ base::Bind(&ImportDataHandler::InitializePage, base::Unretained(this)));
+}
+
+void ImportDataHandler::RegisterMessages() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ web_ui()->RegisterMessageCallback(
+ "importData",
+ base::Bind(&ImportDataHandler::ImportData, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "chooseBookmarksFile",
+ base::Bind(&ImportDataHandler::HandleChooseBookmarksFile,
+ base::Unretained(this)));
+}
+
+void ImportDataHandler::StartImport(
+ const importer::SourceProfile& source_profile,
+ uint16_t imported_items) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if (!imported_items)
+ return;
+
+ // If another import is already ongoing, let it finish silently.
+ if (importer_host_)
+ importer_host_->set_observer(NULL);
+
+ base::Value importing(true);
+ web_ui()->CallJavascriptFunctionUnsafe("ImportDataOverlay.setImportingState",
+ importing);
+ import_did_succeed_ = false;
+
+ importer_host_ = new ExternalProcessImporterHost();
+ importer_host_->set_observer(this);
+ Profile* profile = Profile::FromWebUI(web_ui());
+ importer_host_->StartImportSettings(source_profile, profile,
+ imported_items,
+ new ProfileWriter(profile));
+
+ importer::LogImporterUseToMetrics("ImportDataHandler",
+ source_profile.importer_type);
+}
+
+void ImportDataHandler::ImportData(const base::ListValue* args) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ std::string string_value;
+
+ int browser_index;
+ if (!args->GetString(0, &string_value) ||
+ !base::StringToInt(string_value, &browser_index)) {
+ NOTREACHED();
+ return;
+ }
+
+ uint16_t selected_items = importer::NONE;
+ if (args->GetString(1, &string_value) && string_value == "true") {
+ selected_items |= importer::HISTORY;
+ }
+ if (args->GetString(2, &string_value) && string_value == "true") {
+ selected_items |= importer::FAVORITES;
+ }
+ if (args->GetString(3, &string_value) && string_value == "true") {
+ selected_items |= importer::PASSWORDS;
+ }
+ if (args->GetString(4, &string_value) && string_value == "true") {
+ selected_items |= importer::SEARCH_ENGINES;
+ }
+ if (args->GetString(5, &string_value) && string_value == "true") {
+ selected_items |= importer::AUTOFILL_FORM_DATA;
+ }
+
+ const importer::SourceProfile& source_profile =
+ importer_list_->GetSourceProfileAt(browser_index);
+ uint16_t supported_items = source_profile.services_supported;
+
+ uint16_t imported_items = (selected_items & supported_items);
+ if (imported_items) {
+ StartImport(source_profile, imported_items);
+ } else {
+ LOG(WARNING) << "There were no settings to import from '"
+ << source_profile.importer_name << "'.";
+ }
+}
+
+void ImportDataHandler::InitializePage() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ base::ListValue browser_profiles;
+ for (size_t i = 0; i < importer_list_->count(); ++i) {
+ const importer::SourceProfile& source_profile =
+ importer_list_->GetSourceProfileAt(i);
+ uint16_t browser_services = source_profile.services_supported;
+
+ std::unique_ptr<base::DictionaryValue> browser_profile(
+ new base::DictionaryValue());
+ browser_profile->SetString("name", source_profile.importer_name);
+ browser_profile->SetInteger("index", i);
+ browser_profile->SetBoolean("history",
+ (browser_services & importer::HISTORY) != 0);
+ browser_profile->SetBoolean("favorites",
+ (browser_services & importer::FAVORITES) != 0);
+ browser_profile->SetBoolean("passwords",
+ (browser_services & importer::PASSWORDS) != 0);
+ browser_profile->SetBoolean("search",
+ (browser_services & importer::SEARCH_ENGINES) != 0);
+ browser_profile->SetBoolean("autofill-form-data",
+ (browser_services & importer::AUTOFILL_FORM_DATA) != 0);
+
+ browser_profiles.Append(std::move(browser_profile));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ImportDataOverlay.updateSupportedBrowsers", browser_profiles);
+}
+
+void ImportDataHandler::ImportStarted() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+void ImportDataHandler::ImportItemStarted(importer::ImportItem item) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ // TODO(csilv): show progress detail in the web view.
+}
+
+void ImportDataHandler::ImportItemEnded(importer::ImportItem item) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ // TODO(csilv): show progress detail in the web view.
+ import_did_succeed_ = true;
+}
+
+void ImportDataHandler::ImportEnded() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ importer_host_->set_observer(NULL);
+ importer_host_ = NULL;
+
+ if (import_did_succeed_) {
+ web_ui()->CallJavascriptFunctionUnsafe("ImportDataOverlay.confirmSuccess");
+ } else {
+ base::Value state(false);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ImportDataOverlay.setImportingState", state);
+ web_ui()->CallJavascriptFunctionUnsafe("ImportDataOverlay.dismiss");
+ }
+}
+
+void ImportDataHandler::FileSelected(const base::FilePath& path,
+ int /*index*/,
+ void* /*params*/) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ importer::SourceProfile source_profile;
+ source_profile.importer_type = importer::TYPE_BOOKMARKS_FILE;
+ source_profile.source_path = path;
+
+ StartImport(source_profile, importer::FAVORITES);
+}
+
+void ImportDataHandler::HandleChooseBookmarksFile(const base::ListValue* args) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ DCHECK(args && args->empty());
+ select_file_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
+
+ ui::SelectFileDialog::FileTypeInfo file_type_info;
+ file_type_info.extensions.resize(1);
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("html"));
+
+ Browser* browser =
+ chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
+
+ select_file_dialog_->SelectFile(ui::SelectFileDialog::SELECT_OPEN_FILE,
+ base::string16(),
+ base::FilePath(),
+ &file_type_info,
+ 0,
+ base::FilePath::StringType(),
+ browser->window()->GetNativeWindow(),
+ NULL);
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/import_data_handler.h b/chromium/chrome/browser/ui/webui/options/import_data_handler.h
new file mode 100644
index 00000000000..32bea2b08f3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/import_data_handler.h
@@ -0,0 +1,75 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_IMPORT_DATA_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_IMPORT_DATA_HANDLER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/importer/importer_progress_observer.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "chrome/common/importer/importer_data_types.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+
+class ExternalProcessImporterHost;
+class ImporterList;
+
+namespace options {
+
+// Chrome personal stuff import data overlay UI handler.
+class ImportDataHandler : public OptionsPageUIHandler,
+ public importer::ImporterProgressObserver,
+ public ui::SelectFileDialog::Listener {
+ public:
+ ImportDataHandler();
+ ~ImportDataHandler() override;
+
+ // OptionsPageUIHandler:
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializeHandler() override;
+ void InitializePage() override;
+
+ // content::WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ private:
+ void StartImport(const importer::SourceProfile& source_profile,
+ uint16_t imported_items);
+
+ void ImportData(const base::ListValue* args);
+
+ // importer::ImporterProgressObserver:
+ void ImportStarted() override;
+ void ImportItemStarted(importer::ImportItem item) override;
+ void ImportItemEnded(importer::ImportItem item) override;
+ void ImportEnded() override;
+
+ // ui::SelectFileDialog::Listener:
+ void FileSelected(const base::FilePath& path,
+ int index,
+ void* params) override;
+
+ // Opens a file selection dialog to choose the bookmarks HTML file.
+ void HandleChooseBookmarksFile(const base::ListValue* args);
+
+ std::unique_ptr<ImporterList> importer_list_;
+
+ // If non-null it means importing is in progress. ImporterHost takes care
+ // of deleting itself when import is complete.
+ ExternalProcessImporterHost* importer_host_; // weak
+
+ bool import_did_succeed_;
+
+ scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImportDataHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_IMPORT_DATA_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/language_dictionary_interactive_uitest.cc b/chromium/chrome/browser/ui/webui/options/language_dictionary_interactive_uitest.cc
new file mode 100644
index 00000000000..dbdad4f2af1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/language_dictionary_interactive_uitest.cc
@@ -0,0 +1,255 @@
+// 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.
+
+#include "base/macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/interactive_test_utils.h"
+#include "content/public/test/browser_test_utils.h"
+
+namespace {
+
+// This class tests the language dictionary settings.
+// This test is part of the interactive_ui_tests instead of browser_tests
+// because it is necessary to emulate pushing the tab key.
+class LanguageDictionaryWebUITest : public InProcessBrowserTest {
+ public:
+ LanguageDictionaryWebUITest() {}
+
+ // Navigate to the editDictionary page.
+ void SetUpOnMainThread() override {
+ disable_md_settings_.InitAndDisableFeature(
+ features::kMaterialDesignSettings);
+ const GURL url = chrome::GetSettingsUrl("editDictionary");
+ ui_test_utils::NavigateToURL(browser(), url);
+ }
+
+ protected:
+ const std::string kDictionaryListSelector =
+ "#language-dictionary-overlay-word-list";
+
+ content::RenderFrameHost* GetActiveFrame() {
+ return GetActiveWebContents()->GetFocusedFrame();
+ }
+
+ content::RenderViewHost* GetRenderViewHost() {
+ return GetActiveWebContents()->GetRenderViewHost();
+ }
+
+ content::WebContents* GetActiveWebContents() {
+ return browser()->tab_strip_model()->GetActiveWebContents();
+ }
+
+ // Add a few test words to the dictionary.
+ void SetTestWords(const std::string& list_selector) {
+ const std::string script = base::StringPrintf(
+ "document.querySelector('%s').setWordList(['cat', 'dog', 'bird']);",
+ list_selector.c_str());
+ EXPECT_TRUE(content::ExecuteScript(GetActiveFrame(), script));
+ // Expected list size is 4: 3 word items + 1 placeholder.
+ EXPECT_EQ(4, GetListSize(list_selector));
+ }
+
+ // Returns the number of items in the list.
+ int GetListSize(const std::string& list_selector) {
+ const std::string script = base::StringPrintf(
+ "domAutomationController.send("
+ "document.querySelector('%s').items.length);",
+ list_selector.c_str());
+ int length = -1;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractInt(
+ GetActiveFrame(),
+ script,
+ &length));
+ return length;
+ }
+
+ // Returns true if element contains document.activeElement.
+ bool ContainsActiveElement(const std::string& element_selector) {
+ const std::string script = base::StringPrintf(
+ "domAutomationController.send("
+ "document.querySelector('%s').contains(document.activeElement));",
+ element_selector.c_str());
+ bool result;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+ GetActiveFrame(),
+ script,
+ &result));
+ return result;
+ }
+
+ // Returns true if list item[|index|] contains document.activeElement.
+ bool ListItemContainsActiveElement(const std::string& list_selector,
+ int index) {
+ EXPECT_GE(index, 0);
+ // EXPECT_TRUE will fail if index is out of bounds.
+ const std::string script = base::StringPrintf(
+ "domAutomationController.send("
+ "document.querySelector('%s').items[%d].contains("
+ "document.activeElement));",
+ list_selector.c_str(),
+ index);
+ bool result;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+ GetActiveFrame(),
+ script,
+ &result));
+ return result;
+ }
+
+ // Returns true if list item[|index|] has 'selected' attribute.
+ bool ListItemSelected(const std::string& list_selector, int index) {
+ EXPECT_GE(index, 0);
+ // EXPECT_TRUE will fail if index is out of bounds.
+ const std::string script = base::StringPrintf(
+ "domAutomationController.send("
+ "document.querySelector('%s').items[%d].hasAttribute('selected'));",
+ list_selector.c_str(),
+ index);
+ bool result = false;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+ GetActiveFrame(),
+ script,
+ &result));
+ return result;
+ }
+
+ // Returns true if list item[|index|] has 'selected' attribute and contains
+ // document.activeElement.
+ bool ListItemSelectedAndFocused(const std::string& list_selector,
+ int index) {
+ EXPECT_GE(index, 0);
+ return ListItemSelected(list_selector, index) &&
+ ListItemContainsActiveElement(list_selector, index);
+ }
+
+ // Press and release a key in the browser. This will wait for the element on
+ // the page to change.
+ bool PressKey(ui::KeyboardCode key_code, bool shift) {
+ return ui_test_utils::SendKeyPressAndWait(
+ browser(),
+ key_code,
+ false,
+ shift,
+ false,
+ false,
+ content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
+ content::Source<content::RenderViewHost>(GetRenderViewHost()));
+ }
+
+ void InitializeDomMessageQueue() {
+ dom_message_queue_.reset(new content::DOMMessageQueue);
+ }
+
+ // Wait for a message from the DOM automation controller.
+ void WaitForDomMessage(const std::string& message) {
+ const std::string expected = "\"" + message + "\"";
+ std::string received;
+ do {
+ ASSERT_TRUE(dom_message_queue_->WaitForMessage(&received));
+ } while (received != expected);
+ }
+
+ // Add a JavaScript event listener to send a DOM automation controller message
+ // whenever the |selected| property of the list item changes.
+ void ListenForItemSelectedChange(const std::string& list_selector,
+ int index) {
+ EXPECT_GE(index, 0);
+ // EXPECT_TRUE will fail if index is out of bounds.
+ const std::string script = base::StringPrintf(
+ "document.querySelector('%s').items[%d].addEventListener("
+ "'selectedChange', function() {"
+ "domAutomationController.setAutomationId(0);"
+ "domAutomationController.send('selected=' + this.selected);"
+ "});",
+ list_selector.c_str(),
+ index);
+
+ EXPECT_TRUE(content::ExecuteScript(
+ GetActiveFrame(),
+ script));
+ }
+
+ private:
+ std::unique_ptr<content::DOMMessageQueue> dom_message_queue_;
+ base::test::ScopedFeatureList disable_md_settings_;
+
+ DISALLOW_COPY_AND_ASSIGN(LanguageDictionaryWebUITest);
+};
+
+} // namespace
+
+// Test InlineEditableItemList keyboard focus behavior in editDictionary
+// overlay.
+// editDictionary overlay doesn't exist on OSX so disable it there.
+#if !defined(OS_MACOSX)
+
+// Crashes on Win 7. http://crbug.com/500609
+#if defined(OS_WIN)
+#define MAYBE_TestListKeyboardFocus DISABLED_TestListKeyboardFocus
+#else
+#define MAYBE_TestListKeyboardFocus TestListKeyboardFocus
+#endif
+
+IN_PROC_BROWSER_TEST_F(LanguageDictionaryWebUITest,
+ MAYBE_TestListKeyboardFocus) {
+ const std::string list_selector = kDictionaryListSelector;
+
+ // Populate the list with some test words.
+ SetTestWords(list_selector);
+ int placeholder_index = GetListSize(list_selector) - 1;
+
+ // Listen for changes of the placeholder item's |selected| property so that
+ // test can wait until change has taken place after key press before
+ // continuing.
+ InitializeDomMessageQueue();
+ ListenForItemSelectedChange(list_selector, placeholder_index);
+
+ // Press tab to focus the placeholder.
+ PressKey(ui::VKEY_TAB, false);
+
+ // Wait for placeholder item to become selected.
+ WaitForDomMessage("selected=true");
+
+ // Verify that the placeholder is selected and has focus.
+ EXPECT_TRUE(ListItemSelectedAndFocused(list_selector, placeholder_index));
+
+ // Press up arrow to select item above the placeholder.
+ PressKey(ui::VKEY_UP, false);
+
+ // Wait for placeholder to become unselected.
+ WaitForDomMessage("selected=false");
+
+ // Verify that the placeholder is no longer selected.
+ EXPECT_FALSE(ListItemSelected(list_selector, placeholder_index));
+
+ // Verify that the item above the placeholder is selected and has focus.
+ EXPECT_TRUE(ListItemSelectedAndFocused(list_selector,
+ placeholder_index - 1));
+
+ // Press tab to leave the list.
+ PressKey(ui::VKEY_TAB, false);
+
+ // Verify that focus has left the list.
+ EXPECT_FALSE(ContainsActiveElement(list_selector));
+
+ // Verify that the item above the placeholder is still selected.
+ EXPECT_TRUE(ListItemSelected(list_selector, placeholder_index - 1));
+
+ // Press shift+tab to go back to the list.
+ PressKey(ui::VKEY_TAB, true);
+
+ // Verify that the item above the placeholder is selected and has focus.
+ EXPECT_TRUE(ListItemSelectedAndFocused(list_selector,
+ placeholder_index - 1));
+}
+#endif // !defined(OS_MACOSX)
diff --git a/chromium/chrome/browser/ui/webui/options/language_dictionary_overlay_handler.cc b/chromium/chrome/browser/ui/webui/options/language_dictionary_overlay_handler.cc
new file mode 100644
index 00000000000..247383effce
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/language_dictionary_overlay_handler.cc
@@ -0,0 +1,134 @@
+// 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/ui/webui/options/language_dictionary_overlay_handler.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/spellchecker/spellcheck_factory.h"
+#include "chrome/browser/spellchecker/spellcheck_service.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace options {
+
+LanguageDictionaryOverlayHandler::LanguageDictionaryOverlayHandler()
+ : overlay_initialized_(false),
+ dictionary_(NULL) {
+}
+
+LanguageDictionaryOverlayHandler::~LanguageDictionaryOverlayHandler() {
+}
+
+void LanguageDictionaryOverlayHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ RegisterTitle(localized_strings,
+ "languageDictionaryOverlayPage",
+ IDS_LANGUAGE_DICTIONARY_OVERLAY_TITLE);
+ localized_strings->SetString(
+ "languageDictionaryOverlayTitle",
+ l10n_util::GetStringUTF16(IDS_LANGUAGE_DICTIONARY_OVERLAY_TITLE));
+ localized_strings->SetString(
+ "languageDictionaryOverlayAddWordLabel",
+ l10n_util::GetStringUTF16(
+ IDS_LANGUAGE_DICTIONARY_OVERLAY_ADD_WORD_LABEL));
+ localized_strings->SetString(
+ "languageDictionaryOverlaySearchPlaceholder",
+ l10n_util::GetStringUTF16(
+ IDS_LANGUAGE_DICTIONARY_OVERLAY_SEARCH_PLACEHOLDER));
+ localized_strings->SetString(
+ "languageDictionaryOverlayNoMatches",
+ l10n_util::GetStringUTF16(IDS_LANGUAGE_DICTIONARY_OVERLAY_NO_MATCHES));
+}
+
+void LanguageDictionaryOverlayHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "addDictionaryWord",
+ base::Bind(&LanguageDictionaryOverlayHandler::AddWord,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "removeDictionaryWord",
+ base::Bind(&LanguageDictionaryOverlayHandler::RemoveWord,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "refreshDictionaryWords",
+ base::Bind(&LanguageDictionaryOverlayHandler::RefreshWords,
+ base::Unretained(this)));
+}
+
+void LanguageDictionaryOverlayHandler::Uninitialize() {
+ overlay_initialized_ = false;
+ if (dictionary_)
+ dictionary_->RemoveObserver(this);
+}
+
+void LanguageDictionaryOverlayHandler::OnCustomDictionaryLoaded() {
+ ResetDictionaryWords();
+}
+
+void LanguageDictionaryOverlayHandler::OnCustomDictionaryChanged(
+ const SpellcheckCustomDictionary::Change& dictionary_change) {
+ base::ListValue add_words;
+ for (const std::string& word : dictionary_change.to_add()) {
+ add_words.AppendString(word);
+ }
+
+ base::ListValue remove_words;
+ for (const std::string& word : dictionary_change.to_remove()) {
+ remove_words.AppendString(word);
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("EditDictionaryOverlay.updateWords",
+ add_words, remove_words);
+}
+
+void LanguageDictionaryOverlayHandler::ResetDictionaryWords() {
+ if (!overlay_initialized_)
+ return;
+
+ if (!dictionary_) {
+ SpellcheckService* service = SpellcheckServiceFactory::GetForContext(
+ Profile::FromWebUI(web_ui()));
+ dictionary_ = service->GetCustomDictionary();
+ dictionary_->AddObserver(this);
+ }
+
+ base::ListValue list_value;
+ for (const std::string& word : dictionary_->GetWords()) {
+ list_value.AppendString(word);
+ }
+ web_ui()->CallJavascriptFunctionUnsafe("EditDictionaryOverlay.setWordList",
+ list_value);
+}
+
+void LanguageDictionaryOverlayHandler::RefreshWords(
+ const base::ListValue* args) {
+ overlay_initialized_ = true;
+ ResetDictionaryWords();
+}
+
+void LanguageDictionaryOverlayHandler::AddWord(const base::ListValue* args) {
+ std::string new_word;
+ if (!args->GetString(0, &new_word) || new_word.empty() || !dictionary_) {
+ NOTREACHED();
+ return;
+ }
+ dictionary_->AddWord(new_word);
+}
+
+void LanguageDictionaryOverlayHandler::RemoveWord(const base::ListValue* args) {
+ std::string old_word;
+ if (!args->GetString(0, &old_word) || old_word.empty() || !dictionary_) {
+ NOTREACHED();
+ return;
+ }
+ dictionary_->RemoveWord(old_word);
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/language_dictionary_overlay_handler.h b/chromium/chrome/browser/ui/webui/options/language_dictionary_overlay_handler.h
new file mode 100644
index 00000000000..742272f386a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/language_dictionary_overlay_handler.h
@@ -0,0 +1,61 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_LANGUAGE_DICTIONARY_OVERLAY_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_LANGUAGE_DICTIONARY_OVERLAY_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/spellchecker/spellcheck_custom_dictionary.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+namespace options {
+
+class LanguageDictionaryOverlayHandler
+ : public OptionsPageUIHandler,
+ public SpellcheckCustomDictionary::Observer {
+ public:
+ LanguageDictionaryOverlayHandler();
+ ~LanguageDictionaryOverlayHandler() override;
+
+ // Overridden from OptionsPageUIHandler:
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void RegisterMessages() override;
+ void Uninitialize() override;
+
+ // Overridden from SpellcheckCustomDictionary::Observer:
+ void OnCustomDictionaryLoaded() override;
+ void OnCustomDictionaryChanged(
+ const SpellcheckCustomDictionary::Change& dictionary_change) override;
+
+ private:
+ // Sends the dictionary words to WebUI.
+ void ResetDictionaryWords();
+
+ // Refreshes the displayed words. Called from WebUI.
+ void RefreshWords(const base::ListValue* args);
+
+ // Adds a new word to the dictionary. Called from WebUI.
+ void AddWord(const base::ListValue* args);
+
+ // Removes a word from the dictionary. Called from WebUI.
+ void RemoveWord(const base::ListValue* args);
+
+ // Whether the overlay is initialized and ready to show data.
+ bool overlay_initialized_;
+
+ // The custom spelling dictionary. Used for adding, removing, and reading
+ // words in custom dictionary file.
+ SpellcheckCustomDictionary* dictionary_;
+
+ DISALLOW_COPY_AND_ASSIGN(LanguageDictionaryOverlayHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_LANGUAGE_DICTIONARY_OVERLAY_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/language_options_browsertest.js b/chromium/chrome/browser/ui/webui/options/language_options_browsertest.js
new file mode 100644
index 00000000000..bfd3acb62c9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/language_options_browsertest.js
@@ -0,0 +1,56 @@
+// 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.
+
+GEN_INCLUDE(['options_browsertest_base.js']);
+
+/**
+ * TestFixture for language options WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function LanguageOptionsWebUITest() {}
+
+LanguageOptionsWebUITest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /** @override */
+ browsePreload: 'chrome://settings-frame/languages',
+
+ /** @override */
+ setUp: function() {
+ OptionsBrowsertestBase.prototype.setUp.call(this);
+
+ // Enable when failure is resolved.
+ // AX_ARIA_10: http://crbug.com/559266
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'unsupportedAriaAttribute',
+ '#language-options-list');
+
+ // Enable when failure is resolved.
+ // AX_TEXT_04: http://crbug.com/559271
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'linkWithUnclearPurpose',
+ '#languagePage > .content-area > .language-options-header > A');
+ }
+};
+
+// Test opening language options has correct location.
+TEST_F('LanguageOptionsWebUITest', 'testOpenLanguageOptions', function() {
+ assertEquals(this.browsePreload, document.location.href);
+});
+
+GEN('#if defined(OS_WIN) || defined(OS_CHROMEOS)');
+// Test reselecting the same language as the current UI locale. This should show
+// a "Chrome is displayed in this language" message rather than a restart banner
+// or a [ Display Chrome in this language ] button.
+TEST_F('LanguageOptionsWebUITest', 'reselectUILocale', function() {
+ var currentLang = loadTimeData.getString('currentUiLanguageCode');
+ $('language-options-list').selectLanguageByCode(currentLang);
+ LanguageOptions.uiLanguageSaved(currentLang);
+
+ expectTrue($('language-options-ui-language-button').hidden);
+ expectFalse($('language-options-ui-language-message').hidden);
+ expectTrue($('language-options-ui-notification-bar').hidden);
+});
+GEN('#endif'); // defined(OS_WIN) || defined(OS_CHROMEOS)
diff --git a/chromium/chrome/browser/ui/webui/options/language_options_dictionary_download_browsertest.js b/chromium/chrome/browser/ui/webui/options/language_options_dictionary_download_browsertest.js
new file mode 100644
index 00000000000..174b32365a4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/language_options_dictionary_download_browsertest.js
@@ -0,0 +1,129 @@
+// 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.
+
+GEN_INCLUDE(['options_browsertest_base.js']);
+
+/**
+ * TestFixture for testing messages of dictionary download progress in language
+ * options WebUI.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function LanguagesOptionsDictionaryDownloadWebUITest() {}
+
+LanguagesOptionsDictionaryDownloadWebUITest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /**
+ * Browse to languages options.
+ */
+ browsePreload: 'chrome://settings-frame/languages',
+
+ /**
+ * Register a mock dictionary handler.
+ */
+ preLoad: function() {
+ this.makeAndRegisterMockHandler(['retryDictionaryDownload']);
+ this.mockHandler.stubs().retryDictionaryDownload().
+ will(callFunction(function() {
+ options.LanguageOptions.onDictionaryDownloadBegin('en-US');
+ }));
+ },
+
+ /** @override */
+ setUp: function() {
+ OptionsBrowsertestBase.prototype.setUp.call(this);
+
+ // Enable when failure is resolved.
+ // AX_ARIA_10: http://crbug.com/570554
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'unsupportedAriaAttribute',
+ '#language-options-list');
+
+ // Enable when failure is resolved.
+ // AX_TEXT_04: http://crbug.com/570553
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'linkWithUnclearPurpose',
+ '#languagePage > .content-area > .language-options-header > A');
+ },
+};
+
+// Verify that dictionary download success does not show, "This language can't
+// be used for spellchecking." or "Download failed."
+// Disabled due to flakiness (crbug.com/616550).
+TEST_F('LanguagesOptionsDictionaryDownloadWebUITest',
+ 'DISABLED_testdictionaryDownloadSuccess',
+ function() {
+ options.LanguageOptions.onDictionaryDownloadSuccess('en-US');
+ expectTrue($('spellcheck-language-message').hidden);
+ expectTrue($('language-options-dictionary-downloading-message').hidden);
+ expectTrue($('language-options-dictionary-download-failed-message').hidden);
+ expectTrue(
+ $('language-options-dictionary-download-fail-help-message').hidden);
+});
+
+// Verify that dictionary download in progress shows 'Downloading spell check
+// language' message.
+// Disabled due to flakiness (crbug.com/616550).
+TEST_F('LanguagesOptionsDictionaryDownloadWebUITest',
+ 'DISABLED_testdictionaryDownloadProgress',
+ function() {
+ options.LanguageOptions.onDictionaryDownloadBegin('en-US');
+ expectTrue($('spellcheck-language-message').hidden);
+ expectFalse($('language-options-dictionary-downloading-message').hidden);
+ expectTrue($('language-options-dictionary-download-failed-message').hidden);
+ expectTrue(
+ $('language-options-dictionary-download-fail-help-message').hidden);
+});
+
+// Verify that failure in dictionary download shows 'Dictionary download failed'
+// message.
+TEST_F('LanguagesOptionsDictionaryDownloadWebUITest',
+ 'testdictionaryDownloadFailed',
+ function() {
+ // Clear the failure counter:
+ options.LanguageOptions.onDictionaryDownloadSuccess('en-US');
+
+ // First failure shows a short error message.
+ options.LanguageOptions.onDictionaryDownloadFailure('en-US');
+ expectTrue($('spellcheck-language-message').hidden);
+ expectTrue($('language-options-dictionary-downloading-message').hidden);
+ expectFalse($('language-options-dictionary-download-failed-message').hidden);
+ expectTrue(
+ $('language-options-dictionary-download-fail-help-message').hidden);
+
+ // Second and all following failures show a longer error message.
+ options.LanguageOptions.onDictionaryDownloadFailure('en-US');
+ expectTrue($('spellcheck-language-message').hidden);
+ expectTrue($('language-options-dictionary-downloading-message').hidden);
+ expectFalse($('language-options-dictionary-download-failed-message').hidden);
+ expectFalse(
+ $('language-options-dictionary-download-fail-help-message').hidden);
+
+ options.LanguageOptions.onDictionaryDownloadFailure('en-US');
+ expectTrue($('spellcheck-language-message').hidden);
+ expectTrue($('language-options-dictionary-downloading-message').hidden);
+ expectFalse($('language-options-dictionary-download-failed-message').hidden);
+ expectFalse(
+ $('language-options-dictionary-download-fail-help-message').hidden);
+});
+
+// Verify that clicking the retry button calls the handler.
+// This test is flaky on Windows. https://crbug.com/616791
+GEN('#if defined(OS_WIN)');
+GEN('#define MAYBE_testdictionaryDownloadRetry ' +
+ 'DISABLED_testdictionaryDownloadRetry');
+GEN('#else');
+GEN('#define MAYBE_testdictionaryDownloadRetry testdictionaryDownloadRetry');
+GEN('#endif // defined(OS_WIN)');
+TEST_F('LanguagesOptionsDictionaryDownloadWebUITest',
+ 'MAYBE_testdictionaryDownloadRetry',
+ function() {
+ this.mockHandler.expects(once()).retryDictionaryDownload('en-US').
+ will(callFunction(function() {
+ options.LanguageOptions.onDictionaryDownloadBegin('en-US');
+ }));
+ options.LanguageOptions.onDictionaryDownloadFailure('en-US');
+ $('dictionary-download-retry-button').click();
+});
diff --git a/chromium/chrome/browser/ui/webui/options/language_options_handler.cc b/chromium/chrome/browser/ui/webui/options/language_options_handler.cc
new file mode 100644
index 00000000000..1549192785d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/language_options_handler.cc
@@ -0,0 +1,131 @@
+// 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/ui/webui/options/language_options_handler.h"
+
+#include <stddef.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/i18n/rtl.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using base::UserMetricsAction;
+
+namespace options {
+
+LanguageOptionsHandler::LanguageOptionsHandler() {
+}
+
+LanguageOptionsHandler::~LanguageOptionsHandler() {
+}
+
+void LanguageOptionsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ LanguageOptionsHandlerCommon::GetLocalizedValues(localized_strings);
+
+ RegisterTitle(localized_strings, "languagePage",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_DIALOG_TITLE);
+ localized_strings->SetString("restart_button",
+ l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_RELAUNCH_BUTTON));
+ localized_strings->Set("languageList", GetLanguageList());
+}
+
+void LanguageOptionsHandler::RegisterMessages() {
+ LanguageOptionsHandlerCommon::RegisterMessages();
+
+ web_ui()->RegisterMessageCallback("uiLanguageRestart",
+ base::Bind(&LanguageOptionsHandler::RestartCallback,
+ base::Unretained(this)));
+}
+
+std::unique_ptr<base::ListValue> LanguageOptionsHandler::GetLanguageList() {
+ // Collect the language codes from the supported accept-languages.
+ const std::string app_locale = g_browser_process->GetApplicationLocale();
+ std::vector<std::string> language_codes;
+ l10n_util::GetAcceptLanguagesForLocale(app_locale, &language_codes);
+
+ // Map of display name -> {language code, native_display_name}.
+ // In theory, we should be able to create a map that is sorted by
+ // display names using ICU comparator, but doing it is hard, thus we'll
+ // use an auxiliary vector to achieve the same result.
+ typedef std::pair<std::string, base::string16> LanguagePair;
+ typedef std::map<base::string16, LanguagePair> LanguageMap;
+ LanguageMap language_map;
+ // The auxiliary vector mentioned above.
+ std::vector<base::string16> display_names;
+
+ // Build the list of display names, and build the language map.
+ for (size_t i = 0; i < language_codes.size(); ++i) {
+ base::string16 display_name =
+ l10n_util::GetDisplayNameForLocale(language_codes[i], app_locale,
+ false);
+ base::string16 native_display_name =
+ l10n_util::GetDisplayNameForLocale(language_codes[i], language_codes[i],
+ false);
+ display_names.push_back(display_name);
+ language_map[display_name] =
+ std::make_pair(language_codes[i], native_display_name);
+ }
+ DCHECK_EQ(display_names.size(), language_map.size());
+
+ // Sort display names using locale specific sorter.
+ l10n_util::SortStrings16(app_locale, &display_names);
+
+ // Build the language list from the language map.
+ auto language_list = base::MakeUnique<base::ListValue>();
+ for (size_t i = 0; i < display_names.size(); ++i) {
+ base::string16& display_name = display_names[i];
+ base::string16 adjusted_display_name(display_name);
+ base::i18n::AdjustStringForLocaleDirection(&adjusted_display_name);
+
+ const LanguagePair& pair = language_map[display_name];
+ base::string16 adjusted_native_display_name(pair.second);
+ base::i18n::AdjustStringForLocaleDirection(&adjusted_native_display_name);
+
+ bool has_rtl_chars = base::i18n::StringContainsStrongRTLChars(display_name);
+ std::string directionality = has_rtl_chars ? "rtl" : "ltr";
+
+ std::unique_ptr<base::DictionaryValue> dictionary(
+ new base::DictionaryValue());
+ dictionary->SetString("code", pair.first);
+ dictionary->SetString("displayName", adjusted_display_name);
+ dictionary->SetString("textDirection", directionality);
+ dictionary->SetString("nativeDisplayName", adjusted_native_display_name);
+ language_list->Append(std::move(dictionary));
+ }
+
+ return language_list;
+}
+
+void LanguageOptionsHandler::SetApplicationLocale(
+ const std::string& language_code) {
+ PrefService* pref_service = g_browser_process->local_state();
+ pref_service->SetString(prefs::kApplicationLocale, language_code);
+}
+
+void LanguageOptionsHandler::RestartCallback(const base::ListValue* args) {
+ base::RecordAction(UserMetricsAction("LanguageOptions_Restart"));
+ chrome::AttemptRestart();
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/language_options_handler.h b/chromium/chrome/browser/ui/webui/options/language_options_handler.h
new file mode 100644
index 00000000000..93943cd6744
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/language_options_handler.h
@@ -0,0 +1,52 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_LANGUAGE_OPTIONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_LANGUAGE_OPTIONS_HANDLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/language_options_handler_common.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace options {
+
+// Language options UI page handler for non-Chrome OS platforms. For Chrome OS,
+// see chromeos::CrosLanguageOptionsHandler.
+class LanguageOptionsHandler : public LanguageOptionsHandlerCommon {
+ public:
+ LanguageOptionsHandler();
+ ~LanguageOptionsHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // The following static method is public for ease of testing.
+
+ // Gets the list of languages from the given input descriptors.
+ // The return value will look like:
+ // [{'code': 'fi', 'displayName': 'Finnish', 'nativeDisplayName': 'suomi'},
+ // ...]
+ static std::unique_ptr<base::ListValue> GetLanguageList();
+
+ private:
+ // LanguageOptionsHandlerCommon implementation.
+ void SetApplicationLocale(const std::string& language_code) override;
+
+ // Called when the restart button is clicked.
+ void RestartCallback(const base::ListValue* args);
+
+ DISALLOW_COPY_AND_ASSIGN(LanguageOptionsHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_LANGUAGE_OPTIONS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/language_options_handler_common.cc b/chromium/chrome/browser/ui/webui/options/language_options_handler_common.cc
new file mode 100644
index 00000000000..fd127384e77
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/language_options_handler_common.cc
@@ -0,0 +1,302 @@
+// 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/ui/webui/options/language_options_handler_common.h"
+
+#include <stddef.h>
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/spellchecker/spellcheck_factory.h"
+#include "chrome/browser/spellchecker/spellcheck_service.h"
+#include "chrome/browser/translate/chrome_translate_client.h"
+#include "chrome/browser/translate/translate_service.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.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"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using base::UserMetricsAction;
+
+namespace options {
+
+LanguageOptionsHandlerCommon::LanguageOptionsHandlerCommon() {
+}
+
+LanguageOptionsHandlerCommon::~LanguageOptionsHandlerCommon() {
+}
+
+void LanguageOptionsHandlerCommon::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+#if defined(OS_CHROMEOS)
+ const int product_id = IDS_PRODUCT_OS_NAME;
+#else
+ const int product_id = IDS_PRODUCT_NAME;
+#endif
+
+ static OptionsStringResource resources[] = {
+ { "addButton", IDS_OPTIONS_SETTINGS_LANGUAGES_ADD_BUTTON },
+ { "languages", IDS_OPTIONS_SETTINGS_LANGUAGES_LANGUAGES },
+ { "addLanguageInstructions",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_ADD_LANGUAGE_INSTRUCTIONS },
+ { "cannotBeDisplayedInThisLanguage",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_CANNOT_BE_DISPLAYED_IN_THIS_LANGUAGE,
+ product_id },
+ { "isDisplayedInThisLanguage",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_IS_DISPLAYED_IN_THIS_LANGUAGE,
+ product_id },
+ { "displayInThisLanguage",
+ IDS_OPTIONS_SETTINGS_LANGUAGES_DISPLAY_IN_THIS_LANGUAGE,
+ product_id },
+ { "restartRequired", IDS_OPTIONS_RELAUNCH_REQUIRED },
+ // OS X uses the OS native spellchecker so no need for these strings.
+#if !defined(OS_MACOSX)
+ { "useThisForSpellChecking",
+ IDS_OPTIONS_SETTINGS_USE_THIS_FOR_SPELL_CHECKING },
+ { "cannotBeUsedForSpellChecking",
+ IDS_OPTIONS_SETTINGS_CANNOT_BE_USED_FOR_SPELL_CHECKING },
+ { "enableSpellCheck", IDS_OPTIONS_ENABLE_SPELLCHECK },
+ { "downloadingDictionary", IDS_OPTIONS_DICTIONARY_DOWNLOADING },
+ { "downloadFailed", IDS_OPTIONS_DICTIONARY_DOWNLOAD_FAILED },
+ { "retryButton", IDS_OPTIONS_DICTIONARY_DOWNLOAD_RETRY },
+ { "downloadFailHelp", IDS_OPTIONS_DICTIONARY_DOWNLOAD_FAIL_HELP },
+#endif // !OS_MACOSX
+ { "addLanguageTitle", IDS_OPTIONS_LANGUAGES_ADD_TITLE },
+ { "addLanguageSelectLabel", IDS_OPTIONS_LANGUAGES_ADD_SELECT_LABEL },
+ { "restartButton", IDS_OPTIONS_SETTINGS_LANGUAGES_RELAUNCH_BUTTON },
+ { "offerToTranslateInThisLanguage",
+ IDS_OPTIONS_LANGUAGES_OFFER_TO_TRANSLATE_IN_THIS_LANGUAGE },
+ { "cannotTranslateInThisLanguage",
+ IDS_OPTIONS_LANGUAGES_CANNOT_TRANSLATE_IN_THIS_LANGUAGE },
+ };
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+
+ // The following are resources, rather than local strings.
+ std::string application_locale = g_browser_process->GetApplicationLocale();
+ localized_strings->SetString("currentUiLanguageCode", application_locale);
+ std::string prospective_locale =
+ g_browser_process->local_state()->GetString(prefs::kApplicationLocale);
+ localized_strings->SetString("prospectiveUiLanguageCode",
+ !prospective_locale.empty() ? prospective_locale : application_locale);
+ localized_strings->Set("spellCheckLanguageCodeSet",
+ GetSpellCheckLanguageCodeSet());
+ localized_strings->Set("uiLanguageCodeSet", GetUILanguageCodeSet());
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ PrefService* prefs = profile->GetPrefs();
+ std::string default_target_language =
+ TranslateService::GetTargetLanguage(prefs);
+ localized_strings->SetString("defaultTargetLanguage",
+ default_target_language);
+
+ std::vector<std::string> languages;
+ translate::TranslateDownloadManager::GetSupportedLanguages(&languages);
+
+ auto languages_list = base::MakeUnique<base::ListValue>();
+ for (std::vector<std::string>::iterator it = languages.begin();
+ it != languages.end(); ++it) {
+ languages_list->AppendString(*it);
+ }
+
+ localized_strings->Set("translateSupportedLanguages",
+ std::move(languages_list));
+}
+
+void LanguageOptionsHandlerCommon::Uninitialize() {
+ SpellcheckService* service = GetSpellcheckService();
+ if (!service)
+ return;
+
+ for (const auto& dict : service->GetHunspellDictionaries())
+ dict->RemoveObserver(this);
+}
+
+void LanguageOptionsHandlerCommon::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("languageOptionsOpen",
+ base::Bind(
+ &LanguageOptionsHandlerCommon::LanguageOptionsOpenCallback,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("spellCheckLanguageChange",
+ base::Bind(
+ &LanguageOptionsHandlerCommon::SpellCheckLanguageChangeCallback,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("uiLanguageChange",
+ base::Bind(
+ &LanguageOptionsHandlerCommon::UiLanguageChangeCallback,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("retryDictionaryDownload",
+ base::Bind(
+ &LanguageOptionsHandlerCommon::RetrySpellcheckDictionaryDownload,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("updateLanguageList",
+ base::Bind(
+ &LanguageOptionsHandlerCommon::UpdateLanguageListCallback,
+ base::Unretained(this)));
+}
+
+void LanguageOptionsHandlerCommon::OnHunspellDictionaryInitialized(
+ const std::string& language) {
+}
+
+void LanguageOptionsHandlerCommon::OnHunspellDictionaryDownloadBegin(
+ const std::string& language) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.LanguageOptions.onDictionaryDownloadBegin",
+ base::Value(language));
+}
+
+void LanguageOptionsHandlerCommon::OnHunspellDictionaryDownloadSuccess(
+ const std::string& language) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.LanguageOptions.onDictionaryDownloadSuccess",
+ base::Value(language));
+}
+
+void LanguageOptionsHandlerCommon::OnHunspellDictionaryDownloadFailure(
+ const std::string& language) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.LanguageOptions.onDictionaryDownloadFailure",
+ base::Value(language));
+}
+
+std::unique_ptr<base::DictionaryValue>
+LanguageOptionsHandlerCommon::GetUILanguageCodeSet() {
+ auto dictionary = base::MakeUnique<base::DictionaryValue>();
+ const std::vector<std::string>& available_locales =
+ l10n_util::GetAvailableLocales();
+ for (size_t i = 0; i < available_locales.size(); ++i)
+ dictionary->SetBoolean(available_locales[i], true);
+ return dictionary;
+}
+
+std::unique_ptr<base::DictionaryValue>
+LanguageOptionsHandlerCommon::GetSpellCheckLanguageCodeSet() {
+ auto dictionary = base::MakeUnique<base::DictionaryValue>();
+ std::vector<std::string> spell_check_languages;
+ spellcheck::SpellCheckLanguages(&spell_check_languages);
+ for (size_t i = 0; i < spell_check_languages.size(); ++i) {
+ dictionary->SetBoolean(spell_check_languages[i], true);
+ }
+ return dictionary;
+}
+
+void LanguageOptionsHandlerCommon::LanguageOptionsOpenCallback(
+ const base::ListValue* args) {
+ base::RecordAction(UserMetricsAction("LanguageOptions_Open"));
+ SpellcheckService* service = GetSpellcheckService();
+ if (!service)
+ return;
+
+ for (const auto& dictionary : service->GetHunspellDictionaries()) {
+ dictionary->RemoveObserver(this);
+ dictionary->AddObserver(this);
+
+ if (dictionary->IsDownloadInProgress())
+ OnHunspellDictionaryDownloadBegin(dictionary->GetLanguage());
+ else if (dictionary->IsDownloadFailure())
+ OnHunspellDictionaryDownloadFailure(dictionary->GetLanguage());
+ else
+ OnHunspellDictionaryDownloadSuccess(dictionary->GetLanguage());
+ }
+}
+
+void LanguageOptionsHandlerCommon::UiLanguageChangeCallback(
+ const base::ListValue* args) {
+ const std::string language_code =
+ base::UTF16ToASCII(ExtractStringValue(args));
+ CHECK(!language_code.empty());
+ const std::string action = base::StringPrintf(
+ "LanguageOptions_UiLanguageChange_%s", language_code.c_str());
+ base::RecordComputedAction(action);
+ SetApplicationLocale(language_code);
+ base::Value language_value(language_code);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.LanguageOptions.uiLanguageSaved", language_value);
+}
+
+void LanguageOptionsHandlerCommon::SpellCheckLanguageChangeCallback(
+ const base::ListValue* args) {
+ const std::string language_code =
+ base::UTF16ToASCII(ExtractStringValue(args));
+ const std::string action = base::StringPrintf(
+ "LanguageOptions_SpellCheckLanguageChange_%s", language_code.c_str());
+ base::RecordComputedAction(action);
+
+ SpellcheckService* service = GetSpellcheckService();
+ if (!service)
+ return;
+
+ for (const auto& dictionary : service->GetHunspellDictionaries()) {
+ dictionary->RemoveObserver(this);
+ dictionary->AddObserver(this);
+ }
+}
+
+void LanguageOptionsHandlerCommon::UpdateLanguageListCallback(
+ const base::ListValue* args) {
+ CHECK_EQ(args->GetSize(), 1u);
+ const base::ListValue* language_list;
+ args->GetList(0, &language_list);
+ DCHECK(language_list);
+
+ std::vector<std::string> languages;
+ for (base::ListValue::const_iterator it = language_list->begin();
+ it != language_list->end(); ++it) {
+ std::string lang;
+ it->GetAsString(&lang);
+ languages.push_back(lang);
+ }
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ std::unique_ptr<translate::TranslatePrefs> translate_prefs =
+ ChromeTranslateClient::CreateTranslatePrefs(profile->GetPrefs());
+ translate_prefs->UpdateLanguageList(languages);
+}
+
+void LanguageOptionsHandlerCommon::RetrySpellcheckDictionaryDownload(
+ const base::ListValue* args) {
+ std::string language = base::UTF16ToUTF8(ExtractStringValue(args));
+ SpellcheckService* service = GetSpellcheckService();
+ if (!service)
+ return;
+
+ for (const auto& dictionary : service->GetHunspellDictionaries()) {
+ if (dictionary->GetLanguage() == language) {
+ dictionary->RetryDownloadDictionary(
+ Profile::FromWebUI(web_ui())->GetRequestContext());
+ return;
+ }
+ }
+}
+
+SpellcheckService* LanguageOptionsHandlerCommon::GetSpellcheckService() {
+ return SpellcheckServiceFactory::GetForContext(Profile::FromWebUI(web_ui()));
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/language_options_handler_common.h b/chromium/chrome/browser/ui/webui/options/language_options_handler_common.h
new file mode 100644
index 00000000000..18e33557576
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/language_options_handler_common.h
@@ -0,0 +1,96 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_LANGUAGE_OPTIONS_HANDLER_COMMON_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_LANGUAGE_OPTIONS_HANDLER_COMMON_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+namespace options {
+
+// The base class for language options page UI handlers. This class has code
+// common to the Chrome OS and non-Chrome OS implementation of the handler.
+class LanguageOptionsHandlerCommon
+ : public OptionsPageUIHandler,
+ public SpellcheckHunspellDictionary::Observer {
+ public:
+ LanguageOptionsHandlerCommon();
+ ~LanguageOptionsHandlerCommon() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void Uninitialize() override;
+
+ // DOMMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // SpellcheckHunspellDictionary::Observer implementation.
+ void OnHunspellDictionaryInitialized(const std::string& language) override;
+ void OnHunspellDictionaryDownloadBegin(const std::string& language) override;
+ void OnHunspellDictionaryDownloadSuccess(
+ const std::string& language) override;
+ void OnHunspellDictionaryDownloadFailure(
+ const std::string& language) override;
+
+ // The following static methods are public for ease of testing.
+
+ // Gets the set of language codes that can be used as UI language.
+ // The return value will look like:
+ // {'en-US': true, 'fi': true, 'fr': true, ...}
+ //
+ // Note that true in values does not mean anything. We just use the
+ // dictionary as a set.
+ static std::unique_ptr<base::DictionaryValue> GetUILanguageCodeSet();
+
+ // Gets the set of language codes that can be used for spellchecking.
+ // The return value will look like:
+ // {'en-US': true, 'fi': true, 'fr': true, ...}
+ //
+ // Note that true in values does not mean anything. We just use the
+ // dictionary as a set.
+ static std::unique_ptr<base::DictionaryValue> GetSpellCheckLanguageCodeSet();
+
+ private:
+ // Sets the application locale.
+ virtual void SetApplicationLocale(const std::string& language_code) = 0;
+
+ // Called when the language options is opened.
+ void LanguageOptionsOpenCallback(const base::ListValue* args);
+
+ // Called when the UI language is changed.
+ // |args| will contain the language code as string (ex. "fr").
+ void UiLanguageChangeCallback(const base::ListValue* args);
+
+ // Called when the spell check language is changed.
+ // |args| will contain the language code as string (ex. "fr").
+ void SpellCheckLanguageChangeCallback(const base::ListValue* args);
+
+ // Called when the user clicks "Retry" button for a spellcheck dictionary that
+ // has failed to download. |args| will contain the language code as a string
+ // (ex. "fr").
+ void RetrySpellcheckDictionaryDownload(const base::ListValue* args);
+
+ // Called when the user saves the language list preferences.
+ void UpdateLanguageListCallback(const base::ListValue* args);
+
+ // Returns a pointer to the browser SpellcheckService. Can be null if the
+ // service has already shut down.
+ SpellcheckService* GetSpellcheckService();
+
+ DISALLOW_COPY_AND_ASSIGN(LanguageOptionsHandlerCommon);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_LANGUAGE_OPTIONS_HANDLER_COMMON_H_
diff --git a/chromium/chrome/browser/ui/webui/options/language_options_handler_unittest.cc b/chromium/chrome/browser/ui/webui/options/language_options_handler_unittest.cc
new file mode 100644
index 00000000000..e290a9874d7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/language_options_handler_unittest.cc
@@ -0,0 +1,28 @@
+// 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/ui/webui/options/language_options_handler.h"
+
+#include <string>
+
+#include "base/values.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if !defined(OS_MACOSX)
+TEST(LanguageOptionsHandlerTest, GetUILanguageCodeSet) {
+ std::unique_ptr<base::DictionaryValue> dictionary(
+ options::LanguageOptionsHandler::GetUILanguageCodeSet());
+ EXPECT_TRUE(dictionary->HasKey("en-US"));
+ // Note that we don't test a false case, as such an expectation will
+ // fail when we add support for the language.
+ // EXPECT_FALSE(dictionary->HasKey("no"));
+}
+#endif // !defined(OS_MACOSX)
+
+TEST(LanguageOptionsHandlerTest, GetSpellCheckLanguageCodeSet) {
+ std::unique_ptr<base::DictionaryValue> dictionary(
+ options::LanguageOptionsHandler::GetSpellCheckLanguageCodeSet());
+ EXPECT_TRUE(dictionary->HasKey("en-US"));
+}
diff --git a/chromium/chrome/browser/ui/webui/options/language_options_interactive_uitest.cc b/chromium/chrome/browser/ui/webui/options/language_options_interactive_uitest.cc
new file mode 100644
index 00000000000..e9fa0e27030
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/language_options_interactive_uitest.cc
@@ -0,0 +1,166 @@
+// 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/macros.h"
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/interactive_test_utils.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+
+namespace language_options_ui_test {
+
+namespace {
+
+// This class will test the language options settings.
+// This test is part of the interactive_ui_tests isntead of browser_tests
+// because it is necessary to emulate pushing a button in order to properly
+// test accessibility.
+class LanguageOptionsWebUITest : public InProcessBrowserTest {
+ public:
+ LanguageOptionsWebUITest() {}
+
+ void SetUpInProcessBrowserTestFixture() override {
+ disable_md_settings_.InitAndDisableFeature(
+ features::kMaterialDesignSettings);
+ }
+
+ // This method will navigate to the language settings page and show
+ // a subset of languages from the list of available languages.
+ void SetUpOnMainThread() override {
+#if defined(OS_CHROMEOS)
+ auto* setting_name = prefs::kLanguagePreferredLanguages;
+#else
+ auto* setting_name = prefs::kAcceptLanguages;
+#endif
+
+ const GURL url = chrome::GetSettingsUrl(chrome::kLanguageOptionsSubPage);
+ ui_test_utils::NavigateToURL(browser(), url);
+ browser()->profile()->GetPrefs()->SetString(setting_name, "en-US,es,fr");
+ }
+
+ protected:
+ // Will get the id of the element in the UI that has focus.
+ std::string GetActiveElementId() {
+ std::string get_element_id_script =
+ "domAutomationController.send(document.activeElement.id);";
+ std::string element_id;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractString(
+ GetActiveFrame(),
+ get_element_id_script,
+ &element_id));
+ return element_id;
+ }
+
+ content::RenderFrameHost* GetActiveFrame() {
+ return GetActiveWebContents()->GetFocusedFrame();
+ }
+
+ content::RenderViewHost* GetRenderViewHost() {
+ return GetActiveWebContents()->GetRenderViewHost();
+ }
+
+ content::WebContents* GetActiveWebContents() {
+ return browser()->tab_strip_model()->GetActiveWebContents();
+ }
+
+ // Press and release a key in the browser. This will wait for the element on
+ // the page to change.
+ bool PressKey(ui::KeyboardCode key_code) {
+ return ui_test_utils::SendKeyPressAndWait(
+ browser(),
+ key_code,
+ false,
+ false,
+ false,
+ false,
+ content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
+ content::Source<content::RenderViewHost>(GetRenderViewHost()));
+ }
+
+ private:
+ base::test::ScopedFeatureList disable_md_settings_;
+
+ DISALLOW_COPY_AND_ASSIGN(LanguageOptionsWebUITest);
+};
+
+} // namespace
+
+// This test will verify that the appropriate languages are available.
+// This test will also fail if the language page is not loaded because a random
+// page will not have the language list.
+// Test assumes that the default active element is the list of languages.
+IN_PROC_BROWSER_TEST_F(LanguageOptionsWebUITest, TestAvailableLanguages) {
+ // Verify that the language list is focused by default.
+ std::string original_id = GetActiveElementId();
+ EXPECT_EQ("language-options-list", original_id);
+
+ content::RenderFrameHost* active_frame = GetActiveFrame();
+
+ std::string count_deletable_items_script =
+ "domAutomationController.send("
+ " document.activeElement.querySelectorAll('.deletable-item').length);";
+
+ // Count the number of languages in the list.
+ int language_count = 0;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
+ active_frame,
+ count_deletable_items_script,
+ &language_count));
+ EXPECT_EQ(3, language_count);
+
+ std::string get_children_of_current_element_script =
+ "domAutomationController.send(document.activeElement.textContent);";
+
+ // Verify that the correct languages are added to the list.
+ std::string languages;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ active_frame,
+ get_children_of_current_element_script,
+ &languages));
+ EXPECT_EQ("English (United States)SpanishFrench", languages);
+}
+
+// This test will validate that the language webui is accessible through
+// the keyboard.
+// This test must be updated if the tab order of the elements on this page
+// is changed.
+
+// Crashes on Win 7. http://crbug.com/500609
+#if defined(OS_WIN)
+#define MAYBE_TestListTabAccessibility DISABLED_TestListTabAccessibility
+#else
+#define MAYBE_TestListTabAccessibility TestListTabAccessibility
+#endif
+
+IN_PROC_BROWSER_TEST_F(LanguageOptionsWebUITest,
+ MAYBE_TestListTabAccessibility) {
+ // Verify that the language list is focused by default.
+ std::string original_id = GetActiveElementId();
+ EXPECT_EQ("language-options-list", original_id);
+
+ // Press tab to select the next element.
+ ASSERT_TRUE(PressKey(ui::VKEY_TAB));
+
+ // Make sure that the element is now the button that is next in the tab order.
+ // Checking that the list is no longer selected is not sufficient to validate
+ // this use case because this test should fail if an item inside the list is
+ // selected.
+ std::string new_id = GetActiveElementId();
+ EXPECT_EQ("language-options-add-button", new_id);
+}
+
+} // namespace language_options_ui_test
+
diff --git a/chromium/chrome/browser/ui/webui/options/manage_profile_browsertest.js b/chromium/chrome/browser/ui/webui/options/manage_profile_browsertest.js
new file mode 100644
index 00000000000..d4144e03322
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/manage_profile_browsertest.js
@@ -0,0 +1,672 @@
+// 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.
+
+// None of these tests is relevant for Chrome OS.
+GEN('#if !defined(OS_CHROMEOS)');
+
+/**
+ * TestFixture for ManageProfileOverlay and CreateProfileOverlay WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function ManageProfileUITest() {}
+
+ManageProfileUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /** @override */
+ browsePreload: 'chrome://settings-frame/manageProfile',
+
+ /**
+ * No need to run these for every OptionsPage test, since they'll cover the
+ * whole consolidated page each time.
+ * @override
+ */
+ runAccessibilityChecks: false,
+
+ /**
+ * Some default profile infos.
+ */
+ defaultIconURLs: [],
+ defaultNames: [],
+
+ /**
+ * Returns a test profile-info object with configurable "supervised" status.
+ * @param {boolean} supervised If true, the test profile will be marked as
+ * supervised.
+ * @return {Object} A test profile-info object.
+ */
+ testProfileInfo_: function(supervised) {
+ return {
+ name: 'Test Profile',
+ iconURL: 'chrome://path/to/icon/image',
+ filePath: '/path/to/profile/data/on/disk',
+ isCurrentProfile: true,
+ isSupervised: supervised
+ };
+ },
+
+ /**
+ * Overrides WebUI methods that provide profile info, making them return a
+ * test profile-info object.
+ * @param {boolean} supervised Whether the test profile should be marked
+ * as supervised.
+ * @param {string} mode The mode of the overlay (either 'manage' or 'create').
+ */
+ setProfileSupervised_: function(supervised, mode) {
+ // Override the BrowserOptions method to return the fake info.
+ BrowserOptions.getCurrentProfile = function() {
+ return this.testProfileInfo_(supervised);
+ }.bind(this);
+ // Set the profile info in the overlay.
+ ManageProfileOverlay.setProfileInfo(this.testProfileInfo_(supervised),
+ mode);
+ },
+
+ /**
+ * Set some default profile infos (icon URLs and names).
+ * @param {boolean} supervised Whether the test profile should be marked as
+ * supervised.
+ * @param {string} mode The mode of the overlay (either 'manage' or 'create').
+ */
+ initDefaultProfiles_: function(mode) {
+ PageManager.showPageByName(mode + 'Profile');
+
+ var defaultProfile = {
+ name: 'Default Name',
+ iconURL: '/default/path',
+ };
+ this.defaultIconURLs = ['/some/path',
+ defaultProfile.iconURL,
+ '/another/path',
+ '/one/more/path'];
+ this.defaultNames = ['Some Name', defaultProfile.name, '', 'Another Name'];
+ ManageProfileOverlay.receiveDefaultProfileIconsAndNames(
+ mode, this.defaultIconURLs, this.defaultNames);
+ ManageProfileOverlay.receiveNewProfileDefaults(defaultProfile);
+
+ // Make sure the correct item in the icon grid was selected.
+ var gridEl = $(mode + '-profile-icon-grid');
+ expectEquals(defaultProfile.iconURL, gridEl.selectedItem);
+ },
+};
+
+// Receiving the new profile defaults in the manage-user overlay shouldn't mess
+// up the focus in a visible higher-level overlay.
+TEST_F('ManageProfileUITest', 'NewProfileDefaultsFocus', function() {
+ var self = this;
+
+ function checkFocus(pageName, expectedFocus, initialFocus) {
+ PageManager.showPageByName(pageName);
+ initialFocus.focus();
+ expectEquals(initialFocus, document.activeElement, pageName);
+
+ ManageProfileOverlay.receiveNewProfileDefaults(
+ self.testProfileInfo_(false));
+ expectEquals(expectedFocus, document.activeElement, pageName);
+ PageManager.closeOverlay();
+ }
+
+ // Receiving new profile defaults sets focus to the name field if the create
+ // overlay is open, and should not change focus at all otherwise.
+ checkFocus('manageProfile',
+ $('manage-profile-cancel'),
+ $('manage-profile-cancel'));
+ checkFocus('createProfile',
+ $('create-profile-name'),
+ $('create-profile-cancel'));
+ checkFocus('supervisedUserLearnMore',
+ $('supervised-user-learn-more-done'),
+ $('supervised-user-learn-more-done'));
+ checkFocus('supervisedUserLearnMore',
+ document.querySelector('#supervised-user-learn-more-text a'),
+ document.querySelector('#supervised-user-learn-more-text a'));
+});
+
+// The default options should be reset each time the creation overlay is shown.
+TEST_F('ManageProfileUITest', 'DefaultCreateOptions', function() {
+ PageManager.showPageByName('createProfile');
+ var shortcutsAllowed = loadTimeData.getBoolean('profileShortcutsEnabled');
+ var createShortcut = $('create-shortcut');
+ var createSupervised = $('create-profile-supervised');
+ assertEquals(shortcutsAllowed, createShortcut.checked);
+ assertFalse(createSupervised.checked);
+
+ createShortcut.checked = !shortcutsAllowed;
+ createSupervised.checked = true;
+ PageManager.closeOverlay();
+ PageManager.showPageByName('createProfile');
+ assertEquals(shortcutsAllowed, createShortcut.checked);
+ assertFalse(createSupervised.checked);
+});
+
+// The checkbox label should change depending on whether the user is signed in.
+TEST_F('ManageProfileUITest', 'CreateSupervisedUserText', function() {
+ var signedInText = $('create-profile-supervised-signed-in');
+ var notSignedInText = $('create-profile-supervised-not-signed-in');
+
+ ManageProfileOverlay.getInstance().initializePage();
+
+ var custodianEmail = 'chrome.playpen.test@gmail.com';
+ CreateProfileOverlay.updateSignedInStatus(custodianEmail);
+ assertEquals(custodianEmail,
+ CreateProfileOverlay.getInstance().signedInEmail_);
+ assertFalse(signedInText.hidden);
+ assertTrue(notSignedInText.hidden);
+ // Make sure the email is in the string somewhere, without depending on the
+ // exact details of the message.
+ assertNotEquals(-1, signedInText.textContent.indexOf(custodianEmail));
+
+ CreateProfileOverlay.updateSignedInStatus('');
+ assertEquals('', CreateProfileOverlay.getInstance().signedInEmail_);
+ assertTrue(signedInText.hidden);
+ assertFalse(notSignedInText.hidden);
+ assertFalse($('create-profile-supervised').checked);
+ assertTrue($('create-profile-supervised').disabled);
+});
+
+function ManageProfileUITestAsync() {}
+
+ManageProfileUITestAsync.prototype = {
+ __proto__: ManageProfileUITest.prototype,
+
+ isAsync: true,
+};
+
+// The import link should show up if the user tries to create a profile with the
+// same name as an existing supervised user profile.
+TEST_F('ManageProfileUITestAsync', 'CreateExistingSupervisedUser', function() {
+ // Initialize the list of existing supervised users.
+ var supervisedUsers = [
+ {
+ id: 'supervisedUser1',
+ name: 'Rosalie',
+ iconURL: 'chrome://path/to/icon/image',
+ onCurrentDevice: false,
+ needAvatar: false
+ },
+ {
+ id: 'supervisedUser2',
+ name: 'Fritz',
+ iconURL: 'chrome://path/to/icon/image',
+ onCurrentDevice: false,
+ needAvatar: true
+ },
+ {
+ id: 'supervisedUser3',
+ name: 'Test',
+ iconURL: 'chrome://path/to/icon/image',
+ onCurrentDevice: true,
+ needAvatar: false
+ },
+ {
+ id: 'supervisedUser4',
+ name: 'RepeatingName',
+ iconURL: 'chrome://path/to/icon/image',
+ onCurrentDevice: true,
+ needAvatar: false
+ },
+ {
+ id: 'supervisedUser5',
+ name: 'RepeatingName',
+ iconURL: 'chrome://path/to/icon/image',
+ onCurrentDevice: false,
+ needAvatar: false
+ }];
+ var promise = Promise.resolve(supervisedUsers);
+ options.SupervisedUserListData.getInstance().promise_ = promise;
+
+ // Initialize the ManageProfileOverlay.
+ ManageProfileOverlay.getInstance().initializePage();
+ var custodianEmail = 'chrome.playpen.test@gmail.com';
+ CreateProfileOverlay.updateSignedInStatus(custodianEmail);
+ assertEquals(custodianEmail,
+ CreateProfileOverlay.getInstance().signedInEmail_);
+ this.setProfileSupervised_(false, 'create');
+
+ // Also add the names 'Test' and 'RepeatingName' to |existingProfileNames_| to
+ // simulate that profiles with those names exist on the device.
+ ManageProfileOverlay.getInstance().existingProfileNames_.Test = true;
+ ManageProfileOverlay.getInstance().existingProfileNames_.RepeatingName = true;
+
+ // Initially, the ok button should be enabled and the import link should not
+ // exist.
+ assertFalse($('create-profile-ok').disabled);
+ assertTrue($('supervised-user-import-existing') == null);
+
+ // Now try to create profiles with the names of existing supervised users.
+ $('create-profile-supervised').checked = true;
+ var nameField = $('create-profile-name');
+ // A profile which already has an avatar.
+ nameField.value = 'Rosalie';
+ ManageProfileOverlay.getInstance().onNameChanged_('create');
+ // Need to wait until the promise resolves.
+ promise.then(function() {
+ assertTrue($('create-profile-ok').disabled);
+ assertFalse($('supervised-user-import-existing') == null);
+
+ // A profile which doesn't have an avatar yet.
+ nameField.value = 'Fritz';
+ ManageProfileOverlay.getInstance().onNameChanged_('create');
+ return options.SupervisedUserListData.getInstance().promise_;
+ }).then(function() {
+ assertTrue($('create-profile-ok').disabled);
+ assertFalse($('supervised-user-import-existing') == null);
+
+ // A profile which already exists on the device.
+ nameField.value = 'Test';
+ ManageProfileOverlay.getInstance().onNameChanged_('create');
+ return options.SupervisedUserListData.getInstance().promise_;
+ }).then(function() {
+ assertTrue($('create-profile-ok').disabled);
+ assertTrue($('supervised-user-import-existing') == null);
+
+ // A supervised user profile that is on the device, but has the same name
+ // as a supervised user profile that is not imported.
+ // This can happen due to a bug (https://crbug.com/557445)
+ nameField.value = 'RepeatingName';
+ ManageProfileOverlay.getInstance().onNameChanged_('create');
+ return options.SupervisedUserListData.getInstance().promise_;
+ }).then(function() {
+ assertTrue($('create-profile-ok').disabled);
+ assertFalse($('supervised-user-import-existing') == null);
+
+ // A profile which does not exist yet.
+ nameField.value = 'NewProfileName';
+ ManageProfileOverlay.getInstance().onNameChanged_('create');
+ return options.SupervisedUserListData.getInstance().promise_;
+ }).then(function() {
+ assertFalse($('create-profile-ok').disabled);
+ assertTrue($('supervised-user-import-existing') == null);
+ testDone();
+ });
+});
+
+// Supervised users should not be able to edit their profile names, and the
+// initial focus should be adjusted accordingly.
+TEST_F('ManageProfileUITest', 'EditSupervisedUserNameAllowed', function() {
+ var nameField = $('manage-profile-name');
+
+ this.setProfileSupervised_(false, 'manage');
+ ManageProfileOverlay.showManageDialog();
+ expectFalse(nameField.disabled);
+ expectEquals(nameField, document.activeElement);
+
+ PageManager.closeOverlay();
+
+ this.setProfileSupervised_(true, 'manage');
+ ManageProfileOverlay.showManageDialog();
+ expectTrue(nameField.disabled);
+ expectEquals($('manage-profile-ok'), document.activeElement);
+});
+
+// Setting profile information should allow the confirmation to be shown.
+TEST_F('ManageProfileUITest', 'ShowCreateConfirmation', function() {
+ var testProfile = this.testProfileInfo_(true);
+ testProfile.custodianEmail = 'foo@bar.example.com';
+ SupervisedUserCreateConfirmOverlay.setProfileInfo(testProfile);
+ assertTrue(SupervisedUserCreateConfirmOverlay.getInstance().canShowPage());
+ PageManager.showPageByName('supervisedUserCreateConfirm', false);
+ assertEquals('supervisedUserCreateConfirm',
+ PageManager.getTopmostVisiblePage().name);
+});
+
+// Trying to show a confirmation dialog with no profile information should fall
+// back to the default (main) settings page.
+TEST_F('ManageProfileUITest', 'NoEmptyConfirmation', function() {
+ assertEquals('manageProfile', PageManager.getTopmostVisiblePage().name);
+ assertFalse(SupervisedUserCreateConfirmOverlay.getInstance().canShowPage());
+ PageManager.showPageByName('supervisedUserCreateConfirm', true);
+ assertEquals('settings', PageManager.getTopmostVisiblePage().name);
+});
+
+// A confirmation dialog should be shown after creating a new supervised user.
+TEST_F('ManageProfileUITest', 'ShowCreateConfirmationOnSuccess', function() {
+ PageManager.showPageByName('createProfile');
+ assertEquals('createProfile', PageManager.getTopmostVisiblePage().name);
+ CreateProfileOverlay.onSuccess(this.testProfileInfo_(false));
+ assertEquals('settings', PageManager.getTopmostVisiblePage().name);
+
+ PageManager.showPageByName('createProfile');
+ assertEquals('createProfile', PageManager.getTopmostVisiblePage().name);
+ CreateProfileOverlay.onSuccess(this.testProfileInfo_(true));
+ assertEquals('supervisedUserCreateConfirm',
+ PageManager.getTopmostVisiblePage().name);
+ expectEquals($('supervised-user-created-switch'), document.activeElement);
+});
+
+// An error should be shown if creating a new supervised user fails.
+TEST_F('ManageProfileUITest', 'NoCreateConfirmationOnError', function() {
+ PageManager.showPageByName('createProfile');
+ assertEquals('createProfile', PageManager.getTopmostVisiblePage().name);
+ var errorBubble = $('create-profile-error-bubble');
+ assertTrue(errorBubble.hidden);
+
+ CreateProfileOverlay.onError('An Error Message!');
+ assertEquals('createProfile', PageManager.getTopmostVisiblePage().name);
+ assertFalse(errorBubble.hidden);
+});
+
+// The name and email should be inserted into the confirmation dialog.
+TEST_F('ManageProfileUITest', 'CreateConfirmationText', function() {
+ var self = this;
+ var custodianEmail = 'foo@example.com';
+
+ // Checks the strings in the confirmation dialog. If |expectedNameText| is
+ // given, it should be present in the dialog's textContent; otherwise the name
+ // is expected. If |expectedNameHtml| is given, it should be present in the
+ // dialog's innerHTML; otherwise the expected text is expected in the HTML
+ // too.
+ function checkDialog(name, expectedNameText, expectedNameHtml) {
+ var expectedText = expectedNameText || name;
+ var expectedHtml = expectedNameHtml || expectedText;
+
+ // Configure the test profile and show the confirmation dialog.
+ var testProfile = self.testProfileInfo_(true);
+ testProfile.name = name;
+ CreateProfileOverlay.onSuccess(testProfile);
+ assertEquals('supervisedUserCreateConfirm',
+ PageManager.getTopmostVisiblePage().name);
+
+ // Check for the presence of the name and email in the UI, without depending
+ // on the details of the messages.
+ assertNotEquals(-1,
+ $('supervised-user-created-title').textContent.indexOf(expectedText));
+ assertNotEquals(-1,
+ $('supervised-user-created-switch').textContent.indexOf(expectedText));
+ var message = $('supervised-user-created-text');
+ assertNotEquals(-1, message.textContent.indexOf(expectedText));
+ assertNotEquals(-1, message.textContent.indexOf(custodianEmail));
+
+ // The name should be properly HTML-escaped.
+ assertNotEquals(-1, message.innerHTML.indexOf(expectedHtml));
+
+ PageManager.closeOverlay();
+ assertEquals('settings', PageManager.getTopmostVisiblePage().name, name);
+ }
+
+ // Show and configure the create-profile dialog.
+ PageManager.showPageByName('createProfile');
+ CreateProfileOverlay.updateSignedInStatus(custodianEmail);
+ assertEquals('createProfile', PageManager.getTopmostVisiblePage().name);
+
+ checkDialog('OneWord');
+ checkDialog('Multiple Words');
+ checkDialog('It\'s "<HTML> injection" & more!',
+ 'It\'s "<HTML> injection" & more!',
+ // The innerHTML getter doesn't escape quotation marks,
+ // independent of whether they were escaped in the setter.
+ 'It\'s "&lt;HTML&gt; injection" &amp; more!');
+
+ // Test elision. MAX_LENGTH = 50, minus 1 for the ellipsis.
+ var name49Characters = '0123456789012345678901234567890123456789012345678';
+ var name50Characters = name49Characters + '9';
+ var name51Characters = name50Characters + '0';
+ var name60Characters = name51Characters + '123456789';
+ checkDialog(name49Characters, name49Characters);
+ checkDialog(name50Characters, name50Characters);
+ checkDialog(name51Characters, name49Characters + '\u2026');
+ checkDialog(name60Characters, name49Characters + '\u2026');
+
+ // Test both elision and HTML escaping. The allowed string length is the
+ // visible length, not the length including the entity names.
+ name49Characters = name49Characters.replace('0', '&').replace('1', '>');
+ name60Characters = name60Characters.replace('0', '&').replace('1', '>');
+ var escaped = name49Characters.replace('&', '&amp;').replace('>', '&gt;');
+ checkDialog(
+ name60Characters, name49Characters + '\u2026', escaped + '\u2026');
+});
+
+// The confirmation dialog should close if the new supervised user is deleted.
+TEST_F('ManageProfileUITest', 'CloseConfirmationOnDelete', function() {
+ // Configure the test profile and show the confirmation dialog.
+ var testProfile = this.testProfileInfo_(true);
+ CreateProfileOverlay.onSuccess(testProfile);
+ assertEquals('supervisedUserCreateConfirm',
+ PageManager.getTopmostVisiblePage().name);
+
+ SupervisedUserCreateConfirmOverlay.onDeletedProfile(testProfile.filePath);
+ assertEquals('settings', PageManager.getTopmostVisiblePage().name, name);
+});
+
+// The confirmation dialog should update if the new supervised user's name is
+// changed.
+TEST_F('ManageProfileUITest', 'UpdateConfirmationOnRename', function() {
+ // Configure the test profile and show the confirmation dialog.
+ var testProfile = this.testProfileInfo_(true);
+ CreateProfileOverlay.onSuccess(testProfile);
+ assertEquals('supervisedUserCreateConfirm',
+ PageManager.getTopmostVisiblePage().name);
+
+ var oldName = testProfile.name;
+ var newName = 'New Name';
+ SupervisedUserCreateConfirmOverlay.onUpdatedProfileName(testProfile.filePath,
+ newName);
+ assertEquals('supervisedUserCreateConfirm',
+ PageManager.getTopmostVisiblePage().name);
+
+ var titleElement = $('supervised-user-created-title');
+ var switchElement = $('supervised-user-created-switch');
+ var messageElement = $('supervised-user-created-text');
+
+ assertEquals(-1, titleElement.textContent.indexOf(oldName));
+ assertEquals(-1, switchElement.textContent.indexOf(oldName));
+ assertEquals(-1, messageElement.textContent.indexOf(oldName));
+
+ assertNotEquals(-1, titleElement.textContent.indexOf(newName));
+ assertNotEquals(-1, switchElement.textContent.indexOf(newName));
+ assertNotEquals(-1, messageElement.textContent.indexOf(newName));
+});
+
+// An additional warning should be shown when deleting a supervised user.
+TEST_F('ManageProfileUITest', 'DeleteSupervisedUserWarning', function() {
+ var addendum = $('delete-supervised-profile-addendum');
+
+ ManageProfileOverlay.showDeleteDialog(this.testProfileInfo_(true));
+ assertFalse(addendum.hidden);
+
+ ManageProfileOverlay.showDeleteDialog(this.testProfileInfo_(false));
+ assertTrue(addendum.hidden);
+});
+
+// The policy prohibiting supervised users should update the UI dynamically.
+TEST_F('ManageProfileUITest', 'PolicyDynamicRefresh', function() {
+ ManageProfileOverlay.getInstance().initializePage();
+
+ var custodianEmail = 'chrome.playpen.test@gmail.com';
+ CreateProfileOverlay.updateSignedInStatus(custodianEmail);
+ CreateProfileOverlay.updateSupervisedUsersAllowed(true);
+ var checkbox = $('create-profile-supervised');
+ var signInPromo = $('create-profile-supervised-not-signed-in');
+ var signInLink = $('create-profile-supervised-sign-in-link');
+ var indicator = $('create-profile-supervised-indicator');
+
+ assertFalse(checkbox.disabled, 'allowed and signed in');
+ assertTrue(signInPromo.hidden, 'allowed and signed in');
+ assertEquals('none', window.getComputedStyle(indicator, null).display,
+ 'allowed and signed in');
+
+ CreateProfileOverlay.updateSignedInStatus('');
+ CreateProfileOverlay.updateSupervisedUsersAllowed(true);
+ assertTrue(checkbox.disabled, 'allowed, not signed in');
+ assertFalse(signInPromo.hidden, 'allowed, not signed in');
+ assertTrue(signInLink.enabled, 'allowed, not signed in');
+ assertEquals('none', window.getComputedStyle(indicator, null).display,
+ 'allowed, not signed in');
+
+ CreateProfileOverlay.updateSignedInStatus('');
+ CreateProfileOverlay.updateSupervisedUsersAllowed(false);
+ assertTrue(checkbox.disabled, 'disallowed, not signed in');
+ assertFalse(signInPromo.hidden, 'disallowed, not signed in');
+ assertFalse(signInLink.enabled, 'disallowed, not signed in');
+ assertEquals('inline-block', window.getComputedStyle(indicator, null).display,
+ 'disallowed, not signed in');
+ assertEquals('policy', indicator.getAttribute('controlled-by'));
+
+ CreateProfileOverlay.updateSignedInStatus(custodianEmail);
+ CreateProfileOverlay.updateSupervisedUsersAllowed(false);
+ assertTrue(checkbox.disabled, 'disallowed, signed in');
+ assertTrue(signInPromo.hidden, 'disallowed, signed in');
+ assertEquals('inline-block', window.getComputedStyle(indicator, null).display,
+ 'disallowed, signed in');
+ assertEquals('policy', indicator.getAttribute('controlled-by'));
+
+ CreateProfileOverlay.updateSignedInStatus(custodianEmail);
+ CreateProfileOverlay.updateSupervisedUsersAllowed(true);
+ assertFalse(checkbox.disabled, 're-allowed and signed in');
+ assertTrue(signInPromo.hidden, 're-allowed and signed in');
+ assertEquals('none', window.getComputedStyle(indicator, null).display,
+ 're-allowed and signed in');
+});
+
+// The supervised user checkbox should correctly update its state during profile
+// creation and afterwards.
+TEST_F('ManageProfileUITest', 'CreateInProgress', function() {
+ ManageProfileOverlay.getInstance().initializePage();
+
+ var custodianEmail = 'chrome.playpen.test@gmail.com';
+ CreateProfileOverlay.updateSignedInStatus(custodianEmail);
+ CreateProfileOverlay.updateSupervisedUsersAllowed(true);
+ var checkbox = $('create-profile-supervised');
+ var signInPromo = $('create-profile-supervised-not-signed-in');
+ var indicator = $('create-profile-supervised-indicator');
+
+ assertFalse(checkbox.disabled, 'allowed and signed in');
+ assertTrue(signInPromo.hidden, 'allowed and signed in');
+ assertEquals('none', window.getComputedStyle(indicator, null).display,
+ 'allowed and signed in');
+ assertFalse(indicator.hasAttribute('controlled-by'));
+
+ CreateProfileOverlay.updateCreateInProgress(true);
+ assertTrue(checkbox.disabled, 'creation in progress');
+
+ // A no-op update to the sign-in status should not change the UI.
+ CreateProfileOverlay.updateSignedInStatus(custodianEmail);
+ CreateProfileOverlay.updateSupervisedUsersAllowed(true);
+ assertTrue(checkbox.disabled, 'creation in progress');
+
+ CreateProfileOverlay.updateCreateInProgress(false);
+ assertFalse(checkbox.disabled, 'creation finished');
+});
+
+// Supervised users should be able to open the delete dialog, but not the
+// create dialog.
+TEST_F('ManageProfileUITest', 'SupervisedShowCreate', function() {
+ this.setProfileSupervised_(false, 'create');
+
+ ManageProfileOverlay.showCreateDialog();
+ assertEquals('createProfile', PageManager.getTopmostVisiblePage().name);
+ PageManager.closeOverlay();
+ assertEquals('settings', PageManager.getTopmostVisiblePage().name);
+ ManageProfileOverlay.showDeleteDialog(this.testProfileInfo_(false));
+ assertEquals('manageProfile', PageManager.getTopmostVisiblePage().name);
+ assertFalse($('manage-profile-overlay-delete').hidden);
+ PageManager.closeOverlay();
+ assertEquals('settings', PageManager.getTopmostVisiblePage().name);
+
+ this.setProfileSupervised_(true, 'create');
+ ManageProfileOverlay.showCreateDialog();
+ assertEquals('settings', PageManager.getTopmostVisiblePage().name);
+ ManageProfileOverlay.showDeleteDialog(this.testProfileInfo_(false));
+ assertEquals('manageProfile', PageManager.getTopmostVisiblePage().name);
+});
+
+// Selecting a different avatar image should update the suggested profile name.
+TEST_F('ManageProfileUITest', 'Create_NameUpdateOnAvatarSelected', function() {
+ var mode = 'create';
+ this.initDefaultProfiles_(mode);
+
+ var gridEl = $(mode + '-profile-icon-grid');
+ var nameEl = $(mode + '-profile-name');
+
+ // Select another icon and check that the profile name was updated.
+ assertNotEquals(gridEl.selectedItem, this.defaultIconURLs[0]);
+ gridEl.selectedItem = this.defaultIconURLs[0];
+ expectEquals(this.defaultNames[0], nameEl.value);
+
+ // Select icon without an associated name; the profile name shouldn't change.
+ var oldName = nameEl.value;
+ assertEquals('', this.defaultNames[2]);
+ gridEl.selectedItem = this.defaultIconURLs[2];
+ expectEquals(oldName, nameEl.value);
+
+ // Select another icon with a name and check that the name is updated again.
+ assertNotEquals('', this.defaultNames[1]);
+ gridEl.selectedItem = this.defaultIconURLs[1];
+ expectEquals(this.defaultNames[1], nameEl.value);
+
+ PageManager.closeOverlay();
+});
+
+// After the user edited the profile name, selecting a different avatar image
+// should not update the suggested name anymore.
+TEST_F('ManageProfileUITest', 'Create_NoNameUpdateOnAvatarSelectedAfterEdit',
+ function() {
+ var mode = 'create';
+ this.initDefaultProfiles_(mode);
+
+ var gridEl = $(mode + '-profile-icon-grid');
+ var nameEl = $(mode + '-profile-name');
+
+ // After the user manually entered a name, it should not be changed anymore
+ // (even if the entered name is another default name).
+ nameEl.value = this.defaultNames[3];
+ nameEl.oninput();
+ gridEl.selectedItem = this.defaultIconURLs[0];
+ expectEquals(this.defaultNames[3], nameEl.value);
+
+ PageManager.closeOverlay();
+});
+
+// After the user edited the profile name, selecting a different avatar image
+// should not update the suggested name anymore even if the original suggestion
+// is entered again.
+TEST_F('ManageProfileUITest', 'Create_NoNameUpdateOnAvatarSelectedAfterRevert',
+ function() {
+ var mode = 'create';
+ this.initDefaultProfiles_(mode);
+
+ var gridEl = $(mode + '-profile-icon-grid');
+ var nameEl = $(mode + '-profile-name');
+
+ // After the user manually entered a name, it should not be changed anymore,
+ // even if the user then reverts to the original suggestion.
+ var oldName = nameEl.value;
+ nameEl.value = 'Custom Name';
+ nameEl.oninput();
+ nameEl.value = oldName;
+ nameEl.oninput();
+ // Now select another avatar and check that the name remained the same.
+ assertNotEquals(gridEl.selectedItem, this.defaultIconURLs[0]);
+ gridEl.selectedItem = this.defaultIconURLs[0];
+ expectEquals(oldName, nameEl.value);
+
+ PageManager.closeOverlay();
+});
+
+// In the manage dialog, the name should never be updated on avatar selection.
+TEST_F('ManageProfileUITest', 'Manage_NoNameUpdateOnAvatarSelected',
+ function() {
+ var mode = 'manage';
+ this.setProfileSupervised_(false, mode);
+ PageManager.showPageByName(mode + 'Profile');
+
+ var testProfile = this.testProfileInfo_(false);
+ var iconURLs = [testProfile.iconURL, '/some/path', '/another/path'];
+ var names = [testProfile.name, 'Some Name', ''];
+ ManageProfileOverlay.receiveDefaultProfileIconsAndNames(
+ mode, iconURLs, names);
+
+ var gridEl = $(mode + '-profile-icon-grid');
+ var nameEl = $(mode + '-profile-name');
+
+ // Select another icon and check if the profile name was updated.
+ var oldName = nameEl.value;
+ gridEl.selectedItem = iconURLs[1];
+ expectEquals(oldName, nameEl.value);
+
+ PageManager.closeOverlay();
+});
+
+GEN('#endif // OS_CHROMEOS');
diff --git a/chromium/chrome/browser/ui/webui/options/manage_profile_handler.cc b/chromium/chrome/browser/ui/webui/options/manage_profile_handler.cc
new file mode 100644
index 00000000000..61f6b8ebbdb
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/manage_profile_handler.cc
@@ -0,0 +1,547 @@
+// 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/ui/webui/options/manage_profile_handler.h"
+
+#include <stddef.h>
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/value_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/profiles/gaia_info_update_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_avatar_icon_util.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_metrics.h"
+#include "chrome/browser/profiles/profile_shortcut_manager.h"
+#include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/profiles/profiles_state.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/signin/core/common/profile_management_switches.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_ui.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/webui/web_ui_util.h"
+
+namespace options {
+
+namespace {
+
+const char kCreateProfileIdentifier[] = "create";
+const char kManageProfileIdentifier[] = "manage";
+
+// Given |args| from the WebUI, parses value 0 as a FilePath |profile_file_path|
+// and returns true on success.
+bool GetProfilePathFromArgs(const base::ListValue* args,
+ base::FilePath* profile_file_path) {
+ const base::Value* file_path_value;
+ if (!args->Get(0, &file_path_value))
+ return false;
+ return base::GetValueAsFilePath(*file_path_value, profile_file_path);
+}
+
+void HandleLogDeleteUserDialogShown(const base::ListValue* args) {
+ ProfileMetrics::LogProfileDeleteUser(
+ ProfileMetrics::DELETE_PROFILE_SETTINGS_SHOW_WARNING);
+}
+
+} // namespace
+
+ManageProfileHandler::ManageProfileHandler()
+ : weak_factory_(this) {
+}
+
+ManageProfileHandler::~ManageProfileHandler() {
+ browser_sync::ProfileSyncService* service =
+ ProfileSyncServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()));
+ // Sync may be disabled in tests.
+ if (service)
+ service->RemoveObserver(this);
+}
+
+void ManageProfileHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ static OptionsStringResource resources[] = {
+ { "manageProfilesNameLabel", IDS_PROFILES_MANAGE_NAME_LABEL },
+ { "manageProfilesIconLabel", IDS_PROFILES_MANAGE_ICON_LABEL },
+ { "manageProfilesExistingSupervisedUser",
+ IDS_PROFILES_CREATE_LEGACY_SUPERVISED_USER_ERROR_EXISTS_REMOTELY },
+ { "managedProfilesExistingLocalSupervisedUser",
+ IDS_PROFILES_CREATE_LEGACY_SUPERVISED_USER_ERROR_EXISTS_LOCALLY },
+ { "manageProfilesSupervisedSignedInLabel",
+ IDS_PROFILES_CREATE_SUPERVISED_SIGNED_IN_LABEL },
+ { "manageProfilesSupervisedNotSignedIn",
+ IDS_PROFILES_CREATE_SUPERVISED_NOT_SIGNED_IN_HTML },
+ { "manageProfilesSupervisedAccountDetailsOutOfDate",
+ IDS_PROFILES_CREATE_SUPERVISED_ACCOUNT_DETAILS_OUT_OF_DATE_LABEL },
+ { "manageProfilesSupervisedSignInAgainLink",
+ IDS_PROFILES_GAIA_REAUTH_TITLE },
+ { "manageProfilesConfirm", IDS_SAVE },
+ { "deleteProfileTitle", IDS_PROFILES_DELETE_TITLE },
+ { "deleteProfileOK", IDS_PROFILES_DELETE_OK_BUTTON_LABEL },
+ { "deleteProfileMessage", IDS_PROFILES_DELETE_MESSAGE },
+ { "disconnectManagedProfileTitle",
+ IDS_PROFILES_DISCONNECT_MANAGED_PROFILE_TITLE },
+ { "disconnectManagedProfileOK",
+ IDS_PROFILES_DISCONNECT_MANAGED_PROFILE_OK_BUTTON_LABEL },
+ { "createProfileTitle", IDS_PROFILES_CREATE_TITLE },
+ { "createProfileInstructions", IDS_PROFILES_CREATE_INSTRUCTIONS },
+ { "createProfileConfirm", IDS_ADD },
+ { "createProfileShortcutCheckbox", IDS_PROFILES_CREATE_SHORTCUT_CHECKBOX },
+ { "createProfileShortcutButton", IDS_PROFILES_CREATE_SHORTCUT_BUTTON },
+ { "removeProfileShortcutButton", IDS_PROFILES_REMOVE_SHORTCUT_BUTTON },
+ { "importExistingSupervisedUserLink",
+ IDS_IMPORT_EXISTING_LEGACY_SUPERVISED_USER_TITLE },
+ };
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+ RegisterTitle(localized_strings, "manageProfile", IDS_PROFILES_MANAGE_TITLE);
+ RegisterTitle(localized_strings, "createProfile", IDS_PROFILES_CREATE_TITLE);
+ RegisterTitle(localized_strings, "disconnectAccount",
+ IDS_DISCONNECT_ACCOUNT_TITLE);
+
+ base::string16 supervised_user_dashboard_url =
+ base::ASCIIToUTF16(chrome::kLegacySupervisedUserManagementURL);
+ base::string16 supervised_user_dashboard_display =
+ base::ASCIIToUTF16(chrome::kLegacySupervisedUserManagementDisplayURL);
+ localized_strings->SetString("deleteSupervisedProfileAddendum",
+ l10n_util::GetStringFUTF16(IDS_PROFILES_DELETE_LEGACY_SUPERVISED_ADDENDUM,
+ supervised_user_dashboard_url,
+ supervised_user_dashboard_display));
+
+ localized_strings->SetBoolean("profileShortcutsEnabled",
+ ProfileShortcutManager::IsFeatureEnabled());
+
+ GenerateSignedinUserSpecificStrings(localized_strings);
+}
+
+void ManageProfileHandler::InitializeHandler() {
+ g_browser_process->profile_manager()->
+ GetProfileAttributesStorage().AddObserver(this);
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ pref_change_registrar_.Init(profile->GetPrefs());
+ pref_change_registrar_.Add(
+ prefs::kSupervisedUserCreationAllowed,
+ base::Bind(&ManageProfileHandler::OnCreateSupervisedUserPrefChange,
+ base::Unretained(this)));
+ browser_sync::ProfileSyncService* service =
+ ProfileSyncServiceFactory::GetForProfile(profile);
+ // Sync may be disabled for tests.
+ if (service)
+ service->AddObserver(this);
+}
+
+void ManageProfileHandler::InitializePage() {
+ SendExistingProfileNames();
+ OnCreateSupervisedUserPrefChange();
+}
+
+void ManageProfileHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("setProfileIconAndName",
+ base::Bind(&ManageProfileHandler::SetProfileIconAndName,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("requestDefaultProfileIcons",
+ base::Bind(&ManageProfileHandler::RequestDefaultProfileIcons,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("requestNewProfileDefaults",
+ base::Bind(&ManageProfileHandler::RequestNewProfileDefaults,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("requestHasProfileShortcuts",
+ base::Bind(&ManageProfileHandler::RequestHasProfileShortcuts,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("requestCreateProfileUpdate",
+ base::Bind(&ManageProfileHandler::RequestCreateProfileUpdate,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("profileIconSelectionChanged",
+ base::Bind(&ManageProfileHandler::ProfileIconSelectionChanged,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("addProfileShortcut",
+ base::Bind(&ManageProfileHandler::AddProfileShortcut,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("removeProfileShortcut",
+ base::Bind(&ManageProfileHandler::RemoveProfileShortcut,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("refreshGaiaPicture",
+ base::Bind(&ManageProfileHandler::RefreshGaiaPicture,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "showDisconnectManagedProfileDialog",
+ base::Bind(&ManageProfileHandler::ShowDisconnectManagedProfileDialog,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("logDeleteUserDialogShown",
+ base::Bind(&HandleLogDeleteUserDialogShown));
+}
+
+void ManageProfileHandler::Uninitialize() {
+ g_browser_process->profile_manager()->
+ GetProfileAttributesStorage().RemoveObserver(this);
+}
+
+void ManageProfileHandler::OnProfileAdded(const base::FilePath& profile_path) {
+ SendExistingProfileNames();
+}
+
+void ManageProfileHandler::OnProfileWasRemoved(
+ const base::FilePath& profile_path,
+ const base::string16& profile_name) {
+ SendExistingProfileNames();
+}
+
+void ManageProfileHandler::OnProfileNameChanged(
+ const base::FilePath& profile_path,
+ const base::string16& old_profile_name) {
+ base::Value value(kManageProfileIdentifier);
+ SendProfileIconsAndNames(value);
+}
+
+void ManageProfileHandler::OnProfileAvatarChanged(
+ const base::FilePath& profile_path) {
+ base::Value value(kManageProfileIdentifier);
+ SendProfileIconsAndNames(value);
+}
+
+void ManageProfileHandler::OnStateChanged(syncer::SyncService* sync) {
+ RequestCreateProfileUpdate(NULL);
+}
+
+void ManageProfileHandler::GenerateSignedinUserSpecificStrings(
+ base::DictionaryValue* dictionary) {
+ std::string username;
+ std::string domain_name;
+
+#if !defined(OS_CHROMEOS)
+ Profile* profile = Profile::FromWebUI(web_ui());
+ DCHECK(profile);
+ SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile);
+ if (manager) {
+ username = manager->GetAuthenticatedAccountInfo().email;
+ // If there is no one logged in or if the profile name is empty then the
+ // domain name is empty. This happens in browser tests.
+ if (!username.empty()) {
+ domain_name = "<span id=disconnect-managed-profile-domain-name>" +
+ gaia::ExtractDomainName(username) + "</span>";
+ }
+ }
+#endif
+
+ dictionary->SetString(
+ "disconnectManagedProfileDomainInformation",
+ l10n_util::GetStringFUTF16(
+ IDS_PROFILES_DISCONNECT_MANAGED_PROFILE_DOMAIN_INFORMATION,
+ base::ASCIIToUTF16(domain_name)));
+
+ dictionary->SetString(
+ "disconnectManagedProfileText",
+ l10n_util::GetStringFUTF16(
+ IDS_PROFILES_DISCONNECT_MANAGED_PROFILE_TEXT,
+ base::UTF8ToUTF16(username),
+ base::UTF8ToUTF16(chrome::kSyncGoogleDashboardURL)));
+}
+
+void ManageProfileHandler::RequestDefaultProfileIcons(
+ const base::ListValue* args) {
+ std::string mode;
+ bool ok = args->GetString(0, &mode);
+ DCHECK(ok);
+ DCHECK(mode == kCreateProfileIdentifier || mode == kManageProfileIdentifier);
+ if (ok) {
+ base::Value value(mode);
+ SendProfileIconsAndNames(value);
+ }
+}
+
+void ManageProfileHandler::RequestNewProfileDefaults(
+ const base::ListValue* args) {
+ const ProfileAttributesStorage& storage =
+ g_browser_process->profile_manager()->GetProfileAttributesStorage();
+ const size_t icon_index = storage.ChooseAvatarIconIndexForNewProfile();
+
+ base::DictionaryValue profile_info;
+ profile_info.SetString("name", storage.ChooseNameForNewProfile(icon_index));
+ profile_info.SetString("iconURL",
+ profiles::GetDefaultAvatarIconUrl(icon_index));
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ManageProfileOverlay.receiveNewProfileDefaults", profile_info);
+}
+
+void ManageProfileHandler::SendProfileIconsAndNames(const base::Value& mode) {
+ base::ListValue image_url_list;
+ base::ListValue default_name_list;
+
+ ProfileAttributesStorage& storage =
+ g_browser_process->profile_manager()->GetProfileAttributesStorage();
+
+ // In manage mode, first add the GAIA picture if it is available. No GAIA
+ // picture in create mode.
+ if (mode.GetString() == kManageProfileIdentifier) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ ProfileAttributesEntry* entry = nullptr;
+ bool success = storage.GetProfileAttributesWithPath(profile->GetPath(),
+ &entry);
+ const gfx::Image* icon = success ? entry->GetGAIAPicture() : nullptr;
+ if (icon) {
+ gfx::Image icon2 = profiles::GetAvatarIconForWebUI(*icon, true);
+ gaia_picture_url_ = webui::GetBitmapDataUrl(icon2.AsBitmap());
+ image_url_list.AppendString(gaia_picture_url_);
+ default_name_list.AppendString(std::string());
+ }
+ }
+
+ // Next add the default avatar icons and names.
+ for (size_t i = 0; i < profiles::GetDefaultAvatarIconCount(); i++) {
+ std::string url = profiles::GetDefaultAvatarIconUrl(i);
+ image_url_list.AppendString(url);
+ default_name_list.AppendString(storage.ChooseNameForNewProfile(i));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ManageProfileOverlay.receiveDefaultProfileIconsAndNames", mode,
+ image_url_list, default_name_list);
+}
+
+void ManageProfileHandler::SendExistingProfileNames() {
+ std::vector<ProfileAttributesEntry*> entries =
+ g_browser_process->profile_manager()->
+ GetProfileAttributesStorage().GetAllProfilesAttributes();
+ base::DictionaryValue profile_name_dict;
+ for (const ProfileAttributesEntry* entry : entries)
+ profile_name_dict.SetBoolean(base::UTF16ToUTF8(entry->GetName()), true);
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ManageProfileOverlay.receiveExistingProfileNames", profile_name_dict);
+}
+
+void ManageProfileHandler::ShowDisconnectManagedProfileDialog(
+ const base::ListValue* args) {
+ base::DictionaryValue replacements;
+ GenerateSignedinUserSpecificStrings(&replacements);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ManageProfileOverlay.showDisconnectManagedProfileDialog", replacements);
+}
+
+void ManageProfileHandler::SetProfileIconAndName(const base::ListValue* args) {
+ DCHECK(args);
+
+ base::FilePath profile_file_path;
+ if (!GetProfilePathFromArgs(args, &profile_file_path))
+ return;
+
+ Profile* profile =
+ g_browser_process->profile_manager()->GetProfile(profile_file_path);
+ if (!profile)
+ return;
+
+ std::string icon_url;
+ if (!args->GetString(1, &icon_url))
+ return;
+
+ PrefService* pref_service = profile->GetPrefs();
+ // Updating the profile preferences will cause the cache to be updated.
+
+ // Metrics logging variable.
+ bool previously_using_gaia_icon =
+ pref_service->GetBoolean(prefs::kProfileUsingGAIAAvatar);
+
+ size_t new_icon_index;
+ if (icon_url == gaia_picture_url_) {
+ pref_service->SetBoolean(prefs::kProfileUsingDefaultAvatar, false);
+ pref_service->SetBoolean(prefs::kProfileUsingGAIAAvatar, true);
+ if (!previously_using_gaia_icon) {
+ // Only log if they changed to the GAIA photo.
+ // Selection of GAIA photo as avatar is logged as part of the function
+ // below.
+ ProfileMetrics::LogProfileSwitchGaia(ProfileMetrics::GAIA_OPT_IN);
+ }
+ } else if (profiles::IsDefaultAvatarIconUrl(icon_url, &new_icon_index)) {
+ ProfileMetrics::LogProfileAvatarSelection(new_icon_index);
+ pref_service->SetInteger(prefs::kProfileAvatarIndex, new_icon_index);
+ pref_service->SetBoolean(prefs::kProfileUsingDefaultAvatar, false);
+ pref_service->SetBoolean(prefs::kProfileUsingGAIAAvatar, false);
+ } else {
+ // Only default avatars and Gaia account photos are supported.
+ CHECK(false);
+ }
+ ProfileMetrics::LogProfileUpdate(profile_file_path);
+
+ if (profile->IsLegacySupervised())
+ return;
+
+ base::string16 new_profile_name;
+ if (!args->GetString(2, &new_profile_name))
+ return;
+
+ base::TrimWhitespace(new_profile_name, base::TRIM_ALL, &new_profile_name);
+ CHECK(!new_profile_name.empty());
+ profiles::UpdateProfileName(profile, new_profile_name);
+}
+
+void ManageProfileHandler::ProfileIconSelectionChanged(
+ const base::ListValue* args) {
+ DCHECK(args);
+
+ base::FilePath profile_file_path;
+ if (!GetProfilePathFromArgs(args, &profile_file_path))
+ return;
+
+ // Currently this only supports editing the current profile's info.
+ if (profile_file_path != Profile::FromWebUI(web_ui())->GetPath())
+ return;
+
+ std::string icon_url;
+ if (!args->GetString(1, &icon_url))
+ return;
+
+ if (icon_url != gaia_picture_url_)
+ return;
+
+ // If the selection is the GAIA picture then also show the profile name in the
+ // text field. This will display either the GAIA given name, if available,
+ // or the first name.
+ ProfileAttributesEntry* entry = nullptr;
+ if (!g_browser_process->profile_manager()->GetProfileAttributesStorage().
+ GetProfileAttributesWithPath(profile_file_path, &entry))
+ return;
+ base::string16 gaia_name = entry->GetName();
+ if (gaia_name.empty())
+ return;
+
+ base::Value gaia_name_value(gaia_name);
+ base::Value mode_value(kManageProfileIdentifier);
+ web_ui()->CallJavascriptFunctionUnsafe("ManageProfileOverlay.setProfileName",
+ gaia_name_value, mode_value);
+}
+
+void ManageProfileHandler::RequestHasProfileShortcuts(
+ const base::ListValue* args) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(ProfileShortcutManager::IsFeatureEnabled());
+
+ base::FilePath profile_file_path;
+ if (!GetProfilePathFromArgs(args, &profile_file_path))
+ return;
+
+ ProfileAttributesStorage& storage =
+ g_browser_process->profile_manager()->GetProfileAttributesStorage();
+ ProfileAttributesEntry* entry;
+ if (!storage.GetProfileAttributesWithPath(profile_file_path, &entry))
+ return;
+
+ // Don't show the add/remove desktop shortcut button in the single user case.
+ if (storage.GetNumberOfProfiles() <= 1u)
+ return;
+
+ ProfileShortcutManager* shortcut_manager =
+ g_browser_process->profile_manager()->profile_shortcut_manager();
+ shortcut_manager->HasProfileShortcuts(
+ entry->GetPath(), base::Bind(&ManageProfileHandler::OnHasProfileShortcuts,
+ weak_factory_.GetWeakPtr()));
+}
+
+void ManageProfileHandler::RequestCreateProfileUpdate(
+ const base::ListValue* args) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ SigninManagerBase* manager =
+ SigninManagerFactory::GetForProfile(profile);
+ base::string16 username =
+ base::UTF8ToUTF16(manager->GetAuthenticatedAccountInfo().email);
+ browser_sync::ProfileSyncService* service =
+ ProfileSyncServiceFactory::GetForProfile(profile);
+ GoogleServiceAuthError::State state = GoogleServiceAuthError::NONE;
+
+ // |service| might be null if Sync is disabled from the command line.
+ if (service)
+ state = service->GetAuthError().state();
+
+ bool has_error = (!service ||
+ state == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS ||
+ state == GoogleServiceAuthError::USER_NOT_SIGNED_UP ||
+ state == GoogleServiceAuthError::ACCOUNT_DELETED ||
+ state == GoogleServiceAuthError::ACCOUNT_DISABLED);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "CreateProfileOverlay.updateSignedInStatus", base::Value(username),
+ base::Value(has_error));
+
+ OnCreateSupervisedUserPrefChange();
+}
+
+void ManageProfileHandler::OnCreateSupervisedUserPrefChange() {
+ PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+ base::Value allowed(prefs->GetBoolean(prefs::kSupervisedUserCreationAllowed));
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "CreateProfileOverlay.updateSupervisedUsersAllowed", allowed);
+}
+
+void ManageProfileHandler::OnHasProfileShortcuts(bool has_shortcuts) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ const base::Value has_shortcuts_value(has_shortcuts);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ManageProfileOverlay.receiveHasProfileShortcuts", has_shortcuts_value);
+}
+
+void ManageProfileHandler::AddProfileShortcut(const base::ListValue* args) {
+ base::FilePath profile_file_path;
+ if (!GetProfilePathFromArgs(args, &profile_file_path))
+ return;
+
+ DCHECK(ProfileShortcutManager::IsFeatureEnabled());
+ ProfileShortcutManager* shortcut_manager =
+ g_browser_process->profile_manager()->profile_shortcut_manager();
+ DCHECK(shortcut_manager);
+
+ shortcut_manager->CreateProfileShortcut(profile_file_path);
+
+ // Update the UI buttons.
+ OnHasProfileShortcuts(true);
+}
+
+void ManageProfileHandler::RemoveProfileShortcut(const base::ListValue* args) {
+ base::FilePath profile_file_path;
+ if (!GetProfilePathFromArgs(args, &profile_file_path))
+ return;
+
+ DCHECK(ProfileShortcutManager::IsFeatureEnabled());
+ ProfileShortcutManager* shortcut_manager =
+ g_browser_process->profile_manager()->profile_shortcut_manager();
+ DCHECK(shortcut_manager);
+
+ shortcut_manager->RemoveProfileShortcuts(profile_file_path);
+
+ // Update the UI buttons.
+ OnHasProfileShortcuts(false);
+}
+
+void ManageProfileHandler::RefreshGaiaPicture(const base::ListValue* args) {
+ profiles::UpdateGaiaProfileInfoIfNeeded(Profile::FromWebUI(web_ui()));
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/manage_profile_handler.h b/chromium/chrome/browser/ui/webui/options/manage_profile_handler.h
new file mode 100644
index 00000000000..b0688bfc520
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/manage_profile_handler.h
@@ -0,0 +1,144 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_MANAGE_PROFILE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_MANAGE_PROFILE_HANDLER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/sync/driver/sync_service_observer.h"
+
+namespace base {
+class Value;
+}
+
+namespace options {
+
+// Chrome personal stuff profiles manage overlay UI handler.
+class ManageProfileHandler : public OptionsPageUIHandler,
+ public ProfileAttributesStorage::Observer,
+ public syncer::SyncServiceObserver {
+ public:
+ ManageProfileHandler();
+ ~ManageProfileHandler() override;
+
+ // OptionsPageUIHandler:
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializeHandler() override;
+ void InitializePage() override;
+ void Uninitialize() override;
+
+ // WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ // ProfileAttributesStorage::Observer:
+ void OnProfileAdded(const base::FilePath& profile_path) override;
+ void OnProfileWasRemoved(const base::FilePath& profile_path,
+ const base::string16& profile_name) override;
+ void OnProfileNameChanged(const base::FilePath& profile_path,
+ const base::string16& old_profile_name) override;
+ void OnProfileAvatarChanged(const base::FilePath& profile_path) override;
+
+ // syncer::SyncServiceObserver:
+ void OnStateChanged(syncer::SyncService* sync) override;
+
+ private:
+ // This function creates signed in user specific strings in loadTimeData.
+ void GenerateSignedinUserSpecificStrings(base::DictionaryValue* dictionary);
+
+ // Callback for the "requestDefaultProfileIcons" message.
+ // Sends the array of default profile icon URLs and profile names to WebUI.
+ // First item of |args| is the dialog mode, i.e. "create" or "manage".
+ void RequestDefaultProfileIcons(const base::ListValue* args);
+
+ // Callback for the "requestNewProfileDefaults" message.
+ // Sends an object to WebUI of the form:
+ // { "name": profileName, "iconURL": iconURL }
+ void RequestNewProfileDefaults(const base::ListValue* args);
+
+ // Send all profile icons and their default names to the overlay.
+ // |mode| is the dialog mode, i.e. "create" or "manage".
+ void SendProfileIconsAndNames(const base::Value& mode);
+
+ // Sends an object to WebUI of the form:
+ // profileNames = {
+ // "Profile Name 1": true,
+ // "Profile Name 2": true,
+ // ...
+ // };
+ // This is used to detect duplicate profile names.
+ void SendExistingProfileNames();
+
+ // Show disconnect managed profile dialog after generating domain and user
+ // specific strings.
+ void ShowDisconnectManagedProfileDialog(const base::ListValue* args);
+
+ // Callback for the "setProfileIconAndName" message. Sets the name and icon
+ // of a given profile.
+ // |args| is of the form: [
+ // /*string*/ profileFilePath,
+ // /*string*/ newProfileIconURL
+ // /*string*/ newProfileName,
+ // ]
+ void SetProfileIconAndName(const base::ListValue* args);
+
+ // Callback for the 'profileIconSelectionChanged' message. Used to update the
+ // name in the manager profile dialog based on the selected icon.
+ void ProfileIconSelectionChanged(const base::ListValue* args);
+
+ // Callback for the "requestHasProfileShortcuts" message, which is called
+ // when editing an existing profile. Asks the profile shortcut manager whether
+ // the profile has shortcuts and gets the result in |OnHasProfileShortcuts()|.
+ // |args| is of the form: [ {string} profileFilePath ]
+ void RequestHasProfileShortcuts(const base::ListValue* args);
+
+ // Callback for the "RequestCreateProfileUpdate" message.
+ // Sends the email address of the signed-in user, or an empty string if the
+ // user is not signed in. Also sends information about whether supervised
+ // users may be created.
+ void RequestCreateProfileUpdate(const base::ListValue* args);
+
+ // When the pref allowing supervised-user creation changes, sends the new
+ // value to the UI.
+ void OnCreateSupervisedUserPrefChange();
+
+ // Callback invoked from the profile manager indicating whether the profile
+ // being edited has any desktop shortcuts.
+ void OnHasProfileShortcuts(bool has_shortcuts);
+
+ // Callback for the "addProfileShortcut" message, which is called when editing
+ // an existing profile and the user clicks the "Add desktop shortcut" button.
+ // Adds a desktop shortcut for the profile.
+ void AddProfileShortcut(const base::ListValue* args);
+
+ // Callback for the "removeProfileShortcut" message, which is called when
+ // editing an existing profile and the user clicks the "Remove desktop
+ // shortcut" button. Removes the desktop shortcut for the profile.
+ void RemoveProfileShortcut(const base::ListValue* args);
+
+ // Callback for the "refreshGaiaPicture" message, which is called when the
+ // user is editing an existing profile.
+ void RefreshGaiaPicture(const base::ListValue* args);
+
+ // URL for the current profile's GAIA picture.
+ std::string gaia_picture_url_;
+
+ // Used to observe the preference that allows creating supervised users, which
+ // can be changed by policy.
+ PrefChangeRegistrar pref_change_registrar_;
+
+ // For generating weak pointers to itself for callbacks.
+ base::WeakPtrFactory<ManageProfileHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ManageProfileHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_MANAGE_PROFILE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/media_devices_selection_handler.cc b/chromium/chrome/browser/ui/webui/options/media_devices_selection_handler.cc
new file mode 100644
index 00000000000..df8b4597f0f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/media_devices_selection_handler.cc
@@ -0,0 +1,181 @@
+// 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/ui/webui/options/media_devices_selection_handler.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/strings/grit/extensions_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#endif
+
+namespace {
+
+const char kAudio[] = "mic";
+const char kVideo[] = "camera";
+
+} // namespace
+
+namespace options {
+
+MediaDevicesSelectionHandler::MediaDevicesSelectionHandler() {}
+
+MediaDevicesSelectionHandler::~MediaDevicesSelectionHandler() {
+ MediaCaptureDevicesDispatcher::GetInstance()->RemoveObserver(this);
+}
+
+void MediaDevicesSelectionHandler::GetLocalizedValues(
+ base::DictionaryValue* values) {
+ DCHECK(values);
+
+ static OptionsStringResource resources[] = {
+ { "mediaSelectMicLabel", IDS_MEDIA_SELECTED_MIC_LABEL },
+ { "mediaSelectCameraLabel", IDS_MEDIA_SELECTED_CAMERA_LABEL },
+ };
+
+ RegisterStrings(values, resources, arraysize(resources));
+}
+
+void MediaDevicesSelectionHandler::InitializePage() {
+ // Register to the device observer list to get up-to-date device lists.
+ MediaCaptureDevicesDispatcher::GetInstance()->AddObserver(this);
+
+ // Update the device selection menus.
+ UpdateDevicesMenuForType(AUDIO);
+ UpdateDevicesMenuForType(VIDEO);
+}
+
+void MediaDevicesSelectionHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("setDefaultCaptureDevice",
+ base::Bind(&MediaDevicesSelectionHandler::SetDefaultCaptureDevice,
+ base::Unretained(this)));
+}
+
+void MediaDevicesSelectionHandler::OnUpdateAudioDevices(
+ const content::MediaStreamDevices& devices) {
+ UpdateDevicesMenu(AUDIO, devices);
+}
+
+void MediaDevicesSelectionHandler::OnUpdateVideoDevices(
+ const content::MediaStreamDevices& devices) {
+ UpdateDevicesMenu(VIDEO, devices);
+}
+
+void MediaDevicesSelectionHandler::SetDefaultCaptureDevice(
+ const base::ListValue* args) {
+ DCHECK_EQ(2U, args->GetSize());
+ std::string type, device;
+ if (!(args->GetString(0, &type) && args->GetString(1, &device))) {
+ NOTREACHED();
+ return;
+ }
+
+ DCHECK(!type.empty());
+ DCHECK(!device.empty());
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ PrefService* prefs = profile->GetPrefs();
+ if (type == kAudio)
+ prefs->SetString(prefs::kDefaultAudioCaptureDevice, device);
+ else if (type == kVideo)
+ prefs->SetString(prefs::kDefaultVideoCaptureDevice, device);
+ else
+ NOTREACHED();
+}
+
+void MediaDevicesSelectionHandler::UpdateDevicesMenu(
+ DeviceType type, const content::MediaStreamDevices& devices) {
+ // Get the default device unique id from prefs.
+ Profile* profile = Profile::FromWebUI(web_ui());
+ PrefService* prefs = profile->GetPrefs();
+ std::string default_device;
+ std::string device_type;
+ switch (type) {
+ case AUDIO:
+ default_device = prefs->GetString(prefs::kDefaultAudioCaptureDevice);
+ device_type = kAudio;
+ break;
+ case VIDEO:
+ default_device = prefs->GetString(prefs::kDefaultVideoCaptureDevice);
+ device_type = kVideo;
+ break;
+ }
+
+ // Build the list of devices to send to JS.
+ std::string default_id;
+ base::ListValue device_list;
+ for (size_t i = 0; i < devices.size(); ++i) {
+ std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
+ entry->SetString("name", GetDeviceDisplayName(devices[i]));
+ entry->SetString("id", devices[i].id);
+ device_list.Append(std::move(entry));
+ if (devices[i].id == default_device)
+ default_id = default_device;
+ }
+
+ // Use the first device as the default device if the preferred default device
+ // does not exist in the OS.
+ if (!devices.empty() && default_id.empty())
+ default_id = devices[0].id;
+
+ base::Value default_value(default_id);
+ base::Value type_value(device_type);
+ web_ui()->CallJavascriptFunctionUnsafe("ContentSettings.updateDevicesMenu",
+ type_value, device_list,
+ default_value);
+}
+
+std::string MediaDevicesSelectionHandler::GetDeviceDisplayName(
+ const content::MediaStreamDevice& device) const {
+ std::string facing_info;
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ switch (device.video_facing) {
+ case media::VideoFacingMode::MEDIA_VIDEO_FACING_USER:
+ facing_info = l10n_util::GetStringUTF8(IDS_CAMERA_FACING_USER);
+ break;
+ case media::VideoFacingMode::MEDIA_VIDEO_FACING_ENVIRONMENT:
+ facing_info = l10n_util::GetStringUTF8(IDS_CAMERA_FACING_ENVIRONMENT);
+ break;
+ case media::VideoFacingMode::MEDIA_VIDEO_FACING_NONE:
+ break;
+ case media::VideoFacingMode::NUM_MEDIA_VIDEO_FACING_MODES:
+ NOTREACHED();
+ break;
+ }
+#endif
+
+ if (facing_info.empty())
+ return device.name;
+ return device.name + " " + facing_info;
+}
+
+void MediaDevicesSelectionHandler::UpdateDevicesMenuForType(DeviceType type) {
+ content::MediaStreamDevices devices;
+ switch (type) {
+ case AUDIO:
+ devices = MediaCaptureDevicesDispatcher::GetInstance()->
+ GetAudioCaptureDevices();
+ break;
+ case VIDEO:
+ devices = MediaCaptureDevicesDispatcher::GetInstance()->
+ GetVideoCaptureDevices();
+ break;
+ }
+
+ UpdateDevicesMenu(type, devices);
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/media_devices_selection_handler.h b/chromium/chrome/browser/ui/webui/options/media_devices_selection_handler.h
new file mode 100644
index 00000000000..ff1355ad633
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/media_devices_selection_handler.h
@@ -0,0 +1,59 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_MEDIA_DEVICES_SELECTION_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_MEDIA_DEVICES_SELECTION_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "content/public/browser/web_contents.h"
+
+namespace options {
+
+// Handler for media devices selection in content settings.
+class MediaDevicesSelectionHandler
+ : public MediaCaptureDevicesDispatcher::Observer,
+ public OptionsPageUIHandler {
+ public:
+ MediaDevicesSelectionHandler();
+ ~MediaDevicesSelectionHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* values) override;
+ void InitializePage() override;
+ void RegisterMessages() override;
+
+ // MediaCaptureDevicesDispatcher::Observer implementation.
+ void OnUpdateAudioDevices(
+ const content::MediaStreamDevices& devices) override;
+ void OnUpdateVideoDevices(
+ const content::MediaStreamDevices& devices) override;
+
+ private:
+ enum DeviceType {
+ AUDIO,
+ VIDEO,
+ };
+
+ // Sets the default audio/video capture device for media. |args| includes the
+ // media type (kAuudio/kVideo) and the unique id of the new default device
+ // that the user has chosen.
+ void SetDefaultCaptureDevice(const base::ListValue* args);
+
+ // Helpers methods to update the device menus.
+ void UpdateDevicesMenuForType(DeviceType type);
+ void UpdateDevicesMenu(DeviceType type,
+ const content::MediaStreamDevices& devices);
+
+ // Gets the human readable name of the device.
+ std::string GetDeviceDisplayName(
+ const content::MediaStreamDevice& device) const;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaDevicesSelectionHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_MEDIA_DEVICES_SELECTION_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/multilanguage_options_browsertest.cc b/chromium/chrome/browser/ui/webui/options/multilanguage_options_browsertest.cc
new file mode 100644
index 00000000000..4c95d59f7b9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/multilanguage_options_browsertest.cc
@@ -0,0 +1,48 @@
+// 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.
+
+#include "chrome/browser/ui/webui/options/multilanguage_options_browsertest.h"
+
+#include <string>
+
+#include "base/command_line.h"
+#include "build/build_config.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "components/spellcheck/browser/pref_names.h"
+
+MultilanguageOptionsBrowserTest::MultilanguageOptionsBrowserTest() {
+}
+
+MultilanguageOptionsBrowserTest::~MultilanguageOptionsBrowserTest() {
+}
+
+void MultilanguageOptionsBrowserTest::ClearSpellcheckDictionaries() {
+ SetDictionariesPref(base::ListValue());
+}
+
+void MultilanguageOptionsBrowserTest::SetUpOnMainThread() {
+ WebUIBrowserTest::SetUpOnMainThread();
+#if defined(OS_CHROMEOS)
+ std::string setting_name = prefs::kLanguagePreferredLanguages;
+#else
+ std::string setting_name = prefs::kAcceptLanguages;
+#endif
+
+ PrefService* pref_service = browser()->profile()->GetPrefs();
+ pref_service->SetString(setting_name, "fr,es,de,en");
+ base::ListValue dictionaries;
+ dictionaries.AppendString("fr");
+ SetDictionariesPref(dictionaries);
+ pref_service->SetString(spellcheck::prefs::kSpellCheckDictionary,
+ std::string());
+}
+
+void MultilanguageOptionsBrowserTest::SetDictionariesPref(
+ const base::ListValue& value) {
+ browser()->profile()->GetPrefs()->Set(
+ spellcheck::prefs::kSpellCheckDictionaries, value);
+}
diff --git a/chromium/chrome/browser/ui/webui/options/multilanguage_options_browsertest.h b/chromium/chrome/browser/ui/webui/options/multilanguage_options_browsertest.h
new file mode 100644
index 00000000000..2ea4c66e0dc
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/multilanguage_options_browsertest.h
@@ -0,0 +1,29 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_MULTILANGUAGE_OPTIONS_BROWSERTEST_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_MULTILANGUAGE_OPTIONS_BROWSERTEST_H_
+
+#include "base/macros.h"
+#include "chrome/test/base/web_ui_browser_test.h"
+
+// This is a helper class used by multilanguage_options_webui_browsertest.js
+// to flip the enable-multilingual-spellchecker command line switch and set
+// the AcceptLanguages and SpellcheckDictionaries preferences.
+class MultilanguageOptionsBrowserTest : public WebUIBrowserTest {
+ public:
+ MultilanguageOptionsBrowserTest();
+ ~MultilanguageOptionsBrowserTest() override;
+ // Set the kSpellCheckDictionaries preference to an empty list value.
+ void ClearSpellcheckDictionaries();
+
+ private:
+ // WebUIBrowserTest implementation.
+ void SetUpOnMainThread() override;
+ void SetDictionariesPref(const base::ListValue& value);
+
+ DISALLOW_COPY_AND_ASSIGN(MultilanguageOptionsBrowserTest);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_MULTILANGUAGE_OPTIONS_BROWSERTEST_H_
diff --git a/chromium/chrome/browser/ui/webui/options/multilanguage_options_webui_browsertest.js b/chromium/chrome/browser/ui/webui/options/multilanguage_options_webui_browsertest.js
new file mode 100644
index 00000000000..222016cd4c6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/multilanguage_options_webui_browsertest.js
@@ -0,0 +1,212 @@
+// 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.
+
+GEN_INCLUDE(['options_browsertest_base.js']);
+GEN('#include "chrome/browser/ui/webui/options/' +
+ 'multilanguage_options_browsertest.h"');
+
+/**
+ * Test C++ fixture for Language Options WebUI testing.
+ * @constructor
+ * @extends {testing.Test}
+ */
+function MultilanguageOptionsWebUIBrowserTest() {}
+
+MultilanguageOptionsWebUIBrowserTest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /** @override */
+ browsePreload: 'chrome://settings-frame/languages',
+
+ /** @override */
+ typedefCppFixture: 'MultilanguageOptionsBrowserTest',
+
+ /** @override */
+ accessibilityIssuesAreErrors: true,
+
+ /** @param {string} expected Sorted currently selected languages. */
+ expectCurrentlySelected: function(expected) {
+ var languages = LanguageOptions.getInstance().spellCheckLanguages_;
+ expectEquals(expected, Object.keys(languages).sort().join());
+ },
+
+ /** @override */
+ setUp: function() {
+ OptionsBrowsertestBase.prototype.setUp.call(this);
+
+ assertFalse(cr.isMac);
+ expectFalse($('edit-custom-dictionary-button').hidden);
+ this.expectEnableSpellcheckCheckboxVisible();
+ this.expectCurrentlySelected('fr');
+
+ var requiredOwnedAriaRoleMissingSelectors = [
+ '#default-search-engine-list',
+ '#other-search-engine-list',
+ ];
+
+ // Enable when failure is resolved.
+ // AX_ARIA_08: http://crbug.com/559320
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'requiredOwnedAriaRoleMissing',
+ requiredOwnedAriaRoleMissingSelectors);
+
+ // Enable when failure is resolved.
+ // AX_ARIA_10: http://crbug.com/559266
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'unsupportedAriaAttribute',
+ '#language-options-list');
+
+ // Enable when failure is resolved.
+ // AX_TEXT_04: http://crbug.com/559271
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'linkWithUnclearPurpose',
+ '#languagePage > .content-area > .language-options-header > A');
+ },
+
+ /** @override */
+ tearDown: function() {
+ testing.Test.prototype.tearDown.call(this);
+ this.expectEnableSpellcheckCheckboxVisible();
+ },
+
+ /** Make sure the 'Enable spell checking' checkbox is visible. */
+ expectEnableSpellcheckCheckboxVisible: function() {
+ if ($('enable-spellcheck-container'))
+ expectFalse($('enable-spellcheck-container').hidden);
+ },
+};
+
+// Test that opening language options has the correct location.
+TEST_F('MultilanguageOptionsWebUIBrowserTest', 'TestOpenLanguageOptions',
+ function() {
+ expectEquals('chrome://settings-frame/languages', document.location.href);
+});
+
+// Test that only certain languages can be selected and used for spellchecking.
+// prefs::kLanguagePreferredLanguages/prefs::kAcceptLanguages is set to
+// 'fr,es,de,en' and prefs::kSpellCheckDictionaries is just 'fr'
+TEST_F('MultilanguageOptionsWebUIBrowserTest', 'ChangeSpellcheckLanguages',
+ function() {
+ expectTrue($('language-options-list').selectLanguageByCode('es'));
+ expectFalse($('spellcheck-language-checkbox').checked, 'es');
+
+ // Click 'es' and ensure that it gets checked and 'fr' stays checked.
+ $('spellcheck-language-checkbox').click();
+ expectTrue($('spellcheck-language-checkbox').checked, 'es');
+ expectTrue($('language-options-list').selectLanguageByCode('fr'));
+ expectTrue($('spellcheck-language-checkbox').checked, 'fr');
+ this.expectCurrentlySelected('es,fr');
+
+ // Click 'fr' and ensure that it gets unchecked and 'es' stays checked.
+ $('spellcheck-language-checkbox').click();
+ expectFalse($('spellcheck-language-checkbox').checked, 'fr');
+ $('language-options-list').selectLanguageByCode('es');
+ expectTrue($('spellcheck-language-checkbox').checked, 'es');
+ this.expectCurrentlySelected('es');
+});
+
+// Make sure 'am' cannot be selected as a language and 'fr' stays selected.
+TEST_F('MultilanguageOptionsWebUIBrowserTest', 'NotAcceptLanguage', function() {
+ expectFalse($('language-options-list').selectLanguageByCode('am'));
+ expectTrue($('language-options-list').selectLanguageByCode('fr'));
+ expectTrue($('spellcheck-language-checkbox').checked, 'fr');
+ this.expectCurrentlySelected('fr');
+});
+
+// Make sure 'en' cannot be used as a language and 'fr' stays selected.
+TEST_F('MultilanguageOptionsWebUIBrowserTest', 'UnusableLanguage', function() {
+ expectTrue($('language-options-list').selectLanguageByCode('en'));
+ expectTrue($('spellcheck-language-checkbox-container').hidden);
+ expectFalse($('spellcheck-language-checkbox').checked, 'en');
+ this.expectCurrentlySelected('fr');
+});
+
+/**
+ * Test C++ fixture for Language Options WebUI testing.
+ * @constructor
+ * @extends {MultilanguageOptionsWebUIBrowserTest}
+ */
+function MultilanguagePreferenceWebUIBrowserTest() {}
+
+MultilanguagePreferenceWebUIBrowserTest.prototype = {
+ __proto__: MultilanguageOptionsWebUIBrowserTest.prototype,
+
+ /** @override */
+ testGenPreamble: function() {
+ GEN('ClearSpellcheckDictionaries();');
+ },
+
+ /** @override */
+ isAsync: true,
+
+ /**
+ * @param {string} expected Sorted languages in the kSpellCheckDictionaries
+ * preference.
+ */
+ expectRegisteredDictionariesPref: function(expected) {
+ var registeredPrefs =
+ options.Preferences.getInstance().registeredPreferences_;
+ expectEquals(expected,
+ registeredPrefs['spellcheck.dictionaries'].orig.value.sort().join());
+ },
+
+ /**
+ * Watch for a change to the preference |pref| and then call |callback|.
+ * @param {string} pref The preference to listen for a change on.
+ * @param {function} callback The function to run after a listener event.
+ */
+ addPreferenceListener: function(pref, callback) {
+ options.Preferences.getInstance().addEventListener(pref, callback);
+ },
+
+ /** @override */
+ setUp: function() {
+ OptionsBrowsertestBase.prototype.setUp.call(this);
+
+ assertFalse(cr.isMac);
+ expectTrue($('edit-custom-dictionary-button').hidden);
+ this.expectEnableSpellcheckCheckboxVisible();
+ this.expectCurrentlySelected('');
+ this.expectRegisteredDictionariesPref('');
+
+ // Enable when failure is resolved.
+ // AX_ARIA_10: http://crbug.com/559266
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'unsupportedAriaAttribute',
+ '#language-options-list');
+
+ // Enable when failure is resolved.
+ // AX_TEXT_04: http://crbug.com/559271
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'linkWithUnclearPurpose',
+ '#languagePage > .content-area > .language-options-header > A');
+
+ // Enable when failure is resolved.
+ // AX_FOCUS_01: http://crbug.com/570046
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'focusableElementNotVisibleAndNotAriaHidden',
+ '#offer-to-translate-in-this-language');
+ },
+};
+
+// Make sure the case where no languages are selected is handled properly.
+TEST_F('MultilanguagePreferenceWebUIBrowserTest', 'SelectFromBlank',
+ function() {
+ expectTrue($('language-options-list').selectLanguageByCode('fr'));
+ expectFalse($('spellcheck-language-checkbox').checked, 'fr');
+ expectTrue($('edit-custom-dictionary-button').hidden);
+
+ // Add a preference change event listener which ensures that the preference is
+ // updated correctly and that 'fr' is the only thing in the dictionary object.
+ this.addPreferenceListener('spellcheck.dictionaries', function() {
+ expectTrue($('spellcheck-language-checkbox').checked, 'fr');
+ this.expectRegisteredDictionariesPref('fr');
+ this.expectCurrentlySelected('fr');
+ expectFalse($('edit-custom-dictionary-button').hidden);
+ testDone();
+ }.bind(this));
+
+ // Click 'fr' and trigger the preference listener.
+ $('spellcheck-language-checkbox').click();
+});
diff --git a/chromium/chrome/browser/ui/webui/options/options_browsertest.cc b/chromium/chrome/browser/ui/webui/options/options_browsertest.cc
new file mode 100644
index 00000000000..6eca2fc2dff
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/options_browsertest.cc
@@ -0,0 +1,70 @@
+// 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/options/options_browsertest.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "url/gurl.h"
+
+using content::NavigationController;
+using content::NavigationEntry;
+using content::WebUIMessageHandler;
+
+OptionsBrowserTest::OptionsBrowserTest() {
+}
+
+OptionsBrowserTest::~OptionsBrowserTest() {
+}
+
+void OptionsBrowserTest::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "optionsTestReportHistory", base::Bind(&OptionsBrowserTest::ReportHistory,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "optionsTestSetPref", base::Bind(&OptionsBrowserTest::HandleSetPref,
+ base::Unretained(this)));
+}
+
+// Includes the current entry.
+void OptionsBrowserTest::ReportHistory(const base::ListValue* list_value) {
+ const NavigationController& controller =
+ browser()->tab_strip_model()->GetActiveWebContents()->GetController();
+ base::ListValue history;
+ const int current = controller.GetCurrentEntryIndex();
+ for (int i = 0; i <= current; ++i) {
+ GURL url = controller.GetEntryAtIndex(i)->GetVirtualURL();
+ history.AppendString(url.spec());
+ }
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "OptionsWebUIExtendedTest.verifyHistoryCallback", history);
+}
+
+void OptionsBrowserTest::ClearPref(const char* path) {
+ browser()->profile()->GetPrefs()->ClearPref(path);
+}
+
+void OptionsBrowserTest::HandleSetPref(const base::ListValue* args) {
+ ASSERT_EQ(2u, args->GetSize());
+
+ std::string pref_name;
+ ASSERT_TRUE(args->GetString(0, &pref_name));
+ const base::Value* pref_value;
+ ASSERT_TRUE(args->Get(1, &pref_value));
+
+ browser()->profile()->GetPrefs()->Set(pref_name.c_str(), *pref_value);
+}
+
+content::WebUIMessageHandler* OptionsBrowserTest::GetMockMessageHandler() {
+ return this;
+}
diff --git a/chromium/chrome/browser/ui/webui/options/options_browsertest.h b/chromium/chrome/browser/ui/webui/options/options_browsertest.h
new file mode 100644
index 00000000000..d8135a81dd7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/options_browsertest.h
@@ -0,0 +1,45 @@
+// 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_OPTIONS_OPTIONS_BROWSERTEST_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_OPTIONS_BROWSERTEST_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/test/base/web_ui_browser_test.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+// This is a helper class used by options_browsertest.js to feed the navigation
+// history back to the test.
+class OptionsBrowserTest : public WebUIBrowserTest,
+ public content::WebUIMessageHandler {
+ public:
+ OptionsBrowserTest();
+ ~OptionsBrowserTest() override;
+
+ protected:
+ // Clears the preference at the given |path|.
+ void ClearPref(const char* path);
+
+ private:
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // WebUIBrowserTest implementation.
+ content::WebUIMessageHandler* GetMockMessageHandler() override;
+
+ // A callback for the 'optionsTestReportHistory' message, this sends the
+ // URLs in the "back" tab history, including the current entry, back to the
+ // WebUI via a callback.
+ void ReportHistory(const base::ListValue* list_value);
+
+ // A callback for the 'optionsTestSetPref' message. The first argument should
+ // be a pref path (e.g., "profile.managed_users"); the second, the new value
+ // to set for that pref.
+ void HandleSetPref(const base::ListValue* args);
+
+ DISALLOW_COPY_AND_ASSIGN(OptionsBrowserTest);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_OPTIONS_BROWSERTEST_H_
diff --git a/chromium/chrome/browser/ui/webui/options/options_browsertest.js b/chromium/chrome/browser/ui/webui/options/options_browsertest.js
new file mode 100644
index 00000000000..05dfd8c96b3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/options_browsertest.js
@@ -0,0 +1,969 @@
+// 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.
+
+GEN_INCLUDE(['options_browsertest_base.js']);
+GEN('#include "chrome/browser/ui/webui/options/options_browsertest.h"');
+
+/** @const */ var SUPERVISED_USERS_PREF = 'profile.managed_users';
+
+/**
+ * Wait for the method specified by |methodName|, on the |object| object, to be
+ * called, then execute |afterFunction|.
+ * @param {*} object Object with callable property named |methodName|.
+ * @param {string} methodName The name of the property on |object| to use as a
+ * callback.
+ * @param {!Function} afterFunction A function to call after object.methodName()
+ * is called.
+ */
+function waitForResponse(object, methodName, afterFunction) {
+ var originalCallback = object[methodName];
+
+ // Install a wrapper that temporarily replaces the original function.
+ object[methodName] = function() {
+ object[methodName] = originalCallback;
+ originalCallback.apply(this, arguments);
+ afterFunction();
+ };
+}
+
+/**
+ * Wait for the global window.onpopstate callback to be called (after a tab
+ * history navigation), then execute |afterFunction|.
+ * @param {!Function} afterFunction A function to call after pop state events.
+ */
+function waitForPopstate(afterFunction) {
+ waitForResponse(window, 'onpopstate', afterFunction);
+}
+
+/**
+ * TestFixture for OptionsPage WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function OptionsWebUITest() {}
+
+OptionsWebUITest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /**
+ * Browse to the options page & call our preLoad().
+ * @override
+ */
+ browsePreload: 'chrome://settings-frame',
+
+ /** @override */
+ isAsync: true,
+
+ /**
+ * Register a mock handler to ensure expectations are met and options pages
+ * behave correctly.
+ */
+ preLoad: function() {
+ this.makeAndRegisterMockHandler(
+ ['defaultZoomFactorAction',
+ 'fetchPrefs',
+ 'observePrefs',
+ 'setBooleanPref',
+ 'setIntegerPref',
+ 'setDoublePref',
+ 'setStringPref',
+ 'setObjectPref',
+ 'clearPref',
+ 'coreOptionsUserMetricsAction',
+ ]);
+
+ // Register stubs for methods expected to be called before/during tests.
+ // Specific expectations can be made in the tests themselves.
+ this.mockHandler.stubs().fetchPrefs(ANYTHING);
+ this.mockHandler.stubs().observePrefs(ANYTHING);
+ this.mockHandler.stubs().coreOptionsUserMetricsAction(ANYTHING);
+ },
+
+ /** @override */
+ setUp: function() {
+ OptionsBrowsertestBase.prototype.setUp.call(this);
+
+ // Enable when failure is resolved.
+ // AX_ARIA_10: http://crbug.com/559329
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'unsupportedAriaAttribute',
+ '#profiles-list');
+
+ var linkWithUnclearPurposeSelectors = [
+ '#sync-overview > A',
+ '#privacy-explanation > A',
+ '#languages-section > .settings-row > A',
+ '#cloudprint-options-mdns > .settings-row > A',
+ '#cups-printers-section > .settings-row > A',
+ '#do-not-track-confirm-overlay > .action-area > .hbox.stretch > A',
+ ];
+
+ // Enable when failure is resolved.
+ // AX_TEXT_04: http://crbug.com/559318
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'linkWithUnclearPurpose',
+ linkWithUnclearPurposeSelectors);
+
+ // Causes testDefaultZoomFactor to flake. See http://crbug.com/611233.
+ var requiredOwnedAriaRoleMissingSelectors = [
+ '#default-search-engine-list',
+ '#other-search-engine-list',
+ ];
+
+ // Enable when failure is resolved.
+ // AX_ARIA_08: http://crbug.com/606657
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'requiredOwnedAriaRoleMissing',
+ requiredOwnedAriaRoleMissingSelectors);
+ },
+};
+
+/**
+ * Wait for all targets to be hidden.
+ * @param {Array<Element>} targets
+ */
+function waitUntilHidden(targets) {
+ function isHidden(el) { return el.hidden; }
+ function ensureTransition(el) { ensureTransitionEndEvent(el, 500); }
+
+ document.addEventListener('transitionend', function f(e) {
+ if (targets.indexOf(e.target) >= 0 && targets.every(isHidden)) {
+ document.removeEventListener(f, 'transitionend');
+ testDone();
+ }
+ });
+
+ targets.forEach(ensureTransition);
+}
+
+// Crashes on Mac only. See http://crbug.com/79181
+GEN('#if defined(OS_MACOSX)');
+GEN('#define MAYBE_testSetBooleanPrefTriggers ' +
+ 'DISABLED_testSetBooleanPrefTriggers');
+GEN('#else');
+GEN('#define MAYBE_testSetBooleanPrefTriggers testSetBooleanPrefTriggers');
+GEN('#endif // defined(OS_MACOSX)');
+
+TEST_F('OptionsWebUITest', 'MAYBE_testSetBooleanPrefTriggers', function() {
+ // TODO(dtseng): make generic to click all buttons.
+ var showHomeButton =
+ document.querySelector('input[pref="browser.show_home_button"]');
+ var trueListValue = [
+ 'browser.show_home_button',
+ true,
+ 'Options_Homepage_HomeButton',
+ ];
+ // Note: this expectation is checked in testing::Test::tearDown.
+ this.mockHandler.expects(once()).setBooleanPref(trueListValue);
+
+ // Cause the handler to be called.
+ showHomeButton.click();
+ showHomeButton.blur();
+ testDone();
+});
+
+// Not meant to run on ChromeOS at this time.
+// Not finishing in windows. http://crbug.com/81723
+TEST_F('OptionsWebUITest', 'DISABLED_testRefreshStaysOnCurrentPage',
+ function() {
+ assertTrue($('search-engine-manager-page').hidden);
+ var item = $('manage-default-search-engines');
+ item.click();
+
+ assertFalse($('search-engine-manager-page').hidden);
+
+ window.location.reload();
+
+ assertEquals('chrome://settings-frame/searchEngines', document.location.href);
+ assertFalse($('search-engine-manager-page').hidden);
+ testDone();
+});
+
+/**
+ * Test the default zoom factor select element.
+ */
+TEST_F('OptionsWebUITest', 'testDefaultZoomFactor', function() {
+ // The expected minimum length of the |defaultZoomFactor| element.
+ var defaultZoomFactorMinimumLength = 10;
+ // Verify that the zoom factor element exists.
+ var defaultZoomFactor = $('defaultZoomFactor');
+ assertNotEquals(defaultZoomFactor, null);
+
+ // Verify that the zoom factor element has a reasonable number of choices.
+ expectGE(defaultZoomFactor.options.length, defaultZoomFactorMinimumLength);
+
+ // Simulate a change event, selecting the highest zoom value. Verify that
+ // the javascript handler was invoked once.
+ this.mockHandler.expects(once()).defaultZoomFactorAction(NOT_NULL).
+ will(callFunction(function() { }));
+ defaultZoomFactor.selectedIndex = defaultZoomFactor.options.length - 1;
+ var event = {target: defaultZoomFactor};
+ if (defaultZoomFactor.onchange) defaultZoomFactor.onchange(event);
+ testDone();
+});
+
+/**
+ * If |confirmInterstitial| is true, the OK button of the Do Not Track
+ * interstitial is pressed, otherwise the abort button is pressed.
+ * @param {boolean} confirmInterstitial Whether to confirm the Do Not Track
+ * interstitial.
+ */
+OptionsWebUITest.prototype.testDoNotTrackInterstitial =
+ function(confirmInterstitial) {
+ Preferences.prefsFetchedCallback({'enable_do_not_track': {'value': false}});
+ var buttonToClick = confirmInterstitial ? $('do-not-track-confirm-ok') :
+ $('do-not-track-confirm-cancel');
+ var dntCheckbox = $('do-not-track-enabled');
+ var dntOverlay = PageManager.registeredOverlayPages['donottrackconfirm'];
+ assertFalse(dntCheckbox.checked);
+
+ var visibleChangeCounter = 0;
+ var visibleChangeHandler = function() {
+ ++visibleChangeCounter;
+ switch (visibleChangeCounter) {
+ case 1:
+ window.setTimeout(function() {
+ assertTrue(dntOverlay.visible);
+ buttonToClick.click();
+ }, 0);
+ break;
+ case 2:
+ window.setTimeout(function() {
+ assertFalse(dntOverlay.visible);
+ assertEquals(confirmInterstitial, dntCheckbox.checked);
+ dntOverlay.removeEventListener('visibleChange', visibleChangeHandler);
+ testDone();
+ }, 0);
+ break;
+ default:
+ assertTrue(false);
+ }
+ };
+ dntOverlay.addEventListener('visibleChange', visibleChangeHandler);
+
+ if (confirmInterstitial) {
+ this.mockHandler.expects(once()).setBooleanPref(
+ ['enable_do_not_track', true, 'Options_DoNotTrackCheckbox']);
+ } else {
+ // The mock handler complains if setBooleanPref is called even though
+ // it should not be.
+ }
+
+ dntCheckbox.click();
+};
+
+TEST_F('OptionsWebUITest', 'EnableDoNotTrackAndConfirmInterstitial',
+ function() {
+ this.testDoNotTrackInterstitial(true);
+});
+
+TEST_F('OptionsWebUITest', 'EnableDoNotTrackAndCancelInterstitial',
+ function() {
+ this.testDoNotTrackInterstitial(false);
+});
+
+// Check that the "Do not Track" preference can be correctly disabled.
+// In order to do that, we need to enable it first.
+TEST_F('OptionsWebUITest', 'EnableAndDisableDoNotTrack', function() {
+ Preferences.prefsFetchedCallback({'enable_do_not_track': {'value': false}});
+ var dntCheckbox = $('do-not-track-enabled');
+ var dntOverlay = PageManager.registeredOverlayPages.donottrackconfirm;
+ assertFalse(dntCheckbox.checked);
+
+ var visibleChangeCounter = 0;
+ var visibleChangeHandler = function() {
+ ++visibleChangeCounter;
+ switch (visibleChangeCounter) {
+ case 1:
+ window.setTimeout(function() {
+ assertTrue(dntOverlay.visible);
+ $('do-not-track-confirm-ok').click();
+ }, 0);
+ break;
+ case 2:
+ window.setTimeout(function() {
+ assertFalse(dntOverlay.visible);
+ assertTrue(dntCheckbox.checked);
+ dntOverlay.removeEventListener('visibleChange', visibleChangeHandler);
+ dntCheckbox.click();
+ }, 0);
+ break;
+ default:
+ assertNotReached();
+ }
+ };
+ dntOverlay.addEventListener('visibleChange', visibleChangeHandler);
+
+ this.mockHandler.expects(once()).setBooleanPref(
+ eq(['enable_do_not_track', true, 'Options_DoNotTrackCheckbox']));
+
+ var verifyCorrectEndState = function() {
+ window.setTimeout(function() {
+ assertFalse(dntOverlay.visible);
+ assertFalse(dntCheckbox.checked);
+ testDone();
+ }, 0);
+ };
+ this.mockHandler.expects(once()).setBooleanPref(
+ eq(['enable_do_not_track', false, 'Options_DoNotTrackCheckbox'])).will(
+ callFunction(verifyCorrectEndState));
+
+ dntCheckbox.click();
+});
+
+// Fails on chromeos, http://crbug.com/660867
+// Verify that preventDefault() is called on 'Enter' keydown events that trigger
+// the default button. If this doesn't happen, other elements that may get
+// focus (by the overlay closing for instance), will execute in addition to the
+// default button. See crbug.com/268336.
+TEST_F('OptionsWebUITest', 'DISABLED_EnterPreventsDefault', function() {
+ var page = HomePageOverlay.getInstance();
+ PageManager.showPageByName(page.name);
+ var event = new KeyboardEvent('keydown', {
+ 'bubbles': true,
+ 'cancelable': true,
+ 'key': 'Enter'
+ });
+ assertFalse(event.defaultPrevented);
+ page.pageDiv.dispatchEvent(event);
+ assertTrue(event.defaultPrevented);
+ testDone();
+});
+
+// Verifies that sending an empty list of indexes to move doesn't crash chrome.
+TEST_F('OptionsWebUITest', 'emptySelectedIndexesDoesntCrash', function() {
+ chrome.send('dragDropStartupPage', [0, []]);
+ setTimeout(testDone);
+});
+
+// This test turns out to be flaky on all platforms.
+// See http://crbug.com/315250.
+
+// An overlay's position should remain the same as it shows.
+TEST_F('OptionsWebUITest', 'DISABLED_OverlayShowDoesntShift', function() {
+ var overlayName = 'startup';
+ var overlay = $('startup-overlay');
+ var frozenPages = document.getElementsByClassName('frozen'); // Gets updated.
+ expectEquals(0, frozenPages.length);
+
+ document.addEventListener('transitionend', function(e) {
+ if (e.target != overlay)
+ return;
+
+ assertFalse(overlay.classList.contains('transparent'));
+ expectEquals(numFrozenPages, frozenPages.length);
+ testDone();
+ });
+
+ PageManager.showPageByName(overlayName);
+ var numFrozenPages = frozenPages.length;
+ expectGT(numFrozenPages, 0);
+});
+
+GEN('#if defined(OS_CHROMEOS)');
+// Verify that range inputs respond to touch events. Currently only Chrome OS
+// uses slider options.
+TEST_F('OptionsWebUITest', 'RangeInputHandlesTouchEvents', function() {
+ this.mockHandler.expects(once()).setIntegerPref([
+ 'settings.touchpad.sensitivity2', 1]);
+
+ var touchpadRange = $('touchpad-sensitivity-range');
+ var event = document.createEvent('UIEvent');
+ event.initUIEvent('touchstart', true, true, window);
+ touchpadRange.dispatchEvent(event);
+
+ event = document.createEvent('UIEvent');
+ event.initUIEvent('touchmove', true, true, window);
+ touchpadRange.dispatchEvent(event);
+
+ touchpadRange.value = 1;
+
+ event = document.createEvent('UIEvent');
+ event.initUIEvent('touchend', true, true, window);
+ touchpadRange.dispatchEvent(event);
+
+ // touchcancel should also trigger the handler, since it
+ // changes the slider position.
+ this.mockHandler.expects(once()).setIntegerPref([
+ 'settings.touchpad.sensitivity2', 2]);
+
+ event = document.createEvent('UIEvent');
+ event.initUIEvent('touchstart', true, true, window);
+ touchpadRange.dispatchEvent(event);
+
+ touchpadRange.value = 2;
+
+ event = document.createEvent('UIEvent');
+ event.initUIEvent('touchcancel', true, true, window);
+ touchpadRange.dispatchEvent(event);
+
+ testDone();
+});
+GEN('#endif'); // defined(OS_CHROMEOS)
+
+/**
+ * TestFixture for OptionsPage WebUI testing including tab history and support
+ * for preference manipulation. If you don't need the features in the C++
+ * fixture, use the simpler OptionsWebUITest (above) instead.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function OptionsWebUIExtendedTest() {}
+
+OptionsWebUIExtendedTest.prototype = {
+ __proto__: OptionsWebUITest.prototype,
+
+ /** @override */
+ typedefCppFixture: 'OptionsBrowserTest',
+
+ /** @override */
+ setUp: function() {
+ OptionsWebUITest.prototype.setUp.call(this);
+
+ // Enable when failure is resolved.
+ // AX_ARIA_10: http://crbug.com/559329
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'unsupportedAriaAttribute',
+ '#profiles-list');
+
+ var controlsWithoutLabelSelectors = [
+ '#cookies-view-page > .content-area.cookies-list-content-area > *',
+ '#other-search-engine-list > .deletable-item > DIV > *',
+ ];
+
+ // Enable when failure is resolved.
+ // AX_TEXT_01: http://crbug.com/559330
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'controlsWithoutLabel',
+ controlsWithoutLabelSelectors);
+
+ var linkWithUnclearPurposeSelectors = [
+ '#sync-overview > A',
+ '#privacy-explanation > A',
+ '#languages-section > .settings-row > A',
+ '#cloudprint-options-mdns > .settings-row > A',
+ // Selectors below only affect ChromeOS tests.
+ '#privacy-section > DIV > DIV:nth-of-type(9) > A',
+ '#accessibility-learn-more',
+ ];
+
+ // Enable when failure is resolved.
+ // AX_TEXT_04: http://crbug.com/559326
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'linkWithUnclearPurpose',
+ linkWithUnclearPurposeSelectors);
+
+ var requiredOwnedAriaRoleMissingSelectors = [
+ '#default-search-engine-list',
+ '#other-search-engine-list',
+ ];
+
+ // Enable when failure is resolved.
+ // AX_ARIA_08: http://crbug.com/605689
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'requiredOwnedAriaRoleMissing',
+ requiredOwnedAriaRoleMissingSelectors);
+ },
+
+ testGenPreamble: function() {
+ // Start with no supervised users managed by this profile.
+ GEN(' ClearPref("' + SUPERVISED_USERS_PREF + '");');
+ },
+
+ /**
+ * Asserts that two non-nested arrays are equal. The arrays must contain only
+ * plain data types, no nested arrays or other objects.
+ * @param {Array} expected An array of expected values.
+ * @param {Array} result An array of actual values.
+ * @param {boolean} doSort If true, the arrays will be sorted before being
+ * compared.
+ * @param {string} description A brief description for the array of actual
+ * values, to use in an error message if the arrays differ.
+ * @private
+ */
+ compareArrays_: function(expected, result, doSort, description) {
+ var errorMessage = '\n' + description + ': ' + result +
+ '\nExpected: ' + expected;
+ assertEquals(expected.length, result.length, errorMessage);
+
+ var expectedSorted = expected.slice();
+ var resultSorted = result.slice();
+ if (doSort) {
+ expectedSorted.sort();
+ resultSorted.sort();
+ }
+
+ for (var i = 0; i < expectedSorted.length; ++i) {
+ assertEquals(expectedSorted[i], resultSorted[i], errorMessage);
+ }
+ },
+
+ /**
+ * Verifies that the correct pages are currently open/visible.
+ * @param {!Array<string>} expectedPages An array of page names expected to
+ * be open, with the topmost listed last.
+ * @param {string=} opt_expectedUrl The URL path, including hash, expected to
+ * be open. If undefined, the topmost (last) page name in |expectedPages|
+ * will be used. In either case, 'chrome://settings-frame/' will be
+ * prepended.
+ * @private
+ */
+ verifyOpenPages_: function(expectedPages, opt_expectedUrl) {
+ // Check the topmost page.
+ expectEquals(null, PageManager.getVisibleBubble());
+ var currentPage = PageManager.getTopmostVisiblePage();
+
+ var lastExpected = expectedPages[expectedPages.length - 1];
+ expectEquals(lastExpected, currentPage.name);
+ // We'd like to check the title too, but we have to load the settings-frame
+ // instead of the outer settings page in order to have access to
+ // OptionsPage, and setting the title from within the settings-frame fails
+ // because of cross-origin access restrictions.
+ // TODO(pamg): Add a test fixture that loads chrome://settings and uses
+ // UI elements to access sub-pages, so we can test the titles and
+ // search-page URLs.
+ var expectedUrl = (typeof opt_expectedUrl == 'undefined') ?
+ lastExpected : opt_expectedUrl;
+ var fullExpectedUrl = 'chrome://settings-frame/' + expectedUrl;
+ expectEquals(fullExpectedUrl, window.location.href);
+
+ // Collect open pages.
+ var allPageNames = Object.keys(PageManager.registeredPages).concat(
+ Object.keys(PageManager.registeredOverlayPages));
+ var openPages = [];
+ for (var i = 0; i < allPageNames.length; ++i) {
+ var name = allPageNames[i];
+ var page = PageManager.registeredPages[name] ||
+ PageManager.registeredOverlayPages[name];
+ if (page.visible)
+ openPages.push(page.name);
+ }
+
+ this.compareArrays_(expectedPages, openPages, true, 'Open pages');
+ },
+
+ /*
+ * Verifies that the correct URLs are listed in the history. Asynchronous.
+ * @param {!Array<string>} expectedHistory An array of URL paths expected to
+ * be in the tab navigation history, sorted by visit time, including the
+ * current page as the last entry. The base URL (chrome://settings-frame/)
+ * will be prepended to each. An initial 'about:blank' history entry is
+ * assumed and should not be included in this list.
+ * @param {Function=} callback A function to be called after the history has
+ * been verified successfully. May be undefined.
+ * @private
+ */
+ verifyHistory_: function(expectedHistory, callback) {
+ var self = this;
+ OptionsWebUIExtendedTest.verifyHistoryCallback = function(results) {
+ // The history always starts with a blank page.
+ assertEquals('about:blank', results.shift());
+ var fullExpectedHistory = [];
+ for (var i = 0; i < expectedHistory.length; ++i) {
+ fullExpectedHistory.push(
+ 'chrome://settings-frame/' + expectedHistory[i]);
+ }
+ self.compareArrays_(fullExpectedHistory, results, false, 'History');
+ callback();
+ };
+
+ // The C++ fixture will call verifyHistoryCallback with the results.
+ chrome.send('optionsTestReportHistory');
+ },
+
+ /**
+ * Overrides the page callbacks for the given PageManager overlay to verify
+ * that they are not called.
+ * @param {Object} overlay The singleton instance of the overlay.
+ * @private
+ */
+ prohibitChangesToOverlay_: function(overlay) {
+ overlay.initializePage =
+ overlay.didShowPage =
+ overlay.didClosePage = function() {
+ assertTrue(false,
+ 'Overlay was affected when changes were prohibited.');
+ };
+ },
+};
+
+/**
+ * Set by verifyHistory_ to incorporate a followup callback, then called by the
+ * C++ fixture with the navigation history to be verified.
+ * @type {Function}
+ */
+OptionsWebUIExtendedTest.verifyHistoryCallback = null;
+
+// Show the search page with no query string, to fall back to the settings page.
+// Test disabled because it's flaky. crbug.com/303841
+TEST_F('OptionsWebUIExtendedTest', 'DISABLED_ShowSearchPageNoQuery',
+ function() {
+ PageManager.showPageByName('search');
+ this.verifyOpenPages_(['settings']);
+ this.verifyHistory_(['settings'], testDone);
+});
+
+// Manipulate the search page via the search field.
+TEST_F('OptionsWebUIExtendedTest', 'ShowSearchFromField', function() {
+ $('search-field').onsearch({currentTarget: {value: 'query'}});
+ this.verifyOpenPages_(['settings', 'search'], 'search#query');
+ this.verifyHistory_(['', 'search#query'], function() {
+ $('search-field').onsearch({currentTarget: {value: 'query2'}});
+ this.verifyOpenPages_(['settings', 'search'], 'search#query2');
+ this.verifyHistory_(['', 'search#query', 'search#query2'], function() {
+ $('search-field').onsearch({currentTarget: {value: ''}});
+ this.verifyOpenPages_(['settings'], '');
+ this.verifyHistory_(['', 'search#query', 'search#query2', ''], testDone);
+ }.bind(this));
+ }.bind(this));
+});
+
+// Show a page without updating history.
+TEST_F('OptionsWebUIExtendedTest', 'ShowPageNoHistory', function() {
+ this.verifyOpenPages_(['settings'], '');
+ PageManager.showPageByName('search', true, {hash: '#query'});
+
+ // The settings page is also still "open" (i.e., visible), in order to show
+ // the search results. Furthermore, the URL hasn't been updated in the parent
+ // page, because we've loaded the chrome-settings frame instead of the whole
+ // settings page, so the cross-origin call to set the URL fails.
+ this.verifyOpenPages_(['settings', 'search'], 'search#query');
+ var self = this;
+ this.verifyHistory_(['', 'search#query'], function() {
+ PageManager.showPageByName('settings', false);
+ self.verifyOpenPages_(['settings'], 'search#query');
+ self.verifyHistory_(['', 'search#query'], testDone);
+ });
+});
+
+TEST_F('OptionsWebUIExtendedTest', 'ShowPageWithHistory', function() {
+ PageManager.showPageByName('search', true, {hash: '#query'});
+ var self = this;
+ this.verifyHistory_(['', 'search#query'], function() {
+ PageManager.showPageByName('settings', true);
+ self.verifyOpenPages_(['settings'], '');
+ self.verifyHistory_(['', 'search#query', ''],
+ testDone);
+ });
+});
+
+TEST_F('OptionsWebUIExtendedTest', 'ShowPageReplaceHistory', function() {
+ PageManager.showPageByName('search', true, {hash: '#query'});
+ var self = this;
+ this.verifyHistory_(['', 'search#query'], function() {
+ PageManager.showPageByName('settings', true, {'replaceState': true});
+ self.verifyOpenPages_(['settings'], '');
+ self.verifyHistory_(['', ''], testDone);
+ });
+});
+
+// This should be identical to ShowPageWithHisory.
+TEST_F('OptionsWebUIExtendedTest', 'NavigateToPage', function() {
+ PageManager.showPageByName('search', true, {hash: '#query'});
+ var self = this;
+ this.verifyHistory_(['', 'search#query'], function() {
+ PageManager.showPageByName('settings');
+ self.verifyOpenPages_(['settings'], '');
+ self.verifyHistory_(['', 'search#query', ''], testDone);
+ });
+});
+
+// Settings overlays are much more straightforward than settings pages, opening
+// normally with none of the latter's quirks in the expected history or URL.
+TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayNoHistory', function() {
+ // Open a layer-1 overlay, not updating history.
+ PageManager.showPageByName('languages', false);
+ this.verifyOpenPages_(['settings', 'languages'], '');
+
+ var self = this;
+ this.verifyHistory_([''], function() {
+ // Open a layer-2 overlay for which the layer-1 is a parent, not updating
+ // history.
+ PageManager.showPageByName('addLanguage', false);
+ self.verifyOpenPages_(['settings', 'languages', 'addLanguage'],
+ '');
+ self.verifyHistory_([''], testDone);
+ });
+});
+
+TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayWithHistory', function() {
+ // Open a layer-1 overlay, updating history.
+ PageManager.showPageByName('languages', true);
+ this.verifyOpenPages_(['settings', 'languages']);
+
+ var self = this;
+ this.verifyHistory_(['', 'languages'], function() {
+ // Open a layer-2 overlay, updating history.
+ PageManager.showPageByName('addLanguage', true);
+ self.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
+ self.verifyHistory_(['', 'languages', 'addLanguage'], testDone);
+ });
+});
+
+TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayReplaceHistory', function() {
+ // Open a layer-1 overlay, updating history.
+ PageManager.showPageByName('languages', true);
+ var self = this;
+ this.verifyHistory_(['', 'languages'], function() {
+ // Open a layer-2 overlay, replacing history.
+ PageManager.showPageByName('addLanguage', true, {'replaceState': true});
+ self.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
+ self.verifyHistory_(['', 'addLanguage'], testDone);
+ });
+});
+
+// Directly show an overlay further above this page, i.e. one for which the
+// current page is an ancestor but not a parent.
+TEST_F('OptionsWebUIExtendedTest', 'ShowOverlayFurtherAbove', function() {
+ // Open a layer-2 overlay directly.
+ PageManager.showPageByName('addLanguage', true);
+ this.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
+ var self = this;
+ this.verifyHistory_(['', 'addLanguage'], testDone);
+});
+
+// Directly show a layer-2 overlay for which the layer-1 overlay is not a
+// parent.
+TEST_F('OptionsWebUIExtendedTest', 'ShowUnrelatedOverlay', function() {
+ // Open a layer-1 overlay.
+ PageManager.showPageByName('languages', true);
+ this.verifyOpenPages_(['settings', 'languages']);
+
+ var self = this;
+ this.verifyHistory_(['', 'languages'], function() {
+ // Open an unrelated layer-2 overlay.
+ PageManager.showPageByName('cookies', true);
+ self.verifyOpenPages_(['settings', 'content', 'cookies']);
+ self.verifyHistory_(['', 'languages', 'cookies'], testDone);
+ });
+});
+
+// Close an overlay.
+TEST_F('OptionsWebUIExtendedTest', 'CloseOverlay', function() {
+ // Open a layer-1 overlay, then a layer-2 overlay on top of it.
+ PageManager.showPageByName('languages', true);
+ this.verifyOpenPages_(['settings', 'languages']);
+ PageManager.showPageByName('addLanguage', true);
+ this.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
+
+ var self = this;
+ this.verifyHistory_(['', 'languages', 'addLanguage'], function() {
+ // Close the layer-2 overlay.
+ PageManager.closeOverlay();
+ self.verifyOpenPages_(['settings', 'languages']);
+ self.verifyHistory_(
+ ['', 'languages', 'addLanguage', 'languages'],
+ function() {
+ // Close the layer-1 overlay.
+ PageManager.closeOverlay();
+ self.verifyOpenPages_(['settings'], '');
+ self.verifyHistory_(
+ ['', 'languages', 'addLanguage', 'languages', ''],
+ function noop() {});
+ waitUntilHidden([$('overlay-container-1'), $('overlay-container-2')]);
+ });
+ });
+});
+
+// Hashes are maintained separately for each page and are preserved when
+// overlays close.
+TEST_F('OptionsWebUIExtendedTest', 'CloseOverlayWithHashes', function() {
+ // Open an overlay on top of the search page.
+ PageManager.showPageByName('search', true, {hash: '#1'});
+ this.verifyOpenPages_(['settings', 'search'], 'search#1');
+ PageManager.showPageByName('languages', true, {hash: '#2'});
+ this.verifyOpenPages_(['settings', 'search', 'languages'],
+ 'languages#2');
+ PageManager.showPageByName('addLanguage', true, {hash: '#3'});
+ this.verifyOpenPages_(['settings', 'search', 'languages', 'addLanguage'],
+ 'addLanguage#3');
+
+ this.verifyHistory_(['', 'search#1', 'languages#2', 'addLanguage#3'],
+ function() {
+ // Close the layer-2 overlay.
+ PageManager.closeOverlay();
+ this.verifyOpenPages_(['settings', 'search', 'languages'], 'languages#2');
+ this.verifyHistory_(
+ ['', 'search#1', 'languages#2', 'addLanguage#3', 'languages#2'],
+ function() {
+ // Close the layer-1 overlay.
+ PageManager.closeOverlay();
+ this.verifyOpenPages_(['settings', 'search'], 'search#1');
+ this.verifyHistory_(
+ ['', 'search#1', 'languages#2', 'addLanguage#3', 'languages#2',
+ 'search#1'],
+ function noop() {});
+ waitUntilHidden([$('overlay-container-1'), $('overlay-container-2')]);
+ }.bind(this));
+ }.bind(this));
+});
+
+// Test that closing an overlay that did not push history when opening does not
+// again push history.
+TEST_F('OptionsWebUIExtendedTest', 'CloseOverlayNoHistory', function() {
+ // Open the do not track confirmation prompt.
+ PageManager.showPageByName('doNotTrackConfirm', false);
+
+ // Opening the prompt does not add to the history.
+ this.verifyHistory_([''], function() {
+ // Close the overlay.
+ PageManager.closeOverlay();
+ // Still no history changes.
+ this.verifyHistory_([''], function noop() {});
+ waitUntilHidden([$('overlay-container-1')]);
+ }.bind(this));
+});
+
+// Make sure an overlay isn't closed (even temporarily) when another overlay is
+// opened on top.
+TEST_F('OptionsWebUIExtendedTest', 'OverlayAboveNoReset', function() {
+ // Open a layer-1 overlay.
+ PageManager.showPageByName('languages', true);
+ this.verifyOpenPages_(['settings', 'languages']);
+
+ // Open a layer-2 overlay on top. This should not close 'languages'.
+ this.prohibitChangesToOverlay_(options.LanguageOptions.getInstance());
+ PageManager.showPageByName('addLanguage', true);
+ this.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
+ testDone();
+});
+
+TEST_F('OptionsWebUIExtendedTest', 'OverlayTabNavigation', function() {
+ // Open a layer-1 overlay, then a layer-2 overlay on top of it.
+ PageManager.showPageByName('languages', true);
+ PageManager.showPageByName('addLanguage', true);
+ var self = this;
+
+ // Go back twice, then forward twice.
+ self.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
+ self.verifyHistory_(['', 'languages', 'addLanguage'], function() {
+ window.history.back();
+ waitForPopstate(function() {
+ self.verifyOpenPages_(['settings', 'languages']);
+ self.verifyHistory_(['', 'languages'], function() {
+ window.history.back();
+ waitForPopstate(function() {
+ self.verifyOpenPages_(['settings'], '');
+ self.verifyHistory_([''], function() {
+ window.history.forward();
+ waitForPopstate(function() {
+ self.verifyOpenPages_(['settings', 'languages']);
+ self.verifyHistory_(['', 'languages'], function() {
+ window.history.forward();
+ waitForPopstate(function() {
+ self.verifyOpenPages_(
+ ['settings', 'languages', 'addLanguage']);
+ self.verifyHistory_(
+ ['', 'languages', 'addLanguage'], testDone);
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+});
+
+// Going "back" to an overlay that's a child of the current overlay shouldn't
+// close the current one.
+TEST_F('OptionsWebUIExtendedTest', 'OverlayBackToChild', function() {
+ // Open a layer-1 overlay, then a layer-2 overlay on top of it.
+ PageManager.showPageByName('languages', true);
+ PageManager.showPageByName('addLanguage', true);
+ var self = this;
+
+ self.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
+ self.verifyHistory_(['', 'languages', 'addLanguage'], function() {
+ // Close the top overlay, then go back to it.
+ PageManager.closeOverlay();
+ self.verifyOpenPages_(['settings', 'languages']);
+ self.verifyHistory_(
+ ['', 'languages', 'addLanguage', 'languages'],
+ function() {
+ // Going back to the 'addLanguage' page should not close 'languages'.
+ self.prohibitChangesToOverlay_(options.LanguageOptions.getInstance());
+ window.history.back();
+ waitForPopstate(function() {
+ self.verifyOpenPages_(['settings', 'languages', 'addLanguage']);
+ self.verifyHistory_(['', 'languages', 'addLanguage'],
+ testDone);
+ });
+ });
+ });
+});
+
+// Going back to an unrelated overlay should close the overlay and its parent.
+TEST_F('OptionsWebUIExtendedTest', 'OverlayBackToUnrelated', function() {
+ // Open a layer-1 overlay, then an unrelated layer-2 overlay.
+ PageManager.showPageByName('languages', true);
+ PageManager.showPageByName('cookies', true);
+ var self = this;
+ self.verifyOpenPages_(['settings', 'content', 'cookies']);
+ self.verifyHistory_(['', 'languages', 'cookies'], function() {
+ window.history.back();
+ waitForPopstate(function() {
+ self.verifyOpenPages_(['settings', 'languages']);
+ testDone();
+ });
+ });
+});
+
+// Verify history changes properly while the page is loading.
+TEST_F('OptionsWebUIExtendedTest', 'HistoryUpdatedAfterLoading', function() {
+ var loc = location.href;
+
+ document.documentElement.classList.add('loading');
+ assertTrue(PageManager.isLoading());
+ PageManager.showPageByName('searchEngines');
+ expectNotEquals(loc, location.href);
+
+ document.documentElement.classList.remove('loading');
+ assertFalse(PageManager.isLoading());
+ PageManager.showDefaultPage();
+ expectEquals(loc, location.href);
+
+ testDone();
+});
+
+// A tip should be shown or hidden depending on whether this profile manages any
+// supervised users.
+TEST_F('OptionsWebUIExtendedTest', 'SupervisingUsers', function() {
+ // We start managing no supervised users.
+ assertTrue($('profiles-supervised-dashboard-tip').hidden);
+
+ // Remove all supervised users, then add some, watching for the pref change
+ // notifications and UI updates in each case. Any non-empty pref dictionary
+ // is interpreted as having supervised users.
+ chrome.send('optionsTestSetPref', [SUPERVISED_USERS_PREF, {key: 'value'}]);
+ waitForResponse(BrowserOptions, 'updateManagesSupervisedUsers', function() {
+ assertFalse($('profiles-supervised-dashboard-tip').hidden);
+ chrome.send('optionsTestSetPref', [SUPERVISED_USERS_PREF, {}]);
+ waitForResponse(BrowserOptions, 'updateManagesSupervisedUsers', function() {
+ assertTrue($('profiles-supervised-dashboard-tip').hidden);
+ testDone();
+ });
+ });
+});
+
+/**
+ * TestFixture that loads the options page at a bogus URL.
+ * @extends {OptionsWebUIExtendedTest}
+ * @constructor
+ */
+function OptionsWebUIRedirectTest() {
+ OptionsWebUIExtendedTest.call(this);
+}
+
+OptionsWebUIRedirectTest.prototype = {
+ __proto__: OptionsWebUIExtendedTest.prototype,
+
+ /** @override */
+ browsePreload: 'chrome://settings-frame/nonexistantPage',
+};
+
+TEST_F('OptionsWebUIRedirectTest', 'TestURL', function() {
+ assertEquals('chrome://settings-frame/', document.location.href);
+ this.verifyHistory_([''], testDone);
+});
diff --git a/chromium/chrome/browser/ui/webui/options/options_browsertest_base.js b/chromium/chrome/browser/ui/webui/options/options_browsertest_base.js
new file mode 100644
index 00000000000..073c7dc847e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/options_browsertest_base.js
@@ -0,0 +1,86 @@
+// Copyright (c) 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.
+
+/**
+ * @constructor
+ * @extends {testing.Test}
+ */
+function OptionsBrowsertestBase() {}
+
+OptionsBrowsertestBase.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /** @override */
+ runAccessibilityChecks: true,
+
+ /** @override */
+ accessibilityIssuesAreErrors: true,
+
+ /** @override */
+ setUp: function() {
+ testing.Test.prototype.setUp.call(this);
+
+ var requiredOwnedAriaRoleMissingSelectors = [
+ '#address-list',
+ '#creditcard-list',
+ '#home-page-overlay > .autocomplete-suggestions',
+ '#language-options-list',
+ '#manage-profile-icon-grid',
+ '#create-profile-icon-grid',
+ '#saved-passwords-list',
+ '#password-exceptions-list',
+ '#extension-keyword-list',
+ '#startup-overlay > .autocomplete-suggestions',
+ '#content-settings-exceptions-area > .content-area > *',
+ '#cookies-list',
+ '#handlers-list',
+ '#ignored-handlers-list',
+ '#supervised-user-list',
+ '#select-avatar-grid',
+ '#language-dictionary-overlay-word-list',
+ // Selectors below only affect ChromeOS tests.
+ '#bluetooth-unpaired-devices-list',
+ '#ignored-host-list',
+ '#remembered-network-list',
+ '#bluetooth-paired-devices-list',
+ ];
+
+ // Enable when failure is resolved.
+ // AX_ARIA_08: http://crbug.com/559265
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'requiredOwnedAriaRoleMissing',
+ requiredOwnedAriaRoleMissingSelectors);
+
+ var tabIndexGreaterThanZeroSelectors = [
+ '#user-image-grid',
+ '#discard-photo',
+ '#take-photo',
+ '#flip-photo',
+ '#change-picture-overlay-confirm',
+ ];
+
+ // Enable when failure is resolved.
+ // AX_FOCUS_03: http://crbug.com/560910
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'tabIndexGreaterThanZero',
+ tabIndexGreaterThanZeroSelectors);
+
+ // Enable when audit has improved performance.
+ // AX_HTML_02:
+ // https://github.com/GoogleChrome/accessibility-developer-tools/issues/263
+ this.accessibilityAuditConfig.auditRulesToIgnore.push(
+ 'duplicateId');
+
+ // Enable when failure is resolved.
+ // AX_ARIA_02: http://crbug.com/591547
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'nonExistentAriaRelatedElement', '#input');
+
+ // Enable when failure is resolved.
+ // AX_ARIA_04: http://crbug.com/591550
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'badAriaAttributeValue', '#input');
+
+ },
+};
diff --git a/chromium/chrome/browser/ui/webui/options/options_ui.cc b/chromium/chrome/browser/ui/webui/options/options_ui.cc
new file mode 100644
index 00000000000..4fea457622d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/options_ui.cc
@@ -0,0 +1,661 @@
+// 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/ui/webui/options/options_ui.h"
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/singleton.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_about_handler.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/tab_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/metrics_handler.h"
+#include "chrome/browser/ui/webui/options/autofill_options_handler.h"
+#include "chrome/browser/ui/webui/options/automatic_settings_reset_handler.h"
+#include "chrome/browser/ui/webui/options/browser_options_handler.h"
+#include "chrome/browser/ui/webui/options/clear_browser_data_handler.h"
+#include "chrome/browser/ui/webui/options/content_settings_handler.h"
+#include "chrome/browser/ui/webui/options/cookies_view_handler.h"
+#include "chrome/browser/ui/webui/options/core_options_handler.h"
+#include "chrome/browser/ui/webui/options/create_profile_handler.h"
+#include "chrome/browser/ui/webui/options/easy_unlock_handler.h"
+#include "chrome/browser/ui/webui/options/font_settings_handler.h"
+#include "chrome/browser/ui/webui/options/handler_options_handler.h"
+#include "chrome/browser/ui/webui/options/help_overlay_handler.h"
+#include "chrome/browser/ui/webui/options/home_page_overlay_handler.h"
+#include "chrome/browser/ui/webui/options/import_data_handler.h"
+#include "chrome/browser/ui/webui/options/language_dictionary_overlay_handler.h"
+#include "chrome/browser/ui/webui/options/language_options_handler.h"
+#include "chrome/browser/ui/webui/options/manage_profile_handler.h"
+#include "chrome/browser/ui/webui/options/media_devices_selection_handler.h"
+#include "chrome/browser/ui/webui/options/password_manager_handler.h"
+#include "chrome/browser/ui/webui/options/reset_profile_settings_handler.h"
+#include "chrome/browser/ui/webui/options/search_engine_manager_handler.h"
+#include "chrome/browser/ui/webui/options/startup_pages_handler.h"
+#include "chrome/browser/ui/webui/options/sync_setup_handler.h"
+#include "chrome/browser/ui/webui/theme_source.h"
+#include "chrome/common/features.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/locale_settings.h"
+#include "chrome/grit/options_resources.h"
+#include "chrome/grit/theme_resources.h"
+#include "components/omnibox/browser/autocomplete_match.h"
+#include "components/omnibox/browser/autocomplete_result.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/browser/web_ui.h"
+#include "net/base/escape.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/template_expressions.h"
+#include "ui/base/webui/jstemplate_builder.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "url/gurl.h"
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+#include "chrome/browser/ui/webui/options/supervised_user_create_confirm_handler.h"
+#include "chrome/browser/ui/webui/options/supervised_user_import_handler.h"
+#include "chrome/browser/ui/webui/options/supervised_user_learn_more_handler.h"
+#endif
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/browser/chromeos/system/pointer_device_observer.h"
+#include "chrome/browser/ui/webui/options/chromeos/accounts_options_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/bluetooth_options_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/cros_language_options_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/date_time_options_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/display_options_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/display_overscan_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/internet_options_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/keyboard_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/options_stylus_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/pointer_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/power_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/proxy_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/stats_options_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/storage_manager_handler.h"
+#include "chrome/browser/ui/webui/options/chromeos/user_image_source.h"
+#endif
+
+#if defined(USE_NSS_CERTS)
+#include "chrome/browser/ui/webui/options/certificate_manager_handler.h"
+#endif
+
+#if BUILDFLAG(ENABLE_GOOGLE_NOW)
+#include "chrome/browser/ui/webui/options/geolocation_options_handler.h"
+#endif
+
+using content::RenderViewHost;
+
+namespace {
+
+const char kLocalizedStringsFile[] = "strings.js";
+const char kOptionsBundleJsFile[] = "options_bundle.js";
+
+#if defined(OS_CHROMEOS)
+constexpr char kIconsHTMLPath[] = "icons.html";
+constexpr char kPinKeyboardHTMLPath[] = "people_page/pin_keyboard.html";
+constexpr char kPinKeyboardJSPath[] = "people_page/pin_keyboard.js";
+constexpr char kPasswordPromptDialogHTMLPath[] =
+ "people_page/password_prompt_dialog.html";
+constexpr char kPasswordPromptDialogJSPath[] =
+ "people_page/password_prompt_dialog.js";
+constexpr char kLockScreenConstantsHTMLPath[] =
+ "people_page/lock_screen_constants.html";
+constexpr char kLockScreenConstantsJSPath[] =
+ "people_page/lock_screen_constants.js";
+constexpr char kLockStateBehaviorHTMLPath[] =
+ "people_page/lock_state_behavior.html";
+constexpr char kLockStateBehaviorJSPath[] =
+ "people_page/lock_state_behavior.js";
+constexpr char kLockScreenHTMLPath[] = "people_page/lock_screen.html";
+constexpr char kLockScreenJSPath[] = "people_page/lock_screen.js";
+constexpr char kSetupPinHTMLPath[] = "people_page/setup_pin_dialog.html";
+constexpr char kSetupPinJSPath[] = "people_page/setup_pin_dialog.js";
+constexpr char kEasyUnlockBrowserProxyHTMLPath[] =
+ "people_page/easy_unlock_browser_proxy.html";
+constexpr char kEasyUnlockBrowserProxyJSPath[] =
+ "people_page/easy_unlock_browser_proxy.js";
+constexpr char kEasyUnlockTurnOffDialogHTMLPath[] =
+ "people_page/easy_unlock_turn_off_dialog.html";
+constexpr char kEasyUnlockTurnOffDialogJSPath[] =
+ "people_page/easy_unlock_turn_off_dialog.js";
+constexpr char kFingerprintListHTMLPath[] = "people_page/fingerprint_list.html";
+constexpr char kFingerprintListJSPath[] = "people_page/fingerprint_list.js";
+constexpr char kSetupFingerprintHTMLPath[] =
+ "people_page/setup_fingerprint_dialog.html";
+constexpr char kSetupFingerprintJSPath[] =
+ "people_page/setup_fingerprint_dialog.js";
+constexpr char kFingerprintBrowserProxyHTMLPath[] =
+ "people_page/fingerprint_browser_proxy.html";
+constexpr char kFingerprintBrowserProxyJSPath[] =
+ "people_page/fingerprint_browser_proxy.js";
+constexpr char kFingerprintProgressArcHTMLPath[] =
+ "people_page/fingerprint_progress_arc.html";
+constexpr char kFingerprintProgressArcJSPath[] =
+ "people_page/fingerprint_progress_arc.js";
+constexpr char kSettingsRouteHTMLPath[] = "route.html";
+constexpr char kSettingsRouteJSPath[] = "route.js";
+constexpr char kSettingsSharedCSSHTMLPath[] = "settings_shared_css.html";
+constexpr char kSettingsBooleanControlBehaviorHTMLPath[] =
+ "controls/settings_boolean_control_behavior.html";
+constexpr char kSettingsBooleanControlBehaviorJSPath[] =
+ "controls/settings_boolean_control_behavior.js";
+constexpr char kSettingsPrefControlBehaviorHTMLPath[] =
+ "controls/pref_control_behavior.html";
+constexpr char kSettingsPrefControlBehaviorJSPath[] =
+ "controls/pref_control_behavior.js";
+constexpr char kSettingsToggleButtonHTMLPath[] =
+ "controls/settings_toggle_button.html";
+constexpr char kSettingsToggleButtonJSPath[] =
+ "controls/settings_toggle_button.js";
+constexpr char kSettingsVarsCSSHTMLPath[] = "settings_vars_css.html";
+constexpr char kSettingsPrefsBehaviorHTMLPath[] = "prefs/prefs_behavior.html";
+constexpr char kSettingsPrefsBehaviorJSPath[] = "prefs/prefs_behavior.js";
+constexpr char kSettingsPrefsTypesHTMLPath[] = "prefs/prefs_types.html";
+constexpr char kSettingsPrefsTypesJSPath[] = "prefs/prefs_types.js";
+constexpr char kSettingsPrefsHTMLPath[] = "prefs/prefs.html";
+constexpr char kSettingsPrefsJSPath[] = "prefs/prefs.js";
+constexpr char kSettingsI18nHTMLPath[] = "i18n_setup.html";
+constexpr char kOptionsPolymerHTMLPath[] = "options_polymer.html";
+#endif
+
+} // namespace
+
+namespace options {
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// OptionsUIHTMLSource
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class OptionsUIHTMLSource : public content::URLDataSource {
+ public:
+ // The constructor takes over ownership of |localized_strings|.
+ explicit OptionsUIHTMLSource(base::DictionaryValue* localized_strings);
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ bool AllowCaching() const override;
+ std::string GetMimeType(const std::string&) const override;
+ bool ShouldDenyXFrameOptions() const override;
+
+ private:
+ ~OptionsUIHTMLSource() override;
+ void CreateDataSourceMap();
+
+ // Localized strings collection.
+ std::unique_ptr<base::DictionaryValue> localized_strings_;
+ std::map<std::string, int> path_to_idr_map_;
+ ui::TemplateReplacements replacements_;
+
+ DISALLOW_COPY_AND_ASSIGN(OptionsUIHTMLSource);
+};
+
+OptionsUIHTMLSource::OptionsUIHTMLSource(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+ localized_strings_.reset(localized_strings);
+ CreateDataSourceMap();
+}
+
+std::string OptionsUIHTMLSource::GetSource() const {
+ return chrome::kChromeUISettingsFrameHost;
+}
+
+void OptionsUIHTMLSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ scoped_refptr<base::RefCountedMemory> response_bytes;
+ const std::string& app_locale = g_browser_process->GetApplicationLocale();
+ webui::SetLoadTimeDataDefaults(app_locale, localized_strings_.get());
+ ui::TemplateReplacementsFromDictionaryValue(*localized_strings_,
+ &replacements_);
+
+ std::map<std::string, int>::iterator result;
+ result = path_to_idr_map_.find(path);
+
+ if (path == kLocalizedStringsFile) {
+ // Return dynamically-generated strings from memory.
+ std::string strings_js;
+ webui::AppendJsonJS(localized_strings_.get(), &strings_js);
+ response_bytes = base::RefCountedString::TakeString(&strings_js);
+ } else if (path == kOptionsBundleJsFile) {
+ // Return (and cache) the options javascript code.
+ response_bytes = ui::ResourceBundle::GetSharedInstance().
+ LoadDataResourceBytes(IDR_OPTIONS_BUNDLE_JS);
+ } else if (result != path_to_idr_map_.end()) {
+ response_bytes =
+ ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(
+ result->second);
+ } else {
+ // Return (and cache) the main options html page as the default.
+ response_bytes = ui::ResourceBundle::GetSharedInstance().
+ LoadDataResourceBytes(IDR_OPTIONS_HTML);
+ }
+
+ // pre-process i18n strings
+ if (GetMimeType(path) == "text/html") {
+ std::string replaced = ui::ReplaceTemplateExpressions(
+ base::StringPiece(response_bytes->front_as<char>(),
+ response_bytes->size()),
+ replacements_);
+ response_bytes = base::RefCountedString::TakeString(&replaced);
+ }
+
+ callback.Run(response_bytes.get());
+}
+
+bool OptionsUIHTMLSource::AllowCaching() const {
+ // Should not be cached to reflect dynamically-generated contents that depends
+ // on the current locale.
+ return false;
+}
+
+std::string OptionsUIHTMLSource::GetMimeType(const std::string& path) const {
+ if (base::EndsWith(path, ".js", base::CompareCase::INSENSITIVE_ASCII))
+ return "application/javascript";
+
+ return "text/html";
+}
+
+bool OptionsUIHTMLSource::ShouldDenyXFrameOptions() const {
+ return false;
+}
+
+OptionsUIHTMLSource::~OptionsUIHTMLSource() {}
+
+void OptionsUIHTMLSource::CreateDataSourceMap() {
+#if defined(OS_CHROMEOS)
+ path_to_idr_map_[kIconsHTMLPath] = IDR_OPTIONS_ICONS_HTML;
+
+ // These are part of the LockScreen UI.
+ path_to_idr_map_[kPinKeyboardHTMLPath] = IDR_OPTIONS_PIN_KEYBOARD_HTML;
+ path_to_idr_map_[kPinKeyboardJSPath] = IDR_OPTIONS_PIN_KEYBOARD_JS;
+ path_to_idr_map_[kPasswordPromptDialogHTMLPath] =
+ IDR_OPTIONS_PASSWORD_PROMPT_DIALOG_HTML;
+ path_to_idr_map_[kPasswordPromptDialogJSPath] =
+ IDR_OPTIONS_PASSWORD_PROMPT_DIALOG_JS;
+ path_to_idr_map_[kLockScreenConstantsHTMLPath] =
+ IDR_OPTIONS_LOCK_SCREEN_CONSTANTS_HTML;
+ path_to_idr_map_[kLockScreenConstantsJSPath] =
+ IDR_OPTIONS_LOCK_SCREEN_CONSTANTS_JS;
+ path_to_idr_map_[kLockStateBehaviorHTMLPath] =
+ IDR_OPTIONS_LOCK_STATE_BEHAVIOR_HTML;
+ path_to_idr_map_[kLockStateBehaviorJSPath] =
+ IDR_OPTIONS_LOCK_STATE_BEHAVIOR_JS;
+ path_to_idr_map_[kLockScreenHTMLPath] = IDR_OPTIONS_LOCK_SCREEN_HTML;
+ path_to_idr_map_[kLockScreenJSPath] = IDR_OPTIONS_LOCK_SCREEN_JS;
+ path_to_idr_map_[kEasyUnlockBrowserProxyHTMLPath] =
+ IDR_OPTIONS_EASY_UNLOCK_BROWSER_PROXY_HTML;
+ path_to_idr_map_[kEasyUnlockBrowserProxyJSPath] =
+ IDR_OPTIONS_EASY_UNLOCK_BROWSER_PROXY_JS;
+ path_to_idr_map_[kEasyUnlockTurnOffDialogHTMLPath] =
+ IDR_OPTIONS_EASY_UNLOCK_TURN_OFF_DIALOG_HTML;
+ path_to_idr_map_[kEasyUnlockTurnOffDialogJSPath] =
+ IDR_OPTIONS_EASY_UNLOCK_TURN_OFF_DIALOG_JS;
+ path_to_idr_map_[kSetupPinHTMLPath] = IDR_OPTIONS_SETUP_PIN_DIALOG_HTML;
+ path_to_idr_map_[kSetupPinJSPath] = IDR_OPTIONS_SETUP_PIN_DIALOG_JS;
+ path_to_idr_map_[kFingerprintListHTMLPath] =
+ IDR_OPTIONS_FINGERPRINT_LIST_HTML;
+ path_to_idr_map_[kFingerprintListJSPath] = IDR_OPTIONS_FINGERPRINT_LIST_JS;
+ path_to_idr_map_[kSetupFingerprintHTMLPath] =
+ IDR_OPTIONS_SETUP_FINGERPRINT_DIALOG_HTML;
+ path_to_idr_map_[kSetupFingerprintJSPath] =
+ IDR_OPTIONS_SETUP_FINGERPRINT_DIALOG_JS;
+
+ path_to_idr_map_[kSettingsRouteHTMLPath] = IDR_OPTIONS_ROUTE_HTML;
+ path_to_idr_map_[kSettingsRouteJSPath] = IDR_OPTIONS_ROUTE_JS;
+ path_to_idr_map_[kSettingsSharedCSSHTMLPath] = IDR_SETTINGS_SHARED_CSS_HTML;
+ path_to_idr_map_[kSettingsBooleanControlBehaviorHTMLPath] =
+ IDR_SETTINGS_BOOLEAN_CONTROL_BEHAVIOR_HTML;
+ path_to_idr_map_[kSettingsBooleanControlBehaviorJSPath] =
+ IDR_SETTINGS_BOOLEAN_CONTROL_BEHAVIOR_JS;
+ path_to_idr_map_[kSettingsPrefControlBehaviorHTMLPath] =
+ IDR_SETTINGS_PREF_CONTROL_BEHAVIOR_HTML;
+ path_to_idr_map_[kSettingsPrefControlBehaviorJSPath] =
+ IDR_SETTINGS_PREF_CONTROL_BEHAVIOR_JS;
+ path_to_idr_map_[kSettingsToggleButtonHTMLPath] =
+ IDR_SETTINGS_TOGGLE_BUTTON_HTML;
+ path_to_idr_map_[kSettingsToggleButtonJSPath] = IDR_SETTINGS_TOGGLE_BUTTON_JS;
+ path_to_idr_map_[kSettingsVarsCSSHTMLPath] = IDR_SETTINGS_VARS_CSS_HTML;
+ path_to_idr_map_[kSettingsPrefsBehaviorHTMLPath] =
+ IDR_SETTINGS_PREFS_BEHAVIOR_HTML;
+ path_to_idr_map_[kSettingsPrefsBehaviorJSPath] =
+ IDR_SETTINGS_PREFS_BEHAVIOR_JS;
+ path_to_idr_map_[kSettingsPrefsTypesHTMLPath] = IDR_SETTINGS_PREFS_TYPES_HTML;
+ path_to_idr_map_[kSettingsPrefsTypesJSPath] = IDR_SETTINGS_PREFS_TYPES_JS;
+ path_to_idr_map_[kOptionsPolymerHTMLPath] = IDR_OPTIONS_POLYMER_ELEMENTS_HTML;
+ path_to_idr_map_[kSettingsPrefsHTMLPath] = IDR_SETTINGS_PREFS_HTML;
+ path_to_idr_map_[kSettingsPrefsJSPath] = IDR_SETTINGS_PREFS_JS;
+ path_to_idr_map_[kSettingsI18nHTMLPath] = IDR_OPTIONS_I18N_SETUP_HTML;
+ path_to_idr_map_[kFingerprintBrowserProxyHTMLPath] =
+ IDR_OPTIONS_FINGERPRINT_BROWSER_PROXY_HTML;
+ path_to_idr_map_[kFingerprintBrowserProxyJSPath] =
+ IDR_OPTIONS_FINGERPRINT_BROWSER_PROXY_JS;
+ path_to_idr_map_[kFingerprintProgressArcHTMLPath] =
+ IDR_OPTIONS_FINGERPRINT_PROGRESS_ARC_HTML;
+ path_to_idr_map_[kFingerprintProgressArcJSPath] =
+ IDR_OPTIONS_FINGERPRINT_PROGRESS_ARC_JS;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// OptionsPageUIHandler
+//
+////////////////////////////////////////////////////////////////////////////////
+
+OptionsPageUIHandler::OptionsPageUIHandler() {
+}
+
+OptionsPageUIHandler::~OptionsPageUIHandler() {
+}
+
+bool OptionsPageUIHandler::IsEnabled() {
+ return true;
+}
+
+// static
+void OptionsPageUIHandler::RegisterStrings(
+ base::DictionaryValue* localized_strings,
+ const OptionsStringResource* resources,
+ size_t length) {
+ for (size_t i = 0; i < length; ++i) {
+ base::string16 value;
+ if (resources[i].substitution_id == 0) {
+ value = l10n_util::GetStringUTF16(resources[i].id);
+ } else {
+ value = l10n_util::GetStringFUTF16(
+ resources[i].id,
+ l10n_util::GetStringUTF16(resources[i].substitution_id));
+ }
+ localized_strings->SetString(resources[i].name, value);
+ }
+}
+
+void OptionsPageUIHandler::RegisterTitle(
+ base::DictionaryValue* localized_strings,
+ const std::string& variable_name,
+ int title_id) {
+ localized_strings->SetString(variable_name,
+ l10n_util::GetStringUTF16(title_id));
+ localized_strings->SetString(variable_name + "TabTitle",
+ l10n_util::GetStringFUTF16(IDS_OPTIONS_TAB_TITLE,
+ l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE),
+ l10n_util::GetStringUTF16(title_id)));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// OptionsUI
+//
+////////////////////////////////////////////////////////////////////////////////
+
+OptionsUI::OptionsUI(content::WebUI* web_ui)
+ : WebUIController(web_ui),
+ WebContentsObserver(web_ui->GetWebContents()),
+ initialized_handlers_(false) {
+ base::DictionaryValue* localized_strings = new base::DictionaryValue();
+ CoreOptionsHandler* core_handler;
+#if defined(OS_CHROMEOS)
+ core_handler = new chromeos::options::CoreChromeOSOptionsHandler();
+#else
+ core_handler = new CoreOptionsHandler();
+#endif
+ core_handler->set_handlers_host(this);
+ AddOptionsPageUIHandler(localized_strings, core_handler);
+
+ AddOptionsPageUIHandler(localized_strings, new AutofillOptionsHandler());
+ AddOptionsPageUIHandler(localized_strings,
+ new AutomaticSettingsResetHandler());
+
+ BrowserOptionsHandler* browser_options_handler = new BrowserOptionsHandler();
+ AddOptionsPageUIHandler(localized_strings, browser_options_handler);
+
+ AddOptionsPageUIHandler(localized_strings, new ClearBrowserDataHandler());
+ AddOptionsPageUIHandler(localized_strings, new ContentSettingsHandler());
+ AddOptionsPageUIHandler(localized_strings, new CookiesViewHandler());
+ AddOptionsPageUIHandler(localized_strings, new CreateProfileHandler());
+ AddOptionsPageUIHandler(localized_strings, new EasyUnlockHandler());
+ AddOptionsPageUIHandler(localized_strings, new FontSettingsHandler());
+#if BUILDFLAG(ENABLE_GOOGLE_NOW)
+ AddOptionsPageUIHandler(localized_strings, new GeolocationOptionsHandler());
+#endif
+ AddOptionsPageUIHandler(localized_strings, new options::HelpOverlayHandler());
+ AddOptionsPageUIHandler(localized_strings, new HomePageOverlayHandler());
+ AddOptionsPageUIHandler(localized_strings,
+ new MediaDevicesSelectionHandler());
+#if defined(OS_CHROMEOS)
+ AddOptionsPageUIHandler(localized_strings,
+ new chromeos::options::CrosLanguageOptionsHandler());
+#else
+ AddOptionsPageUIHandler(localized_strings, new LanguageOptionsHandler());
+#endif
+ AddOptionsPageUIHandler(localized_strings,
+ new LanguageDictionaryOverlayHandler());
+ AddOptionsPageUIHandler(localized_strings, new ManageProfileHandler());
+ AddOptionsPageUIHandler(localized_strings, new PasswordManagerHandler());
+ AddOptionsPageUIHandler(localized_strings, new ResetProfileSettingsHandler());
+ AddOptionsPageUIHandler(localized_strings, new SearchEngineManagerHandler());
+ AddOptionsPageUIHandler(localized_strings, new ImportDataHandler());
+ AddOptionsPageUIHandler(localized_strings, new StartupPagesHandler());
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ AddOptionsPageUIHandler(localized_strings,
+ new SupervisedUserCreateConfirmHandler());
+ AddOptionsPageUIHandler(localized_strings, new SupervisedUserImportHandler());
+ AddOptionsPageUIHandler(localized_strings,
+ new SupervisedUserLearnMoreHandler());
+#endif
+ AddOptionsPageUIHandler(localized_strings, new SyncSetupHandler());
+#if defined(OS_CHROMEOS)
+ AddOptionsPageUIHandler(localized_strings,
+ new chromeos::options::AccountsOptionsHandler());
+ AddOptionsPageUIHandler(localized_strings,
+ new chromeos::options::BluetoothOptionsHandler());
+ AddOptionsPageUIHandler(localized_strings,
+ new chromeos::options::DateTimeOptionsHandler());
+ AddOptionsPageUIHandler(localized_strings,
+ new chromeos::options::DisplayOptionsHandler());
+ AddOptionsPageUIHandler(localized_strings,
+ new chromeos::options::DisplayOverscanHandler());
+ AddOptionsPageUIHandler(localized_strings,
+ new chromeos::options::PowerHandler());
+ AddOptionsPageUIHandler(localized_strings,
+ new chromeos::options::InternetOptionsHandler());
+ AddOptionsPageUIHandler(localized_strings,
+ new chromeos::options::KeyboardHandler());
+ AddOptionsPageUIHandler(localized_strings,
+ new chromeos::options::OptionsStylusHandler());
+
+ chromeos::options::PointerHandler* pointer_handler =
+ new chromeos::options::PointerHandler();
+ AddOptionsPageUIHandler(localized_strings, pointer_handler);
+
+ AddOptionsPageUIHandler(localized_strings,
+ new chromeos::options::ProxyHandler());
+ AddOptionsPageUIHandler(
+ localized_strings,
+ new chromeos::options::ChangePictureOptionsHandler());
+ AddOptionsPageUIHandler(localized_strings,
+ new chromeos::options::StatsOptionsHandler());
+ AddOptionsPageUIHandler(localized_strings,
+ new chromeos::options::StorageManagerHandler());
+#endif
+#if defined(USE_NSS_CERTS)
+ AddOptionsPageUIHandler(localized_strings,
+ new CertificateManagerHandler(false));
+#endif
+ AddOptionsPageUIHandler(localized_strings, new HandlerOptionsHandler());
+
+ web_ui->AddMessageHandler(base::MakeUnique<MetricsHandler>());
+
+ // Enable extension API calls in the WebUI.
+ extensions::TabHelper::CreateForWebContents(web_ui->GetWebContents());
+
+ // |localized_strings| ownership is taken over by this constructor.
+ OptionsUIHTMLSource* html_source =
+ new OptionsUIHTMLSource(localized_strings);
+
+ // Set up the chrome://settings-frame/ source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::URLDataSource::Add(profile, html_source);
+
+ // Set up the chrome://theme/ source.
+ ThemeSource* theme = new ThemeSource(profile);
+ content::URLDataSource::Add(profile, theme);
+
+#if defined(OS_CHROMEOS)
+ // Set up the chrome://userimage/ source.
+ chromeos::options::UserImageSource* user_image_source =
+ new chromeos::options::UserImageSource();
+ content::URLDataSource::Add(profile, user_image_source);
+
+ pointer_device_observer_.reset(
+ new chromeos::system::PointerDeviceObserver());
+ pointer_device_observer_->AddObserver(browser_options_handler);
+ pointer_device_observer_->AddObserver(pointer_handler);
+#endif
+}
+
+OptionsUI::~OptionsUI() {
+ // Uninitialize all registered handlers. Deleted by WebUIImpl.
+ for (size_t i = 0; i < handlers_.size(); ++i)
+ handlers_[i]->Uninitialize();
+}
+
+std::unique_ptr<OptionsUI::OnFinishedLoadingCallbackList::Subscription>
+OptionsUI::RegisterOnFinishedLoadingCallback(const base::Closure& callback) {
+ return on_finished_loading_callbacks_.Add(callback);
+}
+
+// static
+void OptionsUI::ProcessAutocompleteSuggestions(
+ const AutocompleteResult& result,
+ base::ListValue* const suggestions) {
+ for (size_t i = 0; i < result.size(); ++i) {
+ const AutocompleteMatch& match = result.match_at(i);
+ AutocompleteMatchType::Type type = match.type;
+ if (type != AutocompleteMatchType::HISTORY_URL &&
+ type != AutocompleteMatchType::HISTORY_TITLE &&
+ type != AutocompleteMatchType::HISTORY_BODY &&
+ type != AutocompleteMatchType::HISTORY_KEYWORD &&
+ type != AutocompleteMatchType::NAVSUGGEST &&
+ type != AutocompleteMatchType::NAVSUGGEST_PERSONALIZED) {
+ continue;
+ }
+ std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
+ entry->SetString("title", match.description);
+ entry->SetString("displayURL", match.contents);
+ entry->SetString("url", match.destination_url.spec());
+ suggestions->Append(std::move(entry));
+ }
+}
+
+void OptionsUI::ReadyToCommitNavigation(
+ content::NavigationHandle* navigation_handle) {
+ if (navigation_handle->IsSameDocument())
+ return;
+
+ load_start_time_ = base::Time::Now();
+ if (navigation_handle->GetRenderFrameHost()->GetRenderViewHost() ==
+ web_ui()->GetWebContents()->GetRenderViewHost() &&
+ navigation_handle->GetURL().host_piece() ==
+ chrome::kChromeUISettingsFrameHost) {
+ for (size_t i = 0; i < handlers_.size(); ++i)
+ handlers_[i]->PageLoadStarted();
+ }
+}
+
+void OptionsUI::DocumentLoadedInFrame(
+ content::RenderFrameHost *render_frame_host) {
+ UMA_HISTOGRAM_TIMES("Settings.LoadDocumentTime",
+ base::Time::Now() - load_start_time_);
+}
+
+void OptionsUI::DocumentOnLoadCompletedInMainFrame() {
+ UMA_HISTOGRAM_TIMES("Settings.LoadCompletedTime",
+ base::Time::Now() - load_start_time_);
+}
+
+void OptionsUI::InitializeHandlers() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ DCHECK(!profile->IsOffTheRecord() || profile->IsGuestSession());
+
+ // A new web page DOM has been brought up in an existing renderer, causing
+ // this method to be called twice. If that happens, ignore the second call.
+ if (!initialized_handlers_) {
+ for (size_t i = 0; i < handlers_.size(); ++i)
+ handlers_[i]->InitializeHandler();
+ initialized_handlers_ = true;
+
+#if defined(OS_CHROMEOS)
+ pointer_device_observer_->Init();
+#endif
+ }
+
+#if defined(OS_CHROMEOS)
+ pointer_device_observer_->CheckDevices();
+#endif
+
+ // Always initialize the page as when handlers are left over we still need to
+ // do various things like show/hide sections and send data to the Javascript.
+ for (size_t i = 0; i < handlers_.size(); ++i)
+ handlers_[i]->InitializePage();
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "BrowserOptions.notifyInitializationComplete");
+}
+
+void OptionsUI::OnFinishedLoading() {
+ on_finished_loading_callbacks_.Notify();
+}
+
+void OptionsUI::AddOptionsPageUIHandler(
+ base::DictionaryValue* localized_strings,
+ OptionsPageUIHandler* handler_raw) {
+ std::unique_ptr<OptionsPageUIHandler> handler(handler_raw);
+ DCHECK(handler.get());
+ // Add only if handler's service is enabled.
+ if (handler->IsEnabled()) {
+ // Add handler to the list and also pass the ownership.
+ web_ui()->AddMessageHandler(std::move(handler));
+ handler_raw->GetLocalizedValues(localized_strings);
+ handlers_.push_back(handler_raw);
+ }
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/options_ui.h b/chromium/chrome/browser/ui/webui/options/options_ui.h
new file mode 100644
index 00000000000..9a597f0157f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/options_ui.h
@@ -0,0 +1,164 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_OPTIONS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_OPTIONS_UI_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback_list.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+class AutocompleteResult;
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+#if defined(OS_CHROMEOS)
+namespace chromeos {
+namespace system {
+class PointerDeviceObserver;
+} // namespace system
+} // namespace chromeos
+#endif
+
+namespace options {
+
+// The base class handler of Javascript messages of options pages.
+class OptionsPageUIHandler : public content::WebUIMessageHandler {
+ public:
+ OptionsPageUIHandler();
+ ~OptionsPageUIHandler() override;
+
+ // Is this handler enabled?
+ virtual bool IsEnabled();
+
+ // Collects localized strings for options page.
+ virtual void GetLocalizedValues(base::DictionaryValue* localized_strings) = 0;
+
+ virtual void PageLoadStarted() {}
+
+ // Will be called only once in the life time of the handler. Generally used to
+ // add observers, initializes preferences, or start asynchronous calls from
+ // various services.
+ virtual void InitializeHandler() {}
+
+ // Initialize the page. Called once the DOM is available for manipulation.
+ // This will be called when a RenderView is re-used (when navigated to with
+ // back/forward or session restored in some cases) or when created.
+ virtual void InitializePage() {}
+
+ // Uninitializes the page. Called just before the object is destructed.
+ virtual void Uninitialize() {}
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override {}
+
+ protected:
+ struct OptionsStringResource {
+ // The name of the resource in templateData.
+ const char* name;
+ // The .grd ID for the resource (IDS_*).
+ int id;
+ // The .grd ID of the string to replace $1 in |id|'s string. If zero or
+ // omitted (default initialized), no substitution is attempted.
+ int substitution_id;
+ };
+
+ // A helper to simplify string registration in WebUI for strings which do not
+ // change at runtime and optionally contain a single substitution.
+ static void RegisterStrings(base::DictionaryValue* localized_strings,
+ const OptionsStringResource* resources,
+ size_t length);
+
+ // Registers string resources for a page's header and tab title.
+ static void RegisterTitle(base::DictionaryValue* localized_strings,
+ const std::string& variable_name,
+ int title_id);
+
+ content::NotificationRegistrar registrar_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OptionsPageUIHandler);
+};
+
+// An interface for common operations that a host of OptionsPageUIHandlers
+// should provide.
+class OptionsPageUIHandlerHost {
+ public:
+ virtual void InitializeHandlers() = 0;
+ virtual void OnFinishedLoading() {}
+
+ protected:
+ virtual ~OptionsPageUIHandlerHost() {}
+};
+
+// The WebUI for chrome:settings-frame.
+class OptionsUI : public content::WebUIController,
+ public content::WebContentsObserver,
+ public OptionsPageUIHandlerHost {
+ public:
+ typedef base::CallbackList<void()> OnFinishedLoadingCallbackList;
+
+ explicit OptionsUI(content::WebUI* web_ui);
+ ~OptionsUI() override;
+
+ // Registers a callback to be called once the settings frame has finished
+ // loading on the HTML/JS side.
+ std::unique_ptr<OnFinishedLoadingCallbackList::Subscription>
+ RegisterOnFinishedLoadingCallback(const base::Closure& callback);
+
+ // Takes the suggestions from |result| and adds them to |suggestions| so that
+ // they can be passed to a JavaScript function.
+ static void ProcessAutocompleteSuggestions(
+ const AutocompleteResult& result,
+ base::ListValue* const suggestions);
+
+ // Overridden from content::WebContentsObserver:
+ void ReadyToCommitNavigation(
+ content::NavigationHandle* navigation_handle) override;
+ void DocumentLoadedInFrame(
+ content::RenderFrameHost *render_frame_host) override;
+ void DocumentOnLoadCompletedInMainFrame() override;
+
+ // Overridden from OptionsPageUIHandlerHost:
+ void InitializeHandlers() override;
+ void OnFinishedLoading() override;
+
+ private:
+ // Adds OptionsPageUiHandler to the handlers list if handler is enabled.
+ void AddOptionsPageUIHandler(base::DictionaryValue* localized_strings,
+ OptionsPageUIHandler* handler);
+
+ bool initialized_handlers_;
+
+ std::vector<OptionsPageUIHandler*> handlers_;
+ OnFinishedLoadingCallbackList on_finished_loading_callbacks_;
+
+#if defined(OS_CHROMEOS)
+ std::unique_ptr<chromeos::system::PointerDeviceObserver>
+ pointer_device_observer_;
+#endif
+
+ base::Time load_start_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(OptionsUI);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_OPTIONS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/options/options_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/options/options_ui_browsertest.cc
new file mode 100644
index 00000000000..b37522637fc
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/options_ui_browsertest.cc
@@ -0,0 +1,344 @@
+// 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/ui/webui/options/options_ui_browsertest.h"
+
+#include "base/scoped_observer.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/signin/account_tracker_service_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "chrome/browser/ui/webui/uber/uber_ui.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if !defined(OS_CHROMEOS)
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/run_loop.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "ui/base/window_open_disposition.h"
+#include "url/gurl.h"
+#endif
+
+using content::MessageLoopRunner;
+
+namespace options {
+
+namespace {
+
+class SignOutWaiter : public SigninManagerBase::Observer {
+ public:
+ explicit SignOutWaiter(SigninManagerBase* signin_manager)
+ : seen_(false), running_(false), scoped_observer_(this) {
+ scoped_observer_.Add(signin_manager);
+ }
+ ~SignOutWaiter() override {}
+
+ void Wait() {
+ if (seen_)
+ return;
+
+ running_ = true;
+ message_loop_runner_ = new MessageLoopRunner;
+ message_loop_runner_->Run();
+ EXPECT_TRUE(seen_);
+ }
+
+ void GoogleSignedOut(const std::string& account_id,
+ const std::string& username) override {
+ seen_ = true;
+ if (!running_)
+ return;
+
+ message_loop_runner_->Quit();
+ running_ = false;
+ }
+
+ private:
+ bool seen_;
+ bool running_;
+ ScopedObserver<SigninManagerBase, SignOutWaiter> scoped_observer_;
+ scoped_refptr<MessageLoopRunner> message_loop_runner_;
+};
+
+#if !defined(OS_CHROMEOS)
+void RunClosureWhenProfileInitialized(const base::Closure& closure,
+ Profile* profile,
+ Profile::CreateStatus status) {
+ if (status == Profile::CREATE_STATUS_INITIALIZED)
+ closure.Run();
+}
+#endif
+
+bool FrameHasSettingsSourceHost(content::RenderFrameHost* frame) {
+ return frame->GetLastCommittedURL().DomainIs(
+ chrome::kChromeUISettingsFrameHost);
+}
+
+} // namespace
+
+OptionsUIBrowserTest::OptionsUIBrowserTest() {
+}
+
+void OptionsUIBrowserTest::SetUpInProcessBrowserTestFixture() {
+ InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
+ disable_md_settings_.InitAndDisableFeature(features::kMaterialDesignSettings);
+}
+
+void OptionsUIBrowserTest::NavigateToSettings() {
+ NavigateToSettingsSubpage("");
+}
+
+void OptionsUIBrowserTest::NavigateToSettingsSubpage(
+ const std::string& sub_page) {
+ const GURL& url = chrome::GetSettingsUrl(sub_page);
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), url, WindowOpenDisposition::CURRENT_TAB, 0);
+
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+ ASSERT_TRUE(web_contents->GetWebUI());
+
+ content::WebUIController* controller =
+ web_contents->GetWebUI()->GetController();
+#if !defined(OS_CHROMEOS)
+ controller = static_cast<UberUI*>(controller)->
+ GetSubpage(chrome::kChromeUISettingsFrameURL)->GetController();
+#endif
+ OptionsUI* options_ui = static_cast<OptionsUI*>(controller);
+
+ // It is not possible to subscribe to the OnFinishedLoading event before the
+ // call to NavigateToURL(), because the WebUI does not yet exist at that time.
+ // However, it is safe to subscribe afterwards, because the event will always
+ // be posted asynchronously to the message loop.
+ scoped_refptr<MessageLoopRunner> message_loop_runner(new MessageLoopRunner);
+ std::unique_ptr<OptionsUI::OnFinishedLoadingCallbackList::Subscription>
+ subscription = options_ui->RegisterOnFinishedLoadingCallback(
+ message_loop_runner->QuitClosure());
+ message_loop_runner->Run();
+
+ // The OnFinishedLoading event, which indicates that all WebUI initialization
+ // methods have been called on the JS side, is temporally unrelated to whether
+ // or not the WebContents considers itself to have finished loading. We want
+ // to wait for this too, however, because, e.g. this is a sufficient condition
+ // to get the focus properly placed on a form element.
+ content::WaitForLoadStop(web_contents);
+}
+
+void OptionsUIBrowserTest::NavigateToSettingsFrame() {
+ const GURL& url = GURL(chrome::kChromeUISettingsFrameURL);
+ ui_test_utils::NavigateToURL(browser(), url);
+}
+
+void OptionsUIBrowserTest::VerifyNavbar() {
+ bool navbar_exist = false;
+#if defined(OS_CHROMEOS)
+ bool should_navbar_exist = false;
+#else
+ bool should_navbar_exist = true;
+#endif
+ EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+ browser()->tab_strip_model()->GetActiveWebContents(),
+ "domAutomationController.send("
+ " !!document.getElementById('navigation'))",
+ &navbar_exist));
+ EXPECT_EQ(should_navbar_exist, navbar_exist);
+}
+
+void OptionsUIBrowserTest::VerifyTitle() {
+ base::string16 title =
+ browser()->tab_strip_model()->GetActiveWebContents()->GetTitle();
+ base::string16 expected_title = l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE);
+ EXPECT_NE(title.find(expected_title), base::string16::npos);
+}
+
+content::RenderFrameHost* OptionsUIBrowserTest::GetSettingsFrame() {
+ // NB: The utility function content::FrameHasSourceUrl can't be used because
+ // the settings frame navigates itself to chrome://settings-frame/settings
+ // to indicate that it's showing the top-level settings. Therefore, just
+ // match the host.
+ return content::FrameMatchingPredicate(
+ browser()->tab_strip_model()->GetActiveWebContents(),
+ base::Bind(&FrameHasSettingsSourceHost));
+}
+
+IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, LoadOptionsByURL) {
+ NavigateToSettings();
+ VerifyTitle();
+ VerifyNavbar();
+}
+
+// Flaky on Linux, Mac and Win: http://crbug.com/469113
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+#define MAYBE_VerifyManagedSignout DISABLED_VerifyManagedSignout
+#else
+#define MAYBE_VerifyManagedSignout VerifyManagedSignout
+#endif
+
+#if !defined(OS_CHROMEOS)
+IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, MAYBE_VerifyManagedSignout) {
+ SigninManager* signin =
+ SigninManagerFactory::GetForProfile(browser()->profile());
+ signin->OnExternalSigninCompleted("test@example.com");
+ signin->ProhibitSignout(true);
+
+ NavigateToSettingsFrame();
+
+ // This script simulates a click on the "Disconnect your Google Account"
+ // button and returns true if the hidden flag of the appropriate dialog gets
+ // flipped.
+ bool result = false;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+ browser()->tab_strip_model()->GetActiveWebContents(),
+ "var dialog = $('manage-profile-overlay-disconnect-managed');"
+ "var original_status = dialog.hidden;"
+ "var original = ManageProfileOverlay.showDisconnectManagedProfileDialog;"
+ "var teststub = function(event) {"
+ " original(event);"
+ " domAutomationController.send(original_status && !dialog.hidden);"
+ "};"
+ "ManageProfileOverlay.showDisconnectManagedProfileDialog = teststub;"
+ "$('start-stop-sync').click();",
+ &result));
+
+ EXPECT_TRUE(result);
+
+ base::FilePath profile_dir = browser()->profile()->GetPath();
+ ProfileAttributesStorage& storage =
+ g_browser_process->profile_manager()->GetProfileAttributesStorage();
+ ProfileAttributesEntry* entry;
+
+ EXPECT_TRUE(DirectoryExists(profile_dir));
+ EXPECT_TRUE(storage.GetProfileAttributesWithPath(profile_dir, &entry));
+
+ // TODO(kaliamoorthi): Get the macos problem fixed and remove this code.
+ // Deleting the Profile also destroys all browser windows of that Profile.
+ // Wait for the current browser to close before resuming, otherwise
+ // the browser_tests shutdown code will be confused on the Mac.
+ content::WindowedNotificationObserver wait_for_browser_closed(
+ chrome::NOTIFICATION_BROWSER_CLOSED,
+ content::NotificationService::AllSources());
+
+ ASSERT_TRUE(content::ExecuteScript(
+ browser()->tab_strip_model()->GetActiveWebContents(),
+ "$('disconnect-managed-profile-ok').click();"));
+
+ EXPECT_TRUE(storage.GetProfileAttributesWithPath(profile_dir, &entry));
+
+ wait_for_browser_closed.Wait();
+}
+
+IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, VerifyUnmanagedSignout) {
+ const std::string user = "test@example.com";
+ AccountTrackerServiceFactory::GetForProfile(browser()->profile())
+ ->SeedAccountInfo("12345", user);
+ SigninManager* signin =
+ SigninManagerFactory::GetForProfile(browser()->profile());
+ signin->OnExternalSigninCompleted(user);
+
+ NavigateToSettingsFrame();
+
+ // This script simulates a click on the "Disconnect your Google Account"
+ // button and returns true if the hidden flag of the appropriate dialog gets
+ // flipped.
+ bool result = false;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+ browser()->tab_strip_model()->GetActiveWebContents(),
+ "var dialog = $('sync-setup-stop-syncing');"
+ "var original_status = dialog.hidden;"
+ "$('start-stop-sync').click();"
+ "domAutomationController.send(original_status && !dialog.hidden);",
+ &result));
+
+ EXPECT_TRUE(result);
+
+ SignOutWaiter sign_out_waiter(signin);
+
+ ASSERT_TRUE(content::ExecuteScript(
+ browser()->tab_strip_model()->GetActiveWebContents(),
+ "$('stop-syncing-ok').click();"));
+
+ sign_out_waiter.Wait();
+
+ EXPECT_TRUE(browser()->profile()->GetProfileUserName() != user);
+ EXPECT_FALSE(signin->IsAuthenticated());
+}
+
+// Regression test for http://crbug.com/301436, excluded on Chrome OS because
+// profile management in the settings UI exists on desktop platforms only.
+IN_PROC_BROWSER_TEST_F(OptionsUIBrowserTest, NavigateBackFromOverlayDialog) {
+ NavigateToSettingsFrame();
+
+ // Click a button that opens an overlay dialog.
+ content::WebContents* contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(content::ExecuteScript(
+ contents, "$('manage-default-search-engines').click();"));
+
+ // Go back to the settings page.
+ content::TestNavigationObserver observer(contents);
+ chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB);
+ observer.Wait();
+
+ // Verify that the settings page lists one profile.
+ const char javascript[] =
+ "domAutomationController.send("
+ " document.querySelectorAll('list#profiles-list > div[role=listitem]')"
+ " .length);";
+ int profiles;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
+ contents, javascript, &profiles));
+ EXPECT_EQ(1, profiles);
+
+ // Create a second profile.
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ const base::FilePath profile_path =
+ profile_manager->GenerateNextProfileDirectoryPath();
+
+ base::RunLoop run_loop;
+ profile_manager->CreateProfileAsync(
+ profile_manager->GenerateNextProfileDirectoryPath(),
+ base::Bind(&RunClosureWhenProfileInitialized,
+ run_loop.QuitClosure()),
+ base::string16(),
+ std::string(),
+ std::string());
+ run_loop.Run();
+
+ // Verify that the settings page has updated and lists two profiles.
+ ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
+ contents, javascript, &profiles));
+ EXPECT_EQ(2, profiles);
+}
+#endif
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/options_ui_browsertest.h b/chromium/chrome/browser/ui/webui/options/options_ui_browsertest.h
new file mode 100644
index 00000000000..35b49b2e6dd
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/options_ui_browsertest.h
@@ -0,0 +1,57 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_OPTIONS_UI_BROWSERTEST_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_OPTIONS_UI_BROWSERTEST_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/test/base/in_process_browser_test.h"
+
+namespace content {
+class RenderFrameHost;
+}
+
+namespace options {
+
+class OptionsUIBrowserTest : public InProcessBrowserTest {
+ public:
+ OptionsUIBrowserTest();
+
+ void SetUpInProcessBrowserTestFixture() override;
+
+ // Navigate to the Uber/Settings page and block until it has loaded.
+ void NavigateToSettings();
+
+ // Navigate to a certain subpage in the Uber/Settings page and block until it
+ // has loaded.
+ void NavigateToSettingsSubpage(const std::string& sub_page);
+
+ // Navigate to the Settings frame and block until complete.
+ void NavigateToSettingsFrame();
+
+ // Check navbar's existence.
+ void VerifyNavbar();
+
+ // Verify that the page title is correct.
+ // The only guarantee we can make about the title of a settings tab is that
+ // it should contain IDS_SETTINGS_TITLE somewhere.
+ void VerifyTitle();
+
+ protected:
+ // Returns the RenderFrameHost associated with the Settings frame inside the
+ // Uber UI page (which must be already loaded).
+ content::RenderFrameHost* GetSettingsFrame();
+
+ private:
+ base::test::ScopedFeatureList disable_md_settings_;
+
+ DISALLOW_COPY_AND_ASSIGN(OptionsUIBrowserTest);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_OPTIONS_UI_BROWSERTEST_H_
diff --git a/chromium/chrome/browser/ui/webui/options/password_manager_browsertest.js b/chromium/chrome/browser/ui/webui/options/password_manager_browsertest.js
new file mode 100644
index 00000000000..e94a72b946e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/password_manager_browsertest.js
@@ -0,0 +1,26 @@
+// 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.
+
+/**
+ * TestFixture for password manager WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function PasswordManagerWebUITest() {}
+
+PasswordManagerWebUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /**
+ * Browse to the password manager.
+ */
+ browsePreload: 'chrome://settings-frame/passwords',
+};
+
+// Test opening the password manager has correct location.
+// Fails on Linux and Windows. http://crbug.com/467916
+TEST_F('PasswordManagerWebUITest', 'DISABLED_testOpenPasswordManager',
+ function() {
+ assertEquals(this.browsePreload, document.location.href);
+ });
diff --git a/chromium/chrome/browser/ui/webui/options/password_manager_handler.cc b/chromium/chrome/browser/ui/webui/options/password_manager_handler.cc
new file mode 100644
index 00000000000..e9f434bb59e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/password_manager_handler.cc
@@ -0,0 +1,402 @@
+// 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/options/password_manager_handler.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/feature_list.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/password_manager/password_store_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/ui/chrome_select_file_policy.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/password_manager/core/browser/export/password_exporter.h"
+#include "components/password_manager/core/browser/password_bubble_experiment.h"
+#include "components/password_manager/core/browser/password_manager_constants.h"
+#include "components/password_manager/core/browser/password_store.h"
+#include "components/password_manager/core/browser/password_ui_utils.h"
+#include "components/password_manager/core/common/experiments.h"
+#include "components/password_manager/core/common/password_manager_features.h"
+#include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/url_formatter/url_formatter.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/common/origin_util.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace options {
+
+namespace {
+// The following constants should be synchronized with the constants in
+// chrome/browser/resources/options/password_manager_list.js.
+const char kUrlField[] = "url";
+const char kShownOriginField[] = "shownOrigin";
+const char kIsAndroidUriField[] = "isAndroidUri";
+const char kIsClickable[] = "isClickable";
+const char kIsSecureField[] = "isSecure";
+const char kUsernameField[] = "username";
+const char kPasswordField[] = "password";
+const char kFederationField[] = "federation";
+
+// Copies from |form| to |entry| the origin, shown origin, whether the origin is
+// Android URI, and whether the origin is secure.
+void CopyOriginInfoOfPasswordForm(const autofill::PasswordForm& form,
+ base::DictionaryValue* entry) {
+ bool is_android_uri = false;
+ bool origin_is_clickable = false;
+ GURL link_url;
+ entry->SetString(
+ kShownOriginField,
+ password_manager::GetShownOriginAndLinkUrl(
+ form, &is_android_uri, &link_url, &origin_is_clickable));
+ DCHECK(link_url.is_valid());
+ entry->SetString(
+ kUrlField, url_formatter::FormatUrl(
+ link_url, url_formatter::kFormatUrlOmitNothing,
+ net::UnescapeRule::SPACES, nullptr, nullptr, nullptr));
+ entry->SetBoolean(kIsAndroidUriField, is_android_uri);
+ entry->SetBoolean(kIsClickable, origin_is_clickable);
+ entry->SetBoolean(kIsSecureField, content::IsOriginSecure(link_url));
+}
+
+} // namespace
+
+PasswordManagerHandler::PasswordManagerHandler() {
+ password_manager_presenter_.reset(new PasswordManagerPresenter(this));
+}
+
+PasswordManagerHandler::PasswordManagerHandler(
+ std::unique_ptr<PasswordManagerPresenter> presenter)
+ : password_manager_presenter_(std::move(presenter)) {}
+
+PasswordManagerHandler::~PasswordManagerHandler() {}
+
+Profile* PasswordManagerHandler::GetProfile() {
+ return Profile::FromWebUI(web_ui());
+}
+
+#if !defined(OS_ANDROID)
+gfx::NativeWindow PasswordManagerHandler::GetNativeWindow() const {
+ return web_ui()->GetWebContents()->GetTopLevelNativeWindow();
+}
+#endif
+
+void PasswordManagerHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ static const OptionsStringResource resources[] = {
+ {"androidUriSuffix", IDS_PASSWORDS_ANDROID_URI_SUFFIX},
+ {"autoSigninTitle", IDS_PASSWORDS_AUTO_SIGNIN_TITLE},
+ {"autoSigninDescription", IDS_PASSWORDS_AUTO_SIGNIN_DESCRIPTION},
+ {"savedPasswordsTitle", IDS_PASSWORD_MANAGER_SHOW_PASSWORDS_TAB_TITLE},
+ {"passwordExceptionsTitle", IDS_PASSWORD_MANAGER_EXCEPTIONS_TAB_TITLE},
+ {"passwordSearchPlaceholder", IDS_PASSWORDS_PAGE_SEARCH_PASSWORDS},
+ {"passwordShowButton", IDS_PASSWORDS_PAGE_VIEW_SHOW_BUTTON},
+ {"passwordHideButton", IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON},
+ {"passwordsNoPasswordsDescription",
+ IDS_PASSWORDS_PAGE_VIEW_NO_PASSWORDS_DESCRIPTION},
+ {"passwordsNoExceptionsDescription",
+ IDS_PASSWORDS_PAGE_VIEW_NO_EXCEPTIONS_DESCRIPTION},
+ {"passwordManagerImportPasswordButtonText",
+ IDS_PASSWORD_MANAGER_IMPORT_BUTTON},
+ {"passwordManagerExportPasswordButtonText",
+ IDS_PASSWORD_MANAGER_EXPORT_BUTTON},
+ };
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+
+ RegisterTitle(localized_strings, "passwordsPage",
+ IDS_PASSWORDS_EXCEPTIONS_WINDOW_TITLE);
+
+ localized_strings->SetString("passwordManagerLearnMoreURL",
+ chrome::kPasswordManagerLearnMoreURL);
+ localized_strings->SetString(
+ "passwordsManagePasswordsLink",
+ password_manager::kPasswordManagerAccountDashboardURL);
+
+ std::string management_hostname =
+ GURL(password_manager::kPasswordManagerAccountDashboardURL).host();
+ base::string16 link_text = base::UTF8ToUTF16(management_hostname);
+ size_t offset;
+ base::string16 full_text = l10n_util::GetStringFUTF16(
+ IDS_MANAGE_PASSWORDS_REMOTE_TEXT, link_text, &offset);
+
+ localized_strings->SetString("passwordsManagePasswordsBeforeLinkText",
+ full_text.substr(0, offset));
+ localized_strings->SetString("passwordsManagePasswordsLinkText",
+ full_text.substr(offset, link_text.size()));
+ localized_strings->SetString("passwordsManagePasswordsAfterLinkText",
+ full_text.substr(offset + link_text.size()));
+
+ bool disable_show_passwords = false;
+
+#if defined(OS_WIN) && defined(USE_ASH)
+ // We disable the ability to show passwords when running in Windows Metro
+ // interface. This is because we cannot pop native Win32 dialogs from the
+ // Metro process.
+ // TODO(wfh): Revisit this if Metro usage grows.
+ if (chrome::IsNativeWindowInAsh(GetNativeWindow()))
+ disable_show_passwords = true;
+#endif
+
+ localized_strings->SetBoolean("disableShowPasswords", disable_show_passwords);
+}
+
+void PasswordManagerHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "updatePasswordLists",
+ base::Bind(&PasswordManagerHandler::HandleUpdatePasswordLists,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "removeSavedPassword",
+ base::Bind(&PasswordManagerHandler::HandleRemoveSavedPassword,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "removePasswordException",
+ base::Bind(&PasswordManagerHandler::HandleRemovePasswordException,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "requestShowPassword",
+ base::Bind(&PasswordManagerHandler::HandleRequestShowPassword,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "importPassword",
+ base::Bind(&PasswordManagerHandler::HandlePasswordImport,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "exportPassword",
+ base::Bind(&PasswordManagerHandler::HandlePasswordExport,
+ base::Unretained(this)));
+}
+
+void PasswordManagerHandler::InitializeHandler() {
+ password_manager_presenter_->Initialize();
+}
+
+void PasswordManagerHandler::InitializePage() {
+ if (base::FeatureList::IsEnabled(
+ password_manager::features::kPasswordImportExport)) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "PasswordManager.showImportExportButton");
+ }
+}
+
+void PasswordManagerHandler::HandleRemoveSavedPassword(
+ const base::ListValue* args) {
+ std::string string_value = base::UTF16ToUTF8(ExtractStringValue(args));
+ int index;
+ if (base::StringToInt(string_value, &index) && index >= 0) {
+ password_manager_presenter_->RemoveSavedPassword(
+ static_cast<size_t>(index));
+ }
+}
+
+void PasswordManagerHandler::HandleRemovePasswordException(
+ const base::ListValue* args) {
+ std::string string_value = base::UTF16ToUTF8(ExtractStringValue(args));
+ int index;
+ if (base::StringToInt(string_value, &index) && index >= 0) {
+ password_manager_presenter_->RemovePasswordException(
+ static_cast<size_t>(index));
+ }
+}
+
+void PasswordManagerHandler::HandleRequestShowPassword(
+ const base::ListValue* args) {
+ int index;
+ if (!ExtractIntegerValue(args, &index))
+ NOTREACHED();
+
+ password_manager_presenter_->RequestShowPassword(static_cast<size_t>(index));
+}
+
+void PasswordManagerHandler::ShowPassword(
+ size_t index,
+ const std::string& origin_url,
+ const std::string& username,
+ const base::string16& password_value) {
+ // Call back the front end to reveal the password.
+ web_ui()->CallJavascriptFunctionUnsafe("PasswordManager.showPassword",
+ base::Value(static_cast<int>(index)),
+ base::Value(password_value));
+}
+
+void PasswordManagerHandler::HandleUpdatePasswordLists(
+ const base::ListValue* args) {
+ password_manager_presenter_->UpdatePasswordLists();
+}
+
+void PasswordManagerHandler::SetPasswordList(
+ const std::vector<std::unique_ptr<autofill::PasswordForm>>& password_list) {
+ base::ListValue entries;
+ base::string16 placeholder(base::ASCIIToUTF16(" "));
+ for (const auto& saved_password : password_list) {
+ std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue);
+ CopyOriginInfoOfPasswordForm(*saved_password, entry.get());
+
+ entry->SetString(kUsernameField, saved_password->username_value);
+ // Use a placeholder value with the same length as the password.
+ entry->SetString(
+ kPasswordField,
+ base::string16(saved_password->password_value.length(), ' '));
+ if (!saved_password->federation_origin.unique()) {
+ entry->SetString(
+ kFederationField,
+ l10n_util::GetStringFUTF16(
+ IDS_PASSWORDS_VIA_FEDERATION,
+ base::UTF8ToUTF16(saved_password->federation_origin.host())));
+ }
+
+ entries.Append(std::move(entry));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "PasswordManager.setSavedPasswordsList", entries);
+}
+
+void PasswordManagerHandler::SetPasswordExceptionList(
+ const std::vector<std::unique_ptr<autofill::PasswordForm>>&
+ password_exception_list) {
+ base::ListValue entries;
+ for (const auto& exception : password_exception_list) {
+ std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue);
+ CopyOriginInfoOfPasswordForm(*exception, entry.get());
+ entries.Append(std::move(entry));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "PasswordManager.setPasswordExceptionsList", entries);
+}
+
+void PasswordManagerHandler::FileSelected(const base::FilePath& path,
+ int index,
+ void* params) {
+ switch (static_cast<FileSelectorCaller>(reinterpret_cast<intptr_t>(params))) {
+ case IMPORT_FILE_SELECTED:
+ ImportPasswordFileSelected(path);
+ break;
+ case EXPORT_FILE_SELECTED:
+ ExportPasswordFileSelected(path);
+ break;
+ }
+}
+
+void PasswordManagerHandler::HandlePasswordImport(const base::ListValue* args) {
+#if !defined(OS_ANDROID) // This is never called on Android.
+ ui::SelectFileDialog::FileTypeInfo file_type_info;
+
+ file_type_info.extensions =
+ password_manager::PasswordImporter::GetSupportedFileExtensions();
+ DCHECK(!file_type_info.extensions.empty() &&
+ !file_type_info.extensions[0].empty());
+ file_type_info.include_all_files = true;
+ select_file_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
+ select_file_dialog_->SelectFile(
+ ui::SelectFileDialog::SELECT_OPEN_FILE,
+ l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_IMPORT_DIALOG_TITLE),
+ base::FilePath(), &file_type_info, 1, file_type_info.extensions[0][0],
+ web_ui()->GetWebContents()->GetTopLevelNativeWindow(),
+ reinterpret_cast<void*>(IMPORT_FILE_SELECTED));
+#endif
+}
+
+void PasswordManagerHandler::ImportPasswordFileSelected(
+ const base::FilePath& path) {
+ scoped_refptr<ImportPasswordResultConsumer> form_consumer(
+ new ImportPasswordResultConsumer(GetProfile()));
+
+ password_manager::PasswordImporter::Import(
+ path, content::BrowserThread::GetTaskRunnerForThread(
+ content::BrowserThread::FILE)
+ .get(),
+ base::Bind(&ImportPasswordResultConsumer::ConsumePassword,
+ form_consumer));
+}
+
+PasswordManagerHandler::ImportPasswordResultConsumer::
+ ImportPasswordResultConsumer(Profile* profile)
+ : profile_(profile) {}
+
+void PasswordManagerHandler::ImportPasswordResultConsumer::ConsumePassword(
+ password_manager::PasswordImporter::Result result,
+ const std::vector<autofill::PasswordForm>& forms) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "PasswordManager.ImportPasswordFromCSVResult", result,
+ password_manager::PasswordImporter::NUM_IMPORT_RESULTS);
+ if (result != password_manager::PasswordImporter::SUCCESS)
+ return;
+
+ UMA_HISTOGRAM_COUNTS("PasswordManager.ImportedPasswordsPerUserInCSV",
+ forms.size());
+
+ scoped_refptr<password_manager::PasswordStore> store(
+ PasswordStoreFactory::GetForProfile(profile_,
+ ServiceAccessType::EXPLICIT_ACCESS));
+ if (store) {
+ for (const autofill::PasswordForm& form : forms) {
+ store->AddLogin(form);
+ }
+ }
+ UMA_HISTOGRAM_BOOLEAN("PasswordManager.StorePasswordImportedFromCSVResult",
+ static_cast<bool>(store));
+}
+
+void PasswordManagerHandler::HandlePasswordExport(const base::ListValue* args) {
+#if !defined(OS_ANDROID) // This is never called on Android.
+ if (!password_manager_presenter_->IsUserAuthenticated())
+ return;
+
+ ui::SelectFileDialog::FileTypeInfo file_type_info;
+ file_type_info.extensions =
+ password_manager::PasswordExporter::GetSupportedFileExtensions();
+ DCHECK(!file_type_info.extensions.empty() &&
+ !file_type_info.extensions[0].empty());
+ file_type_info.include_all_files = true;
+ select_file_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
+ select_file_dialog_->SelectFile(
+ ui::SelectFileDialog::SELECT_SAVEAS_FILE,
+ l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_EXPORT_DIALOG_TITLE),
+ base::FilePath(), &file_type_info, 1, file_type_info.extensions[0][0],
+ GetNativeWindow(), reinterpret_cast<void*>(EXPORT_FILE_SELECTED));
+#endif
+}
+
+void PasswordManagerHandler::ExportPasswordFileSelected(
+ const base::FilePath& path) {
+ std::vector<std::unique_ptr<autofill::PasswordForm>> password_list =
+ password_manager_presenter_->GetAllPasswords();
+ UMA_HISTOGRAM_COUNTS("PasswordManager.ExportedPasswordsPerUserInCSV",
+ password_list.size());
+ password_manager::PasswordExporter::Export(
+ path, password_list, content::BrowserThread::GetTaskRunnerForThread(
+ content::BrowserThread::FILE)
+ .get());
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/password_manager_handler.h b/chromium/chrome/browser/ui/webui/options/password_manager_handler.h
new file mode 100644
index 00000000000..5442df96ad0
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/password_manager_handler.h
@@ -0,0 +1,138 @@
+// 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_OPTIONS_PASSWORD_MANAGER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_PASSWORD_MANAGER_HANDLER_H_
+
+#include <stddef.h>
+
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "chrome/browser/ui/passwords/password_manager_presenter.h"
+#include "chrome/browser/ui/passwords/password_ui_view.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "components/password_manager/core/browser/import/password_importer.h"
+#include "components/prefs/pref_member.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+
+namespace options {
+
+// The WebUI based PasswordUIView. Displays passwords in the web ui.
+class PasswordManagerHandler : public OptionsPageUIHandler,
+ public PasswordUIView,
+ public ui::SelectFileDialog::Listener {
+ public:
+ // Enumeration of different callers of SelectFile.
+ enum FileSelectorCaller {
+ IMPORT_FILE_SELECTED,
+ EXPORT_FILE_SELECTED,
+ };
+
+ PasswordManagerHandler();
+ ~PasswordManagerHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializeHandler() override;
+ void InitializePage() override;
+ void RegisterMessages() override;
+
+ // ui::SelectFileDialog::Listener implementation.
+ // |params| is of type FileSelectorCaller which indicates direction of IO.
+ void FileSelected(const base::FilePath& path,
+ int index,
+ void* params) override;
+
+ // PasswordUIView implementation.
+ Profile* GetProfile() override;
+ void ShowPassword(
+ size_t index,
+ const std::string& origin_url,
+ const std::string& username,
+ const base::string16& password_value) override;
+ void SetPasswordList(
+ const std::vector<std::unique_ptr<autofill::PasswordForm>>& password_list)
+ override;
+ void SetPasswordExceptionList(
+ const std::vector<std::unique_ptr<autofill::PasswordForm>>&
+ password_exception_list) override;
+#if !defined(OS_ANDROID)
+ gfx::NativeWindow GetNativeWindow() const override;
+#endif
+
+ protected:
+ // This constructor is used for testing only.
+ explicit PasswordManagerHandler(
+ std::unique_ptr<PasswordManagerPresenter> presenter);
+
+ private:
+ // Clears and then populates the list of passwords and password exceptions.
+ // Called when the JS PasswordManager object is initialized.
+ void HandleUpdatePasswordLists(const base::ListValue* args);
+
+ // Removes a saved password entry.
+ // |value| the entry index to be removed.
+ void HandleRemoveSavedPassword(const base::ListValue* args);
+
+ // Removes a saved password exception.
+ // |value| the entry index to be removed.
+ void HandleRemovePasswordException(const base::ListValue* args);
+
+ // Requests the plain text password for an entry to be revealed.
+ // |index| The index of the entry.
+ void HandleRequestShowPassword(const base::ListValue* args);
+
+ // Import from CSV/JSON file. The steps are:
+ // 1. user click import button -> HandlePasswordImport() ->
+ // start file selector
+ // 2. user selects file -> ImportPasswordFileSeleted() -> read to memory
+ // 3. read completes -> ImportPasswordFileRead() -> store to PasswordStore
+ void HandlePasswordImport(const base::ListValue* args);
+ void ImportPasswordFileSelected(const base::FilePath& path);
+ void ImportPasswordFileRead(password_manager::PasswordImporter::Result result,
+ const std::vector<autofill::PasswordForm>& forms);
+
+ // Export to CSV/JSON file. The steps are:
+ // 1. user click export button -> HandlePasswordExport() ->
+ // check OS password if necessary -> start file selector
+ // 2. user selects file -> ExportPasswordFileSeleted() ->
+ // write to memory buffer -> start write operation
+ void HandlePasswordExport(const base::ListValue* args);
+ void ExportPasswordFileSelected(const base::FilePath& path);
+
+ // A short class to persist imported password forms to password store.
+ class ImportPasswordResultConsumer
+ : public base::RefCountedThreadSafe<ImportPasswordResultConsumer> {
+ public:
+ explicit ImportPasswordResultConsumer(Profile* profile);
+
+ void ConsumePassword(password_manager::PasswordImporter::Result result,
+ const std::vector<autofill::PasswordForm>& forms);
+
+ private:
+ friend class base::RefCountedThreadSafe<ImportPasswordResultConsumer>;
+
+ ~ImportPasswordResultConsumer() {}
+
+ Profile* profile_;
+ };
+
+ // User pref for storing accept languages.
+ std::string languages_;
+
+ std::unique_ptr<PasswordManagerPresenter> password_manager_presenter_;
+
+ // File picker to import/export file path.
+ scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordManagerHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_PASSWORD_MANAGER_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/password_manager_handler_unittest.cc b/chromium/chrome/browser/ui/webui/options/password_manager_handler_unittest.cc
new file mode 100644
index 00000000000..bdd275e306a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/password_manager_handler_unittest.cc
@@ -0,0 +1,244 @@
+// 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 "chrome/browser/ui/webui/options/password_manager_handler.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/password_manager/password_store_factory.h"
+#include "chrome/browser/ui/passwords/password_manager_presenter.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/password_manager/core/browser/mock_password_store.h"
+#include "components/password_manager/core/browser/password_manager_test_utils.h"
+#include "content/public/test/test_web_ui.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+#include "ui/shell_dialogs/select_file_dialog_factory.h"
+#include "ui/shell_dialogs/select_file_policy.h"
+
+using password_manager::MockPasswordStore;
+
+namespace {
+
+class TestSelectFileDialogFactory final : public ui::SelectFileDialogFactory {
+ public:
+ TestSelectFileDialogFactory() {}
+ ~TestSelectFileDialogFactory() override {}
+ ui::SelectFileDialog* Create(ui::SelectFileDialog::Listener* listener,
+ ui::SelectFilePolicy* policy) override {
+ delete policy; // Ignore the policy, replace it with a test one.
+ return new TestSelectFileDialog(listener, new TestSelectFilePolicy);
+ }
+
+ private:
+ class TestSelectFilePolicy : public ui::SelectFilePolicy {
+ public:
+ bool CanOpenSelectFileDialog() override { return true; }
+ void SelectFileDenied() override {}
+ };
+
+ class TestSelectFileDialog : public ui::SelectFileDialog {
+ public:
+ TestSelectFileDialog(Listener* listener, ui::SelectFilePolicy* policy)
+ : ui::SelectFileDialog(listener, policy) {}
+
+ protected:
+ void SelectFileImpl(Type type,
+ const base::string16& title,
+ const base::FilePath& default_path,
+ const FileTypeInfo* file_types,
+ int file_type_index,
+ const base::FilePath::StringType& default_extension,
+ gfx::NativeWindow owning_window,
+ void* params) override {
+ listener_->FileSelected(default_path, file_type_index, params);
+ }
+ bool IsRunning(gfx::NativeWindow owning_window) const override {
+ return false;
+ }
+ void ListenerDestroyed() override {}
+ bool HasMultipleFileTypeChoicesImpl() override { return false; }
+ ~TestSelectFileDialog() override {}
+ };
+};
+
+class CallbackTestWebUI : public content::TestWebUI {
+ public:
+ CallbackTestWebUI() {}
+ ~CallbackTestWebUI() override {}
+ void RegisterMessageCallback(const std::string& message,
+ const MessageCallback& callback) override;
+ void ProcessWebUIMessage(const GURL& source_url,
+ const std::string& message,
+ const base::ListValue& args) override;
+
+ MOCK_CONST_METHOD0(GetWebContents, content::WebContents*());
+
+ private:
+ std::map<std::string, MessageCallback> message_callbacks_;
+};
+
+void CallbackTestWebUI::RegisterMessageCallback(
+ const std::string& message,
+ const MessageCallback& callback) {
+ message_callbacks_.insert(std::make_pair(message, callback));
+}
+
+void CallbackTestWebUI::ProcessWebUIMessage(const GURL& source_url,
+ const std::string& message,
+ const base::ListValue& args) {
+ std::map<std::string, MessageCallback>::const_iterator callback =
+ message_callbacks_.find(message);
+ if (callback != message_callbacks_.end()) {
+ callback->second.Run(&args);
+ }
+}
+
+class TestPasswordManagerHandler : public options::PasswordManagerHandler {
+ public:
+ TestPasswordManagerHandler(
+ std::unique_ptr<PasswordManagerPresenter> presenter,
+ CallbackTestWebUI* web_ui)
+ : PasswordManagerHandler(std::move(presenter)) {
+ set_web_ui(web_ui);
+ }
+ ~TestPasswordManagerHandler() override {}
+#if !defined(OS_ANDROID)
+ gfx::NativeWindow GetNativeWindow() const override;
+#endif
+
+ MOCK_METHOD3(FileSelected, void(const base::FilePath& path, int, void*));
+};
+#if !defined(OS_ANDROID)
+gfx::NativeWindow TestPasswordManagerHandler::GetNativeWindow() const {
+ return NULL;
+}
+#endif
+
+class MockPasswordManagerPresenter : public PasswordManagerPresenter {
+ public:
+ explicit MockPasswordManagerPresenter(PasswordUIView* handler)
+ : PasswordManagerPresenter(handler) {}
+ ~MockPasswordManagerPresenter() override {}
+
+ MOCK_METHOD0(IsUserAuthenticated, bool());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockPasswordManagerPresenter);
+};
+
+class DummyPasswordManagerHandler : public PasswordUIView {
+ public:
+ explicit DummyPasswordManagerHandler(Profile* profile)
+ : profile_(profile), password_manager_presenter_(this) {
+ password_manager_presenter_.Initialize();
+ }
+ ~DummyPasswordManagerHandler() override {}
+ Profile* GetProfile() override;
+
+ void ShowPassword(size_t,
+ const std::string&,
+ const std::string&,
+ const base::string16&) override {}
+ void SetPasswordList(
+ const std::vector<std::unique_ptr<autofill::PasswordForm>>&) override {}
+ void SetPasswordExceptionList(
+ const std::vector<std::unique_ptr<autofill::PasswordForm>>&) override {}
+
+#if !defined(OS_ANDROID)
+ gfx::NativeWindow GetNativeWindow() const override;
+#endif
+ private:
+ Profile* profile_;
+ PasswordManagerPresenter password_manager_presenter_;
+
+ DISALLOW_COPY_AND_ASSIGN(DummyPasswordManagerHandler);
+};
+
+#if !defined(OS_ANDROID)
+gfx::NativeWindow DummyPasswordManagerHandler::GetNativeWindow() const {
+ return NULL;
+}
+#endif
+
+Profile* DummyPasswordManagerHandler::GetProfile() {
+ return profile_;
+}
+
+} // namespace
+
+class PasswordManagerHandlerTest : public ChromeRenderViewHostTestHarness {
+ protected:
+ PasswordManagerHandlerTest() {}
+ ~PasswordManagerHandlerTest() override {}
+
+ void SetUp() override {
+ ChromeRenderViewHostTestHarness::SetUp();
+ dummy_handler_.reset(new DummyPasswordManagerHandler(profile()));
+ presenter_raw_ = new MockPasswordManagerPresenter(dummy_handler_.get());
+ handler_.reset(new TestPasswordManagerHandler(
+ base::WrapUnique(presenter_raw_), &web_ui_));
+ handler_->RegisterMessages();
+ ui::SelectFileDialog::SetFactory(new TestSelectFileDialogFactory);
+ handler_->InitializeHandler();
+ web_ui_.set_web_contents(web_contents());
+ }
+
+ void TearDown() override {
+ handler_.reset();
+ dummy_handler_.reset();
+ ChromeRenderViewHostTestHarness::TearDown();
+ }
+
+ void ExportPassword() {
+ base::ListValue tmp;
+ web_ui_.ProcessWebUIMessage(GURL(), "exportPassword", tmp);
+ }
+
+ void ImportPassword() {
+ base::ListValue tmp;
+ web_ui_.ProcessWebUIMessage(GURL(), "importPassword", tmp);
+ }
+
+ PasswordManagerPresenter* presenter_raw_;
+ CallbackTestWebUI web_ui_;
+ std::unique_ptr<DummyPasswordManagerHandler> dummy_handler_;
+ std::unique_ptr<TestPasswordManagerHandler> handler_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PasswordManagerHandlerTest);
+};
+
+MATCHER(IsEmptyPath, "") {
+ return arg.empty();
+}
+
+TEST_F(PasswordManagerHandlerTest, PasswordImport) {
+ EXPECT_CALL(web_ui_, GetWebContents())
+ .WillRepeatedly(testing::Return(web_contents()));
+ EXPECT_CALL(
+ *handler_,
+ FileSelected(IsEmptyPath(), 1,
+ reinterpret_cast<void*>(
+ TestPasswordManagerHandler::IMPORT_FILE_SELECTED)));
+ ImportPassword();
+}
+
+TEST_F(PasswordManagerHandlerTest, PasswordExport) {
+ const base::FilePath file_path;
+ EXPECT_CALL(*(static_cast<MockPasswordManagerPresenter*>(presenter_raw_)),
+ IsUserAuthenticated())
+ .Times(testing::AtLeast(1))
+ .WillRepeatedly(testing::Return(true));
+ EXPECT_CALL(
+ *handler_,
+ FileSelected(IsEmptyPath(), 1,
+ reinterpret_cast<void*>(
+ TestPasswordManagerHandler::EXPORT_FILE_SELECTED)));
+ ExportPassword();
+}
diff --git a/chromium/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils.cc b/chromium/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils.cc
new file mode 100644
index 00000000000..9dbab9f5369
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils.cc
@@ -0,0 +1,130 @@
+// 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/ui/webui/options/pepper_flash_content_settings_utils.h"
+
+#include <algorithm>
+#include <memory>
+
+namespace options {
+
+namespace {
+
+int CompareMediaException(const MediaException& i, const MediaException& j) {
+ return i.pattern.Compare(j.pattern);
+}
+
+bool MediaExceptionSortFunc(const MediaException& i, const MediaException& j) {
+ return CompareMediaException(i, j) < 0;
+}
+
+} // namespace
+
+MediaException::MediaException(const ContentSettingsPattern& in_pattern,
+ ContentSetting in_setting)
+ : pattern(in_pattern),
+ setting(in_setting) {
+}
+
+MediaException::~MediaException() {
+}
+
+bool MediaException::operator==(const MediaException& other) const {
+ return pattern == other.pattern && setting == other.setting;
+}
+
+// static
+ContentSetting PepperFlashContentSettingsUtils::FlashPermissionToContentSetting(
+ PP_Flash_BrowserOperations_Permission permission) {
+ switch (permission) {
+ case PP_FLASH_BROWSEROPERATIONS_PERMISSION_DEFAULT:
+ return CONTENT_SETTING_DEFAULT;
+ case PP_FLASH_BROWSEROPERATIONS_PERMISSION_ALLOW:
+ return CONTENT_SETTING_ALLOW;
+ case PP_FLASH_BROWSEROPERATIONS_PERMISSION_BLOCK:
+ return CONTENT_SETTING_BLOCK;
+ case PP_FLASH_BROWSEROPERATIONS_PERMISSION_ASK:
+ return CONTENT_SETTING_ASK;
+ // No default so the compiler will warn us if a new type is added.
+ }
+ return CONTENT_SETTING_DEFAULT;
+}
+
+// static
+void PepperFlashContentSettingsUtils::FlashSiteSettingsToMediaExceptions(
+ const ppapi::FlashSiteSettings& site_settings,
+ MediaExceptions* media_exceptions) {
+ media_exceptions->clear();
+
+ std::unique_ptr<ContentSettingsPattern::BuilderInterface> builder(
+ ContentSettingsPattern::CreateBuilder(false));
+ builder->WithSchemeWildcard()->WithPortWildcard();
+ for (ppapi::FlashSiteSettings::const_iterator iter = site_settings.begin();
+ iter != site_settings.end(); ++iter) {
+ builder->WithHost(iter->site);
+
+ ContentSettingsPattern pattern = builder->Build();
+ if (!pattern.IsValid())
+ continue;
+
+ ContentSetting setting = FlashPermissionToContentSetting(iter->permission);
+
+ media_exceptions->push_back(MediaException(pattern, setting));
+ }
+}
+
+// static
+void PepperFlashContentSettingsUtils::SortMediaExceptions(
+ MediaExceptions* media_exceptions) {
+ std::sort(media_exceptions->begin(), media_exceptions->end(),
+ MediaExceptionSortFunc);
+}
+
+// static
+bool PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
+ ContentSetting default_setting_1,
+ const MediaExceptions& exceptions_1,
+ ContentSetting default_setting_2,
+ const MediaExceptions& exceptions_2) {
+ MediaExceptions::const_iterator iter_1 = exceptions_1.begin();
+ MediaExceptions::const_iterator iter_2 = exceptions_2.begin();
+
+ MediaException default_exception_1(ContentSettingsPattern(),
+ default_setting_1);
+ MediaException default_exception_2(ContentSettingsPattern(),
+ default_setting_2);
+
+ while (iter_1 != exceptions_1.end() && iter_2 != exceptions_2.end()) {
+ int compare_result = CompareMediaException(*iter_1, *iter_2);
+ if (compare_result < 0) {
+ if (iter_1->setting != default_exception_2.setting)
+ return false;
+ ++iter_1;
+ } else if (compare_result > 0) {
+ if (iter_2->setting != default_exception_1.setting) {
+ return false;
+ }
+ ++iter_2;
+ } else {
+ if (iter_1->setting != iter_2->setting)
+ return false;
+ ++iter_1;
+ ++iter_2;
+ }
+ }
+
+ while (iter_1 != exceptions_1.end()) {
+ if (iter_1->setting != default_exception_2.setting)
+ return false;
+ ++iter_1;
+ }
+ while (iter_2 != exceptions_2.end()) {
+ if (iter_2->setting != default_exception_1.setting)
+ return false;
+ ++iter_2;
+ }
+ return true;
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils.h b/chromium/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils.h
new file mode 100644
index 00000000000..1e488239a3c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils.h
@@ -0,0 +1,62 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_PEPPER_FLASH_CONTENT_SETTINGS_UTILS_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_PEPPER_FLASH_CONTENT_SETTINGS_UTILS_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/content_settings_pattern.h"
+#include "ppapi/c/private/ppp_flash_browser_operations.h"
+#include "ppapi/shared_impl/ppp_flash_browser_operations_shared.h"
+
+namespace options {
+
+struct MediaException {
+ MediaException(const ContentSettingsPattern& in_pattern,
+ ContentSetting in_setting);
+ ~MediaException();
+
+ bool operator==(const MediaException& other) const;
+
+ ContentSettingsPattern pattern;
+ ContentSetting setting;
+};
+
+typedef std::vector<MediaException> MediaExceptions;
+
+class PepperFlashContentSettingsUtils {
+ public:
+ static ContentSetting FlashPermissionToContentSetting(
+ PP_Flash_BrowserOperations_Permission permission);
+
+ static void FlashSiteSettingsToMediaExceptions(
+ const ppapi::FlashSiteSettings& site_settings,
+ MediaExceptions* media_exceptions);
+
+ // Sorts |media_exceptions| in ascending order by comparing the |pattern|
+ // field of the elements.
+ static void SortMediaExceptions(MediaExceptions* media_exceptions);
+
+ // Checks whether |exceptions_1| and |exceptions_2| describe the same set of
+ // exceptions. |exceptions_1| and |exceptions_2| should be sorted by
+ // SortMediaExceptions() before passing into this method.
+ //
+ // When an element of |exceptions_1| has a pattern that doesn't match any
+ // element of |exceptions_2|, it would be compared with |default_setting_2|,
+ // and vice versa.
+ static bool AreMediaExceptionsEqual(ContentSetting default_setting_1,
+ const MediaExceptions& exceptions_1,
+ ContentSetting default_setting_2,
+ const MediaExceptions& exceptions_2);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(PepperFlashContentSettingsUtils);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_PEPPER_FLASH_CONTENT_SETTINGS_UTILS_H_
diff --git a/chromium/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils_unittest.cc b/chromium/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils_unittest.cc
new file mode 100644
index 00000000000..dd1317c88c2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/pepper_flash_content_settings_utils_unittest.cc
@@ -0,0 +1,137 @@
+// 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/ui/webui/options/pepper_flash_content_settings_utils.h"
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using options::MediaException;
+using options::MediaExceptions;
+using options::PepperFlashContentSettingsUtils;
+
+namespace {
+
+MediaExceptions ConvertAndSort(const MediaException* items, size_t count) {
+ MediaExceptions result(items, items + count);
+ PepperFlashContentSettingsUtils::SortMediaExceptions(&result);
+ return result;
+}
+
+} // namespace
+
+TEST(PepperFlashContentSettingsUtilsTest, SortMediaExceptions) {
+ MediaException entry_1(ContentSettingsPattern::FromString("www.google.com"),
+ CONTENT_SETTING_ALLOW);
+ MediaException entry_2(ContentSettingsPattern::FromString("www.youtube.com"),
+ CONTENT_SETTING_BLOCK);
+ MediaException entry_3(ContentSettingsPattern::Wildcard(),
+ CONTENT_SETTING_ASK);
+ MediaException entry_4(ContentSettingsPattern(),
+ CONTENT_SETTING_SESSION_ONLY);
+
+ MediaExceptions list_1;
+ list_1.push_back(entry_1);
+ list_1.push_back(entry_2);
+ list_1.push_back(entry_3);
+ list_1.push_back(entry_4);
+
+ MediaExceptions list_2;
+ list_2.push_back(entry_1);
+ list_2.push_back(entry_3);
+ list_2.push_back(entry_2);
+ list_2.push_back(entry_4);
+
+ MediaExceptions list_3;
+ list_3.push_back(entry_4);
+ list_3.push_back(entry_1);
+ list_3.push_back(entry_2);
+ list_3.push_back(entry_3);
+
+ EXPECT_NE(list_1, list_2);
+ EXPECT_NE(list_2, list_3);
+ EXPECT_NE(list_3, list_1);
+
+ PepperFlashContentSettingsUtils::SortMediaExceptions(&list_1);
+ PepperFlashContentSettingsUtils::SortMediaExceptions(&list_2);
+ PepperFlashContentSettingsUtils::SortMediaExceptions(&list_3);
+
+ EXPECT_EQ(list_1, list_2);
+ EXPECT_EQ(list_2, list_3);
+}
+
+TEST(PepperFlashContentSettingsUtilsTest, AreMediaExceptionsEqual) {
+ {
+ // Empty lists are equal.
+ // Default settings are not compared directly, so it is possible to return
+ // true when they are different.
+ EXPECT_TRUE(PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
+ CONTENT_SETTING_BLOCK,
+ MediaExceptions(),
+ CONTENT_SETTING_ASK,
+ MediaExceptions()));
+ }
+
+ {
+ MediaException exceptions_1[] = {
+ MediaException(ContentSettingsPattern::FromString("www.google.com"),
+ CONTENT_SETTING_ALLOW),
+ MediaException(ContentSettingsPattern::FromString("www.youtube.com"),
+ CONTENT_SETTING_ASK)
+ };
+
+ MediaException exceptions_2[] = {
+ MediaException(ContentSettingsPattern::FromString("www.google.com"),
+ CONTENT_SETTING_ALLOW)
+ };
+
+ // The exception of "www.youtube.com" in |exceptions_1| should not affect
+ // the result, because it has the same settings as |default_setting_2|.
+ EXPECT_TRUE(PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
+ CONTENT_SETTING_ALLOW,
+ ConvertAndSort(exceptions_1, arraysize(exceptions_1)),
+ CONTENT_SETTING_ASK,
+ ConvertAndSort(exceptions_2, arraysize(exceptions_2))));
+ EXPECT_TRUE(PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
+ CONTENT_SETTING_ASK,
+ ConvertAndSort(exceptions_2, arraysize(exceptions_2)),
+ CONTENT_SETTING_ALLOW,
+ ConvertAndSort(exceptions_1, arraysize(exceptions_1))));
+ // Changing |default_setting_2| should change the result.
+ EXPECT_FALSE(PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
+ CONTENT_SETTING_ALLOW,
+ ConvertAndSort(exceptions_1, arraysize(exceptions_1)),
+ CONTENT_SETTING_ALLOW,
+ ConvertAndSort(exceptions_2, arraysize(exceptions_2))));
+ }
+
+ {
+ // Similar to the previous block, but reoder the exceptions. The outcome
+ // should be the same.
+ MediaException exceptions_1[] = {
+ MediaException(ContentSettingsPattern::FromString("www.youtube.com"),
+ CONTENT_SETTING_ASK),
+ MediaException(ContentSettingsPattern::FromString("www.google.com"),
+ CONTENT_SETTING_ALLOW)
+ };
+
+ MediaException exceptions_2[] = {
+ MediaException(ContentSettingsPattern::FromString("www.google.com"),
+ CONTENT_SETTING_ALLOW)
+ };
+
+ EXPECT_TRUE(PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
+ CONTENT_SETTING_ALLOW,
+ ConvertAndSort(exceptions_1, arraysize(exceptions_1)),
+ CONTENT_SETTING_ASK,
+ ConvertAndSort(exceptions_2, arraysize(exceptions_2))));
+ EXPECT_FALSE(PepperFlashContentSettingsUtils::AreMediaExceptionsEqual(
+ CONTENT_SETTING_ALLOW,
+ ConvertAndSort(exceptions_1, arraysize(exceptions_1)),
+ CONTENT_SETTING_ALLOW,
+ ConvertAndSort(exceptions_2, arraysize(exceptions_2))));
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/options/preferences_browsertest.cc b/chromium/chrome/browser/ui/webui/options/preferences_browsertest.cc
new file mode 100644
index 00000000000..3cc4c0bd3a3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/preferences_browsertest.cc
@@ -0,0 +1,1106 @@
+// 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/ui/webui/options/preferences_browsertest.h"
+
+#include <stddef.h>
+
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/common/external_data_fetcher.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+#if defined(OS_CHROMEOS)
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/browser/chromeos/proxy_cros_settings_parser.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/chromeos/settings/stub_install_attributes.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/shill_profile_client.h"
+#include "chromeos/dbus/shill_service_client.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "chromeos/network/proxy/proxy_config_handler.h"
+#include "chromeos/settings/cros_settings_names.h"
+#include "components/onc/onc_pref_names.h"
+#include "components/proxy_config/proxy_config_dictionary.h"
+#include "components/proxy_config/proxy_config_pref_names.h"
+#include "content/public/test/test_utils.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+#endif
+
+using testing::AllOf;
+using testing::Mock;
+using testing::Property;
+using testing::Return;
+using testing::_;
+
+namespace base {
+
+// Helper for pretty-printing the contents of base::Value in case of failures.
+void PrintTo(const base::Value& value, std::ostream* stream) {
+ std::string json;
+ JSONWriter::Write(value, &json);
+ *stream << json;
+}
+
+} // namespace base
+
+// Googlemock matcher for base::Value.
+MATCHER_P(EqualsValue, expected, "") {
+ return arg && arg->Equals(expected);
+}
+
+PreferencesBrowserTest::PreferencesBrowserTest() {
+}
+
+PreferencesBrowserTest::~PreferencesBrowserTest() {
+}
+
+PrefService* PreferencesBrowserTest::pref_service() {
+ DCHECK(pref_change_registrar_);
+ return pref_change_registrar_->prefs();
+}
+
+// Navigates to the settings page, causing the JavaScript pref handling code to
+// load and injects JavaScript testing code.
+void PreferencesBrowserTest::SetUpOnMainThread() {
+ ui_test_utils::NavigateToURL(browser(),
+ GURL(chrome::kChromeUISettingsFrameURL));
+ SetUpPrefs();
+}
+
+void PreferencesBrowserTest::TearDownOnMainThread() {
+ pref_change_registrar_.reset();
+}
+
+void PreferencesBrowserTest::SetUpPrefs() {
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+ render_view_host_ = web_contents->GetRenderViewHost();
+ ASSERT_TRUE(render_view_host_);
+
+ DCHECK(!pref_change_registrar_);
+ pref_change_registrar_.reset(new PrefChangeRegistrar);
+ pref_change_registrar_->Init(browser()->profile()->GetPrefs());
+
+ ASSERT_TRUE(content::ExecuteScript(render_view_host_,
+ "function TestEnv() {"
+ " this.sentinelName_ = 'download.prompt_for_download';"
+ " this.prefs_ = [];"
+ " TestEnv.instance_ = this;"
+ "}"
+ ""
+ "TestEnv.handleEvent = function(event) {"
+ " var env = TestEnv.instance_;"
+ " var name = event.type;"
+ " env.removePrefListener_(name);"
+ " if (name == TestEnv.sentinelName_)"
+ " env.sentinelValue_ = event.value.value;"
+ " else"
+ " env.reply_[name] = event.value;"
+ " if (env.fetching_ && !--env.fetching_ ||"
+ " !env.fetching_ && name == env.sentinelName_) {"
+ " env.removePrefListeners_();"
+ " window.domAutomationController.send(JSON.stringify(env.reply_));"
+ " delete env.reply_;"
+ " }"
+ "};"
+ ""
+ "TestEnv.prototype = {"
+ " addPrefListener_: function(name) {"
+ " Preferences.getInstance().addEventListener(name,"
+ " TestEnv.handleEvent);"
+ " },"
+ ""
+ " addPrefListeners_: function() {"
+ " for (var i in this.prefs_)"
+ " this.addPrefListener_(this.prefs_[i]);"
+ " },"
+ ""
+ " removePrefListener_: function(name) {"
+ " Preferences.getInstance().removeEventListener(name,"
+ " TestEnv.handleEvent);"
+ " },"
+ ""
+ " removePrefListeners_: function() {"
+ " for (var i in this.prefs_)"
+ " this.removePrefListener_(this.prefs_[i]);"
+ " },"
+ ""
+ ""
+ " addPref: function(name) {"
+ " this.prefs_.push(name);"
+ " },"
+ ""
+ " setupAndReply: function() {"
+ " this.reply_ = {};"
+ " Preferences.instance_ = new Preferences();"
+ " this.addPref(this.sentinelName_);"
+ " this.fetching_ = this.prefs_.length;"
+ " this.addPrefListeners_();"
+ " Preferences.getInstance().initialize();"
+ " },"
+ ""
+ " runAndReply: function(test) {"
+ " this.reply_ = {};"
+ " this.addPrefListeners_();"
+ " test();"
+ " this.sentinelValue_ = !this.sentinelValue_;"
+ " Preferences.setBooleanPref(this.sentinelName_, this.sentinelValue_,"
+ " true);"
+ " },"
+ ""
+ " startObserving: function() {"
+ " this.reply_ = {};"
+ " this.addPrefListeners_();"
+ " },"
+ ""
+ " finishObservingAndReply: function() {"
+ " this.sentinelValue_ = !this.sentinelValue_;"
+ " Preferences.setBooleanPref(this.sentinelName_, this.sentinelValue_,"
+ " true);"
+ " }"
+ "};"));
+}
+
+// Forwards notifications received when pref values change in the backend.
+void PreferencesBrowserTest::OnPreferenceChanged(const std::string& pref_name) {
+ OnCommit(pref_service()->FindPreference(pref_name.c_str()));
+}
+
+void PreferencesBrowserTest::SetUpInProcessBrowserTestFixture() {
+ // Sets up a mock policy provider for user and device policies.
+ EXPECT_CALL(policy_provider_, IsInitializationComplete(_))
+ .WillRepeatedly(Return(true));
+ policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
+ &policy_provider_);
+}
+
+void PreferencesBrowserTest::SetUserPolicies(
+ const std::vector<std::string>& names,
+ const std::vector<std::unique_ptr<base::Value>>& values,
+ policy::PolicyLevel level) {
+ policy::PolicyMap map;
+ for (size_t i = 0; i < names.size(); ++i) {
+ map.Set(names[i], level, policy::POLICY_SCOPE_USER,
+ policy::POLICY_SOURCE_CLOUD, values[i]->CreateDeepCopy(), nullptr);
+ }
+ policy_provider_.UpdateChromePolicy(map);
+}
+
+void PreferencesBrowserTest::ClearUserPolicies() {
+ policy::PolicyMap empty_policy_map;
+ policy_provider_.UpdateChromePolicy(empty_policy_map);
+}
+
+void PreferencesBrowserTest::SetUserValues(
+ const std::vector<std::string>& names,
+ const std::vector<std::unique_ptr<base::Value>>& values) {
+ for (size_t i = 0; i < names.size(); ++i) {
+ pref_service()->Set(names[i].c_str(), *values[i]);
+ }
+}
+
+void PreferencesBrowserTest::VerifyKeyValue(const base::DictionaryValue& dict,
+ const std::string& key,
+ const base::Value& expected) {
+ const base::Value* actual = NULL;
+ EXPECT_TRUE(dict.Get(key, &actual)) << "Was checking key: " << key;
+ if (actual)
+ EXPECT_EQ(expected, *actual) << "Was checking key: " << key;
+}
+
+void PreferencesBrowserTest::VerifyPref(
+ const base::DictionaryValue* prefs,
+ const std::string& name,
+ const std::unique_ptr<base::Value>& value,
+ const std::string& controlledBy,
+ bool disabled,
+ bool uncommitted) {
+ const base::Value* pref = NULL;
+ const base::DictionaryValue* dict = NULL;
+ ASSERT_TRUE(prefs->GetWithoutPathExpansion(name, &pref));
+ ASSERT_TRUE(pref->GetAsDictionary(&dict));
+ VerifyKeyValue(*dict, "value", *value);
+ if (!controlledBy.empty())
+ VerifyKeyValue(*dict, "controlledBy", base::Value(controlledBy));
+ else
+ EXPECT_FALSE(dict->HasKey("controlledBy"));
+
+ if (disabled)
+ VerifyKeyValue(*dict, "disabled", base::Value(true));
+ else if (dict->HasKey("disabled"))
+ VerifyKeyValue(*dict, "disabled", base::Value(false));
+
+ if (uncommitted)
+ VerifyKeyValue(*dict, "uncommitted", base::Value(true));
+ else if (dict->HasKey("uncommitted"))
+ VerifyKeyValue(*dict, "uncommitted", base::Value(false));
+}
+
+void PreferencesBrowserTest::VerifyObservedPref(
+ const std::string& json,
+ const std::string& name,
+ const std::unique_ptr<base::Value>& value,
+ const std::string& controlledBy,
+ bool disabled,
+ bool uncommitted) {
+ std::unique_ptr<base::Value> observed_value_ptr =
+ base::JSONReader::Read(json);
+ const base::DictionaryValue* observed_dict;
+ ASSERT_TRUE(observed_value_ptr.get());
+ ASSERT_TRUE(observed_value_ptr->GetAsDictionary(&observed_dict));
+ VerifyPref(observed_dict, name, value, controlledBy, disabled, uncommitted);
+}
+
+void PreferencesBrowserTest::VerifyObservedPrefs(
+ const std::string& json,
+ const std::vector<std::string>& names,
+ const std::vector<std::unique_ptr<base::Value>>& values,
+ const std::string& controlledBy,
+ bool disabled,
+ bool uncommitted) {
+ std::unique_ptr<base::Value> observed_value_ptr =
+ base::JSONReader::Read(json);
+ const base::DictionaryValue* observed_dict;
+ ASSERT_TRUE(observed_value_ptr.get());
+ ASSERT_TRUE(observed_value_ptr->GetAsDictionary(&observed_dict));
+ for (size_t i = 0; i < names.size(); ++i) {
+ VerifyPref(observed_dict, names[i], values[i], controlledBy, disabled,
+ uncommitted);
+ }
+}
+
+void PreferencesBrowserTest::ExpectNoCommit(const std::string& name) {
+ pref_change_registrar_->Add(
+ name.c_str(),
+ base::Bind(&PreferencesBrowserTest::OnPreferenceChanged,
+ base::Unretained(this)));
+ EXPECT_CALL(*this, OnCommit(Property(&PrefService::Preference::name, name)))
+ .Times(0);
+}
+
+void PreferencesBrowserTest::ExpectSetCommit(
+ const std::string& name,
+ const std::unique_ptr<base::Value>& value) {
+ pref_change_registrar_->Add(
+ name.c_str(),
+ base::Bind(&PreferencesBrowserTest::OnPreferenceChanged,
+ base::Unretained(this)));
+ EXPECT_CALL(
+ *this,
+ OnCommit(AllOf(Property(&PrefService::Preference::name, name),
+ Property(&PrefService::Preference::IsUserControlled, true),
+ Property(&PrefService::Preference::GetValue,
+ EqualsValue(value.get())))));
+}
+
+void PreferencesBrowserTest::ExpectClearCommit(const std::string& name) {
+ pref_change_registrar_->Add(
+ name.c_str(),
+ base::Bind(&PreferencesBrowserTest::OnPreferenceChanged,
+ base::Unretained(this)));
+ EXPECT_CALL(*this, OnCommit(AllOf(
+ Property(&PrefService::Preference::name, name),
+ Property(&PrefService::Preference::IsUserControlled, false))));
+}
+
+void PreferencesBrowserTest::VerifyAndClearExpectations() {
+ Mock::VerifyAndClearExpectations(this);
+ pref_change_registrar_->RemoveAll();
+}
+
+void PreferencesBrowserTest::SetupJavaScriptTestEnvironment(
+ const std::vector<std::string>& pref_names,
+ std::string* observed_json) const {
+ std::stringstream javascript;
+ javascript << "var testEnv = new TestEnv();";
+ for (const auto& name : pref_names) {
+ javascript << "testEnv.addPref('" << name.c_str() << "');";
+ }
+ javascript << "testEnv.setupAndReply();";
+ std::string temp_observed_json;
+ if (!observed_json)
+ observed_json = &temp_observed_json;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ render_view_host_, javascript.str(), observed_json));
+}
+
+void PreferencesBrowserTest::SetPref(const std::string& name,
+ const std::string& type,
+ const std::unique_ptr<base::Value>& value,
+ bool commit,
+ std::string* observed_json) {
+ std::unique_ptr<base::Value> commit_ptr(new base::Value(commit));
+ std::stringstream javascript;
+ javascript << "testEnv.runAndReply(function() {"
+ << " Preferences.set" << type << "Pref("
+ << " '" << name << "',"
+ << " " << *value << ","
+ << " " << *commit_ptr << ");"
+ << "});";
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ render_view_host_, javascript.str(), observed_json));
+}
+
+void PreferencesBrowserTest::VerifySetPref(
+ const std::string& name,
+ const std::string& type,
+ const std::unique_ptr<base::Value>& value,
+ bool commit) {
+ if (commit)
+ ExpectSetCommit(name, value);
+ else
+ ExpectNoCommit(name);
+ std::string observed_json;
+ SetPref(name, type, value, commit, &observed_json);
+ VerifyObservedPref(observed_json, name, value, std::string(), false, !commit);
+ VerifyAndClearExpectations();
+}
+
+void PreferencesBrowserTest::VerifyClearPref(
+ const std::string& name,
+ const std::unique_ptr<base::Value>& value,
+ bool commit) {
+ if (commit)
+ ExpectClearCommit(name);
+ else
+ ExpectNoCommit(name);
+ std::string commit_json;
+ base::JSONWriter::Write(base::Value(commit), &commit_json);
+ std::stringstream javascript;
+ javascript << "testEnv.runAndReply(function() {"
+ << " Preferences.clearPref("
+ << " '" << name.c_str() << "',"
+ << " " << commit_json.c_str() << ");});";
+ std::string observed_json;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ render_view_host_, javascript.str(), &observed_json));
+ VerifyObservedPref(observed_json, name, value, "recommended", false, !commit);
+ VerifyAndClearExpectations();
+}
+
+void PreferencesBrowserTest::VerifyCommit(
+ const std::string& name,
+ const std::unique_ptr<base::Value>& value,
+ const std::string& controlledBy) {
+ std::stringstream javascript;
+ javascript << "testEnv.runAndReply(function() {"
+ << " Preferences.getInstance().commitPref("
+ << " '" << name.c_str() << "');});";
+ std::string observed_json;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ render_view_host_, javascript.str(), &observed_json));
+ VerifyObservedPref(observed_json, name, value, controlledBy, false, false);
+}
+
+void PreferencesBrowserTest::VerifySetCommit(
+ const std::string& name,
+ const std::unique_ptr<base::Value>& value) {
+ ExpectSetCommit(name, value);
+ VerifyCommit(name, value, std::string());
+ VerifyAndClearExpectations();
+}
+
+void PreferencesBrowserTest::VerifyClearCommit(
+ const std::string& name,
+ const std::unique_ptr<base::Value>& value) {
+ ExpectClearCommit(name);
+ VerifyCommit(name, value, "recommended");
+ VerifyAndClearExpectations();
+}
+
+void PreferencesBrowserTest::VerifyRollback(
+ const std::string& name,
+ const std::unique_ptr<base::Value>& value,
+ const std::string& controlledBy) {
+ ExpectNoCommit(name);
+ std::stringstream javascript;
+ javascript << "testEnv.runAndReply(function() {"
+ << " Preferences.getInstance().rollbackPref("
+ << " '" << name.c_str() << "');});";
+ std::string observed_json;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ render_view_host_, javascript.str(), &observed_json));
+ VerifyObservedPref(observed_json, name, value, controlledBy, false, true);
+ VerifyAndClearExpectations();
+}
+
+void PreferencesBrowserTest::StartObserving() {
+ ASSERT_TRUE(content::ExecuteScript(
+ render_view_host_, "testEnv.startObserving();"));
+}
+
+void PreferencesBrowserTest::FinishObserving(std::string* observed_json) {
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ render_view_host_,
+ "testEnv.finishObservingAndReply();",
+ observed_json));
+}
+
+void PreferencesBrowserTest::UseDefaultTestPrefs(bool includeListPref) {
+ // Boolean pref.
+ types_.push_back("Boolean");
+ pref_names_.push_back(prefs::kAlternateErrorPagesEnabled);
+ policy_names_.push_back(policy::key::kAlternateErrorPagesEnabled);
+ non_default_values_.push_back(base::MakeUnique<base::Value>(false));
+
+ // Integer pref.
+ types_.push_back("Integer");
+ pref_names_.push_back(prefs::kRestoreOnStartup);
+ policy_names_.push_back(policy::key::kRestoreOnStartup);
+ non_default_values_.push_back(base::MakeUnique<base::Value>(4));
+
+ // List pref.
+ if (includeListPref) {
+ types_.push_back("List");
+ pref_names_.push_back(prefs::kURLsToRestoreOnStartup);
+ policy_names_.push_back(policy::key::kRestoreOnStartupURLs);
+ auto list = base::MakeUnique<base::ListValue>();
+ list->AppendString("http://www.example.com");
+ list->AppendString("http://example.com");
+ non_default_values_.push_back(std::move(list));
+ }
+
+ // Retrieve default values.
+ for (const auto& name : pref_names_) {
+ default_values_.push_back(
+ pref_service()->GetDefaultPrefValue(name.c_str())->CreateDeepCopy());
+ }
+}
+
+// Verifies that initializing the JavaScript Preferences class fires the correct
+// notifications in JavaScript.
+IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, FetchPrefs) {
+ UseDefaultTestPrefs(true);
+ std::string observed_json;
+
+ // Verify notifications when default values are in effect.
+ SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, default_values_,
+ std::string(), false, false);
+
+ // Verify notifications when recommended values are in effect.
+ SetUserPolicies(policy_names_, non_default_values_,
+ policy::POLICY_LEVEL_RECOMMENDED);
+ SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, non_default_values_,
+ "recommended", false, false);
+
+ // Verify notifications when mandatory values are in effect.
+ SetUserPolicies(policy_names_, non_default_values_,
+ policy::POLICY_LEVEL_MANDATORY);
+ SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, non_default_values_, "policy",
+ true, false);
+
+ // Verify notifications when user-modified values are in effect.
+ ClearUserPolicies();
+ SetUserValues(pref_names_, non_default_values_);
+ SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, non_default_values_,
+ std::string(), false, false);
+}
+
+// Verifies that setting a user-modified pref value through the JavaScript
+// Preferences class fires the correct notification in JavaScript and causes the
+// change to be committed to the C++ backend.
+IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, SetPrefs) {
+ UseDefaultTestPrefs(false);
+
+ ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
+ for (size_t i = 0; i < pref_names_.size(); ++i) {
+ VerifySetPref(pref_names_[i], types_[i], non_default_values_[i], true);
+ }
+}
+
+// Verifies that clearing a user-modified pref value through the JavaScript
+// Preferences class fires the correct notification in JavaScript and causes the
+// change to be committed to the C++ backend.
+IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, ClearPrefs) {
+ UseDefaultTestPrefs(false);
+
+ SetUserPolicies(policy_names_, default_values_,
+ policy::POLICY_LEVEL_RECOMMENDED);
+ SetUserValues(pref_names_, non_default_values_);
+ ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
+ for (size_t i = 0; i < pref_names_.size(); ++i) {
+ VerifyClearPref(pref_names_[i], default_values_[i], true);
+ }
+}
+
+// Verifies that when the user-modified value of a dialog pref is set and the
+// change then committed through the JavaScript Preferences class, the correct
+// notifications fire and a commit to the C++ backend occurs in the latter step
+// only.
+IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, DialogPrefsSetCommit) {
+ UseDefaultTestPrefs(false);
+
+ ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
+ for (size_t i = 0; i < pref_names_.size(); ++i) {
+ VerifySetPref(pref_names_[i], types_[i], non_default_values_[i], false);
+ VerifySetCommit(pref_names_[i], non_default_values_[i]);
+ }
+}
+
+// Verifies that when the user-modified value of a dialog pref is set and the
+// change then rolled back through the JavaScript Preferences class, the correct
+// notifications fire and no commit to the C++ backend occurs.
+IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, DialogPrefsSetRollback) {
+ UseDefaultTestPrefs(false);
+
+ // Verify behavior when default values are in effect.
+ ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
+ for (size_t i = 0; i < pref_names_.size(); ++i) {
+ VerifySetPref(pref_names_[i], types_[i], non_default_values_[i], false);
+ VerifyRollback(pref_names_[i], default_values_[i], std::string());
+ }
+
+ // Verify behavior when recommended values are in effect.
+ SetUserPolicies(policy_names_, default_values_,
+ policy::POLICY_LEVEL_RECOMMENDED);
+ ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
+ for (size_t i = 0; i < pref_names_.size(); ++i) {
+ VerifySetPref(pref_names_[i], types_[i], non_default_values_[i], false);
+ VerifyRollback(pref_names_[i], default_values_[i], "recommended");
+ }
+}
+
+// Verifies that when the user-modified value of a dialog pref is cleared and
+// the change then committed through the JavaScript Preferences class, the
+// correct notifications fire and a commit to the C++ backend occurs in the
+// latter step only.
+IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, DialogPrefsClearCommit) {
+ UseDefaultTestPrefs(false);
+
+ SetUserPolicies(policy_names_, default_values_,
+ policy::POLICY_LEVEL_RECOMMENDED);
+ SetUserValues(pref_names_, non_default_values_);
+ ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
+ for (size_t i = 0; i < pref_names_.size(); ++i) {
+ VerifyClearPref(pref_names_[i], default_values_[i], false);
+ VerifyClearCommit(pref_names_[i], default_values_[i]);
+ }
+}
+
+// Verifies that when the user-modified value of a dialog pref is cleared and
+// the change then rolled back through the JavaScript Preferences class, the
+// correct notifications fire and no commit to the C++ backend occurs.
+IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, DialogPrefsClearRollback) {
+ UseDefaultTestPrefs(false);
+
+ SetUserPolicies(policy_names_, default_values_,
+ policy::POLICY_LEVEL_RECOMMENDED);
+ SetUserValues(pref_names_, non_default_values_);
+ ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
+ for (size_t i = 0; i < pref_names_.size(); ++i) {
+ VerifyClearPref(pref_names_[i], default_values_[i], false);
+ VerifyRollback(pref_names_[i], non_default_values_[i], std::string());
+ }
+}
+
+// Verifies that when preference values change in the C++ backend, the correct
+// notifications fire in JavaScript.
+IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, NotificationsOnBackendChanges) {
+ UseDefaultTestPrefs(false);
+ std::string observed_json;
+
+ ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
+
+ // Verify notifications when recommended values come into effect.
+ StartObserving();
+ SetUserPolicies(policy_names_, non_default_values_,
+ policy::POLICY_LEVEL_RECOMMENDED);
+ FinishObserving(&observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, non_default_values_,
+ "recommended", false, false);
+
+ // Verify notifications when mandatory values come into effect.
+ StartObserving();
+ SetUserPolicies(policy_names_, non_default_values_,
+ policy::POLICY_LEVEL_MANDATORY);
+ FinishObserving(&observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, non_default_values_, "policy",
+ true, false);
+
+ // Verify notifications when default values come into effect.
+ StartObserving();
+ ClearUserPolicies();
+ FinishObserving(&observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, default_values_,
+ std::string(), false, false);
+
+ // Verify notifications when user-modified values come into effect.
+ StartObserving();
+ SetUserValues(pref_names_, non_default_values_);
+ FinishObserving(&observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, non_default_values_,
+ std::string(), false, false);
+}
+
+#if defined(OS_CHROMEOS)
+
+// Verifies that initializing the JavaScript Preferences class fires the correct
+// notifications in JavaScript for pref values handled by the
+// CoreChromeOSOptionsHandler class.
+IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest, ChromeOSDeviceFetchPrefs) {
+ std::string observed_json;
+
+ // Boolean pref.
+ pref_names_.push_back(chromeos::kAccountsPrefAllowGuest);
+ default_values_.push_back(base::MakeUnique<base::Value>(true));
+
+ // String pref.
+ pref_names_.push_back(chromeos::kReleaseChannel);
+ default_values_.push_back(base::MakeUnique<base::Value>(""));
+
+ // List pref.
+ pref_names_.push_back(chromeos::kAccountsPrefUsers);
+ default_values_.push_back(base::MakeUnique<base::ListValue>());
+
+ // Verify notifications when default values are in effect.
+ SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, default_values_, "owner",
+ true, false);
+}
+
+// Verifies that initializing the JavaScript Preferences class fires the correct
+// notifications in JavaScript for non-privileged pref values handled by the
+// CoreChromeOSOptionsHandler class.
+IN_PROC_BROWSER_TEST_F(PreferencesBrowserTest,
+ ChromeOSDeviceFetchNonPrivilegedPrefs) {
+ std::vector<std::unique_ptr<base::Value>> decorated_non_default_values;
+ std::string observed_json;
+
+ // Non-privileged string pref.
+ pref_names_.push_back(chromeos::kSystemTimezone);
+ default_values_.push_back(
+ base::MakeUnique<base::Value>("America/Los_Angeles"));
+ non_default_values_.push_back(
+ base::MakeUnique<base::Value>("America/New_York"));
+ decorated_non_default_values.push_back(
+ non_default_values_.back()->CreateDeepCopy());
+
+ // Verify notifications when default values are in effect.
+ SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, default_values_,
+ std::string(), false, false);
+
+ chromeos::CrosSettings* cros_settings = chromeos::CrosSettings::Get();
+ cros_settings->Set(pref_names_[0], *non_default_values_[0]);
+
+ // Verify notifications when non-default values are in effect.
+ SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, decorated_non_default_values,
+ std::string(), false, false);
+}
+
+class ManagedPreferencesBrowserTest : public PreferencesBrowserTest {
+ protected:
+ // PreferencesBrowserTest implementation:
+ void SetUpInProcessBrowserTestFixture() override {
+ // Set up fake install attributes.
+ std::unique_ptr<chromeos::StubInstallAttributes> attributes =
+ base::MakeUnique<chromeos::StubInstallAttributes>();
+ attributes->SetCloudManaged("example.com", "fake-id");
+ policy::BrowserPolicyConnectorChromeOS::SetInstallAttributesForTesting(
+ attributes.release());
+
+ PreferencesBrowserTest::SetUpInProcessBrowserTestFixture();
+ }
+};
+
+// Verifies that initializing the JavaScript Preferences class fires the correct
+// notifications in JavaScript for pref values handled by the
+// CoreChromeOSOptionsHandler class for a managed device.
+IN_PROC_BROWSER_TEST_F(ManagedPreferencesBrowserTest,
+ ChromeOSDeviceFetchPrefs) {
+ std::vector<std::unique_ptr<base::Value>> decorated_non_default_values;
+ std::string observed_json;
+
+ // Boolean pref.
+ pref_names_.push_back(chromeos::kAccountsPrefAllowGuest);
+ non_default_values_.push_back(base::MakeUnique<base::Value>(false));
+ decorated_non_default_values.push_back(
+ non_default_values_.back()->CreateDeepCopy());
+
+ // String pref.
+ pref_names_.push_back(chromeos::kReleaseChannel);
+ non_default_values_.push_back(
+ base::MakeUnique<base::Value>("stable-channel"));
+ decorated_non_default_values.push_back(
+ non_default_values_.back()->CreateDeepCopy());
+
+ // List pref.
+ pref_names_.push_back(chromeos::kAccountsPrefUsers);
+ auto list = base::MakeUnique<base::ListValue>();
+ list->AppendString("me@google.com");
+ list->AppendString("you@google.com");
+ non_default_values_.push_back(std::move(list));
+ list = base::MakeUnique<base::ListValue>();
+ auto dict = base::MakeUnique<base::DictionaryValue>();
+ dict->SetString("username", "me@google.com");
+ dict->SetString("name", "me@google.com");
+ dict->SetString("email", "");
+ dict->SetBoolean("owner", false);
+ list->Append(std::move(dict));
+ dict = base::MakeUnique<base::DictionaryValue>();
+ dict->SetString("username", "you@google.com");
+ dict->SetString("name", "you@google.com");
+ dict->SetString("email", "");
+ dict->SetBoolean("owner", false);
+ list->Append(std::move(dict));
+ decorated_non_default_values.push_back(std::move(list));
+
+ chromeos::CrosSettings* cros_settings = chromeos::CrosSettings::Get();
+ for (size_t i = 0; i < pref_names_.size(); ++i) {
+ cros_settings->Set(pref_names_[i], *non_default_values_[i]);
+ }
+
+ // Verify notifications when mandatory values are in effect.
+ SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, decorated_non_default_values,
+ "policy", true, false);
+}
+
+// Verifies that initializing the JavaScript Preferences class fires the correct
+// notifications in JavaScript for non-privileged pref values handled by the
+// CoreChromeOSOptionsHandler class for a managed device.
+IN_PROC_BROWSER_TEST_F(ManagedPreferencesBrowserTest,
+ ChromeOSDeviceFetchNonPrivilegedPrefs) {
+ std::vector<std::unique_ptr<base::Value>> decorated_non_default_values;
+ std::string observed_json;
+
+ // Non-privileged string pref.
+ pref_names_.push_back(chromeos::kSystemTimezone);
+ non_default_values_.push_back(
+ base::MakeUnique<base::Value>("America/New_York"));
+ decorated_non_default_values.push_back(
+ non_default_values_.back()->CreateDeepCopy());
+
+ // Verify notifications when mandatory values are in effect.
+ chromeos::CrosSettings* cros_settings = chromeos::CrosSettings::Get();
+ cros_settings->Set(pref_names_[0], *non_default_values_[0]);
+
+ SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, decorated_non_default_values,
+ std::string(), false, false);
+}
+
+namespace {
+
+const char* kUserProfilePath = "user_profile";
+
+} // namespace
+
+class ProxyPreferencesBrowserTest : public PreferencesBrowserTest {
+ public:
+ void SetUpOnMainThread() override {
+ SetupNetworkEnvironment();
+ content::RunAllPendingInMessageLoop();
+
+ std::unique_ptr<base::DictionaryValue> proxy_config_dict(
+ ProxyConfigDictionary::CreateFixedServers("127.0.0.1:8080",
+ "*.google.com, 1.2.3.4:22"));
+
+ ProxyConfigDictionary proxy_config(std::move(proxy_config_dict));
+
+ const chromeos::NetworkState* network = GetDefaultNetwork();
+ ASSERT_TRUE(network);
+ chromeos::proxy_config::SetProxyConfigForNetwork(proxy_config, *network);
+
+ std::string url = base::StringPrintf("%s?network=%s",
+ chrome::kChromeUIProxySettingsURL,
+ network->guid().c_str());
+
+ ui_test_utils::NavigateToURL(browser(), GURL(url));
+ SetUpPrefs();
+ }
+
+ protected:
+ void SetupNetworkEnvironment() {
+ chromeos::ShillProfileClient::TestInterface* profile_test =
+ chromeos::DBusThreadManager::Get()->GetShillProfileClient()
+ ->GetTestInterface();
+ chromeos::ShillServiceClient::TestInterface* service_test =
+ chromeos::DBusThreadManager::Get()->GetShillServiceClient()
+ ->GetTestInterface();
+
+ profile_test->AddProfile(kUserProfilePath, "user");
+
+ service_test->ClearServices();
+ service_test->AddService("stub_ethernet",
+ "stub_ethernet_guid",
+ "eth0",
+ shill::kTypeEthernet,
+ shill::kStateOnline,
+ true /* add_to_visible */ );
+ service_test->SetServiceProperty("stub_ethernet", shill::kProfileProperty,
+ base::Value(kUserProfilePath));
+ profile_test->AddService(kUserProfilePath, "stub_wifi2");
+ }
+
+ void SetONCPolicy(const char* policy_name, policy::PolicyScope scope) {
+ std::string onc_policy =
+ "{ \"NetworkConfigurations\": ["
+ " { \"GUID\": \"stub_ethernet_guid\","
+ " \"Type\": \"Ethernet\","
+ " \"Name\": \"My Ethernet\","
+ " \"Ethernet\": {"
+ " \"Authentication\": \"None\" },"
+ " \"ProxySettings\": {"
+ " \"PAC\": \"http://domain.com/x\","
+ " \"Type\": \"PAC\" }"
+ " }"
+ " ],"
+ " \"Type\": \"UnencryptedConfiguration\""
+ "}";
+
+ policy::PolicyMap map;
+ map.Set(policy_name, policy::POLICY_LEVEL_MANDATORY, scope,
+ policy::POLICY_SOURCE_CLOUD,
+ base::MakeUnique<base::Value>(onc_policy), nullptr);
+ policy_provider_.UpdateChromePolicy(map);
+
+ content::RunAllPendingInMessageLoop();
+ }
+
+ const chromeos::NetworkState* GetDefaultNetwork() {
+ chromeos::NetworkStateHandler* handler =
+ chromeos::NetworkHandler::Get()->network_state_handler();
+ return handler->DefaultNetwork();
+ }
+
+ void SetProxyPref(const std::string& name, const base::Value& value) {
+ std::string type;
+ switch (value.GetType()) {
+ case base::Value::Type::BOOLEAN:
+ type = "Boolean";
+ break;
+ case base::Value::Type::INTEGER:
+ type = "Integer";
+ break;
+ case base::Value::Type::STRING:
+ type = "String";
+ break;
+ default:
+ ASSERT_TRUE(false);
+ }
+
+ std::string observed_json;
+ SetPref(name, type, value.CreateDeepCopy(), true, &observed_json);
+ }
+
+ void VerifyCurrentProxyServer(const std::string& expected_server,
+ onc::ONCSource expected_source) {
+ const chromeos::NetworkState* network = GetDefaultNetwork();
+ ASSERT_TRUE(network);
+ onc::ONCSource actual_source;
+ std::unique_ptr<ProxyConfigDictionary> proxy_dict =
+ chromeos::proxy_config::GetProxyConfigForNetwork(
+ g_browser_process->local_state(), pref_service(), *network,
+ &actual_source);
+ ASSERT_TRUE(proxy_dict);
+ std::string actual_proxy_server;
+ EXPECT_TRUE(proxy_dict->GetProxyServer(&actual_proxy_server));
+ EXPECT_EQ(expected_server, actual_proxy_server);
+ EXPECT_EQ(expected_source, actual_source);
+ }
+};
+
+// Verifies that proxy settings are correctly pushed to JavaScript during
+// initialization of the proxy settings page.
+IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, ChromeOSInitializeProxy) {
+ // Boolean pref.
+ pref_names_.push_back(chromeos::proxy_cros_settings_parser::kProxySingle);
+ non_default_values_.push_back(base::MakeUnique<base::Value>(true));
+
+ // Integer prefs.
+ pref_names_.push_back(
+ chromeos::proxy_cros_settings_parser::kProxySingleHttpPort);
+ non_default_values_.push_back(base::MakeUnique<base::Value>(8080));
+
+ // String pref.
+ pref_names_.push_back(chromeos::proxy_cros_settings_parser::kProxySingleHttp);
+ non_default_values_.push_back(base::MakeUnique<base::Value>("127.0.0.1"));
+
+ // List pref.
+ pref_names_.push_back(chromeos::proxy_cros_settings_parser::kProxyIgnoreList);
+ auto list = base::MakeUnique<base::ListValue>();
+ list->AppendString("*.google.com");
+ list->AppendString("1.2.3.4:22");
+ non_default_values_.push_back(std::move(list));
+
+ // Verify that no policy is presented to the UI. This must be verified on the
+ // kProxyType and the kUseSharedProxies prefs.
+ pref_names_.push_back(chromeos::proxy_cros_settings_parser::kProxyType);
+ non_default_values_.push_back(base::MakeUnique<base::Value>(2));
+
+ pref_names_.push_back(proxy_config::prefs::kUseSharedProxies);
+ non_default_values_.push_back(base::MakeUnique<base::Value>(false));
+
+ std::string observed_json;
+ SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, non_default_values_, "",
+ false, false);
+}
+
+IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, ONCPolicy) {
+ SetONCPolicy(policy::key::kOpenNetworkConfiguration,
+ policy::POLICY_SCOPE_USER);
+
+ // Verify that per-network policy is presented to the UI. This must be
+ // verified on the kProxyType.
+ pref_names_.push_back(chromeos::proxy_cros_settings_parser::kProxyType);
+ non_default_values_.push_back(base::MakeUnique<base::Value>(3));
+
+ std::string observed_json;
+ SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, non_default_values_, "policy",
+ true, false);
+
+ // Verify that 'use-shared-proxies' is not affected by per-network policy.
+ pref_names_.clear();
+ non_default_values_.clear();
+ pref_names_.push_back(proxy_config::prefs::kUseSharedProxies);
+ non_default_values_.push_back(base::MakeUnique<base::Value>(false));
+
+ SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, non_default_values_, "",
+ false, false);
+}
+
+IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, DeviceONCPolicy) {
+ SetONCPolicy(policy::key::kDeviceOpenNetworkConfiguration,
+ policy::POLICY_SCOPE_MACHINE);
+
+ // Verify that the policy is presented to the UI. This verification must be
+ // done on the kProxyType pref.
+ pref_names_.push_back(chromeos::proxy_cros_settings_parser::kProxyType);
+ non_default_values_.push_back(base::MakeUnique<base::Value>(3));
+
+ std::string observed_json;
+ SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, non_default_values_, "policy",
+ true, false);
+
+ // Verify that 'use-shared-proxies' is not affected by per-network policy.
+ pref_names_.clear();
+ non_default_values_.clear();
+ pref_names_.push_back(proxy_config::prefs::kUseSharedProxies);
+ non_default_values_.push_back(base::MakeUnique<base::Value>(false));
+
+ SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, non_default_values_, "",
+ false, false);
+}
+
+IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, UserProxyPolicy) {
+ policy_names_.push_back(policy::key::kProxyMode);
+ default_values_.push_back(
+ base::MakeUnique<base::Value>(ProxyPrefs::kAutoDetectProxyModeName));
+ SetUserPolicies(policy_names_, default_values_,
+ policy::POLICY_LEVEL_MANDATORY);
+ content::RunAllPendingInMessageLoop();
+
+ // Verify that the policy is presented to the UI. This verification must be
+ // done on the kProxyType pref.
+ pref_names_.push_back(chromeos::proxy_cros_settings_parser::kProxyType);
+ non_default_values_.push_back(base::MakeUnique<base::Value>(3));
+
+ // Verify that 'use-shared-proxies' is controlled by the policy.
+ pref_names_.push_back(proxy_config::prefs::kUseSharedProxies);
+ non_default_values_.push_back(base::MakeUnique<base::Value>(false));
+
+ std::string observed_json;
+ SetupJavaScriptTestEnvironment(pref_names_, &observed_json);
+ VerifyObservedPrefs(observed_json, pref_names_, non_default_values_, "policy",
+ true, false);
+}
+
+// Verifies that modifications to the proxy settings are correctly pushed from
+// JavaScript to the ProxyConfig property stored in the network configuration.
+IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, ChromeOSSetProxy) {
+ ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
+
+ SetProxyPref(chromeos::proxy_cros_settings_parser::kProxySingleHttpPort,
+ base::Value(123));
+ SetProxyPref(chromeos::proxy_cros_settings_parser::kProxySingleHttp,
+ base::Value("www.adomain.xy"));
+
+ VerifyCurrentProxyServer("www.adomain.xy:123",
+ onc::ONC_SOURCE_NONE);
+}
+
+// Verify that default proxy ports are used and that ports can be updated
+// without affecting the previously set hosts.
+IN_PROC_BROWSER_TEST_F(ProxyPreferencesBrowserTest, ChromeOSProxyDefaultPorts) {
+ ASSERT_NO_FATAL_FAILURE(SetupJavaScriptTestEnvironment(pref_names_, NULL));
+
+ // Set to manual, per scheme proxy.
+ SetProxyPref(chromeos::proxy_cros_settings_parser::kProxySingle,
+ base::Value(false));
+
+ // Set hosts but no ports.
+ SetProxyPref(chromeos::proxy_cros_settings_parser::kProxyHttpUrl,
+ base::Value("a.com"));
+ SetProxyPref(chromeos::proxy_cros_settings_parser::kProxyHttpsUrl,
+ base::Value("4.3.2.1"));
+ SetProxyPref(chromeos::proxy_cros_settings_parser::kProxyFtpUrl,
+ base::Value("c.com"));
+ SetProxyPref(chromeos::proxy_cros_settings_parser::kProxySocks,
+ base::Value("d.com"));
+
+ // Verify default ports.
+ VerifyCurrentProxyServer(
+ "http=a.com:80;https=4.3.2.1:80;ftp=c.com:80;socks=socks4://d.com:1080",
+ onc::ONC_SOURCE_NONE);
+
+ // Set and verify the ports.
+ SetProxyPref(chromeos::proxy_cros_settings_parser::kProxyHttpPort,
+ base::Value(1));
+ SetProxyPref(chromeos::proxy_cros_settings_parser::kProxyHttpsPort,
+ base::Value(2));
+ SetProxyPref(chromeos::proxy_cros_settings_parser::kProxyFtpPort,
+ base::Value(3));
+ SetProxyPref(chromeos::proxy_cros_settings_parser::kProxySocksPort,
+ base::Value(4));
+
+ VerifyCurrentProxyServer(
+ "http=a.com:1;https=4.3.2.1:2;ftp=c.com:3;socks=socks4://d.com:4",
+ onc::ONC_SOURCE_NONE);
+}
+
+#endif
diff --git a/chromium/chrome/browser/ui/webui/options/preferences_browsertest.h b/chromium/chrome/browser/ui/webui/options/preferences_browsertest.h
new file mode 100644
index 00000000000..d2136d36e8a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/preferences_browsertest.h
@@ -0,0 +1,196 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_PREFERENCES_BROWSERTEST_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_PREFERENCES_BROWSERTEST_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/notification_observer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace base {
+class DictionaryValue;
+class Value;
+}
+
+namespace content {
+class RenderViewHost;
+}
+
+// Tests verifying that the JavaScript Preferences class, the underlying C++
+// CoreOptionsHandler and the specialized classes handling Chrome OS device and
+// proxy prefs behave correctly.
+class PreferencesBrowserTest : public InProcessBrowserTest {
+ public:
+ PreferencesBrowserTest();
+ ~PreferencesBrowserTest() override;
+
+ // InProcessBrowserTest implementation:
+ void SetUpOnMainThread() override;
+ void TearDownOnMainThread() override;
+
+ void OnPreferenceChanged(const std::string& pref_name);
+
+ protected:
+ MOCK_METHOD1(OnCommit, void(const PrefService::Preference*));
+
+ // The pref service that holds the current pref values in the C++ backend.
+ PrefService* pref_service();
+
+ void SetUpPrefs();
+
+ // InProcessBrowserTest implementation:
+ void SetUpInProcessBrowserTestFixture() override;
+
+ // Sets user policies through the mock policy provider.
+ void SetUserPolicies(const std::vector<std::string>& names,
+ const std::vector<std::unique_ptr<base::Value>>& values,
+ policy::PolicyLevel level);
+ // Clears user policies.
+ void ClearUserPolicies();
+ // Set user-modified pref values directly in the C++ backend.
+ void SetUserValues(const std::vector<std::string>& names,
+ const std::vector<std::unique_ptr<base::Value>>& values);
+
+ // Verifies that a dictionary contains a (key, value) pair. Takes ownership of
+ // |expected|.
+ void VerifyKeyValue(const base::DictionaryValue& dict,
+ const std::string& key,
+ const base::Value& expected);
+ // Verifies that a dictionary contains a given pref and that its value has
+ // been decorated correctly.
+ void VerifyPref(const base::DictionaryValue* prefs,
+ const std::string& name,
+ const std::unique_ptr<base::Value>& value,
+ const std::string& controlledBy,
+ bool disabled,
+ bool uncommitted);
+ // Verifies that a notification received from the JavaScript Preferences
+ // class contains a given pref and that its value has been decorated
+ // correctly.
+ void VerifyObservedPref(const std::string& observed_json,
+ const std::string& name,
+ const std::unique_ptr<base::Value>& value,
+ const std::string& controlledBy,
+ bool disabled,
+ bool uncommitted);
+ // Verifies that notifications received from the JavaScript Preferences class
+ // contain the given prefs and that their values have been decorated
+ // correctly.
+ void VerifyObservedPrefs(
+ const std::string& observed_json,
+ const std::vector<std::string>& names,
+ const std::vector<std::unique_ptr<base::Value>>& values,
+ const std::string& controlledBy,
+ bool disabled,
+ bool uncommitted);
+
+ // Sets up the expectation that the JavaScript Preferences class will make no
+ // change to a user-modified pref value in the C++ backend.
+ void ExpectNoCommit(const std::string& name);
+ // Sets up the expectation that the JavaScript Preferences class will set a
+ // user-modified pref value in the C++ backend.
+ void ExpectSetCommit(const std::string& name,
+ const std::unique_ptr<base::Value>& value);
+ // Sets up the expectation that the JavaScript Preferences class will clear a
+ // user-modified pref value in the C++ backend.
+ void ExpectClearCommit(const std::string& name);
+ // Verifies that previously set expectations are met and clears them.
+ void VerifyAndClearExpectations();
+
+ // Sets up the JavaScript part of the test environment.
+ void SetupJavaScriptTestEnvironment(
+ const std::vector<std::string>& pref_names,
+ std::string* observed_json) const;
+
+ // Sets a value through the JavaScript Preferences class as if the user had
+ // modified it. Returns the observation which can be verified using the
+ // VerifyObserved* methods.
+ void SetPref(const std::string& name,
+ const std::string& type,
+ const std::unique_ptr<base::Value>& value,
+ bool commit,
+ std::string* observed_json);
+
+ // Verifies that setting a user-modified pref value through the JavaScript
+ // Preferences class fires the correct notification in JavaScript and commits
+ // the change to C++ if |commit| is true.
+ void VerifySetPref(const std::string& name,
+ const std::string& type,
+ const std::unique_ptr<base::Value>& value,
+ bool commit);
+ // Verifies that clearing a user-modified pref value through the JavaScript
+ // Preferences class fires the correct notification in JavaScript and does
+ // respectively does not cause the change to be committed to the C++ backend.
+ void VerifyClearPref(const std::string& name,
+ const std::unique_ptr<base::Value>& value,
+ bool commit);
+ // Verifies that committing a previously made change of a user-modified pref
+ // value through the JavaScript Preferences class fires the correct
+ // notification in JavaScript.
+ void VerifyCommit(const std::string& name,
+ const std::unique_ptr<base::Value>& value,
+ const std::string& controlledBy);
+ // Verifies that committing a previously set user-modified pref value through
+ // the JavaScript Preferences class fires the correct notification in
+ // JavaScript and causes the change to be committed to the C++ backend.
+ void VerifySetCommit(const std::string& name,
+ const std::unique_ptr<base::Value>& value);
+ // Verifies that committing the previously cleared user-modified pref value
+ // through the JavaScript Preferences class fires the correct notification in
+ // JavaScript and causes the change to be committed to the C++ backend.
+ void VerifyClearCommit(const std::string& name,
+ const std::unique_ptr<base::Value>& value);
+ // Verifies that rolling back a previously made change of a user-modified pref
+ // value through the JavaScript Preferences class fires the correct
+ // notification in JavaScript and does not cause the change to be committed to
+ // the C++ backend.
+ void VerifyRollback(const std::string& name,
+ const std::unique_ptr<base::Value>& value,
+ const std::string& controlledBy);
+ // Start observing notifications sent by the JavaScript Preferences class for
+ // pref values changes.
+ void StartObserving();
+ // Change the value of a sentinel pref in the C++ backend and finish observing
+ // notifications sent by the JavaScript Preferences class when the
+ // notification for this pref is received.
+ void FinishObserving(std::string* observed_json);
+
+ // Populate the lists of test prefs and corresponding policies with default
+ // values used by most tests.
+ void UseDefaultTestPrefs(bool includeListPref);
+
+ // The current tab's render view host, required to inject JavaScript code into
+ // the tab.
+ content::RenderViewHost* render_view_host_;
+
+ // Mock policy provider for both user and device policies.
+ policy::MockConfigurationPolicyProvider policy_provider_;
+
+ // Pref change registrar that detects changes to user-modified pref values
+ // made in the C++ backend by the JavaScript Preferences class.
+ std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
+
+ // The prefs and corresponding policies used by the current test.
+ std::vector<std::string> types_;
+ std::vector<std::string> pref_names_;
+ std::vector<std::string> policy_names_;
+ std::vector<std::unique_ptr<base::Value>> default_values_;
+ std::vector<std::unique_ptr<base::Value>> non_default_values_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PreferencesBrowserTest);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_PREFERENCES_BROWSERTEST_H_
diff --git a/chromium/chrome/browser/ui/webui/options/profile_settings_reset_browsertest.js b/chromium/chrome/browser/ui/webui/options/profile_settings_reset_browsertest.js
new file mode 100644
index 00000000000..2bd7a053aab
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/profile_settings_reset_browsertest.js
@@ -0,0 +1,38 @@
+// 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.
+
+GEN_INCLUDE(['options_browsertest_base.js']);
+
+/**
+ * TestFixture for profile settings reset WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function ProfileSettingsResetWebUITest() {}
+
+ProfileSettingsResetWebUITest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /**
+ * Browse to the reset profile settings page.
+ */
+ browsePreload: 'chrome://settings-frame/resetProfileSettings',
+
+ /** @override */
+ setUp: function() {
+ OptionsBrowsertestBase.prototype.setUp.call(this);
+
+ // Enable when failure is resolved.
+ // AX_TEXT_04: http://crbug.com/570551
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'linkWithUnclearPurpose',
+ '#reset-profile-settings-overlay > .action-area > .hbox.stretch > A');
+ },
+};
+
+// Test opening the profile settings reset has correct location.
+TEST_F('ProfileSettingsResetWebUITest', 'testOpenProfileSettingsReset',
+ function() {
+ assertEquals(this.browsePreload, document.location.href);
+ });
diff --git a/chromium/chrome/browser/ui/webui/options/reset_profile_settings_handler.cc b/chromium/chrome/browser/ui/webui/options/reset_profile_settings_handler.cc
new file mode 100644
index 00000000000..bc254c3631b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/reset_profile_settings_handler.cc
@@ -0,0 +1,269 @@
+// 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/options/reset_profile_settings_handler.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/string16.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/google/google_brand.h"
+#include "chrome/browser/profile_resetter/brandcode_config_fetcher.h"
+#include "chrome/browser/profile_resetter/brandcoded_default_settings.h"
+#include "chrome/browser/profile_resetter/profile_resetter.h"
+#include "chrome/browser/profile_resetter/resettable_settings_snapshot.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if defined(OS_WIN)
+#include "chrome/browser/profile_resetter/triggered_profile_resetter.h"
+#include "chrome/browser/profile_resetter/triggered_profile_resetter_factory.h"
+#endif // defined(OS_WIN)
+
+namespace options {
+
+namespace {
+
+reset_report::ChromeResetReport::ResetRequestOrigin
+ResetRequestOriginFromString(const std::string& reset_request_origin) {
+ static const char kOriginUserClick[] = "userclick";
+ static const char kOriginTriggeredReset[] = "triggeredreset";
+
+ if (reset_request_origin ==
+ ResetProfileSettingsHandler::kCctResetSettingsHash) {
+ return reset_report::ChromeResetReport::RESET_REQUEST_ORIGIN_CCT;
+ }
+ if (reset_request_origin == kOriginUserClick)
+ return reset_report::ChromeResetReport::RESET_REQUEST_ORIGIN_USER_CLICK;
+ if (reset_request_origin == kOriginTriggeredReset) {
+ return reset_report::ChromeResetReport::
+ RESET_REQUEST_ORIGIN_TRIGGERED_RESET;
+ }
+ if (!reset_request_origin.empty())
+ NOTREACHED();
+
+ return reset_report::ChromeResetReport::RESET_REQUEST_ORIGIN_UNKNOWN;
+}
+
+} // namespace
+
+const char ResetProfileSettingsHandler::kCctResetSettingsHash[] = "cct";
+
+ResetProfileSettingsHandler::ResetProfileSettingsHandler() {
+ google_brand::GetBrand(&brandcode_);
+}
+
+ResetProfileSettingsHandler::~ResetProfileSettingsHandler() {}
+
+void ResetProfileSettingsHandler::InitializeHandler() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ resetter_.reset(new ProfileResetter(profile));
+}
+
+void ResetProfileSettingsHandler::InitializePage() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ResetProfileSettingsOverlay.setResettingState",
+ base::Value(resetter_->IsActive()));
+}
+
+void ResetProfileSettingsHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ static OptionsStringResource resources[] = {
+ { "resetProfileSettingsCommit", IDS_RESET_PROFILE_SETTINGS_COMMIT_BUTTON },
+ { "resetProfileSettingsExplanation",
+ IDS_RESET_PROFILE_SETTINGS_EXPLANATION },
+ { "resetProfileSettingsFeedback", IDS_RESET_PROFILE_SETTINGS_FEEDBACK }
+ };
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+ RegisterTitle(localized_strings, "resetProfileSettingsOverlay",
+ IDS_RESET_PROFILE_SETTINGS_TITLE);
+ localized_strings->SetString(
+ "resetProfileSettingsLearnMoreUrl",
+ chrome::kResetProfileSettingsLearnMoreURL);
+
+ // Set up the localized strings for the triggered profile reset overlay.
+ // The reset tool name can currently only have a custom value on Windows.
+ base::string16 reset_tool_name;
+#if defined(OS_WIN)
+ Profile* profile = Profile::FromWebUI(web_ui());
+ TriggeredProfileResetter* triggered_profile_resetter =
+ TriggeredProfileResetterFactory::GetForBrowserContext(profile);
+ // TriggeredProfileResetter instance will be nullptr for incognito profiles.
+ if (triggered_profile_resetter) {
+ reset_tool_name = triggered_profile_resetter->GetResetToolName();
+
+ // Now that a reset UI has been shown, don't trigger again for this profile.
+ triggered_profile_resetter->ClearResetTrigger();
+ }
+#endif
+
+ if (reset_tool_name.empty()) {
+ reset_tool_name = l10n_util::GetStringUTF16(
+ IDS_TRIGGERED_RESET_PROFILE_SETTINGS_DEFAULT_TOOL_NAME);
+ }
+ localized_strings->SetString(
+ "triggeredResetProfileSettingsOverlay",
+ l10n_util::GetStringFUTF16(IDS_TRIGGERED_RESET_PROFILE_SETTINGS_TITLE,
+ reset_tool_name));
+ // Set the title manually since RegisterTitle() wants an id.
+ base::string16 title_string(l10n_util::GetStringFUTF16(
+ IDS_TRIGGERED_RESET_PROFILE_SETTINGS_TITLE, reset_tool_name));
+ localized_strings->SetString("triggeredResetProfileSettingsOverlay",
+ title_string);
+ localized_strings->SetString(
+ "triggeredResetProfileSettingsOverlayTabTitle",
+ l10n_util::GetStringFUTF16(IDS_OPTIONS_TAB_TITLE,
+ l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE),
+ title_string));
+ localized_strings->SetString(
+ "triggeredResetProfileSettingsExplanation",
+ l10n_util::GetStringFUTF16(
+ IDS_TRIGGERED_RESET_PROFILE_SETTINGS_EXPLANATION, reset_tool_name));
+}
+
+void ResetProfileSettingsHandler::RegisterMessages() {
+ // Setup handlers specific to this panel.
+ web_ui()->RegisterMessageCallback("performResetProfileSettings",
+ base::Bind(&ResetProfileSettingsHandler::HandleResetProfileSettings,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("onShowResetProfileDialog",
+ base::Bind(&ResetProfileSettingsHandler::OnShowResetProfileDialog,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("onHideResetProfileDialog",
+ base::Bind(&ResetProfileSettingsHandler::OnHideResetProfileDialog,
+ base::Unretained(this)));
+}
+
+void ResetProfileSettingsHandler::HandleResetProfileSettings(
+ const base::ListValue* value) {
+ bool send_settings = false;
+ std::string request_origin_string;
+ bool success = value->GetBoolean(0, &send_settings) &&
+ value->GetString(1, &request_origin_string);
+ DCHECK(success);
+
+ DCHECK(brandcode_.empty() || config_fetcher_);
+ reset_report::ChromeResetReport::ResetRequestOrigin request_origin =
+ ResetRequestOriginFromString(request_origin_string);
+ if (config_fetcher_ && config_fetcher_->IsActive()) {
+ // Reset once the prefs are fetched.
+ config_fetcher_->SetCallback(
+ base::Bind(&ResetProfileSettingsHandler::ResetProfile, Unretained(this),
+ send_settings, request_origin));
+ } else {
+ ResetProfile(send_settings, request_origin);
+ }
+}
+
+void ResetProfileSettingsHandler::OnResetProfileSettingsDone(
+ bool send_feedback,
+ reset_report::ChromeResetReport::ResetRequestOrigin request_origin) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ResetProfileSettingsOverlay.doneResetting");
+
+ if (send_feedback && setting_snapshot_) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ ResettableSettingsSnapshot current_snapshot(profile);
+ int difference = setting_snapshot_->FindDifferentFields(current_snapshot);
+ if (difference) {
+ setting_snapshot_->Subtract(current_snapshot);
+ std::unique_ptr<reset_report::ChromeResetReport> report_proto =
+ SerializeSettingsReportToProto(*setting_snapshot_, difference);
+ if (report_proto) {
+ report_proto->set_reset_request_origin(request_origin);
+ SendSettingsFeedbackProto(*report_proto, profile);
+ }
+ }
+ }
+ setting_snapshot_.reset();
+}
+
+void ResetProfileSettingsHandler::OnShowResetProfileDialog(
+ const base::ListValue* value) {
+ if (!resetter_->IsActive()) {
+ setting_snapshot_.reset(
+ new ResettableSettingsSnapshot(Profile::FromWebUI(web_ui())));
+ setting_snapshot_->RequestShortcuts(base::Bind(
+ &ResetProfileSettingsHandler::UpdateFeedbackUI, AsWeakPtr()));
+ UpdateFeedbackUI();
+ }
+
+ if (brandcode_.empty())
+ return;
+ config_fetcher_.reset(new BrandcodeConfigFetcher(
+ base::Bind(&ResetProfileSettingsHandler::OnSettingsFetched,
+ Unretained(this)),
+ GURL("https://tools.google.com/service/update2"),
+ brandcode_));
+}
+
+void ResetProfileSettingsHandler::OnHideResetProfileDialog(
+ const base::ListValue* value) {
+ if (!resetter_->IsActive())
+ setting_snapshot_.reset();
+}
+
+void ResetProfileSettingsHandler::OnSettingsFetched() {
+ DCHECK(config_fetcher_);
+ DCHECK(!config_fetcher_->IsActive());
+ // The master prefs is fetched. We are waiting for user pressing 'Reset'.
+}
+
+void ResetProfileSettingsHandler::ResetProfile(
+ bool send_settings,
+ reset_report::ChromeResetReport::ResetRequestOrigin request_origin) {
+ DCHECK(resetter_);
+ DCHECK(!resetter_->IsActive());
+
+ std::unique_ptr<BrandcodedDefaultSettings> default_settings;
+ if (config_fetcher_) {
+ DCHECK(!config_fetcher_->IsActive());
+ default_settings = config_fetcher_->GetSettings();
+ config_fetcher_.reset();
+ } else {
+ DCHECK(brandcode_.empty());
+ }
+
+ // If failed to fetch BrandcodedDefaultSettings or this is an organic
+ // installation, use default settings.
+ if (!default_settings)
+ default_settings.reset(new BrandcodedDefaultSettings);
+ resetter_->Reset(
+ ProfileResetter::ALL, std::move(default_settings),
+ base::Bind(&ResetProfileSettingsHandler::OnResetProfileSettingsDone,
+ AsWeakPtr(), send_settings, request_origin));
+ base::RecordAction(base::UserMetricsAction("ResetProfile"));
+ UMA_HISTOGRAM_BOOLEAN("ProfileReset.SendFeedback", send_settings);
+ UMA_HISTOGRAM_ENUMERATION(
+ "ProfileReset.ResetRequestOrigin", request_origin,
+ reset_report::ChromeResetReport::ResetRequestOrigin_MAX + 1);
+}
+
+void ResetProfileSettingsHandler::UpdateFeedbackUI() {
+ if (!setting_snapshot_)
+ return;
+ std::unique_ptr<base::ListValue> list = GetReadableFeedbackForSnapshot(
+ Profile::FromWebUI(web_ui()), *setting_snapshot_);
+ base::DictionaryValue feedback_info;
+ feedback_info.Set("feedbackInfo", std::move(list));
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ResetProfileSettingsOverlay.setFeedbackInfo", feedback_info);
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/reset_profile_settings_handler.h b/chromium/chrome/browser/ui/webui/options/reset_profile_settings_handler.h
new file mode 100644
index 00000000000..d0e3405bf64
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/reset_profile_settings_handler.h
@@ -0,0 +1,91 @@
+// 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_OPTIONS_RESET_PROFILE_SETTINGS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_RESET_PROFILE_SETTINGS_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/profile_resetter/profile_reset_report.pb.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+} // namespace base
+
+class BrandcodeConfigFetcher;
+class ProfileResetter;
+class ResettableSettingsSnapshot;
+
+namespace options {
+
+// Handler for both the 'Reset Profile Settings' overlay page and also the
+// corresponding banner that is shown at the top of the options page.
+class ResetProfileSettingsHandler
+ : public OptionsPageUIHandler,
+ public base::SupportsWeakPtr<ResetProfileSettingsHandler> {
+ public:
+ // Hash used by the Chrome Cleanup Tool when launching chrome with the reset
+ // profile settings URL.
+ static const char kCctResetSettingsHash[];
+
+ ResetProfileSettingsHandler();
+ ~ResetProfileSettingsHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializeHandler() override;
+ void InitializePage() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ // Javascript callback to start clearing data.
+ void HandleResetProfileSettings(const base::ListValue* value);
+
+ // Closes the dialog once all requested settings has been reset.
+ void OnResetProfileSettingsDone(
+ bool send_feedback,
+ reset_report::ChromeResetReport::ResetRequestOrigin request_origin);
+
+ // Called when the confirmation box appears.
+ void OnShowResetProfileDialog(const base::ListValue* value);
+
+ // Called when the confirmation box disappears.
+ void OnHideResetProfileDialog(const base::ListValue* value);
+
+ // Called when BrandcodeConfigFetcher completed fetching settings.
+ void OnSettingsFetched();
+
+ // Resets profile settings to default values. |send_settings| is true if user
+ // gave their consent to upload broken settings to Google for analysis.
+ void ResetProfile(
+ bool send_settings,
+ reset_report::ChromeResetReport::ResetRequestOrigin request_origin);
+
+ // Sets new values for the feedback area.
+ void UpdateFeedbackUI();
+
+ std::unique_ptr<ProfileResetter> resetter_;
+
+ std::unique_ptr<BrandcodeConfigFetcher> config_fetcher_;
+
+ // Snapshot of settings before profile was reseted.
+ std::unique_ptr<ResettableSettingsSnapshot> setting_snapshot_;
+
+ // Contains Chrome brand code; empty for organic Chrome.
+ std::string brandcode_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResetProfileSettingsHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_RESET_PROFILE_SETTINGS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/search_engine_manager_browsertest.js b/chromium/chrome/browser/ui/webui/options/search_engine_manager_browsertest.js
new file mode 100644
index 00000000000..88b4a27c2f9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/search_engine_manager_browsertest.js
@@ -0,0 +1,26 @@
+// 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.
+
+/**
+ * TestFixture for search engine manager WebUI testing.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function SearchEngineManagerWebUITest() {}
+
+SearchEngineManagerWebUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /**
+ * Browse to the search engine manager.
+ */
+ browsePreload: 'chrome://settings-frame/searchEngines',
+};
+
+// Disabled due to flaky timeouts; see crbug.com/205693 .
+// Test opening the search engine manager has correct location.
+TEST_F('SearchEngineManagerWebUITest', 'DISABLED_testOpenSearchEngineManager',
+ function() {
+ assertEquals(this.browsePreload, document.location.href);
+ });
diff --git a/chromium/chrome/browser/ui/webui/options/search_engine_manager_handler.cc b/chromium/chrome/browser/ui/webui/options/search_engine_manager_handler.cc
new file mode 100644
index 00000000000..a9d43ebca26
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/search_engine_manager_handler.cc
@@ -0,0 +1,329 @@
+// 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/ui/webui/options/search_engine_manager_handler.h"
+
+#include "base/bind.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
+#include "chrome/browser/ui/search_engines/keyword_editor_controller.h"
+#include "chrome/browser/ui/search_engines/template_url_table_model.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/search_engines/template_url.h"
+#include "components/search_engines/template_url_service.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+enum EngineInfoIndexes {
+ ENGINE_NAME,
+ ENGINE_KEYWORD,
+ ENGINE_URL,
+};
+
+}; // namespace
+
+namespace options {
+
+SearchEngineManagerHandler::SearchEngineManagerHandler() {
+}
+
+SearchEngineManagerHandler::~SearchEngineManagerHandler() {
+ if (list_controller_.get() && list_controller_->table_model())
+ list_controller_->table_model()->SetObserver(NULL);
+}
+
+void SearchEngineManagerHandler::InitializeHandler() {
+ list_controller_.reset(
+ new KeywordEditorController(Profile::FromWebUI(web_ui())));
+ DCHECK(list_controller_.get());
+ list_controller_->table_model()->SetObserver(this);
+}
+
+void SearchEngineManagerHandler::InitializePage() {
+ OnModelChanged();
+}
+
+void SearchEngineManagerHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ RegisterTitle(localized_strings, "searchEngineManagerPage",
+ IDS_SEARCH_ENGINES_EDITOR_WINDOW_TITLE);
+ localized_strings->SetString("defaultSearchEngineListTitle",
+ l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_MAIN_SEPARATOR));
+ localized_strings->SetString("otherSearchEngineListTitle",
+ l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_OTHER_SEPARATOR));
+ localized_strings->SetString("extensionKeywordsListTitle",
+ l10n_util::GetStringUTF16(
+ IDS_SEARCH_ENGINES_EDITOR_EXTENSIONS_SEPARATOR));
+ localized_strings->SetString("makeDefaultSearchEngineButton",
+ l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_MAKE_DEFAULT_BUTTON));
+ localized_strings->SetString("searchEngineTableNamePlaceholder",
+ l10n_util::GetStringUTF16(IDS_SEARCH_ENGINE_ADD_NEW_NAME_PLACEHOLDER));
+ localized_strings->SetString("searchEngineTableKeywordPlaceholder",
+ l10n_util::GetStringUTF16(IDS_SEARCH_ENGINE_ADD_NEW_KEYWORD_PLACEHOLDER));
+ localized_strings->SetString("searchEngineTableURLPlaceholder",
+ l10n_util::GetStringUTF16(IDS_SEARCH_ENGINE_ADD_NEW_URL_PLACEHOLDER));
+ localized_strings->SetString("editSearchEngineInvalidTitleToolTip",
+ l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_INVALID_TITLE_TT));
+ localized_strings->SetString("editSearchEngineInvalidKeywordToolTip",
+ l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_INVALID_KEYWORD_TT));
+ localized_strings->SetString("editSearchEngineInvalidURLToolTip",
+ l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_INVALID_URL_TT));
+}
+
+void SearchEngineManagerHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "managerSetDefaultSearchEngine",
+ base::Bind(&SearchEngineManagerHandler::SetDefaultSearchEngine,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "removeSearchEngine",
+ base::Bind(&SearchEngineManagerHandler::RemoveSearchEngine,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "editSearchEngine",
+ base::Bind(&SearchEngineManagerHandler::EditSearchEngine,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "checkSearchEngineInfoValidity",
+ base::Bind(&SearchEngineManagerHandler::CheckSearchEngineInfoValidity,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "searchEngineEditCancelled",
+ base::Bind(&SearchEngineManagerHandler::EditCancelled,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "searchEngineEditCompleted",
+ base::Bind(&SearchEngineManagerHandler::EditCompleted,
+ base::Unretained(this)));
+}
+
+void SearchEngineManagerHandler::OnModelChanged() {
+ DCHECK(list_controller_.get());
+ if (!list_controller_->loaded())
+ return;
+
+ // Find the default engine.
+ const TemplateURL* default_engine =
+ list_controller_->GetDefaultSearchProvider();
+ int default_index = list_controller_->table_model()->IndexOfTemplateURL(
+ default_engine);
+
+ // Build the first list (default search engine options).
+ base::ListValue defaults_list;
+ int last_default_engine_index =
+ list_controller_->table_model()->last_search_engine_index();
+ for (int i = 0; i < last_default_engine_index; ++i) {
+ // Third argument is false, as the engine is not from an extension.
+ defaults_list.Append(CreateDictionaryForEngine(
+ i, i == default_index));
+ }
+
+ // Build the second list (other search templates).
+ base::ListValue others_list;
+ int last_other_engine_index =
+ list_controller_->table_model()->last_other_engine_index();
+ if (last_default_engine_index < 0)
+ last_default_engine_index = 0;
+ for (int i = last_default_engine_index; i < last_other_engine_index; ++i) {
+ others_list.Append(CreateDictionaryForEngine(i, i == default_index));
+ }
+
+ // Build the extension keywords list.
+ base::ListValue keyword_list;
+ if (last_other_engine_index < 0)
+ last_other_engine_index = 0;
+ int engine_count = list_controller_->table_model()->RowCount();
+ for (int i = last_other_engine_index; i < engine_count; ++i) {
+ keyword_list.Append(CreateDictionaryForEngine(i, i == default_index));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "SearchEngineManager.updateSearchEngineList", defaults_list, others_list,
+ keyword_list);
+}
+
+void SearchEngineManagerHandler::OnItemsChanged(int start, int length) {
+ OnModelChanged();
+}
+
+void SearchEngineManagerHandler::OnItemsAdded(int start, int length) {
+ OnModelChanged();
+}
+
+void SearchEngineManagerHandler::OnItemsRemoved(int start, int length) {
+ OnModelChanged();
+}
+
+std::unique_ptr<base::DictionaryValue>
+SearchEngineManagerHandler::CreateDictionaryForEngine(int index,
+ bool is_default) {
+ TemplateURLTableModel* table_model = list_controller_->table_model();
+ const TemplateURL* template_url = list_controller_->GetTemplateURL(index);
+
+ // The items which are to be written into |dict| are also described in
+ // chrome/browser/resources/options/search_engine_manager_engine_list.js
+ // in @typedef for SearchEngine. Please update it whenever you add or remove
+ // any keys here.
+ std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+ dict->SetString("name", template_url->short_name());
+ dict->SetString("displayName", table_model->GetText(
+ index, IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_COLUMN));
+ dict->SetString("keyword", table_model->GetText(
+ index, IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN));
+ dict->SetString("url", template_url->url_ref().DisplayURL(
+ UIThreadSearchTermsData(Profile::FromWebUI(web_ui()))));
+ dict->SetBoolean("urlLocked", template_url->prepopulate_id() > 0);
+ GURL icon_url = template_url->favicon_url();
+ if (icon_url.is_valid())
+ dict->SetString("iconURL", icon_url.spec());
+ dict->SetString("modelIndex", base::IntToString(index));
+
+ dict->SetBoolean("canBeRemoved",
+ list_controller_->CanRemove(template_url));
+ dict->SetBoolean("canBeDefault",
+ list_controller_->CanMakeDefault(template_url));
+ dict->SetBoolean("default", is_default);
+ dict->SetBoolean("canBeEdited", list_controller_->CanEdit(template_url));
+ TemplateURL::Type type = template_url->type();
+ dict->SetBoolean("isOmniboxExtension",
+ type == TemplateURL::OMNIBOX_API_EXTENSION);
+ if (type == TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION ||
+ type == TemplateURL::OMNIBOX_API_EXTENSION) {
+ const extensions::Extension* extension =
+ extensions::ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))
+ ->GetExtensionById(template_url->GetExtensionId(),
+ extensions::ExtensionRegistry::EVERYTHING);
+ if (extension) {
+ dict->Set("extension", extensions::util::GetExtensionInfo(extension));
+ }
+ }
+ return dict;
+}
+
+void SearchEngineManagerHandler::SetDefaultSearchEngine(
+ const base::ListValue* args) {
+ int index;
+ if (!ExtractIntegerValue(args, &index)) {
+ NOTREACHED();
+ return;
+ }
+ if (index < 0 || index >= list_controller_->table_model()->RowCount())
+ return;
+
+ list_controller_->MakeDefaultTemplateURL(index);
+
+ base::RecordAction(base::UserMetricsAction("Options_SearchEngineSetDefault"));
+}
+
+void SearchEngineManagerHandler::RemoveSearchEngine(
+ const base::ListValue* args) {
+ int index;
+ if (!ExtractIntegerValue(args, &index)) {
+ NOTREACHED();
+ return;
+ }
+ if (index < 0 || index >= list_controller_->table_model()->RowCount())
+ return;
+
+ if (list_controller_->CanRemove(list_controller_->GetTemplateURL(index))) {
+ list_controller_->RemoveTemplateURL(index);
+ base::RecordAction(base::UserMetricsAction("Options_SearchEngineRemoved"));
+ }
+}
+
+void SearchEngineManagerHandler::EditSearchEngine(const base::ListValue* args) {
+ int index;
+ if (!ExtractIntegerValue(args, &index)) {
+ NOTREACHED();
+ return;
+ }
+
+ // Allow -1, which means we are adding a new engine.
+ if (index < -1 || index >= list_controller_->table_model()->RowCount())
+ return;
+
+ edit_controller_.reset(new EditSearchEngineController(
+ (index == -1) ? NULL : list_controller_->GetTemplateURL(index), this,
+ Profile::FromWebUI(web_ui())));
+}
+
+void SearchEngineManagerHandler::OnEditedKeyword(
+ TemplateURL* template_url,
+ const base::string16& title,
+ const base::string16& keyword,
+ const std::string& url) {
+ DCHECK(!url.empty());
+ if (template_url)
+ list_controller_->ModifyTemplateURL(template_url, title, keyword, url);
+ else
+ list_controller_->AddTemplateURL(title, keyword, url);
+ edit_controller_.reset();
+}
+
+void SearchEngineManagerHandler::CheckSearchEngineInfoValidity(
+ const base::ListValue* args) {
+ if (!edit_controller_.get())
+ return;
+ base::string16 name;
+ base::string16 keyword;
+ std::string url;
+ std::string modelIndex;
+ if (!args->GetString(ENGINE_NAME, &name) ||
+ !args->GetString(ENGINE_KEYWORD, &keyword) ||
+ !args->GetString(ENGINE_URL, &url) ||
+ !args->GetString(3, &modelIndex)) {
+ NOTREACHED();
+ return;
+ }
+
+ base::DictionaryValue validity;
+ validity.SetBoolean("name", edit_controller_->IsTitleValid(name));
+ validity.SetBoolean("keyword", edit_controller_->IsKeywordValid(keyword));
+ validity.SetBoolean("url", edit_controller_->IsURLValid(url));
+ base::Value indexValue(modelIndex);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "SearchEngineManager.validityCheckCallback", validity, indexValue);
+}
+
+void SearchEngineManagerHandler::EditCancelled(const base::ListValue* args) {
+ if (!edit_controller_.get())
+ return;
+ edit_controller_->CleanUpCancelledAdd();
+ edit_controller_.reset();
+}
+
+void SearchEngineManagerHandler::EditCompleted(const base::ListValue* args) {
+ if (!edit_controller_.get())
+ return;
+ base::string16 name;
+ base::string16 keyword;
+ std::string url;
+ if (!args->GetString(ENGINE_NAME, &name) ||
+ !args->GetString(ENGINE_KEYWORD, &keyword) ||
+ !args->GetString(ENGINE_URL, &url)) {
+ NOTREACHED();
+ return;
+ }
+
+ // Recheck validity. It's possible to get here with invalid input if e.g. the
+ // user calls the right JS functions directly from the web inspector.
+ if (edit_controller_->IsTitleValid(name) &&
+ edit_controller_->IsKeywordValid(keyword) &&
+ edit_controller_->IsURLValid(url))
+ edit_controller_->AcceptAddOrEdit(name, keyword, url);
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/search_engine_manager_handler.h b/chromium/chrome/browser/ui/webui/options/search_engine_manager_handler.h
new file mode 100644
index 00000000000..9de72bfac3c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/search_engine_manager_handler.h
@@ -0,0 +1,90 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_SEARCH_ENGINE_MANAGER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_SEARCH_ENGINE_MANAGER_HANDLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/search_engines/edit_search_engine_controller.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "ui/base/models/table_model_observer.h"
+
+class KeywordEditorController;
+
+namespace extensions {
+class Extension;
+}
+
+namespace options {
+
+class SearchEngineManagerHandler : public OptionsPageUIHandler,
+ public ui::TableModelObserver,
+ public EditSearchEngineControllerDelegate {
+ public:
+ SearchEngineManagerHandler();
+ ~SearchEngineManagerHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializeHandler() override;
+ void InitializePage() override;
+
+ // ui::TableModelObserver implementation.
+ void OnModelChanged() override;
+ void OnItemsChanged(int start, int length) override;
+ void OnItemsAdded(int start, int length) override;
+ void OnItemsRemoved(int start, int length) override;
+
+ // EditSearchEngineControllerDelegate implementation.
+ void OnEditedKeyword(TemplateURL* template_url,
+ const base::string16& title,
+ const base::string16& keyword,
+ const std::string& url) override;
+
+ void RegisterMessages() override;
+
+ private:
+ std::unique_ptr<KeywordEditorController> list_controller_;
+ std::unique_ptr<EditSearchEngineController> edit_controller_;
+
+ // Removes the search engine at the given index. Called from WebUI.
+ void RemoveSearchEngine(const base::ListValue* args);
+
+ // Sets the search engine at the given index to be default. Called from WebUI.
+ void SetDefaultSearchEngine(const base::ListValue* args);
+
+ // Starts an edit session for the search engine at the given index. If the
+ // index is -1, starts editing a new search engine instead of an existing one.
+ // Called from WebUI.
+ void EditSearchEngine(const base::ListValue* args);
+
+ // Validates the given search engine values, and reports the results back
+ // to WebUI. Called from WebUI.
+ void CheckSearchEngineInfoValidity(const base::ListValue* args);
+
+ // Called when an edit is cancelled.
+ // Called from WebUI.
+ void EditCancelled(const base::ListValue* args);
+
+ // Called when an edit is finished and should be saved.
+ // Called from WebUI.
+ void EditCompleted(const base::ListValue* args);
+
+ // Returns a dictionary to pass to WebUI representing the given search engine.
+ std::unique_ptr<base::DictionaryValue> CreateDictionaryForEngine(
+ int index,
+ bool is_default);
+
+ // Returns a dictionary to pass to WebUI representing the extension.
+ base::DictionaryValue* CreateDictionaryForExtension(
+ const extensions::Extension& extension);
+
+ DISALLOW_COPY_AND_ASSIGN(SearchEngineManagerHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_SEARCH_ENGINE_MANAGER_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/settings_format_browsertest.js b/chromium/chrome/browser/ui/webui/options/settings_format_browsertest.js
new file mode 100644
index 00000000000..a84873fede6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/settings_format_browsertest.js
@@ -0,0 +1,176 @@
+// 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.
+
+GEN_INCLUDE(['options_browsertest_base.js']);
+
+/**
+ * TestFixture for testing the formatting of settings pages.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function SettingsFormatWebUITest() {}
+
+/**
+ * Map of rule exemptions grouped by test.
+ * @const
+ */
+SettingsFormatWebUITest.Filters = {
+ /**
+ * Exemption for checkboxes that do not require an id or pref property.
+ * Input methods use inputMethodId instead of id for unique identification.
+ */
+ 'pref': ['language-options-input-method-template',
+ 'language-options-input-method-list']
+};
+
+/**
+ * Collection of error messages.
+ * @const
+ */
+SettingsFormatWebUITest.Messages = {
+ MISSING_CHECK_WRAPPER: 'Element $1 should be enclosed in <div class="$2">',
+ MISSING_ID_OR_PREF: 'Missing id or pref preoperty for checkbox $1.',
+ MISSING_RADIO_BUTTON_NAME: 'Radio button $1 is missing the name property',
+ MISSING_RADIO_BUTTON_VALUE: 'Radio button $1 is missing the value property',
+};
+
+SettingsFormatWebUITest.prototype = {
+ __proto__: OptionsBrowsertestBase.prototype,
+
+ /**
+ * Navigate to browser settings.
+ */
+ browsePreload: 'chrome://settings-frame/',
+
+ /**
+ * List of errors generated during a test. Used instead of expect* functions
+ * to suppress verbosity. The implementation of errorsToMessage in the
+ * testing API generates a call stack for each error produced which greatly
+ * reduces readability.
+ * @type {Array<string>}
+ */
+ errors: null,
+
+ /** @override */
+ setUp: function() {
+ OptionsBrowsertestBase.prototype.setUp.call(this);
+
+ this.errors = [];
+
+ // Enable when failure is resolved.
+ // AX_TEXT_04: http://crbug.com/570727
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'linkWithUnclearPurpose',
+ '#sync-overview > A');
+
+ // Enable when failure is resolved.
+ // AX_ARIA_10: http://crbug.com/570725
+ this.accessibilityAuditConfig.ignoreSelectors(
+ 'unsupportedAriaAttribute',
+ '#profiles-list');
+ },
+
+ tearDown: function() {
+ assertTrue(this.errors.length == 0, '\n' + this.errors.join('\n'));
+ },
+
+ /**
+ * Generates a failure message. During tear down of the test, the accumulation
+ * of pending messages triggers a test failure.
+ * @param {string} key Label of the message formatting string.
+ * @param {!Element} element The element that triggered the failure.
+ * @param {...string} args Additional arguments for formatting the message.
+ */
+ fail: function(key, element, args) {
+ var subs = [this.getLabel(element)].concat(
+ Array.prototype.slice.call(arguments, 2));
+ var message = SettingsFormatWebUITest.Messages[key].replace(
+ /\$\d/g,
+ function(m) {
+ return subs[m[1] - 1] || '$' + m[1];
+ });
+ assertFalse(/\$\d/.test(message), 'found unreplaced subs');
+ this.errors.push(message);
+ },
+
+ /**
+ * String for identifying a node within an error message.
+ * @param {!Element} element The target element to identify.
+ * @return {string} Name to facilitate tracking down the element.
+ */
+ getLabel: function(element) {
+ if (element.id)
+ return element.id;
+
+ if (element.pref)
+ return element.pref;
+
+ if (element.name && element.value)
+ return element.name + '-' + element.value;
+
+ return this.getLabel(element.parentNode);
+ },
+
+
+ /**
+ * Checks if the node is exempt from following the formatting rule.
+ * @param {!Element} element The candidate element.
+ * @param {Array<string>} filters List of exemptions.
+ * @return {boolean} True if the element is exempt.
+ */
+ isExempt: function(element, filters) {
+ var target = this.getLabel(element);
+ for (var i = 0; i < filters.length; i++) {
+ if (filters[i] == target)
+ return true;
+ }
+ return false;
+ }
+};
+
+/**
+ * Ensure that radio and checkbox buttons have consistent layout.
+ */
+TEST_F('SettingsFormatWebUITest', 'RadioCheckboxStyleCheck', function() {
+ var settings = $('settings');
+ assertTrue(settings != null, 'Unable to access settings');
+ var query = 'input[type=checkbox], input[type=radio]';
+ var elements = document.querySelectorAll(query);
+ assertTrue(elements.length > 0);
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ if (!findAncestorByClass(element, element.type))
+ this.fail('MISSING_CHECK_WRAPPER', element, element.type);
+ }
+});
+
+/**
+ * Each checkbox requires an id or pref property.
+ */
+// Flaky crashes on all platforms; http://crbug.com/613034.
+TEST_F('SettingsFormatWebUITest', 'DISABLED_CheckboxIdOrPrefCheck', function() {
+ var query =
+ 'input[type=checkbox]:not([pref]):not([id]):not(.spacer-checkbox)';
+ var elements = document.querySelectorAll(query);
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ if (!this.isExempt(element, SettingsFormatWebUITest.Filters['pref']))
+ this.fail('MISSING_ID_OR_PREF', element);
+ }
+});
+
+/**
+ * Each radio button requires name and value properties.
+ */
+TEST_F('SettingsFormatWebUITest', 'RadioButtonNameValueCheck', function() {
+ var elements = document.querySelectorAll('input[type=radio]');
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ if (!element.name)
+ this.fail('MISSING_RADIO_BUTTON_NAME', element);
+
+ if (!element.getAttribute('value'))
+ this.fail('MISSING_RADIO_BUTTON_VALUE', element);
+ }
+});
diff --git a/chromium/chrome/browser/ui/webui/options/startup_page_list_browsertest.js b/chromium/chrome/browser/ui/webui/options/startup_page_list_browsertest.js
new file mode 100644
index 00000000000..1df6e28c69b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/startup_page_list_browsertest.js
@@ -0,0 +1,151 @@
+// 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.
+
+/**
+ * Fixture for startup pages WebUI tests.
+ * @extends {testing.Test}
+ * @constructor
+ */
+function StartupPageListWebUITest() {}
+
+StartupPageListWebUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /**
+ * Browse to the options page & call our preLoad().
+ * @override
+ */
+ browsePreload: 'chrome://settings-frame/startup',
+
+ /** @override */
+ setUp: function() {
+ StartupOverlay.updateStartupPages(this.fakeStartupList);
+ // 1 item for entering data, 1+ from |this.fakeStartupList|.
+ assertGE(this.getList().items.length, 2);
+ },
+
+ /**
+ * Returns the list to be tested.
+ * @return {Element} The start-up pages list.
+ * @protected
+ */
+ getList: function() {
+ return $('startupPagesList');
+ },
+
+ /**
+ * Register a mock handler to ensure expectations are met and options pages
+ * behave correctly.
+ * @override
+ */
+ preLoad: function() {
+ this.makeAndRegisterMockHandler(['addStartupPage',
+ 'dragDropStartupPage']);
+ },
+
+ /**
+ * A fake list of startup pages to send to the overlay.
+ * @protected
+ */
+ fakeStartupList: [
+ {
+ title: 'Yahoo!',
+ url: 'http://yahoo.com',
+ tooltip: 'Yahoo! homepage',
+ modelIndex: 0
+ },
+ {
+ title: 'Facebook',
+ url: 'http://facebook.com',
+ tooltip: 'Facebook :: Sign In',
+ modelIndex: 1
+ }
+ ],
+};
+
+(function() {
+
+/**
+ * A mock data transfer object for drag/drop events.
+ * @constructor
+ */
+function MockDataTransfer() {
+ /**
+ * The data this dataTransfer object knows about.
+ * @type {!Object<string>}
+ * @private
+ */
+ this.data_ = {};
+}
+
+/**
+ * Installs a lazily created MockDataTransfer on event#dataTransfer.
+ * @param {!Event} event An event to modify.
+ */
+MockDataTransfer.install = function(event) {
+ event.__defineGetter__('dataTransfer', function() {
+ event.dataTransfer_ = event.dataTransfer_ || new MockDataTransfer;
+ return event.dataTransfer_;
+ });
+};
+
+MockDataTransfer.prototype = {
+ /**
+ * The URL data in this mock drop event.
+ * @param {string} type The text of data being set.
+ * @param {*} val The data to set. Will be stringified.
+ */
+ setData: function(type, val) {
+ this.data_[type] = String(val);
+ },
+
+ /**
+ * Gets data associated with this fake data transfer.
+ * @param {string} type The type of data to get.
+ * @return {string} The requested type of data or '' if not set.
+ */
+ getData: function(type) {
+ return this.data_[type] || '';
+ },
+};
+
+/**
+ * Creates a fake bubbling, cancelable mouse event with a mock data transfer
+ * installed.
+ * @param {string} type A type of mouse event (e.g. 'drop').
+ * @return {!Event} A fake mouse event.
+ */
+function createMouseEvent(type) {
+ var event = new MouseEvent(type, {bubbles: true, cancelable: true});
+ MockDataTransfer.install(event);
+ return event;
+}
+
+// Disabled due to: crbug.com/419370
+TEST_F('StartupPageListWebUITest', 'DISABLED_testDropFromOutsideSource',
+ function() {
+ /** @const */ var NEW_PAGE = 'http://google.com';
+
+ var mockDropEvent = createMouseEvent('drop');
+ mockDropEvent.dataTransfer.setData('url', NEW_PAGE);
+
+ this.mockHandler.expects(once()).addStartupPage([NEW_PAGE, 0]);
+
+ this.getList().items[0].dispatchEvent(mockDropEvent);
+
+ expectTrue(mockDropEvent.defaultPrevented);
+});
+
+// Disabled due to: crbug.com/419370
+TEST_F('StartupPageListWebUITest', 'DISABLED_testDropToReorder', function() {
+ // TODO(dbeam): mock4js doesn't handle complex arguments well. Fix this.
+ this.mockHandler.expects(once()).dragDropStartupPage([0, [1].join()]);
+
+ this.getList().selectionModel.selectedIndex = 1;
+ expectEquals(1, this.getList().selectionModel.selectedIndexes.length);
+
+ this.getList().items[0].dispatchEvent(createMouseEvent('drop'));
+});
+
+}());
diff --git a/chromium/chrome/browser/ui/webui/options/startup_pages_handler.cc b/chromium/chrome/browser/ui/webui/options/startup_pages_handler.cc
new file mode 100644
index 00000000000..b80a4c07e3c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/startup_pages_handler.cc
@@ -0,0 +1,271 @@
+// 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/ui/webui/options/startup_pages_handler.h"
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h"
+#include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/custom_home_pages_table_model.h"
+#include "chrome/browser/prefs/session_startup_pref.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/browser/ui/webui/settings_utils.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/metrics/proto/omnibox_event.pb.h"
+#include "components/omnibox/browser/autocomplete_classifier.h"
+#include "components/omnibox/browser/autocomplete_controller.h"
+#include "components/omnibox/browser/autocomplete_input.h"
+#include "components/omnibox/browser/autocomplete_result.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/web_ui.h"
+#include "url/gurl.h"
+
+namespace options {
+
+StartupPagesHandler::StartupPagesHandler() {
+}
+
+StartupPagesHandler::~StartupPagesHandler() {
+}
+
+void StartupPagesHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ static OptionsStringResource resources[] = {
+ { "startupAddLabel", IDS_OPTIONS_STARTUP_ADD_LABEL },
+ { "startupUseCurrent", IDS_OPTIONS_STARTUP_USE_CURRENT },
+ { "startupPagesPlaceholder", IDS_OPTIONS_STARTUP_PAGES_PLACEHOLDER },
+ };
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+ RegisterTitle(localized_strings, "startupPagesOverlay",
+ IDS_OPTIONS_STARTUP_PAGES_DIALOG_TITLE);
+}
+
+void StartupPagesHandler::RegisterMessages() {
+ // Guest profiles should never have been displayed the option to set these
+ // values.
+ if (Profile::FromWebUI(web_ui())->IsOffTheRecord())
+ return;
+
+ web_ui()->RegisterMessageCallback("removeStartupPages",
+ base::Bind(&StartupPagesHandler::RemoveStartupPages,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("addStartupPage",
+ base::Bind(&StartupPagesHandler::AddStartupPage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("editStartupPage",
+ base::Bind(&StartupPagesHandler::EditStartupPage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("setStartupPagesToCurrentPages",
+ base::Bind(&StartupPagesHandler::SetStartupPagesToCurrentPages,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("dragDropStartupPage",
+ base::Bind(&StartupPagesHandler::DragDropStartupPage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "requestAutocompleteSuggestionsForStartupPages",
+ base::Bind(&StartupPagesHandler::RequestAutocompleteSuggestions,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("commitStartupPrefChanges",
+ base::Bind(&StartupPagesHandler::CommitChanges,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("cancelStartupPrefChanges",
+ base::Bind(&StartupPagesHandler::CancelChanges,
+ base::Unretained(this)));
+}
+
+void StartupPagesHandler::UpdateStartupPages() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ const SessionStartupPref startup_pref =
+ SessionStartupPref::GetStartupPref(profile->GetPrefs());
+ startup_custom_pages_table_model_->SetURLs(startup_pref.urls);
+}
+
+void StartupPagesHandler::InitializeHandler() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+ startup_custom_pages_table_model_.reset(
+ new CustomHomePagesTableModel(profile));
+ startup_custom_pages_table_model_->SetObserver(this);
+
+ pref_change_registrar_.Init(profile->GetPrefs());
+ pref_change_registrar_.Add(
+ prefs::kURLsToRestoreOnStartup,
+ base::Bind(&StartupPagesHandler::UpdateStartupPages,
+ base::Unretained(this)));
+
+ autocomplete_controller_.reset(new AutocompleteController(
+ base::MakeUnique<ChromeAutocompleteProviderClient>(profile), this,
+ AutocompleteClassifier::DefaultOmniboxProviders()));
+}
+
+void StartupPagesHandler::InitializePage() {
+ UpdateStartupPages();
+}
+
+void StartupPagesHandler::OnModelChanged() {
+ base::ListValue startup_pages;
+ int page_count = startup_custom_pages_table_model_->RowCount();
+ std::vector<GURL> urls = startup_custom_pages_table_model_->GetURLs();
+ for (int i = 0; i < page_count; ++i) {
+ std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
+ entry->SetString("title", startup_custom_pages_table_model_->GetText(i, 0));
+ entry->SetString("url", urls[i].spec());
+ entry->SetString("tooltip",
+ startup_custom_pages_table_model_->GetTooltip(i));
+ entry->SetInteger("modelIndex", i);
+ startup_pages.Append(std::move(entry));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("StartupOverlay.updateStartupPages",
+ startup_pages);
+}
+
+void StartupPagesHandler::OnItemsChanged(int start, int length) {
+ OnModelChanged();
+}
+
+void StartupPagesHandler::OnItemsAdded(int start, int length) {
+ OnModelChanged();
+}
+
+void StartupPagesHandler::OnItemsRemoved(int start, int length) {
+ OnModelChanged();
+}
+
+void StartupPagesHandler::SetStartupPagesToCurrentPages(
+ const base::ListValue* args) {
+ startup_custom_pages_table_model_->SetToCurrentlyOpenPages();
+}
+
+void StartupPagesHandler::RemoveStartupPages(const base::ListValue* args) {
+ for (int i = args->GetSize() - 1; i >= 0; --i) {
+ int selected_index;
+ CHECK(args->GetInteger(i, &selected_index));
+
+ if (selected_index < 0 ||
+ selected_index >= startup_custom_pages_table_model_->RowCount()) {
+ NOTREACHED();
+ return;
+ }
+ startup_custom_pages_table_model_->Remove(selected_index);
+ }
+}
+
+void StartupPagesHandler::AddStartupPage(const base::ListValue* args) {
+ std::string url_string;
+ CHECK(args->GetString(0, &url_string));
+
+ GURL fixed_url;
+ if (!settings_utils::FixupAndValidateStartupPage(url_string, &fixed_url)) {
+ NOTREACHED();
+ return;
+ }
+
+ int row_count = startup_custom_pages_table_model_->RowCount();
+ int index;
+ if (!args->GetInteger(1, &index) || index > row_count)
+ index = row_count;
+
+ startup_custom_pages_table_model_->Add(index, fixed_url);
+}
+
+void StartupPagesHandler::EditStartupPage(const base::ListValue* args) {
+ CHECK_EQ(args->GetSize(), 2U);
+ int index;
+ CHECK(args->GetInteger(0, &index));
+
+ if (index < 0 || index > startup_custom_pages_table_model_->RowCount()) {
+ NOTREACHED();
+ return;
+ }
+
+ std::string url_string;
+ CHECK(args->GetString(1, &url_string));
+
+ GURL fixed_url;
+ if (settings_utils::FixupAndValidateStartupPage(url_string, &fixed_url)) {
+ std::vector<GURL> urls = startup_custom_pages_table_model_->GetURLs();
+ urls[index] = fixed_url;
+ startup_custom_pages_table_model_->SetURLs(urls);
+ } else {
+ startup_custom_pages_table_model_->Remove(index);
+ }
+}
+
+void StartupPagesHandler::DragDropStartupPage(const base::ListValue* args) {
+ CHECK_EQ(args->GetSize(), 2U);
+
+ int to_index;
+
+ CHECK(args->GetInteger(0, &to_index));
+
+ const base::ListValue* selected;
+ CHECK(args->GetList(1, &selected));
+
+ std::vector<int> index_list;
+ for (size_t i = 0; i < selected->GetSize(); ++i) {
+ int index;
+ CHECK(selected->GetInteger(i, &index));
+ index_list.push_back(index);
+ }
+
+ startup_custom_pages_table_model_->MoveURLs(to_index, index_list);
+}
+
+void StartupPagesHandler::SaveStartupPagesPref() {
+ PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+
+ SessionStartupPref pref = SessionStartupPref::GetStartupPref(prefs);
+ pref.urls = startup_custom_pages_table_model_->GetURLs();
+
+ if (pref.urls.empty())
+ pref.type = SessionStartupPref::DEFAULT;
+
+ SessionStartupPref::SetStartupPref(prefs, pref);
+}
+
+void StartupPagesHandler::CommitChanges(const base::ListValue* args) {
+ SaveStartupPagesPref();
+}
+
+void StartupPagesHandler::CancelChanges(const base::ListValue* args) {
+ UpdateStartupPages();
+}
+
+void StartupPagesHandler::RequestAutocompleteSuggestions(
+ const base::ListValue* args) {
+ base::string16 input;
+ CHECK_EQ(args->GetSize(), 1U);
+ CHECK(args->GetString(0, &input));
+
+ autocomplete_controller_->Start(AutocompleteInput(
+ input, base::string16::npos, std::string(), GURL(), base::string16(),
+ metrics::OmniboxEventProto::INVALID_SPEC, true, false, false, true, false,
+ ChromeAutocompleteSchemeClassifier(Profile::FromWebUI(web_ui()))));
+}
+
+void StartupPagesHandler::OnResultChanged(bool default_match_changed) {
+ const AutocompleteResult& result = autocomplete_controller_->result();
+ base::ListValue suggestions;
+ OptionsUI::ProcessAutocompleteSuggestions(result, &suggestions);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "StartupOverlay.updateAutocompleteSuggestions", suggestions);
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/startup_pages_handler.h b/chromium/chrome/browser/ui/webui/options/startup_pages_handler.h
new file mode 100644
index 00000000000..2f181744f30
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/startup_pages_handler.h
@@ -0,0 +1,95 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_STARTUP_PAGES_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_STARTUP_PAGES_HANDLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "components/omnibox/browser/autocomplete_controller_delegate.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_member.h"
+#include "ui/base/models/table_model_observer.h"
+
+class AutocompleteController;
+class CustomHomePagesTableModel;
+
+namespace options {
+
+// Chrome browser options page UI handler.
+class StartupPagesHandler : public OptionsPageUIHandler,
+ public AutocompleteControllerDelegate,
+ public ui::TableModelObserver {
+ public:
+ StartupPagesHandler();
+ ~StartupPagesHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializeHandler() override;
+ void InitializePage() override;
+ void RegisterMessages() override;
+
+ // AutocompleteControllerDelegate implementation.
+ void OnResultChanged(bool default_match_changed) override;
+
+ // ui::TableModelObserver implementation.
+ void OnModelChanged() override;
+ void OnItemsChanged(int start, int length) override;
+ void OnItemsAdded(int start, int length) override;
+ void OnItemsRemoved(int start, int length) override;
+
+ private:
+ // Saves the changes that have been made. Called from WebUI.
+ void CommitChanges(const base::ListValue* args);
+
+ // Cancels the changes that have been made. Called from WebUI.
+ void CancelChanges(const base::ListValue* args);
+
+ // Removes the startup page at the given indexes. Called from WebUI.
+ void RemoveStartupPages(const base::ListValue* args);
+
+ // Adds a startup page with the given URL after the given index.
+ // Called from WebUI.
+ void AddStartupPage(const base::ListValue* args);
+
+ // Changes the startup page at the given index to the given URL.
+ // Called from WebUI.
+ void EditStartupPage(const base::ListValue* args);
+
+ // Sets the startup page set to the current pages. Called from WebUI.
+ void SetStartupPagesToCurrentPages(const base::ListValue* args);
+
+ // Writes the current set of startup pages to prefs. Called from WebUI.
+ void DragDropStartupPage(const base::ListValue* args);
+
+ // Gets autocomplete suggestions asychronously for the given string.
+ // Called from WebUI.
+ void RequestAutocompleteSuggestions(const base::ListValue* args);
+
+ // Loads the current set of custom startup pages and reports it to the WebUI.
+ void UpdateStartupPages();
+
+ // Writes the current set of startup pages to prefs.
+ void SaveStartupPagesPref();
+
+ std::unique_ptr<AutocompleteController> autocomplete_controller_;
+
+ // Used to observe updates to the preference of the list of URLs to load
+ // on startup, which can be updated via sync.
+ PrefChangeRegistrar pref_change_registrar_;
+
+ // The set of pages to launch on startup.
+ std::unique_ptr<CustomHomePagesTableModel> startup_custom_pages_table_model_;
+
+ DISALLOW_COPY_AND_ASSIGN(StartupPagesHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_STARTUP_PAGES_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/supervised_user_create_confirm_handler.cc b/chromium/chrome/browser/ui/webui/options/supervised_user_create_confirm_handler.cc
new file mode 100644
index 00000000000..9ff957d40f6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/supervised_user_create_confirm_handler.cc
@@ -0,0 +1,168 @@
+// 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/ui/webui/options/supervised_user_create_confirm_handler.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/value_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/startup/startup_types.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/signin/core/browser/signin_manager_base.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace options {
+
+// ProfileUpdateObserver------------------------------------------------------
+
+class SupervisedUserCreateConfirmHandler::ProfileUpdateObserver
+ : public ProfileAttributesStorage::Observer {
+ public:
+ ProfileUpdateObserver(ProfileAttributesStorage* profile_attributes_storage,
+ SupervisedUserCreateConfirmHandler* handler)
+ : profile_attributes_storage_(profile_attributes_storage),
+ create_confirm_handler_(handler),
+ scoped_observer_(this) {
+ DCHECK(profile_attributes_storage_);
+ DCHECK(create_confirm_handler_);
+ scoped_observer_.Add(profile_attributes_storage_);
+ }
+
+ private:
+ // ProfileAttributesStorage::Observer implementation:
+ // Forward possibly relevant changes to the dialog, which will check the
+ // affected profile and update or close as needed.
+ void OnProfileWasRemoved(const base::FilePath& profile_path,
+ const base::string16& profile_name) override {
+ std::unique_ptr<base::Value> profile_path_value(
+ base::CreateFilePathValue(profile_path));
+ create_confirm_handler_->web_ui()->CallJavascriptFunctionUnsafe(
+ "SupervisedUserCreateConfirmOverlay.onDeletedProfile",
+ *profile_path_value);
+ }
+
+ void OnProfileNameChanged(const base::FilePath& profile_path,
+ const base::string16& old_profile_name) override {
+ ProfileAttributesEntry* entry;
+ if (!profile_attributes_storage_->
+ GetProfileAttributesWithPath(profile_path, &entry))
+ return;
+ base::string16 new_profile_name = entry->GetName();
+ std::unique_ptr<base::Value> profile_path_value(
+ base::CreateFilePathValue(profile_path));
+ create_confirm_handler_->web_ui()->CallJavascriptFunctionUnsafe(
+ "SupervisedUserCreateConfirmOverlay.onUpdatedProfileName",
+ *profile_path_value, base::Value(new_profile_name));
+ }
+
+ // Weak.
+ ProfileAttributesStorage* profile_attributes_storage_;
+
+ // Weak; owns us.
+ SupervisedUserCreateConfirmHandler* create_confirm_handler_;
+
+ // Manages any sources we're observing, ensuring that they're all removed
+ // on destruction.
+ ScopedObserver<ProfileAttributesStorage, ProfileUpdateObserver>
+ scoped_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProfileUpdateObserver);
+};
+
+
+// SupervisedUserCreateConfirmHandler-----------------------------------------
+
+SupervisedUserCreateConfirmHandler::SupervisedUserCreateConfirmHandler() {
+ profile_update_observer_.reset(
+ new SupervisedUserCreateConfirmHandler::ProfileUpdateObserver(
+ &g_browser_process->profile_manager()->GetProfileAttributesStorage(),
+ this));
+}
+
+SupervisedUserCreateConfirmHandler::~SupervisedUserCreateConfirmHandler() {
+}
+
+void SupervisedUserCreateConfirmHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ static OptionsStringResource resources[] = {
+ { "supervisedUserCreatedTitle", IDS_LEGACY_SUPERVISED_USER_CREATED_TITLE },
+ { "supervisedUserCreatedDone",
+ IDS_LEGACY_SUPERVISED_USER_CREATED_DONE_BUTTON },
+ { "supervisedUserCreatedSwitch",
+ IDS_LEGACY_SUPERVISED_USER_CREATED_SWITCH_BUTTON },
+ };
+
+ SigninManagerBase* signin =
+ SigninManagerFactory::GetForProfile(Profile::FromWebUI(web_ui()));
+ if (signin) {
+ localized_strings->SetString("custodianEmail",
+ signin->GetAuthenticatedAccountInfo().email);
+ } else {
+ localized_strings->SetString("custodianEmail", std::string());
+ }
+
+ base::string16 supervised_user_dashboard_url =
+ base::ASCIIToUTF16(chrome::kLegacySupervisedUserManagementURL);
+ base::string16 supervised_user_dashboard_display =
+ base::ASCIIToUTF16(chrome::kLegacySupervisedUserManagementDisplayURL);
+ // The first two substitution parameters need to remain; they will be filled
+ // by the page's JS.
+ localized_strings->SetString("supervisedUserCreatedText",
+ l10n_util::GetStringFUTF16(IDS_LEGACY_SUPERVISED_USER_CREATED_TEXT,
+ base::ASCIIToUTF16("$1"),
+ base::ASCIIToUTF16("$2"),
+ supervised_user_dashboard_url,
+ supervised_user_dashboard_display));
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+}
+
+void SupervisedUserCreateConfirmHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("switchToProfile",
+ base::Bind(&SupervisedUserCreateConfirmHandler::SwitchToProfile,
+ base::Unretained(this)));
+}
+
+void SupervisedUserCreateConfirmHandler::SwitchToProfile(
+ const base::ListValue* args) {
+ DCHECK(args);
+ const base::Value* file_path_value;
+ if (!args->Get(0, &file_path_value))
+ return;
+
+ base::FilePath profile_file_path;
+ if (!base::GetValueAsFilePath(*file_path_value, &profile_file_path))
+ return;
+
+ Profile* profile = g_browser_process->profile_manager()->
+ GetProfileByPath(profile_file_path);
+ DCHECK(profile);
+
+ profiles::FindOrCreateNewWindowForProfile(
+ profile, chrome::startup::IS_PROCESS_STARTUP,
+ chrome::startup::IS_FIRST_RUN, false);
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/supervised_user_create_confirm_handler.h b/chromium/chrome/browser/ui/webui/options/supervised_user_create_confirm_handler.h
new file mode 100644
index 00000000000..cf18d3c260f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/supervised_user_create_confirm_handler.h
@@ -0,0 +1,49 @@
+// 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_UI_WEBUI_OPTIONS_SUPERVISED_USER_CREATE_CONFIRM_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_SUPERVISED_USER_CREATE_CONFIRM_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace options {
+
+// Handler for the confirmation dialog after successful creation of a supervised
+// user.
+class SupervisedUserCreateConfirmHandler : public OptionsPageUIHandler {
+ public:
+ SupervisedUserCreateConfirmHandler();
+ ~SupervisedUserCreateConfirmHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ // An observer for any changes to Profiles in the ProfileAttributesStorage so
+ // that this dialog can be updated or closed.
+ class ProfileUpdateObserver;
+
+ // Callback for the "switchToProfile" message.
+ // Opens a new window for the profile.
+ // |args| is of the form [ {string} profileFilePath ]
+ void SwitchToProfile(const base::ListValue* args);
+
+ // Observes the ProfileAttributesStorage and gets notified when a profile has
+ // been modified, so that the dialog can be updated or closed.
+ std::unique_ptr<ProfileUpdateObserver> profile_update_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(SupervisedUserCreateConfirmHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_SUPERVISED_USER_CREATE_CONFIRM_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/supervised_user_import_handler.cc b/chromium/chrome/browser/ui/webui/options/supervised_user_import_handler.cc
new file mode 100644
index 00000000000..e768ff1ca90
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/supervised_user_import_handler.cc
@@ -0,0 +1,304 @@
+// 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/ui/webui/options/supervised_user_import_handler.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_avatar_icon_util.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/signin/signin_error_controller_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/supervised_user/legacy/supervised_user_shared_settings_service.h"
+#include "chrome/browser/supervised_user/legacy/supervised_user_shared_settings_service_factory.h"
+#include "chrome/browser/supervised_user/legacy/supervised_user_sync_service.h"
+#include "chrome/browser/supervised_user/legacy/supervised_user_sync_service_factory.h"
+#include "chrome/browser/supervised_user/supervised_user_constants.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/theme_resources.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/signin_error_controller.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "content/public/browser/web_ui.h"
+
+namespace {
+
+std::unique_ptr<base::ListValue> GetAvatarIcons() {
+ std::unique_ptr<base::ListValue> avatar_icons(new base::ListValue);
+ for (size_t i = 0; i < profiles::GetDefaultAvatarIconCount(); ++i) {
+ std::string avatar_url = profiles::GetDefaultAvatarIconUrl(i);
+ avatar_icons->AppendString(avatar_url);
+ }
+
+ return avatar_icons;
+}
+
+bool ProfileIsLegacySupervised(const base::FilePath& profile_path) {
+ ProfileAttributesEntry* entry;
+
+ return g_browser_process->profile_manager()->GetProfileAttributesStorage().
+ GetProfileAttributesWithPath(profile_path, &entry) &&
+ entry->IsLegacySupervised();
+}
+
+} // namespace
+
+namespace options {
+
+SupervisedUserImportHandler::SupervisedUserImportHandler()
+ : profile_observer_(this),
+ signin_error_observer_(this),
+ supervised_user_sync_service_observer_(this),
+ removed_profile_is_supervised_(false),
+ weak_ptr_factory_(this) {
+}
+
+SupervisedUserImportHandler::~SupervisedUserImportHandler() {
+}
+
+void SupervisedUserImportHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ static OptionsStringResource resources[] = {
+ { "supervisedUserImportTitle",
+ IDS_IMPORT_EXISTING_LEGACY_SUPERVISED_USER_TITLE },
+ { "supervisedUserImportText",
+ IDS_IMPORT_EXISTING_LEGACY_SUPERVISED_USER_TEXT },
+ { "createNewUserLink", IDS_CREATE_NEW_LEGACY_SUPERVISED_USER_LINK },
+ { "supervisedUserImportOk",
+ IDS_IMPORT_EXISTING_LEGACY_SUPERVISED_USER_OK },
+ { "supervisedUserImportSigninError",
+ IDS_LEGACY_SUPERVISED_USER_IMPORT_SIGN_IN_ERROR },
+ { "supervisedUserAlreadyOnThisDevice",
+ IDS_LEGACY_SUPERVISED_USER_ALREADY_ON_THIS_DEVICE },
+ { "noExistingSupervisedUsers",
+ IDS_LEGACY_SUPERVISED_USER_NO_EXISTING_ERROR },
+ { "supervisedUserSelectAvatarTitle",
+ IDS_LEGACY_SUPERVISED_USER_SELECT_AVATAR_TITLE },
+ { "supervisedUserSelectAvatarText",
+ IDS_LEGACY_SUPERVISED_USER_SELECT_AVATAR_TEXT },
+ { "supervisedUserSelectAvatarOk",
+ IDS_LEGACY_SUPERVISED_USER_SELECT_AVATAR_OK },
+ };
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+ localized_strings->Set("avatarIcons", GetAvatarIcons());
+}
+
+void SupervisedUserImportHandler::InitializeHandler() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (!profile->IsSupervised()) {
+ profile_observer_.Add(
+ &g_browser_process->profile_manager()->GetProfileAttributesStorage());
+ SupervisedUserSyncService* sync_service =
+ SupervisedUserSyncServiceFactory::GetForProfile(profile);
+ if (sync_service) {
+ supervised_user_sync_service_observer_.Add(sync_service);
+ signin_error_observer_.Add(
+ SigninErrorControllerFactory::GetForProfile(profile));
+ SupervisedUserSharedSettingsService* settings_service =
+ SupervisedUserSharedSettingsServiceFactory::GetForBrowserContext(
+ profile);
+ subscription_ = settings_service->Subscribe(
+ base::Bind(&SupervisedUserImportHandler::OnSharedSettingChanged,
+ weak_ptr_factory_.GetWeakPtr()));
+ } else {
+ DCHECK(!SupervisedUserSharedSettingsServiceFactory::GetForBrowserContext(
+ profile));
+ DCHECK(!SigninErrorControllerFactory::GetForProfile(profile));
+ }
+ }
+}
+
+void SupervisedUserImportHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("requestSupervisedUserImportUpdate",
+ base::Bind(&SupervisedUserImportHandler::
+ RequestSupervisedUserImportUpdate,
+ base::Unretained(this)));
+}
+
+void SupervisedUserImportHandler::OnProfileAdded(
+ const base::FilePath& profile_path) {
+ // When a supervised profile is added, re-send the list to update the
+ // the "already on this device" status.
+ if (ProfileIsLegacySupervised(profile_path))
+ FetchSupervisedUsers();
+}
+
+void SupervisedUserImportHandler::OnProfileWillBeRemoved(
+ const base::FilePath& profile_path) {
+ DCHECK(!removed_profile_is_supervised_);
+ // When a supervised profile is removed, re-send the list to update the
+ // "already on this device" status. We can't do that right now because the
+ // profile still exists, so defer to OnProfileWasRemoved.
+ if (ProfileIsLegacySupervised(profile_path))
+ removed_profile_is_supervised_ = true;
+}
+
+void SupervisedUserImportHandler::OnProfileWasRemoved(
+ const base::FilePath& profile_path,
+ const base::string16& profile_name) {
+ if (removed_profile_is_supervised_) {
+ removed_profile_is_supervised_ = false;
+ FetchSupervisedUsers();
+ }
+}
+
+void SupervisedUserImportHandler::OnProfileIsOmittedChanged(
+ const base::FilePath& profile_path) {
+ if (ProfileIsLegacySupervised(profile_path))
+ FetchSupervisedUsers();
+}
+
+void SupervisedUserImportHandler::OnSupervisedUsersChanged() {
+ FetchSupervisedUsers();
+}
+
+void SupervisedUserImportHandler::FetchSupervisedUsers() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.SupervisedUserListData.resetPromise");
+ RequestSupervisedUserImportUpdate(NULL);
+}
+
+void SupervisedUserImportHandler::RequestSupervisedUserImportUpdate(
+ const base::ListValue* /* args */) {
+ if (Profile::FromWebUI(web_ui())->IsSupervised())
+ return;
+
+ if (!IsAccountConnected() || HasAuthError()) {
+ ClearSupervisedUsersAndShowError();
+ } else {
+ SupervisedUserSyncService* supervised_user_sync_service =
+ SupervisedUserSyncServiceFactory::GetForProfile(
+ Profile::FromWebUI(web_ui()));
+ if (supervised_user_sync_service) {
+ supervised_user_sync_service->GetSupervisedUsersAsync(
+ base::Bind(&SupervisedUserImportHandler::SendExistingSupervisedUsers,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+ }
+}
+
+void SupervisedUserImportHandler::SendExistingSupervisedUsers(
+ const base::DictionaryValue* dict) {
+ DCHECK(dict);
+ std::vector<ProfileAttributesEntry*> entries =
+ g_browser_process->profile_manager()->
+ GetProfileAttributesStorage().GetAllProfilesAttributes();
+
+ // Collect the ids of local supervised user profiles.
+ std::set<std::string> supervised_user_ids;
+ for (const ProfileAttributesEntry* entry : entries) {
+ // Filter out omitted profiles. These are currently being imported, and
+ // shouldn't show up as "already on this device" just yet.
+ if (entry->IsLegacySupervised() && !entry->IsOmitted()) {
+ supervised_user_ids.insert(entry->GetSupervisedUserId());
+ }
+ }
+
+ base::ListValue supervised_users;
+ Profile* profile = Profile::FromWebUI(web_ui());
+ SupervisedUserSharedSettingsService* service =
+ SupervisedUserSharedSettingsServiceFactory::GetForBrowserContext(profile);
+ for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
+ const base::DictionaryValue* value = NULL;
+ bool success = it.value().GetAsDictionary(&value);
+ DCHECK(success);
+ std::string name;
+ value->GetString(SupervisedUserSyncService::kName, &name);
+
+ std::unique_ptr<base::DictionaryValue> supervised_user(
+ new base::DictionaryValue);
+ supervised_user->SetString("id", it.key());
+ supervised_user->SetString("name", name);
+
+ int avatar_index = SupervisedUserSyncService::kNoAvatar;
+ const base::Value* avatar_index_value =
+ service->GetValue(it.key(), supervised_users::kChromeAvatarIndex);
+ if (avatar_index_value) {
+ success = avatar_index_value->GetAsInteger(&avatar_index);
+ } else {
+ // Check if there is a legacy avatar index stored.
+ std::string avatar_str;
+ value->GetString(SupervisedUserSyncService::kChromeAvatar, &avatar_str);
+ success =
+ SupervisedUserSyncService::GetAvatarIndex(avatar_str, &avatar_index);
+ }
+ DCHECK(success);
+ supervised_user->SetBoolean(
+ "needAvatar",
+ avatar_index == SupervisedUserSyncService::kNoAvatar);
+
+ std::string supervised_user_icon =
+ std::string(chrome::kChromeUIThemeURL) +
+ "IDR_SUPERVISED_USER_PLACEHOLDER";
+ std::string avatar_url =
+ avatar_index == SupervisedUserSyncService::kNoAvatar ?
+ supervised_user_icon :
+ profiles::GetDefaultAvatarIconUrl(avatar_index);
+ supervised_user->SetString("iconURL", avatar_url);
+ bool on_current_device =
+ supervised_user_ids.find(it.key()) != supervised_user_ids.end();
+ supervised_user->SetBoolean("onCurrentDevice", on_current_device);
+
+ supervised_users.Append(std::move(supervised_user));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.SupervisedUserListData.receiveExistingSupervisedUsers",
+ supervised_users);
+}
+
+void SupervisedUserImportHandler::ClearSupervisedUsersAndShowError() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "options.SupervisedUserListData.onSigninError");
+}
+
+bool SupervisedUserImportHandler::IsAccountConnected() const {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetForProfile(profile);
+ return signin_manager && signin_manager->IsAuthenticated();
+}
+
+bool SupervisedUserImportHandler::HasAuthError() const {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ SigninErrorController* error_controller =
+ SigninErrorControllerFactory::GetForProfile(profile);
+ if (!error_controller)
+ return true;
+
+ GoogleServiceAuthError::State state = error_controller->auth_error().state();
+
+ return state == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS ||
+ state == GoogleServiceAuthError::USER_NOT_SIGNED_UP ||
+ state == GoogleServiceAuthError::ACCOUNT_DELETED ||
+ state == GoogleServiceAuthError::ACCOUNT_DISABLED;
+}
+
+void SupervisedUserImportHandler::OnSharedSettingChanged(
+ const std::string& supervised_user_id,
+ const std::string& key) {
+ if (key == supervised_users::kChromeAvatarIndex)
+ FetchSupervisedUsers();
+}
+
+void SupervisedUserImportHandler::OnErrorChanged() {
+ FetchSupervisedUsers();
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/supervised_user_import_handler.h b/chromium/chrome/browser/ui/webui/options/supervised_user_import_handler.h
new file mode 100644
index 00000000000..9b972e1c96a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/supervised_user_import_handler.h
@@ -0,0 +1,118 @@
+// 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_UI_WEBUI_OPTIONS_SUPERVISED_USER_IMPORT_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_SUPERVISED_USER_IMPORT_HANDLER_H_
+
+#include <string>
+
+#include "base/callback_list.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
+#include "base/strings/string16.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/supervised_user/legacy/supervised_user_sync_service_observer.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "components/signin/core/browser/signin_error_controller.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+class ProfileAttributesStorage;
+class SupervisedUserSyncService;
+
+namespace options {
+
+// Handler for the 'import existing supervised user' dialog.
+class SupervisedUserImportHandler : public OptionsPageUIHandler,
+ public ProfileAttributesStorage::Observer,
+ public SupervisedUserSyncServiceObserver,
+ public SigninErrorController::Observer {
+ public:
+ typedef base::CallbackList<void(const std::string&, const std::string&)>
+ CallbackList;
+
+ SupervisedUserImportHandler();
+ ~SupervisedUserImportHandler() override;
+
+ private:
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void InitializeHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // ProfileAttributesStorage::Observer implementation.
+ void OnProfileAdded(const base::FilePath& profile_path) override;
+ void OnProfileWillBeRemoved(const base::FilePath& profile_path) override;
+ void OnProfileWasRemoved(const base::FilePath& profile_path,
+ const base::string16& profile_name) override;
+ void OnProfileIsOmittedChanged(const base::FilePath& profile_path) override;
+
+ // SupervisedUserSyncServiceObserver implementation.
+ void OnSupervisedUserAcknowledged(
+ const std::string& supervised_user_id) override {}
+ void OnSupervisedUsersSyncingStopped() override {}
+ void OnSupervisedUsersChanged() override;
+
+ // SigninErrorController::Observer implementation.
+ void OnErrorChanged() override;
+
+ // Clears the cached list of supervised users and fetches the new list of
+ // supervised users.
+ void FetchSupervisedUsers();
+
+ // Callback for the "requestSupervisedUserImportUpdate" message.
+ // Checks the sign-in status of the custodian and accordingly
+ // sends an update to the WebUI. The update can be to show/hide
+ // an error bubble and update/clear the supervised user list.
+ void RequestSupervisedUserImportUpdate(const base::ListValue* args);
+
+ // Sends an array of supervised users to WebUI. Each entry is of the form:
+ // supervisedProfile = {
+ // id: "Supervised User ID",
+ // name: "Supervised User Name",
+ // iconURL: "chrome://path/to/icon/image",
+ // onCurrentDevice: true or false,
+ // needAvatar: true or false
+ // }
+ // The array holds all existing supervised users attached to the
+ // custodian's profile which initiated the request.
+ void SendExistingSupervisedUsers(const base::DictionaryValue* dict);
+
+ // Sends messages to the JS side to clear supervised users and show an error
+ // bubble.
+ void ClearSupervisedUsersAndShowError();
+
+ bool IsAccountConnected() const;
+ bool HasAuthError() const;
+
+ // Called when a supervised user shared setting is changed. If the avatar was
+ // changed, FetchSupervisedUsers() is called.
+ void OnSharedSettingChanged(const std::string& supervised_user_id,
+ const std::string& key);
+
+ std::unique_ptr<CallbackList::Subscription> subscription_;
+
+ ScopedObserver<ProfileAttributesStorage, SupervisedUserImportHandler>
+ profile_observer_;
+ ScopedObserver<SigninErrorController, SupervisedUserImportHandler>
+ signin_error_observer_;
+ ScopedObserver<SupervisedUserSyncService, SupervisedUserImportHandler>
+ supervised_user_sync_service_observer_;
+
+ bool removed_profile_is_supervised_;
+
+ base::WeakPtrFactory<SupervisedUserImportHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SupervisedUserImportHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_SUPERVISED_USER_IMPORT_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/supervised_user_learn_more_handler.cc b/chromium/chrome/browser/ui/webui/options/supervised_user_learn_more_handler.cc
new file mode 100644
index 00000000000..f7707e890b3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/supervised_user_learn_more_handler.cc
@@ -0,0 +1,42 @@
+// 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/ui/webui/options/supervised_user_learn_more_handler.h"
+
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace options {
+
+SupervisedUserLearnMoreHandler::SupervisedUserLearnMoreHandler() {
+}
+
+SupervisedUserLearnMoreHandler::~SupervisedUserLearnMoreHandler() {
+}
+
+void SupervisedUserLearnMoreHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ static OptionsStringResource resources[] = {
+ { "supervisedUserLearnMoreTitle",
+ IDS_LEGACY_SUPERVISED_USER_LEARN_MORE_TITLE },
+ { "supervisedUserLearnMoreDone",
+ IDS_LEGACY_SUPERVISED_USER_LEARN_MORE_DONE_BUTTON },
+ };
+
+ base::string16 supervised_user_dashboard_display =
+ base::ASCIIToUTF16(chrome::kLegacySupervisedUserManagementDisplayURL);
+ localized_strings->SetString("supervisedUserLearnMoreText",
+ l10n_util::GetStringFUTF16(IDS_LEGACY_SUPERVISED_USER_LEARN_MORE_TEXT,
+ supervised_user_dashboard_display));
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+}
+
+} // namespace options
diff --git a/chromium/chrome/browser/ui/webui/options/supervised_user_learn_more_handler.h b/chromium/chrome/browser/ui/webui/options/supervised_user_learn_more_handler.h
new file mode 100644
index 00000000000..4d17286816e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/supervised_user_learn_more_handler.h
@@ -0,0 +1,33 @@
+// 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_UI_WEBUI_OPTIONS_SUPERVISED_USER_LEARN_MORE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_SUPERVISED_USER_LEARN_MORE_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace options {
+
+// Handler for the "Learn more" dialog available during creation of a supervised
+// user.
+class SupervisedUserLearnMoreHandler : public OptionsPageUIHandler {
+ public:
+ SupervisedUserLearnMoreHandler();
+ ~SupervisedUserLearnMoreHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SupervisedUserLearnMoreHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_SUPERVISED_USER_LEARN_MORE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/sync_setup_handler.cc b/chromium/chrome/browser/ui/webui/options/sync_setup_handler.cc
new file mode 100644
index 00000000000..6c4e9b632f0
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/sync_setup_handler.cc
@@ -0,0 +1,956 @@
+// 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/ui/webui/options/sync_setup_handler.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/i18n/time_formatting.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_metrics.h"
+#include "chrome/browser/signin/chrome_signin_helper.h"
+#include "chrome/browser/signin/signin_error_controller_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/signin_promo.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/singleton_tabs.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"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/locale_settings.h"
+#include "components/autofill/core/common/autofill_constants.h"
+#include "components/autofill/core/common/autofill_pref_names.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/google/core/browser/google_util.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/signin_error_controller.h"
+#include "components/signin/core/browser/signin_header_helper.h"
+#include "components/signin/core/browser/signin_metrics.h"
+#include "components/signin/core/common/profile_management_switches.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/sync/base/sync_prefs.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+#include "google_apis/gaia/gaia_constants.h"
+#include "net/base/url_util.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if defined(OS_CHROMEOS)
+#include "components/signin/core/browser/signin_manager_base.h"
+#else
+#include "components/signin/core/browser/signin_manager.h"
+#endif
+
+using browser_sync::ProfileSyncService;
+using content::WebContents;
+using l10n_util::GetStringFUTF16;
+using l10n_util::GetStringUTF16;
+
+namespace {
+
+// A structure which contains all the configuration information for sync.
+struct SyncConfigInfo {
+ SyncConfigInfo();
+ ~SyncConfigInfo();
+
+ bool encrypt_all;
+ bool sync_everything;
+ syncer::ModelTypeSet data_types;
+ bool payments_integration_enabled;
+ std::string passphrase;
+ bool passphrase_is_gaia;
+};
+
+SyncConfigInfo::SyncConfigInfo()
+ : encrypt_all(false),
+ sync_everything(false),
+ payments_integration_enabled(false),
+ passphrase_is_gaia(false) {}
+
+SyncConfigInfo::~SyncConfigInfo() {}
+
+bool GetConfiguration(const std::string& json, SyncConfigInfo* config) {
+ std::unique_ptr<base::Value> parsed_value = base::JSONReader::Read(json);
+ base::DictionaryValue* result;
+ if (!parsed_value || !parsed_value->GetAsDictionary(&result)) {
+ DLOG(ERROR) << "GetConfiguration() not passed a Dictionary";
+ return false;
+ }
+
+ if (!result->GetBoolean("syncAllDataTypes", &config->sync_everything)) {
+ DLOG(ERROR) << "GetConfiguration() not passed a syncAllDataTypes value";
+ return false;
+ }
+
+ if (!result->GetBoolean("paymentsIntegrationEnabled",
+ &config->payments_integration_enabled)) {
+ DLOG(ERROR) << "GetConfiguration() not passed a paymentsIntegrationEnabled "
+ << "value";
+ return false;
+ }
+
+ syncer::ModelTypeNameMap type_names = syncer::GetUserSelectableTypeNameMap();
+
+ for (syncer::ModelTypeNameMap::const_iterator it = type_names.begin();
+ it != type_names.end(); ++it) {
+ std::string key_name = it->second + std::string("Synced");
+ bool sync_value;
+ if (!result->GetBoolean(key_name, &sync_value)) {
+ DLOG(ERROR) << "GetConfiguration() not passed a value for " << key_name;
+ return false;
+ }
+ if (sync_value)
+ config->data_types.Put(it->first);
+ }
+
+ // Encryption settings.
+ if (!result->GetBoolean("encryptAllData", &config->encrypt_all)) {
+ DLOG(ERROR) << "GetConfiguration() not passed a value for encryptAllData";
+ return false;
+ }
+
+ // Passphrase settings.
+ bool have_passphrase;
+ if (!result->GetBoolean("usePassphrase", &have_passphrase)) {
+ DLOG(ERROR) << "GetConfiguration() not passed a usePassphrase value";
+ return false;
+ }
+
+ if (have_passphrase) {
+ if (!result->GetBoolean("isGooglePassphrase",
+ &config->passphrase_is_gaia)) {
+ DLOG(ERROR) << "GetConfiguration() not passed isGooglePassphrase value";
+ return false;
+ }
+ if (!result->GetString("passphrase", &config->passphrase)) {
+ DLOG(ERROR) << "GetConfiguration() not passed a passphrase value";
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+SyncSetupHandler::SyncSetupHandler()
+ : configuring_sync_(false) {
+}
+
+SyncSetupHandler::~SyncSetupHandler() {
+ // Just exit if running unit tests (no actual WebUI is attached).
+ if (!web_ui())
+ return;
+
+ // This case is hit when the user performs a back navigation.
+ CloseSyncSetup();
+}
+
+void SyncSetupHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ GetStaticLocalizedValues(localized_strings, web_ui());
+}
+
+void SyncSetupHandler::GetStaticLocalizedValues(
+ base::DictionaryValue* localized_strings,
+ content::WebUI* web_ui) {
+ DCHECK(localized_strings);
+
+ base::string16 product_name(GetStringUTF16(IDS_PRODUCT_NAME));
+ localized_strings->SetString(
+ "chooseDataTypesInstructions",
+ GetStringFUTF16(IDS_SYNC_CHOOSE_DATATYPES_INSTRUCTIONS, product_name));
+ localized_strings->SetString("autofillHelpURL", autofill::kHelpURL);
+ localized_strings->SetString(
+ "encryptionInstructions",
+ GetStringFUTF16(IDS_SYNC_ENCRYPTION_INSTRUCTIONS, product_name));
+ localized_strings->SetString(
+ "encryptionHelpURL", chrome::kSyncEncryptionHelpURL);
+ localized_strings->SetString(
+ "encryptionSectionMessage",
+ GetStringFUTF16(IDS_SYNC_ENCRYPTION_SECTION_MESSAGE, product_name));
+ localized_strings->SetString(
+ "personalizeGoogleServicesTitle",
+ GetStringUTF16(IDS_SYNC_CONFIRMATION_PERSONALIZE_SERVICES_TITLE));
+ localized_strings->SetString(
+ "personalizeGoogleServicesMessage",
+ GetStringFUTF16(
+ IDS_SYNC_PERSONALIZE_GOOGLE_SERVICES_MESSAGE,
+ base::ASCIIToUTF16(chrome::kGoogleAccountActivityControlsURL)));
+ localized_strings->SetString(
+ "passphraseRecover",
+ GetStringFUTF16(
+ IDS_SYNC_PASSPHRASE_RECOVER,
+ base::ASCIIToUTF16(
+ google_util::AppendGoogleLocaleParam(
+ GURL(chrome::kSyncGoogleDashboardURL),
+ g_browser_process->GetApplicationLocale()).spec())));
+ localized_strings->SetString(
+ "stopSyncingExplanation",
+ l10n_util::GetStringFUTF16(
+ IDS_SYNC_STOP_SYNCING_EXPLANATION_LABEL,
+ l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
+ base::ASCIIToUTF16(
+ google_util::AppendGoogleLocaleParam(
+ GURL(chrome::kSyncGoogleDashboardURL),
+ g_browser_process->GetApplicationLocale()).spec())));
+ localized_strings->SetString("deleteProfileLabel",
+ l10n_util::GetStringUTF16(IDS_SYNC_STOP_DELETE_PROFILE_LABEL));
+ localized_strings->SetString("stopSyncingTitle",
+ l10n_util::GetStringUTF16(IDS_SYNC_STOP_SYNCING_DIALOG_TITLE));
+ localized_strings->SetString("stopSyncingConfirm",
+ l10n_util::GetStringUTF16(IDS_SYNC_STOP_SYNCING_CONFIRM_BUTTON_LABEL));
+
+ localized_strings->SetString(
+ "syncEverythingHelpURL", chrome::kSyncEverythingLearnMoreURL);
+ localized_strings->SetString(
+ "syncErrorHelpURL", chrome::kSyncErrorsHelpURL);
+
+ static OptionsStringResource resources[] = {
+ {"syncSetupConfigureTitle", IDS_SYNC_SETUP_CONFIGURE_TITLE},
+ {"syncSetupSpinnerTitle", IDS_SYNC_SETUP_SPINNER_TITLE},
+ {"syncSetupTimeoutTitle", IDS_SYNC_SETUP_TIME_OUT_TITLE},
+ {"syncSetupTimeoutContent", IDS_SYNC_SETUP_TIME_OUT_CONTENT},
+ {"errorLearnMore", IDS_LEARN_MORE},
+ {"cancel", IDS_CANCEL},
+ {"loginSuccess", IDS_SYNC_SUCCESS},
+ {"settingUp", IDS_SYNC_LOGIN_SETTING_UP},
+ {"syncAllDataTypes", IDS_SYNC_EVERYTHING},
+ {"chooseDataTypes", IDS_SYNC_CHOOSE_DATATYPES},
+ {"bookmarks", IDS_SYNC_DATATYPE_BOOKMARKS},
+ {"preferences", IDS_SYNC_DATATYPE_PREFERENCES},
+ {"autofill", IDS_SYNC_DATATYPE_AUTOFILL},
+ {"themes", IDS_SYNC_DATATYPE_THEMES},
+ {"passwords", IDS_SYNC_DATATYPE_PASSWORDS},
+ {"extensions", IDS_SYNC_DATATYPE_EXTENSIONS},
+ {"typedURLs", IDS_SYNC_DATATYPE_TYPED_URLS},
+ {"apps", IDS_SYNC_DATATYPE_APPS},
+ {"openTabs", IDS_SYNC_DATATYPE_TABS},
+ {"enablePaymentsIntegration", IDS_AUTOFILL_USE_PAYMENTS_DATA},
+ {"serviceUnavailableError", IDS_SYNC_SETUP_ABORTED_BY_PENDING_CLEAR},
+ {"confirmLabel", IDS_SYNC_CONFIRM_PASSPHRASE_LABEL},
+ {"emptyErrorMessage", IDS_SYNC_EMPTY_PASSPHRASE_ERROR},
+ {"mismatchErrorMessage", IDS_SYNC_PASSPHRASE_MISMATCH_ERROR},
+ {"customizeLinkLabel", IDS_SYNC_CUSTOMIZE_LINK_LABEL},
+ {"confirmSyncPreferences", IDS_SYNC_CONFIRM_SYNC_PREFERENCES},
+ {"syncEverything", IDS_SYNC_SYNC_EVERYTHING},
+ {"useDefaultSettings", IDS_SYNC_USE_DEFAULT_SETTINGS},
+ {"enterPassphraseBody", IDS_SYNC_ENTER_PASSPHRASE_BODY},
+ {"enterGooglePassphraseBody", IDS_SYNC_ENTER_GOOGLE_PASSPHRASE_BODY},
+ {"passphraseLabel", IDS_SYNC_PASSPHRASE_LABEL},
+ {"incorrectPassphrase", IDS_SYNC_INCORRECT_PASSPHRASE},
+ {"passphraseWarning", IDS_SYNC_PASSPHRASE_WARNING},
+ {"yes", IDS_SYNC_PASSPHRASE_CANCEL_YES},
+ {"no", IDS_SYNC_PASSPHRASE_CANCEL_NO},
+ {"sectionExplicitMessagePrefix", IDS_SYNC_PASSPHRASE_MSG_EXPLICIT_PREFIX},
+ {"sectionExplicitMessagePostfix",
+ IDS_SYNC_PASSPHRASE_MSG_EXPLICIT_POSTFIX},
+ // TODO(rogerta): browser/resource/sync_promo/sync_promo.html and related
+ // file may not be needed any more. If not, then the following promo
+ // strings can also be removed.
+ {"promoPageTitle", IDS_SYNC_PROMO_TAB_TITLE},
+ {"promoSkipButton", IDS_SYNC_PROMO_SKIP_BUTTON},
+ {"promoAdvanced", IDS_SYNC_PROMO_ADVANCED},
+ {"promoLearnMore", IDS_LEARN_MORE},
+ {"promoTitleShort", IDS_SYNC_PROMO_MESSAGE_TITLE_SHORT},
+ {"encryptionSectionTitle", IDS_SYNC_ENCRYPTION_SECTION_TITLE},
+ {"basicEncryptionOption", IDS_SYNC_BASIC_ENCRYPTION_DATA},
+ {"fullEncryptionOption", IDS_SYNC_FULL_ENCRYPTION_DATA},
+ };
+
+ RegisterStrings(localized_strings, resources, arraysize(resources));
+ RegisterTitle(localized_strings, "syncSetupOverlay", IDS_SYNC_SETUP_TITLE);
+}
+
+void SyncSetupHandler::ConfigureSyncDone() {
+ base::Value page("done");
+ web_ui()->CallJavascriptFunctionUnsafe("SyncSetupOverlay.showSyncSetupPage",
+ page);
+
+ // Suppress the sign in promo once the user starts sync. This way the user
+ // doesn't see the sign in promo even if they sign out later on.
+ signin::SetUserSkippedPromo(GetProfile());
+
+ ProfileSyncService* service = GetSyncService();
+ DCHECK(service);
+ if (!service->IsFirstSetupComplete()) {
+ // This is the first time configuring sync, so log it.
+ base::FilePath profile_file_path = GetProfile()->GetPath();
+ ProfileMetrics::LogProfileSyncSignIn(profile_file_path);
+
+ // We're done configuring, so notify ProfileSyncService that it is OK to
+ // start syncing.
+ sync_blocker_.reset();
+ service->SetFirstSetupComplete();
+ }
+}
+
+bool SyncSetupHandler::IsActiveLogin() const {
+ // LoginUIService can be NULL if page is brought up in incognito mode
+ // (i.e. if the user is running in guest mode in cros and brings up settings).
+ LoginUIService* service = GetLoginUIService();
+ return service && (service->current_login_ui() == this);
+}
+
+void SyncSetupHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "SyncSetupDidClosePage",
+ base::Bind(&SyncSetupHandler::OnDidClosePage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "SyncSetupConfigure",
+ base::Bind(&SyncSetupHandler::HandleConfigure,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "SyncSetupShowSetupUI",
+ base::Bind(&SyncSetupHandler::HandleShowSetupUI,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("CloseTimeout",
+ base::Bind(&SyncSetupHandler::HandleCloseTimeout,
+ base::Unretained(this)));
+#if defined(OS_CHROMEOS)
+ web_ui()->RegisterMessageCallback(
+ "AttemptUserExit",
+ base::Bind(&SyncSetupHandler::HandleAttemptUserExit,
+ base::Unretained(this)));
+#else
+ web_ui()->RegisterMessageCallback("SyncSetupStopSyncing",
+ base::Bind(&SyncSetupHandler::HandleStopSyncing,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("SyncSetupStartSignIn",
+ base::Bind(&SyncSetupHandler::HandleStartSignin,
+ base::Unretained(this)));
+#endif
+}
+
+#if !defined(OS_CHROMEOS)
+void SyncSetupHandler::DisplayGaiaLogin(
+ signin_metrics::AccessPoint access_point) {
+ DCHECK(!sync_startup_tracker_);
+ // Advanced options are no longer being configured if the login screen is
+ // visible. If the user exits the signin wizard after this without
+ // configuring sync, CloseSyncSetup() will ensure they are logged out.
+ configuring_sync_ = false;
+ DisplayGaiaLoginInNewTabOrWindow(access_point);
+}
+
+void SyncSetupHandler::DisplayGaiaLoginInNewTabOrWindow(
+ signin_metrics::AccessPoint access_point) {
+ Browser* browser = chrome::FindBrowserWithWebContents(
+ web_ui()->GetWebContents());
+ bool force_new_tab = false;
+ if (!browser) {
+ // Settings is not displayed in a browser window. Open a new window.
+ browser = new Browser(
+ Browser::CreateParams(Browser::TYPE_TABBED, GetProfile(), true));
+ force_new_tab = true;
+ }
+
+ // If the signin manager already has an authenticated username, this is a
+ // re-auth scenario, and we need to ensure that the user signs in with the
+ // same email address.
+ GURL url;
+ if (SigninManagerFactory::GetForProfile(
+ browser->profile())->IsAuthenticated()) {
+ UMA_HISTOGRAM_ENUMERATION("Signin.Reauth",
+ signin_metrics::HISTOGRAM_REAUTH_SHOWN,
+ signin_metrics::HISTOGRAM_REAUTH_MAX);
+
+ SigninErrorController* error_controller =
+ SigninErrorControllerFactory::GetForProfile(browser->profile());
+ DCHECK(error_controller->HasError());
+ if (!force_new_tab) {
+ browser->window()->ShowAvatarBubbleFromAvatarButton(
+ BrowserWindow::AVATAR_BUBBLE_MODE_REAUTH,
+ signin::ManageAccountsParams(), access_point, false);
+ } else {
+ url = signin::GetReauthURL(
+ access_point, signin_metrics::Reason::REASON_REAUTHENTICATION,
+ browser->profile(), error_controller->error_account_id());
+ }
+ } else {
+ if (!force_new_tab) {
+ browser->window()->ShowAvatarBubbleFromAvatarButton(
+ BrowserWindow::AVATAR_BUBBLE_MODE_SIGNIN,
+ signin::ManageAccountsParams(), access_point, false);
+ } else {
+ url = signin::GetPromoURL(
+ access_point, signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT,
+ true);
+ }
+ }
+
+ if (url.is_valid())
+ chrome::ShowSingletonTab(browser, url);
+}
+#endif
+
+bool SyncSetupHandler::PrepareSyncSetup() {
+ // If the wizard is already visible, just focus that one.
+ if (FocusExistingWizardIfPresent()) {
+ if (!IsActiveLogin())
+ CloseSyncSetup();
+ return false;
+ }
+
+ // Notify services that login UI is now active.
+ GetLoginUIService()->SetLoginUI(this);
+
+ ProfileSyncService* service = GetSyncService();
+ if (service)
+ sync_blocker_ = service->GetSetupInProgressHandle();
+
+ return true;
+}
+
+void SyncSetupHandler::DisplaySpinner() {
+ configuring_sync_ = true;
+ base::Value page("spinner");
+ base::DictionaryValue args;
+
+ const int kTimeoutSec = 30;
+ DCHECK(!engine_start_timer_);
+ engine_start_timer_.reset(new base::OneShotTimer());
+ engine_start_timer_->Start(FROM_HERE,
+ base::TimeDelta::FromSeconds(kTimeoutSec), this,
+ &SyncSetupHandler::DisplayTimeout);
+
+ web_ui()->CallJavascriptFunctionUnsafe("SyncSetupOverlay.showSyncSetupPage",
+ page, args);
+}
+
+// TODO(kochi): Handle error conditions other than timeout.
+// http://crbug.com/128692
+void SyncSetupHandler::DisplayTimeout() {
+ // Stop a timer to handle timeout in waiting for checking network connection.
+ engine_start_timer_.reset();
+
+ // Do not listen to sync startup events.
+ sync_startup_tracker_.reset();
+
+ base::Value page("timeout");
+ base::DictionaryValue args;
+ web_ui()->CallJavascriptFunctionUnsafe("SyncSetupOverlay.showSyncSetupPage",
+ page, args);
+}
+
+void SyncSetupHandler::OnDidClosePage(const base::ListValue* args) {
+ CloseSyncSetup();
+}
+
+void SyncSetupHandler::SyncStartupFailed() {
+ // Stop a timer to handle timeout in waiting for checking network connection.
+ engine_start_timer_.reset();
+
+ // Just close the sync overlay (the idea is that the base settings page will
+ // display the current error.)
+ CloseUI();
+}
+
+void SyncSetupHandler::SyncStartupCompleted() {
+ ProfileSyncService* service = GetSyncService();
+ DCHECK(service->IsEngineInitialized());
+
+ // Stop a timer to handle timeout in waiting for checking network connection.
+ engine_start_timer_.reset();
+
+ DisplayConfigureSync(false);
+}
+
+Profile* SyncSetupHandler::GetProfile() const {
+ return Profile::FromWebUI(web_ui());
+}
+
+ProfileSyncService* SyncSetupHandler::GetSyncService() const {
+ Profile* profile = GetProfile();
+ return profile->IsSyncAllowed() ?
+ ProfileSyncServiceFactory::GetForProfile(GetProfile()) : NULL;
+}
+
+void SyncSetupHandler::HandleConfigure(const base::ListValue* args) {
+ DCHECK(!sync_startup_tracker_);
+ std::string json;
+ if (!args->GetString(0, &json)) {
+ NOTREACHED() << "Could not read JSON argument";
+ return;
+ }
+ if (json.empty()) {
+ NOTREACHED();
+ return;
+ }
+
+ SyncConfigInfo configuration;
+ if (!GetConfiguration(json, &configuration)) {
+ // The page sent us something that we didn't understand.
+ // This probably indicates a programming error.
+ NOTREACHED();
+ return;
+ }
+
+ // Start configuring the ProfileSyncService using the configuration passed
+ // to us from the JS layer.
+ ProfileSyncService* service = GetSyncService();
+
+ // If the sync engine has shutdown for some reason, just close the sync
+ // dialog.
+ if (!service || !service->IsEngineInitialized()) {
+ CloseUI();
+ return;
+ }
+
+ // Don't allow "encrypt all" if the ProfileSyncService doesn't allow it.
+ // The UI is hidden, but the user may have enabled it e.g. by fiddling with
+ // the web inspector.
+ if (!service->IsEncryptEverythingAllowed())
+ configuration.encrypt_all = false;
+
+ // Note: Data encryption will not occur until configuration is complete
+ // (when the PSS receives its CONFIGURE_DONE notification from the sync
+ // engine), so the user still has a chance to cancel out of the operation
+ // if (for example) some kind of passphrase error is encountered.
+ if (configuration.encrypt_all)
+ service->EnableEncryptEverything();
+
+ bool passphrase_failed = false;
+ if (!configuration.passphrase.empty()) {
+ // We call IsPassphraseRequired() here (instead of
+ // IsPassphraseRequiredForDecryption()) because the user may try to enter
+ // a passphrase even though no encrypted data types are enabled.
+ if (service->IsPassphraseRequired()) {
+ // If we have pending keys, try to decrypt them with the provided
+ // passphrase. We track if this succeeds or fails because a failed
+ // decryption should result in an error even if there aren't any encrypted
+ // data types.
+ passphrase_failed =
+ !service->SetDecryptionPassphrase(configuration.passphrase);
+ } else {
+ // OK, the user sent us a passphrase, but we don't have pending keys. So
+ // it either means that the pending keys were resolved somehow since the
+ // time the UI was displayed (re-encryption, pending passphrase change,
+ // etc) or the user wants to re-encrypt.
+ if (!configuration.passphrase_is_gaia &&
+ !service->IsUsingSecondaryPassphrase()) {
+ // User passed us a secondary passphrase, and the data is encrypted
+ // with a GAIA passphrase so they must want to encrypt.
+ service->SetEncryptionPassphrase(configuration.passphrase,
+ ProfileSyncService::EXPLICIT);
+ }
+ }
+ }
+
+ bool user_was_prompted_for_passphrase =
+ service->IsPassphraseRequiredForDecryption();
+ service->OnUserChoseDatatypes(configuration.sync_everything,
+ configuration.data_types);
+
+ PrefService* pref_service = GetProfile()->GetPrefs();
+ pref_service->SetBoolean(autofill::prefs::kAutofillWalletImportEnabled,
+ configuration.payments_integration_enabled);
+
+ // Need to call IsPassphraseRequiredForDecryption() *after* calling
+ // OnUserChoseDatatypes() because the user may have just disabled the
+ // encrypted datatypes (in which case we just want to exit, not prompt the
+ // user for a passphrase).
+ if (passphrase_failed || service->IsPassphraseRequiredForDecryption()) {
+ // We need a passphrase, or the user's attempt to set a passphrase failed -
+ // prompt them again. This covers a few subtle cases:
+ // 1) The user enters an incorrect passphrase *and* disabled the encrypted
+ // data types. In that case we want to notify the user that the
+ // passphrase was incorrect even though there are no longer any encrypted
+ // types enabled (IsPassphraseRequiredForDecryption() == false).
+ // 2) The user doesn't enter any passphrase. In this case, we won't call
+ // SetDecryptionPassphrase() (passphrase_failed == false), but we still
+ // want to display an error message to let the user know that their
+ // blank passphrase entry is not acceptable.
+ // 3) The user just enabled an encrypted data type - in this case we don't
+ // want to display an "invalid passphrase" error, since it's the first
+ // time the user is seeing the prompt.
+ DisplayConfigureSync(passphrase_failed || user_was_prompted_for_passphrase);
+ } else {
+ // No passphrase is required from the user so mark the configuration as
+ // complete and close the sync setup overlay.
+ ConfigureSyncDone();
+ }
+
+ ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_CUSTOMIZE);
+ if (configuration.encrypt_all)
+ ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_ENCRYPT);
+ if (configuration.passphrase_is_gaia && !configuration.passphrase.empty())
+ ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_PASSPHRASE);
+ if (!configuration.sync_everything)
+ ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_CHOOSE);
+}
+
+void SyncSetupHandler::HandleShowSetupUI(const base::ListValue* args) {
+ if (!GetSyncService()) {
+ DLOG(WARNING) << "Cannot display sync UI when sync is disabled";
+ CloseUI();
+ return;
+ }
+
+ SigninManagerBase* signin =
+ SigninManagerFactory::GetForProfile(GetProfile());
+ if (!signin->IsAuthenticated()) {
+ // For web-based signin, the signin page is not displayed in an overlay
+ // on the settings page. So if we get here, it must be due to the user
+ // cancelling signin (by reloading the sync settings page during initial
+ // signin) or by directly navigating to settings/syncSetup
+ // (http://crbug.com/229836). So just exit and go back to the settings page.
+ DLOG(WARNING) << "Cannot display sync setup UI when not signed in";
+ CloseUI();
+ return;
+ }
+
+ // If a setup wizard is already present, but not on this page, close the
+ // blank setup overlay on this page by showing the "done" page. This can
+ // happen if the user navigates to chrome://settings/syncSetup in more than
+ // one tab. See crbug.com/261566.
+ // Note: The following block will transfer focus to the existing wizard.
+ if (IsExistingWizardPresent() && !IsActiveLogin())
+ CloseUI();
+
+ // If a setup wizard is present on this page or another, bring it to focus.
+ // Otherwise, display a new one on this page.
+ if (!FocusExistingWizardIfPresent())
+ OpenSyncSetup(false /* creating_supervised_user */);
+}
+
+#if defined(OS_CHROMEOS)
+// On ChromeOS, we need to sign out the user session to fix an auth error, so
+// the user goes through the real signin flow to generate a new auth token.
+void SyncSetupHandler::HandleAttemptUserExit(const base::ListValue* args) {
+ chrome::AttemptUserExit();
+}
+#endif
+
+#if !defined(OS_CHROMEOS)
+void SyncSetupHandler::HandleStartSignin(const base::ListValue* args) {
+ // Should only be called if the user is not already signed in or has an auth
+ // error.
+ DCHECK(
+ !SigninManagerFactory::GetForProfile(GetProfile())->IsAuthenticated() ||
+ SigninErrorControllerFactory::GetForProfile(GetProfile())->HasError());
+ bool creating_supervised_user = false;
+ args->GetBoolean(0, &creating_supervised_user);
+ OpenSyncSetup(creating_supervised_user);
+}
+
+void SyncSetupHandler::HandleStopSyncing(const base::ListValue* args) {
+ if (GetSyncService())
+ ProfileSyncService::SyncEvent(ProfileSyncService::STOP_FROM_OPTIONS);
+
+ bool delete_profile = false;
+ args->GetBoolean(0, &delete_profile);
+ signin_metrics::SignoutDelete delete_metric =
+ delete_profile ? signin_metrics::SignoutDelete::DELETED
+ : signin_metrics::SignoutDelete::KEEPING;
+ SigninManagerFactory::GetForProfile(GetProfile())
+ ->SignOut(signin_metrics::USER_CLICKED_SIGNOUT_SETTINGS, delete_metric);
+
+ if (delete_profile) {
+ // Do as BrowserOptionsHandler::DeleteProfile().
+ webui::DeleteProfileAtPath(GetProfile()->GetPath(),
+ web_ui(),
+ ProfileMetrics::DELETE_PROFILE_SETTINGS);
+ }
+}
+#endif
+
+void SyncSetupHandler::HandleCloseTimeout(const base::ListValue* args) {
+ CloseSyncSetup();
+}
+
+void SyncSetupHandler::CloseSyncSetup() {
+ // Stop a timer to handle timeout in waiting for checking network connection.
+ engine_start_timer_.reset();
+
+ // Clear the sync startup tracker, since the setup wizard is being closed.
+ sync_startup_tracker_.reset();
+
+ ProfileSyncService* sync_service = GetSyncService();
+ if (IsActiveLogin()) {
+ // Don't log a cancel event if the sync setup dialog is being
+ // automatically closed due to an auth error.
+ if (!sync_service || (!sync_service->IsFirstSetupComplete() &&
+ sync_service->GetAuthError().state() ==
+ GoogleServiceAuthError::NONE)) {
+ if (configuring_sync_) {
+ ProfileSyncService::SyncEvent(
+ ProfileSyncService::CANCEL_DURING_CONFIGURE);
+
+ // If the user clicked "Cancel" while setting up sync, disable sync
+ // because we don't want the sync engine to remain in the
+ // first-setup-incomplete state.
+ // Note: In order to disable sync across restarts on Chrome OS,
+ // we must call RequestStop(CLEAR_DATA), which suppresses sync startup
+ // in addition to disabling it.
+ if (sync_service) {
+ DVLOG(1) << "Sync setup aborted by user action";
+ sync_service->RequestStop(ProfileSyncService::CLEAR_DATA);
+ #if !defined(OS_CHROMEOS)
+ // Sign out the user on desktop Chrome if they click cancel during
+ // initial setup.
+ // TODO(rsimha): Revisit this for M30. See http://crbug.com/252049.
+ if (sync_service->IsFirstSetupInProgress()) {
+ SigninManagerFactory::GetForProfile(GetProfile())
+ ->SignOut(signin_metrics::ABORT_SIGNIN,
+ signin_metrics::SignoutDelete::IGNORE_METRIC);
+ }
+ #endif
+ }
+ }
+ }
+ }
+
+ LoginUIService* service = GetLoginUIService();
+ if (service)
+ service->LoginUIClosed(this);
+
+ // Alert the sync service anytime the sync setup dialog is closed. This can
+ // happen due to the user clicking the OK or Cancel button, or due to the
+ // dialog being closed by virtue of sync being disabled in the background.
+ sync_blocker_.reset();
+
+ configuring_sync_ = false;
+}
+
+void SyncSetupHandler::OpenSyncSetup(bool creating_supervised_user) {
+ if (!PrepareSyncSetup())
+ return;
+
+ // There are several different UI flows that can bring the user here:
+ // 1) Signin promo.
+ // 2) Normal signin through settings page (IsAuthenticated() is false).
+ // 3) Previously working credentials have expired.
+ // 4) User is signed in, but has stopped sync via the google dashboard, and
+ // signout is prohibited by policy so we need to force a re-auth.
+ // 5) User clicks [Advanced Settings] button on options page while already
+ // logged in.
+ // 6) One-click signin (credentials are already available, so should display
+ // sync configure UI, not login UI).
+ // 7) User re-enables sync after disabling it via advanced settings.
+#if !defined(OS_CHROMEOS)
+ SigninManagerBase* signin =
+ SigninManagerFactory::GetForProfile(GetProfile());
+
+ if (!signin->IsAuthenticated() ||
+ SigninErrorControllerFactory::GetForProfile(GetProfile())->HasError()) {
+ // User is not logged in (cases 1-2), or login has been specially requested
+ // because previously working credentials have expired (case 3). Close sync
+ // setup including any visible overlays, and display the gaia auth page.
+ // Control will be returned to the sync settings page once auth is complete.
+ CloseUI();
+ DisplayGaiaLogin(
+ creating_supervised_user ?
+ signin_metrics::AccessPoint::ACCESS_POINT_SUPERVISED_USER :
+ signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS);
+ return;
+ }
+#endif
+ if (!GetSyncService()) {
+ // This can happen if the user directly navigates to /settings/syncSetup.
+ DLOG(WARNING) << "Cannot display sync UI when sync is disabled";
+ CloseUI();
+ return;
+ }
+
+ // User is already logged in. They must have brought up the config wizard
+ // via the "Advanced..." button or through One-Click signin (cases 4-6), or
+ // they are re-enabling sync after having disabled it (case 7).
+ DisplayConfigureSync(false);
+}
+
+void SyncSetupHandler::OpenConfigureSync() {
+ if (!PrepareSyncSetup())
+ return;
+
+ DisplayConfigureSync(false);
+}
+
+void SyncSetupHandler::FocusUI() {
+ DCHECK(IsActiveLogin());
+ WebContents* web_contents = web_ui()->GetWebContents();
+ web_contents->GetDelegate()->ActivateContents(web_contents);
+}
+
+void SyncSetupHandler::CloseUI() {
+ CloseSyncSetup();
+ base::Value page("done");
+ web_ui()->CallJavascriptFunctionUnsafe("SyncSetupOverlay.showSyncSetupPage",
+ page);
+}
+
+bool SyncSetupHandler::IsExistingWizardPresent() {
+ LoginUIService* service = GetLoginUIService();
+ DCHECK(service);
+ return service->current_login_ui() != NULL;
+}
+
+bool SyncSetupHandler::FocusExistingWizardIfPresent() {
+ if (!IsExistingWizardPresent())
+ return false;
+
+ LoginUIService* service = GetLoginUIService();
+ DCHECK(service);
+ service->current_login_ui()->FocusUI();
+ return true;
+}
+
+void SyncSetupHandler::DisplayConfigureSync(bool passphrase_failed) {
+ // Should never call this when we are not signed in.
+ DCHECK(SigninManagerFactory::GetForProfile(
+ GetProfile())->IsAuthenticated());
+ ProfileSyncService* service = GetSyncService();
+ DCHECK(service);
+ if (!service->IsEngineInitialized()) {
+ service->RequestStart();
+
+ // See if it's even possible to bring up the sync engine - if not
+ // (unrecoverable error?), don't bother displaying a spinner that will be
+ // immediately closed because this leads to some ugly infinite UI loop (see
+ // http://crbug.com/244769).
+ if (SyncStartupTracker::GetSyncServiceState(GetProfile()) !=
+ SyncStartupTracker::SYNC_STARTUP_ERROR) {
+ DisplaySpinner();
+ }
+
+ // Start SyncSetupTracker to wait for sync to initialize.
+ sync_startup_tracker_.reset(
+ new SyncStartupTracker(GetProfile(), this));
+ return;
+ }
+
+ // Should only get here if user is signed in and sync is initialized, so no
+ // longer need a SyncStartupTracker.
+ sync_startup_tracker_.reset();
+ configuring_sync_ = true;
+ DCHECK(service->IsEngineInitialized())
+ << "Cannot configure sync until the sync engine is initialized";
+
+ // Setup args for the sync configure screen:
+ // syncAllDataTypes: true if the user wants to sync everything
+ // syncNothing: true if the user wants to sync nothing
+ // <data_type>Registered: true if the associated data type is supported
+ // <data_type>Synced: true if the user wants to sync that specific data type
+ // paymentsIntegrationEnabled: true if the user wants Payments integration
+ // encryptionEnabled: true if sync supports encryption
+ // encryptAllData: true if user wants to encrypt all data (not just
+ // passwords)
+ // usePassphrase: true if the data is encrypted with a secondary passphrase
+ // show_passphrase: true if a passphrase is needed to decrypt the sync data
+ base::DictionaryValue args;
+
+ // Tell the UI layer which data types are registered/enabled by the user.
+ const syncer::ModelTypeSet registered_types =
+ service->GetRegisteredDataTypes();
+ const syncer::ModelTypeSet preferred_types = service->GetPreferredDataTypes();
+ const syncer::ModelTypeSet enforced_types = service->GetForcedDataTypes();
+ syncer::ModelTypeNameMap type_names = syncer::GetUserSelectableTypeNameMap();
+ for (syncer::ModelTypeNameMap::const_iterator it = type_names.begin();
+ it != type_names.end(); ++it) {
+ syncer::ModelType sync_type = it->first;
+ const std::string key_name = it->second;
+ args.SetBoolean(key_name + "Registered", registered_types.Has(sync_type));
+ args.SetBoolean(key_name + "Synced", preferred_types.Has(sync_type));
+ args.SetBoolean(key_name + "Enforced", enforced_types.Has(sync_type));
+ // TODO(treib): How do we want to handle pref groups, i.e. when only some of
+ // the sync types behind a checkbox are force-enabled? crbug.com/403326
+ }
+ PrefService* pref_service = GetProfile()->GetPrefs();
+ syncer::SyncPrefs sync_prefs(pref_service);
+ args.SetBoolean("passphraseFailed", passphrase_failed);
+ args.SetBoolean("syncAllDataTypes", sync_prefs.HasKeepEverythingSynced());
+ args.SetBoolean("syncNothing", false); // Always false during initial setup.
+ args.SetBoolean(
+ "paymentsIntegrationEnabled",
+ pref_service->GetBoolean(autofill::prefs::kAutofillWalletImportEnabled));
+ args.SetBoolean("encryptAllData", service->IsEncryptEverythingEnabled());
+ args.SetBoolean("encryptAllDataAllowed",
+ service->IsEncryptEverythingAllowed());
+
+ // We call IsPassphraseRequired() here, instead of calling
+ // IsPassphraseRequiredForDecryption(), because we want to show the passphrase
+ // UI even if no encrypted data types are enabled.
+ args.SetBoolean("showPassphrase", service->IsPassphraseRequired());
+
+ // To distinguish between FROZEN_IMPLICIT_PASSPHRASE and CUSTOM_PASSPHRASE
+ // we only set usePassphrase for PassphraseType::CUSTOM_PASSPHRASE.
+ args.SetBoolean("usePassphrase",
+ service->GetPassphraseType() ==
+ syncer::PassphraseType::CUSTOM_PASSPHRASE);
+ base::Time passphrase_time = service->GetExplicitPassphraseTime();
+ syncer::PassphraseType passphrase_type = service->GetPassphraseType();
+ if (!passphrase_time.is_null()) {
+ base::string16 passphrase_time_str =
+ base::TimeFormatShortDate(passphrase_time);
+ args.SetString(
+ "enterPassphraseBody",
+ GetStringFUTF16(IDS_SYNC_ENTER_PASSPHRASE_BODY_WITH_DATE,
+ passphrase_time_str));
+ args.SetString(
+ "enterGooglePassphraseBody",
+ GetStringFUTF16(IDS_SYNC_ENTER_GOOGLE_PASSPHRASE_BODY_WITH_DATE,
+ passphrase_time_str));
+ switch (passphrase_type) {
+ case syncer::PassphraseType::FROZEN_IMPLICIT_PASSPHRASE:
+ args.SetString(
+ "fullEncryptionBody",
+ GetStringFUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_GOOGLE_WITH_DATE,
+ passphrase_time_str));
+ break;
+ case syncer::PassphraseType::CUSTOM_PASSPHRASE:
+ args.SetString(
+ "fullEncryptionBody",
+ GetStringFUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM_WITH_DATE,
+ passphrase_time_str));
+ break;
+ default:
+ args.SetString(
+ "fullEncryptionBody",
+ GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM));
+ break;
+ }
+ } else if (passphrase_type == syncer::PassphraseType::CUSTOM_PASSPHRASE) {
+ args.SetString(
+ "fullEncryptionBody",
+ GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM));
+ } else {
+ args.SetString(
+ "fullEncryptionBody",
+ GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_DATA));
+ }
+
+ base::Value page("configure");
+ web_ui()->CallJavascriptFunctionUnsafe("SyncSetupOverlay.showSyncSetupPage",
+ page, args);
+
+ // Make sure the tab used for the Gaia sign in does not cover the settings
+ // tab.
+ FocusUI();
+}
+
+LoginUIService* SyncSetupHandler::GetLoginUIService() const {
+ return LoginUIServiceFactory::GetForProfile(GetProfile());
+}
diff --git a/chromium/chrome/browser/ui/webui/options/sync_setup_handler.h b/chromium/chrome/browser/ui/webui/options/sync_setup_handler.h
new file mode 100644
index 00000000000..993293f3633
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/sync_setup_handler.h
@@ -0,0 +1,175 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_SYNC_SETUP_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_SYNC_SETUP_HANDLER_H_
+
+#include <memory>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/timer/timer.h"
+#include "build/build_config.h"
+#include "chrome/browser/sync/sync_startup_tracker.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "chrome/browser/ui/webui/signin/login_ui_service.h"
+
+class LoginUIService;
+
+namespace browser_sync {
+class ProfileSyncService;
+} // namespace browser_sync
+
+namespace signin_metrics {
+enum class AccessPoint;
+} // namespace signin_metrics
+
+namespace syncer {
+class SyncSetupInProgressHandle;
+} // namespace syncer
+
+class SyncSetupHandler : public options::OptionsPageUIHandler,
+ public SyncStartupTracker::Observer,
+ public LoginUIService::LoginUI {
+ public:
+ SyncSetupHandler();
+ ~SyncSetupHandler() override;
+
+ // OptionsPageUIHandler implementation.
+ void GetLocalizedValues(base::DictionaryValue* localized_strings) override;
+ void RegisterMessages() override;
+
+ // SyncStartupTracker::Observer implementation;
+ void SyncStartupCompleted() override;
+ void SyncStartupFailed() override;
+
+ // LoginUIService::LoginUI implementation.
+ void FocusUI() override;
+
+ static void GetStaticLocalizedValues(
+ base::DictionaryValue* localized_strings,
+ content::WebUI* web_ui);
+
+ // Initializes the sync setup flow and shows the setup UI.
+ void OpenSyncSetup(bool creating_supervised_user);
+
+ // Shows advanced configuration dialog without going through sign in dialog.
+ // Kicks the sync engine if necessary with showing spinner dialog until it
+ // gets ready.
+ void OpenConfigureSync();
+
+ // Terminates the sync setup flow.
+ void CloseSyncSetup();
+
+ protected:
+ friend class SyncSetupHandlerTest;
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerTest,
+ DisplayConfigureWithEngineDisabledAndCancel);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerTest, HandleSetupUIWhenSyncDisabled);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerTest, SelectCustomEncryption);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerTest, ShowSyncSetupWhenNotSignedIn);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerTest, SuccessfullySetPassphrase);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerTest, TestSyncEverything);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerTest, TestSyncNothing);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerTest, TestSyncAllManually);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerTest, TestPassphraseStillRequired);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerTest, TestSyncIndividualTypes);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerTest, TurnOnEncryptAll);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerTest, TurnOnEncryptAllDisallowed);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerTest, UnsuccessfullySetPassphrase);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerNonCrosTest,
+ UnrecoverableErrorInitializingSync);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerNonCrosTest,
+ GaiaErrorInitializingSync);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerNonCrosTest, HandleCaptcha);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerNonCrosTest, HandleGaiaAuthFailure);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerNonCrosTest,
+ SubmitAuthWithInvalidUsername);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupHandlerFirstSigninTest, DisplayBasicLogin);
+
+ bool is_configuring_sync() const { return configuring_sync_; }
+
+ // Called when configuring sync is done to close the dialog and start syncing.
+ void ConfigureSyncDone();
+
+ // Helper routine that gets the ProfileSyncService associated with the parent
+ // profile.
+ browser_sync::ProfileSyncService* GetSyncService() const;
+
+ // Returns the LoginUIService for the parent profile.
+ LoginUIService* GetLoginUIService() const;
+
+ private:
+ // Callbacks from the page.
+ void OnDidClosePage(const base::ListValue* args);
+ void HandleConfigure(const base::ListValue* args);
+ void HandlePassphraseEntry(const base::ListValue* args);
+ void HandlePassphraseCancel(const base::ListValue* args);
+ void HandleShowSetupUI(const base::ListValue* args);
+ void HandleAttemptUserExit(const base::ListValue* args);
+ void HandleStartSignin(const base::ListValue* args);
+ void HandleStopSyncing(const base::ListValue* args);
+ void HandleCloseTimeout(const base::ListValue* args);
+#if !defined(OS_CHROMEOS)
+ // Displays the GAIA login form.
+ void DisplayGaiaLogin(signin_metrics::AccessPoint access_point);
+
+ // When web-flow is enabled, displays the Gaia login form in a new tab.
+ // This function is virtual so that tests can override.
+ virtual void DisplayGaiaLoginInNewTabOrWindow(
+ signin_metrics::AccessPoint access_point);
+#endif
+
+ // Helper routine that gets the Profile associated with this object (virtual
+ // so tests can override).
+ virtual Profile* GetProfile() const;
+
+ // A utility function to call before actually showing setup dialog. Makes sure
+ // that a new dialog can be shown and sets flag that setup is in progress.
+ bool PrepareSyncSetup();
+
+ // 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
+ // is running in the background.
+ void DisplaySpinner();
+
+ // Displays an error dialog which shows timeout of starting the sync engine.
+ void DisplayTimeout();
+
+ // Closes the associated sync settings page.
+ void CloseUI();
+
+ // Returns true if this object is the active login object.
+ bool IsActiveLogin() const;
+
+ // If a wizard already exists, return true. Otherwise, return false.
+ bool IsExistingWizardPresent();
+
+ // If a wizard already exists, focus it and return true.
+ bool FocusExistingWizardIfPresent();
+
+ // Display the configure sync UI. If |passphrase_failed| is true, the account
+ // requires a passphrase and one hasn't been provided or it was invalid.
+ void DisplayConfigureSync(bool passphrase_failed);
+
+ // Helper object used to wait for the sync engine to startup.
+ std::unique_ptr<SyncStartupTracker> sync_startup_tracker_;
+
+ // Prevents Sync from running until configuration is complete.
+ std::unique_ptr<syncer::SyncSetupInProgressHandle> sync_blocker_;
+
+ // Set to true whenever the sync configure UI is visible. This is used to tell
+ // what stage of the setup wizard the user was in and to update the UMA
+ // histograms in the case that the user cancels out.
+ bool configuring_sync_;
+
+ // The OneShotTimer object used to timeout of starting the sync engine
+ // service.
+ std::unique_ptr<base::OneShotTimer> engine_start_timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncSetupHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_SYNC_SETUP_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/options/sync_setup_handler_unittest.cc b/chromium/chrome/browser/ui/webui/options/sync_setup_handler_unittest.cc
new file mode 100644
index 00000000000..e24b055d615
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/options/sync_setup_handler_unittest.cc
@@ -0,0 +1,951 @@
+// 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/ui/webui/options/sync_setup_handler.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/json/json_writer.h"
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/signin/fake_signin_manager_builder.h"
+#include "chrome/browser/signin/signin_error_controller_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/sync/profile_sync_test_util.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"
+#include "chrome/common/pref_names.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/prefs/pref_service.h"
+#include "components/signin/core/browser/fake_auth_status_provider.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/sync/base/sync_prefs.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/test/test_browser_thread.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_web_ui.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/layout.h"
+
+using ::testing::_;
+using ::testing::Mock;
+using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::Values;
+using browser_sync::ProfileSyncService;
+using browser_sync::ProfileSyncServiceMock;
+
+typedef GoogleServiceAuthError AuthError;
+
+namespace {
+
+MATCHER_P(ModelTypeSetMatches, value, "") {
+ return arg == value;
+}
+
+const char kTestUser[] = "chrome.p13n.test@gmail.com";
+
+// Returns a ModelTypeSet with all user selectable types set.
+syncer::ModelTypeSet GetAllTypes() {
+ return syncer::UserSelectableTypes();
+}
+
+enum SyncAllDataConfig {
+ SYNC_ALL_DATA,
+ CHOOSE_WHAT_TO_SYNC
+};
+
+enum EncryptAllConfig {
+ ENCRYPT_ALL_DATA,
+ ENCRYPT_PASSWORDS
+};
+
+enum PaymentsIntegrationConfig {
+ PAYMENTS_INTEGRATION_ENABLED,
+ PAYMENTS_INTEGRATION_DISABLED
+};
+
+// Create a json-format string with the key/value pairs appropriate for a call
+// to HandleConfigure(). If |extra_values| is non-null, then the values from
+// the passed dictionary are added to the json.
+std::string GetConfiguration(const base::DictionaryValue* extra_values,
+ SyncAllDataConfig sync_all,
+ syncer::ModelTypeSet types,
+ const std::string& passphrase,
+ EncryptAllConfig encrypt_all,
+ PaymentsIntegrationConfig payments_integration) {
+ base::DictionaryValue result;
+ if (extra_values)
+ result.MergeDictionary(extra_values);
+ result.SetBoolean("syncAllDataTypes", sync_all == SYNC_ALL_DATA);
+ result.SetBoolean("encryptAllData", encrypt_all == ENCRYPT_ALL_DATA);
+ result.SetBoolean("usePassphrase", !passphrase.empty());
+ if (!passphrase.empty())
+ result.SetString("passphrase", passphrase);
+ // Add all of our data types.
+ result.SetBoolean("appsSynced", types.Has(syncer::APPS));
+ result.SetBoolean("autofillSynced", types.Has(syncer::AUTOFILL));
+ result.SetBoolean("bookmarksSynced", types.Has(syncer::BOOKMARKS));
+ result.SetBoolean("extensionsSynced", types.Has(syncer::EXTENSIONS));
+ result.SetBoolean("passwordsSynced", types.Has(syncer::PASSWORDS));
+ result.SetBoolean("preferencesSynced", types.Has(syncer::PREFERENCES));
+ result.SetBoolean("tabsSynced", types.Has(syncer::PROXY_TABS));
+ result.SetBoolean("themesSynced", types.Has(syncer::THEMES));
+ result.SetBoolean("typedUrlsSynced", types.Has(syncer::TYPED_URLS));
+ result.SetBoolean("paymentsIntegrationEnabled",
+ payments_integration == PAYMENTS_INTEGRATION_ENABLED);
+ std::string args;
+ base::JSONWriter::Write(result, &args);
+ return args;
+}
+
+// Checks whether the passed |dictionary| contains a |key| with the given
+// |expected_value|. If |omit_if_false| is true, then the value should only
+// be present if |expected_value| is true.
+void CheckBool(const base::DictionaryValue* dictionary,
+ const std::string& key,
+ bool expected_value,
+ bool omit_if_false) {
+ if (omit_if_false && !expected_value) {
+ EXPECT_FALSE(dictionary->HasKey(key)) <<
+ "Did not expect to find value for " << key;
+ } else {
+ bool actual_value;
+ EXPECT_TRUE(dictionary->GetBoolean(key, &actual_value)) <<
+ "No value found for " << key;
+ EXPECT_EQ(expected_value, actual_value) <<
+ "Mismatch found for " << key;
+ }
+}
+
+void CheckBool(const base::DictionaryValue* dictionary,
+ const std::string& key,
+ bool expected_value) {
+ return CheckBool(dictionary, key, expected_value, false);
+}
+
+// Checks to make sure that the values stored in |dictionary| match the values
+// expected by the showSyncSetupPage() JS function for a given set of data
+// types.
+void CheckConfigDataTypeArguments(const base::DictionaryValue* dictionary,
+ SyncAllDataConfig config,
+ syncer::ModelTypeSet types) {
+ CheckBool(dictionary, "syncAllDataTypes", config == SYNC_ALL_DATA);
+ CheckBool(dictionary, "appsSynced", types.Has(syncer::APPS));
+ CheckBool(dictionary, "autofillSynced", types.Has(syncer::AUTOFILL));
+ CheckBool(dictionary, "bookmarksSynced", types.Has(syncer::BOOKMARKS));
+ CheckBool(dictionary, "extensionsSynced", types.Has(syncer::EXTENSIONS));
+ CheckBool(dictionary, "passwordsSynced", types.Has(syncer::PASSWORDS));
+ CheckBool(dictionary, "preferencesSynced", types.Has(syncer::PREFERENCES));
+ CheckBool(dictionary, "tabsSynced", types.Has(syncer::PROXY_TABS));
+ CheckBool(dictionary, "themesSynced", types.Has(syncer::THEMES));
+ CheckBool(dictionary, "typedUrlsSynced", types.Has(syncer::TYPED_URLS));
+}
+
+
+} // namespace
+
+class TestingSyncSetupHandler : public SyncSetupHandler {
+ public:
+ TestingSyncSetupHandler(content::WebUI* web_ui, Profile* profile)
+ : profile_(profile) {
+ set_web_ui(web_ui);
+ }
+ ~TestingSyncSetupHandler() override { set_web_ui(NULL); }
+
+ void FocusUI() override {}
+
+ Profile* GetProfile() const override { return profile_; }
+
+ using SyncSetupHandler::is_configuring_sync;
+
+ private:
+#if !defined(OS_CHROMEOS)
+ void DisplayGaiaLoginInNewTabOrWindow(
+ signin_metrics::AccessPoint access_point) override {}
+#endif
+
+ // Weak pointer to parent profile.
+ Profile* profile_;
+ DISALLOW_COPY_AND_ASSIGN(TestingSyncSetupHandler);
+};
+
+// The boolean parameter indicates whether the test is run with ClientOAuth
+// or not. The test parameter is a bool: whether or not to test with/
+// /ClientLogin enabled or not.
+class SyncSetupHandlerTest : public testing::Test {
+ public:
+ SyncSetupHandlerTest() : error_(GoogleServiceAuthError::NONE) {}
+ void SetUp() override {
+ error_ = GoogleServiceAuthError::AuthErrorNone();
+
+ TestingProfile::Builder builder;
+ builder.AddTestingFactory(SigninManagerFactory::GetInstance(),
+ BuildFakeSigninManagerBase);
+ profile_ = builder.Build();
+
+ // Sign in the user.
+ mock_signin_ = static_cast<SigninManagerBase*>(
+ SigninManagerFactory::GetForProfile(profile_.get()));
+ std::string username = GetTestUser();
+ if (!username.empty())
+ mock_signin_->SetAuthenticatedAccountInfo(username, username);
+
+ mock_pss_ = static_cast<ProfileSyncServiceMock*>(
+ ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
+ profile_.get(), BuildMockProfileSyncService));
+ EXPECT_CALL(*mock_pss_, GetAuthError()).WillRepeatedly(ReturnRef(error_));
+ ON_CALL(*mock_pss_, GetPassphraseType())
+ .WillByDefault(Return(syncer::PassphraseType::IMPLICIT_PASSPHRASE));
+ ON_CALL(*mock_pss_, GetExplicitPassphraseTime()).WillByDefault(
+ Return(base::Time()));
+ ON_CALL(*mock_pss_, GetRegisteredDataTypes())
+ .WillByDefault(Return(syncer::ModelTypeSet()));
+
+ mock_pss_->Initialize();
+
+ handler_.reset(new TestingSyncSetupHandler(&web_ui_, profile_.get()));
+ }
+
+ // Setup the expectations for calls made when displaying the config page.
+ void SetDefaultExpectationsForConfigPage() {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, GetRegisteredDataTypes())
+ .WillRepeatedly(Return(GetAllTypes()));
+ EXPECT_CALL(*mock_pss_, GetPreferredDataTypes())
+ .WillRepeatedly(Return(GetAllTypes()));
+ EXPECT_CALL(*mock_pss_, GetActiveDataTypes())
+ .WillRepeatedly(Return(GetAllTypes()));
+ EXPECT_CALL(*mock_pss_, IsEncryptEverythingAllowed())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsEncryptEverythingEnabled())
+ .WillRepeatedly(Return(false));
+ }
+
+ void SetupInitializedProfileSyncService() {
+ // An initialized ProfileSyncService will have already completed sync setup
+ // and will have an initialized sync engine.
+ ASSERT_TRUE(mock_signin_->IsInitialized());
+ EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(true));
+ }
+
+ void ExpectConfig() {
+ ASSERT_EQ(1U, web_ui_.call_data().size());
+ const content::TestWebUI::CallData& data = *web_ui_.call_data()[0];
+ EXPECT_EQ("SyncSetupOverlay.showSyncSetupPage", data.function_name());
+ std::string page;
+ ASSERT_TRUE(data.arg1()->GetAsString(&page));
+ EXPECT_EQ(page, "configure");
+ }
+
+ void ExpectDone() {
+ ASSERT_EQ(1U, web_ui_.call_data().size());
+ const content::TestWebUI::CallData& data = *web_ui_.call_data()[0];
+ EXPECT_EQ("SyncSetupOverlay.showSyncSetupPage", data.function_name());
+ std::string page;
+ ASSERT_TRUE(data.arg1()->GetAsString(&page));
+ EXPECT_EQ(page, "done");
+ }
+
+ void ExpectSpinnerAndClose() {
+ // We expect a call to SyncSetupOverlay.showSyncSetupPage.
+ EXPECT_EQ(1U, web_ui_.call_data().size());
+ const content::TestWebUI::CallData& data = *web_ui_.call_data()[0];
+ EXPECT_EQ("SyncSetupOverlay.showSyncSetupPage", data.function_name());
+
+ std::string page;
+ ASSERT_TRUE(data.arg1()->GetAsString(&page));
+ EXPECT_EQ(page, "spinner");
+ // Cancelling the spinner dialog will cause CloseSyncSetup().
+ handler_->CloseSyncSetup();
+ EXPECT_EQ(NULL,
+ LoginUIServiceFactory::GetForProfile(
+ profile_.get())->current_login_ui());
+ }
+
+ // It's difficult to notify sync listeners when using a ProfileSyncServiceMock
+ // so this helper routine dispatches an OnStateChanged() notification to the
+ // SyncStartupTracker.
+ void NotifySyncListeners() {
+ if (handler_->sync_startup_tracker_)
+ handler_->sync_startup_tracker_->OnStateChanged(mock_pss_);
+ }
+
+ virtual std::string GetTestUser() {
+ return std::string(kTestUser);
+ }
+
+ content::TestBrowserThreadBundle thread_bundle_;
+ std::unique_ptr<Profile> profile_;
+ ProfileSyncServiceMock* mock_pss_;
+ GoogleServiceAuthError error_;
+ SigninManagerBase* mock_signin_;
+ content::TestWebUI web_ui_;
+ std::unique_ptr<TestingSyncSetupHandler> handler_;
+};
+
+class SyncSetupHandlerFirstSigninTest : public SyncSetupHandlerTest {
+ std::string GetTestUser() override { return std::string(); }
+};
+
+TEST_F(SyncSetupHandlerTest, Basic) {
+}
+
+#if !defined(OS_CHROMEOS)
+TEST_F(SyncSetupHandlerFirstSigninTest, 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_);
+ manager->SignOut(signin_metrics::SIGNOUT_TEST,
+ signin_metrics::SignoutDelete::IGNORE_METRIC);
+ base::ListValue list_args;
+ handler_->HandleStartSignin(&list_args);
+
+ // Sync setup hands off control to the gaia login tab.
+ EXPECT_EQ(NULL,
+ LoginUIServiceFactory::GetForProfile(
+ profile_.get())->current_login_ui());
+
+ ASSERT_FALSE(handler_->is_configuring_sync());
+
+ handler_->CloseSyncSetup();
+ EXPECT_EQ(NULL,
+ LoginUIServiceFactory::GetForProfile(
+ profile_.get())->current_login_ui());
+}
+
+TEST_F(SyncSetupHandlerTest, ShowSyncSetupWhenNotSignedIn) {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
+ handler_->HandleShowSetupUI(NULL);
+
+ // We expect a call to SyncSetupOverlay.showSyncSetupPage.
+ ASSERT_EQ(1U, web_ui_.call_data().size());
+ const content::TestWebUI::CallData& data = *web_ui_.call_data()[0];
+ EXPECT_EQ("SyncSetupOverlay.showSyncSetupPage", data.function_name());
+
+ ASSERT_FALSE(handler_->is_configuring_sync());
+ EXPECT_EQ(NULL,
+ LoginUIServiceFactory::GetForProfile(
+ profile_.get())->current_login_ui());
+}
+#endif // !defined(OS_CHROMEOS)
+
+// Verifies that the sync setup is terminated correctly when the
+// sync is disabled.
+TEST_F(SyncSetupHandlerTest, HandleSetupUIWhenSyncDisabled) {
+ EXPECT_CALL(*mock_pss_, IsManaged()).WillRepeatedly(Return(true));
+ handler_->HandleShowSetupUI(NULL);
+
+ // Sync setup is closed when sync is disabled.
+ EXPECT_EQ(NULL,
+ LoginUIServiceFactory::GetForProfile(
+ profile_.get())->current_login_ui());
+ ASSERT_FALSE(handler_->is_configuring_sync());
+}
+
+// Verifies that the handler correctly handles a cancellation when
+// it is displaying the spinner to the user.
+TEST_F(SyncSetupHandlerTest, DisplayConfigureWithEngineDisabledAndCancel) {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
+ error_ = GoogleServiceAuthError::AuthErrorNone();
+ EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
+
+ // We're simulating a user setting up sync, which would cause the engine to
+ // kick off initialization, but not download user data types. The sync
+ // engine will try to download control data types (e.g encryption info), but
+ // that won't finish for this test as we're simulating cancelling while the
+ // spinner is showing.
+ handler_->HandleShowSetupUI(NULL);
+
+ EXPECT_EQ(handler_.get(),
+ LoginUIServiceFactory::GetForProfile(
+ profile_.get())->current_login_ui());
+
+ ExpectSpinnerAndClose();
+}
+
+// Verifies that the handler correctly transitions from showing the spinner
+// to showing a configuration page when sync setup completes successfully.
+TEST_F(SyncSetupHandlerTest,
+ DisplayConfigureWithEngineDisabledAndSyncStartupCompleted) {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
+ error_ = GoogleServiceAuthError::AuthErrorNone();
+ // Sync engine is stopped initially, and will start up.
+ EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
+ SetDefaultExpectationsForConfigPage();
+
+ handler_->OpenSyncSetup(false /* creating_supervised_user */);
+
+ // We expect a call to SyncSetupOverlay.showSyncSetupPage.
+ EXPECT_EQ(1U, web_ui_.call_data().size());
+
+ const content::TestWebUI::CallData& data0 = *web_ui_.call_data()[0];
+ EXPECT_EQ("SyncSetupOverlay.showSyncSetupPage", data0.function_name());
+ std::string page;
+ ASSERT_TRUE(data0.arg1()->GetAsString(&page));
+ EXPECT_EQ(page, "spinner");
+
+ Mock::VerifyAndClearExpectations(mock_pss_);
+ // Now, act as if the ProfileSyncService has started up.
+ SetDefaultExpectationsForConfigPage();
+ EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(true));
+ error_ = GoogleServiceAuthError::AuthErrorNone();
+ EXPECT_CALL(*mock_pss_, GetAuthError()).WillRepeatedly(ReturnRef(error_));
+ NotifySyncListeners();
+
+ // We expect a second call to SyncSetupOverlay.showSyncSetupPage.
+ EXPECT_EQ(2U, web_ui_.call_data().size());
+ const content::TestWebUI::CallData& data1 = *web_ui_.call_data().back();
+ EXPECT_EQ("SyncSetupOverlay.showSyncSetupPage", data1.function_name());
+ ASSERT_TRUE(data1.arg1()->GetAsString(&page));
+ EXPECT_EQ(page, "configure");
+ const base::DictionaryValue* dictionary = nullptr;
+ ASSERT_TRUE(data1.arg2()->GetAsDictionary(&dictionary));
+ CheckBool(dictionary, "passphraseFailed", false);
+ CheckBool(dictionary, "syncAllDataTypes", true);
+ CheckBool(dictionary, "encryptAllDataAllowed", true);
+ CheckBool(dictionary, "encryptAllData", false);
+ CheckBool(dictionary, "usePassphrase", false);
+}
+
+// Verifies the case where the user cancels after the sync engine has
+// initialized (meaning it already transitioned from the spinner to a proper
+// configuration page, tested by
+// DisplayConfigureWithEngineDisabledAndSyncStartupCompleted), but before the
+// user has continued on.
+TEST_F(SyncSetupHandlerTest,
+ DisplayConfigureWithEngineDisabledAndCancelAfterSigninSuccess) {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
+ error_ = GoogleServiceAuthError::AuthErrorNone();
+ EXPECT_CALL(*mock_pss_, IsEngineInitialized())
+ .WillOnce(Return(false))
+ .WillRepeatedly(Return(true));
+ SetDefaultExpectationsForConfigPage();
+ handler_->OpenSyncSetup(false /* creating_supervised_user */);
+
+ // It's important to tell sync the user cancelled the setup flow before we
+ // tell it we're through with the setup progress.
+ testing::InSequence seq;
+ EXPECT_CALL(*mock_pss_, RequestStop(ProfileSyncService::CLEAR_DATA));
+ EXPECT_CALL(*mock_pss_, OnSetupInProgressHandleDestroyed());
+
+ handler_->CloseSyncSetup();
+ EXPECT_EQ(NULL,
+ LoginUIServiceFactory::GetForProfile(
+ profile_.get())->current_login_ui());
+}
+
+TEST_F(SyncSetupHandlerTest,
+ DisplayConfigureWithEngineDisabledAndSigninFailed) {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
+ error_ = GoogleServiceAuthError::AuthErrorNone();
+ EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
+
+ handler_->OpenSyncSetup(false /* creating_supervised_user */);
+ const content::TestWebUI::CallData& data = *web_ui_.call_data()[0];
+ EXPECT_EQ("SyncSetupOverlay.showSyncSetupPage", data.function_name());
+ std::string page;
+ ASSERT_TRUE(data.arg1()->GetAsString(&page));
+ EXPECT_EQ(page, "spinner");
+ Mock::VerifyAndClearExpectations(mock_pss_);
+ error_ = GoogleServiceAuthError(
+ GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
+ EXPECT_CALL(*mock_pss_, GetAuthError()).WillRepeatedly(ReturnRef(error_));
+ NotifySyncListeners();
+
+ // On failure, the dialog will be closed.
+ EXPECT_EQ(NULL,
+ LoginUIServiceFactory::GetForProfile(
+ profile_.get())->current_login_ui());
+}
+
+#if !defined(OS_CHROMEOS)
+
+class SyncSetupHandlerNonCrosTest : public SyncSetupHandlerTest {
+ public:
+ SyncSetupHandlerNonCrosTest() {}
+};
+
+TEST_F(SyncSetupHandlerNonCrosTest, HandleGaiaAuthFailure) {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, HasUnrecoverableError())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
+ // Open the web UI.
+ handler_->OpenSyncSetup(false /* creating_supervised_user */);
+
+ ASSERT_FALSE(handler_->is_configuring_sync());
+}
+
+// TODO(kochi): We need equivalent tests for ChromeOS.
+TEST_F(SyncSetupHandlerNonCrosTest, UnrecoverableErrorInitializingSync) {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
+ // Open the web UI.
+ handler_->OpenSyncSetup(false /* creating_supervised_user */);
+
+ ASSERT_FALSE(handler_->is_configuring_sync());
+}
+
+TEST_F(SyncSetupHandlerNonCrosTest, GaiaErrorInitializingSync) {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
+ // Open the web UI.
+ handler_->OpenSyncSetup(false /* creating_supervised_user */);
+
+ ASSERT_FALSE(handler_->is_configuring_sync());
+}
+
+#endif // #if !defined(OS_CHROMEOS)
+
+TEST_F(SyncSetupHandlerTest, TestSyncEverything) {
+ std::string args =
+ GetConfiguration(NULL, SYNC_ALL_DATA, GetAllTypes(), std::string(),
+ ENCRYPT_PASSWORDS, PAYMENTS_INTEGRATION_ENABLED);
+ base::ListValue list_args;
+ list_args.AppendString(args);
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ EXPECT_CALL(*mock_pss_, OnUserChoseDatatypes(true, _));
+ handler_->HandleConfigure(&list_args);
+
+ // Ensure that we navigated to the "done" state since we don't need a
+ // passphrase.
+ ExpectDone();
+}
+
+TEST_F(SyncSetupHandlerTest, TurnOnEncryptAll) {
+ std::string args =
+ GetConfiguration(NULL, SYNC_ALL_DATA, GetAllTypes(), std::string(),
+ ENCRYPT_ALL_DATA, PAYMENTS_INTEGRATION_ENABLED);
+ base::ListValue list_args;
+ list_args.AppendString(args);
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsEncryptEverythingAllowed())
+ .WillRepeatedly(Return(true));
+ SetupInitializedProfileSyncService();
+ EXPECT_CALL(*mock_pss_, EnableEncryptEverything());
+ EXPECT_CALL(*mock_pss_, OnUserChoseDatatypes(true, _));
+ handler_->HandleConfigure(&list_args);
+
+ // Ensure that we navigated to the "done" state since we don't need a
+ // passphrase.
+ ExpectDone();
+}
+
+TEST_F(SyncSetupHandlerTest, TestPassphraseStillRequired) {
+ std::string args =
+ GetConfiguration(NULL, SYNC_ALL_DATA, GetAllTypes(), std::string(),
+ ENCRYPT_PASSWORDS, PAYMENTS_INTEGRATION_ENABLED);
+ base::ListValue list_args;
+ list_args.AppendString(args);
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ EXPECT_CALL(*mock_pss_, OnUserChoseDatatypes(_, _));
+ SetDefaultExpectationsForConfigPage();
+
+ // We should navigate back to the configure page since we need a passphrase.
+ handler_->HandleConfigure(&list_args);
+
+ ExpectConfig();
+}
+
+TEST_F(SyncSetupHandlerTest, SuccessfullySetPassphrase) {
+ base::DictionaryValue dict;
+ dict.SetBoolean("isGooglePassphrase", true);
+ std::string args =
+ GetConfiguration(&dict, SYNC_ALL_DATA, GetAllTypes(), "gaiaPassphrase",
+ ENCRYPT_PASSWORDS, PAYMENTS_INTEGRATION_ENABLED);
+ base::ListValue list_args;
+ list_args.AppendString(args);
+ // Act as if an encryption passphrase is required the first time, then never
+ // again after that.
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired()).WillOnce(Return(true));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ EXPECT_CALL(*mock_pss_, OnUserChoseDatatypes(_, _));
+ EXPECT_CALL(*mock_pss_, SetDecryptionPassphrase("gaiaPassphrase")).
+ WillOnce(Return(true));
+
+ handler_->HandleConfigure(&list_args);
+ // We should navigate to "done" page since we finished configuring.
+ ExpectDone();
+}
+
+TEST_F(SyncSetupHandlerTest, SelectCustomEncryption) {
+ base::DictionaryValue dict;
+ dict.SetBoolean("isGooglePassphrase", false);
+ std::string args =
+ GetConfiguration(&dict, SYNC_ALL_DATA, GetAllTypes(), "custom_passphrase",
+ ENCRYPT_PASSWORDS, PAYMENTS_INTEGRATION_ENABLED);
+ base::ListValue list_args;
+ list_args.AppendString(args);
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ EXPECT_CALL(*mock_pss_, OnUserChoseDatatypes(_, _));
+ EXPECT_CALL(*mock_pss_,
+ SetEncryptionPassphrase("custom_passphrase",
+ ProfileSyncService::EXPLICIT));
+
+ handler_->HandleConfigure(&list_args);
+ // We should navigate to "done" page since we finished configuring.
+ ExpectDone();
+}
+
+TEST_F(SyncSetupHandlerTest, UnsuccessfullySetPassphrase) {
+ base::DictionaryValue dict;
+ dict.SetBoolean("isGooglePassphrase", true);
+ std::string args = GetConfiguration(&dict, SYNC_ALL_DATA, GetAllTypes(),
+ "invalid_passphrase", ENCRYPT_PASSWORDS,
+ PAYMENTS_INTEGRATION_ENABLED);
+ base::ListValue list_args;
+ list_args.AppendString(args);
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ EXPECT_CALL(*mock_pss_, OnUserChoseDatatypes(_, _));
+ EXPECT_CALL(*mock_pss_, SetDecryptionPassphrase("invalid_passphrase")).
+ WillOnce(Return(false));
+
+ SetDefaultExpectationsForConfigPage();
+ // We should navigate back to the configure page since we need a passphrase.
+ handler_->HandleConfigure(&list_args);
+
+ ExpectConfig();
+
+ // Make sure we display an error message to the user due to the failed
+ // passphrase.
+ const content::TestWebUI::CallData& data = *web_ui_.call_data()[0];
+ const base::DictionaryValue* dictionary = nullptr;
+ ASSERT_TRUE(data.arg2()->GetAsDictionary(&dictionary));
+ CheckBool(dictionary, "passphraseFailed", true);
+}
+
+// Walks through each user selectable type, and tries to sync just that single
+// data type.
+TEST_F(SyncSetupHandlerTest, TestSyncIndividualTypes) {
+ syncer::ModelTypeSet user_selectable_types = GetAllTypes();
+ syncer::ModelTypeSet::Iterator it;
+ for (it = user_selectable_types.First(); it.Good(); it.Inc()) {
+ syncer::ModelTypeSet type_to_set;
+ type_to_set.Put(it.Get());
+ std::string args =
+ GetConfiguration(NULL, CHOOSE_WHAT_TO_SYNC, type_to_set, std::string(),
+ ENCRYPT_PASSWORDS, PAYMENTS_INTEGRATION_ENABLED);
+ base::ListValue list_args;
+ list_args.AppendString(args);
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ EXPECT_CALL(*mock_pss_,
+ OnUserChoseDatatypes(false, ModelTypeSetMatches(type_to_set)));
+ handler_->HandleConfigure(&list_args);
+
+ ExpectDone();
+ Mock::VerifyAndClearExpectations(mock_pss_);
+ web_ui_.ClearTrackedCalls();
+ }
+}
+
+TEST_F(SyncSetupHandlerTest, TestSyncAllManually) {
+ std::string args =
+ GetConfiguration(NULL, CHOOSE_WHAT_TO_SYNC, GetAllTypes(), std::string(),
+ ENCRYPT_PASSWORDS, PAYMENTS_INTEGRATION_ENABLED);
+ base::ListValue list_args;
+ list_args.AppendString(args);
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ EXPECT_CALL(*mock_pss_,
+ OnUserChoseDatatypes(false, ModelTypeSetMatches(GetAllTypes())));
+ handler_->HandleConfigure(&list_args);
+
+ ExpectDone();
+}
+
+TEST_F(SyncSetupHandlerTest, ShowSyncSetup) {
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ // This should display the sync setup dialog (not login).
+ SetDefaultExpectationsForConfigPage();
+ handler_->OpenSyncSetup(false /* creating_supervised_user */);
+
+ ExpectConfig();
+}
+
+// We do not display signin on chromeos in the case of auth error.
+TEST_F(SyncSetupHandlerTest, ShowSigninOnAuthError) {
+ // Initialize the system to a signed in state, but with an auth error.
+ error_ = GoogleServiceAuthError(
+ GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
+
+ SetupInitializedProfileSyncService();
+ mock_signin_->SetAuthenticatedAccountInfo(kTestUser, kTestUser);
+ FakeAuthStatusProvider provider(
+ SigninErrorControllerFactory::GetForProfile(profile_.get()));
+ provider.SetAuthError(kTestUser, error_);
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
+
+#if defined(OS_CHROMEOS)
+ // On ChromeOS, auth errors are ignored - instead we just try to start the
+ // sync engine (which will fail due to the auth error). This should only
+ // happen if the user manually navigates to chrome://settings/syncSetup -
+ // clicking on the button in the UI will sign the user out rather than
+ // displaying a spinner. Should be no visible UI on ChromeOS in this case.
+ EXPECT_EQ(NULL, LoginUIServiceFactory::GetForProfile(
+ profile_.get())->current_login_ui());
+#else
+
+ // On ChromeOS, this should display the spinner while we try to startup the
+ // sync engine, and on desktop this displays the login dialog.
+ handler_->OpenSyncSetup(false /* creating_supervised_user */);
+
+ // Sync setup is closed when re-auth is in progress.
+ EXPECT_EQ(NULL,
+ LoginUIServiceFactory::GetForProfile(
+ profile_.get())->current_login_ui());
+
+ ASSERT_FALSE(handler_->is_configuring_sync());
+#endif
+}
+
+TEST_F(SyncSetupHandlerTest, ShowSetupSyncEverything) {
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ SetDefaultExpectationsForConfigPage();
+ // This should display the sync setup dialog (not login).
+ handler_->OpenSyncSetup(false /* creating_supervised_user */);
+
+ ExpectConfig();
+ const content::TestWebUI::CallData& data = *web_ui_.call_data()[0];
+ const base::DictionaryValue* dictionary = nullptr;
+ ASSERT_TRUE(data.arg2()->GetAsDictionary(&dictionary));
+ CheckBool(dictionary, "syncAllDataTypes", true);
+ CheckBool(dictionary, "appsRegistered", true);
+ CheckBool(dictionary, "autofillRegistered", true);
+ CheckBool(dictionary, "bookmarksRegistered", true);
+ CheckBool(dictionary, "extensionsRegistered", true);
+ CheckBool(dictionary, "passwordsRegistered", true);
+ CheckBool(dictionary, "preferencesRegistered", true);
+ CheckBool(dictionary, "tabsRegistered", true);
+ CheckBool(dictionary, "themesRegistered", true);
+ CheckBool(dictionary, "typedUrlsRegistered", true);
+ CheckBool(dictionary, "paymentsIntegrationEnabled", true);
+ CheckBool(dictionary, "showPassphrase", false);
+ CheckBool(dictionary, "usePassphrase", false);
+ CheckBool(dictionary, "passphraseFailed", false);
+ CheckBool(dictionary, "encryptAllData", false);
+ CheckConfigDataTypeArguments(dictionary, SYNC_ALL_DATA, GetAllTypes());
+}
+
+TEST_F(SyncSetupHandlerTest, ShowSetupManuallySyncAll) {
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ syncer::SyncPrefs sync_prefs(profile_->GetPrefs());
+ sync_prefs.SetKeepEverythingSynced(false);
+ SetDefaultExpectationsForConfigPage();
+ // This should display the sync setup dialog (not login).
+ handler_->OpenSyncSetup(false /* creating_supervised_user */);
+
+ ExpectConfig();
+ const content::TestWebUI::CallData& data = *web_ui_.call_data()[0];
+ const base::DictionaryValue* dictionary = nullptr;
+ ASSERT_TRUE(data.arg2()->GetAsDictionary(&dictionary));
+ CheckConfigDataTypeArguments(dictionary, CHOOSE_WHAT_TO_SYNC, GetAllTypes());
+}
+
+TEST_F(SyncSetupHandlerTest, ShowSetupSyncForAllTypesIndividually) {
+ syncer::ModelTypeSet user_selectable_types = GetAllTypes();
+ syncer::ModelTypeSet::Iterator it;
+ for (it = user_selectable_types.First(); it.Good(); it.Inc()) {
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ syncer::SyncPrefs sync_prefs(profile_->GetPrefs());
+ sync_prefs.SetKeepEverythingSynced(false);
+ SetDefaultExpectationsForConfigPage();
+ syncer::ModelTypeSet types;
+ types.Put(it.Get());
+ EXPECT_CALL(*mock_pss_, GetPreferredDataTypes()).
+ WillRepeatedly(Return(types));
+
+ // This should display the sync setup dialog (not login).
+ handler_->OpenSyncSetup(false /* creating_supervised_user */);
+
+ ExpectConfig();
+ // Close the config overlay.
+ LoginUIServiceFactory::GetForProfile(profile_.get())->LoginUIClosed(
+ handler_.get());
+ const content::TestWebUI::CallData& data = *web_ui_.call_data()[0];
+ const base::DictionaryValue* dictionary = nullptr;
+ ASSERT_TRUE(data.arg2()->GetAsDictionary(&dictionary));
+ CheckConfigDataTypeArguments(dictionary, CHOOSE_WHAT_TO_SYNC, types);
+ Mock::VerifyAndClearExpectations(mock_pss_);
+ // Clean up so we can loop back to display the dialog again.
+ web_ui_.ClearTrackedCalls();
+ }
+}
+
+TEST_F(SyncSetupHandlerTest, ShowSetupGaiaPassphraseRequired) {
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ SetDefaultExpectationsForConfigPage();
+
+ // This should display the sync setup dialog (not login).
+ handler_->OpenSyncSetup(false /* creating_supervised_user */);
+
+ ExpectConfig();
+ const content::TestWebUI::CallData& data = *web_ui_.call_data()[0];
+ const base::DictionaryValue* dictionary = nullptr;
+ ASSERT_TRUE(data.arg2()->GetAsDictionary(&dictionary));
+ CheckBool(dictionary, "showPassphrase", true);
+ CheckBool(dictionary, "usePassphrase", false);
+ CheckBool(dictionary, "passphraseFailed", false);
+}
+
+TEST_F(SyncSetupHandlerTest, ShowSetupCustomPassphraseRequired) {
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, GetPassphraseType())
+ .WillRepeatedly(Return(syncer::PassphraseType::CUSTOM_PASSPHRASE));
+ SetupInitializedProfileSyncService();
+ SetDefaultExpectationsForConfigPage();
+
+ // This should display the sync setup dialog (not login).
+ handler_->OpenSyncSetup(false /* creating_supervised_user */);
+
+ ExpectConfig();
+ const content::TestWebUI::CallData& data = *web_ui_.call_data()[0];
+ const base::DictionaryValue* dictionary = nullptr;
+ ASSERT_TRUE(data.arg2()->GetAsDictionary(&dictionary));
+ CheckBool(dictionary, "showPassphrase", true);
+ CheckBool(dictionary, "usePassphrase", true);
+ CheckBool(dictionary, "passphraseFailed", false);
+}
+
+TEST_F(SyncSetupHandlerTest, ShowSetupEncryptAll) {
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ SetDefaultExpectationsForConfigPage();
+ EXPECT_CALL(*mock_pss_, IsEncryptEverythingEnabled())
+ .WillRepeatedly(Return(true));
+
+ // This should display the sync setup dialog (not login).
+ handler_->OpenSyncSetup(false /* creating_supervised_user */);
+
+ ExpectConfig();
+ const content::TestWebUI::CallData& data = *web_ui_.call_data()[0];
+ const base::DictionaryValue* dictionary = nullptr;
+ ASSERT_TRUE(data.arg2()->GetAsDictionary(&dictionary));
+ CheckBool(dictionary, "encryptAllData", true);
+}
+
+TEST_F(SyncSetupHandlerTest, ShowSetupEncryptAllDisallowed) {
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ SetDefaultExpectationsForConfigPage();
+ EXPECT_CALL(*mock_pss_, IsEncryptEverythingAllowed())
+ .WillRepeatedly(Return(false));
+
+ // This should display the sync setup dialog (not login).
+ handler_->OpenSyncSetup(false /* creating_supervised_user */);
+
+ ExpectConfig();
+ const content::TestWebUI::CallData& data = *web_ui_.call_data()[0];
+ const base::DictionaryValue* dictionary = nullptr;
+ ASSERT_TRUE(data.arg2()->GetAsDictionary(&dictionary));
+ CheckBool(dictionary, "encryptAllData", false);
+ CheckBool(dictionary, "encryptAllDataAllowed", false);
+}
+
+TEST_F(SyncSetupHandlerTest, TurnOnEncryptAllDisallowed) {
+ std::string args =
+ GetConfiguration(NULL, SYNC_ALL_DATA, GetAllTypes(), std::string(),
+ ENCRYPT_ALL_DATA, PAYMENTS_INTEGRATION_ENABLED);
+ base::ListValue list_args;
+ list_args.AppendString(args);
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ EXPECT_CALL(*mock_pss_, IsEncryptEverythingAllowed())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, EnableEncryptEverything()).Times(0);
+ EXPECT_CALL(*mock_pss_, OnUserChoseDatatypes(true, _));
+ handler_->HandleConfigure(&list_args);
+
+ // Ensure that we navigated to the "done" state since we don't need a
+ // passphrase.
+ ExpectDone();
+}
diff --git a/chromium/chrome/browser/ui/webui/password_manager_internals/OWNERS b/chromium/chrome/browser/ui/webui/password_manager_internals/OWNERS
new file mode 100644
index 00000000000..e8e1954be35
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/password_manager_internals/OWNERS
@@ -0,0 +1,4 @@
+mkwst@chromium.org
+vabr@chromium.org
+
+# COMPONENT: UI>Browser>Passwords
diff --git a/chromium/chrome/browser/ui/webui/password_manager_internals/password_manager_internals_ui.cc b/chromium/chrome/browser/ui/webui/password_manager_internals/password_manager_internals_ui.cc
new file mode 100644
index 00000000000..976f10af7da
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/password_manager_internals/password_manager_internals_ui.cc
@@ -0,0 +1,130 @@
+// 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/ui/webui/password_manager_internals/password_manager_internals_ui.h"
+
+#include <algorithm>
+#include <set>
+
+#include "base/stl_util.h"
+#include "base/strings/string_split.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/password_manager_internals_resources.h"
+#include "components/password_manager/content/browser/password_manager_internals_service_factory.h"
+#include "components/password_manager/core/browser/password_manager_internals_service.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "net/base/escape.h"
+
+using password_manager::PasswordManagerInternalsService;
+using password_manager::PasswordManagerInternalsServiceFactory;
+
+namespace {
+
+content::WebUIDataSource* CreatePasswordManagerInternalsHTMLSource() {
+ content::WebUIDataSource* source = content::WebUIDataSource::Create(
+ chrome::kChromeUIPasswordManagerInternalsHost);
+
+ source->AddResourcePath(
+ "password_manager_internals.js",
+ IDR_PASSWORD_MANAGER_INTERNALS_PASSWORD_MANAGER_INTERNALS_JS);
+ source->AddResourcePath(
+ "password_manager_internals.css",
+ IDR_PASSWORD_MANAGER_INTERNALS_PASSWORD_MANAGER_INTERNALS_CSS);
+ source->SetDefaultResource(
+ IDR_PASSWORD_MANAGER_INTERNALS_PASSWORD_MANAGER_INTERNALS_HTML);
+ source->UseGzip(std::unordered_set<std::string>());
+ return source;
+}
+
+} // namespace
+
+PasswordManagerInternalsUI::PasswordManagerInternalsUI(content::WebUI* web_ui)
+ : WebUIController(web_ui),
+ WebContentsObserver(web_ui->GetWebContents()),
+ registered_with_logging_service_(false) {
+ // Set up the chrome://password-manager-internals/ source.
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui),
+ CreatePasswordManagerInternalsHTMLSource());
+}
+
+PasswordManagerInternalsUI::~PasswordManagerInternalsUI() {
+ UnregisterFromLoggingServiceIfNecessary();
+}
+
+void PasswordManagerInternalsUI::DidStartLoading() {
+ // In case this tab is being reloaded, unregister itself until the reload is
+ // completed.
+ UnregisterFromLoggingServiceIfNecessary();
+}
+
+void PasswordManagerInternalsUI::DidStopLoading() {
+ DCHECK(!registered_with_logging_service_);
+ PasswordManagerInternalsService* service =
+ PasswordManagerInternalsServiceFactory::GetForBrowserContext(
+ Profile::FromWebUI(web_ui()));
+ // No service means the WebUI is displayed in Incognito.
+ base::Value is_incognito(!service);
+ web_ui()->CallJavascriptFunctionUnsafe("notifyAboutIncognito", is_incognito);
+
+ if (service) {
+ registered_with_logging_service_ = true;
+
+ std::string past_logs(service->RegisterReceiver(this));
+ LogSavePasswordProgress(past_logs);
+ }
+
+ // Reset the first run experience for auto sign-in if the user opened
+ // chrome://password-manager-internals/?reset_fre .
+ // TODO(crbug.com/599454) This may be removed in M53, when the credential
+ // manager API has been launched to stable.
+ GURL url = web_ui()->GetWebContents()->GetVisibleURL();
+ if (url.is_valid() && url.has_query()) {
+ std::vector<std::string> query_parameters = base::SplitString(
+ url.query(), "&", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+ if (base::ContainsValue(query_parameters, "reset_fre"))
+ ResetAutoSignInFirstRunExperience();
+ }
+}
+
+void PasswordManagerInternalsUI::LogSavePasswordProgress(
+ const std::string& text) {
+ if (!registered_with_logging_service_ || text.empty())
+ return;
+ std::string no_quotes(text);
+ std::replace(no_quotes.begin(), no_quotes.end(), '"', ' ');
+ base::Value text_string_value(net::EscapeForHTML(no_quotes));
+ web_ui()->CallJavascriptFunctionUnsafe("addSavePasswordProgressLog",
+ text_string_value);
+}
+
+void PasswordManagerInternalsUI::UnregisterFromLoggingServiceIfNecessary() {
+ if (!registered_with_logging_service_)
+ return;
+ registered_with_logging_service_ = false;
+ PasswordManagerInternalsService* service =
+ PasswordManagerInternalsServiceFactory::GetForBrowserContext(
+ Profile::FromWebUI(web_ui()));
+ if (service)
+ service->UnregisterReceiver(this);
+}
+
+void PasswordManagerInternalsUI::ResetAutoSignInFirstRunExperience() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (profile->GetProfileType() == Profile::REGULAR_PROFILE) {
+ profile->GetPrefs()->SetBoolean(
+ password_manager::prefs::kWasAutoSignInFirstRunExperienceShown, false);
+
+ PasswordManagerInternalsService* service =
+ PasswordManagerInternalsServiceFactory::GetForBrowserContext(
+ Profile::FromWebUI(web_ui()));
+ if (service)
+ service->ProcessLog("Reset auto sign-in first run experience: yes");
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/password_manager_internals/password_manager_internals_ui.h b/chromium/chrome/browser/ui/webui/password_manager_internals/password_manager_internals_ui.h
new file mode 100644
index 00000000000..510408b44df
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/password_manager_internals/password_manager_internals_ui.h
@@ -0,0 +1,41 @@
+// 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_UI_WEBUI_PASSWORD_MANAGER_INTERNALS_PASSWORD_MANAGER_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_PASSWORD_MANAGER_INTERNALS_PASSWORD_MANAGER_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "components/password_manager/core/browser/log_receiver.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_ui_controller.h"
+
+class PasswordManagerInternalsUI : public content::WebUIController,
+ public content::WebContentsObserver,
+ public password_manager::LogReceiver {
+ public:
+ explicit PasswordManagerInternalsUI(content::WebUI* web_ui);
+ ~PasswordManagerInternalsUI() override;
+
+ // WebContentsObserver implementation.
+ void DidStartLoading() override;
+ void DidStopLoading() override;
+
+ // LogReceiver implementation.
+ void LogSavePasswordProgress(const std::string& text) override;
+
+ private:
+ // If currently registered with PasswordManagerInternalsService, unregisters
+ // |this|. Otherwise this is a no-op.
+ void UnregisterFromLoggingServiceIfNecessary();
+
+ void ResetAutoSignInFirstRunExperience();
+
+ // Whether |this| registered as a log receiver with the
+ // PasswordManagerInternalsService.
+ bool registered_with_logging_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordManagerInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_PASSWORD_MANAGER_INTERNALS_PASSWORD_MANAGER_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/password_manager_internals/password_manager_internals_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/password_manager_internals/password_manager_internals_ui_browsertest.cc
new file mode 100644
index 00000000000..a2d08b5ed6b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/password_manager_internals/password_manager_internals_ui_browsertest.cc
@@ -0,0 +1,178 @@
+// 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/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/password_manager_internals/password_manager_internals_ui.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "chrome/test/base/web_ui_browser_test.h"
+#include "components/password_manager/content/browser/password_manager_internals_service_factory.h"
+#include "components/password_manager/core/browser/password_manager_internals_service.h"
+#include "content/public/browser/web_contents.h"
+
+class PasswordManagerInternalsWebUIBrowserTest : public WebUIBrowserTest {
+ public:
+ PasswordManagerInternalsWebUIBrowserTest();
+ ~PasswordManagerInternalsWebUIBrowserTest() override;
+
+ void SetUpOnMainThread() override;
+
+ protected:
+ content::WebContents* GetWebContents();
+
+ // Navigates to the internals page in a tab specified by |disposition| (and
+ // optionally, by |browser|). Also assigns the corresponding UI controller to
+ // |controller_|.
+ void OpenInternalsPage(WindowOpenDisposition disposition);
+ void OpenInternalsPageWithBrowser(Browser* browser,
+ WindowOpenDisposition disposition);
+
+ private:
+ PasswordManagerInternalsUI* controller_;
+};
+
+PasswordManagerInternalsWebUIBrowserTest::
+ PasswordManagerInternalsWebUIBrowserTest()
+ : controller_(NULL) {}
+
+PasswordManagerInternalsWebUIBrowserTest::
+ ~PasswordManagerInternalsWebUIBrowserTest() {}
+
+void PasswordManagerInternalsWebUIBrowserTest::SetUpOnMainThread() {
+ WebUIBrowserTest::SetUpOnMainThread();
+ OpenInternalsPage(WindowOpenDisposition::CURRENT_TAB);
+ AddLibrary(base::FilePath(
+ FILE_PATH_LITERAL("password_manager_internals_browsertest.js")));
+}
+
+content::WebContents*
+PasswordManagerInternalsWebUIBrowserTest::GetWebContents() {
+ return browser()->tab_strip_model()->GetActiveWebContents();
+}
+
+void PasswordManagerInternalsWebUIBrowserTest::OpenInternalsPage(
+ WindowOpenDisposition disposition) {
+ OpenInternalsPageWithBrowser(browser(), disposition);
+}
+
+void PasswordManagerInternalsWebUIBrowserTest::OpenInternalsPageWithBrowser(
+ Browser* browser,
+ WindowOpenDisposition disposition) {
+ std::string url_string("chrome://");
+ url_string += chrome::kChromeUIPasswordManagerInternalsHost;
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser,
+ GURL(url_string),
+ disposition,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+ controller_ = static_cast<PasswordManagerInternalsUI*>(
+ GetWebContents()->GetWebUI()->GetController());
+}
+
+IN_PROC_BROWSER_TEST_F(PasswordManagerInternalsWebUIBrowserTest,
+ LogSavePasswordProgress) {
+ password_manager::PasswordManagerInternalsService* service =
+ password_manager::PasswordManagerInternalsServiceFactory::
+ GetForBrowserContext(browser()->profile());
+ ASSERT_TRUE(service);
+ service->ProcessLog("<script> text for testing");
+ ASSERT_TRUE(RunJavascriptTest("testLogText"));
+}
+
+// Test that a single internals page is empty on load.
+IN_PROC_BROWSER_TEST_F(PasswordManagerInternalsWebUIBrowserTest,
+ LogSavePasswordProgress_EmptyOnLoad) {
+ password_manager::PasswordManagerInternalsService* service =
+ password_manager::PasswordManagerInternalsServiceFactory::
+ GetForBrowserContext(browser()->profile());
+ ASSERT_TRUE(service);
+ ASSERT_TRUE(RunJavascriptTest("testLogEmpty"));
+}
+
+// Test that a single internals page is flushed on reload.
+IN_PROC_BROWSER_TEST_F(PasswordManagerInternalsWebUIBrowserTest,
+ LogSavePasswordProgress_FlushedOnReload) {
+ password_manager::PasswordManagerInternalsService* service =
+ password_manager::PasswordManagerInternalsServiceFactory::
+ GetForBrowserContext(browser()->profile());
+ ASSERT_TRUE(service);
+ service->ProcessLog("<script> text for testing");
+ OpenInternalsPage(WindowOpenDisposition::CURRENT_TAB); // Reload.
+ ASSERT_TRUE(RunJavascriptTest("testLogEmpty"));
+}
+
+// Test that if two tabs with the internals page are open, the second displays
+// the same logs. In particular, this checks that both the second tab gets the
+// logs created before the second tab was opened, and also that the second tab
+// waits with displaying until the internals page is ready (trying to display
+// the old logs just on construction time would fail).
+IN_PROC_BROWSER_TEST_F(PasswordManagerInternalsWebUIBrowserTest,
+ LogSavePasswordProgress_MultipleTabsIdentical) {
+ // First, open one tab with the internals page, and log something.
+ password_manager::PasswordManagerInternalsService* service =
+ password_manager::PasswordManagerInternalsServiceFactory::
+ GetForBrowserContext(browser()->profile());
+ ASSERT_TRUE(service);
+ service->ProcessLog("<script> text for testing");
+ ASSERT_TRUE(RunJavascriptTest("testLogText"));
+ // Now open a second tab with the internals page, but do not log anything.
+ OpenInternalsPage(WindowOpenDisposition::NEW_FOREGROUND_TAB);
+ // The previously logged text should have made it to the page.
+ ASSERT_TRUE(RunJavascriptTest("testLogText"));
+}
+
+// Test that in the presence of more internals pages, reload does not cause
+// flushing the logs.
+IN_PROC_BROWSER_TEST_F(PasswordManagerInternalsWebUIBrowserTest,
+ LogSavePasswordProgress_NotFlushedOnReloadIfMultiple) {
+ // Open one more tab with the internals page.
+ OpenInternalsPage(WindowOpenDisposition::NEW_FOREGROUND_TAB);
+ // Now log something.
+ password_manager::PasswordManagerInternalsService* service =
+ password_manager::PasswordManagerInternalsServiceFactory::
+ GetForBrowserContext(browser()->profile());
+ ASSERT_TRUE(service);
+ service->ProcessLog("<script> text for testing");
+ // Reload.
+ OpenInternalsPage(WindowOpenDisposition::CURRENT_TAB);
+ // The text should still be there.
+ ASSERT_TRUE(RunJavascriptTest("testLogText"));
+}
+
+// Test that navigation away from the internals page works OK.
+IN_PROC_BROWSER_TEST_F(PasswordManagerInternalsWebUIBrowserTest,
+ LogSavePasswordProgress_NavigateAway) {
+ password_manager::PasswordManagerInternalsService* service =
+ password_manager::PasswordManagerInternalsServiceFactory::
+ GetForBrowserContext(browser()->profile());
+ ASSERT_TRUE(service);
+ service->ProcessLog("<script> text for testing");
+ ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIVersionURL));
+}
+
+// Test that the description is correct in a non-Incognito tab.
+IN_PROC_BROWSER_TEST_F(PasswordManagerInternalsWebUIBrowserTest,
+ NonIncognitoMessage) {
+ password_manager::PasswordManagerInternalsService* service =
+ password_manager::PasswordManagerInternalsServiceFactory::
+ GetForBrowserContext(browser()->profile());
+ ASSERT_TRUE(service);
+ ASSERT_TRUE(RunJavascriptTest("testNonIncognitoDescription"));
+}
+
+// Test that the description is correct in an Incognito tab.
+IN_PROC_BROWSER_TEST_F(PasswordManagerInternalsWebUIBrowserTest,
+ IncognitoMessage) {
+ Browser* incognito = CreateIncognitoBrowser();
+ password_manager::PasswordManagerInternalsService* service =
+ password_manager::PasswordManagerInternalsServiceFactory::
+ GetForBrowserContext(incognito->profile()->GetOffTheRecordProfile());
+ EXPECT_FALSE(service); // There should be no service for Incognito.
+ OpenInternalsPageWithBrowser(incognito, WindowOpenDisposition::CURRENT_TAB);
+ SetWebUIInstance(
+ incognito->tab_strip_model()->GetActiveWebContents()->GetWebUI());
+ ASSERT_TRUE(RunJavascriptTest("testIncognitoDescription"));
+}
diff --git a/chromium/chrome/browser/ui/webui/physical_web/physical_web_ui.cc b/chromium/chrome/browser/ui/webui/physical_web/physical_web_ui.cc
new file mode 100644
index 00000000000..a502e19e628
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/physical_web/physical_web_ui.cc
@@ -0,0 +1,92 @@
+// 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 "chrome/browser/ui/webui/physical_web/physical_web_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/metrics/user_metrics.h"
+#include "chrome/browser/browser_process_impl.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "components/grit/components_resources.h"
+#include "components/physical_web/data_source/physical_web_data_source.h"
+#include "components/physical_web/webui/physical_web_base_message_handler.h"
+#include "components/physical_web/webui/physical_web_ui_constants.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace {
+
+content::WebUIDataSource* CreatePhysicalWebHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIPhysicalWebHost);
+
+ source->AddLocalizedString(physical_web_ui::kTitle,
+ IDS_PHYSICAL_WEB_UI_TITLE);
+ source->AddLocalizedString(physical_web_ui::kEmptyMessage,
+ IDS_PHYSICAL_WEB_UI_EMPTY_MESSAGE);
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath(physical_web_ui::kPhysicalWebJS,
+ IDR_PHYSICAL_WEB_UI_JS);
+ source->AddResourcePath(physical_web_ui::kPhysicalWebCSS,
+ IDR_PHYSICAL_WEB_UI_CSS);
+ source->SetDefaultResource(IDR_PHYSICAL_WEB_UI_HTML);
+ source->AddResourcePath(physical_web_ui::kPhysicalWebLinkIcon,
+ IDR_PHYSICAL_WEB_UI_LINK_ICON);
+ return source;
+}
+
+// Implements all MessageHandler core functionality. This is extends the
+// PhysicalWebBaseMessageHandler and implements functions to manipulate an
+// Android-specific WebUI object.
+class MessageHandlerImpl
+ : public physical_web_ui::PhysicalWebBaseMessageHandler {
+ public:
+ explicit MessageHandlerImpl(content::WebUI* web_ui) : web_ui_(web_ui) {}
+ ~MessageHandlerImpl() override {}
+
+ private:
+ void RegisterMessageCallback(
+ const std::string& message,
+ const physical_web_ui::MessageCallback& callback) override {
+ web_ui_->RegisterMessageCallback(message, callback);
+ }
+ void CallJavaScriptFunction(const std::string& function,
+ const base::Value& arg) override {
+ web_ui_->CallJavascriptFunctionUnsafe(function, arg);
+ }
+ physical_web::PhysicalWebDataSource* GetPhysicalWebDataSource() override {
+ return g_browser_process->GetPhysicalWebDataSource();
+ }
+
+ content::WebUI* const web_ui_;
+ DISALLOW_COPY_AND_ASSIGN(MessageHandlerImpl);
+};
+
+class PhysicalWebMessageHandler : public content::WebUIMessageHandler {
+ public:
+ PhysicalWebMessageHandler() {}
+ ~PhysicalWebMessageHandler() override {}
+
+ void RegisterMessages() override {
+ impl_.reset(new MessageHandlerImpl(web_ui()));
+ impl_->RegisterMessages();
+ }
+
+ private:
+ std::unique_ptr<MessageHandlerImpl> impl_;
+ DISALLOW_COPY_AND_ASSIGN(PhysicalWebMessageHandler);
+};
+
+} // namespace
+
+PhysicalWebUI::PhysicalWebUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui),
+ CreatePhysicalWebHTMLSource());
+ web_ui->AddMessageHandler(base::MakeUnique<PhysicalWebMessageHandler>());
+ base::RecordAction(base::UserMetricsAction("PhysicalWeb.WebUI.Open"));
+}
+
+PhysicalWebUI::~PhysicalWebUI() = default;
diff --git a/chromium/chrome/browser/ui/webui/physical_web/physical_web_ui.h b/chromium/chrome/browser/ui/webui/physical_web/physical_web_ui.h
new file mode 100644
index 00000000000..aa5079d2a5a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/physical_web/physical_web_ui.h
@@ -0,0 +1,20 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_PHYSICAL_WEB_PHYSICAL_WEB_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_PHYSICAL_WEB_PHYSICAL_WEB_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+class PhysicalWebUI : public content::WebUIController {
+ public:
+ explicit PhysicalWebUI(content::WebUI* web_ui);
+ ~PhysicalWebUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PhysicalWebUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_PHYSICAL_WEB_PHYSICAL_WEB_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/policy_indicator_localized_strings_provider.cc b/chromium/chrome/browser/ui/webui/policy_indicator_localized_strings_provider.cc
new file mode 100644
index 00000000000..5315755617c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/policy_indicator_localized_strings_provider.cc
@@ -0,0 +1,31 @@
+// 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.
+
+#include "chrome/browser/ui/webui/policy_indicator_localized_strings_provider.h"
+
+#include "build/build_config.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace policy_indicator {
+
+void AddLocalizedStrings(content::WebUIDataSource* html_source) {
+ html_source->AddLocalizedString("controlledSettingPolicy",
+ IDS_OPTIONS_CONTROLLED_SETTING_POLICY);
+ html_source->AddLocalizedString("controlledSettingRecommendedMatches",
+ IDS_OPTIONS_CONTROLLED_SETTING_RECOMMENDED);
+ html_source->AddLocalizedString(
+ "controlledSettingRecommendedDiffers",
+ IDS_OPTIONS_CONTROLLED_SETTING_HAS_RECOMMENDATION);
+ html_source->AddLocalizedString("controlledSettingExtension",
+ IDS_OPTIONS_CONTROLLED_SETTING_EXTENSION);
+#if defined(OS_CHROMEOS)
+ html_source->AddLocalizedString("controlledSettingShared",
+ IDS_OPTIONS_CONTROLLED_SETTING_SHARED);
+ html_source->AddLocalizedString("controlledSettingOwner",
+ IDS_OPTIONS_CONTROLLED_SETTING_OWNER);
+#endif
+}
+
+} // namespace policy_indicator
diff --git a/chromium/chrome/browser/ui/webui/policy_indicator_localized_strings_provider.h b/chromium/chrome/browser/ui/webui/policy_indicator_localized_strings_provider.h
new file mode 100644
index 00000000000..e448a738446
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/policy_indicator_localized_strings_provider.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_POLICY_INDICATOR_LOCALIZED_STRINGS_PROVIDER_H_
+#define CHROME_BROWSER_UI_WEBUI_POLICY_INDICATOR_LOCALIZED_STRINGS_PROVIDER_H_
+
+namespace content {
+class WebUIDataSource;
+}
+
+namespace policy_indicator {
+
+// Adds the strings needed for policy indicators to |html_source|. This function
+// causes |html_source| to expose a strings.js file from its source which
+// contains a mapping from string's name to its translated value.
+void AddLocalizedStrings(content::WebUIDataSource* html_source);
+
+} // namespace policy_indicator
+
+#endif // CHROME_BROWSER_UI_WEBUI_POLICY_INDICATOR_LOCALIZED_STRINGS_PROVIDER_H_
diff --git a/chromium/chrome/browser/ui/webui/policy_material_design_ui.cc b/chromium/chrome/browser/ui/webui/policy_material_design_ui.cc
new file mode 100644
index 00000000000..7948c1c9ceb
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/policy_material_design_ui.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 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.
+
+#include "chrome/browser/ui/webui/policy_material_design_ui.h"
+
+#include <stddef.h>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/policy_ui_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/policy_resources.h"
+#include "chrome/grit/policy_resources_map.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/policy_constants.h"
+#include "components/policy/risk_tag.h"
+#include "components/strings/grit/components_strings.h"
+
+namespace {
+
+// Strings that map from policy::RiskTag enum to i18n string keys and their IDs.
+// Their order has to follow the order of the policy::RiskTag enum.
+const PolicyStringMap kPolicyRiskTags[policy::RISK_TAG_COUNT] = {
+ {"fullAdminAccess", IDS_POLICY_RISK_TAG_FULL_ADMIN_ACCESS},
+ {"systemSecurity", IDS_POLICY_RISK_TAG_SYSTEM_SECURITY},
+ {"websiteSharing", IDS_POLICY_RISK_TAG_WEBSITE_SHARING},
+ {"adminSharing", IDS_POLICY_RISK_TAG_ADMIN_SHARING},
+ {"filtering", IDS_POLICY_RISK_TAG_FILTERING},
+ {"localDataAccess", IDS_POLICY_RISK_TAG_LOCAL_DATA_ACCESS},
+ {"googleSharing", IDS_POLICY_RISK_TAG_GOOGLE_SHARING},
+};
+
+content::WebUIDataSource* CreatePolicyMaterialDesignUIHtmlSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIMdPolicyHost);
+ PolicyUIHandler::AddCommonLocalizedStringsToSource(source);
+ PolicyUIHandler::AddLocalizedPolicyStrings(
+ source, kPolicyRiskTags, static_cast<size_t>(policy::RISK_TAG_COUNT));
+ for (size_t i = 0; i < kPolicyResourcesSize; ++i) {
+ source->AddResourcePath(kPolicyResources[i].name,
+ kPolicyResources[i].value);
+ }
+ source->SetDefaultResource(IDR_MD_POLICY_HTML);
+ return source;
+}
+
+} // namespace
+
+// The JavaScript message handler for the chrome://md-policy page.
+class PolicyMaterialDesignUIHandler : public PolicyUIHandler {
+ public:
+ PolicyMaterialDesignUIHandler();
+ ~PolicyMaterialDesignUIHandler() override;
+
+ protected:
+ // PolicyUIHandler:
+ void AddPolicyName(const std::string& name,
+ base::DictionaryValue* names) const override;
+ void SendPolicyNames() const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PolicyMaterialDesignUIHandler);
+};
+
+PolicyMaterialDesignUIHandler::PolicyMaterialDesignUIHandler() {
+}
+
+PolicyMaterialDesignUIHandler::~PolicyMaterialDesignUIHandler() {
+}
+
+void PolicyMaterialDesignUIHandler::AddPolicyName(
+ const std::string& name, base::DictionaryValue* names) const {
+ std::unique_ptr<base::ListValue> list(new base::ListValue());
+ const policy::RiskTag* tags = policy::GetChromePolicyDetails(name)->risk_tags;
+ for (size_t i = 0; i < policy::kMaxRiskTagCount; ++i) {
+ if (tags[i] != policy::RISK_TAG_NONE)
+ list->AppendString(kPolicyRiskTags[tags[i]].key);
+ }
+ names->Set(name, std::move(list));
+}
+
+void PolicyMaterialDesignUIHandler::SendPolicyNames() const {
+ base::ListValue tags;
+ for (size_t tag = 0; tag < policy::RISK_TAG_COUNT; ++tag)
+ tags.AppendString(kPolicyRiskTags[tag].key);
+
+ web_ui()->CallJavascriptFunctionUnsafe("policy.Page.setPolicyGroups", tags);
+ PolicyUIHandler::SendPolicyNames();
+}
+
+PolicyMaterialDesignUI::PolicyMaterialDesignUI(content::WebUI* web_ui) :
+ WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<PolicyMaterialDesignUIHandler>());
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui),
+ CreatePolicyMaterialDesignUIHtmlSource());
+}
+
+PolicyMaterialDesignUI::~PolicyMaterialDesignUI() {
+}
diff --git a/chromium/chrome/browser/ui/webui/policy_material_design_ui.h b/chromium/chrome/browser/ui/webui/policy_material_design_ui.h
new file mode 100644
index 00000000000..ecf7eb1a678
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/policy_material_design_ui.h
@@ -0,0 +1,25 @@
+// Copyright (c) 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_POLICY_MATERIAL_DESIGN_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_POLICY_MATERIAL_DESIGN_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace content {
+class WebUI;
+}
+
+// The Web UI controller for the chrome://md-policy page.
+class PolicyMaterialDesignUI : public content::WebUIController {
+ public:
+ explicit PolicyMaterialDesignUI(content::WebUI* web_ui);
+ ~PolicyMaterialDesignUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PolicyMaterialDesignUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_POLICY_MATERIAL_DESIGN_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/policy_ui.cc b/chromium/chrome/browser/ui/webui/policy_ui.cc
new file mode 100644
index 00000000000..802be6a4970
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/policy_ui.cc
@@ -0,0 +1,63 @@
+// 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/ui/webui/policy_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/policy_ui_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_ui.h"
+
+namespace {
+
+content::WebUIDataSource* CreatePolicyUIHtmlSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIPolicyHost);
+ PolicyUIHandler::AddCommonLocalizedStringsToSource(source);
+ source->AddLocalizedString("filterPlaceholder",
+ IDS_POLICY_FILTER_PLACEHOLDER);
+ source->AddLocalizedString("reloadPolicies", IDS_POLICY_RELOAD_POLICIES);
+ 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);
+ source->AddLocalizedString("labelDomain", IDS_POLICY_LABEL_DOMAIN);
+ source->AddLocalizedString("labelUsername", IDS_POLICY_LABEL_USERNAME);
+ source->AddLocalizedString("labelClientId", IDS_POLICY_LABEL_CLIENT_ID);
+ source->AddLocalizedString("labelAssetId", IDS_POLICY_LABEL_ASSET_ID);
+ source->AddLocalizedString("labelLocation", IDS_POLICY_LABEL_LOCATION);
+ source->AddLocalizedString("labelDirectoryApiId",
+ IDS_POLICY_LABEL_DIRECTORY_API_ID);
+ source->AddLocalizedString("labelTimeSinceLastRefresh",
+ IDS_POLICY_LABEL_TIME_SINCE_LAST_REFRESH);
+ source->AddLocalizedString("labelRefreshInterval",
+ IDS_POLICY_LABEL_REFRESH_INTERVAL);
+ source->AddLocalizedString("labelStatus", IDS_POLICY_LABEL_STATUS);
+ source->AddLocalizedString("showUnset", IDS_POLICY_SHOW_UNSET);
+ source->AddLocalizedString("noPoliciesSet", IDS_POLICY_NO_POLICIES_SET);
+ source->AddLocalizedString("showExpandedValue",
+ IDS_POLICY_SHOW_EXPANDED_VALUE);
+ source->AddLocalizedString("hideExpandedValue",
+ IDS_POLICY_HIDE_EXPANDED_VALUE);
+ // Add required resources.
+ source->AddResourcePath("policy.css", IDR_POLICY_CSS);
+ source->AddResourcePath("policy.js", IDR_POLICY_JS);
+ source->AddResourcePath("uber_utils.js", IDR_UBER_UTILS_JS);
+ source->SetDefaultResource(IDR_POLICY_HTML);
+ return source;
+}
+
+} // namespace
+
+PolicyUI::PolicyUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<PolicyUIHandler>());
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui),
+ CreatePolicyUIHtmlSource());
+}
+
+PolicyUI::~PolicyUI() {
+}
diff --git a/chromium/chrome/browser/ui/webui/policy_ui.h b/chromium/chrome/browser/ui/webui/policy_ui.h
new file mode 100644
index 00000000000..d8b58392fcd
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/policy_ui.h
@@ -0,0 +1,27 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_POLICY_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_POLICY_UI_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+namespace content {
+class WebUI;
+}
+
+// The Web UI controller for the chrome://policy page.
+class PolicyUI : public content::WebUIController {
+ public:
+ explicit PolicyUI(content::WebUI* web_ui);
+ ~PolicyUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PolicyUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_POLICY_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/policy_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/policy_ui_browsertest.cc
new file mode 100644
index 00000000000..5514daf8734
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/policy_ui_browsertest.cc
@@ -0,0 +1,335 @@
+// 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 <stddef.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/json/json_reader.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/test_extension_system.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.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/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/common/external_data_fetcher.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/core/common/schema.h"
+#include "components/policy/policy_constants.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "extensions/common/extension_builder.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+using testing::Return;
+using testing::_;
+
+namespace {
+
+std::vector<std::string> PopulateExpectedPolicy(
+ const std::string& name,
+ const std::string& value,
+ const std::string& source,
+ const policy::PolicyMap::Entry* metadata,
+ bool unknown) {
+ std::vector<std::string> expected_policy;
+
+ // Populate expected scope.
+ if (metadata) {
+ expected_policy.push_back(l10n_util::GetStringUTF8(
+ metadata->scope == policy::POLICY_SCOPE_MACHINE ?
+ IDS_POLICY_SCOPE_DEVICE : IDS_POLICY_SCOPE_USER));
+ } else {
+ expected_policy.push_back(std::string());
+ }
+
+ // Populate expected level.
+ if (metadata) {
+ expected_policy.push_back(l10n_util::GetStringUTF8(
+ metadata->level == policy::POLICY_LEVEL_RECOMMENDED ?
+ IDS_POLICY_LEVEL_RECOMMENDED : IDS_POLICY_LEVEL_MANDATORY));
+ } else {
+ expected_policy.push_back(std::string());
+ }
+ // Populate expected source name.
+ expected_policy.push_back(source);
+
+ // Populate expected policy name.
+ expected_policy.push_back(name);
+
+ // Populate expected policy value.
+ expected_policy.push_back(value);
+
+ // Populate expected status.
+ if (unknown)
+ expected_policy.push_back(l10n_util::GetStringUTF8(IDS_POLICY_UNKNOWN));
+ else if (metadata)
+ expected_policy.push_back(l10n_util::GetStringUTF8(IDS_POLICY_OK));
+ else
+ expected_policy.push_back(l10n_util::GetStringUTF8(IDS_POLICY_UNSET));
+
+ // Populate expected expanded policy value.
+ expected_policy.push_back(value);
+
+ return expected_policy;
+}
+
+} // namespace
+
+class PolicyUITest : public InProcessBrowserTest {
+ public:
+ PolicyUITest();
+ ~PolicyUITest() override;
+
+ protected:
+ // InProcessBrowserTest implementation.
+ void SetUpInProcessBrowserTestFixture() override;
+
+ void UpdateProviderPolicy(const policy::PolicyMap& policy);
+
+ void VerifyPolicies(const std::vector<std::vector<std::string> >& expected);
+
+ protected:
+ policy::MockConfigurationPolicyProvider provider_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PolicyUITest);
+};
+
+PolicyUITest::PolicyUITest() {
+}
+
+PolicyUITest::~PolicyUITest() {
+}
+
+void PolicyUITest::SetUpInProcessBrowserTestFixture() {
+ EXPECT_CALL(provider_, IsInitializationComplete(_))
+ .WillRepeatedly(Return(true));
+ policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
+}
+
+void PolicyUITest::UpdateProviderPolicy(const policy::PolicyMap& policy) {
+ provider_.UpdateChromePolicy(policy);
+ base::RunLoop loop;
+ loop.RunUntilIdle();
+}
+
+void PolicyUITest::VerifyPolicies(
+ const std::vector<std::vector<std::string> >& expected_policies) {
+ ui_test_utils::NavigateToURL(browser(), GURL("chrome://policy"));
+
+ // Retrieve the text contents of the policy table cells for all policies.
+ const std::string javascript =
+ "var entries = document.querySelectorAll("
+ " 'section.policy-table-section > * > tbody');"
+ "var policies = [];"
+ "for (var i = 0; i < entries.length; ++i) {"
+ " var items = entries[i].querySelectorAll('tr > td');"
+ " var values = [];"
+ " for (var j = 0; j < items.length; ++j) {"
+ " var item = items[j];"
+ " var children = item.getElementsByTagName('div');"
+ " if (children.length == 1)"
+ " item = children[0];"
+ " children = item.getElementsByTagName('span');"
+ " if (children.length == 1)"
+ " item = children[0];"
+ " values.push(item.textContent);"
+ " }"
+ " policies.push(values);"
+ "}"
+ "domAutomationController.send(JSON.stringify(policies));";
+ content::WebContents* contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ std::string json;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(contents, javascript,
+ &json));
+ std::unique_ptr<base::Value> value_ptr = base::JSONReader::Read(json);
+ const base::ListValue* actual_policies = NULL;
+ ASSERT_TRUE(value_ptr.get());
+ ASSERT_TRUE(value_ptr->GetAsList(&actual_policies));
+
+ // Verify that the cells contain the expected strings for all policies.
+ ASSERT_EQ(expected_policies.size(), actual_policies->GetSize());
+ for (size_t i = 0; i < expected_policies.size(); ++i) {
+ const std::vector<std::string> expected_policy = expected_policies[i];
+ const base::ListValue* actual_policy;
+ ASSERT_TRUE(actual_policies->GetList(i, &actual_policy));
+ ASSERT_EQ(expected_policy.size(), actual_policy->GetSize());
+ for (size_t j = 0; j < expected_policy.size(); ++j) {
+ std::string value;
+ ASSERT_TRUE(actual_policy->GetString(j, &value));
+ EXPECT_EQ(expected_policy[j], value);
+ }
+ }
+}
+
+IN_PROC_BROWSER_TEST_F(PolicyUITest, SendPolicyNames) {
+ // Verifies that the names of known policies are sent to the UI and processed
+ // there correctly by checking that the policy table contains all policies in
+ // the correct order.
+
+ // Expect that the policy table contains all known policies in alphabetical
+ // order and none of the policies have a set value.
+ std::vector<std::vector<std::string> > expected_policies;
+ policy::Schema chrome_schema =
+ policy::Schema::Wrap(policy::GetChromeSchemaData());
+ ASSERT_TRUE(chrome_schema.valid());
+ for (policy::Schema::Iterator it = chrome_schema.GetPropertiesIterator();
+ !it.IsAtEnd(); it.Advance()) {
+ expected_policies.push_back(
+ PopulateExpectedPolicy(
+ it.key(), std::string(), std::string(), nullptr, false));
+ }
+
+ // Retrieve the contents of the policy table from the UI and verify that it
+ // matches the expectation.
+ VerifyPolicies(expected_policies);
+}
+
+IN_PROC_BROWSER_TEST_F(PolicyUITest, SendPolicyValues) {
+ // Verifies that policy values are sent to the UI and processed there
+ // correctly by setting the values of four known and one unknown policy and
+ // checking that the policy table contains the policy names, values and
+ // metadata in the correct order.
+ policy::PolicyMap values;
+ std::map<std::string, std::string> expected_values;
+
+ // Set the values of four existing policies.
+ std::unique_ptr<base::ListValue> restore_on_startup_urls(new base::ListValue);
+ restore_on_startup_urls->AppendString("aaa");
+ restore_on_startup_urls->AppendString("bbb");
+ restore_on_startup_urls->AppendString("ccc");
+ values.Set(policy::key::kRestoreOnStartupURLs, policy::POLICY_LEVEL_MANDATORY,
+ policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
+ std::move(restore_on_startup_urls), nullptr);
+ expected_values[policy::key::kRestoreOnStartupURLs] = "aaa,bbb,ccc";
+ values.Set(policy::key::kHomepageLocation, policy::POLICY_LEVEL_MANDATORY,
+ policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_CLOUD,
+ base::MakeUnique<base::Value>("http://google.com"), nullptr);
+ expected_values[policy::key::kHomepageLocation] = "http://google.com";
+ values.Set(policy::key::kRestoreOnStartup, policy::POLICY_LEVEL_RECOMMENDED,
+ policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
+ base::MakeUnique<base::Value>(4), nullptr);
+ expected_values[policy::key::kRestoreOnStartup] = "4";
+ values.Set(policy::key::kShowHomeButton, policy::POLICY_LEVEL_RECOMMENDED,
+ policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_CLOUD,
+ base::MakeUnique<base::Value>(true), nullptr);
+ expected_values[policy::key::kShowHomeButton] = "true";
+ // Set the value of a policy that does not exist.
+ const std::string kUnknownPolicy = "NoSuchThing";
+ values.Set(kUnknownPolicy, policy::POLICY_LEVEL_MANDATORY,
+ policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_PLATFORM,
+ base::MakeUnique<base::Value>(true), nullptr);
+ expected_values[kUnknownPolicy] = "true";
+ UpdateProviderPolicy(values);
+
+ // Expect that the policy table contains, in order:
+ // * All known policies whose value has been set, in alphabetical order.
+ // * The unknown policy.
+ // * All known policies whose value has not been set, in alphabetical order.
+ std::vector<std::vector<std::string> > expected_policies;
+ size_t first_unset_position = 0;
+ policy::Schema chrome_schema =
+ policy::Schema::Wrap(policy::GetChromeSchemaData());
+ ASSERT_TRUE(chrome_schema.valid());
+ for (policy::Schema::Iterator props = chrome_schema.GetPropertiesIterator();
+ !props.IsAtEnd(); props.Advance()) {
+ std::map<std::string, std::string>::const_iterator it =
+ expected_values.find(props.key());
+ const std::string value =
+ it == expected_values.end() ? std::string() : it->second;
+ const std::string source =
+ it == expected_values.end() ? std::string() : "Cloud";
+ const policy::PolicyMap::Entry* metadata = values.Get(props.key());
+ expected_policies.insert(
+ metadata ? expected_policies.begin() + first_unset_position++ :
+ expected_policies.end(),
+ PopulateExpectedPolicy(props.key(), value, source, metadata, false));
+ }
+ expected_policies.insert(
+ expected_policies.begin() + first_unset_position++,
+ PopulateExpectedPolicy(kUnknownPolicy,
+ expected_values[kUnknownPolicy],
+ "Platform",
+ values.Get(kUnknownPolicy),
+ true));
+
+ // Retrieve the contents of the policy table from the UI and verify that it
+ // matches the expectation.
+ VerifyPolicies(expected_policies);
+}
+
+IN_PROC_BROWSER_TEST_F(PolicyUITest, ExtensionLoadAndSendPolicy) {
+ ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIPolicyURL));
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+ base::ScopedTempDir temp_dir_;
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+ const std::string newly_added_policy_name = "new_policy";
+ std::string json_data = "{\"type\": \"object\",\"properties\": {\"" +
+ newly_added_policy_name +
+ "\": { \"type\": \"string\"}}}";
+
+ const std::string schema_file = "schema.json";
+ base::FilePath schema_path = temp_dir_.GetPath().AppendASCII(schema_file);
+ base::WriteFile(schema_path, json_data.data(), json_data.size());
+
+ // Build extension that contains the policy schema.
+ extensions::DictionaryBuilder storage;
+ storage.Set("managed_schema", schema_file);
+
+ extensions::DictionaryBuilder manifest;
+ manifest.Set("name", "test")
+ .Set("version", "1")
+ .Set("manifest_version", 2)
+ .Set("storage", storage.Build());
+
+ extensions::ExtensionBuilder builder;
+ builder.SetPath(temp_dir_.GetPath());
+ builder.SetManifest(manifest.Build());
+
+ // Install extension.
+ ExtensionService* service = extensions::ExtensionSystem::Get(
+ browser()->profile())->extension_service();
+ EXPECT_CALL(provider_, RefreshPolicies());
+ service->OnExtensionInstalled(builder.Build().get(), syncer::StringOrdinal(),
+ 0);
+
+ std::vector<std::vector<std::string>> expected_policies;
+ policy::Schema chrome_schema =
+ policy::Schema::Wrap(policy::GetChromeSchemaData());
+ ASSERT_TRUE(chrome_schema.valid());
+
+ for (policy::Schema::Iterator it = chrome_schema.GetPropertiesIterator();
+ !it.IsAtEnd(); it.Advance()) {
+ expected_policies.push_back(
+ PopulateExpectedPolicy(
+ it.key(), std::string(), std::string(), nullptr, false));
+ }
+ // Add newly added policy to expected policy list.
+ expected_policies.push_back(PopulateExpectedPolicy(
+ newly_added_policy_name, std::string(), std::string(), nullptr, false));
+
+ // Verify if policy UI includes policy that extension have.
+ VerifyPolicies(expected_policies);
+}
diff --git a/chromium/chrome/browser/ui/webui/policy_ui_handler.cc b/chromium/chrome/browser/ui/webui/policy_ui_handler.cc
new file mode 100644
index 00000000000..5ea1cbb4b50
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/policy_ui_handler.cc
@@ -0,0 +1,824 @@
+// 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/ui/webui/policy_ui_handler.h"
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/policy/profile_policy_connector_factory.h"
+#include "chrome/browser/policy/schema_registry_service.h"
+#include "chrome/browser/policy/schema_registry_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/grit/policy_resources.h"
+#include "chrome/grit/policy_resources_map.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/browser/cloud/message_util.h"
+#include "components/policy/core/browser/configuration_policy_handler_list.h"
+#include "components/policy/core/common/cloud/cloud_policy_client.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/core/common/cloud/cloud_policy_core.h"
+#include "components/policy/core/common/cloud/cloud_policy_manager.h"
+#include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h"
+#include "components/policy/core/common/cloud/cloud_policy_store.h"
+#include "components/policy/core/common/cloud/cloud_policy_validator.h"
+#include "components/policy/core/common/policy_details.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/core/common/remote_commands/remote_commands_service.h"
+#include "components/policy/core/common/schema.h"
+#include "components/policy/core/common/schema_map.h"
+#include "components/policy/policy_constants.h"
+#include "components/policy/proto/device_management_backend.pb.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/features/features.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/time_format.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/chromeos/policy/active_directory_policy_manager.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h"
+#include "chrome/browser/chromeos/policy/device_local_account_policy_service.h"
+#include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
+#include "chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.h"
+#include "chrome/browser/chromeos/settings/install_attributes.h"
+#include "components/user_manager/user_manager.h"
+#else
+#include "chrome/browser/policy/cloud/user_cloud_policy_manager_factory.h"
+#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
+#endif
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/manifest.h"
+#include "extensions/common/manifest_constants.h"
+#endif
+
+namespace em = enterprise_management;
+
+namespace {
+
+// Strings that map from PolicySource enum to i18n string keys and their IDs.
+// Their order has to follow the order of the policy::PolicySource enum.
+const PolicyStringMap kPolicySources[policy::POLICY_SOURCE_COUNT] = {
+ {"sourceEnterpriseDefault", IDS_POLICY_SOURCE_ENTERPRISE_DEFAULT},
+ {"sourceCloud", IDS_POLICY_SOURCE_CLOUD},
+ {"sourceActiveDirectory", IDS_POLICY_SOURCE_ACTIVE_DIRECTORY},
+ {"sourcePublicSessionOverride", IDS_POLICY_SOURCE_PUBLIC_SESSION_OVERRIDE},
+ {"sourcePlatform", IDS_POLICY_SOURCE_PLATFORM},
+};
+
+// Formats the association state indicated by |data|. If |data| is NULL, the
+// state is considered to be UNMANAGED.
+base::string16 FormatAssociationState(const em::PolicyData* data) {
+ if (data) {
+ switch (data->state()) {
+ case em::PolicyData::ACTIVE:
+ return l10n_util::GetStringUTF16(IDS_POLICY_ASSOCIATION_STATE_ACTIVE);
+ case em::PolicyData::UNMANAGED:
+ return l10n_util::GetStringUTF16(
+ IDS_POLICY_ASSOCIATION_STATE_UNMANAGED);
+ case em::PolicyData::DEPROVISIONED:
+ return l10n_util::GetStringUTF16(
+ IDS_POLICY_ASSOCIATION_STATE_DEPROVISIONED);
+ }
+ NOTREACHED() << "Unknown state " << data->state();
+ }
+
+ // Default to UNMANAGED for the case of missing policy or bad state enum.
+ return l10n_util::GetStringUTF16(IDS_POLICY_ASSOCIATION_STATE_UNMANAGED);
+}
+
+void GetStatusFromCore(const policy::CloudPolicyCore* core,
+ base::DictionaryValue* dict) {
+ const policy::CloudPolicyStore* store = core->store();
+ const policy::CloudPolicyClient* client = core->client();
+ const policy::CloudPolicyRefreshScheduler* refresh_scheduler =
+ core->refresh_scheduler();
+
+ // CloudPolicyStore errors take precedence to show in the status message.
+ // Other errors (such as transient policy fetching problems) get displayed
+ // only if CloudPolicyStore is in STATUS_OK.
+ base::string16 status =
+ policy::FormatStoreStatus(store->status(), store->validation_status());
+ if (store->status() == policy::CloudPolicyStore::STATUS_OK) {
+ if (client && client->status() != policy::DM_STATUS_SUCCESS)
+ status = policy::FormatDeviceManagementStatus(client->status());
+ else if (!store->is_managed())
+ status = FormatAssociationState(store->policy());
+ }
+
+ const em::PolicyData* policy = store->policy();
+ std::string client_id = policy ? policy->device_id() : std::string();
+ std::string username = policy ? policy->username() : std::string();
+
+ if (policy && policy->has_annotated_asset_id())
+ dict->SetString("assetId", policy->annotated_asset_id());
+ if (policy && policy->has_annotated_location())
+ dict->SetString("location", policy->annotated_location());
+ if (policy && policy->has_directory_api_id())
+ dict->SetString("directoryApiId", policy->directory_api_id());
+
+ base::TimeDelta refresh_interval =
+ base::TimeDelta::FromMilliseconds(refresh_scheduler ?
+ refresh_scheduler->GetActualRefreshDelay() :
+ policy::CloudPolicyRefreshScheduler::kDefaultRefreshDelayMs);
+ base::Time last_refresh_time = refresh_scheduler ?
+ refresh_scheduler->last_refresh() : base::Time();
+
+ bool no_error = store->status() == policy::CloudPolicyStore::STATUS_OK &&
+ client && client->status() == policy::DM_STATUS_SUCCESS;
+ dict->SetBoolean("error", !no_error);
+ dict->SetString("status", status);
+ dict->SetString("clientId", client_id);
+ dict->SetString("username", username);
+ dict->SetString("refreshInterval",
+ ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
+ ui::TimeFormat::LENGTH_SHORT,
+ refresh_interval));
+ dict->SetString("timeSinceLastRefresh", last_refresh_time.is_null() ?
+ l10n_util::GetStringUTF16(IDS_POLICY_NEVER_FETCHED) :
+ ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_ELAPSED,
+ ui::TimeFormat::LENGTH_SHORT,
+ base::Time::NowFromSystemTime() -
+ last_refresh_time));
+}
+
+void ExtractDomainFromUsername(base::DictionaryValue* dict) {
+ std::string username;
+ dict->GetString("username", &username);
+ if (!username.empty())
+ dict->SetString("domain", gaia::ExtractDomainName(username));
+}
+
+// Utility function that returns a JSON serialization of the given |dict|.
+std::unique_ptr<base::Value> DictionaryToJSONString(
+ const base::DictionaryValue& dict) {
+ std::string json_string;
+ base::JSONWriter::WriteWithOptions(dict,
+ base::JSONWriter::OPTIONS_PRETTY_PRINT,
+ &json_string);
+ return base::MakeUnique<base::Value>(json_string);
+}
+
+// Returns a copy of |value| with some values converted to a representation that
+// i18n_template.js will display in a nicer way.
+std::unique_ptr<base::Value> CopyAndConvert(const base::Value* value) {
+ const base::DictionaryValue* dict = NULL;
+ if (value->GetAsDictionary(&dict))
+ return DictionaryToJSONString(*dict);
+
+ std::unique_ptr<base::Value> copy = value->CreateDeepCopy();
+ base::ListValue* list = NULL;
+ if (copy->GetAsList(&list)) {
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ if (list->GetDictionary(i, &dict))
+ list->Set(i, DictionaryToJSONString(*dict));
+ }
+ }
+
+ return copy;
+}
+
+} // namespace
+
+// An interface for querying the status of a policy provider. It surfaces
+// things like last fetch time or status of the backing store, but not the
+// actual policies themselves.
+class PolicyStatusProvider {
+ public:
+ PolicyStatusProvider();
+ virtual ~PolicyStatusProvider();
+
+ // Sets a callback to invoke upon status changes.
+ void SetStatusChangeCallback(const base::Closure& callback);
+
+ virtual void GetStatus(base::DictionaryValue* dict);
+
+ protected:
+ void NotifyStatusChange();
+
+ private:
+ base::Closure callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(PolicyStatusProvider);
+};
+
+// Status provider implementation that pulls cloud policy status from a
+// CloudPolicyCore instance provided at construction time. Also listens for
+// changes on that CloudPolicyCore and reports them through the status change
+// callback.
+class CloudPolicyCoreStatusProvider
+ : public PolicyStatusProvider,
+ public policy::CloudPolicyStore::Observer {
+ public:
+ explicit CloudPolicyCoreStatusProvider(policy::CloudPolicyCore* core);
+ ~CloudPolicyCoreStatusProvider() override;
+
+ // policy::CloudPolicyStore::Observer implementation.
+ void OnStoreLoaded(policy::CloudPolicyStore* store) override;
+ void OnStoreError(policy::CloudPolicyStore* store) override;
+
+ protected:
+ // Policy status is read from the CloudPolicyClient, CloudPolicyStore and
+ // CloudPolicyRefreshScheduler hosted by this |core_|.
+ policy::CloudPolicyCore* core_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CloudPolicyCoreStatusProvider);
+};
+
+// A cloud policy status provider for user policy.
+class UserCloudPolicyStatusProvider : public CloudPolicyCoreStatusProvider {
+ public:
+ explicit UserCloudPolicyStatusProvider(policy::CloudPolicyCore* core);
+ ~UserCloudPolicyStatusProvider() override;
+
+ // CloudPolicyCoreStatusProvider implementation.
+ void GetStatus(base::DictionaryValue* dict) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyStatusProvider);
+};
+
+#if defined(OS_CHROMEOS)
+// A cloud policy status provider for device policy.
+class DevicePolicyStatusProvider : public CloudPolicyCoreStatusProvider {
+ public:
+ explicit DevicePolicyStatusProvider(
+ policy::BrowserPolicyConnectorChromeOS* connector);
+ ~DevicePolicyStatusProvider() override;
+
+ // CloudPolicyCoreStatusProvider implementation.
+ void GetStatus(base::DictionaryValue* dict) override;
+
+ private:
+ std::string domain_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevicePolicyStatusProvider);
+};
+
+// A cloud policy status provider that reads policy status from the policy core
+// associated with the device-local account specified by |user_id| at
+// construction time. The indirection via user ID and
+// DeviceLocalAccountPolicyService is necessary because the device-local account
+// may go away any time behind the scenes, at which point the status message
+// text will indicate CloudPolicyStore::STATUS_BAD_STATE.
+class DeviceLocalAccountPolicyStatusProvider
+ : public PolicyStatusProvider,
+ public policy::DeviceLocalAccountPolicyService::Observer {
+ public:
+ DeviceLocalAccountPolicyStatusProvider(
+ const std::string& user_id,
+ policy::DeviceLocalAccountPolicyService* service);
+ ~DeviceLocalAccountPolicyStatusProvider() override;
+
+ // PolicyStatusProvider implementation.
+ void GetStatus(base::DictionaryValue* dict) override;
+
+ // policy::DeviceLocalAccountPolicyService::Observer implementation.
+ void OnPolicyUpdated(const std::string& user_id) override;
+ void OnDeviceLocalAccountsChanged() override;
+
+ private:
+ const std::string user_id_;
+ policy::DeviceLocalAccountPolicyService* service_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyStatusProvider);
+};
+
+// Provides status for any kind of Active Directory policy (device or user).
+class ActiveDirectoryPolicyStatusProvider
+ : public PolicyStatusProvider,
+ public policy::CloudPolicyStore::Observer {
+ public:
+ explicit ActiveDirectoryPolicyStatusProvider(policy::CloudPolicyStore* store);
+ ~ActiveDirectoryPolicyStatusProvider() override;
+
+ // PolicyStatusProvider implementation.
+ void GetStatus(base::DictionaryValue* dict) override;
+
+ // policy::CloudPolicyStore::Observer implementation.
+ void OnStoreLoaded(policy::CloudPolicyStore* store) override;
+ void OnStoreError(policy::CloudPolicyStore* store) override;
+
+ private:
+ policy::CloudPolicyStore* store_;
+
+ DISALLOW_COPY_AND_ASSIGN(ActiveDirectoryPolicyStatusProvider);
+};
+#endif
+
+PolicyStatusProvider::PolicyStatusProvider() {}
+
+PolicyStatusProvider::~PolicyStatusProvider() {}
+
+void PolicyStatusProvider::SetStatusChangeCallback(
+ const base::Closure& callback) {
+ callback_ = callback;
+}
+
+void PolicyStatusProvider::GetStatus(base::DictionaryValue* dict) {}
+
+void PolicyStatusProvider::NotifyStatusChange() {
+ if (!callback_.is_null())
+ callback_.Run();
+}
+
+CloudPolicyCoreStatusProvider::CloudPolicyCoreStatusProvider(
+ policy::CloudPolicyCore* core) : core_(core) {
+ core_->store()->AddObserver(this);
+ // TODO(bartfab): Add an observer that watches for client errors. Observing
+ // core_->client() directly is not safe as the client may be destroyed and
+ // (re-)created anytime if the user signs in or out on desktop platforms.
+}
+
+CloudPolicyCoreStatusProvider::~CloudPolicyCoreStatusProvider() {
+ core_->store()->RemoveObserver(this);
+}
+
+void CloudPolicyCoreStatusProvider::OnStoreLoaded(
+ policy::CloudPolicyStore* store) {
+ NotifyStatusChange();
+}
+
+void CloudPolicyCoreStatusProvider::OnStoreError(
+ policy::CloudPolicyStore* store) {
+ NotifyStatusChange();
+}
+
+UserCloudPolicyStatusProvider::UserCloudPolicyStatusProvider(
+ policy::CloudPolicyCore* core)
+ : CloudPolicyCoreStatusProvider(core) {}
+
+UserCloudPolicyStatusProvider::~UserCloudPolicyStatusProvider() {}
+
+void UserCloudPolicyStatusProvider::GetStatus(base::DictionaryValue* dict) {
+ if (!core_->store()->is_managed())
+ return;
+ GetStatusFromCore(core_, dict);
+ ExtractDomainFromUsername(dict);
+}
+
+#if defined(OS_CHROMEOS)
+DevicePolicyStatusProvider::DevicePolicyStatusProvider(
+ policy::BrowserPolicyConnectorChromeOS* connector)
+ : CloudPolicyCoreStatusProvider(
+ connector->GetDeviceCloudPolicyManager()->core()) {
+ domain_ = connector->GetEnterpriseDomain();
+}
+
+DevicePolicyStatusProvider::~DevicePolicyStatusProvider() {
+}
+
+void DevicePolicyStatusProvider::GetStatus(base::DictionaryValue* dict) {
+ GetStatusFromCore(core_, dict);
+ dict->SetString("domain", domain_);
+}
+
+DeviceLocalAccountPolicyStatusProvider::DeviceLocalAccountPolicyStatusProvider(
+ const std::string& user_id,
+ policy::DeviceLocalAccountPolicyService* service)
+ : user_id_(user_id),
+ service_(service) {
+ service_->AddObserver(this);
+}
+
+DeviceLocalAccountPolicyStatusProvider::
+ ~DeviceLocalAccountPolicyStatusProvider() {
+ service_->RemoveObserver(this);
+}
+
+void DeviceLocalAccountPolicyStatusProvider::GetStatus(
+ base::DictionaryValue* dict) {
+ const policy::DeviceLocalAccountPolicyBroker* broker =
+ service_->GetBrokerForUser(user_id_);
+ if (broker) {
+ GetStatusFromCore(broker->core(), dict);
+ } else {
+ dict->SetBoolean("error", true);
+ dict->SetString("status",
+ policy::FormatStoreStatus(
+ policy::CloudPolicyStore::STATUS_BAD_STATE,
+ policy::CloudPolicyValidatorBase::VALIDATION_OK));
+ dict->SetString("username", std::string());
+ }
+ ExtractDomainFromUsername(dict);
+ dict->SetBoolean("publicAccount", true);
+}
+
+void DeviceLocalAccountPolicyStatusProvider::OnPolicyUpdated(
+ const std::string& user_id) {
+ if (user_id == user_id_)
+ NotifyStatusChange();
+}
+
+void DeviceLocalAccountPolicyStatusProvider::OnDeviceLocalAccountsChanged() {
+ NotifyStatusChange();
+}
+
+ActiveDirectoryPolicyStatusProvider::ActiveDirectoryPolicyStatusProvider(
+ policy::CloudPolicyStore* store)
+ : store_(store) {
+ store_->AddObserver(this);
+}
+
+ActiveDirectoryPolicyStatusProvider::~ActiveDirectoryPolicyStatusProvider() {
+ store_->RemoveObserver(this);
+}
+
+// TODO(tnagel): Provide more details and/or remove unused fields from UI. See
+// https://crbug.com/664747.
+void ActiveDirectoryPolicyStatusProvider::GetStatus(
+ base::DictionaryValue* dict) {
+ base::string16 status =
+ policy::FormatStoreStatus(store_->status(), store_->validation_status());
+ dict->SetString("status", status);
+}
+
+void ActiveDirectoryPolicyStatusProvider::OnStoreLoaded(
+ policy::CloudPolicyStore* store) {
+ NotifyStatusChange();
+}
+
+void ActiveDirectoryPolicyStatusProvider::OnStoreError(
+ policy::CloudPolicyStore* store) {
+ NotifyStatusChange();
+}
+
+#endif // defined(OS_CHROMEOS)
+
+PolicyUIHandler::PolicyUIHandler()
+ : weak_factory_(this) {
+}
+
+PolicyUIHandler::~PolicyUIHandler() {
+ GetPolicyService()->RemoveObserver(policy::POLICY_DOMAIN_CHROME, this);
+ GetPolicyService()->RemoveObserver(policy::POLICY_DOMAIN_EXTENSIONS, this);
+ policy::SchemaRegistry* registry =
+ policy::SchemaRegistryServiceFactory::GetForContext(
+ Profile::FromWebUI(web_ui())->GetOriginalProfile())->registry();
+ registry->RemoveObserver(this);
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ extensions::ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))
+ ->RemoveObserver(this);
+#endif
+}
+
+void PolicyUIHandler::AddLocalizedPolicyStrings(
+ content::WebUIDataSource* source,
+ const PolicyStringMap* strings,
+ size_t count) {
+ for (size_t i = 0; i < count; ++i)
+ source->AddLocalizedString(strings[i].key, strings[i].string_id);
+}
+
+void PolicyUIHandler::AddCommonLocalizedStringsToSource(
+ content::WebUIDataSource* source) {
+ AddLocalizedPolicyStrings(source, kPolicySources,
+ static_cast<size_t>(policy::POLICY_SOURCE_COUNT));
+ source->AddLocalizedString("title", IDS_POLICY_TITLE);
+ source->AddLocalizedString("headerScope", IDS_POLICY_HEADER_SCOPE);
+ source->AddLocalizedString("headerLevel", IDS_POLICY_HEADER_LEVEL);
+ source->AddLocalizedString("headerName", IDS_POLICY_HEADER_NAME);
+ source->AddLocalizedString("headerValue", IDS_POLICY_HEADER_VALUE);
+ source->AddLocalizedString("headerStatus", IDS_POLICY_HEADER_STATUS);
+ source->AddLocalizedString("headerSource", IDS_POLICY_HEADER_SOURCE);
+ source->AddLocalizedString("scopeUser", IDS_POLICY_SCOPE_USER);
+ source->AddLocalizedString("scopeDevice", IDS_POLICY_SCOPE_DEVICE);
+ source->AddLocalizedString("levelRecommended", IDS_POLICY_LEVEL_RECOMMENDED);
+ source->AddLocalizedString("levelMandatory", IDS_POLICY_LEVEL_MANDATORY);
+ source->AddLocalizedString("ok", IDS_POLICY_OK);
+ source->AddLocalizedString("unset", IDS_POLICY_UNSET);
+ source->AddLocalizedString("unknown", IDS_POLICY_UNKNOWN);
+ source->AddLocalizedString("notSpecified", IDS_POLICY_NOT_SPECIFIED);
+ source->SetJsonPath("strings.js");
+}
+
+void PolicyUIHandler::RegisterMessages() {
+#if defined(OS_CHROMEOS)
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ if (connector->IsEnterpriseManaged()) {
+ if (connector->GetDeviceActiveDirectoryPolicyManager()) {
+ device_status_provider_ =
+ base::MakeUnique<ActiveDirectoryPolicyStatusProvider>(
+ connector->GetDeviceActiveDirectoryPolicyManager()->store());
+ } else {
+ device_status_provider_ =
+ base::MakeUnique<DevicePolicyStatusProvider>(connector);
+ }
+ }
+
+ const user_manager::UserManager* user_manager =
+ user_manager::UserManager::Get();
+ Profile* profile = Profile::FromWebUI(web_ui());
+ policy::DeviceLocalAccountPolicyService* local_account_service =
+ user_manager->IsLoggedInAsPublicAccount()
+ ? connector->GetDeviceLocalAccountPolicyService()
+ : nullptr;
+ policy::UserCloudPolicyManagerChromeOS* user_cloud_policy =
+ policy::UserPolicyManagerFactoryChromeOS::GetCloudPolicyManagerForProfile(
+ profile);
+ policy::ActiveDirectoryPolicyManager* active_directory_policy =
+ policy::UserPolicyManagerFactoryChromeOS::
+ GetActiveDirectoryPolicyManagerForProfile(profile);
+ if (local_account_service) {
+ user_status_provider_ =
+ base::MakeUnique<DeviceLocalAccountPolicyStatusProvider>(
+ user_manager->GetActiveUser()->GetAccountId().GetUserEmail(),
+ local_account_service);
+ } else if (user_cloud_policy) {
+ user_status_provider_ = base::MakeUnique<UserCloudPolicyStatusProvider>(
+ user_cloud_policy->core());
+ } else if (active_directory_policy) {
+ user_status_provider_ =
+ base::MakeUnique<ActiveDirectoryPolicyStatusProvider>(
+ active_directory_policy->store());
+ }
+#else
+ policy::UserCloudPolicyManager* user_cloud_policy_manager =
+ policy::UserCloudPolicyManagerFactory::GetForBrowserContext(
+ web_ui()->GetWebContents()->GetBrowserContext());
+ if (user_cloud_policy_manager) {
+ user_status_provider_ = base::MakeUnique<UserCloudPolicyStatusProvider>(
+ user_cloud_policy_manager->core());
+ }
+#endif
+
+ if (!user_status_provider_.get())
+ user_status_provider_ = base::MakeUnique<PolicyStatusProvider>();
+ if (!device_status_provider_.get())
+ device_status_provider_ = base::MakeUnique<PolicyStatusProvider>();
+
+ base::Closure update_callback(base::Bind(&PolicyUIHandler::SendStatus,
+ base::Unretained(this)));
+ user_status_provider_->SetStatusChangeCallback(update_callback);
+ device_status_provider_->SetStatusChangeCallback(update_callback);
+ GetPolicyService()->AddObserver(policy::POLICY_DOMAIN_CHROME, this);
+ GetPolicyService()->AddObserver(policy::POLICY_DOMAIN_EXTENSIONS, this);
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ extensions::ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))
+ ->AddObserver(this);
+#endif
+ policy::SchemaRegistry* registry =
+ policy::SchemaRegistryServiceFactory::GetForContext(
+ Profile::FromWebUI(web_ui())->GetOriginalProfile())->registry();
+ registry->AddObserver(this);
+
+ web_ui()->RegisterMessageCallback(
+ "initialized",
+ base::Bind(&PolicyUIHandler::HandleInitialized, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "reloadPolicies",
+ base::Bind(&PolicyUIHandler::HandleReloadPolicies,
+ base::Unretained(this)));
+}
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+void PolicyUIHandler::OnExtensionLoaded(
+ content::BrowserContext* browser_context,
+ const extensions::Extension* extension) {
+ SendPolicyNames();
+ SendPolicyValues();
+}
+
+void PolicyUIHandler::OnExtensionUnloaded(
+ content::BrowserContext* browser_context,
+ const extensions::Extension* extension,
+ extensions::UnloadedExtensionReason reason) {
+ SendPolicyNames();
+ SendPolicyValues();
+}
+#endif
+
+void PolicyUIHandler::OnSchemaRegistryUpdated(bool has_new_schemas) {
+ // Update UI when new schema is added.
+ if (has_new_schemas) {
+ SendPolicyNames();
+ SendPolicyValues();
+ }
+}
+
+void PolicyUIHandler::OnPolicyUpdated(const policy::PolicyNamespace& ns,
+ const policy::PolicyMap& previous,
+ const policy::PolicyMap& current) {
+ SendPolicyValues();
+}
+
+void PolicyUIHandler::AddPolicyName(const std::string& name,
+ base::DictionaryValue* names) const {
+ names->SetBoolean(name, true);
+}
+
+void PolicyUIHandler::SendPolicyNames() const {
+ base::DictionaryValue names;
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ policy::SchemaRegistry* registry =
+ policy::SchemaRegistryServiceFactory::GetForContext(
+ profile->GetOriginalProfile())->registry();
+ scoped_refptr<policy::SchemaMap> schema_map = registry->schema_map();
+
+ // Add Chrome policy names.
+ auto chrome_policy_names = base::MakeUnique<base::DictionaryValue>();
+ policy::PolicyNamespace chrome_ns(policy::POLICY_DOMAIN_CHROME, "");
+ const policy::Schema* chrome_schema = schema_map->GetSchema(chrome_ns);
+ for (policy::Schema::Iterator it = chrome_schema->GetPropertiesIterator();
+ !it.IsAtEnd(); it.Advance()) {
+ AddPolicyName(it.key(), chrome_policy_names.get());
+ }
+ names.Set("chromePolicyNames", std::move(chrome_policy_names));
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ // Add extension policy names.
+ auto extension_policy_names = base::MakeUnique<base::DictionaryValue>();
+
+ for (const scoped_refptr<const extensions::Extension>& extension :
+ extensions::ExtensionRegistry::Get(profile)->enabled_extensions()) {
+ // Skip this extension if it's not an enterprise extension.
+ if (!extension->manifest()->HasPath(
+ extensions::manifest_keys::kStorageManagedSchema))
+ continue;
+ auto extension_value = base::MakeUnique<base::DictionaryValue>();
+ extension_value->SetString("name", extension->name());
+ const policy::Schema* schema =
+ schema_map->GetSchema(policy::PolicyNamespace(
+ policy::POLICY_DOMAIN_EXTENSIONS, extension->id()));
+ auto policy_names = base::MakeUnique<base::DictionaryValue>();
+ if (schema && schema->valid()) {
+ // Get policy names from the extension's policy schema.
+ // Store in a map, not an array, for faster lookup on JS side.
+ for (policy::Schema::Iterator prop = schema->GetPropertiesIterator();
+ !prop.IsAtEnd(); prop.Advance()) {
+ policy_names->SetBoolean(prop.key(), true);
+ }
+ }
+ extension_value->Set("policyNames", std::move(policy_names));
+ extension_policy_names->Set(extension->id(), std::move(extension_value));
+ }
+ names.Set("extensionPolicyNames", std::move(extension_policy_names));
+#endif // BUILDFLAG(ENABLE_EXTENSIONS)
+
+ web_ui()->CallJavascriptFunctionUnsafe("policy.Page.setPolicyNames", names);
+}
+
+void PolicyUIHandler::SendPolicyValues() const {
+ base::DictionaryValue all_policies;
+
+ // Add Chrome policy values.
+ auto chrome_policies = base::MakeUnique<base::DictionaryValue>();
+ GetChromePolicyValues(chrome_policies.get());
+ all_policies.Set("chromePolicies", std::move(chrome_policies));
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ // Add extension policy values.
+ extensions::ExtensionRegistry* registry =
+ extensions::ExtensionRegistry::Get(Profile::FromWebUI(web_ui()));
+ auto extension_values = base::MakeUnique<base::DictionaryValue>();
+
+ for (const scoped_refptr<const extensions::Extension>& extension :
+ registry->enabled_extensions()) {
+ // Skip this extension if it's not an enterprise extension.
+ if (!extension->manifest()->HasPath(
+ extensions::manifest_keys::kStorageManagedSchema))
+ continue;
+ auto extension_policies = base::MakeUnique<base::DictionaryValue>();
+ policy::PolicyNamespace policy_namespace = policy::PolicyNamespace(
+ policy::POLICY_DOMAIN_EXTENSIONS, extension->id());
+ policy::PolicyErrorMap empty_error_map;
+ GetPolicyValues(GetPolicyService()->GetPolicies(policy_namespace),
+ &empty_error_map, extension_policies.get());
+ extension_values->Set(extension->id(), std::move(extension_policies));
+ }
+ all_policies.Set("extensionPolicies", std::move(extension_values));
+#endif
+ web_ui()->CallJavascriptFunctionUnsafe("policy.Page.setPolicyValues",
+ all_policies);
+}
+
+void PolicyUIHandler::GetPolicyValues(const policy::PolicyMap& map,
+ policy::PolicyErrorMap* errors,
+ base::DictionaryValue* values) const {
+ for (const auto& entry : map) {
+ std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
+ value->Set("value", CopyAndConvert(entry.second.value.get()));
+ if (entry.second.scope == policy::POLICY_SCOPE_USER)
+ value->SetString("scope", "user");
+ else
+ value->SetString("scope", "machine");
+ if (entry.second.level == policy::POLICY_LEVEL_RECOMMENDED)
+ value->SetString("level", "recommended");
+ else
+ value->SetString("level", "mandatory");
+ value->SetString("source", kPolicySources[entry.second.source].key);
+ base::string16 error = errors->GetErrors(entry.first);
+ if (!error.empty())
+ value->SetString("error", error);
+ values->Set(entry.first, std::move(value));
+ }
+}
+
+void PolicyUIHandler::GetChromePolicyValues(
+ base::DictionaryValue* values) const {
+ policy::PolicyService* policy_service = GetPolicyService();
+ policy::PolicyMap map;
+
+ // Make a copy that can be modified, since some policy values are modified
+ // before being displayed.
+ map.CopyFrom(policy_service->GetPolicies(
+ policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string())));
+
+ // Get a list of all the errors in the policy values.
+ const policy::ConfigurationPolicyHandlerList* handler_list =
+ g_browser_process->browser_policy_connector()->GetHandlerList();
+ policy::PolicyErrorMap errors;
+ handler_list->ApplyPolicySettings(map, NULL, &errors);
+
+ // Convert dictionary values to strings for display.
+ handler_list->PrepareForDisplaying(&map);
+
+ GetPolicyValues(map, &errors, values);
+}
+
+void PolicyUIHandler::SendStatus() const {
+ std::unique_ptr<base::DictionaryValue> device_status(
+ new base::DictionaryValue);
+ device_status_provider_->GetStatus(device_status.get());
+ if (!device_domain_.empty())
+ device_status->SetString("domain", device_domain_);
+ std::unique_ptr<base::DictionaryValue> user_status(new base::DictionaryValue);
+ user_status_provider_->GetStatus(user_status.get());
+ std::string username;
+ user_status->GetString("username", &username);
+ if (!username.empty())
+ user_status->SetString("domain", gaia::ExtractDomainName(username));
+
+ base::DictionaryValue status;
+ if (!device_status->empty())
+ status.Set("device", std::move(device_status));
+ if (!user_status->empty())
+ status.Set("user", std::move(user_status));
+
+ web_ui()->CallJavascriptFunctionUnsafe("policy.Page.setStatus", status);
+}
+
+void PolicyUIHandler::HandleInitialized(const base::ListValue* args) {
+ SendPolicyNames();
+ SendPolicyValues();
+ SendStatus();
+}
+
+void PolicyUIHandler::HandleReloadPolicies(const base::ListValue* args) {
+#if defined(OS_CHROMEOS)
+ // Allow user to manually fetch remote commands, in case invalidation
+ // service is not working properly.
+ // TODO(binjin): evaluate and possibly remove this after invalidation
+ // service is landed and tested. http://crbug.com/480982
+ policy::CloudPolicyManager* manager =
+ g_browser_process->platform_part()
+ ->browser_policy_connector_chromeos()
+ ->GetDeviceCloudPolicyManager();
+ // Active Directory management has no CloudPolicyManager.
+ if (manager) {
+ policy::RemoteCommandsService* remote_commands_service =
+ manager->core()->remote_commands_service();
+ if (remote_commands_service)
+ remote_commands_service->FetchRemoteCommands();
+ }
+#endif
+ GetPolicyService()->RefreshPolicies(base::Bind(
+ &PolicyUIHandler::OnRefreshPoliciesDone, weak_factory_.GetWeakPtr()));
+}
+
+void PolicyUIHandler::OnRefreshPoliciesDone() const {
+ web_ui()->CallJavascriptFunctionUnsafe("policy.Page.reloadPoliciesDone");
+}
+
+policy::PolicyService* PolicyUIHandler::GetPolicyService() const {
+ return policy::ProfilePolicyConnectorFactory::GetForBrowserContext(
+ web_ui()->GetWebContents()->GetBrowserContext())->policy_service();
+}
diff --git a/chromium/chrome/browser/ui/webui/policy_ui_handler.h b/chromium/chrome/browser/ui/webui/policy_ui_handler.h
new file mode 100644
index 00000000000..1201ce0de84
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/policy_ui_handler.h
@@ -0,0 +1,121 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_POLICY_UI_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_POLICY_UI_HANDLER_H_
+
+#include <stddef.h>
+#include <string.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/policy/core/browser/policy_error_map.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/core/common/policy_service.h"
+#include "components/policy/core/common/schema_registry.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "extensions/features/features.h"
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/browser/extension_registry_observer.h"
+#endif
+
+struct PolicyStringMap {
+ const char* key;
+ int string_id;
+};
+
+class PolicyStatusProvider;
+
+// The JavaScript message handler for the chrome://policy page.
+class PolicyUIHandler : public content::WebUIMessageHandler,
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ public extensions::ExtensionRegistryObserver,
+#endif
+ public policy::PolicyService::Observer,
+ public policy::SchemaRegistry::Observer {
+ public:
+ PolicyUIHandler();
+ ~PolicyUIHandler() override;
+
+
+ static void AddLocalizedPolicyStrings(content::WebUIDataSource* source,
+ const PolicyStringMap* strings,
+ size_t count);
+
+ static void AddCommonLocalizedStringsToSource(
+ content::WebUIDataSource* source);
+
+ // content::WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ // extensions::ExtensionRegistryObserver implementation.
+ void OnExtensionLoaded(content::BrowserContext* browser_context,
+ const extensions::Extension* extension) override;
+ void OnExtensionUnloaded(content::BrowserContext* browser_context,
+ const extensions::Extension* extension,
+ extensions::UnloadedExtensionReason reason) override;
+#endif
+
+ // policy::PolicyService::Observer implementation.
+ void OnPolicyUpdated(const policy::PolicyNamespace& ns,
+ const policy::PolicyMap& previous,
+ const policy::PolicyMap& current) override;
+
+ // policy::SchemaRegistry::Observer implementation.
+ void OnSchemaRegistryUpdated(bool has_new_schemas) override;
+
+ protected:
+ virtual void AddPolicyName(const std::string& name,
+ base::DictionaryValue* names) const;
+
+ // Send a dictionary containing the names of all known policies to the UI.
+ virtual void SendPolicyNames() const;
+
+ private:
+ // Send information about the current policy values to the UI. For each policy
+ // whose value has been set, a dictionary containing the value and additional
+ // metadata is sent.
+ void SendPolicyValues() const;
+
+ // Send the status of cloud policy to the UI. For each scope that has cloud
+ // policy enabled (device and/or user), a dictionary containing status
+ // information is sent.
+ void SendStatus() const;
+
+ // Inserts a description of each policy in |policy_map| into |values|, using
+ // the optional errors in |errors| to determine the status of each policy.
+ void GetPolicyValues(const policy::PolicyMap& policy_map,
+ policy::PolicyErrorMap* errors,
+ base::DictionaryValue* values) const;
+
+ void GetChromePolicyValues(base::DictionaryValue* values) const;
+
+ void HandleInitialized(const base::ListValue* args);
+ void HandleReloadPolicies(const base::ListValue* args);
+
+ void OnRefreshPoliciesDone() const;
+
+ policy::PolicyService* GetPolicyService() const;
+
+ std::string device_domain_;
+
+ // Providers that supply status dictionaries for user and device policy,
+ // respectively. These are created on initialization time as appropriate for
+ // the platform (Chrome OS / desktop) and type of policy that is in effect.
+ std::unique_ptr<PolicyStatusProvider> user_status_provider_;
+ std::unique_ptr<PolicyStatusProvider> device_status_provider_;
+
+ base::WeakPtrFactory<PolicyUIHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PolicyUIHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_POLICY_UI_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/popular_sites_internals_ui.cc b/chromium/chrome/browser/ui/webui/popular_sites_internals_ui.cc
new file mode 100644
index 00000000000..88e07798e3f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/popular_sites_internals_ui.cc
@@ -0,0 +1,96 @@
+// 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.
+
+#include "chrome/browser/ui/webui/popular_sites_internals_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/ntp_tiles/chrome_popular_sites_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "components/grit/components_resources.h"
+#include "components/ntp_tiles/popular_sites.h"
+#include "components/ntp_tiles/webui/popular_sites_internals_message_handler.h"
+#include "components/ntp_tiles/webui/popular_sites_internals_message_handler_client.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 "content/public/browser/web_ui_message_handler.h"
+
+namespace {
+
+// The implementation for the chrome://popular-sites-internals page.
+class ChromePopularSitesInternalsMessageHandlerBridge
+ : public content::WebUIMessageHandler,
+ public ntp_tiles::PopularSitesInternalsMessageHandlerClient {
+ public:
+ ChromePopularSitesInternalsMessageHandlerBridge() : handler_(this) {}
+
+ private:
+ // content::WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ // ntp_tiles::PopularSitesInternalsMessageHandlerClient
+ std::unique_ptr<ntp_tiles::PopularSites> MakePopularSites() override;
+ PrefService* GetPrefs() override;
+ void RegisterMessageCallback(
+ const std::string& message,
+ const base::Callback<void(const base::ListValue*)>& callback) override;
+ void CallJavascriptFunctionVector(
+ const std::string& name,
+ const std::vector<const base::Value*>& values) override;
+
+ ntp_tiles::PopularSitesInternalsMessageHandler handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromePopularSitesInternalsMessageHandlerBridge);
+};
+
+void ChromePopularSitesInternalsMessageHandlerBridge::RegisterMessages() {
+ handler_.RegisterMessages();
+}
+
+std::unique_ptr<ntp_tiles::PopularSites>
+ChromePopularSitesInternalsMessageHandlerBridge::MakePopularSites() {
+ return ChromePopularSitesFactory::NewForProfile(Profile::FromWebUI(web_ui()));
+}
+
+PrefService* ChromePopularSitesInternalsMessageHandlerBridge::GetPrefs() {
+ return Profile::FromWebUI(web_ui())->GetPrefs();
+}
+
+void ChromePopularSitesInternalsMessageHandlerBridge::RegisterMessageCallback(
+ const std::string& message,
+ const base::Callback<void(const base::ListValue*)>& callback) {
+ web_ui()->RegisterMessageCallback(message, callback);
+}
+
+void ChromePopularSitesInternalsMessageHandlerBridge::
+ CallJavascriptFunctionVector(
+ const std::string& name,
+ const std::vector<const base::Value*>& values) {
+ web_ui()->CallJavascriptFunctionUnsafe(name, values);
+}
+
+} // namespace
+
+content::WebUIDataSource* CreatePopularSitesInternalsHTMLSource() {
+ content::WebUIDataSource* source = content::WebUIDataSource::Create(
+ chrome::kChromeUIPopularSitesInternalsHost);
+
+ source->AddResourcePath("popular_sites_internals.js",
+ IDR_POPULAR_SITES_INTERNALS_JS);
+ source->AddResourcePath("popular_sites_internals.css",
+ IDR_POPULAR_SITES_INTERNALS_CSS);
+ source->SetDefaultResource(IDR_POPULAR_SITES_INTERNALS_HTML);
+ return source;
+}
+
+PopularSitesInternalsUI::PopularSitesInternalsUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui),
+ CreatePopularSitesInternalsHTMLSource());
+ web_ui->AddMessageHandler(
+ base::MakeUnique<ChromePopularSitesInternalsMessageHandlerBridge>());
+}
+
+PopularSitesInternalsUI::~PopularSitesInternalsUI() {}
diff --git a/chromium/chrome/browser/ui/webui/popular_sites_internals_ui.h b/chromium/chrome/browser/ui/webui/popular_sites_internals_ui.h
new file mode 100644
index 00000000000..8995536620b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/popular_sites_internals_ui.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_POPULAR_SITES_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_POPULAR_SITES_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The implementation for the chrome://popular-sites-internals page.
+class PopularSitesInternalsUI : public content::WebUIController {
+ public:
+ explicit PopularSitesInternalsUI(content::WebUI* web_ui);
+ ~PopularSitesInternalsUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PopularSitesInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_POPULAR_SITES_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/predictors/OWNERS b/chromium/chrome/browser/ui/webui/predictors/OWNERS
new file mode 100644
index 00000000000..0ecdfa84116
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/predictors/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/predictors/OWNERS
diff --git a/chromium/chrome/browser/ui/webui/predictors/predictors_handler.cc b/chromium/chrome/browser/ui/webui/predictors/predictors_handler.cc
new file mode 100644
index 00000000000..76c8ee20444
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/predictors/predictors_handler.cc
@@ -0,0 +1,149 @@
+// 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/ui/webui/predictors/predictors_handler.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/predictors/autocomplete_action_predictor.h"
+#include "chrome/browser/predictors/autocomplete_action_predictor_factory.h"
+#include "chrome/browser/predictors/loading_predictor.h"
+#include "chrome/browser/predictors/loading_predictor_factory.h"
+#include "chrome/browser/predictors/resource_prefetch_predictor_tables.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/common/resource_type.h"
+
+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);
+ loading_predictor_ =
+ predictors::LoadingPredictorFactory::GetForProfile(profile);
+}
+
+PredictorsHandler::~PredictorsHandler() { }
+
+void PredictorsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("requestAutocompleteActionPredictorDb",
+ base::Bind(&PredictorsHandler::RequestAutocompleteActionPredictorDb,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("requestResourcePrefetchPredictorDb",
+ base::Bind(&PredictorsHandler::RequestResourcePrefetchPredictorDb,
+ base::Unretained(this)));
+}
+
+void PredictorsHandler::RequestAutocompleteActionPredictorDb(
+ const base::ListValue* args) {
+ const bool enabled = (autocomplete_action_predictor_ != NULL);
+ base::DictionaryValue dict;
+ dict.SetBoolean("enabled", enabled);
+ if (enabled) {
+ auto db = base::MakeUnique<base::ListValue>();
+ for (AutocompleteActionPredictor::DBCacheMap::const_iterator it =
+ autocomplete_action_predictor_->db_cache_.begin();
+ it != autocomplete_action_predictor_->db_cache_.end();
+ ++it) {
+ std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
+ entry->SetString("user_text", it->first.user_text);
+ entry->SetString("url", it->first.url.spec());
+ entry->SetInteger("hit_count", it->second.number_of_hits);
+ entry->SetInteger("miss_count", it->second.number_of_misses);
+ entry->SetDouble("confidence",
+ autocomplete_action_predictor_->CalculateConfidenceForDbEntry(it));
+ db->Append(std::move(entry));
+ }
+ dict.Set("db", std::move(db));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("updateAutocompleteActionPredictorDb",
+ dict);
+}
+
+void PredictorsHandler::RequestResourcePrefetchPredictorDb(
+ const base::ListValue* args) {
+ const bool enabled = (loading_predictor_ != nullptr);
+ base::DictionaryValue dict;
+ dict.SetBoolean("enabled", enabled);
+
+ if (enabled) {
+ // Url Database cache.
+ auto db = base::MakeUnique<base::ListValue>();
+ auto* resource_prefetch_predictor =
+ loading_predictor_->resource_prefetch_predictor();
+ AddPrefetchDataMapToListValue(
+ *resource_prefetch_predictor->url_table_cache_, db.get());
+ dict.Set("url_db", std::move(db));
+
+ db = base::MakeUnique<base::ListValue>();
+ AddPrefetchDataMapToListValue(
+ *resource_prefetch_predictor->host_table_cache_, db.get());
+ dict.Set("host_db", std::move(db));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("updateResourcePrefetchPredictorDb",
+ dict);
+}
+
+void PredictorsHandler::AddPrefetchDataMapToListValue(
+ const ResourcePrefetchPredictor::PrefetchDataMap& data_map,
+ base::ListValue* db) const {
+ for (const auto& p : data_map) {
+ std::unique_ptr<base::DictionaryValue> main(new base::DictionaryValue());
+ main->SetString("main_frame_url", p.first);
+ auto resources = base::MakeUnique<base::ListValue>();
+ for (const predictors::ResourceData& r : p.second.resources()) {
+ std::unique_ptr<base::DictionaryValue> resource(
+ new 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));
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/predictors/predictors_handler.h b/chromium/chrome/browser/ui/webui/predictors/predictors_handler.h
new file mode 100644
index 00000000000..99796e0234e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/predictors/predictors_handler.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_PREDICTORS_PREDICTORS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_PREDICTORS_PREDICTORS_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/predictors/resource_prefetch_predictor.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace predictors {
+class AutocompleteActionPredictor;
+class LoadingPredictor;
+}
+
+class Profile;
+
+// The handler for Javascript messages for about:predictors.
+class PredictorsHandler : public content::WebUIMessageHandler {
+ public:
+ explicit PredictorsHandler(Profile* profile);
+ ~PredictorsHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ // Synchronously fetches the database from AutocompleteActionPredictor and
+ // calls into JS with the resulting DictionaryValue.
+ void RequestAutocompleteActionPredictorDb(const base::ListValue* args);
+
+ // Fetches stats for the ResourcePrefetchPredictor and returns it as a
+ // DictionaryValue to the JS.
+ void RequestResourcePrefetchPredictorDb(const base::ListValue* args);
+
+ // Helper for RequestResourcePrefetchPredictorDb.
+ void AddPrefetchDataMapToListValue(
+ const predictors::ResourcePrefetchPredictor::PrefetchDataMap& data_map,
+ base::ListValue* db) const;
+
+ predictors::AutocompleteActionPredictor* autocomplete_action_predictor_;
+ predictors::LoadingPredictor* loading_predictor_;
+
+ DISALLOW_COPY_AND_ASSIGN(PredictorsHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_PREDICTORS_PREDICTORS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/predictors/predictors_ui.cc b/chromium/chrome/browser/ui/webui/predictors/predictors_ui.cc
new file mode 100644
index 00000000000..e8cd38a13ef
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/predictors/predictors_ui.cc
@@ -0,0 +1,33 @@
+// 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/ui/webui/predictors/predictors_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/predictors/predictors_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace {
+
+content::WebUIDataSource* CreatePredictorsUIHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIPredictorsHost);
+ source->AddResourcePath("predictors.js", IDR_PREDICTORS_JS);
+ source->SetDefaultResource(IDR_PREDICTORS_HTML);
+ source->UseGzip(std::unordered_set<std::string>());
+ return source;
+}
+
+} // namespace
+
+PredictorsUI::PredictorsUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ web_ui->AddMessageHandler(base::MakeUnique<PredictorsHandler>(profile));
+ content::WebUIDataSource::Add(profile, CreatePredictorsUIHTMLSource());
+}
diff --git a/chromium/chrome/browser/ui/webui/predictors/predictors_ui.h b/chromium/chrome/browser/ui/webui/predictors/predictors_ui.h
new file mode 100644
index 00000000000..0f37e349839
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/predictors/predictors_ui.h
@@ -0,0 +1,19 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_PREDICTORS_PREDICTORS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_PREDICTORS_PREDICTORS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+class PredictorsUI : public content::WebUIController {
+ public:
+ explicit PredictorsUI(content::WebUI* web_ui);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PredictorsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_PREDICTORS_PREDICTORS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/prefs_internals_browsertest.cc b/chromium/chrome/browser/ui/webui/prefs_internals_browsertest.cc
new file mode 100644
index 00000000000..9bdde8705f7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/prefs_internals_browsertest.cc
@@ -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.
+
+#include "base/guid.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.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/prefs/pref_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "url/gurl.h"
+
+using PrefsInternalsTest = InProcessBrowserTest;
+
+IN_PROC_BROWSER_TEST_F(PrefsInternalsTest, TestPrefsAreServed) {
+ // Set a preference to something very unique so we can look for it in the
+ // generated page.
+ std::string guid = base::GenerateGUID();
+ GURL fake_homepage_url = GURL("http://example.com/" + guid);
+ EXPECT_TRUE(fake_homepage_url.is_valid());
+ browser()->profile()->GetPrefs()->SetString(prefs::kHomePage,
+ fake_homepage_url.spec());
+
+ // First, check that navigation succeeds.
+ GURL kUrl("chrome://prefs-internals");
+ ui_test_utils::NavigateToURL(browser(), kUrl);
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(web_contents);
+ EXPECT_EQ(kUrl, web_contents->GetLastCommittedURL());
+ EXPECT_FALSE(web_contents->IsCrashed());
+ EXPECT_FALSE(web_contents->GetInterstitialPage());
+
+ // It's difficult to test the content of the page without duplicating the
+ // implementation, but we can at least assert that something is being shown.
+ bool has_text = false;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+ web_contents,
+ base::StringPrintf("window.domAutomationController.send("
+ "document.body.textContent && "
+ "document.body.textContent.indexOf('%s') >= 0);",
+ guid.c_str()),
+ &has_text));
+ EXPECT_TRUE(has_text);
+}
diff --git a/chromium/chrome/browser/ui/webui/prefs_internals_source.cc b/chromium/chrome/browser/ui/webui/prefs_internals_source.cc
new file mode 100644
index 00000000000..9d30d1190c5
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/prefs_internals_source.cc
@@ -0,0 +1,42 @@
+// 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/prefs_internals_source.h"
+
+#include <string>
+
+#include "base/json/json_writer.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_thread.h"
+
+PrefsInternalsSource::PrefsInternalsSource(Profile* profile)
+ : profile_(profile) {}
+
+PrefsInternalsSource::~PrefsInternalsSource() = default;
+
+std::string PrefsInternalsSource::GetSource() const {
+ return chrome::kChromeUIPrefsInternalsHost;
+}
+
+std::string PrefsInternalsSource::GetMimeType(const std::string& path) const {
+ return "text/plain";
+}
+
+void PrefsInternalsSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ std::string json;
+ std::unique_ptr<base::DictionaryValue> prefs =
+ profile_->GetPrefs()->GetPreferenceValues(PrefService::INCLUDE_DEFAULTS);
+ DCHECK(prefs);
+ CHECK(base::JSONWriter::WriteWithOptions(
+ *prefs, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json));
+ callback.Run(base::RefCountedString::TakeString(&json));
+}
diff --git a/chromium/chrome/browser/ui/webui/prefs_internals_source.h b/chromium/chrome/browser/ui/webui/prefs_internals_source.h
new file mode 100644
index 00000000000..471c05d09e6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/prefs_internals_source.h
@@ -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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_PREFS_INTERNALS_SOURCE_H_
+#define CHROME_BROWSER_UI_WEBUI_PREFS_INTERNALS_SOURCE_H_
+
+#include "base/macros.h"
+#include "content/public/browser/url_data_source.h"
+
+class Profile;
+
+// A simple data source that returns the preferences for the associated profile.
+class PrefsInternalsSource : public content::URLDataSource {
+ public:
+ explicit PrefsInternalsSource(Profile* profile);
+ ~PrefsInternalsSource() override;
+
+ // content::URLDataSource:
+ std::string GetSource() const override;
+ std::string GetMimeType(const std::string& path) const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+
+ private:
+ Profile* profile_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefsInternalsSource);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_PREFS_INTERNALS_SOURCE_H_
diff --git a/chromium/chrome/browser/ui/webui/print_preview/OWNERS b/chromium/chrome/browser/ui/webui/print_preview/OWNERS
new file mode 100644
index 00000000000..9b08c6620bd
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/OWNERS
@@ -0,0 +1 @@
+file://printing/OWNERS
diff --git a/chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc b/chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
new file mode 100644
index 00000000000..2814faff606
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
@@ -0,0 +1,370 @@
+// 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.
+
+#include "chrome/browser/ui/webui/print_preview/extension_printer_handler.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/strings/string_split.h"
+#include "base/task_scheduler/post_task.h"
+#include "chrome/browser/printing/pwg_raster_converter.h"
+#include "components/cloud_devices/common/cloud_device_description.h"
+#include "components/cloud_devices/common/printer_description.h"
+#include "device/base/device_client.h"
+#include "device/usb/usb_device.h"
+#include "device/usb/usb_service.h"
+#include "extensions/browser/api/device_permissions_manager.h"
+#include "extensions/browser/api/printer_provider/printer_provider_api.h"
+#include "extensions/browser/api/printer_provider/printer_provider_api_factory.h"
+#include "extensions/browser/api/printer_provider/printer_provider_print_job.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/api/printer_provider/usb_printer_manifest_data.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "extensions/common/permissions/usb_device_permission.h"
+#include "extensions/common/permissions/usb_device_permission_data.h"
+#include "extensions/common/value_builder.h"
+#include "printing/pwg_raster_settings.h"
+
+using device::UsbDevice;
+using extensions::DevicePermissionsManager;
+using extensions::DictionaryBuilder;
+using extensions::Extension;
+using extensions::ExtensionRegistry;
+using extensions::ListBuilder;
+using extensions::UsbPrinterManifestData;
+using printing::PWGRasterConverter;
+
+namespace {
+
+const char kContentTypePdf[] = "application/pdf";
+const char kContentTypePWGRaster[] = "image/pwg-raster";
+const char kContentTypeAll[] = "*/*";
+
+const char kInvalidDataPrintError[] = "INVALID_DATA";
+const char kInvalidTicketPrintError[] = "INVALID_TICKET";
+
+const char kProvisionalUsbLabel[] = "provisional-usb";
+
+// Updates |job| with raster file path, size and last modification time.
+// Returns the updated print job.
+std::unique_ptr<extensions::PrinterProviderPrintJob>
+UpdateJobFileInfoOnWorkerThread(
+ const base::FilePath& raster_path,
+ std::unique_ptr<extensions::PrinterProviderPrintJob> job) {
+ if (base::GetFileInfo(raster_path, &job->file_info))
+ job->document_path = raster_path;
+ return job;
+}
+
+// Callback to PWG raster conversion.
+// Posts a task to update print job with info about file containing converted
+// PWG raster data.
+void UpdateJobFileInfo(
+ std::unique_ptr<extensions::PrinterProviderPrintJob> job,
+ const ExtensionPrinterHandler::PrintJobCallback& callback,
+ bool success,
+ const base::FilePath& pwg_file_path) {
+ if (!success) {
+ callback.Run(std::move(job));
+ return;
+ }
+
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE,
+ {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+ base::Bind(&UpdateJobFileInfoOnWorkerThread, pwg_file_path,
+ base::Passed(&job)),
+ callback);
+}
+
+bool HasUsbPrinterProviderPermissions(const Extension* extension) {
+ return extension->permissions_data() &&
+ extension->permissions_data()->HasAPIPermission(
+ extensions::APIPermission::kPrinterProvider) &&
+ extension->permissions_data()->HasAPIPermission(
+ extensions::APIPermission::kUsb);
+}
+
+std::string GenerateProvisionalUsbPrinterId(const Extension* extension,
+ const UsbDevice* device) {
+ return base::StringPrintf("%s:%s:%s", kProvisionalUsbLabel,
+ extension->id().c_str(), device->guid().c_str());
+}
+
+bool ParseProvisionalUsbPrinterId(const std::string& printer_id,
+ std::string* extension_id,
+ std::string* device_guid) {
+ std::vector<std::string> components = base::SplitString(
+ printer_id, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+ if (components.size() != 3)
+ return false;
+
+ if (components[0] != kProvisionalUsbLabel)
+ return false;
+
+ *extension_id = components[1];
+ *device_guid = components[2];
+ return true;
+}
+
+} // namespace
+
+ExtensionPrinterHandler::ExtensionPrinterHandler(
+ content::BrowserContext* browser_context)
+ : browser_context_(browser_context), weak_ptr_factory_(this) {}
+
+ExtensionPrinterHandler::~ExtensionPrinterHandler() {
+}
+
+void ExtensionPrinterHandler::Reset() {
+ // TODO(tbarzic): Keep track of pending request ids issued by |this| and
+ // cancel them from here.
+ pending_enumeration_count_ = 0;
+ weak_ptr_factory_.InvalidateWeakPtrs();
+}
+
+void ExtensionPrinterHandler::StartGetPrinters(
+ const PrinterHandler::GetPrintersCallback& callback) {
+ // Assume that there can only be one printer enumeration occuring at once.
+ DCHECK_EQ(pending_enumeration_count_, 0);
+ pending_enumeration_count_ = 1;
+
+ bool extension_supports_usb_printers = false;
+ ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
+ for (const auto& extension : registry->enabled_extensions()) {
+ if (UsbPrinterManifestData::Get(extension.get()) &&
+ HasUsbPrinterProviderPermissions(extension.get())) {
+ extension_supports_usb_printers = true;
+ break;
+ }
+ }
+
+ if (extension_supports_usb_printers) {
+ device::UsbService* service = device::DeviceClient::Get()->GetUsbService();
+ pending_enumeration_count_++;
+ service->GetDevices(
+ base::Bind(&ExtensionPrinterHandler::OnUsbDevicesEnumerated,
+ weak_ptr_factory_.GetWeakPtr(), callback));
+ }
+
+ extensions::PrinterProviderAPIFactory::GetInstance()
+ ->GetForBrowserContext(browser_context_)
+ ->DispatchGetPrintersRequested(
+ base::Bind(&ExtensionPrinterHandler::WrapGetPrintersCallback,
+ weak_ptr_factory_.GetWeakPtr(), callback));
+}
+
+void ExtensionPrinterHandler::StartGetCapability(
+ const std::string& destination_id,
+ const PrinterHandler::GetCapabilityCallback& callback) {
+ extensions::PrinterProviderAPIFactory::GetInstance()
+ ->GetForBrowserContext(browser_context_)
+ ->DispatchGetCapabilityRequested(
+ destination_id,
+ base::Bind(&ExtensionPrinterHandler::WrapGetCapabilityCallback,
+ weak_ptr_factory_.GetWeakPtr(), callback, destination_id));
+}
+
+void ExtensionPrinterHandler::StartPrint(
+ const std::string& destination_id,
+ const std::string& capability,
+ const base::string16& job_title,
+ const std::string& ticket_json,
+ const gfx::Size& page_size,
+ const scoped_refptr<base::RefCountedMemory>& print_data,
+ const PrinterHandler::PrintCallback& callback) {
+ std::unique_ptr<extensions::PrinterProviderPrintJob> print_job(
+ new extensions::PrinterProviderPrintJob());
+ print_job->printer_id = destination_id;
+ print_job->job_title = job_title;
+ print_job->ticket_json = ticket_json;
+
+ cloud_devices::CloudDeviceDescription printer_description;
+ printer_description.InitFromString(capability);
+
+ cloud_devices::printer::ContentTypesCapability content_types;
+ content_types.LoadFrom(printer_description);
+
+ const bool kUsePdf = content_types.Contains(kContentTypePdf) ||
+ content_types.Contains(kContentTypeAll);
+
+ if (kUsePdf) {
+ // TODO(tbarzic): Consider writing larger PDF to disk and provide the data
+ // the same way as it's done with PWG raster.
+ print_job->content_type = kContentTypePdf;
+ print_job->document_bytes = print_data;
+ DispatchPrintJob(callback, std::move(print_job));
+ return;
+ }
+
+ cloud_devices::CloudDeviceDescription ticket;
+ if (!ticket.InitFromString(ticket_json)) {
+ WrapPrintCallback(callback, false, kInvalidTicketPrintError);
+ return;
+ }
+
+ print_job->content_type = kContentTypePWGRaster;
+ ConvertToPWGRaster(print_data, printer_description, ticket, page_size,
+ std::move(print_job),
+ base::Bind(&ExtensionPrinterHandler::DispatchPrintJob,
+ weak_ptr_factory_.GetWeakPtr(), callback));
+}
+
+void ExtensionPrinterHandler::StartGrantPrinterAccess(
+ const std::string& printer_id,
+ const PrinterHandler::GetPrinterInfoCallback& callback) {
+ std::string extension_id;
+ std::string device_guid;
+ if (!ParseProvisionalUsbPrinterId(printer_id, &extension_id, &device_guid)) {
+ callback.Run(base::DictionaryValue());
+ return;
+ }
+
+ device::UsbService* service = device::DeviceClient::Get()->GetUsbService();
+ scoped_refptr<UsbDevice> device = service->GetDevice(device_guid);
+ if (!device) {
+ callback.Run(base::DictionaryValue());
+ return;
+ }
+
+ DevicePermissionsManager* permissions_manager =
+ DevicePermissionsManager::Get(browser_context_);
+ permissions_manager->AllowUsbDevice(extension_id, device);
+
+ extensions::PrinterProviderAPIFactory::GetInstance()
+ ->GetForBrowserContext(browser_context_)
+ ->DispatchGetUsbPrinterInfoRequested(
+ extension_id, device,
+ base::Bind(&ExtensionPrinterHandler::WrapGetPrinterInfoCallback,
+ weak_ptr_factory_.GetWeakPtr(), callback));
+}
+
+void ExtensionPrinterHandler::SetPWGRasterConverterForTesting(
+ std::unique_ptr<PWGRasterConverter> pwg_raster_converter) {
+ pwg_raster_converter_ = std::move(pwg_raster_converter);
+}
+
+void ExtensionPrinterHandler::ConvertToPWGRaster(
+ const scoped_refptr<base::RefCountedMemory>& data,
+ const cloud_devices::CloudDeviceDescription& printer_description,
+ const cloud_devices::CloudDeviceDescription& ticket,
+ const gfx::Size& page_size,
+ std::unique_ptr<extensions::PrinterProviderPrintJob> job,
+ const ExtensionPrinterHandler::PrintJobCallback& callback) {
+ if (!pwg_raster_converter_) {
+ pwg_raster_converter_ = PWGRasterConverter::CreateDefault();
+ }
+ pwg_raster_converter_->Start(
+ data.get(),
+ PWGRasterConverter::GetConversionSettings(printer_description, page_size),
+ PWGRasterConverter::GetBitmapSettings(printer_description, ticket),
+ base::Bind(&UpdateJobFileInfo, base::Passed(&job), callback));
+}
+
+void ExtensionPrinterHandler::DispatchPrintJob(
+ const PrinterHandler::PrintCallback& callback,
+ std::unique_ptr<extensions::PrinterProviderPrintJob> print_job) {
+ if (print_job->document_path.empty() && !print_job->document_bytes) {
+ WrapPrintCallback(callback, false, kInvalidDataPrintError);
+ return;
+ }
+
+ extensions::PrinterProviderAPIFactory::GetInstance()
+ ->GetForBrowserContext(browser_context_)
+ ->DispatchPrintRequested(
+ *print_job, base::Bind(&ExtensionPrinterHandler::WrapPrintCallback,
+ weak_ptr_factory_.GetWeakPtr(), callback));
+}
+
+void ExtensionPrinterHandler::WrapGetPrintersCallback(
+ const PrinterHandler::GetPrintersCallback& callback,
+ const base::ListValue& printers,
+ bool done) {
+ DCHECK_GT(pending_enumeration_count_, 0);
+ if (done)
+ pending_enumeration_count_--;
+
+ callback.Run(printers, pending_enumeration_count_ == 0);
+}
+
+void ExtensionPrinterHandler::WrapGetCapabilityCallback(
+ const PrinterHandler::GetCapabilityCallback& callback,
+ const std::string& destination_id,
+ const base::DictionaryValue& capability) {
+ callback.Run(destination_id, capability);
+}
+
+void ExtensionPrinterHandler::WrapPrintCallback(
+ const PrinterHandler::PrintCallback& callback,
+ bool success,
+ const std::string& status) {
+ callback.Run(success, status);
+}
+
+void ExtensionPrinterHandler::WrapGetPrinterInfoCallback(
+ const PrinterHandler::GetPrinterInfoCallback& callback,
+ const base::DictionaryValue& printer_info) {
+ callback.Run(printer_info);
+}
+
+void ExtensionPrinterHandler::OnUsbDevicesEnumerated(
+ const PrinterHandler::GetPrintersCallback& callback,
+ const std::vector<scoped_refptr<UsbDevice>>& devices) {
+ ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
+ DevicePermissionsManager* permissions_manager =
+ DevicePermissionsManager::Get(browser_context_);
+
+ ListBuilder printer_list;
+
+ for (const auto& extension : registry->enabled_extensions()) {
+ const UsbPrinterManifestData* manifest_data =
+ UsbPrinterManifestData::Get(extension.get());
+ if (!manifest_data || !HasUsbPrinterProviderPermissions(extension.get()))
+ continue;
+
+ const extensions::DevicePermissions* device_permissions =
+ permissions_manager->GetForExtension(extension->id());
+ for (const auto& device : devices) {
+ if (manifest_data->SupportsDevice(device)) {
+ std::unique_ptr<extensions::UsbDevicePermission::CheckParam> param =
+ extensions::UsbDevicePermission::CheckParam::ForUsbDevice(
+ extension.get(), device.get());
+ if (device_permissions->FindUsbDeviceEntry(device) ||
+ extension->permissions_data()->CheckAPIPermissionWithParam(
+ extensions::APIPermission::kUsbDevice, param.get())) {
+ // Skip devices the extension already has permission to access.
+ continue;
+ }
+
+ printer_list.Append(
+ DictionaryBuilder()
+ .Set("id", GenerateProvisionalUsbPrinterId(extension.get(),
+ device.get()))
+ .Set("name",
+ DevicePermissionsManager::GetPermissionMessage(
+ device->vendor_id(), device->product_id(),
+ device->manufacturer_string(),
+ device->product_string(), base::string16(), false))
+ .Set("extensionId", extension->id())
+ .Set("extensionName", extension->name())
+ .Set("provisional", true)
+ .Build());
+ }
+ }
+ }
+
+ DCHECK_GT(pending_enumeration_count_, 0);
+ pending_enumeration_count_--;
+ callback.Run(*printer_list.Build().get(), pending_enumeration_count_ == 0);
+}
diff --git a/chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler.h b/chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler.h
new file mode 100644
index 00000000000..183d172cae4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler.h
@@ -0,0 +1,127 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_EXTENSION_PRINTER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_EXTENSION_PRINTER_HANDLER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+#include "chrome/browser/ui/webui/print_preview/printer_handler.h"
+#include "extensions/browser/api/printer_provider/printer_provider_api.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+class RefCountedMemory;
+}
+
+namespace content {
+class BrowserContext;
+}
+
+namespace cloud_devices {
+class CloudDeviceDescription;
+}
+
+namespace device {
+class UsbDevice;
+}
+
+namespace gfx {
+class Size;
+}
+
+namespace printing {
+class PWGRasterConverter;
+}
+
+// Implementation of PrinterHandler interface backed by printerProvider
+// extension API.
+class ExtensionPrinterHandler : public PrinterHandler {
+ public:
+ using PrintJobCallback = base::Callback<void(
+ std::unique_ptr<extensions::PrinterProviderPrintJob>)>;
+
+ explicit ExtensionPrinterHandler(content::BrowserContext* browser_context);
+
+ ~ExtensionPrinterHandler() override;
+
+ // PrinterHandler implementation:
+ void Reset() override;
+ void StartGetPrinters(
+ const PrinterHandler::GetPrintersCallback& callback) override;
+ void StartGetCapability(
+ const std::string& destination_id,
+ const PrinterHandler::GetCapabilityCallback& calback) override;
+ // TODO(tbarzic): It might make sense to have the strings in a single struct.
+ void StartPrint(const std::string& destination_id,
+ const std::string& capability,
+ const base::string16& job_title,
+ const std::string& ticket_json,
+ const gfx::Size& page_size,
+ const scoped_refptr<base::RefCountedMemory>& print_data,
+ const PrinterHandler::PrintCallback& callback) override;
+ void StartGrantPrinterAccess(
+ const std::string& printer_id,
+ const PrinterHandler::GetPrinterInfoCallback& callback) override;
+
+ private:
+ friend class ExtensionPrinterHandlerTest;
+
+ void SetPWGRasterConverterForTesting(
+ std::unique_ptr<printing::PWGRasterConverter> pwg_raster_converter);
+
+ // Converts |data| to PWG raster format (from PDF) for a printer described
+ // by |printer_description|.
+ // |callback| is called with the converted data.
+ void ConvertToPWGRaster(
+ const scoped_refptr<base::RefCountedMemory>& data,
+ const cloud_devices::CloudDeviceDescription& printer_description,
+ const cloud_devices::CloudDeviceDescription& ticket,
+ const gfx::Size& page_size,
+ std::unique_ptr<extensions::PrinterProviderPrintJob> job,
+ const PrintJobCallback& callback);
+
+ // Sets print job document data and dispatches it using printerProvider API.
+ void DispatchPrintJob(
+ const PrinterHandler::PrintCallback& callback,
+ std::unique_ptr<extensions::PrinterProviderPrintJob> print_job);
+
+ // Methods used as wrappers to callbacks for extensions::PrinterProviderAPI
+ // methods, primarily so the callbacks can be bound to this class' weak ptr.
+ // They just propagate results to callbacks passed to them.
+ void WrapGetPrintersCallback(
+ const PrinterHandler::GetPrintersCallback& callback,
+ const base::ListValue& printers,
+ bool done);
+ void WrapGetCapabilityCallback(
+ const PrinterHandler::GetCapabilityCallback& callback,
+ const std::string& destination_id,
+ const base::DictionaryValue& capability);
+ void WrapPrintCallback(const PrinterHandler::PrintCallback& callback,
+ bool success,
+ const std::string& status);
+ void WrapGetPrinterInfoCallback(const GetPrinterInfoCallback& callback,
+ const base::DictionaryValue& printer_info);
+
+ void OnUsbDevicesEnumerated(
+ const PrinterHandler::GetPrintersCallback& callback,
+ const std::vector<scoped_refptr<device::UsbDevice>>& devices);
+
+ content::BrowserContext* browser_context_;
+
+ std::unique_ptr<printing::PWGRasterConverter> pwg_raster_converter_;
+ int pending_enumeration_count_ = 0;
+
+ base::WeakPtrFactory<ExtensionPrinterHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionPrinterHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_EXTENSION_PRINTER_HANDLER_H_
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
new file mode 100644
index 00000000000..668f8d6d60e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
@@ -0,0 +1,1018 @@
+// 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.
+
+#include "chrome/browser/ui/webui/print_preview/extension_printer_handler.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <queue>
+#include <string>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/strings/string16.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/values_test_util.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/test_extension_environment.h"
+#include "chrome/browser/printing/pwg_raster_converter.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/version_info/version_info.h"
+#include "content/public/test/test_utils.h"
+#include "device/base/mock_device_client.h"
+#include "device/usb/mock_usb_device.h"
+#include "device/usb/mock_usb_service.h"
+#include "extensions/browser/api/device_permissions_manager.h"
+#include "extensions/browser/api/printer_provider/printer_provider_api.h"
+#include "extensions/browser/api/printer_provider/printer_provider_api_factory.h"
+#include "extensions/browser/api/printer_provider/printer_provider_print_job.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/value_builder.h"
+#include "printing/pdf_render_settings.h"
+#include "printing/pwg_raster_settings.h"
+#include "printing/units.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/size.h"
+
+using device::MockUsbDevice;
+using device::MockUsbService;
+using extensions::DictionaryBuilder;
+using extensions::Extension;
+using extensions::PrinterProviderAPI;
+using extensions::PrinterProviderPrintJob;
+using extensions::TestExtensionEnvironment;
+using printing::PWGRasterConverter;
+
+namespace {
+
+// Printer id used for requests in tests.
+const char kPrinterId[] = "printer_id";
+
+// Printer list used a result for getPrinters.
+const char kPrinterDescriptionList[] =
+ "[{"
+ " \"id\": \"printer1\","
+ " \"name\": \"Printer 1\""
+ "}, {"
+ " \"id\": \"printer2\","
+ " \"name\": \"Printer 2\","
+ " \"description\": \"Test printer 2\""
+ "}]";
+
+// Printer capability for printer that supports all content types.
+const char kAllContentTypesSupportedPrinter[] =
+ "{"
+ " \"version\": \"1.0\","
+ " \"printer\": {"
+ " \"supported_content_type\": ["
+ " {\"content_type\": \"*/*\"}"
+ " ]"
+ " }"
+ "}";
+
+// Printer capability for a printer that supports PDF.
+const char kPdfSupportedPrinter[] =
+ "{"
+ " \"version\": \"1.0\","
+ " \"printer\": {"
+ " \"supported_content_type\": ["
+ " {\"content_type\": \"application/pdf\"},"
+ " {\"content_type\": \"image/pwg-raster\"}"
+ " ]"
+ " }"
+ "}";
+
+// Printer capability for a printer that supportd only PWG raster.
+const char kPWGRasterOnlyPrinterSimpleDescription[] =
+ "{"
+ " \"version\": \"1.0\","
+ " \"printer\": {"
+ " \"supported_content_type\": ["
+ " {\"content_type\": \"image/pwg-raster\"}"
+ " ]"
+ " }"
+ "}";
+
+// Printer capability for a printer that supportd only PWG raster that has
+// options other that supported_content_type set.
+const char kPWGRasterOnlyPrinter[] =
+ "{"
+ " \"version\": \"1.0\","
+ " \"printer\": {"
+ " \"supported_content_type\": ["
+ " {\"content_type\": \"image/pwg-raster\"}"
+ " ],"
+ " \"pwg_raster_config\": {"
+ " \"document_sheet_back\": \"FLIPPED\","
+ " \"reverse_order_streaming\": true,"
+ " \"rotate_all_pages\": true"
+ " },"
+ " \"dpi\": {"
+ " \"option\": [{"
+ " \"horizontal_dpi\": 100,"
+ " \"vertical_dpi\": 200,"
+ " \"is_default\": true"
+ " }]"
+ " }"
+ " }"
+ "}";
+
+// Print ticket with no parameters set.
+const char kEmptyPrintTicket[] = "{\"version\": \"1.0\"}";
+
+// Print ticket that has duplex parameter set.
+const char kPrintTicketWithDuplex[] =
+ "{"
+ " \"version\": \"1.0\","
+ " \"print\": {"
+ " \"duplex\": {\"type\": \"LONG_EDGE\"}"
+ " }"
+ "}";
+
+// An extension with permission for 1 printer it supports.
+const char kExtension1[] =
+ "{"
+ " \"name\": \"Provider 1\","
+ " \"app\": {"
+ " \"background\": {"
+ " \"scripts\": [\"background.js\"]"
+ " }"
+ " },"
+ " \"permissions\": ["
+ " \"printerProvider\","
+ " \"usb\","
+ " {"
+ " \"usbDevices\": ["
+ " { \"vendorId\": 0, \"productId\": 1 }"
+ " ]"
+ " },"
+ " ],"
+ " \"usb_printers\": {"
+ " \"filters\": ["
+ " { \"vendorId\": 0, \"productId\": 0 },"
+ " { \"vendorId\": 0, \"productId\": 1 }"
+ " ]"
+ " }"
+ "}";
+
+// An extension with permission for none of the printers it supports.
+const char kExtension2[] =
+ "{"
+ " \"name\": \"Provider 2\","
+ " \"app\": {"
+ " \"background\": {"
+ " \"scripts\": [\"background.js\"]"
+ " }"
+ " },"
+ " \"permissions\": [ \"printerProvider\", \"usb\" ],"
+ " \"usb_printers\": {"
+ " \"filters\": ["
+ " { \"vendorId\": 0, \"productId\": 0 },"
+ " { \"vendorId\": 0, \"productId\": 1 }"
+ " ]"
+ " }"
+ "}";
+
+const char kContentTypePDF[] = "application/pdf";
+const char kContentTypePWG[] = "image/pwg-raster";
+
+// Print request status considered to be successful by fake PrinterProviderAPI.
+const char kPrintRequestSuccess[] = "OK";
+
+// Used as a callback to StartGetPrinters in tests.
+// Increases |*call_count| and records values returned by StartGetPrinters.
+void RecordPrinterList(size_t* call_count,
+ std::unique_ptr<base::ListValue>* printers_out,
+ bool* is_done_out,
+ const base::ListValue& printers,
+ bool is_done) {
+ ++(*call_count);
+ printers_out->reset(printers.DeepCopy());
+ *is_done_out = is_done;
+}
+
+// Used as a callback to StartGetCapability in tests.
+// Increases |*call_count| and records values returned by StartGetCapability.
+void RecordCapability(size_t* call_count,
+ std::string* destination_id_out,
+ std::unique_ptr<base::DictionaryValue>* capability_out,
+ const std::string& destination_id,
+ const base::DictionaryValue& capability) {
+ ++(*call_count);
+ *destination_id_out = destination_id;
+ capability_out->reset(capability.DeepCopy());
+}
+
+// Used as a callback to StartPrint in tests.
+// Increases |*call_count| and records values returned by StartPrint.
+void RecordPrintResult(size_t* call_count,
+ bool* success_out,
+ std::string* status_out,
+ bool success,
+ const std::string& status) {
+ ++(*call_count);
+ *success_out = success;
+ *status_out = status;
+}
+
+// Used as a callback to StartGrantPrinterAccess in tests.
+// Increases |*call_count| and records the value returned.
+void RecordPrinterInfo(size_t* call_count,
+ std::unique_ptr<base::DictionaryValue>* printer_info_out,
+ const base::DictionaryValue& printer_info) {
+ ++(*call_count);
+ printer_info_out->reset(printer_info.DeepCopy());
+}
+
+// Converts JSON string to base::ListValue object.
+// On failure, returns NULL and fills |*error| string.
+std::unique_ptr<base::ListValue> GetJSONAsListValue(const std::string& json,
+ std::string* error) {
+ std::unique_ptr<base::Value> deserialized(
+ JSONStringValueDeserializer(json).Deserialize(NULL, error));
+ if (!deserialized)
+ return std::unique_ptr<base::ListValue>();
+ base::ListValue* list = nullptr;
+ if (!deserialized->GetAsList(&list)) {
+ *error = "Value is not a list.";
+ return std::unique_ptr<base::ListValue>();
+ }
+ return std::unique_ptr<base::ListValue>(list->DeepCopy());
+}
+
+// Converts JSON string to base::DictionaryValue object.
+// On failure, returns NULL and fills |*error| string.
+std::unique_ptr<base::DictionaryValue> GetJSONAsDictionaryValue(
+ const std::string& json,
+ std::string* error) {
+ std::unique_ptr<base::Value> deserialized(
+ JSONStringValueDeserializer(json).Deserialize(NULL, error));
+ if (!deserialized)
+ return std::unique_ptr<base::DictionaryValue>();
+ base::DictionaryValue* dictionary;
+ if (!deserialized->GetAsDictionary(&dictionary)) {
+ *error = "Value is not a dictionary.";
+ return std::unique_ptr<base::DictionaryValue>();
+ }
+ return std::unique_ptr<base::DictionaryValue>(dictionary->DeepCopy());
+}
+
+std::string RefCountedMemoryToString(
+ const scoped_refptr<base::RefCountedMemory>& memory) {
+ return std::string(memory->front_as<char>(), memory->size());
+}
+
+// Fake PWGRasterConverter used in the tests.
+class FakePWGRasterConverter : public PWGRasterConverter {
+ public:
+ FakePWGRasterConverter() : fail_conversion_(false), initialized_(false) {}
+ ~FakePWGRasterConverter() override = default;
+
+ // PWGRasterConverter implementation. It writes |data| to a temp file.
+ // Also, remembers conversion and bitmap settings passed into the method.
+ void Start(base::RefCountedMemory* data,
+ const printing::PdfRenderSettings& conversion_settings,
+ const printing::PwgRasterSettings& bitmap_settings,
+ const ResultCallback& callback) override {
+ if (fail_conversion_) {
+ callback.Run(false, base::FilePath());
+ return;
+ }
+
+ if (!initialized_ && !temp_dir_.CreateUniqueTempDir()) {
+ ADD_FAILURE() << "Unable to create target dir for cenverter";
+ callback.Run(false, base::FilePath());
+ return;
+ }
+
+ initialized_ = true;
+
+ path_ = temp_dir_.GetPath().AppendASCII("output.pwg");
+ std::string data_str(data->front_as<char>(), data->size());
+ int written = WriteFile(path_, data_str.c_str(), data_str.size());
+ if (written != static_cast<int>(data_str.size())) {
+ ADD_FAILURE() << "Failed to write pwg raster file.";
+ callback.Run(false, base::FilePath());
+ return;
+ }
+
+ conversion_settings_ = conversion_settings;
+ bitmap_settings_ = bitmap_settings;
+
+ callback.Run(true, path_);
+ }
+
+ // Makes |Start| method always return an error.
+ void FailConversion() { fail_conversion_ = true; }
+
+ const base::FilePath& path() { return path_; }
+ const printing::PdfRenderSettings& conversion_settings() const {
+ return conversion_settings_;
+ }
+
+ const printing::PwgRasterSettings& bitmap_settings() const {
+ return bitmap_settings_;
+ }
+
+ private:
+ base::ScopedTempDir temp_dir_;
+
+ base::FilePath path_;
+ printing::PdfRenderSettings conversion_settings_;
+ printing::PwgRasterSettings bitmap_settings_;
+ bool fail_conversion_;
+ bool initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakePWGRasterConverter);
+};
+
+// Information about received print requests.
+struct PrintRequestInfo {
+ PrinterProviderAPI::PrintCallback callback;
+ PrinterProviderPrintJob job;
+};
+
+// Fake PrinterProviderAPI used in tests.
+// It caches requests issued to API and exposes methods to trigger their
+// callbacks.
+class FakePrinterProviderAPI : public PrinterProviderAPI {
+ public:
+ FakePrinterProviderAPI() = default;
+ ~FakePrinterProviderAPI() override = default;
+
+ void DispatchGetPrintersRequested(
+ const PrinterProviderAPI::GetPrintersCallback& callback) override {
+ pending_printers_callbacks_.push(callback);
+ }
+
+ void DispatchGetCapabilityRequested(
+ const std::string& destination_id,
+ const PrinterProviderAPI::GetCapabilityCallback& callback) override {
+ pending_capability_callbacks_.push(callback);
+ }
+
+ void DispatchPrintRequested(
+ const PrinterProviderPrintJob& job,
+ const PrinterProviderAPI::PrintCallback& callback) override {
+ PrintRequestInfo request_info;
+ request_info.callback = callback;
+ request_info.job = job;
+
+ pending_print_requests_.push(request_info);
+ }
+
+ void DispatchGetUsbPrinterInfoRequested(
+ const std::string& extension_id,
+ scoped_refptr<device::UsbDevice> device,
+ const PrinterProviderAPI::GetPrinterInfoCallback& callback) override {
+ EXPECT_EQ("fake extension id", extension_id);
+ EXPECT_TRUE(device);
+ pending_usb_info_callbacks_.push(callback);
+ }
+
+ size_t pending_get_printers_count() const {
+ return pending_printers_callbacks_.size();
+ }
+
+ const PrinterProviderPrintJob* GetPrintJob(
+ const extensions::Extension* extension,
+ int request_id) const override {
+ ADD_FAILURE() << "Not reached";
+ return nullptr;
+ }
+
+ void TriggerNextGetPrintersCallback(const base::ListValue& printers,
+ bool done) {
+ ASSERT_GT(pending_get_printers_count(), 0u);
+ pending_printers_callbacks_.front().Run(printers, done);
+ pending_printers_callbacks_.pop();
+ }
+
+ size_t pending_get_capability_count() const {
+ return pending_capability_callbacks_.size();
+ }
+
+ void TriggerNextGetCapabilityCallback(
+ const base::DictionaryValue& description) {
+ ASSERT_GT(pending_get_capability_count(), 0u);
+ pending_capability_callbacks_.front().Run(description);
+ pending_capability_callbacks_.pop();
+ }
+
+ size_t pending_print_count() const { return pending_print_requests_.size(); }
+
+ const PrinterProviderPrintJob* GetNextPendingPrintJob() const {
+ EXPECT_GT(pending_print_count(), 0u);
+ if (pending_print_count() == 0)
+ return NULL;
+ return &pending_print_requests_.front().job;
+ }
+
+ void TriggerNextPrintCallback(const std::string& result) {
+ ASSERT_GT(pending_print_count(), 0u);
+ pending_print_requests_.front().callback.Run(result == kPrintRequestSuccess,
+ result);
+ pending_print_requests_.pop();
+ }
+
+ size_t pending_usb_info_count() const {
+ return pending_usb_info_callbacks_.size();
+ }
+
+ void TriggerNextUsbPrinterInfoCallback(
+ const base::DictionaryValue& printer_info) {
+ ASSERT_GT(pending_usb_info_count(), 0u);
+ pending_usb_info_callbacks_.front().Run(printer_info);
+ pending_usb_info_callbacks_.pop();
+ }
+
+ private:
+ std::queue<PrinterProviderAPI::GetPrintersCallback>
+ pending_printers_callbacks_;
+ std::queue<PrinterProviderAPI::GetCapabilityCallback>
+ pending_capability_callbacks_;
+ std::queue<PrintRequestInfo> pending_print_requests_;
+ std::queue<PrinterProviderAPI::GetPrinterInfoCallback>
+ pending_usb_info_callbacks_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakePrinterProviderAPI);
+};
+
+std::unique_ptr<KeyedService> BuildTestingPrinterProviderAPI(
+ content::BrowserContext* context) {
+ return base::MakeUnique<FakePrinterProviderAPI>();
+}
+
+} // namespace
+
+class ExtensionPrinterHandlerTest : public testing::Test {
+ public:
+ ExtensionPrinterHandlerTest() : pwg_raster_converter_(NULL) {}
+ ~ExtensionPrinterHandlerTest() override = default;
+
+ void SetUp() override {
+ extensions::PrinterProviderAPIFactory::GetInstance()->SetTestingFactory(
+ env_.profile(), &BuildTestingPrinterProviderAPI);
+ extension_printer_handler_.reset(
+ new ExtensionPrinterHandler(env_.profile()));
+
+ pwg_raster_converter_ = new FakePWGRasterConverter();
+ extension_printer_handler_->SetPWGRasterConverterForTesting(
+ std::unique_ptr<PWGRasterConverter>(pwg_raster_converter_));
+ }
+
+ protected:
+ FakePrinterProviderAPI* GetPrinterProviderAPI() {
+ return static_cast<FakePrinterProviderAPI*>(
+ extensions::PrinterProviderAPIFactory::GetInstance()
+ ->GetForBrowserContext(env_.profile()));
+ }
+
+ device::MockUsbService& usb_service() {
+ return *device_client_.usb_service();
+ }
+
+ device::MockDeviceClient device_client_;
+ TestExtensionEnvironment env_;
+ std::unique_ptr<ExtensionPrinterHandler> extension_printer_handler_;
+
+ FakePWGRasterConverter* pwg_raster_converter_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ExtensionPrinterHandlerTest);
+};
+
+TEST_F(ExtensionPrinterHandlerTest, GetPrinters) {
+ size_t call_count = 0;
+ std::unique_ptr<base::ListValue> printers;
+ bool is_done = false;
+
+ extension_printer_handler_->StartGetPrinters(
+ base::Bind(&RecordPrinterList, &call_count, &printers, &is_done));
+
+ EXPECT_FALSE(printers.get());
+ FakePrinterProviderAPI* fake_api = GetPrinterProviderAPI();
+ ASSERT_TRUE(fake_api);
+ ASSERT_EQ(1u, fake_api->pending_get_printers_count());
+
+ std::string error;
+ std::unique_ptr<base::ListValue> original_printers(
+ GetJSONAsListValue(kPrinterDescriptionList, &error));
+ ASSERT_TRUE(original_printers) << "Failed to deserialize printers: " << error;
+
+ fake_api->TriggerNextGetPrintersCallback(*original_printers, true);
+
+ EXPECT_EQ(1u, call_count);
+ EXPECT_TRUE(is_done);
+ ASSERT_TRUE(printers.get());
+ EXPECT_TRUE(printers->Equals(original_printers.get()))
+ << *printers << ", expected: " << *original_printers;
+}
+
+TEST_F(ExtensionPrinterHandlerTest, GetPrinters_Reset) {
+ size_t call_count = 0;
+ std::unique_ptr<base::ListValue> printers;
+ bool is_done = false;
+
+ extension_printer_handler_->StartGetPrinters(
+ base::Bind(&RecordPrinterList, &call_count, &printers, &is_done));
+
+ EXPECT_FALSE(printers.get());
+ FakePrinterProviderAPI* fake_api = GetPrinterProviderAPI();
+ ASSERT_TRUE(fake_api);
+ ASSERT_EQ(1u, fake_api->pending_get_printers_count());
+
+ extension_printer_handler_->Reset();
+
+ std::string error;
+ std::unique_ptr<base::ListValue> original_printers(
+ GetJSONAsListValue(kPrinterDescriptionList, &error));
+ ASSERT_TRUE(original_printers) << "Error deserializing printers: " << error;
+
+ fake_api->TriggerNextGetPrintersCallback(*original_printers, true);
+
+ EXPECT_EQ(0u, call_count);
+}
+
+TEST_F(ExtensionPrinterHandlerTest, GetUsbPrinters) {
+ scoped_refptr<MockUsbDevice> device0 =
+ new MockUsbDevice(0, 0, "Google", "USB Printer", "");
+ usb_service().AddDevice(device0);
+ scoped_refptr<MockUsbDevice> device1 =
+ new MockUsbDevice(0, 1, "Google", "USB Printer", "");
+ usb_service().AddDevice(device1);
+
+ const Extension* extension_1 = env_.MakeExtension(
+ *base::test::ParseJson(kExtension1), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
+ const Extension* extension_2 = env_.MakeExtension(
+ *base::test::ParseJson(kExtension2), "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
+
+ extensions::DevicePermissionsManager* permissions_manager =
+ extensions::DevicePermissionsManager::Get(env_.profile());
+ permissions_manager->AllowUsbDevice(extension_2->id(), device0);
+
+ size_t call_count = 0;
+ std::unique_ptr<base::ListValue> printers;
+ bool is_done = false;
+ extension_printer_handler_->StartGetPrinters(
+ base::Bind(&RecordPrinterList, &call_count, &printers, &is_done));
+
+ base::RunLoop().RunUntilIdle();
+
+ FakePrinterProviderAPI* fake_api = GetPrinterProviderAPI();
+ ASSERT_TRUE(fake_api);
+ ASSERT_EQ(1u, fake_api->pending_get_printers_count());
+
+ EXPECT_EQ(1u, call_count);
+ EXPECT_FALSE(is_done);
+ EXPECT_TRUE(printers.get());
+ EXPECT_EQ(2u, printers->GetSize());
+ std::unique_ptr<base::DictionaryValue> extension_1_entry(
+ DictionaryBuilder()
+ .Set("id", base::StringPrintf("provisional-usb:%s:%s",
+ extension_1->id().c_str(),
+ device0->guid().c_str()))
+ .Set("name", "USB Printer")
+ .Set("extensionName", "Provider 1")
+ .Set("extensionId", extension_1->id())
+ .Set("provisional", true)
+ .Build());
+ std::unique_ptr<base::DictionaryValue> extension_2_entry(
+ DictionaryBuilder()
+ .Set("id", base::StringPrintf("provisional-usb:%s:%s",
+ extension_2->id().c_str(),
+ device1->guid().c_str()))
+ .Set("name", "USB Printer")
+ .Set("extensionName", "Provider 2")
+ .Set("extensionId", extension_2->id())
+ .Set("provisional", true)
+ .Build());
+ EXPECT_TRUE(printers->Find(*extension_1_entry) != printers->end());
+ EXPECT_TRUE(printers->Find(*extension_2_entry) != printers->end());
+
+ fake_api->TriggerNextGetPrintersCallback(base::ListValue(), true);
+
+ EXPECT_EQ(2u, call_count);
+ EXPECT_TRUE(is_done);
+ EXPECT_TRUE(printers.get());
+ EXPECT_EQ(0u, printers->GetSize()); // RecordPrinterList resets |printers|.
+}
+
+TEST_F(ExtensionPrinterHandlerTest, GetCapability) {
+ size_t call_count = 0;
+ std::string destination_id;
+ std::unique_ptr<base::DictionaryValue> capability;
+
+ extension_printer_handler_->StartGetCapability(
+ kPrinterId,
+ base::Bind(&RecordCapability, &call_count, &destination_id, &capability));
+
+ EXPECT_EQ(0u, call_count);
+
+ FakePrinterProviderAPI* fake_api = GetPrinterProviderAPI();
+ ASSERT_TRUE(fake_api);
+ ASSERT_EQ(1u, fake_api->pending_get_capability_count());
+
+ std::string error;
+ std::unique_ptr<base::DictionaryValue> original_capability(
+ GetJSONAsDictionaryValue(kPWGRasterOnlyPrinterSimpleDescription, &error));
+ ASSERT_TRUE(original_capability)
+ << "Error deserializing capability: " << error;
+
+ fake_api->TriggerNextGetCapabilityCallback(*original_capability);
+
+ EXPECT_EQ(1u, call_count);
+ EXPECT_EQ(kPrinterId, destination_id);
+ ASSERT_TRUE(capability.get());
+ EXPECT_TRUE(capability->Equals(original_capability.get()))
+ << *capability << ", expected: " << *original_capability;
+}
+
+TEST_F(ExtensionPrinterHandlerTest, GetCapability_Reset) {
+ size_t call_count = 0;
+ std::string destination_id;
+ std::unique_ptr<base::DictionaryValue> capability;
+
+ extension_printer_handler_->StartGetCapability(
+ kPrinterId,
+ base::Bind(&RecordCapability, &call_count, &destination_id, &capability));
+
+ EXPECT_EQ(0u, call_count);
+
+ FakePrinterProviderAPI* fake_api = GetPrinterProviderAPI();
+ ASSERT_TRUE(fake_api);
+ ASSERT_EQ(1u, fake_api->pending_get_capability_count());
+
+ extension_printer_handler_->Reset();
+
+ std::string error;
+ std::unique_ptr<base::DictionaryValue> original_capability(
+ GetJSONAsDictionaryValue(kPWGRasterOnlyPrinterSimpleDescription, &error));
+ ASSERT_TRUE(original_capability)
+ << "Error deserializing capability: " << error;
+
+ fake_api->TriggerNextGetCapabilityCallback(*original_capability);
+
+ EXPECT_EQ(0u, call_count);
+}
+
+TEST_F(ExtensionPrinterHandlerTest, Print_Pdf) {
+ size_t call_count = 0;
+ bool success = false;
+ std::string status;
+
+ scoped_refptr<base::RefCountedString> print_data(
+ new base::RefCountedString());
+ print_data->data() = "print data, PDF";
+ base::string16 title = base::ASCIIToUTF16("Title");
+
+ extension_printer_handler_->StartPrint(
+ kPrinterId, kPdfSupportedPrinter, title, kEmptyPrintTicket,
+ gfx::Size(100, 100), print_data,
+ base::Bind(&RecordPrintResult, &call_count, &success, &status));
+
+ EXPECT_EQ(0u, call_count);
+ FakePrinterProviderAPI* fake_api = GetPrinterProviderAPI();
+ ASSERT_TRUE(fake_api);
+ ASSERT_EQ(1u, fake_api->pending_print_count());
+
+ const PrinterProviderPrintJob* print_job = fake_api->GetNextPendingPrintJob();
+ ASSERT_TRUE(print_job);
+
+ EXPECT_EQ(kPrinterId, print_job->printer_id);
+ EXPECT_EQ(title, print_job->job_title);
+ EXPECT_EQ(kEmptyPrintTicket, print_job->ticket_json);
+ EXPECT_EQ(kContentTypePDF, print_job->content_type);
+ EXPECT_TRUE(print_job->document_path.empty());
+ ASSERT_TRUE(print_job->document_bytes);
+ EXPECT_EQ(print_data->data(),
+ RefCountedMemoryToString(print_job->document_bytes));
+
+ fake_api->TriggerNextPrintCallback(kPrintRequestSuccess);
+
+ EXPECT_EQ(1u, call_count);
+ EXPECT_TRUE(success);
+ EXPECT_EQ(kPrintRequestSuccess, status);
+}
+
+TEST_F(ExtensionPrinterHandlerTest, Print_Pdf_Reset) {
+ size_t call_count = 0;
+ bool success = false;
+ std::string status;
+
+ scoped_refptr<base::RefCountedString> print_data(
+ new base::RefCountedString());
+ print_data->data() = "print data, PDF";
+ base::string16 title = base::ASCIIToUTF16("Title");
+
+ extension_printer_handler_->StartPrint(
+ kPrinterId, kPdfSupportedPrinter, title, kEmptyPrintTicket,
+ gfx::Size(100, 100), print_data,
+ base::Bind(&RecordPrintResult, &call_count, &success, &status));
+
+ EXPECT_EQ(0u, call_count);
+ FakePrinterProviderAPI* fake_api = GetPrinterProviderAPI();
+ ASSERT_TRUE(fake_api);
+ ASSERT_EQ(1u, fake_api->pending_print_count());
+
+ extension_printer_handler_->Reset();
+
+ fake_api->TriggerNextPrintCallback(kPrintRequestSuccess);
+
+ EXPECT_EQ(0u, call_count);
+}
+
+TEST_F(ExtensionPrinterHandlerTest, Print_All) {
+ size_t call_count = 0;
+ bool success = false;
+ std::string status;
+
+ scoped_refptr<base::RefCountedString> print_data(
+ new base::RefCountedString());
+ print_data->data() = "print data, PDF";
+ base::string16 title = base::ASCIIToUTF16("Title");
+
+ extension_printer_handler_->StartPrint(
+ kPrinterId, kAllContentTypesSupportedPrinter, title, kEmptyPrintTicket,
+ gfx::Size(100, 100), print_data,
+ base::Bind(&RecordPrintResult, &call_count, &success, &status));
+
+ EXPECT_EQ(0u, call_count);
+
+ FakePrinterProviderAPI* fake_api = GetPrinterProviderAPI();
+ ASSERT_TRUE(fake_api);
+ ASSERT_EQ(1u, fake_api->pending_print_count());
+
+ const PrinterProviderPrintJob* print_job = fake_api->GetNextPendingPrintJob();
+ ASSERT_TRUE(print_job);
+
+ EXPECT_EQ(kPrinterId, print_job->printer_id);
+ EXPECT_EQ(title, print_job->job_title);
+ EXPECT_EQ(kEmptyPrintTicket, print_job->ticket_json);
+ EXPECT_EQ(kContentTypePDF, print_job->content_type);
+ EXPECT_TRUE(print_job->document_path.empty());
+ ASSERT_TRUE(print_job->document_bytes);
+ EXPECT_EQ(print_data->data(),
+ RefCountedMemoryToString(print_job->document_bytes));
+
+ fake_api->TriggerNextPrintCallback(kPrintRequestSuccess);
+
+ EXPECT_EQ(1u, call_count);
+ EXPECT_TRUE(success);
+ EXPECT_EQ(kPrintRequestSuccess, status);
+}
+
+TEST_F(ExtensionPrinterHandlerTest, Print_Pwg) {
+ size_t call_count = 0;
+ bool success = false;
+ std::string status;
+
+ scoped_refptr<base::RefCountedString> print_data(
+ new base::RefCountedString());
+ print_data->data() = "print data, PDF";
+ base::string16 title = base::ASCIIToUTF16("Title");
+
+ extension_printer_handler_->StartPrint(
+ kPrinterId, kPWGRasterOnlyPrinterSimpleDescription, title,
+ kEmptyPrintTicket, gfx::Size(100, 50), print_data,
+ base::Bind(&RecordPrintResult, &call_count, &success, &status));
+
+ EXPECT_EQ(0u, call_count);
+
+ content::RunAllBlockingPoolTasksUntilIdle();
+
+ FakePrinterProviderAPI* fake_api = GetPrinterProviderAPI();
+ ASSERT_TRUE(fake_api);
+ ASSERT_EQ(1u, fake_api->pending_print_count());
+
+ EXPECT_EQ(printing::TRANSFORM_NORMAL,
+ pwg_raster_converter_->bitmap_settings().odd_page_transform);
+ EXPECT_FALSE(pwg_raster_converter_->bitmap_settings().rotate_all_pages);
+ EXPECT_FALSE(pwg_raster_converter_->bitmap_settings().reverse_page_order);
+
+ EXPECT_EQ(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
+ pwg_raster_converter_->conversion_settings().area.ToString());
+
+ const PrinterProviderPrintJob* print_job = fake_api->GetNextPendingPrintJob();
+ ASSERT_TRUE(print_job);
+
+ EXPECT_EQ(kPrinterId, print_job->printer_id);
+ EXPECT_EQ(title, print_job->job_title);
+ EXPECT_EQ(kEmptyPrintTicket, print_job->ticket_json);
+ EXPECT_EQ(kContentTypePWG, print_job->content_type);
+ EXPECT_FALSE(print_job->document_bytes);
+ EXPECT_FALSE(print_job->document_path.empty());
+ EXPECT_EQ(pwg_raster_converter_->path(), print_job->document_path);
+ EXPECT_EQ(static_cast<int64_t>(print_data->size()),
+ print_job->file_info.size);
+
+ fake_api->TriggerNextPrintCallback(kPrintRequestSuccess);
+
+ EXPECT_EQ(1u, call_count);
+ EXPECT_TRUE(success);
+ EXPECT_EQ(kPrintRequestSuccess, status);
+}
+
+TEST_F(ExtensionPrinterHandlerTest, Print_Pwg_NonDefaultSettings) {
+ size_t call_count = 0;
+ bool success = false;
+ std::string status;
+
+ scoped_refptr<base::RefCountedString> print_data(
+ new base::RefCountedString());
+ print_data->data() = "print data, PDF";
+ base::string16 title = base::ASCIIToUTF16("Title");
+
+ extension_printer_handler_->StartPrint(
+ kPrinterId, kPWGRasterOnlyPrinter, title, kPrintTicketWithDuplex,
+ gfx::Size(100, 50), print_data,
+ base::Bind(&RecordPrintResult, &call_count, &success, &status));
+
+ EXPECT_EQ(0u, call_count);
+
+ content::RunAllBlockingPoolTasksUntilIdle();
+
+ FakePrinterProviderAPI* fake_api = GetPrinterProviderAPI();
+ ASSERT_TRUE(fake_api);
+ ASSERT_EQ(1u, fake_api->pending_print_count());
+
+ EXPECT_EQ(printing::TRANSFORM_FLIP_VERTICAL,
+ pwg_raster_converter_->bitmap_settings().odd_page_transform);
+ EXPECT_TRUE(pwg_raster_converter_->bitmap_settings().rotate_all_pages);
+ EXPECT_TRUE(pwg_raster_converter_->bitmap_settings().reverse_page_order);
+
+ EXPECT_EQ(200, // max(vertical_dpi, horizontal_dpi)
+ 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
+ pwg_raster_converter_->conversion_settings().area.ToString());
+
+ const PrinterProviderPrintJob* print_job = fake_api->GetNextPendingPrintJob();
+ ASSERT_TRUE(print_job);
+
+ EXPECT_EQ(kPrinterId, print_job->printer_id);
+ EXPECT_EQ(title, print_job->job_title);
+ EXPECT_EQ(kPrintTicketWithDuplex, print_job->ticket_json);
+ EXPECT_EQ(kContentTypePWG, print_job->content_type);
+ EXPECT_FALSE(print_job->document_bytes);
+ EXPECT_FALSE(print_job->document_path.empty());
+ EXPECT_EQ(pwg_raster_converter_->path(), print_job->document_path);
+ EXPECT_EQ(static_cast<int64_t>(print_data->size()),
+ print_job->file_info.size);
+
+ fake_api->TriggerNextPrintCallback(kPrintRequestSuccess);
+
+ EXPECT_EQ(1u, call_count);
+ EXPECT_TRUE(success);
+ EXPECT_EQ(kPrintRequestSuccess, status);
+}
+
+TEST_F(ExtensionPrinterHandlerTest, Print_Pwg_Reset) {
+ size_t call_count = 0;
+ bool success = false;
+ std::string status;
+
+ scoped_refptr<base::RefCountedString> print_data(
+ new base::RefCountedString());
+ print_data->data() = "print data, PDF";
+ base::string16 title = base::ASCIIToUTF16("Title");
+
+ extension_printer_handler_->StartPrint(
+ kPrinterId, kPWGRasterOnlyPrinterSimpleDescription, title,
+ kEmptyPrintTicket, gfx::Size(100, 50), print_data,
+ base::Bind(&RecordPrintResult, &call_count, &success, &status));
+
+ EXPECT_EQ(0u, call_count);
+
+ content::RunAllBlockingPoolTasksUntilIdle();
+
+ FakePrinterProviderAPI* fake_api = GetPrinterProviderAPI();
+ ASSERT_TRUE(fake_api);
+ ASSERT_EQ(1u, fake_api->pending_print_count());
+
+ extension_printer_handler_->Reset();
+
+ fake_api->TriggerNextPrintCallback(kPrintRequestSuccess);
+
+ EXPECT_EQ(0u, call_count);
+}
+
+TEST_F(ExtensionPrinterHandlerTest, Print_Pwg_InvalidTicket) {
+ size_t call_count = 0;
+ bool success = false;
+ std::string status;
+
+ scoped_refptr<base::RefCountedString> print_data(
+ new base::RefCountedString());
+ print_data->data() = "print data, PDF";
+ base::string16 title = base::ASCIIToUTF16("Title");
+
+ extension_printer_handler_->StartPrint(
+ kPrinterId, kPWGRasterOnlyPrinterSimpleDescription, title,
+ "{}" /* ticket */, gfx::Size(100, 100), print_data,
+ base::Bind(&RecordPrintResult, &call_count, &success, &status));
+
+ EXPECT_EQ(1u, call_count);
+
+ EXPECT_FALSE(success);
+ EXPECT_EQ("INVALID_TICKET", status);
+}
+
+TEST_F(ExtensionPrinterHandlerTest, Print_Pwg_FailedConversion) {
+ size_t call_count = 0;
+ bool success = false;
+ std::string status;
+
+ pwg_raster_converter_->FailConversion();
+
+ scoped_refptr<base::RefCountedString> print_data(
+ new base::RefCountedString());
+ print_data->data() = "print data, PDF";
+ base::string16 title = base::ASCIIToUTF16("Title");
+
+ extension_printer_handler_->StartPrint(
+ kPrinterId, kPWGRasterOnlyPrinterSimpleDescription, title,
+ kEmptyPrintTicket, gfx::Size(100, 100), print_data,
+ base::Bind(&RecordPrintResult, &call_count, &success, &status));
+
+ EXPECT_EQ(1u, call_count);
+
+ EXPECT_FALSE(success);
+ EXPECT_EQ("INVALID_DATA", status);
+}
+
+TEST_F(ExtensionPrinterHandlerTest, GrantUsbPrinterAccess) {
+ scoped_refptr<MockUsbDevice> device =
+ new MockUsbDevice(0, 0, "Google", "USB Printer", "");
+ usb_service().AddDevice(device);
+
+ size_t call_count = 0;
+ std::unique_ptr<base::DictionaryValue> printer_info;
+
+ std::string printer_id = base::StringPrintf(
+ "provisional-usb:fake extension id:%s", device->guid().c_str());
+ extension_printer_handler_->StartGrantPrinterAccess(
+ printer_id, base::Bind(&RecordPrinterInfo, &call_count, &printer_info));
+
+ EXPECT_FALSE(printer_info.get());
+ FakePrinterProviderAPI* fake_api = GetPrinterProviderAPI();
+ ASSERT_TRUE(fake_api);
+ ASSERT_EQ(1u, fake_api->pending_usb_info_count());
+
+ std::unique_ptr<base::DictionaryValue> original_printer_info(
+ DictionaryBuilder()
+ .Set("id", "printer1")
+ .Set("name", "Printer 1")
+ .Build());
+
+ fake_api->TriggerNextUsbPrinterInfoCallback(*original_printer_info);
+
+ EXPECT_EQ(1u, call_count);
+ ASSERT_TRUE(printer_info.get());
+ EXPECT_TRUE(printer_info->Equals(original_printer_info.get()))
+ << *printer_info << ", expected: " << *original_printer_info;
+}
+
+TEST_F(ExtensionPrinterHandlerTest, GrantUsbPrinterAccess_Reset) {
+ scoped_refptr<MockUsbDevice> device =
+ new MockUsbDevice(0, 0, "Google", "USB Printer", "");
+ usb_service().AddDevice(device);
+
+ size_t call_count = 0;
+ std::unique_ptr<base::DictionaryValue> printer_info;
+
+ extension_printer_handler_->StartGrantPrinterAccess(
+ base::StringPrintf("provisional-usb:fake extension id:%s",
+ device->guid().c_str()),
+ base::Bind(&RecordPrinterInfo, &call_count, &printer_info));
+
+ EXPECT_FALSE(printer_info.get());
+ FakePrinterProviderAPI* fake_api = GetPrinterProviderAPI();
+ ASSERT_TRUE(fake_api);
+ ASSERT_EQ(1u, fake_api->pending_usb_info_count());
+
+ extension_printer_handler_->Reset();
+
+ std::unique_ptr<base::DictionaryValue> original_printer_info(
+ DictionaryBuilder()
+ .Set("id", "printer1")
+ .Set("name", "Printer 1")
+ .Build());
+
+ fake_api->TriggerNextUsbPrinterInfoCallback(*original_printer_info);
+
+ EXPECT_EQ(0u, call_count);
+ EXPECT_FALSE(printer_info.get());
+}
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
new file mode 100644
index 00000000000..fba7d030cbe
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -0,0 +1,1794 @@
+// 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/ui/webui/print_preview/print_preview_handler.h"
+
+#include <ctype.h>
+#include <stddef.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/i18n/file_util_icu.h"
+#include "base/i18n/number_formatting.h"
+#include "base/json/json_reader.h"
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/threading/thread.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/app_mode/app_mode_utils.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/browser/printing/print_dialog_cloud.h"
+#include "chrome/browser/printing/print_error_dialog.h"
+#include "chrome/browser/printing/print_job_manager.h"
+#include "chrome/browser/printing/print_preview_dialog_controller.h"
+#include "chrome/browser/printing/print_view_manager.h"
+#include "chrome/browser/printing/printer_manager_dialog.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/chrome_select_file_policy.h"
+#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
+#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
+#include "chrome/browser/ui/webui/print_preview/printer_backend_proxy.h"
+#include "chrome/browser/ui/webui/print_preview/printer_capabilities.h"
+#include "chrome/browser/ui/webui/print_preview/printer_handler.h"
+#include "chrome/browser/ui/webui/print_preview/sticky_settings.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 "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/print_messages.h"
+#include "components/signin/core/browser/gaia_cookie_manager_service.h"
+#include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/signin/core/common/profile_management_switches.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "google_apis/gaia/oauth2_token_service.h"
+#include "net/base/url_util.h"
+#include "printing/backend/print_backend.h"
+#include "printing/backend/print_backend_consts.h"
+#include "printing/features/features.h"
+#include "printing/print_settings.h"
+#include "printing/printing_context.h"
+#include "printing/units.h"
+#include "third_party/icu/source/i18n/unicode/ulocdata.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/printing/printers_manager.h"
+#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
+#include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
+#include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
+#include "chrome/common/url_constants.h"
+#include "chromeos/printing/printer_configuration.h"
+#endif
+
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+#include "chrome/browser/printing/cloud_print/privet_constants.h"
+#endif
+
+using content::BrowserThread;
+using content::RenderFrameHost;
+using content::WebContents;
+using printing::PrintViewManager;
+
+namespace {
+
+// This enum is used to back an UMA histogram, and should therefore be treated
+// as append only.
+enum UserActionBuckets {
+ PRINT_TO_PRINTER,
+ PRINT_TO_PDF,
+ CANCEL,
+ FALLBACK_TO_ADVANCED_SETTINGS_DIALOG,
+ PREVIEW_FAILED,
+ PREVIEW_STARTED,
+ INITIATOR_CRASHED_UNUSED,
+ INITIATOR_CLOSED,
+ PRINT_WITH_CLOUD_PRINT,
+ PRINT_WITH_PRIVET,
+ PRINT_WITH_EXTENSION,
+ USERACTION_BUCKET_BOUNDARY
+};
+
+// This enum is used to back an UMA histogram, and should therefore be treated
+// as append only.
+enum PrintSettingsBuckets {
+ LANDSCAPE = 0,
+ PORTRAIT,
+ COLOR,
+ BLACK_AND_WHITE,
+ COLLATE,
+ SIMPLEX,
+ DUPLEX,
+ TOTAL,
+ HEADERS_AND_FOOTERS,
+ CSS_BACKGROUND,
+ SELECTION_ONLY,
+ EXTERNAL_PDF_PREVIEW,
+ PAGE_RANGE,
+ DEFAULT_MEDIA,
+ NON_DEFAULT_MEDIA,
+ COPIES,
+ NON_DEFAULT_MARGINS,
+ DISTILL_PAGE_UNUSED,
+ SCALING,
+ PRINT_AS_IMAGE,
+ PRINT_SETTINGS_BUCKET_BOUNDARY
+};
+
+// This enum is used to back an UMA histogram, and should therefore be treated
+// as append only.
+enum PrintDocumentTypeBuckets {
+ HTML_DOCUMENT = 0,
+ PDF_DOCUMENT,
+ PRINT_DOCUMENT_TYPE_BUCKET_BOUNDARY
+};
+
+void ReportUserActionHistogram(enum UserActionBuckets event) {
+ UMA_HISTOGRAM_ENUMERATION("PrintPreview.UserAction", event,
+ USERACTION_BUCKET_BOUNDARY);
+}
+
+void ReportPrintSettingHistogram(enum PrintSettingsBuckets setting) {
+ UMA_HISTOGRAM_ENUMERATION("PrintPreview.PrintSettings", setting,
+ PRINT_SETTINGS_BUCKET_BOUNDARY);
+}
+
+void ReportPrintDocumentTypeHistogram(enum PrintDocumentTypeBuckets doctype) {
+ UMA_HISTOGRAM_ENUMERATION("PrintPreview.PrintDocumentType", doctype,
+ PRINT_DOCUMENT_TYPE_BUCKET_BOUNDARY);
+}
+
+// Name of a dictionary field holding cloud print related data;
+const char kAppState[] = "appState";
+// Name of a dictionary field holding the initiator title.
+const char kInitiatorTitle[] = "initiatorTitle";
+// Name of a dictionary field holding the measurement system according to the
+// locale.
+const char kMeasurementSystem[] = "measurementSystem";
+// Name of a dictionary field holding the number format according to the locale.
+const char kNumberFormat[] = "numberFormat";
+// Name of a dictionary field specifying whether to print automatically in
+// kiosk mode. See http://crbug.com/31395.
+const char kPrintAutomaticallyInKioskMode[] = "printAutomaticallyInKioskMode";
+// Dictionary field to indicate whether Chrome is running in forced app (app
+// kiosk) mode. It's not the same as desktop Chrome kiosk (the one above).
+const char kAppKioskMode[] = "appKioskMode";
+// Dictionary field to store Cloud Print base URL.
+const char kCloudPrintUrl[] = "cloudPrintUrl";
+// Name of a dictionary field holding the state of selection for document.
+const char kDocumentHasSelection[] = "documentHasSelection";
+// Dictionary field holding the default destination selection rules.
+const char kDefaultDestinationSelectionRules[] =
+ "defaultDestinationSelectionRules";
+
+// Id of the predefined PDF printer.
+const char kLocalPdfPrinterId[] = "Save as PDF";
+
+// Get the print job settings dictionary from |args|. The caller takes
+// ownership of the returned DictionaryValue. Returns NULL on failure.
+std::unique_ptr<base::DictionaryValue> GetSettingsDictionary(
+ const base::ListValue* args) {
+ std::string json_str;
+ if (!args->GetString(0, &json_str)) {
+ NOTREACHED() << "Could not read JSON argument";
+ return NULL;
+ }
+ if (json_str.empty()) {
+ NOTREACHED() << "Empty print job settings";
+ return NULL;
+ }
+ std::unique_ptr<base::DictionaryValue> settings =
+ base::DictionaryValue::From(base::JSONReader::Read(json_str));
+ if (!settings) {
+ NOTREACHED() << "Print job settings must be a dictionary.";
+ return NULL;
+ }
+
+ if (settings->empty()) {
+ NOTREACHED() << "Print job settings dictionary is empty";
+ return NULL;
+ }
+
+ return settings;
+}
+
+// Track the popularity of print settings and report the stats.
+void ReportPrintSettingsStats(const base::DictionaryValue& settings) {
+ ReportPrintSettingHistogram(TOTAL);
+
+ const base::ListValue* page_range_array = NULL;
+ if (settings.GetList(printing::kSettingPageRange, &page_range_array) &&
+ !page_range_array->empty()) {
+ ReportPrintSettingHistogram(PAGE_RANGE);
+ }
+
+ const base::DictionaryValue* media_size_value = NULL;
+ if (settings.GetDictionary(printing::kSettingMediaSize, &media_size_value) &&
+ !media_size_value->empty()) {
+ bool is_default = false;
+ if (media_size_value->GetBoolean(printing::kSettingMediaSizeIsDefault,
+ &is_default) &&
+ is_default) {
+ ReportPrintSettingHistogram(DEFAULT_MEDIA);
+ } else {
+ ReportPrintSettingHistogram(NON_DEFAULT_MEDIA);
+ }
+ }
+
+ bool landscape = false;
+ if (settings.GetBoolean(printing::kSettingLandscape, &landscape))
+ ReportPrintSettingHistogram(landscape ? LANDSCAPE : PORTRAIT);
+
+ int copies = 1;
+ if (settings.GetInteger(printing::kSettingCopies, &copies) && copies > 1)
+ ReportPrintSettingHistogram(COPIES);
+
+ int scaling = 100;
+ if (settings.GetInteger(printing::kSettingScaleFactor, &scaling) &&
+ scaling != 100) {
+ ReportPrintSettingHistogram(SCALING);
+ }
+
+ bool collate = false;
+ if (settings.GetBoolean(printing::kSettingCollate, &collate) && collate)
+ ReportPrintSettingHistogram(COLLATE);
+
+ int duplex_mode = 0;
+ if (settings.GetInteger(printing::kSettingDuplexMode, &duplex_mode))
+ ReportPrintSettingHistogram(duplex_mode ? DUPLEX : SIMPLEX);
+
+ int color_mode = 0;
+ if (settings.GetInteger(printing::kSettingColor, &color_mode)) {
+ ReportPrintSettingHistogram(
+ printing::IsColorModelSelected(color_mode) ? COLOR : BLACK_AND_WHITE);
+ }
+
+ int margins_type = 0;
+ if (settings.GetInteger(printing::kSettingMarginsType, &margins_type) &&
+ margins_type != 0) {
+ ReportPrintSettingHistogram(NON_DEFAULT_MARGINS);
+ }
+
+ bool headers = false;
+ if (settings.GetBoolean(printing::kSettingHeaderFooterEnabled, &headers) &&
+ headers) {
+ ReportPrintSettingHistogram(HEADERS_AND_FOOTERS);
+ }
+
+ bool css_background = false;
+ if (settings.GetBoolean(printing::kSettingShouldPrintBackgrounds,
+ &css_background) && css_background) {
+ ReportPrintSettingHistogram(CSS_BACKGROUND);
+ }
+
+ bool selection_only = false;
+ if (settings.GetBoolean(printing::kSettingShouldPrintSelectionOnly,
+ &selection_only) && selection_only) {
+ ReportPrintSettingHistogram(SELECTION_ONLY);
+ }
+
+ bool external_preview = false;
+ if (settings.GetBoolean(printing::kSettingOpenPDFInPreview,
+ &external_preview) && external_preview) {
+ ReportPrintSettingHistogram(EXTERNAL_PDF_PREVIEW);
+ }
+
+ bool rasterize = false;
+ if (settings.GetBoolean(printing::kSettingRasterizePdf,
+ &rasterize) && rasterize) {
+ ReportPrintSettingHistogram(PRINT_AS_IMAGE);
+ }
+}
+
+// Callback that stores a PDF file on disk.
+void PrintToPdfCallback(const scoped_refptr<base::RefCountedBytes>& data,
+ const base::FilePath& path,
+ const base::Closure& pdf_file_saved_closure) {
+ base::File file(path,
+ base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+ file.WriteAtCurrentPos(reinterpret_cast<const char*>(data->front()),
+ base::checked_cast<int>(data->size()));
+ if (!pdf_file_saved_closure.is_null())
+ pdf_file_saved_closure.Run();
+}
+
+class PrintingContextDelegate : public printing::PrintingContext::Delegate {
+ public:
+ // PrintingContext::Delegate methods.
+ gfx::NativeView GetParentView() override { return NULL; }
+ std::string GetAppLocale() override {
+ return g_browser_process->GetApplicationLocale();
+ }
+};
+
+gfx::Size GetDefaultPdfMediaSizeMicrons() {
+ PrintingContextDelegate delegate;
+ std::unique_ptr<printing::PrintingContext> printing_context(
+ printing::PrintingContext::Create(&delegate));
+ if (printing::PrintingContext::OK != printing_context->UsePdfSettings() ||
+ printing_context->settings().device_units_per_inch() <= 0) {
+ return gfx::Size();
+ }
+ gfx::Size pdf_media_size = printing_context->GetPdfPaperSizeDeviceUnits();
+ float deviceMicronsPerDeviceUnit =
+ (printing::kHundrethsMMPerInch * 10.0f) /
+ printing_context->settings().device_units_per_inch();
+ return gfx::Size(pdf_media_size.width() * deviceMicronsPerDeviceUnit,
+ pdf_media_size.height() * deviceMicronsPerDeviceUnit);
+}
+
+std::unique_ptr<base::DictionaryValue> GetPdfCapabilities(
+ const std::string& locale) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ cloud_devices::CloudDeviceDescription description;
+ using namespace cloud_devices::printer;
+
+ OrientationCapability orientation;
+ orientation.AddOption(cloud_devices::printer::PORTRAIT);
+ orientation.AddOption(cloud_devices::printer::LANDSCAPE);
+ orientation.AddDefaultOption(AUTO_ORIENTATION, true);
+ orientation.SaveTo(&description);
+
+ ColorCapability color;
+ {
+ Color standard_color(STANDARD_COLOR);
+ standard_color.vendor_id = base::IntToString(printing::COLOR);
+ color.AddDefaultOption(standard_color, true);
+ }
+ color.SaveTo(&description);
+
+ static const cloud_devices::printer::MediaType kPdfMedia[] = {
+ ISO_A0,
+ ISO_A1,
+ ISO_A2,
+ ISO_A3,
+ ISO_A4,
+ ISO_A5,
+ NA_LEGAL,
+ NA_LETTER,
+ NA_LEDGER
+ };
+ const gfx::Size default_media_size = GetDefaultPdfMediaSizeMicrons();
+ Media default_media(
+ "", "", default_media_size.width(), default_media_size.height());
+ if (!default_media.MatchBySize() ||
+ std::find(kPdfMedia,
+ kPdfMedia + arraysize(kPdfMedia),
+ default_media.type) == kPdfMedia + arraysize(kPdfMedia)) {
+ default_media = Media(locale == "en-US" ? NA_LETTER : ISO_A4);
+ }
+ MediaCapability media;
+ for (size_t i = 0; i < arraysize(kPdfMedia); ++i) {
+ Media media_option(kPdfMedia[i]);
+ media.AddDefaultOption(media_option,
+ default_media.type == media_option.type);
+ }
+ media.SaveTo(&description);
+
+ return std::unique_ptr<base::DictionaryValue>(description.root().DeepCopy());
+}
+
+void PrintersToValues(const printing::PrinterList& printer_list,
+ base::ListValue* printers) {
+ for (const printing::PrinterBasicInfo& printer : printer_list) {
+ auto printer_info = base::MakeUnique<base::DictionaryValue>();
+ printer_info->SetString(printing::kSettingDeviceName, printer.printer_name);
+
+ const auto printer_name_description =
+ printing::GetPrinterNameAndDescription(printer);
+ const std::string& printer_name = printer_name_description.first;
+ const std::string& printer_description = printer_name_description.second;
+ printer_info->SetString(printing::kSettingPrinterName, printer_name);
+ printer_info->SetString(printing::kSettingPrinterDescription,
+ printer_description);
+
+ auto options = base::MakeUnique<base::DictionaryValue>();
+ for (const auto opt_it : printer.options)
+ options->SetString(opt_it.first, opt_it.second);
+
+ printer_info->SetBoolean(
+ kCUPSEnterprisePrinter,
+ base::ContainsKey(printer.options, kCUPSEnterprisePrinter) &&
+ printer.options.at(kCUPSEnterprisePrinter) == kValueTrue);
+
+ printer_info->Set(printing::kSettingPrinterOptions, std::move(options));
+
+ printers->Append(std::move(printer_info));
+
+ VLOG(1) << "Found printer " << printer_name << " with device name "
+ << printer.printer_name;
+ }
+}
+
+base::LazyInstance<printing::StickySettings>::DestructorAtExit
+ g_sticky_settings = LAZY_INSTANCE_INITIALIZER;
+
+printing::StickySettings* GetStickySettings() {
+ return g_sticky_settings.Pointer();
+}
+
+// Returns a unique path for |path|, just like with downloads.
+base::FilePath GetUniquePath(const base::FilePath& path) {
+ base::FilePath unique_path = path;
+ int uniquifier =
+ base::GetUniquePathNumber(path, base::FilePath::StringType());
+ if (uniquifier > 0) {
+ unique_path = unique_path.InsertBeforeExtensionASCII(
+ base::StringPrintf(" (%d)", uniquifier));
+ }
+ return unique_path;
+}
+
+bool PrivetPrintingEnabled() {
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+ return true;
+#else
+ return false;
+#endif
+}
+
+} // namespace
+
+class PrintPreviewHandler::AccessTokenService
+ : public OAuth2TokenService::Consumer {
+ public:
+ explicit AccessTokenService(PrintPreviewHandler* handler)
+ : OAuth2TokenService::Consumer("print_preview"),
+ handler_(handler) {
+ }
+
+ void RequestToken(const std::string& type) {
+ if (requests_.find(type) != requests_.end())
+ return; // Already in progress.
+
+ OAuth2TokenService* service = NULL;
+ std::string account_id;
+ if (type == "profile") {
+ Profile* profile = Profile::FromWebUI(handler_->web_ui());
+ if (profile) {
+ ProfileOAuth2TokenService* token_service =
+ ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetInstance()->GetForProfile(profile);
+ account_id = signin_manager->GetAuthenticatedAccountId();
+ service = token_service;
+ }
+ } else if (type == "device") {
+#if defined(OS_CHROMEOS)
+ chromeos::DeviceOAuth2TokenService* token_service =
+ chromeos::DeviceOAuth2TokenServiceFactory::Get();
+ account_id = token_service->GetRobotAccountId();
+ service = token_service;
+#endif
+ }
+
+ if (service) {
+ OAuth2TokenService::ScopeSet oauth_scopes;
+ oauth_scopes.insert(cloud_devices::kCloudPrintAuthScope);
+ std::unique_ptr<OAuth2TokenService::Request> request(
+ service->StartRequest(account_id, oauth_scopes, this));
+ requests_[type] = std::move(request);
+ } else {
+ handler_->SendAccessToken(type, std::string()); // Unknown type.
+ }
+ }
+
+ void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
+ const std::string& access_token,
+ const base::Time& expiration_time) override {
+ OnServiceResponce(request, access_token);
+ }
+
+ void OnGetTokenFailure(const OAuth2TokenService::Request* request,
+ const GoogleServiceAuthError& error) override {
+ OnServiceResponce(request, std::string());
+ }
+
+ private:
+ void OnServiceResponce(const OAuth2TokenService::Request* request,
+ const std::string& access_token) {
+ for (Requests::iterator i = requests_.begin(); i != requests_.end(); ++i) {
+ if (i->second.get() == request) {
+ handler_->SendAccessToken(i->first, access_token);
+ requests_.erase(i);
+ return;
+ }
+ }
+ NOTREACHED();
+ }
+
+ using Requests =
+ std::map<std::string, std::unique_ptr<OAuth2TokenService::Request>>;
+ Requests requests_;
+ PrintPreviewHandler* handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(AccessTokenService);
+};
+
+PrintPreviewHandler::PrintPreviewHandler()
+ : regenerate_preview_request_count_(0),
+ manage_printers_dialog_request_count_(0),
+ manage_cloud_printers_dialog_request_count_(0),
+ reported_failed_preview_(false),
+ has_logged_printers_count_(false),
+ gaia_cookie_manager_service_(nullptr),
+ printer_backend_proxy_(nullptr),
+ weak_factory_(this) {
+ ReportUserActionHistogram(PREVIEW_STARTED);
+}
+
+PrintPreviewHandler::~PrintPreviewHandler() {
+ if (select_file_dialog_.get())
+ select_file_dialog_->ListenerDestroyed();
+
+ UnregisterForGaiaCookieChanges();
+}
+
+void PrintPreviewHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("getPrinters",
+ base::Bind(&PrintPreviewHandler::HandleGetPrinters,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("getPreview",
+ base::Bind(&PrintPreviewHandler::HandleGetPreview,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("print",
+ base::Bind(&PrintPreviewHandler::HandlePrint,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getPrinterCapabilities",
+ base::Bind(&PrintPreviewHandler::HandleGetPrinterCapabilities,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setupPrinter", base::Bind(&PrintPreviewHandler::HandlePrinterSetup,
+ base::Unretained(this)));
+#if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
+ web_ui()->RegisterMessageCallback("showSystemDialog",
+ base::Bind(&PrintPreviewHandler::HandleShowSystemDialog,
+ base::Unretained(this)));
+#endif
+ web_ui()->RegisterMessageCallback("signIn",
+ base::Bind(&PrintPreviewHandler::HandleSignin,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("getAccessToken",
+ base::Bind(&PrintPreviewHandler::HandleGetAccessToken,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("manageCloudPrinters",
+ base::Bind(&PrintPreviewHandler::HandleManageCloudPrint,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("manageLocalPrinters",
+ base::Bind(&PrintPreviewHandler::HandleManagePrinters,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("closePrintPreviewDialog",
+ base::Bind(&PrintPreviewHandler::HandleClosePreviewDialog,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("hidePreview",
+ base::Bind(&PrintPreviewHandler::HandleHidePreview,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("cancelPendingPrintRequest",
+ base::Bind(&PrintPreviewHandler::HandleCancelPendingPrintRequest,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("saveAppState",
+ base::Bind(&PrintPreviewHandler::HandleSaveAppState,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("getInitialSettings",
+ base::Bind(&PrintPreviewHandler::HandleGetInitialSettings,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("forceOpenNewTab",
+ base::Bind(&PrintPreviewHandler::HandleForceOpenNewTab,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("getPrivetPrinters",
+ base::Bind(&PrintPreviewHandler::HandleGetPrivetPrinters,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("stopGetPrivetPrinters",
+ base::Bind(&PrintPreviewHandler::HandleStopGetPrivetPrinters,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("getPrivetPrinterCapabilities",
+ base::Bind(&PrintPreviewHandler::HandleGetPrivetPrinterCapabilities,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getExtensionPrinters",
+ base::Bind(&PrintPreviewHandler::HandleGetExtensionPrinters,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getExtensionPrinterCapabilities",
+ base::Bind(&PrintPreviewHandler::HandleGetExtensionPrinterCapabilities,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "grantExtensionPrinterAccess",
+ base::Bind(&PrintPreviewHandler::HandleGrantExtensionPrinterAccess,
+ base::Unretained(this)));
+ RegisterForGaiaCookieChanges();
+}
+
+WebContents* PrintPreviewHandler::preview_web_contents() const {
+ return web_ui()->GetWebContents();
+}
+
+PrintPreviewUI* PrintPreviewHandler::print_preview_ui() const {
+ return static_cast<PrintPreviewUI*>(web_ui()->GetController());
+}
+
+printing::PrinterBackendProxy* PrintPreviewHandler::printer_backend_proxy() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if (!printer_backend_proxy_) {
+#if defined(OS_CHROMEOS)
+ // ChromeOS stores printer information in printer prefs which requires a
+ // profile. Other plaforms retrieve printer information from OS resources.
+ printer_backend_proxy_ =
+ printing::PrinterBackendProxy::Create(Profile::FromWebUI(web_ui()));
+#else
+ printer_backend_proxy_ = printing::PrinterBackendProxy::Create();
+#endif
+ }
+
+ return printer_backend_proxy_.get();
+}
+
+void PrintPreviewHandler::HandleGetPrinters(const base::ListValue* /*args*/) {
+ VLOG(1) << "Enumerate printers start";
+ printer_backend_proxy()->EnumeratePrinters(base::Bind(
+ &PrintPreviewHandler::SetupPrinterList, weak_factory_.GetWeakPtr()));
+}
+
+void PrintPreviewHandler::HandleGetPrivetPrinters(const base::ListValue* args) {
+ if (!PrivetPrintingEnabled())
+ return web_ui()->CallJavascriptFunctionUnsafe("onPrivetPrinterSearchDone");
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+ using local_discovery::ServiceDiscoverySharedClient;
+ scoped_refptr<ServiceDiscoverySharedClient> service_discovery =
+ ServiceDiscoverySharedClient::GetInstance();
+ StartPrivetLister(service_discovery);
+#endif
+}
+
+void PrintPreviewHandler::HandleStopGetPrivetPrinters(
+ const base::ListValue* args) {
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+ if (PrivetPrintingEnabled() && printer_lister_) {
+ printer_lister_->Stop();
+ }
+#endif
+}
+
+void PrintPreviewHandler::HandleGetPrivetPrinterCapabilities(
+ const base::ListValue* args) {
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+ std::string name;
+ bool success = args->GetString(0, &name);
+ DCHECK(success);
+
+ CreatePrivetHTTP(
+ name, base::Bind(&PrintPreviewHandler::PrivetCapabilitiesUpdateClient,
+ weak_factory_.GetWeakPtr()));
+#endif
+}
+
+void PrintPreviewHandler::HandleGetExtensionPrinters(
+ const base::ListValue* args) {
+ EnsureExtensionPrinterHandlerSet();
+ // Make sure all in progress requests are canceled before new printer search
+ // starts.
+ extension_printer_handler_->Reset();
+ extension_printer_handler_->StartGetPrinters(
+ base::Bind(&PrintPreviewHandler::OnGotPrintersForExtension,
+ weak_factory_.GetWeakPtr()));
+}
+
+void PrintPreviewHandler::HandleGrantExtensionPrinterAccess(
+ const base::ListValue* args) {
+ std::string printer_id;
+ bool ok = args->GetString(0, &printer_id);
+ DCHECK(ok);
+
+ EnsureExtensionPrinterHandlerSet();
+ extension_printer_handler_->StartGrantPrinterAccess(
+ printer_id, base::Bind(&PrintPreviewHandler::OnGotExtensionPrinterInfo,
+ weak_factory_.GetWeakPtr(), printer_id));
+}
+
+void PrintPreviewHandler::HandleGetExtensionPrinterCapabilities(
+ const base::ListValue* args) {
+ std::string printer_id;
+ bool ok = args->GetString(0, &printer_id);
+ DCHECK(ok);
+
+ EnsureExtensionPrinterHandlerSet();
+ extension_printer_handler_->StartGetCapability(
+ printer_id,
+ base::Bind(&PrintPreviewHandler::OnGotExtensionPrinterCapabilities,
+ weak_factory_.GetWeakPtr()));
+}
+
+void PrintPreviewHandler::HandleGetPreview(const base::ListValue* args) {
+ DCHECK_EQ(2U, args->GetSize());
+ std::unique_ptr<base::DictionaryValue> settings = GetSettingsDictionary(args);
+ if (!settings)
+ return;
+ int request_id = -1;
+ if (!settings->GetInteger(printing::kPreviewRequestID, &request_id))
+ return;
+
+ print_preview_ui()->OnPrintPreviewRequest(request_id);
+ // Add an additional key in order to identify |print_preview_ui| later on
+ // when calling PrintPreviewUI::GetCurrentPrintPreviewStatus() on the IO
+ // thread.
+ settings->SetInteger(printing::kPreviewUIID,
+ print_preview_ui()->GetIDForPrintPreviewUI());
+
+ // Increment request count.
+ ++regenerate_preview_request_count_;
+
+ WebContents* initiator = GetInitiator();
+ content::RenderFrameHost* rfh =
+ initiator
+ ? PrintViewManager::FromWebContents(initiator)->print_preview_rfh()
+ : nullptr;
+ if (!rfh) {
+ ReportUserActionHistogram(INITIATOR_CLOSED);
+ print_preview_ui()->OnClosePrintPreviewDialog();
+ return;
+ }
+
+ // Retrieve the page title and url and send it to the renderer process if
+ // headers and footers are to be displayed.
+ bool display_header_footer = false;
+ bool success = settings->GetBoolean(printing::kSettingHeaderFooterEnabled,
+ &display_header_footer);
+ DCHECK(success);
+ if (display_header_footer) {
+ settings->SetString(printing::kSettingHeaderFooterTitle,
+ initiator->GetTitle());
+ std::string url;
+ content::NavigationEntry* entry =
+ initiator->GetController().GetLastCommittedEntry();
+ if (entry) {
+ url::Replacements<char> url_sanitizer;
+ url_sanitizer.ClearUsername();
+ url_sanitizer.ClearPassword();
+
+ url = entry->GetVirtualURL().ReplaceComponents(url_sanitizer).spec();
+ }
+ settings->SetString(printing::kSettingHeaderFooterURL, url);
+ }
+
+ bool generate_draft_data = false;
+ success = settings->GetBoolean(printing::kSettingGenerateDraftData,
+ &generate_draft_data);
+ DCHECK(success);
+
+ if (!generate_draft_data) {
+ int page_count = -1;
+ success = args->GetInteger(1, &page_count);
+ DCHECK(success);
+
+ if (page_count != -1) {
+ bool preview_modifiable = false;
+ success = settings->GetBoolean(printing::kSettingPreviewModifiable,
+ &preview_modifiable);
+ DCHECK(success);
+
+ if (preview_modifiable &&
+ print_preview_ui()->GetAvailableDraftPageCount() != page_count) {
+ settings->SetBoolean(printing::kSettingGenerateDraftData, true);
+ }
+ }
+ }
+
+ VLOG(1) << "Print preview request start";
+
+ rfh->Send(new PrintMsg_PrintPreview(rfh->GetRoutingID(), *settings));
+}
+
+void PrintPreviewHandler::HandlePrint(const base::ListValue* args) {
+ ReportStats();
+
+ // Record the number of times the user requests to regenerate preview data
+ // before printing.
+ UMA_HISTOGRAM_COUNTS("PrintPreview.RegeneratePreviewRequest.BeforePrint",
+ regenerate_preview_request_count_);
+
+ std::unique_ptr<base::DictionaryValue> settings = GetSettingsDictionary(args);
+ if (!settings)
+ return;
+
+ ReportPrintSettingsStats(*settings);
+
+ // Report whether the user printed a PDF or an HTML document.
+ ReportPrintDocumentTypeHistogram(print_preview_ui()->source_is_modifiable() ?
+ HTML_DOCUMENT : PDF_DOCUMENT);
+
+ bool print_to_pdf = false;
+ bool is_cloud_printer = false;
+ bool print_with_privet = false;
+ bool print_with_extension = false;
+
+ bool open_pdf_in_preview = false;
+#if defined(OS_MACOSX)
+ open_pdf_in_preview = settings->HasKey(printing::kSettingOpenPDFInPreview);
+#endif
+
+ if (!open_pdf_in_preview) {
+ settings->GetBoolean(printing::kSettingPrintToPDF, &print_to_pdf);
+ settings->GetBoolean(printing::kSettingPrintWithPrivet, &print_with_privet);
+ settings->GetBoolean(printing::kSettingPrintWithExtension,
+ &print_with_extension);
+ is_cloud_printer = settings->HasKey(printing::kSettingCloudPrintId);
+ }
+
+ int page_count = 0;
+ settings->GetInteger(printing::kSettingPreviewPageCount, &page_count);
+
+ if (print_to_pdf) {
+ UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPDF", page_count);
+ ReportUserActionHistogram(PRINT_TO_PDF);
+ PrintToPdf();
+ return;
+ }
+
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+ if (print_with_privet && PrivetPrintingEnabled()) {
+ std::string printer_name;
+ std::string print_ticket;
+ std::string capabilities;
+ UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintWithPrivet", page_count);
+ ReportUserActionHistogram(PRINT_WITH_PRIVET);
+
+ int width = 0;
+ int height = 0;
+ if (!settings->GetString(printing::kSettingDeviceName, &printer_name) ||
+ !settings->GetString(printing::kSettingTicket, &print_ticket) ||
+ !settings->GetString(printing::kSettingCapabilities, &capabilities) ||
+ !settings->GetInteger(printing::kSettingPageWidth, &width) ||
+ !settings->GetInteger(printing::kSettingPageHeight, &height) ||
+ width <= 0 || height <= 0) {
+ NOTREACHED();
+ base::Value http_code_value(-1);
+ web_ui()->CallJavascriptFunctionUnsafe("onPrivetPrintFailed",
+ http_code_value);
+ return;
+ }
+
+ PrintToPrivetPrinter(
+ printer_name, print_ticket, capabilities, gfx::Size(width, height));
+ return;
+ }
+#endif
+
+ if (print_with_extension) {
+ UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintWithExtension",
+ page_count);
+ ReportUserActionHistogram(PRINT_WITH_EXTENSION);
+
+ std::string destination_id;
+ std::string print_ticket;
+ std::string capabilities;
+ int width = 0;
+ int height = 0;
+ if (!settings->GetString(printing::kSettingDeviceName, &destination_id) ||
+ !settings->GetString(printing::kSettingTicket, &print_ticket) ||
+ !settings->GetString(printing::kSettingCapabilities, &capabilities) ||
+ !settings->GetInteger(printing::kSettingPageWidth, &width) ||
+ !settings->GetInteger(printing::kSettingPageHeight, &height) ||
+ width <= 0 || height <= 0) {
+ NOTREACHED();
+ OnExtensionPrintResult(false, "FAILED");
+ return;
+ }
+
+ base::string16 title;
+ scoped_refptr<base::RefCountedBytes> data;
+ if (!GetPreviewDataAndTitle(&data, &title)) {
+ LOG(ERROR) << "Nothing to print; no preview available.";
+ OnExtensionPrintResult(false, "NO_DATA");
+ return;
+ }
+
+ EnsureExtensionPrinterHandlerSet();
+ extension_printer_handler_->StartPrint(
+ destination_id, capabilities, title, print_ticket,
+ gfx::Size(width, height), data,
+ base::Bind(&PrintPreviewHandler::OnExtensionPrintResult,
+ weak_factory_.GetWeakPtr()));
+ return;
+ }
+
+ scoped_refptr<base::RefCountedBytes> data;
+ base::string16 title;
+ if (!GetPreviewDataAndTitle(&data, &title)) {
+ // Nothing to print, no preview available.
+ return;
+ }
+
+ if (is_cloud_printer) {
+ UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToCloudPrint",
+ page_count);
+ ReportUserActionHistogram(PRINT_WITH_CLOUD_PRINT);
+ SendCloudPrintJob(data.get());
+ return;
+ }
+
+#if BUILDFLAG(ENABLE_BASIC_PRINTING)
+ bool system_dialog = false;
+ settings->GetBoolean(printing::kSettingShowSystemDialog, &system_dialog);
+ if (system_dialog) {
+ UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.SystemDialog", page_count);
+ ReportUserActionHistogram(FALLBACK_TO_ADVANCED_SETTINGS_DIALOG);
+ } else {
+ UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPrinter", page_count);
+ ReportUserActionHistogram(PRINT_TO_PRINTER);
+ }
+
+ // This tries to activate the initiator as well, so do not clear the
+ // association with the initiator yet.
+ print_preview_ui()->OnHidePreviewDialog();
+
+ // Grab the current initiator before calling ClearInitiatorDetails() below.
+ // Otherwise calling GetInitiator() later will return the wrong WebContents.
+ // https://crbug.com/407080
+ WebContents* initiator = GetInitiator();
+ if (initiator) {
+ // Save initiator IDs. PrintMsg_PrintForPrintPreview below should cause
+ // the renderer to send PrintHostMsg_UpdatePrintSettings and trigger
+ // PrintingMessageFilter::OnUpdatePrintSettings(), which needs this info.
+ auto* main_render_frame = initiator->GetMainFrame();
+ settings->SetInteger(printing::kPreviewInitiatorHostId,
+ main_render_frame->GetProcess()->GetID());
+ settings->SetInteger(printing::kPreviewInitiatorRoutingId,
+ main_render_frame->GetRoutingID());
+ }
+
+ // Do this so the initiator can open a new print preview dialog, while the
+ // current print preview dialog is still handling its print job.
+ ClearInitiatorDetails();
+
+ // Set ID to know whether printing is for preview.
+ settings->SetInteger(printing::kPreviewUIID,
+ print_preview_ui()->GetIDForPrintPreviewUI());
+ RenderFrameHost* rfh = preview_web_contents()->GetMainFrame();
+ rfh->Send(new PrintMsg_PrintForPrintPreview(rfh->GetRoutingID(), *settings));
+
+ // For all other cases above, the preview dialog will stay open until the
+ // printing has finished. Then the dialog closes and PrintPreviewDone() gets
+ // called. In the case below, since the preview dialog will be hidden and
+ // not closed, we need to make this call.
+ if (initiator) {
+ auto* print_view_manager = PrintViewManager::FromWebContents(initiator);
+ print_view_manager->PrintPreviewDone();
+ }
+#else
+ NOTREACHED();
+#endif // BUILDFLAG(ENABLE_BASIC_PRINTING)
+}
+
+void PrintPreviewHandler::PrintToPdf() {
+ if (!print_to_pdf_path_.empty()) {
+ // User has already selected a path, no need to show the dialog again.
+ PostPrintToPdfTask();
+ } else if (!select_file_dialog_.get() ||
+ !select_file_dialog_->IsRunning(platform_util::GetTopLevel(
+ preview_web_contents()->GetNativeView()))) {
+ // Pre-populating select file dialog with print job title.
+ const base::string16& print_job_title_utf16 =
+ print_preview_ui()->initiator_title();
+
+#if defined(OS_WIN)
+ base::FilePath::StringType print_job_title(print_job_title_utf16);
+#elif defined(OS_POSIX)
+ base::FilePath::StringType print_job_title =
+ base::UTF16ToUTF8(print_job_title_utf16);
+#endif
+
+ base::i18n::ReplaceIllegalCharactersInPath(&print_job_title, '_');
+ base::FilePath default_filename(print_job_title);
+ default_filename =
+ default_filename.ReplaceExtension(FILE_PATH_LITERAL("pdf"));
+
+ base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ bool prompt_user = !cmdline->HasSwitch(switches::kKioskModePrinting);
+ SelectFile(default_filename, prompt_user);
+ }
+}
+
+void PrintPreviewHandler::HandleHidePreview(const base::ListValue* /*args*/) {
+ print_preview_ui()->OnHidePreviewDialog();
+}
+
+void PrintPreviewHandler::HandleCancelPendingPrintRequest(
+ const base::ListValue* /*args*/) {
+ WebContents* initiator = GetInitiator();
+ if (initiator)
+ ClearInitiatorDetails();
+ chrome::ShowPrintErrorDialog();
+}
+
+void PrintPreviewHandler::HandleSaveAppState(const base::ListValue* args) {
+ std::string data_to_save;
+ printing::StickySettings* sticky_settings = GetStickySettings();
+ if (args->GetString(0, &data_to_save) && !data_to_save.empty())
+ sticky_settings->StoreAppState(data_to_save);
+ sticky_settings->SaveInPrefs(Profile::FromBrowserContext(
+ preview_web_contents()->GetBrowserContext())->GetPrefs());
+}
+
+void PrintPreviewHandler::HandleGetPrinterCapabilities(
+ const base::ListValue* args) {
+ std::string printer_name;
+ bool ret = args->GetString(0, &printer_name);
+ if (!ret || printer_name.empty())
+ return;
+
+ if (printer_name == kLocalPdfPrinterId) {
+ auto printer_info = base::MakeUnique<base::DictionaryValue>();
+ printer_info->SetString(printing::kPrinterId, printer_name);
+ printer_info->Set(
+ printing::kPrinterCapabilities,
+ GetPdfCapabilities(g_browser_process->GetApplicationLocale()));
+ SendPrinterCapabilities(printer_name, std::move(printer_info));
+ return;
+ }
+
+ printing::PrinterSetupCallback cb =
+ base::Bind(&PrintPreviewHandler::SendPrinterCapabilities,
+ weak_factory_.GetWeakPtr(), printer_name);
+
+ printer_backend_proxy()->ConfigurePrinterAndFetchCapabilities(printer_name,
+ cb);
+}
+
+// |args| is expected to contain a string with representing the callback id
+// followed by a list of arguments the first of which should be the printer id.
+void PrintPreviewHandler::HandlePrinterSetup(const base::ListValue* args) {
+ AllowJavascript();
+
+ std::string callback_id;
+ std::string printer_name;
+ if (!args->GetString(0, &callback_id) || !args->GetString(1, &printer_name) ||
+ callback_id.empty() || printer_name.empty()) {
+ RejectJavascriptCallback(base::Value(callback_id),
+ base::Value(printer_name));
+ return;
+ }
+
+ printer_backend_proxy()->ConfigurePrinterAndFetchCapabilities(
+ printer_name,
+ base::Bind(&PrintPreviewHandler::SendPrinterSetup,
+ weak_factory_.GetWeakPtr(), callback_id, printer_name));
+}
+
+void PrintPreviewHandler::OnSigninComplete() {
+ if (print_preview_ui())
+ print_preview_ui()->OnReloadPrintersList();
+}
+
+void PrintPreviewHandler::HandleSignin(const base::ListValue* args) {
+ bool add_account = false;
+ bool success = args->GetBoolean(0, &add_account);
+ DCHECK(success);
+
+ Profile* profile = Profile::FromBrowserContext(
+ preview_web_contents()->GetBrowserContext());
+ chrome::ScopedTabbedBrowserDisplayer displayer(profile);
+ print_dialog_cloud::CreateCloudPrintSigninTab(
+ displayer.browser(),
+ add_account,
+ base::Bind(&PrintPreviewHandler::OnSigninComplete,
+ weak_factory_.GetWeakPtr()));
+}
+
+void PrintPreviewHandler::HandleGetAccessToken(const base::ListValue* args) {
+ std::string type;
+ if (!args->GetString(0, &type))
+ return;
+ if (!token_service_)
+ token_service_ = base::MakeUnique<AccessTokenService>(this);
+ token_service_->RequestToken(type);
+}
+
+void PrintPreviewHandler::HandleManageCloudPrint(
+ const base::ListValue* args) {
+ ++manage_cloud_printers_dialog_request_count_;
+ GURL manage_url(cloud_devices::GetCloudPrintRelativeURL("manage.html"));
+ std::string user;
+ if (!args->GetString(0, &user))
+ return;
+ if (!user.empty())
+ manage_url = net::AppendQueryParameter(manage_url, "user", user);
+ preview_web_contents()->OpenURL(
+ content::OpenURLParams(manage_url, content::Referrer(),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui::PAGE_TRANSITION_LINK, false));
+}
+
+#if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
+void PrintPreviewHandler::HandleShowSystemDialog(
+ const base::ListValue* /*args*/) {
+ ReportStats();
+ ReportUserActionHistogram(FALLBACK_TO_ADVANCED_SETTINGS_DIALOG);
+
+ WebContents* initiator = GetInitiator();
+ if (!initiator)
+ return;
+
+ auto* print_view_manager = PrintViewManager::FromWebContents(initiator);
+ print_view_manager->PrintForSystemDialogNow(
+ base::Bind(&PrintPreviewHandler::ClosePreviewDialog,
+ weak_factory_.GetWeakPtr()));
+
+ // Cancel the pending preview request if exists.
+ print_preview_ui()->OnCancelPendingPreviewRequest();
+}
+#endif
+
+void PrintPreviewHandler::HandleManagePrinters(
+ const base::ListValue* /*args*/) {
+ ++manage_printers_dialog_request_count_;
+#if defined(OS_CHROMEOS)
+ GURL local_printers_manage_url(chrome::kChromeUIMdCupsSettingsURL);
+ preview_web_contents()->OpenURL(
+ content::OpenURLParams(local_printers_manage_url, content::Referrer(),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui::PAGE_TRANSITION_LINK, false));
+#else
+ printing::PrinterManagerDialog::ShowPrinterManagerDialog();
+#endif
+}
+
+void PrintPreviewHandler::HandleClosePreviewDialog(
+ const base::ListValue* /*args*/) {
+ ReportStats();
+ ReportUserActionHistogram(CANCEL);
+
+ // Record the number of times the user requests to regenerate preview data
+ // before cancelling.
+ UMA_HISTOGRAM_COUNTS("PrintPreview.RegeneratePreviewRequest.BeforeCancel",
+ regenerate_preview_request_count_);
+}
+
+void PrintPreviewHandler::ReportStats() {
+ UMA_HISTOGRAM_COUNTS("PrintPreview.ManagePrinters",
+ manage_printers_dialog_request_count_);
+ UMA_HISTOGRAM_COUNTS("PrintPreview.ManageCloudPrinters",
+ manage_cloud_printers_dialog_request_count_);
+}
+
+void PrintPreviewHandler::GetNumberFormatAndMeasurementSystem(
+ base::DictionaryValue* settings) {
+
+ // Getting the measurement system based on the locale.
+ UErrorCode errorCode = U_ZERO_ERROR;
+ const char* locale = g_browser_process->GetApplicationLocale().c_str();
+ UMeasurementSystem system = ulocdata_getMeasurementSystem(locale, &errorCode);
+ // On error, assume the units are SI.
+ // Since the only measurement units print preview's WebUI cares about are
+ // those for measuring distance, assume anything non-US is SI.
+ if (errorCode > U_ZERO_ERROR || system != UMS_US)
+ system = UMS_SI;
+
+ // Getting the number formatting based on the locale and writing to
+ // dictionary.
+ settings->SetString(kNumberFormat, base::FormatDouble(123456.78, 2));
+ settings->SetInteger(kMeasurementSystem, system);
+}
+
+void PrintPreviewHandler::HandleGetInitialSettings(
+ const base::ListValue* args) {
+ std::string callback_id;
+ CHECK(args->GetString(0, &callback_id));
+ CHECK(!callback_id.empty());
+
+ AllowJavascript();
+
+ // Send before SendInitialSettings() to allow cloud printer auto select.
+ SendCloudPrintEnabled();
+ printer_backend_proxy()->GetDefaultPrinter(
+ base::Bind(&PrintPreviewHandler::SendInitialSettings,
+ weak_factory_.GetWeakPtr(), callback_id));
+}
+
+void PrintPreviewHandler::HandleForceOpenNewTab(const base::ListValue* args) {
+ std::string url;
+ if (!args->GetString(0, &url))
+ return;
+ Browser* browser = chrome::FindBrowserWithWebContents(GetInitiator());
+ if (!browser)
+ return;
+ chrome::AddSelectedTabWithURL(browser,
+ GURL(url),
+ ui::PAGE_TRANSITION_LINK);
+}
+
+void PrintPreviewHandler::SendInitialSettings(
+ const std::string& callback_id,
+ const std::string& default_printer) {
+ base::DictionaryValue initial_settings;
+ initial_settings.SetString(kInitiatorTitle,
+ print_preview_ui()->initiator_title());
+ initial_settings.SetBoolean(printing::kSettingPreviewModifiable,
+ print_preview_ui()->source_is_modifiable());
+ initial_settings.SetString(printing::kSettingPrinterName, default_printer);
+ initial_settings.SetBoolean(kDocumentHasSelection,
+ print_preview_ui()->source_has_selection());
+ initial_settings.SetBoolean(printing::kSettingShouldPrintSelectionOnly,
+ print_preview_ui()->print_selection_only());
+ PrefService* prefs = Profile::FromBrowserContext(
+ preview_web_contents()->GetBrowserContext())->GetPrefs();
+ printing::StickySettings* sticky_settings = GetStickySettings();
+ sticky_settings->RestoreFromPrefs(prefs);
+ if (sticky_settings->printer_app_state()) {
+ initial_settings.SetString(kAppState,
+ *sticky_settings->printer_app_state());
+ }
+
+ base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ initial_settings.SetBoolean(kPrintAutomaticallyInKioskMode,
+ cmdline->HasSwitch(switches::kKioskModePrinting));
+ initial_settings.SetBoolean(kAppKioskMode,
+ chrome::IsRunningInForcedAppMode());
+ if (prefs) {
+ const std::string rules_str =
+ prefs->GetString(prefs::kPrintPreviewDefaultDestinationSelectionRules);
+ if (!rules_str.empty())
+ initial_settings.SetString(kDefaultDestinationSelectionRules, rules_str);
+ }
+
+ if (print_preview_ui()->source_is_modifiable())
+ GetNumberFormatAndMeasurementSystem(&initial_settings);
+ ResolveJavascriptCallback(base::Value(callback_id), initial_settings);
+}
+
+void PrintPreviewHandler::ClosePreviewDialog() {
+ print_preview_ui()->OnClosePrintPreviewDialog();
+}
+
+void PrintPreviewHandler::SendAccessToken(const std::string& type,
+ const std::string& access_token) {
+ VLOG(1) << "Get getAccessToken finished";
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "onDidGetAccessToken", base::Value(type), base::Value(access_token));
+}
+
+void PrintPreviewHandler::SendPrinterCapabilities(
+ const std::string& printer_name,
+ std::unique_ptr<base::DictionaryValue> settings_info) {
+ // Check that |settings_info| is valid.
+ if (settings_info && settings_info->Get("capabilities", nullptr)) {
+ VLOG(1) << "Get printer capabilities finished";
+ web_ui()->CallJavascriptFunctionUnsafe("updateWithPrinterCapabilities",
+ *settings_info);
+ return;
+ }
+
+ VLOG(1) << "Get printer capabilities failed";
+ web_ui()->CallJavascriptFunctionUnsafe("failedToGetPrinterCapabilities",
+ base::Value(printer_name));
+}
+
+void PrintPreviewHandler::SendPrinterSetup(
+ const std::string& callback_id,
+ const std::string& printer_name,
+ std::unique_ptr<base::DictionaryValue> destination_info) {
+ auto response = base::MakeUnique<base::DictionaryValue>();
+ bool success = true;
+ auto caps_value = base::MakeUnique<base::Value>();
+ auto caps = base::MakeUnique<base::DictionaryValue>();
+ if (destination_info &&
+ destination_info->Remove(printing::kPrinterCapabilities, &caps_value) &&
+ caps_value->IsType(base::Value::Type::DICTIONARY)) {
+ caps = base::DictionaryValue::From(std::move(caps_value));
+ } else {
+ LOG(WARNING) << "Printer setup failed";
+ success = false;
+ }
+
+ response->SetString("printerId", printer_name);
+ response->SetBoolean("success", success);
+ response->Set("capabilities", std::move(caps));
+
+ ResolveJavascriptCallback(base::Value(callback_id), *response);
+}
+
+void PrintPreviewHandler::SetupPrinterList(
+ const printing::PrinterList& printer_list) {
+ base::ListValue printers;
+ PrintersToValues(printer_list, &printers);
+
+ VLOG(1) << "Enumerate printers finished, found " << printers.GetSize()
+ << " printers";
+
+ if (!has_logged_printers_count_) {
+ UMA_HISTOGRAM_COUNTS("PrintPreview.NumberOfPrinters", printers.GetSize());
+ has_logged_printers_count_ = true;
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("setPrinters", printers);
+}
+
+void PrintPreviewHandler::SendCloudPrintEnabled() {
+ Profile* profile = Profile::FromBrowserContext(
+ preview_web_contents()->GetBrowserContext());
+ PrefService* prefs = profile->GetPrefs();
+ if (prefs->GetBoolean(prefs::kCloudPrintSubmitEnabled)) {
+ base::DictionaryValue settings;
+ settings.SetString(kCloudPrintUrl,
+ GURL(cloud_devices::GetCloudPrintURL()).spec());
+ settings.SetBoolean(kAppKioskMode, chrome::IsRunningInForcedAppMode());
+ web_ui()->CallJavascriptFunctionUnsafe("setUseCloudPrint", settings);
+ }
+}
+
+void PrintPreviewHandler::SendCloudPrintJob(const base::RefCountedBytes* data) {
+ // BASE64 encode the job data.
+ const base::StringPiece raw_data(reinterpret_cast<const char*>(data->front()),
+ data->size());
+ std::string base64_data;
+ base::Base64Encode(raw_data, &base64_data);
+ base::Value data_value(base64_data);
+
+ web_ui()->CallJavascriptFunctionUnsafe("printToCloud", data_value);
+}
+
+WebContents* PrintPreviewHandler::GetInitiator() const {
+ printing::PrintPreviewDialogController* dialog_controller =
+ printing::PrintPreviewDialogController::GetInstance();
+ if (!dialog_controller)
+ return NULL;
+ return dialog_controller->GetInitiator(preview_web_contents());
+}
+
+void PrintPreviewHandler::OnAddAccountToCookieCompleted(
+ const std::string& account_id,
+ const GoogleServiceAuthError& error) {
+ OnSigninComplete();
+}
+
+void PrintPreviewHandler::SelectFile(const base::FilePath& default_filename,
+ bool prompt_user) {
+ if (prompt_user) {
+ ChromeSelectFilePolicy policy(GetInitiator());
+ if (!policy.CanOpenSelectFileDialog()) {
+ policy.SelectFileDenied();
+ return ClosePreviewDialog();
+ }
+ }
+
+ // Get save location from Download Preferences.
+ DownloadPrefs* download_prefs = DownloadPrefs::FromBrowserContext(
+ preview_web_contents()->GetBrowserContext());
+ base::FilePath file_path = download_prefs->SaveFilePath();
+ printing::StickySettings* sticky_settings = GetStickySettings();
+ sticky_settings->SaveInPrefs(Profile::FromBrowserContext(
+ preview_web_contents()->GetBrowserContext())->GetPrefs());
+ // Handle the no prompting case. Like the dialog prompt, this function
+ // returns and eventually FileSelected() gets called.
+ if (!prompt_user) {
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&GetUniquePath,
+ download_prefs->SaveFilePath().Append(default_filename)),
+ base::Bind(&PrintPreviewHandler::OnGotUniqueFileName,
+ weak_factory_.GetWeakPtr()));
+ return;
+ }
+
+ // Otherwise prompt the user.
+ ui::SelectFileDialog::FileTypeInfo file_type_info;
+ file_type_info.extensions.resize(1);
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pdf"));
+
+ select_file_dialog_ =
+ ui::SelectFileDialog::Create(this, nullptr /*policy already checked*/);
+ select_file_dialog_->SelectFile(
+ ui::SelectFileDialog::SELECT_SAVEAS_FILE,
+ base::string16(),
+ download_prefs->SaveFilePath().Append(default_filename),
+ &file_type_info,
+ 0,
+ base::FilePath::StringType(),
+ platform_util::GetTopLevel(preview_web_contents()->GetNativeView()),
+ NULL);
+}
+
+void PrintPreviewHandler::OnGotUniqueFileName(const base::FilePath& path) {
+ FileSelected(path, 0, nullptr);
+}
+
+void PrintPreviewHandler::OnPrintPreviewFailed() {
+ if (reported_failed_preview_)
+ return;
+ reported_failed_preview_ = true;
+ ReportUserActionHistogram(PREVIEW_FAILED);
+}
+
+#if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
+void PrintPreviewHandler::ShowSystemDialog() {
+ HandleShowSystemDialog(NULL);
+}
+#endif
+
+void PrintPreviewHandler::FileSelected(const base::FilePath& path,
+ int /* index */,
+ void* /* params */) {
+ // Update downloads location and save sticky settings.
+ DownloadPrefs* download_prefs = DownloadPrefs::FromBrowserContext(
+ preview_web_contents()->GetBrowserContext());
+ download_prefs->SetSaveFilePath(path.DirName());
+ printing::StickySettings* sticky_settings = GetStickySettings();
+ sticky_settings->SaveInPrefs(
+ Profile::FromBrowserContext(preview_web_contents()->GetBrowserContext())
+ ->GetPrefs());
+ web_ui()->CallJavascriptFunctionUnsafe("fileSelectionCompleted");
+ print_to_pdf_path_ = path;
+ PostPrintToPdfTask();
+}
+
+void PrintPreviewHandler::PostPrintToPdfTask() {
+ scoped_refptr<base::RefCountedBytes> data;
+ base::string16 title;
+ if (!GetPreviewDataAndTitle(&data, &title)) {
+ NOTREACHED() << "Preview data was checked before file dialog.";
+ return;
+ }
+
+ base::PostTaskWithTraits(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::BindOnce(&PrintToPdfCallback, data, print_to_pdf_path_,
+ pdf_file_saved_closure_));
+ print_to_pdf_path_.clear();
+ ClosePreviewDialog();
+}
+
+void PrintPreviewHandler::FileSelectionCanceled(void* params) {
+ print_preview_ui()->OnFileSelectionCancelled();
+}
+
+void PrintPreviewHandler::ClearInitiatorDetails() {
+ WebContents* initiator = GetInitiator();
+ if (!initiator)
+ return;
+
+ // We no longer require the initiator details. Remove those details associated
+ // with the preview dialog to allow the initiator to create another preview
+ // dialog.
+ printing::PrintPreviewDialogController* dialog_controller =
+ printing::PrintPreviewDialogController::GetInstance();
+ if (dialog_controller)
+ dialog_controller->EraseInitiatorInfo(preview_web_contents());
+}
+
+bool PrintPreviewHandler::GetPreviewDataAndTitle(
+ scoped_refptr<base::RefCountedBytes>* data,
+ base::string16* title) const {
+ scoped_refptr<base::RefCountedBytes> tmp_data;
+ print_preview_ui()->GetPrintPreviewDataForIndex(
+ printing::COMPLETE_PREVIEW_DOCUMENT_INDEX, &tmp_data);
+
+ if (!tmp_data.get()) {
+ // Nothing to print, no preview available.
+ return false;
+ }
+ DCHECK(tmp_data->size());
+ DCHECK(tmp_data->front());
+
+ *data = tmp_data;
+ *title = print_preview_ui()->initiator_title();
+ return true;
+}
+
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+void PrintPreviewHandler::StartPrivetLister(const scoped_refptr<
+ local_discovery::ServiceDiscoverySharedClient>& client) {
+ if (!PrivetPrintingEnabled())
+ return web_ui()->CallJavascriptFunctionUnsafe("onPrivetPrinterSearchDone");
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ DCHECK(!service_discovery_client_.get() ||
+ service_discovery_client_.get() == client.get());
+ service_discovery_client_ = client;
+ printer_lister_ = base::MakeUnique<cloud_print::PrivetLocalPrinterLister>(
+ service_discovery_client_.get(), profile->GetRequestContext(), this);
+ printer_lister_->Start();
+}
+
+void PrintPreviewHandler::LocalPrinterChanged(
+ const std::string& name,
+ 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)) {
+ base::DictionaryValue info;
+ FillPrinterDescription(name, description, has_local_printing, &info);
+ web_ui()->CallJavascriptFunctionUnsafe("onPrivetPrinterChanged", info);
+ }
+}
+
+void PrintPreviewHandler::LocalPrinterRemoved(const std::string& name) {
+}
+
+void PrintPreviewHandler::LocalPrinterCacheFlushed() {
+}
+
+void PrintPreviewHandler::PrivetCapabilitiesUpdateClient(
+ std::unique_ptr<cloud_print::PrivetHTTPClient> http_client) {
+ if (!PrivetUpdateClient(std::move(http_client)))
+ return;
+
+ privet_capabilities_operation_ =
+ privet_http_client_->CreateCapabilitiesOperation(
+ base::Bind(&PrintPreviewHandler::OnPrivetCapabilities,
+ weak_factory_.GetWeakPtr()));
+ privet_capabilities_operation_->Start();
+}
+
+bool PrintPreviewHandler::PrivetUpdateClient(
+ std::unique_ptr<cloud_print::PrivetHTTPClient> http_client) {
+ if (!http_client) {
+ SendPrivetCapabilitiesError(privet_http_resolution_->GetName());
+ privet_http_resolution_.reset();
+ return false;
+ }
+
+ privet_local_print_operation_.reset();
+ privet_capabilities_operation_.reset();
+ privet_http_client_ = cloud_print::PrivetV1HTTPClient::CreateDefault(
+ std::move(http_client));
+
+ privet_http_resolution_.reset();
+
+ return true;
+}
+
+void PrintPreviewHandler::PrivetLocalPrintUpdateClient(
+ std::string print_ticket,
+ std::string capabilities,
+ gfx::Size page_size,
+ std::unique_ptr<cloud_print::PrivetHTTPClient> http_client) {
+ if (!PrivetUpdateClient(std::move(http_client)))
+ return;
+
+ StartPrivetLocalPrint(print_ticket, capabilities, page_size);
+}
+
+void PrintPreviewHandler::StartPrivetLocalPrint(const std::string& print_ticket,
+ const std::string& capabilities,
+ const gfx::Size& page_size) {
+ privet_local_print_operation_ =
+ privet_http_client_->CreateLocalPrintOperation(this);
+
+ privet_local_print_operation_->SetTicket(print_ticket);
+ privet_local_print_operation_->SetCapabilities(capabilities);
+
+ scoped_refptr<base::RefCountedBytes> data;
+ base::string16 title;
+
+ if (!GetPreviewDataAndTitle(&data, &title)) {
+ base::Value http_code_value(-1);
+ web_ui()->CallJavascriptFunctionUnsafe("onPrivetPrintFailed",
+ http_code_value);
+ return;
+ }
+
+ privet_local_print_operation_->SetJobname(base::UTF16ToUTF8(title));
+ privet_local_print_operation_->SetPageSize(page_size);
+ privet_local_print_operation_->SetData(data.get());
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetForProfileIfExists(profile);
+
+ if (signin_manager) {
+ privet_local_print_operation_->SetUsername(
+ signin_manager->GetAuthenticatedAccountInfo().email);
+ }
+
+ privet_local_print_operation_->Start();
+}
+
+
+void PrintPreviewHandler::OnPrivetCapabilities(
+ const base::DictionaryValue* capabilities) {
+ std::string name = privet_capabilities_operation_->GetHTTPClient()->GetName();
+
+ if (!capabilities || capabilities->HasKey(cloud_print::kPrivetKeyError) ||
+ !printer_lister_) {
+ SendPrivetCapabilitiesError(name);
+ return;
+ }
+
+ base::DictionaryValue printer_info;
+ const cloud_print::DeviceDescription* description =
+ printer_lister_->GetDeviceDescription(name);
+
+ if (!description) {
+ SendPrivetCapabilitiesError(name);
+ return;
+ }
+
+ FillPrinterDescription(name, *description, true, &printer_info);
+
+ web_ui()->CallJavascriptFunctionUnsafe("onPrivetCapabilitiesSet",
+ printer_info, *capabilities);
+
+ privet_capabilities_operation_.reset();
+}
+
+void PrintPreviewHandler::SendPrivetCapabilitiesError(
+ const std::string& device_name) {
+ base::Value name_value(device_name);
+ web_ui()->CallJavascriptFunctionUnsafe("failedToGetPrivetPrinterCapabilities",
+ name_value);
+}
+
+void PrintPreviewHandler::PrintToPrivetPrinter(const std::string& device_name,
+ const std::string& ticket,
+ const std::string& capabilities,
+ const gfx::Size& page_size) {
+ CreatePrivetHTTP(
+ device_name,
+ base::Bind(&PrintPreviewHandler::PrivetLocalPrintUpdateClient,
+ weak_factory_.GetWeakPtr(), ticket, capabilities, page_size));
+}
+
+bool PrintPreviewHandler::CreatePrivetHTTP(
+ const std::string& name,
+ const cloud_print::PrivetHTTPAsynchronousFactory::ResultCallback&
+ callback) {
+ const cloud_print::DeviceDescription* device_description =
+ printer_lister_ ? printer_lister_->GetDeviceDescription(name) : NULL;
+
+ if (!device_description) {
+ SendPrivetCapabilitiesError(name);
+ return false;
+ }
+
+ privet_http_factory_ =
+ cloud_print::PrivetHTTPAsynchronousFactory::CreateInstance(
+ Profile::FromWebUI(web_ui())->GetRequestContext());
+ privet_http_resolution_ = privet_http_factory_->CreatePrivetHTTP(name);
+ privet_http_resolution_->Start(device_description->address, callback);
+
+ return true;
+}
+
+void PrintPreviewHandler::OnPrivetPrintingDone(
+ const cloud_print::PrivetLocalPrintOperation* print_operation) {
+ ClosePreviewDialog();
+}
+
+void PrintPreviewHandler::OnPrivetPrintingError(
+ const cloud_print::PrivetLocalPrintOperation* print_operation,
+ int http_code) {
+ base::Value http_code_value(http_code);
+ web_ui()->CallJavascriptFunctionUnsafe("onPrivetPrintFailed",
+ http_code_value);
+}
+
+void PrintPreviewHandler::FillPrinterDescription(
+ const std::string& name,
+ const cloud_print::DeviceDescription& description,
+ bool has_local_printing,
+ base::DictionaryValue* printer_value) {
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+
+ printer_value->SetString("serviceName", name);
+ printer_value->SetString("name", description.name);
+ printer_value->SetBoolean("hasLocalPrinting", has_local_printing);
+ printer_value->SetBoolean(
+ "isUnregistered",
+ description.id.empty() &&
+ command_line->HasSwitch(switches::kEnablePrintPreviewRegisterPromos));
+ printer_value->SetString("cloudID", description.id);
+}
+#endif // BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+
+void PrintPreviewHandler::EnsureExtensionPrinterHandlerSet() {
+ if (extension_printer_handler_)
+ return;
+
+ extension_printer_handler_ =
+ PrinterHandler::CreateForExtensionPrinters(Profile::FromWebUI(web_ui()));
+}
+
+void PrintPreviewHandler::OnGotPrintersForExtension(
+ const base::ListValue& printers,
+ bool done) {
+ web_ui()->CallJavascriptFunctionUnsafe("onExtensionPrintersAdded", printers,
+ base::Value(done));
+}
+
+void PrintPreviewHandler::OnGotExtensionPrinterInfo(
+ const std::string& printer_id,
+ const base::DictionaryValue& printer_info) {
+ if (printer_info.empty()) {
+ web_ui()->CallJavascriptFunctionUnsafe("failedToResolveProvisionalPrinter",
+ base::Value(printer_id));
+ return;
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("onProvisionalPrinterResolved",
+ base::Value(printer_id), printer_info);
+}
+
+void PrintPreviewHandler::OnGotExtensionPrinterCapabilities(
+ const std::string& printer_id,
+ const base::DictionaryValue& capabilities) {
+ if (capabilities.empty()) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "failedToGetExtensionPrinterCapabilities", base::Value(printer_id));
+ return;
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("onExtensionCapabilitiesSet",
+ base::Value(printer_id), capabilities);
+}
+
+void PrintPreviewHandler::OnExtensionPrintResult(bool success,
+ const std::string& status) {
+ if (success) {
+ ClosePreviewDialog();
+ return;
+ }
+
+ // TODO(tbarzic): This function works for extension printers case too, but it
+ // should be renamed to something more generic.
+ web_ui()->CallJavascriptFunctionUnsafe("onPrivetPrintFailed",
+ base::Value(status));
+}
+
+void PrintPreviewHandler::RegisterForGaiaCookieChanges() {
+ DCHECK(!gaia_cookie_manager_service_);
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (switches::IsEnableAccountConsistency() && !profile->IsOffTheRecord()) {
+ gaia_cookie_manager_service_ =
+ GaiaCookieManagerServiceFactory::GetForProfile(profile);
+ if (gaia_cookie_manager_service_)
+ gaia_cookie_manager_service_->AddObserver(this);
+ }
+}
+
+void PrintPreviewHandler::UnregisterForGaiaCookieChanges() {
+ if (gaia_cookie_manager_service_)
+ gaia_cookie_manager_service_->RemoveObserver(this);
+}
+
+void PrintPreviewHandler::SetPdfSavedClosureForTesting(
+ const base::Closure& closure) {
+ pdf_file_saved_closure_ = closure;
+}
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
new file mode 100644
index 00000000000..2166080fc28
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -0,0 +1,410 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINT_PREVIEW_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINT_PREVIEW_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/common/features.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"
+#include "printing/features/features.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+#include "chrome/browser/local_discovery/service_discovery_shared_client.h"
+#include "chrome/browser/printing/cloud_print/privet_local_printer_lister.h"
+#endif
+
+class PrinterHandler;
+class PrintPreviewUI;
+
+namespace base {
+class DictionaryValue;
+class RefCountedBytes;
+}
+
+namespace content {
+class WebContents;
+}
+
+namespace gfx {
+class Size;
+}
+
+namespace printing {
+class PrinterBackendProxy;
+}
+
+// The handler for Javascript messages related to the print preview dialog.
+class PrintPreviewHandler
+ : public content::WebUIMessageHandler,
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+ public cloud_print::PrivetLocalPrinterLister::Delegate,
+ public cloud_print::PrivetLocalPrintOperation::Delegate,
+#endif
+ public ui::SelectFileDialog::Listener,
+ public GaiaCookieManagerService::Observer {
+ public:
+ PrintPreviewHandler();
+ ~PrintPreviewHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // SelectFileDialog::Listener implementation.
+ void FileSelected(const base::FilePath& path,
+ int index,
+ void* params) override;
+ void FileSelectionCanceled(void* params) override;
+
+ // GaiaCookieManagerService::Observer implementation.
+ void OnAddAccountToCookieCompleted(
+ const std::string& account_id,
+ const GoogleServiceAuthError& error) override;
+
+ // Called when print preview failed.
+ void OnPrintPreviewFailed();
+
+#if BUILDFLAG(ENABLE_BASIC_PRINTING)
+ // Called when the user press ctrl+shift+p to display the native system
+ // dialog.
+ void ShowSystemDialog();
+#endif // BUILDFLAG(ENABLE_BASIC_PRINTING)
+
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+ // PrivetLocalPrinterLister::Delegate implementation.
+ void LocalPrinterChanged(
+ const std::string& name,
+ bool has_local_printing,
+ const cloud_print::DeviceDescription& description) override;
+ void LocalPrinterRemoved(const std::string& name) override;
+ void LocalPrinterCacheFlushed() override;
+
+ // PrivetLocalPrintOperation::Delegate implementation.
+ void OnPrivetPrintingDone(
+ const cloud_print::PrivetLocalPrintOperation* print_operation) override;
+ void OnPrivetPrintingError(
+ const cloud_print::PrivetLocalPrintOperation* print_operation,
+ int http_code) override;
+#endif
+
+ int regenerate_preview_request_count() const {
+ return regenerate_preview_request_count_;
+ }
+
+ // Sets |pdf_file_saved_closure_| to |closure|.
+ void SetPdfSavedClosureForTesting(const base::Closure& closure);
+
+ protected:
+ // If |prompt_user| is true, displays a modal dialog, prompting the user to
+ // select a file. Otherwise, just accept |default_path| and uniquify it.
+ // Protected so unit tests can access.
+ virtual void SelectFile(const base::FilePath& default_path, bool prompt_user);
+
+ // Handles printing to PDF. Protected to expose to unit tests.
+ void PrintToPdf();
+
+ // The underlying dialog object. Protected to expose to unit tests.
+ scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
+
+ private:
+ friend class PrintPreviewPdfGeneratedBrowserTest;
+ FRIEND_TEST_ALL_PREFIXES(PrintPreviewPdfGeneratedBrowserTest,
+ MANUAL_DummyTest);
+ class AccessTokenService;
+
+ content::WebContents* preview_web_contents() const;
+
+ PrintPreviewUI* print_preview_ui() const;
+
+ printing::PrinterBackendProxy* printer_backend_proxy();
+
+ // Gets the list of printers. |args| is unused.
+ void HandleGetPrinters(const base::ListValue* args);
+
+ // Starts getting all local privet printers. |args| is unused.
+ void HandleGetPrivetPrinters(const base::ListValue* args);
+
+ // Starts getting all local extension managed printers. |args| is unused.
+ void HandleGetExtensionPrinters(const base::ListValue* args);
+
+ // Grants an extension access to a provisional printer. First element of
+ // |args| is the provisional printer ID.
+ void HandleGrantExtensionPrinterAccess(const base::ListValue* args);
+
+ // Stops getting all local privet printers. |arg| is unused.
+ void HandleStopGetPrivetPrinters(const base::ListValue* args);
+
+ // Asks the initiator renderer to generate a preview. First element of |args|
+ // is a job settings JSON string.
+ void HandleGetPreview(const base::ListValue* args);
+
+ // Gets the job settings from Web UI and initiate printing. First element of
+ // |args| is a job settings JSON string.
+ void HandlePrint(const base::ListValue* args);
+
+ // Handles the request to hide the preview dialog for printing.
+ // |args| is unused.
+ void HandleHidePreview(const base::ListValue* args);
+
+ // Handles the request to cancel the pending print request. |args| is unused.
+ void HandleCancelPendingPrintRequest(const base::ListValue* args);
+
+ // Handles a request to store data that the web ui wishes to persist.
+ // First element of |args| is the data to persist.
+ void HandleSaveAppState(const base::ListValue* args);
+
+ // Gets the printer capabilities. First element of |args| is the printer name.
+ void HandleGetPrinterCapabilities(const base::ListValue* args);
+
+ // Performs printer setup. First element of |args| is the printer name.
+ void HandlePrinterSetup(const base::ListValue* args);
+
+#if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG)
+ // Asks the initiator renderer to show the native print system dialog. |args|
+ // is unused.
+ void HandleShowSystemDialog(const base::ListValue* args);
+#endif
+
+ // Callback for the signin dialog to call once signin is complete.
+ void OnSigninComplete();
+
+ // Brings up a dialog to allow the user to sign into cloud print.
+ // |args| is unused.
+ void HandleSignin(const base::ListValue* args);
+
+ // Generates new token and sends back to UI.
+ void HandleGetAccessToken(const base::ListValue* args);
+
+ // Brings up a web page to allow the user to configure cloud print.
+ // |args| is unused.
+ void HandleManageCloudPrint(const base::ListValue* args);
+
+ // Gathers UMA stats when the print preview dialog is about to close.
+ // |args| is unused.
+ void HandleClosePreviewDialog(const base::ListValue* args);
+
+ // Asks the browser to show the native printer management dialog.
+ // |args| is unused.
+ void HandleManagePrinters(const base::ListValue* args);
+
+ // Asks the browser for several settings that are needed before the first
+ // preview is displayed.
+ void HandleGetInitialSettings(const base::ListValue* args);
+
+ // Reports histogram data for a print preview UI action. |args| should consist
+ // of two elements: the bucket name, and the bucket event.
+ void HandleReportUiEvent(const base::ListValue* args);
+
+ // Forces the opening of a new tab. |args| should consist of one element: the
+ // URL to set the new tab to.
+ //
+ // NOTE: This is needed to open register promo for Cloud Print as a new tab.
+ // Javascript's "window.open" opens a new window popup (since initiated from
+ // async HTTP request) and worse yet, on Windows and Chrome OS, the opened
+ // window opens behind the initiator window.
+ void HandleForceOpenNewTab(const base::ListValue* args);
+
+ void HandleGetPrivetPrinterCapabilities(const base::ListValue* arg);
+
+ // Requests an extension managed printer's capabilities.
+ // |arg| contains the ID of the printer whose capabilities are requested.
+ void HandleGetExtensionPrinterCapabilities(const base::ListValue* args);
+
+ void SendInitialSettings(const std::string& callback_id,
+ const std::string& default_printer);
+
+ // Send OAuth2 access token.
+ void SendAccessToken(const std::string& type,
+ const std::string& access_token);
+
+ // Sends the printer capabilities to the Web UI. |settings_info| contains
+ // printer capabilities information. If |settings_info| is empty, sends
+ // error notification to the Web UI instead.
+ void SendPrinterCapabilities(
+ const std::string& printer_name,
+ std::unique_ptr<base::DictionaryValue> settings_info);
+
+ // Send the result of performing printer setup. |settings_info| contains
+ // printer capabilities.
+ void SendPrinterSetup(const std::string& callback_id,
+ const std::string& printer_name,
+ std::unique_ptr<base::DictionaryValue> settings_info);
+
+ // Send the list of printers to the Web UI.
+ void SetupPrinterList(const printing::PrinterList& printer_list);
+
+ // Send whether cloud print integration should be enabled.
+ void SendCloudPrintEnabled();
+
+ // Send the PDF data to the cloud to print.
+ void SendCloudPrintJob(const base::RefCountedBytes* data);
+
+ // Gets the initiator for the print preview dialog.
+ content::WebContents* GetInitiator() const;
+
+ // Closes the preview dialog.
+ void ClosePreviewDialog();
+
+ // Adds all the recorded stats taken so far to histogram counts.
+ void ReportStats();
+
+ // Clears initiator details for the print preview dialog.
+ void ClearInitiatorDetails();
+
+ // Posts a task to save |data| to pdf at |print_to_pdf_path_|.
+ void PostPrintToPdfTask();
+
+ // Populates |settings| according to the current locale.
+ void GetNumberFormatAndMeasurementSystem(base::DictionaryValue* settings);
+
+ bool GetPreviewDataAndTitle(scoped_refptr<base::RefCountedBytes>* data,
+ base::string16* title) const;
+
+ // Helper for getting a unique file name for SelectFile() without prompting
+ // the user. Just an adaptor for FileSelected().
+ void OnGotUniqueFileName(const base::FilePath& path);
+
+#if defined(USE_CUPS)
+ void SaveCUPSColorSetting(const base::DictionaryValue* settings);
+
+ void ConvertColorSettingToCUPSColorModel(
+ base::DictionaryValue* settings) const;
+#endif
+
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+ void StartPrivetLister(const scoped_refptr<
+ local_discovery::ServiceDiscoverySharedClient>& client);
+ void OnPrivetCapabilities(const base::DictionaryValue* capabilities);
+ void PrivetCapabilitiesUpdateClient(
+ std::unique_ptr<cloud_print::PrivetHTTPClient> http_client);
+ void PrivetLocalPrintUpdateClient(
+ std::string print_ticket,
+ std::string capabilities,
+ gfx::Size page_size,
+ std::unique_ptr<cloud_print::PrivetHTTPClient> http_client);
+ bool PrivetUpdateClient(
+ std::unique_ptr<cloud_print::PrivetHTTPClient> http_client);
+ void StartPrivetLocalPrint(const std::string& print_ticket,
+ const std::string& capabilities,
+ const gfx::Size& page_size);
+ void SendPrivetCapabilitiesError(const std::string& id);
+ void PrintToPrivetPrinter(const std::string& printer_name,
+ const std::string& print_ticket,
+ const std::string& capabilities,
+ const gfx::Size& page_size);
+ bool CreatePrivetHTTP(
+ const std::string& name,
+ const cloud_print::PrivetHTTPAsynchronousFactory::ResultCallback&
+ callback);
+ void FillPrinterDescription(
+ const std::string& name,
+ const cloud_print::DeviceDescription& description,
+ bool has_local_printing,
+ base::DictionaryValue* printer_value);
+#endif
+
+ // Lazily creates |extension_printer_handler_| that can be used to handle
+ // extension printers requests.
+ void EnsureExtensionPrinterHandlerSet();
+
+ // Called when a list of printers is reported by an extension.
+ // |printers|: The list of printers managed by the extension.
+ // |done|: Whether all the extensions have reported the list of printers
+ // they manage.
+ void OnGotPrintersForExtension(const base::ListValue& printers, bool done);
+
+ // Called when an extension reports information requested for a provisional
+ // printer.
+ // |printer_id|: The provisional printer id.
+ // |printer_info|: The data reported by the extension.
+ void OnGotExtensionPrinterInfo(const std::string& printer_id,
+ const base::DictionaryValue& printer_info);
+
+ // Called when an extension reports the set of print capabilites for a
+ // printer.
+ // |printer_id|: The id of the printer whose capabilities are reported.
+ // |capabilities|: The printer capabilities.
+ void OnGotExtensionPrinterCapabilities(
+ const std::string& printer_id,
+ const base::DictionaryValue& capabilities);
+
+ // Called when an extension print job is completed.
+ // |success|: Whether the job succeeded.
+ // |status|: The returned print job status. Useful for reporting a specific
+ // error.
+ void OnExtensionPrintResult(bool success, const std::string& status);
+
+ // Register/unregister from notifications of changes done to the GAIA
+ // cookie.
+ void RegisterForGaiaCookieChanges();
+ void UnregisterForGaiaCookieChanges();
+
+ // A count of how many requests received to regenerate preview data.
+ // Initialized to 0 then incremented and emitted to a histogram.
+ int regenerate_preview_request_count_;
+
+ // A count of how many requests received to show manage printers dialog.
+ int manage_printers_dialog_request_count_;
+ int manage_cloud_printers_dialog_request_count_;
+
+ // Whether we have already logged a failed print preview.
+ bool reported_failed_preview_;
+
+ // Whether we have already logged the number of printers this session.
+ bool has_logged_printers_count_;
+
+ // Holds the path to the print to pdf request. It is empty if no such request
+ // exists.
+ base::FilePath print_to_pdf_path_;
+
+ // Holds token service to get OAuth2 access tokens.
+ std::unique_ptr<AccessTokenService> token_service_;
+
+ // Pointer to cookie manager service so that print preview can listen for GAIA
+ // cookie changes.
+ GaiaCookieManagerService* gaia_cookie_manager_service_;
+
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+ scoped_refptr<local_discovery::ServiceDiscoverySharedClient>
+ service_discovery_client_;
+ std::unique_ptr<cloud_print::PrivetLocalPrinterLister> printer_lister_;
+
+ std::unique_ptr<cloud_print::PrivetHTTPAsynchronousFactory>
+ privet_http_factory_;
+ std::unique_ptr<cloud_print::PrivetHTTPResolution> privet_http_resolution_;
+ std::unique_ptr<cloud_print::PrivetV1HTTPClient> privet_http_client_;
+ std::unique_ptr<cloud_print::PrivetJSONOperation>
+ privet_capabilities_operation_;
+ std::unique_ptr<cloud_print::PrivetLocalPrintOperation>
+ privet_local_print_operation_;
+#endif
+
+ // Handles requests for extension printers. Created lazily by calling
+ // |EnsureExtensionPrinterHandlerSet|.
+ std::unique_ptr<PrinterHandler> extension_printer_handler_;
+
+ // Notifies tests that want to know if the PDF has been saved. This doesn't
+ // notify the test if it was a successful save, only that it was attempted.
+ base::Closure pdf_file_saved_closure_;
+
+ // Proxy for calls to the print backend. Lazily initialized since web_ui() is
+ // not available at construction time.
+ std::unique_ptr<printing::PrinterBackendProxy> printer_backend_proxy_;
+
+ base::WeakPtrFactory<PrintPreviewHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrintPreviewHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINT_PREVIEW_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_win_unittest.cc b/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_win_unittest.cc
new file mode 100644
index 00000000000..c7b6e5c29e5
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_handler_win_unittest.cc
@@ -0,0 +1,177 @@
+// 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/print_preview/print_preview_handler.h"
+
+#include <commdlg.h>
+#include <windows.h>
+
+#include "base/run_loop.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/browser/printing/print_preview_dialog_controller.h"
+#include "chrome/browser/printing/print_preview_test.h"
+#include "chrome/browser/printing/print_view_manager.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
+#include "chrome/test/base/browser_with_test_window_test.h"
+#include "components/web_modal/web_contents_modal_dialog_manager.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/shell_dialogs/select_file_dialog_win.h"
+
+using content::WebContents;
+
+namespace {
+
+class FakePrintPreviewHandler;
+bool GetOpenFileNameImpl(OPENFILENAME* ofn);
+bool GetSaveFileNameImpl(FakePrintPreviewHandler* handler, OPENFILENAME* ofn);
+
+class FakePrintPreviewHandler : public PrintPreviewHandler {
+ public:
+ explicit FakePrintPreviewHandler(content::WebUI* web_ui)
+ : init_called_(false), save_failed_(false) {
+ set_web_ui(web_ui);
+ }
+
+ void FileSelected(const base::FilePath& path,
+ int index,
+ void* params) override {
+ // Since we always cancel the dialog as soon as it is initialized, this
+ // should never be called.
+ NOTREACHED();
+ }
+
+ void FileSelectionCanceled(void* params) override {
+ save_failed_ = true;
+ run_loop_.Quit();
+ }
+
+ void StartPrintToPdf() {
+ PrintToPdf();
+ run_loop_.Run();
+ }
+
+ bool save_failed() const { return save_failed_; }
+
+ bool init_called() const { return init_called_; }
+
+ void set_init_called() { init_called_ = true; }
+
+ private:
+ // Simplified version of select file to avoid checking preferences and sticky
+ // settings in the test
+ void SelectFile(const base::FilePath& default_filename,
+ bool prompt_user) override {
+ ui::SelectFileDialog::FileTypeInfo file_type_info;
+ file_type_info.extensions.resize(1);
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pdf"));
+ select_file_dialog_ = ui::CreateWinSelectFileDialog(
+ this, nullptr /*policy already checked*/,
+ base::Bind(GetOpenFileNameImpl), base::Bind(GetSaveFileNameImpl, this));
+ select_file_dialog_->SelectFile(
+ ui::SelectFileDialog::SELECT_SAVEAS_FILE, base::string16(),
+ default_filename, &file_type_info, 0, base::FilePath::StringType(),
+ platform_util::GetTopLevel(web_ui()->GetWebContents()->GetNativeView()),
+ nullptr);
+ }
+
+ bool init_called_;
+ bool save_failed_;
+ base::RunLoop run_loop_;
+};
+
+// Hook function to cancel the dialog when it is successfully initialized.
+UINT_PTR CALLBACK PrintPreviewHandlerTestHookFunction(HWND hdlg,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam) {
+ if (message != WM_INITDIALOG)
+ return 0;
+ OPENFILENAME* ofn = reinterpret_cast<OPENFILENAME*>(lparam);
+ FakePrintPreviewHandler* handler =
+ reinterpret_cast<FakePrintPreviewHandler*>(ofn->lCustData);
+ handler->set_init_called();
+ PostMessage(GetParent(hdlg), WM_COMMAND, MAKEWPARAM(IDCANCEL, 0), 0);
+ return 1;
+}
+
+bool GetOpenFileNameImpl(OPENFILENAME* ofn) {
+ return ::GetOpenFileName(ofn);
+}
+
+bool GetSaveFileNameImpl(FakePrintPreviewHandler* handler, OPENFILENAME* ofn) {
+ // Modify ofn so that the hook function will be called.
+ ofn->Flags |= OFN_ENABLEHOOK;
+ ofn->lpfnHook = PrintPreviewHandlerTestHookFunction;
+ ofn->lCustData = reinterpret_cast<LPARAM>(handler);
+ return ::GetSaveFileName(ofn);
+}
+
+} // namespace
+
+class PrintPreviewHandlerTest : public PrintPreviewTest {
+ public:
+ PrintPreviewHandlerTest() : preview_ui_(nullptr) {}
+ ~PrintPreviewHandlerTest() override {}
+
+ void SetUp() override {
+ PrintPreviewTest::SetUp();
+
+ // Create a new tab
+ chrome::NewTab(browser());
+ }
+
+ protected:
+ void CreateUIAndHandler() {
+ WebContents* initiator =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(initiator);
+
+ // Get print preview UI
+ printing::PrintPreviewDialogController* controller =
+ printing::PrintPreviewDialogController::GetInstance();
+ ASSERT_TRUE(controller);
+ printing::PrintViewManager* print_view_manager =
+ printing::PrintViewManager::FromWebContents(initiator);
+ print_view_manager->PrintPreviewNow(initiator->GetMainFrame(), false);
+ WebContents* preview_dialog =
+ controller->GetOrCreatePreviewDialog(initiator);
+ ASSERT_TRUE(preview_dialog);
+ preview_ui_ = static_cast<PrintPreviewUI*>(
+ preview_dialog->GetWebUI()->GetController());
+ ASSERT_TRUE(preview_ui_);
+
+ preview_handler_ =
+ base::MakeUnique<FakePrintPreviewHandler>(preview_dialog->GetWebUI());
+ }
+
+ std::unique_ptr<FakePrintPreviewHandler> preview_handler_;
+ PrintPreviewUI* preview_ui_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrintPreviewHandlerTest);
+};
+
+TEST_F(PrintPreviewHandlerTest, TestSaveAsPdf) {
+ CreateUIAndHandler();
+ preview_ui_->SetInitiatorTitle(L"111111111111111111111.html");
+ preview_handler_->StartPrintToPdf();
+ EXPECT_TRUE(preview_handler_->init_called());
+ EXPECT_TRUE(preview_handler_->save_failed());
+}
+
+TEST_F(PrintPreviewHandlerTest, TestSaveAsPdfLongFileName) {
+ CreateUIAndHandler();
+ preview_ui_->SetInitiatorTitle(
+ L"11111111111111111111111111111111111111111111111111111111111111111111111"
+ L"11111111111111111111111111111111111111111111111111111111111111111111111"
+ L"11111111111111111111111111111111111111111111111111111111111111111111111"
+ L"11111111111111111111111111111111111111111111111111111111111111111111111"
+ L"1111111111111111111111111111111111111111111111111.html");
+ preview_handler_->StartPrintToPdf();
+ EXPECT_TRUE(preview_handler_->init_called());
+ EXPECT_TRUE(preview_handler_->save_failed());
+}
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
new file mode 100644
index 00000000000..e675add9cbe
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -0,0 +1,697 @@
+// 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/ui/webui/print_preview/print_preview_ui.h"
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "base/feature_list.h"
+#include "base/id_map.h"
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/metrics/histogram_macros.h"
+#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/synchronization/lock.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/printing/background_printing_manager.h"
+#include "chrome/browser/printing/print_preview_data_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/metrics_handler.h"
+#include "chrome/browser/ui/webui/print_preview/print_preview_handler.h"
+#include "chrome/browser/ui/webui/theme_source.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/printing/common/print_messages.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "extensions/common/constants.h"
+#include "printing/features/features.h"
+#include "printing/page_size_margins.h"
+#include "printing/print_job_constants.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/web_dialogs/web_dialog_delegate.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+#if defined(OS_CHROMEOS)
+#include "base/command_line.h"
+#include "chrome/common/chrome_switches.h"
+#endif
+
+using content::WebContents;
+using printing::PageSizeMargins;
+
+namespace {
+
+#if defined(OS_MACOSX)
+// U+0028 U+21E7 U+2318 U+0050 U+0029 in UTF8
+const char kBasicPrintShortcut[] = "\x28\xE2\x8c\xA5\xE2\x8C\x98\x50\x29";
+#elif !defined(OS_CHROMEOS)
+const char kBasicPrintShortcut[] = "(Ctrl+Shift+P)";
+#endif
+
+PrintPreviewUI::TestingDelegate* g_testing_delegate = nullptr;
+
+// Thread-safe wrapper around a std::map to keep track of mappings from
+// PrintPreviewUI IDs to most recent print preview request IDs.
+class PrintPreviewRequestIdMapWithLock {
+ public:
+ PrintPreviewRequestIdMapWithLock() {}
+ ~PrintPreviewRequestIdMapWithLock() {}
+
+ // Gets the value for |preview_id|.
+ // Returns true and sets |out_value| on success.
+ bool Get(int32_t preview_id, int* out_value) {
+ base::AutoLock lock(lock_);
+ PrintPreviewRequestIdMap::const_iterator it = map_.find(preview_id);
+ if (it == map_.end())
+ return false;
+ *out_value = it->second;
+ return true;
+ }
+
+ // Sets the |value| for |preview_id|.
+ void Set(int32_t preview_id, int value) {
+ base::AutoLock lock(lock_);
+ map_[preview_id] = value;
+ }
+
+ // Erases the entry for |preview_id|.
+ void Erase(int32_t preview_id) {
+ base::AutoLock lock(lock_);
+ map_.erase(preview_id);
+ }
+
+ private:
+ // Mapping from PrintPreviewUI ID to print preview request ID.
+ typedef std::map<int, int> PrintPreviewRequestIdMap;
+
+ PrintPreviewRequestIdMap map_;
+ base::Lock lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrintPreviewRequestIdMapWithLock);
+};
+
+// Written to on the UI thread, read from any thread.
+base::LazyInstance<PrintPreviewRequestIdMapWithLock>::DestructorAtExit
+ g_print_preview_request_id_map = LAZY_INSTANCE_INITIALIZER;
+
+// PrintPreviewUI IDMap used to avoid exposing raw pointer addresses to WebUI.
+// Only accessed on the UI thread.
+base::LazyInstance<IDMap<PrintPreviewUI*>>::DestructorAtExit
+ g_print_preview_ui_id_map = LAZY_INSTANCE_INITIALIZER;
+
+// PrintPreviewUI serves data for chrome://print requests.
+//
+// The format for requesting PDF data is as follows:
+// chrome://print/<PrintPreviewUIID>/<PageIndex>/print.pdf
+//
+// Parameters (< > required):
+// <PrintPreviewUIID> = PrintPreview UI ID
+// <PageIndex> = Page index is zero-based or
+// |printing::COMPLETE_PREVIEW_DOCUMENT_INDEX| to represent
+// a print ready PDF.
+//
+// Example:
+// chrome://print/123/10/print.pdf
+//
+// Requests to chrome://print with paths not ending in /print.pdf are used
+// to return the markup or other resources for the print preview page itself.
+bool HandleRequestCallback(
+ const std::string& path,
+ const content::WebUIDataSource::GotDataCallback& callback) {
+ // ChromeWebUIDataSource handles most requests except for the print preview
+ // data.
+ std::string file_path = path.substr(0, path.find_first_of('?'));
+ if (!base::EndsWith(file_path, "/print.pdf", base::CompareCase::SENSITIVE))
+ return false;
+
+ // Print Preview data.
+ scoped_refptr<base::RefCountedBytes> data;
+ std::vector<std::string> url_substr = base::SplitString(
+ path, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+ int preview_ui_id = -1;
+ int page_index = 0;
+ if (url_substr.size() == 3 &&
+ base::StringToInt(url_substr[0], &preview_ui_id),
+ base::StringToInt(url_substr[1], &page_index) &&
+ preview_ui_id >= 0) {
+ PrintPreviewDataService::GetInstance()->GetDataEntry(
+ preview_ui_id, page_index, &data);
+ }
+ if (data.get()) {
+ callback.Run(data.get());
+ return true;
+ }
+ // Invalid request.
+ scoped_refptr<base::RefCountedBytes> empty_bytes(new base::RefCountedBytes);
+ callback.Run(empty_bytes.get());
+ return true;
+}
+
+content::WebUIDataSource* CreatePrintPreviewUISource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIPrintHost);
+#if defined(OS_CHROMEOS)
+ source->AddLocalizedString("title",
+ IDS_PRINT_PREVIEW_GOOGLE_CLOUD_PRINT_TITLE);
+#else
+ source->AddLocalizedString("title", IDS_PRINT_PREVIEW_TITLE);
+#endif
+ source->AddLocalizedString("loading", IDS_PRINT_PREVIEW_LOADING);
+ source->AddLocalizedString("noPlugin", IDS_PRINT_PREVIEW_NO_PLUGIN);
+ source->AddLocalizedString("launchNativeDialog",
+ IDS_PRINT_PREVIEW_NATIVE_DIALOG);
+ source->AddLocalizedString("previewFailed", IDS_PRINT_PREVIEW_FAILED);
+ source->AddLocalizedString("invalidPrinterSettings",
+ IDS_PRINT_INVALID_PRINTER_SETTINGS);
+ source->AddLocalizedString("printButton", IDS_PRINT_PREVIEW_PRINT_BUTTON);
+ source->AddLocalizedString("saveButton", IDS_PRINT_PREVIEW_SAVE_BUTTON);
+ source->AddLocalizedString("printing", IDS_PRINT_PREVIEW_PRINTING);
+ source->AddLocalizedString("saving", IDS_PRINT_PREVIEW_SAVING);
+ source->AddLocalizedString("printingToPDFInProgress",
+ IDS_PRINT_PREVIEW_PRINTING_TO_PDF_IN_PROGRESS);
+#if defined(OS_MACOSX)
+ source->AddLocalizedString("openingPDFInPreview",
+ IDS_PRINT_PREVIEW_OPENING_PDF_IN_PREVIEW);
+#endif
+ source->AddLocalizedString("destinationLabel",
+ IDS_PRINT_PREVIEW_DESTINATION_LABEL);
+ source->AddLocalizedString("copiesLabel", IDS_PRINT_PREVIEW_COPIES_LABEL);
+ source->AddLocalizedString("scalingLabel", IDS_PRINT_PREVIEW_SCALING_LABEL);
+ source->AddLocalizedString("examplePageRangeText",
+ IDS_PRINT_PREVIEW_EXAMPLE_PAGE_RANGE_TEXT);
+ source->AddLocalizedString("layoutLabel", IDS_PRINT_PREVIEW_LAYOUT_LABEL);
+ source->AddLocalizedString("optionAllPages",
+ IDS_PRINT_PREVIEW_OPTION_ALL_PAGES);
+ source->AddLocalizedString("optionBw", IDS_PRINT_PREVIEW_OPTION_BW);
+ source->AddLocalizedString("optionCollate", IDS_PRINT_PREVIEW_OPTION_COLLATE);
+ source->AddLocalizedString("optionColor", IDS_PRINT_PREVIEW_OPTION_COLOR);
+ source->AddLocalizedString("optionLandscape",
+ IDS_PRINT_PREVIEW_OPTION_LANDSCAPE);
+ source->AddLocalizedString("optionPortrait",
+ IDS_PRINT_PREVIEW_OPTION_PORTRAIT);
+ source->AddLocalizedString("optionTwoSided",
+ IDS_PRINT_PREVIEW_OPTION_TWO_SIDED);
+ source->AddLocalizedString("pagesLabel", IDS_PRINT_PREVIEW_PAGES_LABEL);
+ source->AddLocalizedString("pageRangeTextBox",
+ IDS_PRINT_PREVIEW_PAGE_RANGE_TEXT);
+ source->AddLocalizedString("pageRangeRadio",
+ IDS_PRINT_PREVIEW_PAGE_RANGE_RADIO);
+ source->AddLocalizedString("printToPDF", IDS_PRINT_PREVIEW_PRINT_TO_PDF);
+ source->AddLocalizedString("printPreviewSummaryFormatShort",
+ IDS_PRINT_PREVIEW_SUMMARY_FORMAT_SHORT);
+ source->AddLocalizedString("printPreviewSummaryFormatLong",
+ IDS_PRINT_PREVIEW_SUMMARY_FORMAT_LONG);
+ source->AddLocalizedString("printPreviewSheetsLabelSingular",
+ IDS_PRINT_PREVIEW_SHEETS_LABEL_SINGULAR);
+ source->AddLocalizedString("printPreviewSheetsLabelPlural",
+ IDS_PRINT_PREVIEW_SHEETS_LABEL_PLURAL);
+ source->AddLocalizedString("printPreviewPageLabelSingular",
+ IDS_PRINT_PREVIEW_PAGE_LABEL_SINGULAR);
+ source->AddLocalizedString("printPreviewPageLabelPlural",
+ IDS_PRINT_PREVIEW_PAGE_LABEL_PLURAL);
+ source->AddLocalizedString("selectButton",
+ IDS_PRINT_PREVIEW_BUTTON_SELECT);
+ source->AddLocalizedString("goBackButton",
+ IDS_PRINT_PREVIEW_BUTTON_GO_BACK);
+ source->AddLocalizedString(
+ "resolveExtensionUSBPermissionMessage",
+ IDS_PRINT_PREVIEW_RESOLVE_EXTENSION_USB_PERMISSION_MESSAGE);
+ source->AddLocalizedString(
+ "resolveExtensionUSBErrorMessage",
+ IDS_PRINT_PREVIEW_RESOLVE_EXTENSION_USB_ERROR_MESSAGE);
+#if defined(OS_CHROMEOS)
+ source->AddLocalizedString("configuringInProgressText",
+ IDS_PRINT_CONFIGURING_IN_PROGRESS_TEXT);
+ source->AddLocalizedString("configuringFailedText",
+ IDS_PRINT_CONFIGURING_FAILED_TEXT);
+#else
+ const base::string16 shortcut_text(base::UTF8ToUTF16(kBasicPrintShortcut));
+ source->AddString(
+ "systemDialogOption",
+ l10n_util::GetStringFUTF16(
+ IDS_PRINT_PREVIEW_SYSTEM_DIALOG_OPTION,
+ shortcut_text));
+#endif
+#if defined(OS_MACOSX)
+ source->AddLocalizedString("openPdfInPreviewOption",
+ IDS_PRINT_PREVIEW_OPEN_PDF_IN_PREVIEW_APP);
+#endif
+ source->AddString(
+ "printWithCloudPrintWait",
+ l10n_util::GetStringFUTF16(
+ IDS_PRINT_PREVIEW_PRINT_WITH_CLOUD_PRINT_WAIT,
+ l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT)));
+ source->AddString(
+ "noDestsPromoLearnMoreUrl",
+ chrome::kCloudPrintNoDestinationsLearnMoreURL);
+ source->AddLocalizedString("pageRangeLimitInstruction",
+ IDS_PRINT_PREVIEW_PAGE_RANGE_LIMIT_INSTRUCTION);
+ source->AddLocalizedString(
+ "pageRangeLimitInstructionWithValue",
+ IDS_PRINT_PREVIEW_PAGE_RANGE_LIMIT_INSTRUCTION_WITH_VALUE);
+ source->AddLocalizedString("pageRangeSyntaxInstruction",
+ IDS_PRINT_PREVIEW_PAGE_RANGE_SYNTAX_INSTRUCTION);
+ source->AddLocalizedString("copiesInstruction",
+ IDS_PRINT_PREVIEW_COPIES_INSTRUCTION);
+ source->AddLocalizedString("scalingInstruction",
+ IDS_PRINT_PREVIEW_SCALING_INSTRUCTION);
+ source->AddLocalizedString("printPagesLabel",
+ IDS_PRINT_PREVIEW_PRINT_PAGES_LABEL);
+ source->AddLocalizedString("optionsLabel", IDS_PRINT_PREVIEW_OPTIONS_LABEL);
+ source->AddLocalizedString("optionHeaderFooter",
+ IDS_PRINT_PREVIEW_OPTION_HEADER_FOOTER);
+ source->AddLocalizedString("optionFitToPage",
+ IDS_PRINT_PREVIEW_OPTION_FIT_TO_PAGE);
+ source->AddLocalizedString(
+ "optionBackgroundColorsAndImages",
+ IDS_PRINT_PREVIEW_OPTION_BACKGROUND_COLORS_AND_IMAGES);
+ source->AddLocalizedString("optionSelectionOnly",
+ IDS_PRINT_PREVIEW_OPTION_SELECTION_ONLY);
+ source->AddLocalizedString("optionRasterize",
+ IDS_PRINT_PREVIEW_OPTION_RASTERIZE);
+ source->AddLocalizedString("marginsLabel", IDS_PRINT_PREVIEW_MARGINS_LABEL);
+ source->AddLocalizedString("defaultMargins",
+ IDS_PRINT_PREVIEW_DEFAULT_MARGINS);
+ source->AddLocalizedString("noMargins", IDS_PRINT_PREVIEW_NO_MARGINS);
+ source->AddLocalizedString("customMargins", IDS_PRINT_PREVIEW_CUSTOM_MARGINS);
+ source->AddLocalizedString("minimumMargins",
+ IDS_PRINT_PREVIEW_MINIMUM_MARGINS);
+ source->AddLocalizedString("top", IDS_PRINT_PREVIEW_TOP_MARGIN_LABEL);
+ source->AddLocalizedString("bottom", IDS_PRINT_PREVIEW_BOTTOM_MARGIN_LABEL);
+ source->AddLocalizedString("left", IDS_PRINT_PREVIEW_LEFT_MARGIN_LABEL);
+ source->AddLocalizedString("right", IDS_PRINT_PREVIEW_RIGHT_MARGIN_LABEL);
+ source->AddLocalizedString("mediaSizeLabel",
+ IDS_PRINT_PREVIEW_MEDIA_SIZE_LABEL);
+ source->AddLocalizedString("dpiLabel", IDS_PRINT_PREVIEW_DPI_LABEL);
+ source->AddLocalizedString("dpiItemLabel", IDS_PRINT_PREVIEW_DPI_ITEM_LABEL);
+ source->AddLocalizedString("nonIsotropicDpiItemLabel",
+ IDS_PRINT_PREVIEW_NON_ISOTROPIC_DPI_ITEM_LABEL);
+ source->AddLocalizedString("destinationSearchTitle",
+ IDS_PRINT_PREVIEW_DESTINATION_SEARCH_TITLE);
+ source->AddLocalizedString("accountSelectTitle",
+ IDS_PRINT_PREVIEW_ACCOUNT_SELECT_TITLE);
+ source->AddLocalizedString("addAccountTitle",
+ IDS_PRINT_PREVIEW_ADD_ACCOUNT_TITLE);
+ source->AddLocalizedString("cloudPrintPromotion",
+ IDS_PRINT_PREVIEW_CLOUD_PRINT_PROMOTION);
+ source->AddLocalizedString("searchBoxPlaceholder",
+ IDS_PRINT_PREVIEW_SEARCH_BOX_PLACEHOLDER);
+ source->AddLocalizedString("noDestinationsMessage",
+ IDS_PRINT_PREVIEW_NO_DESTINATIONS_MESSAGE);
+ source->AddLocalizedString("showAllButtonText",
+ IDS_PRINT_PREVIEW_SHOW_ALL_BUTTON_TEXT);
+ source->AddLocalizedString("destinationCount",
+ IDS_PRINT_PREVIEW_DESTINATION_COUNT);
+ source->AddLocalizedString("recentDestinationsTitle",
+ IDS_PRINT_PREVIEW_RECENT_DESTINATIONS_TITLE);
+ source->AddLocalizedString("localDestinationsTitle",
+ IDS_PRINT_PREVIEW_LOCAL_DESTINATIONS_TITLE);
+ source->AddLocalizedString("cloudDestinationsTitle",
+ IDS_PRINT_PREVIEW_CLOUD_DESTINATIONS_TITLE);
+ source->AddLocalizedString("manage", IDS_PRINT_PREVIEW_MANAGE);
+ source->AddLocalizedString("setupCloudPrinters",
+ IDS_PRINT_PREVIEW_SETUP_CLOUD_PRINTERS);
+ source->AddLocalizedString("changeDestination",
+ IDS_PRINT_PREVIEW_CHANGE_DESTINATION);
+ source->AddLocalizedString("offlineForYear",
+ IDS_PRINT_PREVIEW_OFFLINE_FOR_YEAR);
+ source->AddLocalizedString("offlineForMonth",
+ IDS_PRINT_PREVIEW_OFFLINE_FOR_MONTH);
+ source->AddLocalizedString("offlineForWeek",
+ IDS_PRINT_PREVIEW_OFFLINE_FOR_WEEK);
+ source->AddLocalizedString("offline", IDS_PRINT_PREVIEW_OFFLINE);
+ source->AddLocalizedString("noDestsPromoTitle",
+ IDS_PRINT_PREVIEW_NO_DESTS_PROMO_TITLE);
+ source->AddLocalizedString("noDestsPromoBody",
+ IDS_PRINT_PREVIEW_NO_DESTS_PROMO_BODY);
+ source->AddLocalizedString("noDestsPromoGcpDesc",
+ IDS_PRINT_PREVIEW_NO_DESTS_GCP_DESC);
+ source->AddLocalizedString("learnMore",
+ IDS_LEARN_MORE);
+ source->AddLocalizedString(
+ "noDestsPromoAddPrinterButtonLabel",
+ IDS_PRINT_PREVIEW_NO_DESTS_PROMO_ADD_PRINTER_BUTTON_LABEL);
+ source->AddLocalizedString("noDestsPromoNotNowButtonLabel", IDS_NOT_NOW);
+ source->AddLocalizedString("couldNotPrint",
+ IDS_PRINT_PREVIEW_COULD_NOT_PRINT);
+ source->AddLocalizedString("registerPromoButtonText",
+ IDS_PRINT_PREVIEW_REGISTER_PROMO_BUTTON_TEXT);
+ source->AddLocalizedString(
+ "extensionDestinationIconTooltip",
+ IDS_PRINT_PREVIEW_EXTENSION_DESTINATION_ICON_TOOLTIP);
+ source->AddLocalizedString(
+ "advancedSettingsSearchBoxPlaceholder",
+ IDS_PRINT_PREVIEW_ADVANCED_SETTINGS_SEARCH_BOX_PLACEHOLDER);
+ source->AddLocalizedString("advancedSettingsDialogTitle",
+ IDS_PRINT_PREVIEW_ADVANCED_SETTINGS_DIALOG_TITLE);
+ source->AddLocalizedString(
+ "noAdvancedSettingsMatchSearchHint",
+ IDS_PRINT_PREVIEW_NO_ADVANCED_SETTINGS_MATCH_SEARCH_HINT);
+ source->AddLocalizedString(
+ "advancedSettingsDialogConfirm",
+ IDS_PRINT_PREVIEW_ADVANCED_SETTINGS_DIALOG_CONFIRM);
+ source->AddLocalizedString("cancel", IDS_CANCEL);
+ source->AddLocalizedString("advancedOptionsLabel",
+ IDS_PRINT_PREVIEW_ADVANCED_OPTIONS_LABEL);
+ source->AddLocalizedString("showAdvancedOptions",
+ IDS_PRINT_PREVIEW_SHOW_ADVANCED_OPTIONS);
+
+ source->AddLocalizedString("accept", IDS_PRINT_PREVIEW_ACCEPT_INVITE);
+ source->AddLocalizedString(
+ "acceptForGroup", IDS_PRINT_PREVIEW_ACCEPT_GROUP_INVITE);
+ source->AddLocalizedString("reject", IDS_PRINT_PREVIEW_REJECT_INVITE);
+ source->AddLocalizedString(
+ "groupPrinterSharingInviteText", IDS_PRINT_PREVIEW_GROUP_INVITE_TEXT);
+ source->AddLocalizedString(
+ "printerSharingInviteText", IDS_PRINT_PREVIEW_INVITE_TEXT);
+
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("print_preview.js", IDR_PRINT_PREVIEW_JS);
+ source->AddResourcePath("pdf_preview.html",
+ IDR_PRINT_PREVIEW_PDF_PREVIEW_HTML);
+ source->AddResourcePath("images/printer.png",
+ IDR_PRINT_PREVIEW_IMAGES_PRINTER);
+ source->AddResourcePath("images/printer_shared.png",
+ IDR_PRINT_PREVIEW_IMAGES_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);
+ source->AddResourcePath("images/mobile.png", IDR_PRINT_PREVIEW_IMAGES_MOBILE);
+ source->AddResourcePath("images/mobile_shared.png",
+ IDR_PRINT_PREVIEW_IMAGES_MOBILE_SHARED);
+ source->SetDefaultResource(IDR_PRINT_PREVIEW_HTML);
+ source->SetRequestFilter(base::Bind(&HandleRequestCallback));
+ source->OverrideContentSecurityPolicyScriptSrc(
+ base::StringPrintf("script-src chrome://resources 'self' 'unsafe-eval' "
+ "chrome-extension://%s;",
+ extension_misc::kPdfExtensionId));
+ source->OverrideContentSecurityPolicyChildSrc("child-src 'self';");
+ source->DisableDenyXFrameOptions();
+ source->OverrideContentSecurityPolicyObjectSrc("object-src 'self';");
+ source->AddLocalizedString("moreOptionsLabel", IDS_MORE_OPTIONS_LABEL);
+ source->AddLocalizedString("lessOptionsLabel", IDS_LESS_OPTIONS_LABEL);
+
+#if !defined(OS_MACOSX) && !defined(OS_WIN)
+ bool print_pdf_as_image_enabled = base::FeatureList::IsEnabled(
+ features::kPrintPdfAsImage);
+ source->AddBoolean("printPdfAsImageEnabled", print_pdf_as_image_enabled);
+#else
+ source->AddBoolean("printPdfAsImageEnabled", false);
+#endif
+#if defined(OS_CHROMEOS)
+ bool cups_and_md_settings_enabled =
+ !base::CommandLine::ForCurrentProcess()->HasSwitch(
+ ::switches::kDisableNativeCups);
+ source->AddBoolean("showLocalManageButton", cups_and_md_settings_enabled);
+#else
+ source->AddBoolean("showLocalManageButton", true);
+#endif
+ return source;
+}
+
+} // namespace
+
+PrintPreviewUI::PrintPreviewUI(content::WebUI* web_ui)
+ : ConstrainedWebDialogUI(web_ui),
+ initial_preview_start_time_(base::TimeTicks::Now()),
+ id_(g_print_preview_ui_id_map.Get().Add(this)),
+ handler_(nullptr),
+ source_is_modifiable_(true),
+ source_has_selection_(false),
+ print_selection_only_(false),
+ dialog_closed_(false) {
+ // Set up the chrome://print/ data source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreatePrintPreviewUISource());
+
+ // Set up the chrome://theme/ source.
+ content::URLDataSource::Add(profile, new ThemeSource(profile));
+
+ auto handler = base::MakeUnique<PrintPreviewHandler>();
+ handler_ = handler.get();
+ web_ui->AddMessageHandler(std::move(handler));
+ web_ui->AddMessageHandler(base::MakeUnique<MetricsHandler>());
+
+ g_print_preview_request_id_map.Get().Set(id_, -1);
+}
+
+PrintPreviewUI::~PrintPreviewUI() {
+ PrintPreviewDataService::GetInstance()->RemoveEntry(id_);
+ g_print_preview_request_id_map.Get().Erase(id_);
+ g_print_preview_ui_id_map.Get().Remove(id_);
+}
+
+void PrintPreviewUI::GetPrintPreviewDataForIndex(
+ int index,
+ scoped_refptr<base::RefCountedBytes>* data) const {
+ PrintPreviewDataService::GetInstance()->GetDataEntry(id_, index, data);
+}
+
+void PrintPreviewUI::SetPrintPreviewDataForIndex(
+ int index,
+ scoped_refptr<base::RefCountedBytes> data) {
+ PrintPreviewDataService::GetInstance()->SetDataEntry(id_, index,
+ std::move(data));
+}
+
+void PrintPreviewUI::ClearAllPreviewData() {
+ PrintPreviewDataService::GetInstance()->RemoveEntry(id_);
+}
+
+int PrintPreviewUI::GetAvailableDraftPageCount() const {
+ return PrintPreviewDataService::GetInstance()->GetAvailableDraftPageCount(
+ id_);
+}
+
+void PrintPreviewUI::SetInitiatorTitle(
+ const base::string16& job_title) {
+ initiator_title_ = job_title;
+}
+
+// static
+void PrintPreviewUI::SetInitialParams(
+ content::WebContents* print_preview_dialog,
+ const PrintHostMsg_RequestPrintPreview_Params& params) {
+ if (!print_preview_dialog || !print_preview_dialog->GetWebUI())
+ return;
+ PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
+ print_preview_dialog->GetWebUI()->GetController());
+ print_preview_ui->source_is_modifiable_ = params.is_modifiable;
+ print_preview_ui->source_has_selection_ = params.has_selection;
+ print_preview_ui->print_selection_only_ = params.selection_only;
+}
+
+// static
+void PrintPreviewUI::GetCurrentPrintPreviewStatus(int32_t preview_ui_id,
+ int request_id,
+ bool* cancel) {
+ int current_id = -1;
+ if (!g_print_preview_request_id_map.Get().Get(preview_ui_id, &current_id)) {
+ *cancel = true;
+ return;
+ }
+ *cancel = (request_id != current_id);
+}
+
+int32_t PrintPreviewUI::GetIDForPrintPreviewUI() const {
+ return id_;
+}
+
+void PrintPreviewUI::OnPrintPreviewDialogClosed() {
+ WebContents* preview_dialog = web_ui()->GetWebContents();
+ printing::BackgroundPrintingManager* background_printing_manager =
+ g_browser_process->background_printing_manager();
+ if (background_printing_manager->HasPrintPreviewDialog(preview_dialog))
+ return;
+ OnClosePrintPreviewDialog();
+}
+
+void PrintPreviewUI::OnInitiatorClosed() {
+ WebContents* preview_dialog = web_ui()->GetWebContents();
+ printing::BackgroundPrintingManager* background_printing_manager =
+ g_browser_process->background_printing_manager();
+ if (background_printing_manager->HasPrintPreviewDialog(preview_dialog))
+ web_ui()->CallJavascriptFunctionUnsafe("cancelPendingPrintRequest");
+ else
+ OnClosePrintPreviewDialog();
+}
+
+void PrintPreviewUI::OnPrintPreviewRequest(int request_id) {
+ if (!initial_preview_start_time_.is_null()) {
+ UMA_HISTOGRAM_TIMES("PrintPreview.InitializationTime",
+ base::TimeTicks::Now() - initial_preview_start_time_);
+ }
+ g_print_preview_request_id_map.Get().Set(id_, request_id);
+}
+
+void PrintPreviewUI::OnDidGetPreviewPageCount(
+ const PrintHostMsg_DidGetPreviewPageCount_Params& params) {
+ DCHECK_GT(params.page_count, 0);
+ if (g_testing_delegate)
+ g_testing_delegate->DidGetPreviewPageCount(params.page_count);
+ base::Value count(params.page_count);
+ base::Value request_id(params.preview_request_id);
+ base::Value fit_to_page_scaling(params.fit_to_page_scaling);
+ web_ui()->CallJavascriptFunctionUnsafe("onDidGetPreviewPageCount", count,
+ request_id, fit_to_page_scaling);
+}
+
+void PrintPreviewUI::OnDidGetDefaultPageLayout(
+ const PageSizeMargins& page_layout, const gfx::Rect& printable_area,
+ bool has_custom_page_size_style) {
+ if (page_layout.margin_top < 0 || page_layout.margin_left < 0 ||
+ page_layout.margin_bottom < 0 || page_layout.margin_right < 0 ||
+ page_layout.content_width < 0 || page_layout.content_height < 0 ||
+ printable_area.width() <= 0 || printable_area.height() <= 0) {
+ NOTREACHED();
+ return;
+ }
+
+ base::DictionaryValue layout;
+ layout.SetDouble(printing::kSettingMarginTop, page_layout.margin_top);
+ layout.SetDouble(printing::kSettingMarginLeft, page_layout.margin_left);
+ layout.SetDouble(printing::kSettingMarginBottom, page_layout.margin_bottom);
+ layout.SetDouble(printing::kSettingMarginRight, page_layout.margin_right);
+ layout.SetDouble(printing::kSettingContentWidth, page_layout.content_width);
+ layout.SetDouble(printing::kSettingContentHeight, page_layout.content_height);
+ layout.SetInteger(printing::kSettingPrintableAreaX, printable_area.x());
+ layout.SetInteger(printing::kSettingPrintableAreaY, printable_area.y());
+ layout.SetInteger(printing::kSettingPrintableAreaWidth,
+ printable_area.width());
+ layout.SetInteger(printing::kSettingPrintableAreaHeight,
+ printable_area.height());
+
+ base::Value has_page_size_style(has_custom_page_size_style);
+ web_ui()->CallJavascriptFunctionUnsafe("onDidGetDefaultPageLayout", layout,
+ has_page_size_style);
+}
+
+void PrintPreviewUI::OnDidPreviewPage(int page_number,
+ int preview_request_id) {
+ DCHECK_GE(page_number, 0);
+ base::Value number(page_number);
+ base::Value ui_identifier(id_);
+ base::Value request_id(preview_request_id);
+ if (g_testing_delegate)
+ g_testing_delegate->DidRenderPreviewPage(web_ui()->GetWebContents());
+ web_ui()->CallJavascriptFunctionUnsafe("onDidPreviewPage", number,
+ ui_identifier, request_id);
+}
+
+void PrintPreviewUI::OnPreviewDataIsAvailable(int expected_pages_count,
+ int preview_request_id) {
+ VLOG(1) << "Print preview request finished with "
+ << expected_pages_count << " pages";
+
+ if (!initial_preview_start_time_.is_null()) {
+ UMA_HISTOGRAM_TIMES("PrintPreview.InitialDisplayTime",
+ base::TimeTicks::Now() - initial_preview_start_time_);
+ UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.Initial",
+ expected_pages_count);
+ UMA_HISTOGRAM_COUNTS(
+ "PrintPreview.RegeneratePreviewRequest.BeforeFirstData",
+ handler_->regenerate_preview_request_count());
+ initial_preview_start_time_ = base::TimeTicks();
+ }
+ base::Value ui_identifier(id_);
+ base::Value ui_preview_request_id(preview_request_id);
+ web_ui()->CallJavascriptFunctionUnsafe("updatePrintPreview", ui_identifier,
+ ui_preview_request_id);
+}
+
+void PrintPreviewUI::OnFileSelectionCancelled() {
+ web_ui()->CallJavascriptFunctionUnsafe("fileSelectionCancelled");
+}
+
+void PrintPreviewUI::OnCancelPendingPreviewRequest() {
+ g_print_preview_request_id_map.Get().Set(id_, -1);
+}
+
+void PrintPreviewUI::OnPrintPreviewFailed() {
+ handler_->OnPrintPreviewFailed();
+ web_ui()->CallJavascriptFunctionUnsafe("printPreviewFailed");
+}
+
+void PrintPreviewUI::OnInvalidPrinterSettings() {
+ web_ui()->CallJavascriptFunctionUnsafe("invalidPrinterSettings");
+}
+
+void PrintPreviewUI::OnHidePreviewDialog() {
+ WebContents* preview_dialog = web_ui()->GetWebContents();
+ printing::BackgroundPrintingManager* background_printing_manager =
+ g_browser_process->background_printing_manager();
+ if (background_printing_manager->HasPrintPreviewDialog(preview_dialog))
+ return;
+
+ ConstrainedWebDialogDelegate* delegate = GetConstrainedDelegate();
+ if (!delegate)
+ return;
+ std::unique_ptr<content::WebContents> preview_contents =
+ delegate->ReleaseWebContents();
+ DCHECK_EQ(preview_dialog, preview_contents.get());
+ background_printing_manager->OwnPrintPreviewDialog(
+ preview_contents.release());
+ OnClosePrintPreviewDialog();
+}
+
+void PrintPreviewUI::OnClosePrintPreviewDialog() {
+ if (dialog_closed_)
+ return;
+ dialog_closed_ = true;
+ ConstrainedWebDialogDelegate* delegate = GetConstrainedDelegate();
+ if (!delegate)
+ return;
+ delegate->GetWebDialogDelegate()->OnDialogClosed(std::string());
+ delegate->OnDialogCloseFromWebUI();
+}
+
+void PrintPreviewUI::OnReloadPrintersList() {
+ web_ui()->CallJavascriptFunctionUnsafe("reloadPrintersList");
+}
+
+void PrintPreviewUI::OnSetOptionsFromDocument(
+ const PrintHostMsg_SetOptionsFromDocument_Params& params) {
+ base::DictionaryValue options;
+ options.SetBoolean(printing::kSettingDisableScaling,
+ params.is_scaling_disabled);
+ options.SetInteger(printing::kSettingCopies, params.copies);
+ options.SetInteger(printing::kSettingDuplexMode, params.duplex);
+ web_ui()->CallJavascriptFunctionUnsafe("printPresetOptionsFromDocument",
+ options);
+}
+
+// static
+void PrintPreviewUI::SetDelegateForTesting(TestingDelegate* delegate) {
+ g_testing_delegate = delegate;
+}
+
+void PrintPreviewUI::SetSelectedFileForTesting(const base::FilePath& path) {
+ handler_->FileSelected(path, 0, NULL);
+}
+
+void PrintPreviewUI::SetPdfSavedClosureForTesting(
+ const base::Closure& closure) {
+ handler_->SetPdfSavedClosureForTesting(closure);
+}
diff --git a/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui.h b/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui.h
new file mode 100644
index 00000000000..360a1cd23ef
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui.h
@@ -0,0 +1,193 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINT_PREVIEW_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINT_PREVIEW_UI_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
+#include "printing/features/features.h"
+
+class PrintPreviewHandler;
+struct PrintHostMsg_DidGetPreviewPageCount_Params;
+struct PrintHostMsg_RequestPrintPreview_Params;
+struct PrintHostMsg_SetOptionsFromDocument_Params;
+
+namespace base {
+class FilePath;
+class RefCountedBytes;
+}
+
+namespace gfx {
+class Rect;
+}
+
+namespace printing {
+struct PageSizeMargins;
+}
+
+class PrintPreviewUI : public ConstrainedWebDialogUI {
+ public:
+ explicit PrintPreviewUI(content::WebUI* web_ui);
+ ~PrintPreviewUI() override;
+
+ // Gets the print preview |data|. |index| is zero-based, and can be
+ // |printing::COMPLETE_PREVIEW_DOCUMENT_INDEX| to get the entire preview
+ // document.
+ void GetPrintPreviewDataForIndex(
+ int index,
+ scoped_refptr<base::RefCountedBytes>* data) const;
+
+ // Sets the print preview |data|. |index| is zero-based, and can be
+ // |printing::COMPLETE_PREVIEW_DOCUMENT_INDEX| to set the entire preview
+ // document.
+ void SetPrintPreviewDataForIndex(int index,
+ scoped_refptr<base::RefCountedBytes> data);
+
+ // Clear the existing print preview data.
+ void ClearAllPreviewData();
+
+ // Returns the available draft page count.
+ int GetAvailableDraftPageCount() const;
+
+ // Setters
+ void SetInitiatorTitle(const base::string16& initiator_title);
+
+ const base::string16& initiator_title() const { return initiator_title_; }
+
+ bool source_is_modifiable() const { return source_is_modifiable_; }
+
+ bool source_has_selection() const { return source_has_selection_; }
+
+ bool print_selection_only() const { return print_selection_only_; }
+
+ // Set initial settings for PrintPreviewUI.
+ static void SetInitialParams(
+ content::WebContents* print_preview_dialog,
+ const PrintHostMsg_RequestPrintPreview_Params& params);
+
+ // Determines whether to cancel a print preview request based on
+ // |preview_ui_id| and |request_id|.
+ // Can be called from any thread.
+ static void GetCurrentPrintPreviewStatus(int32_t preview_ui_id,
+ int request_id,
+ bool* cancel);
+
+ // Returns an id to uniquely identify this PrintPreviewUI.
+ int32_t GetIDForPrintPreviewUI() const;
+
+ // Notifies the Web UI of a print preview request with |request_id|.
+ void OnPrintPreviewRequest(int request_id);
+
+ // Notifies the Web UI about the page count of the request preview.
+ void OnDidGetPreviewPageCount(
+ const PrintHostMsg_DidGetPreviewPageCount_Params& params);
+
+ // Notifies the Web UI of the default page layout according to the currently
+ // selected printer and page size.
+ void OnDidGetDefaultPageLayout(const printing::PageSizeMargins& page_layout,
+ const gfx::Rect& printable_area,
+ bool has_custom_page_size_style);
+
+ // Notifies the Web UI that the 0-based page |page_number| has been rendered.
+ // |preview_request_id| indicates wich request resulted in this response.
+ void OnDidPreviewPage(int page_number, int preview_request_id);
+
+ // Notifies the Web UI renderer that preview data is available.
+ // |expected_pages_count| specifies the total number of pages.
+ // |preview_request_id| indicates which request resulted in this response.
+ void OnPreviewDataIsAvailable(int expected_pages_count,
+ int preview_request_id);
+
+ // Notifies the Web UI that the print preview failed to render.
+ void OnPrintPreviewFailed();
+
+ // Notified the Web UI that this print preview dialog's RenderProcess has been
+ // closed, which may occur for several reasons, e.g. tab closure or crash.
+ void OnPrintPreviewDialogClosed();
+
+ // Notifies the Web UI that initiator is closed, so we can disable all the
+ // controls that need the initiator for generating the preview data.
+ void OnInitiatorClosed();
+
+ // Notifies the Web UI renderer that file selection has been cancelled.
+ void OnFileSelectionCancelled();
+
+ // Notifies the Web UI that the printer is unavailable or its settings are
+ // invalid.
+ void OnInvalidPrinterSettings();
+
+ // Notifies the Web UI to cancel the pending preview request.
+ void OnCancelPendingPreviewRequest();
+
+ // Hides the print preview dialog.
+ void OnHidePreviewDialog();
+
+ // Closes the print preview dialog.
+ void OnClosePrintPreviewDialog();
+
+ // Reload the printers list.
+ void OnReloadPrintersList();
+
+ // Notifies the WebUI to set print preset options from source PDF.
+ void OnSetOptionsFromDocument(
+ const PrintHostMsg_SetOptionsFromDocument_Params& params);
+
+ // Allows tests to wait until the print preview dialog is loaded.
+ class TestingDelegate {
+ public:
+ virtual void DidGetPreviewPageCount(int page_count) = 0;
+ virtual void DidRenderPreviewPage(content::WebContents* preview_dialog) = 0;
+ };
+
+ static void SetDelegateForTesting(TestingDelegate* delegate);
+
+ // Allows for tests to set a file path to print a PDF to. This also initiates
+ // the printing without having to click a button on the print preview dialog.
+ void SetSelectedFileForTesting(const base::FilePath& path);
+
+ // Passes |closure| to PrintPreviewHandler::SetPdfSavedClosureForTesting().
+ void SetPdfSavedClosureForTesting(const base::Closure& closure);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(PrintPreviewDialogControllerUnitTest,
+ TitleAfterReload);
+
+ base::TimeTicks initial_preview_start_time_;
+
+ // The unique ID for this class instance. Stored here to avoid calling
+ // GetIDForPrintPreviewUI() everywhere.
+ const int32_t id_;
+
+ // Weak pointer to the WebUI handler.
+ PrintPreviewHandler* handler_;
+
+ // Indicates whether the source document can be modified.
+ bool source_is_modifiable_;
+
+ // Indicates whether the source document has selection.
+ bool source_has_selection_;
+
+ // Indicates whether only the selection should be printed.
+ bool print_selection_only_;
+
+ // Store the initiator title, used for populating the print preview dialog
+ // title.
+ base::string16 initiator_title_;
+
+ // Keeps track of whether OnClosePrintPreviewDialog() has been called or not.
+ bool dialog_closed_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrintPreviewUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINT_PREVIEW_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui_browsertest.cc
new file mode 100644
index 00000000000..070051eb316
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui_browsertest.cc
@@ -0,0 +1,149 @@
+// 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 "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/task_manager/task_manager_browsertest_util.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.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/content_settings/core/browser/host_content_settings_map.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "content/public/test/test_utils.h"
+#include "printing/features/features.h"
+#include "url/url_constants.h"
+
+#if defined(OS_WIN)
+#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
+#endif
+
+using task_manager::browsertest_util::MatchAboutBlankTab;
+using task_manager::browsertest_util::MatchAnyPrint;
+using task_manager::browsertest_util::MatchAnyTab;
+using task_manager::browsertest_util::MatchPrint;
+using task_manager::browsertest_util::WaitForTaskManagerRows;
+
+namespace {
+
+class PrintPreviewBrowserTest : public InProcessBrowserTest {
+ public:
+ PrintPreviewBrowserTest() {}
+
+ void Print() {
+ content::TestNavigationObserver nav_observer(NULL);
+ nav_observer.StartWatchingNewWebContents();
+ chrome::ExecuteCommand(browser(), IDC_PRINT);
+ nav_observer.Wait();
+ nav_observer.StopWatchingNewWebContents();
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(PrintPreviewBrowserTest, PrintCommands) {
+ // We start off at about:blank page.
+ // Make sure there is 1 tab and print is enabled.
+ ASSERT_EQ(1, browser()->tab_strip_model()->count());
+
+ ASSERT_TRUE(chrome::IsCommandEnabled(browser(), IDC_PRINT));
+
+#if BUILDFLAG(ENABLE_BASIC_PRINTING) && !defined(OS_CHROMEOS)
+ // This is analagous to ENABLE_BASIC_PRINT_DIALOG but helps to verify
+ // that it is defined as expected.
+ bool is_basic_print_expected = true;
+#else
+ bool is_basic_print_expected = false;
+#endif
+
+ ASSERT_EQ(is_basic_print_expected,
+ chrome::IsCommandEnabled(browser(), IDC_BASIC_PRINT));
+
+ // Create the print preview dialog.
+ Print();
+
+ ASSERT_FALSE(chrome::IsCommandEnabled(browser(), IDC_PRINT));
+
+ ASSERT_EQ(is_basic_print_expected,
+ chrome::IsCommandEnabled(browser(), IDC_BASIC_PRINT));
+
+ content::TestNavigationObserver reload_observer(
+ browser()->tab_strip_model()->GetActiveWebContents());
+ chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
+ reload_observer.Wait();
+
+ ASSERT_TRUE(chrome::IsCommandEnabled(browser(), IDC_PRINT));
+
+ ASSERT_EQ(is_basic_print_expected,
+ chrome::IsCommandEnabled(browser(), IDC_BASIC_PRINT));
+}
+
+// Disable the test for mac, see http://crbug/367665.
+#if defined(OS_MACOSX) || defined(OS_LINUX)
+#define MAYBE_TaskManagerNewPrintPreview DISABLED_TaskManagerNewPrintPreview
+#else
+#define MAYBE_TaskManagerNewPrintPreview TaskManagerNewPrintPreview
+#endif
+IN_PROC_BROWSER_TEST_F(PrintPreviewBrowserTest,
+ MAYBE_TaskManagerNewPrintPreview) {
+ chrome::ShowTaskManager(browser()); // Show task manager BEFORE print dialog.
+
+ ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
+ ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+ ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyPrint()));
+
+ // Create the print preview dialog.
+ Print();
+
+ ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
+ ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+ ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrint()));
+ ASSERT_NO_FATAL_FAILURE(
+ WaitForTaskManagerRows(1, MatchPrint(url::kAboutBlankURL)));
+}
+
+// http://crbug/367665.
+IN_PROC_BROWSER_TEST_F(PrintPreviewBrowserTest,
+ DISABLED_TaskManagerExistingPrintPreview) {
+ // Create the print preview dialog.
+ Print();
+
+ chrome::ShowTaskManager(browser()); // Show task manager AFTER print dialog.
+
+ ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
+ ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+ ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrint()));
+ ASSERT_NO_FATAL_FAILURE(
+ WaitForTaskManagerRows(1, MatchPrint(url::kAboutBlankURL)));
+}
+
+#if defined(OS_WIN)
+// http://crbug.com/396360
+IN_PROC_BROWSER_TEST_F(PrintPreviewBrowserTest,
+ DISABLED_NoCrashOnCloseWithOtherTabs) {
+ // Now print preview.
+ Print();
+
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), GURL("about:blank"), WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+
+ browser()->tab_strip_model()->ActivateTabAt(0, true);
+
+ // Navigate main tab to hide print preview.
+ ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
+
+ browser()->tab_strip_model()->ActivateTabAt(1, true);
+}
+#endif // defined(OS_WIN)
+
+} // namespace
diff --git a/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui_unittest.cc b/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui_unittest.cc
new file mode 100644
index 00000000000..12910fae0f9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/print_preview_ui_unittest.cc
@@ -0,0 +1,236 @@
+// 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 <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted_memory.h"
+#include "chrome/browser/printing/print_preview_dialog_controller.h"
+#include "chrome/browser/printing/print_preview_test.h"
+#include "chrome/browser/printing/print_view_manager.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
+#include "chrome/test/base/browser_with_test_window_test.h"
+#include "components/prefs/pref_service.h"
+#include "components/web_modal/web_contents_modal_dialog_manager.h"
+#include "content/public/browser/plugin_service.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/browser/web_contents.h"
+#include "printing/print_job_constants.h"
+
+using content::WebContents;
+using web_modal::WebContentsModalDialogManager;
+
+namespace {
+
+base::RefCountedBytes* CreateTestData() {
+ const unsigned char blob1[] =
+ "12346102356120394751634516591348710478123649165419234519234512349134";
+ std::vector<unsigned char> preview_data(blob1, blob1 + sizeof(blob1));
+ return new base::RefCountedBytes(preview_data);
+}
+
+bool IsShowingWebContentsModalDialog(WebContents* tab) {
+ WebContentsModalDialogManager* web_contents_modal_dialog_manager =
+ WebContentsModalDialogManager::FromWebContents(tab);
+ return web_contents_modal_dialog_manager->IsDialogActive();
+}
+
+} // namespace
+
+class PrintPreviewUIUnitTest : public PrintPreviewTest {
+ public:
+ PrintPreviewUIUnitTest();
+ ~PrintPreviewUIUnitTest() override;
+
+ protected:
+ void SetUp() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrintPreviewUIUnitTest);
+};
+
+PrintPreviewUIUnitTest::PrintPreviewUIUnitTest() {}
+PrintPreviewUIUnitTest::~PrintPreviewUIUnitTest() {}
+
+void PrintPreviewUIUnitTest::SetUp() {
+ PrintPreviewTest::SetUp();
+
+ chrome::NewTab(browser());
+}
+
+// Create/Get a preview tab for initiator.
+TEST_F(PrintPreviewUIUnitTest, PrintPreviewData) {
+ WebContents* initiator = browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(initiator);
+ EXPECT_FALSE(IsShowingWebContentsModalDialog(initiator));
+
+ printing::PrintPreviewDialogController* controller =
+ printing::PrintPreviewDialogController::GetInstance();
+ ASSERT_TRUE(controller);
+
+ printing::PrintViewManager* print_view_manager =
+ printing::PrintViewManager::FromWebContents(initiator);
+ print_view_manager->PrintPreviewNow(initiator->GetMainFrame(), false);
+ WebContents* preview_dialog = controller->GetOrCreatePreviewDialog(initiator);
+
+ EXPECT_NE(initiator, preview_dialog);
+ EXPECT_EQ(1, browser()->tab_strip_model()->count());
+ EXPECT_TRUE(IsShowingWebContentsModalDialog(initiator));
+
+ PrintPreviewUI* preview_ui = static_cast<PrintPreviewUI*>(
+ preview_dialog->GetWebUI()->GetController());
+ ASSERT_TRUE(preview_ui != NULL);
+
+ scoped_refptr<base::RefCountedBytes> data;
+ preview_ui->GetPrintPreviewDataForIndex(
+ printing::COMPLETE_PREVIEW_DOCUMENT_INDEX,
+ &data);
+ EXPECT_EQ(NULL, data.get());
+
+ scoped_refptr<base::RefCountedBytes> dummy_data = CreateTestData();
+
+ preview_ui->SetPrintPreviewDataForIndex(
+ printing::COMPLETE_PREVIEW_DOCUMENT_INDEX,
+ dummy_data.get());
+ preview_ui->GetPrintPreviewDataForIndex(
+ printing::COMPLETE_PREVIEW_DOCUMENT_INDEX,
+ &data);
+ EXPECT_EQ(dummy_data->size(), data->size());
+ EXPECT_EQ(dummy_data.get(), data.get());
+
+ // This should not cause any memory leaks.
+ dummy_data = new base::RefCountedBytes();
+ preview_ui->SetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX,
+ dummy_data.get());
+
+ // Clear the preview data.
+ preview_ui->ClearAllPreviewData();
+
+ preview_ui->GetPrintPreviewDataForIndex(
+ printing::COMPLETE_PREVIEW_DOCUMENT_INDEX,
+ &data);
+ EXPECT_EQ(NULL, data.get());
+}
+
+// Set and get the individual draft pages.
+TEST_F(PrintPreviewUIUnitTest, PrintPreviewDraftPages) {
+ WebContents* initiator = browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(initiator);
+
+ printing::PrintPreviewDialogController* controller =
+ printing::PrintPreviewDialogController::GetInstance();
+ ASSERT_TRUE(controller);
+
+ printing::PrintViewManager* print_view_manager =
+ printing::PrintViewManager::FromWebContents(initiator);
+ print_view_manager->PrintPreviewNow(initiator->GetMainFrame(), false);
+ WebContents* preview_dialog = controller->GetOrCreatePreviewDialog(initiator);
+
+ EXPECT_NE(initiator, preview_dialog);
+ EXPECT_EQ(1, browser()->tab_strip_model()->count());
+ EXPECT_TRUE(IsShowingWebContentsModalDialog(initiator));
+
+ PrintPreviewUI* preview_ui = static_cast<PrintPreviewUI*>(
+ preview_dialog->GetWebUI()->GetController());
+ ASSERT_TRUE(preview_ui != NULL);
+
+ scoped_refptr<base::RefCountedBytes> data;
+ preview_ui->GetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX, &data);
+ EXPECT_EQ(NULL, data.get());
+
+ scoped_refptr<base::RefCountedBytes> dummy_data = CreateTestData();
+
+ preview_ui->SetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX,
+ dummy_data.get());
+ preview_ui->GetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX, &data);
+ EXPECT_EQ(dummy_data->size(), data->size());
+ EXPECT_EQ(dummy_data.get(), data.get());
+
+ // Set and get the third page data.
+ preview_ui->SetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX + 2,
+ dummy_data.get());
+ preview_ui->GetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX + 2,
+ &data);
+ EXPECT_EQ(dummy_data->size(), data->size());
+ EXPECT_EQ(dummy_data.get(), data.get());
+
+ // Get the second page data.
+ preview_ui->GetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX + 1,
+ &data);
+ EXPECT_EQ(NULL, data.get());
+
+ preview_ui->SetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX + 1,
+ dummy_data.get());
+ preview_ui->GetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX + 1,
+ &data);
+ EXPECT_EQ(dummy_data->size(), data->size());
+ EXPECT_EQ(dummy_data.get(), data.get());
+
+ // Clear the preview data.
+ preview_ui->ClearAllPreviewData();
+ preview_ui->GetPrintPreviewDataForIndex(printing::FIRST_PAGE_INDEX, &data);
+ EXPECT_EQ(NULL, data.get());
+}
+
+// Test the browser-side print preview cancellation functionality.
+TEST_F(PrintPreviewUIUnitTest, GetCurrentPrintPreviewStatus) {
+ WebContents* initiator = browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(initiator);
+
+ printing::PrintPreviewDialogController* controller =
+ printing::PrintPreviewDialogController::GetInstance();
+ ASSERT_TRUE(controller);
+
+ printing::PrintViewManager* print_view_manager =
+ printing::PrintViewManager::FromWebContents(initiator);
+ print_view_manager->PrintPreviewNow(initiator->GetMainFrame(), false);
+ WebContents* preview_dialog = controller->GetOrCreatePreviewDialog(initiator);
+
+ EXPECT_NE(initiator, preview_dialog);
+ EXPECT_EQ(1, browser()->tab_strip_model()->count());
+ EXPECT_TRUE(IsShowingWebContentsModalDialog(initiator));
+
+ PrintPreviewUI* preview_ui = static_cast<PrintPreviewUI*>(
+ preview_dialog->GetWebUI()->GetController());
+ ASSERT_TRUE(preview_ui != NULL);
+
+ // Test with invalid |preview_ui_addr|.
+ bool cancel = false;
+ const int32_t kInvalidId = -5;
+ preview_ui->GetCurrentPrintPreviewStatus(kInvalidId, 0, &cancel);
+ EXPECT_TRUE(cancel);
+
+ const int kFirstRequestId = 1000;
+ const int kSecondRequestId = 1001;
+ const int32_t preview_ui_addr = preview_ui->GetIDForPrintPreviewUI();
+
+ // Test with kFirstRequestId.
+ preview_ui->OnPrintPreviewRequest(kFirstRequestId);
+ cancel = true;
+ preview_ui->GetCurrentPrintPreviewStatus(preview_ui_addr, kFirstRequestId,
+ &cancel);
+ EXPECT_FALSE(cancel);
+
+ cancel = false;
+ preview_ui->GetCurrentPrintPreviewStatus(preview_ui_addr, kSecondRequestId,
+ &cancel);
+ EXPECT_TRUE(cancel);
+
+ // Test with kSecondRequestId.
+ preview_ui->OnPrintPreviewRequest(kSecondRequestId);
+ cancel = false;
+ preview_ui->GetCurrentPrintPreviewStatus(preview_ui_addr, kFirstRequestId,
+ &cancel);
+ EXPECT_TRUE(cancel);
+
+ cancel = true;
+ preview_ui->GetCurrentPrintPreviewStatus(preview_ui_addr, kSecondRequestId,
+ &cancel);
+ EXPECT_FALSE(cancel);
+}
diff --git a/chromium/chrome/browser/ui/webui/print_preview/printer_backend_proxy.cc b/chromium/chrome/browser/ui/webui/print_preview/printer_backend_proxy.cc
new file mode 100644
index 00000000000..822d241bea6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/printer_backend_proxy.cc
@@ -0,0 +1,112 @@
+// 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 "chrome/browser/ui/webui/print_preview/printer_backend_proxy.h"
+
+#include <string>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/threading/thread_restrictions.h"
+#include "chrome/browser/ui/webui/print_preview/printer_capabilities.h"
+#include "content/public/browser/browser_thread.h"
+#include "printing/backend/print_backend.h"
+
+namespace printing {
+
+namespace {
+
+PrinterList EnumeratePrintersAsync() {
+ base::ThreadRestrictions::AssertIOAllowed();
+ scoped_refptr<PrintBackend> print_backend(
+ PrintBackend::CreateInstance(nullptr));
+
+ PrinterList printer_list;
+ print_backend->EnumeratePrinters(&printer_list);
+
+ return printer_list;
+}
+
+std::unique_ptr<base::DictionaryValue> FetchCapabilitiesAsync(
+ const std::string& device_name) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ scoped_refptr<printing::PrintBackend> print_backend(
+ printing::PrintBackend::CreateInstance(nullptr));
+
+ VLOG(1) << "Get printer capabilities start for " << device_name;
+
+ std::unique_ptr<base::DictionaryValue> printer_info;
+ if (!print_backend->IsValidPrinter(device_name)) {
+ LOG(WARNING) << "Invalid printer " << device_name;
+ return nullptr;
+ }
+
+ PrinterBasicInfo basic_info;
+ if (!print_backend->GetPrinterBasicInfo(device_name, &basic_info)) {
+ return nullptr;
+ }
+
+ return GetSettingsOnBlockingPool(device_name, basic_info);
+}
+
+std::string GetDefaultPrinterAsync() {
+ base::ThreadRestrictions::AssertIOAllowed();
+ scoped_refptr<printing::PrintBackend> print_backend(
+ PrintBackend::CreateInstance(nullptr));
+
+ std::string default_printer = print_backend->GetDefaultPrinterName();
+ VLOG(1) << "Default Printer: " << default_printer;
+ return default_printer;
+}
+
+// Default implementation of PrinterBackendProxy. Makes calls directly to
+// the print backend on the appropriate thread.
+class PrinterBackendProxyDefault : public PrinterBackendProxy {
+ public:
+ PrinterBackendProxyDefault() {}
+
+ ~PrinterBackendProxyDefault() override {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ }
+
+ void GetDefaultPrinter(const DefaultPrinterCallback& cb) override {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&GetDefaultPrinterAsync), cb);
+ }
+
+ void EnumeratePrinters(const EnumeratePrintersCallback& cb) override {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&EnumeratePrintersAsync), cb);
+ }
+
+ void ConfigurePrinterAndFetchCapabilities(
+ const std::string& device_name,
+ const PrinterSetupCallback& cb) override {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&FetchCapabilitiesAsync, device_name), cb);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrinterBackendProxyDefault);
+};
+
+} // namespace
+
+std::unique_ptr<PrinterBackendProxy> PrinterBackendProxy::Create() {
+ return base::MakeUnique<PrinterBackendProxyDefault>();
+}
+
+} // namespace printing
diff --git a/chromium/chrome/browser/ui/webui/print_preview/printer_backend_proxy.h b/chromium/chrome/browser/ui/webui/print_preview/printer_backend_proxy.h
new file mode 100644
index 00000000000..016b688190d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/printer_backend_proxy.h
@@ -0,0 +1,57 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINTER_BACKEND_PROXY_H_
+#define CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINTER_BACKEND_PROXY_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/values.h"
+#include "printing/backend/print_backend.h"
+
+#if defined(OS_CHROMEOS)
+class Profile;
+#endif
+
+namespace printing {
+
+using DefaultPrinterCallback =
+ base::Callback<void(const std::string& printer_name)>;
+
+using EnumeratePrintersCallback = base::Callback<void(const PrinterList&)>;
+
+using PrinterSetupCallback =
+ base::Callback<void(std::unique_ptr<base::DictionaryValue> settings)>;
+
+class PrinterBackendProxy {
+ public:
+#if defined(OS_CHROMEOS)
+ static std::unique_ptr<PrinterBackendProxy> Create(Profile* profile);
+#else
+ static std::unique_ptr<PrinterBackendProxy> Create();
+#endif
+
+ virtual ~PrinterBackendProxy() = default;
+
+ // Returns the name of the default printer through |cb|. This method expects
+ // to be called on the UI thread.
+ virtual void GetDefaultPrinter(const DefaultPrinterCallback& cb) = 0;
+
+ // Retrieves printers for display in the print dialog and calls |cb| with the
+ // list. This method expects to be called on the UI thread.
+ virtual void EnumeratePrinters(const EnumeratePrintersCallback& cb) = 0;
+
+ // Verifies printer setup if needed then retrieves printer capabilities for
+ // |printer_name|. |cb| is called with the capabilities dictionary or nullptr
+ // if one of the steps failed.
+ virtual void ConfigurePrinterAndFetchCapabilities(
+ const std::string& printer_name,
+ const PrinterSetupCallback& cb) = 0;
+};
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINTER_BACKEND_PROXY_H_
diff --git a/chromium/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc b/chromium/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
new file mode 100644
index 00000000000..2286776946f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
@@ -0,0 +1,201 @@
+// 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 "chrome/browser/ui/webui/print_preview/printer_backend_proxy.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/printing/cups_print_job_manager.h"
+#include "chrome/browser/chromeos/printing/cups_print_job_manager_factory.h"
+#include "chrome/browser/chromeos/printing/ppd_provider_factory.h"
+#include "chrome/browser/chromeos/printing/printer_configurer.h"
+#include "chrome/browser/chromeos/printing/printers_manager.h"
+#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/print_preview/printer_capabilities.h"
+#include "chrome/common/chrome_switches.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/debug_daemon_client.h"
+#include "chromeos/printing/ppd_provider.h"
+#include "chromeos/printing/printer_configuration.h"
+#include "content/public/browser/browser_thread.h"
+#include "printing/backend/print_backend_consts.h"
+
+namespace printing {
+
+namespace {
+
+// Store the name used in CUPS, Printer#id in |printer_name|, the description
+// as the system_driverinfo option value, and the Printer#display_name in
+// the |printer_description| field. This will match how Mac OS X presents
+// printer information.
+printing::PrinterBasicInfo ToBasicInfo(const chromeos::Printer& printer) {
+ PrinterBasicInfo basic_info;
+
+ // TODO(skau): Unify Mac with the other platforms for display name
+ // presentation so I can remove this strange code.
+ basic_info.options[kDriverInfoTagName] = printer.description();
+ basic_info.options[kCUPSEnterprisePrinter] =
+ (printer.source() == chromeos::Printer::SRC_POLICY) ? kValueTrue
+ : kValueFalse;
+ basic_info.printer_name = printer.id();
+ basic_info.printer_description = printer.display_name();
+ return basic_info;
+}
+
+void AddPrintersToList(
+ const std::vector<std::unique_ptr<chromeos::Printer>>& printers,
+ PrinterList* list) {
+ for (const auto& printer : printers) {
+ list->push_back(ToBasicInfo(*printer));
+ }
+}
+
+void FetchCapabilities(std::unique_ptr<chromeos::Printer> printer,
+ const PrinterSetupCallback& cb) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ PrinterBasicInfo basic_info = ToBasicInfo(*printer);
+
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&GetSettingsOnBlockingPool, printer->id(), basic_info), cb);
+}
+
+class PrinterBackendProxyChromeos : public PrinterBackendProxy {
+ public:
+ explicit PrinterBackendProxyChromeos(Profile* profile)
+ : prefs_(chromeos::PrintersManagerFactory::GetForBrowserContext(profile)),
+ printer_configurer_(chromeos::PrinterConfigurer::Create(profile)),
+ weak_factory_(this) {
+ // Construct the CupsPrintJobManager to listen for printing events.
+ chromeos::CupsPrintJobManagerFactory::GetForBrowserContext(profile);
+ }
+
+ ~PrinterBackendProxyChromeos() override {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ }
+
+ void GetDefaultPrinter(const DefaultPrinterCallback& cb) override {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ // TODO(crbug.com/660898): Add default printers to ChromeOS.
+
+ content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
+ base::Bind(cb, ""));
+ };
+
+ void EnumeratePrinters(const EnumeratePrintersCallback& cb) override {
+ // PrintersManager is not thread safe and must be called from the UI
+ // thread.
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ PrinterList printer_list;
+
+ if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableNativeCups)) {
+ AddPrintersToList(prefs_->GetPrinters(), &printer_list);
+ AddPrintersToList(prefs_->GetRecommendedPrinters(), &printer_list);
+ }
+
+ content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
+ base::Bind(cb, printer_list));
+ };
+
+ void ConfigurePrinterAndFetchCapabilities(
+ const std::string& printer_name,
+ const PrinterSetupCallback& cb) override {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ std::unique_ptr<chromeos::Printer> printer =
+ prefs_->GetPrinter(printer_name);
+ if (!printer) {
+ // If the printer was removed, the lookup will fail.
+ content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
+ base::Bind(cb, nullptr));
+ return;
+ }
+
+ // Log printer configuration for selected printer.
+ UMA_HISTOGRAM_ENUMERATION("Printing.CUPS.ProtocolUsed",
+ printer->GetProtocol(),
+ chromeos::Printer::kProtocolMax);
+
+ if (prefs_->IsConfigurationCurrent(*printer)) {
+ // Skip setup if the printer is already installed.
+ HandlePrinterSetup(std::move(printer), cb, chromeos::kSuccess);
+ return;
+ }
+
+ const chromeos::Printer& printer_ref = *printer;
+ printer_configurer_->SetUpPrinter(
+ printer_ref,
+ base::Bind(&PrinterBackendProxyChromeos::HandlePrinterSetup,
+ weak_factory_.GetWeakPtr(), base::Passed(&printer), cb));
+ };
+
+ private:
+ void HandlePrinterSetup(std::unique_ptr<chromeos::Printer> printer,
+ const PrinterSetupCallback& cb,
+ chromeos::PrinterSetupResult result) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ switch (result) {
+ case chromeos::PrinterSetupResult::kSuccess:
+ VLOG(1) << "Printer setup successful for " << printer->id()
+ << " fetching properties";
+ prefs_->PrinterInstalled(*printer);
+
+ // fetch settings on the blocking pool and invoke callback.
+ FetchCapabilities(std::move(printer), cb);
+ return;
+ case chromeos::PrinterSetupResult::kPpdNotFound:
+ LOG(WARNING) << "Could not find PPD. Check printer configuration.";
+ // Prompt user to update configuration.
+ // TODO(skau): Fill me in
+ break;
+ case chromeos::PrinterSetupResult::kPpdUnretrievable:
+ LOG(WARNING) << "Could not download PPD. Check Internet connection.";
+ // Could not download PPD. Connect to Internet.
+ // TODO(skau): Fill me in
+ break;
+ case chromeos::PrinterSetupResult::kPrinterUnreachable:
+ case chromeos::PrinterSetupResult::kDbusError:
+ case chromeos::PrinterSetupResult::kPpdTooLarge:
+ case chromeos::PrinterSetupResult::kInvalidPpd:
+ case chromeos::PrinterSetupResult::kFatalError:
+ LOG(ERROR) << "Unexpected error in printer setup." << result;
+ break;
+ case chromeos::PrinterSetupResult::kMaxValue:
+ NOTREACHED() << "This value is not expected";
+ break;
+ }
+
+ // TODO(skau): Open printer settings if this is resolvable.
+ cb.Run(nullptr);
+ }
+
+ chromeos::PrintersManager* prefs_;
+ scoped_refptr<chromeos::printing::PpdProvider> ppd_provider_;
+ std::unique_ptr<chromeos::PrinterConfigurer> printer_configurer_;
+ base::WeakPtrFactory<PrinterBackendProxyChromeos> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrinterBackendProxyChromeos);
+};
+
+} // namespace
+
+std::unique_ptr<PrinterBackendProxy> PrinterBackendProxy::Create(
+ Profile* profile) {
+ return base::MakeUnique<PrinterBackendProxyChromeos>(profile);
+}
+
+} // namespace printing
diff --git a/chromium/chrome/browser/ui/webui/print_preview/printer_capabilities.cc b/chromium/chrome/browser/ui/webui/print_preview/printer_capabilities.cc
new file mode 100644
index 00000000000..1d318088a9d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/printer_capabilities.cc
@@ -0,0 +1,111 @@
+// 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 "chrome/browser/ui/webui/print_preview/printer_capabilities.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
+#include "base/strings/string_piece.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/values.h"
+#include "chrome/common/cloud_print/cloud_print_cdd_conversion.h"
+#include "chrome/common/crash_keys.h"
+#include "printing/backend/print_backend.h"
+#include "printing/backend/print_backend_consts.h"
+
+namespace printing {
+
+const char kPrinterId[] = "printerId";
+const char kPrinterCapabilities[] = "capabilities";
+
+namespace {
+
+// Returns a dictionary representing printer capabilities as CDD. Returns
+// nullptr if a dictionary could not be generated.
+std::unique_ptr<base::DictionaryValue>
+GetPrinterCapabilitiesOnBlockingPoolThread(const std::string& device_name) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(!device_name.empty());
+
+ scoped_refptr<PrintBackend> print_backend(
+ PrintBackend::CreateInstance(nullptr));
+
+ VLOG(1) << "Get printer capabilities start for " << device_name;
+ crash_keys::ScopedPrinterInfo crash_key(
+ print_backend->GetPrinterDriverInfo(device_name));
+
+ std::unique_ptr<base::DictionaryValue> printer_info;
+ if (!print_backend->IsValidPrinter(device_name)) {
+ LOG(WARNING) << "Invalid printer " << device_name;
+ return nullptr;
+ }
+
+ PrinterSemanticCapsAndDefaults info;
+ if (!print_backend->GetPrinterSemanticCapsAndDefaults(device_name, &info)) {
+ LOG(WARNING) << "Failed to get capabilities for " << device_name;
+ return nullptr;
+ }
+
+ std::unique_ptr<base::DictionaryValue> printer_capabilities =
+ cloud_print::PrinterSemanticCapsAndDefaultsToCdd(info);
+ if (!printer_capabilities) {
+ LOG(WARNING) << "Failed to convert capabilities for " << device_name;
+ return nullptr;
+ }
+
+ return printer_capabilities;
+}
+
+} // namespace
+
+std::pair<std::string, std::string> GetPrinterNameAndDescription(
+ const PrinterBasicInfo& printer) {
+#if defined(OS_MACOSX) || defined(OS_CHROMEOS)
+ // On Mac, |printer.printer_description| specifies the printer name and
+ // |printer.printer_name| specifies the device name / printer queue name.
+ // Chrome OS emulates the Mac behavior.
+ const std::string& real_name = printer.printer_description;
+ std::string real_description;
+ const auto it = printer.options.find(kDriverNameTagName);
+ if (it != printer.options.end())
+ real_description = it->second;
+ return std::make_pair(real_name, real_description);
+#else
+ return std::make_pair(printer.printer_name, printer.printer_description);
+#endif
+}
+
+std::unique_ptr<base::DictionaryValue> GetSettingsOnBlockingPool(
+ const std::string& device_name,
+ const PrinterBasicInfo& basic_info) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ const auto printer_name_description =
+ GetPrinterNameAndDescription(basic_info);
+ const std::string& printer_name = printer_name_description.first;
+ const std::string& printer_description = printer_name_description.second;
+
+ auto printer_info = base::MakeUnique<base::DictionaryValue>();
+ printer_info->SetString(kPrinterId, device_name);
+ printer_info->SetString(kSettingPrinterName, printer_name);
+ printer_info->SetString(kSettingPrinterDescription, printer_description);
+ printer_info->SetBoolean(
+ kCUPSEnterprisePrinter,
+ base::ContainsKey(basic_info.options, kCUPSEnterprisePrinter) &&
+ basic_info.options.at(kCUPSEnterprisePrinter) == kValueTrue);
+
+ auto capabilities = GetPrinterCapabilitiesOnBlockingPoolThread(device_name);
+ if (!capabilities)
+ capabilities = base::MakeUnique<base::DictionaryValue>();
+ printer_info->Set(kPrinterCapabilities, std::move(capabilities));
+
+ return printer_info;
+}
+
+} // namespace printing
diff --git a/chromium/chrome/browser/ui/webui/print_preview/printer_capabilities.h b/chromium/chrome/browser/ui/webui/print_preview/printer_capabilities.h
new file mode 100644
index 00000000000..fcd8bdb57b7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/printer_capabilities.h
@@ -0,0 +1,36 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINTER_CAPABILITIES_H_
+#define CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINTER_CAPABILITIES_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/values.h"
+
+namespace printing {
+
+struct PrinterBasicInfo;
+
+// Printer capability setting keys.
+extern const char kPrinterId[];
+extern const char kPrinterCapabilities[];
+
+// Extracts the printer display name and description from the
+// appropriate fields in |printer| for the platform.
+std::pair<std::string, std::string> GetPrinterNameAndDescription(
+ const PrinterBasicInfo& printer);
+
+// Returns the JSON representing printer capabilities for the device registered
+// as |device_name| in the PrinterBackend. The returned dictionary is suitable
+// for passage to the WebUI.
+std::unique_ptr<base::DictionaryValue> GetSettingsOnBlockingPool(
+ const std::string& device_name,
+ const PrinterBasicInfo& basic_info);
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINTER_CAPABILITIES_H_
diff --git a/chromium/chrome/browser/ui/webui/print_preview/printer_capabilities_unittest.cc b/chromium/chrome/browser/ui/webui/print_preview/printer_capabilities_unittest.cc
new file mode 100644
index 00000000000..be776532888
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/printer_capabilities_unittest.cc
@@ -0,0 +1,126 @@
+// 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 <memory>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "base/task_runner_util.h"
+#include "base/test/values_test_util.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "chrome/browser/ui/webui/print_preview/printer_capabilities.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "printing/backend/test_print_backend.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace {
+
+void SettingsReply(std::unique_ptr<base::DictionaryValue>* out,
+ std::unique_ptr<base::DictionaryValue> reply) {
+ *out = std::move(reply);
+}
+
+std::unique_ptr<base::DictionaryValue> GetSettingsSynchronous(
+ const std::string& printer_name,
+ const printing::PrinterBasicInfo& basic_info) {
+ std::unique_ptr<base::DictionaryValue> settings_dictionary;
+
+ base::SequencedWorkerPool* worker_pool =
+ content::BrowserThread::GetBlockingPool();
+
+ base::PostTaskAndReplyWithResult(
+ worker_pool, FROM_HERE, base::Bind(&printing::GetSettingsOnBlockingPool,
+ printer_name, basic_info),
+ base::Bind(&SettingsReply, base::Unretained(&settings_dictionary)));
+
+ worker_pool->FlushForTesting();
+ base::RunLoop().RunUntilIdle();
+
+ return settings_dictionary;
+}
+
+} // namespace
+
+class PrinterCapabilitiesTest : public testing::Test {
+ public:
+ PrinterCapabilitiesTest() : test_browser_threads_() {}
+
+ protected:
+ void SetUp() override {
+ test_backend_ = new printing::TestPrintBackend();
+ printing::PrintBackend::SetPrintBackendForTesting(test_backend_.get());
+ }
+
+ void TearDown() override { test_backend_ = nullptr; }
+
+ printing::TestPrintBackend* print_backend() { return test_backend_.get(); }
+
+ private:
+ content::TestBrowserThreadBundle test_browser_threads_;
+ scoped_refptr<printing::TestPrintBackend> test_backend_;
+};
+
+// Verify that we don't crash for a missing printer and a nullptr is never
+// returned.
+TEST_F(PrinterCapabilitiesTest, NonNullForMissingPrinter) {
+ printing::PrinterBasicInfo basic_info;
+ std::string printer_name = "missing_printer";
+
+ std::unique_ptr<base::DictionaryValue> settings_dictionary =
+ GetSettingsSynchronous(printer_name, basic_info);
+
+ ASSERT_TRUE(settings_dictionary);
+}
+
+TEST_F(PrinterCapabilitiesTest, ProvidedCapabilitiesUsed) {
+ std::string printer_name = "test_printer";
+ printing::PrinterBasicInfo basic_info;
+ std::unique_ptr<printing::PrinterSemanticCapsAndDefaults> caps =
+ base::MakeUnique<printing::PrinterSemanticCapsAndDefaults>();
+
+ // set a capability
+ caps->dpis = {gfx::Size(600, 600)};
+
+ print_backend()->AddValidPrinter(printer_name, std::move(caps));
+
+ std::unique_ptr<base::DictionaryValue> settings_dictionary =
+ GetSettingsSynchronous(printer_name, basic_info);
+
+ // verify settings were created
+ ASSERT_TRUE(settings_dictionary);
+
+ // verify capabilities and have one entry
+ base::DictionaryValue* cdd;
+ ASSERT_TRUE(settings_dictionary->GetDictionary("capabilities", &cdd));
+
+ // read the CDD for the dpi attribute.
+ base::DictionaryValue* caps_dict;
+ ASSERT_TRUE(cdd->GetDictionary("printer", &caps_dict));
+ EXPECT_TRUE(caps_dict->HasKey("dpi"));
+}
+
+// Ensure that the capabilities dictionary is present but empty if the backend
+// doesn't return capabilities.
+TEST_F(PrinterCapabilitiesTest, NullCapabilitiesExcluded) {
+ std::string printer_name = "test_printer";
+ printing::PrinterBasicInfo basic_info;
+
+ // return false when attempting to retrieve capabilities
+ print_backend()->AddValidPrinter(printer_name, nullptr);
+
+ std::unique_ptr<base::DictionaryValue> settings_dictionary =
+ GetSettingsSynchronous(printer_name, basic_info);
+
+ // verify settings were created
+ ASSERT_TRUE(settings_dictionary);
+
+ // verify that capabilities is an empty dictionary
+ base::DictionaryValue* caps_dict;
+ ASSERT_TRUE(settings_dictionary->GetDictionary("capabilities", &caps_dict));
+ EXPECT_TRUE(caps_dict->empty());
+}
diff --git a/chromium/chrome/browser/ui/webui/print_preview/printer_handler.cc b/chromium/chrome/browser/ui/webui/print_preview/printer_handler.cc
new file mode 100644
index 00000000000..14dad1238b3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/printer_handler.cc
@@ -0,0 +1,14 @@
+// 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.
+
+#include "chrome/browser/ui/webui/print_preview/printer_handler.h"
+
+#include "chrome/browser/ui/webui/print_preview/extension_printer_handler.h"
+
+// static
+std::unique_ptr<PrinterHandler> PrinterHandler::CreateForExtensionPrinters(
+ content::BrowserContext* browser_context) {
+ return std::unique_ptr<ExtensionPrinterHandler>(
+ new ExtensionPrinterHandler(browser_context));
+}
diff --git a/chromium/chrome/browser/ui/webui/print_preview/printer_handler.h b/chromium/chrome/browser/ui/webui/print_preview/printer_handler.h
new file mode 100644
index 00000000000..ccf12af9e2b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/printer_handler.h
@@ -0,0 +1,91 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINTER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINTER_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string16.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+class RefCountedMemory;
+}
+
+namespace content {
+class BrowserContext;
+}
+
+namespace gfx {
+class Size;
+}
+
+// Wrapper around PrinterProviderAPI to be used by print preview.
+// It makes request lifetime management easier, and hides details of more
+// complex operations like printing from the print preview handler.
+// TODO(tbarzic): Use the same interface for other printer types.
+class PrinterHandler {
+ public:
+ using GetPrintersCallback =
+ base::Callback<void(const base::ListValue& printers, bool done)>;
+ using GetCapabilityCallback =
+ base::Callback<void(const std::string& printer_id,
+ const base::DictionaryValue& capability)>;
+ using PrintCallback =
+ base::Callback<void(bool success, const std::string& error)>;
+ using GetPrinterInfoCallback =
+ base::Callback<void(const base::DictionaryValue& printer_info)>;
+
+ // Creates an instance of an PrinterHandler for extension printers.
+ static std::unique_ptr<PrinterHandler> CreateForExtensionPrinters(
+ content::BrowserContext* browser_context);
+
+ virtual ~PrinterHandler() {}
+
+ // Cancels all pending requests.
+ virtual void Reset() = 0;
+
+ // Starts getting available printers.
+ // |callback| should be called in the response to the request.
+ virtual void StartGetPrinters(const GetPrintersCallback& callback) = 0;
+
+ // Starts getting printing capability of the printer with the provided
+ // destination ID.
+ // |callback| should be called in the response to the request.
+ virtual void StartGetCapability(const std::string& destination_id,
+ const GetCapabilityCallback& callback) = 0;
+
+ // Starts granting access to the given provisional printer. The print handler
+ // will respond with more information about the printer including its non-
+ // provisional printer id.
+ // |callback| should be called in response to the request.
+ virtual void StartGrantPrinterAccess(
+ const std::string& printer_id,
+ const GetPrinterInfoCallback& callback) = 0;
+
+ // Starts a print request.
+ // |destination_id|: The printer to which print job should be sent.
+ // |capability|: Capability reported by the printer.
+ // |job_title|: The title used for print job.
+ // |ticket_json|: The print job ticket as JSON string.
+ // |page_size|: The document page size.
+ // |print_data|: The document bytes to print.
+ // |callback| should be called in the response to the request.
+ // TODO(tbarzic): Page size should be extracted from print data.
+ virtual void StartPrint(
+ const std::string& destination_id,
+ const std::string& capability,
+ const base::string16& job_title,
+ const std::string& ticket_json,
+ const gfx::Size& page_size,
+ const scoped_refptr<base::RefCountedMemory>& print_data,
+ const PrintCallback& callback) = 0;
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINTER_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/print_preview/sticky_settings.cc b/chromium/chrome/browser/ui/webui/print_preview/sticky_settings.cc
new file mode 100644
index 00000000000..b2507188aa0
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/sticky_settings.cc
@@ -0,0 +1,53 @@
+// 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/ui/webui/print_preview/sticky_settings.h"
+
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
+
+namespace printing {
+
+namespace {
+
+const char kSettingAppState[] = "appState";
+
+} // namespace
+
+StickySettings::StickySettings() {}
+
+StickySettings::~StickySettings() {}
+
+void StickySettings::StoreAppState(const std::string& data) {
+ printer_app_state_.reset(new std::string(data));
+}
+
+void StickySettings::SaveInPrefs(PrefService* prefs) {
+ std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
+ if (printer_app_state_)
+ value->SetString(kSettingAppState, *printer_app_state_);
+ prefs->Set(prefs::kPrintPreviewStickySettings, *value);
+}
+
+void StickySettings::RestoreFromPrefs(PrefService* prefs) {
+ const base::DictionaryValue* value =
+ prefs->GetDictionary(prefs::kPrintPreviewStickySettings);
+ std::string buffer;
+ if (value->GetString(kSettingAppState, &buffer))
+ StoreAppState(buffer);
+}
+
+void StickySettings::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterDictionaryPref(prefs::kPrintPreviewStickySettings);
+}
+
+std::string* StickySettings::printer_app_state() {
+ return printer_app_state_.get();
+}
+
+} // namespace printing
diff --git a/chromium/chrome/browser/ui/webui/print_preview/sticky_settings.h b/chromium/chrome/browser/ui/webui/print_preview/sticky_settings.h
new file mode 100644
index 00000000000..ddc142bb29d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/print_preview/sticky_settings.h
@@ -0,0 +1,44 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_STICKY_SETTINGS_H_
+#define CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_STICKY_SETTINGS_H_
+
+#include <memory>
+#include <string>
+
+#include "printing/print_job_constants.h"
+
+class PrefService;
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+namespace printing {
+
+// Holds all the settings that should be remembered (sticky) in print preview.
+// A sticky setting will be restored next time the user launches print preview.
+// Only one instance of this class is instantiated.
+class StickySettings {
+ public:
+ StickySettings();
+ ~StickySettings();
+
+ std::string* printer_app_state();
+
+ // Stores app state for the last used printer.
+ void StoreAppState(const std::string& app_state);
+
+ void SaveInPrefs(PrefService* profile);
+ void RestoreFromPrefs(PrefService* profile);
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ private:
+ std::unique_ptr<std::string> printer_app_state_;
+};
+
+} // namespace printing
+
+#endif // CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_STICKY_SETTINGS_H_
diff --git a/chromium/chrome/browser/ui/webui/profile_helper.cc b/chromium/chrome/browser/ui/webui/profile_helper.cc
new file mode 100644
index 00000000000..528292e1003
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/profile_helper.cc
@@ -0,0 +1,103 @@
+// 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 "chrome/browser/ui/webui/profile_helper.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/lifetime/keep_alive_types.h"
+#include "chrome/browser/lifetime/scoped_keep_alive.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_metrics.h"
+#include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/profiles/profiles_state.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/user_manager.h"
+#include "chrome/browser/ui/webui/signin/signin_utils.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/browser/app_window/app_window.h"
+#include "extensions/browser/app_window/app_window_registry.h"
+
+namespace webui {
+namespace {
+
+void ShowUserManager(const ProfileManager::CreateCallback& callback) {
+ if (!UserManager::IsShowing()) {
+ UserManager::Show(base::FilePath(), profiles::USER_MANAGER_NO_TUTORIAL,
+ profiles::USER_MANAGER_SELECT_PROFILE_NO_ACTION);
+ }
+
+ g_browser_process->profile_manager()->CreateProfileAsync(
+ ProfileManager::GetSystemProfilePath(), callback, base::string16(),
+ std::string(), std::string());
+}
+
+std::string GetProfileUserName(Profile* profile) {
+ ProfileAttributesEntry* entry;
+ if (!g_browser_process->profile_manager()
+ ->GetProfileAttributesStorage()
+ .GetProfileAttributesWithPath(profile->GetPath(), &entry))
+ return std::string();
+ return base::UTF16ToUTF8(entry->GetUserName());
+}
+
+void ShowSigninDialog(base::FilePath signin_profile_path,
+ Profile* system_profile,
+ Profile::CreateStatus status) {
+ UserManagerProfileDialog::ShowSigninDialog(system_profile,
+ signin_profile_path);
+}
+
+void ShowReauthDialog(const std::string& user_name,
+ Profile* system_profile,
+ Profile::CreateStatus status) {
+ UserManagerProfileDialog::ShowReauthDialog(
+ system_profile, user_name, signin_metrics::Reason::REASON_UNLOCK);
+}
+
+void DeleteProfileCallback(std::unique_ptr<ScopedKeepAlive> keep_alive,
+ Profile* profile,
+ Profile::CreateStatus status) {
+ if (status != Profile::CREATE_STATUS_INITIALIZED)
+ return;
+
+ OpenNewWindowForProfile(profile);
+}
+
+} // namespace
+
+void OpenNewWindowForProfile(Profile* profile) {
+ if (profiles::IsProfileLocked(profile->GetPath())) {
+ if (signin::IsForceSigninEnabled()) {
+ ShowUserManager(base::Bind(&ShowSigninDialog, profile->GetPath()));
+ } else {
+ ShowUserManager(
+ base::Bind(&ShowReauthDialog, GetProfileUserName(profile)));
+ }
+ } else {
+ profiles::FindOrCreateNewWindowForProfile(
+ profile, chrome::startup::IS_PROCESS_STARTUP,
+ chrome::startup::IS_FIRST_RUN, false);
+ }
+}
+
+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(
+ file_path, base::Bind(&DeleteProfileCallback,
+ base::Passed(base::MakeUnique<ScopedKeepAlive>(
+ KeepAliveOrigin::PROFILE_HELPER,
+ KeepAliveRestartOption::DISABLED))),
+ deletion_source);
+}
+
+} // namespace webui
diff --git a/chromium/chrome/browser/ui/webui/profile_helper.h b/chromium/chrome/browser/ui/webui/profile_helper.h
new file mode 100644
index 00000000000..1b0f930765d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/profile_helper.h
@@ -0,0 +1,29 @@
+// 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_PROFILE_HELPER_H_
+#define CHROME_BROWSER_UI_WEBUI_PROFILE_HELPER_H_
+
+#include "base/files/file_path.h"
+#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
+
+
+
+#endif // CHROME_BROWSER_UI_WEBUI_PROFILE_HELPER_H_
diff --git a/chromium/chrome/browser/ui/webui/profile_helper_browsertest.cc b/chromium/chrome/browser/ui/webui/profile_helper_browsertest.cc
new file mode 100644
index 00000000000..9f95578423d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/profile_helper_browsertest.cc
@@ -0,0 +1,196 @@
+// 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 "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_list_observer.h"
+#include "chrome/browser/ui/webui/profile_helper.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/browser/browsing_data_remover.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/test/browsing_data_remover_test_util.h"
+#include "content/public/test/test_utils.h"
+#include "content/public/test/test_web_ui.h"
+
+namespace {
+
+// An observer that returns back to test code after a new profile is
+// initialized.
+void UnblockOnProfileCreation(base::RunLoop* run_loop,
+ Profile* profile,
+ Profile::CreateStatus status) {
+ if (status == Profile::CREATE_STATUS_INITIALIZED)
+ run_loop->Quit();
+}
+
+Profile* CreateProfile() {
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ base::FilePath new_path = profile_manager->GenerateNextProfileDirectoryPath();
+ base::RunLoop run_loop;
+ profile_manager->CreateProfileAsync(
+ new_path, base::Bind(&UnblockOnProfileCreation, &run_loop),
+ base::string16(), std::string(), std::string());
+ run_loop.Run();
+ return profile_manager->GetProfileByPath(new_path);
+}
+
+// An observer returns back to test code after brower window associated with
+// the profile is activated.
+class ExpectBrowserActivationForProfile : public chrome::BrowserListObserver {
+ public:
+ explicit ExpectBrowserActivationForProfile(Profile* profile)
+ : profile_(profile), scoped_observer_(this) {
+ scoped_observer_.Add(BrowserList::GetInstance());
+ }
+
+ void Wait() {
+ loop_.Run();
+ }
+
+ protected:
+ void OnBrowserSetLastActive(Browser* browser) override {
+ if (browser->profile() == profile_)
+ loop_.Quit();
+ }
+
+ private:
+ Profile* profile_;
+ base::RunLoop loop_;
+ ScopedObserver<BrowserList, chrome::BrowserListObserver> scoped_observer_;
+};
+
+} // namespace
+
+using ProfileHelperTest = InProcessBrowserTest;
+
+IN_PROC_BROWSER_TEST_F(ProfileHelperTest, OpenNewWindowForProfile) {
+ BrowserList* browser_list = BrowserList::GetInstance();
+
+ Browser* original_browser = browser();
+ Profile* original_profile = original_browser->profile();
+ std::unique_ptr<ExpectBrowserActivationForProfile> activation_observer;
+
+ // Sanity checks.
+ EXPECT_EQ(1u, browser_list->size());
+ EXPECT_TRUE(base::ContainsValue(*browser_list, original_browser));
+
+ // Opening existing browser profile shouldn't open additional browser windows.
+ webui::OpenNewWindowForProfile(original_profile);
+ EXPECT_EQ(1u, browser_list->size());
+ EXPECT_EQ(original_browser, browser_list->GetLastActive());
+
+ // Open additional browser will add new window and activates it.
+ Profile* additional_profile = CreateProfile();
+ activation_observer =
+ base::MakeUnique<ExpectBrowserActivationForProfile>(additional_profile);
+ webui::OpenNewWindowForProfile(additional_profile);
+ EXPECT_EQ(2u, browser_list->size());
+ activation_observer->Wait();
+ EXPECT_EQ(additional_profile, browser_list->GetLastActive()->profile());
+
+// On Macs OpenNewWindowForProfile does not activate existing browser
+// while non of the browser windows have focus. BrowserWindowCocoa::Show() got
+// the same issue as BrowserWindowCocoa::Activate(), and execute call
+// BrowserList::SetLastActive() directly. Not sure if it is a bug or desired
+// behaviour.
+#if !defined(OS_MACOSX)
+ // Switch to original browser. Only LastActive should change.
+ activation_observer =
+ base::MakeUnique<ExpectBrowserActivationForProfile>(original_profile);
+ webui::OpenNewWindowForProfile(original_profile);
+ EXPECT_EQ(2u, browser_list->size());
+ activation_observer->Wait();
+ EXPECT_EQ(original_profile, browser_list->GetLastActive()->profile());
+#endif
+}
+
+IN_PROC_BROWSER_TEST_F(ProfileHelperTest, DeleteSoleProfile) {
+ content::TestWebUI web_ui;
+ Browser* original_browser = browser();
+ ProfileAttributesStorage& storage =
+ g_browser_process->profile_manager()->GetProfileAttributesStorage();
+
+ BrowserList* browser_list = BrowserList::GetInstance();
+ EXPECT_EQ(1u, browser_list->size());
+ EXPECT_TRUE(base::ContainsValue(*browser_list, original_browser));
+ EXPECT_EQ(1u, storage.GetNumberOfProfiles());
+
+ // Original browser will be closed, and browser with the new profile created.
+ content::WindowedNotificationObserver open_observer(
+ chrome::NOTIFICATION_BROWSER_OPENED,
+ content::NotificationService::AllSources());
+ content::WindowedNotificationObserver close_observer(
+ chrome::NOTIFICATION_BROWSER_CLOSED, content::Source<Browser>(browser()));
+ webui::DeleteProfileAtPath(original_browser->profile()->GetPath(), &web_ui,
+ ProfileMetrics::DELETE_PROFILE_SETTINGS);
+ open_observer.Wait();
+ close_observer.Wait();
+
+ EXPECT_EQ(1u, browser_list->size());
+ EXPECT_FALSE(base::ContainsValue(*browser_list, original_browser));
+ EXPECT_EQ(1u, storage.GetNumberOfProfiles());
+}
+
+IN_PROC_BROWSER_TEST_F(ProfileHelperTest, DeleteActiveProfile) {
+ content::TestWebUI web_ui;
+ Browser* original_browser = browser();
+ ProfileAttributesStorage& storage =
+ g_browser_process->profile_manager()->GetProfileAttributesStorage();
+
+ BrowserList* browser_list = BrowserList::GetInstance();
+ EXPECT_EQ(1u, browser_list->size());
+ EXPECT_TRUE(base::ContainsValue(*browser_list, original_browser));
+ EXPECT_EQ(1u, storage.GetNumberOfProfiles());
+
+ Profile* additional_profile = CreateProfile();
+ EXPECT_EQ(2u, storage.GetNumberOfProfiles());
+
+ // Original browser will be closed, and browser with the new profile created.
+ content::WindowedNotificationObserver open_observer(
+ chrome::NOTIFICATION_BROWSER_OPENED,
+ content::NotificationService::AllSources());
+ content::WindowedNotificationObserver close_observer(
+ chrome::NOTIFICATION_BROWSER_CLOSED, content::Source<Browser>(browser()));
+ webui::DeleteProfileAtPath(original_browser->profile()->GetPath(), &web_ui,
+ ProfileMetrics::DELETE_PROFILE_SETTINGS);
+ open_observer.Wait();
+ close_observer.Wait();
+
+ EXPECT_EQ(1u, browser_list->size());
+ EXPECT_EQ(additional_profile, browser_list->get(0)->profile());
+ EXPECT_EQ(1u, storage.GetNumberOfProfiles());
+}
+
+IN_PROC_BROWSER_TEST_F(ProfileHelperTest, DeleteInactiveProfile) {
+ content::TestWebUI web_ui;
+ Browser* original_browser = browser();
+ ProfileAttributesStorage& storage =
+ g_browser_process->profile_manager()->GetProfileAttributesStorage();
+
+ BrowserList* browser_list = BrowserList::GetInstance();
+ EXPECT_EQ(1u, browser_list->size());
+ EXPECT_TRUE(base::ContainsValue(*browser_list, original_browser));
+ EXPECT_EQ(1u, storage.GetNumberOfProfiles());
+
+ Profile* additional_profile = CreateProfile();
+ EXPECT_EQ(2u, storage.GetNumberOfProfiles());
+
+ content::BrowsingDataRemoverCompletionInhibitor inhibitor(
+ content::BrowserContext::GetBrowsingDataRemover(additional_profile));
+ webui::DeleteProfileAtPath(additional_profile->GetPath(), &web_ui,
+ ProfileMetrics::DELETE_PROFILE_SETTINGS);
+ inhibitor.BlockUntilNearCompletion();
+ inhibitor.ContinueToCompletion();
+
+ EXPECT_EQ(1u, browser_list->size());
+ EXPECT_TRUE(base::ContainsValue(*browser_list, original_browser));
+ EXPECT_EQ(1u, storage.GetNumberOfProfiles());
+}
diff --git a/chromium/chrome/browser/ui/webui/profile_info_watcher.cc b/chromium/chrome/browser/ui/webui/profile_info_watcher.cc
new file mode 100644
index 00000000000..3286a9fbeed
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/profile_info_watcher.cc
@@ -0,0 +1,60 @@
+// 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.
+
+#include "chrome/browser/ui/webui/profile_info_watcher.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/signin/core/common/signin_pref_names.h"
+
+ProfileInfoWatcher::ProfileInfoWatcher(
+ Profile* profile, const base::Closure& callback)
+ : profile_(profile), callback_(callback) {
+ DCHECK(profile_);
+ DCHECK(!callback_.is_null());
+
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ // The profile_manager might be NULL in testing environments.
+ if (profile_manager)
+ profile_manager->GetProfileAttributesStorage().AddObserver(this);
+
+ signin_allowed_pref_.Init(prefs::kSigninAllowed, profile_->GetPrefs(),
+ base::Bind(&ProfileInfoWatcher::RunCallback, base::Unretained(this)));
+}
+
+ProfileInfoWatcher::~ProfileInfoWatcher() {
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ // The profile_manager might be NULL in testing environments.
+ if (profile_manager)
+ profile_manager->GetProfileAttributesStorage().RemoveObserver(this);
+}
+
+void ProfileInfoWatcher::OnProfileAuthInfoChanged(
+ const base::FilePath& profile_path) {
+ RunCallback();
+}
+
+std::string ProfileInfoWatcher::GetAuthenticatedUsername() const {
+ std::string username;
+ SigninManagerBase* signin_manager = GetSigninManager();
+ if (signin_manager)
+ username = signin_manager->GetAuthenticatedAccountInfo().email;
+ return username;
+}
+
+SigninManagerBase* ProfileInfoWatcher::GetSigninManager() const {
+ return SigninManagerFactory::GetForProfile(profile_);
+}
+
+void ProfileInfoWatcher::RunCallback() {
+ if (GetSigninManager())
+ callback_.Run();
+}
diff --git a/chromium/chrome/browser/ui/webui/profile_info_watcher.h b/chromium/chrome/browser/ui/webui/profile_info_watcher.h
new file mode 100644
index 00000000000..114282d6a32
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/profile_info_watcher.h
@@ -0,0 +1,50 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_PROFILE_INFO_WATCHER_H_
+#define CHROME_BROWSER_UI_WEBUI_PROFILE_INFO_WATCHER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "components/prefs/pref_member.h"
+
+class Profile;
+class SigninManagerBase;
+
+// Watches profiles for changes in their cached info (e.g. the authenticated
+// username changes).
+class ProfileInfoWatcher : public ProfileAttributesStorage::Observer {
+ public:
+ ProfileInfoWatcher(Profile* profile, const base::Closure& callback);
+ ~ProfileInfoWatcher() override;
+
+ // Gets the authenticated username (e.g. username@gmail.com) for |profile_|.
+ std::string GetAuthenticatedUsername() const;
+
+ private:
+ // ProfileAttributesStorage::Observer:
+ void OnProfileAuthInfoChanged(const base::FilePath& profile_path) override;
+
+ // Gets the SigninManagerBase for |profile_|.
+ SigninManagerBase* GetSigninManager() const;
+
+ // Runs |callback_| when a profile changes. No-ops if |GetSigninManager()|
+ // returns nullptr.
+ void RunCallback();
+
+ // Weak reference to the profile this class observes.
+ Profile* const profile_;
+
+ // Called when the authenticated username changes.
+ base::Closure callback_;
+
+ BooleanPrefMember signin_allowed_pref_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProfileInfoWatcher);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_PROFILE_INFO_WATCHER_H_
diff --git a/chromium/chrome/browser/ui/webui/profiler_ui.cc b/chromium/chrome/browser/ui/webui/profiler_ui.cc
new file mode 100644
index 00000000000..2bf297e4fdd
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/profiler_ui.cc
@@ -0,0 +1,186 @@
+// 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/ui/webui/profiler_ui.h"
+
+#include <string>
+
+// When testing the javacript code, it is cumbersome to have to keep
+// re-building the resouces package and reloading the browser. To solve
+// this, enable the following flag to read the webapp's source files
+// directly off disk, so all you have to do is refresh the page to
+// test the modifications.
+// #define USE_SOURCE_FILES_DIRECTLY
+
+#include "base/bind.h"
+#include "base/debug/debugging_flags.h"
+#include "base/debug/thread_heap_usage_tracker.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+#include "base/tracked_objects.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/task_profiler/task_profiler_data_serializer.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "components/metrics/profiler/tracking_synchronizer.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+#ifdef USE_SOURCE_FILES_DIRECTLY
+#include "base/base_paths.h"
+#include "base/files/file_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/path_service.h"
+#endif // USE_SOURCE_FILES_DIRECTLY
+
+using content::BrowserThread;
+using content::WebContents;
+using content::WebUIMessageHandler;
+using metrics::TrackingSynchronizer;
+
+namespace {
+
+#ifdef USE_SOURCE_FILES_DIRECTLY
+
+class ProfilerWebUIDataSource : public content::URLDataSource {
+ public:
+ ProfilerWebUIDataSource() {
+ }
+
+ protected:
+ // content::URLDataSource implementation.
+ std::string GetSource() override {
+ return chrome::kChromeUIProfilerHost;
+ }
+
+ std::string GetMimeType(const std::string& path) const override {
+ if (base::EndsWith(path, ".js", base::CompareCase::INSENSITIVE_ASCII))
+ return "application/javascript";
+ return "text/html";
+ }
+
+ void StartDataRequest(
+ const std::string& path,
+ bool is_incognito,
+ const content::URLDataSource::GotDataCallback& callback) override {
+ base::FilePath base_path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &base_path);
+ base_path = base_path.AppendASCII("chrome");
+ base_path = base_path.AppendASCII("browser");
+ base_path = base_path.AppendASCII("resources");
+ base_path = base_path.AppendASCII("profiler");
+
+ // If no resource was specified, default to profiler.html.
+ std::string filename = path.empty() ? "profiler.html" : path;
+
+ base::FilePath file_path;
+ file_path = base_path.AppendASCII(filename);
+
+ // Read the file synchronously and send it as the response.
+ base::ThreadRestrictions::ScopedAllowIO allow;
+ std::string file_contents;
+ if (!base::ReadFileToString(file_path, &file_contents))
+ LOG(ERROR) << "Couldn't read file: " << file_path.value();
+ scoped_refptr<base::RefCountedString> response =
+ new base::RefCountedString();
+ response->data() = file_contents;
+ callback.Run(response);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProfilerWebUIDataSource);
+};
+
+#else // USE_SOURCE_FILES_DIRECTLY
+
+content::WebUIDataSource* CreateProfilerHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIProfilerHost);
+
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("profiler.js", IDR_PROFILER_JS);
+ source->SetDefaultResource(IDR_PROFILER_HTML);
+ source->UseGzip(std::unordered_set<std::string>());
+ source->AddBoolean(
+ "enableMemoryTaskProfiler",
+ base::debug::ThreadHeapUsageTracker::IsHeapTrackingEnabled());
+
+ return source;
+}
+
+#endif
+
+// This class receives javascript messages from the renderer.
+// Note that the WebUI infrastructure runs on the UI thread, therefore all of
+// this class's methods are expected to run on the UI thread.
+class ProfilerMessageHandler : public WebUIMessageHandler {
+ public:
+ ProfilerMessageHandler() {}
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // Messages.
+ void OnGetData(const base::ListValue* list);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProfilerMessageHandler);
+};
+
+void ProfilerMessageHandler::RegisterMessages() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ web_ui()->RegisterMessageCallback("getData",
+ base::Bind(&ProfilerMessageHandler::OnGetData, base::Unretained(this)));
+}
+
+void ProfilerMessageHandler::OnGetData(const base::ListValue* list) {
+ ProfilerUI* profiler_ui = static_cast<ProfilerUI*>(web_ui()->GetController());
+ profiler_ui->GetData();
+}
+
+} // namespace
+
+ProfilerUI::ProfilerUI(content::WebUI* web_ui)
+ : WebUIController(web_ui),
+ weak_ptr_factory_(this) {
+ web_ui->AddMessageHandler(base::MakeUnique<ProfilerMessageHandler>());
+
+ // Set up the chrome://profiler/ source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+#if defined(USE_SOURCE_FILES_DIRECTLY)
+ content::URLDataSource::Add(profile, new ProfilerWebUIDataSource);
+#else
+ content::WebUIDataSource::Add(profile, CreateProfilerHTMLSource());
+#endif
+}
+
+ProfilerUI::~ProfilerUI() {
+}
+
+void ProfilerUI::GetData() {
+ TrackingSynchronizer::FetchProfilerDataAsynchronously(
+ weak_ptr_factory_.GetWeakPtr());
+}
+
+void ProfilerUI::ReceivedProfilerData(
+ const metrics::ProfilerDataAttributes& attributes,
+ const tracked_objects::ProcessDataPhaseSnapshot& process_data_phase,
+ const metrics::ProfilerEvents& past_events) {
+ // Serialize the data to JSON.
+ base::DictionaryValue json_data;
+ task_profiler::TaskProfilerDataSerializer::ToValue(
+ process_data_phase, attributes.process_id, attributes.process_type,
+ &json_data);
+
+ // Send the data to the renderer.
+ web_ui()->CallJavascriptFunctionUnsafe("g_browserBridge.receivedData",
+ json_data);
+}
diff --git a/chromium/chrome/browser/ui/webui/profiler_ui.h b/chromium/chrome/browser/ui/webui/profiler_ui.h
new file mode 100644
index 00000000000..350fe7ebc05
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/profiler_ui.h
@@ -0,0 +1,36 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_PROFILER_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_PROFILER_UI_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/metrics/profiler/tracking_synchronizer_observer.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The C++ back-end for the chrome://profiler webui page.
+class ProfilerUI : public content::WebUIController,
+ public metrics::TrackingSynchronizerObserver {
+ public:
+ explicit ProfilerUI(content::WebUI* web_ui);
+ ~ProfilerUI() override;
+
+ // Get the tracking data from TrackingSynchronizer.
+ void GetData();
+
+ private:
+ // TrackingSynchronizerObserver:
+ void ReceivedProfilerData(
+ const metrics::ProfilerDataAttributes& attributes,
+ const tracked_objects::ProcessDataPhaseSnapshot& process_data_phase,
+ const metrics::ProfilerEvents& past_events) override;
+
+ // Used to get |weak_ptr_| to self on the UI thread.
+ base::WeakPtrFactory<ProfilerUI> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProfilerUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_PROFILER_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/quota_internals/OWNERS b/chromium/chrome/browser/ui/webui/quota_internals/OWNERS
new file mode 100644
index 00000000000..ed698ac5e50
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/quota_internals/OWNERS
@@ -0,0 +1,2 @@
+tzik@chromium.org
+michaeln@chromium.org
diff --git a/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_handler.cc b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_handler.cc
new file mode 100644
index 00000000000..1c632127e20
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_handler.cc
@@ -0,0 +1,91 @@
+// Copyright (c) 2011 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/quota_internals/quota_internals_handler.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/quota_internals/quota_internals_proxy.h"
+#include "chrome/browser/ui/webui/quota_internals/quota_internals_types.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_ui.h"
+
+using content::BrowserContext;
+
+namespace quota_internals {
+
+QuotaInternalsHandler::QuotaInternalsHandler() {}
+
+QuotaInternalsHandler::~QuotaInternalsHandler() {
+ if (proxy_.get())
+ proxy_->handler_ = NULL;
+}
+
+void QuotaInternalsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("requestInfo",
+ base::Bind(&QuotaInternalsHandler::OnRequestInfo,
+ base::Unretained(this)));
+}
+
+void QuotaInternalsHandler::ReportAvailableSpace(int64_t available_space) {
+ SendMessage("AvailableSpaceUpdated",
+ base::Value(static_cast<double>(available_space)));
+}
+
+void QuotaInternalsHandler::ReportGlobalInfo(const GlobalStorageInfo& data) {
+ std::unique_ptr<base::Value> value(data.NewValue());
+ SendMessage("GlobalInfoUpdated", *value);
+}
+
+void QuotaInternalsHandler::ReportPerHostInfo(
+ const std::vector<PerHostStorageInfo>& hosts) {
+ base::ListValue values;
+ typedef std::vector<PerHostStorageInfo>::const_iterator iterator;
+ for (iterator itr(hosts.begin()); itr != hosts.end(); ++itr) {
+ values.Append(itr->NewValue());
+ }
+
+ SendMessage("PerHostInfoUpdated", values);
+}
+
+void QuotaInternalsHandler::ReportPerOriginInfo(
+ const std::vector<PerOriginStorageInfo>& origins) {
+ base::ListValue origins_value;
+ typedef std::vector<PerOriginStorageInfo>::const_iterator iterator;
+ for (iterator itr(origins.begin()); itr != origins.end(); ++itr) {
+ origins_value.Append(itr->NewValue());
+ }
+
+ SendMessage("PerOriginInfoUpdated", origins_value);
+}
+
+void QuotaInternalsHandler::ReportStatistics(const Statistics& stats) {
+ base::DictionaryValue dict;
+ typedef Statistics::const_iterator iterator;
+ for (iterator itr(stats.begin()); itr != stats.end(); ++itr) {
+ dict.SetString(itr->first, itr->second);
+ }
+
+ SendMessage("StatisticsUpdated", dict);
+}
+
+void QuotaInternalsHandler::SendMessage(const std::string& message,
+ const base::Value& value) {
+ web_ui()->CallJavascriptFunctionUnsafe("cr.quota.messageHandler",
+ base::Value(message), value);
+}
+
+void QuotaInternalsHandler::OnRequestInfo(const base::ListValue*) {
+ if (!proxy_.get())
+ proxy_ = new QuotaInternalsProxy(this);
+ proxy_->RequestInfo(
+ BrowserContext::GetDefaultStoragePartition(
+ Profile::FromWebUI(web_ui()))->GetQuotaManager());
+}
+
+} // namespace quota_internals
diff --git a/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_handler.h b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_handler.h
new file mode 100644
index 00000000000..b3a7eb770cc
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_handler.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2011 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_QUOTA_INTERNALS_QUOTA_INTERNALS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_QUOTA_INTERNALS_QUOTA_INTERNALS_HANDLER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "storage/common/quota/quota_types.h"
+
+namespace base {
+class Value;
+class ListValue;
+}
+
+namespace quota_internals {
+
+class QuotaInternalsProxy;
+class GlobalStorageInfo;
+class PerHostStorageInfo;
+class PerOriginStorageInfo;
+typedef std::map<std::string, std::string> Statistics;
+
+// This class handles message from WebUI page of chrome://quota-internals/.
+// All methods in this class should be called on UI thread.
+class QuotaInternalsHandler : public content::WebUIMessageHandler {
+ public:
+ QuotaInternalsHandler();
+ ~QuotaInternalsHandler() override;
+ void RegisterMessages() override;
+
+ // Called by QuotaInternalsProxy to report information to WebUI page.
+ void ReportAvailableSpace(int64_t available_space);
+ void ReportGlobalInfo(const GlobalStorageInfo& data);
+ void ReportPerHostInfo(const std::vector<PerHostStorageInfo>& hosts);
+ void ReportPerOriginInfo(const std::vector<PerOriginStorageInfo>& origins);
+ void ReportStatistics(const Statistics& stats);
+
+ private:
+ void OnRequestInfo(const base::ListValue*);
+ void SendMessage(const std::string& message, const base::Value& value);
+
+ scoped_refptr<QuotaInternalsProxy> proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuotaInternalsHandler);
+};
+} // namespace quota_internals
+
+#endif // CHROME_BROWSER_UI_WEBUI_QUOTA_INTERNALS_QUOTA_INTERNALS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.cc b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.cc
new file mode 100644
index 00000000000..923f99df4ab
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.cc
@@ -0,0 +1,226 @@
+// 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/ui/webui/quota_internals/quota_internals_proxy.h"
+
+#include <set>
+#include <string>
+
+#include "base/bind.h"
+#include "base/trace_event/trace_event.h"
+#include "chrome/browser/ui/webui/quota_internals/quota_internals_handler.h"
+#include "chrome/browser/ui/webui/quota_internals/quota_internals_types.h"
+#include "net/base/url_util.h"
+
+using content::BrowserThread;
+
+namespace quota_internals {
+
+QuotaInternalsProxy::QuotaInternalsProxy(QuotaInternalsHandler* handler)
+ : handler_(handler),
+ weak_factory_(this) {
+}
+
+void QuotaInternalsProxy::RequestInfo(
+ scoped_refptr<storage::QuotaManager> quota_manager) {
+ DCHECK(quota_manager.get());
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::BindOnce(&QuotaInternalsProxy::RequestInfo, this, quota_manager));
+ return;
+ }
+ quota_manager_ = quota_manager;
+
+ quota_manager_->GetQuotaSettings(base::Bind(
+ &QuotaInternalsProxy::DidGetSettings, weak_factory_.GetWeakPtr()));
+
+ quota_manager_->GetStorageCapacity(base::Bind(
+ &QuotaInternalsProxy::DidGetCapacity, weak_factory_.GetWeakPtr()));
+
+ quota_manager_->GetGlobalUsage(
+ storage::kStorageTypeTemporary,
+ base::Bind(&QuotaInternalsProxy::DidGetGlobalUsage,
+ weak_factory_.GetWeakPtr(),
+ storage::kStorageTypeTemporary));
+
+ quota_manager_->GetGlobalUsage(
+ storage::kStorageTypePersistent,
+ base::Bind(&QuotaInternalsProxy::DidGetGlobalUsage,
+ weak_factory_.GetWeakPtr(),
+ storage::kStorageTypePersistent));
+
+ quota_manager_->GetGlobalUsage(
+ storage::kStorageTypeSyncable,
+ base::Bind(&QuotaInternalsProxy::DidGetGlobalUsage,
+ weak_factory_.GetWeakPtr(),
+ storage::kStorageTypeSyncable));
+
+ quota_manager_->DumpQuotaTable(
+ base::Bind(&QuotaInternalsProxy::DidDumpQuotaTable,
+ weak_factory_.GetWeakPtr()));
+
+ quota_manager_->DumpOriginInfoTable(
+ base::Bind(&QuotaInternalsProxy::DidDumpOriginInfoTable,
+ weak_factory_.GetWeakPtr()));
+
+ std::map<std::string, std::string> stats;
+ quota_manager_->GetStatistics(&stats);
+ ReportStatistics(stats);
+}
+
+QuotaInternalsProxy::~QuotaInternalsProxy() {}
+
+#define RELAY_TO_HANDLER(func, arg_t) \
+ void QuotaInternalsProxy::func(arg_t arg) { \
+ if (!handler_) \
+ return; \
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { \
+ BrowserThread::PostTask( \
+ BrowserThread::UI, FROM_HERE, \
+ base::BindOnce(&QuotaInternalsProxy::func, this, arg)); \
+ return; \
+ } \
+ \
+ handler_->func(arg); \
+ }
+
+RELAY_TO_HANDLER(ReportAvailableSpace, int64_t)
+RELAY_TO_HANDLER(ReportGlobalInfo, const GlobalStorageInfo&)
+RELAY_TO_HANDLER(ReportPerHostInfo, const std::vector<PerHostStorageInfo>&)
+RELAY_TO_HANDLER(ReportPerOriginInfo, const std::vector<PerOriginStorageInfo>&)
+RELAY_TO_HANDLER(ReportStatistics, const Statistics&)
+
+#undef RELAY_TO_HANDLER
+
+void QuotaInternalsProxy::DidGetSettings(
+ const storage::QuotaSettings& settings) {
+ // TODO(michaeln): also report the other config fields
+ GlobalStorageInfo info(storage::kStorageTypeTemporary);
+ info.set_quota(settings.pool_size);
+ ReportGlobalInfo(info);
+}
+
+void QuotaInternalsProxy::DidGetCapacity(int64_t total_space,
+ int64_t available_space) {
+ // TODO(michaeln): also report total_space
+ ReportAvailableSpace(available_space);
+}
+
+void QuotaInternalsProxy::DidGetGlobalUsage(storage::StorageType type,
+ int64_t usage,
+ int64_t unlimited_usage) {
+ GlobalStorageInfo info(type);
+ info.set_usage(usage);
+ info.set_unlimited_usage(unlimited_usage);
+
+ ReportGlobalInfo(info);
+ RequestPerOriginInfo(type);
+}
+
+void QuotaInternalsProxy::DidDumpQuotaTable(const QuotaTableEntries& entries) {
+ std::vector<PerHostStorageInfo> host_info;
+ host_info.reserve(entries.size());
+
+ typedef QuotaTableEntries::const_iterator iterator;
+ for (iterator itr(entries.begin()); itr != entries.end(); ++itr) {
+ PerHostStorageInfo info(itr->host, itr->type);
+ info.set_quota(itr->quota);
+ host_info.push_back(info);
+ }
+
+ ReportPerHostInfo(host_info);
+}
+
+void QuotaInternalsProxy::DidDumpOriginInfoTable(
+ const OriginInfoTableEntries& entries) {
+ std::vector<PerOriginStorageInfo> origin_info;
+ origin_info.reserve(entries.size());
+
+ typedef OriginInfoTableEntries::const_iterator iterator;
+ for (iterator itr(entries.begin()); itr != entries.end(); ++itr) {
+ PerOriginStorageInfo info(itr->origin, itr->type);
+ info.set_used_count(itr->used_count);
+ info.set_last_access_time(itr->last_access_time);
+ info.set_last_modified_time(itr->last_modified_time);
+
+ origin_info.push_back(info);
+ }
+
+ ReportPerOriginInfo(origin_info);
+}
+
+void QuotaInternalsProxy::DidGetHostUsage(const std::string& host,
+ storage::StorageType type,
+ int64_t usage) {
+ DCHECK(type == storage::kStorageTypeTemporary ||
+ type == storage::kStorageTypePersistent ||
+ type == storage::kStorageTypeSyncable);
+
+ PerHostStorageInfo info(host, type);
+ info.set_usage(usage);
+
+ report_pending_.push_back(info);
+ hosts_pending_.erase(make_pair(host, type));
+ if (report_pending_.size() >= 10 || hosts_pending_.empty()) {
+ ReportPerHostInfo(report_pending_);
+ report_pending_.clear();
+ }
+
+ if (!hosts_pending_.empty())
+ GetHostUsage(hosts_pending_.begin()->first,
+ hosts_pending_.begin()->second);
+}
+
+void QuotaInternalsProxy::RequestPerOriginInfo(storage::StorageType type) {
+ DCHECK(quota_manager_.get());
+
+ std::set<GURL> origins;
+ quota_manager_->GetCachedOrigins(type, &origins);
+
+ std::vector<PerOriginStorageInfo> origin_info;
+ origin_info.reserve(origins.size());
+
+ std::set<std::string> hosts;
+ std::vector<PerHostStorageInfo> host_info;
+
+ for (std::set<GURL>::iterator itr(origins.begin());
+ itr != origins.end(); ++itr) {
+ PerOriginStorageInfo info(*itr, type);
+ info.set_in_use(quota_manager_->IsOriginInUse(*itr));
+ origin_info.push_back(info);
+
+ std::string host(net::GetHostOrSpecFromURL(*itr));
+ if (hosts.insert(host).second) {
+ PerHostStorageInfo info(host, type);
+ host_info.push_back(info);
+ VisitHost(host, type);
+ }
+ }
+ ReportPerOriginInfo(origin_info);
+ ReportPerHostInfo(host_info);
+}
+
+void QuotaInternalsProxy::VisitHost(const std::string& host,
+ storage::StorageType type) {
+ if (hosts_visited_.insert(std::make_pair(host, type)).second) {
+ hosts_pending_.insert(std::make_pair(host, type));
+ if (hosts_pending_.size() == 1) {
+ GetHostUsage(host, type);
+ }
+ }
+}
+
+void QuotaInternalsProxy::GetHostUsage(const std::string& host,
+ storage::StorageType type) {
+ DCHECK(quota_manager_.get());
+ quota_manager_->GetHostUsage(host,
+ type,
+ base::Bind(&QuotaInternalsProxy::DidGetHostUsage,
+ weak_factory_.GetWeakPtr(),
+ host,
+ type));
+}
+
+} // namespace quota_internals
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
new file mode 100644
index 00000000000..c8e750f8caf
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_proxy.h
@@ -0,0 +1,91 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_QUOTA_INTERNALS_QUOTA_INTERNALS_PROXY_H_
+#define CHROME_BROWSER_UI_WEBUI_QUOTA_INTERNALS_QUOTA_INTERNALS_PROXY_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "content/public/browser/browser_thread.h"
+#include "storage/browser/quota/quota_manager.h"
+#include "storage/common/quota/quota_types.h"
+
+namespace quota_internals {
+
+class QuotaInternalsHandler;
+class GlobalStorageInfo;
+class PerHostStorageInfo;
+class PerOriginStorageInfo;
+typedef std::map<std::string, std::string> Statistics;
+
+// This class is the bridge between QuotaInternalsHandler and QuotaManager.
+// Each QuotaInternalsHandler instances creates and owns a instance of this
+// class.
+class QuotaInternalsProxy
+ : public base::RefCountedThreadSafe<
+ QuotaInternalsProxy,
+ content::BrowserThread::DeleteOnIOThread> {
+ public:
+ explicit QuotaInternalsProxy(QuotaInternalsHandler* handler);
+
+ void RequestInfo(scoped_refptr<storage::QuotaManager> quota_manager);
+
+ private:
+ friend class base::DeleteHelper<QuotaInternalsProxy>;
+ friend struct content::BrowserThread::DeleteOnThread<
+ content::BrowserThread::IO>;
+ friend class QuotaInternalsHandler;
+
+ typedef storage::QuotaManager::QuotaTableEntries QuotaTableEntries;
+ typedef storage::QuotaManager::OriginInfoTableEntries OriginInfoTableEntries;
+
+ virtual ~QuotaInternalsProxy();
+
+ void ReportAvailableSpace(int64_t available_space);
+ void ReportGlobalInfo(const GlobalStorageInfo& data);
+ void ReportPerHostInfo(const std::vector<PerHostStorageInfo>& hosts);
+ void ReportPerOriginInfo(const std::vector<PerOriginStorageInfo>& origins);
+ void ReportStatistics(const Statistics& stats);
+
+ // Called on IO Thread by QuotaManager as callback.
+ void DidGetSettings(const storage::QuotaSettings& settings);
+ void DidGetCapacity(int64_t total_space, int64_t available_space);
+ void DidGetGlobalUsage(storage::StorageType type,
+ int64_t usage,
+ int64_t unlimited_usage);
+ void DidDumpQuotaTable(const QuotaTableEntries& entries);
+ void DidDumpOriginInfoTable(const OriginInfoTableEntries& entries);
+ void DidGetHostUsage(const std::string& host,
+ storage::StorageType type,
+ int64_t usage);
+
+ // Helper. Called on IO Thread.
+ void RequestPerOriginInfo(storage::StorageType type);
+ void VisitHost(const std::string& host, storage::StorageType type);
+ void GetHostUsage(const std::string& host, storage::StorageType type);
+
+ // Used on UI Thread.
+ QuotaInternalsHandler* handler_;
+
+ // Used on IO Thread.
+ scoped_refptr<storage::QuotaManager> quota_manager_;
+ std::set<std::pair<std::string, storage::StorageType> > hosts_visited_,
+ hosts_pending_;
+ std::vector<PerHostStorageInfo> report_pending_;
+ base::WeakPtrFactory<QuotaInternalsProxy> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuotaInternalsProxy);
+};
+} // namespace quota_internals
+
+#endif // CHROME_BROWSER_UI_WEBUI_QUOTA_INTERNALS_QUOTA_INTERNALS_PROXY_H_
diff --git a/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_types.cc b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_types.cc
new file mode 100644
index 00000000000..d481c56c322
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_types.cc
@@ -0,0 +1,108 @@
+// Copyright (c) 2011 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/quota_internals/quota_internals_types.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/values.h"
+#include "net/base/url_util.h"
+
+namespace {
+
+std::string StorageTypeToString(storage::StorageType type) {
+ switch (type) {
+ case storage::kStorageTypeTemporary:
+ return "temporary";
+ case storage::kStorageTypePersistent:
+ return "persistent";
+ case storage::kStorageTypeSyncable:
+ return "syncable";
+ case storage::kStorageTypeQuotaNotManaged:
+ return "quota not managed";
+ case storage::kStorageTypeUnknown:
+ return "unknown";
+ }
+ return "unknown";
+}
+
+} // anonymous namespace
+
+namespace quota_internals {
+
+GlobalStorageInfo::GlobalStorageInfo(storage::StorageType type)
+ : type_(type), usage_(-1), unlimited_usage_(-1), quota_(-1) {
+}
+
+GlobalStorageInfo::~GlobalStorageInfo() {}
+
+std::unique_ptr<base::Value> GlobalStorageInfo::NewValue() const {
+ // TODO(tzik): Add CreateLongIntegerValue to base/values.h and remove
+ // all static_cast<double> in this file.
+ std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+ dict->SetString("type", StorageTypeToString(type_));
+ if (usage_ >= 0)
+ dict->SetDouble("usage", static_cast<double>(usage_));
+ if (unlimited_usage_ >= 0)
+ dict->SetDouble("unlimitedUsage", static_cast<double>(unlimited_usage_));
+ if (quota_ >= 0)
+ dict->SetDouble("quota", static_cast<double>(quota_));
+ return std::move(dict);
+}
+
+PerHostStorageInfo::PerHostStorageInfo(const std::string& host,
+ storage::StorageType type)
+ : host_(host), type_(type), usage_(-1), quota_(-1) {
+}
+
+PerHostStorageInfo::~PerHostStorageInfo() {}
+
+std::unique_ptr<base::Value> PerHostStorageInfo::NewValue() const {
+ std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+ DCHECK(!host_.empty());
+ dict->SetString("host", host_);
+ dict->SetString("type", StorageTypeToString(type_));
+ if (usage_ >= 0)
+ dict->SetDouble("usage", static_cast<double>(usage_));
+ if (quota_ >= 0)
+ dict->SetDouble("quota", static_cast<double>(quota_));
+ return std::move(dict);
+}
+
+PerOriginStorageInfo::PerOriginStorageInfo(const GURL& origin,
+ storage::StorageType type)
+ : origin_(origin),
+ type_(type),
+ host_(net::GetHostOrSpecFromURL(origin)),
+ in_use_(-1),
+ used_count_(-1) {
+}
+
+PerOriginStorageInfo::PerOriginStorageInfo(const PerOriginStorageInfo& other) =
+ default;
+
+PerOriginStorageInfo::~PerOriginStorageInfo() {}
+
+std::unique_ptr<base::Value> PerOriginStorageInfo::NewValue() const {
+ std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+ DCHECK(!origin_.is_empty());
+ DCHECK(!host_.empty());
+ dict->SetString("origin", origin_.spec());
+ dict->SetString("type", StorageTypeToString(type_));
+ dict->SetString("host", host_);
+ if (in_use_ >= 0)
+ dict->SetBoolean("inUse", (in_use_ > 0));
+ if (used_count_ >= 0)
+ dict->SetInteger("usedCount", used_count_);
+ if (!last_access_time_.is_null())
+ dict->SetDouble("lastAccessTime", last_access_time_.ToDoubleT() * 1000.0);
+ if (!last_modified_time_.is_null()) {
+ dict->SetDouble("lastModifiedTime",
+ last_modified_time_.ToDoubleT() * 1000.0);
+ }
+ return std::move(dict);
+}
+
+} // 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
new file mode 100644
index 00000000000..94a877e5d79
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_types.h
@@ -0,0 +1,108 @@
+// Copyright (c) 2011 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_QUOTA_INTERNALS_QUOTA_INTERNALS_TYPES_H_
+#define CHROME_BROWSER_UI_WEBUI_QUOTA_INTERNALS_QUOTA_INTERNALS_TYPES_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/time/time.h"
+#include "storage/common/quota/quota_types.h"
+#include "url/gurl.h"
+
+namespace base {
+class Value;
+}
+
+namespace quota_internals {
+
+// Represends global usage and quota information for specific type of storage.
+class GlobalStorageInfo {
+ public:
+ explicit GlobalStorageInfo(storage::StorageType type);
+ ~GlobalStorageInfo();
+
+ void set_usage(int64_t usage) { usage_ = usage; }
+
+ void set_unlimited_usage(int64_t unlimited_usage) {
+ unlimited_usage_ = unlimited_usage;
+ }
+
+ void set_quota(int64_t quota) { quota_ = quota; }
+
+ // Create new Value for passing to WebUI page.
+ std::unique_ptr<base::Value> NewValue() const;
+
+ private:
+ storage::StorageType type_;
+
+ int64_t usage_;
+ int64_t unlimited_usage_;
+ int64_t quota_;
+};
+
+// Represents per host usage and quota information for the storage.
+class PerHostStorageInfo {
+ public:
+ PerHostStorageInfo(const std::string& host, storage::StorageType type);
+ ~PerHostStorageInfo();
+
+ void set_usage(int64_t usage) { usage_ = usage; }
+
+ void set_quota(int64_t quota) { quota_ = quota; }
+
+ // Create new Value for passing to WebUI page.
+ std::unique_ptr<base::Value> NewValue() const;
+
+ private:
+ std::string host_;
+ storage::StorageType type_;
+
+ int64_t usage_;
+ int64_t quota_;
+};
+
+// Represendts per origin usage and access time information.
+class PerOriginStorageInfo {
+ public:
+ PerOriginStorageInfo(const GURL& origin, storage::StorageType type);
+ PerOriginStorageInfo(const PerOriginStorageInfo& other);
+ ~PerOriginStorageInfo();
+
+ void set_in_use(bool in_use) {
+ in_use_ = in_use ? 1 : 0;
+ }
+
+ void set_used_count(int used_count) {
+ used_count_ = used_count;
+ }
+
+ void set_last_access_time(base::Time last_access_time) {
+ last_access_time_ = last_access_time;
+ }
+
+ void set_last_modified_time(base::Time last_modified_time) {
+ last_modified_time_ = last_modified_time;
+ }
+
+ // Create new Value for passing to WebUI page.
+ std::unique_ptr<base::Value> NewValue() const;
+
+ private:
+ GURL origin_;
+ storage::StorageType type_;
+ std::string host_;
+
+ int in_use_;
+ int used_count_;
+ base::Time last_access_time_;
+ base::Time last_modified_time_;
+};
+} // namespace quota_internals
+
+#endif // CHROME_BROWSER_UI_WEBUI_QUOTA_INTERNALS_QUOTA_INTERNALS_TYPES_H_
diff --git a/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_ui.cc b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_ui.cc
new file mode 100644
index 00000000000..d4d06d661a7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_ui.cc
@@ -0,0 +1,44 @@
+// 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/ui/webui/quota_internals/quota_internals_ui.h"
+
+#include <string>
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/quota_internals/quota_internals_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/quota_internals_resources.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+using content::WebContents;
+
+namespace {
+
+content::WebUIDataSource* CreateQuotaInternalsHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIQuotaInternalsHost);
+
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath(
+ "event_handler.js", IDR_QUOTA_INTERNALS_EVENT_HANDLER_JS);
+ source->AddResourcePath(
+ "message_dispatcher.js", IDR_QUOTA_INTERNALS_MESSAGE_DISPATCHER_JS);
+ source->SetDefaultResource(IDR_QUOTA_INTERNALS_MAIN_HTML);
+ source->UseGzip(std::unordered_set<std::string>());
+ return source;
+}
+
+} // namespace
+
+QuotaInternalsUI::QuotaInternalsUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ web_ui->AddMessageHandler(
+ base::MakeUnique<quota_internals::QuotaInternalsHandler>());
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreateQuotaInternalsHTMLSource());
+}
diff --git a/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_ui.h b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_ui.h
new file mode 100644
index 00000000000..9dc78b070ce
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/quota_internals/quota_internals_ui.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 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_QUOTA_INTERNALS_QUOTA_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_QUOTA_INTERNALS_QUOTA_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+class QuotaInternalsUI : public content::WebUIController {
+ public:
+ explicit QuotaInternalsUI(content::WebUI* web_ui);
+ ~QuotaInternalsUI() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuotaInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_QUOTA_INTERNALS_QUOTA_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/sandbox_internals_ui.cc b/chromium/chrome/browser/ui/webui/sandbox_internals_ui.cc
new file mode 100644
index 00000000000..63a6225327e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sandbox_internals_ui.cc
@@ -0,0 +1,83 @@
+// 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/sandbox_internals_ui.h"
+
+#include <string>
+#include <unordered_set>
+
+#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"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+#if defined(OS_LINUX)
+#include "content/public/browser/zygote_host_linux.h"
+#include "content/public/common/sandbox_linux.h"
+#endif
+
+namespace {
+
+#if defined(OS_LINUX)
+static void SetSandboxStatusData(content::WebUIDataSource* source) {
+ // Get expected sandboxing status of renderers.
+ const int status =
+ content::ZygoteHost::GetInstance()->GetRendererSandboxStatus();
+
+ source->AddBoolean("suid", status & content::kSandboxLinuxSUID);
+ source->AddBoolean("userNs", status & content::kSandboxLinuxUserNS);
+ source->AddBoolean("pidNs", status & content::kSandboxLinuxPIDNS);
+ source->AddBoolean("netNs", status & content::kSandboxLinuxNetNS);
+ source->AddBoolean("seccompBpf", status & content::kSandboxLinuxSeccompBPF);
+ source->AddBoolean("seccompTsync",
+ status & content::kSandboxLinuxSeccompTSYNC);
+ source->AddBoolean("yama", status & content::kSandboxLinuxYama);
+
+ // Require either the setuid or namespace sandbox for our first-layer sandbox.
+ bool good_layer1 = (status & content::kSandboxLinuxSUID ||
+ status & content::kSandboxLinuxUserNS) &&
+ status & content::kSandboxLinuxPIDNS &&
+ status & content::kSandboxLinuxNetNS;
+ // A second-layer sandbox is also required to be adequately sandboxed.
+ bool good_layer2 = status & content::kSandboxLinuxSeccompBPF;
+ source->AddBoolean("sandboxGood", good_layer1 && good_layer2);
+}
+#endif
+
+content::WebUIDataSource* CreateDataSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUISandboxHost);
+ source->SetDefaultResource(IDR_SANDBOX_INTERNALS_HTML);
+ source->AddResourcePath("sandbox_internals.js", IDR_SANDBOX_INTERNALS_JS);
+ source->UseGzip(std::unordered_set<std::string>());
+
+#if defined(OS_LINUX)
+ SetSandboxStatusData(source);
+ source->SetJsonPath("strings.js");
+#endif
+
+ return source;
+}
+
+} // namespace
+
+SandboxInternalsUI::SandboxInternalsUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreateDataSource());
+}
+
+void SandboxInternalsUI::RenderFrameCreated(
+ content::RenderFrameHost* render_frame_host) {
+#if defined(OS_ANDROID)
+ render_frame_host->Send(new ChromeViewMsg_AddSandboxStatusExtension(
+ render_frame_host->GetRoutingID()));
+#endif
+}
+
+SandboxInternalsUI::~SandboxInternalsUI() {}
diff --git a/chromium/chrome/browser/ui/webui/sandbox_internals_ui.h b/chromium/chrome/browser/ui/webui/sandbox_internals_ui.h
new file mode 100644
index 00000000000..c8c59692774
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sandbox_internals_ui.h
@@ -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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SANDBOX_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SANDBOX_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// This WebUI page displays the status of the renderer sandbox on Linux and
+// Android. The two OSes share the same basic page, but the data reported are
+// obtained from different places:
+// - On Linux, this object in the browser queries the renderer ZygoteHost
+// to get the sandbox status of the renderers. The data are then specified
+// as loadTimeData on the WebUI Page.
+// - On Android, this object sends an IPC message to the
+// SandboxStatusExtension in the renderer, which installs a JavaScript
+// function on the web page to return the current sandbox status.
+class SandboxInternalsUI : public content::WebUIController {
+ public:
+ explicit SandboxInternalsUI(content::WebUI* web_ui);
+ ~SandboxInternalsUI() override;
+
+ void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SandboxInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SANDBOX_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/set_as_default_browser_ui_browsertest_win.cc b/chromium/chrome/browser/ui/webui/set_as_default_browser_ui_browsertest_win.cc
new file mode 100644
index 00000000000..eef4638710b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/set_as_default_browser_ui_browsertest_win.cc
@@ -0,0 +1,60 @@
+// 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 "chrome/browser/ui/webui/set_as_default_browser_ui_win.h"
+
+#include "base/command_line.h"
+#include "base/win/windows_version.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "ui/views/widget/widget.h"
+
+namespace {
+
+bool IsBrowserVisible(Browser* browser) {
+ return views::Widget::GetWidgetForNativeWindow(
+ browser->window()->GetNativeWindow())
+ ->IsVisible();
+}
+
+} // namespace
+
+using SetAsDefaultBrowserUIBrowserTestWithoutFirstRun = InProcessBrowserTest;
+
+class SetAsDefaultBrowserUIBrowserTestWithFirstRun
+ : public InProcessBrowserTest {
+ public:
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ InProcessBrowserTest::SetUpCommandLine(command_line);
+ command_line->AppendSwitch(switches::kForceFirstRun);
+ }
+
+ protected:
+ void TearDownInProcessBrowserTestFixture() override {
+ ASSERT_FALSE(SetAsDefaultBrowserUI::GetDialogWidgetForTesting());
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(SetAsDefaultBrowserUIBrowserTestWithFirstRun, Test) {
+ // Windows 8 only test case.
+ if (base::win::GetVersion() <= base::win::VERSION_WIN7 ||
+ base::win::GetVersion() >= base::win::VERSION_WIN10) {
+ return;
+ }
+ ASSERT_FALSE(IsBrowserVisible(browser()));
+ views::Widget* dialog_widget =
+ SetAsDefaultBrowserUI::GetDialogWidgetForTesting();
+ ASSERT_TRUE(dialog_widget);
+ ASSERT_TRUE(dialog_widget->IsVisible());
+ dialog_widget->CloseNow();
+ ASSERT_TRUE(IsBrowserVisible(browser()));
+}
+
+IN_PROC_BROWSER_TEST_F(SetAsDefaultBrowserUIBrowserTestWithoutFirstRun,
+ TestWithoutFirstRun) {
+ ASSERT_TRUE(IsBrowserVisible(browser()));
+ EXPECT_EQ(nullptr, SetAsDefaultBrowserUI::GetDialogWidgetForTesting());
+}
diff --git a/chromium/chrome/browser/ui/webui/set_as_default_browser_ui_win.cc b/chromium/chrome/browser/ui/webui/set_as_default_browser_ui_win.cc
new file mode 100644
index 00000000000..d0cecfb2392
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/set_as_default_browser_ui_win.cc
@@ -0,0 +1,371 @@
+// 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/ui/webui/set_as_default_browser_ui_win.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/metrics/histogram_macros.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/shell_integration.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_list_observer.h"
+#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/startup/default_browser_prompt.h"
+#include "chrome/browser/ui/sync/sync_promo_ui.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/locale_settings.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "ui/base/l10n/l10n_font_util.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/font.h"
+#include "ui/views/widget/widget.h"
+#include "ui/web_dialogs/web_dialog_delegate.h"
+
+using content::BrowserThread;
+using content::WebContents;
+using content::WebUIMessageHandler;
+
+namespace {
+
+const char kSetAsDefaultBrowserHistogram[] = "DefaultBrowser.InteractionResult";
+
+// The enum permits registering in UMA the three possible outcomes (do not
+// reorder these).
+// ACCEPTED: user pressed Next and made Chrome default.
+// DECLINED: user simply closed the dialog without making Chrome default.
+// REGRETTED: user pressed Next but then elected a different default browser.
+enum MakeChromeDefaultResult {
+ MAKE_CHROME_DEFAULT_ACCEPTED = 0,
+ MAKE_CHROME_DEFAULT_DECLINED = 1,
+ MAKE_CHROME_DEFAULT_REGRETTED = 2,
+ // MAKE_CHROME_DEFAULT_ACCEPTED_IMMERSE = 3, // Deprecated.
+ MAKE_CHROME_DEFAULT_MAX
+};
+
+content::WebUIDataSource* CreateSetAsDefaultBrowserUIHTMLSource() {
+ content::WebUIDataSource* data_source =
+ content::WebUIDataSource::Create(chrome::kChromeUIMetroFlowHost);
+ data_source->AddLocalizedString("page-title", IDS_METRO_FLOW_TAB_TITLE);
+ data_source->AddLocalizedString("flowTitle", IDS_METRO_FLOW_TITLE_SHORT);
+ data_source->AddLocalizedString("flowDescription",
+ IDS_METRO_FLOW_DESCRIPTION);
+ data_source->AddLocalizedString("flowNext", IDS_METRO_FLOW_SET_DEFAULT);
+ data_source->AddLocalizedString("chromeLogoString",
+ IDS_SHORT_PRODUCT_LOGO_ALT_TEXT);
+ data_source->SetJsonPath("strings.js");
+ data_source->AddResourcePath("set_as_default_browser.js",
+ IDR_SET_AS_DEFAULT_BROWSER_JS);
+ data_source->SetDefaultResource(IDR_SET_AS_DEFAULT_BROWSER_HTML);
+ return data_source;
+}
+
+// A simple class serving as a delegate for passing down the result of the
+// interaction.
+class ResponseDelegate {
+ public:
+ virtual void SetDialogInteractionResult(MakeChromeDefaultResult result) = 0;
+
+ protected:
+ virtual ~ResponseDelegate() {}
+};
+
+// Event handler for SetAsDefaultBrowserUI. Capable of setting Chrome as the
+// default browser on button click, closing itself and triggering Chrome
+// restart.
+class SetAsDefaultBrowserHandler : public WebUIMessageHandler {
+ public:
+ explicit SetAsDefaultBrowserHandler(
+ const base::WeakPtr<ResponseDelegate>& response_delegate);
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ // Handler for the 'Next' (or 'make Chrome the Metro browser') button.
+ void HandleLaunchSetDefaultBrowserFlow(const base::ListValue* args);
+
+ // Close this web ui.
+ void ConcludeInteraction(MakeChromeDefaultResult interaction_result);
+
+ void OnDefaultBrowserWorkerFinished(
+ shell_integration::DefaultWebClientState state);
+
+ // The worker pointer is reference counted. While it is running, the
+ // message loops of the FILE and UI thread will hold references to it
+ // and it will be automatically freed once all its tasks have finished.
+ scoped_refptr<shell_integration::DefaultBrowserWorker>
+ default_browser_worker_;
+ base::WeakPtr<ResponseDelegate> response_delegate_;
+
+ // Used to invalidate the DefaultBrowserWorker callback.
+ base::WeakPtrFactory<SetAsDefaultBrowserHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SetAsDefaultBrowserHandler);
+};
+
+SetAsDefaultBrowserHandler::SetAsDefaultBrowserHandler(
+ const base::WeakPtr<ResponseDelegate>& response_delegate)
+ : response_delegate_(response_delegate), weak_ptr_factory_(this) {
+ default_browser_worker_ = new shell_integration::DefaultBrowserWorker(
+ base::Bind(&SetAsDefaultBrowserHandler::OnDefaultBrowserWorkerFinished,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void SetAsDefaultBrowserHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "SetAsDefaultBrowser:LaunchSetDefaultBrowserFlow",
+ base::Bind(&SetAsDefaultBrowserHandler::HandleLaunchSetDefaultBrowserFlow,
+ base::Unretained(this)));
+}
+
+void SetAsDefaultBrowserHandler::HandleLaunchSetDefaultBrowserFlow(
+ const base::ListValue* args) {
+ default_browser_worker_->StartSetAsDefault();
+}
+
+void SetAsDefaultBrowserHandler::ConcludeInteraction(
+ MakeChromeDefaultResult interaction_result) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if (response_delegate_)
+ response_delegate_->SetDialogInteractionResult(interaction_result);
+
+ WebContents* contents = web_ui()->GetWebContents();
+
+ if (contents) {
+ content::WebContentsDelegate* delegate = contents->GetDelegate();
+ if (delegate)
+ delegate->CloseContents(contents);
+ }
+}
+
+void SetAsDefaultBrowserHandler::OnDefaultBrowserWorkerFinished(
+ shell_integration::DefaultWebClientState state) {
+ // The callback is expected to be invoked once the procedure has completed.
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (state == shell_integration::NOT_DEFAULT) {
+ // The operation concluded, but Chrome is still not the default. This
+ // suggests the user has decided not to make Chrome the default.
+ ConcludeInteraction(MAKE_CHROME_DEFAULT_REGRETTED);
+ } else if (state == shell_integration::IS_DEFAULT) {
+ ConcludeInteraction(MAKE_CHROME_DEFAULT_ACCEPTED);
+ }
+
+ // Otherwise, keep the dialog open since the user probably didn't make a
+ // choice.
+}
+
+// A web dialog delegate implementation for when 'Make Chrome Metro' UI
+// is displayed on a dialog.
+class SetAsDefaultBrowserDialogImpl : public ui::WebDialogDelegate,
+ public ResponseDelegate,
+ public chrome::BrowserListObserver {
+ public:
+ explicit SetAsDefaultBrowserDialogImpl(Profile* profile);
+ ~SetAsDefaultBrowserDialogImpl() override;
+ // Show a modal web dialog with kChromeUIMetroFlowURL page.
+ void ShowDialog();
+ static views::Widget* dialog_widget() { return dialog_widget_; }
+
+ protected:
+ // Overridden from WebDialogDelegate:
+ ui::ModalType GetDialogModalType() const override;
+ base::string16 GetDialogTitle() const override;
+ GURL GetDialogContentURL() const override;
+ void GetWebUIMessageHandlers(
+ std::vector<WebUIMessageHandler*>* handlers) const override;
+ void GetDialogSize(gfx::Size* size) const override;
+ std::string GetDialogArgs() const override;
+ void OnDialogClosed(const std::string& json_retval) override;
+ void OnCloseContents(WebContents* source, bool* out_close_dialog) override;
+ bool ShouldShowDialogTitle() const override;
+ bool HandleContextMenu(const content::ContextMenuParams& params) override;
+
+ // Overridden from ResponseDelegate:
+ void SetDialogInteractionResult(MakeChromeDefaultResult result) override;
+
+ // Overridden from BrowserListObserver:
+ void OnBrowserAdded(Browser* browser) override;
+ void OnBrowserRemoved(Browser* browser) override;
+
+ private:
+ Profile* profile_;
+ Browser* browser_;
+ mutable bool owns_handler_;
+ base::WeakPtrFactory<ResponseDelegate> response_delegate_ptr_factory_;
+ SetAsDefaultBrowserHandler* handler_;
+ MakeChromeDefaultResult dialog_interaction_result_;
+
+ static views::Widget* dialog_widget_;
+
+ DISALLOW_COPY_AND_ASSIGN(SetAsDefaultBrowserDialogImpl);
+};
+
+// static
+views::Widget* SetAsDefaultBrowserDialogImpl::dialog_widget_ = nullptr;
+
+SetAsDefaultBrowserDialogImpl::SetAsDefaultBrowserDialogImpl(Profile* profile)
+ : profile_(profile),
+ browser_(nullptr),
+ owns_handler_(true),
+ response_delegate_ptr_factory_(this),
+ handler_(new SetAsDefaultBrowserHandler(
+ response_delegate_ptr_factory_.GetWeakPtr())),
+ dialog_interaction_result_(MAKE_CHROME_DEFAULT_DECLINED) {
+ BrowserList::AddObserver(this);
+}
+
+SetAsDefaultBrowserDialogImpl::~SetAsDefaultBrowserDialogImpl() {
+ if (browser_)
+ BrowserList::RemoveObserver(this);
+ if (owns_handler_)
+ delete handler_;
+}
+
+void SetAsDefaultBrowserDialogImpl::ShowDialog() {
+ // Use a NULL parent window to make sure that the dialog will have an item
+ // in the Windows task bar. The code below will make it highlight if the
+ // dialog is not in the foreground.
+ gfx::NativeWindow native_window = chrome::ShowWebDialog(NULL, profile_, this);
+ DCHECK(!dialog_widget_);
+ dialog_widget_ = views::Widget::GetWidgetForNativeWindow(native_window);
+ dialog_widget_->FlashFrame(true);
+}
+
+ui::ModalType SetAsDefaultBrowserDialogImpl::GetDialogModalType() const {
+ return ui::MODAL_TYPE_SYSTEM;
+}
+
+base::string16 SetAsDefaultBrowserDialogImpl::GetDialogTitle() const {
+ return l10n_util::GetStringUTF16(IDS_METRO_FLOW_TAB_TITLE);
+}
+
+GURL SetAsDefaultBrowserDialogImpl::GetDialogContentURL() const {
+ std::string url_string(chrome::kChromeUIMetroFlowURL);
+ return GURL(url_string);
+}
+
+void SetAsDefaultBrowserDialogImpl::GetWebUIMessageHandlers(
+ std::vector<WebUIMessageHandler*>* handlers) const {
+ handlers->push_back(handler_);
+ owns_handler_ = false;
+}
+
+void SetAsDefaultBrowserDialogImpl::GetDialogSize(gfx::Size* size) const {
+ PrefService* prefs = profile_->GetPrefs();
+ gfx::Font approximate_web_font(
+ prefs->GetString(prefs::kWebKitSansSerifFontFamily),
+ prefs->GetInteger(prefs::kWebKitDefaultFontSize));
+
+ *size = ui::GetLocalizedContentsSizeForFont(IDS_METRO_FLOW_WIDTH_CHARS,
+ IDS_METRO_FLOW_HEIGHT_LINES,
+ approximate_web_font);
+}
+
+std::string SetAsDefaultBrowserDialogImpl::GetDialogArgs() const {
+ return "[]";
+}
+
+void SetAsDefaultBrowserDialogImpl::OnDialogClosed(
+ const std::string& json_retval) {
+ // Register the user's response in UMA.
+ UMA_HISTOGRAM_ENUMERATION(kSetAsDefaultBrowserHistogram,
+ dialog_interaction_result_,
+ MAKE_CHROME_DEFAULT_MAX);
+
+ // Suppress showing the default browser infobar if the user explicitly elected
+ // *not to* make Chrome default.
+ if (dialog_interaction_result_ == MAKE_CHROME_DEFAULT_REGRETTED)
+ chrome::DefaultBrowserPromptDeclined(profile_);
+
+ // Carry on with a normal chrome session. For the purpose of surfacing this
+ // dialog the actual browser window had to remain hidden. Now it's time to
+ // show it.
+ if (browser_) {
+ BrowserWindow* window = browser_->window();
+ WebContents* contents = browser_->tab_strip_model()->GetActiveWebContents();
+ window->Show();
+ if (contents)
+ contents->SetInitialFocus();
+ }
+
+ DCHECK(dialog_widget_);
+ dialog_widget_ = nullptr;
+
+ delete this;
+}
+
+void SetAsDefaultBrowserDialogImpl::OnCloseContents(WebContents* source,
+ bool* out_close_dialog) {
+ *out_close_dialog = true;
+}
+
+bool SetAsDefaultBrowserDialogImpl::ShouldShowDialogTitle() const {
+ return true;
+}
+
+bool SetAsDefaultBrowserDialogImpl::HandleContextMenu(
+ const content::ContextMenuParams& params) {
+ return true;
+}
+
+void SetAsDefaultBrowserDialogImpl::SetDialogInteractionResult(
+ MakeChromeDefaultResult result) {
+ dialog_interaction_result_ = result;
+}
+
+void SetAsDefaultBrowserDialogImpl::OnBrowserAdded(Browser* browser) {
+ if (browser_ || !browser || !browser->is_type_tabbed())
+ return;
+ browser_ = browser;
+ ShowDialog();
+}
+
+void SetAsDefaultBrowserDialogImpl::OnBrowserRemoved(Browser* browser) {
+ if (browser_ == browser) {
+ browser_ = NULL;
+ BrowserList::RemoveObserver(this);
+ }
+}
+
+} // namespace
+
+SetAsDefaultBrowserUI::SetAsDefaultBrowserUI(content::WebUI* web_ui)
+ : ui::WebDialogUI(web_ui) {
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui),
+ CreateSetAsDefaultBrowserUIHTMLSource());
+}
+
+// static
+void SetAsDefaultBrowserUI::Show(Profile* profile) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ new SetAsDefaultBrowserDialogImpl(profile);
+}
+
+// static
+views::Widget* SetAsDefaultBrowserUI::GetDialogWidgetForTesting() {
+ return SetAsDefaultBrowserDialogImpl::dialog_widget();
+}
diff --git a/chromium/chrome/browser/ui/webui/set_as_default_browser_ui_win.h b/chromium/chrome/browser/ui/webui/set_as_default_browser_ui_win.h
new file mode 100644
index 00000000000..308200d435f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/set_as_default_browser_ui_win.h
@@ -0,0 +1,34 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SET_AS_DEFAULT_BROWSER_UI_WIN_H_
+#define CHROME_BROWSER_UI_WEBUI_SET_AS_DEFAULT_BROWSER_UI_WIN_H_
+
+#include "base/macros.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+class Profile;
+
+namespace views {
+class Widget;
+}
+
+// The UI used in first-run flow to prompt the user to set Chrome as the
+// default Windows browser and *the browser* of Metro mode. Intended for
+// Windows 8 only.
+class SetAsDefaultBrowserUI : public ui::WebDialogUI {
+ public:
+ explicit SetAsDefaultBrowserUI(content::WebUI* web_ui);
+
+ // Present metroizer UI either in a new singleton tab or in a dialog window.
+ static void Show(Profile* profile);
+
+ // Returns the web dialog widget for testing.
+ static views::Widget* GetDialogWidgetForTesting();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SetAsDefaultBrowserUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SET_AS_DEFAULT_BROWSER_UI_WIN_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/OWNERS b/chromium/chrome/browser/ui/webui/settings/OWNERS
new file mode 100644
index 00000000000..03050fea1b8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/OWNERS
@@ -0,0 +1,3 @@
+file://chrome/browser/resources/settings/OWNERS
+
+# COMPONENT: UI>Settings
diff --git a/chromium/chrome/browser/ui/webui/settings/about_handler.cc b/chromium/chrome/browser/ui/webui/settings/about_handler.cc
new file mode 100644
index 00000000000..05db7d110ad
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/about_handler.cc
@@ -0,0 +1,695 @@
+// 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 "chrome/browser/ui/webui/settings/about_handler.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "ash/system/devicetype_utils.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/i18n/message_formatter.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/obsolete_system/obsolete_system.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/chrome_content_client.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/google/core/browser/google_util.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/policy_constants.h"
+#include "components/strings/grit/components_chromium_strings.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/version_info/version_info.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/common/user_agent.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "v8/include/v8-version-string.h"
+
+#if defined(OS_CHROMEOS)
+#include "base/files/file_util_proxy.h"
+#include "base/i18n/time_formatting.h"
+#include "base/sys_info.h"
+#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
+#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/chromeos/settings/cros_settings.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/chromeos/image_source.h"
+#include "chrome/browser/ui/webui/help/help_utils_chromeos.h"
+#include "chrome/browser/ui/webui/help/version_updater_chromeos.h"
+#include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/power_manager_client.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "chromeos/system/statistics_provider.h"
+#include "components/user_manager/user_manager.h"
+#endif
+
+using base::ListValue;
+using content::BrowserThread;
+
+namespace {
+
+#if defined(OS_CHROMEOS)
+
+// Directory containing the regulatory labels for supported regions.
+const char kRegulatoryLabelsDirectory[] = "regulatory_labels";
+
+// File names of the image file and the file containing alt text for the label.
+const char kRegulatoryLabelImageFilename[] = "label.png";
+const char kRegulatoryLabelTextFilename[] = "label.txt";
+
+// Default region code to use if there's no label for the VPD region code.
+const char kDefaultRegionCode[] = "us";
+
+struct RegulatoryLabel {
+ const std::string label_text;
+ const std::string image_url;
+};
+
+// Returns message that informs user that for update it's better to
+// connect to a network of one of the allowed types.
+base::string16 GetAllowedConnectionTypesMessage() {
+ const chromeos::NetworkState* network = chromeos::NetworkHandler::Get()
+ ->network_state_handler()
+ ->DefaultNetwork();
+ const bool mobile_data =
+ network && network->IsConnectedState() && network->IsUsingMobileData();
+
+ if (help_utils_chromeos::IsUpdateOverCellularAllowed(
+ true /* interactive */)) {
+ return mobile_data
+ ? l10n_util::GetStringUTF16(
+ IDS_UPGRADE_NETWORK_LIST_CELLULAR_ALLOWED_NOT_AUTOMATIC)
+ : l10n_util::GetStringUTF16(
+ IDS_UPGRADE_NETWORK_LIST_CELLULAR_ALLOWED);
+ } else {
+ return l10n_util::GetStringUTF16(
+ IDS_UPGRADE_NETWORK_LIST_CELLULAR_DISALLOWED);
+ }
+}
+
+// Returns true if the device is enterprise managed, false otherwise.
+bool IsEnterpriseManaged() {
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ return connector->IsEnterpriseManaged();
+}
+
+// Returns true if current user can change channel, false otherwise.
+bool CanChangeChannel(Profile* profile) {
+ // On a managed machine we delegate this setting to the users of the same
+ // domain only if the policy value is "domain".
+ if (IsEnterpriseManaged()) {
+ bool value = false;
+ chromeos::CrosSettings::Get()->GetBoolean(
+ chromeos::kReleaseChannelDelegated, &value);
+ if (!value)
+ return false;
+
+ // Get the currently logged-in user and strip the domain part only.
+ std::string domain = "";
+ const user_manager::User* user =
+ profile ? chromeos::ProfileHelper::Get()->GetUserByProfile(profile)
+ : nullptr;
+ std::string email =
+ user ? user->GetAccountId().GetUserEmail() : std::string();
+ size_t at_pos = email.find('@');
+ if (at_pos != std::string::npos && at_pos + 1 < email.length())
+ domain = email.substr(email.find('@') + 1);
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ return domain == connector->GetEnterpriseDomain();
+ }
+
+ // On non-managed machines, only the local owner can change the channel.
+ chromeos::OwnerSettingsServiceChromeOS* service =
+ chromeos::OwnerSettingsServiceChromeOSFactory::GetInstance()
+ ->GetForBrowserContext(profile);
+ return service && service->IsOwner();
+}
+
+// Returns the path of the regulatory labels directory for a given region, if
+// found. Must be called from the blocking pool.
+base::FilePath GetRegulatoryLabelDirForRegion(const std::string& region) {
+ // Generate the path under the asset dir or URL host to the regulatory files
+ // for the region, e.g., "regulatory_labels/us/".
+ const base::FilePath region_path =
+ base::FilePath(kRegulatoryLabelsDirectory).AppendASCII(region);
+
+ // Check for file existence starting in /usr/share/chromeos-assets/, e.g.,
+ // "/usr/share/chromeos-assets/regulatory_labels/us/label.png".
+ const base::FilePath asset_dir(chrome::kChromeOSAssetPath);
+ if (base::PathExists(asset_dir.Append(region_path)
+ .AppendASCII(kRegulatoryLabelImageFilename))) {
+ return region_path;
+ }
+
+ return base::FilePath();
+}
+
+// Finds the directory for the regulatory label, using the VPD region code.
+// Also tries "us" as a fallback region. Must be called from the blocking pool.
+base::FilePath FindRegulatoryLabelDir() {
+ std::string region;
+ base::FilePath region_path;
+ // Use the VPD region code to find the label dir.
+ if (chromeos::system::StatisticsProvider::GetInstance()->GetMachineStatistic(
+ "region", &region) &&
+ !region.empty()) {
+ region_path = GetRegulatoryLabelDirForRegion(region);
+ }
+
+ // Try the fallback region code if no directory was found.
+ if (region_path.empty() && region != kDefaultRegionCode)
+ region_path = GetRegulatoryLabelDirForRegion(kDefaultRegionCode);
+
+ return region_path;
+}
+
+// Reads the file containing the regulatory label text, if found, relative to
+// the asset directory. Must be called from the blocking pool.
+std::string ReadRegulatoryLabelText(const base::FilePath& label_dir_path) {
+ base::FilePath text_path(chrome::kChromeOSAssetPath);
+ text_path = text_path.Append(label_dir_path);
+ text_path = text_path.AppendASCII(kRegulatoryLabelTextFilename);
+
+ std::string contents;
+ if (base::ReadFileToString(text_path, &contents))
+ return contents;
+ return std::string();
+}
+
+std::unique_ptr<base::DictionaryValue> GetVersionInfo() {
+ std::unique_ptr<base::DictionaryValue> version_info(
+ new base::DictionaryValue);
+
+ version_info->SetString("osVersion",
+ chromeos::version_loader::GetVersion(
+ chromeos::version_loader::VERSION_FULL));
+ version_info->SetString("arcVersion",
+ chromeos::version_loader::GetARCVersion());
+ version_info->SetString("osFirmware",
+ chromeos::version_loader::GetFirmware());
+
+ return version_info;
+}
+
+#endif // defined(OS_CHROMEOS)
+
+std::string UpdateStatusToString(VersionUpdater::Status status) {
+ std::string status_str;
+ switch (status) {
+ case VersionUpdater::CHECKING:
+ status_str = "checking";
+ break;
+ case VersionUpdater::UPDATING:
+ status_str = "updating";
+ break;
+ case VersionUpdater::NEARLY_UPDATED:
+ status_str = "nearly_updated";
+ break;
+ case VersionUpdater::UPDATED:
+ status_str = "updated";
+ break;
+ case VersionUpdater::FAILED:
+ case VersionUpdater::FAILED_OFFLINE:
+ case VersionUpdater::FAILED_CONNECTION_TYPE_DISALLOWED:
+ status_str = "failed";
+ break;
+ case VersionUpdater::DISABLED:
+ status_str = "disabled";
+ break;
+ case VersionUpdater::DISABLED_BY_ADMIN:
+ status_str = "disabled_by_admin";
+ break;
+ case VersionUpdater::NEED_PERMISSION_TO_UPDATE:
+ status_str = "need_permission_to_update";
+ break;
+ }
+
+ return status_str;
+}
+
+} // namespace
+
+namespace settings {
+
+AboutHandler::AboutHandler() : weak_factory_(this) {}
+
+AboutHandler::~AboutHandler() {}
+
+AboutHandler* AboutHandler::Create(content::WebUIDataSource* html_source,
+ Profile* profile) {
+ html_source->AddString(
+ "aboutBrowserVersion",
+ l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_ABOUT_PAGE_BROWSER_VERSION,
+ base::UTF8ToUTF16(version_info::GetVersionNumber()),
+ l10n_util::GetStringUTF16(version_info::IsOfficialBuild()
+ ? IDS_VERSION_UI_OFFICIAL
+ : IDS_VERSION_UI_UNOFFICIAL),
+ base::UTF8ToUTF16(chrome::GetChannelString()),
+#if defined(ARCH_CPU_64_BITS)
+ l10n_util::GetStringUTF16(IDS_VERSION_UI_64BIT)));
+#else
+ l10n_util::GetStringUTF16(IDS_VERSION_UI_32BIT)));
+#endif
+
+ html_source->AddString(
+ "aboutProductCopyright",
+ base::i18n::MessageFormatter::FormatWithNumberedArgs(
+ l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_COPYRIGHT),
+ base::Time::Now()));
+
+ base::string16 license = l10n_util::GetStringFUTF16(
+ IDS_VERSION_UI_LICENSE, base::ASCIIToUTF16(chrome::kChromiumProjectURL),
+ base::ASCIIToUTF16(chrome::kChromeUICreditsURL));
+ html_source->AddString("aboutProductLicense", license);
+
+ html_source->AddBoolean("aboutObsoleteNowOrSoon",
+ ObsoleteSystem::IsObsoleteNowOrSoon());
+ html_source->AddBoolean("aboutObsoleteEndOfTheLine",
+ ObsoleteSystem::IsObsoleteNowOrSoon() &&
+ ObsoleteSystem::IsEndOfTheLine());
+ html_source->AddString("aboutObsoleteSystem",
+ ObsoleteSystem::LocalizedObsoleteString());
+ html_source->AddString("aboutObsoleteSystemURL",
+ ObsoleteSystem::GetLinkURL());
+
+#if defined(GOOGLE_CHROME_BUILD)
+ base::string16 tos = l10n_util::GetStringFUTF16(
+ IDS_ABOUT_TERMS_OF_SERVICE, base::UTF8ToUTF16(chrome::kChromeUITermsURL));
+ html_source->AddString("aboutProductTos", tos);
+#endif
+
+#if defined(OS_CHROMEOS)
+ base::string16 os_license = l10n_util::GetStringFUTF16(
+ IDS_ABOUT_CROS_VERSION_LICENSE,
+ base::ASCIIToUTF16(chrome::kChromeUIOSCreditsURL));
+ html_source->AddString("aboutProductOsLicense", os_license);
+ html_source->AddBoolean("aboutEnterpriseManaged", IsEnterpriseManaged());
+
+ base::Time build_time = base::SysInfo::GetLsbReleaseTime();
+ base::string16 build_date = base::TimeFormatFriendlyDate(build_time);
+ html_source->AddString("aboutBuildDate", build_date);
+
+ base::CommandLine::StringType command_line =
+ base::CommandLine::ForCurrentProcess()->GetCommandLineString();
+ html_source->AddString("aboutCommandLine", command_line);
+
+ html_source->AddString("aboutUserAgent", GetUserAgent());
+ html_source->AddString("aboutJsEngineVersion", V8_VERSION_STRING);
+ html_source->AddString("aboutBlinkVersion", content::GetWebKitVersion());
+#endif
+
+ return new AboutHandler();
+}
+
+void AboutHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "aboutPageReady",
+ base::Bind(&AboutHandler::HandlePageReady, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "refreshUpdateStatus",
+ base::Bind(&AboutHandler::HandleRefreshUpdateStatus,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "openFeedbackDialog", base::Bind(&AboutHandler::HandleOpenFeedbackDialog,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "openHelpPage",
+ base::Bind(&AboutHandler::HandleOpenHelpPage, base::Unretained(this)));
+#if defined(OS_CHROMEOS)
+ web_ui()->RegisterMessageCallback(
+ "setChannel",
+ base::Bind(&AboutHandler::HandleSetChannel, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "requestUpdate",
+ base::Bind(&AboutHandler::HandleRequestUpdate, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "requestUpdateOverCellular",
+ base::Bind(&AboutHandler::HandleRequestUpdateOverCellular,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getVersionInfo",
+ base::Bind(&AboutHandler::HandleGetVersionInfo, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getRegulatoryInfo", base::Bind(&AboutHandler::HandleGetRegulatoryInfo,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getChannelInfo", base::Bind(&AboutHandler::HandleGetChannelInfo,
+ base::Unretained(this)));
+#endif
+#if defined(OS_MACOSX)
+ web_ui()->RegisterMessageCallback(
+ "promoteUpdater",
+ base::Bind(&AboutHandler::PromoteUpdater, base::Unretained(this)));
+#endif
+
+#if defined(OS_CHROMEOS)
+ // Handler for the product label image, which will be shown if available.
+ content::URLDataSource::Add(Profile::FromWebUI(web_ui()),
+ new chromeos::ImageSource());
+#endif
+}
+
+void AboutHandler::OnJavascriptAllowed() {
+ version_updater_.reset(VersionUpdater::Create(web_ui()->GetWebContents()));
+ registrar_.Add(this, chrome::NOTIFICATION_UPGRADE_RECOMMENDED,
+ content::NotificationService::AllSources());
+ policy_registrar_.reset(new policy::PolicyChangeRegistrar(
+ g_browser_process->policy_service(),
+ policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string())));
+ policy_registrar_->Observe(
+ policy::key::kDeviceAutoUpdateDisabled,
+ base::Bind(&AboutHandler::OnDeviceAutoUpdatePolicyChanged,
+ base::Unretained(this)));
+}
+
+void AboutHandler::OnJavascriptDisallowed() {
+ version_updater_.reset();
+ policy_registrar_.reset();
+ registrar_.Remove(this, chrome::NOTIFICATION_UPGRADE_RECOMMENDED,
+ content::NotificationService::AllSources());
+}
+
+void AboutHandler::Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK_EQ(chrome::NOTIFICATION_UPGRADE_RECOMMENDED, type);
+
+ // A version update is installed and ready to go. Refresh the UI so the
+ // correct state will be shown.
+ RequestUpdate();
+}
+
+void AboutHandler::OnDeviceAutoUpdatePolicyChanged(
+ const base::Value* previous_policy,
+ const base::Value* current_policy) {
+ bool previous_auto_update_disabled = false;
+ if (previous_policy)
+ CHECK(previous_policy->GetAsBoolean(&previous_auto_update_disabled));
+
+ bool current_auto_update_disabled = false;
+ if (current_policy)
+ CHECK(current_policy->GetAsBoolean(&current_auto_update_disabled));
+
+ if (current_auto_update_disabled != previous_auto_update_disabled) {
+ // Refresh the update status to refresh the status of the UI.
+ RefreshUpdateStatus();
+ }
+}
+
+void AboutHandler::HandlePageReady(const base::ListValue* args) {
+ AllowJavascript();
+}
+
+void AboutHandler::HandleRefreshUpdateStatus(const base::ListValue* args) {
+ RefreshUpdateStatus();
+}
+
+void AboutHandler::RefreshUpdateStatus() {
+// On Chrome OS, do not check for an update automatically.
+#if defined(OS_CHROMEOS)
+ static_cast<VersionUpdaterCros*>(version_updater_.get())
+ ->GetUpdateStatus(
+ base::Bind(&AboutHandler::SetUpdateStatus, base::Unretained(this)));
+#else
+ RequestUpdate();
+#endif
+}
+
+#if defined(OS_MACOSX)
+void AboutHandler::PromoteUpdater(const base::ListValue* args) {
+ version_updater_->PromoteUpdater();
+}
+#endif
+
+void AboutHandler::HandleOpenFeedbackDialog(const base::ListValue* args) {
+ DCHECK(args->empty());
+ Browser* browser =
+ chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
+ chrome::OpenFeedbackDialog(browser,
+ chrome::kFeedbackSourceMdSettingsAboutPage);
+}
+
+void AboutHandler::HandleOpenHelpPage(const base::ListValue* args) {
+ DCHECK(args->empty());
+ Browser* browser =
+ chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
+ chrome::ShowHelp(browser, chrome::HELP_SOURCE_WEBUI);
+}
+
+#if defined(OS_CHROMEOS)
+
+void AboutHandler::HandleSetChannel(const base::ListValue* args) {
+ DCHECK(args->GetSize() == 2);
+
+ if (!CanChangeChannel(Profile::FromWebUI(web_ui()))) {
+ LOG(WARNING) << "Non-owner tried to change release track.";
+ return;
+ }
+
+ base::string16 channel;
+ bool is_powerwash_allowed;
+ if (!args->GetString(0, &channel) ||
+ !args->GetBoolean(1, &is_powerwash_allowed)) {
+ LOG(ERROR) << "Can't parse SetChannel() args";
+ return;
+ }
+
+ version_updater_->SetChannel(base::UTF16ToUTF8(channel),
+ is_powerwash_allowed);
+ if (user_manager::UserManager::Get()->IsCurrentUserOwner()) {
+ // Check for update after switching release channel.
+ version_updater_->CheckForUpdate(
+ base::Bind(&AboutHandler::SetUpdateStatus, base::Unretained(this)),
+ VersionUpdater::PromoteCallback());
+ }
+}
+
+void AboutHandler::HandleGetVersionInfo(const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ std::string callback_id;
+ CHECK(args->GetString(0, &callback_id));
+
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+ base::Bind(&GetVersionInfo),
+ base::Bind(&AboutHandler::OnGetVersionInfoReady,
+ weak_factory_.GetWeakPtr(), callback_id));
+}
+
+void AboutHandler::OnGetVersionInfoReady(
+ std::string callback_id,
+ std::unique_ptr<base::DictionaryValue> version_info) {
+ ResolveJavascriptCallback(base::Value(callback_id), *version_info);
+}
+
+void AboutHandler::HandleGetRegulatoryInfo(const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ std::string callback_id;
+ CHECK(args->GetString(0, &callback_id));
+
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+ base::Bind(&FindRegulatoryLabelDir),
+ base::Bind(&AboutHandler::OnRegulatoryLabelDirFound,
+ weak_factory_.GetWeakPtr(), callback_id));
+}
+
+void AboutHandler::HandleGetChannelInfo(const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ std::string callback_id;
+ CHECK(args->GetString(0, &callback_id));
+ version_updater_->GetChannel(
+ true /* get current channel */,
+ base::Bind(&AboutHandler::OnGetCurrentChannel, weak_factory_.GetWeakPtr(),
+ callback_id));
+}
+
+void AboutHandler::OnGetCurrentChannel(std::string callback_id,
+ const std::string& current_channel) {
+ version_updater_->GetChannel(
+ false /* get target channel */,
+ base::Bind(&AboutHandler::OnGetTargetChannel, weak_factory_.GetWeakPtr(),
+ callback_id, current_channel));
+}
+
+void AboutHandler::OnGetTargetChannel(std::string callback_id,
+ const std::string& current_channel,
+ const std::string& target_channel) {
+ std::unique_ptr<base::DictionaryValue> channel_info(
+ new base::DictionaryValue);
+ channel_info->SetString("currentChannel", current_channel);
+ channel_info->SetString("targetChannel", target_channel);
+ channel_info->SetBoolean("canChangeChannel",
+ CanChangeChannel(Profile::FromWebUI(web_ui())));
+
+ ResolveJavascriptCallback(base::Value(callback_id), *channel_info);
+}
+
+void AboutHandler::HandleRequestUpdate(const base::ListValue* args) {
+ RequestUpdate();
+}
+
+void AboutHandler::HandleRequestUpdateOverCellular(
+ const base::ListValue* args) {
+ CHECK_EQ(2U, args->GetSize());
+
+ std::string target_version;
+ std::string target_size_string;
+ int64_t target_size;
+
+ CHECK(args->GetString(0, &target_version));
+ CHECK(args->GetString(1, &target_size_string));
+ CHECK(base::StringToInt64(target_size_string, &target_size));
+
+ RequestUpdateOverCellular(target_version, target_size);
+}
+
+void AboutHandler::RequestUpdateOverCellular(const std::string& target_version,
+ int64_t target_size) {
+ version_updater_->SetUpdateOverCellularTarget(
+ base::Bind(&AboutHandler::SetUpdateStatus, base::Unretained(this)),
+ target_version, target_size);
+}
+
+#endif // defined(OS_CHROMEOS)
+
+void AboutHandler::RequestUpdate() {
+ version_updater_->CheckForUpdate(
+ base::Bind(&AboutHandler::SetUpdateStatus, base::Unretained(this)),
+#if defined(OS_MACOSX)
+ base::Bind(&AboutHandler::SetPromotionState, base::Unretained(this)));
+#else
+ VersionUpdater::PromoteCallback());
+#endif // OS_MACOSX
+}
+
+void AboutHandler::SetUpdateStatus(VersionUpdater::Status status,
+ int progress,
+ const std::string& version,
+ int64_t size,
+ const base::string16& message) {
+ // Only UPDATING state should have progress set.
+ DCHECK(status == VersionUpdater::UPDATING || progress == 0);
+
+ std::unique_ptr<base::DictionaryValue> event(new base::DictionaryValue);
+ event->SetString("status", UpdateStatusToString(status));
+ event->SetString("message", message);
+ event->SetInteger("progress", progress);
+ event->SetString("version", version);
+ // DictionaryValue does not support int64_t, so convert to string.
+ event->SetString("size", base::Int64ToString(size));
+#if defined(OS_CHROMEOS)
+ if (status == VersionUpdater::FAILED_OFFLINE ||
+ status == VersionUpdater::FAILED_CONNECTION_TYPE_DISALLOWED) {
+ base::string16 types_msg = GetAllowedConnectionTypesMessage();
+ if (!types_msg.empty())
+ event->SetString("connectionTypes", types_msg);
+ else
+ event->Set("connectionTypes", base::MakeUnique<base::Value>());
+ } else {
+ event->Set("connectionTypes", base::MakeUnique<base::Value>());
+ }
+#endif // defined(OS_CHROMEOS)
+
+ FireWebUIListener("update-status-changed", *event);
+}
+
+#if defined(OS_MACOSX)
+void AboutHandler::SetPromotionState(VersionUpdater::PromotionState state) {
+ // Worth noting: PROMOTE_DISABLED indicates that promotion is possible,
+ // there's just something else going on right now (e.g. checking for update).
+ bool hidden = state == VersionUpdater::PROMOTE_HIDDEN;
+ bool disabled = state == VersionUpdater::PROMOTE_HIDDEN ||
+ state == VersionUpdater::PROMOTE_DISABLED ||
+ state == VersionUpdater::PROMOTED;
+ bool actionable = state == VersionUpdater::PROMOTE_DISABLED ||
+ state == VersionUpdater::PROMOTE_ENABLED;
+
+ base::string16 text = base::string16();
+ if (actionable)
+ text = l10n_util::GetStringUTF16(IDS_ABOUT_CHROME_AUTOUPDATE_ALL);
+ else if (state == VersionUpdater::PROMOTED)
+ text = l10n_util::GetStringUTF16(IDS_ABOUT_CHROME_AUTOUPDATE_ALL_IS_ON);
+
+ base::DictionaryValue promo_state;
+ promo_state.SetBoolean("hidden", hidden);
+ promo_state.SetBoolean("disabled", disabled);
+ promo_state.SetBoolean("actionable", actionable);
+ if (!text.empty())
+ promo_state.SetString("text", text);
+
+ FireWebUIListener("promotion-state-changed", promo_state);
+}
+#endif // defined(OS_MACOSX)
+
+#if defined(OS_CHROMEOS)
+void AboutHandler::OnRegulatoryLabelDirFound(
+ std::string callback_id,
+ const base::FilePath& label_dir_path) {
+ if (label_dir_path.empty()) {
+ ResolveJavascriptCallback(base::Value(callback_id), base::Value());
+ return;
+ }
+
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+ base::Bind(&ReadRegulatoryLabelText, label_dir_path),
+ base::Bind(&AboutHandler::OnRegulatoryLabelTextRead,
+ weak_factory_.GetWeakPtr(), callback_id, label_dir_path));
+}
+
+void AboutHandler::OnRegulatoryLabelTextRead(
+ std::string callback_id,
+ const base::FilePath& label_dir_path,
+ const std::string& text) {
+ std::unique_ptr<base::DictionaryValue> regulatory_info(
+ new base::DictionaryValue);
+ // Remove unnecessary whitespace.
+ regulatory_info->SetString("text", base::CollapseWhitespaceASCII(text, true));
+
+ std::string image_path =
+ label_dir_path.AppendASCII(kRegulatoryLabelImageFilename).MaybeAsASCII();
+ std::string url =
+ std::string("chrome://") + chrome::kChromeOSAssetHost + "/" + image_path;
+ regulatory_info->SetString("url", url);
+
+ ResolveJavascriptCallback(base::Value(callback_id), *regulatory_info);
+}
+#endif // defined(OS_CHROMEOS)
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/about_handler.h b/chromium/chrome/browser/ui/webui/settings/about_handler.h
new file mode 100644
index 00000000000..5dc9d2331b3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/about_handler.h
@@ -0,0 +1,168 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_ABOUT_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ABOUT_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+#include "build/build_config.h"
+#include "chrome/browser/ui/webui/help/version_updater.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "components/policy/core/common/policy_service.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+#if defined(OS_CHROMEOS)
+#include "base/task/cancelable_task_tracker.h"
+#include "chromeos/system/version_loader.h"
+#endif // defined(OS_CHROMEOS)
+
+namespace base {
+class DictionaryValue;
+class FilePath;
+class ListValue;
+}
+
+namespace content {
+class WebUIDataSource;
+}
+
+class Profile;
+
+namespace settings {
+
+// WebUI message handler for the help page.
+class AboutHandler : public settings::SettingsPageUIHandler,
+ public content::NotificationObserver {
+ public:
+ AboutHandler();
+ ~AboutHandler() override;
+
+ static AboutHandler* Create(content::WebUIDataSource* html_source,
+ Profile* profile);
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ // NotificationObserver implementation.
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ // Returns the browser version as a string.
+ static base::string16 BuildBrowserVersionString();
+
+ private:
+ void OnDeviceAutoUpdatePolicyChanged(const base::Value* previous_policy,
+ const base::Value* current_policy);
+
+ // Called once the JS page is ready to be called, serves as a signal to the
+ // handler to register C++ observers.
+ void HandlePageReady(const base::ListValue* args);
+
+ // Called once when the page has loaded. On ChromeOS, this gets the current
+ // update status. On other platforms, it will request and perform an update
+ // (if one is available).
+ void HandleRefreshUpdateStatus(const base::ListValue* args);
+ void RefreshUpdateStatus();
+
+#if defined(OS_MACOSX)
+ // Promotes the updater for all users.
+ void PromoteUpdater(const base::ListValue* args);
+#endif
+
+ // Opens the feedback dialog. |args| must be empty.
+ void HandleOpenFeedbackDialog(const base::ListValue* args);
+
+ // Opens the help page. |args| must be empty.
+ void HandleOpenHelpPage(const base::ListValue* args);
+
+#if defined(OS_CHROMEOS)
+ // Sets the release track version.
+ void HandleSetChannel(const base::ListValue* args);
+
+ // Retrieves OS, ARC and firmware versions.
+ void HandleGetVersionInfo(const base::ListValue* args);
+ void OnGetVersionInfoReady(
+ std::string callback_id,
+ std::unique_ptr<base::DictionaryValue> version_info);
+
+ // Retrieves combined channel info.
+ void HandleGetChannelInfo(const base::ListValue* args);
+ // Callbacks for version_updater_->GetChannel calls.
+ void OnGetCurrentChannel(std::string callback_id,
+ const std::string& current_channel);
+ void OnGetTargetChannel(std::string callback_id,
+ const std::string& current_channel,
+ const std::string& target_channel);
+
+ // Checks for and applies update, triggered by JS.
+ void HandleRequestUpdate(const base::ListValue* args);
+
+ // Checks for and applies update over cellular connection, triggered by JS.
+ // Target version and size should be included in the list of arguments.
+ void HandleRequestUpdateOverCellular(const base::ListValue* args);
+
+ // Checks for and applies update over cellular connection to the given target.
+ void RequestUpdateOverCellular(const std::string& target_version,
+ int64_t target_size);
+
+#endif
+
+ // Checks for and applies update.
+ void RequestUpdate();
+
+ // Callback method which forwards status updates to the page.
+ void SetUpdateStatus(VersionUpdater::Status status,
+ int progress,
+ const std::string& version,
+ int64_t size,
+ const base::string16& fail_message);
+
+#if defined(OS_MACOSX)
+ // Callback method which forwards promotion state to the page.
+ void SetPromotionState(VersionUpdater::PromotionState state);
+#endif
+
+#if defined(OS_CHROMEOS)
+ void HandleGetRegulatoryInfo(const base::ListValue* args);
+
+ // Callback for when the directory with the regulatory label image and alt
+ // text has been found.
+ void OnRegulatoryLabelDirFound(std::string callback_id,
+ const base::FilePath& label_dir_path);
+
+ // Callback for when the regulatory text has been read.
+ void OnRegulatoryLabelTextRead(std::string callback_id,
+ const base::FilePath& label_dir_path,
+ const std::string& text);
+#endif
+
+ // Specialized instance of the VersionUpdater used to update the browser.
+ std::unique_ptr<VersionUpdater> version_updater_;
+
+ // Used to observe notifications.
+ content::NotificationRegistrar registrar_;
+
+ // Used to observe changes in the |kDeviceAutoUpdateDisabled| policy.
+ std::unique_ptr<policy::PolicyChangeRegistrar> policy_registrar_;
+
+ // Used for callbacks.
+ base::WeakPtrFactory<AboutHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(AboutHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_ABOUT_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/appearance_handler.cc b/chromium/chrome/browser/ui/webui/settings/appearance_handler.cc
new file mode 100644
index 00000000000..c863519b315
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/appearance_handler.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 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.
+
+#include "chrome/browser/ui/webui/settings/appearance_handler.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/themes/theme_service_factory.h"
+#include "content/public/browser/web_ui.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
+#endif
+
+namespace settings {
+
+AppearanceHandler::AppearanceHandler(content::WebUI* webui)
+ : profile_(Profile::FromWebUI(webui)) {
+}
+
+AppearanceHandler::~AppearanceHandler() {}
+
+void AppearanceHandler::OnJavascriptAllowed() {}
+void AppearanceHandler::OnJavascriptDisallowed() {}
+
+void AppearanceHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "useDefaultTheme",
+ base::Bind(&AppearanceHandler::HandleUseDefaultTheme,
+ base::Unretained(this)));
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+ web_ui()->RegisterMessageCallback(
+ "useSystemTheme",
+ base::Bind(&AppearanceHandler::HandleUseSystemTheme,
+ base::Unretained(this)));
+#endif
+#if defined(OS_CHROMEOS)
+ web_ui()->RegisterMessageCallback(
+ "openWallpaperManager",
+ base::Bind(&AppearanceHandler::HandleOpenWallpaperManager,
+ base::Unretained(this)));
+#endif
+}
+
+void AppearanceHandler::HandleUseDefaultTheme(const base::ListValue* args) {
+ ThemeServiceFactory::GetForProfile(profile_)->UseDefaultTheme();
+}
+
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+void AppearanceHandler::HandleUseSystemTheme(const base::ListValue* args) {
+ if (profile_->IsSupervised())
+ NOTREACHED();
+ else
+ ThemeServiceFactory::GetForProfile(profile_)->UseSystemTheme();
+}
+#endif
+
+#if defined(OS_CHROMEOS)
+void AppearanceHandler::HandleOpenWallpaperManager(
+ const base::ListValue* /*args*/) {
+ chromeos::WallpaperManager::Get()->Open();
+}
+#endif
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/appearance_handler.h b/chromium/chrome/browser/ui/webui/settings/appearance_handler.h
new file mode 100644
index 00000000000..a00a9637dc7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/appearance_handler.h
@@ -0,0 +1,55 @@
+// Copyright (c) 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_APPEARANCE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_APPEARANCE_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace content {
+class WebUI;
+}
+
+class Profile;
+
+namespace settings {
+
+// Chrome "Appearance" settings page UI handler.
+class AppearanceHandler : public SettingsPageUIHandler {
+ public:
+ explicit AppearanceHandler(content::WebUI* webui);
+ ~AppearanceHandler() override;
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ private:
+ // Changes the UI theme of the browser to the default theme.
+ void HandleUseDefaultTheme(const base::ListValue* args);
+
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+ // Changes the UI theme of the browser to the system (GTK+) theme.
+ void HandleUseSystemTheme(const base::ListValue* args);
+#endif
+
+#if defined(OS_CHROMEOS)
+ // Open the wallpaper manager app.
+ void HandleOpenWallpaperManager(const base::ListValue* args);
+#endif
+
+ Profile* profile_; // Weak pointer.
+
+ DISALLOW_COPY_AND_ASSIGN(AppearanceHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_APPEARANCE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc b/chromium/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc
new file mode 100644
index 00000000000..477aa793415
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc
@@ -0,0 +1,80 @@
+// 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 "chrome/browser/ui/webui/settings/browser_lifetime_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "components/user_manager/user_manager.h"
+#endif // defined(OS_CHROMEOS)
+
+namespace settings {
+
+BrowserLifetimeHandler::BrowserLifetimeHandler() {}
+
+BrowserLifetimeHandler::~BrowserLifetimeHandler() {}
+
+void BrowserLifetimeHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("restart",
+ base::Bind(&BrowserLifetimeHandler::HandleRestart,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("relaunch",
+ base::Bind(&BrowserLifetimeHandler::HandleRelaunch,
+ base::Unretained(this)));
+#if defined(OS_CHROMEOS)
+ web_ui()->RegisterMessageCallback("signOutAndRestart",
+ base::Bind(&BrowserLifetimeHandler::HandleSignOutAndRestart,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("factoryReset",
+ base::Bind(&BrowserLifetimeHandler::HandleFactoryReset,
+ base::Unretained(this)));
+#endif // defined(OS_CHROMEOS)
+}
+
+void BrowserLifetimeHandler::HandleRestart(
+ const base::ListValue* args) {
+ chrome::AttemptRestart();
+}
+
+void BrowserLifetimeHandler::HandleRelaunch(
+ const base::ListValue* args) {
+ chrome::AttemptRelaunch();
+}
+
+#if defined(OS_CHROMEOS)
+void BrowserLifetimeHandler::HandleSignOutAndRestart(
+ const base::ListValue* args) {
+ chrome::AttemptUserExit();
+}
+
+void BrowserLifetimeHandler::HandleFactoryReset(
+ const base::ListValue* args) {
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ bool allow_powerwash = !connector->IsEnterpriseManaged() &&
+ !user_manager::UserManager::Get()->IsLoggedInAsGuest() &&
+ !user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser();
+
+ if (!allow_powerwash)
+ return;
+
+ PrefService* prefs = g_browser_process->local_state();
+ prefs->SetBoolean(prefs::kFactoryResetRequested, true);
+ prefs->CommitPendingWrite();
+
+ // Perform sign out. Current chrome process will then terminate, new one will
+ // be launched (as if it was a restart).
+ chrome::AttemptRelaunch();
+}
+#endif // defined(OS_CHROMEOS)
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/browser_lifetime_handler.h b/chromium/chrome/browser/ui/webui/settings/browser_lifetime_handler.h
new file mode 100644
index 00000000000..b5394d1673b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/browser_lifetime_handler.h
@@ -0,0 +1,40 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_BROWSER_LIFETIME_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_BROWSER_LIFETIME_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace settings {
+
+class BrowserLifetimeHandler : public SettingsPageUIHandler {
+ public:
+ BrowserLifetimeHandler();
+ ~BrowserLifetimeHandler() override;
+
+ // SettingsPageUIHandler:
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override {}
+ void OnJavascriptDisallowed() override {}
+
+ private:
+ void HandleRestart(const base::ListValue* /*args*/);
+ void HandleRelaunch(const base::ListValue* /*args*/);
+#if defined(OS_CHROMEOS)
+ void HandleSignOutAndRestart(const base::ListValue* /*args*/);
+ void HandleFactoryReset(const base::ListValue* /*args*/);
+#endif // defined(OS_CHROMEOS)
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserLifetimeHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_BROWSER_LIFETIME_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/certificates_handler.cc b/chromium/chrome/browser/ui/webui/settings/certificates_handler.cc
new file mode 100644
index 00000000000..f3d3a22f896
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/certificates_handler.cc
@@ -0,0 +1,1145 @@
+// 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 "chrome/browser/ui/webui/settings/certificates_handler.h"
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <map>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_util.h" // for FileAccessProvider
+#include "base/i18n/string_compare.h"
+#include "base/id_map.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/posix/safe_strerror.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/certificate_viewer.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/certificate_dialogs.h"
+#include "chrome/browser/ui/chrome_select_file_policy.h"
+#include "chrome/browser/ui/crypto_module_password_dialog_nss.h"
+#include "chrome/browser/ui/webui/certificate_viewer_webui.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "net/base/net_errors.h"
+#include "net/cert/x509_certificate.h"
+#include "net/der/input.h"
+#include "net/der/parser.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/policy/user_network_configuration_updater.h"
+#include "chrome/browser/chromeos/policy/user_network_configuration_updater_factory.h"
+#endif
+
+using base::UTF8ToUTF16;
+using content::BrowserThread;
+
+namespace {
+
+// Field names for communicating certificate info to JS.
+static const char kEmailField[] = "email";
+static const char kExtractableField[] = "extractable";
+static const char kKeyField[] = "id";
+static const char kNameField[] = "name";
+static const char kObjSignField[] = "objSign";
+static const char kPolicyField[] = "policy";
+static const char kReadonlyField[] = "readonly";
+static const char kSslField[] = "ssl";
+static const char kSubnodesField[] = "subnodes";
+static const char kUntrustedField[] = "untrusted";
+
+// Field names for communicating erros to JS.
+static const char kCertificateErrors[] = "certificateErrors";
+static const char kErrorDescription[] = "description";
+static const char kErrorField[] = "error";
+static const char kErrorTitle[] = "title";
+
+// Enumeration of different callers of SelectFile. (Start counting at 1 so
+// if SelectFile is accidentally called with params=NULL it won't match any.)
+enum {
+ EXPORT_PERSONAL_FILE_SELECTED = 1,
+ IMPORT_PERSONAL_FILE_SELECTED,
+ IMPORT_SERVER_FILE_SELECTED,
+ IMPORT_CA_FILE_SELECTED,
+};
+
+std::string OrgNameToId(const std::string& org) {
+ return "org-" + org;
+}
+
+struct DictionaryIdComparator {
+ explicit DictionaryIdComparator(icu::Collator* collator)
+ : collator_(collator) {}
+
+ bool operator()(const base::Value& a, const base::Value& b) const {
+ DCHECK(a.GetType() == base::Value::Type::DICTIONARY);
+ DCHECK(b.GetType() == base::Value::Type::DICTIONARY);
+ const base::DictionaryValue* a_dict;
+ bool a_is_dictionary = a.GetAsDictionary(&a_dict);
+ DCHECK(a_is_dictionary);
+ const base::DictionaryValue* b_dict;
+ bool b_is_dictionary = b.GetAsDictionary(&b_dict);
+ DCHECK(b_is_dictionary);
+ base::string16 a_str;
+ base::string16 b_str;
+ a_dict->GetString(kNameField, &a_str);
+ b_dict->GetString(kNameField, &b_str);
+ if (collator_ == NULL)
+ return a_str < b_str;
+ return base::i18n::CompareString16WithCollator(*collator_, a_str, b_str) ==
+ UCOL_LESS;
+ }
+
+ icu::Collator* collator_;
+};
+
+std::string NetErrorToString(int net_error) {
+ switch (net_error) {
+ // TODO(mattm): handle more cases.
+ case net::ERR_IMPORT_CA_CERT_NOT_CA:
+ return l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_ERROR_NOT_CA);
+ case net::ERR_IMPORT_CERT_ALREADY_EXISTS:
+ return l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_ERROR_CERT_ALREADY_EXISTS);
+ default:
+ return l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_UNKNOWN_ERROR);
+ }
+}
+
+// Struct to bind the Equals member function to an object for use in find_if.
+struct CertEquals {
+ explicit CertEquals(const net::X509Certificate* cert) : cert_(cert) {}
+ bool operator()(const scoped_refptr<net::X509Certificate> cert) const {
+ return cert_->Equals(cert.get());
+ }
+ const net::X509Certificate* cert_;
+};
+
+// Determine whether a certificate was stored with web trust by a policy.
+bool IsPolicyInstalledWithWebTrust(const net::CertificateList& web_trust_certs,
+ net::X509Certificate* cert) {
+ return std::find_if(web_trust_certs.begin(), web_trust_certs.end(),
+ CertEquals(cert)) != web_trust_certs.end();
+}
+
+#if defined(OS_CHROMEOS)
+void ShowCertificateViewerModalDialog(content::WebContents* web_contents,
+ gfx::NativeWindow parent,
+ net::X509Certificate* cert) {
+ CertificateViewerModalDialog* dialog = new CertificateViewerModalDialog(cert);
+ dialog->Show(web_contents, parent);
+}
+#endif
+
+// Determine if |data| could be a PFX Protocol Data Unit.
+// This only does the minimum parsing necessary to distinguish a PFX file from a
+// DER encoded Certificate.
+//
+// From RFC 7292 section 4:
+// PFX ::= SEQUENCE {
+// version INTEGER {v3(3)}(v3,...),
+// authSafe ContentInfo,
+// macData MacData OPTIONAL
+// }
+// From RFC 5280 section 4.1:
+// Certificate ::= SEQUENCE {
+// tbsCertificate TBSCertificate,
+// signatureAlgorithm AlgorithmIdentifier,
+// signatureValue BIT STRING }
+//
+// Certificate must be DER encoded, while PFX may be BER encoded.
+// Therefore PFX can be distingushed by checking if the file starts with an
+// indefinite SEQUENCE, or a definite SEQUENCE { INTEGER, ... }.
+bool CouldBePFX(const std::string& data) {
+ if (data.size() < 4)
+ return false;
+
+ // Indefinite length SEQUENCE.
+ if (data[0] == 0x30 && static_cast<uint8_t>(data[1]) == 0x80)
+ return true;
+
+ // If the SEQUENCE is definite length, it can be parsed through the version
+ // tag using DER parser, since INTEGER must be definite length, even in BER.
+ net::der::Parser parser((net::der::Input(&data)));
+ net::der::Parser sequence_parser;
+ if (!parser.ReadSequence(&sequence_parser))
+ return false;
+ if (!sequence_parser.SkipTag(net::der::kInteger))
+ return false;
+ return true;
+}
+
+} // namespace
+
+namespace settings {
+
+///////////////////////////////////////////////////////////////////////////////
+// CertIdMap
+
+class CertIdMap {
+ public:
+ CertIdMap() {}
+ ~CertIdMap() {}
+
+ std::string CertToId(net::X509Certificate* cert);
+ net::X509Certificate* IdToCert(const std::string& id);
+ net::X509Certificate* CallbackArgsToCert(const base::ListValue* args);
+
+ private:
+ typedef std::map<net::X509Certificate*, int32_t> CertMap;
+
+ // Creates an ID for cert and looks up the cert for an ID.
+ IDMap<net::X509Certificate*> id_map_;
+
+ // Finds the ID for a cert.
+ CertMap cert_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(CertIdMap);
+};
+
+std::string CertIdMap::CertToId(net::X509Certificate* cert) {
+ CertMap::const_iterator iter = cert_map_.find(cert);
+ if (iter != cert_map_.end())
+ return base::IntToString(iter->second);
+
+ int32_t new_id = id_map_.Add(cert);
+ cert_map_[cert] = new_id;
+ return base::IntToString(new_id);
+}
+
+net::X509Certificate* CertIdMap::IdToCert(const std::string& id) {
+ int32_t cert_id = 0;
+ if (!base::StringToInt(id, &cert_id))
+ return NULL;
+
+ return id_map_.Lookup(cert_id);
+}
+
+net::X509Certificate* CertIdMap::CallbackArgsToCert(
+ const base::ListValue* args) {
+ std::string node_id;
+ if (!args->GetString(0, &node_id))
+ return NULL;
+
+ net::X509Certificate* cert = IdToCert(node_id);
+ if (!cert) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ return cert;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FileAccessProvider
+
+// TODO(mattm): Move to some shared location?
+class FileAccessProvider
+ : public base::RefCountedThreadSafe<FileAccessProvider> {
+ public:
+ // The first parameter is 0 on success or errno on failure. The second
+ // parameter is read result.
+ typedef base::Callback<void(const int*, const std::string*)> ReadCallback;
+
+ // The first parameter is 0 on success or errno on failure. The second
+ // parameter is the number of bytes written on success.
+ typedef base::Callback<void(const int*, const int*)> WriteCallback;
+
+ base::CancelableTaskTracker::TaskId StartRead(
+ const base::FilePath& path,
+ const ReadCallback& callback,
+ base::CancelableTaskTracker* tracker);
+ base::CancelableTaskTracker::TaskId StartWrite(
+ const base::FilePath& path,
+ const std::string& data,
+ const WriteCallback& callback,
+ base::CancelableTaskTracker* tracker);
+
+ private:
+ friend class base::RefCountedThreadSafe<FileAccessProvider>;
+ virtual ~FileAccessProvider() {}
+
+ // Reads file at |path|. |saved_errno| is 0 on success or errno on failure.
+ // When success, |data| has file content.
+ void DoRead(const base::FilePath& path, int* saved_errno, std::string* data);
+ // Writes data to file at |path|. |saved_errno| is 0 on success or errno on
+ // failure. When success, |bytes_written| has number of bytes written.
+ void DoWrite(const base::FilePath& path,
+ const std::string& data,
+ int* saved_errno,
+ int* bytes_written);
+};
+
+base::CancelableTaskTracker::TaskId FileAccessProvider::StartRead(
+ const base::FilePath& path,
+ const ReadCallback& callback,
+ base::CancelableTaskTracker* tracker) {
+ // Owned by reply callback posted below.
+ int* saved_errno = new int(0);
+ std::string* data = new std::string();
+
+ // Post task to file thread to read file.
+ return tracker->PostTaskAndReply(
+ BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get(),
+ FROM_HERE,
+ base::BindOnce(&FileAccessProvider::DoRead, this, path, saved_errno,
+ data),
+ base::BindOnce(callback, base::Owned(saved_errno), base::Owned(data)));
+}
+
+base::CancelableTaskTracker::TaskId FileAccessProvider::StartWrite(
+ const base::FilePath& path,
+ const std::string& data,
+ const WriteCallback& callback,
+ base::CancelableTaskTracker* tracker) {
+ // Owned by reply callback posted below.
+ int* saved_errno = new int(0);
+ int* bytes_written = new int(0);
+
+ // Post task to file thread to write file.
+ return tracker->PostTaskAndReply(
+ BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get(),
+ FROM_HERE,
+ base::BindOnce(&FileAccessProvider::DoWrite, this, path, data,
+ saved_errno, bytes_written),
+ base::BindOnce(callback, base::Owned(saved_errno),
+ base::Owned(bytes_written)));
+}
+
+void FileAccessProvider::DoRead(const base::FilePath& path,
+ int* saved_errno,
+ std::string* data) {
+ bool success = base::ReadFileToString(path, data);
+ *saved_errno = success ? 0 : errno;
+}
+
+void FileAccessProvider::DoWrite(const base::FilePath& path,
+ const std::string& data,
+ int* saved_errno,
+ int* bytes_written) {
+ *bytes_written = base::WriteFile(path, data.data(), data.size());
+ *saved_errno = *bytes_written >= 0 ? 0 : errno;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CertificatesHandler
+
+CertificatesHandler::CertificatesHandler(bool show_certs_in_modal_dialog)
+ : show_certs_in_modal_dialog_(show_certs_in_modal_dialog),
+ requested_certificate_manager_model_(false),
+ use_hardware_backed_(false),
+ file_access_provider_(new FileAccessProvider()),
+ cert_id_map_(new CertIdMap),
+ weak_ptr_factory_(this) {}
+
+CertificatesHandler::~CertificatesHandler() {}
+
+void CertificatesHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "viewCertificate", base::Bind(&CertificatesHandler::HandleViewCertificate,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "getCaCertificateTrust",
+ base::Bind(&CertificatesHandler::HandleGetCATrust,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "editCaCertificateTrust",
+ base::Bind(&CertificatesHandler::HandleEditCATrust,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "cancelImportExportCertificate",
+ base::Bind(&CertificatesHandler::HandleCancelImportExportProcess,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "exportPersonalCertificate",
+ base::Bind(&CertificatesHandler::HandleExportPersonal,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "exportPersonalCertificatePasswordSelected",
+ base::Bind(&CertificatesHandler::HandleExportPersonalPasswordSelected,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "importPersonalCertificate",
+ base::Bind(&CertificatesHandler::HandleImportPersonal,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "importPersonalCertificatePasswordSelected",
+ base::Bind(&CertificatesHandler::HandleImportPersonalPasswordSelected,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "importCaCertificate",
+ base::Bind(&CertificatesHandler::HandleImportCA, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "importCaCertificateTrustSelected",
+ base::Bind(&CertificatesHandler::HandleImportCATrustSelected,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "importServerCertificate",
+ base::Bind(&CertificatesHandler::HandleImportServer,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "exportCertificate",
+ base::Bind(&CertificatesHandler::HandleExportCertificate,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "deleteCertificate",
+ base::Bind(&CertificatesHandler::HandleDeleteCertificate,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "refreshCertificates",
+ base::Bind(&CertificatesHandler::HandleRefreshCertificates,
+ base::Unretained(this)));
+}
+
+void CertificatesHandler::CertificatesRefreshed() {
+ net::CertificateList web_trusted_certs;
+#if defined(OS_CHROMEOS)
+ policy::UserNetworkConfigurationUpdater* service =
+ policy::UserNetworkConfigurationUpdaterFactory::GetForProfile(
+ Profile::FromWebUI(web_ui()));
+ if (service)
+ service->GetWebTrustedCertificates(&web_trusted_certs);
+#endif
+ PopulateTree("personalCerts", net::USER_CERT, web_trusted_certs);
+ PopulateTree("serverCerts", net::SERVER_CERT, web_trusted_certs);
+ PopulateTree("caCerts", net::CA_CERT, web_trusted_certs);
+ PopulateTree("otherCerts", net::OTHER_CERT, web_trusted_certs);
+}
+
+void CertificatesHandler::FileSelected(const base::FilePath& path,
+ int index,
+ void* params) {
+ switch (reinterpret_cast<intptr_t>(params)) {
+ case EXPORT_PERSONAL_FILE_SELECTED:
+ ExportPersonalFileSelected(path);
+ break;
+ case IMPORT_PERSONAL_FILE_SELECTED:
+ ImportPersonalFileSelected(path);
+ break;
+ case IMPORT_SERVER_FILE_SELECTED:
+ ImportServerFileSelected(path);
+ break;
+ case IMPORT_CA_FILE_SELECTED:
+ ImportCAFileSelected(path);
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
+void CertificatesHandler::FileSelectionCanceled(void* params) {
+ switch (reinterpret_cast<intptr_t>(params)) {
+ case EXPORT_PERSONAL_FILE_SELECTED:
+ case IMPORT_PERSONAL_FILE_SELECTED:
+ case IMPORT_SERVER_FILE_SELECTED:
+ case IMPORT_CA_FILE_SELECTED:
+ ImportExportCleanup();
+ RejectCallback(base::Value());
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
+void CertificatesHandler::HandleViewCertificate(const base::ListValue* args) {
+ net::X509Certificate* cert = cert_id_map_->CallbackArgsToCert(args);
+ if (!cert)
+ return;
+#if defined(OS_CHROMEOS)
+ if (show_certs_in_modal_dialog_) {
+ ShowCertificateViewerModalDialog(web_ui()->GetWebContents(),
+ GetParentWindow(), cert);
+ return;
+ }
+#endif
+ ShowCertificateViewer(web_ui()->GetWebContents(), GetParentWindow(), cert);
+}
+
+void CertificatesHandler::AssignWebUICallbackId(const base::ListValue* args) {
+ CHECK_LE(1U, args->GetSize());
+ CHECK(webui_callback_id_.empty());
+ CHECK(args->GetString(0, &webui_callback_id_));
+}
+
+void CertificatesHandler::HandleGetCATrust(const base::ListValue* args) {
+ AllowJavascript();
+
+ CHECK_EQ(2U, args->GetSize());
+ AssignWebUICallbackId(args);
+ std::string node_id;
+ CHECK(args->GetString(1, &node_id));
+
+ net::X509Certificate* cert = cert_id_map_->IdToCert(node_id);
+ CHECK(cert);
+
+ net::NSSCertDatabase::TrustBits trust_bits =
+ certificate_manager_model_->cert_db()->GetCertTrust(cert, net::CA_CERT);
+ std::unique_ptr<base::DictionaryValue> ca_trust_info(
+ new base::DictionaryValue);
+ ca_trust_info->SetBoolean(
+ kSslField,
+ static_cast<bool>(trust_bits & net::NSSCertDatabase::TRUSTED_SSL));
+ ca_trust_info->SetBoolean(
+ kEmailField,
+ static_cast<bool>(trust_bits & net::NSSCertDatabase::TRUSTED_EMAIL));
+ ca_trust_info->SetBoolean(
+ kObjSignField,
+ static_cast<bool>(trust_bits & net::NSSCertDatabase::TRUSTED_OBJ_SIGN));
+ ResolveCallback(*ca_trust_info);
+}
+
+void CertificatesHandler::HandleEditCATrust(const base::ListValue* args) {
+ CHECK_EQ(5U, args->GetSize());
+ AssignWebUICallbackId(args);
+ std::string node_id;
+ CHECK(args->GetString(1, &node_id));
+
+ net::X509Certificate* cert = cert_id_map_->IdToCert(node_id);
+ CHECK(cert);
+
+ bool trust_ssl = false;
+ bool trust_email = false;
+ bool trust_obj_sign = false;
+ CHECK(args->GetBoolean(2, &trust_ssl));
+ CHECK(args->GetBoolean(3, &trust_email));
+ CHECK(args->GetBoolean(4, &trust_obj_sign));
+
+ bool result = certificate_manager_model_->SetCertTrust(
+ cert, net::CA_CERT,
+ trust_ssl * net::NSSCertDatabase::TRUSTED_SSL +
+ trust_email * net::NSSCertDatabase::TRUSTED_EMAIL +
+ trust_obj_sign * net::NSSCertDatabase::TRUSTED_OBJ_SIGN);
+ if (!result) {
+ // TODO(mattm): better error messages?
+ RejectCallbackWithError(
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_SET_TRUST_ERROR_TITLE),
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_UNKNOWN_ERROR));
+ } else {
+ ResolveCallback(base::Value());
+ }
+}
+
+void CertificatesHandler::HandleExportPersonal(const base::ListValue* args) {
+ CHECK_EQ(2U, args->GetSize());
+ AssignWebUICallbackId(args);
+ std::string node_id;
+ CHECK(args->GetString(1, &node_id));
+
+ net::X509Certificate* cert = cert_id_map_->IdToCert(node_id);
+ CHECK(cert);
+ selected_cert_list_.push_back(cert);
+
+ ui::SelectFileDialog::FileTypeInfo file_type_info;
+ file_type_info.extensions.resize(1);
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("p12"));
+ file_type_info.extension_description_overrides.push_back(
+ l10n_util::GetStringUTF16(IDS_SETTINGS_CERTIFICATE_MANAGER_PKCS12_FILES));
+ file_type_info.include_all_files = true;
+ select_file_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
+ select_file_dialog_->SelectFile(
+ ui::SelectFileDialog::SELECT_SAVEAS_FILE, base::string16(),
+ base::FilePath(), &file_type_info, 1, FILE_PATH_LITERAL("p12"),
+ GetParentWindow(),
+ reinterpret_cast<void*>(EXPORT_PERSONAL_FILE_SELECTED));
+}
+
+void CertificatesHandler::ExportPersonalFileSelected(
+ const base::FilePath& path) {
+ file_path_ = path;
+ ResolveCallback(base::Value());
+}
+
+void CertificatesHandler::HandleExportPersonalPasswordSelected(
+ const base::ListValue* args) {
+ CHECK_EQ(2U, args->GetSize());
+ AssignWebUICallbackId(args);
+ CHECK(args->GetString(1, &password_));
+
+ // Currently, we don't support exporting more than one at a time. If we do,
+ // this would need to either change this to use UnlockSlotsIfNecessary or
+ // change UnlockCertSlotIfNecessary to take a CertificateList.
+ DCHECK_EQ(selected_cert_list_.size(), 1U);
+
+ // TODO(mattm): do something smarter about non-extractable keys
+ chrome::UnlockCertSlotIfNecessary(
+ selected_cert_list_[0].get(), chrome::kCryptoModulePasswordCertExport,
+ net::HostPortPair(), // unused.
+ GetParentWindow(),
+ base::Bind(&CertificatesHandler::ExportPersonalSlotsUnlocked,
+ base::Unretained(this)));
+}
+
+void CertificatesHandler::ExportPersonalSlotsUnlocked() {
+ std::string output;
+ int num_exported = certificate_manager_model_->cert_db()->ExportToPKCS12(
+ selected_cert_list_, password_, &output);
+ if (!num_exported) {
+ RejectCallbackWithError(
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_PKCS12_EXPORT_ERROR_TITLE),
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_UNKNOWN_ERROR));
+ ImportExportCleanup();
+ return;
+ }
+ file_access_provider_->StartWrite(
+ file_path_, output,
+ base::Bind(&CertificatesHandler::ExportPersonalFileWritten,
+ base::Unretained(this)),
+ &tracker_);
+}
+
+void CertificatesHandler::ExportPersonalFileWritten(const int* write_errno,
+ const int* bytes_written) {
+ ImportExportCleanup();
+ if (*write_errno) {
+ RejectCallbackWithError(
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_PKCS12_EXPORT_ERROR_TITLE),
+ l10n_util::GetStringFUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_WRITE_ERROR_FORMAT,
+ UTF8ToUTF16(base::safe_strerror(*write_errno))));
+ } else {
+ ResolveCallback(base::Value());
+ }
+}
+
+void CertificatesHandler::HandleImportPersonal(const base::ListValue* args) {
+ CHECK_EQ(2U, args->GetSize());
+ AssignWebUICallbackId(args);
+ CHECK(args->GetBoolean(1, &use_hardware_backed_));
+
+ ui::SelectFileDialog::FileTypeInfo file_type_info;
+ file_type_info.extensions.resize(1);
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("p12"));
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pfx"));
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("crt"));
+ file_type_info.extension_description_overrides.push_back(
+ l10n_util::GetStringUTF16(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_USAGE_SSL_CLIENT));
+ file_type_info.include_all_files = true;
+ select_file_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
+ select_file_dialog_->SelectFile(
+ ui::SelectFileDialog::SELECT_OPEN_FILE, base::string16(),
+ base::FilePath(), &file_type_info, 1, FILE_PATH_LITERAL("p12"),
+ GetParentWindow(),
+ reinterpret_cast<void*>(IMPORT_PERSONAL_FILE_SELECTED));
+}
+
+void CertificatesHandler::ImportPersonalFileSelected(
+ const base::FilePath& path) {
+ file_access_provider_->StartRead(
+ path, base::Bind(&CertificatesHandler::ImportPersonalFileRead,
+ base::Unretained(this)),
+ &tracker_);
+}
+
+void CertificatesHandler::ImportPersonalFileRead(const int* read_errno,
+ const std::string* data) {
+ if (*read_errno) {
+ ImportExportCleanup();
+ RejectCallbackWithError(
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringFUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_READ_ERROR_FORMAT,
+ UTF8ToUTF16(base::safe_strerror(*read_errno))));
+ return;
+ }
+
+ file_data_ = *data;
+
+ if (CouldBePFX(file_data_)) {
+ ResolveCallback(base::Value(true));
+ return;
+ }
+
+ // Non .p12/.pfx files are assumed to be single/chain certificates without
+ // private key data. The default extension according to spec is '.crt',
+ // however other extensions are also used in some places to represent these
+ // certificates.
+ int result = certificate_manager_model_->ImportUserCert(file_data_);
+ ImportExportCleanup();
+ int string_id;
+ switch (result) {
+ case net::OK:
+ ResolveCallback(base::Value(false));
+ return;
+ case net::ERR_NO_PRIVATE_KEY_FOR_CERT:
+ string_id = IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_MISSING_KEY;
+ break;
+ case net::ERR_CERT_INVALID:
+ string_id = IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_INVALID_FILE;
+ break;
+ default:
+ string_id = IDS_SETTINGS_CERTIFICATE_MANAGER_UNKNOWN_ERROR;
+ break;
+ }
+ RejectCallbackWithError(
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringUTF8(string_id));
+}
+
+void CertificatesHandler::HandleImportPersonalPasswordSelected(
+ const base::ListValue* args) {
+ CHECK_EQ(2U, args->GetSize());
+ AssignWebUICallbackId(args);
+ CHECK(args->GetString(1, &password_));
+
+ if (use_hardware_backed_) {
+ slot_ = certificate_manager_model_->cert_db()->GetPrivateSlot();
+ } else {
+ slot_ = certificate_manager_model_->cert_db()->GetPublicSlot();
+ }
+
+ std::vector<crypto::ScopedPK11Slot> modules;
+ modules.push_back(crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot_.get())));
+ chrome::UnlockSlotsIfNecessary(
+ std::move(modules), chrome::kCryptoModulePasswordCertImport,
+ net::HostPortPair(), // unused.
+ GetParentWindow(),
+ base::Bind(&CertificatesHandler::ImportPersonalSlotUnlocked,
+ base::Unretained(this)));
+}
+
+void CertificatesHandler::ImportPersonalSlotUnlocked() {
+ // Determine if the private key should be unextractable after the import.
+ // We do this by checking the value of |use_hardware_backed_| which is set
+ // to true if importing into a hardware module. Currently, this only happens
+ // for Chrome OS when the "Import and Bind" option is chosen.
+ bool is_extractable = !use_hardware_backed_;
+ int result = certificate_manager_model_->ImportFromPKCS12(
+ slot_.get(), file_data_, password_, is_extractable);
+ ImportExportCleanup();
+ int string_id;
+ switch (result) {
+ case net::OK:
+ ResolveCallback(base::Value());
+ return;
+ case net::ERR_PKCS12_IMPORT_BAD_PASSWORD:
+ // TODO(mattm): if the error was a bad password, we should reshow the
+ // password dialog after the user dismisses the error dialog.
+ string_id = IDS_SETTINGS_CERTIFICATE_MANAGER_BAD_PASSWORD;
+ break;
+ case net::ERR_PKCS12_IMPORT_INVALID_MAC:
+ string_id = IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_INVALID_MAC;
+ break;
+ case net::ERR_PKCS12_IMPORT_INVALID_FILE:
+ string_id = IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_INVALID_FILE;
+ break;
+ case net::ERR_PKCS12_IMPORT_UNSUPPORTED:
+ string_id = IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_UNSUPPORTED;
+ break;
+ default:
+ string_id = IDS_SETTINGS_CERTIFICATE_MANAGER_UNKNOWN_ERROR;
+ break;
+ }
+ RejectCallbackWithError(
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringUTF8(string_id));
+}
+
+void CertificatesHandler::HandleCancelImportExportProcess(
+ const base::ListValue* args) {
+ ImportExportCleanup();
+}
+
+void CertificatesHandler::ImportExportCleanup() {
+ file_path_.clear();
+ password_.clear();
+ file_data_.clear();
+ use_hardware_backed_ = false;
+ selected_cert_list_.clear();
+ slot_.reset();
+ tracker_.TryCancelAll();
+
+ // There may be pending file dialogs, we need to tell them that we've gone
+ // away so they don't try and call back to us.
+ if (select_file_dialog_.get())
+ select_file_dialog_->ListenerDestroyed();
+ select_file_dialog_ = NULL;
+}
+
+void CertificatesHandler::HandleImportServer(const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ AssignWebUICallbackId(args);
+
+ select_file_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
+ ShowCertSelectFileDialog(
+ select_file_dialog_.get(), ui::SelectFileDialog::SELECT_OPEN_FILE,
+ base::FilePath(), GetParentWindow(),
+ reinterpret_cast<void*>(IMPORT_SERVER_FILE_SELECTED));
+}
+
+void CertificatesHandler::ImportServerFileSelected(const base::FilePath& path) {
+ file_access_provider_->StartRead(
+ path, base::Bind(&CertificatesHandler::ImportServerFileRead,
+ base::Unretained(this)),
+ &tracker_);
+}
+
+void CertificatesHandler::ImportServerFileRead(const int* read_errno,
+ const std::string* data) {
+ if (*read_errno) {
+ ImportExportCleanup();
+ RejectCallbackWithError(
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_SERVER_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringFUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_READ_ERROR_FORMAT,
+ UTF8ToUTF16(base::safe_strerror(*read_errno))));
+ return;
+ }
+
+ selected_cert_list_ = net::X509Certificate::CreateCertificateListFromBytes(
+ data->data(), data->size(), net::X509Certificate::FORMAT_AUTO);
+ if (selected_cert_list_.empty()) {
+ ImportExportCleanup();
+ RejectCallbackWithError(
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_SERVER_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_CERT_PARSE_ERROR));
+ return;
+ }
+
+ net::NSSCertDatabase::ImportCertFailureList not_imported;
+ // TODO(mattm): Add UI for trust. http://crbug.com/76274
+ bool result = certificate_manager_model_->ImportServerCert(
+ selected_cert_list_, net::NSSCertDatabase::TRUST_DEFAULT, &not_imported);
+ if (!result) {
+ RejectCallbackWithError(
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_SERVER_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_UNKNOWN_ERROR));
+ } else if (!not_imported.empty()) {
+ RejectCallbackWithImportError(
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_SERVER_IMPORT_ERROR_TITLE),
+ not_imported);
+ } else {
+ ResolveCallback(base::Value());
+ }
+ ImportExportCleanup();
+}
+
+void CertificatesHandler::HandleImportCA(const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ AssignWebUICallbackId(args);
+
+ select_file_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
+ ShowCertSelectFileDialog(select_file_dialog_.get(),
+ ui::SelectFileDialog::SELECT_OPEN_FILE,
+ base::FilePath(), GetParentWindow(),
+ reinterpret_cast<void*>(IMPORT_CA_FILE_SELECTED));
+}
+
+void CertificatesHandler::ImportCAFileSelected(const base::FilePath& path) {
+ file_access_provider_->StartRead(
+ path, base::Bind(&CertificatesHandler::ImportCAFileRead,
+ base::Unretained(this)),
+ &tracker_);
+}
+
+void CertificatesHandler::ImportCAFileRead(const int* read_errno,
+ const std::string* data) {
+ if (*read_errno) {
+ ImportExportCleanup();
+ RejectCallbackWithError(
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_CA_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringFUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_READ_ERROR_FORMAT,
+ UTF8ToUTF16(base::safe_strerror(*read_errno))));
+ return;
+ }
+
+ selected_cert_list_ = net::X509Certificate::CreateCertificateListFromBytes(
+ data->data(), data->size(), net::X509Certificate::FORMAT_AUTO);
+ if (selected_cert_list_.empty()) {
+ ImportExportCleanup();
+ RejectCallbackWithError(
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_CA_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_CERT_PARSE_ERROR));
+ return;
+ }
+
+ scoped_refptr<net::X509Certificate> root_cert =
+ certificate_manager_model_->cert_db()->FindRootInList(
+ selected_cert_list_);
+
+ // TODO(mattm): check here if root_cert is not a CA cert and show error.
+
+ base::Value cert_name(root_cert->subject().GetDisplayName());
+ ResolveCallback(cert_name);
+}
+
+void CertificatesHandler::HandleImportCATrustSelected(
+ const base::ListValue* args) {
+ CHECK_EQ(4U, args->GetSize());
+ AssignWebUICallbackId(args);
+
+ bool trust_ssl = false;
+ bool trust_email = false;
+ bool trust_obj_sign = false;
+ CHECK(args->GetBoolean(1, &trust_ssl));
+ CHECK(args->GetBoolean(2, &trust_email));
+ CHECK(args->GetBoolean(3, &trust_obj_sign));
+
+ // TODO(mattm): add UI for setting explicit distrust, too.
+ // http://crbug.com/128411
+ net::NSSCertDatabase::ImportCertFailureList not_imported;
+ bool result = certificate_manager_model_->ImportCACerts(
+ selected_cert_list_,
+ trust_ssl * net::NSSCertDatabase::TRUSTED_SSL +
+ trust_email * net::NSSCertDatabase::TRUSTED_EMAIL +
+ trust_obj_sign * net::NSSCertDatabase::TRUSTED_OBJ_SIGN,
+ &not_imported);
+ if (!result) {
+ RejectCallbackWithError(
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_CA_IMPORT_ERROR_TITLE),
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_UNKNOWN_ERROR));
+ } else if (!not_imported.empty()) {
+ RejectCallbackWithImportError(
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_CA_IMPORT_ERROR_TITLE),
+ not_imported);
+ } else {
+ ResolveCallback(base::Value());
+ }
+ ImportExportCleanup();
+}
+
+void CertificatesHandler::HandleExportCertificate(const base::ListValue* args) {
+ net::X509Certificate* cert = cert_id_map_->CallbackArgsToCert(args);
+ if (!cert)
+ return;
+ ShowCertExportDialog(web_ui()->GetWebContents(), GetParentWindow(), cert);
+}
+
+void CertificatesHandler::HandleDeleteCertificate(const base::ListValue* args) {
+ CHECK_EQ(2U, args->GetSize());
+ AssignWebUICallbackId(args);
+ std::string node_id;
+ CHECK(args->GetString(1, &node_id));
+
+ net::X509Certificate* cert = cert_id_map_->IdToCert(node_id);
+ CHECK(cert);
+
+ bool result = certificate_manager_model_->Delete(cert);
+ if (!result) {
+ // TODO(mattm): better error messages?
+ RejectCallbackWithError(
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_CERT_ERROR_TITLE),
+ l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_UNKNOWN_ERROR));
+ } else {
+ ResolveCallback(base::Value());
+ }
+}
+
+void CertificatesHandler::OnCertificateManagerModelCreated(
+ std::unique_ptr<CertificateManagerModel> model) {
+ certificate_manager_model_ = std::move(model);
+ CertificateManagerModelReady();
+}
+
+void CertificatesHandler::CertificateManagerModelReady() {
+ base::Value user_db_available_value(
+ certificate_manager_model_->is_user_db_available());
+ base::Value tpm_available_value(
+ certificate_manager_model_->is_tpm_available());
+ FireWebUIListener("certificates-model-ready", user_db_available_value,
+ tpm_available_value);
+ certificate_manager_model_->Refresh();
+}
+
+void CertificatesHandler::HandleRefreshCertificates(
+ const base::ListValue* args) {
+ AllowJavascript();
+
+ if (certificate_manager_model_) {
+ // Already have a model, the webui must be re-loading. Just re-run the
+ // webui initialization.
+ CertificateManagerModelReady();
+ return;
+ }
+
+ if (!requested_certificate_manager_model_) {
+ // Request that a model be created.
+ CertificateManagerModel::Create(
+ Profile::FromWebUI(web_ui()), this,
+ base::Bind(&CertificatesHandler::OnCertificateManagerModelCreated,
+ weak_ptr_factory_.GetWeakPtr()));
+ requested_certificate_manager_model_ = true;
+ return;
+ }
+
+ // We are already waiting for a CertificateManagerModel to be created, no need
+ // to do anything.
+}
+
+void CertificatesHandler::PopulateTree(
+ const std::string& tab_name,
+ net::CertType type,
+ const net::CertificateList& web_trust_certs) {
+ std::unique_ptr<icu::Collator> collator;
+ UErrorCode error = U_ZERO_ERROR;
+ collator.reset(icu::Collator::createInstance(
+ icu::Locale(g_browser_process->GetApplicationLocale().c_str()), error));
+ if (U_FAILURE(error))
+ collator.reset(NULL);
+ DictionaryIdComparator comparator(collator.get());
+ CertificateManagerModel::OrgGroupingMap map;
+
+ certificate_manager_model_->FilterAndBuildOrgGroupingMap(type, &map);
+
+ {
+ std::unique_ptr<base::ListValue> nodes =
+ base::MakeUnique<base::ListValue>();
+ for (CertificateManagerModel::OrgGroupingMap::iterator i = map.begin();
+ i != map.end(); ++i) {
+ // Populate first level (org name).
+ std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+ dict->SetString(kKeyField, OrgNameToId(i->first));
+ dict->SetString(kNameField, i->first);
+
+ // Populate second level (certs).
+ auto subnodes = base::MakeUnique<base::ListValue>();
+ for (net::CertificateList::const_iterator org_cert_it = i->second.begin();
+ org_cert_it != i->second.end(); ++org_cert_it) {
+ std::unique_ptr<base::DictionaryValue> cert_dict(
+ new base::DictionaryValue);
+ net::X509Certificate* cert = org_cert_it->get();
+ cert_dict->SetString(kKeyField, cert_id_map_->CertToId(cert));
+ cert_dict->SetString(
+ kNameField, certificate_manager_model_->GetColumnText(
+ *cert, CertificateManagerModel::COL_SUBJECT_NAME));
+ cert_dict->SetBoolean(
+ kReadonlyField,
+ certificate_manager_model_->cert_db()->IsReadOnly(cert));
+ // Policy-installed certificates with web trust are trusted.
+ bool policy_trusted =
+ IsPolicyInstalledWithWebTrust(web_trust_certs, cert);
+ cert_dict->SetBoolean(
+ kUntrustedField,
+ !policy_trusted &&
+ certificate_manager_model_->cert_db()->IsUntrusted(cert));
+ cert_dict->SetBoolean(kPolicyField, policy_trusted);
+ // TODO(hshi): This should be determined by testing for PKCS #11
+ // CKA_EXTRACTABLE attribute. We may need to use the NSS function
+ // PK11_ReadRawAttribute to do that.
+ cert_dict->SetBoolean(
+ kExtractableField,
+ !certificate_manager_model_->IsHardwareBacked(cert));
+ // TODO(mattm): Other columns.
+ subnodes->Append(std::move(cert_dict));
+ }
+ std::sort(subnodes->begin(), subnodes->end(), comparator);
+
+ dict->Set(kSubnodesField, std::move(subnodes));
+ nodes->Append(std::move(dict));
+ }
+ std::sort(nodes->begin(), nodes->end(), comparator);
+
+ FireWebUIListener("certificates-changed", base::Value(tab_name), *nodes);
+ }
+}
+
+void CertificatesHandler::ResolveCallback(const base::Value& response) {
+ DCHECK(!webui_callback_id_.empty());
+ ResolveJavascriptCallback(base::Value(webui_callback_id_), response);
+ webui_callback_id_.clear();
+}
+
+void CertificatesHandler::RejectCallback(const base::Value& response) {
+ DCHECK(!webui_callback_id_.empty());
+ RejectJavascriptCallback(base::Value(webui_callback_id_), response);
+ webui_callback_id_.clear();
+}
+
+void CertificatesHandler::RejectCallbackWithError(const std::string& title,
+ const std::string& error) {
+ std::unique_ptr<base::DictionaryValue> error_info(new base::DictionaryValue);
+ error_info->SetString(kErrorTitle, title);
+ error_info->SetString(kErrorDescription, error);
+ RejectCallback(*error_info);
+}
+
+void CertificatesHandler::RejectCallbackWithImportError(
+ const std::string& title,
+ const net::NSSCertDatabase::ImportCertFailureList& not_imported) {
+ std::string error;
+ if (selected_cert_list_.size() == 1)
+ error = l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_SINGLE_NOT_IMPORTED);
+ else if (not_imported.size() == selected_cert_list_.size())
+ error = l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_ALL_NOT_IMPORTED);
+ else
+ error = l10n_util::GetStringUTF8(
+ IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_SOME_NOT_IMPORTED);
+
+ std::unique_ptr<base::ListValue> cert_error_list =
+ base::MakeUnique<base::ListValue>();
+ for (size_t i = 0; i < not_imported.size(); ++i) {
+ const net::NSSCertDatabase::ImportCertFailure& failure = not_imported[i];
+ std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+ dict->SetString(kNameField,
+ failure.certificate->subject().GetDisplayName());
+ dict->SetString(kErrorField, NetErrorToString(failure.net_error));
+ cert_error_list->Append(std::move(dict));
+ }
+
+ std::unique_ptr<base::DictionaryValue> error_info(new base::DictionaryValue);
+ error_info->SetString(kErrorTitle, title);
+ error_info->SetString(kErrorDescription, error);
+ error_info->Set(kCertificateErrors,
+ base::WrapUnique(cert_error_list.release()));
+ RejectCallback(*error_info);
+}
+
+gfx::NativeWindow CertificatesHandler::GetParentWindow() const {
+ return web_ui()->GetWebContents()->GetTopLevelNativeWindow();
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/certificates_handler.h b/chromium/chrome/browser/ui/webui/settings/certificates_handler.h
new file mode 100644
index 00000000000..839351ea3d3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/certificates_handler.h
@@ -0,0 +1,203 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CERTIFICATES_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CERTIFICATES_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "chrome/browser/certificate_manager_model.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "net/cert/nss_cert_database.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+
+namespace settings {
+
+class CertIdMap;
+class FileAccessProvider;
+
+class CertificatesHandler
+ : public SettingsPageUIHandler,
+ public CertificateManagerModel::Observer,
+ public ui::SelectFileDialog::Listener {
+ public:
+ explicit CertificatesHandler(bool show_certs_in_modal_dialog);
+ ~CertificatesHandler() override;
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override {}
+ void OnJavascriptDisallowed() override {}
+
+ // CertificateManagerModel::Observer implementation.
+ void CertificatesRefreshed() override;
+
+ // SelectFileDialog::Listener implementation.
+ void FileSelected(const base::FilePath& path,
+ int index,
+ void* params) override;
+ void FileSelectionCanceled(void* params) override;
+
+ private:
+ // View certificate.
+ void HandleViewCertificate(const base::ListValue* args);
+
+ // Edit certificate authority trust values. The sequence goes like:
+ // 1. user clicks edit button -> Edit dialog is shown ->
+ // HandleGetCATrust -> Edit dialog is populated.
+ // 2. User checks/unchecks boxes, and clicks ok -> HandleEditCATrust ->
+ // edit dialog is dismissed upon success.
+ void HandleGetCATrust(const base::ListValue* args);
+ void HandleEditCATrust(const base::ListValue* args);
+
+ // Cleanup state stored during import or export process.
+ void HandleCancelImportExportProcess(const base::ListValue* args);
+ void ImportExportCleanup();
+
+ // Export to PKCS #12 file. The sequence goes like:
+ // 1. user click on export button -> HandleExportPersonal -> launches file
+ // selector
+ // 2. user selects file -> ExportPersonalFileSelected -> launches password
+ // dialog
+ // 3. user enters password -> HandleExportPersonalPasswordSelected ->
+ // unlock slots
+ // 4. slots unlocked -> ExportPersonalSlotsUnlocked -> exports to memory
+ // buffer -> starts async write operation
+ // 5. write finishes (or fails) -> ExportPersonalFileWritten
+ void HandleExportPersonal(const base::ListValue* args);
+ void ExportPersonalFileSelected(const base::FilePath& path);
+ void HandleExportPersonalPasswordSelected(const base::ListValue* args);
+ void ExportPersonalSlotsUnlocked();
+ void ExportPersonalFileWritten(const int* write_errno,
+ const int* bytes_written);
+
+ // Import from PKCS #12 or cert file. The sequence goes like:
+ // 1. user click on import button -> HandleImportPersonal ->
+ // launches file selector
+ // 2. user selects file -> ImportPersonalFileSelected -> starts async
+ // read operation
+ // 3. read operation completes -> ImportPersonalFileRead ->
+ // If file is PFX -> launches password dialog, goto step 4
+ // Else -> import as certificate, goto step 6
+ // 4. user enters password -> HandleImportPersonalPasswordSelected ->
+ // unlock slot
+ // 5. slot unlocked -> ImportPersonalSlotUnlocked attempts to
+ // import with previously entered password
+ // 6a. if import succeeds -> ImportExportCleanup
+ // 6b. if import fails -> show error, ImportExportCleanup
+ // TODO(mattm): allow retrying with different password
+ void HandleImportPersonal(const base::ListValue* args);
+ void ImportPersonalFileSelected(const base::FilePath& path);
+ void ImportPersonalFileRead(const int* read_errno, const std::string* data);
+ void HandleImportPersonalPasswordSelected(const base::ListValue* args);
+ void ImportPersonalSlotUnlocked();
+
+ // Import Server certificates from file. Sequence goes like:
+ // 1. user clicks on import button -> HandleImportServer -> launches file
+ // selector
+ // 2. user selects file -> ImportServerFileSelected -> starts async read
+ // 3. read completes -> ImportServerFileRead -> parse certs -> attempt import
+ // 4a. if import succeeds -> ImportExportCleanup
+ // 4b. if import fails -> show error, ImportExportCleanup
+ void HandleImportServer(const base::ListValue* args);
+ void ImportServerFileSelected(const base::FilePath& path);
+ void ImportServerFileRead(const int* read_errno, const std::string* data);
+
+ // Import Certificate Authorities from file. Sequence goes like:
+ // 1. user clicks on import button -> HandleImportCA -> launches file
+ // selector
+ // 2. user selects file -> ImportCAFileSelected -> starts async read
+ // 3. read completes -> ImportCAFileRead -> parse certs -> Certificate trust
+ // level dialog is shown.
+ // 4. user clicks ok -> HandleImportCATrustSelected -> attempt import
+ // 5a. if import succeeds -> ImportExportCleanup
+ // 5b. if import fails -> show error, ImportExportCleanup
+ void HandleImportCA(const base::ListValue* args);
+ void ImportCAFileSelected(const base::FilePath& path);
+ void ImportCAFileRead(const int* read_errno, const std::string* data);
+ void HandleImportCATrustSelected(const base::ListValue* args);
+
+ // Export a certificate.
+ void HandleExportCertificate(const base::ListValue* args);
+
+ // Delete certificate and private key (if any).
+ void HandleDeleteCertificate(const base::ListValue* args);
+
+ // Model initialization methods.
+ void OnCertificateManagerModelCreated(
+ std::unique_ptr<CertificateManagerModel> model);
+ void CertificateManagerModelReady();
+
+ // Populate the trees in all the tabs.
+ void HandleRefreshCertificates(const base::ListValue* args);
+
+ // Populate the given tab's tree.
+ void PopulateTree(const std::string& tab_name,
+ net::CertType type,
+ const net::CertificateList& web_trust_certs);
+
+ // Populate the tree after retrieving the list of policy-installed
+ // web-trusted certificates.
+ void OnPolicyWebTrustCertsRetrieved(
+ const net::CertificateList& web_trust_certs);
+
+ void ResolveCallback(const base::Value& response);
+ void RejectCallback(const base::Value& response);
+
+ // Reject the pending JS callback with a generic error.
+ void RejectCallbackWithError(
+ const std::string& title, const std::string& error);
+
+ // Reject the pending JS callback with a certificate import error.
+ void RejectCallbackWithImportError(
+ const std::string& title,
+ const net::NSSCertDatabase::ImportCertFailureList& not_imported);
+
+ // Assigns a new |webui_callback_id_|. Ensures that previous in-flight request
+ // has been fulfilled.
+ void AssignWebUICallbackId(const base::ListValue* args);
+
+ gfx::NativeWindow GetParentWindow() const;
+
+ // True if certificate viewer should be shown in modal instead of constrianed
+ // dialog.
+ bool show_certs_in_modal_dialog_;
+ // The Certificates Manager model
+ bool requested_certificate_manager_model_;
+ std::unique_ptr<CertificateManagerModel> certificate_manager_model_;
+
+ // For multi-step import or export processes, we need to store the path,
+ // password, etc the user chose while we wait for them to enter a password,
+ // wait for file to be read, etc.
+ base::FilePath file_path_;
+ base::string16 password_;
+ // The WebUI callback ID of the last in-flight async request. There is always
+ // only one in-flight such request.
+ std::string webui_callback_id_;
+ bool use_hardware_backed_;
+ std::string file_data_;
+ net::CertificateList selected_cert_list_;
+ scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
+ crypto::ScopedPK11Slot slot_;
+
+ // Used in reading and writing certificate files.
+ base::CancelableTaskTracker tracker_;
+ scoped_refptr<FileAccessProvider> file_access_provider_;
+
+ std::unique_ptr<CertIdMap> cert_id_map_;
+
+ base::WeakPtrFactory<CertificatesHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CertificatesHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CERTIFICATES_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.cc
new file mode 100644
index 00000000000..a74053c73a4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.cc
@@ -0,0 +1,67 @@
+// 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 "chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/browser/extension_registry.h"
+
+namespace chromeos {
+namespace settings {
+
+AccessibilityHandler::AccessibilityHandler(content::WebUI* webui)
+ : profile_(Profile::FromWebUI(webui)) {
+}
+
+AccessibilityHandler::~AccessibilityHandler() {}
+
+void AccessibilityHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "showChromeVoxSettings",
+ base::Bind(&AccessibilityHandler::HandleShowChromeVoxSettings,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "showSelectToSpeakSettings",
+ base::Bind(&AccessibilityHandler::HandleShowSelectToSpeakSettings,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "showSwitchAccessSettings",
+ base::Bind(&AccessibilityHandler::HandleShowSwitchAccessSettings,
+ base::Unretained(this)));
+}
+
+void AccessibilityHandler::HandleShowChromeVoxSettings(
+ const base::ListValue* args) {
+ OpenExtensionOptionsPage(extension_misc::kChromeVoxExtensionId);
+}
+
+void AccessibilityHandler::HandleShowSelectToSpeakSettings(
+ const base::ListValue* args) {
+ OpenExtensionOptionsPage(extension_misc::kSelectToSpeakExtensionId);
+}
+
+void AccessibilityHandler::HandleShowSwitchAccessSettings(
+ const base::ListValue* args) {
+ OpenExtensionOptionsPage(extension_misc::kSwitchAccessExtensionId);
+}
+
+void AccessibilityHandler::OpenExtensionOptionsPage(const char extension_id[]) {
+ const extensions::Extension* extension =
+ extensions::ExtensionRegistry::Get(profile_)->GetExtensionById(
+ extension_id, extensions::ExtensionRegistry::ENABLED);
+ if (!extension)
+ return;
+ extensions::ExtensionTabUtil::OpenOptionsPage(
+ extension,
+ chrome::FindBrowserWithWebContents(web_ui()->GetWebContents()));
+}
+
+} // namespace settings
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h
new file mode 100644
index 00000000000..d03c83e2ea6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h
@@ -0,0 +1,51 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_ACCESSIBILITY_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_ACCESSIBILITY_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace content {
+class WebUI;
+}
+
+class Profile;
+
+namespace chromeos {
+namespace settings {
+
+class AccessibilityHandler : public ::settings::SettingsPageUIHandler {
+ public:
+ explicit AccessibilityHandler(content::WebUI* webui);
+ ~AccessibilityHandler() override;
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override {}
+ void OnJavascriptDisallowed() override {}
+
+ private:
+ // Callback for the messages to show settings for ChromeVox,
+ // Select To Speak, or Switch Access.
+ void HandleShowChromeVoxSettings(const base::ListValue* args);
+ void HandleShowSelectToSpeakSettings(const base::ListValue* args);
+ void HandleShowSwitchAccessSettings(const base::ListValue* args);
+
+ void OpenExtensionOptionsPage(const char extension_id[]);
+
+ Profile* profile_; // Weak pointer.
+
+ DISALLOW_COPY_AND_ASSIGN(AccessibilityHandler);
+};
+
+} // namespace settings
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_ACCESSIBILITY_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/android_apps_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/android_apps_handler.cc
new file mode 100644
index 00000000000..51e85cb3f92
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/android_apps_handler.cc
@@ -0,0 +1,103 @@
+// 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 "chrome/browser/ui/webui/settings/chromeos/android_apps_handler.h"
+
+#include "base/values.h"
+#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/arc/arc_app_utils.h" // kSettingsAppId
+#include "ui/events/event_constants.h"
+
+namespace chromeos {
+namespace settings {
+
+AndroidAppsHandler::AndroidAppsHandler(Profile* profile)
+ : arc_prefs_observer_(this),
+ arc_session_manager_observer_(this),
+ profile_(profile),
+ weak_ptr_factory_(this) {}
+
+AndroidAppsHandler::~AndroidAppsHandler() {}
+
+void AndroidAppsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "requestAndroidAppsInfo",
+ base::Bind(&AndroidAppsHandler::HandleRequestAndroidAppsInfo,
+ weak_ptr_factory_.GetWeakPtr()));
+ web_ui()->RegisterMessageCallback(
+ "showAndroidAppsSettings",
+ base::Bind(&AndroidAppsHandler::ShowAndroidAppsSettings,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void AndroidAppsHandler::OnJavascriptAllowed() {
+ ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(profile_);
+ if (arc_prefs) {
+ arc_prefs_observer_.Add(arc_prefs);
+ // arc::ArcSessionManager is assosiated with primary profile.
+ arc_session_manager_observer_.Add(arc::ArcSessionManager::Get());
+ }
+}
+
+void AndroidAppsHandler::OnJavascriptDisallowed() {
+ arc_prefs_observer_.RemoveAll();
+ arc_session_manager_observer_.RemoveAll();
+}
+
+void AndroidAppsHandler::OnAppRegistered(
+ const std::string& app_id,
+ const ArcAppListPrefs::AppInfo& app_info) {
+ OnAppChanged(app_id);
+}
+
+void AndroidAppsHandler::OnAppRemoved(const std::string& app_id) {
+ OnAppChanged(app_id);
+}
+
+void AndroidAppsHandler::OnAppChanged(const std::string& app_id) {
+ if (app_id != arc::kSettingsAppId)
+ return;
+ SendAndroidAppsInfo();
+}
+
+void AndroidAppsHandler::OnArcPlayStoreEnabledChanged(bool enabled) {
+ SendAndroidAppsInfo();
+}
+
+std::unique_ptr<base::DictionaryValue>
+AndroidAppsHandler::BuildAndroidAppsInfo() {
+ std::unique_ptr<base::DictionaryValue> info(new base::DictionaryValue);
+ info->SetBoolean("playStoreEnabled",
+ arc::IsArcPlayStoreEnabledForProfile(profile_));
+ info->SetBoolean(
+ "settingsAppAvailable",
+ ArcAppListPrefs::Get(profile_)->IsRegistered(arc::kSettingsAppId));
+ return info;
+}
+
+void AndroidAppsHandler::HandleRequestAndroidAppsInfo(
+ const base::ListValue* args) {
+ SendAndroidAppsInfo();
+}
+
+void AndroidAppsHandler::SendAndroidAppsInfo() {
+ AllowJavascript();
+ std::unique_ptr<base::DictionaryValue> info = BuildAndroidAppsInfo();
+ FireWebUIListener("android-apps-info-update", *info);
+}
+
+void AndroidAppsHandler::ShowAndroidAppsSettings(const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ bool activated_from_keyboard = false;
+ args->GetBoolean(0, &activated_from_keyboard);
+ int flags = activated_from_keyboard ? ui::EF_NONE : ui::EF_LEFT_MOUSE_BUTTON;
+
+ // Settings in secondary profile cannot access ARC.
+ CHECK(arc::IsArcAllowedForProfile(profile_));
+ arc::LaunchAndroidSettingsApp(profile_, flags);
+}
+
+} // namespace settings
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/android_apps_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/android_apps_handler.h
new file mode 100644
index 00000000000..62a530c4742
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/android_apps_handler.h
@@ -0,0 +1,66 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_ANDROID_APPS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_ANDROID_APPS_HANDLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/chromeos/arc/arc_session_manager.h"
+#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+class Profile;
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace chromeos {
+namespace settings {
+
+class AndroidAppsHandler : public ::settings::SettingsPageUIHandler,
+ public ArcAppListPrefs::Observer,
+ public arc::ArcSessionManager::Observer {
+ public:
+ explicit AndroidAppsHandler(Profile* profile);
+ ~AndroidAppsHandler() override;
+
+ // SettingsPageUIHandler
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ // ArcAppListPrefs::Observer
+ void OnAppRemoved(const std::string& app_id) override;
+ void OnAppRegistered(const std::string& app_id,
+ const ArcAppListPrefs::AppInfo& app_info) override;
+
+ // arc::ArcSessionManager::Observer:
+ void OnArcPlayStoreEnabledChanged(bool enabled) override;
+
+ private:
+ std::unique_ptr<base::DictionaryValue> BuildAndroidAppsInfo();
+ void OnAppChanged(const std::string& app_id);
+ void HandleRequestAndroidAppsInfo(const base::ListValue* args);
+ void SendAndroidAppsInfo();
+ void ShowAndroidAppsSettings(const base::ListValue* args);
+
+ ScopedObserver<ArcAppListPrefs, ArcAppListPrefs::Observer>
+ arc_prefs_observer_;
+ ScopedObserver<arc::ArcSessionManager, arc::ArcSessionManager::Observer>
+ arc_session_manager_observer_;
+ Profile* profile_; // unowned
+ base::WeakPtrFactory<AndroidAppsHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(AndroidAppsHandler);
+};
+
+} // namespace settings
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_ANDROID_APPS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc
new file mode 100644
index 00000000000..378b08c4b00
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc
@@ -0,0 +1,409 @@
+// 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.
+
+#include "chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
+#include "chrome/browser/chromeos/camera_presence_notifier.h"
+#include "chrome/browser/chromeos/login/users/avatar/user_image_manager.h"
+#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
+#include "chrome/browser/chromeos/login/users/default_user_image/default_user_images.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/chrome_select_file_policy.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/audio/chromeos_sounds.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_image/user_image.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/common/url_constants.h"
+#include "media/audio/sounds/sounds_manager.h"
+#include "net/base/data_url.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "ui/views/widget/widget.h"
+#include "url/gurl.h"
+
+using content::BrowserThread;
+
+namespace chromeos {
+namespace settings {
+
+namespace {
+
+// Returns info about extensions for files we support as user images.
+ui::SelectFileDialog::FileTypeInfo GetUserImageFileTypeInfo() {
+ ui::SelectFileDialog::FileTypeInfo file_type_info;
+ file_type_info.extensions.resize(1);
+
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("bmp"));
+
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("jpg"));
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("jpeg"));
+
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("png"));
+
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("tif"));
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("tiff"));
+
+ file_type_info.extension_description_overrides.resize(1);
+ file_type_info.extension_description_overrides[0] =
+ l10n_util::GetStringUTF16(IDS_IMAGE_FILES);
+
+ return file_type_info;
+}
+
+// Time histogram suffix for profile image download.
+const char kProfileDownloadReason[] = "Preferences";
+
+} // namespace
+
+ChangePictureHandler::ChangePictureHandler()
+ : previous_image_url_(url::kAboutBlankURL),
+ previous_image_index_(user_manager::User::USER_IMAGE_INVALID),
+ user_manager_observer_(this),
+ camera_observer_(this) {
+ ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+ media::SoundsManager* manager = media::SoundsManager::Get();
+ manager->Initialize(SOUND_OBJECT_DELETE,
+ bundle.GetRawDataResource(IDR_SOUND_OBJECT_DELETE_WAV));
+ manager->Initialize(SOUND_CAMERA_SNAP,
+ bundle.GetRawDataResource(IDR_SOUND_CAMERA_SNAP_WAV));
+}
+
+ChangePictureHandler::~ChangePictureHandler() {
+ if (select_file_dialog_.get())
+ select_file_dialog_->ListenerDestroyed();
+}
+
+void ChangePictureHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "chooseFile", base::Bind(&ChangePictureHandler::HandleChooseFile,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "photoTaken", base::Bind(&ChangePictureHandler::HandlePhotoTaken,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "discardPhoto", base::Bind(&ChangePictureHandler::HandleDiscardPhoto,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "onChangePicturePageInitialized",
+ base::Bind(&ChangePictureHandler::HandlePageInitialized,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "selectImage", base::Bind(&ChangePictureHandler::HandleSelectImage,
+ base::Unretained(this)));
+}
+
+void ChangePictureHandler::OnJavascriptAllowed() {
+ user_manager_observer_.Add(user_manager::UserManager::Get());
+ camera_observer_.Add(CameraPresenceNotifier::GetInstance());
+}
+
+void ChangePictureHandler::OnJavascriptDisallowed() {
+ user_manager_observer_.Remove(user_manager::UserManager::Get());
+ camera_observer_.Remove(CameraPresenceNotifier::GetInstance());
+}
+
+void ChangePictureHandler::SendDefaultImages() {
+ base::ListValue image_urls;
+ for (int i = default_user_image::kFirstDefaultImageIndex;
+ i < default_user_image::kDefaultImagesCount; ++i) {
+ std::unique_ptr<base::DictionaryValue> image_data(
+ new base::DictionaryValue);
+ image_data->SetString("url", default_user_image::GetDefaultImageUrl(i));
+ image_data->SetString("author",
+ l10n_util::GetStringUTF16(
+ default_user_image::kDefaultImageAuthorIDs[i]));
+ image_data->SetString("website",
+ l10n_util::GetStringUTF16(
+ default_user_image::kDefaultImageWebsiteIDs[i]));
+ image_data->SetString("title",
+ default_user_image::GetDefaultImageDescription(i));
+ image_urls.Append(std::move(image_data));
+ }
+ FireWebUIListener("default-images-changed", image_urls);
+}
+
+void ChangePictureHandler::HandleChooseFile(const base::ListValue* args) {
+ DCHECK(args && args->empty());
+ select_file_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
+
+ base::FilePath downloads_path;
+ if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &downloads_path)) {
+ NOTREACHED();
+ return;
+ }
+
+ // Static so we initialize it only once.
+ CR_DEFINE_STATIC_LOCAL(ui::SelectFileDialog::FileTypeInfo, file_type_info,
+ (GetUserImageFileTypeInfo()));
+
+ select_file_dialog_->SelectFile(
+ ui::SelectFileDialog::SELECT_OPEN_FILE,
+ l10n_util::GetStringUTF16(IDS_DOWNLOAD_TITLE), downloads_path,
+ &file_type_info, 0, FILE_PATH_LITERAL(""), GetBrowserWindow(), NULL);
+}
+
+void ChangePictureHandler::HandleDiscardPhoto(const base::ListValue* args) {
+ DCHECK(args->empty());
+ AccessibilityManager::Get()->PlayEarcon(
+ SOUND_OBJECT_DELETE, PlaySoundOption::SPOKEN_FEEDBACK_ENABLED);
+}
+
+void ChangePictureHandler::HandlePhotoTaken(const base::ListValue* args) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ AccessibilityManager::Get()->PlayEarcon(
+ SOUND_CAMERA_SNAP, PlaySoundOption::SPOKEN_FEEDBACK_ENABLED);
+
+ std::string image_url;
+ if (!args || args->GetSize() != 1 || !args->GetString(0, &image_url))
+ NOTREACHED();
+ DCHECK(!image_url.empty());
+
+ std::string mime_type, charset, raw_data;
+ if (!net::DataURL::Parse(GURL(image_url), &mime_type, &charset, &raw_data))
+ NOTREACHED();
+ DCHECK_EQ("image/png", mime_type);
+
+ user_photo_ = gfx::ImageSkia();
+ user_photo_data_url_ = image_url;
+
+ ImageDecoder::Cancel(this);
+ ImageDecoder::Start(this, raw_data);
+}
+
+void ChangePictureHandler::HandlePageInitialized(const base::ListValue* args) {
+ DCHECK(args && args->empty());
+
+ AllowJavascript();
+
+ SendDefaultImages();
+ SendSelectedImage();
+ UpdateProfileImage();
+}
+
+void ChangePictureHandler::SendSelectedImage() {
+ const user_manager::User* user = GetUser();
+ DCHECK(user->GetAccountId().is_valid());
+
+ previous_image_index_ = user->image_index();
+ switch (previous_image_index_) {
+ case user_manager::User::USER_IMAGE_EXTERNAL: {
+ // User has image from camera/file, record it and add to the image list.
+ previous_image_ = user->GetImage();
+ SendOldImage(webui::GetBitmapDataUrl(*previous_image_.bitmap()));
+ break;
+ }
+ case user_manager::User::USER_IMAGE_PROFILE: {
+ // User has their Profile image as the current image.
+ SendProfileImage(user->GetImage(), true);
+ break;
+ }
+ default: {
+ DCHECK(previous_image_index_ >= 0 &&
+ previous_image_index_ < default_user_image::kDefaultImagesCount);
+ if (previous_image_index_ >=
+ default_user_image::kFirstDefaultImageIndex) {
+ // User has image from the current set of default images.
+ base::Value image_url(
+ default_user_image::GetDefaultImageUrl(previous_image_index_));
+ FireWebUIListener("selected-image-changed", image_url);
+ } else {
+ // User has an old default image, so present it in the same manner as a
+ // previous image from file.
+ SendOldImage(
+ default_user_image::GetDefaultImageUrl(previous_image_index_));
+ }
+ }
+ }
+}
+
+void ChangePictureHandler::SendProfileImage(const gfx::ImageSkia& image,
+ bool should_select) {
+ base::Value data_url(webui::GetBitmapDataUrl(*image.bitmap()));
+ base::Value select(should_select);
+ FireWebUIListener("profile-image-changed", data_url, select);
+}
+
+void ChangePictureHandler::UpdateProfileImage() {
+ UserImageManager* user_image_manager =
+ ChromeUserManager::Get()->GetUserImageManager(GetUser()->GetAccountId());
+ // If we have a downloaded profile image and haven't sent it in
+ // |SendSelectedImage|, send it now (without selecting).
+ if (previous_image_index_ != user_manager::User::USER_IMAGE_PROFILE &&
+ !user_image_manager->DownloadedProfileImage().isNull())
+ SendProfileImage(user_image_manager->DownloadedProfileImage(), false);
+
+ user_image_manager->DownloadProfileImage(kProfileDownloadReason);
+}
+
+void ChangePictureHandler::SendOldImage(const std::string& image_url) {
+ previous_image_url_ = image_url;
+ base::Value url(image_url);
+ FireWebUIListener("old-image-changed", url);
+}
+
+void ChangePictureHandler::HandleSelectImage(const base::ListValue* args) {
+ std::string image_url;
+ std::string image_type;
+ if (!args || args->GetSize() != 2 || !args->GetString(0, &image_url) ||
+ !args->GetString(1, &image_type)) {
+ NOTREACHED();
+ return;
+ }
+ // |image_url| may be empty unless |image_type| is "default".
+ DCHECK(!image_type.empty());
+
+ UserImageManager* user_image_manager =
+ ChromeUserManager::Get()->GetUserImageManager(GetUser()->GetAccountId());
+ int image_index = user_manager::User::USER_IMAGE_INVALID;
+ bool waiting_for_camera_photo = false;
+
+ if (image_type == "old") {
+ // Previous image (from camera or manually uploaded) re-selected.
+ DCHECK(!previous_image_.isNull());
+ user_image_manager->SaveUserImage(
+ user_manager::UserImage::CreateAndEncode(
+ previous_image_,
+ user_manager::UserImage::FORMAT_JPEG));
+
+ UMA_HISTOGRAM_EXACT_LINEAR("UserImage.ChangeChoice",
+ default_user_image::kHistogramImageOld,
+ default_user_image::kHistogramImagesCount);
+ VLOG(1) << "Selected old user image";
+ } else if (image_type == "default" &&
+ default_user_image::IsDefaultImageUrl(image_url, &image_index)) {
+ // One of the default user images.
+ user_image_manager->SaveUserDefaultImageIndex(image_index);
+
+ UMA_HISTOGRAM_EXACT_LINEAR(
+ "UserImage.ChangeChoice",
+ default_user_image::GetDefaultImageHistogramValue(image_index),
+ default_user_image::kHistogramImagesCount);
+ VLOG(1) << "Selected default user image: " << image_index;
+ } else if (image_type == "camera") {
+ // Camera image is selected.
+ if (user_photo_.isNull()) {
+ waiting_for_camera_photo = true;
+ VLOG(1) << "Still waiting for camera image to decode";
+ } else {
+ SetImageFromCamera(user_photo_);
+ }
+ } else if (image_type == "profile") {
+ // Profile image selected. Could be previous (old) user image.
+ user_image_manager->SaveUserImageFromProfileImage();
+
+ if (previous_image_index_ == user_manager::User::USER_IMAGE_PROFILE) {
+ UMA_HISTOGRAM_EXACT_LINEAR("UserImage.ChangeChoice",
+ default_user_image::kHistogramImageOld,
+ default_user_image::kHistogramImagesCount);
+ VLOG(1) << "Selected old (profile) user image";
+ } else {
+ UMA_HISTOGRAM_EXACT_LINEAR("UserImage.ChangeChoice",
+ default_user_image::kHistogramImageFromProfile,
+ default_user_image::kHistogramImagesCount);
+ VLOG(1) << "Selected profile image";
+ }
+ } else {
+ NOTREACHED() << "Unexpected image type: " << image_type;
+ }
+
+ // Ignore the result of the previous decoding if it's no longer needed.
+ if (!waiting_for_camera_photo)
+ ImageDecoder::Cancel(this);
+}
+
+void ChangePictureHandler::FileSelected(const base::FilePath& path,
+ int index,
+ void* params) {
+ ChromeUserManager::Get()
+ ->GetUserImageManager(GetUser()->GetAccountId())
+ ->SaveUserImageFromFile(path);
+ UMA_HISTOGRAM_EXACT_LINEAR("UserImage.ChangeChoice",
+ default_user_image::kHistogramImageFromFile,
+ default_user_image::kHistogramImagesCount);
+ VLOG(1) << "Selected image from file";
+}
+
+void ChangePictureHandler::SetImageFromCamera(const gfx::ImageSkia& photo) {
+ ChromeUserManager::Get()
+ ->GetUserImageManager(GetUser()->GetAccountId())
+ ->SaveUserImage(user_manager::UserImage::CreateAndEncode(
+ photo, user_manager::UserImage::FORMAT_JPEG));
+ UMA_HISTOGRAM_EXACT_LINEAR("UserImage.ChangeChoice",
+ default_user_image::kHistogramImageFromCamera,
+ default_user_image::kHistogramImagesCount);
+ VLOG(1) << "Selected camera photo";
+}
+
+void ChangePictureHandler::SetCameraPresent(bool present) {
+ FireWebUIListener("camera-presence-changed", base::Value(present));
+}
+
+void ChangePictureHandler::OnCameraPresenceCheckDone(bool is_camera_present) {
+ SetCameraPresent(is_camera_present);
+}
+
+void ChangePictureHandler::OnUserImageChanged(const user_manager::User& user) {
+ // Not initialized yet.
+ if (previous_image_index_ == user_manager::User::USER_IMAGE_INVALID)
+ return;
+ SendSelectedImage();
+}
+
+void ChangePictureHandler::OnUserProfileImageUpdated(
+ const user_manager::User& user,
+ const gfx::ImageSkia& profile_image) {
+ // User profile image has been updated.
+ SendProfileImage(profile_image, false);
+}
+
+gfx::NativeWindow ChangePictureHandler::GetBrowserWindow() const {
+ Browser* browser =
+ chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
+ return browser->window()->GetNativeWindow();
+}
+
+void ChangePictureHandler::OnImageDecoded(const SkBitmap& decoded_image) {
+ user_photo_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
+ SetImageFromCamera(user_photo_);
+}
+
+void ChangePictureHandler::OnDecodeImageFailed() {
+ NOTREACHED() << "Failed to decode PNG image from WebUI";
+}
+
+const user_manager::User* ChangePictureHandler::GetUser() const {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ const user_manager::User* user =
+ ProfileHelper::Get()->GetUserByProfile(profile);
+ if (!user)
+ return user_manager::UserManager::Get()->GetActiveUser();
+ return user;
+}
+
+} // namespace settings
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h
new file mode 100644
index 00000000000..a213282241e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h
@@ -0,0 +1,139 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CHANGE_PICTURE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CHANGE_PICTURE_HANDLER_H_
+
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/chromeos/camera_presence_notifier.h"
+#include "chrome/browser/image_decoder.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "components/user_manager/user_manager.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace user_manager {
+class User;
+}
+
+namespace chromeos {
+
+namespace settings {
+
+// ChromeOS user image settings page UI handler.
+class ChangePictureHandler : public ::settings::SettingsPageUIHandler,
+ public ui::SelectFileDialog::Listener,
+ public user_manager::UserManager::Observer,
+ public ImageDecoder::ImageRequest,
+ public CameraPresenceNotifier::Observer {
+ public:
+ ChangePictureHandler();
+ ~ChangePictureHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ // CameraPresenceNotifier::Observer implementation:
+ void OnCameraPresenceCheckDone(bool is_camera_present) override;
+
+ private:
+ // Sends list of available default images to the page.
+ void SendDefaultImages();
+
+ // Sends current selection to the page.
+ void SendSelectedImage();
+
+ // Sends the profile image to the page. If |should_select| is true then
+ // the profile image element is selected.
+ void SendProfileImage(const gfx::ImageSkia& image, bool should_select);
+
+ // Starts profile image update and shows the last downloaded profile image,
+ // if any, on the page. Shouldn't be called before |SendProfileImage|.
+ void UpdateProfileImage();
+
+ // Sends previous user image to the page.
+ void SendOldImage(const std::string& image_url);
+
+ // Starts camera presence check.
+ void CheckCameraPresence();
+
+ // Updates UI with camera presence state.
+ void SetCameraPresent(bool present);
+
+ // Opens a file selection dialog to choose user image from file.
+ void HandleChooseFile(const base::ListValue* args);
+
+ // Handles photo taken with WebRTC UI.
+ void HandlePhotoTaken(const base::ListValue* args);
+
+ // Handles 'discard-photo' button click.
+ void HandleDiscardPhoto(const base::ListValue* args);
+
+ // Gets the list of available user images and sends it to the page.
+ void HandleGetAvailableImages(const base::ListValue* args);
+
+ // Handles page initialized event.
+ void HandlePageInitialized(const base::ListValue* args);
+
+ // Selects one of the available images as user's.
+ void HandleSelectImage(const base::ListValue* args);
+
+ // SelectFileDialog::Delegate implementation.
+ void FileSelected(const base::FilePath& path,
+ int index,
+ void* params) override;
+
+ // user_manager::UserManager::Observer implementation.
+ void OnUserImageChanged(const user_manager::User& user) override;
+ void OnUserProfileImageUpdated(const user_manager::User& user,
+ const gfx::ImageSkia& profile_image) override;
+
+ // Sets user image to photo taken from camera.
+ void SetImageFromCamera(const gfx::ImageSkia& photo);
+
+ // Returns handle to browser window or NULL if it can't be found.
+ gfx::NativeWindow GetBrowserWindow() const;
+
+ // Overriden from ImageDecoder::ImageRequest:
+ void OnImageDecoded(const SkBitmap& decoded_image) override;
+ void OnDecodeImageFailed() override;
+
+ // Returns user related to current WebUI. If this user doesn't exist,
+ // returns active user.
+ const user_manager::User* GetUser() const;
+
+ scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
+
+ // Previous user image from camera/file and its data URL.
+ gfx::ImageSkia previous_image_;
+ std::string previous_image_url_;
+
+ // Index of the previous user image.
+ int previous_image_index_;
+
+ // Last user photo, if taken.
+ gfx::ImageSkia user_photo_;
+
+ // Data URL for |user_photo_|.
+ std::string user_photo_data_url_;
+
+ ScopedObserver<user_manager::UserManager, ChangePictureHandler>
+ user_manager_observer_;
+ ScopedObserver<CameraPresenceNotifier, ChangePictureHandler> camera_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChangePictureHandler);
+};
+
+} // namespace settings
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CHANGE_PICTURE_HANDLER_H_
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
new file mode 100644
index 00000000000..e5d02eb7315
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
@@ -0,0 +1,447 @@
+// 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 "chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_util.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/printing/ppd_provider_factory.h"
+#include "chrome/browser/chromeos/printing/printer_configurer.h"
+#include "chrome/browser/chromeos/printing/printer_discoverer.h"
+#include "chrome/browser/chromeos/printing/printers_manager_factory.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/chrome_select_file_policy.h"
+#include "chrome/common/chrome_paths.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/debug_daemon_client.h"
+#include "chromeos/printing/ppd_cache.h"
+#include "chromeos/printing/ppd_provider.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_ui.h"
+#include "google_apis/google_api_keys.h"
+#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 {
+
+// 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 };
+
+void RecordPpdSource(const PpdSourceForHistogram& source) {
+ UMA_HISTOGRAM_ENUMERATION("Printing.CUPS.PpdSource", source, kPpdSourceMax);
+}
+
+void OnRemovedPrinter(const Printer::PrinterProtocol& protocol, bool success) {
+ UMA_HISTOGRAM_ENUMERATION("Printing.CUPS.PrinterRemoved", protocol,
+ Printer::PrinterProtocol::kProtocolMax);
+}
+
+std::unique_ptr<base::DictionaryValue> GetPrinterInfo(const Printer& printer) {
+ std::unique_ptr<base::DictionaryValue> printer_info =
+ base::MakeUnique<base::DictionaryValue>();
+ printer_info->SetString("printerId", printer.id());
+ printer_info->SetString("printerName", printer.display_name());
+ printer_info->SetString("printerDescription", printer.description());
+ printer_info->SetString("printerManufacturer", printer.manufacturer());
+ printer_info->SetString("printerModel", printer.model());
+ // Get protocol, ip address and queue from the printer's URI.
+ const std::string printer_uri = printer.uri();
+ url::Parsed parsed;
+ url::ParseStandardURL(printer_uri.c_str(), printer_uri.length(), &parsed);
+
+ std::string scheme;
+ std::string host;
+ std::string path;
+ if (parsed.scheme.len > 0)
+ scheme = std::string(printer_uri, parsed.scheme.begin, parsed.scheme.len);
+ if (parsed.host.len > 0)
+ host = std::string(printer_uri, parsed.host.begin, parsed.host.len);
+ if (parsed.path.len > 0)
+ path = std::string(printer_uri, parsed.path.begin, parsed.path.len);
+ if (base::ToLowerASCII(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
+ // entire host/path/query block is the printer address for USB.
+ printer_info->SetString("printerAddress",
+ printer_uri.substr(parsed.host.begin));
+ } else {
+ printer_info->SetString("printerAddress", host);
+ if (!path.empty()) {
+ printer_info->SetString("printerQueue", path.substr(1));
+ }
+ }
+ printer_info->SetString("printerProtocol", base::ToLowerASCII(scheme));
+
+ return printer_info;
+}
+
+} // namespace
+
+CupsPrintersHandler::CupsPrintersHandler(content::WebUI* webui)
+ : printer_discoverer_(nullptr),
+ profile_(Profile::FromWebUI(webui)),
+ weak_factory_(this) {
+ ppd_provider_ = printing::CreateProvider(profile_);
+ printer_configurer_ = chromeos::PrinterConfigurer::Create(profile_);
+}
+
+CupsPrintersHandler::~CupsPrintersHandler() {}
+
+void CupsPrintersHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "getCupsPrintersList",
+ base::Bind(&CupsPrintersHandler::HandleGetCupsPrintersList,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "updateCupsPrinter",
+ base::Bind(&CupsPrintersHandler::HandleUpdateCupsPrinter,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "removeCupsPrinter",
+ base::Bind(&CupsPrintersHandler::HandleRemoveCupsPrinter,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "addCupsPrinter", base::Bind(&CupsPrintersHandler::HandleAddCupsPrinter,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getCupsPrinterManufacturersList",
+ base::Bind(&CupsPrintersHandler::HandleGetCupsPrinterManufacturers,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getCupsPrinterModelsList",
+ base::Bind(&CupsPrintersHandler::HandleGetCupsPrinterModels,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "selectPPDFile", base::Bind(&CupsPrintersHandler::HandleSelectPPDFile,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "startDiscoveringPrinters",
+ base::Bind(&CupsPrintersHandler::HandleStartDiscovery,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "stopDiscoveringPrinters",
+ base::Bind(&CupsPrintersHandler::HandleStopDiscovery,
+ base::Unretained(this)));
+}
+
+void CupsPrintersHandler::HandleGetCupsPrintersList(
+ const base::ListValue* args) {
+ AllowJavascript();
+
+ CHECK_EQ(1U, args->GetSize());
+ std::string callback_id;
+ CHECK(args->GetString(0, &callback_id));
+
+ std::vector<std::unique_ptr<Printer>> printers =
+ PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinters();
+
+ auto printers_list = base::MakeUnique<base::ListValue>();
+ for (const std::unique_ptr<Printer>& printer : printers) {
+ std::unique_ptr<base::DictionaryValue> printer_info =
+ GetPrinterInfo(*printer.get());
+ printers_list->Append(std::move(printer_info));
+ }
+
+ auto response = base::MakeUnique<base::DictionaryValue>();
+ response->Set("printerList", std::move(printers_list));
+ ResolveJavascriptCallback(base::Value(callback_id), *response);
+}
+
+void CupsPrintersHandler::HandleUpdateCupsPrinter(const base::ListValue* args) {
+ std::string printer_id;
+ std::string printer_name;
+ CHECK(args->GetString(0, &printer_id));
+ CHECK(args->GetString(1, &printer_name));
+
+ std::unique_ptr<Printer> printer = base::MakeUnique<Printer>(printer_id);
+ printer->set_display_name(printer_name);
+ PrintersManagerFactory::GetForBrowserContext(profile_)->RegisterPrinter(
+ std::move(printer));
+}
+
+void CupsPrintersHandler::HandleRemoveCupsPrinter(const base::ListValue* args) {
+ std::string printer_id;
+ std::string printer_name;
+ CHECK(args->GetString(0, &printer_id));
+ CHECK(args->GetString(1, &printer_name));
+ PrintersManager* prefs =
+ PrintersManagerFactory::GetForBrowserContext(profile_);
+ auto printer = prefs->GetPrinter(printer_id);
+ if (!printer)
+ return;
+
+ Printer::PrinterProtocol protocol = printer->GetProtocol();
+ prefs->RemovePrinter(printer_id);
+
+ chromeos::DebugDaemonClient* client =
+ chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
+ client->CupsRemovePrinter(printer_name,
+ base::Bind(&OnRemovedPrinter, protocol),
+ base::Bind(&base::DoNothing));
+}
+
+void CupsPrintersHandler::HandleAddCupsPrinter(const base::ListValue* args) {
+ AllowJavascript();
+
+ const base::DictionaryValue* printer_dict = nullptr;
+ CHECK(args->GetDictionary(0, &printer_dict));
+
+ std::string printer_id;
+ std::string printer_name;
+ std::string printer_description;
+ std::string printer_manufacturer;
+ std::string printer_model;
+ std::string printer_address;
+ std::string printer_protocol;
+ std::string printer_queue;
+ std::string printer_ppd_path;
+ CHECK(printer_dict->GetString("printerId", &printer_id));
+ CHECK(printer_dict->GetString("printerName", &printer_name));
+ CHECK(printer_dict->GetString("printerDescription", &printer_description));
+ CHECK(printer_dict->GetString("printerManufacturer", &printer_manufacturer));
+ CHECK(printer_dict->GetString("printerModel", &printer_model));
+ CHECK(printer_dict->GetString("printerAddress", &printer_address));
+ CHECK(printer_dict->GetString("printerProtocol", &printer_protocol));
+ // printerQueue might be null for a printer whose protocol is not 'LPD'.
+ printer_dict->GetString("printerQueue", &printer_queue);
+
+ // printerPPDPath might be null for an auto-discovered printer.
+ printer_dict->GetString("printerPPDPath", &printer_ppd_path);
+ std::string printer_uri = printer_protocol + "://" + printer_address;
+ if (!printer_queue.empty()) {
+ printer_uri += "/" + printer_queue;
+ }
+
+ std::unique_ptr<Printer> printer = base::MakeUnique<Printer>(printer_id);
+ printer->set_display_name(printer_name);
+ printer->set_description(printer_description);
+ printer->set_manufacturer(printer_manufacturer);
+ printer->set_model(printer_model);
+ printer->set_uri(printer_uri);
+
+ // Verify a valid ppd path is present.
+ if (!printer_ppd_path.empty()) {
+ RecordPpdSource(kUser);
+ GURL tmp = net::FilePathToFileURL(base::FilePath(printer_ppd_path));
+ if (!tmp.is_valid()) {
+ LOG(ERROR) << "Invalid ppd path: " << printer_ppd_path;
+ OnAddPrinterError();
+ return;
+ }
+ printer->mutable_ppd_reference()->user_supplied_ppd_url = tmp.spec();
+ } else if (!printer_manufacturer.empty() && !printer_model.empty()) {
+ RecordPpdSource(kScs);
+ // Using the manufacturer and model, get a ppd reference.
+ if (!ppd_provider_->GetPpdReference(printer_manufacturer, printer_model,
+ printer->mutable_ppd_reference())) {
+ LOG(ERROR) << "Failed to get ppd reference";
+ OnAddPrinterError();
+ return;
+ }
+ }
+
+ // Copy the printer for the configurer. Ownership needs to be transfered to
+ // the receiver of the callback.
+ const Printer printer_copy = *printer;
+ printer_configurer_->SetUpPrinter(
+ printer_copy,
+ base::Bind(&CupsPrintersHandler::OnAddedPrinter,
+ weak_factory_.GetWeakPtr(), base::Passed(&printer)));
+}
+
+void CupsPrintersHandler::OnAddedPrinter(
+ std::unique_ptr<Printer> printer,
+ chromeos::PrinterSetupResult result_code) {
+ std::string printer_name = printer->display_name();
+ UMA_HISTOGRAM_ENUMERATION("Printing.CUPS.PrinterSetupResult", result_code,
+ chromeos::PrinterSetupResult::kMaxValue);
+ switch (result_code) {
+ case chromeos::PrinterSetupResult::kSuccess: {
+ UMA_HISTOGRAM_ENUMERATION("Printing.CUPS.PrinterAdded",
+ printer->GetProtocol(), Printer::kProtocolMax);
+ auto* manager = PrintersManagerFactory::GetForBrowserContext(profile_);
+ manager->PrinterInstalled(*printer);
+ manager->RegisterPrinter(std::move(printer));
+ break;
+ }
+ case chromeos::PrinterSetupResult::kPpdNotFound:
+ LOG(WARNING) << "Could not locate requested PPD";
+ break;
+ case chromeos::PrinterSetupResult::kPpdTooLarge:
+ LOG(WARNING) << "PPD is too large";
+ break;
+ case chromeos::PrinterSetupResult::kPpdUnretrievable:
+ LOG(WARNING) << "Could not retrieve PPD from server";
+ break;
+ case chromeos::PrinterSetupResult::kInvalidPpd:
+ LOG(WARNING) << "Provided PPD is invalid.";
+ break;
+ case chromeos::PrinterSetupResult::kPrinterUnreachable:
+ LOG(WARNING) << "Could not contact printer for configuration";
+ break;
+ case chromeos::PrinterSetupResult::kDbusError:
+ case chromeos::PrinterSetupResult::kFatalError:
+ LOG(ERROR) << "Unrecoverable error. Reboot required.";
+ break;
+ case chromeos::PrinterSetupResult::kMaxValue:
+ NOTREACHED() << "This is not an expected value";
+ break;
+ }
+ CallJavascriptFunction(
+ "cr.webUIListenerCallback", base::Value("on-add-cups-printer"),
+ base::Value(result_code == chromeos::PrinterSetupResult::kSuccess),
+ base::Value(printer_name));
+}
+
+void CupsPrintersHandler::OnAddPrinterError() {
+ FireWebUIListener("on-add-cups-printer", base::Value(false), base::Value(""));
+}
+
+void CupsPrintersHandler::HandleGetCupsPrinterManufacturers(
+ const base::ListValue* args) {
+ AllowJavascript();
+ std::string js_callback;
+ CHECK_EQ(1U, args->GetSize());
+ CHECK(args->GetString(0, &js_callback));
+ ppd_provider_->ResolveManufacturers(
+ base::Bind(&CupsPrintersHandler::ResolveManufacturersDone,
+ weak_factory_.GetWeakPtr(), js_callback));
+}
+
+void CupsPrintersHandler::HandleGetCupsPrinterModels(
+ const base::ListValue* args) {
+ AllowJavascript();
+ std::string js_callback;
+ std::string manufacturer;
+ CHECK_EQ(2U, args->GetSize());
+ CHECK(args->GetString(0, &js_callback));
+ CHECK(args->GetString(1, &manufacturer));
+
+ // Empty manufacturer queries may be triggered as a part of the ui
+ // initialization, and should just return empty results.
+ if (manufacturer.empty()) {
+ base::DictionaryValue response;
+ response.SetBoolean("success", true);
+ response.Set("models", base::MakeUnique<base::ListValue>());
+ ResolveJavascriptCallback(base::Value(js_callback), response);
+ return;
+ }
+
+ ppd_provider_->ResolvePrinters(
+ manufacturer, base::Bind(&CupsPrintersHandler::ResolvePrintersDone,
+ weak_factory_.GetWeakPtr(), js_callback));
+}
+
+void CupsPrintersHandler::HandleSelectPPDFile(const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ CHECK(args->GetString(0, &webui_callback_id_));
+
+ base::FilePath downloads_path =
+ DownloadPrefs::FromDownloadManager(
+ content::BrowserContext::GetDownloadManager(profile_))
+ ->DownloadPath();
+
+ select_file_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
+ gfx::NativeWindow owning_window =
+ chrome::FindBrowserWithWebContents(web_ui()->GetWebContents())
+ ->window()
+ ->GetNativeWindow();
+ select_file_dialog_->SelectFile(
+ ui::SelectFileDialog::SELECT_OPEN_FILE, base::string16(), downloads_path,
+ nullptr, 0, FILE_PATH_LITERAL(""), owning_window, nullptr);
+}
+
+void CupsPrintersHandler::ResolveManufacturersDone(
+ const std::string& js_callback,
+ chromeos::printing::PpdProvider::CallbackResultCode result_code,
+ const std::vector<std::string>& manufacturers) {
+ auto manufacturers_value = base::MakeUnique<base::ListValue>();
+ if (result_code == chromeos::printing::PpdProvider::SUCCESS) {
+ manufacturers_value->AppendStrings(manufacturers);
+ }
+ base::DictionaryValue response;
+ response.SetBoolean("success",
+ result_code == chromeos::printing::PpdProvider::SUCCESS);
+ response.Set("manufacturers", std::move(manufacturers_value));
+ ResolveJavascriptCallback(base::Value(js_callback), response);
+}
+
+void CupsPrintersHandler::ResolvePrintersDone(
+ const std::string& js_callback,
+ chromeos::printing::PpdProvider::CallbackResultCode result_code,
+ const std::vector<std::string>& printers) {
+ auto printers_value = base::MakeUnique<base::ListValue>();
+ if (result_code == chromeos::printing::PpdProvider::SUCCESS) {
+ printers_value->AppendStrings(printers);
+ }
+ base::DictionaryValue response;
+ response.SetBoolean("success",
+ result_code == chromeos::printing::PpdProvider::SUCCESS);
+ response.Set("models", std::move(printers_value));
+ ResolveJavascriptCallback(base::Value(js_callback), response);
+}
+
+void CupsPrintersHandler::FileSelected(const base::FilePath& path,
+ int index,
+ void* params) {
+ DCHECK(!webui_callback_id_.empty());
+ ResolveJavascriptCallback(base::Value(webui_callback_id_),
+ base::Value(path.value()));
+ webui_callback_id_.clear();
+}
+
+void CupsPrintersHandler::HandleStartDiscovery(const base::ListValue* args) {
+ if (!printer_discoverer_.get()) {
+ printer_discoverer_ =
+ chromeos::PrinterDiscoverer::CreateForProfile(profile_);
+ }
+
+ printer_discoverer_->AddObserver(this);
+}
+
+void CupsPrintersHandler::HandleStopDiscovery(const base::ListValue* args) {
+ printer_discoverer_.reset();
+}
+
+void CupsPrintersHandler::OnPrintersFound(
+ const std::vector<Printer>& printers) {
+ std::unique_ptr<base::ListValue> printers_list =
+ base::MakeUnique<base::ListValue>();
+ for (const auto& printer : printers) {
+ printers_list->Append(GetPrinterInfo(printer));
+ }
+
+ FireWebUIListener("on-printer-discovered", *printers_list);
+}
+
+void CupsPrintersHandler::OnDiscoveryInitialScanDone(int printer_count) {
+ UMA_HISTOGRAM_COUNTS_100("Printing.CUPS.PrintersDiscovered", printer_count);
+ FireWebUIListener("on-printer-discovery-done");
+}
+
+} // namespace settings
+} // namespace chromeos
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
new file mode 100644
index 00000000000..343daeab6df
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
@@ -0,0 +1,118 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CUPS_PRINTERS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CUPS_PRINTERS_HANDLER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/printing/printer_configurer.h"
+#include "chrome/browser/chromeos/printing/printer_discoverer.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "chromeos/printing/ppd_provider.h"
+#include "chromeos/printing/printer_configuration.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+
+namespace base {
+class ListValue;
+} // namespace base
+
+class Profile;
+
+namespace chromeos {
+namespace printing {
+class PpdProvider;
+}
+namespace settings {
+
+// Chrome OS CUPS printing settings page UI handler.
+class CupsPrintersHandler : public ::settings::SettingsPageUIHandler,
+ public ui::SelectFileDialog::Listener,
+ public chromeos::PrinterDiscoverer::Observer {
+ public:
+ explicit CupsPrintersHandler(content::WebUI* webui);
+ ~CupsPrintersHandler() override;
+
+ // SettingsPageUIHandler overrides:
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override {}
+ void OnJavascriptDisallowed() override {}
+
+ private:
+ // Gets all CUPS printers and return it to WebUI.
+ void HandleGetCupsPrintersList(const base::ListValue* args);
+ void HandleUpdateCupsPrinter(const base::ListValue* args);
+ void HandleRemoveCupsPrinter(const base::ListValue* args);
+
+ void HandleAddCupsPrinter(const base::ListValue* args);
+ void OnAddedPrinter(std::unique_ptr<Printer> printer,
+ chromeos::PrinterSetupResult result);
+ void OnAddPrinterError();
+
+ // Get a list of all manufacturers for which we have at least one model of
+ // printer supported. Takes one argument, the callback id for the result.
+ // The callback will be invoked with {success: <boolean>, models:
+ // <Array<string>>}.
+ void HandleGetCupsPrinterManufacturers(const base::ListValue* args);
+
+ // Given a manufacturer, get a list of all models of printers for which we can
+ // get drivers. Takes two arguments - the callback id and the manufacturer
+ // name for which we want to list models. The callback will be called with
+ // {success: <boolean>, models: Array<string>}.
+ void HandleGetCupsPrinterModels(const base::ListValue* args);
+
+ void HandleSelectPPDFile(const base::ListValue* args);
+
+ // PpdProvider callback handlers.
+ void ResolveManufacturersDone(
+ const std::string& js_callback,
+ chromeos::printing::PpdProvider::CallbackResultCode result_code,
+ const std::vector<std::string>& available);
+ void ResolvePrintersDone(
+ const std::string& js_callback,
+ chromeos::printing::PpdProvider::CallbackResultCode result_code,
+ const std::vector<std::string>& available);
+
+ // ui::SelectFileDialog::Listener override:
+ void FileSelected(const base::FilePath& path,
+ int index,
+ void* params) override;
+
+ void HandleStartDiscovery(const base::ListValue* args);
+ void HandleStopDiscovery(const base::ListValue* args);
+
+ // chromeos::PrinterDiscoverer::Observer override:
+ void OnPrintersFound(const std::vector<Printer>& printers) override;
+ void OnDiscoveryInitialScanDone(int printer_count) override;
+
+ // Invokes debugd to add the printer to CUPS. If |ipp_everywhere| is true,
+ // automatic configuration will be attempted and |ppd_path| is ignored.
+ // |ppd_path| is the path to a Postscript Printer Description file that will
+ // be used to configure the printer capabilities. This file must be in
+ // Downloads or the PPD Cache.
+ void AddPrinterToCups(std::unique_ptr<Printer> printer,
+ const base::FilePath& ppd_path,
+ bool ipp_everywhere);
+
+ std::unique_ptr<chromeos::PrinterDiscoverer> printer_discoverer_;
+ scoped_refptr<chromeos::printing::PpdProvider> ppd_provider_;
+ std::unique_ptr<chromeos::PrinterConfigurer> printer_configurer_;
+
+ Profile* profile_;
+ scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
+ std::string webui_callback_id_;
+
+ base::WeakPtrFactory<CupsPrintersHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CupsPrintersHandler);
+};
+
+} // namespace settings
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CUPS_PRINTERS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc
new file mode 100644
index 00000000000..2562de268c0
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc
@@ -0,0 +1,182 @@
+// 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 "chrome/browser/ui/webui/settings/chromeos/date_time_handler.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h"
+#include "chrome/browser/chromeos/set_time_dialog.h"
+#include "chrome/browser/chromeos/system/timezone_resolver_manager.h"
+#include "chrome/browser/chromeos/system/timezone_util.h"
+#include "chrome/common/pref_names.h"
+#include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/system_clock_client.h"
+#include "chromeos/settings/timezone_settings.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace chromeos {
+namespace settings {
+
+namespace {
+
+// Returns whether the system time zone automatic detection policy is disabled
+// by a flag.
+bool IsSystemTimezoneAutomaticDetectionPolicyFlagDisabled() {
+ return base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableSystemTimezoneAutomaticDetectionPolicy);
+}
+
+// Returns whether the system's automatic time zone detection setting is
+// managed, which may override the user's setting.
+bool IsSystemTimezoneAutomaticDetectionManaged() {
+ if (IsSystemTimezoneAutomaticDetectionPolicyFlagDisabled())
+ return false;
+
+ return g_browser_process->local_state()->IsManagedPreference(
+ prefs::kSystemTimezoneAutomaticDetectionPolicy);
+}
+
+// Returns the system's automatic time zone detection policy value, which
+// corresponds to the SystemTimezoneProto's AutomaticTimezoneDetectionType
+// enum and determines whether the user's setting will be overridden.
+int GetSystemTimezoneAutomaticDetectionPolicyValue() {
+ DCHECK(IsSystemTimezoneAutomaticDetectionManaged());
+
+ return g_browser_process->local_state()->GetInteger(
+ prefs::kSystemTimezoneAutomaticDetectionPolicy);
+}
+
+// Returns whether the user can set the automatic detection setting, based on
+// flags and policies.
+bool IsTimezoneAutomaticDetectionUserEditable() {
+ if (system::HasSystemTimezonePolicy())
+ return false;
+
+ if (IsSystemTimezoneAutomaticDetectionManaged()) {
+ return GetSystemTimezoneAutomaticDetectionPolicyValue() ==
+ enterprise_management::SystemTimezoneProto::USERS_DECIDE;
+ }
+
+ return true;
+}
+
+} // namespace
+
+DateTimeHandler::DateTimeHandler()
+ : scoped_observer_(this), weak_ptr_factory_(this) {}
+
+DateTimeHandler::~DateTimeHandler() = default;
+
+DateTimeHandler* DateTimeHandler::Create(
+ content::WebUIDataSource* html_source) {
+ // Set the initial time zone to show.
+ html_source->AddString("timeZoneName", system::GetCurrentTimezoneName());
+ html_source->AddString(
+ "timeZoneID",
+ system::TimezoneSettings::GetInstance()->GetCurrentTimezoneID());
+
+ if (!IsTimezoneAutomaticDetectionUserEditable()) {
+ html_source->AddBoolean("timeZoneAutoDetectValueFromPolicy",
+ g_browser_process->platform_part()
+ ->GetTimezoneResolverManager()
+ ->ShouldApplyResolvedTimezone());
+ }
+
+ return new DateTimeHandler;
+}
+
+void DateTimeHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "dateTimePageReady", base::Bind(&DateTimeHandler::HandleDateTimePageReady,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getTimeZones",
+ base::Bind(&DateTimeHandler::HandleGetTimeZones, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "showSetDateTimeUI", base::Bind(&DateTimeHandler::HandleShowSetDateTimeUI,
+ base::Unretained(this)));
+}
+
+void DateTimeHandler::OnJavascriptAllowed() {
+ SystemClockClient* system_clock_client =
+ DBusThreadManager::Get()->GetSystemClockClient();
+ scoped_observer_.Add(system_clock_client);
+ SystemClockCanSetTimeChanged(system_clock_client->CanSetTime());
+
+ // The system time zone policy disables auto-detection entirely. (However,
+ // the time zone policy does not override the user's time zone itself.)
+ system_timezone_policy_subscription_ =
+ CrosSettings::Get()->AddSettingsObserver(
+ kSystemTimezonePolicy,
+ base::Bind(&DateTimeHandler::NotifyTimezoneAutomaticDetectionPolicy,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ if (IsSystemTimezoneAutomaticDetectionPolicyFlagDisabled())
+ return;
+
+ // The auto-detection policy can force auto-detection on or off.
+ local_state_pref_change_registrar_.Init(g_browser_process->local_state());
+ local_state_pref_change_registrar_.Add(
+ prefs::kSystemTimezoneAutomaticDetectionPolicy,
+ base::Bind(&DateTimeHandler::NotifyTimezoneAutomaticDetectionPolicy,
+ base::Unretained(this)));
+}
+
+void DateTimeHandler::OnJavascriptDisallowed() {
+ scoped_observer_.RemoveAll();
+ system_timezone_policy_subscription_.reset();
+
+ if (!IsSystemTimezoneAutomaticDetectionPolicyFlagDisabled())
+ local_state_pref_change_registrar_.RemoveAll();
+}
+
+void DateTimeHandler::HandleDateTimePageReady(const base::ListValue* args) {
+ AllowJavascript();
+
+ // Send the time zone automatic detection policy in case it changed after the
+ // handler was created.
+ NotifyTimezoneAutomaticDetectionPolicy();
+}
+
+void DateTimeHandler::HandleGetTimeZones(const base::ListValue* args) {
+ AllowJavascript();
+
+ CHECK_EQ(1U, args->GetSize());
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+ ResolveJavascriptCallback(*callback_id, *system::GetTimezoneList().release());
+}
+
+void DateTimeHandler::HandleShowSetDateTimeUI(const base::ListValue* args) {
+ // Make sure the clock status hasn't changed since the button was clicked.
+ if (!DBusThreadManager::Get()->GetSystemClockClient()->CanSetTime())
+ return;
+ SetTimeDialog::ShowDialogInParent(
+ web_ui()->GetWebContents()->GetTopLevelNativeWindow());
+}
+
+void DateTimeHandler::NotifyTimezoneAutomaticDetectionPolicy() {
+ bool managed = !IsTimezoneAutomaticDetectionUserEditable();
+ bool force_enabled = managed &&
+ g_browser_process->platform_part()
+ ->GetTimezoneResolverManager()
+ ->ShouldApplyResolvedTimezone();
+
+ FireWebUIListener("time-zone-auto-detect-policy", base::Value(managed),
+ base::Value(force_enabled));
+}
+
+void DateTimeHandler::SystemClockCanSetTimeChanged(bool can_set_time) {
+ FireWebUIListener("can-set-date-time-changed", base::Value(can_set_time));
+}
+
+} // namespace settings
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_handler.h
new file mode 100644
index 00000000000..b04142f82f7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_handler.h
@@ -0,0 +1,78 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DATE_TIME_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DATE_TIME_HANDLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "chromeos/dbus/system_clock_client.h"
+#include "components/prefs/pref_change_registrar.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace content {
+class WebUIDataSource;
+}
+
+namespace chromeos {
+namespace settings {
+
+// Chrome OS date and time settings page UI handler.
+class DateTimeHandler : public ::settings::SettingsPageUIHandler,
+ public SystemClockClient::Observer {
+ public:
+ ~DateTimeHandler() override;
+
+ // Adds load-time values to html_source before creating the handler.
+ static DateTimeHandler* Create(content::WebUIDataSource* html_source);
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ private:
+ DateTimeHandler();
+
+ // SystemClockClient::Observer implementation.
+ void SystemClockCanSetTimeChanged(bool can_set_time) override;
+
+ // Called when the page is ready.
+ void HandleDateTimePageReady(const base::ListValue* args);
+
+ // Handler to fetch the list of time zones.
+ void HandleGetTimeZones(const base::ListValue* args);
+
+ // Called to show the Set Time UI.
+ void HandleShowSetDateTimeUI(const base::ListValue* args);
+
+ // Updates the UI, enabling or disabling the time zone automatic detection
+ // setting according to policy.
+ void NotifyTimezoneAutomaticDetectionPolicy();
+
+ std::unique_ptr<chromeos::CrosSettings::ObserverSubscription>
+ system_timezone_policy_subscription_;
+
+ // Used to listen to changes to the system time zone detection policy.
+ PrefChangeRegistrar local_state_pref_change_registrar_;
+
+ ScopedObserver<SystemClockClient, SystemClockClient::Observer>
+ scoped_observer_;
+ base::WeakPtrFactory<DateTimeHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DateTimeHandler);
+};
+
+} // namespace settings
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DATE_TIME_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc
new file mode 100644
index 00000000000..dbd70059915
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc
@@ -0,0 +1,82 @@
+// 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 "chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h"
+
+#include "ash/new_window_controller.h"
+#include "ash/shell.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chromeos/chromeos_switches.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/events/devices/input_device_manager.h"
+
+namespace {
+
+bool HasExternalKeyboard() {
+ for (const ui::InputDevice& keyboard :
+ ui::InputDeviceManager::GetInstance()->GetKeyboardDevices()) {
+ if (keyboard.type == ui::InputDeviceType::INPUT_DEVICE_EXTERNAL)
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace
+
+namespace chromeos {
+namespace settings {
+
+KeyboardHandler::KeyboardHandler(content::WebUI* webui)
+ : profile_(Profile::FromWebUI(webui)), observer_(this) {}
+
+KeyboardHandler::~KeyboardHandler() {
+}
+
+void KeyboardHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "initializeKeyboardSettings",
+ base::Bind(&KeyboardHandler::HandleInitialize,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "showKeyboardShortcutsOverlay",
+ base::Bind(&KeyboardHandler::HandleShowKeyboardShortcutsOverlay,
+ base::Unretained(this)));
+}
+
+void KeyboardHandler::OnJavascriptAllowed() {
+ observer_.Add(ui::InputDeviceManager::GetInstance());
+}
+
+void KeyboardHandler::OnJavascriptDisallowed() {
+ observer_.RemoveAll();
+}
+
+void KeyboardHandler::OnKeyboardDeviceConfigurationChanged() {
+ UpdateShowKeys();
+}
+
+void KeyboardHandler::HandleInitialize(const base::ListValue* args) {
+ AllowJavascript();
+ UpdateShowKeys();
+}
+
+void KeyboardHandler::HandleShowKeyboardShortcutsOverlay(
+ const base::ListValue* args) const {
+ ash::Shell::Get()->new_window_controller()->ShowKeyboardOverlay();
+}
+
+void KeyboardHandler::UpdateShowKeys() {
+ const base::Value has_caps_lock(HasExternalKeyboard());
+ const base::Value has_diamond_key(
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kHasChromeOSDiamondKey));
+ FireWebUIListener("show-keys-changed", has_caps_lock, has_diamond_key);
+}
+
+} // namespace settings
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h
new file mode 100644
index 00000000000..7ac9d447d48
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h
@@ -0,0 +1,67 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_KEYBOARD_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_KEYBOARD_HANDLER_H_
+
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "ui/events/devices/input_device_event_observer.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace content {
+class WebUI;
+}
+
+namespace ui {
+class InputDeviceManager;
+}
+
+class Profile;
+
+namespace chromeos {
+namespace settings {
+
+// Chrome OS "Keyboard" settings page UI handler.
+class KeyboardHandler
+ : public ::settings::SettingsPageUIHandler,
+ public ui::InputDeviceEventObserver {
+ public:
+ explicit KeyboardHandler(content::WebUI* webui);
+ ~KeyboardHandler() override;
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ // ui::InputDeviceEventObserver implementation.
+ void OnKeyboardDeviceConfigurationChanged() override;
+
+ private:
+ // Initializes the page with the current keyboard information.
+ void HandleInitialize(const base::ListValue* args);
+
+ // Shows the Ash keyboard shortcuts overlay.
+ void HandleShowKeyboardShortcutsOverlay(const base::ListValue* args) const;
+
+ // Shows or hides the Caps Lock and Diamond key settings based on whether the
+ // system status.
+ void UpdateShowKeys();
+
+ Profile* profile_; // Weak pointer.
+
+ ScopedObserver<ui::InputDeviceManager, KeyboardHandler> observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(KeyboardHandler);
+};
+
+} // namespace settings
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_KEYBOARD_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.cc
new file mode 100644
index 00000000000..061dad6d182
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.cc
@@ -0,0 +1,51 @@
+// 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 "chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "content/public/browser/web_ui.h"
+
+namespace chromeos {
+namespace settings {
+
+PointerHandler::PointerHandler() {}
+
+PointerHandler::~PointerHandler() {}
+
+void PointerHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "initializePointerSettings",
+ base::Bind(&PointerHandler::HandleInitialize, base::Unretained(this)));
+}
+
+void PointerHandler::OnJavascriptAllowed() {
+ if (!pointer_device_observer_) {
+ pointer_device_observer_.reset(new system::PointerDeviceObserver());
+ pointer_device_observer_->Init();
+ }
+
+ pointer_device_observer_->AddObserver(this);
+ pointer_device_observer_->CheckDevices();
+}
+
+void PointerHandler::OnJavascriptDisallowed() {
+ pointer_device_observer_->RemoveObserver(this);
+}
+
+void PointerHandler::TouchpadExists(bool exists) {
+ FireWebUIListener("has-touchpad-changed", base::Value(exists));
+}
+
+void PointerHandler::MouseExists(bool exists) {
+ FireWebUIListener("has-mouse-changed", base::Value(exists));
+}
+
+void PointerHandler::HandleInitialize(const base::ListValue* args) {
+ AllowJavascript();
+}
+
+} // namespace settings
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.h
new file mode 100644
index 00000000000..25f54393c73
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.h
@@ -0,0 +1,51 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_POINTER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_POINTER_HANDLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/system/pointer_device_observer.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace chromeos {
+namespace settings {
+
+// Chrome OS "Mouse and touchpad" settings page UI handler.
+class PointerHandler
+ : public ::settings::SettingsPageUIHandler,
+ public chromeos::system::PointerDeviceObserver::Observer {
+ public:
+ PointerHandler();
+ ~PointerHandler() override;
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ private:
+ // PointerDeviceObserver implementation.
+ void TouchpadExists(bool exists) override;
+ void MouseExists(bool exists) override;
+
+ // Initializes the page with the current pointer information.
+ void HandleInitialize(const base::ListValue* args);
+
+ std::unique_ptr<chromeos::system::PointerDeviceObserver>
+ pointer_device_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(PointerHandler);
+};
+
+} // namespace settings
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_POINTER_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler.cc
new file mode 100644
index 00000000000..535e9418eeb
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler.cc
@@ -0,0 +1,138 @@
+// 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/settings/chromeos/device_power_handler.h"
+
+#include <memory>
+#include <utility>
+
+#include "ash/resources/grit/ash_resources.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/time_format.h"
+#include "ui/base/webui/web_ui_util.h"
+
+namespace chromeos {
+namespace settings {
+namespace {
+
+base::string16 GetBatteryTimeText(base::TimeDelta time_left) {
+ int hour = 0;
+ int min = 0;
+ ash::PowerStatus::SplitTimeIntoHoursAndMinutes(time_left, &hour, &min);
+
+ base::string16 time_text;
+ if (hour == 0 || min == 0) {
+ // Display only one unit ("2 hours" or "10 minutes").
+ return ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
+ ui::TimeFormat::LENGTH_LONG, time_left);
+ }
+
+ return ui::TimeFormat::Detailed(ui::TimeFormat::FORMAT_DURATION,
+ ui::TimeFormat::LENGTH_LONG,
+ -1, // force hour and minute output
+ time_left);
+}
+
+} // namespace
+
+PowerHandler::PowerHandler()
+ : power_observer_(this) {
+ power_status_ = ash::PowerStatus::Get();
+}
+
+PowerHandler::~PowerHandler() {}
+
+void PowerHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "updatePowerStatus", base::Bind(&PowerHandler::HandleUpdatePowerStatus,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setPowerSource",
+ base::Bind(&PowerHandler::HandleSetPowerSource, base::Unretained(this)));
+}
+
+void PowerHandler::OnJavascriptAllowed() {
+ power_observer_.Add(power_status_);
+}
+
+void PowerHandler::OnJavascriptDisallowed() {
+ power_observer_.RemoveAll();
+}
+
+void PowerHandler::OnPowerStatusChanged() {
+ SendBatteryStatus();
+ SendPowerSources();
+}
+
+void PowerHandler::HandleUpdatePowerStatus(const base::ListValue* args) {
+ AllowJavascript();
+ power_status_->RequestStatusUpdate();
+}
+
+void PowerHandler::HandleSetPowerSource(const base::ListValue* args) {
+ AllowJavascript();
+
+ std::string id;
+ CHECK(args->GetString(0, &id));
+ power_status_->SetPowerSource(id);
+}
+
+void PowerHandler::SendBatteryStatus() {
+ bool charging = power_status_->IsBatteryCharging();
+ bool calculating = power_status_->IsBatteryTimeBeingCalculated();
+ int percent = power_status_->GetRoundedBatteryPercent();
+ base::TimeDelta time_left;
+ bool show_time = false;
+
+ if (!calculating) {
+ time_left = charging ? power_status_->GetBatteryTimeToFull()
+ : power_status_->GetBatteryTimeToEmpty();
+ show_time = ash::PowerStatus::ShouldDisplayBatteryTime(time_left);
+ }
+
+ base::string16 status_text;
+ if (show_time) {
+ status_text = l10n_util::GetStringFUTF16(
+ charging ? IDS_OPTIONS_BATTERY_STATUS_CHARGING
+ : IDS_OPTIONS_BATTERY_STATUS,
+ base::IntToString16(percent), GetBatteryTimeText(time_left));
+ } else {
+ status_text = l10n_util::GetStringFUTF16(IDS_OPTIONS_BATTERY_STATUS_SHORT,
+ base::IntToString16(percent));
+ }
+
+ base::DictionaryValue battery_dict;
+ battery_dict.SetBoolean("charging", charging);
+ battery_dict.SetBoolean("calculating", calculating);
+ battery_dict.SetInteger("percent", percent);
+ battery_dict.SetString("statusText", status_text);
+
+ FireWebUIListener("battery-status-changed", battery_dict);
+}
+
+void PowerHandler::SendPowerSources() {
+ base::ListValue sources_list;
+ for (const auto& source : power_status_->GetPowerSources()) {
+ std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+ dict->SetString("id", source.id);
+ dict->SetInteger("type", source.type);
+ dict->SetString("description",
+ l10n_util::GetStringUTF16(source.description_id));
+ sources_list.Append(std::move(dict));
+ }
+
+ FireWebUIListener("power-sources-changed", sources_list,
+ base::Value(power_status_->GetCurrentPowerSourceID()),
+ base::Value(power_status_->IsUsbChargerConnected()));
+}
+
+} // namespace settings
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler.h
new file mode 100644
index 00000000000..eaaf0cc9a58
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler.h
@@ -0,0 +1,59 @@
+// 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_SETTINGS_CHROMEOS_DEVICE_POWER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_POWER_HANDLER_H_
+
+#include "ash/system/power/power_status.h"
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "base/strings/string16.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace chromeos {
+namespace settings {
+
+// Chrome OS battery status and power settings handler.
+class PowerHandler : public ::settings::SettingsPageUIHandler,
+ public ash::PowerStatus::Observer {
+ public:
+ PowerHandler();
+ ~PowerHandler() override;
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ // ash::PowerStatus::Observer implementation.
+ void OnPowerStatusChanged() override;
+
+ private:
+ // Handler to request updating the power status.
+ void HandleUpdatePowerStatus(const base::ListValue* args);
+
+ // Handler to change the power source.
+ void HandleSetPowerSource(const base::ListValue* args);
+
+ // Updates the UI with the current battery status.
+ void SendBatteryStatus();
+
+ // Updates the UI with a list of available dual-role power sources.
+ void SendPowerSources();
+
+ ash::PowerStatus* power_status_;
+
+ ScopedObserver<ash::PowerStatus, PowerHandler> power_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(PowerHandler);
+};
+
+} // namespace settings
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_POWER_HANDLER_H_
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
new file mode 100644
index 00000000000..5b59c57ccdb
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc
@@ -0,0 +1,370 @@
+// 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 "chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h"
+
+#include <algorithm>
+#include <numeric>
+#include <string>
+
+#include "base/files/file_util.h"
+#include "base/sys_info.h"
+#include "base/task_scheduler/post_task.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browsing_data/browsing_data_appcache_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_cache_storage_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_channel_id_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_cookie_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_database_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_file_system_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_flash_lso_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_service_worker_helper.h"
+#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/drive/file_system_util.h"
+#include "chrome/browser/chromeos/file_manager/path_util.h"
+#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 "components/arc/arc_util.h"
+#include "components/browsing_data/content/conditional_cache_counting_helper.h"
+#include "components/drive/chromeos/file_system_interface.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/text/bytes_formatting.h"
+
+namespace chromeos {
+namespace settings {
+namespace {
+
+void GetSizeStatBlocking(const base::FilePath& mount_path,
+ int64_t* total_size,
+ int64_t* available_size) {
+ int64_t size = base::SysInfo::AmountOfTotalDiskSpace(mount_path);
+ if (size >= 0)
+ *total_size = size;
+ size = base::SysInfo::AmountOfFreeDiskSpace(mount_path);
+ if (size >= 0)
+ *available_size = size;
+}
+
+// Threshold to show a message indicating space is critically low (512 MB).
+const int64_t kSpaceCriticallyLowBytes = 512 * 1024 * 1024;
+
+// Threshold to show a message indicating space is low (1 GB).
+const int64_t kSpaceLowBytes = 1 * 1024 * 1024 * 1024;
+
+} // namespace
+
+StorageHandler::StorageHandler()
+ : browser_cache_size_(-1),
+ has_browser_cache_size_(false),
+ browser_site_data_size_(-1),
+ has_browser_site_data_size_(false),
+ updating_downloads_size_(false),
+ updating_drive_cache_size_(false),
+ updating_browsing_data_size_(false),
+ updating_android_size_(false),
+ updating_other_users_size_(false),
+ weak_ptr_factory_(this) {}
+
+StorageHandler::~StorageHandler() {
+}
+
+void StorageHandler::RegisterMessages() {
+ DCHECK(web_ui());
+
+ web_ui()->RegisterMessageCallback(
+ "updateStorageInfo",
+ base::Bind(&StorageHandler::HandleUpdateStorageInfo,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "openDownloads",
+ base::Bind(&StorageHandler::HandleOpenDownloads,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "openArcStorage",
+ base::Bind(&StorageHandler::HandleOpenArcStorage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "clearDriveCache",
+ base::Bind(&StorageHandler::HandleClearDriveCache,
+ base::Unretained(this)));
+}
+
+void StorageHandler::HandleUpdateStorageInfo(const base::ListValue* args) {
+ AllowJavascript();
+
+ UpdateSizeStat();
+ UpdateDownloadsSize();
+ UpdateDriveCacheSize();
+ UpdateBrowsingDataSize();
+ UpdateOtherUsersSize();
+ UpdateAndroidSize();
+}
+
+void StorageHandler::HandleOpenDownloads(
+ const base::ListValue* unused_args) {
+ Profile* const profile = Profile::FromWebUI(web_ui());
+ const base::FilePath downloads_path =
+ file_manager::util::GetDownloadsFolderForProfile(profile);
+ platform_util::OpenItem(
+ profile,
+ downloads_path,
+ platform_util::OPEN_FOLDER,
+ platform_util::OpenOperationCallback());
+}
+
+void StorageHandler::HandleOpenArcStorage(
+ const base::ListValue* unused_args) {
+ arc::ArcStorageManager::Get()->OpenPrivateVolumeSettings();
+}
+
+void StorageHandler::HandleClearDriveCache(
+ const base::ListValue* unused_args) {
+ drive::FileSystemInterface* const file_system =
+ drive::util::GetFileSystemByProfile(Profile::FromWebUI(web_ui()));
+ file_system->FreeDiskSpaceIfNeededFor(
+ std::numeric_limits<int64_t>::max(), // Removes as much as possible.
+ base::Bind(&StorageHandler::OnClearDriveCacheDone,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void StorageHandler::UpdateSizeStat() {
+ Profile* const profile = Profile::FromWebUI(web_ui());
+ const base::FilePath downloads_path =
+ file_manager::util::GetDownloadsFolderForProfile(profile);
+
+ int64_t* total_size = new int64_t(0);
+ int64_t* available_size = new int64_t(0);
+ base::PostTaskWithTraitsAndReply(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+ base::Bind(&GetSizeStatBlocking, downloads_path, total_size,
+ available_size),
+ base::Bind(&StorageHandler::OnGetSizeStat, weak_ptr_factory_.GetWeakPtr(),
+ base::Owned(total_size), base::Owned(available_size)));
+}
+
+void StorageHandler::OnGetSizeStat(int64_t* total_size,
+ int64_t* available_size) {
+ int64_t used_size = *total_size - *available_size;
+ base::DictionaryValue size_stat;
+ size_stat.SetString("totalSize", ui::FormatBytes(*total_size));
+ size_stat.SetString("availableSize", ui::FormatBytes(*available_size));
+ size_stat.SetString("usedSize", ui::FormatBytes(used_size));
+ size_stat.SetDouble("usedRatio",
+ static_cast<double>(used_size) / *total_size);
+ int storage_space_state = STORAGE_SPACE_NORMAL;
+ if (*available_size < kSpaceCriticallyLowBytes)
+ storage_space_state = STORAGE_SPACE_CRITICALLY_LOW;
+ else if (*available_size < kSpaceLowBytes)
+ storage_space_state = STORAGE_SPACE_LOW;
+ size_stat.SetInteger("spaceState", storage_space_state);
+
+ FireWebUIListener("storage-size-stat-changed", size_stat);
+}
+
+void StorageHandler::UpdateDownloadsSize() {
+ if (updating_downloads_size_)
+ return;
+ updating_downloads_size_ = true;
+
+ Profile* const profile = Profile::FromWebUI(web_ui());
+ const base::FilePath downloads_path =
+ file_manager::util::GetDownloadsFolderForProfile(profile);
+
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&base::ComputeDirectorySize, downloads_path),
+ base::Bind(&StorageHandler::OnGetDownloadsSize,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void StorageHandler::OnGetDownloadsSize(int64_t size) {
+ updating_downloads_size_ = false;
+ FireWebUIListener("storage-downloads-size-changed",
+ base::Value(ui::FormatBytes(size)));
+}
+
+void StorageHandler::UpdateDriveCacheSize() {
+ if (updating_drive_cache_size_)
+ return;
+
+ drive::FileSystemInterface* const file_system =
+ drive::util::GetFileSystemByProfile(Profile::FromWebUI(web_ui()));
+ if (!file_system)
+ return;
+
+ // Shows the item "Offline cache" and start calculating size.
+ FireWebUIListener("storage-drive-enabled-changed", base::Value(true));
+ updating_drive_cache_size_ = true;
+ file_system->CalculateCacheSize(base::Bind(
+ &StorageHandler::OnGetDriveCacheSize, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void StorageHandler::OnGetDriveCacheSize(int64_t size) {
+ updating_drive_cache_size_ = false;
+ FireWebUIListener("storage-drive-cache-size-changed",
+ base::Value(ui::FormatBytes(size)), base::Value(size > 0));
+}
+
+void StorageHandler::UpdateBrowsingDataSize() {
+ if (updating_browsing_data_size_)
+ return;
+ updating_browsing_data_size_ = true;
+
+ has_browser_cache_size_ = false;
+ has_browser_site_data_size_ = false;
+ Profile* const profile = Profile::FromWebUI(web_ui());
+ // Fetch the size of http cache in browsing data.
+ // ConditionalCacheCountingHelper deletes itself when it is done.
+ browsing_data::ConditionalCacheCountingHelper::CreateForRange(
+ content::BrowserContext::GetDefaultStoragePartition(profile),
+ base::Time(), base::Time::Max())
+ ->CountAndDestroySelfWhenFinished(base::Bind(
+ &StorageHandler::OnGetCacheSize, weak_ptr_factory_.GetWeakPtr()));
+
+ // Fetch the size of site data in browsing data.
+ if (!site_data_size_collector_.get()) {
+ content::StoragePartition* storage_partition =
+ content::BrowserContext::GetDefaultStoragePartition(profile);
+ site_data_size_collector_.reset(new SiteDataSizeCollector(
+ storage_partition->GetPath(),
+ new BrowsingDataCookieHelper(profile->GetRequestContext()),
+ new BrowsingDataDatabaseHelper(profile),
+ new BrowsingDataLocalStorageHelper(profile),
+ new BrowsingDataAppCacheHelper(profile),
+ new BrowsingDataIndexedDBHelper(
+ storage_partition->GetIndexedDBContext()),
+ BrowsingDataFileSystemHelper::Create(
+ storage_partition->GetFileSystemContext()),
+ BrowsingDataChannelIDHelper::Create(profile->GetRequestContext()),
+ new BrowsingDataServiceWorkerHelper(
+ storage_partition->GetServiceWorkerContext()),
+ new BrowsingDataCacheStorageHelper(
+ storage_partition->GetCacheStorageContext()),
+ BrowsingDataFlashLSOHelper::Create(profile)));
+ }
+ site_data_size_collector_->Fetch(
+ base::Bind(&StorageHandler::OnGetBrowsingDataSize,
+ weak_ptr_factory_.GetWeakPtr(), true));
+}
+
+void StorageHandler::OnGetCacheSize(bool is_upper_limit, int64_t size) {
+ DCHECK(!is_upper_limit);
+ OnGetBrowsingDataSize(false, size);
+}
+
+void StorageHandler::OnGetBrowsingDataSize(bool is_site_data, int64_t size) {
+ if (is_site_data) {
+ has_browser_site_data_size_ = true;
+ browser_site_data_size_ = size;
+ } else {
+ has_browser_cache_size_ = true;
+ browser_cache_size_ = size;
+ }
+ if (has_browser_cache_size_ && has_browser_site_data_size_) {
+ base::string16 size_string;
+ if (browser_cache_size_ >= 0 && browser_site_data_size_ >= 0) {
+ size_string = ui::FormatBytes(
+ browser_site_data_size_ + browser_cache_size_);
+ } else {
+ size_string = l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SIZE_UNKNOWN);
+ }
+ updating_browsing_data_size_ = false;
+ FireWebUIListener("storage-browsing-data-size-changed",
+ base::Value(size_string));
+ }
+}
+
+void StorageHandler::UpdateOtherUsersSize() {
+ if (updating_other_users_size_)
+ return;
+ updating_other_users_size_ = true;
+
+ other_users_.clear();
+ user_sizes_.clear();
+ const user_manager::UserList& users =
+ user_manager::UserManager::Get()->GetUsers();
+ for (auto* user : users) {
+ if (user->is_active())
+ continue;
+ other_users_.push_back(user);
+ cryptohome::HomedirMethods::GetInstance()->GetAccountDiskUsage(
+ cryptohome::Identification(user->GetAccountId()),
+ base::Bind(&StorageHandler::OnGetOtherUserSize,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+ // We should show "0 B" if there is no other user.
+ if (other_users_.empty()) {
+ updating_other_users_size_ = false;
+ FireWebUIListener("storage-other-users-size-changed",
+ base::Value(ui::FormatBytes(0)));
+ }
+}
+
+void StorageHandler::OnGetOtherUserSize(bool success, int64_t size) {
+ user_sizes_.push_back(success ? size : -1);
+ if (user_sizes_.size() == other_users_.size()) {
+ base::string16 size_string;
+ // If all the requests succeed, shows the total bytes in the UI.
+ if (std::count(user_sizes_.begin(), user_sizes_.end(), -1) == 0) {
+ size_string = ui::FormatBytes(
+ std::accumulate(user_sizes_.begin(), user_sizes_.end(), 0LL));
+ } else {
+ size_string = l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SIZE_UNKNOWN);
+ }
+ updating_other_users_size_ = false;
+ FireWebUIListener("storage-other-users-size-changed",
+ base::Value(size_string));
+ }
+}
+
+void StorageHandler::UpdateAndroidSize() {
+ if (updating_android_size_)
+ return;
+ updating_android_size_ = true;
+
+ Profile* const profile = Profile::FromWebUI(web_ui());
+ if (!arc::IsArcPlayStoreEnabledForProfile(profile) ||
+ arc::IsArcOptInVerificationDisabled()) {
+ return;
+ }
+
+ // Shows the item "Android apps and cache" and start calculating size.
+ FireWebUIListener("storage-android-enabled-changed", base::Value(true));
+ bool success = arc::ArcStorageManager::Get()->GetApplicationsSize(base::Bind(
+ &StorageHandler::OnGetAndroidSize, weak_ptr_factory_.GetWeakPtr()));
+ if (!success)
+ updating_android_size_ = false;
+}
+
+void StorageHandler::OnGetAndroidSize(bool succeeded,
+ arc::mojom::ApplicationsSizePtr size) {
+ base::string16 size_string;
+ if (succeeded) {
+ uint64_t total_bytes = size->total_code_bytes +
+ size->total_data_bytes +
+ size->total_cache_bytes;
+ size_string = ui::FormatBytes(total_bytes);
+ } else {
+ size_string = l10n_util::GetStringUTF16(
+ IDS_OPTIONS_SETTINGS_STORAGE_SIZE_UNKNOWN);
+ }
+ updating_android_size_ = false;
+ FireWebUIListener("storage-android-size-changed", base::Value(size_string));
+}
+
+void StorageHandler::OnClearDriveCacheDone(bool success) {
+ UpdateDriveCacheSize();
+}
+
+} // namespace settings
+} // namespace chromeos
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
new file mode 100644
index 00000000000..9da1d5d437b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h
@@ -0,0 +1,127 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_STORAGE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_STORAGE_HANDLER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/browsing_data/site_data_size_collector.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "components/arc/storage_manager/arc_storage_manager.h"
+#include "components/user_manager/user.h"
+
+namespace chromeos {
+namespace settings {
+
+class StorageHandler : public ::settings::SettingsPageUIHandler {
+ public:
+ // Enumeration for device state about remaining space. These values must be
+ // kept in sync with settings.StorageSpaceState in JS code.
+ enum StorageSpaceState {
+ STORAGE_SPACE_NORMAL = 0,
+ STORAGE_SPACE_LOW = 1,
+ STORAGE_SPACE_CRITICALLY_LOW = 2,
+ };
+
+ StorageHandler();
+ ~StorageHandler() override;
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override {}
+ void OnJavascriptDisallowed() override {}
+
+ private:
+ // Handlers of JS messages.
+ void HandleUpdateStorageInfo(const base::ListValue* unused_args);
+ void HandleOpenDownloads(const base::ListValue* unused_args);
+ void HandleOpenArcStorage(const base::ListValue* unused_args);
+ void HandleClearDriveCache(const base::ListValue* unused_args);
+
+ // Requests updating disk space information.
+ void UpdateSizeStat();
+
+ // Callback to update the UI about disk space information.
+ void OnGetSizeStat(int64_t* total_size, int64_t* available_size);
+
+ // Requests updating the size of Downloads directory.
+ void UpdateDownloadsSize();
+
+ // Callback to update the UI about the size of Downloads directory.
+ void OnGetDownloadsSize(int64_t size);
+
+ // Requests updating the size of Drive Cache.
+ void UpdateDriveCacheSize();
+
+ // Callback to update the UI about the size of Drive Cache.
+ void OnGetDriveCacheSize(int64_t size);
+
+ // Requests updating the size of browsing data.
+ void UpdateBrowsingDataSize();
+
+ // Callback to receive the cache size.
+ void OnGetCacheSize(bool is_upper_limit, int64_t size);
+
+ // Callback to update the UI about the size of browsing data.
+ void OnGetBrowsingDataSize(bool is_site_data, int64_t size);
+
+ // Requests updating the total size of other users' data.
+ void UpdateOtherUsersSize();
+
+ // Callback to save the fetched user sizes and update the UI.
+ void OnGetOtherUserSize(bool success, int64_t size);
+
+ // Requests updating the space size used by Android apps and cache.
+ void UpdateAndroidSize();
+
+ // Callback to update the UI about Android apps and cache.
+ void OnGetAndroidSize(bool succeeded, arc::mojom::ApplicationsSizePtr size);
+
+ // Callback called when clearing Drive cache is done.
+ void OnClearDriveCacheDone(bool success);
+
+ // Total size of cache data in browsing data.
+ int64_t browser_cache_size_;
+
+ // True if we have already received the size of http cache.
+ bool has_browser_cache_size_;
+
+ // Total size of site data in browsing data.
+ int64_t browser_site_data_size_;
+
+ // True if we have already received the size of site data.
+ bool has_browser_site_data_size_;
+
+ // The list of other users whose directory sizes will be accumulated as the
+ // size of "Other users".
+ user_manager::UserList other_users_;
+
+ // Fetched sizes of user directories.
+ std::vector<int64_t> user_sizes_;
+
+ // Helper to compute the total size of all types of site date.
+ std::unique_ptr<SiteDataSizeCollector> site_data_size_collector_;
+
+ // Flags indicating fetch operations for storage sizes are ongoing.
+ bool updating_downloads_size_;
+ bool updating_drive_cache_size_;
+ bool updating_browsing_data_size_;
+ bool updating_android_size_;
+ bool updating_other_users_size_;
+
+ base::WeakPtrFactory<StorageHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(StorageHandler);
+};
+
+} // namespace settings
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_STORAGE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.cc
new file mode 100644
index 00000000000..7f7ba9c3b47
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.cc
@@ -0,0 +1,143 @@
+// 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/settings/chromeos/device_stylus_handler.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "ash/system/palette/palette_utils.h"
+#include "base/bind.h"
+#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
+#include "ui/events/devices/input_device_manager.h"
+
+namespace chromeos {
+namespace settings {
+
+namespace {
+
+// Keys in objects passed to onNoteTakingAppsUpdated.
+constexpr char kAppNameKey[] = "name";
+constexpr char kAppIdKey[] = "value";
+constexpr char kAppPreferredKey[] = "preferred";
+constexpr char kAppLockScreenSupportKey[] = "supportsLockScreen";
+
+} // namespace
+
+StylusHandler::StylusHandler() {
+ NoteTakingHelper::Get()->AddObserver(this);
+ ui::InputDeviceManager::GetInstance()->AddObserver(this);
+}
+
+StylusHandler::~StylusHandler() {
+ ui::InputDeviceManager::GetInstance()->RemoveObserver(this);
+ NoteTakingHelper::Get()->RemoveObserver(this);
+}
+
+void StylusHandler::RegisterMessages() {
+ DCHECK(web_ui());
+
+ web_ui()->RegisterMessageCallback(
+ "initializeStylusSettings",
+ base::Bind(&StylusHandler::HandleInitialize, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "requestNoteTakingApps",
+ base::Bind(&StylusHandler::RequestApps, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setPreferredNoteTakingApp",
+ base::Bind(&StylusHandler::SetPreferredNoteTakingApp,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "showPlayStoreApps",
+ base::Bind(&StylusHandler::ShowPlayStoreApps, base::Unretained(this)));
+}
+
+void StylusHandler::OnAvailableNoteTakingAppsUpdated() {
+ UpdateNoteTakingApps();
+}
+
+void StylusHandler::OnDeviceListsComplete() {
+ SendHasStylus();
+}
+
+void StylusHandler::UpdateNoteTakingApps() {
+ bool waiting_for_android = false;
+ note_taking_app_ids_.clear();
+ base::ListValue apps_list;
+
+ NoteTakingHelper* helper = NoteTakingHelper::Get();
+ if (helper->play_store_enabled() && !helper->android_apps_received()) {
+ // If Play Store is enabled but not ready yet, let the JS know so it can
+ // disable the menu and display an explanatory message.
+ waiting_for_android = true;
+ } else {
+ std::vector<NoteTakingAppInfo> available_apps =
+ helper->GetAvailableApps(Profile::FromWebUI(web_ui()));
+ for (const NoteTakingAppInfo& info : available_apps) {
+ auto dict = base::MakeUnique<base::DictionaryValue>();
+ dict->SetString(kAppNameKey, info.name);
+ dict->SetString(kAppIdKey, info.app_id);
+ dict->SetBoolean(kAppPreferredKey, info.preferred);
+ dict->SetBoolean(kAppLockScreenSupportKey,
+ info.lock_screen_support !=
+ NoteTakingLockScreenSupport::kNotSupported);
+ apps_list.Append(std::move(dict));
+
+ note_taking_app_ids_.insert(info.app_id);
+ }
+ }
+
+ AllowJavascript();
+ FireWebUIListener("onNoteTakingAppsUpdated", apps_list,
+ base::Value(waiting_for_android));
+}
+
+void StylusHandler::RequestApps(const base::ListValue* unused_args) {
+ UpdateNoteTakingApps();
+}
+
+void StylusHandler::SetPreferredNoteTakingApp(const base::ListValue* args) {
+ std::string app_id;
+ CHECK(args->GetString(0, &app_id));
+
+ // Sanity check: make sure that the ID we got back from WebUI is in the
+ // currently-available set.
+ if (!note_taking_app_ids_.count(app_id)) {
+ LOG(ERROR) << "Got unknown note-taking-app ID \"" << app_id << "\"";
+ return;
+ }
+
+ NoteTakingHelper::Get()->SetPreferredApp(Profile::FromWebUI(web_ui()),
+ app_id);
+}
+
+void StylusHandler::HandleInitialize(const base::ListValue* args) {
+ if (ui::InputDeviceManager::GetInstance()->AreDeviceListsComplete())
+ SendHasStylus();
+}
+
+void StylusHandler::SendHasStylus() {
+ DCHECK(ui::InputDeviceManager::GetInstance()->AreDeviceListsComplete());
+ AllowJavascript();
+ FireWebUIListener("has-stylus-changed",
+ base::Value(ash::palette_utils::HasStylusInput()));
+}
+
+void StylusHandler::ShowPlayStoreApps(const base::ListValue* args) {
+ std::string apps_url;
+ args->GetString(0, &apps_url);
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (!arc::IsArcAllowedForProfile(profile)) {
+ VLOG(1) << "ARC is not enabled for this profile";
+ return;
+ }
+
+ arc::LaunchPlayStoreWithUrl(apps_url);
+}
+
+} // namespace settings
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.h
new file mode 100644
index 00000000000..b8cd2567f99
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.h
@@ -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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_STYLUS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_STYLUS_HANDLER_H_
+
+#include <set>
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/note_taking_helper.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "ui/events/devices/input_device_event_observer.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace chromeos {
+namespace settings {
+
+// Chrome OS stylus settings handler.
+class StylusHandler : public ::settings::SettingsPageUIHandler,
+ public chromeos::NoteTakingHelper::Observer,
+ public ui::InputDeviceEventObserver {
+ public:
+ StylusHandler();
+ ~StylusHandler() override;
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override {}
+ void OnJavascriptDisallowed() override {}
+
+ // chromeos::NoteTakingHelper::Observer implementation.
+ void OnAvailableNoteTakingAppsUpdated() override;
+
+ // ui::InputDeviceObserver:
+ void OnDeviceListsComplete() override;
+
+ private:
+ void UpdateNoteTakingApps();
+ void RequestApps(const base::ListValue* unused_args);
+ void SetPreferredNoteTakingApp(const base::ListValue* args);
+
+ // Called by JS to request a |SendHasStylus| call.
+ void HandleInitialize(const base::ListValue* args);
+ // Enables or disables the stylus UI section.
+ void SendHasStylus();
+
+ // Called by JS to show the Play Store Android app.
+ void ShowPlayStoreApps(const base::ListValue* args);
+
+ // IDs of available note-taking apps.
+ std::set<std::string> note_taking_app_ids_;
+
+ DISALLOW_COPY_AND_ASSIGN(StylusHandler);
+};
+
+} // namespace settings
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_STYLUS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/easy_unlock_settings_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/easy_unlock_settings_handler.cc
new file mode 100644
index 00000000000..299d20bd1b7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/easy_unlock_settings_handler.cc
@@ -0,0 +1,160 @@
+// 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 "chrome/browser/ui/webui/settings/chromeos/easy_unlock_settings_handler.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/easy_unlock_service.h"
+#include "chrome/common/pref_names.h"
+#include "components/proximity_auth/switches.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace chromeos {
+namespace settings {
+
+EasyUnlockSettingsHandler::EasyUnlockSettingsHandler(Profile* profile)
+ : profile_(profile) {
+ profile_pref_registrar_.Init(profile->GetPrefs());
+}
+
+EasyUnlockSettingsHandler::~EasyUnlockSettingsHandler() {
+ EasyUnlockService::Get(profile_)->RemoveObserver(this);
+}
+
+EasyUnlockSettingsHandler* EasyUnlockSettingsHandler::Create(
+ content::WebUIDataSource* html_source,
+ Profile* profile) {
+ EasyUnlockService* easy_unlock_service = EasyUnlockService::Get(profile);
+ bool allowed = easy_unlock_service->IsAllowed();
+ html_source->AddBoolean("easyUnlockAllowed", allowed);
+ html_source->AddBoolean("easyUnlockEnabled",
+ allowed ? easy_unlock_service->IsEnabled() : false);
+ if (!allowed)
+ return nullptr;
+
+ html_source->AddBoolean(
+ "easyUnlockProximityDetectionAllowed",
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ proximity_auth::switches::kEnableProximityDetection));
+
+ return new EasyUnlockSettingsHandler(profile);
+}
+
+void EasyUnlockSettingsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "easyUnlockGetEnabledStatus",
+ base::Bind(&EasyUnlockSettingsHandler::HandleGetEnabledStatus,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "easyUnlockStartTurnOnFlow",
+ base::Bind(&EasyUnlockSettingsHandler::HandleStartTurnOnFlow,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "easyUnlockGetTurnOffFlowStatus",
+ base::Bind(&EasyUnlockSettingsHandler::HandleGetTurnOffFlowStatus,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "easyUnlockStartTurnOffFlow",
+ base::Bind(&EasyUnlockSettingsHandler::HandleStartTurnOffFlow,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "easyUnlockCancelTurnOffFlow",
+ base::Bind(&EasyUnlockSettingsHandler::HandleCancelTurnOffFlow,
+ base::Unretained(this)));
+}
+
+void EasyUnlockSettingsHandler::OnJavascriptAllowed() {
+ EasyUnlockService::Get(profile_)->AddObserver(this);
+
+ profile_pref_registrar_.Add(
+ prefs::kEasyUnlockPairing,
+ base::Bind(&EasyUnlockSettingsHandler::SendEnabledStatus,
+ base::Unretained(this)));
+}
+
+void EasyUnlockSettingsHandler::OnJavascriptDisallowed() {
+ EasyUnlockService::Get(profile_)->RemoveObserver(this);
+ profile_pref_registrar_.RemoveAll();
+}
+
+void EasyUnlockSettingsHandler::OnTurnOffOperationStatusChanged() {
+ FireWebUIListener("easy-unlock-turn-off-flow-status",
+ base::Value(GetTurnOffFlowStatus()));
+}
+
+void EasyUnlockSettingsHandler::SendEnabledStatus() {
+ CallJavascriptFunction(
+ "cr.webUIListenerCallback", base::Value("easy-unlock-enabled-status"),
+ base::Value(EasyUnlockService::Get(profile_)->IsEnabled()));
+}
+
+std::string EasyUnlockSettingsHandler::GetTurnOffFlowStatus() {
+ EasyUnlockService::TurnOffFlowStatus status =
+ EasyUnlockService::Get(profile_)->GetTurnOffFlowStatus();
+
+ // Translate status into JS UI state string. Note the translated string
+ // should match UIState defined in easy_unlock_turn_off_dialog.js.
+ std::string status_string;
+ switch (status) {
+ case EasyUnlockService::IDLE:
+ status_string = "idle";
+ break;
+ case EasyUnlockService::PENDING:
+ status_string = "pending";
+ break;
+ case EasyUnlockService::FAIL:
+ status_string = "server-error";
+ break;
+ default:
+ LOG(ERROR) << "Unknown Easy unlock turn-off operation status: " << status;
+ status_string = "idle";
+ break;
+ }
+
+ return status_string;
+}
+
+void EasyUnlockSettingsHandler::HandleGetEnabledStatus(
+ 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(EasyUnlockService::Get(profile_)->IsEnabled()));
+}
+
+void EasyUnlockSettingsHandler::HandleStartTurnOnFlow(
+ const base::ListValue* args) {
+ EasyUnlockService::Get(profile_)->LaunchSetup();
+}
+
+void EasyUnlockSettingsHandler::HandleGetTurnOffFlowStatus(
+ const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+ ResolveJavascriptCallback(*callback_id, base::Value(GetTurnOffFlowStatus()));
+}
+
+void EasyUnlockSettingsHandler::HandleStartTurnOffFlow(
+ const base::ListValue* args) {
+ EasyUnlockService::Get(profile_)->RunTurnOffFlow();
+}
+
+void EasyUnlockSettingsHandler::HandleCancelTurnOffFlow(
+ const base::ListValue* args) {
+ EasyUnlockService::Get(profile_)->ResetTurnOffFlow();
+}
+
+} // namespace settings
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/easy_unlock_settings_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/easy_unlock_settings_handler.h
new file mode 100644
index 00000000000..3fb3706c524
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/easy_unlock_settings_handler.h
@@ -0,0 +1,67 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_EASY_UNLOCK_SETTINGS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_EASY_UNLOCK_SETTINGS_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/signin/easy_unlock_service_observer.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "components/prefs/pref_change_registrar.h"
+
+namespace content {
+class WebUIDataSource;
+}
+
+class Profile;
+
+namespace chromeos {
+namespace settings {
+
+class EasyUnlockSettingsHandler : public ::settings::SettingsPageUIHandler,
+ public EasyUnlockServiceObserver {
+ public:
+ // Returns nullptr if EasyUnlock is not allowed for this device.
+ static EasyUnlockSettingsHandler* Create(
+ content::WebUIDataSource* html_source,
+ Profile* profile);
+
+ ~EasyUnlockSettingsHandler() override;
+
+ // SettingsPageUIHandler:
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ // EasyUnlockServiceObserver:
+ void OnTurnOffOperationStatusChanged() override;
+
+ protected:
+ explicit EasyUnlockSettingsHandler(Profile* profile);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(EasyUnlockSettingsHandlerTest, EnabledStatus);
+ FRIEND_TEST_ALL_PREFIXES(EasyUnlockSettingsHandlerTest, TurnOffFlowStatus);
+
+ void SendEnabledStatus();
+ std::string GetTurnOffFlowStatus();
+
+ // JS callbacks.
+ void HandleGetEnabledStatus(const base::ListValue* args);
+ void HandleStartTurnOnFlow(const base::ListValue* args);
+ void HandleGetTurnOffFlowStatus(const base::ListValue* args);
+ void HandleStartTurnOffFlow(const base::ListValue* args);
+ void HandleCancelTurnOffFlow(const base::ListValue* args);
+
+ Profile* const profile_;
+
+ PrefChangeRegistrar profile_pref_registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(EasyUnlockSettingsHandler);
+};
+
+} // namespace settings
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_EASY_UNLOCK_SETTINGS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/easy_unlock_settings_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/easy_unlock_settings_handler_unittest.cc
new file mode 100644
index 00000000000..d56e735aa3e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/easy_unlock_settings_handler_unittest.cc
@@ -0,0 +1,264 @@
+// 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 "chrome/browser/ui/webui/settings/chromeos/easy_unlock_settings_handler.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/signin/easy_unlock_service.h"
+#include "chrome/browser/signin/easy_unlock_service_factory.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_web_ui.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+namespace settings {
+
+namespace {
+
+class FakeEasyUnlockService : public EasyUnlockService {
+ public:
+ explicit FakeEasyUnlockService(Profile* profile)
+ : EasyUnlockService(profile),
+ turn_off_status_(IDLE),
+ is_allowed_(true),
+ is_enabled_(false) {}
+
+ TurnOffFlowStatus GetTurnOffFlowStatus() const override {
+ return turn_off_status_;
+ }
+
+ bool IsAllowed() const override { return is_allowed_; }
+ void set_is_allowed(bool is_allowed) { is_allowed_ = is_allowed; }
+
+ bool IsEnabled() const override { return is_enabled_; }
+ void set_is_enabled(bool is_enabled) { is_enabled_ = is_enabled; }
+
+ void RunTurnOffFlow() override {
+ turn_off_status_ = PENDING;
+ NotifyTurnOffOperationStatusChanged();
+ }
+
+ void ResetTurnOffFlow() override {
+ turn_off_status_ = IDLE;
+ NotifyTurnOffOperationStatusChanged();
+ }
+
+ void SetTurnOffFlowFailForTest() {
+ turn_off_status_ = FAIL;
+ NotifyTurnOffOperationStatusChanged();
+ }
+
+ private:
+ Type GetType() const override { return TYPE_REGULAR; }
+ AccountId GetAccountId() const override { return EmptyAccountId(); }
+ void LaunchSetup() override {}
+ const base::DictionaryValue* GetPermitAccess() const override {
+ return nullptr;
+ }
+ void SetPermitAccess(const base::DictionaryValue& permit) override {}
+ void ClearPermitAccess() override {}
+
+ const base::ListValue* GetRemoteDevices() const override { return nullptr; }
+ void SetRemoteDevices(const base::ListValue& devices) override {}
+ void SetRemoteBleDevices(const base::ListValue& devices) override {}
+
+ std::string GetChallenge() const override { return std::string(); }
+ std::string GetWrappedSecret() const override { return std::string(); }
+ void RecordEasySignInOutcome(const AccountId& account_id,
+ bool success) const override {}
+ void RecordPasswordLoginEvent(const AccountId& account_id) const override {}
+ void StartAutoPairing(const AutoPairingResultCallback& callback) override {}
+ void SetAutoPairingResult(bool success, const std::string& error) override {}
+
+ void InitializeInternal() override {}
+ void ShutdownInternal() override {}
+ bool IsAllowedInternal() const override { return false; }
+ void OnWillFinalizeUnlock(bool success) override {}
+ void OnSuspendDoneInternal() override {}
+
+ TurnOffFlowStatus turn_off_status_;
+ bool is_allowed_;
+ bool is_enabled_;
+};
+
+class TestEasyUnlockSettingsHandler : public EasyUnlockSettingsHandler {
+ public:
+ explicit TestEasyUnlockSettingsHandler(Profile* profile)
+ : EasyUnlockSettingsHandler(profile) {}
+
+ using EasyUnlockSettingsHandler::set_web_ui;
+};
+
+std::unique_ptr<KeyedService> CreateEasyUnlockServiceForTest(
+ content::BrowserContext* context) {
+ return base::MakeUnique<FakeEasyUnlockService>(
+ Profile::FromBrowserContext(context));
+}
+
+} // namespace
+
+class EasyUnlockSettingsHandlerTest : public testing::Test {
+ public:
+ EasyUnlockSettingsHandlerTest() {}
+
+ void SetUp() override {
+ TestingProfile::Builder builder;
+ builder.AddTestingFactory(EasyUnlockServiceFactory::GetInstance(),
+ &CreateEasyUnlockServiceForTest);
+ profile_ = builder.Build();
+ }
+
+ Profile* profile() { return profile_.get(); }
+ content::TestWebUI* web_ui() { return &web_ui_; }
+ FakeEasyUnlockService* fake_easy_unlock_service() {
+ return static_cast<FakeEasyUnlockService*>(
+ EasyUnlockService::Get(profile_.get()));
+ }
+
+ void VerifyEnabledStatusCallback(size_t expected_total_calls,
+ bool expected_status) {
+ std::string event;
+ bool status;
+
+ EXPECT_EQ(expected_total_calls, web_ui_.call_data().size());
+
+ const content::TestWebUI::CallData& data = *web_ui_.call_data().back();
+ EXPECT_EQ("cr.webUIListenerCallback", data.function_name());
+ ASSERT_TRUE(data.arg1()->GetAsString(&event));
+ EXPECT_EQ("easy-unlock-enabled-status", event);
+ ASSERT_TRUE(data.arg2()->GetAsBoolean(&status));
+
+ EXPECT_EQ(expected_status, status);
+ }
+
+ void VerifyTurnOffFlowStatusWebUIListenerCallback(
+ size_t expected_total_calls,
+ const std::string& expected_status) {
+ std::string event;
+ std::string status;
+
+ EXPECT_EQ(expected_total_calls, web_ui_.call_data().size());
+
+ const content::TestWebUI::CallData& data = *web_ui_.call_data().back();
+ EXPECT_EQ("cr.webUIListenerCallback", data.function_name());
+ ASSERT_TRUE(data.arg1()->GetAsString(&event));
+ EXPECT_EQ("easy-unlock-turn-off-flow-status", event);
+ ASSERT_TRUE(data.arg2()->GetAsString(&status));
+
+ EXPECT_EQ(expected_status, status);
+ }
+
+ void VerifyTurnOffFlowStatusWebUIResponse(
+ size_t expected_total_calls,
+ const std::string& expected_callback_id,
+ const std::string& expected_status) {
+ EXPECT_EQ(expected_total_calls, web_ui()->call_data().size());
+
+ const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
+ EXPECT_EQ("cr.webUIResponse", data.function_name());
+
+ std::string callback_id;
+ ASSERT_TRUE(data.arg1()->GetAsString(&callback_id));
+ EXPECT_EQ(expected_callback_id, callback_id);
+
+ std::string actual_status;
+ ASSERT_TRUE(data.arg3()->GetAsString(&actual_status));
+ EXPECT_EQ(expected_status, actual_status);
+ }
+
+ private:
+ content::TestBrowserThreadBundle thread_bundle_;
+ std::unique_ptr<TestingProfile> profile_;
+ content::TestWebUI web_ui_;
+};
+
+TEST_F(EasyUnlockSettingsHandlerTest, OnlyCreatedWhenEasyUnlockAllowed) {
+ std::unique_ptr<EasyUnlockSettingsHandler> handler;
+ content::WebUIDataSource* data_source =
+ content::WebUIDataSource::Create("test-data-source");
+ content::WebUIDataSource::Add(profile(), data_source);
+ handler.reset(
+ EasyUnlockSettingsHandler::Create(data_source, profile()));
+ EXPECT_TRUE(handler.get());
+
+ fake_easy_unlock_service()->set_is_allowed(false);
+ handler.reset(EasyUnlockSettingsHandler::Create(data_source, profile()));
+ EXPECT_FALSE(handler.get());
+}
+
+TEST_F(EasyUnlockSettingsHandlerTest, EnabledStatus) {
+ std::unique_ptr<EasyUnlockSettingsHandler> handler;
+ handler.reset(new TestEasyUnlockSettingsHandler(profile()));
+ handler->set_web_ui(web_ui());
+
+ // Test the JS -> C++ -> JS callback path.
+ base::ListValue list_args;
+ list_args.AppendString("test-callback-id");
+ handler->HandleGetEnabledStatus(&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("test-callback-id", callback_id);
+
+ bool enabled_status = false;
+ ASSERT_TRUE(data.arg3()->GetAsBoolean(&enabled_status));
+ EXPECT_FALSE(enabled_status);
+
+ // Test the C++ -> JS push path.
+ handler->SendEnabledStatus();
+ VerifyEnabledStatusCallback(2U, false);
+
+ fake_easy_unlock_service()->set_is_enabled(true);
+ handler->SendEnabledStatus();
+ VerifyEnabledStatusCallback(3U, true);
+}
+
+TEST_F(EasyUnlockSettingsHandlerTest, TurnOffFlowStatus) {
+ std::unique_ptr<EasyUnlockSettingsHandler> handler;
+ handler.reset(new TestEasyUnlockSettingsHandler(profile()));
+ handler->set_web_ui(web_ui());
+
+ // Send an initial status query to turn on service observer.
+ base::ListValue list_args1;
+ list_args1.AppendString("test-callback-id-1");
+ handler->HandleGetEnabledStatus(&list_args1);
+ EXPECT_EQ(1U, web_ui()->call_data().size());
+
+ base::ListValue list_args2;
+ list_args2.AppendString("test-callback-id-2");
+ handler->HandleGetTurnOffFlowStatus(&list_args2);
+ VerifyTurnOffFlowStatusWebUIResponse(2U, "test-callback-id-2", "idle");
+
+ handler->HandleStartTurnOffFlow(nullptr);
+ VerifyTurnOffFlowStatusWebUIListenerCallback(3U, "pending");
+
+ base::ListValue list_args3;
+ list_args3.AppendString("test-callback-id-3");
+ handler->HandleGetTurnOffFlowStatus(&list_args3);
+ VerifyTurnOffFlowStatusWebUIResponse(4U, "test-callback-id-3", "pending");
+
+ handler->HandleCancelTurnOffFlow(nullptr);
+ VerifyTurnOffFlowStatusWebUIListenerCallback(5U, "idle");
+
+ fake_easy_unlock_service()->SetTurnOffFlowFailForTest();
+ VerifyTurnOffFlowStatusWebUIListenerCallback(6U, "server-error");
+
+ base::ListValue list_args4;
+ list_args4.AppendString("test-callback-id-4");
+ handler->HandleGetTurnOffFlowStatus(&list_args4);
+ VerifyTurnOffFlowStatusWebUIResponse(7U, "test-callback-id-4",
+ "server-error");
+}
+
+} // namespace settings
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc
new file mode 100644
index 00000000000..afcd3c13819
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc
@@ -0,0 +1,327 @@
+// 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/settings/chromeos/fingerprint_handler.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#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/service_manager/public/cpp/connector.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using session_manager::SessionManager;
+using session_manager::SessionState;
+
+namespace chromeos {
+namespace settings {
+namespace {
+
+// The max number of fingerprints that can be stored.
+const int kMaxAllowedFingerprints = 5;
+
+std::unique_ptr<base::DictionaryValue> GetFingerprintsInfo(
+ const std::vector<std::string>& fingerprints_list) {
+ auto response = base::MakeUnique<base::DictionaryValue>();
+ auto fingerprints = base::MakeUnique<base::ListValue>();
+
+ DCHECK_LE(static_cast<int>(fingerprints_list.size()),
+ kMaxAllowedFingerprints);
+ for (auto& fingerprint_name: fingerprints_list) {
+ std::unique_ptr<base::Value> str =
+ base::MakeUnique<base::Value>(fingerprint_name);
+ fingerprints->Append(std::move(str));
+ }
+
+ response->Set("fingerprintsList", std::move(fingerprints));
+ response->SetBoolean("isMaxed", static_cast<int>(fingerprints_list.size()) >=
+ kMaxAllowedFingerprints);
+ return response;
+}
+
+} // namespace
+
+FingerprintHandler::FingerprintHandler(Profile* profile)
+ : profile_(profile), binding_(this), weak_ptr_factory_(this) {
+ service_manager::Connector* connector =
+ content::ServiceManagerConnection::GetForProcess()->GetConnector();
+ connector->BindInterface(device::mojom::kServiceName, &fp_service_);
+ fp_service_->AddFingerprintObserver(binding_.CreateInterfacePtrAndBind());
+ user_id_ = ProfileHelper::Get()->GetUserIdHashFromProfile(profile);
+ // SessionManager may not exist in some tests.
+ if (SessionManager::Get())
+ SessionManager::Get()->AddObserver(this);
+}
+
+FingerprintHandler::~FingerprintHandler() {
+ if (SessionManager::Get())
+ SessionManager::Get()->RemoveObserver(this);
+}
+
+void FingerprintHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "getFingerprintsList",
+ base::Bind(&FingerprintHandler::HandleGetFingerprintsList,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getNumFingerprints",
+ base::Bind(&FingerprintHandler::HandleGetNumFingerprints,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "startEnroll",
+ base::Bind(&FingerprintHandler::HandleStartEnroll,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "cancelCurrentEnroll",
+ base::Bind(&FingerprintHandler::HandleCancelCurrentEnroll,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getEnrollmentLabel",
+ base::Bind(&FingerprintHandler::HandleGetEnrollmentLabel,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "removeEnrollment",
+ base::Bind(&FingerprintHandler::HandleRemoveEnrollment,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "changeEnrollmentLabel",
+ base::Bind(&FingerprintHandler::HandleChangeEnrollmentLabel,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "startAuthentication",
+ base::Bind(&FingerprintHandler::HandleStartAuthentication,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "endCurrentAuthentication",
+ base::Bind(&FingerprintHandler::HandleEndCurrentAuthentication,
+ base::Unretained(this)));
+}
+
+void FingerprintHandler::OnJavascriptAllowed() {}
+
+void FingerprintHandler::OnJavascriptDisallowed() {}
+
+void FingerprintHandler::OnRestarted() {}
+
+void FingerprintHandler::OnEnrollScanDone(uint32_t scan_result,
+ bool enroll_session_complete,
+ int percent_complete) {
+ AllowJavascript();
+ auto scan_attempt = base::MakeUnique<base::DictionaryValue>();
+ scan_attempt->SetInteger("result", scan_result);
+ scan_attempt->SetBoolean("isComplete", enroll_session_complete);
+ scan_attempt->SetInteger("percentComplete", percent_complete);
+
+ FireWebUIListener("on-fingerprint-scan-received", *scan_attempt);
+}
+
+void FingerprintHandler::OnAuthScanDone(
+ uint32_t scan_result,
+ const std::unordered_map<std::string, std::vector<std::string>>& matches) {
+ if (SessionManager::Get()->session_state() == SessionState::LOCKED)
+ return;
+
+ // When the user touches the sensor, highlight the label(s) that finger is
+ // associated with, if it is registered with this user.
+ auto it = matches.find(user_id_);
+ if (it == matches.end() || it->second.size() < 1)
+ return;
+
+ AllowJavascript();
+ auto fingerprint_ids = base::MakeUnique<base::ListValue>();
+
+ for (const std::string& matched_path : it->second) {
+ auto path_it = std::find(fingerprints_paths_.begin(),
+ fingerprints_paths_.end(), matched_path);
+ DCHECK(path_it != fingerprints_paths_.end());
+ fingerprint_ids->AppendInteger(
+ static_cast<int>(path_it - fingerprints_paths_.begin()));
+ }
+
+ auto fingerprint_attempt = base::MakeUnique<base::DictionaryValue>();
+ fingerprint_attempt->SetInteger("result", scan_result);
+ fingerprint_attempt->Set("indexes", std::move(fingerprint_ids));
+
+ FireWebUIListener("on-fingerprint-attempt-received", *fingerprint_attempt);
+}
+
+void FingerprintHandler::OnSessionFailed() {}
+
+void FingerprintHandler::OnSessionStateChanged() {
+ SessionState state = SessionManager::Get()->session_state();
+
+ AllowJavascript();
+ FireWebUIListener("on-screen-locked",
+ base::Value(state == SessionState::LOCKED));
+}
+
+void FingerprintHandler::HandleGetFingerprintsList(
+ const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ std::string callback_id;
+ CHECK(args->GetString(0, &callback_id));
+
+ fp_service_->GetRecordsForUser(
+ user_id_, base::Bind(&FingerprintHandler::OnGetFingerprintsList,
+ weak_ptr_factory_.GetWeakPtr(), callback_id));
+}
+
+void FingerprintHandler::OnGetFingerprintsList(
+ const std::string& callback_id,
+ const std::unordered_map<std::string, std::string>&
+ fingerprints_list_mapping) {
+ AllowJavascript();
+ fingerprints_labels_.clear();
+ fingerprints_paths_.clear();
+ for (auto it = fingerprints_list_mapping.begin();
+ it != fingerprints_list_mapping.end(); ++it) {
+ fingerprints_paths_.push_back(it->first);
+ fingerprints_labels_.push_back(it->second);
+ }
+
+ profile_->GetPrefs()->SetInteger(prefs::kQuickUnlockFingerprintRecord,
+ fingerprints_list_mapping.size());
+
+ std::unique_ptr<base::DictionaryValue> fingerprint_info =
+ GetFingerprintsInfo(fingerprints_labels_);
+ ResolveJavascriptCallback(base::Value(callback_id), *fingerprint_info);
+}
+
+void FingerprintHandler::HandleGetNumFingerprints(const base::ListValue* args) {
+ AllowJavascript();
+
+ CHECK_EQ(1U, args->GetSize());
+ std::string callback_id;
+ CHECK(args->GetString(0, &callback_id));
+
+ int fingerprints_num =
+ profile_->GetPrefs()->GetInteger(prefs::kQuickUnlockFingerprintRecord);
+
+ ResolveJavascriptCallback(base::Value(callback_id),
+ base::Value(fingerprints_num));
+}
+
+void FingerprintHandler::HandleStartEnroll(const base::ListValue* args) {
+ // Determines what the newly added fingerprint's name should be.
+ for (int i = 1; i <= kMaxAllowedFingerprints; ++i) {
+ std::string fingerprint_name = l10n_util::GetStringFUTF8(
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_NEW_FINGERPRINT_DEFAULT_NAME,
+ base::IntToString16(i));
+ if (std::find(fingerprints_labels_.begin(), fingerprints_labels_.end(),
+ fingerprint_name) == fingerprints_labels_.end()) {
+ fp_service_->StartEnrollSession(user_id_, fingerprint_name);
+ break;
+ }
+ }
+}
+
+void FingerprintHandler::HandleCancelCurrentEnroll(
+ const base::ListValue* args) {
+ fp_service_->CancelCurrentEnrollSession(
+ base::Bind(&FingerprintHandler::OnCancelCurrentEnrollSession,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void FingerprintHandler::OnCancelCurrentEnrollSession(bool success) {
+ if (!success)
+ LOG(ERROR) << "Failed to cancel current fingerprint enroll session.";
+}
+
+void FingerprintHandler::HandleGetEnrollmentLabel(const base::ListValue* args) {
+ CHECK_EQ(2U, args->GetSize());
+ std::string callback_id;
+ int index;
+ CHECK(args->GetString(0, &callback_id));
+ CHECK(args->GetInteger(1, &index));
+
+ DCHECK_LT(index, static_cast<int>(fingerprints_labels_.size()));
+ fp_service_->RequestRecordLabel(
+ fingerprints_paths_[index],
+ base::Bind(&FingerprintHandler::OnRequestRecordLabel,
+ weak_ptr_factory_.GetWeakPtr(), callback_id));
+}
+
+void FingerprintHandler::OnRequestRecordLabel(const std::string& callback_id,
+ const std::string& label) {
+ AllowJavascript();
+ ResolveJavascriptCallback(base::Value(callback_id), base::Value(label));
+}
+
+void FingerprintHandler::HandleRemoveEnrollment(const base::ListValue* args) {
+ CHECK_EQ(2U, args->GetSize());
+ std::string callback_id;
+ int index;
+ CHECK(args->GetString(0, &callback_id));
+ CHECK(args->GetInteger(1, &index));
+
+ DCHECK_LT(index, static_cast<int>(fingerprints_paths_.size()));
+ fp_service_->RemoveRecord(
+ fingerprints_paths_[index],
+ base::Bind(&FingerprintHandler::OnRemoveRecord,
+ weak_ptr_factory_.GetWeakPtr(), callback_id));
+}
+
+void FingerprintHandler::OnRemoveRecord(const std::string& callback_id,
+ bool success) {
+ if (!success)
+ LOG(ERROR) << "Failed to remove fingerprint record.";
+ AllowJavascript();
+ ResolveJavascriptCallback(base::Value(callback_id), base::Value(success));
+}
+
+void FingerprintHandler::HandleChangeEnrollmentLabel(
+ const base::ListValue* args) {
+ CHECK_EQ(3U, args->GetSize());
+ std::string callback_id;
+ int index;
+ std::string new_label;
+
+ CHECK(args->GetString(0, &callback_id));
+ CHECK(args->GetInteger(1, &index));
+ CHECK(args->GetString(2, &new_label));
+
+ fp_service_->SetRecordLabel(
+ new_label, fingerprints_paths_[index],
+ base::Bind(&FingerprintHandler::OnSetRecordLabel,
+ weak_ptr_factory_.GetWeakPtr(), callback_id));
+}
+
+void FingerprintHandler::OnSetRecordLabel(const std::string& callback_id,
+ bool success) {
+ if (!success)
+ LOG(ERROR) << "Failed to set fingerprint record label.";
+ AllowJavascript();
+ ResolveJavascriptCallback(base::Value(callback_id), base::Value(success));
+}
+
+void FingerprintHandler::HandleStartAuthentication(
+ const base::ListValue* args) {
+ fp_service_->StartAuthSession();
+}
+
+void FingerprintHandler::HandleEndCurrentAuthentication(
+ const base::ListValue* args) {
+ fp_service_->EndCurrentAuthSession(
+ base::Bind(&FingerprintHandler::OnEndCurrentAuthSession,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void FingerprintHandler::OnEndCurrentAuthSession(bool success) {
+ if (!success)
+ LOG(ERROR) << "Failed to end current fingerprint authentication session.";
+}
+
+} // namespace settings
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.h
new file mode 100644
index 00000000000..5c296d69fe7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.h
@@ -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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FINGERPRINT_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FINGERPRINT_HANDLER_H_
+
+#include <unordered_map>
+
+#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"
+
+class Profile;
+
+namespace base {
+class ListValue;
+} // namespace base
+
+namespace chromeos {
+namespace settings {
+
+// Chrome OS fingerprint setup settings page UI handler.
+class FingerprintHandler : public ::settings::SettingsPageUIHandler,
+ public device::mojom::FingerprintObserver,
+ public session_manager::SessionManagerObserver {
+ public:
+ explicit FingerprintHandler(Profile* profile);
+ ~FingerprintHandler() override;
+
+ // SettingsPageUIHandler overrides:
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ private:
+ using AttemptMatches =
+ std::unordered_map<std::string, std::vector<std::string>>;
+
+ // device::mojom::FingerprintObserver:
+ void OnRestarted() override;
+ void OnEnrollScanDone(uint32_t scan_result,
+ bool enroll_session_complete,
+ int percent_complete) override;
+ void OnAuthScanDone(uint32_t scan_result,
+ const AttemptMatches& matches) override;
+ void OnSessionFailed() override;
+
+ // session_manager::SessionManagerObserver:
+ void OnSessionStateChanged() override;
+
+ void HandleGetFingerprintsList(const base::ListValue* args);
+ void HandleGetNumFingerprints(const base::ListValue* args);
+ void HandleStartEnroll(const base::ListValue* args);
+ void HandleCancelCurrentEnroll(const base::ListValue* args);
+ void HandleGetEnrollmentLabel(const base::ListValue* args);
+ void HandleRemoveEnrollment(const base::ListValue* args);
+ void HandleChangeEnrollmentLabel(const base::ListValue* args);
+ void HandleStartAuthentication(const base::ListValue* args);
+ void HandleEndCurrentAuthentication(const base::ListValue* args);
+
+ void OnGetFingerprintsList(const std::string& callback_id,
+ const std::unordered_map<std::string, std::string>&
+ fingerprints_list_mapping);
+ void OnRequestRecordLabel(const std::string& callback_id,
+ const std::string& label);
+ void OnCancelCurrentEnrollSession(bool success);
+ void OnRemoveRecord(const std::string& callback_id, bool success);
+ void OnSetRecordLabel(const std::string& callback_id, bool success);
+ void OnEndCurrentAuthSession(bool success);
+
+ Profile* profile_; // unowned
+
+ std::vector<std::string> fingerprints_labels_;
+ std::vector<std::string> fingerprints_paths_;
+ std::string user_id_;
+
+ device::mojom::FingerprintPtr fp_service_;
+ mojo::Binding<device::mojom::FingerprintObserver> binding_;
+
+ base::WeakPtrFactory<FingerprintHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FingerprintHandler);
+};
+
+} // namespace settings
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FINGERPRINT_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc
new file mode 100644
index 00000000000..1af49e99b48
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc
@@ -0,0 +1,132 @@
+// 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 "chrome/browser/ui/webui/settings/chromeos/internet_handler.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/options/network_config_view.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/ui/choose_mobile_network_dialog.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "components/onc/onc_constants.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/browser/api/vpn_provider/vpn_service.h"
+#include "extensions/browser/api/vpn_provider/vpn_service_factory.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+
+namespace {
+
+const char kAddNetworkMessage[] = "addNetwork";
+const char kConfigureNetworkMessage[] = "configureNetwork";
+
+std::string ServicePathFromGuid(const std::string& guid) {
+ const NetworkState* network =
+ NetworkHandler::Get()->network_state_handler()->GetNetworkStateFromGuid(
+ guid);
+ return network ? network->path() : "";
+}
+
+Profile* GetProfileForPrimaryUser() {
+ return ProfileHelper::Get()->GetProfileByUser(
+ user_manager::UserManager::Get()->GetPrimaryUser());
+}
+
+} // namespace
+
+namespace settings {
+
+InternetHandler::InternetHandler() {}
+
+InternetHandler::~InternetHandler() {}
+
+void InternetHandler::RegisterMessages() {
+ // TODO(stevenjb): Eliminate once network configuration UI is integrated
+ // into settings.
+ web_ui()->RegisterMessageCallback(
+ kAddNetworkMessage,
+ base::Bind(&InternetHandler::AddNetwork, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kConfigureNetworkMessage,
+ base::Bind(&InternetHandler::ConfigureNetwork, base::Unretained(this)));
+}
+
+void InternetHandler::OnJavascriptAllowed() {}
+
+void InternetHandler::OnJavascriptDisallowed() {}
+
+void InternetHandler::AddNetwork(const base::ListValue* args) {
+ std::string onc_type;
+ if (args->GetSize() < 1 || !args->GetString(0, &onc_type)) {
+ NOTREACHED() << "Invalid args for: " << kAddNetworkMessage;
+ return;
+ }
+
+ if (onc_type == ::onc::network_type::kVPN) {
+ std::string extension_id;
+ if (args->GetSize() >= 2)
+ args->GetString(1, &extension_id);
+ if (extension_id.empty()) {
+ // Show the "add network" dialog for the built-in OpenVPN/L2TP provider.
+ NetworkConfigView::ShowForType(shill::kTypeVPN, GetNativeWindow());
+ return;
+ }
+ // Request that the third-party VPN provider identified by |provider_id|
+ // show its "add network" dialog.
+ VpnServiceFactory::GetForBrowserContext(GetProfileForPrimaryUser())
+ ->SendShowAddDialogToExtension(extension_id);
+ } else if (onc_type == ::onc::network_type::kWiFi) {
+ NetworkConfigView::ShowForType(shill::kTypeWifi, GetNativeWindow());
+ } else if (onc_type == ::onc::network_type::kCellular) {
+ ChooseMobileNetworkDialog::ShowDialog(GetNativeWindow());
+ } else {
+ LOG(ERROR) << "Unsupported type for: " << kAddNetworkMessage;
+ }
+}
+
+void InternetHandler::ConfigureNetwork(const base::ListValue* args) {
+ std::string guid;
+ if (args->GetSize() < 1 || !args->GetString(0, &guid)) {
+ NOTREACHED() << "Invalid args for: " << kConfigureNetworkMessage;
+ return;
+ }
+
+ const std::string service_path = ServicePathFromGuid(guid);
+ if (service_path.empty()) {
+ LOG(ERROR) << "Network not found: " << guid;
+ return;
+ }
+
+ const NetworkState* network =
+ NetworkHandler::Get()->network_state_handler()->GetNetworkState(
+ service_path);
+ if (!network) {
+ LOG(ERROR) << "Network not found with service_path: " << service_path;
+ return;
+ }
+
+ if (network->type() == shill::kTypeVPN &&
+ network->vpn_provider_type() == shill::kProviderThirdPartyVpn) {
+ // Request that the third-party VPN provider used by the |network| show a
+ // configuration dialog for it.
+ VpnServiceFactory::GetForBrowserContext(GetProfileForPrimaryUser())
+ ->SendShowConfigureDialogToExtension(
+ network->third_party_vpn_provider_extension_id(), network->name());
+ return;
+ }
+
+ NetworkConfigView::ShowForNetworkId(network->guid(), GetNativeWindow());
+}
+
+gfx::NativeWindow InternetHandler::GetNativeWindow() const {
+ return web_ui()->GetWebContents()->GetTopLevelNativeWindow();
+}
+
+} // namespace settings
+} // namespace chromeos
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/internet_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/internet_handler.h
new file mode 100644
index 00000000000..64ceabfe29b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/chromeos/internet_handler.h
@@ -0,0 +1,41 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_INTERNET_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_INTERNET_HANDLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace chromeos {
+namespace settings {
+
+// Chrome OS Internet settings page UI handler.
+class InternetHandler : public ::settings::SettingsPageUIHandler {
+ public:
+ InternetHandler();
+ ~InternetHandler() override;
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ private:
+ // Settings JS handlers.
+ void AddNetwork(const base::ListValue* args);
+ void ConfigureNetwork(const base::ListValue* args);
+
+ gfx::NativeWindow GetNativeWindow() const;
+
+ DISALLOW_COPY_AND_ASSIGN(InternetHandler);
+};
+
+} // namespace settings
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_INTERNET_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/downloads_handler.cc b/chromium/chrome/browser/ui/webui/settings/downloads_handler.cc
new file mode 100644
index 00000000000..e5acd37e95a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/downloads_handler.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 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.
+
+#include "chrome/browser/ui/webui/settings/downloads_handler.h"
+
+#include "base/metrics/user_metrics.h"
+#include "base/values.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/chrome_select_file_policy.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using base::UserMetricsAction;
+
+namespace settings {
+
+DownloadsHandler::DownloadsHandler(Profile* profile) : profile_(profile) {}
+
+DownloadsHandler::~DownloadsHandler() {
+ // There may be pending file dialogs, we need to tell them that we've gone
+ // away so they don't try and call back to us.
+ if (select_folder_dialog_.get())
+ select_folder_dialog_->ListenerDestroyed();
+}
+
+void DownloadsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "initializeDownloads",
+ base::Bind(&DownloadsHandler::HandleInitialize, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "resetAutoOpenFileTypes",
+ base::Bind(&DownloadsHandler::HandleResetAutoOpenFileTypes,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "selectDownloadLocation",
+ base::Bind(&DownloadsHandler::HandleSelectDownloadLocation,
+ base::Unretained(this)));
+}
+
+void DownloadsHandler::OnJavascriptAllowed() {
+ pref_registrar_.Init(profile_->GetPrefs());
+ pref_registrar_.Add(
+ prefs::kDownloadExtensionsToOpen,
+ base::Bind(&DownloadsHandler::SendAutoOpenDownloadsToJavascript,
+ base::Unretained(this)));
+}
+
+void DownloadsHandler::OnJavascriptDisallowed() {
+ pref_registrar_.RemoveAll();
+}
+
+void DownloadsHandler::HandleInitialize(const base::ListValue* args) {
+ AllowJavascript();
+ SendAutoOpenDownloadsToJavascript();
+}
+
+void DownloadsHandler::SendAutoOpenDownloadsToJavascript() {
+ content::DownloadManager* manager =
+ content::BrowserContext::GetDownloadManager(profile_);
+ bool auto_open_downloads =
+ DownloadPrefs::FromDownloadManager(manager)->IsAutoOpenUsed();
+ FireWebUIListener("auto-open-downloads-changed",
+ base::Value(auto_open_downloads));
+}
+
+void DownloadsHandler::HandleResetAutoOpenFileTypes(
+ const base::ListValue* args) {
+ base::RecordAction(UserMetricsAction("Options_ResetAutoOpenFiles"));
+ content::DownloadManager* manager =
+ content::BrowserContext::GetDownloadManager(profile_);
+ DownloadPrefs::FromDownloadManager(manager)->ResetAutoOpen();
+}
+
+void DownloadsHandler::HandleSelectDownloadLocation(
+ const base::ListValue* args) {
+ PrefService* pref_service = profile_->GetPrefs();
+ select_folder_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
+ ui::SelectFileDialog::FileTypeInfo info;
+ info.allowed_paths = ui::SelectFileDialog::FileTypeInfo::NATIVE_OR_DRIVE_PATH;
+ select_folder_dialog_->SelectFile(
+ ui::SelectFileDialog::SELECT_FOLDER,
+ l10n_util::GetStringUTF16(IDS_SETTINGS_DOWNLOAD_LOCATION),
+ pref_service->GetFilePath(prefs::kDownloadDefaultDirectory), &info, 0,
+ base::FilePath::StringType(),
+ web_ui()->GetWebContents()->GetTopLevelNativeWindow(), NULL);
+}
+
+void DownloadsHandler::FileSelected(const base::FilePath& path,
+ int index,
+ void* params) {
+ base::RecordAction(UserMetricsAction("Options_SetDownloadDirectory"));
+ PrefService* pref_service = profile_->GetPrefs();
+ pref_service->SetFilePath(prefs::kDownloadDefaultDirectory, path);
+ pref_service->SetFilePath(prefs::kSaveFileDefaultDirectory, path);
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/downloads_handler.h b/chromium/chrome/browser/ui/webui/settings/downloads_handler.h
new file mode 100644
index 00000000000..289cf74ab82
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/downloads_handler.h
@@ -0,0 +1,65 @@
+// Copyright (c) 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_DOWNLOADS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_DOWNLOADS_HANDLER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+
+class Profile;
+
+namespace settings {
+
+// Chrome "Downloads" settings page UI handler.
+class DownloadsHandler : public SettingsPageUIHandler,
+ public ui::SelectFileDialog::Listener {
+ public:
+ explicit DownloadsHandler(Profile* profile);
+ ~DownloadsHandler() override;
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ private:
+ friend class DownloadsHandlerTest;
+ FRIEND_TEST_ALL_PREFIXES(DownloadsHandlerTest, AutoOpenDownloads);
+
+ // Callback for the "initializeDownloads" message. This starts observers and
+ // retrieves the current browser state.
+ void HandleInitialize(const base::ListValue* args);
+
+ void SendAutoOpenDownloadsToJavascript();
+
+ // Resets the list of filetypes that are auto-opened after download.
+ void HandleResetAutoOpenFileTypes(const base::ListValue* args);
+
+ // Callback for the "selectDownloadLocation" message. This will prompt the
+ // user for a destination folder using platform-specific APIs.
+ void HandleSelectDownloadLocation(const base::ListValue* args);
+
+ // SelectFileDialog::Listener implementation.
+ void FileSelected(const base::FilePath& path,
+ int index,
+ void* params) override;
+
+ Profile* profile_;
+
+ PrefChangeRegistrar pref_registrar_;
+
+ scoped_refptr<ui::SelectFileDialog> select_folder_dialog_;
+
+ DISALLOW_COPY_AND_ASSIGN(DownloadsHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_DOWNLOADS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/downloads_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/downloads_handler_unittest.cc
new file mode 100644
index 00000000000..c20ca4db582
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/downloads_handler_unittest.cc
@@ -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.
+
+#include "chrome/browser/ui/webui/settings/downloads_handler.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/download/chrome_download_manager_delegate.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/test/mock_download_manager.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_web_ui.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace settings {
+
+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_));
+ EXPECT_CALL(*download_manager_, Shutdown());
+
+ handler_.set_web_ui(&test_web_ui_);
+ }
+
+ void SetUp() override {
+ EXPECT_TRUE(test_web_ui_.call_data().empty());
+
+ base::ListValue args;
+ handler()->HandleInitialize(&args);
+
+ EXPECT_TRUE(handler()->IsJavascriptAllowed());
+ VerifyAutoOpenDownloadsChangedCallback();
+
+ test_web_ui_.ClearTrackedCalls();
+ }
+
+ void TearDown() override {
+ chrome_download_manager_delegate_.Shutdown();
+ testing::Test::TearDown();
+ }
+
+ void VerifyAutoOpenDownloadsChangedCallback() {
+ EXPECT_EQ(1u, test_web_ui_.call_data().size());
+
+ auto& data = *(test_web_ui_.call_data().back());
+ EXPECT_EQ("cr.webUIListenerCallback", data.function_name());
+ std::string event;
+ ASSERT_TRUE(data.arg1()->GetAsString(&event));
+ EXPECT_EQ("auto-open-downloads-changed", event);
+ bool auto_open_downloads = false;
+ ASSERT_TRUE(data.arg2()->GetAsBoolean(&auto_open_downloads));
+ EXPECT_FALSE(auto_open_downloads);
+ }
+
+ Profile* profile() { return &profile_; }
+ DownloadsHandler* handler() { return &handler_; }
+
+ private:
+ content::TestBrowserThreadBundle thread_bundle_;
+ content::TestWebUI test_web_ui_;
+ TestingProfile profile_;
+
+ content::MockDownloadManager* download_manager_; // Owned by |profile_|.
+ ChromeDownloadManagerDelegate chrome_download_manager_delegate_;
+
+ DownloadsHandler handler_;
+};
+
+TEST_F(DownloadsHandlerTest, AutoOpenDownloads) {
+ // Touch the pref.
+ profile()->GetPrefs()->SetString(prefs::kDownloadExtensionsToOpen, "");
+ VerifyAutoOpenDownloadsChangedCallback();
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/extension_control_handler.cc b/chromium/chrome/browser/ui/webui/settings/extension_control_handler.cc
new file mode 100644
index 00000000000..5439d599a88
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/extension_control_handler.cc
@@ -0,0 +1,38 @@
+// 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 "chrome/browser/ui/webui/settings/extension_control_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension.h"
+
+namespace settings {
+
+ExtensionControlHandler::ExtensionControlHandler() {}
+ExtensionControlHandler::~ExtensionControlHandler() {}
+
+void ExtensionControlHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("disableExtension",
+ base::Bind(&ExtensionControlHandler::HandleDisableExtension,
+ base::Unretained(this)));
+}
+
+void ExtensionControlHandler::HandleDisableExtension(
+ const base::ListValue* args) {
+ std::string extension_id;
+ CHECK(args->GetString(0, &extension_id));
+ ExtensionService* extension_service = extensions::ExtensionSystem::Get(
+ Profile::FromWebUI(web_ui()))->extension_service();
+ DCHECK(extension_service);
+ extension_service->DisableExtension(
+ extension_id, extensions::Extension::DISABLE_USER_ACTION);
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/extension_control_handler.h b/chromium/chrome/browser/ui/webui/settings/extension_control_handler.h
new file mode 100644
index 00000000000..b948e7914c1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/extension_control_handler.h
@@ -0,0 +1,37 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_EXTENSION_CONTROL_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_EXTENSION_CONTROL_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace settings {
+
+class ExtensionControlHandler : public SettingsPageUIHandler {
+ public:
+ ExtensionControlHandler();
+ ~ExtensionControlHandler() override;
+
+ // SettingsPageUIHandler:
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override {}
+ void OnJavascriptDisallowed() override {}
+
+ private:
+ // Handler for the "disableExtension" message. Extension ID is passed as the
+ // single string argument.
+ void HandleDisableExtension(const base::ListValue* args);
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionControlHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_EXTENSION_CONTROL_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/font_handler.cc b/chromium/chrome/browser/ui/webui/settings/font_handler.cc
new file mode 100644
index 00000000000..954f7f6633f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/font_handler.cc
@@ -0,0 +1,149 @@
+// 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.
+
+#include "chrome/browser/ui/webui/settings/font_handler.h"
+
+#include <stddef.h>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind_helpers.h"
+#include "base/i18n/rtl.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/webui/options/font_settings_utils.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/font_list_async.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension_urls.h"
+
+namespace {
+
+const char kAdvancedFontSettingsExtensionId[] =
+ "caclkomlalccbpcdllchkeecicepbmbm";
+
+} // namespace
+
+namespace settings {
+
+FontHandler::FontHandler(content::WebUI* webui)
+ : extension_registry_observer_(this),
+ profile_(Profile::FromWebUI(webui)),
+ weak_ptr_factory_(this) {
+ // Perform validation for saved fonts.
+ options::FontSettingsUtilities::ValidateSavedFonts(profile_->GetPrefs());
+}
+
+FontHandler::~FontHandler() {}
+
+void FontHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "fetchFontsData", base::Bind(&FontHandler::HandleFetchFontsData,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "observeAdvancedFontExtensionAvailable",
+ base::Bind(&FontHandler::HandleObserveAdvancedFontExtensionAvailable,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "openAdvancedFontSettings",
+ base::Bind(&FontHandler::HandleOpenAdvancedFontSettings,
+ base::Unretained(this)));
+}
+
+void FontHandler::OnJavascriptAllowed() {
+ extension_registry_observer_.Add(
+ extensions::ExtensionRegistry::Get(profile_));
+}
+
+void FontHandler::OnJavascriptDisallowed() {
+ extension_registry_observer_.RemoveAll();
+}
+
+void FontHandler::HandleFetchFontsData(const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ std::string callback_id;
+ CHECK(args->GetString(0, &callback_id));
+
+ content::GetFontListAsync(base::Bind(&FontHandler::FontListHasLoaded,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback_id));
+}
+
+void FontHandler::HandleObserveAdvancedFontExtensionAvailable(
+ const base::ListValue* /*args*/) {
+ AllowJavascript();
+ NotifyAdvancedFontSettingsAvailability();
+}
+
+void FontHandler::HandleOpenAdvancedFontSettings(
+ const base::ListValue* /*args*/) {
+ const extensions::Extension* extension = GetAdvancedFontSettingsExtension();
+ if (!extension)
+ return;
+ extensions::ExtensionTabUtil::OpenOptionsPage(
+ extension,
+ chrome::FindBrowserWithWebContents(web_ui()->GetWebContents()));
+}
+
+const extensions::Extension* FontHandler::GetAdvancedFontSettingsExtension() {
+ ExtensionService* service =
+ extensions::ExtensionSystem::Get(profile_)->extension_service();
+ if (!service->IsExtensionEnabled(kAdvancedFontSettingsExtensionId))
+ return nullptr;
+ return service->GetInstalledExtension(kAdvancedFontSettingsExtensionId);
+}
+
+void FontHandler::NotifyAdvancedFontSettingsAvailability() {
+ CallJavascriptFunction(
+ "cr.webUIListenerCallback",
+ base::Value("advanced-font-settings-installed"),
+ base::Value(GetAdvancedFontSettingsExtension() != nullptr));
+}
+
+void FontHandler::OnExtensionLoaded(content::BrowserContext*,
+ const extensions::Extension*) {
+ NotifyAdvancedFontSettingsAvailability();
+}
+
+void FontHandler::OnExtensionUnloaded(content::BrowserContext*,
+ const extensions::Extension*,
+ extensions::UnloadedExtensionReason) {
+ NotifyAdvancedFontSettingsAvailability();
+}
+
+void FontHandler::FontListHasLoaded(std::string callback_id,
+ std::unique_ptr<base::ListValue> list) {
+ // Font list. Selects the directionality for the fonts in the given list.
+ for (size_t i = 0; i < list->GetSize(); i++) {
+ base::ListValue* font;
+ bool has_font = list->GetList(i, &font);
+ DCHECK(has_font);
+
+ base::string16 value;
+ bool has_value = font->GetString(1, &value);
+ DCHECK(has_value);
+
+ bool has_rtl_chars = base::i18n::StringContainsStrongRTLChars(value);
+ font->AppendString(has_rtl_chars ? "rtl" : "ltr");
+ }
+
+ base::DictionaryValue response;
+ response.Set("fontList", std::move(list));
+
+ GURL extension_url(extension_urls::GetWebstoreItemDetailURLPrefix());
+ response.SetString(
+ "extensionUrl",
+ extension_url.Resolve(kAdvancedFontSettingsExtensionId).spec());
+
+ ResolveJavascriptCallback(base::Value(callback_id), response);
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/font_handler.h b/chromium/chrome/browser/ui/webui/settings/font_handler.h
new file mode 100644
index 00000000000..d7c58c2285f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/font_handler.h
@@ -0,0 +1,85 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_FONT_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_FONT_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "extensions/browser/extension_registry_observer.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace content {
+class WebUI;
+}
+
+namespace extensions {
+class Extension;
+class ExtensionRegistry;
+}
+
+class Profile;
+
+namespace settings {
+
+// Handle OS font list and font preference settings.
+class FontHandler : public SettingsPageUIHandler,
+ public extensions::ExtensionRegistryObserver {
+ public:
+ explicit FontHandler(content::WebUI* webui);
+ ~FontHandler() override;
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ // ExtensionRegistryObserver implementation.
+ void OnExtensionLoaded(content::BrowserContext* browser_context,
+ const extensions::Extension* extension) override;
+ void OnExtensionUnloaded(content::BrowserContext* browser_context,
+ const extensions::Extension* extension,
+ extensions::UnloadedExtensionReason reason) override;
+
+ private:
+ // Handler for script asking for font information.
+ void HandleFetchFontsData(const base::ListValue* args);
+
+ // Listen for changes to whether the advanced font extension is available.
+ // An initial update will be sent when observation begins.
+ void HandleObserveAdvancedFontExtensionAvailable(const base::ListValue* args);
+
+ // Open the advanced font settings page.
+ void HandleOpenAdvancedFontSettings(const base::ListValue* args);
+
+ // Callback to handle fonts loading.
+ void FontListHasLoaded(std::string callback_id,
+ std::unique_ptr<base::ListValue> list);
+
+ const extensions::Extension* GetAdvancedFontSettingsExtension();
+
+ void NotifyAdvancedFontSettingsAvailability();
+
+ ScopedObserver<extensions::ExtensionRegistry,
+ extensions::ExtensionRegistryObserver>
+ extension_registry_observer_;
+
+ Profile* profile_; // Weak pointer.
+
+ base::WeakPtrFactory<FontHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FontHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_FONT_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/languages_handler.cc b/chromium/chrome/browser/ui/webui/settings/languages_handler.cc
new file mode 100644
index 00000000000..2f98827ad2d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/languages_handler.cc
@@ -0,0 +1,88 @@
+// 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.
+
+#include "chrome/browser/ui/webui/settings/languages_handler.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_ui.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "components/user_manager/user_manager.h"
+#include "components/user_manager/user_type.h"
+#endif
+
+namespace settings {
+
+LanguagesHandler::LanguagesHandler(content::WebUI* webui)
+ : profile_(Profile::FromWebUI(webui)) {
+}
+
+LanguagesHandler::~LanguagesHandler() {
+}
+
+void LanguagesHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "getProspectiveUILanguage",
+ base::Bind(&LanguagesHandler::HandleGetProspectiveUILanguage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setProspectiveUILanguage",
+ base::Bind(&LanguagesHandler::HandleSetProspectiveUILanguage,
+ base::Unretained(this)));
+}
+
+void LanguagesHandler::HandleGetProspectiveUILanguage(
+ const base::ListValue* args) {
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+
+ AllowJavascript();
+
+ std::string locale;
+#if defined(OS_CHROMEOS)
+ // On Chrome OS, an individual profile may have a preferred locale.
+ locale = profile_->GetPrefs()->GetString(prefs::kApplicationLocale);
+#endif // defined(OS_CHROMEOS)
+
+ if (locale.empty()) {
+ locale =
+ g_browser_process->local_state()->GetString(prefs::kApplicationLocale);
+ }
+
+ ResolveJavascriptCallback(*callback_id, base::Value(locale));
+}
+
+void LanguagesHandler::HandleSetProspectiveUILanguage(
+ const base::ListValue* args) {
+ AllowJavascript();
+ CHECK_EQ(1U, args->GetSize());
+
+ std::string language_code;
+ CHECK(args->GetString(0, &language_code));
+
+#if defined(OS_WIN)
+ PrefService* prefs = g_browser_process->local_state();
+ prefs->SetString(prefs::kApplicationLocale, language_code);
+#elif defined(OS_CHROMEOS)
+ // Secondary users and public session users cannot change the locale.
+ user_manager::UserManager* user_manager = user_manager::UserManager::Get();
+ const user_manager::User* user =
+ chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
+ if (user &&
+ user->GetAccountId() == user_manager->GetPrimaryUser()->GetAccountId() &&
+ user->GetType() != user_manager::USER_TYPE_PUBLIC_ACCOUNT) {
+ profile_->ChangeAppLocale(language_code,
+ Profile::APP_LOCALE_CHANGED_VIA_SETTINGS);
+ }
+#endif
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/languages_handler.h b/chromium/chrome/browser/ui/webui/settings/languages_handler.h
new file mode 100644
index 00000000000..e1cfd55e98f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/languages_handler.h
@@ -0,0 +1,51 @@
+// Copyright (c) 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_LANGUAGES_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_LANGUAGES_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace content {
+class WebUI;
+}
+
+class Profile;
+
+namespace settings {
+
+// Chrome "Languages" settings page UI handler.
+class LanguagesHandler : public SettingsPageUIHandler {
+ public:
+ explicit LanguagesHandler(content::WebUI* webui);
+ ~LanguagesHandler() override;
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override {}
+ void OnJavascriptDisallowed() override {}
+
+ private:
+ // Returns the prospective UI language. May not match the actual UI language,
+ // depending on the user's permissions and whether the language is substituted
+ // for another locale.
+ void HandleGetProspectiveUILanguage(const base::ListValue* args);
+
+ // Changes the preferred UI language, provided the user is allowed to do so.
+ // The actual UI language will not change until the next restart.
+ void HandleSetProspectiveUILanguage(const base::ListValue* args);
+
+ Profile* profile_; // Weak pointer.
+
+ DISALLOW_COPY_AND_ASSIGN(LanguagesHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_LANGUAGES_HANDLER_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
new file mode 100644
index 00000000000..b714d9b070f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -0,0 +1,2130 @@
+// 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.
+
+#include "chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.h"
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/i18n/number_formatting.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.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"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_shortcut_manager.h"
+#include "chrome/browser/ui/webui/policy_indicator_localized_strings_provider.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/locale_settings.h"
+#include "components/autofill/core/browser/payments/payments_service_url.h"
+#include "components/autofill/core/common/autofill_constants.h"
+#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/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 "ui/base/l10n/l10n_util.h"
+
+#if defined(OS_CHROMEOS)
+#include "ash/system/devicetype_utils.h"
+#include "ash/system/night_light/night_light_controller.h"
+#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
+#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/ui/webui/chromeos/network_element_localized_strings_provider.h"
+#include "chrome/browser/ui/webui/chromeos/ui_account_tweaks.h"
+#include "chromeos/chromeos_switches.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
+#include "ui/chromeos/strings/grit/ui_chromeos_strings.h"
+#include "ui/display/display_switches.h"
+#else
+#include "chrome/browser/ui/webui/settings/system_handler.h"
+#endif
+
+namespace settings {
+namespace {
+
+// Note that settings.html contains a <script> tag which imports a script of
+// the following name. These names must be kept in sync.
+const char kLocalizedStringsFile[] = "strings.js";
+
+struct LocalizedString {
+ const char* name;
+ int id;
+};
+
+void AddLocalizedStringsBulk(content::WebUIDataSource* html_source,
+ LocalizedString localized_strings[],
+ size_t num_strings) {
+ for (size_t i = 0; i < num_strings; i++) {
+ html_source->AddLocalizedString(localized_strings[i].name,
+ localized_strings[i].id);
+ }
+}
+
+void AddCommonStrings(content::WebUIDataSource* html_source, Profile* profile) {
+ LocalizedString localized_strings[] = {
+ {"add", IDS_ADD},
+ {"advancedPageTitle", IDS_SETTINGS_ADVANCED},
+ {"back", IDS_ACCNAME_BACK},
+ {"basicPageTitle", IDS_SETTINGS_BASIC},
+ {"cancel", IDS_CANCEL},
+ {"clear", IDS_SETTINGS_CLEAR},
+ {"close", IDS_CLOSE},
+ {"confirm", IDS_CONFIRM},
+ {"controlledByExtension", IDS_SETTINGS_CONTROLLED_BY_EXTENSION},
+#if defined(OS_CHROMEOS)
+ {"deviceOff", IDS_SETTINGS_DEVICE_OFF},
+ {"deviceOn", IDS_SETTINGS_DEVICE_ON},
+#endif
+ {"disable", IDS_DISABLE},
+ {"done", IDS_DONE},
+ {"edit", IDS_SETTINGS_EDIT},
+ {"learnMore", IDS_LEARN_MORE},
+ {"menuButtonLabel", IDS_SETTINGS_MENU_BUTTON_LABEL},
+ {"moreActions", IDS_SETTINGS_MORE_ACTIONS},
+ {"ok", IDS_OK},
+ {"restart", IDS_SETTINGS_RESTART},
+ {"save", IDS_SAVE},
+ {"settings", IDS_SETTINGS_SETTINGS},
+ {"toggleOn", IDS_SETTINGS_TOGGLE_ON},
+ {"toggleOff", IDS_SETTINGS_TOGGLE_OFF},
+ {"notValid", IDS_SETTINGS_NOT_VALID},
+ {"notValidWebAddress", IDS_SETTINGS_NOT_VALID_WEB_ADDRESS},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+
+ html_source->AddBoolean(
+ "isGuest",
+#if defined(OS_CHROMEOS)
+ user_manager::UserManager::Get()->IsLoggedInAsGuest() ||
+ user_manager::UserManager::Get()->IsLoggedInAsPublicAccount());
+#else
+ profile->IsOffTheRecord());
+#endif
+
+ html_source->AddBoolean("isSupervised", profile->IsSupervised());
+}
+
+void AddA11yStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"a11yPageTitle", IDS_SETTINGS_ACCESSIBILITY},
+ {"a11yWebStore", IDS_SETTINGS_ACCESSIBILITY_WEB_STORE},
+ {"moreFeaturesLink", IDS_SETTINGS_MORE_FEATURES_LINK},
+ {"moreFeaturesLinkDescription",
+ IDS_SETTINGS_MORE_FEATURES_LINK_DESCRIPTION},
+#if defined(OS_CHROMEOS)
+ {"optionsInMenuLabel", IDS_SETTINGS_OPTIONS_IN_MENU_LABEL},
+ {"largeMouseCursorLabel", IDS_SETTINGS_LARGE_MOUSE_CURSOR_LABEL},
+ {"largeMouseCursorSizeLabel", IDS_SETTINGS_LARGE_MOUSE_CURSOR_SIZE_LABEL},
+ {"largeMouseCursorSizeDefaultLabel",
+ IDS_SETTINGS_LARGE_MOUSE_CURSOR_SIZE_DEFAULT_LABEL},
+ {"largeMouseCursorSizeLargeLabel",
+ IDS_SETTINGS_LARGE_MOUSE_CURSOR_SIZE_LARGE_LABEL},
+ {"highContrastLabel", IDS_SETTINGS_HIGH_CONTRAST_LABEL},
+ {"stickyKeysLabel", IDS_SETTINGS_STICKY_KEYS_LABEL},
+ {"chromeVoxLabel", IDS_SETTINGS_CHROMEVOX_LABEL},
+ {"chromeVoxOptionsLabel", IDS_SETTINGS_CHROMEVOX_OPTIONS_LABEL},
+ {"screenMagnifierLabel", IDS_SETTINGS_SCREEN_MAGNIFIER_LABEL},
+ {"tapDraggingLabel", IDS_SETTINGS_TAP_DRAGGING_LABEL},
+ {"clickOnStopLabel", IDS_SETTINGS_CLICK_ON_STOP_LABEL},
+ {"delayBeforeClickLabel", IDS_SETTINGS_DELAY_BEFORE_CLICK_LABEL},
+ {"delayBeforeClickExtremelyShort",
+ IDS_SETTINGS_DELAY_BEFORE_CLICK_EXTREMELY_SHORT},
+ {"delayBeforeClickVeryShort", IDS_SETTINGS_DELAY_BEFORE_CLICK_VERY_SHORT},
+ {"delayBeforeClickShort", IDS_SETTINGS_DELAY_BEFORE_CLICK_SHORT},
+ {"delayBeforeClickLong", IDS_SETTINGS_DELAY_BEFORE_CLICK_LONG},
+ {"delayBeforeClickVeryLong", IDS_SETTINGS_DELAY_BEFORE_CLICK_VERY_LONG},
+ {"onScreenKeyboardLabel", IDS_SETTINGS_ON_SCREEN_KEYBOARD_LABEL},
+ {"monoAudioLabel", IDS_SETTINGS_MONO_AUDIO_LABEL},
+ {"a11yExplanation", IDS_SETTINGS_ACCESSIBILITY_EXPLANATION},
+ {"caretHighlightLabel",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_CARET_HIGHLIGHT_DESCRIPTION},
+ {"cursorHighlightLabel",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_CURSOR_HIGHLIGHT_DESCRIPTION},
+ {"focusHighlightLabel",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_FOCUS_HIGHLIGHT_DESCRIPTION},
+ {"selectToSpeakTitle",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_TITLE},
+ {"selectToSpeakDescription",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_DESCRIPTION},
+ {"selectToSpeakOptionsLabel",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_OPTIONS_LABEL},
+ {"switchAccessLabel",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SWITCH_ACCESS_DESCRIPTION},
+ {"switchAccessOptionsLabel",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_SWITCH_ACCESS_OPTIONS_LABEL},
+ {"manageAccessibilityFeatures",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_MANAGE_ACCESSIBILITY_FEATURES},
+ {"textToSpeechHeading",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_TEXT_TO_SPEECH_HEADING},
+ {"displayHeading", IDS_OPTIONS_SETTINGS_ACCESSIBILITY_DISPLAY_HEADING},
+ {"displaySettingsTitle",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_DISPLAY_SETTINGS_TITLE},
+ {"displaySettingsDescription",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_DISPLAY_SETTINGS_DESCRIPTION},
+ {"appearanceSettingsTitle",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_APPEARANCE_SETTINGS_TITLE},
+ {"appearanceSettingsDescription",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_APPEARANCE_SETTINGS_DESCRIPTION},
+ {"keyboardHeading", IDS_OPTIONS_SETTINGS_ACCESSIBILITY_KEYBOARD_HEADING},
+ {"keyboardSettingsTitle",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_KEYBOARD_SETTINGS_TITLE},
+ {"keyboardSettingsDescription",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_KEYBOARD_SETTINGS_DESCRIPTION},
+ {"mouseAndTouchpadHeading",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_MOUSE_AND_TOUCHPAD_HEADING},
+ {"mouseSettingsTitle",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_MOUSE_SETTINGS_TITLE},
+ {"mouseSettingsDescription",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_MOUSE_SETTINGS_DESCRIPTION},
+ {"audioHeading", IDS_OPTIONS_SETTINGS_ACCESSIBILITY_AUDIO_HEADING},
+ {"additionalFeaturesTitle",
+ IDS_OPTIONS_SETTINGS_ACCESSIBILITY_ADDITIONAL_FEATURES_TITLE},
+#endif
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+
+#if defined(OS_CHROMEOS)
+ html_source->AddString("a11yLearnMoreUrl",
+ chrome::kChromeAccessibilityHelpURL);
+
+ html_source->AddBoolean(
+ "showExperimentalA11yFeatures",
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kEnableExperimentalAccessibilityFeatures));
+#endif
+}
+
+void AddAboutStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"aboutProductLogoAlt", IDS_SHORT_PRODUCT_LOGO_ALT_TEXT},
+ {"aboutPageTitle", IDS_SETTINGS_ABOUT_PROGRAM},
+#if defined(OS_CHROMEOS)
+ {"aboutProductTitle", IDS_PRODUCT_OS_NAME},
+#else
+ {"aboutProductTitle", IDS_PRODUCT_NAME},
+#endif
+ {"aboutGetHelpUsingChrome", IDS_SETTINGS_GET_HELP_USING_CHROME},
+
+#if defined(GOOGLE_CHROME_BUILD)
+ {"aboutReportAnIssue", IDS_SETTINGS_ABOUT_PAGE_REPORT_AN_ISSUE},
+#endif
+
+ {"aboutRelaunch", IDS_SETTINGS_ABOUT_PAGE_RELAUNCH},
+ {"aboutUpgradeCheckStarted", IDS_SETTINGS_ABOUT_UPGRADE_CHECK_STARTED},
+ {"aboutUpgradeRelaunch", IDS_SETTINGS_UPGRADE_SUCCESSFUL_RELAUNCH},
+ {"aboutUpgradeUpdating", IDS_SETTINGS_UPGRADE_UPDATING},
+ {"aboutUpgradeUpdatingPercent", IDS_SETTINGS_UPGRADE_UPDATING_PERCENT},
+
+#if defined(OS_CHROMEOS)
+ {"aboutArcVersionLabel", IDS_SETTINGS_ABOUT_PAGE_ARC_VERSION},
+ {"aboutBuildDateLabel", IDS_VERSION_UI_BUILD_DATE},
+ {"aboutChannelBeta", IDS_SETTINGS_ABOUT_PAGE_CURRENT_CHANNEL_BETA},
+ {"aboutChannelDev", IDS_SETTINGS_ABOUT_PAGE_CURRENT_CHANNEL_DEV},
+ {"aboutChannelLabel", IDS_SETTINGS_ABOUT_PAGE_CHANNEL},
+ {"aboutChannelStable", IDS_SETTINGS_ABOUT_PAGE_CURRENT_CHANNEL_STABLE},
+ {"aboutCheckForUpdates", IDS_SETTINGS_ABOUT_PAGE_CHECK_FOR_UPDATES},
+ {"aboutCommandLineLabel", IDS_VERSION_UI_COMMAND_LINE},
+ {"aboutCurrentlyOnChannel", IDS_SETTINGS_ABOUT_PAGE_CURRENT_CHANNEL},
+ {"aboutDetailedBuildInfo", IDS_SETTINGS_ABOUT_PAGE_DETAILED_BUILD_INFO},
+ {"aboutFirmwareLabel", IDS_SETTINGS_ABOUT_PAGE_FIRMWARE},
+ {"aboutPlatformLabel", IDS_SETTINGS_ABOUT_PAGE_PLATFORM},
+ {"aboutRelaunchAndPowerwash",
+ IDS_SETTINGS_ABOUT_PAGE_RELAUNCH_AND_POWERWASH},
+ {"aboutUpgradeUpdatingChannelSwitch",
+ IDS_SETTINGS_UPGRADE_UPDATING_CHANNEL_SWITCH},
+ {"aboutUpgradeSuccessChannelSwitch",
+ IDS_SETTINGS_UPGRADE_SUCCESSFUL_CHANNEL_SWITCH},
+ {"aboutUserAgentLabel", IDS_VERSION_UI_USER_AGENT},
+
+ // About page, channel switcher dialog.
+ {"aboutChangeChannel", IDS_SETTINGS_ABOUT_PAGE_CHANGE_CHANNEL},
+ {"aboutChangeChannelAndPowerwash",
+ IDS_SETTINGS_ABOUT_PAGE_CHANGE_CHANNEL_AND_POWERWASH},
+ {"aboutDelayedWarningMessage",
+ IDS_SETTINGS_ABOUT_PAGE_DELAYED_WARNING_MESSAGE},
+ {"aboutDelayedWarningTitle", IDS_SETTINGS_ABOUT_PAGE_DELAYED_WARNING_TITLE},
+ {"aboutPowerwashWarningMessage",
+ IDS_SETTINGS_ABOUT_PAGE_POWERWASH_WARNING_MESSAGE},
+ {"aboutPowerwashWarningTitle",
+ IDS_SETTINGS_ABOUT_PAGE_POWERWASH_WARNING_TITLE},
+ {"aboutUnstableWarningMessage",
+ IDS_SETTINGS_ABOUT_PAGE_UNSTABLE_WARNING_MESSAGE},
+ {"aboutUnstableWarningTitle",
+ IDS_SETTINGS_ABOUT_PAGE_UNSTABLE_WARNING_TITLE},
+ {"aboutChannelDialogBeta", IDS_SETTINGS_ABOUT_PAGE_DIALOG_CHANNEL_BETA},
+ {"aboutChannelDialogDev", IDS_SETTINGS_ABOUT_PAGE_DIALOG_CHANNEL_DEV},
+ {"aboutChannelDialogStable", IDS_SETTINGS_ABOUT_PAGE_DIALOG_CHANNEL_STABLE},
+
+ // About page, update warning dialog.
+ {"aboutUpdateWarningMessage",
+ IDS_SETTINGS_ABOUT_PAGE_UPDATE_WARNING_MESSAGE},
+ {"aboutUpdateWarningTitle", IDS_SETTINGS_ABOUT_PAGE_UPDATE_WARNING_TITLE},
+ {"aboutUpdateWarningContinue",
+ IDS_SETTINGS_ABOUT_PAGE_UPDATE_WARNING_CONTINUE_BUTTON},
+#endif // defined(OS_CHROMEOS)
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+
+ html_source->AddString(
+ "aboutUpgradeUpToDate",
+#if defined(OS_CHROMEOS)
+ ash::SubstituteChromeOSDeviceType(IDS_SETTINGS_UPGRADE_UP_TO_DATE));
+#else
+ l10n_util::GetStringUTF16(IDS_SETTINGS_UPGRADE_UP_TO_DATE));
+#endif
+}
+
+#if defined(OS_CHROMEOS)
+void AddAccountUITweaksStrings(content::WebUIDataSource* html_source,
+ Profile* profile) {
+ base::DictionaryValue localized_values;
+ chromeos::AddAccountUITweaksLocalizedValues(&localized_values, profile);
+ html_source->AddLocalizedStrings(localized_values);
+}
+
+void AddAndroidAppStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"androidAppsPageTitle", IDS_SETTINGS_ANDROID_APPS_TITLE},
+ {"androidAppsPageLabel", IDS_SETTINGS_ANDROID_APPS_LABEL},
+ {"androidAppsEnable", IDS_SETTINGS_ANDROID_APPS_ENABLE},
+ {"androidAppsManageApps", IDS_SETTINGS_ANDROID_APPS_MANAGE_APPS},
+ {"androidAppsRemove", IDS_SETTINGS_ANDROID_APPS_REMOVE},
+ {"androidAppsLearnMore", IDS_SETTINGS_ANDROID_APPS_LEARN_MORE},
+ {"androidAppsDisableDialogTitle",
+ IDS_SETTINGS_ANDROID_APPS_DISABLE_DIALOG_TITLE},
+ {"androidAppsDisableDialogMessage",
+ IDS_SETTINGS_ANDROID_APPS_DISABLE_DIALOG_MESSAGE},
+ {"androidAppsDisableDialogRemove",
+ IDS_SETTINGS_ANDROID_APPS_DISABLE_DIALOG_REMOVE},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+ html_source->AddString(
+ "androidAppsSubtext",
+ l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_ANDROID_APPS_SUBTEXT,
+ base::ASCIIToUTF16(chrome::kAndroidAppsLearnMoreURL)));
+}
+#endif
+
+void AddAppearanceStrings(content::WebUIDataSource* html_source,
+ Profile* profile) {
+ LocalizedString localized_strings[] = {
+ {"appearancePageTitle", IDS_SETTINGS_APPEARANCE},
+ {"customWebAddress", IDS_SETTINGS_CUSTOM_WEB_ADDRESS},
+ {"enterCustomWebAddress", IDS_SETTINGS_ENTER_CUSTOM_WEB_ADDRESS},
+ {"homeButtonDisabled", IDS_SETTINGS_HOME_BUTTON_DISABLED},
+ {"themes", IDS_SETTINGS_THEMES},
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+ {"systemTheme", IDS_SETTINGS_SYSTEM_THEME},
+ {"useSystemTheme", IDS_SETTINGS_USE_SYSTEM_THEME},
+ {"classicTheme", IDS_SETTINGS_CLASSIC_THEME},
+ {"useClassicTheme", IDS_SETTINGS_USE_CLASSIC_THEME},
+#else
+ {"resetToDefaultTheme", IDS_SETTINGS_RESET_TO_DEFAULT_THEME},
+#endif
+ {"showHomeButton", IDS_SETTINGS_SHOW_HOME_BUTTON},
+ {"showBookmarksBar", IDS_SETTINGS_SHOW_BOOKMARKS_BAR},
+ {"homePageNtp", IDS_SETTINGS_HOME_PAGE_NTP},
+ {"changeHomePage", IDS_SETTINGS_CHANGE_HOME_PAGE},
+ {"themesGalleryUrl", IDS_THEMES_GALLERY_URL},
+ {"chooseFromWebStore", IDS_SETTINGS_WEB_STORE},
+#if defined(OS_CHROMEOS)
+ {"openWallpaperApp", IDS_SETTINGS_OPEN_WALLPAPER_APP},
+ {"setWallpaper", IDS_SETTINGS_SET_WALLPAPER},
+#endif
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+ {"showWindowDecorations", IDS_SHOW_WINDOW_DECORATIONS},
+#endif
+#if defined(OS_MACOSX)
+ // TODO(dbeam): use an IDS_SETTINGS_* string instead.
+ {"tabsToLinks", IDS_OPTIONS_TABS_TO_LINKS_PREF},
+#endif
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+}
+
+#if defined(OS_CHROMEOS)
+void AddBluetoothStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"bluetoothAccept", IDS_OPTIONS_SETTINGS_BLUETOOTH_ACCEPT_PASSKEY},
+ {"bluetoothConnected", IDS_SETTINGS_BLUETOOTH_CONNECTED},
+ {"bluetoothConnecting", IDS_SETTINGS_BLUETOOTH_CONNECTING},
+ {"bluetoothDeviceListPaired", IDS_SETTINGS_BLUETOOTH_DEVICE_LIST_PAIRED},
+ {"bluetoothDeviceListUnpaired",
+ IDS_SETTINGS_BLUETOOTH_DEVICE_LIST_UNPAIRED},
+ {"bluetoothConnect", IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT},
+ {"bluetoothDisconnect", IDS_OPTIONS_SETTINGS_BLUETOOTH_DISCONNECT},
+ {"bluetoothDismiss", IDS_OPTIONS_SETTINGS_BLUETOOTH_DISMISS_ERROR},
+ {"bluetoothToggleA11yLabel",
+ IDS_SETTINGS_BLUETOOTH_TOGGLE_ACCESSIBILITY_LABEL},
+ {"bluetoothExpandA11yLabel",
+ IDS_SETTINGS_BLUETOOTH_EXPAND_ACCESSIBILITY_LABEL},
+ {"bluetoothNoDevices", IDS_SETTINGS_BLUETOOTH_NO_DEVICES},
+ {"bluetoothNoDevicesFound", IDS_SETTINGS_BLUETOOTH_NO_DEVICES_FOUND},
+ {"bluetoothNotConnected", IDS_SETTINGS_BLUETOOTH_NOT_CONNECTED},
+ {"bluetoothPageTitle", IDS_SETTINGS_BLUETOOTH},
+ {"bluetoothPair", IDS_SETTINGS_BLUETOOTH_PAIR},
+ {"bluetoothPairDevicePageTitle",
+ IDS_SETTINGS_BLUETOOTH_PAIR_DEVICE_TITLE},
+ {"bluetoothReject", IDS_OPTIONS_SETTINGS_BLUETOOTH_REJECT_PASSKEY},
+ {"bluetoothRemove", IDS_SETTINGS_BLUETOOTH_REMOVE},
+ // Device connecting and pairing.
+ {"bluetoothStartConnecting", IDS_SETTINGS_BLUETOOTH_START_CONNECTING},
+ {"bluetoothEnterKey", IDS_OPTIONS_SETTINGS_BLUETOOTH_ENTER_KEY},
+ // These ids are generated in JS using 'bluetooth_' + a value from
+ // bluetoothPrivate.PairingEventType (see bluetooth_private.idl).
+ // 'keysEntered', and 'requestAuthorization' have no associated message.
+ {"bluetooth_requestPincode", IDS_SETTINGS_BLUETOOTH_REQUEST_PINCODE},
+ {"bluetooth_displayPincode", IDS_SETTINGS_BLUETOOTH_DISPLAY_PINCODE},
+ {"bluetooth_requestPasskey", IDS_SETTINGS_BLUETOOTH_REQUEST_PASSKEY},
+ {"bluetooth_displayPasskey", IDS_SETTINGS_BLUETOOTH_DISPLAY_PASSKEY},
+ {"bluetooth_confirmPasskey", IDS_SETTINGS_BLUETOOTH_CONFIRM_PASSKEY},
+ // These ids are generated in JS using 'bluetooth_connect_' + a value from
+ // bluetoothPrivate.ConnectResultType (see bluetooth_private.idl).
+ {"bluetooth_connect_attributeLengthInvalid",
+ IDS_SETTINGS_BLUETOOTH_CONNECT_ATTRIBUTE_LENGTH_INVALID},
+ {"bluetooth_connect_authCanceled",
+ IDS_SETTINGS_BLUETOOTH_CONNECT_AUTH_CANCELED},
+ {"bluetooth_connect_authFailed",
+ IDS_SETTINGS_BLUETOOTH_CONNECT_AUTH_FAILED},
+ {"bluetooth_connect_authRejected",
+ IDS_SETTINGS_BLUETOOTH_CONNECT_AUTH_REJECTED},
+ {"bluetooth_connect_authTimeout",
+ IDS_SETTINGS_BLUETOOTH_CONNECT_AUTH_TIMEOUT},
+ {"bluetooth_connect_connectionCongested",
+ IDS_SETTINGS_BLUETOOTH_CONNECT_CONNECTION_CONGESTED},
+ {"bluetooth_connect_failed", IDS_SETTINGS_BLUETOOTH_CONNECT_FAILED},
+ {"bluetooth_connect_inProgress",
+ IDS_SETTINGS_BLUETOOTH_CONNECT_IN_PROGRESS},
+ {"bluetooth_connect_insufficientEncryption",
+ IDS_SETTINGS_BLUETOOTH_CONNECT_INSUFFICIENT_ENCRYPTION},
+ {"bluetooth_connect_offsetInvalid",
+ IDS_SETTINGS_BLUETOOTH_CONNECT_OFFSET_INVALID},
+ {"bluetooth_connect_readNotPermitted",
+ IDS_SETTINGS_BLUETOOTH_CONNECT_READ_NOT_PERMITTED},
+ {"bluetooth_connect_requestNotSupported",
+ IDS_SETTINGS_BLUETOOTH_CONNECT_REQUEST_NOT_SUPPORTED},
+ {"bluetooth_connect_unsupportedDevice",
+ IDS_SETTINGS_BLUETOOTH_CONNECT_UNSUPPORTED_DEVICE},
+ {"bluetooth_connect_writeNotPermitted",
+ IDS_SETTINGS_BLUETOOTH_CONNECT_WRITE_NOT_PERMITTED},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+}
+#endif
+
+#if defined(USE_NSS_CERTS)
+void AddCertificateManagerStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"certificateManagerPageTitle", IDS_SETTINGS_CERTIFICATE_MANAGER},
+ {"certificateManagerExpandA11yLabel",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_EXPAND_ACCESSIBILITY_LABEL},
+ {"certificateManagerNoCertificates",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_NO_CERTIFICATES},
+ {"certificateManagerYourCertificates",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_YOUR_CERTIFICATES},
+ {"certificateManagerYourCertificatesDescription",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_YOUR_CERTIFICATES_DESCRIPTION},
+ {"certificateManagerServers", IDS_SETTINGS_CERTIFICATE_MANAGER_SERVERS},
+ {"certificateManagerServersDescription",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_SERVERS_DESCRIPTION},
+ {"certificateManagerAuthorities",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_AUTHORITIES},
+ {"certificateManagerAuthoritiesDescription",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_AUTHORITIES_DESCRIPTION},
+ {"certificateManagerOthers", IDS_SETTINGS_CERTIFICATE_MANAGER_OTHERS},
+ {"certificateManagerOthersDescription",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_OTHERS_DESCRIPTION},
+ {"certificateManagerView", IDS_SETTINGS_CERTIFICATE_MANAGER_VIEW},
+ {"certificateManagerImport", IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT},
+ {"certificateManagerImportAndBind",
+ 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.
+ {"certificateManagerCaTrustEditDialogTitle",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_TITLE},
+ {"certificateManagerCaTrustEditDialogDescription",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_DESCRIPTION},
+ {"certificateManagerCaTrustEditDialogExplanation",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_EXPLANATION},
+ {"certificateManagerCaTrustEditDialogSsl",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_SSL},
+ {"certificateManagerCaTrustEditDialogEmail",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_EMAIL},
+ {"certificateManagerCaTrustEditDialogObjSign",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_OBJ_SIGN},
+ // Certificate delete confirmation dialog.
+ {"certificateManagerDeleteUserTitle",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_USER_TITLE},
+ {"certificateManagerDeleteUserDescription",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_USER_DESCRIPTION},
+ {"certificateManagerDeleteServerTitle",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_SERVER_TITLE},
+ {"certificateManagerDeleteServerDescription",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_SERVER_DESCRIPTION},
+ {"certificateManagerDeleteCaTitle",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_CA_TITLE},
+ {"certificateManagerDeleteCaDescription",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_CA_DESCRIPTION},
+ {"certificateManagerDeleteOtherTitle",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_OTHER_TITLE},
+ // Encrypt/decrypt password dialogs.
+ {"certificateManagerEncryptPasswordTitle",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_ENCRYPT_PASSWORD_TITLE},
+ {"certificateManagerDecryptPasswordTitle",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_DECRYPT_PASSWORD_TITLE},
+ {"certificateManagerEncryptPasswordDescription",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_ENCRYPT_PASSWORD_DESCRIPTION},
+ {"certificateManagerPassword", IDS_SETTINGS_CERTIFICATE_MANAGER_PASSWORD},
+ {"certificateManagerConfirmPassword",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_CONFIRM_PASSWORD},
+ {"certificateImportErrorFormat",
+ IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_ERROR_FORMAT},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+}
+#endif
+
+void AddClearBrowsingDataStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"clearFollowingItemsFrom", IDS_SETTINGS_CLEAR_FOLLOWING_ITEMS_FROM},
+ {"clearBrowsingHistory", IDS_SETTINGS_CLEAR_BROWSING_HISTORY},
+ {"clearDownloadHistory", IDS_SETTINGS_CLEAR_DOWNLOAD_HISTORY},
+ {"clearCache", IDS_SETTINGS_CLEAR_CACHE},
+ {"clearCookies", IDS_SETTINGS_CLEAR_COOKIES},
+ {"clearCookiesCounter", IDS_DEL_COOKIES_COUNTER},
+ {"clearCookiesFlash", IDS_SETTINGS_CLEAR_COOKIES_FLASH},
+ {"clearPasswords", IDS_SETTINGS_CLEAR_PASSWORDS},
+ {"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},
+ {"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},
+ {"importantSitesSubtitle", IDS_SETTINGS_IMPORTANT_SITES_SUBTITLE},
+ {"importantSitesConfirm", IDS_SETTINGS_IMPORTANT_SITES_CONFIRM},
+ {"notificationWarning", IDS_SETTINGS_NOTIFICATION_WARNING},
+ };
+
+ 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,
+ l10n_util::GetStringUTF16(
+ IDS_SETTINGS_CLEAR_DATA_WEB_HISTORY_URL_IN_DIALOG)));
+
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+}
+
+#if !defined(OS_CHROMEOS)
+void AddDefaultBrowserStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"defaultBrowser", IDS_SETTINGS_DEFAULT_BROWSER},
+ {"defaultBrowserDefault", IDS_SETTINGS_DEFAULT_BROWSER_DEFAULT},
+ {"defaultBrowserMakeDefault", IDS_SETTINGS_DEFAULT_BROWSER_MAKE_DEFAULT},
+ {"defaultBrowserMakeDefaultButton",
+ IDS_SETTINGS_DEFAULT_BROWSER_MAKE_DEFAULT_BUTTON},
+ {"defaultBrowserError", IDS_SETTINGS_DEFAULT_BROWSER_ERROR},
+ {"defaultBrowserSecondary", IDS_SETTINGS_DEFAULT_BROWSER_SECONDARY},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+}
+#endif
+
+#if defined(OS_CHROMEOS)
+void AddDeviceStrings(content::WebUIDataSource* html_source) {
+ LocalizedString device_strings[] = {
+ {"devicePageTitle", IDS_SETTINGS_DEVICE_TITLE},
+ {"scrollLabel", IDS_SETTINGS_SCROLL_LABEL},
+ {"traditionalScrollLabel", IDS_SETTINGS_TRADITIONAL_SCROLL_LABEL},
+ {"naturalScrollLabel", IDS_SETTINGS_NATURAL_SCROLL_LABEL},
+ {"naturalScrollLearnMore", IDS_SETTINGS_NATURAL_SCROLL_LEARN_MORE},
+ };
+ AddLocalizedStringsBulk(html_source, device_strings,
+ arraysize(device_strings));
+
+ LocalizedString pointers_strings[] = {
+ {"mouseTitle", IDS_SETTINGS_MOUSE_TITLE},
+ {"touchpadTitle", IDS_SETTINGS_TOUCHPAD_TITLE},
+ {"mouseAndTouchpadTitle", IDS_SETTINGS_MOUSE_AND_TOUCHPAD_TITLE},
+ {"touchpadTapToClickEnabledLabel",
+ IDS_SETTINGS_TOUCHPAD_TAP_TO_CLICK_ENABLED_LABEL},
+ {"touchpadSpeed", IDS_SETTINGS_TOUCHPAD_SPEED_LABEL},
+ {"pointerSlow", IDS_SETTINGS_POINTER_SPEED_SLOW_LABEL},
+ {"pointerFast", IDS_SETTINGS_POINTER_SPEED_FAST_LABEL},
+ {"mouseSpeed", IDS_SETTINGS_MOUSE_SPEED_LABEL},
+ {"mouseSwapButtons", IDS_SETTINGS_MOUSE_SWAP_BUTTONS_LABEL},
+ };
+ AddLocalizedStringsBulk(html_source, pointers_strings,
+ arraysize(pointers_strings));
+
+ LocalizedString keyboard_strings[] = {
+ {"keyboardTitle", IDS_SETTINGS_KEYBOARD_TITLE},
+ {"keyboardKeySearch", IDS_SETTINGS_KEYBOARD_KEY_SEARCH},
+ {"keyboardKeyCtrl", IDS_SETTINGS_KEYBOARD_KEY_LEFT_CTRL},
+ {"keyboardKeyAlt", IDS_SETTINGS_KEYBOARD_KEY_LEFT_ALT},
+ {"keyboardKeyCapsLock", IDS_SETTINGS_KEYBOARD_KEY_CAPS_LOCK},
+ {"keyboardKeyDiamond", IDS_SETTINGS_KEYBOARD_KEY_DIAMOND},
+ {"keyboardKeyEscape", IDS_SETTINGS_KEYBOARD_KEY_ESCAPE},
+ {"keyboardKeyBackspace", IDS_SETTINGS_KEYBOARD_KEY_BACKSPACE},
+ {"keyboardKeyDisabled", IDS_SETTINGS_KEYBOARD_KEY_DISABLED},
+ {"keyboardSendFunctionKeys", IDS_SETTINGS_KEYBOARD_SEND_FUNCTION_KEYS},
+ {"keyboardSendFunctionKeysDescription",
+ IDS_SETTINGS_KEYBOARD_SEND_FUNCTION_KEYS_DESCRIPTION},
+ {"keyboardEnableAutoRepeat", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_ENABLE},
+ {"keyRepeatDelay", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_DELAY},
+ {"keyRepeatDelayLong", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_DELAY_LONG},
+ {"keyRepeatDelayShort", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_DELAY_SHORT},
+ {"keyRepeatRate", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_RATE},
+ {"keyRepeatRateSlow", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_RATE_SLOW},
+ {"keyRepeatRateFast", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_FAST},
+ {"showKeyboardShortcutsOverlay",
+ IDS_SETTINGS_KEYBOARD_SHOW_KEYBOARD_SHORTCUTS_OVERLAY},
+ {"keyboardShowLanguageAndInput",
+ IDS_SETTINGS_KEYBOARD_SHOW_LANGUAGE_AND_INPUT},
+ };
+ AddLocalizedStringsBulk(html_source, keyboard_strings,
+ arraysize(keyboard_strings));
+
+ LocalizedString stylus_strings[] = {
+ {"stylusTitle", IDS_SETTINGS_STYLUS_TITLE},
+ {"stylusEnableStylusTools", IDS_SETTINGS_STYLUS_ENABLE_STYLUS_TOOLS},
+ {"stylusAutoOpenStylusTools", IDS_SETTINGS_STYLUS_AUTO_OPEN_STYLUS_TOOLS},
+ {"stylusFindMoreAppsPrimary", IDS_SETTINGS_STYLUS_FIND_MORE_APPS_PRIMARY},
+ {"stylusFindMoreAppsSecondary",
+ IDS_SETTINGS_STYLUS_FIND_MORE_APPS_SECONDARY},
+ {"stylusNoteTakingApp", IDS_SETTINGS_STYLUS_NOTE_TAKING_APP_LABEL},
+ {"stylusNoteTakingAppEnabledOnLockScreen",
+ IDS_SETTINGS_STYLUS_NOTE_TAKING_APP_LOCK_SCREEN_CHECKBOX},
+ {"stylusNoteTakingAppNoneAvailable",
+ IDS_SETTINGS_STYLUS_NOTE_TAKING_APP_NONE_AVAILABLE},
+ {"stylusNoteTakingAppWaitingForAndroid",
+ IDS_SETTINGS_STYLUS_NOTE_TAKING_APP_WAITING_FOR_ANDROID}};
+ AddLocalizedStringsBulk(html_source, stylus_strings,
+ arraysize(stylus_strings));
+
+ LocalizedString display_strings[] = {
+ {"displayTitle", IDS_SETTINGS_DISPLAY_TITLE},
+ {"displayArrangementText", IDS_SETTINGS_DISPLAY_ARRANGEMENT_TEXT},
+ {"displayArrangementTitle", IDS_SETTINGS_DISPLAY_ARRANGEMENT_TITLE},
+ {"displayMirror", IDS_SETTINGS_DISPLAY_MIRROR},
+ {"displayNightLightLabel", IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_LABEL},
+ {"displayNightLightScheduleCustom",
+ IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_SCHEDULE_CUSTOM},
+ {"displayNightLightScheduleLabel",
+ IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_SCHEDULE_LABEL},
+ {"displayNightLightScheduleNever",
+ IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_SCHEDULE_NEVER},
+ {"displayNightLightScheduleSunsetToSunRise",
+ IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_SCHEDULE_SUNSET_TO_SUNRISE},
+ {"displayNightLightText", IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_TEXT},
+ {"displayNightLightTemperatureLabel",
+ IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_TEMPERATURE_LABEL},
+ {"displayNightLightTempSliderMaxLabel",
+ IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_TEMP_SLIDER_MAX_LABEL},
+ {"displayNightLightTempSliderMinLabel",
+ IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_TEMP_SLIDER_MIN_LABEL},
+ {"displayUnfiedDesktop", IDS_SETTINGS_DISPLAY_UNIFIED_DESKTOP},
+ {"displayResolutionTitle", IDS_SETTINGS_DISPLAY_RESOLUTION_TITLE},
+ {"displayResolutionText", IDS_SETTINGS_DISPLAY_RESOLUTION_TEXT},
+ {"displayResolutionTextBest", IDS_SETTINGS_DISPLAY_RESOLUTION_TEXT_BEST},
+ {"displayResolutionTextNative",
+ IDS_SETTINGS_DISPLAY_RESOLUTION_TEXT_NATIVE},
+ {"displayScreenTitle", IDS_SETTINGS_DISPLAY_SCREEN},
+ {"displayScreenExtended", IDS_SETTINGS_DISPLAY_SCREEN_EXTENDED},
+ {"displayScreenPrimary", IDS_SETTINGS_DISPLAY_SCREEN_PRIMARY},
+ {"displayOrientation", IDS_SETTINGS_DISPLAY_ORIENTATION},
+ {"displayOrientationStandard", IDS_SETTINGS_DISPLAY_ORIENTATION_STANDARD},
+ {"displayOverscanPageText", IDS_SETTINGS_DISPLAY_OVERSCAN_TEXT},
+ {"displayOverscanPageTitle", IDS_SETTINGS_DISPLAY_OVERSCAN_TITLE},
+ {"displayOverscanSubtitle", IDS_SETTINGS_DISPLAY_OVERSCAN_SUBTITLE},
+ {"displayOverscanInstructions",
+ IDS_SETTINGS_DISPLAY_OVERSCAN_INSTRUCTIONS},
+ {"displayOverscanResize", IDS_SETTINGS_DISPLAY_OVERSCAN_RESIZE},
+ {"displayOverscanPosition", IDS_SETTINGS_DISPLAY_OVERSCAN_POSITION},
+ {"displayOverscanReset", IDS_SETTINGS_DISPLAY_OVERSCAN_RESET},
+ {"displayTouchCalibrationTitle",
+ IDS_SETTINGS_DISPLAY_TOUCH_CALIBRATION_TITLE},
+ {"displayTouchCalibrationText",
+ IDS_SETTINGS_DISPLAY_TOUCH_CALIBRATION_TEXT}};
+ AddLocalizedStringsBulk(html_source, display_strings,
+ arraysize(display_strings));
+ html_source->AddBoolean("unifiedDesktopAvailable",
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ ::switches::kEnableUnifiedDesktop));
+
+ html_source->AddBoolean(
+ "enableTouchCalibrationSetting",
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kEnableTouchCalibrationSetting));
+
+ html_source->AddBoolean("nightLightFeatureEnabled",
+ ash::NightLightController::IsFeatureEnabled());
+
+ LocalizedString storage_strings[] = {
+ {"storageTitle", IDS_SETTINGS_STORAGE_TITLE},
+ {"storageItemInUse", IDS_SETTINGS_STORAGE_ITEM_IN_USE},
+ {"storageItemAvailable", IDS_SETTINGS_STORAGE_ITEM_AVAILABLE},
+ {"storageItemDownloads", IDS_SETTINGS_STORAGE_ITEM_DOWNLOADS},
+ {"storageItemDriveCache", IDS_SETTINGS_STORAGE_ITEM_DRIVE_CACHE},
+ {"storageItemBrowsingData", IDS_SETTINGS_STORAGE_ITEM_BROWSING_DATA},
+ {"storageItemAndroid", IDS_SETTINGS_STORAGE_ITEM_ANDROID},
+ {"storageItemOtherUsers", IDS_SETTINGS_STORAGE_ITEM_OTHER_USERS},
+ {"storageSizeComputing", IDS_SETTINGS_STORAGE_SIZE_CALCULATING},
+ {"storageSizeUnknown", IDS_SETTINGS_STORAGE_SIZE_UNKNOWN},
+ {"storageSpaceLowMessageTitle",
+ IDS_SETTINGS_STORAGE_SPACE_LOW_MESSAGE_TITLE},
+ {"storageSpaceLowMessageLine1",
+ IDS_SETTINGS_STORAGE_SPACE_LOW_MESSAGE_LINE_1},
+ {"storageSpaceLowMessageLine2",
+ IDS_SETTINGS_STORAGE_SPACE_LOW_MESSAGE_LINE_2},
+ {"storageSpaceCriticallyLowMessageTitle",
+ IDS_SETTINGS_STORAGE_SPACE_CRITICALLY_LOW_MESSAGE_TITLE},
+ {"storageSpaceCriticallyLowMessageLine1",
+ IDS_SETTINGS_STORAGE_SPACE_CRITICALLY_LOW_MESSAGE_LINE_1},
+ {"storageSpaceCriticallyLowMessageLine2",
+ IDS_SETTINGS_STORAGE_SPACE_CRITICALLY_LOW_MESSAGE_LINE_2},
+ {"storageClearDriveCacheDialogTitle",
+ IDS_SETTINGS_STORAGE_CLEAR_DRIVE_CACHE_DIALOG_TITLE},
+ {"storageClearDriveCacheDialogDescription",
+ IDS_SETTINGS_STORAGE_CLEAR_DRIVE_CACHE_DESCRIPTION},
+ {"storageDeleteAllButtonTitle",
+ IDS_SETTINGS_STORAGE_DELETE_ALL_BUTTON_TITLE}};
+ AddLocalizedStringsBulk(html_source, storage_strings,
+ arraysize(storage_strings));
+
+ LocalizedString power_strings[] = {
+ {"powerTitle", IDS_SETTINGS_POWER_TITLE},
+ {"powerSourceLabel", IDS_SETTINGS_POWER_SOURCE_LABEL},
+ {"powerSourceBattery", IDS_SETTINGS_POWER_SOURCE_BATTERY},
+ {"powerSourceAcAdapter", IDS_SETTINGS_POWER_SOURCE_AC_ADAPTER},
+ {"powerSourceLowPowerCharger",
+ IDS_SETTINGS_POWER_SOURCE_LOW_POWER_CHARGER},
+ {"calculatingPower", IDS_SETTINGS_POWER_SOURCE_CALCULATING}};
+ AddLocalizedStringsBulk(html_source, power_strings, arraysize(power_strings));
+
+ html_source->AddString("naturalScrollLearnMoreLink",
+ base::ASCIIToUTF16(chrome::kNaturalScrollHelpURL));
+}
+#endif
+
+void AddDownloadsStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"downloadsPageTitle", IDS_SETTINGS_DOWNLOADS},
+ {"downloadLocation", IDS_SETTINGS_DOWNLOAD_LOCATION},
+ {"changeDownloadLocation", IDS_SETTINGS_CHANGE_DOWNLOAD_LOCATION},
+ {"promptForDownload", IDS_SETTINGS_PROMPT_FOR_DOWNLOAD},
+ {"disconnectGoogleDriveAccount", IDS_SETTINGS_DISCONNECT_GOOGLE_DRIVE},
+ {"openFileTypesAutomatically",
+ IDS_SETTINGS_OPEN_FILE_TYPES_AUTOMATICALLY}};
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+}
+
+void AddResetStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"resetPageTitle", IDS_SETTINGS_RESET},
+ {"resetPageDescription", IDS_SETTINGS_RESET_PROFILE_SETTINGS_DESCRIPTION},
+ {"resetPageExplanation", IDS_RESET_PROFILE_SETTINGS_EXPLANATION},
+ {"triggeredResetPageExplanation",
+ IDS_TRIGGERED_RESET_PROFILE_SETTINGS_EXPLANATION},
+ {"triggeredResetPageTitle", IDS_TRIGGERED_RESET_PROFILE_SETTINGS_TITLE},
+ {"resetPageCommit", IDS_RESET_PROFILE_SETTINGS_COMMIT_BUTTON},
+ {"resetPageFeedback", IDS_SETTINGS_RESET_PROFILE_FEEDBACK},
+#if defined(OS_CHROMEOS)
+ {"powerwashTitle", IDS_OPTIONS_FACTORY_RESET},
+ {"powerwashDialogTitle", IDS_OPTIONS_FACTORY_RESET_HEADING},
+ {"powerwashDialogExplanation", IDS_OPTIONS_FACTORY_RESET_WARNING},
+ {"powerwashDialogButton", IDS_SETTINGS_RESTART},
+ {"powerwashLearnMoreUrl", IDS_FACTORY_RESET_HELP_URL},
+#endif
+ // Automatic reset banner (now a dialog).
+ {"resetAutomatedDialogTitle", IDS_SETTINGS_RESET_AUTOMATED_DIALOG_TITLE},
+ {"resetProfileBannerButton",
+ IDS_AUTOMATIC_SETTINGS_RESET_BANNER_RESET_BUTTON_TEXT},
+ {"resetProfileBannerDescription", IDS_AUTOMATIC_SETTINGS_RESET_BANNER_TEXT},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+
+ html_source->AddString("resetPageLearnMoreUrl",
+ chrome::kResetProfileSettingsLearnMoreURL);
+ html_source->AddString("resetProfileBannerLearnMoreUrl",
+ chrome::kAutomaticSettingsResetLearnMoreURL);
+#if defined(OS_CHROMEOS)
+ html_source->AddString(
+ "powerwashDescription",
+ l10n_util::GetStringFUTF16(IDS_OPTIONS_FACTORY_RESET_DESCRIPTION,
+ l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
+#endif
+}
+
+#if !defined(OS_CHROMEOS)
+void AddImportDataStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"importTitle", IDS_SETTINGS_IMPORT_SETTINGS_TITLE},
+ {"importFromLabel", IDS_SETTINGS_IMPORT_FROM_LABEL},
+ {"importDescription", IDS_SETTINGS_IMPORT_ITEMS_LABEL},
+ {"importLoading", IDS_SETTINGS_IMPORT_LOADING_PROFILES},
+ {"importHistory", IDS_SETTINGS_IMPORT_HISTORY_CHECKBOX},
+ {"importFavorites", IDS_SETTINGS_IMPORT_FAVORITES_CHECKBOX},
+ {"importPasswords", IDS_SETTINGS_IMPORT_PASSWORDS_CHECKBOX},
+ {"importSearch", IDS_SETTINGS_IMPORT_SEARCH_ENGINES_CHECKBOX},
+ {"importAutofillFormData", IDS_SETTINGS_IMPORT_AUTOFILL_FORM_DATA_CHECKBOX},
+ {"importChooseFile", IDS_SETTINGS_IMPORT_CHOOSE_FILE},
+ {"importCommit", IDS_SETTINGS_IMPORT_COMMIT},
+ {"noProfileFound", IDS_SETTINGS_IMPORT_NO_PROFILE_FOUND},
+ {"importSuccess", IDS_SETTINGS_IMPORT_SUCCESS},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+}
+#endif
+
+#if defined(OS_CHROMEOS)
+void AddDateTimeStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"dateTimePageTitle", IDS_SETTINGS_DATE_TIME},
+ {"timeZone", IDS_SETTINGS_TIME_ZONE},
+ {"timeZoneGeolocation", IDS_SETTINGS_TIME_ZONE_GEOLOCATION},
+ {"use24HourClock", IDS_SETTINGS_USE_24_HOUR_CLOCK},
+ {"setDateTime", IDS_SETTINGS_SET_DATE_TIME},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+}
+
+void AddEasyUnlockStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"easyUnlockSectionTitle", IDS_SETTINGS_EASY_UNLOCK_SECTION_TITLE},
+ {"easyUnlockSetupButton", IDS_SETTINGS_EASY_UNLOCK_SETUP},
+ // Easy Unlock turn-off dialog.
+ {"easyUnlockTurnOffButton", IDS_SETTINGS_EASY_UNLOCK_TURN_OFF},
+ {"easyUnlockTurnOffOfflineTitle",
+ IDS_SETTINGS_EASY_UNLOCK_TURN_OFF_OFFLINE_TITLE},
+ {"easyUnlockTurnOffOfflineMessage",
+ IDS_SETTINGS_EASY_UNLOCK_TURN_OFF_OFFLINE_MESSAGE},
+ {"easyUnlockTurnOffErrorTitle",
+ IDS_SETTINGS_EASY_UNLOCK_TURN_OFF_ERROR_TITLE},
+ {"easyUnlockTurnOffErrorMessage",
+ IDS_SETTINGS_EASY_UNLOCK_TURN_OFF_ERROR_MESSAGE},
+ {"easyUnlockTurnOffRetryButton", IDS_SETTINGS_EASY_UNLOCK_TURN_OFF_RETRY},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+
+ base::string16 device_name =
+ l10n_util::GetStringUTF16(ash::GetChromeOSDeviceTypeResourceId());
+ html_source->AddString(
+ "easyUnlockSetupIntro",
+ l10n_util::GetStringFUTF16(IDS_SETTINGS_EASY_UNLOCK_SETUP_INTRO,
+ device_name));
+ html_source->AddString(
+ "easyUnlockDescription",
+ l10n_util::GetStringFUTF16(IDS_SETTINGS_EASY_UNLOCK_DESCRIPTION,
+ device_name));
+ html_source->AddString(
+ "easyUnlockTurnOffTitle",
+ l10n_util::GetStringFUTF16(IDS_SETTINGS_EASY_UNLOCK_TURN_OFF_TITLE,
+ device_name));
+ html_source->AddString(
+ "easyUnlockTurnOffDescription",
+ l10n_util::GetStringFUTF16(IDS_SETTINGS_EASY_UNLOCK_TURN_OFF_DESCRIPTION,
+ device_name));
+ html_source->AddString(
+ "easyUnlockRequireProximityLabel",
+ l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_EASY_UNLOCK_REQUIRE_PROXIMITY_LABEL, device_name));
+
+ html_source->AddString("easyUnlockLearnMoreURL",
+ chrome::kEasyUnlockLearnMoreUrl);
+}
+
+void AddInternetStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"internetAddConnection", IDS_SETTINGS_INTERNET_ADD_CONNECTION},
+ {"internetAddConnectionExpandA11yLabel",
+ IDS_SETTINGS_INTERNET_ADD_CONNECTION_EXPAND_ACCESSIBILITY_LABEL},
+ {"internetAddConnectionNotAllowed",
+ IDS_SETTINGS_INTERNET_ADD_CONNECTION_NOT_ALLOWED},
+ {"internetAddThirdPartyVPN", IDS_SETTINGS_INTERNET_ADD_THIRD_PARTY_VPN},
+ {"internetAddVPN", IDS_SETTINGS_INTERNET_ADD_VPN},
+ {"internetAddWiFi", IDS_SETTINGS_INTERNET_ADD_WIFI},
+ {"internetConfigTitle", IDS_SETTINGS_INTERNET_CONFIG},
+ {"internetDetailPageTitle", IDS_SETTINGS_INTERNET_DETAIL},
+ {"internetDeviceEnabling", IDS_SETTINGS_INTERNET_DEVICE_ENABLING},
+ {"internetKnownNetworksPageTitle", IDS_SETTINGS_INTERNET_KNOWN_NETWORKS},
+ {"internetMobileSearching", IDS_SETTINGS_INTERNET_MOBILE_SEARCH},
+ {"internetNoNetworks", IDS_SETTINGS_INTERNET_NO_NETWORKS},
+ {"internetPageTitle", IDS_SETTINGS_INTERNET},
+ {"internetToggleMobileA11yLabel",
+ IDS_SETTINGS_INTERNET_TOGGLE_MOBILE_ACCESSIBILITY_LABEL},
+ {"internetToggleTetherA11yLabel",
+ IDS_SETTINGS_INTERNET_TOGGLE_TETHER_ACCESSIBILITY_LABEL},
+ {"internetToggleWiFiA11yLabel",
+ IDS_SETTINGS_INTERNET_TOGGLE_WIFI_ACCESSIBILITY_LABEL},
+ {"internetToggleWiMAXA11yLabel",
+ IDS_SETTINGS_INTERNET_TOGGLE_WIMAX_ACCESSIBILITY_LABEL},
+ {"knownNetworksAll", IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_ALL},
+ {"knownNetworksButton", IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_BUTTON},
+ {"knownNetworksMessage", IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_MESSAGE},
+ {"knownNetworksPreferred",
+ IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_PREFFERED},
+ {"knownNetworksMenuAddPreferred",
+ IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_MENU_ADD_PREFERRED},
+ {"knownNetworksMenuRemovePreferred",
+ IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_MENU_REMOVE_PREFERRED},
+ {"knownNetworksMenuForget",
+ IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_MENU_FORGET},
+ {"networkAllowDataRoaming",
+ IDS_SETTINGS_SETTINGS_NETWORK_ALLOW_DATA_ROAMING},
+ {"networkAutoConnect", IDS_SETTINGS_INTERNET_NETWORK_AUTO_CONNECT},
+ {"networkButtonActivate", IDS_SETTINGS_INTERNET_BUTTON_ACTIVATE},
+ {"networkButtonConfigure", IDS_SETTINGS_INTERNET_BUTTON_CONFIGURE},
+ {"networkButtonConnect", IDS_SETTINGS_INTERNET_BUTTON_CONNECT},
+ {"networkButtonDisconnect", IDS_SETTINGS_INTERNET_BUTTON_DISCONNECT},
+ {"networkButtonForget", IDS_SETTINGS_INTERNET_BUTTON_FORGET},
+ {"networkButtonViewAccount", IDS_SETTINGS_INTERNET_BUTTON_VIEW_ACCOUNT},
+ {"networkConnectNotAllowed", IDS_SETTINGS_INTERNET_CONNECT_NOT_ALLOWED},
+ {"networkConfigSaveCredentials",
+ IDS_SETTINGS_INTERNET_CONFIG_SAVE_CREDENTIALS},
+ {"networkConfigShare", IDS_SETTINGS_INTERNET_CONFIG_SHARE},
+ {"networkIPAddress", IDS_SETTINGS_INTERNET_NETWORK_IP_ADDRESS},
+ {"networkIPConfigAuto", IDS_SETTINGS_INTERNET_NETWORK_IP_CONFIG_AUTO},
+ {"networkPrefer", IDS_SETTINGS_INTERNET_NETWORK_PREFER},
+ {"networkPrimaryUserControlled",
+ IDS_SETTINGS_INTERNET_NETWORK_PRIMARY_USER_CONTROLLED},
+ {"networkProxy", IDS_SETTINGS_INTERNET_NETWORK_PROXY_PROXY},
+ {"networkProxyAddException",
+ IDS_SETTINGS_INTERNET_NETWORK_PROXY_ADD_EXCEPTION},
+ {"networkProxyAllowShared",
+ IDS_SETTINGS_INTERNET_NETWORK_PROXY_ALLOW_SHARED},
+ {"networkProxyAllowSharedWarningTitle",
+ IDS_SETTINGS_INTERNET_NETWORK_PROXY_ALLOW_SHARED_WARNING_TITLE},
+ {"networkProxyAllowSharedWarningMessage",
+ IDS_SETTINGS_INTERNET_NETWORK_PROXY_ALLOW_SHARED_WARNING_MESSAGE},
+ {"networkProxyAutoConfig",
+ IDS_SETTINGS_INTERNET_NETWORK_PROXY_AUTO_CONFIG},
+ {"networkProxyConnectionType",
+ IDS_SETTINGS_INTERNET_NETWORK_PROXY_CONNECTION_TYPE},
+ {"networkProxyEnforcedPolicy",
+ IDS_SETTINGS_INTERNET_NETWORK_PROXY_ENFORCED_POLICY},
+ {"networkProxyExceptionList",
+ IDS_SETTINGS_INTERNET_NETWORK_PROXY_EXCEPTION_LIST},
+ {"networkProxyFtp", IDS_SETTINGS_INTERNET_NETWORK_PROXY_FTP_PROXY},
+ {"networkProxyHttp", IDS_SETTINGS_INTERNET_NETWORK_PROXY_HTTP_PROXY},
+ {"networkProxyPort", IDS_SETTINGS_INTERNET_NETWORK_PROXY_PORT},
+ {"networkProxyShttp", IDS_SETTINGS_INTERNET_NETWORK_PROXY_SHTTP_PROXY},
+ {"networkProxySocks", IDS_SETTINGS_INTERNET_NETWORK_PROXY_SOCKS_HOST},
+ {"networkProxyTypeDirect",
+ IDS_SETTINGS_INTERNET_NETWORK_PROXY_TYPE_DIRECT},
+ {"networkProxyTypeManual",
+ IDS_SETTINGS_INTERNET_NETWORK_PROXY_TYPE_MANUAL},
+ {"networkProxyTypePac", IDS_SETTINGS_INTERNET_NETWORK_PROXY_TYPE_PAC},
+ {"networkProxyTypeWpad", IDS_SETTINGS_INTERNET_NETWORK_PROXY_TYPE_WPAD},
+ {"networkProxyUseSame", IDS_SETTINGS_INTERNET_NETWORK_PROXY_USE_SAME},
+ {"networkSectionAccessPoint",
+ IDS_SETTINGS_INTERNET_NETWORK_SECTION_ACCESS_POINT},
+ {"networkSectionAdvanced",
+ IDS_SETTINGS_INTERNET_NETWORK_SECTION_ADVANCED},
+ {"networkSectionAdvancedA11yLabel",
+ IDS_SETTINGS_INTERNET_NETWORK_SECTION_ADVANCED_ACCESSIBILITY_LABEL},
+ {"networkSectionNameservers",
+ IDS_SETTINGS_INTERNET_NETWORK_SECTION_NAMESERVERS},
+ {"networkSectionNetwork", IDS_SETTINGS_INTERNET_NETWORK_SECTION_NETWORK},
+ {"networkSectionNetworkExpandA11yLabel",
+ IDS_SETTINGS_INTERNET_NETWORK_SECTION_NETWORK_ACCESSIBILITY_LABEL},
+ {"networkSectionProxy", IDS_SETTINGS_INTERNET_NETWORK_SECTION_PROXY},
+ {"networkSectionProxyExpandA11yLabel",
+ IDS_SETTINGS_INTERNET_NETWORK_SECTION_PROXY_ACCESSIBILITY_LABEL},
+ {"networkSectionWpad", IDS_SETTINGS_INTERNET_NETWORK_SECTION_WPAD},
+ {"networkShared", IDS_SETTINGS_INTERNET_NETWORK_SHARED},
+ {"networkSimCardLocked", IDS_SETTINGS_INTERNET_NETWORK_SIM_CARD_LOCKED},
+ {"networkSimCardMissing", IDS_SETTINGS_INTERNET_NETWORK_SIM_CARD_MISSING},
+ {"networkSimChange", IDS_SETTINGS_INTERNET_NETWORK_SIM_BUTTON_CHANGE},
+ {"networkSimChangePin", IDS_SETTINGS_INTERNET_NETWORK_SIM_CHANGE_PIN},
+ {"networkSimChangePinTitle",
+ IDS_SETTINGS_INTERNET_NETWORK_SIM_CHANGE_PIN_TITLE},
+ {"networkSimEnter", IDS_SETTINGS_INTERNET_NETWORK_SIM_BUTTON_ENTER},
+ {"networkSimEnterNewPin",
+ IDS_SETTINGS_INTERNET_NETWORK_SIM_ENTER_NEW_PIN},
+ {"networkSimEnterOldPin",
+ IDS_SETTINGS_INTERNET_NETWORK_SIM_ENTER_OLD_PIN},
+ {"networkSimEnterPin", IDS_SETTINGS_INTERNET_NETWORK_SIM_ENTER_PIN},
+ {"networkSimEnterPinTitle",
+ IDS_SETTINGS_INTERNET_NETWORK_SIM_ENTER_PIN_TITLE},
+ {"networkSimEnterPuk", IDS_SETTINGS_INTERNET_NETWORK_SIM_ENTER_PUK},
+ {"networkSimLockEnable", IDS_SETTINGS_INTERNET_NETWORK_SIM_LOCK_ENABLE},
+ {"networkSimLockedTitle", IDS_SETTINGS_INTERNET_NETWORK_SIM_LOCKED_TITLE},
+ {"networkSimLockedWarning",
+ IDS_SETTINGS_INTERNET_NETWORK_SIM_LOCKED_WARNING},
+ {"networkSimReEnterNewPin",
+ IDS_SETTINGS_INTERNET_NETWORK_SIM_RE_ENTER_NEW_PIN},
+ {"networkSimReEnterNewPin",
+ IDS_SETTINGS_INTERNET_NETWORK_SIM_RE_ENTER_NEW_PIN},
+ {"networkSimUnlock", IDS_SETTINGS_INTERNET_NETWORK_SIM_BUTTON_UNLOCK},
+ {"networkVpnBuiltin", IDS_NETWORK_TYPE_VPN_BUILTIN},
+ {"tetherConnectionDialogTitle",
+ IDS_SETTINGS_INTERNET_TETHER_CONNECTION_DIALOG_TITLE},
+ {"tetherConnectionBatteryPercentage",
+ IDS_SETTINGS_INTERNET_TETHER_CONNECTION_BATTERY_PERCENTAGE},
+ {"tetherConnectionExplanation",
+ IDS_SETTINGS_INTERNET_TETHER_CONNECTION_EXPLANATION},
+ {"tetherConnectionCarrierWarning",
+ IDS_SETTINGS_INTERNET_TETHER_CONNECTION_CARRIER_WARNING},
+ {"tetherConnectionDescriptionTitle",
+ IDS_SETTINGS_INTERNET_TETHER_CONNECTION_DESCRIPTION_TITLE},
+ {"tetherConnectionDescriptionCellData",
+ IDS_SETTINGS_INTERNET_TETHER_CONNECTION_DESCRIPTION_CELLULAR_DATA},
+ {"tetherConnectionDescriptionBattery",
+ IDS_SETTINGS_INTERNET_TETHER_CONNECTION_DESCRIPTION_BATTERY},
+ {"tetherConnectionDescriptionWiFi",
+ IDS_SETTINGS_INTERNET_TETHER_CONNECTION_DESCRIPTION_WIFI},
+ {"tetherConnectionNotNowButton",
+ IDS_SETTINGS_INTERNET_TETHER_CONNECTION_NOT_NOW_BUTTON},
+ {"tetherConnectionConnectButton",
+ IDS_SETTINGS_INTERNET_TETHER_CONNECTION_CONNECT_BUTTON},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+ html_source->AddBoolean("networkSettingsConfig",
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ chromeos::switches::kNetworkSettingsConfig));
+}
+#endif
+
+void AddLanguagesStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"languagesPageTitle", IDS_SETTINGS_LANGUAGES_PAGE_TITLE},
+ {"languagesListTitle", IDS_SETTINGS_LANGUAGES_LANGUAGES_LIST_TITLE},
+ {"languagesExpandA11yLabel",
+ IDS_SETTINGS_LANGUAGES_EXPAND_ACCESSIBILITY_LABEL},
+ {"orderLanguagesInstructions",
+ IDS_SETTINGS_LANGUAGES_LANGUAGES_LIST_ORDERING_INSTRUCTIONS},
+ {"moveToTop", IDS_SETTINGS_LANGUAGES_LANGUAGES_LIST_MOVE_TO_TOP},
+ {"moveUp", IDS_SETTINGS_LANGUAGES_LANGUAGES_LIST_MOVE_UP},
+ {"moveDown", IDS_SETTINGS_LANGUAGES_LANGUAGES_LIST_MOVE_DOWN},
+ {"removeLanguage", IDS_SETTINGS_LANGUAGES_LANGUAGES_LIST_REMOVE},
+ {"addLanguages", IDS_SETTINGS_LANGUAGES_LANGUAGES_ADD},
+#if defined(OS_CHROMEOS)
+ {"inputMethodsListTitle", IDS_SETTINGS_LANGUAGES_INPUT_METHODS_LIST_TITLE},
+ {"inputMethodEnabled", IDS_SETTINGS_LANGUAGES_INPUT_METHOD_ENABLED},
+ {"inputMethodsExpandA11yLabel",
+ IDS_SETTINGS_LANGUAGES_INPUT_METHODS_EXPAND_ACCESSIBILITY_LABEL},
+ {"manageInputMethods", IDS_SETTINGS_LANGUAGES_INPUT_METHODS_MANAGE},
+ {"manageInputMethodsPageTitle",
+ IDS_SETTINGS_LANGUAGES_MANAGE_INPUT_METHODS_TITLE},
+ {"showImeMenu", IDS_SETTINGS_LANGUAGES_SHOW_IME_MENU},
+#endif
+ {"addLanguagesDialogTitle", IDS_SETTINGS_LANGUAGES_MANAGE_LANGUAGES_TITLE},
+ {"allLanguages", IDS_SETTINGS_LANGUAGES_ALL_LANGUAGES},
+ {"enabledLanguages", IDS_SETTINGS_LANGUAGES_ENABLED_LANGUAGES},
+ {"isDisplayedInThisLanguage",
+ IDS_SETTINGS_LANGUAGES_IS_DISPLAYED_IN_THIS_LANGUAGE},
+ {"displayInThisLanguage", IDS_SETTINGS_LANGUAGES_DISPLAY_IN_THIS_LANGUAGE},
+ {"offerToTranslateInThisLanguage",
+ IDS_SETTINGS_LANGUAGES_OFFER_TO_TRANSLATE_IN_THIS_LANGUAGE},
+ {"offerToEnableTranslate",
+ IDS_SETTINGS_LANGUAGES_OFFER_TO_ENABLE_TRANSLATE},
+#if !defined(OS_MACOSX)
+ {"spellCheckListTitle", IDS_SETTINGS_LANGUAGES_SPELL_CHECK_LIST_TITLE},
+ {"spellCheckExpandA11yLabel",
+ IDS_SETTINGS_LANGUAGES_SPELL_CHECK_EXPAND_ACCESSIBILITY_LABEL},
+ {"spellCheckSummaryTwoLanguages",
+ IDS_SETTINGS_LANGUAGES_SPELL_CHECK_SUMMARY_TWO_LANGUAGES},
+ // TODO(michaelpg): Use ICU plural format when available to properly
+ // translate "and [n] other(s)".
+ {"spellCheckSummaryThreeLanguages",
+ IDS_SETTINGS_LANGUAGES_SPELL_CHECK_SUMMARY_THREE_LANGUAGES},
+ {"spellCheckSummaryMultipleLanguages",
+ IDS_SETTINGS_LANGUAGES_SPELL_CHECK_SUMMARY_MULTIPLE_LANGUAGES},
+ {"manageSpellCheck", IDS_SETTINGS_LANGUAGES_SPELL_CHECK_MANAGE},
+ {"editDictionaryPageTitle", IDS_SETTINGS_LANGUAGES_EDIT_DICTIONARY_TITLE},
+ {"addDictionaryWordLabel", IDS_SETTINGS_LANGUAGES_ADD_DICTIONARY_WORD},
+ {"addDictionaryWordButton",
+ IDS_SETTINGS_LANGUAGES_ADD_DICTIONARY_WORD_BUTTON},
+ {"customDictionaryWords", IDS_SETTINGS_LANGUAGES_DICTIONARY_WORDS},
+ {"noCustomDictionaryWordsFound",
+ IDS_SETTINGS_LANGUAGES_DICTIONARY_WORDS_NONE},
+#endif
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+
+#if defined(OS_CHROMEOS)
+ // Only the Chrome OS help article explains how language order affects website
+ // language.
+ html_source->AddString(
+ "languagesLearnMoreURL",
+ base::ASCIIToUTF16(chrome::kLanguageSettingsLearnMoreUrl));
+#endif
+}
+
+#if defined(OS_CHROMEOS)
+void AddChromeOSUserStrings(content::WebUIDataSource* html_source,
+ Profile* profile) {
+ user_manager::UserManager* user_manager = user_manager::UserManager::Get();
+
+ const user_manager::User* user =
+ chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
+ const user_manager::User* primary_user = user_manager->GetPrimaryUser();
+ std::string primary_user_email = primary_user->GetAccountId().GetUserEmail();
+ html_source->AddString("primaryUserEmail", primary_user_email);
+ html_source->AddBoolean(
+ "isSecondaryUser",
+ user && user->GetAccountId() != primary_user->GetAccountId());
+ html_source->AddString(
+ "secondaryUserBannerText",
+ l10n_util::GetStringFUTF16(IDS_SETTINGS_SECONDARY_USER_BANNER,
+ base::ASCIIToUTF16(primary_user_email)));
+
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ if (!connector->IsEnterpriseManaged() &&
+ !user_manager->IsCurrentUserOwner()) {
+ html_source->AddString("ownerEmail",
+ user_manager->GetOwnerAccountId().GetUserEmail());
+ }
+}
+#endif
+
+void AddOnStartupStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"onStartup", IDS_SETTINGS_ON_STARTUP},
+ {"onStartupOpenNewTab", IDS_SETTINGS_ON_STARTUP_OPEN_NEW_TAB},
+ {"onStartupContinue", IDS_SETTINGS_ON_STARTUP_CONTINUE},
+ {"onStartupOpenSpecific", IDS_SETTINGS_ON_STARTUP_OPEN_SPECIFIC},
+ {"onStartupUseCurrent", IDS_SETTINGS_ON_STARTUP_USE_CURRENT},
+ {"onStartupAddNewPage", IDS_SETTINGS_ON_STARTUP_ADD_NEW_PAGE},
+ {"onStartupEditPage", IDS_SETTINGS_ON_STARTUP_EDIT_PAGE},
+ {"onStartupSiteUrl", IDS_SETTINGS_ON_STARTUP_SITE_URL},
+ {"onStartupRemove", IDS_SETTINGS_ON_STARTUP_REMOVE},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+}
+
+void AddPasswordsAndFormsStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"passwordsAndAutofillPageTitle",
+ IDS_SETTINGS_PASSWORDS_AND_AUTOFILL_PAGE_TITLE},
+ {"autofill", IDS_SETTINGS_AUTOFILL},
+ {"googlePayments", IDS_SETTINGS_GOOGLE_PAYMENTS},
+ {"googlePaymentsCached", IDS_SETTINGS_GOOGLE_PAYMENTS_CACHED},
+ {"addresses", IDS_SETTINGS_AUTOFILL_ADDRESSES_HEADING},
+ {"addAddressTitle", IDS_SETTINGS_AUTOFILL_ADDRESSES_ADD_TITLE},
+ {"editAddressTitle", IDS_SETTINGS_AUTOFILL_ADDRESSES_EDIT_TITLE},
+ {"addressCountry", IDS_SETTINGS_AUTOFILL_ADDRESSES_COUNTRY},
+ {"addressPhone", IDS_SETTINGS_AUTOFILL_ADDRESSES_PHONE},
+ {"addressEmail", IDS_SETTINGS_AUTOFILL_ADDRESSES_EMAIL},
+ {"removeAddress", IDS_SETTINGS_ADDRESS_REMOVE},
+ {"creditCards", IDS_SETTINGS_AUTOFILL_CREDIT_CARD_HEADING},
+ {"removeCreditCard", IDS_SETTINGS_CREDIT_CARD_REMOVE},
+ {"clearCreditCard", IDS_SETTINGS_CREDIT_CARD_CLEAR},
+ {"creditCardType", IDS_SETTINGS_AUTOFILL_CREDIT_CARD_TYPE_COLUMN_LABEL},
+ {"creditCardExpiration", IDS_SETTINGS_CREDIT_CARD_EXPIRATION_DATE},
+ {"creditCardName", IDS_SETTINGS_NAME_ON_CREDIT_CARD},
+ {"creditCardNumber", IDS_SETTINGS_CREDIT_CARD_NUMBER},
+ {"creditCardExpirationMonth", IDS_SETTINGS_CREDIT_CARD_EXPIRATION_MONTH},
+ {"creditCardExpirationYear", IDS_SETTINGS_CREDIT_CARD_EXPIRATION_YEAR},
+ {"creditCardExpired", IDS_SETTINGS_CREDIT_CARD_EXPIRED},
+ {"editCreditCardTitle", IDS_SETTINGS_EDIT_CREDIT_CARD_TITLE},
+ {"addCreditCardTitle", IDS_SETTINGS_ADD_CREDIT_CARD_TITLE},
+ {"autofillDetail", IDS_SETTINGS_AUTOFILL_DETAIL},
+ {"passwords", IDS_SETTINGS_PASSWORDS},
+ {"passwordsAutosigninLabel",
+ IDS_SETTINGS_PASSWORDS_AUTOSIGNIN_CHECKBOX_LABEL},
+ {"passwordsAutosigninDescription",
+ IDS_SETTINGS_PASSWORDS_AUTOSIGNIN_CHECKBOX_DESC},
+ {"passwordsDetail", IDS_SETTINGS_PASSWORDS_DETAIL},
+ {"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},
+ {"hidePassword", IDS_SETTINGS_PASSWORD_HIDE},
+ {"passwordDetailsTitle", IDS_SETTINGS_PASSWORDS_VIEW_DETAILS_TITLE},
+ {"passwordViewDetails", IDS_SETTINGS_PASSWORD_DETAILS},
+ {"editPasswordWebsiteLabel", IDS_SETTINGS_PASSWORDS_WEBSITE},
+ {"editPasswordUsernameLabel", IDS_SETTINGS_PASSWORDS_USERNAME},
+ {"editPasswordPasswordLabel", IDS_SETTINGS_PASSWORDS_PASSWORD},
+ {"noAddressesFound", IDS_SETTINGS_ADDRESS_NONE},
+ {"noCreditCardsFound", IDS_SETTINGS_CREDIT_CARD_NONE},
+ {"noPasswordsFound", IDS_SETTINGS_PASSWORDS_NONE},
+ {"noExceptionsFound", IDS_SETTINGS_PASSWORDS_EXCEPTIONS_NONE},
+ };
+
+ html_source->AddString(
+ "managePasswordsLabel",
+ l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_PASSWORDS_MANAGE_PASSWORDS,
+ base::ASCIIToUTF16(
+ password_manager::kPasswordManagerAccountDashboardURL)));
+ html_source->AddString("passwordManagerLearnMoreURL",
+ chrome::kPasswordManagerLearnMoreURL);
+ html_source->AddString(
+ "manageAddressesUrl",
+ autofill::payments::GetManageAddressesUrl(0).spec());
+ html_source->AddString(
+ "manageCreditCardsUrl",
+ autofill::payments::GetManageInstrumentsUrl(0).spec());
+
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+}
+
+void AddPeopleStrings(content::WebUIDataSource* html_source) {
+ 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",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_LOCATE_SCANNER},
+ {"configureFingerprintInstructionMoveFingerStep",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_MOVE_FINGER},
+ {"configureFingerprintInstructionReadyStep",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_READY},
+ {"configureFingerprintLiftFinger",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_LIFT_FINGER},
+ {"configureFingerprintPartialData",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_PARTIAL_DATA},
+ {"configureFingerprintInsufficientData",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSUFFICIENT_DATA},
+ {"configureFingerprintSensorDirty",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_SENSOR_DIRTY},
+ {"configureFingerprintTooSlow",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_FINGER_TOO_SLOW},
+ {"configureFingerprintTooFast",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_FINGER_TOO_FAST},
+ {"configureFingerprintImmobile",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_FINGER_IMMOBILE},
+ {"configureFingerprintCancelButton",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_CANCEL_BUTTON},
+ {"configureFingerprintDoneButton",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_DONE_BUTTON},
+ {"configureFingerprintAddAnotherButton",
+ IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_ADD_ANOTHER_BUTTON},
+ {"configurePinChoosePinTitle",
+ IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_CHOOSE_PIN_TITLE},
+ {"configurePinConfirmPinTitle",
+ IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_CONFIRM_PIN_TITLE},
+ {"configurePinContinueButton",
+ IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_CONTINUE_BUTTON},
+ {"configurePinMismatched", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_MISMATCHED},
+ {"configurePinTooShort", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_TOO_SHORT},
+ {"configurePinTooLong", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_TOO_LONG},
+ {"configurePinWeakPin", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_WEAK_PIN},
+ {"enableScreenlock", IDS_SETTINGS_PEOPLE_ENABLE_SCREENLOCK},
+ {"lockScreenAddFingerprint",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_ADD_FINGERPRINT_BUTTON},
+ {"lockScreenChangePinButton",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_CHANGE_PIN_BUTTON},
+ {"lockScreenEditFingerprints",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_EDIT_FINGERPRINTS},
+ {"lockScreenEditFingerprintsDescription",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_EDIT_FINGERPRINTS_DESCRIPTION},
+ {"lockScreenSetupFingerprintButton",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_FINGERPRINT_SETUP_BUTTON},
+ {"lockScreenNumberFingerprints",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_NUM_FINGERPRINTS},
+ {"lockScreenNone", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_NONE},
+ {"lockScreenFingerprintEnable",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_ENABLE_FINGERPRINT_CHECKBOX_LABEL},
+ {"lockScreenFingerprintNewName",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_NEW_FINGERPRINT_DEFAULT_NAME},
+ {"lockScreenFingerprintTitle",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_FINGERPRINT_SUBPAGE_TITLE},
+ {"lockScreenFingerprintWarning",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_FINGERPRINT_LESS_SECURE},
+ {"lockScreenOptions", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_OPTIONS},
+ {"lockScreenPasswordOnly", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PASSWORD_ONLY},
+ {"lockScreenPinOrPassword",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PIN_OR_PASSWORD},
+ {"lockScreenRegisteredFingerprints",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_REGISTERED_FINGERPRINTS_LABEL},
+ {"lockScreenSetupPinButton",
+ IDS_SETTINGS_PEOPLE_LOCK_SCREEN_SETUP_PIN_BUTTON},
+ {"lockScreenTitle", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_TITLE},
+ {"passwordPromptEnterPassword",
+ IDS_SETTINGS_PEOPLE_PASSWORD_PROMPT_ENTER_PASSWORD},
+ {"passwordPromptInvalidPassword",
+ IDS_SETTINGS_PEOPLE_PASSWORD_PROMPT_INVALID_PASSWORD},
+ {"passwordPromptPasswordLabel",
+ IDS_SETTINGS_PEOPLE_PASSWORD_PROMPT_PASSWORD_LABEL},
+ {"passwordPromptTitle", IDS_SETTINGS_PEOPLE_PASSWORD_PROMPT_TITLE},
+ {"pinKeyboardPlaceholderPin", IDS_PIN_KEYBOARD_HINT_TEXT_PIN},
+ {"pinKeyboardPlaceholderPinPassword",
+ IDS_PIN_KEYBOARD_HINT_TEXT_PIN_PASSWORD},
+ {"pinKeyboardDeleteAccessibleName",
+ IDS_PIN_KEYBOARD_DELETE_ACCESSIBLE_NAME},
+ {"changePictureTitle", IDS_SETTINGS_CHANGE_PICTURE_DIALOG_TITLE},
+ {"changePicturePageDescription", IDS_SETTINGS_CHANGE_PICTURE_DIALOG_TEXT},
+ {"takePhoto", IDS_SETTINGS_CHANGE_PICTURE_TAKE_PHOTO},
+ {"discardPhoto", IDS_SETTINGS_CHANGE_PICTURE_DISCARD_PHOTO},
+ {"flipPhoto", IDS_SETTINGS_CHANGE_PICTURE_FLIP_PHOTO},
+ {"chooseFile", IDS_SETTINGS_CHANGE_PICTURE_CHOOSE_FILE},
+ {"profilePhoto", IDS_SETTINGS_CHANGE_PICTURE_PROFILE_PHOTO},
+ {"oldPhoto", IDS_SETTINGS_CHANGE_PICTURE_OLD_PHOTO},
+ {"profilePhotoLoading", IDS_SETTINGS_CHANGE_PICTURE_PROFILE_LOADING_PHOTO},
+ {"previewAltText", IDS_SETTINGS_CHANGE_PICTURE_PREVIEW_ALT},
+ {"authorCredit", IDS_SETTINGS_CHANGE_PICTURE_AUTHOR_TEXT},
+ {"photoFlippedAccessibleText", IDS_SETTINGS_PHOTO_FLIP_ACCESSIBLE_TEXT},
+ {"photoFlippedBackAccessibleText",
+ IDS_SETTINGS_PHOTO_FLIPBACK_ACCESSIBLE_TEXT},
+ {"photoCaptureAccessibleText", IDS_SETTINGS_PHOTO_CAPTURE_ACCESSIBLE_TEXT},
+ {"photoDiscardAccessibleText", IDS_SETTINGS_PHOTO_DISCARD_ACCESSIBLE_TEXT},
+#else // !defined(OS_CHROMEOS)
+ {"domainManagedProfile", IDS_SETTINGS_PEOPLE_DOMAIN_MANAGED_PROFILE},
+ {"editPerson", IDS_SETTINGS_EDIT_PERSON},
+ {"showShortcutLabel", IDS_SETTINGS_PROFILE_SHORTCUT_TOGGLE_LABEL},
+#endif // defined(OS_CHROMEOS)
+ {"syncOverview", IDS_SETTINGS_SYNC_OVERVIEW},
+ {"syncSignin", IDS_SETTINGS_SYNC_SIGNIN},
+ {"syncDisconnect", IDS_SETTINGS_SYNC_DISCONNECT},
+ {"syncDisconnectTitle", IDS_SETTINGS_SYNC_DISCONNECT_TITLE},
+ {"syncDisconnectDeleteProfile",
+ IDS_SETTINGS_SYNC_DISCONNECT_DELETE_PROFILE},
+ {"deleteProfileWarningExpandA11yLabel",
+ IDS_SETTINGS_SYNC_DISCONNECT_EXPAND_ACCESSIBILITY_LABEL},
+ {"deleteProfileWarningWithCountsSingular",
+ IDS_SETTINGS_SYNC_DISCONNECT_DELETE_PROFILE_WARNING_WITH_COUNTS_SINGULAR},
+ {"deleteProfileWarningWithCountsPlural",
+ IDS_SETTINGS_SYNC_DISCONNECT_DELETE_PROFILE_WARNING_WITH_COUNTS_PLURAL},
+ {"deleteProfileWarningWithoutCounts",
+ IDS_SETTINGS_SYNC_DISCONNECT_DELETE_PROFILE_WARNING_WITHOUT_COUNTS},
+ {"syncDisconnectExplanation", IDS_SETTINGS_SYNC_DISCONNECT_EXPLANATION},
+ {"syncDisconnectConfirm", IDS_SETTINGS_SYNC_DISCONNECT_CONFIRM},
+ {"sync", IDS_SETTINGS_SYNC},
+ {"syncPageTitle", IDS_SETTINGS_SYNC_PAGE_TITLE},
+ {"syncLoading", IDS_SETTINGS_SYNC_LOADING},
+ {"syncTimeout", IDS_SETTINGS_SYNC_TIMEOUT},
+ {"syncEverythingCheckboxLabel",
+ IDS_SETTINGS_SYNC_EVERYTHING_CHECKBOX_LABEL},
+ {"appCheckboxLabel", IDS_SETTINGS_APPS_CHECKBOX_LABEL},
+ {"extensionsCheckboxLabel", IDS_SETTINGS_EXTENSIONS_CHECKBOX_LABEL},
+ {"settingsCheckboxLabel", IDS_SETTINGS_SETTINGS_CHECKBOX_LABEL},
+ {"autofillCheckboxLabel", IDS_SETTINGS_AUTOFILL_CHECKBOX_LABEL},
+ {"historyCheckboxLabel", IDS_SETTINGS_HISTORY_CHECKBOX_LABEL},
+ {"themesAndWallpapersCheckboxLabel",
+ IDS_SETTINGS_THEMES_AND_WALLPAPERS_CHECKBOX_LABEL},
+ {"bookmarksCheckboxLabel", IDS_SETTINGS_BOOKMARKS_CHECKBOX_LABEL},
+ {"passwordsCheckboxLabel", IDS_SETTINGS_PASSWORDS_CHECKBOX_LABEL},
+ {"openTabsCheckboxLabel", IDS_SETTINGS_OPEN_TABS_CHECKBOX_LABEL},
+ {"enablePaymentsIntegrationCheckboxLabel",
+ IDS_SETTINGS_ENABLE_PAYMENTS_INTEGRATION_CHECKBOX_LABEL},
+ {"manageSyncedDataTitle", IDS_SETTINGS_MANAGE_SYNCED_DATA_TITLE},
+ {"encryptionOptionsTitle", IDS_SETTINGS_ENCRYPTION_OPTIONS},
+ {"syncDataEncryptedText", IDS_SETTINGS_SYNC_DATA_ENCRYPTED_TEXT},
+ {"encryptWithGoogleCredentialsLabel",
+ IDS_SETTINGS_ENCRYPT_WITH_GOOGLE_CREDENTIALS_LABEL},
+ {"useDefaultSettingsButton", IDS_SETTINGS_USE_DEFAULT_SETTINGS},
+ {"emptyPassphraseError", IDS_SETTINGS_EMPTY_PASSPHRASE_ERROR},
+ {"mismatchedPassphraseError", IDS_SETTINGS_MISMATCHED_PASSPHRASE_ERROR},
+ {"incorrectPassphraseError", IDS_SETTINGS_INCORRECT_PASSPHRASE_ERROR},
+ {"passphrasePlaceholder", IDS_SETTINGS_PASSPHRASE_PLACEHOLDER},
+ {"passphraseConfirmationPlaceholder",
+ IDS_SETTINGS_PASSPHRASE_CONFIRMATION_PLACEHOLDER},
+ {"submitPassphraseButton", IDS_SETTINGS_SUBMIT_PASSPHRASE},
+ {"personalizeGoogleServicesTitle",
+ IDS_SETTINGS_PERSONALIZE_GOOGLE_SERVICES_TITLE},
+ {"personalizeGoogleServicesText",
+ IDS_SETTINGS_PERSONALIZE_GOOGLE_SERVICES_TEXT},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+
+ // Format numbers to be used on the pin keyboard.
+ for (int j = 0; j <= 9; j++) {
+ html_source->AddString("pinKeyboard" + base::IntToString(j),
+ base::FormatNumber(int64_t{j}));
+ }
+
+ html_source->AddString("syncLearnMoreUrl", chrome::kSyncLearnMoreURL);
+ html_source->AddString("autofillHelpURL", autofill::kHelpURL);
+ html_source->AddString("supervisedUsersUrl",
+ chrome::kLegacySupervisedUserManagementURL);
+
+ html_source->AddString(
+ "encryptWithSyncPassphraseLabel",
+ l10n_util::GetStringFUTF8(
+ IDS_SETTINGS_ENCRYPT_WITH_SYNC_PASSPHRASE_LABEL,
+ base::ASCIIToUTF16(chrome::kSyncEncryptionHelpURL)));
+
+ std::string sync_dashboard_url =
+ google_util::AppendGoogleLocaleParam(
+ GURL(chrome::kSyncGoogleDashboardURL),
+ g_browser_process->GetApplicationLocale())
+ .spec();
+ html_source->AddString("syncDashboardUrl", sync_dashboard_url);
+
+ html_source->AddString(
+ "passphraseExplanationText",
+ l10n_util::GetStringFUTF8(IDS_SETTINGS_PASSPHRASE_EXPLANATION_TEXT,
+ base::ASCIIToUTF16(sync_dashboard_url)));
+ html_source->AddString(
+ "passphraseResetHint",
+ l10n_util::GetStringFUTF8(IDS_SETTINGS_PASSPHRASE_RESET_HINT,
+ base::ASCIIToUTF16(sync_dashboard_url)));
+ html_source->AddString(
+ "passphraseRecover",
+ l10n_util::GetStringFUTF8(IDS_SETTINGS_PASSPHRASE_RECOVER,
+ base::ASCIIToUTF16(sync_dashboard_url)));
+ html_source->AddString(
+ "syncDisconnectExplanation",
+ l10n_util::GetStringFUTF8(IDS_SETTINGS_SYNC_DISCONNECT_EXPLANATION,
+ base::ASCIIToUTF16(sync_dashboard_url)));
+#if !defined(OS_CHROMEOS)
+ html_source->AddString(
+ "syncDisconnectManagedProfileExplanation",
+ l10n_util::GetStringFUTF8(
+ IDS_SETTINGS_SYNC_DISCONNECT_MANAGED_PROFILE_EXPLANATION,
+ base::ASCIIToUTF16("$1"),
+ base::ASCIIToUTF16(sync_dashboard_url)));
+#endif
+
+ html_source->AddString("syncErrorHelpUrl", chrome::kSyncErrorsHelpURL);
+
+ html_source->AddString("activityControlsUrl",
+ chrome::kGoogleAccountActivityControlsURL);
+
+ html_source->AddBoolean("profileShortcutsEnabled",
+ ProfileShortcutManager::IsFeatureEnabled());
+}
+
+void AddPrintingStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"printingPageTitle", IDS_SETTINGS_PRINTING},
+ {"printingCloudPrintLearnMoreLabel",
+ IDS_SETTINGS_PRINTING_CLOUD_PRINT_LEARN_MORE_LABEL},
+ {"printingNotificationsLabel", IDS_SETTINGS_PRINTING_NOTIFICATIONS_LABEL},
+ {"printingManageCloudPrintDevices",
+ IDS_SETTINGS_PRINTING_MANAGE_CLOUD_PRINT_DEVICES},
+ {"cloudPrintersTitle", IDS_SETTINGS_PRINTING_CLOUD_PRINTERS},
+ {"cloudPrintersTitleDescription",
+ IDS_SETTINGS_PRINTING_CLOUD_PRINTERS_DESCRIPTION},
+#if defined(OS_CHROMEOS)
+ {"cupsPrintersTitle", IDS_SETTINGS_PRINTING_CUPS_PRINTERS},
+ {"addCupsPrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_ADD_PRINTER},
+ {"cupsPrinterDetails", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_DETAILS},
+ {"removePrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_REMOVE},
+ {"searchLabel", IDS_SETTINGS_PRINTING_CUPS_SEARCH_LABEL},
+ {"printerDetailsTitle", IDS_SETTINGS_PRINTING_CUPS_PRINTER_DETAILS_TITLE},
+ {"printerName", IDS_SETTINGS_PRINTING_CUPS_PRINTER_DETAILS_NAME},
+ {"printerModel", IDS_SETTINGS_PRINTING_CUPS_PRINTER_DETAILS_MODEL},
+ {"printerQueue", IDS_SETTINGS_PRINTING_CUPS_PRINTER_DETAILS_QUEUE},
+ {"addPrintersNearbyTitle",
+ IDS_SETTINGS_PRINTING_CUPS_ADD_PRINTERS_NEARBY_TITLE},
+ {"addPrintersManuallyTitle",
+ IDS_SETTINGS_PRINTING_CUPS_ADD_PRINTERS_MANUALLY_TITLE},
+ {"cancelButtonText", IDS_SETTINGS_PRINTING_CUPS_ADD_PRINTER_BUTTON_CANCEL},
+ {"addPrinterButtonText", IDS_SETTINGS_PRINTING_CUPS_ADD_PRINTER_BUTTON_ADD},
+ {"printerDetailsAdvanced", IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADVANCED},
+ {"printerDetailsA11yLabel",
+ IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADVANCED_ACCESSIBILITY_LABEL},
+ {"printerAddress", IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADVANCED_ADDRESS},
+ {"printerProtocol", IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADVANCED_PROTOCOL},
+ {"printerURI", IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADVANCED_URI},
+ {"manuallyAddPrinterButtonText",
+ IDS_SETTINGS_PRINTING_CUPS_ADD_PRINTER_BUTTON_MANUAL_ADD},
+ {"discoverPrintersButtonText",
+ IDS_SETTINGS_PRINTING_CUPS_ADD_PRINTER_BUTTON_DISCOVER_PRINTERS},
+ {"printerProtocolIpp", IDS_SETTINGS_PRINTING_CUPS_PRINTER_PROTOCOL_IPP},
+ {"printerProtocolIpps", IDS_SETTINGS_PRINTING_CUPS_PRINTER_PROTOCOL_IPPS},
+ {"printerProtocolHttp", IDS_SETTINGS_PRINTING_CUPS_PRINTER_PROTOCOL_HTTP},
+ {"printerProtocolHttps", IDS_SETTINGS_PRINTING_CUPS_PRINTER_PROTOCOL_HTTPS},
+ {"printerProtocolAppSocket",
+ IDS_SETTINGS_PRINTING_CUPS_PRINTER_PROTOCOL_APP_SOCKET},
+ {"printerProtocolLpd", IDS_SETTINGS_PRINTING_CUPS_PRINTER_PROTOCOL_LPD},
+ {"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},
+ {"printerAddedSuccessfulMessage",
+ IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADDED_PRINTER_DONE_MESSAGE},
+ {"noPrinterNearbyMessage",
+ IDS_SETTINGS_PRINTING_CUPS_PRINTER_NO_PRINTER_NEARBY},
+ {"searchingNearbyPrinters",
+ IDS_SETTINGS_PRINTING_CUPS_PRINTER_SEARCHING_NEARBY_PRINTER},
+ {"printerAddedFailedMessage",
+ IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADDED_PRINTER_ERROR_MESSAGE},
+ {"printerAddedTryAgainMessage",
+ IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADDED_PRINTER_TRY_AGAIN_MESSAGE},
+ {"requireNetworkMessage",
+ IDS_SETTINGS_PRINTING_CUPS_PRINTER_REQUIRE_INTERNET_MESSAGE},
+#endif
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+
+ html_source->AddString("devicesUrl", chrome::kChromeUIDevicesURL);
+ html_source->AddString("printingCloudPrintLearnMoreUrl",
+ chrome::kCloudPrintLearnMoreURL);
+
+#if defined(OS_CHROMEOS)
+ html_source->AddBoolean("showCupsPrintingFeatures",
+ !base::CommandLine::ForCurrentProcess()->HasSwitch(
+ ::switches::kDisableNativeCups));
+#endif
+}
+
+void AddPrivacyStrings(content::WebUIDataSource* html_source,
+ Profile* profile) {
+ LocalizedString localized_strings[] = {
+ {"privacyPageTitle", IDS_SETTINGS_PRIVACY},
+ {"linkDoctorPref", IDS_SETTINGS_LINKDOCTOR_PREF},
+ {"searchSuggestPref", IDS_SETTINGS_SUGGEST_PREF},
+ {"networkPredictionEnabled",
+ IDS_SETTINGS_NETWORK_PREDICTION_ENABLED_DESCRIPTION},
+ {"safeBrowsingEnableProtection",
+ IDS_SETTINGS_SAFEBROWSING_ENABLEPROTECTION},
+ {"spellingPref", IDS_SETTINGS_SPELLING_PREF},
+ {"spellingDescription", IDS_SETTINGS_SPELLING_DESCRIPTION},
+#if defined(OS_CHROMEOS)
+ {"enableLogging", IDS_SETTINGS_ENABLE_LOGGING_DIAGNOSTIC_AND_USAGE_DATA},
+#else
+ {"enableLogging", IDS_SETTINGS_ENABLE_LOGGING},
+#endif
+ {"doNotTrack", IDS_SETTINGS_ENABLE_DO_NOT_TRACK},
+ {"doNotTrackDialogTitle", IDS_SETTINGS_ENABLE_DO_NOT_TRACK_DIALOG_TITLE},
+ {"enableContentProtectionAttestation",
+ IDS_SETTINGS_ENABLE_CONTENT_PROTECTION_ATTESTATION},
+ {"wakeOnWifi", IDS_SETTINGS_WAKE_ON_WIFI_DESCRIPTION},
+ {"manageCertificates", IDS_SETTINGS_MANAGE_CERTIFICATES},
+ {"manageCertificatesDescription",
+ IDS_SETTINGS_MANAGE_CERTIFICATES_DESCRIPTION},
+ {"contentSettings", IDS_SETTINGS_CONTENT_SETTINGS},
+ {"siteSettings", IDS_SETTINGS_SITE_SETTINGS},
+ {"siteSettingsDescription", IDS_SETTINGS_SITE_SETTINGS_DESCRIPTION},
+ {"clearBrowsingData", IDS_SETTINGS_CLEAR_DATA},
+ {"clearBrowsingDataDescription", IDS_SETTINGS_CLEAR_DATA_DESCRIPTION},
+ {"titleAndCount", IDS_SETTINGS_TITLE_AND_COUNT},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+
+ html_source->AddBoolean(
+ "importantSitesInCbd",
+ base::FeatureList::IsEnabled(features::kImportantSitesInCbd));
+
+ html_source->AddLocalizedString(
+ "safeBrowsingEnableExtendedReporting",
+ safe_browsing::ChooseOptInTextResource(
+ *profile->GetPrefs(),
+ IDS_SETTINGS_SAFEBROWSING_ENABLE_EXTENDED_REPORTING,
+ IDS_SETTINGS_SAFEBROWSING_ENABLE_SCOUT_REPORTING));
+ html_source->AddString("improveBrowsingExperience",
+ l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_IMPROVE_BROWSING_EXPERIENCE,
+ base::ASCIIToUTF16(chrome::kPrivacyLearnMoreURL)));
+ html_source->AddString(
+ "doNotTrackDialogMessage",
+ l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_ENABLE_DO_NOT_TRACK_DIALOG_TEXT,
+ base::ASCIIToUTF16(chrome::kDoNotTrackLearnMoreURL)));
+ html_source->AddString(
+ "exceptionsLearnMoreURL",
+ base::ASCIIToUTF16(chrome::kContentSettingsExceptionsLearnMoreURL));
+}
+
+void AddSearchInSettingsStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"searchPrompt", IDS_SETTINGS_SEARCH_PROMPT},
+ {"searchNoResults", IDS_SETTINGS_SEARCH_NO_RESULTS},
+ {"searchResults", IDS_SETTINGS_SEARCH_RESULTS},
+ // TODO(dpapad): IDS_DOWNLOAD_CLEAR_SEARCH and IDS_MD_HISTORY_CLEAR_SEARCH
+ // are identical, merge them to one and re-use here.
+ {"clearSearch", IDS_DOWNLOAD_CLEAR_SEARCH},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+
+ base::string16 help_text = l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_SEARCH_NO_RESULTS_HELP,
+ base::ASCIIToUTF16(chrome::kSettingsSearchHelpURL));
+ html_source->AddString("searchNoResultsHelp", help_text);
+}
+
+void AddSearchStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"searchPageTitle", IDS_SETTINGS_SEARCH},
+ {"searchEnginesManage", IDS_SETTINGS_SEARCH_MANAGE_SEARCH_ENGINES},
+ {"searchOkGoogleLabel", IDS_SETTINGS_SEARCH_OK_GOOGLE_LABEL},
+ {"searchOkGoogleSubtextAlwaysOn",
+ IDS_SETTINGS_SEARCH_OK_GOOGLE_SUBTEXT_ALWAYS_ON},
+ {"searchOkGoogleSubtextNoHardware",
+ IDS_SETTINGS_SEARCH_OK_GOOGLE_SUBTEXT_NO_HARDWARE},
+ {"searchOkGoogleAudioHistoryLabel",
+ IDS_SETTINGS_SEARCH_OK_GOOGLE_AUDIO_HISTORY_LABEL},
+ {"searchOkGoogleAudioHistorySubtext",
+ IDS_SETTINGS_SEARCH_OK_GOOGLE_AUDIO_HISTORY_SUBTEXT},
+ {"searchOkGoogleRetrain", IDS_SETTINGS_SEARCH_OK_GOOGLE_RETRAIN},
+ {"searchEnableGoogleNowLabel", IDS_SETTINGS_SEARCH_ENABLE_GOOGLE_NOW},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+ html_source->AddString("hotwordLearnMoreUrl",
+ chrome::kHotwordLearnMoreURL);
+ html_source->AddString("manageAudioHistoryUrl",
+ chrome::kManageAudioHistoryURL);
+ base::string16 search_explanation_text = l10n_util::GetStringFUTF16(
+ IDS_SETTINGS_SEARCH_EXPLANATION,
+ base::ASCIIToUTF16(chrome::kOmniboxLearnMoreURL));
+ html_source->AddString("searchExplanation", search_explanation_text);
+}
+
+void AddSearchEnginesStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"searchEnginesPageTitle", IDS_SETTINGS_SEARCH_ENGINES},
+ {"searchEnginesAddSearchEngine",
+ IDS_SETTINGS_SEARCH_ENGINES_ADD_SEARCH_ENGINE},
+ {"searchEnginesEditSearchEngine",
+ IDS_SETTINGS_SEARCH_ENGINES_EDIT_SEARCH_ENGINE},
+ {"searchEngines", IDS_SETTINGS_SEARCH_ENGINES},
+ {"searchEnginesDefault", IDS_SETTINGS_SEARCH_ENGINES_DEFAULT_ENGINES},
+ {"searchEnginesOther", IDS_SETTINGS_SEARCH_ENGINES_OTHER_ENGINES},
+ {"searchEnginesNoOtherEngines",
+ IDS_SETTINGS_SEARCH_ENGINES_NO_OTHER_ENGINES},
+ {"searchEnginesExtension", IDS_SETTINGS_SEARCH_ENGINES_EXTENSION_ENGINES},
+ {"searchEnginesSearchEngine", IDS_SETTINGS_SEARCH_ENGINES_SEARCH_ENGINE},
+ {"searchEnginesKeyword", IDS_SETTINGS_SEARCH_ENGINES_KEYWORD},
+ {"searchEnginesQueryURL", IDS_SETTINGS_SEARCH_ENGINES_QUERY_URL},
+ {"searchEnginesQueryURLExplanation",
+ IDS_SETTINGS_SEARCH_ENGINES_QUERY_URL_EXPLANATION},
+ {"searchEnginesMakeDefault", IDS_SETTINGS_SEARCH_ENGINES_MAKE_DEFAULT},
+ {"searchEnginesRemoveFromList",
+ IDS_SETTINGS_SEARCH_ENGINES_REMOVE_FROM_LIST},
+ {"searchEnginesManageExtension",
+ IDS_SETTINGS_SEARCH_ENGINES_MANAGE_EXTENSION},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+}
+
+void AddSiteSettingsStrings(content::WebUIDataSource* html_source,
+ Profile* profile) {
+ LocalizedString localized_strings[] = {
+ {"addSite", IDS_SETTINGS_ADD_SITE},
+ {"addSiteExceptionPlaceholder",
+ IDS_SETTINGS_ADD_SITE_EXCEPTION_PLACEHOLDER},
+ {"addSiteTitle", IDS_SETTINGS_ADD_SITE_TITLE},
+ {"cookieAppCache", IDS_SETTINGS_COOKIES_APPLICATION_CACHE},
+ {"cookieCacheStorage", IDS_SETTINGS_COOKIES_CACHE_STORAGE},
+ {"cookieChannelId", IDS_SETTINGS_COOKIES_CHANNEL_ID},
+ {"cookieDatabaseStorage", IDS_SETTINGS_COOKIES_DATABASE_STORAGE},
+ {"cookieFileSystem", IDS_SETTINGS_COOKIES_FILE_SYSTEM},
+ {"cookieFlashLso", IDS_SETTINGS_COOKIES_FLASH_LSO},
+ {"cookieLocalStorage", IDS_SETTINGS_COOKIES_LOCAL_STORAGE},
+ {"cookieMediaLicense", IDS_SETTINGS_COOKIES_MEDIA_LICENSE},
+ {"cookiePlural", IDS_SETTINGS_COOKIES_PLURAL_COOKIES},
+ {"cookieServiceWorker", IDS_SETTINGS_COOKIES_SERVICE_WORKER},
+ {"cookieSingular", IDS_SETTINGS_COOKIES_SINGLE_COOKIE},
+ {"embeddedOnHost", IDS_EXCEPTIONS_GEOLOCATION_EMBEDDED_ON_HOST},
+ {"editSiteTitle", IDS_SETTINGS_EDIT_SITE_TITLE},
+ {"appCacheManifest", IDS_SETTINGS_COOKIES_APPLICATION_CACHE_MANIFEST_LABEL},
+ {"cacheStorageLastModified",
+ IDS_SETTINGS_COOKIES_LOCAL_STORAGE_LAST_MODIFIED_LABEL},
+ {"cacheStorageOrigin", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
+ {"cacheStorageSize", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
+ {"channelIdServerId", IDS_SETTINGS_COOKIES_CHANNEL_ID_ORIGIN_LABEL},
+ {"channelIdType", IDS_SETTINGS_COOKIES_CHANNEL_ID_TYPE_LABEL},
+ {"channelIdCreated", IDS_SETTINGS_COOKIES_CHANNEL_ID_CREATED_LABEL},
+ {"channelIdExpires", IDS_SETTINGS_COOKIES_CHANNEL_ID_EXPIRES_LABEL},
+ {"cookieAccessibleToScript",
+ IDS_SETTINGS_COOKIES_COOKIE_ACCESSIBLE_TO_SCRIPT_LABEL},
+ {"cookieLastAccessed", IDS_SETTINGS_COOKIES_LAST_ACCESSED_LABEL},
+ {"cookieContent", IDS_SETTINGS_COOKIES_COOKIE_CONTENT_LABEL},
+ {"cookieCreated", IDS_SETTINGS_COOKIES_COOKIE_CREATED_LABEL},
+ {"cookieDomain", IDS_SETTINGS_COOKIES_COOKIE_DOMAIN_LABEL},
+ {"cookieExpires", IDS_SETTINGS_COOKIES_COOKIE_EXPIRES_LABEL},
+ {"cookieName", IDS_SETTINGS_COOKIES_COOKIE_NAME_LABEL},
+ {"cookiePath", IDS_SETTINGS_COOKIES_COOKIE_PATH_LABEL},
+ {"cookieSendFor", IDS_SETTINGS_COOKIES_COOKIE_SENDFOR_LABEL},
+ {"fileSystemOrigin", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
+ {"fileSystemPersistentUsage",
+ IDS_SETTINGS_COOKIES_FILE_SYSTEM_PERSISTENT_USAGE_LABEL},
+ {"fileSystemTemporaryUsage",
+ IDS_SETTINGS_COOKIES_FILE_SYSTEM_TEMPORARY_USAGE_LABEL},
+ {"indexedDbSize", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
+ {"indexedDbLastModified",
+ IDS_SETTINGS_COOKIES_LOCAL_STORAGE_LAST_MODIFIED_LABEL},
+ {"indexedDbOrigin", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
+ {"localStorageLastModified",
+ IDS_SETTINGS_COOKIES_LOCAL_STORAGE_LAST_MODIFIED_LABEL},
+ {"localStorageOrigin", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
+ {"localStorageSize", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
+ {"mediaLicenseOrigin", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
+ {"mediaLicenseSize", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
+ {"mediaLicenseLastModified",
+ IDS_SETTINGS_COOKIES_LOCAL_STORAGE_LAST_MODIFIED_LABEL},
+ {"noUsbDevicesFound", IDS_SETTINGS_NO_USB_DEVICES_FOUND},
+ {"serviceWorkerOrigin", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
+ {"serviceWorkerScopes", IDS_SETTINGS_COOKIES_SERVICE_WORKER_SCOPES_LABEL},
+ {"serviceWorkerSize",
+ IDS_SETTINGS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
+ {"webdbDesc", IDS_SETTINGS_COOKIES_WEB_DATABASE_DESCRIPTION_LABEL},
+ {"siteSettingsCategoryPageTitle", IDS_SETTINGS_SITE_SETTINGS_CATEGORY},
+ {"siteSettingsCategoryAllSites", IDS_SETTINGS_SITE_SETTINGS_ALL_SITES},
+ {"siteSettingsCategoryCamera", IDS_SETTINGS_SITE_SETTINGS_CAMERA},
+ {"siteSettingsCameraLabel", IDS_SETTINGS_SITE_SETTINGS_CAMERA_LABEL},
+ {"siteSettingsCategoryCookies", IDS_SETTINGS_SITE_SETTINGS_COOKIES},
+ {"siteSettingsCategoryHandlers", IDS_SETTINGS_SITE_SETTINGS_HANDLERS},
+ {"siteSettingsCategoryImages", IDS_SETTINGS_SITE_SETTINGS_IMAGES},
+ {"siteSettingsCategoryLocation", IDS_SETTINGS_SITE_SETTINGS_LOCATION},
+ {"siteSettingsCategoryJavascript", IDS_SETTINGS_SITE_SETTINGS_JAVASCRIPT},
+ {"siteSettingsCategoryMicrophone", IDS_SETTINGS_SITE_SETTINGS_MIC},
+ {"siteSettingsMicrophoneLabel", IDS_SETTINGS_SITE_SETTINGS_MIC_LABEL},
+ {"siteSettingsCategoryNotifications",
+ IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS},
+ {"siteSettingsCategoryPopups", IDS_SETTINGS_SITE_SETTINGS_POPUPS},
+ {"siteSettingsCategoryZoomLevels", IDS_SETTINGS_SITE_SETTINGS_ZOOM_LEVELS},
+ {"siteSettingsAllSites", IDS_SETTINGS_SITE_SETTINGS_ALL_SITES},
+ {"siteSettingsAutomaticDownloads",
+ IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DOWNLOADS},
+ {"siteSettingsBackgroundSync", IDS_SETTINGS_SITE_SETTINGS_BACKGROUND_SYNC},
+ {"siteSettingsCamera", IDS_SETTINGS_SITE_SETTINGS_CAMERA},
+ {"siteSettingsCookies", IDS_SETTINGS_SITE_SETTINGS_COOKIES},
+ {"siteSettingsHandlers", IDS_SETTINGS_SITE_SETTINGS_HANDLERS},
+ {"siteSettingsLocation", IDS_SETTINGS_SITE_SETTINGS_LOCATION},
+ {"siteSettingsMic", IDS_SETTINGS_SITE_SETTINGS_MIC},
+ {"siteSettingsNotifications", IDS_SETTINGS_SITE_SETTINGS_NOTIFICATIONS},
+ {"siteSettingsImages", IDS_SETTINGS_SITE_SETTINGS_IMAGES},
+ {"siteSettingsJavascript", IDS_SETTINGS_SITE_SETTINGS_JAVASCRIPT},
+ {"siteSettingsFlash", IDS_SETTINGS_SITE_SETTINGS_FLASH},
+ {"siteSettingsPdfDocuments", IDS_SETTINGS_SITE_SETTINGS_PDF_DOCUMENTS},
+ {"siteSettingsPdfDownloadPdfs",
+ IDS_SETTINGS_SITE_SETTINGS_PDF_DOWNLOAD_PDFS},
+ {"siteSettingsProtectedContent",
+ IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT},
+ {"siteSettingsProtectedContentEnable",
+ IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_ENABLE},
+#if defined(OS_CHROMEOS) || defined(OS_WIN)
+ {"siteSettingsProtectedContentIdentifiersExplanation",
+ IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_IDENTIFIERS_EXPLANATION},
+ {"siteSettingsProtectedContentEnableIdentifiers",
+ IDS_SETTINGS_SITE_SETTINGS_PROTECTED_CONTENT_ENABLE_IDENTIFIERS},
+#endif
+ {"siteSettingsPopups", IDS_SETTINGS_SITE_SETTINGS_POPUPS},
+ {"siteSettingsUnsandboxedPlugins",
+ IDS_SETTINGS_SITE_SETTINGS_UNSANDBOXED_PLUGINS},
+ {"siteSettingsMidiDevices", IDS_SETTINGS_SITE_SETTINGS_MIDI_DEVICES},
+ {"siteSettingsMidiDevicesAsk", IDS_SETTINGS_SITE_SETTINGS_MIDI_DEVICES_ASK},
+ {"siteSettingsMidiDevicesAskRecommended",
+ IDS_SETTINGS_SITE_SETTINGS_MIDI_DEVICES_ASK_RECOMMENDED},
+ {"siteSettingsMidiDevicesBlock",
+ IDS_SETTINGS_SITE_SETTINGS_MIDI_DEVICES_BLOCK},
+ {"siteSettingsUsbDevices", IDS_SETTINGS_SITE_SETTINGS_USB_DEVICES},
+ {"siteSettingsRemoveZoomLevel",
+ IDS_SETTINGS_SITE_SETTINGS_REMOVE_ZOOM_LEVEL},
+ {"siteSettingsZoomLevels", IDS_SETTINGS_SITE_SETTINGS_ZOOM_LEVELS},
+ {"siteSettingsNoZoomedSites", IDS_SETTINGS_SITE_SETTINGS_NO_ZOOMED_SITES},
+ {"siteSettingsMaySaveCookies", IDS_SETTINGS_SITE_SETTINGS_MAY_SAVE_COOKIES},
+ {"siteSettingsAskFirst", IDS_SETTINGS_SITE_SETTINGS_ASK_FIRST},
+ {"siteSettingsAskFirstRecommended",
+ IDS_SETTINGS_SITE_SETTINGS_ASK_FIRST_RECOMMENDED},
+ {"siteSettingsAskBeforeAccessing",
+ IDS_SETTINGS_SITE_SETTINGS_ASK_BEFORE_ACCESSING},
+ {"siteSettingsAskBeforeAccessingRecommended",
+ IDS_SETTINGS_SITE_SETTINGS_ASK_BEFORE_ACCESSING_RECOMMENDED},
+ {"siteSettingsAskBeforeSending",
+ IDS_SETTINGS_SITE_SETTINGS_ASK_BEFORE_SENDING},
+ {"siteSettingsAskBeforeSendingRecommended",
+ IDS_SETTINGS_SITE_SETTINGS_ASK_BEFORE_SENDING_RECOMMENDED},
+ {"siteSettingsFlashAllow", IDS_SETTINGS_SITE_SETTINGS_FLASH_ALLOW},
+ {"siteSettingsFlashBlock", IDS_SETTINGS_SITE_SETTINGS_FLASH_BLOCK},
+ {"siteSettingsAllowRecentlyClosedSites",
+ IDS_SETTINGS_SITE_SETTINGS_BACKGROUND_SYNC_ALLOW_RECENTLY_CLOSED_SITES},
+ {"siteSettingsAllowRecentlyClosedSitesRecommended",
+ IDS_SETTINGS_SITE_SETTINGS_BACKGROUND_SYNC_ALLOW_RECENTLY_CLOSED_SITES_RECOMMENDED},
+ {"siteSettingsBackgroundSyncBlocked",
+ IDS_SETTINGS_SITE_SETTINGS_BACKGROUND_SYNC_BLOCKED},
+ {"siteSettingsHandlersAsk", IDS_SETTINGS_SITE_SETTINGS_HANDLERS_ASK},
+ {"siteSettingsHandlersAskRecommended",
+ IDS_SETTINGS_SITE_SETTINGS_HANDLERS_ASK_RECOMMENDED},
+ {"siteSettingsHandlersBlocked",
+ IDS_SETTINGS_SITE_SETTINGS_HANDLERS_BLOCKED},
+ {"siteSettingsAutoDownloadAsk",
+ IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DOWNLOAD_ASK},
+ {"siteSettingsAutoDownloadAskRecommended",
+ IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DOWNLOAD_ASK_RECOMMENDED},
+ {"siteSettingsAutoDownloadBlock",
+ IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DOWNLOAD_BLOCK},
+ {"siteSettingsUnsandboxedPluginsAsk",
+ IDS_SETTINGS_SITE_SETTINGS_UNSANDBOXED_PLUGINS_ASK},
+ {"siteSettingsUnsandboxedPluginsAskRecommended",
+ IDS_SETTINGS_SITE_SETTINGS_UNSANDBOXED_PLUGINS_ASK_RECOMMENDED},
+ {"siteSettingsUnsandboxedPluginsBlock",
+ IDS_SETTINGS_SITE_SETTINGS_UNSANDBOXED_PLUGINS_BLOCK},
+ {"siteSettingsDontShowImages", IDS_SETTINGS_SITE_SETTINGS_DONT_SHOW_IMAGES},
+ {"siteSettingsShowAll", IDS_SETTINGS_SITE_SETTINGS_SHOW_ALL},
+ {"siteSettingsShowAllRecommended",
+ IDS_SETTINGS_SITE_SETTINGS_SHOW_ALL_RECOMMENDED},
+ {"siteSettingsCookiesAllowed",
+ IDS_SETTINGS_SITE_SETTINGS_COOKIES_ALLOW_SITES},
+ {"siteSettingsCookiesAllowedRecommended",
+ IDS_SETTINGS_SITE_SETTINGS_COOKIES_ALLOW_SITES_RECOMMENDED},
+ {"siteSettingsAllow", IDS_SETTINGS_SITE_SETTINGS_ALLOW},
+ {"siteSettingsBlock", IDS_SETTINGS_SITE_SETTINGS_BLOCK},
+ {"siteSettingsSessionOnly", IDS_SETTINGS_SITE_SETTINGS_SESSION_ONLY},
+ {"siteSettingsAllowed", IDS_SETTINGS_SITE_SETTINGS_ALLOWED},
+ {"siteSettingsAllowedRecommended",
+ IDS_SETTINGS_SITE_SETTINGS_ALLOWED_RECOMMENDED},
+ {"siteSettingsBlocked", IDS_SETTINGS_SITE_SETTINGS_BLOCKED},
+ {"siteSettingsBlockedRecommended",
+ IDS_SETTINGS_SITE_SETTINGS_BLOCKED_RECOMMENDED},
+ {"siteSettingsSiteUrl", IDS_SETTINGS_SITE_SETTINGS_SITE_URL},
+ {"siteSettingsActionAllow", IDS_SETTINGS_SITE_SETTINGS_ALLOW_MENU},
+ {"siteSettingsActionBlock", IDS_SETTINGS_SITE_SETTINGS_BLOCK_MENU},
+ {"siteSettingsActionReset", IDS_SETTINGS_SITE_SETTINGS_RESET_MENU},
+ {"siteSettingsActionSessionOnly",
+ IDS_SETTINGS_SITE_SETTINGS_SESSION_ONLY_MENU},
+ {"siteSettingsUsage", IDS_SETTINGS_SITE_SETTINGS_USAGE},
+ {"siteSettingsPermissions", IDS_SETTINGS_SITE_SETTINGS_PERMISSIONS},
+ {"siteSettingsClearAndReset", IDS_SETTINGS_SITE_SETTINGS_CLEAR_BUTTON},
+ {"siteSettingsCookieHeader", IDS_SETTINGS_SITE_SETTINGS_COOKIE_HEADER},
+ {"siteSettingsCookieRemove", IDS_SETTINGS_SITE_SETTINGS_COOKIE_REMOVE},
+ {"siteSettingsCookieRemoveAll",
+ IDS_SETTINGS_SITE_SETTINGS_COOKIE_REMOVE_ALL},
+ {"siteSettingsCookieRemoveAllShown",
+ IDS_SETTINGS_SITE_SETTINGS_COOKIE_REMOVE_ALL_SHOWN},
+ {"siteSettingsCookieRemoveDialogTitle",
+ IDS_SETTINGS_SITE_SETTINGS_COOKIE_REMOVE_DIALOG_TITLE},
+ {"siteSettingsCookieRemoveMultipleConfirmation",
+ IDS_SETTINGS_SITE_SETTINGS_COOKIE_REMOVE_MULTIPLE},
+ {"siteSettingsCookieRemoveSite",
+ IDS_SETTINGS_SITE_SETTINGS_COOKIE_REMOVE_SITE},
+ {"siteSettingsCookiesClearAll",
+ IDS_SETTINGS_SITE_SETTINGS_COOKIES_CLEAR_ALL},
+ {"siteSettingsCookieSearch", IDS_SETTINGS_SITE_SETTINGS_COOKIE_SEARCH},
+ {"siteSettingsCookieSubpage", IDS_SETTINGS_SITE_SETTINGS_COOKIE_SUBPAGE},
+ {"siteSettingsDelete", IDS_SETTINGS_SITE_SETTINGS_DELETE},
+ {"siteSettingsSiteClearAll", IDS_SETTINGS_SITE_SETTINGS_SITE_CLEAR_ALL},
+ {"siteSettingsSiteRemoveConfirmation",
+ IDS_SETTINGS_SITE_SETTINGS_SITE_REMOVE_CONFIRMATION},
+ {"siteSettingsSiteRemoveDialogTitle",
+ IDS_SETTINGS_SITE_SETTINGS_SITE_REMOVE_DIALOG_TITLE},
+ {"thirdPartyCookie", IDS_SETTINGS_SITE_SETTINGS_THIRD_PARTY_COOKIE},
+ {"thirdPartyCookieSublabel",
+ IDS_SETTINGS_SITE_SETTINGS_THIRD_PARTY_COOKIE_SUBLABEL},
+ {"deleteDataPostSession",
+ IDS_SETTINGS_SITE_SETTINGS_DELETE_DATA_POST_SESSION},
+ {"handlerIsDefault", IDS_SETTINGS_SITE_SETTINGS_HANDLER_IS_DEFAULT},
+ {"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},
+ {"incognitoSite", IDS_SETTINGS_SITE_SETTINGS_INCOGNITO},
+ {"incognitoSiteOnly", IDS_SETTINGS_SITE_SETTINGS_INCOGNITO_ONLY},
+ {"embeddedIncognitoSite", IDS_SETTINGS_SITE_SETTINGS_INCOGNITO_EMBEDDED},
+ {"siteSettingsSiteDetails", IDS_SETTINGS_SITE_DETAILS},
+ {"noSitesAdded", IDS_SETTINGS_SITE_NO_SITES_ADDED},
+ {"siteSettingsSubresourceFilter", IDS_SUBRESOURCE_FILTER_HEADER},
+ {"siteSettingsSubresourceFilterAllow", IDS_SUBRESOURCE_FILTER_ALLOW_RADIO},
+ {"siteSettingsSubresourceFilterBlock", IDS_SUBRESOURCE_FILTER_BLOCK_RADIO},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+
+ html_source->AddBoolean("enableSiteSettings",
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableSiteSettings));
+ html_source->AddBoolean(
+ "enableSafeBrowsingSubresourceFilter",
+ base::FeatureList::IsEnabled(
+ subresource_filter::kSafeBrowsingSubresourceFilterExperimentalUI));
+
+ if (PluginUtils::ShouldPreferHtmlOverPlugins(
+ HostContentSettingsMapFactory::GetForProfile(profile))) {
+ LocalizedString flash_strings[] = {
+ {"siteSettingsFlashAskBefore",
+ IDS_SETTINGS_SITE_SETTINGS_FLASH_ASK_BEFORE_RUNNING},
+ {"siteSettingsFlashAskBeforeSubtitle",
+ IDS_SETTINGS_SITE_SETTINGS_FLASH_ASK_BEFORE_RUNNING_SUBTITLE},
+ };
+ AddLocalizedStringsBulk(html_source, flash_strings,
+ arraysize(flash_strings));
+ } else {
+ LocalizedString flash_strings[] = {
+ {"siteSettingsFlashAskBefore",
+ IDS_SETTINGS_SITE_SETTINGS_FLASH_DETECT_IMPORTANT},
+ {"siteSettingsFlashAskBeforeSubtitle",
+ IDS_SETTINGS_SITE_SETTINGS_FLASH_DETECT_IMPORTANT_SUBTITLE},
+ };
+ AddLocalizedStringsBulk(html_source, flash_strings,
+ arraysize(flash_strings));
+ }
+}
+
+#if defined(OS_CHROMEOS)
+void AddUsersStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"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},
+ {"addUsers", IDS_SETTINGS_USERS_ADD_USERS},
+ {"addUsersEmail", IDS_SETTINGS_USERS_ADD_USERS_EMAIL},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+}
+#endif
+
+#if !defined(OS_CHROMEOS)
+void AddSystemStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"systemPageTitle", IDS_SETTINGS_SYSTEM},
+#if !defined(OS_MACOSX)
+ {"backgroundAppsLabel", IDS_SETTINGS_SYSTEM_BACKGROUND_APPS_LABEL},
+#endif
+ {"hardwareAccelerationLabel",
+ IDS_SETTINGS_SYSTEM_HARDWARE_ACCELERATION_LABEL},
+ {"proxySettingsLabel", IDS_SETTINGS_SYSTEM_PROXY_SETTINGS_LABEL},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+
+ // TODO(dbeam): we should probably rename anything involving "localized
+ // strings" to "load time data" as all primitive types are used now.
+ SystemHandler::AddLoadTimeData(html_source);
+}
+#endif
+
+void AddWebContentStrings(content::WebUIDataSource* html_source) {
+ LocalizedString localized_strings[] = {
+ {"webContent", IDS_SETTINGS_WEB_CONTENT},
+ {"pageZoom", IDS_SETTINGS_PAGE_ZOOM_LABEL},
+ {"fontSize", IDS_SETTINGS_FONT_SIZE_LABEL},
+ {"verySmall", IDS_SETTINGS_VERY_SMALL_FONT},
+ {"small", IDS_SETTINGS_SMALL_FONT},
+ {"medium", IDS_SETTINGS_MEDIUM_FONT},
+ {"large", IDS_SETTINGS_LARGE_FONT},
+ {"veryLarge", IDS_SETTINGS_VERY_LARGE_FONT},
+ {"custom", IDS_SETTINGS_CUSTOM},
+ {"customizeFonts", IDS_SETTINGS_CUSTOMIZE_FONTS},
+ {"fonts", IDS_SETTINGS_FONTS},
+ {"standardFont", IDS_SETTINGS_STANDARD_FONT_LABEL},
+ {"serifFont", IDS_SETTINGS_SERIF_FONT_LABEL},
+ {"sansSerifFont", IDS_SETTINGS_SANS_SERIF_FONT_LABEL},
+ {"fixedWidthFont", IDS_SETTINGS_FIXED_WIDTH_FONT_LABEL},
+ {"minimumFont", IDS_SETTINGS_MINIMUM_FONT_SIZE_LABEL},
+ {"tiny", IDS_SETTINGS_TINY_FONT_SIZE},
+ {"huge", IDS_SETTINGS_HUGE_FONT_SIZE},
+ {"loremIpsum", IDS_SETTINGS_LOREM_IPSUM},
+ {"loading", IDS_SETTINGS_LOADING},
+ {"advancedFontSettings", IDS_SETTINGS_ADVANCED_FONT_SETTINGS},
+ {"openAdvancedFontSettings", IDS_SETTINGS_OPEN_ADVANCED_FONT_SETTINGS},
+ {"requiresWebStoreExtension", IDS_SETTINGS_REQUIRES_WEB_STORE_EXTENSION},
+ {"quickBrownFox", IDS_SETTINGS_QUICK_BROWN_FOX},
+ };
+ AddLocalizedStringsBulk(html_source, localized_strings,
+ arraysize(localized_strings));
+}
+
+#if defined(OS_CHROMEOS)
+void AddOncStrings(content::WebUIDataSource* html_source) {
+ LocalizedString onc_property_strings[] = {
+ // Thes strings are generated by prepending 'Onc' to the ONC property
+ // name. Any '.' in the property name is replaced with '-'. Properties
+ // with translatable enumerated values have the value appended after '_'.
+ {"OncCellular-APN-AccessPointName",
+ IDS_ONC_CELLULAR_APN_ACCESS_POINT_NAME},
+ {"OncCellular-APN-AccessPointName_none",
+ IDS_ONC_CELLULAR_APN_ACCESS_POINT_NAME_NONE},
+ {"OncCellular-APN-Password", IDS_ONC_CELLULAR_APN_PASSWORD},
+ {"OncCellular-APN-Username", IDS_ONC_CELLULAR_APN_USERNAME},
+ {"OncCellular-ActivationState", IDS_ONC_CELLULAR_ACTIVATION_STATE},
+ {"OncCellular-ActivationState_Activated",
+ IDS_ONC_CELLULAR_ACTIVATION_STATE_ACTIVATED},
+ {"OncCellular-ActivationState_Activating",
+ IDS_ONC_CELLULAR_ACTIVATION_STATE_ACTIVATING},
+ {"OncCellular-ActivationState_NotActivated",
+ IDS_ONC_CELLULAR_ACTIVATION_STATE_NOT_ACTIVATED},
+ {"OncCellular-ActivationState_PartiallyActivated",
+ IDS_ONC_CELLULAR_ACTIVATION_STATE_PARTIALLY_ACTIVATED},
+ {"OncCellular-Carrier", IDS_ONC_CELLULAR_CARRIER},
+ {"OncCellular-Family", IDS_ONC_CELLULAR_FAMILY},
+ {"OncCellular-FirmwareRevision", IDS_ONC_CELLULAR_FIRMWARE_REVISION},
+ {"OncCellular-HardwareRevision", IDS_ONC_CELLULAR_HARDWARE_REVISION},
+ {"OncCellular-HomeProvider-Code", IDS_ONC_CELLULAR_HOME_PROVIDER_CODE},
+ {"OncCellular-HomeProvider-Country",
+ IDS_ONC_CELLULAR_HOME_PROVIDER_COUNTRY},
+ {"OncCellular-HomeProvider-Name", IDS_ONC_CELLULAR_HOME_PROVIDER_NAME},
+ {"OncCellular-Manufacturer", IDS_ONC_CELLULAR_MANUFACTURER},
+ {"OncCellular-ModelID", IDS_ONC_CELLULAR_MODEL_ID},
+ {"OncCellular-NetworkTechnology", IDS_ONC_CELLULAR_NETWORK_TECHNOLOGY},
+ {"OncCellular-PRLVersion", IDS_ONC_CELLULAR_PRL_VERSION},
+ {"OncCellular-RoamingState", IDS_ONC_CELLULAR_ROAMING_STATE},
+ {"OncCellular-RoamingState_Home", IDS_ONC_CELLULAR_ROAMING_STATE_HOME},
+ {"OncCellular-RoamingState_Roaming",
+ IDS_ONC_CELLULAR_ROAMING_STATE_ROAMING},
+ {"OncCellular-ServingOperator-Code",
+ IDS_ONC_CELLULAR_SERVING_OPERATOR_CODE},
+ {"OncCellular-ServingOperator-Name",
+ IDS_ONC_CELLULAR_SERVING_OPERATOR_NAME},
+ {"OncConnected", IDS_ONC_CONNECTED},
+ {"OncConnecting", IDS_ONC_CONNECTING},
+ {"OncEAP-AnonymousIdentity", IDS_ONC_EAP_ANONYMOUS_IDENTITY},
+ {"OncEAP-Identity", IDS_ONC_EAP_IDENTITY},
+ {"OncEAP-Inner", IDS_ONC_EAP_INNER},
+ {"OncEAP-Inner_Automatic", IDS_ONC_EAP_INNER_AUTOMATIC},
+ {"OncEAP-Inner_CHAP", IDS_ONC_EAP_INNER_CHAP},
+ {"OncEAP-Inner_GTC", IDS_ONC_EAP_INNER_GTC},
+ {"OncEAP-Inner_MD5", IDS_ONC_EAP_INNER_MD5},
+ {"OncEAP-Inner_MSCHAP", IDS_ONC_EAP_INNER_MSCHAP},
+ {"OncEAP-Inner_MSCHAPv2", IDS_ONC_EAP_INNER_MSCHAPV2},
+ {"OncEAP-Inner_PAP", IDS_ONC_EAP_INNER_PAP},
+ {"OncEAP-Outer", IDS_ONC_EAP_OUTER},
+ {"OncEAP-Outer_LEAP", IDS_ONC_EAP_OUTER_LEAP},
+ {"OncEAP-Outer_PEAP", IDS_ONC_EAP_OUTER_PEAP},
+ {"OncEAP-Outer_EAP-TLS", IDS_ONC_EAP_OUTER_TLS},
+ {"OncEAP-Outer_EAP-TTLS", IDS_ONC_EAP_OUTER_TTLS},
+ {"OncEAP-Password", IDS_ONC_WIFI_PASSWORD},
+ {"OncEAP-SubjectMatch", IDS_ONC_EAP_SUBJECT_MATCH},
+ {"OncMacAddress", IDS_ONC_MAC_ADDRESS},
+ {"OncNotConnected", IDS_ONC_NOT_CONNECTED},
+ {"OncRestrictedConnectivity", IDS_ONC_RESTRICTED_CONNECTIVITY},
+ {"OncTether-BatteryPercentage", IDS_ONC_TETHER_BATTERY_PERCENTAGE},
+ {"OncTether-BatteryPercentage_Value",
+ IDS_ONC_TETHER_BATTERY_PERCENTAGE_VALUE},
+ {"OncTether-SignalStrength", IDS_ONC_TETHER_SIGNAL_STRENGTH},
+ {"OncTether-SignalStrength_Weak", IDS_ONC_TETHER_SIGNAL_STRENGTH_WEAK},
+ {"OncTether-SignalStrength_Okay", IDS_ONC_TETHER_SIGNAL_STRENGTH_OKAY},
+ {"OncTether-SignalStrength_Good", IDS_ONC_TETHER_SIGNAL_STRENGTH_GOOD},
+ {"OncTether-SignalStrength_Strong",
+ IDS_ONC_TETHER_SIGNAL_STRENGTH_STRONG},
+ {"OncTether-SignalStrength_VeryStrong",
+ IDS_ONC_TETHER_SIGNAL_STRENGTH_VERY_STRONG},
+ {"OncTether-Carrier", IDS_ONC_TETHER_CARRIER},
+ {"OncTether-Carrier_Unknown", IDS_ONC_TETHER_CARRIER_UNKNOWN},
+ {"OncVPN-Host", IDS_ONC_VPN_HOST},
+ {"OncVPN-L2TP-Username", IDS_ONC_VPN_L2TP_USERNAME},
+ {"OncVPN-OpenVPN-Username", IDS_ONC_VPN_OPEN_VPN_USERNAME},
+ {"OncVPN-ThirdPartyVPN-ProviderName",
+ IDS_ONC_VPN_THIRD_PARTY_VPN_PROVIDER_NAME},
+ {"OncVPN-Type", IDS_ONC_VPN_TYPE},
+ {"OncWiFi-Frequency", IDS_ONC_WIFI_FREQUENCY},
+ {"OncWiFi-Passphrase", IDS_ONC_WIFI_PASSWORD},
+ {"OncWiFi-SSID", IDS_ONC_WIFI_SSID},
+ {"OncWiFi-Security", IDS_ONC_WIFI_SECURITY},
+ {"OncWiFi-Security_None", IDS_ONC_WIFI_SECURITY_NONE},
+ {"OncWiFi-Security_WEP-PSK", IDS_ONC_WIFI_SECURITY_WEP},
+ {"OncWiFi-Security_WPA-EAP", IDS_ONC_WIFI_SECURITY_EAP},
+ {"OncWiFi-Security_WPA-PSK", IDS_ONC_WIFI_SECURITY_PSK},
+ {"OncWiFi-Security_WEP-8021X", IDS_ONC_WIFI_SECURITY_EAP},
+ {"OncWiFi-SignalStrength", IDS_ONC_WIFI_SIGNAL_STRENGTH},
+ {"OncWiMAX-EAP-Identity", IDS_ONC_WIMAX_EAP_IDENTITY},
+ {"Oncipv4-Gateway", IDS_ONC_IPV4_GATEWAY},
+ {"Oncipv4-IPAddress", IDS_ONC_IPV4_ADDRESS},
+ {"Oncipv4-RoutingPrefix", IDS_ONC_IPV4_ROUTING_PREFIX},
+ {"Oncipv6-IPAddress", IDS_ONC_IPV6_ADDRESS},
+ };
+ AddLocalizedStringsBulk(html_source, onc_property_strings,
+ arraysize(onc_property_strings));
+}
+#endif // OS_CHROMEOS
+
+} // namespace
+
+void AddLocalizedStrings(content::WebUIDataSource* html_source,
+ Profile* profile) {
+ AddA11yStrings(html_source);
+ AddAboutStrings(html_source);
+ AddAppearanceStrings(html_source, profile);
+ AddClearBrowsingDataStrings(html_source);
+ AddCommonStrings(html_source, profile);
+ AddDownloadsStrings(html_source);
+ AddLanguagesStrings(html_source);
+ AddOnStartupStrings(html_source);
+ AddPasswordsAndFormsStrings(html_source);
+ AddPeopleStrings(html_source);
+ AddPrintingStrings(html_source);
+ AddPrivacyStrings(html_source, profile);
+ AddResetStrings(html_source);
+ AddSearchEnginesStrings(html_source);
+ AddSearchInSettingsStrings(html_source);
+ AddSearchStrings(html_source);
+ AddSiteSettingsStrings(html_source, profile);
+ AddWebContentStrings(html_source);
+
+#if defined(OS_CHROMEOS)
+ AddAccountUITweaksStrings(html_source, profile);
+ AddAndroidAppStrings(html_source);
+ AddBluetoothStrings(html_source);
+ AddChromeOSUserStrings(html_source, profile);
+ AddDateTimeStrings(html_source);
+ AddDeviceStrings(html_source);
+ AddEasyUnlockStrings(html_source);
+ AddInternetStrings(html_source);
+ AddOncStrings(html_source);
+ AddUsersStrings(html_source);
+#else
+ AddDefaultBrowserStrings(html_source);
+ AddImportDataStrings(html_source);
+ AddSystemStrings(html_source);
+#endif
+
+#if defined(USE_NSS_CERTS)
+ AddCertificateManagerStrings(html_source);
+#endif
+
+#if defined(OS_CHROMEOS)
+ chromeos::network_element::AddLocalizedStrings(html_source);
+#endif
+ policy_indicator::AddLocalizedStrings(html_source);
+
+ html_source->SetJsonPath(kLocalizedStringsFile);
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.h b/chromium/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.h
new file mode 100644
index 00000000000..cfa37864dc0
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.h
@@ -0,0 +1,24 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_MD_SETTINGS_LOCALIZED_STRINGS_PROVIDER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_MD_SETTINGS_LOCALIZED_STRINGS_PROVIDER_H_
+
+class Profile;
+
+namespace content {
+class WebUIDataSource;
+}
+
+namespace settings {
+
+// Adds the strings needed by the settings page to |html_source|. This function
+// causes |html_source| to expose a strings.js file from its source which
+// contains a mapping from string's name to its translated value.
+void AddLocalizedStrings(content::WebUIDataSource* html_source,
+ Profile* profile);
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_MD_SETTINGS_LOCALIZED_STRINGS_PROVIDER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chromium/chrome/browser/ui/webui/settings/md_settings_ui.cc
new file mode 100644
index 00000000000..d073d8dfa66
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -0,0 +1,265 @@
+// 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.
+
+#include "chrome/browser/ui/webui/settings/md_settings_ui.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/metrics_handler.h"
+#include "chrome/browser/ui/webui/settings/about_handler.h"
+#include "chrome/browser/ui/webui/settings/appearance_handler.h"
+#include "chrome/browser/ui/webui/settings/browser_lifetime_handler.h"
+#include "chrome/browser/ui/webui/settings/downloads_handler.h"
+#include "chrome/browser/ui/webui/settings/extension_control_handler.h"
+#include "chrome/browser/ui/webui/settings/font_handler.h"
+#include "chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.h"
+#include "chrome/browser/ui/webui/settings/metrics_reporting_handler.h"
+#include "chrome/browser/ui/webui/settings/on_startup_handler.h"
+#include "chrome/browser/ui/webui/settings/people_handler.h"
+#include "chrome/browser/ui/webui/settings/profile_info_handler.h"
+#include "chrome/browser/ui/webui/settings/protocol_handlers_handler.h"
+#include "chrome/browser/ui/webui/settings/reset_settings_handler.h"
+#include "chrome/browser/ui/webui/settings/safe_browsing_handler.h"
+#include "chrome/browser/ui/webui/settings/search_engines_handler.h"
+#include "chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h"
+#include "chrome/browser/ui/webui/settings/settings_cookies_view_handler.h"
+#include "chrome/browser/ui/webui/settings/settings_import_data_handler.h"
+#include "chrome/browser/ui/webui/settings/settings_media_devices_selection_handler.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "chrome/browser/ui/webui/settings/settings_startup_pages_handler.h"
+#include "chrome/browser/ui/webui/settings/site_settings_handler.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/settings_resources.h"
+#include "chrome/grit/settings_resources_map.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+#if defined(OS_WIN) || defined(OS_CHROMEOS)
+#include "chrome/browser/ui/webui/settings/languages_handler.h"
+#endif // defined(OS_WIN) || defined(OS_CHROMEOS)
+
+#if defined(OS_CHROMEOS)
+#include "ash/system/palette/palette_utils.h"
+#include "ash/system/power/power_status.h"
+#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h"
+#include "chrome/browser/ui/ash/ash_util.h"
+#include "chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/android_apps_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/date_time_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/device_power_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/easy_unlock_settings_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/internet_handler.h"
+#include "chrome/common/chrome_switches.h"
+#include "components/arc/arc_util.h"
+#else // !defined(OS_CHROMEOS)
+#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"
+#endif // defined(OS_CHROMEOS)
+
+#if defined(USE_NSS_CERTS)
+#include "chrome/browser/ui/webui/settings/certificates_handler.h"
+#elif defined(OS_WIN) || defined(OS_MACOSX)
+#include "chrome/browser/ui/webui/settings/native_certificates_handler.h"
+#endif // defined(USE_NSS_CERTS)
+
+namespace settings {
+
+// static
+void MdSettingsUI::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterBooleanPref(prefs::kImportDialogAutofillFormData, true);
+ registry->RegisterBooleanPref(prefs::kImportDialogBookmarks, true);
+ registry->RegisterBooleanPref(prefs::kImportDialogHistory, true);
+ registry->RegisterBooleanPref(prefs::kImportDialogSavedPasswords, true);
+ registry->RegisterBooleanPref(prefs::kImportDialogSearchEngine, true);
+}
+
+MdSettingsUI::MdSettingsUI(content::WebUI* web_ui, const GURL& url)
+ : content::WebUIController(web_ui),
+ WebContentsObserver(web_ui->GetWebContents()) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ AddSettingsPageUIHandler(base::MakeUnique<AppearanceHandler>(web_ui));
+
+#if defined(USE_NSS_CERTS)
+ AddSettingsPageUIHandler(base::MakeUnique<CertificatesHandler>(false));
+#elif defined(OS_WIN) || defined(OS_MACOSX)
+ AddSettingsPageUIHandler(base::MakeUnique<NativeCertificatesHandler>());
+#endif // defined(USE_NSS_CERTS)
+
+ AddSettingsPageUIHandler(base::MakeUnique<BrowserLifetimeHandler>());
+ AddSettingsPageUIHandler(base::MakeUnique<ClearBrowsingDataHandler>(web_ui));
+ AddSettingsPageUIHandler(base::MakeUnique<CookiesViewHandler>());
+ AddSettingsPageUIHandler(base::MakeUnique<DownloadsHandler>(profile));
+ AddSettingsPageUIHandler(base::MakeUnique<ExtensionControlHandler>());
+ AddSettingsPageUIHandler(base::MakeUnique<FontHandler>(web_ui));
+ AddSettingsPageUIHandler(base::MakeUnique<ImportDataHandler>());
+
+#if defined(OS_WIN) || defined(OS_CHROMEOS)
+ AddSettingsPageUIHandler(base::MakeUnique<LanguagesHandler>(web_ui));
+#endif // defined(OS_WIN) || defined(OS_CHROMEOS)
+
+ AddSettingsPageUIHandler(
+ base::MakeUnique<MediaDevicesSelectionHandler>(profile));
+#if defined(GOOGLE_CHROME_BUILD) && !defined(OS_CHROMEOS)
+ AddSettingsPageUIHandler(base::MakeUnique<MetricsReportingHandler>());
+#endif
+ AddSettingsPageUIHandler(base::MakeUnique<OnStartupHandler>());
+ AddSettingsPageUIHandler(base::MakeUnique<PeopleHandler>(profile));
+ AddSettingsPageUIHandler(base::MakeUnique<ProfileInfoHandler>(profile));
+ AddSettingsPageUIHandler(base::MakeUnique<ProtocolHandlersHandler>());
+ AddSettingsPageUIHandler(
+ base::MakeUnique<SafeBrowsingHandler>(profile->GetPrefs()));
+ AddSettingsPageUIHandler(base::MakeUnique<SearchEnginesHandler>(profile));
+ AddSettingsPageUIHandler(base::MakeUnique<SiteSettingsHandler>(profile));
+ AddSettingsPageUIHandler(base::MakeUnique<StartupPagesHandler>(web_ui));
+
+#if defined(OS_CHROMEOS)
+ AddSettingsPageUIHandler(
+ base::MakeUnique<chromeos::settings::AccessibilityHandler>(web_ui));
+ AddSettingsPageUIHandler(
+ base::MakeUnique<chromeos::settings::AndroidAppsHandler>(profile));
+ AddSettingsPageUIHandler(
+ base::MakeUnique<chromeos::settings::ChangePictureHandler>());
+ AddSettingsPageUIHandler(
+ base::MakeUnique<chromeos::settings::CupsPrintersHandler>(web_ui));
+ AddSettingsPageUIHandler(
+ base::MakeUnique<chromeos::settings::FingerprintHandler>(profile));
+ AddSettingsPageUIHandler(
+ base::MakeUnique<chromeos::settings::KeyboardHandler>(web_ui));
+ AddSettingsPageUIHandler(
+ base::MakeUnique<chromeos::settings::PointerHandler>());
+ AddSettingsPageUIHandler(
+ base::MakeUnique<chromeos::settings::StorageHandler>());
+ AddSettingsPageUIHandler(
+ base::MakeUnique<chromeos::settings::StylusHandler>());
+ AddSettingsPageUIHandler(
+ base::MakeUnique<chromeos::settings::InternetHandler>());
+#else
+ AddSettingsPageUIHandler(base::MakeUnique<DefaultBrowserHandler>(web_ui));
+ AddSettingsPageUIHandler(base::MakeUnique<ManageProfileHandler>(profile));
+ AddSettingsPageUIHandler(base::MakeUnique<SystemHandler>());
+#endif
+
+ // Host must be derived from the visible URL, since this might be serving
+ // either chrome://settings or chrome://md-settings.
+ CHECK(url.GetOrigin() == GURL(chrome::kChromeUISettingsURL).GetOrigin() ||
+ url.GetOrigin() == GURL(chrome::kChromeUIMdSettingsURL).GetOrigin());
+
+ content::WebUIDataSource* html_source =
+ content::WebUIDataSource::Create(url.host());
+ html_source->AddString("hostname", url.host());
+
+#if defined(OS_CHROMEOS)
+ chromeos::settings::EasyUnlockSettingsHandler* easy_unlock_handler =
+ chromeos::settings::EasyUnlockSettingsHandler::Create(html_source,
+ profile);
+ if (easy_unlock_handler)
+ AddSettingsPageUIHandler(base::WrapUnique(easy_unlock_handler));
+
+ AddSettingsPageUIHandler(base::WrapUnique(
+ chromeos::settings::DateTimeHandler::Create(html_source)));
+
+ AddSettingsPageUIHandler(
+ base::MakeUnique<chromeos::settings::StylusHandler>());
+ html_source->AddBoolean(
+ "quickUnlockEnabled",
+ chromeos::quick_unlock::IsPinEnabled(profile->GetPrefs()));
+ html_source->AddBoolean("fingerprintUnlockEnabled",
+ chromeos::quick_unlock::IsFingerprintEnabled());
+ html_source->AddBoolean("androidAppsAllowed",
+ arc::IsArcAllowedForProfile(profile) &&
+ !arc::IsArcOptInVerificationDisabled());
+
+ // TODO(mash): Support Chrome power settings in Mash. crbug.com/644348
+ bool enable_power_settings =
+ !ash_util::IsRunningInMash() &&
+ (switches::PowerOverlayEnabled() ||
+ (ash::PowerStatus::Get()->IsBatteryPresent() &&
+ ash::PowerStatus::Get()->SupportsDualRoleDevices()));
+ html_source->AddBoolean("enablePowerSettings", enable_power_settings);
+ if (enable_power_settings) {
+ AddSettingsPageUIHandler(
+ base::MakeUnique<chromeos::settings::PowerHandler>());
+ }
+#endif
+
+ AddSettingsPageUIHandler(
+ base::WrapUnique(AboutHandler::Create(html_source, profile)));
+ AddSettingsPageUIHandler(
+ base::WrapUnique(ResetSettingsHandler::Create(html_source, profile)));
+
+ // Add the metrics handler to write uma stats.
+ web_ui->AddMessageHandler(base::MakeUnique<MetricsHandler>());
+
+#if BUILDFLAG(USE_VULCANIZE)
+ html_source->AddResourcePath("crisper.js", IDR_MD_SETTINGS_CRISPER_JS);
+ html_source->AddResourcePath("lazy_load.crisper.js",
+ IDR_MD_SETTINGS_LAZY_LOAD_CRISPER_JS);
+ html_source->AddResourcePath("lazy_load.html",
+ IDR_MD_SETTINGS_LAZY_LOAD_VULCANIZED_HTML);
+ html_source->SetDefaultResource(IDR_MD_SETTINGS_VULCANIZED_HTML);
+ html_source->UseGzip(std::unordered_set<std::string>());
+#else
+ // Add all settings resources.
+ for (size_t i = 0; i < kSettingsResourcesSize; ++i) {
+ html_source->AddResourcePath(kSettingsResources[i].name,
+ kSettingsResources[i].value);
+ }
+ html_source->SetDefaultResource(IDR_SETTINGS_SETTINGS_HTML);
+#endif
+
+ AddLocalizedStrings(html_source, profile);
+
+ content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
+ html_source);
+}
+
+MdSettingsUI::~MdSettingsUI() {
+}
+
+void MdSettingsUI::AddSettingsPageUIHandler(
+ std::unique_ptr<SettingsPageUIHandler> handler) {
+ DCHECK(handler);
+ handlers_.insert(handler.get());
+ web_ui()->AddMessageHandler(std::move(handler));
+}
+
+void MdSettingsUI::DidStartNavigation(
+ content::NavigationHandle* navigation_handle) {
+ if (navigation_handle->IsSameDocument())
+ return;
+
+ load_start_time_ = base::Time::Now();
+}
+
+void MdSettingsUI::DocumentLoadedInFrame(
+ content::RenderFrameHost* render_frame_host) {
+ UMA_HISTOGRAM_TIMES("Settings.LoadDocumentTime.MD",
+ base::Time::Now() - load_start_time_);
+}
+
+void MdSettingsUI::DocumentOnLoadCompletedInMainFrame() {
+ UMA_HISTOGRAM_TIMES("Settings.LoadCompletedTime.MD",
+ base::Time::Now() - load_start_time_);
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/md_settings_ui.h b/chromium/chrome/browser/ui/webui/settings/md_settings_ui.h
new file mode 100644
index 00000000000..2262702c736
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/md_settings_ui.h
@@ -0,0 +1,54 @@
+// Copyright (c) 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_MD_SETTINGS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_MD_SETTINGS_UI_H_
+
+#include <unordered_set>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_ui_controller.h"
+
+class GURL;
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+namespace settings {
+
+class SettingsPageUIHandler;
+
+// The WebUI handler for chrome://md-settings.
+class MdSettingsUI : public content::WebUIController,
+ public content::WebContentsObserver {
+ public:
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ MdSettingsUI(content::WebUI* web_ui, const GURL& url);
+ ~MdSettingsUI() override;
+
+ // content::WebContentsObserver:
+ void DidStartNavigation(
+ content::NavigationHandle* navigation_handle) override;
+ void DocumentLoadedInFrame(
+ content::RenderFrameHost *render_frame_host) override;
+ void DocumentOnLoadCompletedInMainFrame() override;
+
+ private:
+ void AddSettingsPageUIHandler(std::unique_ptr<SettingsPageUIHandler> handler);
+
+ // Weak references; all |handlers_| are owned by |web_ui()|.
+ std::unordered_set<SettingsPageUIHandler*> handlers_;
+
+ base::Time load_start_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(MdSettingsUI);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_MD_SETTINGS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/md_settings_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/settings/md_settings_ui_browsertest.cc
new file mode 100644
index 00000000000..a6da5edc656
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/md_settings_ui_browsertest.cc
@@ -0,0 +1,67 @@
+// 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 <string>
+
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.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 "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "content/public/common/url_constants.h"
+#include "content/public/test/browser_test_utils.h"
+#include "ui/base/window_open_disposition.h"
+#include "url/gurl.h"
+
+typedef InProcessBrowserTest MdSettingsUITest;
+
+using ui_test_utils::NavigateToURL;
+using content::WaitForLoadStop;
+
+IN_PROC_BROWSER_TEST_F(MdSettingsUITest, ViewSourceDoesntCrash) {
+ NavigateToURL(browser(), GURL(content::kViewSourceScheme + std::string(":") +
+ chrome::kChromeUIMdSettingsURL +
+ std::string("strings.js")));
+}
+
+// May not complete on memory and Windows debug bots. TODO(dbeam): investigate
+// and fix. See https://crbug.com/558434, https://crbug.com/620370 and
+// https://crbug.com/651296.
+#if defined(MEMORY_SANITIZER) || defined(OS_WIN) || defined(OS_CHROMEOS)
+#define MAYBE_BackForwardDoesntCrash DISABLED_BackForwardDoesntCrash
+#else
+#define MAYBE_BackForwardDoesntCrash BackForwardDoesntCrash
+#endif
+
+IN_PROC_BROWSER_TEST_F(MdSettingsUITest, MAYBE_BackForwardDoesntCrash) {
+ NavigateToURL(browser(), GURL(chrome::kChromeUIMdSettingsURL));
+
+ NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
+
+ chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB);
+ WaitForLoadStop(browser()->tab_strip_model()->GetActiveWebContents());
+}
+
+// Catch lifetime issues in message handlers. There was previously a problem
+// with PrefMember calling Init again after Destroy.
+IN_PROC_BROWSER_TEST_F(MdSettingsUITest, ToggleJavaScript) {
+ NavigateToURL(browser(), GURL(chrome::kChromeUIMdSettingsURL));
+
+ const auto& handlers = *browser()
+ ->tab_strip_model()
+ ->GetActiveWebContents()
+ ->GetWebUI()
+ ->GetHandlersForTesting();
+
+ for (const std::unique_ptr<content::WebUIMessageHandler>& handler :
+ handlers) {
+ handler->AllowJavascriptForTesting();
+ handler->DisallowJavascript();
+ handler->AllowJavascriptForTesting();
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/settings/metrics_reporting_handler.cc b/chromium/chrome/browser/ui/webui/settings/metrics_reporting_handler.cc
new file mode 100644
index 00000000000..77b1b4e6b22
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/metrics_reporting_handler.cc
@@ -0,0 +1,106 @@
+// 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.
+
+#if defined(GOOGLE_CHROME_BUILD) && !defined(OS_CHROMEOS)
+
+#include "chrome/browser/ui/webui/settings/metrics_reporting_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
+#include "chrome/browser/metrics/metrics_reporting_state.h"
+#include "components/metrics/metrics_pref_names.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_ui.h"
+
+namespace settings {
+
+MetricsReportingHandler::MetricsReportingHandler() {}
+MetricsReportingHandler::~MetricsReportingHandler() {}
+
+void MetricsReportingHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("getMetricsReporting",
+ base::Bind(&MetricsReportingHandler::HandleGetMetricsReporting,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("setMetricsReportingEnabled",
+ base::Bind(&MetricsReportingHandler::HandleSetMetricsReportingEnabled,
+ base::Unretained(this)));
+}
+
+void MetricsReportingHandler::OnJavascriptAllowed() {
+ pref_member_ = base::MakeUnique<BooleanPrefMember>();
+ pref_member_->Init(metrics::prefs::kMetricsReportingEnabled,
+ g_browser_process->local_state(),
+ base::Bind(&MetricsReportingHandler::OnPrefChanged,
+ base::Unretained(this)));
+
+ policy_registrar_ = base::MakeUnique<policy::PolicyChangeRegistrar>(
+ g_browser_process->policy_service(),
+ policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string()));
+ policy_registrar_->Observe(policy::key::kMetricsReportingEnabled,
+ base::Bind(&MetricsReportingHandler::OnPolicyChanged,
+ base::Unretained(this)));
+}
+
+void MetricsReportingHandler::OnJavascriptDisallowed() {
+ pref_member_.reset();
+ policy_registrar_.reset();
+}
+
+void MetricsReportingHandler::HandleGetMetricsReporting(
+ const base::ListValue* args) {
+ AllowJavascript();
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+ ResolveJavascriptCallback(*callback_id, *CreateMetricsReportingDict());
+}
+
+std::unique_ptr<base::DictionaryValue>
+ MetricsReportingHandler::CreateMetricsReportingDict() {
+ std::unique_ptr<base::DictionaryValue> dict(
+ base::MakeUnique<base::DictionaryValue>());
+ dict->SetBoolean("enabled",
+ ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled());
+ dict->SetBoolean("managed", IsMetricsReportingPolicyManaged());
+ return dict;
+}
+
+void MetricsReportingHandler::HandleSetMetricsReportingEnabled(
+ const base::ListValue* args) {
+ if (IsMetricsReportingPolicyManaged()) {
+ NOTREACHED();
+ // NOTE: ChangeMetricsReportingState() already checks whether metrics
+ // reporting is managed by policy. Also, the UI really shouldn't be able to
+ // send this message when managed. However, in this specific case, there's a
+ // sane, graceful fallback so we might as well do that.
+ SendMetricsReportingChange();
+ return;
+ }
+
+ bool enabled;
+ CHECK(args->GetBoolean(0, &enabled));
+ ChangeMetricsReportingState(enabled);
+}
+
+void MetricsReportingHandler::OnPolicyChanged(const base::Value* previous,
+ const base::Value* current) {
+ SendMetricsReportingChange();
+}
+
+void MetricsReportingHandler::OnPrefChanged(const std::string& pref_name) {
+ DCHECK_EQ(metrics::prefs::kMetricsReportingEnabled, pref_name);
+ SendMetricsReportingChange();
+}
+
+void MetricsReportingHandler::SendMetricsReportingChange() {
+ FireWebUIListener("metrics-reporting-change", *CreateMetricsReportingDict());
+}
+
+} // namespace settings
+
+#endif // defined(GOOGLE_CHROME_BUILD) && !defined(OS_CHROMEOS)
diff --git a/chromium/chrome/browser/ui/webui/settings/metrics_reporting_handler.h b/chromium/chrome/browser/ui/webui/settings/metrics_reporting_handler.h
new file mode 100644
index 00000000000..73cc5bd9572
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/metrics_reporting_handler.h
@@ -0,0 +1,73 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_METRICS_REPORTING_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_METRICS_REPORTING_HANDLER_H_
+
+#if defined(GOOGLE_CHROME_BUILD) && !defined(OS_CHROMEOS)
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "components/policy/core/common/policy_service.h"
+#include "components/prefs/pref_member.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace settings {
+
+class MetricsReportingHandler : public SettingsPageUIHandler {
+ public:
+ MetricsReportingHandler();
+ ~MetricsReportingHandler() override;
+
+ // SettingsPageUIHandler:
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ protected:
+ // Handler for "getMetricsReporting" message. No arguments. Protected for
+ // testing.
+ void HandleGetMetricsReporting(const base::ListValue* args);
+
+ private:
+ // Describes the state of metrics reporting in a base::DictionaryValue.
+ // Friends with ChromeMetricsServiceAccessor.
+ std::unique_ptr<base::DictionaryValue> CreateMetricsReportingDict();
+
+ // Handler for "setMetricsReportingEnabled" message. Passed a single,
+ // |enabled| boolean argument.
+ void HandleSetMetricsReportingEnabled(const base::ListValue* args);
+
+ // Called when the policies that affect whether metrics reporting is managed
+ // change.
+ void OnPolicyChanged(const base::Value* current_policy,
+ const base::Value* previous_policy);
+
+ // Called when the local state pref controlling metrics reporting changes.
+ void OnPrefChanged(const std::string& pref_name);
+
+ // Sends a "metrics-reporting-change" WebUI listener event to the page.
+ void SendMetricsReportingChange();
+
+ // Used to track pref changes that affect whether metrics reporting is
+ // enabled.
+ std::unique_ptr<BooleanPrefMember> pref_member_;
+
+ // Used to track policy changes that affect whether metrics reporting is
+ // enabled or managed.
+ std::unique_ptr<policy::PolicyChangeRegistrar> policy_registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(MetricsReportingHandler);
+};
+
+} // namespace settings
+
+#endif // defined(GOOGLE_CHROME_BUILD) && !defined(OS_CHROMEOS)
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_METRICS_REPORTING_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/metrics_reporting_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/metrics_reporting_handler_unittest.cc
new file mode 100644
index 00000000000..2452adb6c79
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/metrics_reporting_handler_unittest.cc
@@ -0,0 +1,128 @@
+// 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.
+
+#if defined(GOOGLE_CHROME_BUILD) && !defined(OS_CHROMEOS)
+
+#include "chrome/browser/ui/webui/settings/metrics_reporting_handler.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/values.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "components/metrics/metrics_pref_names.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_web_ui.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace settings {
+
+class TestingMetricsReportingHandler : public MetricsReportingHandler {
+ public:
+ using MetricsReportingHandler::set_web_ui;
+ using MetricsReportingHandler::HandleGetMetricsReporting;
+};
+
+class MetricsReportingHandlerTest : public testing::Test {
+ public:
+ MetricsReportingHandlerTest() {
+ // Local state must be set up before |handler_|.
+ local_state_.reset(new ScopedTestingLocalState(
+ TestingBrowserProcess::GetGlobal()));
+
+ handler_.reset(new TestingMetricsReportingHandler);
+ handler_->set_web_ui(&test_web_ui_);
+
+ EXPECT_CALL(provider_, IsInitializationComplete(testing::_)).WillRepeatedly(
+ testing::Return(true));
+ policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
+ }
+
+ void SetUp() override {
+ ASSERT_EQ(local_state(), g_browser_process->local_state());
+ EXPECT_TRUE(test_web_ui()->call_data().empty());
+
+ base::ListValue args;
+ args.Append(base::MakeUnique<base::Value>(1));
+ handler()->HandleGetMetricsReporting(&args);
+
+ EXPECT_TRUE(handler()->IsJavascriptAllowed());
+ EXPECT_EQ(1u, test_web_ui()->call_data().size());
+
+ test_web_ui()->ClearTrackedCalls();
+ }
+
+ void TearDown() override {
+ // For crbug.com/637068 which only run on official bots with no try jobs.
+ base::RunLoop().RunUntilIdle();
+ handler_.reset();
+ base::RunLoop().RunUntilIdle();
+ local_state_.reset();
+ base::RunLoop().RunUntilIdle();
+ }
+
+ PrefService* local_state() { return local_state_->Get(); }
+ TestingMetricsReportingHandler* handler() { return handler_.get(); }
+ content::TestWebUI* test_web_ui() { return &test_web_ui_; }
+ policy::PolicyMap* map() { return &map_; }
+ policy::MockConfigurationPolicyProvider* provider() { return &provider_; }
+
+ private:
+ content::TestBrowserThreadBundle thread_bundle_;
+ content::TestWebUI test_web_ui_;
+ std::unique_ptr<ScopedTestingLocalState> local_state_;
+ std::unique_ptr<TestingMetricsReportingHandler> handler_;
+
+ policy::MockConfigurationPolicyProvider provider_;
+ policy::PolicyMap map_;
+};
+
+TEST_F(MetricsReportingHandlerTest, PrefChangesNotifyPage) {
+ // Toggle the pref.
+ local_state()->SetBoolean(
+ metrics::prefs::kMetricsReportingEnabled,
+ !local_state()->GetBoolean(metrics::prefs::kMetricsReportingEnabled));
+ EXPECT_EQ(1u, test_web_ui()->call_data().size());
+
+ test_web_ui()->ClearTrackedCalls();
+ handler()->DisallowJavascript();
+
+ // Toggle the pref again, while JavaScript is disabled.
+ local_state()->SetBoolean(
+ metrics::prefs::kMetricsReportingEnabled,
+ !local_state()->GetBoolean(metrics::prefs::kMetricsReportingEnabled));
+ EXPECT_TRUE(test_web_ui()->call_data().empty());
+}
+
+TEST_F(MetricsReportingHandlerTest, PolicyChangesNotifyPage) {
+ // Change the policy, check that the page was notified.
+ map()->Set(policy::key::kMetricsReportingEnabled,
+ policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
+ policy::POLICY_SOURCE_CLOUD, base::MakeUnique<base::Value>(true),
+ nullptr);
+ provider()->UpdateChromePolicy(*map());
+ EXPECT_EQ(1u, test_web_ui()->call_data().size());
+
+ test_web_ui()->ClearTrackedCalls();
+ handler()->DisallowJavascript();
+
+ // Policies changing while JavaScript is disabled shouldn't notify the page.
+ map()->Set(policy::key::kMetricsReportingEnabled,
+ policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
+ policy::POLICY_SOURCE_CLOUD, base::MakeUnique<base::Value>(false),
+ nullptr);
+ provider()->UpdateChromePolicy(*map());
+ EXPECT_TRUE(test_web_ui()->call_data().empty());
+}
+
+} // namespace settings
+
+#endif // defined(GOOGLE_CHROME_BUILD) && !defined(OS_CHROMEOS)
diff --git a/chromium/chrome/browser/ui/webui/settings/native_certificates_handler.cc b/chromium/chrome/browser/ui/webui/settings/native_certificates_handler.cc
new file mode 100644
index 00000000000..4661e32a12a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/native_certificates_handler.cc
@@ -0,0 +1,32 @@
+// 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 "chrome/browser/ui/webui/settings/native_certificates_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/metrics/user_metrics.h"
+#include "chrome/browser/ui/webui/settings_utils.h"
+#include "content/public/browser/web_ui.h"
+
+namespace settings {
+
+NativeCertificatesHandler::NativeCertificatesHandler() {}
+
+NativeCertificatesHandler::~NativeCertificatesHandler() {}
+
+void NativeCertificatesHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "showManageSSLCertificates",
+ base::Bind(&NativeCertificatesHandler::HandleShowManageSSLCertificates,
+ base::Unretained(this)));
+}
+
+void NativeCertificatesHandler::HandleShowManageSSLCertificates(
+ const base::ListValue* args) {
+ base::RecordAction(base::UserMetricsAction("Options_ManageSSLCertificates"));
+ settings_utils::ShowManageSSLCertificates(web_ui()->GetWebContents());
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/native_certificates_handler.h b/chromium/chrome/browser/ui/webui/settings/native_certificates_handler.h
new file mode 100644
index 00000000000..3264392da3c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/native_certificates_handler.h
@@ -0,0 +1,33 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_NATIVE_CERTIFICATES_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_NATIVE_CERTIFICATES_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+namespace settings {
+
+class NativeCertificatesHandler : public SettingsPageUIHandler {
+ public:
+ NativeCertificatesHandler();
+ ~NativeCertificatesHandler() override;
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override {}
+ void OnJavascriptDisallowed() override {}
+
+ // Callback for the "showManageSSLCertificates" message. This will invoke
+ // an appropriate certificate management action based on the platform.
+ void HandleShowManageSSLCertificates(const base::ListValue* args);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NativeCertificatesHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_NATIVE_CERTIFICATES_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/on_startup_handler.cc b/chromium/chrome/browser/ui/webui/settings/on_startup_handler.cc
new file mode 100644
index 00000000000..b42ffc85717
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/on_startup_handler.cc
@@ -0,0 +1,73 @@
+// 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 "chrome/browser/ui/webui/settings/on_startup_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "chrome/browser/extensions/settings_api_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/settings_utils.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/management_policy.h"
+#include "extensions/common/extension.h"
+
+namespace settings {
+
+OnStartupHandler::OnStartupHandler() {}
+OnStartupHandler::~OnStartupHandler() {}
+
+void OnStartupHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "getNtpExtension", base::Bind(&OnStartupHandler::HandleGetNtpExtension,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "validateStartupPage",
+ base::Bind(&OnStartupHandler::HandleValidateStartupPage,
+ base::Unretained(this)));
+}
+
+void OnStartupHandler::HandleGetNtpExtension(const base::ListValue* args) {
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+
+ AllowJavascript();
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ const extensions::Extension* ntp_extension =
+ extensions::GetExtensionOverridingNewTabPage(profile);
+
+ if (!ntp_extension) {
+ ResolveJavascriptCallback(*callback_id, base::Value());
+ return;
+ }
+
+ base::DictionaryValue dict;
+ dict.SetString("id", ntp_extension->id());
+ dict.SetString("name", ntp_extension->name());
+ dict.SetBoolean("canBeDisabled",
+ !extensions::ExtensionSystem::Get(profile)
+ ->management_policy()
+ ->MustRemainEnabled(ntp_extension, nullptr));
+ ResolveJavascriptCallback(*callback_id, dict);
+}
+
+void OnStartupHandler::HandleValidateStartupPage(const base::ListValue* args) {
+ AllowJavascript();
+
+ CHECK_EQ(args->GetSize(), 2U);
+
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+
+ std::string url_string;
+ CHECK(args->GetString(1, &url_string));
+
+ bool valid = settings_utils::FixupAndValidateStartupPage(url_string, nullptr);
+ ResolveJavascriptCallback(*callback_id, base::Value(valid));
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/on_startup_handler.h b/chromium/chrome/browser/ui/webui/settings/on_startup_handler.h
new file mode 100644
index 00000000000..013d1d98ff8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/on_startup_handler.h
@@ -0,0 +1,40 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_ON_STARTUP_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ON_STARTUP_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace settings {
+
+class OnStartupHandler : public SettingsPageUIHandler {
+ public:
+ OnStartupHandler();
+ ~OnStartupHandler() override;
+
+ // SettingsPageUIHandler:
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override {}
+ void OnJavascriptDisallowed() override {}
+
+ private:
+ // Handler for the "getNtpExtension" message. No arguments.
+ void HandleGetNtpExtension(const base::ListValue* /*args*/);
+
+ // Handles the "validateStartupPage" message. Passed a URL that might be a
+ // valid startup page.
+ void HandleValidateStartupPage(const base::ListValue* args);
+
+ DISALLOW_COPY_AND_ASSIGN(OnStartupHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_ON_STARTUP_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/people_handler.cc b/chromium/chrome/browser/ui/webui/settings/people_handler.cc
new file mode 100644
index 00000000000..4c37c95afc8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/people_handler.cc
@@ -0,0 +1,913 @@
+// 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.
+
+#include "chrome/browser/ui/webui/settings/people_handler.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/i18n/time_formatting.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_metrics.h"
+#include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/signin/chrome_signin_helper.h"
+#include "chrome/browser/signin/signin_error_controller_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/signin_promo.h"
+#include "chrome/browser/signin/signin_ui_util.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/sync/sync_ui_util.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.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"
+#include "chrome/grit/generated_resources.h"
+#include "components/autofill/core/common/autofill_constants.h"
+#include "components/autofill/core/common/autofill_pref_names.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/signin_error_controller.h"
+#include "components/signin/core/browser/signin_header_helper.h"
+#include "components/signin/core/browser/signin_metrics.h"
+#include "components/signin/core/common/profile_management_switches.h"
+#include "components/signin/core/common/signin_pref_names.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/sync/base/passphrase_type.h"
+#include "components/sync/base/sync_prefs.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+#include "google_apis/gaia/gaia_constants.h"
+#include "net/base/url_util.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/webui/web_ui_util.h"
+
+#if defined(OS_CHROMEOS)
+#include "components/signin/core/browser/signin_manager_base.h"
+#else
+#include "components/signin/core/browser/signin_manager.h"
+#endif
+
+using browser_sync::ProfileSyncService;
+using content::WebContents;
+using l10n_util::GetStringFUTF16;
+using l10n_util::GetStringUTF16;
+
+namespace {
+
+// A structure which contains all the configuration information for sync.
+struct SyncConfigInfo {
+ SyncConfigInfo();
+ ~SyncConfigInfo();
+
+ bool encrypt_all;
+ bool sync_everything;
+ syncer::ModelTypeSet data_types;
+ bool payments_integration_enabled;
+ std::string passphrase;
+ bool set_new_passphrase;
+};
+
+SyncConfigInfo::SyncConfigInfo()
+ : encrypt_all(false),
+ sync_everything(false),
+ payments_integration_enabled(false),
+ set_new_passphrase(false) {}
+
+SyncConfigInfo::~SyncConfigInfo() {}
+
+bool GetConfiguration(const std::string& json, SyncConfigInfo* config) {
+ std::unique_ptr<base::Value> parsed_value = base::JSONReader::Read(json);
+ base::DictionaryValue* result;
+ if (!parsed_value || !parsed_value->GetAsDictionary(&result)) {
+ DLOG(ERROR) << "GetConfiguration() not passed a Dictionary";
+ return false;
+ }
+
+ if (!result->GetBoolean("syncAllDataTypes", &config->sync_everything)) {
+ DLOG(ERROR) << "GetConfiguration() not passed a syncAllDataTypes value";
+ return false;
+ }
+
+ if (!result->GetBoolean("paymentsIntegrationEnabled",
+ &config->payments_integration_enabled)) {
+ DLOG(ERROR) << "GetConfiguration() not passed a paymentsIntegrationEnabled "
+ << "value";
+ return false;
+ }
+
+ syncer::ModelTypeNameMap type_names = syncer::GetUserSelectableTypeNameMap();
+
+ for (syncer::ModelTypeNameMap::const_iterator it = type_names.begin();
+ it != type_names.end(); ++it) {
+ std::string key_name = it->second + std::string("Synced");
+ bool sync_value;
+ if (!result->GetBoolean(key_name, &sync_value)) {
+ DLOG(ERROR) << "GetConfiguration() not passed a value for " << key_name;
+ return false;
+ }
+ if (sync_value)
+ config->data_types.Put(it->first);
+ }
+
+ // Encryption settings.
+ if (!result->GetBoolean("encryptAllData", &config->encrypt_all)) {
+ DLOG(ERROR) << "GetConfiguration() not passed a value for encryptAllData";
+ return false;
+ }
+
+ // Passphrase settings.
+ if (result->GetString("passphrase", &config->passphrase) &&
+ !config->passphrase.empty() &&
+ !result->GetBoolean("setNewPassphrase", &config->set_new_passphrase)) {
+ DLOG(ERROR) << "GetConfiguration() not passed a set_new_passphrase value";
+ return false;
+ }
+ return true;
+}
+
+// Guaranteed to return a valid result (or crash).
+void ParseConfigurationArguments(const base::ListValue* args,
+ SyncConfigInfo* config,
+ const base::Value** callback_id) {
+ std::string json;
+ if (args->Get(0, callback_id) && args->GetString(1, &json) && !json.empty())
+ CHECK(GetConfiguration(json, config));
+ else
+ NOTREACHED();
+}
+
+std::string GetSyncErrorAction(sync_ui_util::ActionType action_type) {
+ switch (action_type) {
+ case sync_ui_util::REAUTHENTICATE:
+ return "reauthenticate";
+ case sync_ui_util::SIGNOUT_AND_SIGNIN:
+ return "signOutAndSignIn";
+ case sync_ui_util::UPGRADE_CLIENT:
+ return "upgradeClient";
+ case sync_ui_util::ENTER_PASSPHRASE:
+ return "enterPassphrase";
+ default:
+ return "noAction";
+ }
+}
+
+} // namespace
+
+namespace settings {
+
+// static
+const char PeopleHandler::kSpinnerPageStatus[] = "spinner";
+const char PeopleHandler::kConfigurePageStatus[] = "configure";
+const char PeopleHandler::kTimeoutPageStatus[] = "timeout";
+const char PeopleHandler::kDonePageStatus[] = "done";
+const char PeopleHandler::kPassphraseFailedPageStatus[] = "passphraseFailed";
+
+PeopleHandler::PeopleHandler(Profile* profile)
+ : profile_(profile),
+ configuring_sync_(false),
+ signin_observer_(this),
+ sync_service_observer_(this) {}
+
+PeopleHandler::~PeopleHandler() {
+ // Early exit if running unit tests (no actual WebUI is attached).
+ if (!web_ui())
+ return;
+
+ // This case is hit when the user performs a back navigation.
+ CloseSyncSetup();
+}
+
+void PeopleHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "SyncSetupDidClosePage",
+ base::Bind(&PeopleHandler::OnDidClosePage, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "SyncSetupSetDatatypes",
+ base::Bind(&PeopleHandler::HandleSetDatatypes, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "SyncSetupSetEncryption",
+ base::Bind(&PeopleHandler::HandleSetEncryption, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "SyncSetupShowSetupUI",
+ base::Bind(&PeopleHandler::HandleShowSetupUI, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "SyncSetupGetSyncStatus",
+ base::Bind(&PeopleHandler::HandleGetSyncStatus, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "SyncSetupManageOtherPeople",
+ base::Bind(&PeopleHandler::HandleManageOtherPeople,
+ base::Unretained(this)));
+#if defined(OS_CHROMEOS)
+ web_ui()->RegisterMessageCallback(
+ "AttemptUserExit",
+ base::Bind(&PeopleHandler::HandleAttemptUserExit,
+ base::Unretained(this)));
+#else
+ web_ui()->RegisterMessageCallback(
+ "SyncSetupStopSyncing",
+ base::Bind(&PeopleHandler::HandleStopSyncing, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "SyncSetupStartSignIn",
+ base::Bind(&PeopleHandler::HandleStartSignin, base::Unretained(this)));
+#endif
+}
+
+void PeopleHandler::OnJavascriptAllowed() {
+ PrefService* prefs = profile_->GetPrefs();
+ profile_pref_registrar_.Init(prefs);
+ profile_pref_registrar_.Add(
+ prefs::kSigninAllowed,
+ base::Bind(&PeopleHandler::UpdateSyncStatus, base::Unretained(this)));
+
+ SigninManagerBase* signin_manager(
+ SigninManagerFactory::GetInstance()->GetForProfile(profile_));
+ if (signin_manager)
+ signin_observer_.Add(signin_manager);
+
+ ProfileSyncService* sync_service(
+ ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_));
+ if (sync_service)
+ sync_service_observer_.Add(sync_service);
+}
+
+void PeopleHandler::OnJavascriptDisallowed() {
+ profile_pref_registrar_.RemoveAll();
+ signin_observer_.RemoveAll();
+ sync_service_observer_.RemoveAll();
+}
+
+#if !defined(OS_CHROMEOS)
+void PeopleHandler::DisplayGaiaLogin(signin_metrics::AccessPoint access_point) {
+ DCHECK(!sync_startup_tracker_);
+ // Advanced options are no longer being configured if the login screen is
+ // visible. If the user exits the signin wizard after this without
+ // configuring sync, CloseSyncSetup() will ensure they are logged out.
+ configuring_sync_ = false;
+ DisplayGaiaLoginInNewTabOrWindow(access_point);
+}
+
+void PeopleHandler::DisplayGaiaLoginInNewTabOrWindow(
+ signin_metrics::AccessPoint access_point) {
+ Browser* browser =
+ chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
+ bool force_new_tab = false;
+ if (!browser) {
+ // Settings is not displayed in a browser window. Open a new window.
+ browser = new Browser(
+ Browser::CreateParams(Browser::TYPE_TABBED, profile_, true));
+ force_new_tab = true;
+ }
+
+ // If the signin manager already has an authenticated username, this is a
+ // re-auth scenario, and we need to ensure that the user signs in with the
+ // same email address.
+ GURL url;
+ if (SigninManagerFactory::GetForProfile(browser->profile())
+ ->IsAuthenticated()) {
+ UMA_HISTOGRAM_ENUMERATION("Signin.Reauth",
+ signin_metrics::HISTOGRAM_REAUTH_SHOWN,
+ signin_metrics::HISTOGRAM_REAUTH_MAX);
+
+ SigninErrorController* error_controller =
+ SigninErrorControllerFactory::GetForProfile(browser->profile());
+ DCHECK(error_controller->HasError());
+ if (!force_new_tab) {
+ browser->window()->ShowAvatarBubbleFromAvatarButton(
+ BrowserWindow::AVATAR_BUBBLE_MODE_REAUTH,
+ signin::ManageAccountsParams(), access_point, false);
+ } else {
+ url = signin::GetReauthURL(
+ access_point, signin_metrics::Reason::REASON_REAUTHENTICATION,
+ browser->profile(), error_controller->error_account_id());
+ }
+ } else {
+ if (!force_new_tab) {
+ browser->window()->ShowAvatarBubbleFromAvatarButton(
+ BrowserWindow::AVATAR_BUBBLE_MODE_SIGNIN,
+ signin::ManageAccountsParams(), access_point, false);
+ } else {
+ url = signin::GetPromoURL(
+ access_point, signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT,
+ true);
+ }
+ }
+
+ if (url.is_valid())
+ chrome::ShowSingletonTab(browser, url);
+}
+#endif
+
+void PeopleHandler::DisplaySpinner() {
+ configuring_sync_ = true;
+
+ const int kTimeoutSec = 30;
+ DCHECK(!engine_start_timer_);
+ engine_start_timer_.reset(new base::OneShotTimer());
+ engine_start_timer_->Start(FROM_HERE,
+ base::TimeDelta::FromSeconds(kTimeoutSec), this,
+ &PeopleHandler::DisplayTimeout);
+
+ FireWebUIListener("page-status-changed", base::Value(kSpinnerPageStatus));
+}
+
+void PeopleHandler::DisplayTimeout() {
+ // Stop a timer to handle timeout in waiting for checking network connection.
+ engine_start_timer_.reset();
+
+ // Do not listen to sync startup events.
+ sync_startup_tracker_.reset();
+
+ FireWebUIListener("page-status-changed", base::Value(kTimeoutPageStatus));
+}
+
+void PeopleHandler::OnDidClosePage(const base::ListValue* args) {
+ MarkFirstSetupComplete();
+ CloseSyncSetup();
+}
+
+void PeopleHandler::SyncStartupFailed() {
+ // Stop a timer to handle timeout in waiting for checking network connection.
+ engine_start_timer_.reset();
+
+ // Just close the sync overlay (the idea is that the base settings page will
+ // display the current error.)
+ CloseUI();
+}
+
+void PeopleHandler::SyncStartupCompleted() {
+ ProfileSyncService* service = GetSyncService();
+ DCHECK(service->IsEngineInitialized());
+
+ // Stop a timer to handle timeout in waiting for checking network connection.
+ engine_start_timer_.reset();
+
+ sync_startup_tracker_.reset();
+
+ PushSyncPrefs();
+}
+
+ProfileSyncService* PeopleHandler::GetSyncService() const {
+ return profile_->IsSyncAllowed()
+ ? ProfileSyncServiceFactory::GetForProfile(profile_)
+ : nullptr;
+}
+
+void PeopleHandler::HandleSetDatatypes(const base::ListValue* args) {
+ DCHECK(!sync_startup_tracker_);
+
+ SyncConfigInfo configuration;
+ const base::Value* callback_id = nullptr;
+ ParseConfigurationArguments(args, &configuration, &callback_id);
+
+ PrefService* pref_service = profile_->GetPrefs();
+ pref_service->SetBoolean(autofill::prefs::kAutofillWalletImportEnabled,
+ configuration.payments_integration_enabled);
+
+ // Start configuring the ProfileSyncService using the configuration passed
+ // to us from the JS layer.
+ ProfileSyncService* service = GetSyncService();
+
+ // If the sync engine has shutdown for some reason, just close the sync
+ // dialog.
+ if (!service || !service->IsEngineInitialized()) {
+ CloseSyncSetup();
+ ResolveJavascriptCallback(*callback_id, base::Value(kDonePageStatus));
+ return;
+ }
+
+ service->OnUserChoseDatatypes(configuration.sync_everything,
+ configuration.data_types);
+
+ // Choosing data types to sync never fails.
+ ResolveJavascriptCallback(*callback_id, base::Value(kConfigurePageStatus));
+
+ ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_CUSTOMIZE);
+ if (!configuration.sync_everything)
+ ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_CHOOSE);
+}
+
+void PeopleHandler::HandleSetEncryption(const base::ListValue* args) {
+ DCHECK(!sync_startup_tracker_);
+
+ SyncConfigInfo configuration;
+ const base::Value* callback_id = nullptr;
+ ParseConfigurationArguments(args, &configuration, &callback_id);
+
+ // Start configuring the ProfileSyncService using the configuration passed
+ // to us from the JS layer.
+ ProfileSyncService* service = GetSyncService();
+
+ // If the sync engine has shutdown for some reason, just close the sync
+ // dialog.
+ if (!service || !service->IsEngineInitialized()) {
+ CloseSyncSetup();
+ ResolveJavascriptCallback(*callback_id, base::Value(kDonePageStatus));
+ return;
+ }
+
+ // Don't allow "encrypt all" if the ProfileSyncService doesn't allow it.
+ // The UI is hidden, but the user may have enabled it e.g. by fiddling with
+ // the web inspector.
+ if (!service->IsEncryptEverythingAllowed())
+ configuration.encrypt_all = false;
+
+ // Note: Data encryption will not occur until configuration is complete
+ // (when the PSS receives its CONFIGURE_DONE notification from the sync
+ // engine), so the user still has a chance to cancel out of the operation
+ // if (for example) some kind of passphrase error is encountered.
+ if (configuration.encrypt_all)
+ service->EnableEncryptEverything();
+
+ bool passphrase_failed = false;
+ if (!configuration.passphrase.empty()) {
+ // We call IsPassphraseRequired() here (instead of
+ // IsPassphraseRequiredForDecryption()) because the user may try to enter
+ // a passphrase even though no encrypted data types are enabled.
+ if (service->IsPassphraseRequired()) {
+ // If we have pending keys, try to decrypt them with the provided
+ // passphrase. We track if this succeeds or fails because a failed
+ // decryption should result in an error even if there aren't any encrypted
+ // data types.
+ passphrase_failed =
+ !service->SetDecryptionPassphrase(configuration.passphrase);
+ } else {
+ // OK, the user sent us a passphrase, but we don't have pending keys. So
+ // it either means that the pending keys were resolved somehow since the
+ // time the UI was displayed (re-encryption, pending passphrase change,
+ // etc) or the user wants to re-encrypt.
+ if (configuration.set_new_passphrase &&
+ !service->IsUsingSecondaryPassphrase()) {
+ service->SetEncryptionPassphrase(configuration.passphrase,
+ ProfileSyncService::EXPLICIT);
+ }
+ }
+ }
+
+ if (passphrase_failed || service->IsPassphraseRequiredForDecryption()) {
+ // If the user doesn't enter any passphrase, we won't call
+ // SetDecryptionPassphrase() (passphrase_failed == false), but we still
+ // want to display an error message to let the user know that their blank
+ // passphrase entry is not acceptable.
+
+ // TODO(tommycli): Switch this to RejectJavascriptCallback once the
+ // Sync page JavaScript has been further refactored.
+ ResolveJavascriptCallback(*callback_id,
+ base::Value(kPassphraseFailedPageStatus));
+ } else {
+ ResolveJavascriptCallback(*callback_id, base::Value(kConfigurePageStatus));
+ }
+
+ if (configuration.encrypt_all)
+ ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_ENCRYPT);
+ if (!configuration.set_new_passphrase && !configuration.passphrase.empty())
+ ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_PASSPHRASE);
+}
+
+void PeopleHandler::HandleShowSetupUI(const base::ListValue* args) {
+ AllowJavascript();
+
+ if (!GetSyncService()) {
+ CloseUI();
+ return;
+ }
+
+ SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile_);
+ if (!signin->IsAuthenticated()) {
+ // For web-based signin, the signin page is not displayed in an overlay
+ // on the settings page. So if we get here, it must be due to the user
+ // cancelling signin (by reloading the sync settings page during initial
+ // signin) or by directly navigating to settings/syncSetup
+ // (http://crbug.com/229836). So just exit and go back to the settings page.
+ DLOG(WARNING) << "Cannot display sync setup UI when not signed in";
+ CloseUI();
+ return;
+ }
+
+ OpenSyncSetup();
+}
+
+#if defined(OS_CHROMEOS)
+// On ChromeOS, we need to sign out the user session to fix an auth error, so
+// the user goes through the real signin flow to generate a new auth token.
+void PeopleHandler::HandleAttemptUserExit(const base::ListValue* args) {
+ DVLOG(1) << "Signing out the user to fix a sync error.";
+ chrome::AttemptUserExit();
+}
+#endif
+
+#if !defined(OS_CHROMEOS)
+void PeopleHandler::HandleStartSignin(const base::ListValue* args) {
+ AllowJavascript();
+
+ // Should only be called if the user is not already signed in or has an auth
+ // error.
+ DCHECK(!SigninManagerFactory::GetForProfile(profile_)->IsAuthenticated() ||
+ SigninErrorControllerFactory::GetForProfile(profile_)->HasError());
+
+ OpenSyncSetup();
+}
+
+void PeopleHandler::HandleStopSyncing(const base::ListValue* args) {
+ bool delete_profile = false;
+ args->GetBoolean(0, &delete_profile);
+
+ if (!SigninManagerFactory::GetForProfile(profile_)->IsSignoutProhibited()) {
+ if (GetSyncService())
+ ProfileSyncService::SyncEvent(ProfileSyncService::STOP_FROM_OPTIONS);
+
+ signin_metrics::SignoutDelete delete_metric =
+ delete_profile ? signin_metrics::SignoutDelete::DELETED
+ : signin_metrics::SignoutDelete::KEEPING;
+ SigninManagerFactory::GetForProfile(profile_)
+ ->SignOut(signin_metrics::USER_CLICKED_SIGNOUT_SETTINGS, delete_metric);
+ }
+
+ if (delete_profile) {
+ webui::DeleteProfileAtPath(profile_->GetPath(),
+ web_ui(),
+ ProfileMetrics::DELETE_PROFILE_SETTINGS);
+ }
+}
+#endif
+
+void PeopleHandler::HandleGetSyncStatus(const base::ListValue* args) {
+ AllowJavascript();
+
+ CHECK_EQ(1U, args->GetSize());
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+
+ ResolveJavascriptCallback(*callback_id, *GetSyncStatusDictionary());
+}
+
+void PeopleHandler::HandleManageOtherPeople(const base::ListValue* /* args */) {
+ UserManager::Show(base::FilePath(), profiles::USER_MANAGER_NO_TUTORIAL,
+ profiles::USER_MANAGER_SELECT_PROFILE_NO_ACTION);
+}
+
+void PeopleHandler::CloseSyncSetup() {
+ // Stop a timer to handle timeout in waiting for checking network connection.
+ engine_start_timer_.reset();
+
+ // Clear the sync startup tracker, since the setup wizard is being closed.
+ sync_startup_tracker_.reset();
+
+ ProfileSyncService* sync_service = GetSyncService();
+
+ // LoginUIService can be nullptr if page is brought up in incognito mode
+ // (i.e. if the user is running in guest mode in cros and brings up settings).
+ LoginUIService* service = GetLoginUIService();
+ if (service) {
+ // Don't log a cancel event if the sync setup dialog is being
+ // automatically closed due to an auth error.
+ if ((service->current_login_ui() == this) &&
+ (!sync_service || (!sync_service->IsFirstSetupComplete() &&
+ sync_service->GetAuthError().state() ==
+ GoogleServiceAuthError::NONE))) {
+ if (configuring_sync_) {
+ ProfileSyncService::SyncEvent(
+ ProfileSyncService::CANCEL_DURING_CONFIGURE);
+
+ // If the user clicked "Cancel" while setting up sync, disable sync
+ // because we don't want the sync engine to remain in the
+ // first-setup-incomplete state.
+ // Note: In order to disable sync across restarts on Chrome OS,
+ // we must call RequestStop(CLEAR_DATA), which suppresses sync startup
+ // in addition to disabling it.
+ if (sync_service) {
+ DVLOG(1) << "Sync setup aborted by user action";
+ sync_service->RequestStop(ProfileSyncService::CLEAR_DATA);
+#if !defined(OS_CHROMEOS)
+ // Sign out the user on desktop Chrome if they click cancel during
+ // initial setup.
+ // TODO(rsimha): Revisit this for M30. See http://crbug.com/252049.
+ if (sync_service->IsFirstSetupInProgress()) {
+ SigninManagerFactory::GetForProfile(profile_)
+ ->SignOut(signin_metrics::ABORT_SIGNIN,
+ signin_metrics::SignoutDelete::IGNORE_METRIC);
+ }
+#endif
+ }
+ }
+ }
+
+ service->LoginUIClosed(this);
+ }
+
+ // Alert the sync service anytime the sync setup dialog is closed. This can
+ // happen due to the user clicking the OK or Cancel button, or due to the
+ // dialog being closed by virtue of sync being disabled in the background.
+ sync_blocker_.reset();
+
+ configuring_sync_ = false;
+}
+
+void PeopleHandler::OpenSyncSetup() {
+ // Notify services that login UI is now active.
+ GetLoginUIService()->SetLoginUI(this);
+
+ ProfileSyncService* service = GetSyncService();
+ if (service)
+ sync_blocker_ = service->GetSetupInProgressHandle();
+
+ // There are several different UI flows that can bring the user here:
+ // 1) Signin promo.
+ // 2) Normal signin through settings page (IsAuthenticated() is false).
+ // 3) Previously working credentials have expired.
+ // 4) User is signed in, but has stopped sync via the google dashboard, and
+ // signout is prohibited by policy so we need to force a re-auth.
+ // 5) User clicks [Advanced Settings] button on options page while already
+ // logged in.
+ // 6) One-click signin (credentials are already available, so should display
+ // sync configure UI, not login UI).
+ // 7) User re-enables sync after disabling it via advanced settings.
+#if !defined(OS_CHROMEOS)
+ if (!SigninManagerFactory::GetForProfile(profile_)->IsAuthenticated() ||
+ SigninErrorControllerFactory::GetForProfile(profile_)->HasError()) {
+ // User is not logged in (cases 1-2), or login has been specially requested
+ // because previously working credentials have expired (case 3). Close sync
+ // setup including any visible overlays, and display the gaia auth page.
+ // Control will be returned to the sync settings page once auth is complete.
+ CloseUI();
+ DisplayGaiaLogin(signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS);
+ return;
+ }
+#endif
+ if (!service) {
+ // This can happen if the user directly navigates to /settings/syncSetup.
+ DLOG(WARNING) << "Cannot display sync UI when sync is disabled";
+ CloseUI();
+ return;
+ }
+
+ // Early exit if there is already a preferences push pending sync startup.
+ if (sync_startup_tracker_)
+ return;
+
+ if (!service->IsEngineInitialized()) {
+ // Requesting the sync service to start may trigger call to PushSyncPrefs.
+ // Setting up the startup tracker beforehand correctly signals the
+ // re-entrant call to early exit.
+ sync_startup_tracker_.reset(new SyncStartupTracker(profile_, this));
+ service->RequestStart();
+
+ // See if it's even possible to bring up the sync engine - if not
+ // (unrecoverable error?), don't bother displaying a spinner that will be
+ // immediately closed because this leads to some ugly infinite UI loop (see
+ // http://crbug.com/244769).
+ if (SyncStartupTracker::GetSyncServiceState(profile_) !=
+ SyncStartupTracker::SYNC_STARTUP_ERROR) {
+ DisplaySpinner();
+ }
+ return;
+ }
+
+ // User is already logged in. They must have brought up the config wizard
+ // via the "Advanced..." button or through One-Click signin (cases 4-6), or
+ // they are re-enabling sync after having disabled it (case 7).
+ PushSyncPrefs();
+}
+
+void PeopleHandler::FocusUI() {
+ WebContents* web_contents = web_ui()->GetWebContents();
+ web_contents->GetDelegate()->ActivateContents(web_contents);
+}
+
+void PeopleHandler::CloseUI() {
+ CloseSyncSetup();
+ FireWebUIListener("page-status-changed", base::Value(kDonePageStatus));
+}
+
+void PeopleHandler::GoogleSigninSucceeded(const std::string& /* account_id */,
+ const std::string& /* username */,
+ const std::string& /* password */) {
+ UpdateSyncStatus();
+}
+
+void PeopleHandler::GoogleSignedOut(const std::string& /* account_id */,
+ const std::string& /* username */) {
+ UpdateSyncStatus();
+}
+
+void PeopleHandler::OnStateChanged(syncer::SyncService* sync) {
+ UpdateSyncStatus();
+
+ // When the SyncService changes its state, we should also push the updated
+ // sync preferences.
+ PushSyncPrefs();
+}
+
+std::unique_ptr<base::DictionaryValue>
+PeopleHandler::GetSyncStatusDictionary() {
+ // The items which are to be written into |sync_status| are also described in
+ // chrome/browser/resources/options/browser_options.js in @typedef
+ // for SyncStatus. Please update it whenever you add or remove any keys here.
+ std::unique_ptr<base::DictionaryValue> sync_status(new base::DictionaryValue);
+ if (profile_->IsGuestSession()) {
+ // Cannot display signin status when running in guest mode on chromeos
+ // because there is no SigninManager.
+ sync_status->SetBoolean("signinAllowed", false);
+ return sync_status;
+ }
+
+ sync_status->SetBoolean("supervisedUser", profile_->IsSupervised());
+ sync_status->SetBoolean("childUser", profile_->IsChild());
+
+ SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile_);
+ DCHECK(signin);
+#if !defined(OS_CHROMEOS)
+ // Signout is not allowed if the user has policy (crbug.com/172204).
+ if (SigninManagerFactory::GetForProfile(profile_)->IsSignoutProhibited()) {
+ std::string username = signin->GetAuthenticatedAccountInfo().email;
+
+ // If there is no one logged in or if the profile name is empty then the
+ // domain name is empty. This happens in browser tests.
+ if (!username.empty())
+ sync_status->SetString("domain", gaia::ExtractDomainName(username));
+ }
+#endif
+
+ ProfileSyncService* service =
+ 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());
+
+ base::string16 status_label;
+ base::string16 link_label;
+ sync_ui_util::ActionType action_type = sync_ui_util::NO_ACTION;
+ bool status_has_error =
+ sync_ui_util::GetStatusLabels(profile_, service, *signin,
+ sync_ui_util::PLAIN_TEXT, &status_label,
+ &link_label, &action_type) ==
+ sync_ui_util::SYNC_ERROR;
+ sync_status->SetString("statusText", status_label);
+ sync_status->SetBoolean("hasError", status_has_error);
+ sync_status->SetString("statusAction", GetSyncErrorAction(action_type));
+
+ sync_status->SetBoolean("managed", service && service->IsManaged());
+ sync_status->SetBoolean("signedIn", signin->IsAuthenticated());
+ sync_status->SetString("signedInUsername",
+ signin_ui_util::GetAuthenticatedUsername(signin));
+ sync_status->SetBoolean("hasUnrecoverableError",
+ service && service->HasUnrecoverableError());
+
+ return sync_status;
+}
+
+void PeopleHandler::PushSyncPrefs() {
+#if !defined(OS_CHROMEOS)
+ // Early exit if the user has not signed in yet.
+ if (!SigninManagerFactory::GetForProfile(profile_)->IsAuthenticated() ||
+ SigninErrorControllerFactory::GetForProfile(profile_)->HasError()) {
+ return;
+ }
+#endif
+
+ ProfileSyncService* service = GetSyncService();
+ // The sync service may be nullptr if it has been just disabled by policy.
+ if (!service || !service->IsEngineInitialized()) {
+ return;
+ }
+
+ configuring_sync_ = true;
+ DCHECK(service->IsEngineInitialized())
+ << "Cannot configure sync until the sync engine is initialized";
+
+ // Setup args for the sync configure screen:
+ // syncAllDataTypes: true if the user wants to sync everything
+ // <data_type>Registered: true if the associated data type is supported
+ // <data_type>Synced: true if the user wants to sync that specific data type
+ // paymentsIntegrationEnabled: true if the user wants Payments integration
+ // encryptionEnabled: true if sync supports encryption
+ // encryptAllData: true if user wants to encrypt all data (not just
+ // passwords)
+ // passphraseRequired: true if a passphrase is needed to start sync
+ // passphraseTypeIsCustom: true if the passphrase type is custom
+ //
+ base::DictionaryValue args;
+
+ // Tell the UI layer which data types are registered/enabled by the user.
+ const syncer::ModelTypeSet registered_types =
+ service->GetRegisteredDataTypes();
+ const syncer::ModelTypeSet preferred_types = service->GetPreferredDataTypes();
+ const syncer::ModelTypeSet enforced_types = service->GetForcedDataTypes();
+ syncer::ModelTypeNameMap type_names = syncer::GetUserSelectableTypeNameMap();
+ for (syncer::ModelTypeNameMap::const_iterator it = type_names.begin();
+ it != type_names.end(); ++it) {
+ syncer::ModelType sync_type = it->first;
+ const std::string key_name = it->second;
+ args.SetBoolean(key_name + "Registered", registered_types.Has(sync_type));
+ args.SetBoolean(key_name + "Synced", preferred_types.Has(sync_type));
+ args.SetBoolean(key_name + "Enforced", enforced_types.Has(sync_type));
+ // TODO(treib): How do we want to handle pref groups, i.e. when only some of
+ // the sync types behind a checkbox are force-enabled? crbug.com/403326
+ }
+ PrefService* pref_service = profile_->GetPrefs();
+ syncer::SyncPrefs sync_prefs(pref_service);
+ args.SetBoolean("syncAllDataTypes", sync_prefs.HasKeepEverythingSynced());
+ args.SetBoolean(
+ "paymentsIntegrationEnabled",
+ pref_service->GetBoolean(autofill::prefs::kAutofillWalletImportEnabled));
+ args.SetBoolean("encryptAllData", service->IsEncryptEverythingEnabled());
+ args.SetBoolean("encryptAllDataAllowed",
+ service->IsEncryptEverythingAllowed());
+
+ // We call IsPassphraseRequired() here, instead of calling
+ // IsPassphraseRequiredForDecryption(), because we want to show the passphrase
+ // UI even if no encrypted data types are enabled.
+ args.SetBoolean("passphraseRequired", service->IsPassphraseRequired());
+
+ // To distinguish between PassphraseType::FROZEN_IMPLICIT_PASSPHRASE and
+ // PassphraseType::CUSTOM_PASSPHRASE
+ // we only set passphraseTypeIsCustom for PassphraseType::CUSTOM_PASSPHRASE.
+ args.SetBoolean("passphraseTypeIsCustom",
+ service->GetPassphraseType() ==
+ syncer::PassphraseType::CUSTOM_PASSPHRASE);
+ base::Time passphrase_time = service->GetExplicitPassphraseTime();
+ syncer::PassphraseType passphrase_type = service->GetPassphraseType();
+ if (!passphrase_time.is_null()) {
+ base::string16 passphrase_time_str =
+ base::TimeFormatShortDate(passphrase_time);
+ args.SetString("enterPassphraseBody",
+ GetStringFUTF16(IDS_SYNC_ENTER_PASSPHRASE_BODY_WITH_DATE,
+ passphrase_time_str));
+ args.SetString(
+ "enterGooglePassphraseBody",
+ GetStringFUTF16(IDS_SYNC_ENTER_GOOGLE_PASSPHRASE_BODY_WITH_DATE,
+ passphrase_time_str));
+ switch (passphrase_type) {
+ case syncer::PassphraseType::FROZEN_IMPLICIT_PASSPHRASE:
+ args.SetString(
+ "fullEncryptionBody",
+ GetStringFUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_GOOGLE_WITH_DATE,
+ passphrase_time_str));
+ break;
+ case syncer::PassphraseType::CUSTOM_PASSPHRASE:
+ args.SetString(
+ "fullEncryptionBody",
+ GetStringFUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM_WITH_DATE,
+ passphrase_time_str));
+ break;
+ default:
+ args.SetString("fullEncryptionBody",
+ GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM));
+ break;
+ }
+ } else if (passphrase_type == syncer::PassphraseType::CUSTOM_PASSPHRASE) {
+ args.SetString("fullEncryptionBody",
+ GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM));
+ }
+
+ FireWebUIListener("sync-prefs-changed", args);
+}
+
+LoginUIService* PeopleHandler::GetLoginUIService() const {
+ return LoginUIServiceFactory::GetForProfile(profile_);
+}
+
+void PeopleHandler::UpdateSyncStatus() {
+ FireWebUIListener("sync-status-changed", *GetSyncStatusDictionary());
+}
+
+void PeopleHandler::MarkFirstSetupComplete() {
+ // Suppress the sign in promo once the user starts sync. This way the user
+ // doesn't see the sign in promo even if they sign out later on.
+ signin::SetUserSkippedPromo(profile_);
+
+ ProfileSyncService* service = GetSyncService();
+ // The sync service may be nullptr if it has been just disabled by policy.
+ if (!service || service->IsFirstSetupComplete())
+ return;
+
+ // This is the first time configuring sync, so log it.
+ base::FilePath profile_file_path = profile_->GetPath();
+ ProfileMetrics::LogProfileSyncSignIn(profile_file_path);
+
+ // We're done configuring, so notify ProfileSyncService that it is OK to
+ // start syncing.
+ sync_blocker_.reset();
+ service->SetFirstSetupComplete();
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/people_handler.h b/chromium/chrome/browser/ui/webui/settings/people_handler.h
new file mode 100644
index 00000000000..255967957c1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/people_handler.h
@@ -0,0 +1,209 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_PEOPLE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_PEOPLE_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/timer/timer.h"
+#include "build/build_config.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_manager_base.h"
+#include "components/sync/driver/sync_service_observer.h"
+
+class LoginUIService;
+class SigninManagerBase;
+
+namespace browser_sync {
+class ProfileSyncService;
+} // namespace browser_sync
+
+namespace content {
+class WebUI;
+} // namespace content
+
+namespace signin_metrics {
+enum class AccessPoint;
+} // namespace signin_metrics
+
+namespace syncer {
+class SyncSetupInProgressHandle;
+} // namespace syncer
+
+namespace settings {
+
+class PeopleHandler : public SettingsPageUIHandler,
+ public SigninManagerBase::Observer,
+ public SyncStartupTracker::Observer,
+ public LoginUIService::LoginUI,
+ public syncer::SyncServiceObserver {
+ public:
+ // TODO(tommycli): Remove these strings and instead use WebUIListener events.
+ // These string constants are used from JavaScript (sync_browser_proxy.js).
+ static const char kSpinnerPageStatus[];
+ static const char kConfigurePageStatus[];
+ static const char kTimeoutPageStatus[];
+ static const char kDonePageStatus[];
+ static const char kPassphraseFailedPageStatus[];
+
+ explicit PeopleHandler(Profile* profile);
+ ~PeopleHandler() override;
+
+ // Initializes the sync setup flow and shows the setup UI.
+ void OpenSyncSetup();
+
+ // Terminates the sync setup flow.
+ void CloseSyncSetup();
+
+ protected:
+ bool is_configuring_sync() const { return configuring_sync_; }
+
+ private:
+ friend class PeopleHandlerTest;
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest,
+ DisplayConfigureWithEngineDisabledAndCancel);
+ FRIEND_TEST_ALL_PREFIXES(
+ PeopleHandlerTest,
+ DisplayConfigureWithEngineDisabledAndSyncStartupCompleted);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, HandleSetupUIWhenSyncDisabled);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, SelectCustomEncryption);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, ShowSyncSetupWhenNotSignedIn);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, SuccessfullySetPassphrase);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, TestSyncEverything);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, TestSyncNothing);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, TestSyncAllManually);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, TestPassphraseStillRequired);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, TestSyncIndividualTypes);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest,
+ EnterExistingFrozenImplicitPassword);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, SetNewCustomPassphrase);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, EnterWrongExistingPassphrase);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, EnterBlankExistingPassphrase);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerTest, TurnOnEncryptAllDisallowed);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerNonCrosTest,
+ UnrecoverableErrorInitializingSync);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerNonCrosTest, GaiaErrorInitializingSync);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerNonCrosTest, HandleCaptcha);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerNonCrosTest, HandleGaiaAuthFailure);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerNonCrosTest,
+ SubmitAuthWithInvalidUsername);
+ FRIEND_TEST_ALL_PREFIXES(PeopleHandlerFirstSigninTest, DisplayBasicLogin);
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ // SyncStartupTracker::Observer implementation.
+ void SyncStartupCompleted() override;
+ void SyncStartupFailed() override;
+
+ // LoginUIService::LoginUI implementation.
+ void FocusUI() override;
+
+ // SigninManagerBase::Observer implementation.
+ void GoogleSigninSucceeded(const std::string& account_id,
+ const std::string& username,
+ const std::string& password) override;
+ void GoogleSignedOut(const std::string& account_id,
+ const std::string& username) override;
+
+ // syncer::SyncServiceObserver implementation.
+ void OnStateChanged(syncer::SyncService* sync) override;
+
+ // Returns a newly created dictionary with a number of properties that
+ // correspond to the status of sync.
+ std::unique_ptr<base::DictionaryValue> GetSyncStatusDictionary();
+
+ // Helper routine that gets the ProfileSyncService associated with the parent
+ // profile.
+ browser_sync::ProfileSyncService* GetSyncService() const;
+
+ // Returns the LoginUIService for the parent profile.
+ LoginUIService* GetLoginUIService() const;
+
+ // Callbacks from the page.
+ void HandleGetProfileInfo(const base::ListValue* args);
+ void OnDidClosePage(const base::ListValue* args);
+ void HandleSetDatatypes(const base::ListValue* args);
+ void HandleSetEncryption(const base::ListValue* args);
+ void HandleShowSetupUI(const base::ListValue* args);
+ void HandleAttemptUserExit(const base::ListValue* args);
+ void HandleStartSignin(const base::ListValue* args);
+ void HandleStopSyncing(const base::ListValue* args);
+ void HandleGetSyncStatus(const base::ListValue* args);
+ void HandleManageOtherPeople(const base::ListValue* args);
+
+#if !defined(OS_CHROMEOS)
+ // Displays the GAIA login form.
+ void DisplayGaiaLogin(signin_metrics::AccessPoint access_point);
+
+ // When web-flow is enabled, displays the Gaia login form in a new tab.
+ // This function is virtual so that tests can override.
+ virtual void DisplayGaiaLoginInNewTabOrWindow(
+ signin_metrics::AccessPoint access_point);
+#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
+ // is running in the background.
+ void DisplaySpinner();
+
+ // Displays an error dialog which shows timeout of starting the sync engine.
+ void DisplayTimeout();
+
+ // Closes the associated sync settings page.
+ void CloseUI();
+
+ // Pushes the updated sync prefs to JavaScript.
+ void PushSyncPrefs();
+
+ // Sends the current sync status to the JavaScript WebUI code.
+ void UpdateSyncStatus();
+
+ // Suppresses any further signin promos, since the user has signed in once.
+ void MarkFirstSetupComplete();
+
+ // Weak pointer.
+ Profile* profile_;
+
+ // Helper object used to wait for the sync engine to startup.
+ std::unique_ptr<SyncStartupTracker> sync_startup_tracker_;
+
+ // Prevents Sync from running until configuration is complete.
+ std::unique_ptr<syncer::SyncSetupInProgressHandle> sync_blocker_;
+
+ // Set to true whenever the sync configure UI is visible. This is used to tell
+ // what stage of the setup wizard the user was in and to update the UMA
+ // histograms in the case that the user cancels out.
+ bool configuring_sync_;
+
+ // The OneShotTimer object used to timeout of starting the sync engine
+ // service.
+ std::unique_ptr<base::OneShotTimer> engine_start_timer_;
+
+ // Used to listen for pref changes to allow or disallow signin.
+ PrefChangeRegistrar profile_pref_registrar_;
+
+ // Manages observer lifetimes.
+ ScopedObserver<SigninManagerBase, PeopleHandler> signin_observer_;
+ ScopedObserver<browser_sync::ProfileSyncService, PeopleHandler>
+ sync_service_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(PeopleHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_PEOPLE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/people_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/people_handler_unittest.cc
new file mode 100644
index 00000000000..45c7fc1e74e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -0,0 +1,924 @@
+// 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.
+
+#include "chrome/browser/ui/webui/settings/people_handler.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/json/json_writer.h"
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/signin/fake_signin_manager_builder.h"
+#include "chrome/browser/signin/signin_error_controller_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/sync/profile_sync_test_util.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"
+#include "chrome/common/pref_names.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 "chrome/test/base/testing_profile_manager.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/fake_auth_status_provider.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/sync/base/sync_prefs.h"
+#include "components/sync_preferences/pref_service_syncable.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/test/test_browser_thread.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_web_ui.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/layout.h"
+
+using ::testing::_;
+using ::testing::Mock;
+using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::Values;
+using browser_sync::ProfileSyncService;
+using browser_sync::ProfileSyncServiceMock;
+
+typedef GoogleServiceAuthError AuthError;
+
+namespace {
+
+MATCHER_P(ModelTypeSetMatches, value, "") {
+ return arg == value;
+}
+
+const char kTestUser[] = "chrome.p13n.test@gmail.com";
+const char kTestCallbackId[] = "test-callback-id";
+
+// Returns a ModelTypeSet with all user selectable types set.
+syncer::ModelTypeSet GetAllTypes() {
+ return syncer::UserSelectableTypes();
+}
+
+enum SyncAllDataConfig {
+ SYNC_ALL_DATA,
+ CHOOSE_WHAT_TO_SYNC
+};
+
+enum EncryptAllConfig {
+ ENCRYPT_ALL_DATA,
+ ENCRYPT_PASSWORDS
+};
+
+// Create a json-format string with the key/value pairs appropriate for a call
+// to HandleSetEncryption(). If |extra_values| is non-null, then the values from
+// the passed dictionary are added to the json.
+std::string GetConfiguration(const base::DictionaryValue* extra_values,
+ SyncAllDataConfig sync_all,
+ syncer::ModelTypeSet types,
+ const std::string& passphrase,
+ EncryptAllConfig encrypt_all) {
+ base::DictionaryValue result;
+ if (extra_values)
+ result.MergeDictionary(extra_values);
+ result.SetBoolean("syncAllDataTypes", sync_all == SYNC_ALL_DATA);
+ result.SetBoolean("encryptAllData", encrypt_all == ENCRYPT_ALL_DATA);
+ if (!passphrase.empty())
+ result.SetString("passphrase", passphrase);
+ // Add all of our data types.
+ result.SetBoolean("appsSynced", types.Has(syncer::APPS));
+ result.SetBoolean("autofillSynced", types.Has(syncer::AUTOFILL));
+ result.SetBoolean("bookmarksSynced", types.Has(syncer::BOOKMARKS));
+ result.SetBoolean("extensionsSynced", types.Has(syncer::EXTENSIONS));
+ result.SetBoolean("passwordsSynced", types.Has(syncer::PASSWORDS));
+ result.SetBoolean("preferencesSynced", types.Has(syncer::PREFERENCES));
+ result.SetBoolean("tabsSynced", types.Has(syncer::PROXY_TABS));
+ result.SetBoolean("themesSynced", types.Has(syncer::THEMES));
+ result.SetBoolean("typedUrlsSynced", types.Has(syncer::TYPED_URLS));
+ result.SetBoolean("paymentsIntegrationEnabled", false);
+ std::string args;
+ base::JSONWriter::Write(result, &args);
+ return args;
+}
+
+// Checks whether the passed |dictionary| contains a |key| with the given
+// |expected_value|. If |omit_if_false| is true, then the value should only
+// be present if |expected_value| is true.
+void CheckBool(const base::DictionaryValue* dictionary,
+ const std::string& key,
+ bool expected_value,
+ bool omit_if_false) {
+ if (omit_if_false && !expected_value) {
+ EXPECT_FALSE(dictionary->HasKey(key)) <<
+ "Did not expect to find value for " << key;
+ } else {
+ bool actual_value;
+ EXPECT_TRUE(dictionary->GetBoolean(key, &actual_value)) <<
+ "No value found for " << key;
+ EXPECT_EQ(expected_value, actual_value) <<
+ "Mismatch found for " << key;
+ }
+}
+
+void CheckBool(const base::DictionaryValue* dictionary,
+ const std::string& key,
+ bool expected_value) {
+ return CheckBool(dictionary, key, expected_value, false);
+}
+
+// Checks to make sure that the values stored in |dictionary| match the values
+// expected by the showSyncSetupPage() JS function for a given set of data
+// types.
+void CheckConfigDataTypeArguments(const base::DictionaryValue* dictionary,
+ SyncAllDataConfig config,
+ syncer::ModelTypeSet types) {
+ CheckBool(dictionary, "syncAllDataTypes", config == SYNC_ALL_DATA);
+ CheckBool(dictionary, "appsSynced", types.Has(syncer::APPS));
+ CheckBool(dictionary, "autofillSynced", types.Has(syncer::AUTOFILL));
+ CheckBool(dictionary, "bookmarksSynced", types.Has(syncer::BOOKMARKS));
+ CheckBool(dictionary, "extensionsSynced", types.Has(syncer::EXTENSIONS));
+ CheckBool(dictionary, "passwordsSynced", types.Has(syncer::PASSWORDS));
+ CheckBool(dictionary, "preferencesSynced", types.Has(syncer::PREFERENCES));
+ CheckBool(dictionary, "tabsSynced", types.Has(syncer::PROXY_TABS));
+ CheckBool(dictionary, "themesSynced", types.Has(syncer::THEMES));
+ CheckBool(dictionary, "typedUrlsSynced", types.Has(syncer::TYPED_URLS));
+}
+
+} // namespace
+
+namespace settings {
+
+class TestingPeopleHandler : public PeopleHandler {
+ public:
+ TestingPeopleHandler(content::WebUI* web_ui, Profile* profile)
+ : PeopleHandler(profile) {
+ set_web_ui(web_ui);
+ }
+
+ using PeopleHandler::is_configuring_sync;
+
+ private:
+#if !defined(OS_CHROMEOS)
+ void DisplayGaiaLoginInNewTabOrWindow(
+ signin_metrics::AccessPoint access_point) override {}
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(TestingPeopleHandler);
+};
+
+// The boolean parameter indicates whether the test is run with ClientOAuth
+// or not. The test parameter is a bool: whether or not to test with/
+// /ClientLogin enabled or not.
+class PeopleHandlerTest : public testing::Test {
+ public:
+ PeopleHandlerTest() : error_(GoogleServiceAuthError::NONE) {}
+ void SetUp() override {
+ error_ = GoogleServiceAuthError::AuthErrorNone();
+
+ profile_manager_.reset(
+ new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
+ ASSERT_TRUE(profile_manager_->SetUp());
+
+ TestingProfile::TestingFactories testing_factories;
+ testing_factories.push_back(std::make_pair(
+ SigninManagerFactory::GetInstance(), BuildFakeSigninManagerBase));
+ profile_ = profile_manager_->CreateTestingProfile(
+ "Person 1", nullptr, base::UTF8ToUTF16("Person 1"), 0, std::string(),
+ testing_factories);
+
+ // Sign in the user.
+ mock_signin_ = static_cast<SigninManagerBase*>(
+ SigninManagerFactory::GetForProfile(profile_));
+ std::string username = GetTestUser();
+ if (!username.empty())
+ mock_signin_->SetAuthenticatedAccountInfo(username, username);
+
+ mock_pss_ = static_cast<ProfileSyncServiceMock*>(
+ ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
+ profile_, BuildMockProfileSyncService));
+ EXPECT_CALL(*mock_pss_, GetAuthError()).WillRepeatedly(ReturnRef(error_));
+ ON_CALL(*mock_pss_, GetPassphraseType())
+ .WillByDefault(Return(syncer::PassphraseType::IMPLICIT_PASSPHRASE));
+ ON_CALL(*mock_pss_, GetExplicitPassphraseTime()).WillByDefault(
+ Return(base::Time()));
+ ON_CALL(*mock_pss_, GetRegisteredDataTypes())
+ .WillByDefault(Return(syncer::ModelTypeSet()));
+
+ mock_pss_->Initialize();
+
+ handler_.reset(new TestingPeopleHandler(&web_ui_, profile_));
+ handler_->AllowJavascript();
+ }
+
+ // Setup the expectations for calls made when displaying the config page.
+ void SetDefaultExpectationsForConfigPage() {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, GetRegisteredDataTypes())
+ .WillRepeatedly(Return(GetAllTypes()));
+ EXPECT_CALL(*mock_pss_, GetPreferredDataTypes())
+ .WillRepeatedly(Return(GetAllTypes()));
+ EXPECT_CALL(*mock_pss_, GetActiveDataTypes())
+ .WillRepeatedly(Return(GetAllTypes()));
+ EXPECT_CALL(*mock_pss_, IsEncryptEverythingAllowed())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsEncryptEverythingEnabled())
+ .WillRepeatedly(Return(false));
+ }
+
+ void SetupInitializedProfileSyncService() {
+ // An initialized ProfileSyncService will have already completed sync setup
+ // and will have an initialized sync engine.
+ ASSERT_TRUE(mock_signin_->IsInitialized());
+ EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(true));
+ }
+
+ void ExpectPageStatusResponse(const std::string& expected_status) {
+ auto& 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(kTestCallbackId, callback_id);
+ bool success = false;
+ ASSERT_TRUE(data.arg2()->GetAsBoolean(&success));
+ EXPECT_TRUE(success);
+ std::string status;
+ ASSERT_TRUE(data.arg3()->GetAsString(&status));
+ EXPECT_EQ(expected_status, status);
+ }
+
+ void ExpectPageStatusChanged(const std::string& expected_status) {
+ auto& data = *web_ui_.call_data().back();
+ EXPECT_EQ("cr.webUIListenerCallback", data.function_name());
+ std::string event;
+ ASSERT_TRUE(data.arg1()->GetAsString(&event));
+ EXPECT_EQ("page-status-changed", event);
+ std::string status;
+ ASSERT_TRUE(data.arg2()->GetAsString(&status));
+ EXPECT_EQ(expected_status, status);
+ }
+
+ void ExpectSpinnerAndClose() {
+ ExpectPageStatusChanged(PeopleHandler::kSpinnerPageStatus);
+
+ // Cancelling the spinner dialog will cause CloseSyncSetup().
+ handler_->CloseSyncSetup();
+ EXPECT_EQ(NULL,
+ LoginUIServiceFactory::GetForProfile(
+ profile_)->current_login_ui());
+ }
+
+ const base::DictionaryValue* ExpectSyncPrefsChanged() {
+ const content::TestWebUI::CallData& data1 = *web_ui_.call_data().back();
+ EXPECT_EQ("cr.webUIListenerCallback", data1.function_name());
+
+ std::string event;
+ EXPECT_TRUE(data1.arg1()->GetAsString(&event));
+ EXPECT_EQ(event, "sync-prefs-changed");
+
+ const base::DictionaryValue* dictionary = nullptr;
+ EXPECT_TRUE(data1.arg2()->GetAsDictionary(&dictionary));
+ return dictionary;
+ }
+
+ // It's difficult to notify sync listeners when using a ProfileSyncServiceMock
+ // so this helper routine dispatches an OnStateChanged() notification to the
+ // SyncStartupTracker.
+ void NotifySyncListeners() {
+ if (handler_->sync_startup_tracker_)
+ handler_->sync_startup_tracker_->OnStateChanged(mock_pss_);
+ }
+
+ void NotifySyncStateChanged() { handler_->OnStateChanged(mock_pss_); }
+
+ virtual std::string GetTestUser() {
+ return std::string(kTestUser);
+ }
+
+ content::TestBrowserThreadBundle thread_bundle_;
+ std::unique_ptr<TestingProfileManager> profile_manager_;
+ Profile* profile_;
+ ProfileSyncServiceMock* mock_pss_;
+ GoogleServiceAuthError error_;
+ SigninManagerBase* mock_signin_;
+ content::TestWebUI web_ui_;
+ std::unique_ptr<TestingPeopleHandler> handler_;
+};
+
+class PeopleHandlerFirstSigninTest : public PeopleHandlerTest {
+ std::string GetTestUser() override { return std::string(); }
+};
+
+#if !defined(OS_CHROMEOS)
+TEST_F(PeopleHandlerFirstSigninTest, DisplayBasicLogin) {
+ // Test that the HandleStartSignin call enables JavaScript.
+ handler_->DisallowJavascript();
+
+ 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_);
+ manager->SignOut(signin_metrics::SIGNOUT_TEST,
+ signin_metrics::SignoutDelete::IGNORE_METRIC);
+ base::ListValue list_args;
+ handler_->HandleStartSignin(&list_args);
+
+ // Sync setup hands off control to the gaia login tab.
+ EXPECT_EQ(NULL,
+ LoginUIServiceFactory::GetForProfile(
+ profile_)->current_login_ui());
+
+ ASSERT_FALSE(handler_->is_configuring_sync());
+
+ handler_->CloseSyncSetup();
+ EXPECT_EQ(NULL,
+ LoginUIServiceFactory::GetForProfile(
+ profile_)->current_login_ui());
+}
+
+TEST_F(PeopleHandlerTest, ShowSyncSetupWhenNotSignedIn) {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
+ handler_->HandleShowSetupUI(NULL);
+
+ ExpectPageStatusChanged(PeopleHandler::kDonePageStatus);
+
+ ASSERT_FALSE(handler_->is_configuring_sync());
+ EXPECT_EQ(NULL,
+ LoginUIServiceFactory::GetForProfile(
+ profile_)->current_login_ui());
+}
+#endif // !defined(OS_CHROMEOS)
+
+// Verifies that the sync setup is terminated correctly when the
+// sync is disabled.
+TEST_F(PeopleHandlerTest, HandleSetupUIWhenSyncDisabled) {
+ EXPECT_CALL(*mock_pss_, IsManaged()).WillRepeatedly(Return(true));
+ handler_->HandleShowSetupUI(NULL);
+
+ // Sync setup is closed when sync is disabled.
+ EXPECT_EQ(NULL,
+ LoginUIServiceFactory::GetForProfile(
+ profile_)->current_login_ui());
+ ASSERT_FALSE(handler_->is_configuring_sync());
+}
+
+// Verifies that the handler correctly handles a cancellation when
+// it is displaying the spinner to the user.
+TEST_F(PeopleHandlerTest, DisplayConfigureWithEngineDisabledAndCancel) {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
+ error_ = GoogleServiceAuthError::AuthErrorNone();
+ EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, RequestStart());
+
+ // We're simulating a user setting up sync, which would cause the engine to
+ // kick off initialization, but not download user data types. The sync
+ // engine will try to download control data types (e.g encryption info), but
+ // that won't finish for this test as we're simulating cancelling while the
+ // spinner is showing.
+ handler_->HandleShowSetupUI(NULL);
+
+ EXPECT_EQ(handler_.get(),
+ LoginUIServiceFactory::GetForProfile(
+ profile_)->current_login_ui());
+
+ ExpectSpinnerAndClose();
+}
+
+// Verifies that the handler correctly transitions from showing the spinner
+// to showing a configuration page when sync setup completes successfully.
+TEST_F(PeopleHandlerTest,
+ DisplayConfigureWithEngineDisabledAndSyncStartupCompleted) {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
+ error_ = GoogleServiceAuthError::AuthErrorNone();
+ // Sync engine is stopped initially, and will start up.
+ EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, RequestStart());
+ SetDefaultExpectationsForConfigPage();
+
+ handler_->OpenSyncSetup();
+
+ EXPECT_EQ(1U, web_ui_.call_data().size());
+ ExpectPageStatusChanged(PeopleHandler::kSpinnerPageStatus);
+
+ Mock::VerifyAndClearExpectations(mock_pss_);
+ // Now, act as if the ProfileSyncService has started up.
+ SetDefaultExpectationsForConfigPage();
+ EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(true));
+ error_ = GoogleServiceAuthError::AuthErrorNone();
+ EXPECT_CALL(*mock_pss_, GetAuthError()).WillRepeatedly(ReturnRef(error_));
+ handler_->SyncStartupCompleted();
+
+ EXPECT_EQ(2U, web_ui_.call_data().size());
+
+ const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
+ CheckBool(dictionary, "syncAllDataTypes", true);
+ CheckBool(dictionary, "encryptAllDataAllowed", true);
+ CheckBool(dictionary, "encryptAllData", false);
+ CheckBool(dictionary, "passphraseRequired", false);
+}
+
+// Verifies the case where the user cancels after the sync engine has
+// initialized (meaning it already transitioned from the spinner to a proper
+// configuration page, tested by
+// DisplayConfigureWithEngineDisabledAndSyncStartupCompleted), but before the
+// user has continued on.
+TEST_F(PeopleHandlerTest,
+ DisplayConfigureWithEngineDisabledAndCancelAfterSigninSuccess) {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
+ error_ = GoogleServiceAuthError::AuthErrorNone();
+ EXPECT_CALL(*mock_pss_, IsEngineInitialized())
+ .WillOnce(Return(false))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, RequestStart());
+ SetDefaultExpectationsForConfigPage();
+ handler_->OpenSyncSetup();
+
+ // It's important to tell sync the user cancelled the setup flow before we
+ // tell it we're through with the setup progress.
+ testing::InSequence seq;
+ EXPECT_CALL(*mock_pss_, RequestStop(ProfileSyncService::CLEAR_DATA));
+ EXPECT_CALL(*mock_pss_, OnSetupInProgressHandleDestroyed());
+
+ handler_->CloseSyncSetup();
+ EXPECT_EQ(NULL,
+ LoginUIServiceFactory::GetForProfile(
+ profile_)->current_login_ui());
+}
+
+TEST_F(PeopleHandlerTest, DisplayConfigureWithEngineDisabledAndSigninFailed) {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
+ error_ = GoogleServiceAuthError::AuthErrorNone();
+ EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, RequestStart());
+
+ handler_->OpenSyncSetup();
+ ExpectPageStatusChanged(PeopleHandler::kSpinnerPageStatus);
+ Mock::VerifyAndClearExpectations(mock_pss_);
+ error_ = GoogleServiceAuthError(
+ GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
+ EXPECT_CALL(*mock_pss_, GetAuthError()).WillRepeatedly(ReturnRef(error_));
+ NotifySyncListeners();
+
+ // On failure, the dialog will be closed.
+ EXPECT_EQ(NULL,
+ LoginUIServiceFactory::GetForProfile(
+ profile_)->current_login_ui());
+}
+
+// Tests that signals not related to user intention to configure sync don't
+// trigger sync engine start.
+TEST_F(PeopleHandlerTest, OnlyStartEngineWhenConfiguringSync) {
+ EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, RequestStart()).Times(0);
+ NotifySyncStateChanged();
+}
+
+#if !defined(OS_CHROMEOS)
+
+class PeopleHandlerNonCrosTest : public PeopleHandlerTest {
+ public:
+ PeopleHandlerNonCrosTest() {}
+};
+
+TEST_F(PeopleHandlerNonCrosTest, HandleGaiaAuthFailure) {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, HasUnrecoverableError())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
+ // Open the web UI.
+ handler_->OpenSyncSetup();
+
+ ASSERT_FALSE(handler_->is_configuring_sync());
+}
+
+// TODO(kochi): We need equivalent tests for ChromeOS.
+TEST_F(PeopleHandlerNonCrosTest, UnrecoverableErrorInitializingSync) {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
+ // Open the web UI.
+ handler_->OpenSyncSetup();
+
+ ASSERT_FALSE(handler_->is_configuring_sync());
+}
+
+TEST_F(PeopleHandlerNonCrosTest, GaiaErrorInitializingSync) {
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsFirstSetupComplete()).WillRepeatedly(Return(false));
+ // Open the web UI.
+ handler_->OpenSyncSetup();
+
+ ASSERT_FALSE(handler_->is_configuring_sync());
+}
+
+#endif // #if !defined(OS_CHROMEOS)
+
+TEST_F(PeopleHandlerTest, TestSyncEverything) {
+ std::string args = GetConfiguration(
+ NULL, SYNC_ALL_DATA, GetAllTypes(), std::string(), ENCRYPT_PASSWORDS);
+ base::ListValue list_args;
+ list_args.AppendString(kTestCallbackId);
+ list_args.AppendString(args);
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ EXPECT_CALL(*mock_pss_, OnUserChoseDatatypes(true, _));
+ handler_->HandleSetDatatypes(&list_args);
+
+ ExpectPageStatusResponse(PeopleHandler::kConfigurePageStatus);
+}
+
+TEST_F(PeopleHandlerTest, TestPassphraseStillRequired) {
+ std::string args = GetConfiguration(
+ NULL, SYNC_ALL_DATA, GetAllTypes(), std::string(), ENCRYPT_PASSWORDS);
+ base::ListValue list_args;
+ list_args.AppendString(kTestCallbackId);
+ list_args.AppendString(args);
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ SetDefaultExpectationsForConfigPage();
+
+ handler_->HandleSetEncryption(&list_args);
+ // We should navigate back to the configure page since we need a passphrase.
+ ExpectPageStatusResponse(PeopleHandler::kPassphraseFailedPageStatus);
+}
+
+TEST_F(PeopleHandlerTest, EnterExistingFrozenImplicitPassword) {
+ base::DictionaryValue dict;
+ dict.SetBoolean("setNewPassphrase", false);
+ std::string args = GetConfiguration(&dict, SYNC_ALL_DATA, GetAllTypes(),
+ "oldGaiaPassphrase", ENCRYPT_PASSWORDS);
+ base::ListValue list_args;
+ list_args.AppendString(kTestCallbackId);
+ list_args.AppendString(args);
+ // Act as if an encryption passphrase is required the first time, then never
+ // again after that.
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired()).WillOnce(Return(true));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ EXPECT_CALL(*mock_pss_, SetDecryptionPassphrase("oldGaiaPassphrase"))
+ .WillOnce(Return(true));
+
+ handler_->HandleSetEncryption(&list_args);
+ ExpectPageStatusResponse(PeopleHandler::kConfigurePageStatus);
+}
+
+TEST_F(PeopleHandlerTest, SetNewCustomPassphrase) {
+ base::DictionaryValue dict;
+ dict.SetBoolean("setNewPassphrase", true);
+ std::string args = GetConfiguration(&dict, SYNC_ALL_DATA, GetAllTypes(),
+ "custom_passphrase", ENCRYPT_ALL_DATA);
+ base::ListValue list_args;
+ list_args.AppendString(kTestCallbackId);
+ list_args.AppendString(args);
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ EXPECT_CALL(*mock_pss_,
+ SetEncryptionPassphrase("custom_passphrase",
+ ProfileSyncService::EXPLICIT));
+
+ handler_->HandleSetEncryption(&list_args);
+ ExpectPageStatusResponse(PeopleHandler::kConfigurePageStatus);
+}
+
+TEST_F(PeopleHandlerTest, EnterWrongExistingPassphrase) {
+ base::DictionaryValue dict;
+ dict.SetBoolean("setNewPassphrase", false);
+ std::string args = GetConfiguration(&dict, SYNC_ALL_DATA, GetAllTypes(),
+ "invalid_passphrase", ENCRYPT_ALL_DATA);
+ base::ListValue list_args;
+ list_args.AppendString(kTestCallbackId);
+ list_args.AppendString(args);
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ EXPECT_CALL(*mock_pss_, SetDecryptionPassphrase("invalid_passphrase")).
+ WillOnce(Return(false));
+
+ SetDefaultExpectationsForConfigPage();
+
+ handler_->HandleSetEncryption(&list_args);
+ // We should navigate back to the configure page since we need a passphrase.
+ ExpectPageStatusResponse(PeopleHandler::kPassphraseFailedPageStatus);
+}
+
+TEST_F(PeopleHandlerTest, EnterBlankExistingPassphrase) {
+ base::DictionaryValue dict;
+ dict.SetBoolean("setNewPassphrase", false);
+ std::string args = GetConfiguration(&dict,
+ SYNC_ALL_DATA,
+ GetAllTypes(),
+ "",
+ ENCRYPT_PASSWORDS);
+ base::ListValue list_args;
+ list_args.AppendString(kTestCallbackId);
+ list_args.AppendString(args);
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+
+ SetDefaultExpectationsForConfigPage();
+
+ handler_->HandleSetEncryption(&list_args);
+ // We should navigate back to the configure page since we need a passphrase.
+ ExpectPageStatusResponse(PeopleHandler::kPassphraseFailedPageStatus);
+}
+
+// Walks through each user selectable type, and tries to sync just that single
+// data type.
+TEST_F(PeopleHandlerTest, TestSyncIndividualTypes) {
+ syncer::ModelTypeSet user_selectable_types = GetAllTypes();
+ syncer::ModelTypeSet::Iterator it;
+ for (it = user_selectable_types.First(); it.Good(); it.Inc()) {
+ syncer::ModelTypeSet type_to_set;
+ type_to_set.Put(it.Get());
+ std::string args = GetConfiguration(NULL,
+ CHOOSE_WHAT_TO_SYNC,
+ type_to_set,
+ std::string(),
+ ENCRYPT_PASSWORDS);
+ base::ListValue list_args;
+ list_args.AppendString(kTestCallbackId);
+ list_args.AppendString(args);
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ EXPECT_CALL(*mock_pss_,
+ OnUserChoseDatatypes(false, ModelTypeSetMatches(type_to_set)));
+
+ handler_->HandleSetDatatypes(&list_args);
+ ExpectPageStatusResponse(PeopleHandler::kConfigurePageStatus);
+ Mock::VerifyAndClearExpectations(mock_pss_);
+ }
+}
+
+TEST_F(PeopleHandlerTest, TestSyncAllManually) {
+ std::string args = GetConfiguration(NULL,
+ CHOOSE_WHAT_TO_SYNC,
+ GetAllTypes(),
+ std::string(),
+ ENCRYPT_PASSWORDS);
+ base::ListValue list_args;
+ list_args.AppendString(kTestCallbackId);
+ list_args.AppendString(args);
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ EXPECT_CALL(*mock_pss_,
+ OnUserChoseDatatypes(false, ModelTypeSetMatches(GetAllTypes())));
+ handler_->HandleSetDatatypes(&list_args);
+
+ ExpectPageStatusResponse(PeopleHandler::kConfigurePageStatus);
+}
+
+TEST_F(PeopleHandlerTest, ShowSyncSetup) {
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ // This should display the sync setup dialog (not login).
+ SetDefaultExpectationsForConfigPage();
+ handler_->OpenSyncSetup();
+
+ ExpectSyncPrefsChanged();
+}
+
+// We do not display signin on chromeos in the case of auth error.
+TEST_F(PeopleHandlerTest, ShowSigninOnAuthError) {
+ // Initialize the system to a signed in state, but with an auth error.
+ error_ = GoogleServiceAuthError(
+ GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
+
+ SetupInitializedProfileSyncService();
+ mock_signin_->SetAuthenticatedAccountInfo(kTestUser, kTestUser);
+ FakeAuthStatusProvider provider(
+ SigninErrorControllerFactory::GetForProfile(profile_));
+ provider.SetAuthError(kTestUser, error_);
+ EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsEngineInitialized()).WillRepeatedly(Return(false));
+
+#if defined(OS_CHROMEOS)
+ // On ChromeOS, auth errors are ignored - instead we just try to start the
+ // sync engine (which will fail due to the auth error). This should only
+ // happen if the user manually navigates to chrome://settings/syncSetup -
+ // clicking on the button in the UI will sign the user out rather than
+ // displaying a spinner. Should be no visible UI on ChromeOS in this case.
+ EXPECT_EQ(NULL, LoginUIServiceFactory::GetForProfile(
+ profile_)->current_login_ui());
+#else
+
+ // On ChromeOS, this should display the spinner while we try to startup the
+ // sync engine, and on desktop this displays the login dialog.
+ handler_->OpenSyncSetup();
+
+ // Sync setup is closed when re-auth is in progress.
+ EXPECT_EQ(NULL,
+ LoginUIServiceFactory::GetForProfile(
+ profile_)->current_login_ui());
+
+ ASSERT_FALSE(handler_->is_configuring_sync());
+#endif
+}
+
+TEST_F(PeopleHandlerTest, ShowSetupSyncEverything) {
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ SetDefaultExpectationsForConfigPage();
+ // This should display the sync setup dialog (not login).
+ handler_->OpenSyncSetup();
+
+ const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
+ CheckBool(dictionary, "syncAllDataTypes", true);
+ CheckBool(dictionary, "appsRegistered", true);
+ CheckBool(dictionary, "autofillRegistered", true);
+ CheckBool(dictionary, "bookmarksRegistered", true);
+ CheckBool(dictionary, "extensionsRegistered", true);
+ CheckBool(dictionary, "passwordsRegistered", true);
+ CheckBool(dictionary, "preferencesRegistered", true);
+ CheckBool(dictionary, "tabsRegistered", true);
+ CheckBool(dictionary, "themesRegistered", true);
+ CheckBool(dictionary, "typedUrlsRegistered", true);
+ CheckBool(dictionary, "paymentsIntegrationEnabled", true);
+ CheckBool(dictionary, "passphraseRequired", false);
+ CheckBool(dictionary, "passphraseTypeIsCustom", false);
+ CheckBool(dictionary, "encryptAllData", false);
+ CheckConfigDataTypeArguments(dictionary, SYNC_ALL_DATA, GetAllTypes());
+}
+
+TEST_F(PeopleHandlerTest, ShowSetupManuallySyncAll) {
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ syncer::SyncPrefs sync_prefs(profile_->GetPrefs());
+ sync_prefs.SetKeepEverythingSynced(false);
+ SetDefaultExpectationsForConfigPage();
+ // This should display the sync setup dialog (not login).
+ handler_->OpenSyncSetup();
+
+ const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
+ CheckConfigDataTypeArguments(dictionary, CHOOSE_WHAT_TO_SYNC, GetAllTypes());
+}
+
+TEST_F(PeopleHandlerTest, ShowSetupSyncForAllTypesIndividually) {
+ syncer::ModelTypeSet user_selectable_types = GetAllTypes();
+ syncer::ModelTypeSet::Iterator it;
+ for (it = user_selectable_types.First(); it.Good(); it.Inc()) {
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ syncer::SyncPrefs sync_prefs(profile_->GetPrefs());
+ sync_prefs.SetKeepEverythingSynced(false);
+ SetDefaultExpectationsForConfigPage();
+ syncer::ModelTypeSet types;
+ types.Put(it.Get());
+ EXPECT_CALL(*mock_pss_, GetPreferredDataTypes()).
+ WillRepeatedly(Return(types));
+
+ // This should display the sync setup dialog (not login).
+ handler_->OpenSyncSetup();
+
+ // Close the config overlay.
+ LoginUIServiceFactory::GetForProfile(profile_)->LoginUIClosed(
+ handler_.get());
+
+ const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
+ CheckConfigDataTypeArguments(dictionary, CHOOSE_WHAT_TO_SYNC, types);
+ Mock::VerifyAndClearExpectations(mock_pss_);
+ // Clean up so we can loop back to display the dialog again.
+ web_ui_.ClearTrackedCalls();
+ }
+}
+
+TEST_F(PeopleHandlerTest, ShowSetupOldGaiaPassphraseRequired) {
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, GetPassphraseType())
+ .WillRepeatedly(
+ Return(syncer::PassphraseType::FROZEN_IMPLICIT_PASSPHRASE));
+ SetupInitializedProfileSyncService();
+ SetDefaultExpectationsForConfigPage();
+
+ // This should display the sync setup dialog (not login).
+ handler_->OpenSyncSetup();
+
+ const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
+ CheckBool(dictionary, "passphraseRequired", true);
+ CheckBool(dictionary, "passphraseTypeIsCustom", false);
+}
+
+TEST_F(PeopleHandlerTest, ShowSetupCustomPassphraseRequired) {
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*mock_pss_, GetPassphraseType())
+ .WillRepeatedly(Return(syncer::PassphraseType::CUSTOM_PASSPHRASE));
+ SetupInitializedProfileSyncService();
+ SetDefaultExpectationsForConfigPage();
+
+ // This should display the sync setup dialog (not login).
+ handler_->OpenSyncSetup();
+
+ const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
+ CheckBool(dictionary, "passphraseRequired", true);
+ CheckBool(dictionary, "passphraseTypeIsCustom", true);
+}
+
+TEST_F(PeopleHandlerTest, ShowSetupEncryptAll) {
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ SetDefaultExpectationsForConfigPage();
+ EXPECT_CALL(*mock_pss_, IsEncryptEverythingEnabled())
+ .WillRepeatedly(Return(true));
+
+ // This should display the sync setup dialog (not login).
+ handler_->OpenSyncSetup();
+
+ const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
+ CheckBool(dictionary, "encryptAllData", true);
+}
+
+TEST_F(PeopleHandlerTest, ShowSetupEncryptAllDisallowed) {
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ SetDefaultExpectationsForConfigPage();
+ EXPECT_CALL(*mock_pss_, IsEncryptEverythingAllowed())
+ .WillRepeatedly(Return(false));
+
+ // This should display the sync setup dialog (not login).
+ handler_->OpenSyncSetup();
+
+ const base::DictionaryValue* dictionary = ExpectSyncPrefsChanged();
+ CheckBool(dictionary, "encryptAllData", false);
+ CheckBool(dictionary, "encryptAllDataAllowed", false);
+}
+
+TEST_F(PeopleHandlerTest, TurnOnEncryptAllDisallowed) {
+ std::string args = GetConfiguration(
+ NULL, SYNC_ALL_DATA, GetAllTypes(), std::string(), ENCRYPT_ALL_DATA);
+ base::ListValue list_args;
+ list_args.AppendString(kTestCallbackId);
+ list_args.AppendString(args);
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequiredForDecryption())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, IsPassphraseRequired())
+ .WillRepeatedly(Return(false));
+ SetupInitializedProfileSyncService();
+ EXPECT_CALL(*mock_pss_, IsEncryptEverythingAllowed())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*mock_pss_, EnableEncryptEverything()).Times(0);
+ handler_->HandleSetEncryption(&list_args);
+
+ ExpectPageStatusResponse(PeopleHandler::kConfigurePageStatus);
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/profile_info_handler.cc b/chromium/chrome/browser/ui/webui/settings/profile_info_handler.cc
new file mode 100644
index 00000000000..bac2e490ef5
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/profile_info_handler.cc
@@ -0,0 +1,225 @@
+// 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 "chrome/browser/ui/webui/settings/profile_info_handler.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#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/common/pref_names.h"
+#include "ui/base/webui/web_ui_util.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/ui/webui/options/chromeos/user_image_source.h"
+#include "components/signin/core/account_id/account_id.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/notification_service.h"
+#else
+#include "chrome/browser/profiles/profile_avatar_icon_util.h"
+#include "chrome/browser/profiles/profile_statistics.h"
+#include "chrome/browser/profiles/profile_statistics_factory.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#endif
+
+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";
+
+ProfileInfoHandler::ProfileInfoHandler(Profile* profile)
+ : profile_(profile),
+#if defined(OS_CHROMEOS)
+ user_manager_observer_(this),
+#endif
+ profile_observer_(this),
+ callback_weak_ptr_factory_(this) {
+#if defined(OS_CHROMEOS)
+ // Set up the chrome://userimage/ source.
+ content::URLDataSource::Add(profile,
+ new chromeos::options::UserImageSource());
+#endif
+}
+
+ProfileInfoHandler::~ProfileInfoHandler() {}
+
+void ProfileInfoHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "getProfileInfo", base::Bind(&ProfileInfoHandler::HandleGetProfileInfo,
+ base::Unretained(this)));
+#if !defined(OS_CHROMEOS)
+ web_ui()->RegisterMessageCallback(
+ "getProfileStatsCount",
+ 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
+}
+
+void ProfileInfoHandler::OnJavascriptDisallowed() {
+ callback_weak_ptr_factory_.InvalidateWeakPtrs();
+
+ 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
+}
+
+#if defined(OS_CHROMEOS)
+void ProfileInfoHandler::OnUserImageChanged(const user_manager::User& user) {
+ PushProfileInfo();
+}
+#endif
+
+void ProfileInfoHandler::OnProfileNameChanged(
+ const base::FilePath& /* profile_path */,
+ const base::string16& /* old_profile_name */) {
+ PushProfileInfo();
+}
+
+void ProfileInfoHandler::OnProfileAvatarChanged(
+ const base::FilePath& /* profile_path */) {
+ PushProfileInfo();
+}
+
+void ProfileInfoHandler::HandleGetProfileInfo(const base::ListValue* args) {
+ AllowJavascript();
+
+ CHECK_EQ(1U, args->GetSize());
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+
+ ResolveJavascriptCallback(*callback_id, *GetAccountNameAndIcon());
+}
+
+#if !defined(OS_CHROMEOS)
+void ProfileInfoHandler::HandleGetProfileStats(const base::ListValue* args) {
+ AllowJavascript();
+
+ // Because there is open browser window for the current profile, statistics
+ // from the ProfileAttributesStorage may not be up-to-date or may be missing
+ // (e.g., |item.success| is false). Therefore, query the actual statistics.
+ ProfileStatisticsFactory::GetForProfile(profile_)->GatherStatistics(
+ base::Bind(&ProfileInfoHandler::PushProfileStatsCount,
+ callback_weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ProfileInfoHandler::PushProfileStatsCount(
+ profiles::ProfileCategoryStats stats) {
+ int count = 0;
+ for (const auto& item : stats) {
+ std::unique_ptr<base::DictionaryValue> stat(new base::DictionaryValue);
+ if (!item.success) {
+ count = 0;
+ break;
+ }
+ count += item.count;
+ }
+ // PushProfileStatsCount gets invoked multiple times as each stat becomes
+ // available. Therefore, webUIListenerCallback mechanism is used instead of
+ // the Promise callback approach.
+ FireWebUIListener(kProfileStatsCountReadyEventName, base::Value(count));
+}
+#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;
+ std::string icon_url;
+
+#if defined(OS_CHROMEOS)
+ const user_manager::User* user =
+ chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
+ DCHECK(user);
+ name = base::UTF16ToUTF8(user->GetDisplayName());
+
+ // Get image as data URL instead of using chrome://userimage source to avoid
+ // issues with caching.
+ scoped_refptr<base::RefCountedMemory> image =
+ chromeos::options::UserImageSource::GetUserImage(user->GetAccountId());
+ icon_url = webui::GetPngDataUrl(image->front(), image->size());
+#else // !defined(OS_CHROMEOS)
+ ProfileAttributesEntry* entry;
+ if (g_browser_process->profile_manager()
+ ->GetProfileAttributesStorage()
+ .GetProfileAttributesWithPath(profile_->GetPath(), &entry)) {
+ name = base::UTF16ToUTF8(entry->GetName());
+ // TODO(crbug.com/710660): return chrome://theme/IDR_PROFILE_AVATAR_*
+ // and update theme_source.cc to get high res avatar icons. This does less
+ // work here, sends less over IPC, and is more stable with returned results.
+ int kAvatarIconSize = 40.f * web_ui()->GetDeviceScaleFactor();
+ gfx::Image icon = profiles::GetSizedAvatarIcon(
+ entry->GetAvatarIcon(), true, kAvatarIconSize, kAvatarIconSize);
+ icon_url = webui::GetBitmapDataUrl(icon.AsBitmap());
+ }
+#endif // defined(OS_CHROMEOS)
+
+ base::DictionaryValue* response = new base::DictionaryValue();
+ response->SetString("name", name);
+ response->SetString("iconUrl", icon_url);
+ return base::WrapUnique(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
new file mode 100644
index 00000000000..5d8386beace
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/profile_info_handler.h
@@ -0,0 +1,107 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_PROFILE_INFO_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_PROFILE_INFO_HANDLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
+#include "build/build_config.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "components/prefs/pref_change_registrar.h"
+
+#if defined(OS_CHROMEOS)
+#include "components/user_manager/user_manager.h"
+#else
+#include "chrome/browser/profiles/profile_statistics_common.h"
+#endif
+
+class Profile;
+
+namespace settings {
+
+class ProfileInfoHandler : public SettingsPageUIHandler,
+#if defined(OS_CHROMEOS)
+ public user_manager::UserManager::Observer,
+#endif
+ public ProfileAttributesStorage::Observer {
+ public:
+ static const char kProfileInfoChangedEventName[];
+ static const char kProfileManagesSupervisedUsersChangedEventName[];
+ static const char kProfileStatsCountReadyEventName[];
+
+ explicit ProfileInfoHandler(Profile* profile);
+ ~ProfileInfoHandler() override;
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+#if defined(OS_CHROMEOS)
+ // user_manager::UserManager::Observer implementation.
+ void OnUserImageChanged(const user_manager::User& user) override;
+#endif
+
+ // ProfileAttributesStorage::Observer implementation.
+ void OnProfileNameChanged(const base::FilePath& profile_path,
+ const base::string16& old_profile_name) override;
+ void OnProfileAvatarChanged(const base::FilePath& profile_path) override;
+
+ 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)
+ void HandleGetProfileStats(const base::ListValue* args);
+
+ // Returns the sum of the counts of individual profile states. Returns 0 if
+ // there exists a stat that was not successfully retrieved.
+ 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.
+ Profile* profile_;
+
+#if defined(OS_CHROMEOS)
+ ScopedObserver<user_manager::UserManager, ProfileInfoHandler>
+ user_manager_observer_;
+#endif
+
+ 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_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProfileInfoHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_PROFILE_INFO_HANDLER_H_
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
new file mode 100644
index 00000000000..a002473f7e9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/profile_info_handler_unittest.cc
@@ -0,0 +1,203 @@
+// 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 "chrome/browser/ui/webui/settings/profile_info_handler.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_web_ui.h"
+#include "net/base/data_url.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "url/gurl.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
+#endif
+
+namespace settings {
+
+namespace {
+
+#if defined(OS_CHROMEOS)
+constexpr char fake_id[] = "fake_id";
+constexpr char fake_email[] = "fake_id@gmail.com";
+#endif
+
+class TestProfileInfoHandler : public ProfileInfoHandler {
+ public:
+ explicit TestProfileInfoHandler(Profile* profile)
+ : ProfileInfoHandler(profile) {}
+
+ using ProfileInfoHandler::set_web_ui;
+};
+
+} // namespace
+
+class ProfileInfoHandlerTest : public testing::Test {
+ public:
+ ProfileInfoHandlerTest()
+ : profile_manager_(TestingBrowserProcess::GetGlobal()),
+ profile_(nullptr) {}
+
+ void SetUp() override {
+ ASSERT_TRUE(profile_manager_.SetUp());
+
+#if defined(OS_CHROMEOS)
+ chromeos::FakeChromeUserManager* fake_user_manager =
+ new chromeos::FakeChromeUserManager;
+ user_manager_enabler_.reset(
+ new chromeos::ScopedUserManagerEnabler(fake_user_manager));
+ profile_ = profile_manager_.CreateTestingProfile(fake_email);
+ fake_user_manager->AddUser(AccountId::FromUserEmail(fake_email));
+#else
+ profile_ = profile_manager_.CreateTestingProfile("Profile 1");
+#endif
+
+ handler_.reset(new TestProfileInfoHandler(profile_));
+ handler_->set_web_ui(&web_ui_);
+ }
+
+ void VerifyProfileInfo(const base::Value* call_argument) {
+ const base::DictionaryValue* response = nullptr;
+ ASSERT_TRUE(call_argument->GetAsDictionary(&response));
+
+ std::string name;
+ std::string icon_url;
+ ASSERT_TRUE(response->GetString("name", &name));
+ ASSERT_TRUE(response->GetString("iconUrl", &icon_url));
+
+#if defined(OS_CHROMEOS)
+ EXPECT_EQ(fake_id, name);
+ EXPECT_FALSE(icon_url.empty());
+#else
+ EXPECT_EQ("Profile 1", name);
+
+ std::string mime, charset, data;
+ EXPECT_TRUE(net::DataURL::Parse(GURL(icon_url), &mime, &charset, &data));
+
+ EXPECT_EQ("image/png", mime);
+ SkBitmap bitmap;
+ EXPECT_TRUE(gfx::PNGCodec::Decode(
+ reinterpret_cast<const unsigned char*>(data.data()), data.size(),
+ &bitmap));
+#endif
+ }
+
+ content::TestWebUI* web_ui() { return &web_ui_; }
+ Profile* profile() const { return profile_; }
+ TestProfileInfoHandler* handler() const { return handler_.get(); }
+
+ private:
+ content::TestBrowserThreadBundle thread_bundle_;
+ TestingProfileManager profile_manager_;
+ content::TestWebUI web_ui_;
+
+#if defined(OS_CHROMEOS)
+ std::unique_ptr<chromeos::ScopedUserManagerEnabler> user_manager_enabler_;
+#endif
+
+ Profile* profile_;
+ std::unique_ptr<TestProfileInfoHandler> handler_;
+};
+
+TEST_F(ProfileInfoHandlerTest, GetProfileInfo) {
+ base::ListValue list_args;
+ list_args.AppendString("get-profile-info-callback-id");
+ handler()->HandleGetProfileInfo(&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-info-callback-id", callback_id);
+
+ bool success = false;
+ ASSERT_TRUE(data.arg2()->GetAsBoolean(&success));
+ EXPECT_TRUE(success);
+
+ VerifyProfileInfo(data.arg3());
+}
+
+TEST_F(ProfileInfoHandlerTest, PushProfileInfo) {
+ handler()->AllowJavascript();
+
+ handler()->OnProfileAvatarChanged(base::FilePath());
+
+ 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::kProfileInfoChangedEventName, event_id);
+
+ 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",
+ base::MakeUnique<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
new file mode 100644
index 00000000000..701fdad9632
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/protocol_handlers_handler.cc
@@ -0,0 +1,227 @@
+// 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 "chrome/browser/ui/webui/settings/protocol_handlers_handler.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "components/google/core/browser/google_util.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_ui.h"
+
+namespace settings {
+
+ProtocolHandlersHandler::ProtocolHandlersHandler() {
+}
+
+ProtocolHandlersHandler::~ProtocolHandlersHandler() {
+}
+
+void ProtocolHandlersHandler::OnJavascriptAllowed() {
+ notification_registrar_.Add(
+ this, chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED,
+ content::Source<Profile>(Profile::FromWebUI(web_ui())));
+}
+
+void ProtocolHandlersHandler::OnJavascriptDisallowed() {
+ notification_registrar_.RemoveAll();
+}
+
+void ProtocolHandlersHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("observeProtocolHandlers",
+ base::Bind(&ProtocolHandlersHandler::HandleObserveProtocolHandlers,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("observeProtocolHandlersEnabledState",
+ 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)));
+ web_ui()->RegisterMessageCallback("setHandlersEnabled",
+ base::Bind(&ProtocolHandlersHandler::HandleSetHandlersEnabled,
+ base::Unretained(this)));
+ 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() {
+ return ProtocolHandlerRegistryFactory::GetForBrowserContext(
+ Profile::FromWebUI(web_ui()));
+}
+
+static void GetHandlersAsListValue(
+ const ProtocolHandlerRegistry::ProtocolHandlerList& handlers,
+ base::ListValue* handler_list) {
+ ProtocolHandlerRegistry::ProtocolHandlerList::const_iterator handler;
+ for (handler = handlers.begin(); handler != handlers.end(); ++handler) {
+ std::unique_ptr<base::DictionaryValue> handler_value(
+ new base::DictionaryValue());
+ handler_value->SetString("protocol", handler->protocol());
+ handler_value->SetString("spec", handler->url().spec());
+ handler_value->SetString("host", handler->url().host());
+ handler_list->Append(std::move(handler_value));
+ }
+}
+
+void ProtocolHandlersHandler::GetHandlersForProtocol(
+ const std::string& protocol,
+ base::DictionaryValue* handlers_value) {
+ ProtocolHandlerRegistry* registry = GetProtocolHandlerRegistry();
+ // The items which are to be written into |handlers_value| are also described
+ // in chrome/browser/resources/options/handler_options.js in @typedef
+ // for Handlers. Please update them whenever you add or remove any keys here.
+ 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 = base::MakeUnique<base::ListValue>();
+ GetHandlersAsListValue(registry->GetHandlersFor(protocol),
+ handlers_list.get());
+ handlers_value->Set("handlers", std::move(handlers_list));
+}
+
+void ProtocolHandlersHandler::GetIgnoredHandlers(base::ListValue* handlers) {
+ ProtocolHandlerRegistry* registry = GetProtocolHandlerRegistry();
+ ProtocolHandlerRegistry::ProtocolHandlerList ignored_handlers =
+ registry->GetIgnoredHandlers();
+ return GetHandlersAsListValue(ignored_handlers, handlers);
+}
+
+void ProtocolHandlersHandler::UpdateHandlerList() {
+ ProtocolHandlerRegistry* registry = GetProtocolHandlerRegistry();
+ std::vector<std::string> protocols;
+ registry->GetRegisteredProtocols(&protocols);
+
+ base::ListValue handlers;
+ for (std::vector<std::string>::iterator protocol = protocols.begin();
+ protocol != protocols.end(); protocol++) {
+ std::unique_ptr<base::DictionaryValue> handler_value(
+ new base::DictionaryValue());
+ GetHandlersForProtocol(*protocol, handler_value.get());
+ handlers.Append(std::move(handler_value));
+ }
+
+ std::unique_ptr<base::ListValue> ignored_handlers(new base::ListValue());
+ GetIgnoredHandlers(ignored_handlers.get());
+ FireWebUIListener("setProtocolHandlers", handlers);
+ FireWebUIListener("setIgnoredProtocolHandlers", *ignored_handlers);
+}
+
+void ProtocolHandlersHandler::HandleObserveProtocolHandlers(
+ const base::ListValue* args) {
+ AllowJavascript();
+ SendHandlersEnabledValue();
+ UpdateHandlerList();
+}
+
+void ProtocolHandlersHandler::HandleObserveProtocolHandlersEnabledState(
+ const base::ListValue* args) {
+ AllowJavascript();
+ SendHandlersEnabledValue();
+}
+
+void ProtocolHandlersHandler::SendHandlersEnabledValue() {
+ FireWebUIListener("setHandlersEnabled",
+ base::Value(GetProtocolHandlerRegistry()->enabled()));
+}
+
+void ProtocolHandlersHandler::HandleRemoveHandler(const base::ListValue* args) {
+ const base::ListValue* list;
+ if (!args->GetList(0, &list)) {
+ NOTREACHED();
+ return;
+ }
+
+ ProtocolHandler handler(ParseHandlerFromArgs(list));
+ GetProtocolHandlerRegistry()->RemoveHandler(handler);
+
+ // No need to call UpdateHandlerList() - we should receive a notification
+ // that the ProtocolHandlerRegistry has changed and we will update the view
+ // 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;
+ CHECK(args->GetBoolean(0, &enabled));
+ if (enabled)
+ GetProtocolHandlerRegistry()->Enable();
+ else
+ 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));
+ CHECK(!handler.IsEmpty());
+ GetProtocolHandlerRegistry()->OnAcceptRegisterProtocolHandler(handler);
+}
+
+ProtocolHandler ProtocolHandlersHandler::ParseHandlerFromArgs(
+ const base::ListValue* args) const {
+ base::string16 protocol;
+ base::string16 url;
+ bool ok = args->GetString(0, &protocol) && args->GetString(1, &url);
+ if (!ok)
+ return ProtocolHandler::EmptyProtocolHandler();
+ return ProtocolHandler::CreateProtocolHandler(base::UTF16ToUTF8(protocol),
+ GURL(base::UTF16ToUTF8(url)));
+}
+
+void ProtocolHandlersHandler::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK_EQ(chrome::NOTIFICATION_PROTOCOL_HANDLER_REGISTRY_CHANGED, type);
+ SendHandlersEnabledValue();
+ UpdateHandlerList();
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/protocol_handlers_handler.h b/chromium/chrome/browser/ui/webui/settings/protocol_handlers_handler.h
new file mode 100644
index 00000000000..269f27f6061
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/protocol_handlers_handler.h
@@ -0,0 +1,102 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_PROTOCOL_HANDLERS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_PROTOCOL_HANDLERS_HANDLER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/custom_handlers/protocol_handler_registry.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "chrome/common/custom_handlers/protocol_handler.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// ProtocolHandlersHandler
+
+// Listen for changes to protocol handlers (i.e. registerProtocolHandler()).
+// This get triggered whenever a user allows a specific website or application
+// to handle clicks on a link with a specified protocol (i.e. mailto: -> Gmail).
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace settings {
+
+class ProtocolHandlersHandler : public SettingsPageUIHandler,
+ public content::NotificationObserver {
+ public:
+ ProtocolHandlersHandler();
+ ~ProtocolHandlersHandler() override;
+
+ // SettingsPageUIHandler:
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+ void RegisterMessages() override;
+
+ // content::NotificationObserver:
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ private:
+ // Called to fetch the state of the protocol handlers. If the full list of
+ // handlers is not needed, consider HandleObserveProtocolHandlersEnabledState
+ // instead.
+ void HandleObserveProtocolHandlers(const base::ListValue* args);
+
+ // Called to begin updates to the handlers enabled status. This is a subset
+ // (lighter alternative) of HandleObserveProtocolHandlers. There's no need to
+ // call this function if HandleObserveProtocolHandlers is called.
+ void HandleObserveProtocolHandlersEnabledState(const base::ListValue* args);
+
+ // Notifies the JS side whether the handlers are enabled or not.
+ void SendHandlersEnabledValue();
+
+ // Called when the user toggles whether custom handlers are enabled.
+ void HandleSetHandlersEnabled(const base::ListValue* args);
+
+ // 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].
+ ProtocolHandler ParseHandlerFromArgs(const base::ListValue* args) const;
+
+ // Returns a JSON object describing the set of protocol handlers for the
+ // given protocol.
+ void GetHandlersForProtocol(const std::string& protocol,
+ base::DictionaryValue* value);
+
+ // Returns a JSON list of the ignored protocol handlers.
+ void GetIgnoredHandlers(base::ListValue* handlers);
+
+ // Called when the JS PasswordManager object is initialized.
+ void UpdateHandlerList();
+
+ // Remove a handler.
+ // |args| is a list of [protocol, url, title].
+ 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_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProtocolHandlersHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_PROTOCOL_HANDLERS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/reset_settings_handler.cc b/chromium/chrome/browser/ui/webui/settings/reset_settings_handler.cc
new file mode 100644
index 00000000000..7f30825234b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/reset_settings_handler.cc
@@ -0,0 +1,310 @@
+// 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/settings/reset_settings_handler.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/string16.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/google/google_brand.h"
+#include "chrome/browser/prefs/chrome_pref_service_factory.h"
+#include "chrome/browser/profile_resetter/brandcode_config_fetcher.h"
+#include "chrome/browser/profile_resetter/brandcoded_default_settings.h"
+#include "chrome/browser/profile_resetter/profile_resetter.h"
+#include "chrome/browser/profile_resetter/resettable_settings_snapshot.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/browser/chromeos/reset/metrics.h"
+#include "chrome/common/pref_names.h"
+#include "components/user_manager/user_manager.h"
+#endif // defined(OS_CHROMEOS)
+
+#if defined(OS_WIN)
+#include "chrome/browser/profile_resetter/triggered_profile_resetter.h"
+#include "chrome/browser/profile_resetter/triggered_profile_resetter_factory.h"
+#endif // defined(OS_WIN)
+
+namespace settings {
+
+namespace {
+
+reset_report::ChromeResetReport::ResetRequestOrigin
+ResetRequestOriginFromString(const std::string& request_origin) {
+ static const char kOriginUserClick[] = "userclick";
+ static const char kOriginTriggeredReset[] = "triggeredreset";
+
+ if (request_origin == ResetSettingsHandler::kCctResetSettingsHash)
+ return reset_report::ChromeResetReport::RESET_REQUEST_ORIGIN_CCT;
+ if (request_origin == kOriginUserClick)
+ return reset_report::ChromeResetReport::RESET_REQUEST_ORIGIN_USER_CLICK;
+ if (request_origin == kOriginTriggeredReset) {
+ return reset_report::ChromeResetReport::
+ RESET_REQUEST_ORIGIN_TRIGGERED_RESET;
+ }
+ if (!request_origin.empty())
+ NOTREACHED();
+
+ return reset_report::ChromeResetReport::RESET_REQUEST_ORIGIN_UNKNOWN;
+}
+
+} // namespace
+
+const char ResetSettingsHandler::kCctResetSettingsHash[] = "cct";
+
+ResetSettingsHandler::ResetSettingsHandler(Profile* profile)
+ : profile_(profile), weak_ptr_factory_(this) {
+ google_brand::GetBrand(&brandcode_);
+}
+
+ResetSettingsHandler::~ResetSettingsHandler() {}
+
+ResetSettingsHandler* ResetSettingsHandler::Create(
+ content::WebUIDataSource* html_source, Profile* profile) {
+#if defined(OS_CHROMEOS)
+ bool allow_powerwash = false;
+ policy::BrowserPolicyConnectorChromeOS* connector =
+ g_browser_process->platform_part()->browser_policy_connector_chromeos();
+ allow_powerwash = !connector->IsEnterpriseManaged() &&
+ !user_manager::UserManager::Get()->IsLoggedInAsGuest() &&
+ !user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser();
+ html_source->AddBoolean("allowPowerwash", allow_powerwash);
+#endif // defined(OS_CHROMEOS)
+
+ bool show_reset_profile_banner = false;
+ static const int kBannerShowTimeInDays = 5;
+ const base::Time then = chrome_prefs::GetResetTime(profile);
+ if (!then.is_null()) {
+ show_reset_profile_banner =
+ (base::Time::Now() - then).InDays() < kBannerShowTimeInDays;
+ }
+ html_source->AddBoolean("showResetProfileBanner", show_reset_profile_banner);
+
+ return new ResetSettingsHandler(profile);
+}
+
+void ResetSettingsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("performResetProfileSettings",
+ base::Bind(&ResetSettingsHandler::HandleResetProfileSettings,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("onShowResetProfileDialog",
+ base::Bind(&ResetSettingsHandler::OnShowResetProfileDialog,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("getReportedSettings",
+ base::Bind(&ResetSettingsHandler::HandleGetReportedSettings,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("onHideResetProfileDialog",
+ base::Bind(&ResetSettingsHandler::OnHideResetProfileDialog,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("onHideResetProfileBanner",
+ base::Bind(&ResetSettingsHandler::OnHideResetProfileBanner,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getTriggeredResetToolName",
+ base::Bind(&ResetSettingsHandler::HandleGetTriggeredResetToolName,
+ base::Unretained(this)));
+#if defined(OS_CHROMEOS)
+ web_ui()->RegisterMessageCallback(
+ "onPowerwashDialogShow",
+ base::Bind(&ResetSettingsHandler::OnShowPowerwashDialog,
+ base::Unretained(this)));
+#endif // defined(OS_CHROMEOS)
+}
+
+void ResetSettingsHandler::HandleResetProfileSettings(
+ const base::ListValue* args) {
+ AllowJavascript();
+
+ CHECK_EQ(3U, args->GetSize());
+ std::string callback_id;
+ CHECK(args->GetString(0, &callback_id));
+ bool send_settings = false;
+ CHECK(args->GetBoolean(1, &send_settings));
+ std::string request_origin_string;
+ CHECK(args->GetString(2, &request_origin_string));
+ reset_report::ChromeResetReport::ResetRequestOrigin request_origin =
+ ResetRequestOriginFromString(request_origin_string);
+
+ DCHECK(brandcode_.empty() || config_fetcher_);
+ if (config_fetcher_ && config_fetcher_->IsActive()) {
+ // Reset once the prefs are fetched.
+ config_fetcher_->SetCallback(base::Bind(&ResetSettingsHandler::ResetProfile,
+ base::Unretained(this), callback_id,
+ send_settings, request_origin));
+ } else {
+ ResetProfile(callback_id, send_settings, request_origin);
+ }
+}
+
+void ResetSettingsHandler::OnResetProfileSettingsDone(
+ std::string callback_id,
+ bool send_feedback,
+ reset_report::ChromeResetReport::ResetRequestOrigin request_origin) {
+ ResolveJavascriptCallback(base::Value(callback_id), base::Value());
+ if (send_feedback && setting_snapshot_) {
+ ResettableSettingsSnapshot current_snapshot(profile_);
+ int difference = setting_snapshot_->FindDifferentFields(current_snapshot);
+ if (difference) {
+ setting_snapshot_->Subtract(current_snapshot);
+ std::unique_ptr<reset_report::ChromeResetReport> report_proto =
+ SerializeSettingsReportToProto(*setting_snapshot_, difference);
+ if (report_proto) {
+ report_proto->set_reset_request_origin(request_origin);
+ SendSettingsFeedbackProto(*report_proto, profile_);
+ }
+ }
+ }
+ setting_snapshot_.reset();
+}
+
+void ResetSettingsHandler::HandleGetReportedSettings(
+ const base::ListValue* args) {
+ AllowJavascript();
+
+ CHECK_EQ(1U, args->GetSize());
+ std::string callback_id;
+ CHECK(args->GetString(0, &callback_id));
+
+ setting_snapshot_->RequestShortcuts(base::Bind(
+ &ResetSettingsHandler::OnGetReportedSettingsDone,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback_id));
+}
+
+void ResetSettingsHandler::OnGetReportedSettingsDone(std::string callback_id) {
+ std::unique_ptr<base::ListValue> list =
+ GetReadableFeedbackForSnapshot(profile_, *setting_snapshot_);
+ ResolveJavascriptCallback(base::Value(callback_id), *list);
+}
+
+void ResetSettingsHandler::OnShowResetProfileDialog(
+ const base::ListValue* args) {
+ if (!GetResetter()->IsActive()) {
+ setting_snapshot_.reset(new ResettableSettingsSnapshot(profile_));
+ }
+
+ if (brandcode_.empty())
+ return;
+ config_fetcher_.reset(new BrandcodeConfigFetcher(
+ base::Bind(&ResetSettingsHandler::OnSettingsFetched,
+ base::Unretained(this)),
+ GURL("https://tools.google.com/service/update2"),
+ brandcode_));
+}
+
+void ResetSettingsHandler::OnHideResetProfileDialog(
+ const base::ListValue* args) {
+ if (!GetResetter()->IsActive())
+ setting_snapshot_.reset();
+}
+
+void ResetSettingsHandler::OnHideResetProfileBanner(
+ const base::ListValue* args) {
+ chrome_prefs::ClearResetTime(profile_);
+}
+
+void ResetSettingsHandler::OnSettingsFetched() {
+ DCHECK(config_fetcher_);
+ DCHECK(!config_fetcher_->IsActive());
+ // The master prefs is fetched. We are waiting for user pressing 'Reset'.
+}
+
+void ResetSettingsHandler::ResetProfile(
+ const std::string& callback_id,
+ bool send_settings,
+ reset_report::ChromeResetReport::ResetRequestOrigin request_origin) {
+ DCHECK(!GetResetter()->IsActive());
+
+ std::unique_ptr<BrandcodedDefaultSettings> default_settings;
+ if (config_fetcher_) {
+ DCHECK(!config_fetcher_->IsActive());
+ default_settings = config_fetcher_->GetSettings();
+ config_fetcher_.reset();
+ } else {
+ DCHECK(brandcode_.empty());
+ }
+
+ // If failed to fetch BrandcodedDefaultSettings or this is an organic
+ // installation, use default settings.
+ if (!default_settings)
+ default_settings.reset(new BrandcodedDefaultSettings);
+
+ GetResetter()->Reset(
+ ProfileResetter::ALL, std::move(default_settings),
+ base::Bind(&ResetSettingsHandler::OnResetProfileSettingsDone,
+ weak_ptr_factory_.GetWeakPtr(), callback_id, send_settings,
+ request_origin));
+ base::RecordAction(base::UserMetricsAction("ResetProfile"));
+ UMA_HISTOGRAM_BOOLEAN("ProfileReset.SendFeedback", send_settings);
+ UMA_HISTOGRAM_ENUMERATION(
+ "ProfileReset.ResetRequestOrigin", request_origin,
+ reset_report::ChromeResetReport::ResetRequestOrigin_MAX + 1);
+}
+
+ProfileResetter* ResetSettingsHandler::GetResetter() {
+ if (!resetter_)
+ resetter_.reset(new ProfileResetter(profile_));
+ return resetter_.get();
+}
+
+void ResetSettingsHandler::HandleGetTriggeredResetToolName(
+ const base::ListValue* args) {
+ AllowJavascript();
+
+ CHECK_EQ(1U, args->GetSize());
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+
+ // Set up the localized strings for the triggered profile reset dialog.
+ // Custom reset tool names are supported on Windows only.
+ base::string16 reset_tool_name;
+#if defined(OS_WIN)
+ Profile* profile = Profile::FromWebUI(web_ui());
+ TriggeredProfileResetter* triggered_profile_resetter =
+ TriggeredProfileResetterFactory::GetForBrowserContext(profile);
+ // TriggeredProfileResetter instance will be nullptr for incognito profiles.
+ if (triggered_profile_resetter) {
+ reset_tool_name = triggered_profile_resetter->GetResetToolName();
+
+ // Now that a reset UI has been shown, don't trigger again for this profile.
+ triggered_profile_resetter->ClearResetTrigger();
+ }
+#endif // defined(OS_WIN)
+
+ if (reset_tool_name.empty()) {
+ reset_tool_name = l10n_util::GetStringUTF16(
+ IDS_TRIGGERED_RESET_PROFILE_SETTINGS_DEFAULT_TOOL_NAME);
+ }
+
+ base::Value string_value(reset_tool_name);
+ ResolveJavascriptCallback(*callback_id, string_value);
+}
+
+#if defined(OS_CHROMEOS)
+void ResetSettingsHandler::OnShowPowerwashDialog(
+ const base::ListValue* args) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "Reset.ChromeOS.PowerwashDialogShown",
+ chromeos::reset::DIALOG_FROM_OPTIONS,
+ chromeos::reset::DIALOG_VIEW_TYPE_SIZE);
+}
+#endif // defined(OS_CHROMEOS)
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/reset_settings_handler.h b/chromium/chrome/browser/ui/webui/settings/reset_settings_handler.h
new file mode 100644
index 00000000000..bb4d9f2207c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/reset_settings_handler.h
@@ -0,0 +1,120 @@
+// 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_SETTINGS_RESET_SETTINGS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_RESET_SETTINGS_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "build/build_config.h"
+#include "chrome/browser/profile_resetter/profile_reset_report.pb.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+namespace base {
+class ListValue;
+} // namespace base
+
+namespace content {
+class WebUIDataSource;
+}
+
+class BrandcodeConfigFetcher;
+class Profile;
+class ProfileResetter;
+class ResettableSettingsSnapshot;
+
+namespace settings {
+
+// Handler for
+// 1) 'Reset Profile Settings' dialog
+// 2) 'Powerwash' dialog (ChromeOS only)
+class ResetSettingsHandler : public SettingsPageUIHandler {
+ public:
+ // Hash used by the Chrome Cleanup Tool when launching chrome with the reset
+ // profile settings URL.
+ static const char kCctResetSettingsHash[];
+
+ ~ResetSettingsHandler() override;
+
+ static ResetSettingsHandler* Create(
+ content::WebUIDataSource* html_source, Profile* profile);
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override {}
+ void OnJavascriptDisallowed() override {}
+
+ protected:
+ explicit ResetSettingsHandler(Profile* profile);
+
+ // Overriden in tests to substitute with a test version of ProfileResetter.
+ virtual ProfileResetter* GetResetter();
+
+ // Javascript callback to start clearing data.
+ void HandleResetProfileSettings(const base::ListValue* args);
+
+ private:
+ // Retrieves the settings that will be reported, called from Javascript.
+ void HandleGetReportedSettings(const base::ListValue* args);
+
+ // Called once the settings that will be reported have been retrieved.
+ void OnGetReportedSettingsDone(std::string callback_id);
+
+ // Called when the reset profile dialog is shown.
+ void OnShowResetProfileDialog(const base::ListValue* args);
+
+ // Called when the reset profile dialog is hidden.
+ void OnHideResetProfileDialog(const base::ListValue* args);
+
+ // Called when the reset profile banner is shown.
+ void OnHideResetProfileBanner(const base::ListValue* args);
+
+ // Retrieve the triggered reset tool name, called from Javascript.
+ void HandleGetTriggeredResetToolName(const base::ListValue* args);
+
+ // Called when BrandcodeConfigFetcher completed fetching settings.
+ void OnSettingsFetched();
+
+ // Resets profile settings to default values. |send_settings| is true if user
+ // gave their consent to upload broken settings to Google for analysis.
+ void ResetProfile(
+ const std::string& callback_id,
+ bool send_settings,
+ reset_report::ChromeResetReport::ResetRequestOrigin request_origin);
+
+ // Closes the dialog once all requested settings has been reset.
+ void OnResetProfileSettingsDone(
+ std::string callback_id,
+ bool send_feedback,
+ reset_report::ChromeResetReport::ResetRequestOrigin request_origin);
+
+#if defined(OS_CHROMEOS)
+ // Will be called when powerwash dialog is shown.
+ void OnShowPowerwashDialog(const base::ListValue* args);
+#endif // defined(OS_CHROMEOS)
+
+ Profile* const profile_;
+
+ std::unique_ptr<ProfileResetter> resetter_;
+
+ std::unique_ptr<BrandcodeConfigFetcher> config_fetcher_;
+
+ // Snapshot of settings before profile was reseted.
+ std::unique_ptr<ResettableSettingsSnapshot> setting_snapshot_;
+
+ // Contains Chrome brand code; empty for organic Chrome.
+ std::string brandcode_;
+
+ base::WeakPtrFactory<ResetSettingsHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResetSettingsHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_RESET_SETTINGS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/reset_settings_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/reset_settings_handler_unittest.cc
new file mode 100644
index 00000000000..7c8f38e8be7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/reset_settings_handler_unittest.cc
@@ -0,0 +1,107 @@
+// 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.
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "base/values.h"
+#include "chrome/browser/google/google_brand.h"
+#include "chrome/browser/profile_resetter/profile_resetter.h"
+#include "chrome/browser/ui/webui/settings/reset_settings_handler.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_web_ui.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using settings::ResetSettingsHandler;
+
+namespace {
+
+// Mock version of ProfileResetter to be used in tests.
+class MockProfileResetter : public ProfileResetter {
+ public:
+ explicit MockProfileResetter(TestingProfile* profile)
+ : ProfileResetter(profile) {
+ }
+
+ bool IsActive() const override {
+ return false;
+ }
+
+ void Reset(ResettableFlags resettable_flags,
+ std::unique_ptr<BrandcodedDefaultSettings> master_settings,
+ const base::Closure& callback) override {
+ resets_++;
+ callback.Run();
+ }
+
+ size_t resets() const {
+ return resets_;
+ }
+
+ private:
+ size_t resets_ = 0;
+};
+
+class TestingResetSettingsHandler : public ResetSettingsHandler {
+ public:
+ TestingResetSettingsHandler(
+ TestingProfile* profile, content::WebUI* web_ui)
+ : ResetSettingsHandler(profile),
+ resetter_(profile) {
+ set_web_ui(web_ui);
+ }
+
+ size_t resets() const { return resetter_.resets(); }
+
+ using settings::ResetSettingsHandler::HandleResetProfileSettings;
+
+ protected:
+ ProfileResetter* GetResetter() override {
+ return &resetter_;
+ }
+
+private:
+ MockProfileResetter resetter_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestingResetSettingsHandler);
+};
+
+class ResetSettingsHandlerTest : public testing::Test {
+ public:
+ ResetSettingsHandlerTest() {
+ google_brand::BrandForTesting brand_for_testing("");
+ handler_.reset(new TestingResetSettingsHandler(&profile_, &web_ui_));
+ }
+
+ TestingResetSettingsHandler* handler() { return handler_.get(); }
+ content::TestWebUI* web_ui() { return &web_ui_; }
+
+ private:
+ // The order here matters.
+ content::TestBrowserThreadBundle thread_bundle_;
+ TestingProfile profile_;
+ content::TestWebUI web_ui_;
+ std::unique_ptr<TestingResetSettingsHandler> handler_;
+};
+
+TEST_F(ResetSettingsHandlerTest, HandleResetProfileSettings) {
+ base::ListValue list;
+ std::string expected_callback_id("dummyCallbackId");
+ list.AppendString(expected_callback_id);
+ list.AppendBoolean(false);
+ list.AppendString("");
+ handler()->HandleResetProfileSettings(&list);
+ // Check that the delegate ProfileResetter was called.
+ EXPECT_EQ(1u, handler()->resets());
+ // Check that Javascript side is notified after resetting is done.
+ EXPECT_EQ("cr.webUIResponse",
+ web_ui()->call_data()[0]->function_name());
+ std::string callback_id;
+ EXPECT_TRUE(web_ui()->call_data()[0]->arg1()->GetAsString(&callback_id));
+ EXPECT_EQ(expected_callback_id, callback_id);
+}
+
+} // namespace
diff --git a/chromium/chrome/browser/ui/webui/settings/safe_browsing_handler.cc b/chromium/chrome/browser/ui/webui/settings/safe_browsing_handler.cc
new file mode 100644
index 00000000000..e42d983ce75
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/safe_browsing_handler.cc
@@ -0,0 +1,70 @@
+// 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 "chrome/browser/ui/webui/settings/safe_browsing_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/values.h"
+#include "components/prefs/pref_service.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "content/public/browser/web_ui.h"
+
+namespace settings {
+
+SafeBrowsingHandler::SafeBrowsingHandler(PrefService* prefs) : prefs_(prefs) {}
+SafeBrowsingHandler::~SafeBrowsingHandler() {}
+
+void SafeBrowsingHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "getSafeBrowsingExtendedReporting",
+ base::Bind(&SafeBrowsingHandler::HandleGetSafeBrowsingExtendedReporting,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setSafeBrowsingExtendedReportingEnabled",
+ base::Bind(
+ &SafeBrowsingHandler::HandleSetSafeBrowsingExtendedReportingEnabled,
+ base::Unretained(this)));
+}
+
+void SafeBrowsingHandler::OnJavascriptAllowed() {
+ profile_pref_registrar_.Init(prefs_);
+ profile_pref_registrar_.Add(
+ prefs::kSafeBrowsingExtendedReportingEnabled,
+ base::Bind(&SafeBrowsingHandler::OnPrefChanged, base::Unretained(this)));
+ profile_pref_registrar_.Add(
+ prefs::kSafeBrowsingScoutReportingEnabled,
+ base::Bind(&SafeBrowsingHandler::OnPrefChanged, base::Unretained(this)));
+}
+
+void SafeBrowsingHandler::OnJavascriptDisallowed() {
+ profile_pref_registrar_.RemoveAll();
+}
+
+void SafeBrowsingHandler::HandleGetSafeBrowsingExtendedReporting(
+ const base::ListValue* args) {
+ 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);
+}
+
+void SafeBrowsingHandler::HandleSetSafeBrowsingExtendedReportingEnabled(
+ const base::ListValue* args) {
+ bool enabled;
+ CHECK(args->GetBoolean(0, &enabled));
+ safe_browsing::SetExtendedReportingPrefAndMetric(
+ prefs_, enabled, safe_browsing::SBER_OPTIN_SITE_CHROME_SETTINGS);
+}
+
+void SafeBrowsingHandler::OnPrefChanged(const std::string& pref_name) {
+ DCHECK(pref_name == prefs::kSafeBrowsingExtendedReportingEnabled ||
+ pref_name == prefs::kSafeBrowsingScoutReportingEnabled);
+
+ base::Value is_enabled(safe_browsing::IsExtendedReportingEnabled(*prefs_));
+ FireWebUIListener("safe-browsing-extended-reporting-change", is_enabled);
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/safe_browsing_handler.h b/chromium/chrome/browser/ui/webui/settings/safe_browsing_handler.h
new file mode 100644
index 00000000000..45d3bd21df2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/safe_browsing_handler.h
@@ -0,0 +1,52 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_SAFE_BROWSING_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_SAFE_BROWSING_HANDLER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "components/prefs/pref_change_registrar.h"
+
+namespace settings {
+
+class SafeBrowsingHandler : public SettingsPageUIHandler {
+ public:
+ explicit SafeBrowsingHandler(PrefService* prefs);
+ ~SafeBrowsingHandler() override;
+
+ // SettingsPageUIHandler:
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ private:
+ // Handler for "getSafeBrowsingExtendedReporting" message. Passed a single
+ // callback ID argument.
+ void HandleGetSafeBrowsingExtendedReporting(const base::ListValue* args);
+
+ // Handler for "setSafeBrowsingExtendedReportingEnabled" message. Passed a
+ // single |enabled| boolean argument.
+ void HandleSetSafeBrowsingExtendedReportingEnabled(
+ const base::ListValue* args);
+
+ // Called when the local state pref controlling Safe Browsing extended
+ // reporting changes.
+ void OnPrefChanged(const std::string& pref_name);
+
+ // Used to track pref changes that affect whether Safe Browsing extended
+ // reporting is enabled.
+ PrefChangeRegistrar profile_pref_registrar_;
+
+ // Weak pointer.
+ PrefService* prefs_;
+
+ DISALLOW_COPY_AND_ASSIGN(SafeBrowsingHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_SAFE_BROWSING_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/search_engines_handler.cc b/chromium/chrome/browser/ui/webui/settings/search_engines_handler.cc
new file mode 100644
index 00000000000..31be170b319
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/search_engines_handler.cc
@@ -0,0 +1,560 @@
+// 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 "chrome/browser/ui/webui/settings/search_engines_handler.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search/hotword_audio_history_handler.h"
+#include "chrome/browser/search/hotword_service.h"
+#include "chrome/browser/search/hotword_service_factory.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/ui/search_engines/template_url_table_model.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
+#include "components/search_engines/template_url.h"
+#include "components/search_engines/template_url_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/management_policy.h"
+#include "extensions/common/extension.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+// The following strings need to match with the IDs of the paper-input elements
+// at settings/search_engines_page/add_search_engine_dialog.html.
+const char kSearchEngineField[] = "searchEngine";
+const char kKeywordField[] = "keyword";
+const char kQueryUrlField[] = "queryUrl";
+
+// Fields for hotwordUpdateInfo result.
+const char kHotwordSatusAllowed[] = "allowed";
+const char kHotwordSatusEnabled[] = "enabled";
+const char kHotwordStatusAlwaysOn[] = "alwaysOn";
+const char kHotwordSatusErrorMessage[] = "errorMessage";
+const char kHotwordSatusUserUserName[] = "userName";
+const char kHotwordSatusHistoryEnabled[] = "historyEnabled";
+
+// Dummy number used for indicating that a new search engine is added.
+const int kNewSearchEngineIndex = -1;
+
+bool IsGoogleDefaultSearch(Profile* profile) {
+ TemplateURLService* template_url_service =
+ TemplateURLServiceFactory::GetForProfile(profile);
+ if (!template_url_service)
+ return false;
+ const TemplateURL* url_template =
+ template_url_service->GetDefaultSearchProvider();
+ return url_template &&
+ url_template->url_ref().HasGoogleBaseURLs(
+ template_url_service->search_terms_data());
+}
+
+bool GetHotwordAlwaysOn(Profile* profile) {
+ SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile);
+ return signin && signin->IsAuthenticated() &&
+ HotwordServiceFactory::IsAlwaysOnAvailable();
+}
+
+bool IsGoogleNowAvailable(Profile* profile) {
+ std::string group = base::FieldTrialList::FindFullName("GoogleNowExtension");
+ bool has_field_trial = !group.empty() && group != "Disabled";
+ return has_field_trial && IsGoogleDefaultSearch(profile);
+}
+
+} // namespace
+
+namespace settings {
+
+SearchEnginesHandler::SearchEnginesHandler(Profile* profile)
+ : profile_(profile), list_controller_(profile), weak_ptr_factory_(this) {
+ pref_change_registrar_.Init(profile_->GetPrefs());
+}
+
+SearchEnginesHandler::~SearchEnginesHandler() {
+ // TODO(tommycli): Refactor KeywordEditorController to be compatible with
+ // ScopedObserver so this is no longer necessary.
+ list_controller_.table_model()->SetObserver(nullptr);
+}
+
+void SearchEnginesHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "getSearchEnginesList",
+ base::Bind(&SearchEnginesHandler::HandleGetSearchEnginesList,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setDefaultSearchEngine",
+ base::Bind(&SearchEnginesHandler::HandleSetDefaultSearchEngine,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "removeSearchEngine",
+ base::Bind(&SearchEnginesHandler::HandleRemoveSearchEngine,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "validateSearchEngineInput",
+ base::Bind(&SearchEnginesHandler::HandleValidateSearchEngineInput,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "searchEngineEditStarted",
+ base::Bind(&SearchEnginesHandler::HandleSearchEngineEditStarted,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "searchEngineEditCancelled",
+ base::Bind(&SearchEnginesHandler::HandleSearchEngineEditCancelled,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "searchEngineEditCompleted",
+ base::Bind(&SearchEnginesHandler::HandleSearchEngineEditCompleted,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getHotwordInfo", base::Bind(&SearchEnginesHandler::HandleGetHotwordInfo,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setHotwordSearchEnabled",
+ base::Bind(&SearchEnginesHandler::HandleSetHotwordSearchEnabled,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getGoogleNowAvailability",
+ base::Bind(&SearchEnginesHandler::HandleGetGoogleNowAvailability,
+ base::Unretained(this)));
+}
+
+void SearchEnginesHandler::OnJavascriptAllowed() {
+ list_controller_.table_model()->SetObserver(this);
+ pref_change_registrar_.Add(prefs::kHotwordSearchEnabled,
+ base::Bind(&SearchEnginesHandler::SendHotwordInfo,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(prefs::kHotwordAlwaysOnSearchEnabled,
+ base::Bind(&SearchEnginesHandler::SendHotwordInfo,
+ base::Unretained(this)));
+}
+
+void SearchEnginesHandler::OnJavascriptDisallowed() {
+ list_controller_.table_model()->SetObserver(nullptr);
+ pref_change_registrar_.RemoveAll();
+}
+
+std::unique_ptr<base::DictionaryValue>
+SearchEnginesHandler::GetSearchEnginesList() {
+ // Find the default engine.
+ const TemplateURL* default_engine =
+ list_controller_.GetDefaultSearchProvider();
+ int default_index =
+ list_controller_.table_model()->IndexOfTemplateURL(default_engine);
+
+ // Build the first list (default search engines).
+ std::unique_ptr<base::ListValue> defaults =
+ base::MakeUnique<base::ListValue>();
+ int last_default_engine_index =
+ list_controller_.table_model()->last_search_engine_index();
+ for (int i = 0; i < last_default_engine_index; ++i) {
+ // Third argument is false, as the engine is not from an extension.
+ defaults->Append(CreateDictionaryForEngine(i, i == default_index));
+ }
+
+ // Build the second list (other search engines).
+ std::unique_ptr<base::ListValue> others = base::MakeUnique<base::ListValue>();
+ int last_other_engine_index =
+ list_controller_.table_model()->last_other_engine_index();
+ for (int i = std::max(last_default_engine_index, 0);
+ i < last_other_engine_index; ++i) {
+ others->Append(CreateDictionaryForEngine(i, i == default_index));
+ }
+
+ // Build the third list (omnibox extensions).
+ std::unique_ptr<base::ListValue> extensions =
+ base::MakeUnique<base::ListValue>();
+ int engine_count = list_controller_.table_model()->RowCount();
+ for (int i = std::max(last_other_engine_index, 0); i < engine_count; ++i) {
+ extensions->Append(CreateDictionaryForEngine(i, i == default_index));
+ }
+
+ std::unique_ptr<base::DictionaryValue> search_engines_info(
+ new base::DictionaryValue);
+ search_engines_info->Set("defaults", base::WrapUnique(defaults.release()));
+ search_engines_info->Set("others", base::WrapUnique(others.release()));
+ search_engines_info->Set("extensions",
+ base::WrapUnique(extensions.release()));
+ return search_engines_info;
+}
+
+void SearchEnginesHandler::OnModelChanged() {
+ AllowJavascript();
+ FireWebUIListener("search-engines-changed", *GetSearchEnginesList());
+
+ // Google Now availability may have changed.
+ FireWebUIListener("google-now-availability-changed",
+ base::Value(IsGoogleNowAvailable(profile_)));
+}
+
+void SearchEnginesHandler::OnItemsChanged(int start, int length) {
+ OnModelChanged();
+}
+
+void SearchEnginesHandler::OnItemsAdded(int start, int length) {
+ OnModelChanged();
+}
+
+void SearchEnginesHandler::OnItemsRemoved(int start, int length) {
+ OnModelChanged();
+}
+
+std::unique_ptr<base::DictionaryValue>
+SearchEnginesHandler::CreateDictionaryForEngine(int index, bool is_default) {
+ TemplateURLTableModel* table_model = list_controller_.table_model();
+ const TemplateURL* template_url = list_controller_.GetTemplateURL(index);
+
+ // The items which are to be written into |dict| are also described in
+ // chrome/browser/resources/settings/search_engines_page/
+ // in @typedef for SearchEngine. Please update it whenever you add or remove
+ // any keys here.
+ std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+ dict->SetString("name", template_url->short_name());
+ dict->SetString("displayName",
+ table_model->GetText(
+ index, IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_COLUMN));
+ dict->SetString(
+ "keyword",
+ table_model->GetText(index, IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN));
+ Profile* profile = Profile::FromWebUI(web_ui());
+ dict->SetString("url", template_url->url_ref().DisplayURL(
+ UIThreadSearchTermsData(profile)));
+ dict->SetBoolean("urlLocked", template_url->prepopulate_id() > 0);
+ GURL icon_url = template_url->favicon_url();
+ if (icon_url.is_valid())
+ dict->SetString("iconURL", icon_url.spec());
+ dict->SetInteger("modelIndex", index);
+
+ dict->SetBoolean("canBeRemoved", list_controller_.CanRemove(template_url));
+ dict->SetBoolean("canBeDefault",
+ list_controller_.CanMakeDefault(template_url));
+ dict->SetBoolean("default", is_default);
+ dict->SetBoolean("canBeEdited", list_controller_.CanEdit(template_url));
+ TemplateURL::Type type = template_url->type();
+ dict->SetBoolean("isOmniboxExtension",
+ type == TemplateURL::OMNIBOX_API_EXTENSION);
+ if (type == TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION ||
+ type == TemplateURL::OMNIBOX_API_EXTENSION) {
+ const extensions::Extension* extension =
+ extensions::ExtensionRegistry::Get(profile)->GetExtensionById(
+ template_url->GetExtensionId(),
+ extensions::ExtensionRegistry::EVERYTHING);
+ if (extension) {
+ std::unique_ptr<base::DictionaryValue> ext_info =
+ extensions::util::GetExtensionInfo(extension);
+ ext_info->SetBoolean("canBeDisabled",
+ !extensions::ExtensionSystem::Get(profile)
+ ->management_policy()
+ ->MustRemainEnabled(extension, nullptr));
+ dict->Set("extension", std::move(ext_info));
+ }
+ }
+ return dict;
+}
+
+void SearchEnginesHandler::HandleGetSearchEnginesList(
+ const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+ AllowJavascript();
+ ResolveJavascriptCallback(*callback_id, *GetSearchEnginesList());
+}
+
+void SearchEnginesHandler::HandleSetDefaultSearchEngine(
+ const base::ListValue* args) {
+ int index;
+ if (!ExtractIntegerValue(args, &index)) {
+ NOTREACHED();
+ return;
+ }
+ if (index < 0 || index >= list_controller_.table_model()->RowCount())
+ return;
+
+ list_controller_.MakeDefaultTemplateURL(index);
+
+ // If the new search engine is not Google, disable hotword search.
+ // TODO(stevenjb): Investigate migrating this logic to
+ // MakeDefaultTemplateURL.
+ if (!IsGoogleDefaultSearch(profile_)) {
+ HotwordService* hotword_service =
+ HotwordServiceFactory::GetForProfile(profile_);
+ if (hotword_service)
+ hotword_service->DisableHotwordPreferences();
+ }
+ // Hotword status may have changed.
+ SendHotwordInfo();
+
+ base::RecordAction(base::UserMetricsAction("Options_SearchEngineSetDefault"));
+}
+
+void SearchEnginesHandler::HandleRemoveSearchEngine(
+ const base::ListValue* args) {
+ int index;
+ if (!ExtractIntegerValue(args, &index)) {
+ NOTREACHED();
+ return;
+ }
+ if (index < 0 || index >= list_controller_.table_model()->RowCount())
+ return;
+
+ if (list_controller_.CanRemove(list_controller_.GetTemplateURL(index))) {
+ list_controller_.RemoveTemplateURL(index);
+ base::RecordAction(base::UserMetricsAction("Options_SearchEngineRemoved"));
+ }
+}
+
+void SearchEnginesHandler::HandleSearchEngineEditStarted(
+ const base::ListValue* args) {
+ int index;
+ if (!ExtractIntegerValue(args, &index)) {
+ NOTREACHED();
+ return;
+ }
+
+ // Allow -1, which means we are adding a new engine.
+ if (index < kNewSearchEngineIndex ||
+ index >= list_controller_.table_model()->RowCount()) {
+ return;
+ }
+
+ edit_controller_.reset(new EditSearchEngineController(
+ index == kNewSearchEngineIndex ? nullptr
+ : list_controller_.GetTemplateURL(index),
+ this, Profile::FromWebUI(web_ui())));
+}
+
+void SearchEnginesHandler::OnEditedKeyword(TemplateURL* template_url,
+ const base::string16& title,
+ const base::string16& keyword,
+ const std::string& url) {
+ DCHECK(!url.empty());
+ if (template_url)
+ list_controller_.ModifyTemplateURL(template_url, title, keyword, url);
+ else
+ list_controller_.AddTemplateURL(title, keyword, url);
+
+ edit_controller_.reset();
+}
+
+void SearchEnginesHandler::HandleValidateSearchEngineInput(
+ const base::ListValue* args) {
+ CHECK_EQ(3U, args->GetSize());
+
+ const base::Value* callback_id;
+ std::string field_name;
+ std::string field_value;
+ CHECK(args->Get(0, &callback_id));
+ CHECK(args->GetString(1, &field_name));
+ CHECK(args->GetString(2, &field_value));
+ ResolveJavascriptCallback(
+ *callback_id, base::Value(CheckFieldValidity(field_name, field_value)));
+}
+
+bool SearchEnginesHandler::CheckFieldValidity(const std::string& field_name,
+ const std::string& field_value) {
+ if (!edit_controller_.get())
+ return false;
+
+ bool is_valid = false;
+ if (field_name.compare(kSearchEngineField) == 0)
+ is_valid = edit_controller_->IsTitleValid(base::UTF8ToUTF16(field_value));
+ else if (field_name.compare(kKeywordField) == 0)
+ is_valid = edit_controller_->IsKeywordValid(base::UTF8ToUTF16(field_value));
+ else if (field_name.compare(kQueryUrlField) == 0)
+ is_valid = edit_controller_->IsURLValid(field_value);
+ else
+ NOTREACHED();
+
+ return is_valid;
+}
+
+void SearchEnginesHandler::HandleSearchEngineEditCancelled(
+ const base::ListValue* args) {
+ if (!edit_controller_.get())
+ return;
+ edit_controller_->CleanUpCancelledAdd();
+ edit_controller_.reset();
+}
+
+void SearchEnginesHandler::HandleSearchEngineEditCompleted(
+ const base::ListValue* args) {
+ if (!edit_controller_.get())
+ return;
+ std::string search_engine;
+ std::string keyword;
+ std::string query_url;
+ CHECK(args->GetString(0, &search_engine));
+ CHECK(args->GetString(1, &keyword));
+ CHECK(args->GetString(2, &query_url));
+
+ // Recheck validity. It's possible to get here with invalid input if e.g. the
+ // user calls the right JS functions directly from the web inspector.
+ if (CheckFieldValidity(kSearchEngineField, search_engine) &&
+ CheckFieldValidity(kKeywordField, keyword) &&
+ CheckFieldValidity(kQueryUrlField, query_url)) {
+ edit_controller_->AcceptAddOrEdit(base::UTF8ToUTF16(search_engine),
+ base::UTF8ToUTF16(keyword), query_url);
+ }
+}
+
+void SearchEnginesHandler::HandleGetHotwordInfo(const base::ListValue* args) {
+ AllowJavascript();
+
+ std::unique_ptr<base::Value> callback_id;
+ if (args) {
+ CHECK_EQ(1U, args->GetSize());
+ const base::Value* id;
+ CHECK(args->Get(0, &id));
+ callback_id = id->CreateDeepCopy();
+ }
+
+ std::unique_ptr<base::DictionaryValue> status = GetHotwordInfo();
+ bool enabled = false;
+ status->GetBoolean(kHotwordSatusEnabled, &enabled);
+ bool always_on = false;
+ status->GetBoolean(kHotwordStatusAlwaysOn, &always_on);
+ if (!enabled || !always_on) {
+ HotwordInfoComplete(callback_id.get(), *status);
+ return;
+ }
+
+ // OnGetHotwordAudioHistoryEnabled will call HotwordInfoComplete().
+ HotwordServiceFactory::GetForProfile(profile_)
+ ->GetAudioHistoryHandler()
+ ->GetAudioHistoryEnabled(
+ base::Bind(&SearchEnginesHandler::OnGetHotwordAudioHistoryEnabled,
+ weak_ptr_factory_.GetWeakPtr(), base::Passed(&callback_id),
+ base::Passed(&status)));
+}
+
+void SearchEnginesHandler::HandleGetGoogleNowAvailability(
+ const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+ AllowJavascript();
+ ResolveJavascriptCallback(*callback_id,
+ base::Value(IsGoogleNowAvailable(profile_)));
+}
+
+std::unique_ptr<base::DictionaryValue> SearchEnginesHandler::GetHotwordInfo() {
+ auto status = base::MakeUnique<base::DictionaryValue>();
+ if (!IsGoogleDefaultSearch(profile_)) {
+ status->SetBoolean(kHotwordSatusAllowed, false);
+ return status;
+ }
+
+ status->SetBoolean(kHotwordSatusAllowed,
+ HotwordServiceFactory::IsHotwordAllowed(profile_));
+
+ HotwordServiceFactory::IsServiceAvailable(profile_); // Update error value.
+ int hotword_error = HotwordServiceFactory::GetCurrentError(profile_);
+ if (hotword_error) {
+ base::string16 hotword_error_message;
+ if (hotword_error != IDS_HOTWORD_GENERIC_ERROR_MESSAGE) {
+ hotword_error_message = l10n_util::GetStringUTF16(hotword_error);
+ } else {
+ hotword_error_message = l10n_util::GetStringFUTF16(
+ hotword_error, base::ASCIIToUTF16(chrome::kHotwordLearnMoreURL));
+ }
+ status->SetString(kHotwordSatusErrorMessage, hotword_error_message);
+ }
+
+ if (!HotwordServiceFactory::GetForProfile(profile_)) {
+ status->SetBoolean(kHotwordSatusEnabled, false);
+ return status;
+ }
+
+ bool always_on = GetHotwordAlwaysOn(profile_);
+ status->SetBoolean(kHotwordStatusAlwaysOn, always_on);
+
+ std::string pref_name = always_on ? prefs::kHotwordAlwaysOnSearchEnabled
+ : prefs::kHotwordSearchEnabled;
+ bool enabled = profile_->GetPrefs()->GetBoolean(pref_name);
+ status->SetBoolean(kHotwordSatusEnabled, enabled);
+ if (!enabled)
+ return status;
+
+ SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile_);
+ std::string user_display_name =
+ signin ? signin->GetAuthenticatedAccountInfo().email : "";
+ status->SetString(kHotwordSatusUserUserName, user_display_name);
+ return status;
+}
+
+void SearchEnginesHandler::OnGetHotwordAudioHistoryEnabled(
+ std::unique_ptr<base::Value> callback_id,
+ std::unique_ptr<base::DictionaryValue> status,
+ bool success,
+ bool logging_enabled) {
+ if (success)
+ status->SetBoolean(kHotwordSatusHistoryEnabled, logging_enabled);
+ HotwordInfoComplete(callback_id.get(), *status);
+}
+
+void SearchEnginesHandler::HotwordInfoComplete(
+ const base::Value* callback_id,
+ const base::DictionaryValue& status) {
+ if (callback_id)
+ ResolveJavascriptCallback(*callback_id, status);
+ else
+ FireWebUIListener("hotword-info-update", status);
+}
+
+void SearchEnginesHandler::SendHotwordInfo() {
+ HandleGetHotwordInfo(nullptr);
+}
+
+void SearchEnginesHandler::HandleSetHotwordSearchEnabled(
+ const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ bool enabled;
+ CHECK(args->GetBoolean(0, &enabled));
+
+ bool always_on = GetHotwordAlwaysOn(profile_);
+ if (!always_on) {
+ profile_->GetPrefs()->SetBoolean(prefs::kHotwordSearchEnabled, enabled);
+ return;
+ }
+
+ HotwordService* hotword_service =
+ HotwordServiceFactory::GetForProfile(profile_);
+ if (!enabled || !hotword_service) {
+ profile_->GetPrefs()->SetBoolean(prefs::kHotwordAlwaysOnSearchEnabled,
+ false);
+ return;
+ }
+
+ bool was_enabled =
+ profile_->GetPrefs()->GetBoolean(prefs::kHotwordAlwaysOnSearchEnabled);
+ HotwordService::LaunchMode launch_mode;
+ if (was_enabled) {
+ launch_mode = HotwordService::RETRAIN;
+ } else {
+ bool logging_enabled =
+ profile_->GetPrefs()->GetBoolean(prefs::kHotwordAudioLoggingEnabled);
+ launch_mode = logging_enabled ? HotwordService::HOTWORD_ONLY
+ : HotwordService::HOTWORD_AND_AUDIO_HISTORY;
+ }
+ hotword_service->OptIntoHotwording(launch_mode);
+ SendHotwordInfo();
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/search_engines_handler.h b/chromium/chrome/browser/ui/webui/settings/search_engines_handler.h
new file mode 100644
index 00000000000..2d13fbc35e1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/search_engines_handler.h
@@ -0,0 +1,137 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_SEARCH_ENGINES_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_SEARCH_ENGINES_HANDLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/search_engines/edit_search_engine_controller.h"
+#include "chrome/browser/ui/search_engines/keyword_editor_controller.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "ui/base/models/table_model_observer.h"
+
+class Profile;
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+class Value;
+}
+
+namespace extensions {
+class Extension;
+}
+
+namespace settings {
+
+class SearchEnginesHandler : public SettingsPageUIHandler,
+ public ui::TableModelObserver,
+ public EditSearchEngineControllerDelegate {
+ public:
+ explicit SearchEnginesHandler(Profile* profile);
+ ~SearchEnginesHandler() override;
+
+ // ui::TableModelObserver implementation.
+ void OnModelChanged() override;
+ void OnItemsChanged(int start, int length) override;
+ void OnItemsAdded(int start, int length) override;
+ void OnItemsRemoved(int start, int length) override;
+
+ // EditSearchEngineControllerDelegate implementation.
+ void OnEditedKeyword(TemplateURL* template_url,
+ const base::string16& title,
+ const base::string16& keyword,
+ const std::string& url) override;
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ private:
+ // Retrieves all search engines and returns them to WebUI.
+ void HandleGetSearchEnginesList(const base::ListValue* args);
+
+ std::unique_ptr<base::DictionaryValue> GetSearchEnginesList();
+
+ // Removes the search engine at the given index. Called from WebUI.
+ void HandleRemoveSearchEngine(const base::ListValue* args);
+
+ // Sets the search engine at the given index to be default. Called from WebUI.
+ void HandleSetDefaultSearchEngine(const base::ListValue* args);
+
+ // Starts an edit session for the search engine at the given index. If the
+ // index is -1, starts editing a new search engine instead of an existing one.
+ // Called from WebUI.
+ void HandleSearchEngineEditStarted(const base::ListValue* args);
+
+ // Validates the given search engine values, and reports the results back
+ // to WebUI. Called from WebUI.
+ void HandleValidateSearchEngineInput(const base::ListValue* args);
+
+ // Checks whether the given user input field (searchEngine, keyword, queryUrl)
+ // is populated with a valid value.
+ bool CheckFieldValidity(const std::string& field_name,
+ const std::string& field_value);
+
+ // Called when an edit is canceled.
+ // Called from WebUI.
+ void HandleSearchEngineEditCancelled(const base::ListValue* args);
+
+ // Called when an edit is finished and should be saved.
+ // Called from WebUI.
+ void HandleSearchEngineEditCompleted(const base::ListValue* args);
+
+ // Returns a dictionary to pass to WebUI representing the given search engine.
+ std::unique_ptr<base::DictionaryValue> CreateDictionaryForEngine(
+ int index,
+ bool is_default);
+
+ // Returns a dictionary to pass to WebUI representing the extension.
+ base::DictionaryValue* CreateDictionaryForExtension(
+ const extensions::Extension& extension);
+
+ // WebUI call to request a dictionary of hotword related properties.
+ void HandleGetHotwordInfo(const base::ListValue* args);
+
+ // WebUI call to request the availability of Google Now cards.
+ void HandleGetGoogleNowAvailability(const base::ListValue* args);
+
+ // Constructs a SearchPageHotwordInfo dictionary.
+ std::unique_ptr<base::DictionaryValue> GetHotwordInfo();
+
+ // Callback for HotwordService::AudioHistoryHandler::GetAudioHistoryEnabled.
+ void OnGetHotwordAudioHistoryEnabled(
+ std::unique_ptr<base::Value> callback_id,
+ std::unique_ptr<base::DictionaryValue> status,
+ bool success,
+ bool logging_enabled);
+
+ // Calls either ResolveJavascriptCallback or CallJavascriptFunction.
+ void HotwordInfoComplete(const base::Value* callback_id,
+ const base::DictionaryValue& status);
+
+ // Calls WebUI to send hotword search info updates.
+ void SendHotwordInfo();
+
+ // WebUI call to enable hotword search.
+ void HandleSetHotwordSearchEnabled(const base::ListValue* args);
+
+ Profile* const profile_;
+
+ KeywordEditorController list_controller_;
+ std::unique_ptr<EditSearchEngineController> edit_controller_;
+ PrefChangeRegistrar pref_change_registrar_;
+ base::WeakPtrFactory<SearchEnginesHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SearchEnginesHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_SEARCH_ENGINES_HANDLER_H_
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
new file mode 100644
index 00000000000..8ba91de6eac
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc
@@ -0,0 +1,418 @@
+// 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.
+
+#include "chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h"
+
+#include <stddef.h>
+#include <vector>
+
+#include "base/feature_list.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/values.h"
+#include "chrome/browser/browsing_data/browsing_data_counter_factory.h"
+#include "chrome/browser/browsing_data/browsing_data_counter_utils.h"
+#include "chrome/browser/browsing_data/browsing_data_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_important_sites_util.h"
+#include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
+#include "chrome/browser/engagement/important_sites_usage_counter.h"
+#include "chrome/browser/engagement/important_sites_util.h"
+#include "chrome/browser/history/web_history_service_factory.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/chrome_features.h"
+#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/prefs/pref_service.h"
+#include "content/public/browser/browsing_data_filter_builder.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/text/bytes_formatting.h"
+
+using ImportantReason = ImportantSitesUtil::ImportantReason;
+
+namespace {
+
+const int kMaxTimesHistoryNoticeShown = 1;
+
+// TODO(msramek): Get the list of deletion preferences from the JS side.
+const char* kCounterPrefs[] = {
+ browsing_data::prefs::kDeleteBrowsingHistory,
+ browsing_data::prefs::kDeleteCache,
+ browsing_data::prefs::kDeleteDownloadHistory,
+ browsing_data::prefs::kDeleteFormData,
+ browsing_data::prefs::kDeleteHostedAppsData,
+ browsing_data::prefs::kDeleteMediaLicenses,
+ browsing_data::prefs::kDeletePasswords,
+};
+
+const char kRegisterableDomainField[] = "registerableDomain";
+const char kReasonBitField[] = "reasonBitfield";
+const char kExampleOriginField[] = "exampleOrigin";
+const char kIsCheckedField[] = "isChecked";
+const char kStorageSizeField[] = "storageSize";
+const char kHasNotificationsField[] = "hasNotifications";
+
+} // namespace
+
+namespace settings {
+
+// ClearBrowsingDataHandler ----------------------------------------------------
+
+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) {}
+
+ClearBrowsingDataHandler::~ClearBrowsingDataHandler() {
+}
+
+void ClearBrowsingDataHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "clearBrowsingData",
+ base::Bind(&ClearBrowsingDataHandler::HandleClearBrowsingData,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "getImportantSites",
+ base::Bind(&ClearBrowsingDataHandler::HandleGetImportantSites,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "initializeClearBrowsingData",
+ base::Bind(&ClearBrowsingDataHandler::HandleInitialize,
+ base::Unretained(this)));
+}
+
+void ClearBrowsingDataHandler::OnJavascriptAllowed() {
+ if (sync_service_)
+ sync_service_observer_.Add(sync_service_);
+
+ DCHECK(counters_.empty());
+ for (const std::string& pref : kCounterPrefs) {
+ AddCounter(
+ BrowsingDataCounterFactory::GetForProfileAndPref(profile_, pref));
+ }
+}
+
+void ClearBrowsingDataHandler::OnJavascriptDisallowed() {
+ sync_service_observer_.RemoveAll();
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ counters_.clear();
+}
+
+void ClearBrowsingDataHandler::HandleClearBrowsingData(
+ const base::ListValue* args) {
+ PrefService* prefs = profile_->GetPrefs();
+
+ int site_data_mask = ChromeBrowsingDataRemoverDelegate::DATA_TYPE_SITE_DATA;
+ // Don't try to clear LSO data if it's not supported.
+ if (!prefs->GetBoolean(prefs::kClearPluginLSODataEnabled))
+ site_data_mask &= ~ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PLUGIN_DATA;
+
+ int remove_mask = 0;
+ if (prefs->GetBoolean(prefs::kAllowDeletingBrowserHistory)) {
+ if (prefs->GetBoolean(browsing_data::prefs::kDeleteBrowsingHistory))
+ remove_mask |= ChromeBrowsingDataRemoverDelegate::DATA_TYPE_HISTORY;
+ if (prefs->GetBoolean(browsing_data::prefs::kDeleteDownloadHistory))
+ remove_mask |= content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS;
+ }
+
+ if (prefs->GetBoolean(browsing_data::prefs::kDeleteCache))
+ remove_mask |= content::BrowsingDataRemover::DATA_TYPE_CACHE;
+
+ int origin_mask = 0;
+ if (prefs->GetBoolean(browsing_data::prefs::kDeleteCookies)) {
+ remove_mask |= site_data_mask;
+ origin_mask |= content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB;
+ }
+
+ if (prefs->GetBoolean(browsing_data::prefs::kDeletePasswords))
+ remove_mask |= ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PASSWORDS;
+
+ if (prefs->GetBoolean(browsing_data::prefs::kDeleteFormData))
+ remove_mask |= ChromeBrowsingDataRemoverDelegate::DATA_TYPE_FORM_DATA;
+
+ if (prefs->GetBoolean(browsing_data::prefs::kDeleteMediaLicenses))
+ remove_mask |= content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES;
+
+ if (prefs->GetBoolean(browsing_data::prefs::kDeleteHostedAppsData)) {
+ remove_mask |= site_data_mask;
+ origin_mask |= content::BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB;
+ }
+
+ // Record the deletion of cookies and cache.
+ content::BrowsingDataRemover::CookieOrCacheDeletionChoice choice =
+ content::BrowsingDataRemover::NEITHER_COOKIES_NOR_CACHE;
+ if (prefs->GetBoolean(browsing_data::prefs::kDeleteCookies)) {
+ choice = prefs->GetBoolean(browsing_data::prefs::kDeleteCache)
+ ? content::BrowsingDataRemover::BOTH_COOKIES_AND_CACHE
+ : content::BrowsingDataRemover::ONLY_COOKIES;
+ } else if (prefs->GetBoolean(browsing_data::prefs::kDeleteCache)) {
+ choice = content::BrowsingDataRemover::ONLY_CACHE;
+ }
+
+ UMA_HISTOGRAM_ENUMERATION(
+ "History.ClearBrowsingData.UserDeletedCookieOrCacheFromDialog", choice,
+ content::BrowsingDataRemover::MAX_CHOICE_VALUE);
+
+ // Record the circumstances under which passwords are deleted.
+ if (prefs->GetBoolean(browsing_data::prefs::kDeletePasswords)) {
+ static const char* other_types[] = {
+ browsing_data::prefs::kDeleteBrowsingHistory,
+ browsing_data::prefs::kDeleteDownloadHistory,
+ browsing_data::prefs::kDeleteCache,
+ browsing_data::prefs::kDeleteCookies,
+ browsing_data::prefs::kDeleteFormData,
+ browsing_data::prefs::kDeleteHostedAppsData,
+ browsing_data::prefs::kDeleteMediaLicenses,
+ };
+ static size_t num_other_types = arraysize(other_types);
+ int checked_other_types = std::count_if(
+ other_types, other_types + num_other_types,
+ [prefs](const std::string& pref) { return prefs->GetBoolean(pref); });
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "History.ClearBrowsingData.PasswordsDeletion.AdditionalDatatypesCount",
+ checked_other_types);
+ }
+
+ int period_selected =
+ prefs->GetInteger(browsing_data::prefs::kDeleteTimePeriod);
+
+ std::string webui_callback_id;
+ CHECK_EQ(2U, args->GetSize());
+ CHECK(args->GetString(0, &webui_callback_id));
+
+ const base::ListValue* important_sites = nullptr;
+ CHECK(args->GetList(1, &important_sites));
+ std::unique_ptr<content::BrowsingDataFilterBuilder> filter_builder =
+ ProcessImportantSites(important_sites);
+
+ content::BrowsingDataRemover* remover =
+ content::BrowserContext::GetBrowsingDataRemover(profile_);
+
+ base::OnceClosure callback =
+ base::BindOnce(&ClearBrowsingDataHandler::OnClearingTaskFinished,
+ weak_ptr_factory_.GetWeakPtr(), webui_callback_id);
+ browsing_data::TimePeriod time_period =
+ static_cast<browsing_data::TimePeriod>(period_selected);
+
+ browsing_data_important_sites_util::Remove(
+ remove_mask, origin_mask, time_period, std::move(filter_builder), remover,
+ std::move(callback));
+}
+
+std::unique_ptr<content::BrowsingDataFilterBuilder>
+ClearBrowsingDataHandler::ProcessImportantSites(
+ const base::ListValue* important_sites) {
+ std::vector<std::string> excluding_domains;
+ std::vector<int32_t> excluding_domain_reasons;
+ std::vector<std::string> ignoring_domains;
+ std::vector<int32_t> ignoring_domain_reasons;
+ for (const auto& item : *important_sites) {
+ const base::DictionaryValue* site = nullptr;
+ CHECK(item.GetAsDictionary(&site));
+ bool is_checked = false;
+ CHECK(site->GetBoolean(kIsCheckedField, &is_checked));
+ std::string domain;
+ CHECK(site->GetString(kRegisterableDomainField, &domain));
+ int domain_reason = -1;
+ CHECK(site->GetInteger(kReasonBitField, &domain_reason));
+ if (is_checked) { // Selected important sites should be deleted.
+ ignoring_domains.push_back(domain);
+ ignoring_domain_reasons.push_back(domain_reason);
+ } else { // Unselected sites should be kept.
+ excluding_domains.push_back(domain);
+ excluding_domain_reasons.push_back(domain_reason);
+ }
+ }
+
+ if (!excluding_domains.empty() || !ignoring_domains.empty()) {
+ ImportantSitesUtil::RecordBlacklistedAndIgnoredImportantSites(
+ profile_->GetOriginalProfile(), excluding_domains,
+ excluding_domain_reasons, ignoring_domains, ignoring_domain_reasons);
+ }
+
+ std::unique_ptr<content::BrowsingDataFilterBuilder> filter_builder(
+ content::BrowsingDataFilterBuilder::Create(
+ content::BrowsingDataFilterBuilder::BLACKLIST));
+ for (const std::string& domain : excluding_domains) {
+ filter_builder->AddRegisterableDomain(domain);
+ }
+ return filter_builder;
+}
+
+void ClearBrowsingDataHandler::OnClearingTaskFinished(
+ const std::string& webui_callback_id) {
+ PrefService* prefs = profile_->GetPrefs();
+ int notice_shown_times = prefs->GetInteger(
+ browsing_data::prefs::kClearBrowsingDataHistoryNoticeShownTimes);
+
+ // When the deletion is complete, we might show an additional dialog with
+ // a notice about other forms of browsing history. This is the case if
+ const bool show_notice =
+ // 1. The dialog is relevant for the user.
+ show_history_deletion_dialog_ &&
+ // 2. The notice has been shown less than |kMaxTimesHistoryNoticeShown|.
+ notice_shown_times < kMaxTimesHistoryNoticeShown &&
+ // 3. The selected data types contained browsing history.
+ prefs->GetBoolean(browsing_data::prefs::kDeleteBrowsingHistory);
+
+ if (show_notice) {
+ // Increment the preference.
+ prefs->SetInteger(
+ browsing_data::prefs::kClearBrowsingDataHistoryNoticeShownTimes,
+ notice_shown_times + 1);
+ }
+
+ UMA_HISTOGRAM_BOOLEAN(
+ "History.ClearBrowsingData.ShownHistoryNoticeAfterClearing", show_notice);
+
+ ResolveJavascriptCallback(base::Value(webui_callback_id),
+ base::Value(show_notice));
+}
+
+void ClearBrowsingDataHandler::HandleGetImportantSites(
+ const base::ListValue* args) {
+ AllowJavascript();
+ std::string callback_id;
+ CHECK(args->GetString(0, &callback_id));
+ DCHECK(base::FeatureList::IsEnabled(features::kImportantSitesInCbd));
+
+ Profile* profile = profile_->GetOriginalProfile();
+ bool important_sites_dialog_disabled =
+ ImportantSitesUtil::IsDialogDisabled(profile);
+
+ if (important_sites_dialog_disabled) {
+ ResolveJavascriptCallback(base::Value(callback_id), base::ListValue());
+ return;
+ }
+
+ std::vector<ImportantSitesUtil::ImportantDomainInfo> important_sites =
+ ImportantSitesUtil::GetImportantRegisterableDomains(
+ profile, ImportantSitesUtil::kMaxImportantSites);
+ content::StoragePartition* partition =
+ content::BrowserContext::GetDefaultStoragePartition(profile);
+ storage::QuotaManager* quota_manager = partition->GetQuotaManager();
+ content::DOMStorageContext* dom_storage = partition->GetDOMStorageContext();
+
+ ImportantSitesUsageCounter::GetUsage(
+ std::move(important_sites), quota_manager, dom_storage,
+ base::BindOnce(&ClearBrowsingDataHandler::OnFetchImportantSitesFinished,
+ weak_ptr_factory_.GetWeakPtr(), callback_id));
+}
+
+void ClearBrowsingDataHandler::OnFetchImportantSitesFinished(
+ const std::string& callback_id,
+ std::vector<ImportantSitesUtil::ImportantDomainInfo> important_sites) {
+ base::ListValue important_sites_list;
+
+ for (const auto& info : important_sites) {
+ auto entry = base::MakeUnique<base::DictionaryValue>();
+ entry->SetString(kRegisterableDomainField, info.registerable_domain);
+ // The |reason_bitfield| is only passed to Javascript to be logged
+ // from |HandleClearBrowsingData|.
+ entry->SetInteger(kReasonBitField, info.reason_bitfield);
+ entry->SetString(kExampleOriginField, info.example_origin.spec());
+ // Initially all sites are selected for deletion.
+ entry->SetBoolean(kIsCheckedField, true);
+ entry->SetString(kStorageSizeField, ui::FormatBytes(info.usage));
+ bool has_notifications =
+ (info.reason_bitfield & (1 << ImportantReason::NOTIFICATIONS)) != 0;
+ entry->SetBoolean(kHasNotificationsField, has_notifications);
+ important_sites_list.Append(std::move(entry));
+ }
+ ResolveJavascriptCallback(base::Value(callback_id), important_sites_list);
+}
+
+void ClearBrowsingDataHandler::HandleInitialize(const base::ListValue* args) {
+ AllowJavascript();
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+
+ // Needed because WebUI doesn't handle renderer crashes. See crbug.com/610450.
+ weak_ptr_factory_.InvalidateWeakPtrs();
+
+ UpdateSyncState();
+ RefreshHistoryNotice();
+
+ // Restart the counters each time the dialog is reopened.
+ for (const auto& counter : counters_)
+ counter->Restart();
+
+ ResolveJavascriptCallback(*callback_id, base::Value() /* Promise<void> */);
+}
+
+void ClearBrowsingDataHandler::OnStateChanged(syncer::SyncService* sync) {
+ UpdateSyncState();
+}
+
+void ClearBrowsingDataHandler::UpdateSyncState() {
+ CallJavascriptFunction(
+ "cr.webUIListenerCallback", base::Value("update-footer"),
+ base::Value(sync_service_ && sync_service_->IsSyncActive()),
+ base::Value(show_history_footer_));
+}
+
+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.
+ int notice_shown_times = profile_->GetPrefs()->GetInteger(
+ browsing_data::prefs::kClearBrowsingDataHistoryNoticeShownTimes);
+
+ if (notice_shown_times < kMaxTimesHistoryNoticeShown) {
+ browsing_data::ShouldPopupDialogAboutOtherFormsOfBrowsingHistory(
+ sync_service_,
+ WebHistoryServiceFactory::GetForProfile(profile_),
+ chrome::GetChannel(),
+ base::Bind(&ClearBrowsingDataHandler::UpdateHistoryDeletionDialog,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+}
+
+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;
+}
+
+void ClearBrowsingDataHandler::AddCounter(
+ std::unique_ptr<browsing_data::BrowsingDataCounter> counter) {
+ counter->Init(profile_->GetPrefs(),
+ browsing_data::ClearBrowsingDataTab::ADVANCED,
+ base::Bind(&ClearBrowsingDataHandler::UpdateCounterText,
+ base::Unretained(this)));
+ counters_.push_back(std::move(counter));
+}
+
+void ClearBrowsingDataHandler::UpdateCounterText(
+ std::unique_ptr<browsing_data::BrowsingDataCounter::Result> result) {
+ CallJavascriptFunction(
+ "cr.webUIListenerCallback", base::Value("update-counter-text"),
+ base::Value(result->source()->GetPrefName()),
+ base::Value(GetChromeCounterTextFromResult(result.get())));
+}
+
+} // namespace settings
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
new file mode 100644
index 00000000000..07cce948091
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h
@@ -0,0 +1,122 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_CLEAR_BROWSING_DATA_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_CLEAR_BROWSING_DATA_HANDLER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/engagement/important_sites_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/browsing_data/core/counters/browsing_data_counter.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace content {
+class BrowsingDataFilterBuilder;
+class WebUI;
+}
+
+namespace settings {
+
+// Chrome browser startup settings handler.
+class ClearBrowsingDataHandler : public SettingsPageUIHandler,
+ public syncer::SyncServiceObserver {
+ public:
+ explicit ClearBrowsingDataHandler(content::WebUI* webui);
+ ~ClearBrowsingDataHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ private:
+ // Clears browsing data, called by Javascript.
+ void HandleClearBrowsingData(const base::ListValue* value);
+
+ // Parses a ListValue with important site information and creates a
+ // BrowsingDataFilterBuilder.
+ std::unique_ptr<content::BrowsingDataFilterBuilder> ProcessImportantSites(
+ const base::ListValue* important_sites);
+
+ // Called when a clearing task finished. |webui_callback_id| is provided
+ // by the WebUI action that initiated it.
+ void OnClearingTaskFinished(const std::string& webui_callback_id);
+
+ // Get important sites, called by Javascript.
+ void HandleGetImportantSites(const base::ListValue* value);
+
+ void OnFetchImportantSitesFinished(
+ const std::string& callback_id,
+ std::vector<ImportantSitesUtil::ImportantDomainInfo> sites);
+
+ // Initializes the dialog UI. Called by JavaScript when the DOM is ready.
+ void HandleInitialize(const base::ListValue* args);
+
+ // Implementation of SyncServiceObserver.
+ void OnStateChanged(syncer::SyncService* sync) override;
+
+ // Updates the footer of the dialog when the sync state changes.
+ void UpdateSyncState();
+
+ // Finds out whether we should show notice about other forms of history stored
+ // 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.
+ void UpdateHistoryDeletionDialog(bool show);
+
+ // Adds a browsing data |counter|.
+ void AddCounter(std::unique_ptr<browsing_data::BrowsingDataCounter> counter);
+
+ // Updates a counter text according to the |result|.
+ void UpdateCounterText(
+ std::unique_ptr<browsing_data::BrowsingDataCounter::Result> result);
+
+ // Cached profile corresponding to the WebUI of this handler.
+ Profile* profile_;
+
+ // Counters that calculate the data volume for individual data types.
+ std::vector<std::unique_ptr<browsing_data::BrowsingDataCounter>> counters_;
+
+ // ProfileSyncService to observe sync state changes.
+ browser_sync::ProfileSyncService* sync_service_;
+ 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_;
+
+ // A weak pointer factory for asynchronous calls referencing this class.
+ // The weak pointers are invalidated in |OnJavascriptDisallowed()| and
+ // |HandleInitialize()| to cancel previously initiated tasks.
+ base::WeakPtrFactory<ClearBrowsingDataHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClearBrowsingDataHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_CLEAR_BROWSING_DATA_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc b/chromium/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc
new file mode 100644
index 00000000000..d6d1093dd93
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/settings_cookies_view_handler.cc
@@ -0,0 +1,285 @@
+// 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 "chrome/browser/ui/webui/settings/settings_cookies_view_handler.h"
+
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browsing_data/browsing_data_appcache_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_cache_storage_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_channel_id_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_cookie_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_database_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_file_system_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_flash_lso_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_media_license_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_quota_helper.h"
+#include "chrome/browser/browsing_data/browsing_data_service_worker_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/cookies_tree_model_util.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_ui.h"
+
+namespace storage {
+class FileSystemContext;
+}
+
+namespace settings {
+
+const char kId[] = "id";
+const char kChildren[] = "children";
+const char kStart[] = "start";
+const char kCount[] = "count";
+
+CookiesViewHandler::CookiesViewHandler()
+ : batch_update_(false),
+ model_util_(new CookiesTreeModelUtil) {
+}
+
+CookiesViewHandler::~CookiesViewHandler() {
+}
+
+void CookiesViewHandler::OnJavascriptAllowed() {
+}
+
+void CookiesViewHandler::OnJavascriptDisallowed() {
+}
+
+void CookiesViewHandler::RegisterMessages() {
+ EnsureCookiesTreeModelCreated();
+
+ web_ui()->RegisterMessageCallback(
+ "getCookieDetails",
+ base::Bind(&CookiesViewHandler::HandleGetCookieDetails,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "updateCookieSearchResults",
+ base::Bind(&CookiesViewHandler::HandleUpdateSearchResults,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "removeAllCookies",
+ base::Bind(&CookiesViewHandler::HandleRemoveAll, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "removeCookie",
+ base::Bind(&CookiesViewHandler::HandleRemove, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "loadCookie", base::Bind(&CookiesViewHandler::HandleLoadChildren,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "reloadCookies", base::Bind(&CookiesViewHandler::HandleReloadCookies,
+ base::Unretained(this)));
+}
+
+void CookiesViewHandler::TreeNodesAdded(ui::TreeModel* model,
+ ui::TreeModelNode* parent,
+ int start,
+ int count) {
+ // Skip if there is a batch update in progress.
+ if (batch_update_)
+ return;
+
+ CookiesTreeModel* tree_model = static_cast<CookiesTreeModel*>(model);
+ CookieTreeNode* parent_node = tree_model->AsNode(parent);
+
+ std::unique_ptr<base::ListValue> children(new base::ListValue);
+ // Passing false for |include_quota_nodes| since they don't reflect reality
+ // until bug http://crbug.com/642955 is fixed and local/session storage is
+ // counted against the total.
+ model_util_->GetChildNodeList(
+ parent_node, start, count, /*include_quota_nodes=*/false, children.get());
+
+ base::DictionaryValue args;
+ if (parent == tree_model->GetRoot())
+ args.Set(kId, base::MakeUnique<base::Value>());
+ else
+ args.SetString(kId, model_util_->GetTreeNodeId(parent_node));
+ args.SetInteger(kStart, start);
+ args.Set(kChildren, std::move(children));
+ FireWebUIListener("onTreeItemAdded", args);
+}
+
+void CookiesViewHandler::TreeNodesRemoved(ui::TreeModel* model,
+ ui::TreeModelNode* parent,
+ int start,
+ int count) {
+ // Skip if there is a batch update in progress.
+ if (batch_update_)
+ return;
+
+ CookiesTreeModel* tree_model = static_cast<CookiesTreeModel*>(model);
+
+ base::DictionaryValue args;
+ if (parent == tree_model->GetRoot())
+ args.Set(kId, base::MakeUnique<base::Value>());
+ else
+ args.SetString(kId, model_util_->GetTreeNodeId(tree_model->AsNode(parent)));
+ args.SetInteger(kStart, start);
+ args.SetInteger(kCount, count);
+ FireWebUIListener("onTreeItemRemoved", args);
+}
+
+void CookiesViewHandler::TreeModelBeginBatch(CookiesTreeModel* model) {
+ DCHECK(!batch_update_); // There should be no nested batch begin.
+ batch_update_ = true;
+}
+
+void CookiesViewHandler::TreeModelEndBatch(CookiesTreeModel* model) {
+ DCHECK(batch_update_);
+ batch_update_ = false;
+
+ if (IsJavascriptAllowed())
+ SendChildren(model->GetRoot());
+}
+
+void CookiesViewHandler::EnsureCookiesTreeModelCreated() {
+ if (!cookies_tree_model_.get()) {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ content::StoragePartition* storage_partition =
+ content::BrowserContext::GetDefaultStoragePartition(profile);
+ content::IndexedDBContext* indexed_db_context =
+ storage_partition->GetIndexedDBContext();
+ content::ServiceWorkerContext* service_worker_context =
+ storage_partition->GetServiceWorkerContext();
+ content::CacheStorageContext* cache_storage_context =
+ storage_partition->GetCacheStorageContext();
+ storage::FileSystemContext* file_system_context =
+ storage_partition->GetFileSystemContext();
+ LocalDataContainer* container = new LocalDataContainer(
+ new BrowsingDataCookieHelper(profile->GetRequestContext()),
+ new BrowsingDataDatabaseHelper(profile),
+ new BrowsingDataLocalStorageHelper(profile), NULL,
+ new BrowsingDataAppCacheHelper(profile),
+ new BrowsingDataIndexedDBHelper(indexed_db_context),
+ BrowsingDataFileSystemHelper::Create(file_system_context),
+ BrowsingDataQuotaHelper::Create(profile),
+ BrowsingDataChannelIDHelper::Create(profile->GetRequestContext()),
+ new BrowsingDataServiceWorkerHelper(service_worker_context),
+ new BrowsingDataCacheStorageHelper(cache_storage_context),
+ BrowsingDataFlashLSOHelper::Create(profile),
+ BrowsingDataMediaLicenseHelper::Create(file_system_context));
+ cookies_tree_model_.reset(
+ new CookiesTreeModel(container,
+ profile->GetExtensionSpecialStoragePolicy()));
+ cookies_tree_model_->AddCookiesTreeObserver(this);
+ }
+}
+
+void CookiesViewHandler::HandleUpdateSearchResults(
+ const base::ListValue* args) {
+ base::string16 query;
+ CHECK(args->GetString(0, &query));
+
+ cookies_tree_model_->UpdateSearchResults(query);
+}
+
+void CookiesViewHandler::HandleGetCookieDetails(const base::ListValue* args) {
+ CHECK_EQ(2U, args->GetSize());
+ CHECK(args->GetString(0, &callback_id_));
+ std::string site;
+ CHECK(args->GetString(1, &site));
+
+ AllowJavascript();
+ const CookieTreeNode* node = model_util_->GetTreeNodeFromTitle(
+ cookies_tree_model_->GetRoot(), base::UTF8ToUTF16(site));
+
+ if (!node) {
+ RejectJavascriptCallback(base::Value(callback_id_), base::Value());
+ callback_id_.clear();
+ return;
+ }
+
+ SendCookieDetails(node);
+}
+
+void CookiesViewHandler::HandleReloadCookies(const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ CHECK(args->GetString(0, &callback_id_));
+
+ AllowJavascript();
+ cookies_tree_model_.reset();
+ EnsureCookiesTreeModelCreated();
+}
+
+void CookiesViewHandler::HandleRemoveAll(const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ CHECK(args->GetString(0, &callback_id_));
+
+ cookies_tree_model_->DeleteAllStoredObjects();
+}
+
+void CookiesViewHandler::HandleRemove(const base::ListValue* args) {
+ std::string node_path;
+ CHECK(args->GetString(0, &node_path));
+
+ const CookieTreeNode* node = model_util_->GetTreeNodeFromPath(
+ cookies_tree_model_->GetRoot(), node_path);
+ if (node)
+ cookies_tree_model_->DeleteCookieNode(const_cast<CookieTreeNode*>(node));
+}
+
+void CookiesViewHandler::HandleLoadChildren(const base::ListValue* args) {
+ CHECK_EQ(2U, args->GetSize());
+ CHECK(args->GetString(0, &callback_id_));
+
+ std::string node_path;
+ CHECK(args->GetString(1, &node_path));
+
+ const CookieTreeNode* node = model_util_->GetTreeNodeFromPath(
+ cookies_tree_model_->GetRoot(), node_path);
+ if (node)
+ SendChildren(node);
+}
+
+void CookiesViewHandler::SendChildren(const CookieTreeNode* parent) {
+ std::unique_ptr<base::ListValue> children(new base::ListValue);
+ // Passing false for |include_quota_nodes| since they don't reflect reality
+ // until bug http://crbug.com/642955 is fixed and local/session storage is
+ // counted against the total.
+ model_util_->GetChildNodeList(parent, /*start=*/0, parent->child_count(),
+ /*include_quota_nodes=*/false, children.get());
+
+ base::DictionaryValue args;
+ if (parent == cookies_tree_model_->GetRoot())
+ args.Set(kId, base::MakeUnique<base::Value>());
+ else
+ args.SetString(kId, model_util_->GetTreeNodeId(parent));
+ args.Set(kChildren, std::move(children));
+
+ ResolveJavascriptCallback(base::Value(callback_id_), args);
+ callback_id_.clear();
+}
+
+void CookiesViewHandler::SendCookieDetails(const CookieTreeNode* parent) {
+ std::unique_ptr<base::ListValue> children(new base::ListValue);
+ // Passing false for |include_quota_nodes| since they don't reflect reality
+ // until bug http://crbug.com/642955 is fixed and local/session storage is
+ // counted against the total.
+ model_util_->GetChildNodeDetails(parent, /*start=*/0, parent->child_count(),
+ /*include_quota_nodes=*/false,
+ children.get());
+
+ base::DictionaryValue args;
+ if (parent == cookies_tree_model_->GetRoot())
+ args.Set(kId, base::MakeUnique<base::Value>());
+ else
+ args.SetString(kId, model_util_->GetTreeNodeId(parent));
+ args.Set(kChildren, std::move(children));
+
+ ResolveJavascriptCallback(base::Value(callback_id_), args);
+ callback_id_.clear();
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_cookies_view_handler.h b/chromium/chrome/browser/ui/webui/settings/settings_cookies_view_handler.h
new file mode 100644
index 00000000000..e087c55d302
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/settings_cookies_view_handler.h
@@ -0,0 +1,92 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_COOKIES_VIEW_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_COOKIES_VIEW_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/browsing_data/cookies_tree_model.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+class CookiesTreeModelUtil;
+
+namespace settings {
+
+class CookiesViewHandler : public SettingsPageUIHandler,
+ public CookiesTreeModel::Observer {
+ public:
+ CookiesViewHandler();
+ ~CookiesViewHandler() override;
+
+ // SettingsPageUIHandler:
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+ void RegisterMessages() override;
+
+ // CookiesTreeModel::Observer:
+ void TreeNodesAdded(ui::TreeModel* model,
+ ui::TreeModelNode* parent,
+ int start,
+ int count) override;
+ void TreeNodesRemoved(ui::TreeModel* model,
+ ui::TreeModelNode* parent,
+ int start,
+ int count) override;
+ void TreeNodeChanged(ui::TreeModel* model, ui::TreeModelNode* node) override {
+ }
+ void TreeModelBeginBatch(CookiesTreeModel* model) override;
+ void TreeModelEndBatch(CookiesTreeModel* model) override;
+
+ private:
+ // Creates the CookiesTreeModel if neccessary.
+ void EnsureCookiesTreeModelCreated();
+
+ // Updates search filter for cookies tree model.
+ void HandleUpdateSearchResults(const base::ListValue* args);
+
+ // Retrieve cookie details for a specific site.
+ void HandleGetCookieDetails(const base::ListValue* args);
+
+ // Remove all sites data.
+ void HandleRemoveAll(const base::ListValue* args);
+
+ // Remove selected sites data.
+ void HandleRemove(const base::ListValue* args);
+
+ // Get the tree node using the tree path info in |args| and call
+ // SendChildren to pass back children nodes data to WebUI.
+ void HandleLoadChildren(const base::ListValue* args);
+
+ // Get children nodes data and pass it to 'CookiesView.loadChildren' to
+ // update the WebUI.
+ void SendChildren(const CookieTreeNode* parent);
+
+ // Package and send cookie details for a site.
+ void SendCookieDetails(const CookieTreeNode* parent);
+
+ // Reloads the CookiesTreeModel and passes the nodes to
+ // 'CookiesView.loadChildren' to update the WebUI.
+ void HandleReloadCookies(const base::ListValue* args);
+
+ // The Cookies Tree model
+ std::unique_ptr<CookiesTreeModel> cookies_tree_model_;
+
+ // Flag to indicate whether there is a batch update in progress.
+ bool batch_update_;
+
+ std::unique_ptr<CookiesTreeModelUtil> model_util_;
+
+ // The callback ID for the current outstanding request.
+ std::string callback_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(CookiesViewHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_COOKIES_VIEW_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_default_browser_handler.cc b/chromium/chrome/browser/ui/webui/settings/settings_default_browser_handler.cc
new file mode 100644
index 00000000000..a96ea7524f8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/settings_default_browser_handler.cc
@@ -0,0 +1,107 @@
+// 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.
+
+#include "chrome/browser/ui/webui/settings/settings_default_browser_handler.h"
+
+#include "base/bind.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/startup/default_browser_prompt.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_ui.h"
+
+namespace settings {
+
+namespace {
+
+bool DefaultBrowserIsDisabledByPolicy() {
+ const PrefService::Preference* pref =
+ g_browser_process->local_state()->FindPreference(
+ prefs::kDefaultBrowserSettingEnabled);
+ DCHECK(pref);
+ bool may_set_default_browser;
+ bool success = pref->GetValue()->GetAsBoolean(&may_set_default_browser);
+ DCHECK(success);
+ return pref->IsManaged() && !may_set_default_browser;
+}
+
+} // namespace
+
+DefaultBrowserHandler::DefaultBrowserHandler(content::WebUI* webui)
+ : weak_ptr_factory_(this) {
+}
+
+DefaultBrowserHandler::~DefaultBrowserHandler() {}
+
+void DefaultBrowserHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "SettingsDefaultBrowser.requestDefaultBrowserState",
+ base::Bind(&DefaultBrowserHandler::RequestDefaultBrowserState,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "SettingsDefaultBrowser.setAsDefaultBrowser",
+ base::Bind(&DefaultBrowserHandler::SetAsDefaultBrowser,
+ base::Unretained(this)));
+}
+
+void DefaultBrowserHandler::OnJavascriptAllowed() {
+ PrefService* prefs = g_browser_process->local_state();
+ local_state_pref_registrar_.Init(prefs);
+ local_state_pref_registrar_.Add(
+ prefs::kDefaultBrowserSettingEnabled,
+ base::Bind(&DefaultBrowserHandler::RequestDefaultBrowserState,
+ base::Unretained(this), nullptr));
+ default_browser_worker_ = new shell_integration::DefaultBrowserWorker(
+ base::Bind(&DefaultBrowserHandler::OnDefaultBrowserWorkerFinished,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DefaultBrowserHandler::OnJavascriptDisallowed() {
+ local_state_pref_registrar_.RemoveAll();
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ default_browser_worker_ = nullptr;
+}
+
+void DefaultBrowserHandler::RequestDefaultBrowserState(
+ const base::ListValue* /*args*/) {
+ AllowJavascript();
+
+ default_browser_worker_->StartCheckIsDefault();
+}
+
+void DefaultBrowserHandler::SetAsDefaultBrowser(const base::ListValue* args) {
+ CHECK(!DefaultBrowserIsDisabledByPolicy());
+
+ base::RecordAction(base::UserMetricsAction("Options_SetAsDefaultBrowser"));
+ UMA_HISTOGRAM_COUNTS("Settings.StartSetAsDefault", true);
+
+ default_browser_worker_->StartSetAsDefault();
+
+ // If the user attempted to make Chrome the default browser, notify
+ // them when this changes.
+ chrome::ResetDefaultBrowserPrompt(Profile::FromWebUI(web_ui()));
+}
+
+void DefaultBrowserHandler::OnDefaultBrowserWorkerFinished(
+ shell_integration::DefaultWebClientState state) {
+ if (state == shell_integration::IS_DEFAULT) {
+ // Notify the user in the future if Chrome ceases to be the user's chosen
+ // default browser.
+ chrome::ResetDefaultBrowserPrompt(Profile::FromWebUI(web_ui()));
+ }
+
+ base::DictionaryValue dict;
+ dict.SetBoolean("isDefault", state == shell_integration::IS_DEFAULT);
+ dict.SetBoolean("canBeDefault",
+ shell_integration::CanSetAsDefaultBrowser());
+ dict.SetBoolean("isUnknownError",
+ state == shell_integration::UNKNOWN_DEFAULT);
+ dict.SetBoolean("isDisabledByPolicy", DefaultBrowserIsDisabledByPolicy());
+
+ FireWebUIListener("settings.updateDefaultBrowserState", dict);
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_default_browser_handler.h b/chromium/chrome/browser/ui/webui/settings/settings_default_browser_handler.h
new file mode 100644
index 00000000000..cc2f4ec1e10
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/settings_default_browser_handler.h
@@ -0,0 +1,65 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_DEFAULT_BROWSER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_DEFAULT_BROWSER_HANDLER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/shell_integration.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "components/prefs/pref_change_registrar.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace content {
+class WebUI;
+}
+
+namespace settings {
+
+// The application used by the OS to open web documents (e.g. *.html)
+// is the "default browser". This class is an API for the JavaScript
+// settings code to change the default browser settings.
+class DefaultBrowserHandler : public SettingsPageUIHandler {
+ public:
+ explicit DefaultBrowserHandler(content::WebUI* webui);
+ ~DefaultBrowserHandler() override;
+
+ // SettingsPageUIHandler implementation.
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ private:
+ // Called from WebUI to request the current state.
+ void RequestDefaultBrowserState(const base::ListValue* args);
+
+ // Makes this the default browser. Called from WebUI.
+ void SetAsDefaultBrowser(const base::ListValue* args);
+
+ // Called with the default browser state when the DefaultBrowserWorker is
+ // done.
+ void OnDefaultBrowserWorkerFinished(
+ shell_integration::DefaultWebClientState state);
+
+ // Reference to a background worker that handles default browser settings.
+ scoped_refptr<shell_integration::DefaultBrowserWorker>
+ default_browser_worker_;
+
+ // Used to listen for changes to if the default browser setting is managed.
+ PrefChangeRegistrar local_state_pref_registrar_;
+
+ // Used to invalidate the DefaultBrowserWorker callback.
+ base::WeakPtrFactory<DefaultBrowserHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DefaultBrowserHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_DEFAULT_BROWSER_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_import_data_handler.cc b/chromium/chrome/browser/ui/webui/settings/settings_import_data_handler.cc
new file mode 100644
index 00000000000..a29576c7926
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/settings_import_data_handler.cc
@@ -0,0 +1,255 @@
+// 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 "chrome/browser/ui/webui/settings/settings_import_data_handler.h"
+
+#include <stddef.h>
+
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/importer/external_process_importer_host.h"
+#include "chrome/browser/importer/importer_list.h"
+#include "chrome/browser/importer/importer_uma.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/chrome_select_file_policy.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_ui.h"
+
+using content::BrowserThread;
+
+namespace settings {
+
+namespace {
+const char kImportStatusInProgress[] = "inProgress";
+const char kImportStatusSucceeded[] = "succeeded";
+const char kImportStatusFailed[] = "failed";
+}
+
+ImportDataHandler::ImportDataHandler()
+ : importer_host_(NULL), import_did_succeed_(false) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+ImportDataHandler::~ImportDataHandler() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if (importer_host_)
+ importer_host_->set_observer(NULL);
+
+ if (select_file_dialog_.get())
+ select_file_dialog_->ListenerDestroyed();
+}
+
+void ImportDataHandler::RegisterMessages() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ web_ui()->RegisterMessageCallback(
+ "initializeImportDialog",
+ base::Bind(&ImportDataHandler::InitializeDialog, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "importData",
+ base::Bind(&ImportDataHandler::ImportData, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "importFromBookmarksFile",
+ base::Bind(&ImportDataHandler::HandleChooseBookmarksFile,
+ base::Unretained(this)));
+}
+
+void ImportDataHandler::OnJavascriptDisallowed() {
+ // Cancels outstanding profile list detections.
+ importer_list_.reset();
+
+ // Stops listening to updates from any ongoing imports.
+ if (importer_host_)
+ importer_host_->set_observer(NULL);
+}
+
+void ImportDataHandler::StartImport(
+ const importer::SourceProfile& source_profile,
+ uint16_t imported_items) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ if (!imported_items)
+ return;
+
+ // If another import is already ongoing, let it finish silently.
+ if (importer_host_)
+ importer_host_->set_observer(NULL);
+
+ FireWebUIListener("import-data-status-changed",
+ base::Value(kImportStatusInProgress));
+ import_did_succeed_ = false;
+
+ importer_host_ = new ExternalProcessImporterHost();
+ importer_host_->set_observer(this);
+ Profile* profile = Profile::FromWebUI(web_ui());
+ importer_host_->StartImportSettings(source_profile, profile,
+ imported_items,
+ new ProfileWriter(profile));
+
+ importer::LogImporterUseToMetrics("ImportDataHandler",
+ source_profile.importer_type);
+}
+
+void ImportDataHandler::ImportData(const base::ListValue* args) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ int browser_index;
+ CHECK(args->GetInteger(0, &browser_index));
+
+ PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+
+ uint16_t selected_items = importer::NONE;
+ if (prefs->GetBoolean(prefs::kImportDialogAutofillFormData))
+ selected_items |= importer::AUTOFILL_FORM_DATA;
+ if (prefs->GetBoolean(prefs::kImportDialogBookmarks))
+ selected_items |= importer::FAVORITES;
+ if (prefs->GetBoolean(prefs::kImportDialogHistory))
+ selected_items |= importer::HISTORY;
+ if (prefs->GetBoolean(prefs::kImportDialogSavedPasswords))
+ selected_items |= importer::PASSWORDS;
+ if (prefs->GetBoolean(prefs::kImportDialogSearchEngine))
+ selected_items |= importer::SEARCH_ENGINES;
+
+ const importer::SourceProfile& source_profile =
+ importer_list_->GetSourceProfileAt(browser_index);
+ uint16_t supported_items = source_profile.services_supported;
+
+ uint16_t imported_items = (selected_items & supported_items);
+ if (imported_items) {
+ StartImport(source_profile, imported_items);
+ } else {
+ LOG(WARNING) << "There were no settings to import from '"
+ << source_profile.importer_name << "'.";
+ }
+}
+
+void ImportDataHandler::InitializeDialog(const base::ListValue* args) {
+ AllowJavascript();
+
+ CHECK_EQ(1U, args->GetSize());
+ std::string callback_id;
+ CHECK(args->GetString(0, &callback_id));
+
+ importer_list_.reset(new ImporterList());
+ importer_list_->DetectSourceProfiles(
+ g_browser_process->GetApplicationLocale(),
+ true, // include_interactive_profiles
+ base::Bind(&ImportDataHandler::SendBrowserProfileData,
+ base::Unretained(this), callback_id));
+}
+
+void ImportDataHandler::SendBrowserProfileData(const std::string& callback_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ base::ListValue browser_profiles;
+ for (size_t i = 0; i < importer_list_->count(); ++i) {
+ const importer::SourceProfile& source_profile =
+ importer_list_->GetSourceProfileAt(i);
+ uint16_t browser_services = source_profile.services_supported;
+
+ std::unique_ptr<base::DictionaryValue> browser_profile(
+ new base::DictionaryValue());
+ browser_profile->SetString("name", source_profile.importer_name);
+ browser_profile->SetInteger("index", i);
+ browser_profile->SetBoolean("history",
+ (browser_services & importer::HISTORY) != 0);
+ browser_profile->SetBoolean("favorites",
+ (browser_services & importer::FAVORITES) != 0);
+ browser_profile->SetBoolean("passwords",
+ (browser_services & importer::PASSWORDS) != 0);
+ browser_profile->SetBoolean("search",
+ (browser_services & importer::SEARCH_ENGINES) != 0);
+ browser_profile->SetBoolean(
+ "autofillFormData",
+ (browser_services & importer::AUTOFILL_FORM_DATA) != 0);
+
+ browser_profiles.Append(std::move(browser_profile));
+ }
+
+ ResolveJavascriptCallback(base::Value(callback_id), browser_profiles);
+}
+
+void ImportDataHandler::ImportStarted() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+void ImportDataHandler::ImportItemStarted(importer::ImportItem item) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ // TODO(csilv): show progress detail in the web view.
+}
+
+void ImportDataHandler::ImportItemEnded(importer::ImportItem item) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ // TODO(csilv): show progress detail in the web view.
+ import_did_succeed_ = true;
+}
+
+void ImportDataHandler::ImportEnded() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ importer_host_->set_observer(NULL);
+ importer_host_ = NULL;
+
+ CallJavascriptFunction(
+ "cr.webUIListenerCallback", base::Value("import-data-status-changed"),
+ base::Value(import_did_succeed_ ? kImportStatusSucceeded
+ : kImportStatusFailed));
+}
+
+void ImportDataHandler::FileSelected(const base::FilePath& path,
+ int /*index*/,
+ void* /*params*/) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ importer::SourceProfile source_profile;
+ source_profile.importer_type = importer::TYPE_BOOKMARKS_FILE;
+ source_profile.source_path = path;
+
+ StartImport(source_profile, importer::FAVORITES);
+}
+
+void ImportDataHandler::HandleChooseBookmarksFile(const base::ListValue* args) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ DCHECK(args && args->empty());
+ select_file_dialog_ = ui::SelectFileDialog::Create(
+ this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
+
+ ui::SelectFileDialog::FileTypeInfo file_type_info;
+ file_type_info.extensions.resize(1);
+ file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("html"));
+
+ Browser* browser =
+ chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
+
+ select_file_dialog_->SelectFile(ui::SelectFileDialog::SELECT_OPEN_FILE,
+ base::string16(),
+ base::FilePath(),
+ &file_type_info,
+ 0,
+ base::FilePath::StringType(),
+ browser->window()->GetNativeWindow(),
+ NULL);
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_import_data_handler.h b/chromium/chrome/browser/ui/webui/settings/settings_import_data_handler.h
new file mode 100644
index 00000000000..68c9c7519e5
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/settings_import_data_handler.h
@@ -0,0 +1,75 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_IMPORT_DATA_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_IMPORT_DATA_HANDLER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/importer/importer_progress_observer.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "chrome/common/importer/importer_data_types.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+
+class ExternalProcessImporterHost;
+class ImporterList;
+
+namespace settings {
+
+// Chrome personal stuff import data overlay UI handler.
+class ImportDataHandler : public SettingsPageUIHandler,
+ public importer::ImporterProgressObserver,
+ public ui::SelectFileDialog::Listener {
+ public:
+ ImportDataHandler();
+ ~ImportDataHandler() override;
+
+ // SettingsPageUIHandler
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override {}
+ void OnJavascriptDisallowed() override;
+
+ private:
+ void StartImport(const importer::SourceProfile& source_profile,
+ uint16_t imported_items);
+
+ void ImportData(const base::ListValue* args);
+
+ void InitializeDialog(const base::ListValue* args);
+ void SendBrowserProfileData(const std::string& callback_id);
+
+ // importer::ImporterProgressObserver:
+ void ImportStarted() override;
+ void ImportItemStarted(importer::ImportItem item) override;
+ void ImportItemEnded(importer::ImportItem item) override;
+ void ImportEnded() override;
+
+ // ui::SelectFileDialog::Listener:
+ void FileSelected(const base::FilePath& path,
+ int index,
+ void* params) override;
+
+ // Opens a file selection dialog to choose the bookmarks HTML file.
+ void HandleChooseBookmarksFile(const base::ListValue* args);
+
+ std::unique_ptr<ImporterList> importer_list_;
+
+ // If non-null it means importing is in progress. ImporterHost takes care
+ // of deleting itself when import is complete.
+ ExternalProcessImporterHost* importer_host_; // weak
+
+ bool import_did_succeed_;
+
+ scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImportDataHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_IMPORT_DATA_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc b/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc
new file mode 100644
index 00000000000..384014361c5
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc
@@ -0,0 +1,257 @@
+// 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.
+
+#include "chrome/browser/ui/webui/settings/settings_manage_profile_handler.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/value_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/profiles/gaia_info_update_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_avatar_icon_util.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_metrics.h"
+#include "chrome/browser/profiles/profile_shortcut_manager.h"
+#include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/profiles/profiles_state.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/signin/core/common/profile_management_switches.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_ui.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/webui/web_ui_util.h"
+
+namespace settings {
+
+namespace {
+
+const char kProfileShortcutSettingHidden[] = "profileShortcutSettingHidden";
+const char kProfileShortcutFound[] = "profileShortcutFound";
+const char kProfileShortcutNotFound[] = "profileShortcutNotFound";
+
+} // namespace
+
+ManageProfileHandler::ManageProfileHandler(Profile* profile)
+ : profile_(profile), observer_(this), weak_factory_(this) {}
+
+ManageProfileHandler::~ManageProfileHandler() {}
+
+void ManageProfileHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "getAvailableIcons",
+ base::Bind(&ManageProfileHandler::HandleGetAvailableIcons,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setProfileIconToGaiaAvatar",
+ base::Bind(&ManageProfileHandler::HandleSetProfileIconToGaiaAvatar,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setProfileIconToDefaultAvatar",
+ base::Bind(&ManageProfileHandler::HandleSetProfileIconToDefaultAvatar,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setProfileName", base::Bind(&ManageProfileHandler::HandleSetProfileName,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "requestProfileShortcutStatus",
+ base::Bind(&ManageProfileHandler::HandleRequestProfileShortcutStatus,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "addProfileShortcut",
+ base::Bind(&ManageProfileHandler::HandleAddProfileShortcut,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "removeProfileShortcut",
+ base::Bind(&ManageProfileHandler::HandleRemoveProfileShortcut,
+ base::Unretained(this)));
+}
+
+void ManageProfileHandler::OnJavascriptAllowed() {
+ observer_.Add(
+ &g_browser_process->profile_manager()->GetProfileAttributesStorage());
+}
+
+void ManageProfileHandler::OnJavascriptDisallowed() {
+ observer_.RemoveAll();
+}
+
+void ManageProfileHandler::OnProfileAvatarChanged(
+ const base::FilePath& profile_path) {
+ // This is necessary to send the potentially updated GAIA photo.
+ FireWebUIListener("available-icons-changed", *GetAvailableIcons());
+}
+
+void ManageProfileHandler::HandleGetAvailableIcons(
+ const base::ListValue* args) {
+ AllowJavascript();
+
+ profiles::UpdateGaiaProfileInfoIfNeeded(profile_);
+
+ CHECK_EQ(1U, args->GetSize());
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+ ResolveJavascriptCallback(*callback_id, *GetAvailableIcons());
+}
+
+std::unique_ptr<base::ListValue> ManageProfileHandler::GetAvailableIcons() {
+ std::unique_ptr<base::ListValue> image_url_list(
+ profiles::GetDefaultProfileAvatarIconsAndLabels());
+
+ // Add the GAIA picture to the beginning of the list if it is available.
+ ProfileAttributesEntry* entry;
+ if (g_browser_process->profile_manager()->GetProfileAttributesStorage().
+ GetProfileAttributesWithPath(profile_->GetPath(), &entry)) {
+ const gfx::Image* icon = entry->GetGAIAPicture();
+ if (icon) {
+ auto gaia_picture_info = base::MakeUnique<base::DictionaryValue>();
+ gfx::Image avatar_icon = profiles::GetAvatarIconForWebUI(*icon, true);
+ gaia_picture_info->SetString(
+ "url", webui::GetBitmapDataUrl(avatar_icon.AsBitmap()));
+ gaia_picture_info->SetString(
+ "label",
+ l10n_util::GetStringUTF16(IDS_SETTINGS_CHANGE_PICTURE_PROFILE_PHOTO));
+ gaia_picture_info->SetBoolean("isGaiaAvatar", true);
+ image_url_list->Insert(0, std::move(gaia_picture_info));
+ }
+ }
+
+ return image_url_list;
+}
+
+void ManageProfileHandler::HandleSetProfileIconToGaiaAvatar(
+ const base::ListValue* /* args */) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ PrefService* pref_service = profile_->GetPrefs();
+ bool previously_using_gaia_icon =
+ pref_service->GetBoolean(prefs::kProfileUsingGAIAAvatar);
+
+ pref_service->SetBoolean(prefs::kProfileUsingDefaultAvatar, false);
+ pref_service->SetBoolean(prefs::kProfileUsingGAIAAvatar, true);
+ if (!previously_using_gaia_icon) {
+ // Only log if they changed to the GAIA photo.
+ // Selection of GAIA photo as avatar is logged as part of the function
+ // below.
+ ProfileMetrics::LogProfileSwitchGaia(ProfileMetrics::GAIA_OPT_IN);
+ }
+
+ ProfileMetrics::LogProfileUpdate(profile_->GetPath());
+}
+
+void ManageProfileHandler::HandleSetProfileIconToDefaultAvatar(
+ const base::ListValue* args) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(args);
+ DCHECK_EQ(1u, args->GetSize());
+
+ std::string icon_url;
+ CHECK(args->GetString(0, &icon_url));
+
+ size_t new_icon_index = 0;
+ CHECK(profiles::IsDefaultAvatarIconUrl(icon_url, &new_icon_index));
+
+ PrefService* pref_service = profile_->GetPrefs();
+ pref_service->SetInteger(prefs::kProfileAvatarIndex, new_icon_index);
+ pref_service->SetBoolean(prefs::kProfileUsingDefaultAvatar, false);
+ pref_service->SetBoolean(prefs::kProfileUsingGAIAAvatar, false);
+
+ ProfileMetrics::LogProfileAvatarSelection(new_icon_index);
+ ProfileMetrics::LogProfileUpdate(profile_->GetPath());
+}
+
+void ManageProfileHandler::HandleSetProfileName(const base::ListValue* args) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(args);
+ DCHECK_EQ(1u, args->GetSize());
+
+ if (profile_->IsLegacySupervised())
+ return;
+
+ base::string16 new_profile_name;
+ CHECK(args->GetString(0, &new_profile_name));
+
+ base::TrimWhitespace(new_profile_name, base::TRIM_ALL, &new_profile_name);
+ CHECK(!new_profile_name.empty());
+ profiles::UpdateProfileName(profile_, new_profile_name);
+
+ ProfileMetrics::LogProfileUpdate(profile_->GetPath());
+}
+
+void ManageProfileHandler::HandleRequestProfileShortcutStatus(
+ const base::ListValue* args) {
+ AllowJavascript();
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(ProfileShortcutManager::IsFeatureEnabled());
+
+ CHECK_EQ(1U, args->GetSize());
+ std::string callback_id;
+ CHECK(args->GetString(0, &callback_id));
+
+ // Don't show the add/remove desktop shortcut button in the single user case.
+ ProfileAttributesStorage& storage =
+ g_browser_process->profile_manager()->GetProfileAttributesStorage();
+ if (storage.GetNumberOfProfiles() <= 1u) {
+ ResolveJavascriptCallback(base::Value(callback_id),
+ base::Value(kProfileShortcutSettingHidden));
+ return;
+ }
+
+ ProfileShortcutManager* shortcut_manager =
+ g_browser_process->profile_manager()->profile_shortcut_manager();
+ DCHECK(shortcut_manager);
+ shortcut_manager->HasProfileShortcuts(
+ profile_->GetPath(),
+ base::Bind(&ManageProfileHandler::OnHasProfileShortcuts,
+ weak_factory_.GetWeakPtr(), callback_id));
+}
+
+void ManageProfileHandler::OnHasProfileShortcuts(
+ const std::string& callback_id, bool has_shortcuts) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ ResolveJavascriptCallback(
+ base::Value(callback_id),
+ base::Value(has_shortcuts ? kProfileShortcutFound
+ : kProfileShortcutNotFound));
+}
+
+void ManageProfileHandler::HandleAddProfileShortcut(
+ const base::ListValue* args) {
+ DCHECK(ProfileShortcutManager::IsFeatureEnabled());
+ ProfileShortcutManager* shortcut_manager =
+ g_browser_process->profile_manager()->profile_shortcut_manager();
+ DCHECK(shortcut_manager);
+
+ shortcut_manager->CreateProfileShortcut(profile_->GetPath());
+}
+
+void ManageProfileHandler::HandleRemoveProfileShortcut(
+ const base::ListValue* args) {
+ DCHECK(ProfileShortcutManager::IsFeatureEnabled());
+ ProfileShortcutManager* shortcut_manager =
+ g_browser_process->profile_manager()->profile_shortcut_manager();
+ DCHECK(shortcut_manager);
+
+ shortcut_manager->RemoveProfileShortcuts(profile_->GetPath());
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler.h b/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler.h
new file mode 100644
index 00000000000..0929d3db294
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler.h
@@ -0,0 +1,93 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_MANAGE_PROFILE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_MANAGE_PROFILE_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+class Profile;
+
+namespace settings {
+
+// Chrome personal stuff profiles manage overlay UI handler.
+class ManageProfileHandler : public settings::SettingsPageUIHandler,
+ public ProfileAttributesStorage::Observer {
+ public:
+ explicit ManageProfileHandler(Profile* profile);
+ ~ManageProfileHandler() override;
+
+ // settings::SettingsPageUIHandler:
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ // ProfileAttributesStorage::Observer:
+ void OnProfileAvatarChanged(const base::FilePath& profile_path) override;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(ManageProfileHandlerTest,
+ HandleSetProfileIconToGaiaAvatar);
+ FRIEND_TEST_ALL_PREFIXES(ManageProfileHandlerTest,
+ HandleSetProfileIconToDefaultAvatar);
+ FRIEND_TEST_ALL_PREFIXES(ManageProfileHandlerTest, HandleSetProfileName);
+ FRIEND_TEST_ALL_PREFIXES(ManageProfileHandlerTest, HandleGetAvailableIcons);
+
+ // Callback for the "getAvailableIcons" message.
+ // Sends the array of default profile icon URLs and profile names to WebUI.
+ void HandleGetAvailableIcons(const base::ListValue* args);
+
+ // Get all the available profile icons to choose from.
+ std::unique_ptr<base::ListValue> GetAvailableIcons();
+
+ // Callback for the "setProfileIconToGaiaAvatar" message.
+ void HandleSetProfileIconToGaiaAvatar(const base::ListValue* args);
+
+ // Callback for the "setProfileIconToDefaultAvatar" message.
+ void HandleSetProfileIconToDefaultAvatar(const base::ListValue* args);
+
+ // Callback for the "setProfileName" message.
+ void HandleSetProfileName(const base::ListValue* args);
+
+ // Callback for the "requestProfileShortcutStatus" message, which is called
+ // when editing an existing profile. Asks the profile shortcut manager whether
+ // the profile has shortcuts and gets the result in |OnHasProfileShortcuts()|.
+ void HandleRequestProfileShortcutStatus(const base::ListValue* args);
+
+ // Callback invoked from the profile manager indicating whether the profile
+ // being edited has any desktop shortcuts.
+ void OnHasProfileShortcuts(const std::string& callback_id,
+ bool has_shortcuts);
+
+ // Callback for the "addProfileShortcut" message, which is called when editing
+ // an existing profile and the user clicks the "Add desktop shortcut" button.
+ // Adds a desktop shortcut for the profile.
+ void HandleAddProfileShortcut(const base::ListValue* args);
+
+ // Callback for the "removeProfileShortcut" message, which is called when
+ // editing an existing profile and the user clicks the "Remove desktop
+ // shortcut" button. Removes the desktop shortcut for the profile.
+ void HandleRemoveProfileShortcut(const base::ListValue* args);
+
+ // Non-owning pointer to the associated profile.
+ Profile* profile_;
+
+ // Used to observe profile avatar updates.
+ ScopedObserver<ProfileAttributesStorage, ManageProfileHandler> observer_;
+
+ // For generating weak pointers to itself for callbacks.
+ base::WeakPtrFactory<ManageProfileHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ManageProfileHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_MANAGE_PROFILE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler_unittest.cc
new file mode 100644
index 00000000000..5a2e216865b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler_unittest.cc
@@ -0,0 +1,140 @@
+// 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 "chrome/browser/ui/webui/settings/settings_manage_profile_handler.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_web_ui.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace settings {
+
+namespace {
+
+class TestManageProfileHandler : public ManageProfileHandler {
+ public:
+ explicit TestManageProfileHandler(Profile* profile)
+ : ManageProfileHandler(profile) {}
+
+ using ManageProfileHandler::set_web_ui;
+ using ManageProfileHandler::AllowJavascript;
+};
+
+} // namespace
+
+class ManageProfileHandlerTest : public testing::Test {
+ public:
+ ManageProfileHandlerTest()
+ : profile_manager_(TestingBrowserProcess::GetGlobal()),
+ profile_(nullptr) {}
+
+ void SetUp() override {
+ ASSERT_TRUE(profile_manager_.SetUp());
+ profile_ = profile_manager_.CreateTestingProfile("Profile 1");
+
+ handler_.reset(new TestManageProfileHandler(profile_));
+ handler_->set_web_ui(&web_ui_);
+ handler()->AllowJavascript();
+ web_ui()->ClearTrackedCalls();
+ }
+
+ void VerifyIconList(const base::Value* value) {
+ const base::ListValue* icons = nullptr;
+ ASSERT_TRUE(value->GetAsList(&icons));
+
+ // Expect a non-empty list of dictionaries containing non-empty strings for
+ // profile avatar icon urls and labels.
+ EXPECT_FALSE(icons->empty());
+ for (size_t i = 0; i < icons->GetSize(); ++i) {
+ const base::DictionaryValue* icon = nullptr;
+ EXPECT_TRUE(icons->GetDictionary(i, &icon));
+ std::string icon_url;
+ EXPECT_TRUE(icon->GetString("url", &icon_url));
+ EXPECT_FALSE(icon_url.empty());
+ std::string icon_label;
+ EXPECT_TRUE(icon->GetString("label", &icon_label));
+ EXPECT_FALSE(icon_label.empty());
+ }
+ }
+
+ content::TestWebUI* web_ui() { return &web_ui_; }
+ Profile* profile() const { return profile_; }
+ TestManageProfileHandler* handler() const { return handler_.get(); }
+
+ private:
+ content::TestBrowserThreadBundle thread_bundle_;
+ TestingProfileManager profile_manager_;
+ content::TestWebUI web_ui_;
+
+ Profile* profile_;
+ std::unique_ptr<TestManageProfileHandler> handler_;
+};
+
+TEST_F(ManageProfileHandlerTest, HandleSetProfileIconToGaiaAvatar) {
+ handler()->HandleSetProfileIconToGaiaAvatar(nullptr);
+
+ PrefService* pref_service = profile()->GetPrefs();
+ EXPECT_FALSE(pref_service->GetBoolean(prefs::kProfileUsingDefaultAvatar));
+ EXPECT_TRUE(pref_service->GetBoolean(prefs::kProfileUsingGAIAAvatar));
+}
+
+TEST_F(ManageProfileHandlerTest, HandleSetProfileIconToDefaultAvatar) {
+ base::ListValue list_args;
+ list_args.AppendString("chrome://theme/IDR_PROFILE_AVATAR_15");
+ handler()->HandleSetProfileIconToDefaultAvatar(&list_args);
+
+ PrefService* pref_service = profile()->GetPrefs();
+ EXPECT_EQ(15, pref_service->GetInteger(prefs::kProfileAvatarIndex));
+ EXPECT_FALSE(pref_service->GetBoolean(prefs::kProfileUsingDefaultAvatar));
+ EXPECT_FALSE(pref_service->GetBoolean(prefs::kProfileUsingGAIAAvatar));
+}
+
+TEST_F(ManageProfileHandlerTest, HandleSetProfileName) {
+ base::ListValue list_args;
+ list_args.AppendString("New Profile Name");
+ handler()->HandleSetProfileName(&list_args);
+
+ PrefService* pref_service = profile()->GetPrefs();
+ EXPECT_EQ("New Profile Name", pref_service->GetString(prefs::kProfileName));
+}
+
+TEST_F(ManageProfileHandlerTest, HandleGetAvailableIcons) {
+ base::ListValue list_args;
+ list_args.AppendString("get-icons-callback-id");
+ handler()->HandleGetAvailableIcons(&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-icons-callback-id", callback_id);
+
+ VerifyIconList(data.arg3());
+}
+
+TEST_F(ManageProfileHandlerTest, ProfileAvatarChangedWebUIEvent) {
+ handler()->OnProfileAvatarChanged(base::FilePath());
+
+ 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("available-icons-changed", event_id);
+
+ VerifyIconList(data.arg2());
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_media_devices_selection_handler.cc b/chromium/chrome/browser/ui/webui/settings/settings_media_devices_selection_handler.cc
new file mode 100644
index 00000000000..17987af0a56
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/settings_media_devices_selection_handler.cc
@@ -0,0 +1,187 @@
+// 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 "chrome/browser/ui/webui/settings/settings_media_devices_selection_handler.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/strings/grit/extensions_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#endif
+
+namespace {
+
+const char kAudio[] = "mic";
+const char kVideo[] = "camera";
+
+} // namespace
+
+namespace settings {
+
+MediaDevicesSelectionHandler::MediaDevicesSelectionHandler(Profile* profile)
+ : profile_(profile), observer_(this) {
+}
+
+MediaDevicesSelectionHandler::~MediaDevicesSelectionHandler() {
+}
+
+void MediaDevicesSelectionHandler::OnJavascriptAllowed() {
+ // Register to the device observer list to get up-to-date device lists.
+ observer_.Add(MediaCaptureDevicesDispatcher::GetInstance());
+}
+
+void MediaDevicesSelectionHandler::OnJavascriptDisallowed() {
+ observer_.RemoveAll();
+}
+
+void MediaDevicesSelectionHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("getDefaultCaptureDevices",
+ base::Bind(&MediaDevicesSelectionHandler::GetDefaultCaptureDevices,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("setDefaultCaptureDevice",
+ base::Bind(&MediaDevicesSelectionHandler::SetDefaultCaptureDevice,
+ base::Unretained(this)));
+}
+
+void MediaDevicesSelectionHandler::OnUpdateAudioDevices(
+ const content::MediaStreamDevices& devices) {
+ UpdateDevicesMenu(AUDIO, devices);
+}
+
+void MediaDevicesSelectionHandler::OnUpdateVideoDevices(
+ const content::MediaStreamDevices& devices) {
+ UpdateDevicesMenu(VIDEO, devices);
+}
+
+void MediaDevicesSelectionHandler::GetDefaultCaptureDevices(
+ const base::ListValue* args) {
+ DCHECK_EQ(1U, args->GetSize());
+ std::string type;
+ if (!args->GetString(0, &type)) {
+ NOTREACHED();
+ return;
+ }
+ DCHECK(!type.empty());
+
+ if (type == kAudio)
+ UpdateDevicesMenuForType(AUDIO);
+ else if (type == kVideo)
+ UpdateDevicesMenuForType(VIDEO);
+}
+
+void MediaDevicesSelectionHandler::SetDefaultCaptureDevice(
+ const base::ListValue* args) {
+ DCHECK_EQ(2U, args->GetSize());
+ std::string type, device;
+ if (!(args->GetString(0, &type) && args->GetString(1, &device))) {
+ NOTREACHED();
+ return;
+ }
+
+ DCHECK(!type.empty());
+ DCHECK(!device.empty());
+
+ PrefService* prefs = profile_->GetPrefs();
+ if (type == kAudio)
+ prefs->SetString(prefs::kDefaultAudioCaptureDevice, device);
+ else if (type == kVideo)
+ prefs->SetString(prefs::kDefaultVideoCaptureDevice, device);
+ else
+ NOTREACHED();
+}
+
+void MediaDevicesSelectionHandler::UpdateDevicesMenu(
+ DeviceType type, const content::MediaStreamDevices& devices) {
+ AllowJavascript();
+
+ // Get the default device unique id from prefs.
+ PrefService* prefs = profile_->GetPrefs();
+ std::string default_device;
+ std::string device_type;
+ switch (type) {
+ case AUDIO:
+ default_device = prefs->GetString(prefs::kDefaultAudioCaptureDevice);
+ device_type = kAudio;
+ break;
+ case VIDEO:
+ default_device = prefs->GetString(prefs::kDefaultVideoCaptureDevice);
+ device_type = kVideo;
+ break;
+ }
+
+ // Build the list of devices to send to JS.
+ std::string default_id;
+ base::ListValue device_list;
+ for (size_t i = 0; i < devices.size(); ++i) {
+ std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
+ entry->SetString("name", GetDeviceDisplayName(devices[i]));
+ entry->SetString("id", devices[i].id);
+ device_list.Append(std::move(entry));
+ if (devices[i].id == default_device)
+ default_id = default_device;
+ }
+
+ // Use the first device as the default device if the preferred default device
+ // does not exist in the OS.
+ if (!devices.empty() && default_id.empty())
+ default_id = devices[0].id;
+
+ base::Value default_value(default_id);
+ base::Value type_value(device_type);
+ FireWebUIListener("updateDevicesMenu", type_value, device_list,
+ default_value);
+}
+
+std::string MediaDevicesSelectionHandler::GetDeviceDisplayName(
+ const content::MediaStreamDevice& device) const {
+ std::string facing_info;
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ switch (device.video_facing) {
+ case media::VideoFacingMode::MEDIA_VIDEO_FACING_USER:
+ facing_info = l10n_util::GetStringUTF8(IDS_CAMERA_FACING_USER);
+ break;
+ case media::VideoFacingMode::MEDIA_VIDEO_FACING_ENVIRONMENT:
+ facing_info = l10n_util::GetStringUTF8(IDS_CAMERA_FACING_ENVIRONMENT);
+ break;
+ case media::VideoFacingMode::MEDIA_VIDEO_FACING_NONE:
+ break;
+ case media::VideoFacingMode::NUM_MEDIA_VIDEO_FACING_MODES:
+ NOTREACHED();
+ break;
+ }
+#endif
+
+ if (facing_info.empty())
+ return device.name;
+ return device.name + " " + facing_info;
+}
+
+void MediaDevicesSelectionHandler::UpdateDevicesMenuForType(DeviceType type) {
+ content::MediaStreamDevices devices;
+ switch (type) {
+ case AUDIO:
+ devices = MediaCaptureDevicesDispatcher::GetInstance()->
+ GetAudioCaptureDevices();
+ break;
+ case VIDEO:
+ devices = MediaCaptureDevicesDispatcher::GetInstance()->
+ GetVideoCaptureDevices();
+ break;
+ }
+
+ UpdateDevicesMenu(type, devices);
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_media_devices_selection_handler.h b/chromium/chrome/browser/ui/webui/settings/settings_media_devices_selection_handler.h
new file mode 100644
index 00000000000..9d0863b955a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/settings_media_devices_selection_handler.h
@@ -0,0 +1,68 @@
+// Copyright (c) 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_MEDIA_DEVICES_SELECTION_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_MEDIA_DEVICES_SELECTION_HANDLER_H_
+
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "content/public/browser/web_contents.h"
+
+namespace settings {
+
+// Handler for media devices selection in content settings.
+class MediaDevicesSelectionHandler
+ : public MediaCaptureDevicesDispatcher::Observer,
+ public SettingsPageUIHandler {
+ public:
+ explicit MediaDevicesSelectionHandler(Profile* profile);
+ ~MediaDevicesSelectionHandler() override;
+
+ // SettingsPageUIHandler:
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+ void RegisterMessages() override;
+
+ // MediaCaptureDevicesDispatcher::Observer:
+ void OnUpdateAudioDevices(
+ const content::MediaStreamDevices& devices) override;
+ void OnUpdateVideoDevices(
+ const content::MediaStreamDevices& devices) override;
+
+ private:
+ enum DeviceType {
+ AUDIO,
+ VIDEO,
+ };
+
+ // Fetches the list of default capture devices.
+ void GetDefaultCaptureDevices(const base::ListValue* args);
+
+ // Sets the default audio/video capture device for media. |args| includes the
+ // media type (kAuudio/kVideo) and the unique id of the new default device
+ // that the user has chosen.
+ void SetDefaultCaptureDevice(const base::ListValue* args);
+
+ // Helpers methods to update the device menus.
+ void UpdateDevicesMenuForType(DeviceType type);
+ void UpdateDevicesMenu(DeviceType type,
+ const content::MediaStreamDevices& devices);
+
+ // Gets the human readable name of the device.
+ std::string GetDeviceDisplayName(
+ const content::MediaStreamDevice& device) const;
+
+ Profile* profile_; // Weak pointer.
+
+ ScopedObserver<MediaCaptureDevicesDispatcher,
+ MediaCaptureDevicesDispatcher::Observer> observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaDevicesSelectionHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_MEDIA_DEVICES_SELECTION_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_page_ui_handler.cc b/chromium/chrome/browser/ui/webui/settings/settings_page_ui_handler.cc
new file mode 100644
index 00000000000..facd01d9521
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/settings_page_ui_handler.cc
@@ -0,0 +1,15 @@
+// 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 "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+#include "content/public/browser/web_ui.h"
+
+namespace settings {
+
+SettingsPageUIHandler::SettingsPageUIHandler() {}
+
+SettingsPageUIHandler::~SettingsPageUIHandler() {}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_page_ui_handler.h b/chromium/chrome/browser/ui/webui/settings/settings_page_ui_handler.h
new file mode 100644
index 00000000000..7007a751424
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/settings_page_ui_handler.h
@@ -0,0 +1,30 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_PAGE_UI_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_PAGE_UI_HANDLER_H_
+
+#include "base/macros.h"
+#include "base/values.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace settings {
+
+// The base class handler of Javascript messages of settings pages.
+class SettingsPageUIHandler : public content::WebUIMessageHandler {
+ public:
+ SettingsPageUIHandler();
+ ~SettingsPageUIHandler() override;
+
+ private:
+ // SettingsPageUIHandler subclasses must be JavaScript-lifecycle safe.
+ void OnJavascriptAllowed() override = 0;
+ void OnJavascriptDisallowed() override = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(SettingsPageUIHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_PAGE_UI_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_startup_pages_handler.cc b/chromium/chrome/browser/ui/webui/settings/settings_startup_pages_handler.cc
new file mode 100644
index 00000000000..5c9142f2fbb
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/settings_startup_pages_handler.cc
@@ -0,0 +1,201 @@
+// 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.
+
+#include "chrome/browser/ui/webui/settings/settings_startup_pages_handler.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "chrome/browser/prefs/session_startup_pref.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/settings_utils.h"
+#include "chrome/common/pref_names.h"
+#include "content/public/browser/web_ui.h"
+#include "url/gurl.h"
+
+namespace settings {
+
+StartupPagesHandler::StartupPagesHandler(content::WebUI* webui)
+ : startup_custom_pages_table_model_(Profile::FromWebUI(webui)) {
+}
+
+StartupPagesHandler::~StartupPagesHandler() {
+}
+
+void StartupPagesHandler::RegisterMessages() {
+ if (Profile::FromWebUI(web_ui())->IsOffTheRecord())
+ return;
+
+ web_ui()->RegisterMessageCallback("addStartupPage",
+ base::Bind(&StartupPagesHandler::HandleAddStartupPage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("editStartupPage",
+ base::Bind(&StartupPagesHandler::HandleEditStartupPage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("onStartupPrefsPageLoad",
+ base::Bind(&StartupPagesHandler::HandleOnStartupPrefsPageLoad,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("removeStartupPage",
+ base::Bind(&StartupPagesHandler::HandleRemoveStartupPage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("setStartupPagesToCurrentPages",
+ base::Bind(&StartupPagesHandler::HandleSetStartupPagesToCurrentPages,
+ base::Unretained(this)));
+}
+
+void StartupPagesHandler::OnJavascriptAllowed() {
+ startup_custom_pages_table_model_.SetObserver(this);
+
+ PrefService* prefService = Profile::FromWebUI(web_ui())->GetPrefs();
+ SessionStartupPref pref = SessionStartupPref::GetStartupPref(prefService);
+ startup_custom_pages_table_model_.SetURLs(pref.urls);
+
+ if (pref.urls.empty())
+ pref.type = SessionStartupPref::DEFAULT;
+
+ pref_change_registrar_.Init(prefService);
+ pref_change_registrar_.Add(
+ prefs::kURLsToRestoreOnStartup,
+ base::Bind(&StartupPagesHandler::UpdateStartupPages,
+ base::Unretained(this)));
+}
+
+void StartupPagesHandler::OnJavascriptDisallowed() {
+ startup_custom_pages_table_model_.SetObserver(nullptr);
+ pref_change_registrar_.RemoveAll();
+}
+
+void StartupPagesHandler::OnModelChanged() {
+ base::ListValue startup_pages;
+ int page_count = startup_custom_pages_table_model_.RowCount();
+ std::vector<GURL> urls = startup_custom_pages_table_model_.GetURLs();
+ for (int i = 0; i < page_count; ++i) {
+ std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
+ entry->SetString("title", startup_custom_pages_table_model_.GetText(i, 0));
+ entry->SetString("url", urls[i].spec());
+ entry->SetString("tooltip",
+ startup_custom_pages_table_model_.GetTooltip(i));
+ entry->SetInteger("modelIndex", i);
+ startup_pages.Append(std::move(entry));
+ }
+
+ FireWebUIListener("update-startup-pages", startup_pages);
+}
+
+void StartupPagesHandler::OnItemsChanged(int start, int length) {
+ OnModelChanged();
+}
+
+void StartupPagesHandler::OnItemsAdded(int start, int length) {
+ OnModelChanged();
+}
+
+void StartupPagesHandler::OnItemsRemoved(int start, int length) {
+ OnModelChanged();
+}
+
+void StartupPagesHandler::HandleAddStartupPage(const base::ListValue* args) {
+ CHECK_EQ(2U, args->GetSize());
+
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+
+ std::string url_string;
+ CHECK(args->GetString(1, &url_string));
+
+ GURL url;
+ if (!settings_utils::FixupAndValidateStartupPage(url_string, &url)) {
+ ResolveJavascriptCallback(*callback_id, base::Value(false));
+ return;
+ }
+
+ int row_count = startup_custom_pages_table_model_.RowCount();
+ int index;
+ if (!args->GetInteger(1, &index) || index > row_count)
+ index = row_count;
+
+ startup_custom_pages_table_model_.Add(index, url);
+ SaveStartupPagesPref();
+ ResolveJavascriptCallback(*callback_id, base::Value(true));
+}
+
+void StartupPagesHandler::HandleEditStartupPage(const base::ListValue* args) {
+ CHECK_EQ(args->GetSize(), 3U);
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+ int index;
+ CHECK(args->GetInteger(1, &index));
+
+ if (index < 0 || index > startup_custom_pages_table_model_.RowCount()) {
+ RejectJavascriptCallback(*callback_id, base::Value());
+ NOTREACHED();
+ return;
+ }
+
+ std::string url_string;
+ CHECK(args->GetString(2, &url_string));
+
+ GURL fixed_url;
+ if (settings_utils::FixupAndValidateStartupPage(url_string, &fixed_url)) {
+ std::vector<GURL> urls = startup_custom_pages_table_model_.GetURLs();
+ urls[index] = fixed_url;
+ startup_custom_pages_table_model_.SetURLs(urls);
+ SaveStartupPagesPref();
+ ResolveJavascriptCallback(*callback_id, base::Value(true));
+ } else {
+ ResolveJavascriptCallback(*callback_id, base::Value(false));
+ }
+}
+
+void StartupPagesHandler::HandleOnStartupPrefsPageLoad(
+ const base::ListValue* args) {
+ AllowJavascript();
+}
+
+void StartupPagesHandler::HandleRemoveStartupPage(const base::ListValue* args) {
+ int selected_index;
+ if (!args->GetInteger(0, &selected_index)) {
+ NOTREACHED();
+ return;
+ }
+
+ if (selected_index < 0 ||
+ selected_index >= startup_custom_pages_table_model_.RowCount()) {
+ NOTREACHED();
+ return;
+ }
+
+ startup_custom_pages_table_model_.Remove(selected_index);
+ SaveStartupPagesPref();
+}
+
+void StartupPagesHandler::HandleSetStartupPagesToCurrentPages(
+ const base::ListValue* args) {
+ startup_custom_pages_table_model_.SetToCurrentlyOpenPages();
+ SaveStartupPagesPref();
+}
+
+void StartupPagesHandler::SaveStartupPagesPref() {
+ PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
+
+ SessionStartupPref pref = SessionStartupPref::GetStartupPref(prefs);
+ pref.urls = startup_custom_pages_table_model_.GetURLs();
+
+ if (pref.urls.empty())
+ pref.type = SessionStartupPref::DEFAULT;
+
+ SessionStartupPref::SetStartupPref(prefs, pref);
+}
+
+void StartupPagesHandler::UpdateStartupPages() {
+ const SessionStartupPref startup_pref = SessionStartupPref::GetStartupPref(
+ Profile::FromWebUI(web_ui())->GetPrefs());
+ startup_custom_pages_table_model_.SetURLs(startup_pref.urls);
+ // The change will go to the JS code in the
+ // StartupPagesHandler::OnModelChanged() method.
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/settings_startup_pages_handler.h b/chromium/chrome/browser/ui/webui/settings/settings_startup_pages_handler.h
new file mode 100644
index 00000000000..1fdafc531e6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/settings_startup_pages_handler.h
@@ -0,0 +1,77 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_STARTUP_PAGES_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_STARTUP_PAGES_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/custom_home_pages_table_model.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "ui/base/models/table_model_observer.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace content {
+class WebUI;
+}
+
+namespace settings {
+
+// Chrome browser startup settings handler.
+class StartupPagesHandler : public SettingsPageUIHandler,
+ public ui::TableModelObserver {
+ public:
+ explicit StartupPagesHandler(content::WebUI* webui);
+ ~StartupPagesHandler() override;
+
+ // SettingsPageUIHandler:
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ // ui::TableModelObserver:
+ void OnModelChanged() override;
+ void OnItemsChanged(int start, int length) override;
+ void OnItemsAdded(int start, int length) override;
+ void OnItemsRemoved(int start, int length) override;
+
+ private:
+ // Adds a startup page with the given URL after the given index.
+ void HandleAddStartupPage(const base::ListValue* args);
+
+ // Changes the startup page at the given index to the given URL.
+ void HandleEditStartupPage(const base::ListValue* args);
+
+ // Informs the code that the JS page has loaded.
+ void HandleOnStartupPrefsPageLoad(const base::ListValue* args);
+
+ // Removes the startup page at the given index.
+ void HandleRemoveStartupPage(const base::ListValue* args);
+
+ // Sets the startup page set to the current pages.
+ void HandleSetStartupPagesToCurrentPages(const base::ListValue* args);
+
+ // Stores the current state of the startup page preferences.
+ void SaveStartupPagesPref();
+
+ // Informs the that the preferences have changed. It is a callback
+ // for the pref changed registrar.
+ void UpdateStartupPages();
+
+ // Used to observe updates to the preference of the list of URLs to load
+ // on startup, which can be updated via sync.
+ PrefChangeRegistrar pref_change_registrar_;
+
+ // The set of pages to launch on startup.
+ CustomHomePagesTableModel startup_custom_pages_table_model_;
+
+ DISALLOW_COPY_AND_ASSIGN(StartupPagesHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_STARTUP_PAGES_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chromium/chrome/browser/ui/webui/settings/site_settings_handler.cc
new file mode 100644
index 00000000000..8d1981cadf5
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -0,0 +1,758 @@
+// 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 "chrome/browser/ui/webui/settings/site_settings_handler.h"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/i18n/number_formatting.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/content_settings/web_site_settings_uma_util.h"
+#include "chrome/browser/permissions/chooser_context_base.h"
+#include "chrome/browser/permissions/permission_uma_util.h"
+#include "chrome/browser/permissions/permission_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/site_settings_helper.h"
+#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/common/content_settings_types.h"
+#include "components/crx_file/id_util.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/common/page_zoom.h"
+#include "content/public/common/url_constants.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/permissions/api_permission.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "storage/browser/quota/quota_manager.h"
+#include "storage/common/quota/quota_status_code.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/text/bytes_formatting.h"
+
+#if defined(OS_CHROMEOS)
+#include "components/user_manager/user_manager.h"
+#endif
+
+namespace settings {
+
+namespace {
+
+const char kZoom[] = "zoom";
+
+// Return an appropriate API Permission ID for the given string name.
+extensions::APIPermission::APIPermission::ID APIPermissionFromGroupName(
+ std::string type) {
+ // Once there are more than two groups to consider, this should be changed to
+ // something better than if's.
+
+ if (site_settings::ContentSettingsTypeFromGroupName(type) ==
+ CONTENT_SETTINGS_TYPE_GEOLOCATION)
+ return extensions::APIPermission::APIPermission::kGeolocation;
+
+ if (site_settings::ContentSettingsTypeFromGroupName(type) ==
+ CONTENT_SETTINGS_TYPE_NOTIFICATIONS)
+ return extensions::APIPermission::APIPermission::kNotifications;
+
+ return extensions::APIPermission::APIPermission::kInvalid;
+}
+
+// Asks the |profile| for hosted apps which have the |permission| set, and
+// adds their web extent and launch URL to the |exceptions| list.
+void AddExceptionsGrantedByHostedApps(content::BrowserContext* context,
+ extensions::APIPermission::APIPermission::ID permission,
+ base::ListValue* exceptions) {
+ const extensions::ExtensionSet& extensions =
+ extensions::ExtensionRegistry::Get(context)->enabled_extensions();
+ for (extensions::ExtensionSet::const_iterator extension = extensions.begin();
+ extension != extensions.end(); ++extension) {
+ if (!(*extension)->is_hosted_app() ||
+ !(*extension)->permissions_data()->HasAPIPermission(permission))
+ continue;
+
+ 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) {
+ std::string url_pattern = pattern->GetAsString();
+ site_settings::AddExceptionForHostedApp(
+ url_pattern, *extension->get(), exceptions);
+ }
+ // Retrieve the launch URL.
+ GURL launch_url =
+ extensions::AppLaunchInfo::GetLaunchWebURL(extension->get());
+ // Skip adding the launch URL if it is part of the web extent.
+ if (web_extent.MatchesURL(launch_url))
+ continue;
+ site_settings::AddExceptionForHostedApp(
+ launch_url.spec(), *extension->get(), exceptions);
+ }
+}
+
+} // namespace
+
+
+SiteSettingsHandler::SiteSettingsHandler(Profile* profile)
+ : profile_(profile), observer_(this) {
+}
+
+SiteSettingsHandler::~SiteSettingsHandler() {
+}
+
+void SiteSettingsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "fetchUsageTotal",
+ base::Bind(&SiteSettingsHandler::HandleFetchUsageTotal,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "clearUsage",
+ base::Bind(&SiteSettingsHandler::HandleClearUsage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "fetchUsbDevices",
+ base::Bind(&SiteSettingsHandler::HandleFetchUsbDevices,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "removeUsbDevice",
+ base::Bind(&SiteSettingsHandler::HandleRemoveUsbDevice,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setDefaultValueForContentType",
+ base::Bind(&SiteSettingsHandler::HandleSetDefaultValueForContentType,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getDefaultValueForContentType",
+ base::Bind(&SiteSettingsHandler::HandleGetDefaultValueForContentType,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getExceptionList",
+ base::Bind(&SiteSettingsHandler::HandleGetExceptionList,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "resetCategoryPermissionForOrigin",
+ base::Bind(&SiteSettingsHandler::HandleResetCategoryPermissionForOrigin,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "setCategoryPermissionForOrigin",
+ base::Bind(&SiteSettingsHandler::HandleSetCategoryPermissionForOrigin,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getSiteDetails",
+ base::Bind(&SiteSettingsHandler::HandleGetSiteDetails,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "isPatternValid",
+ base::Bind(&SiteSettingsHandler::HandleIsPatternValid,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "updateIncognitoStatus",
+ base::Bind(&SiteSettingsHandler::HandleUpdateIncognitoStatus,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "fetchZoomLevels",
+ base::Bind(&SiteSettingsHandler::HandleFetchZoomLevels,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "removeZoomLevel",
+ base::Bind(&SiteSettingsHandler::HandleRemoveZoomLevel,
+ base::Unretained(this)));
+}
+
+void SiteSettingsHandler::OnJavascriptAllowed() {
+ observer_.Add(HostContentSettingsMapFactory::GetForProfile(profile_));
+ if (profile_->HasOffTheRecordProfile()) {
+ auto* map = HostContentSettingsMapFactory::GetForProfile(
+ profile_->GetOffTheRecordProfile());
+ if (!observer_.IsObserving(map))
+ observer_.Add(map);
+ }
+
+ notification_registrar_.Add(
+ this, chrome::NOTIFICATION_PROFILE_CREATED,
+ content::NotificationService::AllSources());
+ notification_registrar_.Add(
+ this, chrome::NOTIFICATION_PROFILE_DESTROYED,
+ content::NotificationService::AllSources());
+
+ // Here we only subscribe to the HostZoomMap for the default storage partition
+ // since we don't allow the user to manage the zoom levels for apps.
+ // We're only interested in zoom-levels that are persisted, since the user
+ // is given the opportunity to view/delete these in the content-settings page.
+ host_zoom_map_subscription_ =
+ content::HostZoomMap::GetDefaultForBrowserContext(profile_)
+ ->AddZoomLevelChangedCallback(
+ base::Bind(&SiteSettingsHandler::OnZoomLevelChanged,
+ base::Unretained(this)));
+}
+
+void SiteSettingsHandler::OnJavascriptDisallowed() {
+ observer_.RemoveAll();
+ notification_registrar_.RemoveAll();
+ host_zoom_map_subscription_.reset();
+}
+
+void SiteSettingsHandler::OnGetUsageInfo(
+ const storage::UsageInfoEntries& entries) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ for (const auto& entry : entries) {
+ if (entry.usage <= 0) continue;
+ if (entry.host == usage_host_) {
+ CallJavascriptFunction("settings.WebsiteUsagePrivateApi.returnUsageTotal",
+ base::Value(entry.host),
+ base::Value(ui::FormatBytes(entry.usage)),
+ base::Value(entry.type));
+ return;
+ }
+ }
+}
+
+void SiteSettingsHandler::OnUsageInfoCleared(storage::QuotaStatusCode code) {
+ if (code == storage::kQuotaStatusOk) {
+ CallJavascriptFunction("settings.WebsiteUsagePrivateApi.onUsageCleared",
+ base::Value(clearing_origin_));
+ }
+}
+
+void SiteSettingsHandler::OnContentSettingChanged(
+ const ContentSettingsPattern& primary_pattern,
+ const ContentSettingsPattern& secondary_pattern,
+ ContentSettingsType content_type,
+ std::string resource_identifier) {
+ if (!site_settings::HasRegisteredGroupName(content_type))
+ return;
+
+ if (primary_pattern.ToString().empty()) {
+ CallJavascriptFunction(
+ "cr.webUIListenerCallback",
+ base::Value("contentSettingCategoryChanged"),
+ base::Value(
+ site_settings::ContentSettingsTypeToGroupName(content_type)));
+ } else {
+ CallJavascriptFunction(
+ "cr.webUIListenerCallback",
+ base::Value("contentSettingSitePermissionChanged"),
+ base::Value(
+ site_settings::ContentSettingsTypeToGroupName(content_type)),
+ base::Value(primary_pattern.ToString()),
+ base::Value(secondary_pattern == ContentSettingsPattern::Wildcard()
+ ? ""
+ : secondary_pattern.ToString()));
+ }
+}
+
+void SiteSettingsHandler::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ switch (type) {
+ case chrome::NOTIFICATION_PROFILE_DESTROYED: {
+ Profile* profile = content::Source<Profile>(source).ptr();
+ if (!profile_->IsSameProfile(profile))
+ break;
+ SendIncognitoStatus(profile, /*was_destroyed=*/ true);
+
+ HostContentSettingsMap* settings_map =
+ HostContentSettingsMapFactory::GetForProfile(profile);
+ if (profile->IsOffTheRecord() &&
+ observer_.IsObserving(settings_map)) {
+ observer_.Remove(settings_map);
+ }
+
+ break;
+ }
+
+ case chrome::NOTIFICATION_PROFILE_CREATED: {
+ Profile* profile = content::Source<Profile>(source).ptr();
+ if (!profile_->IsSameProfile(profile))
+ break;
+ SendIncognitoStatus(profile, /*was_destroyed=*/ false);
+
+ observer_.Add(HostContentSettingsMapFactory::GetForProfile(profile));
+ break;
+ }
+ }
+}
+
+void SiteSettingsHandler::OnZoomLevelChanged(
+ const content::HostZoomMap::ZoomLevelChange& change) {
+ SendZoomLevels();
+}
+
+void SiteSettingsHandler::HandleFetchUsageTotal(
+ const base::ListValue* args) {
+ AllowJavascript();
+
+ CHECK_EQ(1U, args->GetSize());
+ std::string host;
+ CHECK(args->GetString(0, &host));
+ usage_host_ = host;
+
+ scoped_refptr<StorageInfoFetcher> storage_info_fetcher
+ = new StorageInfoFetcher(profile_);
+ storage_info_fetcher->FetchStorageInfo(
+ base::Bind(&SiteSettingsHandler::OnGetUsageInfo, base::Unretained(this)));
+}
+
+void SiteSettingsHandler::HandleClearUsage(
+ const base::ListValue* args) {
+ CHECK_EQ(2U, args->GetSize());
+ std::string origin;
+ CHECK(args->GetString(0, &origin));
+ double storage_type;
+ CHECK(args->GetDouble(1, &storage_type));
+
+ GURL url(origin);
+ if (url.is_valid()) {
+ clearing_origin_ = origin;
+
+ // Start by clearing the storage data asynchronously.
+ scoped_refptr<StorageInfoFetcher> storage_info_fetcher
+ = new StorageInfoFetcher(profile_);
+ storage_info_fetcher->ClearStorage(
+ url.host(),
+ static_cast<storage::StorageType>(static_cast<int>(storage_type)),
+ base::Bind(&SiteSettingsHandler::OnUsageInfoCleared,
+ base::Unretained(this)));
+
+ // Also clear the *local* storage data.
+ scoped_refptr<BrowsingDataLocalStorageHelper> local_storage_helper =
+ new BrowsingDataLocalStorageHelper(profile_);
+ local_storage_helper->DeleteOrigin(url);
+ }
+}
+
+void SiteSettingsHandler::HandleFetchUsbDevices(const base::ListValue* args) {
+ AllowJavascript();
+
+ CHECK_EQ(1U, args->GetSize());
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+
+ base::ListValue exceptions;
+ const site_settings::ChooserTypeNameEntry* chooser_type =
+ site_settings::ChooserTypeFromGroupName(site_settings::kGroupTypeUsb);
+ // TODO(finnur): Figure out whether incognito permissions are also needed.
+ site_settings::GetChooserExceptionsFromProfile(
+ profile_, false, *chooser_type, &exceptions);
+ ResolveJavascriptCallback(*callback_id, exceptions);
+}
+
+void SiteSettingsHandler::HandleRemoveUsbDevice(const base::ListValue* args) {
+ CHECK_EQ(3U, args->GetSize());
+
+ std::string origin_string;
+ CHECK(args->GetString(0, &origin_string));
+ GURL requesting_origin(origin_string);
+ CHECK(requesting_origin.is_valid());
+
+ std::string embedding_origin_string;
+ CHECK(args->GetString(1, &embedding_origin_string));
+ GURL embedding_origin(embedding_origin_string);
+ CHECK(embedding_origin.is_valid());
+
+ const base::DictionaryValue* object = nullptr;
+ CHECK(args->GetDictionary(2, &object));
+
+ const site_settings::ChooserTypeNameEntry* chooser_type =
+ site_settings::ChooserTypeFromGroupName(site_settings::kGroupTypeUsb);
+ ChooserContextBase* chooser_context = chooser_type->get_context(profile_);
+ chooser_context->RevokeObjectPermission(requesting_origin, embedding_origin,
+ *object);
+}
+
+void SiteSettingsHandler::HandleSetDefaultValueForContentType(
+ const base::ListValue* args) {
+ CHECK_EQ(2U, args->GetSize());
+ std::string content_type;
+ CHECK(args->GetString(0, &content_type));
+ std::string setting;
+ CHECK(args->GetString(1, &setting));
+ ContentSetting default_setting;
+ CHECK(content_settings::ContentSettingFromString(setting, &default_setting));
+
+ Profile* profile = profile_;
+#if defined(OS_CHROMEOS)
+ // ChromeOS special case: in Guest mode, settings are opened in Incognito
+ // mode so we need the original profile to actually modify settings.
+ if (user_manager::UserManager::Get()->IsLoggedInAsGuest())
+ profile = profile->GetOriginalProfile();
+#endif
+ HostContentSettingsMap* map =
+ HostContentSettingsMapFactory::GetForProfile(profile);
+ map->SetDefaultContentSetting(
+ static_cast<ContentSettingsType>(static_cast<int>(
+ site_settings::ContentSettingsTypeFromGroupName(content_type))),
+ default_setting);
+}
+
+void SiteSettingsHandler::HandleGetDefaultValueForContentType(
+ const base::ListValue* args) {
+ AllowJavascript();
+
+ CHECK_EQ(2U, args->GetSize());
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+ std::string type;
+ CHECK(args->GetString(1, &type));
+
+ ContentSettingsType content_type =
+ static_cast<ContentSettingsType>(static_cast<int>(
+ site_settings::ContentSettingsTypeFromGroupName(type)));
+ HostContentSettingsMap* map =
+ HostContentSettingsMapFactory::GetForProfile(profile_);
+
+ base::DictionaryValue category;
+ site_settings::GetContentCategorySetting(map, content_type, &category);
+ ResolveJavascriptCallback(*callback_id, category);
+}
+
+void SiteSettingsHandler::HandleGetExceptionList(const base::ListValue* args) {
+ AllowJavascript();
+
+ CHECK_EQ(2U, args->GetSize());
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+ std::string type;
+ CHECK(args->GetString(1, &type));
+ ContentSettingsType content_type =
+ static_cast<ContentSettingsType>(static_cast<int>(
+ site_settings::ContentSettingsTypeFromGroupName(type)));
+
+ std::unique_ptr<base::ListValue> exceptions(new base::ListValue);
+
+ HostContentSettingsMap* map =
+ HostContentSettingsMapFactory::GetForProfile(profile_);
+ const auto* extension_registry = extensions::ExtensionRegistry::Get(profile_);
+ AddExceptionsGrantedByHostedApps(profile_, APIPermissionFromGroupName(type),
+ exceptions.get());
+ site_settings::GetExceptionsFromHostContentSettingsMap(
+ map, content_type, extension_registry, web_ui(), /*incognito=*/false,
+ /*filter=*/nullptr, exceptions.get());
+
+ Profile* incognito = profile_->HasOffTheRecordProfile()
+ ? profile_->GetOffTheRecordProfile()
+ : nullptr;
+ // On Chrome OS in Guest mode the incognito profile is the primary profile,
+ // so do not fetch an extra copy of the same exceptions.
+ if (incognito && incognito != profile_) {
+ map = HostContentSettingsMapFactory::GetForProfile(incognito);
+ extension_registry = extensions::ExtensionRegistry::Get(incognito);
+ site_settings::GetExceptionsFromHostContentSettingsMap(
+ map, content_type, extension_registry, web_ui(), /*incognito=*/true,
+ /*filter=*/nullptr, exceptions.get());
+ }
+
+ ResolveJavascriptCallback(*callback_id, *exceptions.get());
+}
+
+void SiteSettingsHandler::HandleResetCategoryPermissionForOrigin(
+ const base::ListValue* args) {
+ CHECK_EQ(4U, args->GetSize());
+ std::string primary_pattern_string;
+ CHECK(args->GetString(0, &primary_pattern_string));
+ std::string secondary_pattern_string;
+ CHECK(args->GetString(1, &secondary_pattern_string));
+ std::string type;
+ CHECK(args->GetString(2, &type));
+ bool incognito;
+ CHECK(args->GetBoolean(3, &incognito));
+
+ ContentSettingsType content_type =
+ static_cast<ContentSettingsType>(static_cast<int>(
+ site_settings::ContentSettingsTypeFromGroupName(type)));
+
+ Profile* profile = nullptr;
+ if (incognito) {
+ if (!profile_->HasOffTheRecordProfile())
+ return;
+ profile = profile_->GetOffTheRecordProfile();
+ } else {
+ profile = profile_;
+ }
+
+ HostContentSettingsMap* map =
+ HostContentSettingsMapFactory::GetForProfile(profile);
+
+ ContentSettingsPattern primary_pattern =
+ ContentSettingsPattern::FromString(primary_pattern_string);
+ ContentSettingsPattern secondary_pattern =
+ secondary_pattern_string.empty()
+ ? ContentSettingsPattern::Wildcard()
+ : ContentSettingsPattern::FromString(secondary_pattern_string);
+ PermissionUtil::ScopedRevocationReporter scoped_revocation_reporter(
+ profile, primary_pattern, secondary_pattern, content_type,
+ PermissionSourceUI::SITE_SETTINGS);
+
+ map->SetContentSettingCustomScope(primary_pattern, secondary_pattern,
+ content_type, "", CONTENT_SETTING_DEFAULT);
+
+ WebSiteSettingsUmaUtil::LogPermissionChange(
+ content_type, ContentSetting::CONTENT_SETTING_DEFAULT);
+}
+
+void SiteSettingsHandler::HandleSetCategoryPermissionForOrigin(
+ const base::ListValue* args) {
+ CHECK_EQ(5U, args->GetSize());
+ std::string primary_pattern_string;
+ CHECK(args->GetString(0, &primary_pattern_string));
+ // TODO(dschuyler): Review whether |secondary_pattern_string| should be used.
+ std::string secondary_pattern_string;
+ CHECK(args->GetString(1, &secondary_pattern_string));
+ std::string type;
+ CHECK(args->GetString(2, &type));
+ std::string value;
+ CHECK(args->GetString(3, &value));
+ bool incognito;
+ CHECK(args->GetBoolean(4, &incognito));
+
+ ContentSettingsType content_type =
+ static_cast<ContentSettingsType>(static_cast<int>(
+ site_settings::ContentSettingsTypeFromGroupName(type)));
+ ContentSetting setting;
+ CHECK(content_settings::ContentSettingFromString(value, &setting));
+
+ Profile* profile = nullptr;
+ if (incognito) {
+ if (!profile_->HasOffTheRecordProfile())
+ return;
+ profile = profile_->GetOffTheRecordProfile();
+ } else {
+ profile = profile_;
+ }
+
+ HostContentSettingsMap* map =
+ HostContentSettingsMapFactory::GetForProfile(profile);
+
+ ContentSettingsPattern primary_pattern =
+ ContentSettingsPattern::FromString(primary_pattern_string);
+ ContentSettingsPattern secondary_pattern =
+ secondary_pattern_string.empty()
+ ? ContentSettingsPattern::Wildcard()
+ : ContentSettingsPattern::FromString(secondary_pattern_string);
+
+ PermissionUtil::ScopedRevocationReporter scoped_revocation_reporter(
+ profile, primary_pattern, secondary_pattern, content_type,
+ PermissionSourceUI::SITE_SETTINGS);
+
+ map->SetContentSettingCustomScope(primary_pattern, secondary_pattern,
+ content_type, "", setting);
+
+ WebSiteSettingsUmaUtil::LogPermissionChange(content_type, setting);
+}
+
+void SiteSettingsHandler::HandleGetSiteDetails(
+ const base::ListValue* args) {
+ AllowJavascript();
+
+ CHECK_EQ(2U, args->GetSize());
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+ std::string site;
+ CHECK(args->GetString(1, &site));
+
+ // A subset of the ContentSettingsType enum that we show in the settings UI.
+ const ContentSettingsType kSettingsDetailTypes[] = {
+ CONTENT_SETTINGS_TYPE_COOKIES,
+ CONTENT_SETTINGS_TYPE_IMAGES,
+ CONTENT_SETTINGS_TYPE_JAVASCRIPT,
+ CONTENT_SETTINGS_TYPE_PLUGINS,
+ CONTENT_SETTINGS_TYPE_POPUPS,
+ CONTENT_SETTINGS_TYPE_GEOLOCATION,
+ CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+ CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
+ CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
+ CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS,
+ CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS,
+ CONTENT_SETTINGS_TYPE_BACKGROUND_SYNC,
+ CONTENT_SETTINGS_TYPE_USB_CHOOSER_DATA,
+ CONTENT_SETTINGS_TYPE_PROMPT_NO_DECISION_COUNT,
+ };
+
+ // Create a list to be consistent with existing API, we are expecting a single
+ // element (or none).
+ std::unique_ptr<base::ListValue> exceptions(new base::ListValue);
+ for (size_t type = 0; type < arraysize(kSettingsDetailTypes); ++type) {
+ ContentSettingsType content_type = kSettingsDetailTypes[type];
+
+ HostContentSettingsMap* map =
+ HostContentSettingsMapFactory::GetForProfile(profile_);
+ const auto* extension_registry =
+ extensions::ExtensionRegistry::Get(profile_);
+ site_settings::GetExceptionsFromHostContentSettingsMap(
+ map, content_type, extension_registry, web_ui(), /*incognito=*/false,
+ /*filter=*/&site, exceptions.get());
+
+ if (profile_->HasOffTheRecordProfile()) {
+ Profile* incognito = profile_->GetOffTheRecordProfile();
+ map = HostContentSettingsMapFactory::GetForProfile(incognito);
+ extension_registry = extensions::ExtensionRegistry::Get(incognito);
+ site_settings::GetExceptionsFromHostContentSettingsMap(
+ map, content_type, extension_registry, web_ui(), /*incognito=*/true,
+ /*filter=*/&site, exceptions.get());
+ }
+ }
+
+ if (!exceptions->GetSize()) {
+ RejectJavascriptCallback(*callback_id, base::Value());
+ return;
+ }
+
+ // We only need a single response element.
+ const base::DictionaryValue* exception = nullptr;
+ exceptions->GetDictionary(0, &exception);
+ ResolveJavascriptCallback(*callback_id, *exception);
+}
+
+void SiteSettingsHandler::HandleIsPatternValid(
+ const base::ListValue* args) {
+ CHECK_EQ(2U, args->GetSize());
+ const base::Value* callback_id;
+ CHECK(args->Get(0, &callback_id));
+ std::string pattern_string;
+ CHECK(args->GetString(1, &pattern_string));
+
+ ContentSettingsPattern pattern =
+ ContentSettingsPattern::FromString(pattern_string);
+ bool valid = pattern.IsValid();
+
+ // If the input is just '*' don't allow it, even though it's a valid pattern.
+ // This changes the default setting.
+ if (pattern == ContentSettingsPattern::Wildcard())
+ valid = false;
+
+ ResolveJavascriptCallback(*callback_id, base::Value(valid));
+}
+
+void SiteSettingsHandler::HandleUpdateIncognitoStatus(
+ const base::ListValue* args) {
+ AllowJavascript();
+ SendIncognitoStatus(profile_, /*was_destroyed=*/ false);
+}
+
+void SiteSettingsHandler::SendIncognitoStatus(
+ Profile* profile, bool was_destroyed) {
+ if (!IsJavascriptAllowed())
+ return;
+
+ // When an incognito profile is destroyed, it sends out the destruction
+ // message before destroying, so HasOffTheRecordProfile for profile_ won't
+ // return false until after the profile actually been destroyed.
+ bool incognito_enabled = profile_->HasOffTheRecordProfile() &&
+ !(was_destroyed && profile == profile_->GetOffTheRecordProfile());
+
+ FireWebUIListener("onIncognitoStatusChanged", base::Value(incognito_enabled));
+}
+
+void SiteSettingsHandler::HandleFetchZoomLevels(const base::ListValue* args) {
+ AllowJavascript();
+ SendZoomLevels();
+}
+
+void SiteSettingsHandler::SendZoomLevels() {
+ if (!IsJavascriptAllowed())
+ return;
+
+ base::ListValue zoom_levels_exceptions;
+
+ content::HostZoomMap* host_zoom_map =
+ content::HostZoomMap::GetDefaultForBrowserContext(profile_);
+ content::HostZoomMap::ZoomLevelVector zoom_levels(
+ host_zoom_map->GetAllZoomLevels());
+
+ const auto* extension_registry = extensions::ExtensionRegistry::Get(profile_);
+
+ // Sort ZoomLevelChanges by host and scheme
+ // (a.com < http://a.com < https://a.com < b.com).
+ std::sort(zoom_levels.begin(), zoom_levels.end(),
+ [](const content::HostZoomMap::ZoomLevelChange& a,
+ const content::HostZoomMap::ZoomLevelChange& b) {
+ return a.host == b.host ? a.scheme < b.scheme : a.host < b.host;
+ });
+ for (const auto& zoom_level : zoom_levels) {
+ std::unique_ptr<base::DictionaryValue> exception(new base::DictionaryValue);
+ switch (zoom_level.mode) {
+ case content::HostZoomMap::ZOOM_CHANGED_FOR_HOST: {
+ std::string host = zoom_level.host;
+ if (host == content::kUnreachableWebDataURL) {
+ host =
+ l10n_util::GetStringUTF8(IDS_ZOOMLEVELS_CHROME_ERROR_PAGES_LABEL);
+ }
+ exception->SetString(site_settings::kOrigin, host);
+
+ std::string display_name = host;
+ std::string origin_for_favicon = host;
+ // As an optimization, only check hosts that could be an extension.
+ if (crx_file::id_util::IdIsValid(host)) {
+ // Look up the host as an extension, if found then it is an extension.
+ const extensions::Extension* extension =
+ extension_registry->GetExtensionById(
+ host, extensions::ExtensionRegistry::EVERYTHING);
+ if (extension) {
+ origin_for_favicon = extension->url().spec();
+ display_name = extension->name();
+ }
+ }
+ exception->SetString(site_settings::kDisplayName, display_name);
+ exception->SetString(site_settings::kOriginForFavicon,
+ origin_for_favicon);
+ break;
+ }
+ case content::HostZoomMap::ZOOM_CHANGED_FOR_SCHEME_AND_HOST:
+ // These are not stored in preferences and get cleared on next browser
+ // start. Therefore, we don't care for them.
+ continue;
+ case content::HostZoomMap::PAGE_SCALE_IS_ONE_CHANGED:
+ continue;
+ case content::HostZoomMap::ZOOM_CHANGED_TEMPORARY_ZOOM:
+ NOTREACHED();
+ }
+
+ std::string setting_string =
+ content_settings::ContentSettingToString(CONTENT_SETTING_DEFAULT);
+ DCHECK(!setting_string.empty());
+
+ exception->SetString(site_settings::kSetting, setting_string);
+
+ // Calculate the zoom percent from the factor. Round up to the nearest whole
+ // number.
+ int zoom_percent = static_cast<int>(
+ content::ZoomLevelToZoomFactor(zoom_level.zoom_level) * 100 + 0.5);
+ exception->SetString(kZoom, base::FormatPercent(zoom_percent));
+ exception->SetString(
+ site_settings::kSource, site_settings::kPreferencesSource);
+ // Append the new entry to the list and map.
+ zoom_levels_exceptions.Append(std::move(exception));
+ }
+
+ FireWebUIListener("onZoomLevelsChanged", zoom_levels_exceptions);
+}
+
+void SiteSettingsHandler::HandleRemoveZoomLevel(const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+
+ std::string origin;
+ CHECK(args->GetString(0, &origin));
+
+ if (origin ==
+ l10n_util::GetStringUTF8(IDS_ZOOMLEVELS_CHROME_ERROR_PAGES_LABEL)) {
+ origin = content::kUnreachableWebDataURL;
+ }
+
+ content::HostZoomMap* host_zoom_map;
+ host_zoom_map = content::HostZoomMap::GetDefaultForBrowserContext(profile_);
+ double default_level = host_zoom_map->GetDefaultZoomLevel();
+ host_zoom_map->SetZoomLevelForHost(origin, default_level);
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/site_settings_handler.h b/chromium/chrome/browser/ui/webui/settings/site_settings_handler.h
new file mode 100644
index 00000000000..443da67359c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/site_settings_handler.h
@@ -0,0 +1,136 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_SITE_SETTINGS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_SITE_SETTINGS_HANDLER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/scoped_observer.h"
+#include "chrome/browser/storage/storage_info_fetcher.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "components/content_settings/core/browser/content_settings_observer.h"
+#include "content/public/browser/host_zoom_map.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+class HostContentSettingsMap;
+class Profile;
+
+namespace base {
+class ListValue;
+}
+
+namespace settings {
+
+// Chrome "ContentSettings" settings page UI handler.
+class SiteSettingsHandler : public SettingsPageUIHandler,
+ public content_settings::Observer,
+ public content::NotificationObserver {
+ public:
+ explicit SiteSettingsHandler(Profile* profile);
+ ~SiteSettingsHandler() override;
+
+ // SettingsPageUIHandler:
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override;
+ void OnJavascriptDisallowed() override;
+
+ // Usage info.
+ void OnGetUsageInfo(const storage::UsageInfoEntries& entries);
+ void OnUsageInfoCleared(storage::QuotaStatusCode code);
+
+ // content_settings::Observer:
+ void OnContentSettingChanged(const ContentSettingsPattern& primary_pattern,
+ const ContentSettingsPattern& secondary_pattern,
+ ContentSettingsType content_type,
+ std::string resource_identifier) override;
+
+ // content::NotificationObserver:
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ // content::HostZoomMap subscription.
+ void OnZoomLevelChanged(const content::HostZoomMap::ZoomLevelChange& change);
+
+ private:
+ friend class SiteSettingsHandlerTest;
+ FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, GetAndSetDefault);
+ FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, Origins);
+ FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ExceptionHelpers);
+ FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, Patterns);
+ FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, Incognito);
+ FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ZoomLevels);
+
+ // Asynchronously fetches the usage for a given origin. Replies back with
+ // OnGetUsageInfo above.
+ void HandleFetchUsageTotal(const base::ListValue* args);
+
+ // Deletes the storage being used for a given host.
+ void HandleClearUsage(const base::ListValue* args);
+
+ // Handles the request for a list of all USB devices.
+ void HandleFetchUsbDevices(const base::ListValue* args);
+
+ // Removes a particular USB device permission.
+ void HandleRemoveUsbDevice(const base::ListValue* args);
+
+ // Gets and sets the default value for a particular content settings type.
+ void HandleSetDefaultValueForContentType(const base::ListValue* args);
+ void HandleGetDefaultValueForContentType(const base::ListValue* args);
+
+ // Returns the list of site exceptions for a given content settings type.
+ void HandleGetExceptionList(const base::ListValue* args);
+
+ // Handles setting and resetting of an origin permission.
+ void HandleResetCategoryPermissionForOrigin(const base::ListValue* args);
+ void HandleSetCategoryPermissionForOrigin(const base::ListValue* args);
+
+ // Return site exceptions for a single site.
+ void HandleGetSiteDetails(const base::ListValue* args);
+
+ // Returns whether a given pattern is valid.
+ void HandleIsPatternValid(const base::ListValue* args);
+
+ // Looks up whether an incognito session is active.
+ void HandleUpdateIncognitoStatus(const base::ListValue* args);
+
+ // Notifies the JS side whether incognito is enabled.
+ void SendIncognitoStatus(Profile* profile, bool was_destroyed);
+
+ // Handles the request for a list of all zoom levels.
+ void HandleFetchZoomLevels(const base::ListValue* args);
+
+ // Sends the zoom level list down to the web ui.
+ void SendZoomLevels();
+
+ // Removes a particular zoom level for a given host.
+ void HandleRemoveZoomLevel(const base::ListValue* args);
+
+ Profile* profile_;
+
+ content::NotificationRegistrar notification_registrar_;
+
+ // Keeps track of events related to zooming.
+ std::unique_ptr<content::HostZoomMap::Subscription>
+ host_zoom_map_subscription_;
+
+ // The host for which to fetch usage.
+ std::string usage_host_;
+
+ // The origin for which to clear usage.
+ std::string clearing_origin_;
+
+ // Change observer for content settings.
+ ScopedObserver<HostContentSettingsMap, content_settings::Observer> observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(SiteSettingsHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_SITE_SETTINGS_HANDLER_H_
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
new file mode 100644
index 00000000000..53be2fd2283
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
@@ -0,0 +1,418 @@
+// 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 "chrome/browser/ui/webui/settings/site_settings_handler.h"
+
+#include <memory>
+
+#include "base/test/histogram_tester.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/ui/webui/site_settings_helper.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/content_settings_types.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_web_ui.h"
+#include "extensions/common/extension_builder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/login/users/mock_user_manager.h"
+#include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
+#endif
+
+namespace {
+
+const char kCallbackId[] = "test-callback-id";
+const char kSetting[] = "setting";
+const char kSource[] = "source";
+
+}
+
+namespace settings {
+
+class SiteSettingsHandlerTest : public testing::Test {
+ public:
+ SiteSettingsHandlerTest() : handler_(&profile_) {
+#if defined(OS_CHROMEOS)
+ mock_user_manager_ = new chromeos::MockUserManager;
+ user_manager_enabler_.reset(
+ new chromeos::ScopedUserManagerEnabler(mock_user_manager_));
+#endif
+ }
+
+ void SetUp() override {
+ handler()->set_web_ui(web_ui());
+ handler()->AllowJavascript();
+ web_ui()->ClearTrackedCalls();
+ }
+
+ Profile* profile() { return &profile_; }
+ content::TestWebUI* web_ui() { return &web_ui_; }
+ SiteSettingsHandler* handler() { return &handler_; }
+
+ void ValidateDefault(const std::string& expected_setting,
+ const std::string& expected_source,
+ size_t expected_total_calls) {
+ EXPECT_EQ(expected_total_calls, web_ui()->call_data().size());
+
+ const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
+ EXPECT_EQ("cr.webUIResponse", data.function_name());
+
+ std::string callback_id;
+ ASSERT_TRUE(data.arg1()->GetAsString(&callback_id));
+ EXPECT_EQ(kCallbackId, callback_id);
+
+ bool success = false;
+ ASSERT_TRUE(data.arg2()->GetAsBoolean(&success));
+ ASSERT_TRUE(success);
+
+ const base::DictionaryValue* default_value = nullptr;
+ ASSERT_TRUE(data.arg3()->GetAsDictionary(&default_value));
+ std::string setting;
+ ASSERT_TRUE(default_value->GetString(kSetting, &setting));
+ EXPECT_EQ(expected_setting, setting);
+ std::string source;
+ if (default_value->GetString(kSource, &source))
+ EXPECT_EQ(expected_source, source);
+ }
+
+ void ValidateOrigin(
+ const std::string& expected_origin,
+ const std::string& expected_display_name,
+ const std::string& expected_embedding,
+ const std::string& expected_setting,
+ const std::string& expected_source,
+ size_t expected_total_calls) {
+ EXPECT_EQ(expected_total_calls, web_ui()->call_data().size());
+
+ const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
+ EXPECT_EQ("cr.webUIResponse", data.function_name());
+
+ std::string callback_id;
+ ASSERT_TRUE(data.arg1()->GetAsString(&callback_id));
+ EXPECT_EQ(kCallbackId, callback_id);
+ bool success = false;
+ ASSERT_TRUE(data.arg2()->GetAsBoolean(&success));
+ ASSERT_TRUE(success);
+
+ const base::ListValue* exceptions;
+ ASSERT_TRUE(data.arg3()->GetAsList(&exceptions));
+ EXPECT_EQ(1U, exceptions->GetSize());
+ const base::DictionaryValue* exception;
+ ASSERT_TRUE(exceptions->GetDictionary(0, &exception));
+ std::string origin, embedding_origin, display_name, setting, source;
+ ASSERT_TRUE(exception->GetString(site_settings::kOrigin, &origin));
+ ASSERT_EQ(expected_origin, origin);
+ ASSERT_TRUE(
+ exception->GetString(site_settings::kDisplayName, &display_name));
+ ASSERT_EQ(expected_display_name, display_name);
+ ASSERT_TRUE(exception->GetString(
+ site_settings::kEmbeddingOrigin, &embedding_origin));
+ ASSERT_EQ(expected_embedding, embedding_origin);
+ ASSERT_TRUE(exception->GetString(site_settings::kSetting, &setting));
+ ASSERT_EQ(expected_setting, setting);
+ ASSERT_TRUE(exception->GetString(site_settings::kSource, &source));
+ ASSERT_EQ(expected_source, source);
+ }
+
+ void ValidateNoOrigin(size_t expected_total_calls) {
+ EXPECT_EQ(expected_total_calls, web_ui()->call_data().size());
+
+ const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
+ EXPECT_EQ("cr.webUIResponse", data.function_name());
+
+ std::string callback_id;
+ ASSERT_TRUE(data.arg1()->GetAsString(&callback_id));
+ EXPECT_EQ(kCallbackId, callback_id);
+
+ bool success = false;
+ ASSERT_TRUE(data.arg2()->GetAsBoolean(&success));
+ ASSERT_TRUE(success);
+
+ const base::ListValue* exceptions;
+ ASSERT_TRUE(data.arg3()->GetAsList(&exceptions));
+ EXPECT_EQ(0U, exceptions->GetSize());
+ }
+
+ void ValidatePattern(bool expected_validity, size_t expected_total_calls) {
+ EXPECT_EQ(expected_total_calls, web_ui()->call_data().size());
+
+ const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
+ EXPECT_EQ("cr.webUIResponse", data.function_name());
+
+ std::string callback_id;
+ ASSERT_TRUE(data.arg1()->GetAsString(&callback_id));
+ EXPECT_EQ(kCallbackId, callback_id);
+
+ bool success = false;
+ ASSERT_TRUE(data.arg2()->GetAsBoolean(&success));
+ ASSERT_TRUE(success);
+
+ bool valid;
+ ASSERT_TRUE(data.arg3()->GetAsBoolean(&valid));
+ EXPECT_EQ(expected_validity, valid);
+ }
+
+ void ValidateIncognitoExists(
+ bool expected_incognito, size_t expected_total_calls) {
+ EXPECT_EQ(expected_total_calls, web_ui()->call_data().size());
+
+ const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
+ EXPECT_EQ("cr.webUIListenerCallback", data.function_name());
+
+ std::string callback_id;
+ ASSERT_TRUE(data.arg1()->GetAsString(&callback_id));
+ EXPECT_EQ("onIncognitoStatusChanged", callback_id);
+
+ bool incognito;
+ ASSERT_TRUE(data.arg2()->GetAsBoolean(&incognito));
+ EXPECT_EQ(expected_incognito, incognito);
+ }
+
+ void ValidateZoom(const std::string& expected_host,
+ const std::string& expected_zoom, size_t expected_total_calls) {
+ EXPECT_EQ(expected_total_calls, web_ui()->call_data().size());
+
+ const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
+ EXPECT_EQ("cr.webUIListenerCallback", data.function_name());
+
+ std::string callback_id;
+ ASSERT_TRUE(data.arg1()->GetAsString(&callback_id));
+ EXPECT_EQ("onZoomLevelsChanged", callback_id);
+
+ const base::ListValue* exceptions;
+ ASSERT_TRUE(data.arg2()->GetAsList(&exceptions));
+ if (expected_host.empty()) {
+ EXPECT_EQ(0U, exceptions->GetSize());
+ } else {
+ EXPECT_EQ(1U, exceptions->GetSize());
+
+ const base::DictionaryValue* exception;
+ ASSERT_TRUE(exceptions->GetDictionary(0, &exception));
+
+ std::string host;
+ ASSERT_TRUE(exception->GetString("origin", &host));
+ ASSERT_EQ(expected_host, host);
+
+ std::string zoom;
+ ASSERT_TRUE(exception->GetString("zoom", &zoom));
+ ASSERT_EQ(expected_zoom, zoom);
+ }
+ }
+
+ void CreateIncognitoProfile() {
+ incognito_profile_ = TestingProfile::Builder().BuildIncognito(&profile_);
+ }
+
+ void DestroyIncognitoProfile() {
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_PROFILE_DESTROYED,
+ content::Source<Profile>(static_cast<Profile*>(incognito_profile_)),
+ content::NotificationService::NoDetails());
+ profile_.SetOffTheRecordProfile(nullptr);
+ ASSERT_FALSE(profile_.HasOffTheRecordProfile());
+ incognito_profile_ = nullptr;
+ }
+
+ private:
+ content::TestBrowserThreadBundle thread_bundle_;
+ TestingProfile profile_;
+ TestingProfile* incognito_profile_;
+ content::TestWebUI web_ui_;
+ SiteSettingsHandler handler_;
+#if defined(OS_CHROMEOS)
+ chromeos::MockUserManager* mock_user_manager_; // Not owned.
+ std::unique_ptr<chromeos::ScopedUserManagerEnabler> user_manager_enabler_;
+#endif
+};
+
+TEST_F(SiteSettingsHandlerTest, GetAndSetDefault) {
+ // Test the JS -> C++ -> JS callback path for getting and setting defaults.
+ base::ListValue getArgs;
+ getArgs.AppendString(kCallbackId);
+ getArgs.AppendString("notifications");
+ handler()->HandleGetDefaultValueForContentType(&getArgs);
+ ValidateDefault("ask", "default", 1U);
+
+ // Set the default to 'Blocked'.
+ base::ListValue setArgs;
+ setArgs.AppendString("notifications");
+ setArgs.AppendString("block");
+ handler()->HandleSetDefaultValueForContentType(&setArgs);
+
+ EXPECT_EQ(2U, web_ui()->call_data().size());
+
+ // Verify that the default has been set to 'Blocked'.
+ handler()->HandleGetDefaultValueForContentType(&getArgs);
+ ValidateDefault("block", "default", 3U);
+}
+
+TEST_F(SiteSettingsHandlerTest, Origins) {
+ // Test the JS -> C++ -> JS callback path for configuring origins, by setting
+ // Google.com to blocked.
+ const std::string google("http://www.google.com");
+ const std::string kUmaBase("WebsiteSettings.Menu.PermissionChanged");
+ {
+ base::ListValue set_args;
+ set_args.AppendString(google); // Primary pattern.
+ set_args.AppendString(google); // Secondary pattern.
+ set_args.AppendString("notifications");
+ set_args.AppendString("block");
+ set_args.AppendBoolean(false); // Incognito.
+ base::HistogramTester histograms;
+ handler()->HandleSetCategoryPermissionForOrigin(&set_args);
+ EXPECT_EQ(1U, web_ui()->call_data().size());
+ histograms.ExpectTotalCount(kUmaBase, 1);
+ histograms.ExpectTotalCount(kUmaBase + ".Allowed", 0);
+ histograms.ExpectTotalCount(kUmaBase + ".Blocked", 1);
+ histograms.ExpectTotalCount(kUmaBase + ".Reset", 0);
+ }
+
+ // Verify the change was successful.
+ base::ListValue listArgs;
+ listArgs.AppendString(kCallbackId);
+ listArgs.AppendString("notifications");
+ handler()->HandleGetExceptionList(&listArgs);
+ ValidateOrigin(google, google, google, "block", "preference", 2U);
+
+ {
+ // Reset things back to how they were.
+ base::ListValue reset_args;
+ reset_args.AppendString(google);
+ reset_args.AppendString(google);
+ reset_args.AppendString("notifications");
+ reset_args.AppendBoolean(false); // Incognito.
+ base::HistogramTester histograms;
+ handler()->HandleResetCategoryPermissionForOrigin(&reset_args);
+ EXPECT_EQ(3U, web_ui()->call_data().size());
+ histograms.ExpectTotalCount(kUmaBase, 1);
+ histograms.ExpectTotalCount(kUmaBase + ".Allowed", 0);
+ histograms.ExpectTotalCount(kUmaBase + ".Blocked", 0);
+ histograms.ExpectTotalCount(kUmaBase + ".Reset", 1);
+ }
+
+ // Verify the reset was successful.
+ handler()->HandleGetExceptionList(&listArgs);
+ ValidateNoOrigin(4U);
+}
+
+TEST_F(SiteSettingsHandlerTest, ExceptionHelpers) {
+ ContentSettingsPattern pattern =
+ ContentSettingsPattern::FromString("[*.]google.com");
+ std::unique_ptr<base::DictionaryValue> exception =
+ site_settings::GetExceptionForPage(pattern, pattern, pattern.ToString(),
+ CONTENT_SETTING_BLOCK, "preference",
+ false);
+
+ std::string primary_pattern, secondary_pattern, display_name, type;
+ bool incognito;
+ CHECK(exception->GetString(site_settings::kOrigin, &primary_pattern));
+ CHECK(exception->GetString(site_settings::kDisplayName, &display_name));
+ CHECK(exception->GetString(site_settings::kEmbeddingOrigin,
+ &secondary_pattern));
+ CHECK(exception->GetString(site_settings::kSetting, &type));
+ CHECK(exception->GetBoolean(site_settings::kIncognito, &incognito));
+
+ base::ListValue args;
+ args.AppendString(primary_pattern);
+ args.AppendString(secondary_pattern);
+ args.AppendString("notifications"); // Chosen arbitrarily.
+ args.AppendString(type);
+ args.AppendBoolean(incognito);
+
+ // We don't need to check the results. This is just to make sure it doesn't
+ // crash on the input.
+ handler()->HandleSetCategoryPermissionForOrigin(&args);
+
+ scoped_refptr<const extensions::Extension> extension;
+ extension = extensions::ExtensionBuilder()
+ .SetManifest(extensions::DictionaryBuilder()
+ .Set("name", "Test extension")
+ .Set("version", "1.0.0")
+ .Set("manifest_version", 2)
+ .Build())
+ .SetID("ahfgeienlihckogmohjhadlkjgocpleb")
+ .Build();
+
+ std::unique_ptr<base::ListValue> exceptions(new base::ListValue);
+ site_settings::AddExceptionForHostedApp(
+ "[*.]google.com", *extension.get(), exceptions.get());
+
+ const base::DictionaryValue* dictionary;
+ CHECK(exceptions->GetDictionary(0, &dictionary));
+ CHECK(dictionary->GetString(site_settings::kOrigin, &primary_pattern));
+ CHECK(dictionary->GetString(site_settings::kDisplayName, &display_name));
+ CHECK(dictionary->GetString(site_settings::kEmbeddingOrigin,
+ &secondary_pattern));
+ CHECK(dictionary->GetString(site_settings::kSetting, &type));
+ CHECK(dictionary->GetBoolean(site_settings::kIncognito, &incognito));
+
+ // Again, don't need to check the results.
+ handler()->HandleSetCategoryPermissionForOrigin(&args);
+}
+
+TEST_F(SiteSettingsHandlerTest, Patterns) {
+ base::ListValue args;
+ std::string pattern("[*.]google.com");
+ args.AppendString(kCallbackId);
+ args.AppendString(pattern);
+ handler()->HandleIsPatternValid(&args);
+ ValidatePattern(true, 1U);
+
+ base::ListValue invalid;
+ std::string bad_pattern(";");
+ invalid.AppendString(kCallbackId);
+ invalid.AppendString(bad_pattern);
+ handler()->HandleIsPatternValid(&invalid);
+ ValidatePattern(false, 2U);
+
+ // The wildcard pattern ('*') is a valid pattern, but not allowed to be
+ // entered in site settings as it changes the default setting.
+ // (crbug.com/709539).
+ base::ListValue invalid_wildcard;
+ std::string bad_pattern_wildcard("*");
+ invalid_wildcard.AppendString(kCallbackId);
+ invalid_wildcard.AppendString(bad_pattern_wildcard);
+ handler()->HandleIsPatternValid(&invalid_wildcard);
+ ValidatePattern(false, 3U);
+}
+
+TEST_F(SiteSettingsHandlerTest, Incognito) {
+ base::ListValue args;
+ handler()->HandleUpdateIncognitoStatus(&args);
+ ValidateIncognitoExists(false, 1U);
+
+ CreateIncognitoProfile();
+ ValidateIncognitoExists(true, 2U);
+
+ DestroyIncognitoProfile();
+ ValidateIncognitoExists(false, 3U);
+}
+
+TEST_F(SiteSettingsHandlerTest, ZoomLevels) {
+ std::string host("http://www.google.com");
+ double zoom_level = 1.1;
+
+ content::HostZoomMap* host_zoom_map =
+ content::HostZoomMap::GetDefaultForBrowserContext(profile());
+ host_zoom_map->SetZoomLevelForHost(host, zoom_level);
+ ValidateZoom(host, "122%", 1U);
+
+ base::ListValue args;
+ handler()->HandleFetchZoomLevels(&args);
+ ValidateZoom(host, "122%", 2U);
+
+ args.AppendString("http://www.google.com");
+ handler()->HandleRemoveZoomLevel(&args);
+ ValidateZoom("", "", 3U);
+
+ double default_level = host_zoom_map->GetDefaultZoomLevel();
+ double level = host_zoom_map->GetZoomLevelForHostAndScheme("http", host);
+ EXPECT_EQ(default_level, level);
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/system_handler.cc b/chromium/chrome/browser/ui/webui/settings/system_handler.cc
new file mode 100644
index 00000000000..c87ab9fe623
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/system_handler.cc
@@ -0,0 +1,40 @@
+// 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 "chrome/browser/ui/webui/settings/system_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/gpu/gpu_mode_manager.h"
+#include "chrome/browser/ui/webui/settings_utils.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace settings {
+
+SystemHandler::SystemHandler() {}
+
+SystemHandler::~SystemHandler() {}
+
+// static
+void SystemHandler::AddLoadTimeData(content::WebUIDataSource* data_source) {
+ data_source->AddBoolean("hardwareAccelerationEnabledAtStartup",
+ g_browser_process->gpu_mode_manager()->initial_gpu_mode_pref());
+}
+
+void SystemHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "showProxySettings", base::Bind(&SystemHandler::HandleShowProxySettings,
+ base::Unretained(this)));
+}
+
+void SystemHandler::HandleShowProxySettings(const base::ListValue* /*args*/) {
+ base::RecordAction(base::UserMetricsAction("Options_ShowProxySettings"));
+ settings_utils::ShowNetworkProxySettings(web_ui()->GetWebContents());
+}
+
+} // namespace settings
diff --git a/chromium/chrome/browser/ui/webui/settings/system_handler.h b/chromium/chrome/browser/ui/webui/settings/system_handler.h
new file mode 100644
index 00000000000..8bfadf746c8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings/system_handler.h
@@ -0,0 +1,43 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_SYSTEM_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_SYSTEM_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace content {
+class WebUIDataSource;
+}
+
+namespace settings {
+
+class SystemHandler : public SettingsPageUIHandler {
+ public:
+ SystemHandler();
+ ~SystemHandler() override;
+
+ // Populates handler-specific loadTimeData values used by the system page.
+ static void AddLoadTimeData(content::WebUIDataSource* data_source);
+
+ // SettingsPageUIHandler:
+ void RegisterMessages() override;
+ void OnJavascriptAllowed() override {}
+ void OnJavascriptDisallowed() override {}
+
+ private:
+ // Handler for the "showProxySettings" message. No args.
+ void HandleShowProxySettings(const base::ListValue* /*args*/);
+
+ DISALLOW_COPY_AND_ASSIGN(SystemHandler);
+};
+
+} // namespace settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_SYSTEM_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/settings_utils.cc b/chromium/chrome/browser/ui/webui/settings_utils.cc
new file mode 100644
index 00000000000..687fe4ad4c4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings_utils.cc
@@ -0,0 +1,29 @@
+// 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 "chrome/browser/ui/webui/settings_utils.h"
+
+#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/grit/theme_resources.h"
+#include "components/url_formatter/url_fixer.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "url/gurl.h"
+
+namespace settings_utils {
+
+bool FixupAndValidateStartupPage(const std::string& url_string,
+ GURL* fixed_url) {
+ GURL url = url_formatter::FixupURL(url_string, std::string());
+ bool valid = url.is_valid() && !extensions::ExtensionTabUtil::IsKillURL(url);
+ if (valid && fixed_url)
+ fixed_url->Swap(&url);
+ return valid;
+}
+
+base::RefCountedMemory* GetFaviconResourceBytes(ui::ScaleFactor scale_factor) {
+ return ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
+ IDR_SETTINGS_FAVICON, scale_factor);
+}
+
+} // namespace settings_utils
diff --git a/chromium/chrome/browser/ui/webui/settings_utils.h b/chromium/chrome/browser/ui/webui/settings_utils.h
new file mode 100644
index 00000000000..c32310e6800
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings_utils.h
@@ -0,0 +1,40 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_UTILS_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_UTILS_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "ui/base/resource/scale_factor.h"
+
+class GURL;
+
+namespace base {
+class RefCountedMemory;
+}
+
+namespace content {
+class WebContents;
+}
+
+namespace settings_utils {
+
+// Invoke UI for network proxy settings.
+void ShowNetworkProxySettings(content::WebContents* web_contents);
+
+// Invoke UI for SSL certificates.
+void ShowManageSSLCertificates(content::WebContents* web_contents);
+
+// Returns whether |url_string| is a valid startup page. |fixed_url| is set to
+// the fixed up, valid URL if not null.
+bool FixupAndValidateStartupPage(const std::string& url_string,
+ GURL* fixed_url);
+
+base::RefCountedMemory* GetFaviconResourceBytes(ui::ScaleFactor scale_factor);
+
+} // namespace settings_utils
+
+#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_UTILS_H_
diff --git a/chromium/chrome/browser/ui/webui/settings_utils_linux.cc b/chromium/chrome/browser/ui/webui/settings_utils_linux.cc
new file mode 100644
index 00000000000..f72fa4d93f5
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings_utils_linux.cc
@@ -0,0 +1,145 @@
+// 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 "chrome/browser/ui/webui/settings_utils.h"
+
+#include <stddef.h>
+
+#include "base/bind.h"
+#include "base/environment.h"
+#include "base/files/file_util.h"
+#include "base/nix/xdg_util.h"
+#include "base/process/launch.h"
+#include "build/build_config.h"
+#include "chrome/browser/tab_contents/tab_util.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+
+using content::BrowserThread;
+using content::OpenURLParams;
+using content::Referrer;
+
+namespace {
+
+// Command used to configure GNOME 2 proxy settings.
+const char* const kGNOME2ProxyConfigCommand[] = {"gnome-network-properties",
+ nullptr};
+// In GNOME 3, we might need to run gnome-control-center instead. We try this
+// only after gnome-network-properties is not found, because older GNOME also
+// has this but it doesn't do the same thing. See below where we use it.
+const char* const kGNOME3ProxyConfigCommand[] = {"gnome-control-center",
+ "network", nullptr};
+// KDE3, 4, and 5 are only slightly different, but incompatible. Go figure.
+const char* const kKDE3ProxyConfigCommand[] = {"kcmshell", "proxy", nullptr};
+const char* const kKDE4ProxyConfigCommand[] = {"kcmshell4", "proxy", nullptr};
+const char* const kKDE5ProxyConfigCommand[] = {"kcmshell5", "proxy", nullptr};
+
+// The URL for Linux proxy configuration help when not running under a
+// supported desktop environment.
+const char kLinuxProxyConfigUrl[] = "about:linux-proxy-config";
+
+// Show the proxy config URL in the given tab.
+void ShowLinuxProxyConfigUrl(int render_process_id, int render_view_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ std::unique_ptr<base::Environment> env(base::Environment::Create());
+ const char* name = base::nix::GetDesktopEnvironmentName(env.get());
+ if (name)
+ LOG(ERROR) << "Could not find " << name << " network settings in $PATH";
+ OpenURLParams params(GURL(kLinuxProxyConfigUrl), Referrer(),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui::PAGE_TRANSITION_LINK, false);
+
+ content::WebContents* web_contents =
+ tab_util::GetWebContentsByID(render_process_id, render_view_id);
+ if (web_contents)
+ web_contents->OpenURL(params);
+}
+
+// Start the given proxy configuration utility.
+bool StartProxyConfigUtil(const char* const command[]) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ // base::LaunchProcess() returns true ("success") if the fork()
+ // succeeds, but not necessarily the exec(). We'd like to be able to
+ // use StartProxyConfigUtil() to search possible options and stop on
+ // success, so we search $PATH first to predict whether the exec is
+ // expected to succeed.
+ std::unique_ptr<base::Environment> env(base::Environment::Create());
+ if (!base::ExecutableExistsInPath(env.get(), command[0]))
+ return false;
+
+ std::vector<std::string> argv;
+ for (size_t i = 0; command[i]; ++i)
+ argv.push_back(command[i]);
+ base::Process process = base::LaunchProcess(argv, base::LaunchOptions());
+ if (!process.IsValid()) {
+ LOG(ERROR) << "StartProxyConfigUtil failed to start " << command[0];
+ return false;
+ }
+ base::EnsureProcessGetsReaped(process.Pid());
+ return true;
+}
+
+// Detect, and if possible, start the appropriate proxy config utility. On
+// failure to do so, show the Linux proxy config URL in a new tab instead.
+void DetectAndStartProxyConfigUtil(int render_process_id,
+ int render_view_id) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ std::unique_ptr<base::Environment> env(base::Environment::Create());
+
+ bool launched = false;
+ switch (base::nix::GetDesktopEnvironment(env.get())) {
+ case base::nix::DESKTOP_ENVIRONMENT_GNOME:
+ case base::nix::DESKTOP_ENVIRONMENT_UNITY: {
+ launched = StartProxyConfigUtil(kGNOME2ProxyConfigCommand);
+ if (!launched) {
+ // We try this second, even though it's the newer way, because this
+ // command existed in older versions of GNOME, but it didn't do the
+ // same thing. The older command is gone though, so this should do
+ // the right thing. (Also some distributions have blurred the lines
+ // between GNOME 2 and 3, so we can't necessarily detect what the
+ // right thing is based on indications of which version we have.)
+ launched = StartProxyConfigUtil(kGNOME3ProxyConfigCommand);
+ }
+ break;
+ }
+
+ case base::nix::DESKTOP_ENVIRONMENT_KDE3:
+ launched = StartProxyConfigUtil(kKDE3ProxyConfigCommand);
+ break;
+
+ case base::nix::DESKTOP_ENVIRONMENT_KDE4:
+ launched = StartProxyConfigUtil(kKDE4ProxyConfigCommand);
+ break;
+
+ case base::nix::DESKTOP_ENVIRONMENT_KDE5:
+ launched = StartProxyConfigUtil(kKDE5ProxyConfigCommand);
+ break;
+
+ case base::nix::DESKTOP_ENVIRONMENT_XFCE:
+ case base::nix::DESKTOP_ENVIRONMENT_OTHER:
+ break;
+ }
+
+ if (launched)
+ return;
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::BindOnce(&ShowLinuxProxyConfigUrl,
+ render_process_id, render_view_id));
+}
+
+} // namespace
+
+namespace settings_utils {
+
+void ShowNetworkProxySettings(content::WebContents* web_contents) {
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::BindOnce(&DetectAndStartProxyConfigUtil,
+ web_contents->GetRenderProcessHost()->GetID(),
+ web_contents->GetRenderViewHost()->GetRoutingID()));
+}
+
+} // namespace settings_utils
diff --git a/chromium/chrome/browser/ui/webui/settings_utils_mac.mm b/chromium/chrome/browser/ui/webui/settings_utils_mac.mm
new file mode 100644
index 00000000000..1f68f49dae7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings_utils_mac.mm
@@ -0,0 +1,44 @@
+// 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.
+
+#import <Cocoa/Cocoa.h>
+
+#include "chrome/browser/ui/webui/settings_utils.h"
+
+#include "base/logging.h"
+#include "base/mac/mac_logging.h"
+#include "base/mac/scoped_aedesc.h"
+
+namespace settings_utils {
+
+void ShowNetworkProxySettings(content::WebContents* web_contents) {
+ NSArray* itemsToOpen = [NSArray arrayWithObject:[NSURL fileURLWithPath:
+ @"/System/Library/PreferencePanes/Network.prefPane"]];
+
+ const char* proxyPrefCommand = "Proxies";
+ base::mac::ScopedAEDesc<> openParams;
+ OSStatus status = AECreateDesc('ptru',
+ proxyPrefCommand,
+ strlen(proxyPrefCommand),
+ openParams.OutPointer());
+ OSSTATUS_LOG_IF(ERROR, status != noErr, status)
+ << "Failed to create open params";
+
+ LSLaunchURLSpec launchSpec = { 0 };
+ launchSpec.itemURLs = (CFArrayRef)itemsToOpen;
+ launchSpec.passThruParams = openParams;
+ launchSpec.launchFlags = kLSLaunchAsync | kLSLaunchDontAddToRecents;
+ LSOpenFromURLSpec(&launchSpec, NULL);
+}
+
+void ShowManageSSLCertificates(content::WebContents* web_contents) {
+ NSString* const kKeychainBundleId = @"com.apple.keychainaccess";
+ [[NSWorkspace sharedWorkspace]
+ launchAppWithBundleIdentifier:kKeychainBundleId
+ options:0L
+ additionalEventParamDescriptor:nil
+ launchIdentifier:nil];
+}
+
+} // namespace settings_utils
diff --git a/chromium/chrome/browser/ui/webui/settings_utils_unittest.cc b/chromium/chrome/browser/ui/webui/settings_utils_unittest.cc
new file mode 100644
index 00000000000..477cbf10728
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings_utils_unittest.cc
@@ -0,0 +1,28 @@
+// 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 "chrome/browser/ui/webui/settings_utils.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace settings_utils {
+
+TEST(SettingsUtilsTest, FixupAndValidateStartupPage) {
+ EXPECT_FALSE(FixupAndValidateStartupPage(std::string(), nullptr));
+ EXPECT_FALSE(FixupAndValidateStartupPage(" ", nullptr));
+ EXPECT_FALSE(FixupAndValidateStartupPage("^&*@)^)", nullptr));
+ EXPECT_FALSE(FixupAndValidateStartupPage("chrome://quit", nullptr));
+
+ EXPECT_TRUE(FixupAndValidateStartupPage("facebook.com", nullptr));
+ EXPECT_TRUE(FixupAndValidateStartupPage("http://reddit.com", nullptr));
+ EXPECT_TRUE(FixupAndValidateStartupPage("https://google.com", nullptr));
+ EXPECT_TRUE(FixupAndValidateStartupPage("chrome://apps", nullptr));
+
+ GURL fixed_url;
+ EXPECT_TRUE(FixupAndValidateStartupPage("about:settings", &fixed_url));
+ EXPECT_EQ("chrome://settings/", fixed_url.spec());
+}
+
+} // namespace settings_utils
diff --git a/chromium/chrome/browser/ui/webui/settings_utils_win.cc b/chromium/chrome/browser/ui/webui/settings_utils_win.cc
new file mode 100644
index 00000000000..8380d43ef0e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/settings_utils_win.cc
@@ -0,0 +1,115 @@
+// 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/ui/webui/settings_utils.h"
+
+#include <windows.h>
+#include <cryptuiapi.h>
+#include <shellapi.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/browser_process.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/shell_dialogs/base_shell_dialog_win.h"
+#include "ui/views/win/hwnd_util.h"
+
+using content::BrowserThread;
+
+namespace settings_utils {
+
+namespace {
+
+// Shows a Windows certificate management dialog on the dialog thread.
+class ManageCertificatesDialog : public ui::BaseShellDialogImpl {
+ public:
+ ManageCertificatesDialog() {}
+
+ // Shows the dialog and calls |callback| when the dialog closes. The caller
+ // must ensure the ManageCertificatesDialog remains valid until then.
+ void Show(HWND parent, const base::Closure& callback) {
+ if (IsRunningDialogForOwner(parent)) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+ return;
+ }
+
+ RunState run_state = BeginRun(parent);
+ run_state.dialog_thread->task_runner()->PostTaskAndReply(
+ FROM_HERE, base::Bind(&ManageCertificatesDialog::ShowOnDialogThread,
+ base::Unretained(this), run_state),
+ base::Bind(&ManageCertificatesDialog::OnDialogClosed,
+ base::Unretained(this), run_state, callback));
+ }
+
+ private:
+ void ShowOnDialogThread(const RunState& run_state) {
+ CRYPTUI_CERT_MGR_STRUCT cert_mgr = {0};
+ cert_mgr.dwSize = sizeof(CRYPTUI_CERT_MGR_STRUCT);
+ cert_mgr.hwndParent = run_state.owner;
+ ::CryptUIDlgCertMgr(&cert_mgr);
+ }
+
+ void OnDialogClosed(const RunState& run_state,
+ const base::Closure& callback) {
+ EndRun(run_state);
+ // May delete |this|.
+ callback.Run();
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ManageCertificatesDialog);
+};
+
+} // namespace
+
+// Callback that opens the Internet Options control panel dialog with the
+// Connections tab selected.
+void OpenConnectionDialogCallback() {
+ // Using rundll32 seems better than LaunchConnectionDialog which causes a
+ // new dialog to be made for each call. rundll32 uses the same global
+ // dialog and it seems to share with the shortcut in control panel.
+ base::FilePath rundll32;
+ PathService::Get(base::DIR_SYSTEM, &rundll32);
+ rundll32 = rundll32.AppendASCII("rundll32.exe");
+
+ base::FilePath shell32dll;
+ PathService::Get(base::DIR_SYSTEM, &shell32dll);
+ shell32dll = shell32dll.AppendASCII("shell32.dll");
+
+ base::FilePath inetcpl;
+ PathService::Get(base::DIR_SYSTEM, &inetcpl);
+ inetcpl = inetcpl.AppendASCII("inetcpl.cpl,,4");
+
+ std::wstring args(shell32dll.value());
+ args.append(L",Control_RunDLL ");
+ args.append(inetcpl.value());
+
+ ShellExecute(NULL, L"open", rundll32.value().c_str(), args.c_str(), NULL,
+ SW_SHOWNORMAL);
+}
+
+void ShowNetworkProxySettings(content::WebContents* web_contents) {
+ DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::FILE));
+ BrowserThread::PostTask(BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&OpenConnectionDialogCallback));
+}
+
+void ShowManageSSLCertificates(content::WebContents* web_contents) {
+ HWND parent =
+ views::HWNDForNativeWindow(web_contents->GetTopLevelNativeWindow());
+
+ ManageCertificatesDialog* dialog = new ManageCertificatesDialog;
+ dialog->Show(
+ parent,
+ base::Bind(&base::DeletePointer<ManageCertificatesDialog>, dialog));
+}
+
+} // namespace settings_utils
diff --git a/chromium/chrome/browser/ui/webui/signin/OWNERS b/chromium/chrome/browser/ui/webui/signin/OWNERS
new file mode 100644
index 00000000000..af356ecab22
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/OWNERS
@@ -0,0 +1,7 @@
+achuith@chromium.org
+anthonyvd@chromium.org
+msarda@chromium.org
+rogerta@chromium.org
+xiyuan@chromium.org
+
+# COMPONENT: Services>SignIn
diff --git a/chromium/chrome/browser/ui/webui/signin/inline_login_handler.cc b/chromium/chrome/browser/ui/webui/signin/inline_login_handler.cc
new file mode 100644
index 00000000000..72d748ea7ec
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/inline_login_handler.cc
@@ -0,0 +1,292 @@
+// 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/signin/inline_login_handler.h"
+
+#include <limits.h>
+
+#include "base/bind.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/signin/gaia_auth_extension_loader.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/signin_promo.h"
+#include "chrome/browser/ui/browser_navigator.h"
+#include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/browser/ui/signin_view_controller_delegate.h"
+#include "chrome/browser/ui/user_manager.h"
+#include "chrome/browser/ui/webui/signin/signin_utils.h"
+#include "chrome/common/pref_names.h"
+#include "components/metrics/metrics_pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/common/signin_pref_names.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "net/base/url_util.h"
+
+InlineLoginHandler::InlineLoginHandler() : weak_ptr_factory_(this) {}
+
+InlineLoginHandler::~InlineLoginHandler() {}
+
+void InlineLoginHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("initialize",
+ base::Bind(&InlineLoginHandler::HandleInitializeMessage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("completeLogin",
+ base::Bind(&InlineLoginHandler::HandleCompleteLoginMessage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "switchToFullTab",
+ base::Bind(&InlineLoginHandler::HandleSwitchToFullTabMessage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("navigationButtonClicked",
+ base::Bind(&InlineLoginHandler::HandleNavigationButtonClicked,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("dialogClose",
+ base::Bind(&InlineLoginHandler::HandleDialogClose,
+ base::Unretained(this)));
+}
+
+void InlineLoginHandler::HandleInitializeMessage(const base::ListValue* args) {
+ AllowJavascript();
+ content::WebContents* contents = web_ui()->GetWebContents();
+ content::StoragePartition* partition =
+ content::BrowserContext::GetStoragePartitionForSite(
+ contents->GetBrowserContext(), signin::GetSigninPartitionURL());
+ if (partition) {
+ const GURL& current_url = web_ui()->GetWebContents()->GetURL();
+
+ // If the kSignInPromoQueryKeyForceKeepData param is missing, or if it is
+ // present and its value is zero, this means we don't want to keep the
+ // the data.
+ std::string value;
+ if (!net::GetValueForKeyInQuery(current_url,
+ signin::kSignInPromoQueryKeyForceKeepData,
+ &value) ||
+ value == "0") {
+ partition->ClearData(
+ content::StoragePartition::REMOVE_DATA_MASK_ALL,
+ content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
+ GURL(),
+ content::StoragePartition::OriginMatcherFunction(),
+ base::Time(),
+ base::Time::Max(),
+ base::Bind(&InlineLoginHandler::ContinueHandleInitializeMessage,
+ weak_ptr_factory_.GetWeakPtr()));
+ } else {
+ ContinueHandleInitializeMessage();
+ }
+ }
+}
+
+void InlineLoginHandler::RecordSigninUserActionForAccessPoint(
+ signin_metrics::AccessPoint access_point) {
+ switch (access_point) {
+ case signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromStartPage"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_NTP_LINK:
+ base::RecordAction(base::UserMetricsAction("Signin_Signin_FromNTP"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_MENU:
+ base::RecordAction(base::UserMetricsAction("Signin_Signin_FromMenu"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS:
+ base::RecordAction(base::UserMetricsAction("Signin_Signin_FromSettings"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_SUPERVISED_USER:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromSupervisedUser"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromExtensionInstallBubble"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_EXTENSIONS:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromExtensions"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_APPS_PAGE_LINK:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromAppsPageLink"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromBookmarkBubble"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromBookmarkManager"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromAvatarBubbleSignin"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromUserManager"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_DEVICES_PAGE:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromDevicesPage"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_CLOUD_PRINT:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromCloudPrint"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_CONTENT_AREA:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromContentArea"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromSigninPromo"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromRecentTabs"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromUnknownAccessPoint"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromPasswordBubble"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromAutofillDropdown"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromNTPContentSuggestions"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromReSigninInfobar"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_TAB_SWITCHER:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_Signin_FromTabSwitcher"));
+ break;
+ case signin_metrics::AccessPoint::ACCESS_POINT_MAX:
+ NOTREACHED();
+ break;
+ }
+}
+
+void InlineLoginHandler::ContinueHandleInitializeMessage() {
+ base::DictionaryValue params;
+
+ const std::string& app_locale = g_browser_process->GetApplicationLocale();
+ params.SetString("hl", app_locale);
+ GaiaUrls* gaiaUrls = GaiaUrls::GetInstance();
+ params.SetString("gaiaUrl", gaiaUrls->gaia_url().spec());
+ params.SetInteger("authMode", InlineLoginHandler::kDesktopAuthMode);
+
+ const GURL& current_url = web_ui()->GetWebContents()->GetURL();
+ signin_metrics::AccessPoint access_point =
+ signin::GetAccessPointForPromoURL(current_url);
+ signin_metrics::Reason reason =
+ signin::GetSigninReasonForPromoURL(current_url);
+
+ if (reason != signin_metrics::Reason::REASON_REAUTHENTICATION &&
+ reason != signin_metrics::Reason::REASON_UNLOCK &&
+ reason != signin_metrics::Reason::REASON_ADD_SECONDARY_ACCOUNT) {
+ signin_metrics::LogSigninAccessPointStarted(access_point);
+ RecordSigninUserActionForAccessPoint(access_point);
+ base::RecordAction(base::UserMetricsAction("Signin_SigninPage_Loading"));
+ params.SetBoolean("isLoginPrimaryAccount", true);
+ }
+
+ params.SetString("continueUrl", signin::GetLandingURL(access_point).spec());
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ std::string default_email;
+ if (reason == signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT) {
+ default_email =
+ profile->GetPrefs()->GetString(prefs::kGoogleServicesLastUsername);
+ } else {
+ if (!net::GetValueForKeyInQuery(current_url, "email", &default_email))
+ default_email.clear();
+ }
+ if (!default_email.empty())
+ params.SetString("email", default_email);
+
+ std::string is_constrained;
+ net::GetValueForKeyInQuery(
+ current_url, signin::kSignInPromoQueryKeyConstrained, &is_constrained);
+ if (!is_constrained.empty())
+ params.SetString(signin::kSignInPromoQueryKeyConstrained, is_constrained);
+
+ // TODO(rogerta): this needs to be passed on to gaia somehow.
+ std::string read_only_email;
+ net::GetValueForKeyInQuery(current_url, "readOnlyEmail", &read_only_email);
+ params.SetBoolean("readOnlyEmail", !read_only_email.empty());
+
+ SetExtraInitParams(params);
+ CallJavascriptFunction("inline.login.loadAuthExtension", params);
+}
+
+void InlineLoginHandler::HandleCompleteLoginMessage(
+ const base::ListValue* args) {
+ CompleteLogin(args);
+}
+
+void InlineLoginHandler::HandleSwitchToFullTabMessage(
+ const base::ListValue* args) {
+ std::string url_str;
+ CHECK(args->GetString(0, &url_str));
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ GURL main_frame_url(web_ui()->GetWebContents()->GetURL());
+
+ // Adds extra parameters to the signin URL so that Chrome will close the tab
+ // and show the account management view of the avatar menu upon completion.
+ main_frame_url = net::AppendOrReplaceQueryParameter(
+ main_frame_url, signin::kSignInPromoQueryKeyAutoClose, "1");
+ main_frame_url = net::AppendOrReplaceQueryParameter(
+ main_frame_url, signin::kSignInPromoQueryKeyShowAccountManagement, "1");
+ main_frame_url = net::AppendOrReplaceQueryParameter(
+ main_frame_url, signin::kSignInPromoQueryKeyForceKeepData, "1");
+
+ chrome::NavigateParams params(
+ profile,
+ net::AppendOrReplaceQueryParameter(
+ main_frame_url, signin::kSignInPromoQueryKeyConstrained, "0"),
+ ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
+ chrome::Navigate(&params);
+
+ CloseDialogFromJavascript();
+}
+
+void InlineLoginHandler::HandleNavigationButtonClicked(
+ const base::ListValue* args) {
+ Browser* browser = signin::GetDesktopBrowser(web_ui());
+ DCHECK(browser);
+
+ browser->signin_view_controller()->delegate()->PerformNavigation();
+}
+
+void InlineLoginHandler::HandleDialogClose(const base::ListValue* args) {
+ Browser* browser = signin::GetDesktopBrowser(web_ui());
+ // If the dialog was opened in the User Manager browser will be null here.
+ if (browser)
+ browser->signin_view_controller()->CloseModalSignin();
+
+ // Does nothing if user manager is not showing.
+ UserManagerProfileDialog::HideDialog();
+}
+
+void InlineLoginHandler::CloseDialogFromJavascript() {
+ if (IsJavascriptAllowed())
+ CallJavascriptFunction("inline.login.closeDialog");
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/inline_login_handler.h b/chromium/chrome/browser/ui/webui/signin/inline_login_handler.h
new file mode 100644
index 00000000000..350d4d01b44
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/inline_login_handler.h
@@ -0,0 +1,77 @@
+// 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_SIGNIN_INLINE_LOGIN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_INLINE_LOGIN_HANDLER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace signin_metrics {
+enum class AccessPoint;
+}
+
+// The base class handler for the inline login WebUI.
+class InlineLoginHandler : public content::WebUIMessageHandler {
+ public:
+ InlineLoginHandler();
+ ~InlineLoginHandler() override;
+
+ // content::WebUIMessageHandler overrides:
+ void RegisterMessages() override;
+
+ protected:
+ // Enum for gaia auth mode, must match AuthMode defined in
+ // chrome/browser/resources/gaia_auth_host/gaia_auth_host.js.
+ enum AuthMode {
+ kDefaultAuthMode = 0,
+ kOfflineAuthMode = 1,
+ kDesktopAuthMode = 2
+ };
+
+ // Closes the dialog by calling the |inline.login.closeDialog| Javascript
+ // function.
+ // Does nothing if calling Javascript functions is not allowed.
+ void CloseDialogFromJavascript();
+
+ private:
+ // Record correspond sign in user action for an access point.
+ void RecordSigninUserActionForAccessPoint(
+ signin_metrics::AccessPoint access_point);
+
+ // JS callback to prepare for starting auth.
+ void HandleInitializeMessage(const base::ListValue* args);
+
+ // Continue to initialize the gaia auth extension. It calls
+ // |SetExtraInitParams| to set extra init params.
+ void ContinueHandleInitializeMessage();
+
+ // JS callback to complete login. It calls |CompleteLogin| to do the real
+ // work.
+ void HandleCompleteLoginMessage(const base::ListValue* args);
+
+ // JS callback to switch the UI from a constrainted dialog to a full tab.
+ void HandleSwitchToFullTabMessage(const base::ListValue* args);
+
+ // Handles the web ui message sent when the navigation button is clicked by
+ // the user, requesting either a back navigation or closing the dialog.
+ void HandleNavigationButtonClicked(const base::ListValue* args);
+
+ // Handles the web ui message sent when the window is closed from javascript.
+ void HandleDialogClose(const base::ListValue* args);
+
+ virtual void SetExtraInitParams(base::DictionaryValue& params) {}
+ virtual void CompleteLogin(const base::ListValue* args) = 0;
+
+ base::WeakPtrFactory<InlineLoginHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(InlineLoginHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_INLINE_LOGIN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc b/chromium/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
new file mode 100644
index 00000000000..2dde2ab0a1e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
@@ -0,0 +1,884 @@
+// 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/signin/inline_login_handler_impl.h"
+
+#include <stddef.h>
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/signin/about_signin_internals_factory.h"
+#include "chrome/browser/signin/account_tracker_service_factory.h"
+#include "chrome/browser/signin/chrome_signin_client_factory.h"
+#include "chrome/browser/signin/investigator_dependency_provider.h"
+#include "chrome/browser/signin/local_auth.h"
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
+#include "chrome/browser/signin/signin_error_controller_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/signin_promo.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/tab_modal_confirm_dialog.h"
+#include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/user_manager.h"
+#include "chrome/browser/ui/webui/signin/login_ui_service.h"
+#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
+#include "chrome/browser/ui/webui/signin/signin_utils.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/about_signin_internals.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/signin_error_controller.h"
+#include "components/signin/core/browser/signin_header_helper.h"
+#include "components/signin/core/browser/signin_investigator.h"
+#include "components/signin/core/browser/signin_metrics.h"
+#include "components/signin/core/common/signin_pref_names.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_ui.h"
+#include "google_apis/gaia/gaia_auth_fetcher.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+#include "google_apis/gaia/gaia_constants.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "net/base/url_util.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+void LogHistogramValue(int action) {
+ UMA_HISTOGRAM_ENUMERATION("Signin.AllAccessPointActions", action,
+ signin_metrics::HISTOGRAM_MAX);
+}
+
+// Returns true if |profile| is a system profile or created from one.
+bool IsSystemProfile(Profile* profile) {
+ return profile->GetOriginalProfile()->IsSystemProfile();
+}
+
+void RedirectToNtpOrAppsPage(content::WebContents* contents,
+ signin_metrics::AccessPoint access_point) {
+ // Do nothing if a navigation is pending, since this call can be triggered
+ // from DidStartLoading. This avoids deleting the pending entry while we are
+ // still navigating to it. See crbug/346632.
+ if (contents->GetController().GetPendingEntry())
+ return;
+
+ VLOG(1) << "RedirectToNtpOrAppsPage";
+ // Redirect to NTP/Apps page and display a confirmation bubble
+ GURL url(access_point ==
+ signin_metrics::AccessPoint::ACCESS_POINT_APPS_PAGE_LINK
+ ? chrome::kChromeUIAppsURL
+ : chrome::kChromeUINewTabURL);
+ content::OpenURLParams params(url, content::Referrer(),
+ WindowOpenDisposition::CURRENT_TAB,
+ ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false);
+ contents->OpenURL(params);
+}
+
+void RedirectToNtpOrAppsPageIfNecessary(
+ content::WebContents* contents,
+ signin_metrics::AccessPoint access_point) {
+ if (access_point != signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS)
+ RedirectToNtpOrAppsPage(contents, access_point);
+}
+
+void CloseModalSigninIfNeeded(InlineLoginHandlerImpl* handler) {
+ if (handler) {
+ Browser* browser = handler->GetDesktopBrowser();
+ if (browser)
+ browser->signin_view_controller()->CloseModalSignin();
+ }
+}
+
+void UnlockProfileAndHideLoginUI(const base::FilePath profile_path,
+ InlineLoginHandlerImpl* handler) {
+ if (!profile_path.empty()) {
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ if (profile_manager) {
+ ProfileAttributesEntry* entry;
+ if (profile_manager->GetProfileAttributesStorage()
+ .GetProfileAttributesWithPath(profile_path, &entry)) {
+ entry->SetIsSigninRequired(false);
+ }
+ }
+ }
+ if (handler)
+ handler->CloseDialogFromJavascript();
+
+ UserManager::Hide();
+}
+
+bool IsCrossAccountError(Profile* profile,
+ const std::string& email,
+ const std::string& gaia_id) {
+ InvestigatorDependencyProvider provider(profile);
+ InvestigatedScenario scenario =
+ SigninInvestigator(email, gaia_id, &provider).Investigate();
+
+ return scenario == InvestigatedScenario::DIFFERENT_ACCOUNT;
+}
+
+} // namespace
+
+InlineSigninHelper::InlineSigninHelper(
+ base::WeakPtr<InlineLoginHandlerImpl> handler,
+ net::URLRequestContextGetter* getter,
+ Profile* profile,
+ Profile::CreateStatus create_status,
+ const GURL& current_url,
+ const std::string& email,
+ const std::string& gaia_id,
+ const std::string& password,
+ const std::string& session_index,
+ const std::string& auth_code,
+ const std::string& signin_scoped_device_id,
+ bool choose_what_to_sync,
+ bool confirm_untrusted_signin,
+ bool is_force_sign_in_with_usermanager)
+ : gaia_auth_fetcher_(this, GaiaConstants::kChromeSource, getter),
+ handler_(handler),
+ profile_(profile),
+ create_status_(create_status),
+ current_url_(current_url),
+ email_(email),
+ gaia_id_(gaia_id),
+ password_(password),
+ session_index_(session_index),
+ auth_code_(auth_code),
+ choose_what_to_sync_(choose_what_to_sync),
+ confirm_untrusted_signin_(confirm_untrusted_signin),
+ is_force_sign_in_with_usermanager_(is_force_sign_in_with_usermanager) {
+ DCHECK(profile_);
+ DCHECK(!email_.empty());
+ if (!auth_code_.empty()) {
+ gaia_auth_fetcher_.StartAuthCodeForOAuth2TokenExchangeWithDeviceId(
+ auth_code, signin_scoped_device_id);
+ } else {
+ DCHECK(!session_index_.empty());
+ gaia_auth_fetcher_.StartCookieForOAuthLoginTokenExchangeWithDeviceId(
+ session_index_, signin_scoped_device_id);
+ }
+}
+
+InlineSigninHelper::~InlineSigninHelper() {}
+
+void InlineSigninHelper::OnClientOAuthSuccess(const ClientOAuthResult& result) {
+ if (is_force_sign_in_with_usermanager_) {
+ // If user sign in in UserManager with force sign in enabled, the browser
+ // window won't be opened until now.
+ profiles::OpenBrowserWindowForProfile(
+ base::Bind(&InlineSigninHelper::OnClientOAuthSuccessAndBrowserOpened,
+ base::Unretained(this), result),
+ true, false, profile_, create_status_);
+ } else {
+ OnClientOAuthSuccessAndBrowserOpened(result, profile_, create_status_);
+ }
+}
+
+void InlineSigninHelper::OnClientOAuthSuccessAndBrowserOpened(
+ const ClientOAuthResult& result,
+ Profile* profile,
+ Profile::CreateStatus status) {
+ if (is_force_sign_in_with_usermanager_)
+ UnlockProfileAndHideLoginUI(profile_->GetPath(), handler_.get());
+ content::WebContents* contents = NULL;
+ Browser* browser = NULL;
+ if (handler_) {
+ contents = handler_->web_ui()->GetWebContents();
+ browser = handler_->GetDesktopBrowser();
+ }
+
+ AboutSigninInternals* about_signin_internals =
+ AboutSigninInternalsFactory::GetForProfile(profile_);
+ about_signin_internals->OnRefreshTokenReceived("Successful");
+
+ // Prime the account tracker with this combination of gaia id/display email.
+ std::string account_id =
+ AccountTrackerServiceFactory::GetForProfile(profile_)
+ ->SeedAccountInfo(gaia_id_, email_);
+
+ signin_metrics::AccessPoint access_point =
+ signin::GetAccessPointForPromoURL(current_url_);
+ signin_metrics::Reason reason =
+ signin::GetSigninReasonForPromoURL(current_url_);
+
+ SigninManager* signin_manager = SigninManagerFactory::GetForProfile(profile_);
+ std::string primary_email =
+ signin_manager->GetAuthenticatedAccountInfo().email;
+ if (gaia::AreEmailsSame(email_, primary_email) &&
+ (reason == signin_metrics::Reason::REASON_REAUTHENTICATION ||
+ reason == signin_metrics::Reason::REASON_UNLOCK) &&
+ !password_.empty() && profiles::IsLockAvailable(profile_)) {
+ LocalAuth::SetLocalAuthCredentials(profile_, password_);
+ }
+
+ if (reason == signin_metrics::Reason::REASON_REAUTHENTICATION ||
+ reason == signin_metrics::Reason::REASON_UNLOCK ||
+ reason == signin_metrics::Reason::REASON_ADD_SECONDARY_ACCOUNT) {
+ ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->
+ UpdateCredentials(account_id, result.refresh_token);
+
+ if (signin::IsAutoCloseEnabledInURL(current_url_)) {
+ // Close the gaia sign in tab via a task to make sure we aren't in the
+ // middle of any webui handler code.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&InlineLoginHandlerImpl::CloseTab, handler_,
+ signin::ShouldShowAccountManagement(current_url_)));
+ }
+
+ if (reason == signin_metrics::Reason::REASON_REAUTHENTICATION ||
+ reason == signin_metrics::Reason::REASON_UNLOCK) {
+ signin_manager->MergeSigninCredentialIntoCookieJar();
+ }
+ LogSigninReason(reason);
+ } else {
+ browser_sync::ProfileSyncService* sync_service =
+ ProfileSyncServiceFactory::GetForProfile(profile_);
+ SigninErrorController* error_controller =
+ SigninErrorControllerFactory::GetForProfile(profile_);
+
+ OneClickSigninSyncStarter::StartSyncMode start_mode =
+ OneClickSigninSyncStarter::CONFIRM_SYNC_SETTINGS_FIRST;
+ if (access_point == signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS ||
+ choose_what_to_sync_) {
+ bool show_settings_without_configure =
+ error_controller->HasError() && sync_service &&
+ sync_service->IsFirstSetupComplete();
+ if (!show_settings_without_configure)
+ start_mode = OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST;
+ }
+
+ OneClickSigninSyncStarter::ConfirmationRequired confirmation_required =
+ confirm_untrusted_signin_ ?
+ OneClickSigninSyncStarter::CONFIRM_UNTRUSTED_SIGNIN :
+ OneClickSigninSyncStarter::CONFIRM_AFTER_SIGNIN;
+
+ bool start_signin = !HandleCrossAccountError(
+ result.refresh_token, confirmation_required, start_mode);
+ if (start_signin) {
+ CreateSyncStarter(browser, contents, current_url_,
+ signin::GetNextPageURLForPromoURL(current_url_),
+ result.refresh_token,
+ OneClickSigninSyncStarter::CURRENT_PROFILE, start_mode,
+ confirmation_required);
+ base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
+ }
+ }
+}
+
+void InlineSigninHelper::CreateSyncStarter(
+ Browser* browser,
+ content::WebContents* contents,
+ const GURL& current_url,
+ const GURL& continue_url,
+ const std::string& refresh_token,
+ OneClickSigninSyncStarter::ProfileMode profile_mode,
+ OneClickSigninSyncStarter::StartSyncMode start_mode,
+ OneClickSigninSyncStarter::ConfirmationRequired confirmation_required) {
+ // OneClickSigninSyncStarter will delete itself once the job is done.
+ new OneClickSigninSyncStarter(
+ profile_, browser, gaia_id_, email_, password_, refresh_token,
+ profile_mode, start_mode, contents, confirmation_required, current_url,
+ continue_url,
+ base::Bind(&InlineLoginHandlerImpl::SyncStarterCallback, handler_));
+}
+
+bool InlineSigninHelper::HandleCrossAccountError(
+ const std::string& refresh_token,
+ OneClickSigninSyncStarter::ConfirmationRequired confirmation_required,
+ OneClickSigninSyncStarter::StartSyncMode start_mode) {
+ // With force sign in enabled, cross account
+ // sign in will be rejected in the early stage so there is no need to show the
+ // warning page here.
+ if (signin::IsForceSigninEnabled())
+ return false;
+
+ std::string last_email =
+ profile_->GetPrefs()->GetString(prefs::kGoogleServicesLastUsername);
+
+ // TODO(skym): Warn for high risk upgrade scenario, crbug.com/572754.
+ if (!IsCrossAccountError(profile_, email_, gaia_id_))
+ return false;
+
+ Browser* browser = chrome::FindLastActiveWithProfile(profile_);
+ content::WebContents* web_contents =
+ browser->tab_strip_model()->GetActiveWebContents();
+
+ SigninEmailConfirmationDialog::AskForConfirmation(
+ web_contents, profile_, last_email, email_,
+ base::Bind(&InlineSigninHelper::ConfirmEmailAction,
+ base::Unretained(this), web_contents, refresh_token,
+ confirmation_required, start_mode));
+ return true;
+}
+
+void InlineSigninHelper::ConfirmEmailAction(
+ content::WebContents* web_contents,
+ const std::string& refresh_token,
+ OneClickSigninSyncStarter::ConfirmationRequired confirmation_required,
+ OneClickSigninSyncStarter::StartSyncMode start_mode,
+ SigninEmailConfirmationDialog::Action action) {
+ Browser* browser = chrome::FindLastActiveWithProfile(profile_);
+ switch (action) {
+ case SigninEmailConfirmationDialog::CREATE_NEW_USER:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_ImportDataPrompt_DontImport"));
+ CreateSyncStarter(browser, web_contents, current_url_, GURL(),
+ refresh_token, OneClickSigninSyncStarter::NEW_PROFILE,
+ start_mode, confirmation_required);
+ break;
+ case SigninEmailConfirmationDialog::START_SYNC:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_ImportDataPrompt_ImportData"));
+ CreateSyncStarter(browser, web_contents, current_url_, GURL(),
+ refresh_token,
+ OneClickSigninSyncStarter::CURRENT_PROFILE, start_mode,
+ confirmation_required);
+ break;
+ case SigninEmailConfirmationDialog::CLOSE:
+ base::RecordAction(
+ base::UserMetricsAction("Signin_ImportDataPrompt_Cancel"));
+ if (handler_) {
+ handler_->SyncStarterCallback(
+ OneClickSigninSyncStarter::SYNC_SETUP_FAILURE);
+ }
+ break;
+ default:
+ DCHECK(false) << "Invalid action";
+ }
+ base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
+}
+
+void InlineSigninHelper::OnClientOAuthFailure(
+ const GoogleServiceAuthError& error) {
+ if (handler_)
+ handler_->HandleLoginError(error.ToString(), base::string16());
+
+ AboutSigninInternals* about_signin_internals =
+ AboutSigninInternalsFactory::GetForProfile(profile_);
+ about_signin_internals->OnRefreshTokenReceived("Failure");
+
+ base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
+}
+
+InlineLoginHandlerImpl::InlineLoginHandlerImpl()
+ : confirm_untrusted_signin_(false),
+ weak_factory_(this) {
+}
+
+InlineLoginHandlerImpl::~InlineLoginHandlerImpl() {}
+
+// This method is not called with webview sign in enabled.
+void InlineLoginHandlerImpl::DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) {
+ if (!web_contents() ||
+ !navigation_handle->HasCommitted() ||
+ navigation_handle->IsErrorPage()) {
+ return;
+ }
+
+ // Returns early if this is not a gaia webview navigation.
+ content::RenderFrameHost* gaia_frame =
+ signin::GetAuthFrame(web_contents(), "signin-frame");
+ if (navigation_handle->GetRenderFrameHost() != gaia_frame)
+ return;
+
+ // Loading any untrusted (e.g., HTTP) URLs in the privileged sign-in process
+ // will require confirmation before the sign in takes effect.
+ const GURL kGaiaExtOrigin(
+ GaiaUrls::GetInstance()->signin_completed_continue_url().GetOrigin());
+ if (!navigation_handle->GetURL().is_empty()) {
+ GURL origin(navigation_handle->GetURL().GetOrigin());
+ if (navigation_handle->GetURL().spec() != url::kAboutBlankURL &&
+ origin != kGaiaExtOrigin &&
+ !gaia::IsGaiaSignonRealm(origin)) {
+ confirm_untrusted_signin_ = true;
+ }
+ }
+}
+
+// static
+bool InlineLoginHandlerImpl::CanOffer(Profile* profile,
+ CanOfferFor can_offer_for,
+ const std::string& gaia_id,
+ const std::string& email,
+ std::string* error_message) {
+ if (error_message)
+ error_message->clear();
+
+ if (!profile)
+ return false;
+
+ SigninManager* manager = SigninManagerFactory::GetForProfile(profile);
+ if (manager && !manager->IsSigninAllowed())
+ return false;
+
+ if (!ChromeSigninClient::ProfileAllowsSigninCookies(profile))
+ return false;
+
+ if (!email.empty()) {
+ if (!manager)
+ return false;
+
+ // Make sure this username is not prohibited by policy.
+ if (!manager->IsAllowedUsername(email)) {
+ if (error_message) {
+ error_message->assign(
+ l10n_util::GetStringUTF8(IDS_SYNC_LOGIN_NAME_PROHIBITED));
+ }
+ return false;
+ }
+
+ if (can_offer_for == CAN_OFFER_FOR_SECONDARY_ACCOUNT)
+ return true;
+
+ // If the signin manager already has an authenticated name, then this is a
+ // re-auth scenario. Make sure the email just signed in corresponds to
+ // the one sign in manager expects.
+ std::string current_email = manager->GetAuthenticatedAccountInfo().email;
+ const bool same_email = gaia::AreEmailsSame(current_email, email);
+ if (!current_email.empty() && !same_email) {
+ UMA_HISTOGRAM_ENUMERATION("Signin.Reauth",
+ signin_metrics::HISTOGRAM_ACCOUNT_MISSMATCH,
+ signin_metrics::HISTOGRAM_REAUTH_MAX);
+ if (error_message) {
+ error_message->assign(
+ l10n_util::GetStringFUTF8(IDS_SYNC_WRONG_EMAIL,
+ base::UTF8ToUTF16(current_email)));
+ }
+ return false;
+ }
+
+ // If some profile, not just the current one, is already connected to this
+ // account, don't show the infobar.
+ if (g_browser_process && !same_email) {
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ if (profile_manager) {
+ std::vector<ProfileAttributesEntry*> entries =
+ profile_manager->GetProfileAttributesStorage().
+ GetAllProfilesAttributes();
+
+ for (const ProfileAttributesEntry* entry : entries) {
+ // For backward compatibility, need to check also the username of the
+ // profile, since the GAIA ID may not have been set yet in the
+ // ProfileAttributesStorage. It will be set once the profile
+ // is opened.
+ std::string profile_gaia_id = entry->GetGAIAId();
+ std::string profile_email = base::UTF16ToUTF8(entry->GetUserName());
+ if (gaia_id == profile_gaia_id ||
+ gaia::AreEmailsSame(email, profile_email)) {
+ if (error_message) {
+ error_message->assign(
+ l10n_util::GetStringUTF8(IDS_SYNC_USER_NAME_IN_USE_ERROR));
+ }
+ return false;
+ }
+ }
+ }
+ }
+
+ // With force sign in enabled, cross account sign in is not allowed.
+ if (signin::IsForceSigninEnabled() &&
+ IsCrossAccountError(profile, email, gaia_id)) {
+ if (error_message) {
+ std::string last_email =
+ profile->GetPrefs()->GetString(prefs::kGoogleServicesLastUsername);
+ error_message->assign(l10n_util::GetStringFUTF8(
+ IDS_SYNC_USED_PROFILE_ERROR, base::UTF8ToUTF16(last_email)));
+ }
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void InlineLoginHandlerImpl::SetExtraInitParams(base::DictionaryValue& params) {
+ params.SetString("service", "chromiumsync");
+
+ // If this was called from the user manager to reauthenticate the profile,
+ // make sure the webui is aware.
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (IsSystemProfile(profile))
+ params.SetBoolean("dontResizeNonEmbeddedPages", true);
+
+ content::WebContents* contents = web_ui()->GetWebContents();
+ const GURL& current_url = contents->GetURL();
+ signin_metrics::Reason reason =
+ signin::GetSigninReasonForPromoURL(current_url);
+
+ std::string is_constrained;
+ net::GetValueForKeyInQuery(current_url, "constrained", &is_constrained);
+
+ // Use new embedded flow if in constrained window.
+ if (is_constrained == "1") {
+ const GURL& url = GaiaUrls::GetInstance()->embedded_signin_url();
+ params.SetBoolean("isNewGaiaFlow", true);
+ params.SetString("clientId",
+ GaiaUrls::GetInstance()->oauth2_chrome_client_id());
+ params.SetString("gaiaPath", url.path().substr(1));
+
+ std::string flow;
+ switch (reason) {
+ case signin_metrics::Reason::REASON_ADD_SECONDARY_ACCOUNT:
+ flow = "addaccount";
+ break;
+ case signin_metrics::Reason::REASON_REAUTHENTICATION:
+ case signin_metrics::Reason::REASON_UNLOCK:
+ flow = "reauth";
+ break;
+ default:
+ flow = "signin";
+ break;
+ }
+ params.SetString("flow", flow);
+ }
+
+ content::WebContentsObserver::Observe(contents);
+ LogHistogramValue(signin_metrics::HISTOGRAM_SHOWN);
+}
+
+void InlineLoginHandlerImpl::CompleteLogin(const base::ListValue* args) {
+ content::WebContents* contents = web_ui()->GetWebContents();
+ const GURL& current_url = contents->GetURL();
+
+ const base::DictionaryValue* dict = NULL;
+ args->GetDictionary(0, &dict);
+
+ bool skip_for_now = false;
+ dict->GetBoolean("skipForNow", &skip_for_now);
+ if (skip_for_now) {
+ signin::SetUserSkippedPromo(Profile::FromWebUI(web_ui()));
+ SyncStarterCallback(OneClickSigninSyncStarter::SYNC_SETUP_FAILURE);
+ return;
+ }
+
+ // This value exists only for webview sign in.
+ bool trusted = false;
+ if (dict->GetBoolean("trusted", &trusted))
+ confirm_untrusted_signin_ = !trusted;
+
+ base::string16 email_string16;
+ dict->GetString("email", &email_string16);
+ DCHECK(!email_string16.empty());
+ std::string email(base::UTF16ToASCII(email_string16));
+
+ base::string16 password_string16;
+ dict->GetString("password", &password_string16);
+ std::string password(base::UTF16ToASCII(password_string16));
+
+ base::string16 gaia_id_string16;
+ dict->GetString("gaiaId", &gaia_id_string16);
+ DCHECK(!gaia_id_string16.empty());
+ std::string gaia_id = base::UTF16ToASCII(gaia_id_string16);
+
+ std::string is_constrained;
+ net::GetValueForKeyInQuery(current_url, "constrained", &is_constrained);
+ const bool is_password_separated_signin_flow = is_constrained == "1";
+
+ base::string16 session_index_string16;
+ dict->GetString("sessionIndex", &session_index_string16);
+ std::string session_index = base::UTF16ToASCII(session_index_string16);
+ DCHECK(is_password_separated_signin_flow || !session_index.empty());
+
+ base::string16 auth_code_string16;
+ dict->GetString("authCode", &auth_code_string16);
+ std::string auth_code = base::UTF16ToASCII(auth_code_string16);
+ DCHECK(!is_password_separated_signin_flow || !auth_code.empty());
+
+ bool choose_what_to_sync = false;
+ dict->GetBoolean("chooseWhatToSync", &choose_what_to_sync);
+
+ content::StoragePartition* partition =
+ content::BrowserContext::GetStoragePartitionForSite(
+ contents->GetBrowserContext(), signin::GetSigninPartitionURL());
+
+ // If this was called from the user manager to reauthenticate the profile,
+ // the current profile is the system profile. In this case, use the email to
+ // find the right profile to reauthenticate. Otherwise the profile can be
+ // taken from web_ui().
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (IsSystemProfile(profile)) {
+ ProfileManager* manager = g_browser_process->profile_manager();
+ base::FilePath path = profiles::GetPathOfProfileWithEmail(manager, email);
+ if (path.empty()) {
+ path = UserManager::GetSigninProfilePath();
+ }
+ if (!path.empty()) {
+ signin_metrics::Reason reason =
+ signin::GetSigninReasonForPromoURL(current_url);
+ // If we are only reauthenticating a profile in the user manager (and not
+ // unlocking it), load the profile and finish the login.
+ if (reason == signin_metrics::Reason::REASON_REAUTHENTICATION) {
+ FinishCompleteLoginParams params(
+ this, partition, current_url, base::FilePath(),
+ confirm_untrusted_signin_, email, gaia_id, password, session_index,
+ auth_code, choose_what_to_sync, false);
+ ProfileManager::CreateCallback callback =
+ base::Bind(&InlineLoginHandlerImpl::FinishCompleteLogin, params);
+ profiles::LoadProfileAsync(path, callback);
+ } else {
+ // Otherwise, switch to the profile and finish the login. Pass the
+ // profile path so it can be marked as unlocked. Don't pass a handler
+ // pointer since it will be destroyed before the callback runs.
+ bool is_force_signin_enabled = signin::IsForceSigninEnabled();
+ InlineLoginHandlerImpl* handler = nullptr;
+ if (is_force_signin_enabled)
+ handler = this;
+ FinishCompleteLoginParams params(
+ handler, partition, current_url, path, confirm_untrusted_signin_,
+ email, gaia_id, password, session_index, auth_code,
+ choose_what_to_sync, is_force_signin_enabled);
+ ProfileManager::CreateCallback callback =
+ base::Bind(&InlineLoginHandlerImpl::FinishCompleteLogin, params);
+ if (is_force_signin_enabled) {
+ // Browser window will be opened after ClientOAuthSuccess.
+ profiles::LoadProfileAsync(path, callback);
+ } else {
+ profiles::SwitchToProfile(path, true, callback,
+ ProfileMetrics::SWITCH_PROFILE_UNLOCK);
+ }
+ }
+ }
+ } else {
+ FinishCompleteLogin(
+ FinishCompleteLoginParams(this, partition, current_url,
+ base::FilePath(), confirm_untrusted_signin_,
+ email, gaia_id, password, session_index,
+ auth_code, choose_what_to_sync, false),
+ profile, Profile::CREATE_STATUS_CREATED);
+ }
+}
+
+InlineLoginHandlerImpl::FinishCompleteLoginParams::FinishCompleteLoginParams(
+ InlineLoginHandlerImpl* handler,
+ content::StoragePartition* partition,
+ const GURL& url,
+ const base::FilePath& profile_path,
+ bool confirm_untrusted_signin,
+ const std::string& email,
+ const std::string& gaia_id,
+ const std::string& password,
+ const std::string& session_index,
+ const std::string& auth_code,
+ bool choose_what_to_sync,
+ bool is_force_sign_in_with_usermanager)
+ : handler(handler),
+ partition(partition),
+ url(url),
+ profile_path(profile_path),
+ confirm_untrusted_signin(confirm_untrusted_signin),
+ email(email),
+ gaia_id(gaia_id),
+ password(password),
+ session_index(session_index),
+ auth_code(auth_code),
+ choose_what_to_sync(choose_what_to_sync),
+ is_force_sign_in_with_usermanager(is_force_sign_in_with_usermanager) {}
+
+InlineLoginHandlerImpl::FinishCompleteLoginParams::FinishCompleteLoginParams(
+ const FinishCompleteLoginParams& other) = default;
+
+InlineLoginHandlerImpl::
+ FinishCompleteLoginParams::~FinishCompleteLoginParams() {}
+
+// static
+void InlineLoginHandlerImpl::FinishCompleteLogin(
+ const FinishCompleteLoginParams& params,
+ Profile* profile,
+ Profile::CreateStatus status) {
+ // When doing a SAML sign in, this email check may result in a false
+ // positive. This happens when the user types one email address in the
+ // gaia sign in page, but signs in to a different account in the SAML sign in
+ // page.
+ std::string default_email;
+ std::string validate_email;
+ if (net::GetValueForKeyInQuery(params.url, "email", &default_email) &&
+ net::GetValueForKeyInQuery(params.url, "validateEmail",
+ &validate_email) &&
+ validate_email == "1" && !default_email.empty()) {
+ if (!gaia::AreEmailsSame(params.email, default_email)) {
+ if (params.handler) {
+ params.handler->HandleLoginError(
+ l10n_util::GetStringFUTF8(IDS_SYNC_WRONG_EMAIL,
+ base::UTF8ToUTF16(default_email)),
+ base::UTF8ToUTF16(params.email));
+ }
+ return;
+ }
+ }
+
+ signin_metrics::AccessPoint access_point =
+ signin::GetAccessPointForPromoURL(params.url);
+ signin_metrics::Reason reason =
+ signin::GetSigninReasonForPromoURL(params.url);
+ LogHistogramValue(signin_metrics::HISTOGRAM_ACCEPTED);
+ bool switch_to_advanced =
+ params.choose_what_to_sync &&
+ (access_point != signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS);
+ LogHistogramValue(
+ switch_to_advanced ? signin_metrics::HISTOGRAM_WITH_ADVANCED :
+ signin_metrics::HISTOGRAM_WITH_DEFAULTS);
+
+ CanOfferFor can_offer_for = CAN_OFFER_FOR_ALL;
+ switch (reason) {
+ case signin_metrics::Reason::REASON_ADD_SECONDARY_ACCOUNT:
+ can_offer_for = CAN_OFFER_FOR_SECONDARY_ACCOUNT;
+ break;
+ case signin_metrics::Reason::REASON_REAUTHENTICATION:
+ case signin_metrics::Reason::REASON_UNLOCK: {
+ std::string primary_username =
+ SigninManagerFactory::GetForProfile(profile)
+ ->GetAuthenticatedAccountInfo()
+ .email;
+ if (!gaia::AreEmailsSame(default_email, primary_username))
+ can_offer_for = CAN_OFFER_FOR_SECONDARY_ACCOUNT;
+ break;
+ }
+ default:
+ // No need to change |can_offer_for|.
+ break;
+ }
+
+ std::string error_msg;
+ bool can_offer = CanOffer(profile, can_offer_for, params.gaia_id,
+ params.email, &error_msg);
+ if (!can_offer) {
+ if (params.handler) {
+ params.handler->HandleLoginError(error_msg,
+ base::UTF8ToUTF16(params.email));
+ }
+ return;
+ }
+
+ AboutSigninInternals* about_signin_internals =
+ AboutSigninInternalsFactory::GetForProfile(profile);
+ about_signin_internals->OnAuthenticationResultReceived("Successful");
+
+ SigninClient* signin_client =
+ ChromeSigninClientFactory::GetForProfile(profile);
+ std::string signin_scoped_device_id =
+ signin_client->GetSigninScopedDeviceId();
+ base::WeakPtr<InlineLoginHandlerImpl> handler_weak_ptr;
+ if (params.handler)
+ handler_weak_ptr = params.handler->GetWeakPtr();
+
+ // InlineSigninHelper will delete itself.
+ new InlineSigninHelper(
+ handler_weak_ptr, params.partition->GetURLRequestContext(), profile,
+ status, params.url, params.email, params.gaia_id, params.password,
+ params.session_index, params.auth_code, signin_scoped_device_id,
+ params.choose_what_to_sync, params.confirm_untrusted_signin,
+ params.is_force_sign_in_with_usermanager);
+
+ // If opened from user manager to unlock a profile, make sure the user manager
+ // is closed and that the profile is marked as unlocked.
+ if (!params.is_force_sign_in_with_usermanager) {
+ UnlockProfileAndHideLoginUI(params.profile_path, params.handler);
+ }
+}
+
+void InlineLoginHandlerImpl::HandleLoginError(const std::string& error_msg,
+ const base::string16& email) {
+ SyncStarterCallback(OneClickSigninSyncStarter::SYNC_SETUP_FAILURE);
+ Browser* browser = GetDesktopBrowser();
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+ if (IsSystemProfile(profile))
+ profile = g_browser_process->profile_manager()->GetProfileByPath(
+ UserManager::GetSigninProfilePath());
+ CloseModalSigninIfNeeded(this);
+ if (!error_msg.empty()) {
+ LoginUIServiceFactory::GetForProfile(profile)->DisplayLoginResult(
+ browser, base::UTF8ToUTF16(error_msg), email);
+ }
+}
+
+Browser* InlineLoginHandlerImpl::GetDesktopBrowser() {
+ Browser* browser = chrome::FindBrowserWithWebContents(
+ web_ui()->GetWebContents());
+ if (!browser)
+ browser = chrome::FindLastActiveWithProfile(Profile::FromWebUI(web_ui()));
+ return browser;
+}
+
+void InlineLoginHandlerImpl::SyncStarterCallback(
+ OneClickSigninSyncStarter::SyncSetupResult result) {
+ content::WebContents* contents = web_ui()->GetWebContents();
+
+ if (contents->GetController().GetPendingEntry()) {
+ // Do nothing if a navigation is pending, since this call can be triggered
+ // from DidStartLoading. This avoids deleting the pending entry while we are
+ // still navigating to it. See crbug/346632.
+ return;
+ }
+
+ const GURL& current_url = contents->GetLastCommittedURL();
+ signin_metrics::AccessPoint access_point =
+ signin::GetAccessPointForPromoURL(current_url);
+ bool auto_close = signin::IsAutoCloseEnabledInURL(current_url);
+
+ if (result == OneClickSigninSyncStarter::SYNC_SETUP_FAILURE) {
+ RedirectToNtpOrAppsPage(contents, access_point);
+ } else if (auto_close) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&InlineLoginHandlerImpl::CloseTab,
+ weak_factory_.GetWeakPtr(),
+ signin::ShouldShowAccountManagement(current_url)));
+ } else {
+ RedirectToNtpOrAppsPageIfNecessary(contents, access_point);
+ }
+}
+
+void InlineLoginHandlerImpl::CloseTab(bool show_account_management) {
+ content::WebContents* tab = web_ui()->GetWebContents();
+ Browser* browser = chrome::FindBrowserWithWebContents(tab);
+ if (browser) {
+ TabStripModel* tab_strip_model = browser->tab_strip_model();
+ if (tab_strip_model) {
+ int index = tab_strip_model->GetIndexOfWebContents(tab);
+ if (index != TabStripModel::kNoTab) {
+ tab_strip_model->ExecuteContextMenuCommand(
+ index, TabStripModel::CommandCloseTab);
+ }
+ }
+
+ if (show_account_management) {
+ browser->window()->ShowAvatarBubbleFromAvatarButton(
+ BrowserWindow::AVATAR_BUBBLE_MODE_ACCOUNT_MANAGEMENT,
+ signin::ManageAccountsParams(),
+ signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN,
+ false);
+ }
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/inline_login_handler_impl.h b/chromium/chrome/browser/ui/webui/signin/inline_login_handler_impl.h
new file mode 100644
index 00000000000..881cc5e18ce
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/inline_login_handler_impl.h
@@ -0,0 +1,217 @@
+// 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_SIGNIN_INLINE_LOGIN_HANDLER_IMPL_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_INLINE_LOGIN_HANDLER_IMPL_H_
+
+#include <memory>
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/sync/one_click_signin_sync_starter.h"
+#include "chrome/browser/ui/webui/signin/inline_login_handler.h"
+#include "chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h"
+#include "google_apis/gaia/gaia_auth_consumer.h"
+
+// Implementation for the inline login WebUI handler on desktop Chrome. Once
+// CrOS migrates to the same webview approach as desktop Chrome, much of the
+// code in this class should move to its base class |InlineLoginHandler|.
+class InlineLoginHandlerImpl : public InlineLoginHandler,
+ public content::WebContentsObserver {
+ public:
+ InlineLoginHandlerImpl();
+ ~InlineLoginHandlerImpl() override;
+
+ using InlineLoginHandler::web_ui;
+ using InlineLoginHandler::CloseDialogFromJavascript;
+
+ base::WeakPtr<InlineLoginHandlerImpl> GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+ }
+
+ Browser* GetDesktopBrowser();
+ void SyncStarterCallback(OneClickSigninSyncStarter::SyncSetupResult result);
+ // Closes the current tab and shows the account management view of the avatar
+ // bubble if |show_account_management| is true.
+ void CloseTab(bool show_account_management);
+ void HandleLoginError(const std::string& error_msg,
+ const base::string16& email);
+
+ private:
+ friend class InlineLoginUIBrowserTest;
+ FRIEND_TEST_ALL_PREFIXES(InlineLoginUIBrowserTest, CanOfferNoProfile);
+ FRIEND_TEST_ALL_PREFIXES(InlineLoginUIBrowserTest, CanOffer);
+ FRIEND_TEST_ALL_PREFIXES(InlineLoginUIBrowserTest, CanOfferProfileConnected);
+ FRIEND_TEST_ALL_PREFIXES(InlineLoginUIBrowserTest,
+ CanOfferUsernameNotAllowed);
+ FRIEND_TEST_ALL_PREFIXES(InlineLoginUIBrowserTest, CanOfferWithRejectedEmail);
+ FRIEND_TEST_ALL_PREFIXES(InlineLoginUIBrowserTest, CanOfferNoSigninCookies);
+
+ // Argument to CanOffer().
+ enum CanOfferFor {
+ CAN_OFFER_FOR_ALL,
+ CAN_OFFER_FOR_SECONDARY_ACCOUNT
+ };
+
+ static bool CanOffer(Profile* profile,
+ CanOfferFor can_offer_for,
+ const std::string& gaia_id,
+ const std::string& email,
+ std::string* error_message);
+
+ // InlineLoginHandler overrides:
+ void SetExtraInitParams(base::DictionaryValue& params) override;
+ void CompleteLogin(const base::ListValue* args) override;
+
+ // This struct exists to pass paramters to the FinishCompleteLogin() method,
+ // since the base::Bind() call does not support this many template args.
+ struct FinishCompleteLoginParams {
+ public:
+ FinishCompleteLoginParams(InlineLoginHandlerImpl* handler,
+ content::StoragePartition* partition,
+ const GURL& url,
+ const base::FilePath& profile_path,
+ bool confirm_untrusted_signin,
+ const std::string& email,
+ const std::string& gaia_id,
+ const std::string& password,
+ const std::string& session_index,
+ const std::string& auth_code,
+ bool choose_what_to_sync,
+ bool is_force_sign_in_with_usermanager);
+ FinishCompleteLoginParams(const FinishCompleteLoginParams& other);
+ ~FinishCompleteLoginParams();
+
+ // Pointer to WebUI handler. May be nullptr.
+ InlineLoginHandlerImpl* handler;
+ // The isolate storage partition containing sign in cookies.
+ content::StoragePartition* partition;
+ // URL of sign in containing parameters such as email, source, etc.
+ GURL url;
+ // Path to profile being signed in. Non empty only when unlocking a profile
+ // from the user manager.
+ base::FilePath profile_path;
+ // When true, an extra prompt will be shown to the user before sign in
+ // completes.
+ bool confirm_untrusted_signin;
+ // Email address of the account used to sign in.
+ std::string email;
+ // Obfustcated gaia id of the account used to sign in.
+ std::string gaia_id;
+ // Password of the account used to sign in.
+ std::string password;
+ // Index within gaia cookie of the account used to sign in. Used only
+ // with password combined signin flow.
+ std::string session_index;
+ // Authentication code used to exchange for a login scoped refresh token
+ // for the account used to sign in. Used only with password separated
+ // signin flow.
+ std::string auth_code;
+ // True if the user wants to configure sync before signing in.
+ bool choose_what_to_sync;
+ // True if user signing in with UserManager when force-sign-in policy is
+ // enabled.
+ bool is_force_sign_in_with_usermanager;
+ };
+
+ static void FinishCompleteLogin(const FinishCompleteLoginParams& params,
+ Profile* profile,
+ Profile::CreateStatus status);
+
+ // content::WebContentsObserver implementation:
+ void DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) override;
+
+ // True if the user has navigated to untrusted domains during the signin
+ // process.
+ bool confirm_untrusted_signin_;
+
+ base::WeakPtrFactory<InlineLoginHandlerImpl> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(InlineLoginHandlerImpl);
+};
+
+// Handles details of signing the user in with SigninManager and turning on
+// sync after InlineLoginHandlerImpl has acquired the auth tokens from GAIA.
+// This is a separate class from InlineLoginHandlerImpl because the full signin
+// process is asynchronous and can outlive the signin UI.
+// InlineLoginHandlerImpl is destryed once the UI is closed.
+class InlineSigninHelper : public GaiaAuthConsumer {
+ public:
+ InlineSigninHelper(base::WeakPtr<InlineLoginHandlerImpl> handler,
+ net::URLRequestContextGetter* getter,
+ Profile* profile,
+ Profile::CreateStatus create_status,
+ const GURL& current_url,
+ const std::string& email,
+ const std::string& gaia_id,
+ const std::string& password,
+ const std::string& session_index,
+ const std::string& auth_code,
+ const std::string& signin_scoped_device_id,
+ bool choose_what_to_sync,
+ bool confirm_untrusted_signin,
+ bool is_force_sign_in_with_usermanager);
+ ~InlineSigninHelper() override;
+
+ private:
+ // Handles cross account sign in error. If the supplied |email| does not match
+ // the last signed in email of the current profile, then Chrome will show a
+ // confirmation dialog before starting sync. It returns true if there is a
+ // cross account error, and false otherwise.
+ bool HandleCrossAccountError(
+ const std::string& refresh_token,
+ OneClickSigninSyncStarter::ConfirmationRequired confirmation_required,
+ OneClickSigninSyncStarter::StartSyncMode start_mode);
+
+ // Callback used with ConfirmEmailDialogDelegate.
+ void ConfirmEmailAction(
+ content::WebContents* web_contents,
+ const std::string& refresh_token,
+ OneClickSigninSyncStarter::ConfirmationRequired confirmation_required,
+ OneClickSigninSyncStarter::StartSyncMode start_mode,
+ SigninEmailConfirmationDialog::Action action);
+
+ // Overridden from GaiaAuthConsumer.
+ void OnClientOAuthSuccess(const ClientOAuthResult& result) override;
+ void OnClientOAuthFailure(const GoogleServiceAuthError& error)
+ override;
+
+ void OnClientOAuthSuccessAndBrowserOpened(const ClientOAuthResult& result,
+ Profile* profile,
+ Profile::CreateStatus status);
+
+ // Creates the sync starter. Virtual for tests. Call to exchange oauth code
+ // for tokens.
+ virtual void CreateSyncStarter(
+ Browser* browser,
+ content::WebContents* contents,
+ const GURL& current_url,
+ const GURL& continue_url,
+ const std::string& refresh_token,
+ OneClickSigninSyncStarter::ProfileMode profile_mode,
+ OneClickSigninSyncStarter::StartSyncMode start_mode,
+ OneClickSigninSyncStarter::ConfirmationRequired confirmation_required);
+
+ GaiaAuthFetcher gaia_auth_fetcher_;
+ base::WeakPtr<InlineLoginHandlerImpl> handler_;
+ Profile* profile_;
+ Profile::CreateStatus create_status_;
+ GURL current_url_;
+ std::string email_;
+ std::string gaia_id_;
+ std::string password_;
+ std::string session_index_;
+ std::string auth_code_;
+ bool choose_what_to_sync_;
+ bool confirm_untrusted_signin_;
+ bool is_force_sign_in_with_usermanager_;
+
+ DISALLOW_COPY_AND_ASSIGN(InlineSigninHelper);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_INLINE_LOGIN_HANDLER_IMPL_H_
diff --git a/chromium/chrome/browser/ui/webui/signin/inline_login_ui.cc b/chromium/chrome/browser/ui/webui/signin/inline_login_ui.cc
new file mode 100644
index 00000000000..5880825b948
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/inline_login_ui.cc
@@ -0,0 +1,80 @@
+// 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/signin/inline_login_ui.h"
+
+#include "base/command_line.h"
+#include "base/memory/ptr_util.h"
+#include "build/build_config.h"
+#include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
+#include "chrome/browser/extensions/tab_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sessions/session_tab_helper.h"
+#include "chrome/browser/ui/webui/metrics_handler.h"
+#include "chrome/browser/ui/webui/signin/inline_login_handler_impl.h"
+#include "chrome/browser/ui/webui/test_files_request_filter.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/signin/core/common/profile_management_switches.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/common/content_switches.h"
+
+namespace {
+
+content::WebUIDataSource* CreateWebUIDataSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIChromeSigninHost);
+ source->OverrideContentSecurityPolicyChildSrc("child-src chrome-extension:;");
+ source->OverrideContentSecurityPolicyObjectSrc("object-src chrome:;");
+ source->SetJsonPath("strings.js");
+
+ source->SetDefaultResource(IDR_INLINE_LOGIN_HTML);
+
+ // Only add a filter when runing as test.
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ const bool is_running_test = command_line->HasSwitch(::switches::kTestName) ||
+ command_line->HasSwitch(::switches::kTestType);
+ if (is_running_test)
+ source->SetRequestFilter(test::GetTestFilesRequestFilter());
+
+ source->AddResourcePath("inline_login.css", IDR_INLINE_LOGIN_CSS);
+ source->AddResourcePath("inline_login.js", IDR_INLINE_LOGIN_JS);
+ source->AddResourcePath("gaia_auth_host.js", IDR_GAIA_AUTH_AUTHENTICATOR_JS);
+
+ source->AddLocalizedString("title", IDS_CHROME_SIGNIN_TITLE);
+ source->AddLocalizedString(
+ "accessibleCloseButtonLabel", IDS_SIGNIN_ACCESSIBLE_CLOSE_BUTTON);
+ source->AddLocalizedString(
+ "accessibleBackButtonLabel", IDS_SIGNIN_ACCESSIBLE_BACK_BUTTON);
+ return source;
+}
+
+} // empty namespace
+
+InlineLoginUI::InlineLoginUI(content::WebUI* web_ui)
+ : WebDialogUI(web_ui),
+ auth_extension_(Profile::FromWebUI(web_ui)) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreateWebUIDataSource());
+ web_ui->AddMessageHandler(base::MakeUnique<InlineLoginHandlerImpl>());
+ web_ui->AddMessageHandler(base::MakeUnique<MetricsHandler>());
+
+ content::WebContents* contents = web_ui->GetWebContents();
+ // Required for intercepting extension function calls when the page is loaded
+ // in a bubble (not a full tab, thus tab helpers are not registered
+ // automatically).
+ extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
+ contents);
+ extensions::TabHelper::CreateForWebContents(contents);
+ // Ensure that the login UI has a tab ID, which will allow the GAIA auth
+ // extension's background script to tell it apart from iframes injected by
+ // other extensions.
+ SessionTabHelper::CreateForWebContents(contents);
+}
+
+InlineLoginUI::~InlineLoginUI() {}
diff --git a/chromium/chrome/browser/ui/webui/signin/inline_login_ui.h b/chromium/chrome/browser/ui/webui/signin/inline_login_ui.h
new file mode 100644
index 00000000000..55b00d2f5be
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/inline_login_ui.h
@@ -0,0 +1,27 @@
+// 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_SIGNIN_INLINE_LOGIN_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_INLINE_LOGIN_UI_H_
+
+#include "base/macros.h"
+#include "chrome/browser/extensions/signin/scoped_gaia_auth_extension.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+// Inline login WebUI in various signin flows for ChromeOS and Chrome desktop.
+// The authentication is carried out via the host gaia_auth extension. Upon
+// success, the profile of the webui should be populated with proper cookies.
+// Then this UI would fetch the oauth2 tokens using the cookies.
+class InlineLoginUI : public ui::WebDialogUI {
+ public:
+ explicit InlineLoginUI(content::WebUI* web_ui);
+ ~InlineLoginUI() override;
+
+ private:
+ ScopedGaiaAuthExtension auth_extension_;
+
+ DISALLOW_COPY_AND_ASSIGN(InlineLoginUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_INLINE_LOGIN_UI_H_
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
new file mode 100644
index 00000000000..34325a271b8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/inline_login_ui_browsertest.cc
@@ -0,0 +1,932 @@
+// 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 "base/command_line.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "chrome/browser/content_settings/cookie_settings_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_manager.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/signin_promo.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/signin/inline_login_handler_impl.h"
+#include "chrome/browser/ui/webui/signin/inline_login_ui.h"
+#include "chrome/browser/ui/webui/signin/login_ui_service.h"
+#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
+#include "chrome/browser/ui/webui/signin/login_ui_test_utils.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_browser_window.h"
+#include "chrome/test/base/test_chrome_web_ui_controller_factory.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/content_settings/core/browser/cookie_settings.h"
+#include "components/guest_view/browser/guest_view_manager.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/signin/core/common/profile_management_switches.h"
+#include "components/signin/core/common/signin_pref_names.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/session_storage_namespace.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/common/url_constants.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "google_apis/gaia/fake_gaia.h"
+#include "google_apis/gaia/gaia_switches.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "net/base/url_util.h"
+#include "net/http/http_response_headers.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request_status.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+
+using guest_view::GuestViewManager;
+using login_ui_test_utils::ExecuteJsToSigninInSigninFrame;
+using login_ui_test_utils::WaitUntilUIReady;
+
+namespace {
+
+struct ContentInfo {
+ ContentInfo(content::WebContents* contents,
+ int pid,
+ content::StoragePartition* storage_partition) {
+ this->contents = contents;
+ this->pid = pid;
+ this->storage_partition = storage_partition;
+ }
+
+ content::WebContents* contents;
+ int pid;
+ content::StoragePartition* storage_partition;
+};
+
+ContentInfo NavigateAndGetInfo(
+ Browser* browser,
+ const GURL& url,
+ WindowOpenDisposition disposition) {
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser, url, disposition,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+ content::WebContents* contents =
+ browser->tab_strip_model()->GetActiveWebContents();
+ content::RenderProcessHost* process = contents->GetRenderProcessHost();
+ return ContentInfo(contents, process->GetID(),
+ process->GetStoragePartition());
+}
+
+// Returns a new WebUI object for the WebContents from |arg0|.
+ACTION(ReturnNewWebUI) {
+ return new content::WebUIController(arg0);
+}
+
+GURL GetSigninPromoURL() {
+ return signin::GetPromoURL(
+ signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE,
+ signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT, false);
+}
+
+// Mock the TestChromeWebUIControllerFactory::WebUIProvider to prove that we are
+// not called as expected.
+class FooWebUIProvider
+ : public TestChromeWebUIControllerFactory::WebUIProvider {
+ public:
+ MOCK_METHOD2(NewWebUI, content::WebUIController*(content::WebUI* web_ui,
+ const GURL& url));
+};
+
+const char kFooWebUIURL[] = "chrome://foo/";
+
+bool AddToSet(std::set<content::WebContents*>* set,
+ content::WebContents* web_contents) {
+ set->insert(web_contents);
+ return false;
+}
+
+// This class is used to mock out virtual methods with side effects so that
+// tests below can ensure they are called without causing side effects.
+class MockInlineSigninHelper : public InlineSigninHelper {
+ public:
+ MockInlineSigninHelper(
+ base::WeakPtr<InlineLoginHandlerImpl> handler,
+ net::URLRequestContextGetter* getter,
+ Profile* profile,
+ const GURL& current_url,
+ const std::string& email,
+ const std::string& gaia_id,
+ const std::string& password,
+ const std::string& session_index,
+ const std::string& auth_code,
+ const std::string& signin_scoped_device_id,
+ bool choose_what_to_sync,
+ bool confirm_untrusted_signin);
+
+ MOCK_METHOD1(OnClientOAuthSuccess, void(const ClientOAuthResult& result));
+ MOCK_METHOD1(OnClientOAuthFailure, void(const GoogleServiceAuthError& error));
+ MOCK_METHOD8(CreateSyncStarter,
+ void(Browser*,
+ content::WebContents*,
+ const GURL&,
+ const GURL&,
+ const std::string&,
+ OneClickSigninSyncStarter::ProfileMode,
+ OneClickSigninSyncStarter::StartSyncMode,
+ OneClickSigninSyncStarter::ConfirmationRequired));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockInlineSigninHelper);
+};
+
+MockInlineSigninHelper::MockInlineSigninHelper(
+ base::WeakPtr<InlineLoginHandlerImpl> handler,
+ net::URLRequestContextGetter* getter,
+ Profile* profile,
+ const GURL& current_url,
+ const std::string& email,
+ const std::string& gaia_id,
+ const std::string& password,
+ const std::string& session_index,
+ const std::string& auth_code,
+ const std::string& signin_scoped_device_id,
+ bool choose_what_to_sync,
+ bool confirm_untrusted_signin)
+ : InlineSigninHelper(handler,
+ getter,
+ profile,
+ Profile::CreateStatus::CREATE_STATUS_INITIALIZED,
+ current_url,
+ email,
+ gaia_id,
+ password,
+ session_index,
+ auth_code,
+ signin_scoped_device_id,
+ choose_what_to_sync,
+ confirm_untrusted_signin,
+ false) {}
+
+// This class is used to mock out virtual methods with side effects so that
+// tests below can ensure they are called without causing side effects.
+class MockSyncStarterInlineSigninHelper : public InlineSigninHelper {
+ public:
+ MockSyncStarterInlineSigninHelper(
+ base::WeakPtr<InlineLoginHandlerImpl> handler,
+ net::URLRequestContextGetter* getter,
+ Profile* profile,
+ const GURL& current_url,
+ const std::string& email,
+ const std::string& gaia_id,
+ const std::string& password,
+ const std::string& session_index,
+ const std::string& auth_code,
+ const std::string& signin_scoped_device_id,
+ bool choose_what_to_sync,
+ bool confirm_untrusted_signin,
+ bool is_force_sign_in_with_usermanager);
+
+ MOCK_METHOD8(CreateSyncStarter,
+ void(Browser*,
+ content::WebContents*,
+ const GURL&,
+ const GURL&,
+ const std::string&,
+ OneClickSigninSyncStarter::ProfileMode,
+ OneClickSigninSyncStarter::StartSyncMode,
+ OneClickSigninSyncStarter::ConfirmationRequired));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockSyncStarterInlineSigninHelper);
+};
+
+MockSyncStarterInlineSigninHelper::MockSyncStarterInlineSigninHelper(
+ base::WeakPtr<InlineLoginHandlerImpl> handler,
+ net::URLRequestContextGetter* getter,
+ Profile* profile,
+ const GURL& current_url,
+ const std::string& email,
+ const std::string& gaia_id,
+ const std::string& password,
+ const std::string& session_index,
+ const std::string& auth_code,
+ const std::string& signin_scoped_device_id,
+ bool choose_what_to_sync,
+ bool confirm_untrusted_signin,
+ bool is_force_sign_in_with_usermanager)
+ : InlineSigninHelper(handler,
+ getter,
+ profile,
+ Profile::CreateStatus::CREATE_STATUS_INITIALIZED,
+ current_url,
+ email,
+ gaia_id,
+ password,
+ session_index,
+ auth_code,
+ signin_scoped_device_id,
+ choose_what_to_sync,
+ confirm_untrusted_signin,
+ is_force_sign_in_with_usermanager) {}
+
+} // namespace
+
+class InlineLoginUIBrowserTest : public InProcessBrowserTest {
+ public:
+ InlineLoginUIBrowserTest() {}
+
+ void SetUpSigninManager(const std::string& username);
+ void EnableSigninAllowed(bool enable);
+ void EnableOneClick(bool enable);
+ void AddEmailToOneClickRejectedList(const std::string& email);
+ void AllowSigninCookies(bool enable);
+ void SetAllowedUsernamePattern(const std::string& pattern);
+
+ protected:
+ content::WebContents* web_contents() { return nullptr; }
+};
+
+void InlineLoginUIBrowserTest::SetUpSigninManager(const std::string& username) {
+ if (username.empty())
+ return;
+
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetForProfile(browser()->profile());
+ signin_manager->SetAuthenticatedAccountInfo(username, username);
+}
+
+void InlineLoginUIBrowserTest::EnableSigninAllowed(bool enable) {
+ PrefService* pref_service = browser()->profile()->GetPrefs();
+ pref_service->SetBoolean(prefs::kSigninAllowed, enable);
+}
+
+void InlineLoginUIBrowserTest::EnableOneClick(bool enable) {
+ PrefService* pref_service = browser()->profile()->GetPrefs();
+ pref_service->SetBoolean(prefs::kReverseAutologinEnabled, enable);
+}
+
+void InlineLoginUIBrowserTest::AddEmailToOneClickRejectedList(
+ const std::string& email) {
+ PrefService* pref_service = browser()->profile()->GetPrefs();
+ ListPrefUpdate updater(pref_service,
+ prefs::kReverseAutologinRejectedEmailList);
+ updater->AppendIfNotPresent(base::MakeUnique<base::Value>(email));
+}
+
+void InlineLoginUIBrowserTest::AllowSigninCookies(bool enable) {
+ content_settings::CookieSettings* cookie_settings =
+ CookieSettingsFactory::GetForProfile(browser()->profile()).get();
+ cookie_settings->SetDefaultCookieSetting(enable ? CONTENT_SETTING_ALLOW
+ : CONTENT_SETTING_BLOCK);
+}
+
+void InlineLoginUIBrowserTest::SetAllowedUsernamePattern(
+ const std::string& pattern) {
+ PrefService* local_state = g_browser_process->local_state();
+ local_state->SetString(prefs::kGoogleServicesUsernamePattern, pattern);
+}
+
+#if defined(OS_LINUX) || defined(OS_WIN)
+// crbug.com/422868
+#define MAYBE_DifferentStorageId DISABLED_DifferentStorageId
+#else
+#define MAYBE_DifferentStorageId DifferentStorageId
+#endif
+IN_PROC_BROWSER_TEST_F(InlineLoginUIBrowserTest, MAYBE_DifferentStorageId) {
+ ContentInfo info = NavigateAndGetInfo(browser(), GetSigninPromoURL(),
+ WindowOpenDisposition::CURRENT_TAB);
+ WaitUntilUIReady(browser());
+
+ // Make sure storage partition of embedded webview is different from
+ // parent.
+ std::set<content::WebContents*> set;
+ GuestViewManager* manager = GuestViewManager::FromBrowserContext(
+ info.contents->GetBrowserContext());
+ manager->ForEachGuest(info.contents, base::Bind(&AddToSet, &set));
+ ASSERT_EQ(1u, set.size());
+ content::WebContents* webview_contents = *set.begin();
+ content::RenderProcessHost* process =
+ webview_contents->GetRenderProcessHost();
+ ASSERT_NE(info.pid, process->GetID());
+ ASSERT_NE(info.storage_partition, process->GetStoragePartition());
+}
+
+IN_PROC_BROWSER_TEST_F(InlineLoginUIBrowserTest, OneProcessLimit) {
+ GURL test_url_1 = ui_test_utils::GetTestUrl(
+ 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!")));
+
+ // Even when the process limit is set to one, the signin process should
+ // still be given its own process and storage partition.
+ content::RenderProcessHost::SetMaxRendererProcessCount(1);
+
+ ContentInfo info1 = NavigateAndGetInfo(browser(), test_url_1,
+ WindowOpenDisposition::CURRENT_TAB);
+ ContentInfo info2 = NavigateAndGetInfo(browser(), test_url_2,
+ WindowOpenDisposition::CURRENT_TAB);
+ ContentInfo info3 = NavigateAndGetInfo(browser(), GetSigninPromoURL(),
+ WindowOpenDisposition::CURRENT_TAB);
+
+ ASSERT_EQ(info1.pid, info2.pid);
+ ASSERT_NE(info1.pid, info3.pid);
+}
+
+IN_PROC_BROWSER_TEST_F(InlineLoginUIBrowserTest, CanOfferNoProfile) {
+ std::string error_message;
+ EXPECT_FALSE(InlineLoginHandlerImpl::CanOffer(
+ NULL, InlineLoginHandlerImpl::CAN_OFFER_FOR_ALL,
+ "12345", "user@gmail.com", &error_message));
+ EXPECT_EQ("", error_message);
+}
+
+IN_PROC_BROWSER_TEST_F(InlineLoginUIBrowserTest, CanOffer) {
+ EnableOneClick(true);
+ EXPECT_TRUE(InlineLoginHandlerImpl::CanOffer(
+ browser()->profile(), InlineLoginHandlerImpl::CAN_OFFER_FOR_ALL,
+ "12345", "user@gmail.com", NULL));
+
+ EnableOneClick(false);
+
+ std::string error_message;
+
+ EXPECT_TRUE(InlineLoginHandlerImpl::CanOffer(
+ browser()->profile(), InlineLoginHandlerImpl::CAN_OFFER_FOR_ALL,
+ "12345", "user@gmail.com", &error_message));
+}
+
+IN_PROC_BROWSER_TEST_F(InlineLoginUIBrowserTest, CanOfferProfileConnected) {
+ SetUpSigninManager("foo@gmail.com");
+ EnableSigninAllowed(true);
+
+ std::string error_message;
+
+ EXPECT_TRUE(InlineLoginHandlerImpl::CanOffer(
+ browser()->profile(), InlineLoginHandlerImpl::CAN_OFFER_FOR_ALL,
+ "12345", "foo@gmail.com", &error_message));
+ EXPECT_TRUE(InlineLoginHandlerImpl::CanOffer(
+ browser()->profile(), InlineLoginHandlerImpl::CAN_OFFER_FOR_ALL,
+ "12345", "foo", &error_message));
+ EXPECT_FALSE(InlineLoginHandlerImpl::CanOffer(
+ browser()->profile(), InlineLoginHandlerImpl::CAN_OFFER_FOR_ALL,
+ "12345", "user@gmail.com", &error_message));
+ EXPECT_EQ(l10n_util::GetStringFUTF8(IDS_SYNC_WRONG_EMAIL,
+ base::UTF8ToUTF16("foo@gmail.com")),
+ error_message);
+}
+
+IN_PROC_BROWSER_TEST_F(InlineLoginUIBrowserTest, CanOfferUsernameNotAllowed) {
+ SetAllowedUsernamePattern("*.google.com");
+
+ std::string error_message;
+ EXPECT_FALSE(InlineLoginHandlerImpl::CanOffer(
+ browser()->profile(), InlineLoginHandlerImpl::CAN_OFFER_FOR_ALL,
+ "12345", "foo@gmail.com", &error_message));
+ EXPECT_EQ(l10n_util::GetStringUTF8(IDS_SYNC_LOGIN_NAME_PROHIBITED),
+ error_message);
+}
+
+IN_PROC_BROWSER_TEST_F(InlineLoginUIBrowserTest, CanOfferWithRejectedEmail) {
+ EnableSigninAllowed(true);
+
+ AddEmailToOneClickRejectedList("foo@gmail.com");
+ AddEmailToOneClickRejectedList("user@gmail.com");
+
+ std::string error_message;
+ EXPECT_TRUE(InlineLoginHandlerImpl::CanOffer(
+ browser()->profile(), InlineLoginHandlerImpl::CAN_OFFER_FOR_ALL,
+ "12345", "foo@gmail.com", &error_message));
+ EXPECT_TRUE(InlineLoginHandlerImpl::CanOffer(
+ browser()->profile(), InlineLoginHandlerImpl::CAN_OFFER_FOR_ALL,
+ "12345", "user@gmail.com", &error_message));
+}
+
+IN_PROC_BROWSER_TEST_F(InlineLoginUIBrowserTest, CanOfferNoSigninCookies) {
+ AllowSigninCookies(false);
+ EnableSigninAllowed(true);
+
+ std::string error_message;
+ EXPECT_FALSE(InlineLoginHandlerImpl::CanOffer(
+ browser()->profile(), InlineLoginHandlerImpl::CAN_OFFER_FOR_ALL,
+ "12345", "user@gmail.com", &error_message));
+ EXPECT_EQ("", error_message);
+}
+
+class InlineLoginHelperBrowserTest : public InProcessBrowserTest {
+ public:
+ InlineLoginHelperBrowserTest() {}
+
+ void SetUpInProcessBrowserTestFixture() override {
+ InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
+
+ will_create_browser_context_services_subscription_ =
+ BrowserContextDependencyManager::GetInstance()
+ ->RegisterWillCreateBrowserContextServicesCallbackForTesting(
+ base::Bind(&InlineLoginHelperBrowserTest::
+ OnWillCreateBrowserContextServices,
+ base::Unretained(this)));
+ }
+
+ void OnWillCreateBrowserContextServices(content::BrowserContext* context) {
+ // Replace the signin manager and token service with fakes. Do this ahead of
+ // creating the browser so that a bunch of classes don't register as
+ // observers and end up needing to unregister when the fake is substituted.
+ SigninManagerFactory::GetInstance()->SetTestingFactory(
+ context, &BuildFakeSigninManagerBase);
+ ProfileOAuth2TokenServiceFactory::GetInstance()->SetTestingFactory(
+ context, &BuildFakeProfileOAuth2TokenService);
+ }
+
+ void SetUpOnMainThread() override {
+ InProcessBrowserTest::SetUpOnMainThread();
+
+ // Grab references to the fake signin manager and token service.
+ Profile* profile = browser()->profile();
+ signin_manager_ = static_cast<FakeSigninManagerForTesting*>(
+ SigninManagerFactory::GetInstance()->GetForProfile(profile));
+ ASSERT_TRUE(signin_manager_);
+ token_service_ = static_cast<FakeProfileOAuth2TokenService*>(
+ ProfileOAuth2TokenServiceFactory::GetInstance()->GetForProfile(
+ profile));
+ ASSERT_TRUE(token_service_);
+ }
+
+ void SimulateStartCookieForOAuthLoginTokenExchangeSuccess(
+ const std::string& cookie_string) {
+ net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ scoped_refptr<net::HttpResponseHeaders> reponse_headers =
+ new net::HttpResponseHeaders("");
+ reponse_headers->AddCookie(cookie_string);
+ fetcher->set_status(net::URLRequestStatus());
+ fetcher->set_response_code(net::HTTP_OK);
+ fetcher->set_response_headers(reponse_headers);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ }
+
+ void SimulateStartAuthCodeForOAuth2TokenExchangeSuccess(
+ const std::string& json_response) {
+ net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_status(net::URLRequestStatus());
+ fetcher->set_response_code(net::HTTP_OK);
+ fetcher->SetResponseString(json_response);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ }
+
+ void SimulateOnClientOAuthSuccess(GaiaAuthConsumer* consumer,
+ const std::string& refresh_token) {
+ GaiaAuthConsumer::ClientOAuthResult result;
+ result.refresh_token = refresh_token;
+ consumer->OnClientOAuthSuccess(result);
+ }
+
+ FakeSigninManagerForTesting* signin_manager() { return signin_manager_; }
+ FakeProfileOAuth2TokenService* token_service() { return token_service_; }
+
+ private:
+ net::TestURLFetcherFactory url_fetcher_factory_;
+ FakeSigninManagerForTesting* signin_manager_;
+ FakeProfileOAuth2TokenService* token_service_;
+ std::unique_ptr<
+ base::CallbackList<void(content::BrowserContext*)>::Subscription>
+ will_create_browser_context_services_subscription_;
+
+ DISALLOW_COPY_AND_ASSIGN(InlineLoginHelperBrowserTest);
+};
+
+// Test signin helper calls correct fetcher methods when called with a
+// session index.
+IN_PROC_BROWSER_TEST_F(InlineLoginHelperBrowserTest, WithSessionIndex) {
+ base::WeakPtr<InlineLoginHandlerImpl> handler;
+ MockInlineSigninHelper helper(handler,
+ browser()->profile()->GetRequestContext(),
+ browser()->profile(),
+ GURL(),
+ "foo@gmail.com",
+ "gaiaid-12345",
+ "password",
+ "0", // session index from above
+ std::string(), // auth code
+ std::string(),
+ false, // choose what to sync
+ false); // confirm untrusted signin
+ EXPECT_CALL(helper, OnClientOAuthSuccess(_));
+
+ SimulateStartCookieForOAuthLoginTokenExchangeSuccess(
+ "secure; httponly; oauth_code=code");
+ SimulateStartAuthCodeForOAuth2TokenExchangeSuccess(
+ "{\"access_token\": \"access_token\", \"expires_in\": 1234567890,"
+ " \"refresh_token\": \"refresh_token\"}");
+}
+
+// Test signin helper calls correct fetcher methods when called with an
+// auth code.
+IN_PROC_BROWSER_TEST_F(InlineLoginHelperBrowserTest, WithAuthCode) {
+ base::WeakPtr<InlineLoginHandlerImpl> handler;
+ MockInlineSigninHelper helper(handler,
+ browser()->profile()->GetRequestContext(),
+ browser()->profile(),
+ GURL(),
+ "foo@gmail.com",
+ "gaiaid-12345",
+ "password",
+ "", // session index
+ "auth_code", // auth code
+ std::string(),
+ false, // choose what to sync
+ false); // confirm untrusted signin
+ EXPECT_CALL(helper, OnClientOAuthSuccess(_));
+
+ SimulateStartAuthCodeForOAuth2TokenExchangeSuccess(
+ "{\"access_token\": \"access_token\", \"expires_in\": 1234567890,"
+ " \"refresh_token\": \"refresh_token\"}");
+}
+
+// Test signin helper creates sync starter with correct confirmation when
+// signing in with default sync options.
+IN_PROC_BROWSER_TEST_F(InlineLoginHelperBrowserTest,
+ SigninCreatesSyncStarter1) {
+ // See Source enum in components/signin/core/browser/signin_metrics.h for
+ // possible values of access_point=, reason=.
+ GURL url("chrome://chrome-signin/?access_point=0&reason=0");
+ base::WeakPtr<InlineLoginHandlerImpl> handler;
+ // MockSyncStarterInlineSigninHelper will delete itself when done using
+ // base::ThreadTaskRunnerHandle::DeleteSoon(), so need to delete here. But
+ // do need the RunUntilIdle() at the end.
+ MockSyncStarterInlineSigninHelper* helper =
+ new MockSyncStarterInlineSigninHelper(
+ handler, browser()->profile()->GetRequestContext(),
+ browser()->profile(), url, "foo@gmail.com", "gaiaid-12345",
+ "password",
+ "", // session index
+ "auth_code", // auth code
+ std::string(),
+ false, // choose what to sync
+ false, // confirm untrusted signin
+ false);
+ EXPECT_CALL(
+ *helper,
+ CreateSyncStarter(_, _, _, _, "refresh_token",
+ OneClickSigninSyncStarter::CURRENT_PROFILE,
+ OneClickSigninSyncStarter::CONFIRM_SYNC_SETTINGS_FIRST,
+ OneClickSigninSyncStarter::CONFIRM_AFTER_SIGNIN));
+
+ ProfileAttributesEntry* entry;
+ ASSERT_TRUE(g_browser_process->profile_manager()
+ ->GetProfileAttributesStorage()
+ .GetProfileAttributesWithPath(browser()->profile()->GetPath(),
+ &entry));
+ entry->SetIsSigninRequired(true);
+
+ ASSERT_EQ(1ul, BrowserList::GetInstance()->size());
+ SimulateOnClientOAuthSuccess(helper, "refresh_token");
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(1ul, BrowserList::GetInstance()->size());
+ // if |force_sign_in_with_user_manager| is false, the profile should be
+ // unlocked early and InlineLoginHelper won't try to do it again
+ ASSERT_TRUE(entry->IsSigninRequired());
+}
+
+// Test signin helper creates sync starter with correct confirmation when
+// signing in and choosing what to sync first.
+IN_PROC_BROWSER_TEST_F(InlineLoginHelperBrowserTest,
+ SigninCreatesSyncStarter2) {
+ // See Source enum in components/signin/core/browser/signin_metrics.h for
+ // possible values of access_point=, reason=.
+ const GURL url("chrome://chrome-signin/?access_point=0&reason=0");
+ base::WeakPtr<InlineLoginHandlerImpl> handler;
+ // MockSyncStarterInlineSigninHelper will delete itself when done using
+ // base::ThreadTaskRunnerHandle::DeleteSoon(), so need to delete here. But
+ // do need the RunUntilIdle() at the end.
+ MockSyncStarterInlineSigninHelper* helper =
+ new MockSyncStarterInlineSigninHelper(
+ handler, browser()->profile()->GetRequestContext(),
+ browser()->profile(), url, "foo@gmail.com", "gaiaid-12345",
+ "password",
+ "", // session index
+ "auth_code", // auth code
+ std::string(),
+ true, // choose what to sync
+ false, // confirm untrusted signin
+ false);
+ EXPECT_CALL(*helper, CreateSyncStarter(
+ _, _, _, _, "refresh_token",
+ OneClickSigninSyncStarter::CURRENT_PROFILE,
+ OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST,
+ OneClickSigninSyncStarter::CONFIRM_AFTER_SIGNIN));
+
+ SimulateOnClientOAuthSuccess(helper, "refresh_token");
+ base::RunLoop().RunUntilIdle();
+}
+
+// Test signin helper creates sync starter with correct confirmation when
+// signing in with an untrusted sign occurs.
+IN_PROC_BROWSER_TEST_F(InlineLoginHelperBrowserTest,
+ SigninCreatesSyncStarter3) {
+ // See Source enum in components/signin/core/browser/signin_metrics.h for
+ // possible values of access_point=, reason=.
+ GURL url("chrome://chrome-signin/?access_point=0&reason=0");
+ base::WeakPtr<InlineLoginHandlerImpl> handler;
+ // MockSyncStarterInlineSigninHelper will delete itself when done using
+ // base::ThreadTaskRunnerHandle::DeleteSoon(), so need to delete here. But
+ // do need the RunUntilIdle() at the end.
+ MockSyncStarterInlineSigninHelper* helper =
+ new MockSyncStarterInlineSigninHelper(
+ handler, browser()->profile()->GetRequestContext(),
+ browser()->profile(), url, "foo@gmail.com", "gaiaid-12345",
+ "password",
+ "", // session index
+ "auth_code", // auth code
+ std::string(),
+ false, // choose what to sync
+ true, // confirm untrusted signin
+ false);
+ EXPECT_CALL(
+ *helper,
+ CreateSyncStarter(_, _, _, _, "refresh_token",
+ OneClickSigninSyncStarter::CURRENT_PROFILE,
+ OneClickSigninSyncStarter::CONFIRM_SYNC_SETTINGS_FIRST,
+ OneClickSigninSyncStarter::CONFIRM_UNTRUSTED_SIGNIN));
+
+ SimulateOnClientOAuthSuccess(helper, "refresh_token");
+ base::RunLoop().RunUntilIdle();
+}
+
+// Test signin helper creates sync starter with correct confirmation during
+// re-auth.
+IN_PROC_BROWSER_TEST_F(InlineLoginHelperBrowserTest,
+ SigninCreatesSyncStarter4) {
+ // See Source enum in components/signin/core/browser/signin_metrics.h for
+ // possible values of access_point=, reason=.
+ const GURL url("chrome://chrome-signin/?access_point=3&reason=0");
+ base::WeakPtr<InlineLoginHandlerImpl> handler;
+ // MockSyncStarterInlineSigninHelper will delete itself when done using
+ // base::ThreadTaskRunnerHandle::DeleteSoon(), so need to delete here. But
+ // do need the RunUntilIdle() at the end.
+ MockSyncStarterInlineSigninHelper* helper =
+ new MockSyncStarterInlineSigninHelper(
+ handler, browser()->profile()->GetRequestContext(),
+ browser()->profile(), url, "foo@gmail.com", "gaiaid-12345",
+ "password",
+ "", // session index
+ "auth_code", // auth code
+ std::string(),
+ false, // choose what to sync
+ false, // confirm untrusted signin
+ false);
+
+ // Even though "choose what to sync" is false, the source of the URL is
+ // settings, which means the user wants to CONFIGURE_SYNC_FIRST.
+ EXPECT_CALL(*helper, CreateSyncStarter(
+ _, _, _, _, "refresh_token",
+ OneClickSigninSyncStarter::CURRENT_PROFILE,
+ OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST,
+ OneClickSigninSyncStarter::CONFIRM_AFTER_SIGNIN));
+
+ SimulateOnClientOAuthSuccess(helper, "refresh_token");
+ base::RunLoop().RunUntilIdle();
+}
+
+// Test signin helper does not create sync starter when reauthenticating.
+IN_PROC_BROWSER_TEST_F(InlineLoginHelperBrowserTest,
+ ReauthCallsUpdateCredentials) {
+ ASSERT_EQ(0ul, token_service()->GetAccounts().size());
+
+ // See Source enum in components/signin/core/browser/signin_metrics.h for
+ // possible values of access_point=, reason=.
+ GURL url("chrome://chrome-signin/?access_point=3&reason=2");
+ base::WeakPtr<InlineLoginHandlerImpl> handler;
+ InlineSigninHelper helper(handler, browser()->profile()->GetRequestContext(),
+ browser()->profile(),
+ Profile::CreateStatus::CREATE_STATUS_INITIALIZED,
+ url, "foo@gmail.com", "gaiaid-12345", "password",
+ "", // session index
+ "auth_code", // auth code
+ std::string(),
+ false, // choose what to sync
+ false, // confirm untrusted signin
+ false);
+ SimulateOnClientOAuthSuccess(&helper, "refresh_token");
+ ASSERT_EQ(1ul, token_service()->GetAccounts().size());
+ base::RunLoop().RunUntilIdle();
+}
+
+// Test signin helper does not create sync starter when adding another account
+// to profile.
+IN_PROC_BROWSER_TEST_F(InlineLoginHelperBrowserTest,
+ AddAccountsCallsUpdateCredentials) {
+ ASSERT_EQ(0ul, token_service()->GetAccounts().size());
+
+ // See Source enum in components/signin/core/browser/signin_metrics.h for
+ // possible values of access_point=, reason=.
+ GURL url("chrome://chrome-signin/?access_point=10&reason=1");
+ base::WeakPtr<InlineLoginHandlerImpl> handler;
+ InlineSigninHelper helper(handler, browser()->profile()->GetRequestContext(),
+ browser()->profile(),
+ Profile::CreateStatus::CREATE_STATUS_INITIALIZED,
+ url, "foo@gmail.com", "gaiaid-12345", "password",
+ "", // session index
+ "auth_code", // auth code
+ std::string(),
+ false, // choose what to sync
+ false, // confirm untrusted signin
+ false);
+ SimulateOnClientOAuthSuccess(&helper, "refresh_token");
+ ASSERT_EQ(1ul, token_service()->GetAccounts().size());
+ base::RunLoop().RunUntilIdle();
+}
+
+IN_PROC_BROWSER_TEST_F(InlineLoginHelperBrowserTest,
+ ForceSigninWithUserManager) {
+ GURL url("chrome://chrome-signin/?access_point=0&reason=0");
+ base::WeakPtr<InlineLoginHandlerImpl> handler;
+ // MockSyncStarterInlineSigninHelper will delete itself when done using
+ // base::ThreadTaskRunnerHandle::DeleteSoon(), so need to delete here. But
+ // do need the RunUntilIdle() at the end.
+ MockSyncStarterInlineSigninHelper* helper =
+ new MockSyncStarterInlineSigninHelper(
+ handler, browser()->profile()->GetRequestContext(),
+ browser()->profile(), url, "foo@gmail.com", "gaiaid-12345",
+ "password", "", "auth_code", std::string(), false, false, true);
+ EXPECT_CALL(
+ *helper,
+ CreateSyncStarter(_, _, _, _, "refresh_token",
+ OneClickSigninSyncStarter::CURRENT_PROFILE,
+ OneClickSigninSyncStarter::CONFIRM_SYNC_SETTINGS_FIRST,
+ OneClickSigninSyncStarter::CONFIRM_AFTER_SIGNIN));
+
+ ProfileAttributesEntry* entry;
+ ASSERT_TRUE(g_browser_process->profile_manager()
+ ->GetProfileAttributesStorage()
+ .GetProfileAttributesWithPath(browser()->profile()->GetPath(),
+ &entry));
+ entry->SetIsSigninRequired(true);
+
+ ASSERT_EQ(1ul, BrowserList::GetInstance()->size());
+ SimulateOnClientOAuthSuccess(helper, "refresh_token");
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(2ul, BrowserList::GetInstance()->size());
+ ASSERT_FALSE(entry->IsSigninRequired());
+}
+
+class InlineLoginUISafeIframeBrowserTest : public InProcessBrowserTest {
+ public:
+ FooWebUIProvider& foo_provider() { return foo_provider_; }
+
+ private:
+ void SetUp() override {
+ // Don't spin up the IO thread yet since no threads are allowed while
+ // spawning sandbox host process. See crbug.com/322732.
+ ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
+
+ InProcessBrowserTest::SetUp();
+ }
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ const GURL& base_url = embedded_test_server()->base_url();
+ command_line->AppendSwitchASCII(::switches::kGaiaUrl, base_url.spec());
+ command_line->AppendSwitchASCII(::switches::kLsoUrl, base_url.spec());
+ command_line->AppendSwitchASCII(::switches::kGoogleApisUrl,
+ base_url.spec());
+ }
+
+ void SetUpOnMainThread() override {
+ embedded_test_server()->StartAcceptingConnections();
+
+ content::WebUIControllerFactory::UnregisterFactoryForTesting(
+ ChromeWebUIControllerFactory::GetInstance());
+ test_factory_.reset(new TestChromeWebUIControllerFactory);
+ content::WebUIControllerFactory::RegisterFactory(test_factory_.get());
+ test_factory_->AddFactoryOverride(
+ GURL(kFooWebUIURL).host(), &foo_provider_);
+ }
+
+ void TearDownOnMainThread() override {
+ test_factory_->RemoveFactoryOverride(GURL(kFooWebUIURL).host());
+ content::WebUIControllerFactory::UnregisterFactoryForTesting(
+ test_factory_.get());
+ test_factory_.reset();
+ EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
+ }
+
+ FooWebUIProvider foo_provider_;
+ std::unique_ptr<TestChromeWebUIControllerFactory> test_factory_;
+};
+
+// Make sure that the foo webui handler is working properly and that it gets
+// created when navigated to normally.
+IN_PROC_BROWSER_TEST_F(InlineLoginUISafeIframeBrowserTest, Basic) {
+ const GURL kUrl(kFooWebUIURL);
+ EXPECT_CALL(foo_provider(), NewWebUI(_, ::testing::Eq(kUrl)))
+ .WillOnce(ReturnNewWebUI());
+ ui_test_utils::NavigateToURL(browser(), GURL(kFooWebUIURL));
+}
+
+// Make sure that the foo webui handler does not get created when we try to
+// load it inside the iframe of the login ui.
+IN_PROC_BROWSER_TEST_F(InlineLoginUISafeIframeBrowserTest, NoWebUIInIframe) {
+ GURL url = GetSigninPromoURL().Resolve(
+ "?source=0&access_point=0&reason=0&frameUrl=chrome://foo");
+ EXPECT_CALL(foo_provider(), NewWebUI(_, _)).Times(0);
+ ui_test_utils::NavigateToURL(browser(), url);
+}
+
+// Make sure that "success.html" can be loaded by chrome://chrome-signin.
+// http://crbug.com/709117.
+// Flaky on Linux and Mac. http://crbug.com/722164.
+IN_PROC_BROWSER_TEST_F(InlineLoginUISafeIframeBrowserTest,
+ DISABLED_LoadSuccessContinueURL) {
+ ui_test_utils::NavigateToURL(browser(), GetSigninPromoURL());
+ WaitUntilUIReady(browser());
+
+ const std::string success_url =
+ GaiaUrls::GetInstance()->signin_completed_continue_url().spec();
+ const char* kLoadSuccessPageScript =
+ "var handler = function(e) {"
+ " if (e.url == '%s') {"
+ " window.domAutomationController.send('success_page_loaded');"
+ " }"
+ "};"
+ "var extension_webview = inline.login.getAuthExtHost().webview_;"
+ "extension_webview.addEventListener('loadcommit', handler);"
+ "extension_webview.src = '%s';";
+ std::string script = base::StringPrintf(
+ kLoadSuccessPageScript, success_url.c_str(), success_url.c_str());
+
+ std::string message;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ browser()->tab_strip_model()->GetActiveWebContents(), script, &message));
+ EXPECT_EQ("success_page_loaded", message);
+}
+
+// Make sure that the gaia iframe cannot trigger top-frame navigation.
+// TODO(guohui): flaky on trybot crbug/364759.
+IN_PROC_BROWSER_TEST_F(InlineLoginUISafeIframeBrowserTest,
+ TopFrameNavigationDisallowed) {
+ // Loads into gaia iframe a web page that attempts to deframe on load.
+ GURL deframe_url(embedded_test_server()->GetURL("/login/deframe.html"));
+ GURL url(net::AppendOrReplaceQueryParameter(GetSigninPromoURL(), "frameUrl",
+ deframe_url.spec()));
+ ui_test_utils::NavigateToURL(browser(), url);
+ WaitUntilUIReady(browser());
+
+ content::WebContents* contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ EXPECT_EQ(url, contents->GetVisibleURL());
+
+ content::NavigationController& controller = contents->GetController();
+ EXPECT_TRUE(controller.GetPendingEntry() == NULL);
+}
+
+// Flaky on CrOS, http://crbug.com/364759.
+// Also flaky on Mac, http://crbug.com/442674.
+// Also flaky on Linux which is just too flaky
+IN_PROC_BROWSER_TEST_F(InlineLoginUISafeIframeBrowserTest,
+ DISABLED_NavigationToOtherChromeURLDisallowed) {
+ ui_test_utils::NavigateToURL(browser(), GetSigninPromoURL());
+ WaitUntilUIReady(browser());
+
+ content::WebContents* contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(content::ExecuteScript(
+ contents, "window.location.href = 'chrome://foo'"));
+
+ content::TestNavigationObserver navigation_observer(contents, 1);
+ navigation_observer.Wait();
+
+ EXPECT_EQ(GURL("about:blank"), contents->GetVisibleURL());
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/login_ui_service.cc b/chromium/chrome/browser/ui/webui/signin/login_ui_service.cc
new file mode 100644
index 00000000000..a48468537b8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/login_ui_service.cc
@@ -0,0 +1,97 @@
+// 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/ui/webui/signin/login_ui_service.h"
+
+#include "build/build_config.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/signin_promo.h"
+#include "chrome/browser/ui/browser.h"
+#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/signin_header_helper.h"
+#include "components/signin/core/common/profile_management_switches.h"
+
+LoginUIService::LoginUIService(Profile* profile)
+#if !defined(OS_CHROMEOS)
+ : profile_(profile)
+#endif
+{
+}
+
+LoginUIService::~LoginUIService() {}
+
+void LoginUIService::AddObserver(LoginUIService::Observer* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void LoginUIService::RemoveObserver(LoginUIService::Observer* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
+LoginUIService::LoginUI* LoginUIService::current_login_ui() const {
+ return ui_list_.empty() ? nullptr : ui_list_.front();
+}
+
+void LoginUIService::SetLoginUI(LoginUI* ui) {
+ ui_list_.remove(ui);
+ ui_list_.push_front(ui);
+}
+
+void LoginUIService::LoginUIClosed(LoginUI* ui) {
+ ui_list_.remove(ui);
+}
+
+void LoginUIService::SyncConfirmationUIClosed(
+ SyncConfirmationUIClosedResult result) {
+ for (Observer& observer : observer_list_)
+ observer.OnSyncConfirmationUIClosed(result);
+}
+
+void LoginUIService::ShowLoginPopup() {
+#if defined(OS_CHROMEOS)
+ NOTREACHED();
+#else
+ chrome::ScopedTabbedBrowserDisplayer displayer(profile_);
+ chrome::ShowBrowserSignin(
+ displayer.browser(),
+ signin_metrics::AccessPoint::ACCESS_POINT_EXTENSIONS);
+#endif
+}
+
+void LoginUIService::DisplayLoginResult(Browser* browser,
+ const base::string16& error_message,
+ const base::string16& email) {
+#if defined(OS_CHROMEOS)
+ // ChromeOS doesn't have the avatar bubble so it never calls this function.
+ NOTREACHED();
+#else
+ last_login_result_ = error_message;
+ last_login_error_email_ = email;
+ if (!error_message.empty()) {
+ if (browser)
+ browser->signin_view_controller()->ShowModalSigninErrorDialog(browser);
+ else
+ UserManagerProfileDialog::DisplayErrorMessage();
+ } else if (browser) {
+ browser->window()->ShowAvatarBubbleFromAvatarButton(
+ error_message.empty() ? BrowserWindow::AVATAR_BUBBLE_MODE_CONFIRM_SIGNIN
+ : BrowserWindow::AVATAR_BUBBLE_MODE_SHOW_ERROR,
+ signin::ManageAccountsParams(),
+ signin_metrics::AccessPoint::ACCESS_POINT_EXTENSIONS, false);
+ }
+#endif
+}
+
+const base::string16& LoginUIService::GetLastLoginResult() const {
+ return last_login_result_;
+}
+
+const base::string16& LoginUIService::GetLastLoginErrorEmail() const {
+ return last_login_error_email_;
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/login_ui_service.h b/chromium/chrome/browser/ui/webui/signin/login_ui_service.h
new file mode 100644
index 00000000000..ff46d43afd6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/login_ui_service.h
@@ -0,0 +1,112 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_LOGIN_UI_SERVICE_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_LOGIN_UI_SERVICE_H_
+
+#include <list>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/strings/string16.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+class Browser;
+class Profile;
+
+// The LoginUIService helps track per-profile information for the login related
+// UIs - for example, whether there is login UI currently on-screen.
+class LoginUIService : public KeyedService {
+ public:
+ // Various UI components implement this API to allow LoginUIService to
+ // manipulate their associated login UI.
+ class LoginUI {
+ public:
+ // Invoked when the login UI should be brought to the foreground.
+ virtual void FocusUI() = 0;
+
+ protected:
+ virtual ~LoginUI() {}
+ };
+
+ // Used when the sync confirmation UI is closed to signify which option was
+ // selected by the user.
+ enum SyncConfirmationUIClosedResult {
+ // Start sync immediately.
+ SYNC_WITH_DEFAULT_SETTINGS,
+ // Show the user the sync settings before starting sync.
+ CONFIGURE_SYNC_FIRST,
+ // The signing process was aborted, don't start sync or show settings.
+ ABORT_SIGNIN,
+ };
+
+ // Interface for obervers of LoginUIService.
+ class Observer {
+ public:
+ // Called when the sync confirmation UI is closed. |result| indicates the
+ // option chosen by the user in the confirmation UI.
+ virtual void OnSyncConfirmationUIClosed(
+ SyncConfirmationUIClosedResult result) {}
+
+ protected:
+ virtual ~Observer() {}
+ };
+
+ explicit LoginUIService(Profile* profile);
+ ~LoginUIService() override;
+
+ // |observer| The observer to add or remove; cannot be NULL.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ // Gets the currently active login UI, or null if no login UI is active.
+ LoginUI* current_login_ui() const;
+
+ // Sets the currently active login UI. Callers must call LoginUIClosed when
+ // |ui| is no longer valid.
+ void SetLoginUI(LoginUI* ui);
+
+ // Called when login UI is closed.
+ void LoginUIClosed(LoginUI* ui);
+
+ // Called when the sync confirmation UI is closed. |result| indicates the
+ // option chosen by the user in the confirmation UI.
+ void SyncConfirmationUIClosed(SyncConfirmationUIClosedResult result);
+
+ // Delegate to an existing login dialog if one exists.
+ // If not, we make a new popup dialog window, and set it to
+ // chrome://signin to ask the user to sign in to chrome.
+ void ShowLoginPopup();
+
+ // Displays login results. This is either the Modal Signin Error dialog if
+ // |error_message| is a non-empty string, or the User Menu with a blue header
+ // toast otherwise.
+ void DisplayLoginResult(Browser* browser,
+ const base::string16& error_message,
+ const base::string16& email);
+
+ // Gets the last login result set through |DisplayLoginResult|.
+ const base::string16& GetLastLoginResult() const;
+
+ // Gets the last email used for signing in when a signin error occured; set
+ // through |DisplayLoginResult|.
+ const base::string16& GetLastLoginErrorEmail() const;
+
+ private:
+ // Weak pointers to the recently opened UIs, with the most recent in front.
+ std::list<LoginUI*> ui_list_;
+#if !defined(OS_CHROMEOS)
+ Profile* profile_;
+#endif
+
+ // List of observers.
+ base::ObserverList<Observer> observer_list_;
+
+ base::string16 last_login_result_;
+ base::string16 last_login_error_email_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoginUIService);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_LOGIN_UI_SERVICE_H_
diff --git a/chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.cc b/chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.cc
new file mode 100644
index 00000000000..66c7b5c39ea
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.cc
@@ -0,0 +1,45 @@
+// 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/ui/webui/signin/login_ui_service_factory.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/signin/login_ui_service.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+
+LoginUIServiceFactory::LoginUIServiceFactory()
+ : BrowserContextKeyedServiceFactory(
+ "LoginUIServiceFactory",
+ BrowserContextDependencyManager::GetInstance()) {
+}
+
+LoginUIServiceFactory::~LoginUIServiceFactory() {}
+
+// static
+LoginUIService* LoginUIServiceFactory::GetForProfile(Profile* profile) {
+ return static_cast<LoginUIService*>(
+ GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+// static
+LoginUIServiceFactory* LoginUIServiceFactory::GetInstance() {
+ return base::Singleton<LoginUIServiceFactory>::get();
+}
+
+// static
+base::Closure LoginUIServiceFactory::GetShowLoginPopupCallbackForProfile(
+ Profile* profile) {
+ return base::Bind(
+ &LoginUIService::ShowLoginPopup,
+ base::Unretained(LoginUIServiceFactory::GetForProfile(profile)));
+}
+
+KeyedService* LoginUIServiceFactory::BuildServiceInstanceFor(
+ content::BrowserContext* profile) const {
+ return new LoginUIService(static_cast<Profile*>(profile));
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.h b/chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.h
new file mode 100644
index 00000000000..d940d346685
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/login_ui_service_factory.h
@@ -0,0 +1,46 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_LOGIN_UI_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_LOGIN_UI_SERVICE_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class LoginUIService;
+class Profile;
+
+// Singleton that owns all LoginUIServices and associates them with
+// Profiles. Listens for the Profile's destruction notification and cleans up
+// the associated LoginUIService.
+class LoginUIServiceFactory : public BrowserContextKeyedServiceFactory {
+ public:
+ // Returns the instance of LoginUIService associated with this profile
+ // (creating one if none exists). Returns NULL if this profile cannot have a
+ // LoginUIService (for example, if |profile| is incognito).
+ static LoginUIService* GetForProfile(Profile* profile);
+
+ // Returns an instance of the LoginUIServiceFactory singleton.
+ static LoginUIServiceFactory* GetInstance();
+
+ // Helper method that returns a closure displaying the login popup for
+ // |profile|.
+ // This closure must not be called after the LoginUIService is destroyed.
+ static base::Closure GetShowLoginPopupCallbackForProfile(Profile* profile);
+
+ private:
+ friend struct base::DefaultSingletonTraits<LoginUIServiceFactory>;
+
+ LoginUIServiceFactory();
+ ~LoginUIServiceFactory() override;
+
+ // BrowserContextKeyedServiceFactory:
+ KeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* profile) const override;
+
+ DISALLOW_COPY_AND_ASSIGN(LoginUIServiceFactory);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_LOGIN_UI_SERVICE_FACTORY_H_
diff --git a/chromium/chrome/browser/ui/webui/signin/login_ui_service_unittest.cc b/chromium/chrome/browser/ui/webui/signin/login_ui_service_unittest.cc
new file mode 100644
index 00000000000..ffe14f05f88
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/login_ui_service_unittest.cc
@@ -0,0 +1,49 @@
+// 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/ui/webui/signin/login_ui_service.h"
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class TestLoginUI : public LoginUIService::LoginUI {
+ public:
+ TestLoginUI() { }
+ ~TestLoginUI() override {}
+ void FocusUI() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestLoginUI);
+};
+
+TEST(LoginUIServiceTest, CanSetMultipleLoginUIs) {
+ LoginUIService service(nullptr);
+
+ EXPECT_EQ(nullptr, service.current_login_ui());
+
+ TestLoginUI ui;
+ service.SetLoginUI(&ui);
+ EXPECT_EQ(&ui, service.current_login_ui());
+
+ // Test that we can replace the active login UI.
+ TestLoginUI other_ui;
+ service.SetLoginUI(&other_ui);
+ EXPECT_EQ(&other_ui, service.current_login_ui());
+
+ // Test that closing the non-active login UI has no effect.
+ service.LoginUIClosed(&ui);
+ EXPECT_EQ(&other_ui, service.current_login_ui());
+
+ // Test that closing the foreground UI yields the background UI.
+ service.SetLoginUI(&ui);
+ EXPECT_EQ(&ui, service.current_login_ui());
+ service.LoginUIClosed(&ui);
+ EXPECT_EQ(&other_ui, service.current_login_ui());
+
+ // Test that closing the last login UI makes the current login UI nullptr.
+ service.LoginUIClosed(&other_ui);
+ EXPECT_EQ(nullptr, service.current_login_ui());
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/login_ui_test_utils.cc b/chromium/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
new file mode 100644
index 00000000000..06fe6ac739b
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
@@ -0,0 +1,293 @@
+// 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/ui/webui/signin/login_ui_test_utils.h"
+
+#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/signin/signin_promo.h"
+#include "chrome/browser/signin/signin_tracker_factory.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/signin_view_controller_delegate.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/signin/signin_utils.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+
+using content::MessageLoopRunner;
+
+// anonymous namespace for signin with UI helper functions.
+namespace {
+
+// The SignInObserver observes the signin manager and blocks until a
+// GoogleSigninSucceeded or a GoogleSigninFailed notification is fired.
+class SignInObserver : public SigninTracker::Observer {
+ public:
+ explicit SignInObserver(bool wait_for_account_cookies)
+ : seen_(false),
+ running_(false),
+ signed_in_(false),
+ wait_for_account_cookies_(wait_for_account_cookies) {}
+
+ virtual ~SignInObserver() {}
+
+ // Returns whether a GoogleSigninSucceeded event has happened.
+ bool DidSignIn() {
+ return signed_in_;
+ }
+
+ // Blocks and waits until the user signs in. Wait() does not block if a
+ // GoogleSigninSucceeded or a GoogleSigninFailed has already occurred.
+ void Wait() {
+ if (seen_)
+ return;
+
+ running_ = true;
+ message_loop_runner_ = new MessageLoopRunner;
+ message_loop_runner_->Run();
+ EXPECT_TRUE(seen_);
+ }
+
+ void SigninFailed(const GoogleServiceAuthError& error) override {
+ DVLOG(1) << "Google signin failed.";
+ QuitLoopRunner();
+ }
+
+ void AccountAddedToCookie(const GoogleServiceAuthError& error) override {
+ if (!wait_for_account_cookies_)
+ return;
+ if (error.state() != GoogleServiceAuthError::NONE) {
+ DVLOG(1) << "Error signing the account, error " << error.state();
+ } else {
+ DVLOG(1) << "Account cookies are added to cookie jar.";
+ signed_in_ = true;
+ }
+ QuitLoopRunner();
+ }
+
+ void SigninSuccess() override {
+ DVLOG(1) << "Google signin succeeded.";
+ if (wait_for_account_cookies_)
+ return;
+ signed_in_ = true;
+ QuitLoopRunner();
+ }
+
+ void QuitLoopRunner() {
+ seen_ = true;
+ if (!running_)
+ return;
+ message_loop_runner_->Quit();
+ running_ = false;
+ }
+
+ private:
+ // Bool to mark an observed event as seen prior to calling Wait(), used to
+ // prevent the observer from blocking.
+ bool seen_;
+ // True is the message loop runner is running.
+ bool running_;
+ // True if a GoogleSigninSucceeded event has been observed.
+ bool signed_in_;
+ // Whether we should block until the account cookies are added or not.
+ // If false, we only wait until SigninSuccess event is fired which happens
+ // prior to adding account to cookie.
+ bool wait_for_account_cookies_;
+ scoped_refptr<MessageLoopRunner> message_loop_runner_;
+};
+
+void RunLoopFor(base::TimeDelta duration) {
+ base::RunLoop run_loop;
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, run_loop.QuitClosure(), duration);
+ run_loop.Run();
+}
+
+} // anonymous namespace
+
+
+namespace login_ui_test_utils {
+
+void WaitUntilUIReady(Browser* browser) {
+ std::string message;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ browser->tab_strip_model()->GetActiveWebContents(),
+ "if (!inline.login.getAuthExtHost())"
+ " inline.login.initialize();"
+ "var handler = function() {"
+ " window.domAutomationController.send('ready');"
+ "};"
+ "if (inline.login.isAuthReady())"
+ " handler();"
+ "else"
+ " inline.login.getAuthExtHost().addEventListener('ready', handler);",
+ &message));
+ ASSERT_EQ("ready", message);
+}
+
+void WaitUntilElementExistsInSigninFrame(Browser* browser,
+ const std::string& element_id) {
+ std::string message;
+ std::string js =
+ "function WaitForElementById(elementId) {"
+ " var retries = 10; /* 10 seconds. */"
+ " function CheckelementExists() {"
+ " if (document.getElementById(elementId) != null) {"
+ " window.domAutomationController.send('found');"
+ " } else if (retries > 0) { "
+ " retries--;"
+ " window.setTimeout(CheckelementExists, 1000);"
+ " } else {"
+ " window.domAutomationController.send('failed');"
+ " }"
+ " }"
+ " CheckelementExists();"
+ "}"
+ "WaitForElementById('" + element_id + "');";
+ content::WebContents* web_contents =
+ browser->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ signin::GetAuthFrame(web_contents, "signin-frame"), js, &message));
+
+ ASSERT_EQ("found", message) <<
+ "Failed to find element with id " << element_id;
+}
+
+bool ElementExistsInSigninFrame(Browser* browser,
+ const std::string& element_id) {
+ content::WebContents* web_contents =
+ browser->tab_strip_model()->GetActiveWebContents();
+ bool result = false;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+ signin::GetAuthFrame(web_contents, "signin-frame"),
+ "window.domAutomationController.send("
+ " document.getElementById('" +
+ element_id + "') != null);",
+ &result));
+ return result;
+}
+
+void SigninInNewGaiaFlow(Browser* browser,
+ const std::string& email,
+ const std::string& password) {
+ std::string js = "document.getElementById('Email').value = '" + email + "';"
+ "document.getElementById('next').click();";
+
+ content::WebContents* web_contents =
+ browser->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(content::ExecuteScript(
+ signin::GetAuthFrame(web_contents, "signin-frame"), js));
+
+ WaitUntilElementExistsInSigninFrame(browser, "Passwd");
+ js = "document.getElementById('Passwd').value = '" + password + "';"
+ "document.getElementById('signIn').click();";
+
+ ASSERT_TRUE(content::ExecuteScript(
+ signin::GetAuthFrame(web_contents, "signin-frame"), js));
+}
+
+void SigninInOldGaiaFlow(Browser* browser,
+ const std::string& email,
+ const std::string& password) {
+ std::string js =
+ "document.getElementById('Email').value = '" + email + "';"
+ "document.getElementById('Passwd').value = '" + password + "';"
+ "document.getElementById('signIn').click();";
+
+ content::WebContents* web_contents =
+ browser->tab_strip_model()->GetActiveWebContents();
+ ASSERT_TRUE(content::ExecuteScript(
+ signin::GetAuthFrame(web_contents, "signin-frame"), js));
+}
+
+void ExecuteJsToSigninInSigninFrame(Browser* browser,
+ const std::string& email,
+ const std::string& password) {
+ WaitUntilElementExistsInSigninFrame(browser, "Email");
+ if (ElementExistsInSigninFrame(browser, "next"))
+ SigninInNewGaiaFlow(browser, email, password);
+ else
+ SigninInOldGaiaFlow(browser, email, password);
+}
+
+bool SignInWithUI(Browser* browser,
+ const std::string& username,
+ const std::string& password,
+ bool wait_for_account_cookies,
+ signin_metrics::AccessPoint access_point,
+ signin_metrics::Reason signin_reason) {
+ SignInObserver signin_observer(wait_for_account_cookies);
+ std::unique_ptr<SigninTracker> tracker =
+ SigninTrackerFactory::CreateForProfile(browser->profile(),
+ &signin_observer);
+
+ GURL signin_url = signin::GetPromoURL(access_point, signin_reason, false);
+ DVLOG(1) << "Navigating to " << signin_url;
+ // For some tests, the window is not shown yet and this might be the first tab
+ // navigation, so GetActiveWebContents() for CURRENT_TAB is NULL. That's why
+ // we use NEW_FOREGROUND_TAB rather than the CURRENT_TAB used by default in
+ // ui_test_utils::NavigateToURL().
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser, signin_url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+
+ DVLOG(1) << "Wait for login UI to be ready.";
+ WaitUntilUIReady(browser);
+ DVLOG(1) << "Sign in user: " << username;
+ ExecuteJsToSigninInSigninFrame(browser, username, password);
+ signin_observer.Wait();
+ return signin_observer.DidSignIn();
+}
+
+bool SignInWithUI(Browser* browser,
+ const std::string& username,
+ const std::string& password) {
+ return SignInWithUI(browser, username, password,
+ false /* wait_for_account_cookies */,
+ signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE,
+ signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT);
+}
+
+bool TryDismissSyncConfirmationDialog(Browser* browser) {
+#if defined(OS_CHROMEOS)
+ NOTREACHED();
+ return false;
+#else
+ SigninViewController* signin_view_controller =
+ browser->signin_view_controller();
+ DCHECK_NE(signin_view_controller, nullptr);
+ SigninViewControllerDelegate* delegate = signin_view_controller->delegate();
+ if (delegate == nullptr)
+ return false;
+ content::WebContents* dialog_web_contents =
+ delegate->web_contents_for_testing();
+ DCHECK_NE(dialog_web_contents, nullptr);
+ std::string message;
+ std::string js =
+ "if (document.getElementById('confirmButton') == null) {"
+ " window.domAutomationController.send('NotFound');"
+ "} else {"
+ " document.getElementById('confirmButton').click();"
+ " window.domAutomationController.send('Ok');"
+ "}";
+ EXPECT_TRUE(content::ExecuteScriptAndExtractString(dialog_web_contents, js,
+ &message));
+ return message == "Ok";
+#endif
+}
+
+bool DismissSyncConfirmationDialog(Browser* browser, base::TimeDelta timeout) {
+ const base::Time expire_time = base::Time::Now() + timeout;
+ while (base::Time::Now() <= expire_time) {
+ if (TryDismissSyncConfirmationDialog(browser))
+ return true;
+ RunLoopFor(base::TimeDelta::FromMilliseconds(1000));
+ }
+ return false;
+}
+
+} // namespace login_ui_test_utils
diff --git a/chromium/chrome/browser/ui/webui/signin/login_ui_test_utils.h b/chromium/chrome/browser/ui/webui/signin/login_ui_test_utils.h
new file mode 100644
index 00000000000..71c21725408
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/login_ui_test_utils.h
@@ -0,0 +1,70 @@
+// 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_UI_WEBUI_SIGNIN_LOGIN_UI_TEST_UTILS_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_LOGIN_UI_TEST_UTILS_H_
+
+#include <string>
+
+#include "base/time/time.h"
+#include "components/signin/core/browser/signin_metrics.h"
+
+class Browser;
+
+namespace login_ui_test_utils {
+
+// Blocks until the login UI is available and ready for authorization.
+void WaitUntilUIReady(Browser* browser);
+
+// Blocks until an element with id |element_id| exists in the signin page.
+void WaitUntilElementExistsInSigninFrame(Browser* browser,
+ const std::string& element_id);
+
+// Returns whether an element with id |element_id| exists in the signin page.
+bool ElementExistsInSigninFrame(Browser* browser,
+ const std::string& element_id);
+
+// Executes JavaScript code to sign in a user with email and password to the
+// auth iframe hosted by gaia_auth extension. This function automatically
+// detects the version of GAIA sign in page to use.
+void ExecuteJsToSigninInSigninFrame(Browser* browser,
+ const std::string& email,
+ const std::string& password);
+
+// Executes JS to sign in the user in the new GAIA sign in flow.
+void SigninInNewGaiaFlow(Browser* browser,
+ const std::string& email,
+ const std::string& password);
+
+// Executes JS to sign in the user in the old GAIA sign in flow.
+void SigninInOldGaiaFlow(Browser* browser,
+ const std::string& email,
+ const std::string& password);
+
+// A function to sign in a user using Chrome sign-in UI interface.
+// This will block until a signin succeeded or failed notification is observed.
+// In case |wait_for_account_cookies|, the call will block until the account
+// cookies have been written to the cookie jar.
+// |access_point| identifies the access point used to load the signin page.
+bool SignInWithUI(Browser* browser,
+ const std::string& email,
+ const std::string& password,
+ bool wait_for_account_cookies,
+ signin_metrics::AccessPoint access_point,
+ signin_metrics::Reason signin_reason);
+
+// Most common way to sign in a user, it does not wait for cookies to be set
+// and uses the SOURCE_START_PAGE as signin source.
+bool SignInWithUI(Browser* browser,
+ const std::string& email,
+ const std::string& password);
+
+// Waits for sync confirmation dialog to get displayed, then executes javascript
+// to click on confirm button. Returns false if dialog wasn't dismissed before
+// |timeout|.
+bool DismissSyncConfirmationDialog(Browser* browser, base::TimeDelta timeout);
+
+} // namespace login_ui_test_utils
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_LOGIN_UI_TEST_UTILS_H_
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
new file mode 100644
index 00000000000..f208584461f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/md_user_manager_ui.cc
@@ -0,0 +1,134 @@
+// 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 "chrome/browser/ui/webui/signin/md_user_manager_ui.h"
+
+#include <string>
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_shortcut_manager.h"
+#include "chrome/browser/ui/webui/signin/signin_create_profile_handler.h"
+#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/features.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/settings_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/web_ui_util.h"
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+#include "chrome/browser/ui/webui/signin/signin_supervised_user_import_handler.h"
+#endif
+
+MDUserManagerUI::MDUserManagerUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ auto signin_create_profile_handler =
+ base::MakeUnique<SigninCreateProfileHandler>();
+ signin_create_profile_handler_ = signin_create_profile_handler.get();
+ web_ui->AddMessageHandler(std::move(signin_create_profile_handler));
+ auto user_manager_screen_handler =
+ base::MakeUnique<UserManagerScreenHandler>();
+ user_manager_screen_handler_ = user_manager_screen_handler.get();
+ web_ui->AddMessageHandler(std::move(user_manager_screen_handler));
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ auto signin_supervised_user_import_handler =
+ base::MakeUnique<SigninSupervisedUserImportHandler>();
+ signin_supervised_user_import_handler_ =
+ signin_supervised_user_import_handler.get();
+ web_ui->AddMessageHandler(std::move(signin_supervised_user_import_handler));
+#endif
+
+ base::DictionaryValue localized_strings;
+ GetLocalizedStrings(&localized_strings);
+
+ Profile* profile = Profile::FromWebUI(web_ui);
+ // Set up the chrome://md-user-manager/ source.
+ content::WebUIDataSource::Add(profile, CreateUIDataSource(localized_strings));
+
+ // Set up the chrome://theme/ source
+ ThemeSource* theme = new ThemeSource(profile);
+ content::URLDataSource::Add(profile, theme);
+}
+
+MDUserManagerUI::~MDUserManagerUI() {}
+
+content::WebUIDataSource* MDUserManagerUI::CreateUIDataSource(
+ const base::DictionaryValue& localized_strings) {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIMdUserManagerHost);
+ source->AddLocalizedStrings(localized_strings);
+ source->AddBoolean("profileShortcutsEnabled",
+ ProfileShortcutManager::IsFeatureEnabled());
+ source->AddBoolean("isForceSigninEnabled", signin::IsForceSigninEnabled());
+
+ source->SetJsonPath("strings.js");
+
+ source->AddResourcePath("control_bar.html", IDR_MD_CONTROL_BAR_HTML);
+ source->AddResourcePath("control_bar.js", IDR_MD_CONTROL_BAR_JS);
+ source->AddResourcePath("create_profile.html", IDR_MD_CREATE_PROFILE_HTML);
+ source->AddResourcePath("create_profile.js", IDR_MD_CREATE_PROFILE_JS);
+ source->AddResourcePath("error_dialog.html", IDR_MD_ERROR_DIALOG_HTML);
+ source->AddResourcePath("error_dialog.js", IDR_MD_ERROR_DIALOG_JS);
+ source->AddResourcePath("icons.html", IDR_MD_USER_MANAGER_ICONS_HTML);
+ source->AddResourcePath("import_supervised_user.html",
+ IDR_MD_IMPORT_SUPERVISED_USER_HTML);
+ source->AddResourcePath("import_supervised_user.js",
+ IDR_MD_IMPORT_SUPERVISED_USER_JS);
+ source->AddResourcePath("profile_browser_proxy.html",
+ IDR_MD_PROFILE_BROWSER_PROXY_HTML);
+ source->AddResourcePath("profile_browser_proxy.js",
+ IDR_MD_PROFILE_BROWSER_PROXY_JS);
+ source->AddResourcePath("shared_styles.html",
+ IDR_MD_USER_MANAGER_SHARED_STYLES_HTML);
+ source->AddResourcePath("strings.html", IDR_MD_USER_MANAGER_STRINGS_HTML);
+ source->AddResourcePath("supervised_user_create_confirm.html",
+ IDR_MD_SUPERVISED_USER_CREATE_CONFIRM_HTML);
+ source->AddResourcePath("supervised_user_create_confirm.js",
+ IDR_MD_SUPERVISED_USER_CREATE_CONFIRM_JS);
+ source->AddResourcePath("supervised_user_learn_more.html",
+ IDR_MD_SUPERVISED_USER_LEARN_MORE_HTML);
+ source->AddResourcePath("supervised_user_learn_more.js",
+ IDR_MD_SUPERVISED_USER_LEARN_MORE_JS);
+ source->AddResourcePath("user_manager.js", IDR_MD_USER_MANAGER_JS);
+ source->AddResourcePath("user_manager_dialog.html",
+ IDR_MD_USER_MANAGER_DIALOG_HTML);
+ source->AddResourcePath("user_manager_dialog.js",
+ IDR_MD_USER_MANAGER_DIALOG_JS);
+ source->AddResourcePath("user_manager_pages.html",
+ IDR_MD_USER_MANAGER_PAGES_HTML);
+ source->AddResourcePath("user_manager_pages.js",
+ IDR_MD_USER_MANAGER_PAGES_JS);
+ source->AddResourcePath("user_manager_tutorial.html",
+ IDR_MD_USER_MANAGER_TUTORIAL_HTML);
+ source->AddResourcePath("user_manager_tutorial.js",
+ IDR_MD_USER_MANAGER_TUTORIAL_JS);
+
+ source->SetDefaultResource(IDR_MD_USER_MANAGER_HTML);
+
+ return source;
+}
+
+void MDUserManagerUI::GetLocalizedStrings(
+ base::DictionaryValue* localized_strings) {
+ user_manager_screen_handler_->GetLocalizedValues(localized_strings);
+ signin_create_profile_handler_->GetLocalizedValues(localized_strings);
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ signin_supervised_user_import_handler_->GetLocalizedValues(localized_strings);
+#endif
+ const std::string& app_locale = g_browser_process->GetApplicationLocale();
+ webui::SetLoadTimeDataDefaults(app_locale, localized_strings);
+
+#if defined(GOOGLE_CHROME_BUILD)
+ localized_strings->SetString("buildType", "chrome");
+#else
+ localized_strings->SetString("buildType", "chromium");
+#endif
+}
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
new file mode 100644
index 00000000000..0aa98246525
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/md_user_manager_ui.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_MD_USER_MANAGER_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_MD_USER_MANAGER_UI_H_
+
+#include "base/macros.h"
+#include "chrome/common/features.h"
+#include "content/public/browser/web_ui_controller.h"
+
+class SigninCreateProfileHandler;
+class UserManagerScreenHandler;
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+class SigninSupervisedUserImportHandler;
+#endif
+
+namespace base {
+class DictionaryValue;
+}
+namespace content {
+class WebUIDataSource;
+}
+
+// A WebUI dialog to display available users.
+class MDUserManagerUI : public content::WebUIController {
+ public:
+ explicit MDUserManagerUI(content::WebUI* web_ui);
+ ~MDUserManagerUI() override;
+
+ private:
+ content::WebUIDataSource* CreateUIDataSource(
+ const base::DictionaryValue& localized_strings);
+ void GetLocalizedStrings(base::DictionaryValue* localized_strings);
+
+ SigninCreateProfileHandler* signin_create_profile_handler_ = nullptr;
+ UserManagerScreenHandler* user_manager_screen_handler_ = nullptr;
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ SigninSupervisedUserImportHandler* signin_supervised_user_import_handler_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(MDUserManagerUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_MD_USER_MANAGER_UI_H_
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
new file mode 100644
index 00000000000..c6f32829ea4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc
@@ -0,0 +1,817 @@
+// 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 "chrome/browser/ui/webui/signin/signin_create_profile_handler.h"
+
+#include <stddef.h>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task_runner_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/value_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_avatar_icon_util.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_metrics.h"
+#include "chrome/browser/profiles/profiles_state.h"
+#include "chrome/browser/signin/signin_error_controller_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/ui/browser_finder.h"
+#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/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/signin_error_controller.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+#include "chrome/browser/supervised_user/legacy/supervised_user_registration_utility.h"
+#include "chrome/browser/supervised_user/legacy/supervised_user_sync_service.h"
+#include "chrome/browser/supervised_user/legacy/supervised_user_sync_service_factory.h"
+#include "chrome/browser/supervised_user/supervised_user_service.h"
+#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
+#endif
+
+SigninCreateProfileHandler::SigninCreateProfileHandler()
+ : profile_creation_type_(NO_CREATION_IN_PROGRESS),
+ weak_ptr_factory_(this) {
+ g_browser_process->profile_manager()->
+ GetProfileAttributesStorage().AddObserver(this);
+}
+
+SigninCreateProfileHandler::~SigninCreateProfileHandler() {
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ // Cancellation is only supported for supervised users.
+ CancelProfileRegistration(false);
+#endif
+ g_browser_process->profile_manager()->
+ GetProfileAttributesStorage().RemoveObserver(this);
+}
+
+void SigninCreateProfileHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ localized_strings->SetString(
+ "createDesktopShortcutLabel",
+ l10n_util::GetStringUTF16(
+ IDS_PROFILES_CREATE_DESKTOP_SHORTCUT_LABEL));
+ localized_strings->SetString(
+ "manageProfilesSupervisedSignedInLabel",
+ l10n_util::GetStringUTF16(
+ IDS_PROFILES_CREATE_SUPERVISED_MULTI_SIGNED_IN_LABEL));
+ localized_strings->SetString(
+ "noSignedInUserMessage",
+ l10n_util::GetStringUTF16(
+ IDS_PROFILES_CREATE_SUPERVISED_NO_SIGNED_IN_USER_TEXT));
+ localized_strings->SetString("createProfileConfirm",
+ l10n_util::GetStringUTF16(IDS_SAVE));
+ localized_strings->SetString("learnMore",
+ l10n_util::GetStringUTF16(IDS_LEARN_MORE));
+ localized_strings->SetString(
+ "selectAnAccount",
+ l10n_util::GetStringUTF16(
+ IDS_PROFILES_CREATE_SUPERVISED_SENTINEL_MENU_ITEM_TEXT));
+ localized_strings->SetString(
+ "createProfileTitle",
+ l10n_util::GetStringUTF16(IDS_PROFILES_CREATE_TITLE));
+ localized_strings->SetString(
+ "supervisedUserLearnMoreTitle",
+ l10n_util::GetStringUTF16(IDS_LEGACY_SUPERVISED_USER_LEARN_MORE_TITLE));
+ localized_strings->SetString(
+ "supervisedUserLearnMoreDone",
+ l10n_util::GetStringUTF16(
+ IDS_LEGACY_SUPERVISED_USER_LEARN_MORE_DONE_BUTTON));
+ localized_strings->SetString(
+ "supervisedUserLearnMoreText",
+ l10n_util::GetStringFUTF16(
+ IDS_SUPERVISED_USER_LEARN_MORE_TEXT,
+ base::ASCIIToUTF16(
+ chrome::kLegacySupervisedUserManagementURL),
+ base::ASCIIToUTF16(
+ chrome::kLegacySupervisedUserManagementDisplayURL)));
+ localized_strings->SetString(
+ "importExistingSupervisedUserLink",
+ l10n_util::GetStringUTF16(
+ IDS_IMPORT_EXISTING_LEGACY_SUPERVISED_USER_TITLE));
+ localized_strings->SetString(
+ "manageProfilesExistingSupervisedUser",
+ l10n_util::GetStringUTF16(
+ IDS_PROFILES_CREATE_LEGACY_SUPERVISED_USER_ERROR_EXISTS_REMOTELY));
+ localized_strings->SetString(
+ "managedProfilesExistingLocalSupervisedUser",
+ l10n_util::GetStringUTF16(
+ IDS_PROFILES_CREATE_LEGACY_SUPERVISED_USER_ERROR_EXISTS_LOCALLY));
+ localized_strings->SetString(
+ "custodianAccountNotSelectedError",
+ l10n_util::GetStringUTF16(
+ IDS_PROFILES_CREATE_NO_CUSTODIAN_ACCOUNT_ERROR));
+ localized_strings->SetString(
+ "supervisedUserCreatedTitle",
+ l10n_util::GetStringUTF16(IDS_LEGACY_SUPERVISED_USER_CREATED_TITLE));
+ // The first two substitution parameters remain to be filled by the page JS.
+ localized_strings->SetString(
+ "supervisedUserCreatedText",
+ l10n_util::GetStringFUTF16(
+ IDS_SUPERVISED_USER_CREATED_TEXT,
+ base::ASCIIToUTF16("$1"),
+ base::ASCIIToUTF16("$2"),
+ base::ASCIIToUTF16(chrome::kLegacySupervisedUserManagementURL),
+ base::ASCIIToUTF16(
+ chrome::kLegacySupervisedUserManagementDisplayURL)));
+ localized_strings->SetString(
+ "exitAndChildlockLabel",
+ l10n_util::GetStringUTF16(
+ IDS_PROFILES_PROFILE_SIGNOUT_BUTTON));
+ localized_strings->SetString(
+ "supervisedUserCreatedDone",
+ l10n_util::GetStringUTF16(
+ IDS_LEGACY_SUPERVISED_USER_CREATED_DONE_BUTTON));
+ localized_strings->SetString(
+ "supervisedUserCreatedSwitch",
+ l10n_util::GetStringUTF16(
+ IDS_LEGACY_SUPERVISED_USER_CREATED_SWITCH_BUTTON));
+}
+
+void SigninCreateProfileHandler::RegisterMessages() {
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ // Cancellation is only supported for supervised users.
+ web_ui()->RegisterMessageCallback(
+ "cancelCreateProfile",
+ base::Bind(&SigninCreateProfileHandler::HandleCancelProfileCreation,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "switchToProfile",
+ base::Bind(&SigninCreateProfileHandler::SwitchToProfile,
+ base::Unretained(this)));
+#endif
+ web_ui()->RegisterMessageCallback(
+ "createProfile", base::Bind(&SigninCreateProfileHandler::CreateProfile,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "requestDefaultProfileIcons",
+ base::Bind(&SigninCreateProfileHandler::RequestDefaultProfileIcons,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "requestSignedInProfiles",
+ base::Bind(&SigninCreateProfileHandler::RequestSignedInProfiles,
+ base::Unretained(this)));
+}
+
+void SigninCreateProfileHandler::RequestDefaultProfileIcons(
+ const base::ListValue* args) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "cr.webUIListenerCallback", base::Value("profile-icons-received"),
+ *profiles::GetDefaultProfileAvatarIconsAndLabels());
+
+ SendNewProfileDefaults();
+}
+
+void SigninCreateProfileHandler::SendNewProfileDefaults() {
+ ProfileAttributesStorage& storage =
+ g_browser_process->profile_manager()->GetProfileAttributesStorage();
+ base::DictionaryValue profile_info;
+ profile_info.SetString("name", storage.ChooseNameForNewProfile(0));
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "cr.webUIListenerCallback", base::Value("profile-defaults-received"),
+ profile_info);
+}
+
+void SigninCreateProfileHandler::RequestSignedInProfiles(
+ const base::ListValue* args) {
+ base::ListValue user_info_list;
+ std::vector<ProfileAttributesEntry*> entries =
+ g_browser_process->profile_manager()->
+ GetProfileAttributesStorage().GetAllProfilesAttributesSortedByName();
+ for (ProfileAttributesEntry* entry : entries) {
+ base::string16 username = entry->GetUserName();
+ if (username.empty())
+ continue;
+ base::string16 profile_path = entry->GetPath().AsUTF16Unsafe();
+ std::unique_ptr<base::DictionaryValue> user_info(
+ new base::DictionaryValue());
+ user_info->SetString("username", username);
+ user_info->SetString("profilePath", profile_path);
+
+ user_info_list.Append(std::move(user_info));
+ }
+ web_ui()->CallJavascriptFunctionUnsafe("cr.webUIListenerCallback",
+ base::Value("signedin-users-received"),
+ user_info_list);
+}
+
+void SigninCreateProfileHandler::OnProfileAuthInfoChanged(
+ const base::FilePath& profile_path) {
+ RequestSignedInProfiles(nullptr);
+}
+
+void SigninCreateProfileHandler::CreateProfile(const base::ListValue* args) {
+ if (!profiles::IsMultipleProfilesEnabled())
+ return;
+
+ // We can have only one in progress profile creation
+ // at any given moment, if new ones are initiated just
+ // ignore them until we are done with the old one.
+ if (profile_creation_type_ != NO_CREATION_IN_PROGRESS)
+ return;
+
+ profile_creation_type_ = NON_SUPERVISED_PROFILE_CREATION;
+
+ DCHECK(profile_path_being_created_.empty());
+ profile_creation_start_time_ = base::TimeTicks::Now();
+
+ base::string16 name;
+ std::string icon_url;
+ bool create_shortcut = false;
+ if (args->GetString(0, &name) && args->GetString(1, &icon_url)) {
+ DCHECK(base::IsStringASCII(icon_url));
+ base::TrimWhitespace(name, base::TRIM_ALL, &name);
+ CHECK(!name.empty());
+#ifndef NDEBUG
+ size_t icon_index;
+ DCHECK(profiles::IsDefaultAvatarIconUrl(icon_url, &icon_index));
+#endif
+ args->GetBoolean(2, &create_shortcut);
+ }
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ std::string supervised_user_id;
+ base::FilePath custodian_profile_path;
+ if (GetSupervisedCreateProfileArgs(args, &supervised_user_id,
+ &custodian_profile_path)) {
+ // Load custodian profile.
+ g_browser_process->profile_manager()->CreateProfileAsync(
+ custodian_profile_path,
+ base::Bind(&SigninCreateProfileHandler::LoadCustodianProfileCallback,
+ weak_ptr_factory_.GetWeakPtr(), name, icon_url,
+ create_shortcut, supervised_user_id),
+ base::string16(), std::string(), std::string());
+ } else {
+ DoCreateProfile(name, icon_url, create_shortcut, std::string(), nullptr);
+ }
+#else
+ DoCreateProfile(name, icon_url, create_shortcut, std::string(), nullptr);
+#endif
+}
+
+void SigninCreateProfileHandler::DoCreateProfile(
+ const base::string16& name,
+ const std::string& icon_url,
+ bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* custodian_profile) {
+ ProfileMetrics::LogProfileAddNewUser(ProfileMetrics::ADD_NEW_USER_DIALOG);
+
+ profile_path_being_created_ = ProfileManager::CreateMultiProfileAsync(
+ name, icon_url,
+ base::Bind(&SigninCreateProfileHandler::OnProfileCreated,
+ weak_ptr_factory_.GetWeakPtr(), create_shortcut,
+ supervised_user_id, custodian_profile),
+ supervised_user_id);
+}
+
+void SigninCreateProfileHandler::OnProfileCreated(
+ bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* custodian_profile,
+ Profile* profile,
+ Profile::CreateStatus status) {
+ if (status != Profile::CREATE_STATUS_CREATED)
+ RecordProfileCreationMetrics(status);
+
+ switch (status) {
+ case Profile::CREATE_STATUS_LOCAL_FAIL: {
+ ShowProfileCreationError(profile, GetProfileCreationErrorMessageLocal());
+ break;
+ }
+ case Profile::CREATE_STATUS_CREATED: {
+ // Do nothing for an intermediate status.
+ break;
+ }
+ case Profile::CREATE_STATUS_INITIALIZED: {
+ HandleProfileCreationSuccess(create_shortcut, supervised_user_id,
+ custodian_profile, profile);
+ break;
+ }
+ // User-initiated cancellation is handled in CancelProfileRegistration and
+ // does not call this callback.
+ case Profile::CREATE_STATUS_CANCELED:
+ // Supervised user registration errors are handled in
+ // OnSupervisedUserRegistered().
+ case Profile::CREATE_STATUS_REMOTE_FAIL:
+ case Profile::MAX_CREATE_STATUS: {
+ NOTREACHED();
+ break;
+ }
+ }
+}
+
+void SigninCreateProfileHandler::HandleProfileCreationSuccess(
+ bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* custodian_profile,
+ Profile* profile) {
+ switch (profile_creation_type_) {
+ case NON_SUPERVISED_PROFILE_CREATION: {
+ DCHECK(supervised_user_id.empty());
+ CreateShortcutAndShowSuccess(create_shortcut, nullptr, profile);
+ break;
+ }
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ case SUPERVISED_PROFILE_CREATION:
+ case SUPERVISED_PROFILE_IMPORT:
+ RegisterSupervisedUser(create_shortcut, supervised_user_id,
+ custodian_profile, profile);
+ break;
+#endif
+ case NO_CREATION_IN_PROGRESS:
+ NOTREACHED();
+ break;
+ }
+}
+
+void SigninCreateProfileHandler::CreateShortcutAndShowSuccess(
+ bool create_shortcut,
+ Profile* custodian_profile,
+ Profile* profile) {
+ if (create_shortcut) {
+ DCHECK(ProfileShortcutManager::IsFeatureEnabled());
+ ProfileShortcutManager* shortcut_manager =
+ g_browser_process->profile_manager()->profile_shortcut_manager();
+ DCHECK(shortcut_manager);
+ if (shortcut_manager)
+ shortcut_manager->CreateProfileShortcut(profile->GetPath());
+ }
+
+ DCHECK_EQ(profile_path_being_created_.value(), profile->GetPath().value());
+ profile_path_being_created_.clear();
+ DCHECK_NE(NO_CREATION_IN_PROGRESS, profile_creation_type_);
+ base::DictionaryValue dict;
+ dict.SetString("name", profile->GetPrefs()->GetString(prefs::kProfileName));
+ dict.Set("filePath", base::CreateFilePathValue(profile->GetPath()));
+
+ bool is_force_signin_enabled = signin::IsForceSigninEnabled();
+ bool open_new_window = !is_force_signin_enabled;
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ // If the new profile is a supervised user, instead of opening a new window
+ // right away, a confirmation page will be shown by JS from the creation
+ // dialog. If we are importing an existing supervised profile or creating a
+ // new non-supervised user profile we don't show any confirmation, so open
+ // the new window now.
+
+ open_new_window =
+ open_new_window && profile_creation_type_ != SUPERVISED_PROFILE_CREATION;
+
+ dict.SetBoolean("showConfirmation",
+ profile_creation_type_ == SUPERVISED_PROFILE_CREATION);
+
+ bool is_supervised = profile_creation_type_ == SUPERVISED_PROFILE_CREATION ||
+ profile_creation_type_ == SUPERVISED_PROFILE_IMPORT;
+ dict.SetBoolean("isSupervised", is_supervised);
+
+ if (is_supervised) {
+ DCHECK(custodian_profile);
+ if (custodian_profile) {
+ std::string custodian_username = custodian_profile->GetProfileUserName();
+ dict.SetString("custodianUsername", custodian_username);
+ }
+ }
+#endif
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "cr.webUIListenerCallback",
+ GetWebUIListenerName(PROFILE_CREATION_SUCCESS), dict);
+
+ if (open_new_window) {
+ // Opening the new window must be the last action, after all callbacks
+ // have been run, to give them a chance to initialize the profile.
+ OpenNewWindowForProfile(profile, Profile::CREATE_STATUS_INITIALIZED);
+ } else if (is_force_signin_enabled) {
+ OpenSigninDialogForProfile(profile);
+ }
+ profile_creation_type_ = NO_CREATION_IN_PROGRESS;
+}
+
+void SigninCreateProfileHandler::OpenNewWindowForProfile(
+ Profile* profile,
+ Profile::CreateStatus status) {
+ profiles::OpenBrowserWindowForProfile(
+ base::Bind(&SigninCreateProfileHandler::OnBrowserReadyCallback,
+ weak_ptr_factory_.GetWeakPtr()),
+ false, // Don't create a window if one already exists.
+ true, // Create a first run window.
+ profile,
+ status);
+}
+
+void SigninCreateProfileHandler::OpenSigninDialogForProfile(Profile* profile) {
+ UserManagerProfileDialog::ShowSigninDialog(
+ web_ui()->GetWebContents()->GetBrowserContext(), profile->GetPath());
+}
+
+void SigninCreateProfileHandler::ShowProfileCreationError(
+ Profile* profile,
+ const base::string16& error) {
+ DCHECK_NE(NO_CREATION_IN_PROGRESS, profile_creation_type_);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "cr.webUIListenerCallback", GetWebUIListenerName(PROFILE_CREATION_ERROR),
+ base::Value(error));
+ // 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;
+ profile_path_being_created_.clear();
+}
+
+void SigninCreateProfileHandler::RecordProfileCreationMetrics(
+ Profile::CreateStatus status) {
+ UMA_HISTOGRAM_ENUMERATION("Profile.CreateResult", status,
+ Profile::MAX_CREATE_STATUS);
+ UMA_HISTOGRAM_MEDIUM_TIMES(
+ "Profile.CreateTimeNoTimeout",
+ base::TimeTicks::Now() - profile_creation_start_time_);
+}
+
+base::string16 SigninCreateProfileHandler::GetProfileCreationErrorMessageLocal()
+ const {
+ int message_id = IDS_PROFILES_CREATE_LOCAL_ERROR;
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ // Local errors can occur during supervised profile import.
+ if (profile_creation_type_ == SUPERVISED_PROFILE_IMPORT)
+ message_id = IDS_LEGACY_SUPERVISED_USER_IMPORT_LOCAL_ERROR;
+#endif
+ return l10n_util::GetStringUTF16(message_id);
+}
+
+void SigninCreateProfileHandler::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK_EQ(chrome::NOTIFICATION_BROWSER_WINDOW_READY, type);
+
+ // Only respond to one Browser Window Ready event.
+ registrar_.Remove(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+ content::NotificationService::AllSources());
+ UserManager::Hide();
+}
+
+void SigninCreateProfileHandler::OnBrowserReadyCallback(
+ Profile* profile,
+ Profile::CreateStatus profile_create_status) {
+ Browser* browser = chrome::FindAnyBrowser(profile, false);
+ // Closing the User Manager before the window is created can flakily cause
+ // Chrome to close.
+ if (browser && browser->window()) {
+ UserManager::Hide();
+ } else {
+ registrar_.Add(this,
+ chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+ content::NotificationService::AllSources());
+ }
+}
+
+base::Value SigninCreateProfileHandler::GetWebUIListenerName(
+ ProfileCreationStatus status) const {
+ switch (status) {
+ case PROFILE_CREATION_SUCCESS:
+ return base::Value("create-profile-success");
+ case PROFILE_CREATION_ERROR:
+ return base::Value("create-profile-error");
+ }
+ NOTREACHED();
+ return base::Value(std::string());
+}
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+base::string16 SigninCreateProfileHandler::GetProfileCreateErrorMessageRemote()
+ const {
+ return l10n_util::GetStringUTF16(
+ profile_creation_type_ == SUPERVISED_PROFILE_IMPORT
+ ? IDS_LEGACY_SUPERVISED_USER_IMPORT_REMOTE_ERROR
+ : IDS_PROFILES_CREATE_REMOTE_ERROR);
+}
+
+base::string16 SigninCreateProfileHandler::GetProfileCreateErrorMessageSignin()
+ const {
+ return l10n_util::GetStringUTF16(
+ profile_creation_type_ == SUPERVISED_PROFILE_IMPORT
+ ? IDS_LEGACY_SUPERVISED_USER_IMPORT_SIGN_IN_ERROR
+ : IDS_PROFILES_CREATE_SIGN_IN_ERROR);
+}
+
+bool SigninCreateProfileHandler::GetSupervisedCreateProfileArgs(
+ const base::ListValue* args,
+ std::string* supervised_user_id,
+ base::FilePath* custodian_profile_path) {
+ bool supervised_user = false;
+ bool success = args->GetBoolean(3, &supervised_user);
+ DCHECK(success);
+
+ if (!supervised_user)
+ return false;
+
+ success = args->GetString(4, supervised_user_id);
+ DCHECK(success);
+ const base::Value* path_value;
+ success = args->Get(5, &path_value);
+ DCHECK(success);
+ success = base::GetValueAsFilePath(*path_value, custodian_profile_path);
+ DCHECK(success);
+
+ return !custodian_profile_path->empty();
+}
+
+void SigninCreateProfileHandler::LoadCustodianProfileCallback(
+ const base::string16& name,
+ const std::string& icon_url,
+ bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* custodian_profile,
+ Profile::CreateStatus status) {
+ // This method gets called once before with Profile::CREATE_STATUS_CREATED.
+ switch (status) {
+ case Profile::CREATE_STATUS_LOCAL_FAIL: {
+ ShowProfileCreationError(nullptr, GetProfileCreationErrorMessageLocal());
+ break;
+ }
+ case Profile::CREATE_STATUS_CREATED: {
+ // Ignore the intermediate status.
+ break;
+ }
+ case Profile::CREATE_STATUS_INITIALIZED: {
+ // We are only interested in Profile::CREATE_STATUS_INITIALIZED when
+ // everything is ready.
+ if (!IsAccountConnected(custodian_profile) ||
+ HasAuthError(custodian_profile)) {
+ ShowProfileCreationError(nullptr, l10n_util::GetStringFUTF16(
+ IDS_PROFILES_CREATE_CUSTODIAN_ACCOUNT_DETAILS_OUT_OF_DATE_ERROR,
+ base::ASCIIToUTF16(custodian_profile->GetProfileUserName())));
+ return;
+ }
+
+ PrefService* prefs = custodian_profile->GetPrefs();
+ if (!prefs->GetBoolean(prefs::kSupervisedUserCreationAllowed)) {
+ ShowProfileCreationError(
+ nullptr,
+ l10n_util::GetStringUTF16(
+ IDS_PROFILES_CREATE_SUPERVISED_NOT_ALLOWED_BY_POLICY));
+ return;
+ }
+
+ if (!supervised_user_id.empty()) {
+ profile_creation_type_ = SUPERVISED_PROFILE_IMPORT;
+
+ // Load all supervised users managed by this user in order to
+ // check if this supervised user already exists on this device.
+ SupervisedUserSyncService* supervised_user_sync_service =
+ SupervisedUserSyncServiceFactory::GetForProfile(custodian_profile);
+ if (supervised_user_sync_service) {
+ supervised_user_sync_service->GetSupervisedUsersAsync(base::Bind(
+ &SigninCreateProfileHandler::DoCreateProfileIfPossible,
+ weak_ptr_factory_.GetWeakPtr(), name, icon_url, create_shortcut,
+ supervised_user_id, custodian_profile));
+ } else {
+ ShowProfileCreationError(nullptr,
+ GetProfileCreateErrorMessageRemote());
+ }
+ } else {
+ profile_creation_type_ = SUPERVISED_PROFILE_CREATION;
+ std::string new_supervised_user_id =
+ SupervisedUserRegistrationUtility::GenerateNewSupervisedUserId();
+
+ // If sync is not yet fully initialized, the creation may take extra
+ // time, so show a message. Import doesn't wait for an acknowledgment,
+ // so it won't have the same potential delay.
+ browser_sync::ProfileSyncService* sync_service =
+ ProfileSyncServiceFactory::GetInstance()->GetForProfile(
+ custodian_profile);
+ browser_sync::ProfileSyncService::SyncStatusSummary status =
+ sync_service->QuerySyncStatusSummary();
+ if (status ==
+ browser_sync::ProfileSyncService::DATATYPES_NOT_INITIALIZED) {
+ ShowProfileCreationWarning(l10n_util::GetStringUTF16(
+ IDS_PROFILES_CREATE_SUPERVISED_JUST_SIGNED_IN));
+ }
+
+ DoCreateProfile(name, icon_url, create_shortcut, new_supervised_user_id,
+ custodian_profile);
+ }
+ break;
+ }
+ case Profile::CREATE_STATUS_CANCELED:
+ case Profile::CREATE_STATUS_REMOTE_FAIL:
+ case Profile::MAX_CREATE_STATUS: {
+ NOTREACHED();
+ break;
+ }
+ }
+}
+
+bool SigninCreateProfileHandler::IsAccountConnected(Profile* profile)
+ const {
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetForProfile(profile);
+ return signin_manager && signin_manager->IsAuthenticated();
+}
+
+bool SigninCreateProfileHandler::HasAuthError(Profile* profile)
+ const {
+ SigninErrorController* error_controller =
+ SigninErrorControllerFactory::GetForProfile(profile);
+ if (!error_controller)
+ return true;
+
+ GoogleServiceAuthError::State state = error_controller->auth_error().state();
+ return state != GoogleServiceAuthError::NONE;
+}
+
+void SigninCreateProfileHandler::DoCreateProfileIfPossible(
+ const base::string16& name,
+ const std::string& icon_url,
+ bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* custodian_profile,
+ const base::DictionaryValue* dict) {
+ DCHECK(dict);
+ if (!dict->HasKey(supervised_user_id))
+ return;
+
+ // Check if this supervised user already exists on this machine.
+ std::vector<ProfileAttributesEntry*> entries =
+ g_browser_process->profile_manager()->
+ GetProfileAttributesStorage().GetAllProfilesAttributes();
+ for (ProfileAttributesEntry* entry : entries) {
+ if (supervised_user_id == entry->GetSupervisedUserId()) {
+ ShowProfileCreationError(nullptr, GetProfileCreationErrorMessageLocal());
+ return;
+ }
+ }
+
+ DoCreateProfile(name, icon_url, create_shortcut, supervised_user_id,
+ custodian_profile);
+}
+
+void SigninCreateProfileHandler::HandleCancelProfileCreation(
+ const base::ListValue* args) {
+ CancelProfileRegistration(true);
+}
+
+// Non-supervised user creation cannot be canceled. (Creating a non-supervised
+// profile shouldn't take significant time, and it can easily be deleted
+// afterward.)
+void SigninCreateProfileHandler::CancelProfileRegistration(
+ bool user_initiated) {
+ if (profile_path_being_created_.empty())
+ return;
+
+ ProfileManager* manager = g_browser_process->profile_manager();
+ Profile* new_profile = manager->GetProfileByPath(profile_path_being_created_);
+ if (!new_profile || !new_profile->IsSupervised())
+ return;
+
+ DCHECK(supervised_user_registration_utility_.get());
+ supervised_user_registration_utility_.reset();
+
+ if (user_initiated) {
+ UMA_HISTOGRAM_MEDIUM_TIMES(
+ "Profile.CreateTimeCanceledNoTimeout",
+ base::TimeTicks::Now() - profile_creation_start_time_);
+ RecordProfileCreationMetrics(Profile::CREATE_STATUS_CANCELED);
+ }
+
+ DCHECK_NE(NO_CREATION_IN_PROGRESS, profile_creation_type_);
+ profile_creation_type_ = NO_CREATION_IN_PROGRESS;
+
+ // Canceling registration means the callback passed into
+ // 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);
+}
+
+void SigninCreateProfileHandler::RegisterSupervisedUser(
+ bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* custodian_profile,
+ Profile* new_profile) {
+ DCHECK_EQ(profile_path_being_created_.value(),
+ new_profile->GetPath().value());
+
+ SupervisedUserService* supervised_user_service =
+ SupervisedUserServiceFactory::GetForProfile(new_profile);
+
+ // Register the supervised user using the profile of the custodian.
+ supervised_user_registration_utility_ =
+ SupervisedUserRegistrationUtility::Create(custodian_profile);
+ if (supervised_user_service) {
+ supervised_user_service->RegisterAndInitSync(
+ supervised_user_registration_utility_.get(), custodian_profile,
+ supervised_user_id,
+ base::Bind(&SigninCreateProfileHandler::OnSupervisedUserRegistered,
+ weak_ptr_factory_.GetWeakPtr(), create_shortcut,
+ custodian_profile, new_profile));
+ }
+}
+
+void SigninCreateProfileHandler::OnSupervisedUserRegistered(
+ bool create_shortcut,
+ Profile* custodian_profile,
+ Profile* profile,
+ const GoogleServiceAuthError& error) {
+ GoogleServiceAuthError::State state = error.state();
+ RecordSupervisedProfileCreationMetrics(state);
+ if (state == GoogleServiceAuthError::NONE) {
+ CreateShortcutAndShowSuccess(create_shortcut, custodian_profile, profile);
+ return;
+ }
+
+ base::string16 error_msg;
+ if (state == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS ||
+ state == GoogleServiceAuthError::USER_NOT_SIGNED_UP ||
+ state == GoogleServiceAuthError::ACCOUNT_DELETED ||
+ state == GoogleServiceAuthError::ACCOUNT_DISABLED) {
+ error_msg = GetProfileCreateErrorMessageSignin();
+ } else {
+ error_msg = GetProfileCreateErrorMessageRemote();
+ }
+ ShowProfileCreationError(profile, error_msg);
+}
+
+void SigninCreateProfileHandler::ShowProfileCreationWarning(
+ const base::string16& warning) {
+ DCHECK_EQ(SUPERVISED_PROFILE_CREATION, profile_creation_type_);
+ web_ui()->CallJavascriptFunctionUnsafe("cr.webUIListenerCallback",
+ base::Value("create-profile-warning"),
+ base::Value(warning));
+}
+
+void SigninCreateProfileHandler::RecordSupervisedProfileCreationMetrics(
+ GoogleServiceAuthError::State error_state) {
+ if (profile_creation_type_ == SUPERVISED_PROFILE_CREATION) {
+ UMA_HISTOGRAM_ENUMERATION("Profile.SupervisedProfileCreateError",
+ error_state, GoogleServiceAuthError::NUM_STATES);
+ UMA_HISTOGRAM_MEDIUM_TIMES(
+ "Profile.SupervisedProfileTotalCreateTime",
+ base::TimeTicks::Now() - profile_creation_start_time_);
+ } else {
+ DCHECK_EQ(SUPERVISED_PROFILE_IMPORT, profile_creation_type_);
+ UMA_HISTOGRAM_ENUMERATION("Profile.SupervisedProfileImportError",
+ error_state, GoogleServiceAuthError::NUM_STATES);
+ UMA_HISTOGRAM_MEDIUM_TIMES(
+ "Profile.SupervisedProfileTotalImportTime",
+ base::TimeTicks::Now() - profile_creation_start_time_);
+ }
+}
+
+void SigninCreateProfileHandler::SwitchToProfile(
+ const base::ListValue* args) {
+ DCHECK(args);
+ const base::Value* file_path_value;
+ if (!args->Get(0, &file_path_value))
+ return;
+
+ base::FilePath profile_file_path;
+ if (!base::GetValueAsFilePath(*file_path_value, &profile_file_path))
+ return;
+
+ Profile* profile = g_browser_process->profile_manager()->
+ GetProfileByPath(profile_file_path);
+ DCHECK(profile);
+
+ profiles::OpenBrowserWindowForProfile(
+ base::Bind(&SigninCreateProfileHandler::OnBrowserReadyCallback,
+ weak_ptr_factory_.GetWeakPtr()),
+ false, // Don't create a window if one already exists.
+ true, // Create a first run window.
+ profile,
+ Profile::CREATE_STATUS_INITIALIZED);
+}
+#endif
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
new file mode 100644
index 00000000000..87bf6e44a84
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler.h
@@ -0,0 +1,265 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_CREATE_PROFILE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_CREATE_PROFILE_HANDLER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#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 "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+class SupervisedUserRegistrationUtility;
+#endif
+
+// Handler for the 'create profile' page.
+class SigninCreateProfileHandler : public content::WebUIMessageHandler,
+ public content::NotificationObserver,
+ public ProfileAttributesStorage::Observer {
+ public:
+ SigninCreateProfileHandler();
+ ~SigninCreateProfileHandler() override;
+
+ void GetLocalizedValues(base::DictionaryValue* localized_strings);
+
+ protected:
+ FRIEND_TEST_ALL_PREFIXES(SigninCreateProfileHandlerTest,
+ ReturnDefaultProfileNameAndIcons);
+ FRIEND_TEST_ALL_PREFIXES(SigninCreateProfileHandlerTest,
+ ReturnSignedInProfiles);
+ FRIEND_TEST_ALL_PREFIXES(SigninCreateProfileHandlerTest,
+ CreateProfile);
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ FRIEND_TEST_ALL_PREFIXES(SigninCreateProfileHandlerTest,
+ CreateSupervisedUser);
+ FRIEND_TEST_ALL_PREFIXES(SigninCreateProfileHandlerTest,
+ ImportSupervisedUser);
+ FRIEND_TEST_ALL_PREFIXES(SigninCreateProfileHandlerTest,
+ ImportSupervisedUserAlreadyOnDevice);
+ FRIEND_TEST_ALL_PREFIXES(SigninCreateProfileHandlerTest,
+ CustodianNotAuthenticated);
+ FRIEND_TEST_ALL_PREFIXES(SigninCreateProfileHandlerTest,
+ CustodianHasAuthError);
+ FRIEND_TEST_ALL_PREFIXES(SigninCreateProfileHandlerTest,
+ NotAllowedToCreateSupervisedUser);
+ FRIEND_TEST_ALL_PREFIXES(SigninCreateProfileHandlerTest,
+ CreateProfileWithForceSignin);
+#endif
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // content::NotificationObserver implementation:
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ // ProfileAttributesStorage::Observer implementation:
+ void OnProfileAuthInfoChanged(const base::FilePath& profile_path) override;
+
+ // Represents the final profile creation status. It is used to map
+ // the status to the javascript method to be called.
+ enum ProfileCreationStatus {
+ PROFILE_CREATION_SUCCESS,
+ PROFILE_CREATION_ERROR,
+ };
+
+ // Represents the type of the in progress profile creation operation.
+ // It is used to map the type of the profile creation operation to the
+ // correct UMA metric name.
+ enum ProfileCreationOperationType {
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ SUPERVISED_PROFILE_CREATION,
+ SUPERVISED_PROFILE_IMPORT,
+#endif
+ NON_SUPERVISED_PROFILE_CREATION,
+ NO_CREATION_IN_PROGRESS
+ };
+
+ // Callback for the "requestDefaultProfileIcons" message.
+ // Sends the array of default profile icon URLs to WebUI.
+ void RequestDefaultProfileIcons(const base::ListValue* args);
+
+ // Sends an object to WebUI of the form: { "name": profileName } after
+ // "requestDefaultProfileIcons" is fulfilled.
+ void SendNewProfileDefaults();
+
+ // Callback for the "requestSignedInProfiles" message.
+ // Sends the email address of the signed-in user, or an empty string if the
+ // user is not signed in.
+ void RequestSignedInProfiles(const base::ListValue* args);
+
+ // Asynchronously creates and initializes a new profile.
+ // The arguments are as follows:
+ // 0: name (string)
+ // 1: icon (string)
+ // 2: a flag stating whether we should create a profile desktop shortcut
+ // (optional, boolean)
+ // 3: a flag stating whether the user should be supervised
+ // (optional, boolean)
+ // 4: a string representing the supervised user ID.
+ // 5: a string representing the custodian profile path.
+ void CreateProfile(const base::ListValue* args);
+
+ // If a local error occurs during profile creation, then show an appropriate
+ // error message. However, if profile creation succeeded and the
+ // profile being created/imported is a supervised user profile,
+ // then proceed with the registration step. Otherwise, update the UI
+ // as the final task after a new profile has been created.
+ void OnProfileCreated(bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* custodian_profile,
+ Profile* profile,
+ Profile::CreateStatus status);
+
+ void HandleProfileCreationSuccess(bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* custodian_profile,
+ Profile* profile);
+
+ // Creates desktop shortcut and updates the UI to indicate success
+ // when creating a profile.
+ void CreateShortcutAndShowSuccess(bool create_shortcut,
+ Profile* custodian_profile,
+ Profile* profile);
+
+ // Opens a new window for |profile|.
+ virtual void OpenNewWindowForProfile(Profile* profile,
+ Profile::CreateStatus status);
+
+ // Opens a new signin dialog for |profile|.
+ virtual void OpenSigninDialogForProfile(Profile* profile);
+
+ // This callback is run after a new browser (but not the window) has been
+ // created for the new profile.
+ void OnBrowserReadyCallback(Profile* profile, Profile::CreateStatus status);
+
+ // Updates the UI to show an error when creating a profile.
+ void ShowProfileCreationError(Profile* profile, const base::string16& error);
+
+ // Records UMA histograms relevant to profile creation.
+ void RecordProfileCreationMetrics(Profile::CreateStatus status);
+
+ base::string16 GetProfileCreationErrorMessageLocal() const;
+
+ base::Value GetWebUIListenerName(ProfileCreationStatus status) const;
+
+ // Used to allow canceling a profile creation (particularly a supervised-user
+ // registration) in progress. Set when profile creation is begun, and
+ // cleared when all the callbacks have been run and creation is complete.
+ base::FilePath profile_path_being_created_;
+
+ // Used to track how long profile creation takes.
+ base::TimeTicks profile_creation_start_time_;
+
+ // Indicates the type of the in progress profile creation operation.
+ // The value is only relevant while we are creating/importing a profile.
+ ProfileCreationOperationType profile_creation_type_;
+
+ // Asynchronously creates and initializes a new profile.
+ virtual void DoCreateProfile(const base::string16& name,
+ const std::string& icon_url,
+ bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* custodian_profile);
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ base::string16 GetProfileCreateErrorMessageRemote() const;
+ base::string16 GetProfileCreateErrorMessageSignin() const;
+
+ // Extracts the supervised user ID and the custodian user profile path from
+ // the args passed into CreateProfile.
+ bool GetSupervisedCreateProfileArgs(const base::ListValue* args,
+ std::string* supervised_user_id,
+ base::FilePath* custodian_profile_path);
+
+ // Callback that runs once the custodian profile has been loaded. It sets
+ // |profile_creation_type_| if necessary, and calls |DoCreateProfile| if the
+ // supervised user id specified in |args| is valid.
+ void LoadCustodianProfileCallback(const base::string16& name,
+ const std::string& icon_url,
+ bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* custodian_profile,
+ Profile::CreateStatus status);
+
+ // Cancels creation of a supervised-user profile currently in progress, as
+ // indicated by profile_path_being_created_, removing the object and files
+ // and canceling supervised-user registration. This is the handler for the
+ // "cancelCreateProfile" message. |args| is not used.
+ void HandleCancelProfileCreation(const base::ListValue* args);
+
+ // Internal implementation. This may safely be called whether profile creation
+ // or registration is in progress or not. |user_initiated| should be true if
+ // the cancellation was deliberately requested by the user, and false if it
+ // was caused implicitly, e.g. by shutting down the browser.
+ void CancelProfileRegistration(bool user_initiated);
+
+ // Returns true if profile has signed into chrome.
+ bool IsAccountConnected(Profile* profile) const;
+ // Returns true if profile has authentication error.
+ bool HasAuthError(Profile* profile) const;
+
+ // After a new supervised-user profile has been created, registers the user
+ // with the management server.
+ virtual void RegisterSupervisedUser(bool create_shortcut,
+ const std::string& managed_user_id,
+ Profile* custodian_profile,
+ Profile* new_profile);
+
+ // Called back with the result of the supervised user registration.
+ void OnSupervisedUserRegistered(bool create_shortcut,
+ Profile* custodian_profile,
+ Profile* profile,
+ const GoogleServiceAuthError& error);
+
+ // Updates the UI to show a non-fatal warning when creating a profile.
+ void ShowProfileCreationWarning(const base::string16& warning);
+
+ // Records UMA histograms relevant to supervised user profiles
+ // creation and registration.
+ void RecordSupervisedProfileCreationMetrics(
+ GoogleServiceAuthError::State error_state);
+
+ // Creates the supervised user with the given |supervised_user_id| if the user
+ // doesn't already exist on the machine.
+ void DoCreateProfileIfPossible(const base::string16& name,
+ const std::string& icon_url,
+ bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* custodian_profile,
+ const base::DictionaryValue* dict);
+
+ // Callback for the "switchToProfile" message. Opens a new window for the
+ // profile. The profile file path is passed as a string argument.
+ void SwitchToProfile(const base::ListValue* args);
+
+ std::unique_ptr<SupervisedUserRegistrationUtility>
+ supervised_user_registration_utility_;
+#endif
+
+ content::NotificationRegistrar registrar_;
+
+ base::WeakPtrFactory<SigninCreateProfileHandler> weak_ptr_factory_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SigninCreateProfileHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_CREATE_PROFILE_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
new file mode 100644
index 00000000000..5c746b5eb90
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc
@@ -0,0 +1,659 @@
+// 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 "chrome/browser/ui/webui/signin/signin_create_profile_handler.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/prefs/browser_prefs.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_avatar_icon_util.h"
+#include "chrome/browser/signin/fake_signin_manager_builder.h"
+#include "chrome/browser/signin/signin_error_controller_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/ui/webui/signin/signin_utils.h"
+#include "chrome/common/features.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"
+#include "components/sync/protocol/sync.pb.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "content/public/test/test_web_ui.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+#include "chrome/browser/supervised_user/legacy/supervised_user_sync_service.h"
+#include "chrome/browser/supervised_user/legacy/supervised_user_sync_service_factory.h"
+#endif
+
+// Gmock matchers and actions.
+using testing::_;
+using testing::Invoke;
+
+namespace {
+
+const char kTestProfileName[] = "test-profile-name";
+
+const char kTestGaiaId1[] = "test-gaia-id-1";
+const char kTestEmail1[] = "foo1@bar.com";
+
+const char kTestGaiaId2[] = "test-gaia-id-2";
+const char kTestEmail2[] = "foo2@bar.com";
+
+const char kTestWebUIResponse[] = "cr.webUIListenerCallback";
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+const char kSupervisedUserId1[] = "test-supervised-id-1";
+const char kSupervisedUserId2[] = "test-supervised-id-2";
+
+const char kSupervisedUsername1[] = "test-supervised-username-1";
+const char kSupervisedUsername2[] = "test-supervised-username-2";
+
+const char kSupervisedUserAvatarName1[] = "chrome-avatar-index:0";
+const char kSupervisedUserAvatarName2[] = "chrome-avatar-index:1";
+
+syncer::SyncData CreateSyncData(const std::string& id,
+ const std::string& name,
+ const std::string& chrome_avatar) {
+ sync_pb::EntitySpecifics specifics;
+ specifics.mutable_managed_user()->set_id(id);
+ specifics.mutable_managed_user()->set_name(name);
+ 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());
+}
+#endif
+
+} // namespace
+
+class TestSigninCreateProfileHandler : public SigninCreateProfileHandler {
+ public:
+ explicit TestSigninCreateProfileHandler(
+ content::WebUI* web_ui,
+ TestingProfileManager* profile_manager)
+ : profile_manager_(profile_manager) {
+ set_web_ui(web_ui);
+ }
+
+ // Mock this method since it tries to create a profile asynchronously and the
+ // test terminates before the callback gets called.
+ MOCK_METHOD5(DoCreateProfile,
+ void(const base::string16& name,
+ const std::string& icon_url,
+ bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* custodian_profile));
+
+ // Creates the profile synchronously, sets the appropriate flag and calls the
+ // callback method to resume profile creation flow.
+ void RealDoCreateProfile(const base::string16& name,
+ const std::string& icon_url,
+ bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* custodian_profile) {
+ // Create the profile synchronously.
+ Profile* profile = profile_manager_->CreateTestingProfile(
+ kTestProfileName,
+ std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>(), name,
+ 0, supervised_user_id, TestingProfile::TestingFactories());
+
+ // Set the flag used to track the state of the creation flow.
+ profile_path_being_created_ = profile->GetPath();
+
+ // Call the callback method to resume profile creation flow.
+ SigninCreateProfileHandler::OnProfileCreated(
+ create_shortcut,
+ supervised_user_id,
+ custodian_profile,
+ profile,
+ Profile::CREATE_STATUS_INITIALIZED);
+ }
+
+ // Mock this method to track when an attempt to open a new browser window for
+ // the newly created/imported profile is made.
+ MOCK_METHOD2(OpenNewWindowForProfile,
+ void(Profile* profile, Profile::CreateStatus status));
+
+ // Mock this method so that we don't actually open the signin dialog during
+ // the test.
+ MOCK_METHOD1(OpenSigninDialogForProfile, void(Profile* profile));
+
+ // We don't actually need to register supervised users in the test. Mock this
+ // method to fake the registration part.
+ MOCK_METHOD4(RegisterSupervisedUser,
+ void(bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* custodian_profile,
+ Profile* new_profile));
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ // Calls the callback method to resume profile creation flow.
+ void RealRegisterSupervisedUser(bool create_shortcut,
+ const std::string& supervised_user_id,
+ Profile* custodian_profile,
+ Profile* new_profile) {
+ // Call the callback method to resume profile creation flow.
+ SigninCreateProfileHandler::OnSupervisedUserRegistered(
+ create_shortcut,
+ custodian_profile,
+ new_profile,
+ GoogleServiceAuthError(GoogleServiceAuthError::NONE));
+ }
+#endif
+
+ private:
+ TestingProfileManager* profile_manager_;
+ DISALLOW_COPY_AND_ASSIGN(TestSigninCreateProfileHandler);
+};
+
+class SigninCreateProfileHandlerTest : public BrowserWithTestWindowTest {
+ public:
+ SigninCreateProfileHandlerTest()
+ : web_ui_(new content::TestWebUI) {}
+
+ void SetUp() override {
+ BrowserWithTestWindowTest::SetUp();
+
+ profile_manager_.reset(
+ new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
+ ASSERT_TRUE(profile_manager_->SetUp());
+
+ handler_.reset(new TestSigninCreateProfileHandler(web_ui(),
+ profile_manager()));
+
+ TestingProfile::TestingFactories factories;
+ factories.push_back(std::make_pair(SigninManagerFactory::GetInstance(),
+ BuildFakeSigninManagerBase));
+ custodian_ = profile_manager_.get()->CreateTestingProfile(
+ "custodian-profile",
+ std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>(),
+ base::UTF8ToUTF16("custodian-profile"), 0, std::string(), factories);
+
+ // Authenticate the custodian profile.
+ fake_signin_manager_ = static_cast<FakeSigninManagerForTesting*>(
+ SigninManagerFactory::GetForProfile(custodian_));
+ fake_signin_manager_->SetAuthenticatedAccountInfo(kTestGaiaId1,
+ kTestEmail1);
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+ // Add supervised users to the custodian profile.
+ SupervisedUserSyncService* sync_service_ =
+ SupervisedUserSyncServiceFactory::GetForProfile(custodian_);
+ syncer::SyncDataList sync_data;
+ sync_data.push_back(CreateSyncData(kSupervisedUserId1,
+ kSupervisedUsername1,
+ kSupervisedUserAvatarName1));
+ sync_data.push_back(CreateSyncData(kSupervisedUserId2,
+ kSupervisedUsername2,
+ kSupervisedUserAvatarName2));
+ syncer::SyncMergeResult result = sync_service_->MergeDataAndStartSyncing(
+ syncer::SUPERVISED_USERS,
+ sync_data,
+ std::unique_ptr<syncer::SyncChangeProcessor>(
+ new syncer::FakeSyncChangeProcessor()),
+ std::unique_ptr<syncer::SyncErrorFactory>(
+ new syncer::SyncErrorFactoryMock()));
+ EXPECT_FALSE(result.error().IsSet());
+ EXPECT_EQ(2u, sync_service_->GetSupervisedUsers()->size());
+
+ // The second supervised user exists on the device.
+ profile_manager()->CreateTestingProfile(
+ kSupervisedUsername2,
+ std::unique_ptr<sync_preferences::PrefServiceSyncable>(),
+ base::UTF8ToUTF16(kSupervisedUsername2), 0,
+ kSupervisedUserId2, // supervised_user_id
+ TestingProfile::TestingFactories());
+
+ EXPECT_EQ(2u,
+ profile_manager()->profile_attributes_storage()->GetNumberOfProfiles());
+#endif
+ }
+
+ void TearDown() override {
+ handler_.reset();
+ profile_manager_.reset();
+ BrowserWithTestWindowTest::TearDown();
+ }
+
+ content::TestWebUI* web_ui() {
+ return web_ui_.get();
+ }
+
+ TestSigninCreateProfileHandler* handler() {
+ return handler_.get();
+ }
+
+ TestingProfileManager* profile_manager() {
+ return profile_manager_.get();
+ }
+
+ TestingProfile* custodian() {
+ return custodian_;
+ }
+
+ FakeSigninManagerForTesting* signin_manager() {
+ return fake_signin_manager_;
+ }
+
+ private:
+ std::unique_ptr<content::TestWebUI> web_ui_;
+ std::unique_ptr<TestingProfileManager> profile_manager_;
+ TestingProfile* custodian_;
+ FakeSigninManagerForTesting* fake_signin_manager_;
+ std::unique_ptr<TestSigninCreateProfileHandler> handler_;
+};
+
+TEST_F(SigninCreateProfileHandlerTest, ReturnDefaultProfileNameAndIcons) {
+ // Request default profile information.
+ base::ListValue list_args;
+ handler()->RequestDefaultProfileIcons(&list_args);
+
+ // Expect two JS callbacks. One with profile avatar icons and the other with
+ // the default profile name.
+ EXPECT_EQ(2U, web_ui()->call_data().size());
+
+ EXPECT_EQ(kTestWebUIResponse, web_ui()->call_data()[0]->function_name());
+
+ std::string callback_name;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg1()->GetAsString(&callback_name));
+ EXPECT_EQ("profile-icons-received", callback_name);
+
+ const base::ListValue* profile_icons;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg2()->GetAsList(&profile_icons));
+ EXPECT_NE(0U, profile_icons->GetSize());
+
+ EXPECT_EQ(kTestWebUIResponse, web_ui()->call_data()[1]->function_name());
+
+ ASSERT_TRUE(web_ui()->call_data()[1]->arg1()->GetAsString(&callback_name));
+ EXPECT_EQ("profile-defaults-received", callback_name);
+
+ const base::DictionaryValue* profile_info;
+ ASSERT_TRUE(web_ui()->call_data()[1]->arg2()->GetAsDictionary(&profile_info));
+ std::string profile_name;
+ ASSERT_TRUE(profile_info->GetString("name", &profile_name));
+ EXPECT_NE("", profile_name);
+}
+
+TEST_F(SigninCreateProfileHandlerTest, ReturnSignedInProfiles) {
+ // Create two test profiles.
+ Profile* profile_1 = profile_manager()->CreateTestingProfile("profile_1");
+ ASSERT_TRUE(profile_1);
+ Profile* profile_2 = profile_manager()->CreateTestingProfile("profile_2");
+ ASSERT_TRUE(profile_2);
+
+ // Set Auth Info only for profile_2.
+ ProfileAttributesEntry* entry;
+ ASSERT_TRUE(profile_manager()->profile_attributes_storage()->
+ GetProfileAttributesWithPath(profile_2->GetPath(), &entry));
+ entry->SetAuthInfo(kTestGaiaId2, base::UTF8ToUTF16(kTestEmail2));
+
+ // Expect a JS callback with a list containing profile_2.
+ EXPECT_EQ(1U, web_ui()->call_data().size());
+
+ EXPECT_EQ(kTestWebUIResponse, web_ui()->call_data()[0]->function_name());
+
+ std::string callback_name;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg1()->GetAsString(&callback_name));
+ EXPECT_EQ("signedin-users-received", callback_name);
+
+ const base::ListValue* signed_in_profiles;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg2()->GetAsList(&signed_in_profiles));
+ EXPECT_EQ(1U, signed_in_profiles->GetSize());
+
+ const base::DictionaryValue* signed_in_profile;
+ ASSERT_TRUE(signed_in_profiles->GetDictionary(0, &signed_in_profile));
+ std::string user_name;
+ ASSERT_TRUE(signed_in_profile->GetString("username", &user_name));
+ EXPECT_EQ(kTestEmail2, user_name);
+ base::string16 profile_path;
+ ASSERT_TRUE(signed_in_profile->GetString("profilePath", &profile_path));
+ EXPECT_EQ(profile_2->GetPath().AsUTF16Unsafe(), profile_path);
+}
+
+
+TEST_F(SigninCreateProfileHandlerTest, CreateProfile) {
+ // Expect the call to create the profile.
+ EXPECT_CALL(*handler(), DoCreateProfile(_, _, _, _, _))
+ .WillOnce(Invoke(handler(),
+ &TestSigninCreateProfileHandler::RealDoCreateProfile));
+
+ // Expect no calls to register a supervised user.
+ EXPECT_CALL(*handler(), RegisterSupervisedUser(_, _, _, _)).Times(0);
+
+ // Expect a new browser window for the new profile to be opened.
+ EXPECT_CALL(*handler(), OpenNewWindowForProfile(_, _));
+
+ // Expect no signin dialog opened for the new profile.
+ EXPECT_CALL(*handler(), OpenSigninDialogForProfile(_)).Times(0);
+
+ // Create a non-supervised profile.
+ base::ListValue list_args;
+ list_args.AppendString(kTestProfileName);
+ list_args.AppendString(profiles::GetDefaultAvatarIconUrl(0));
+ list_args.AppendBoolean(false); // create_shortcut
+ list_args.AppendBoolean(false); // is_supervised
+ handler()->CreateProfile(&list_args);
+
+ // Expect a JS callbacks with the new profile information.
+ EXPECT_EQ(1U, web_ui()->call_data().size());
+
+ EXPECT_EQ(kTestWebUIResponse, web_ui()->call_data()[0]->function_name());
+
+ std::string callback_name;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg1()->GetAsString(&callback_name));
+ EXPECT_EQ("create-profile-success", callback_name);
+
+ const base::DictionaryValue* profile;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg2()->GetAsDictionary(&profile));
+ std::string profile_name;
+ ASSERT_TRUE(profile->GetString("name", &profile_name));
+ EXPECT_NE("", profile_name);
+ std::string profile_path;
+ ASSERT_TRUE(profile->GetString("filePath", &profile_path));
+ EXPECT_NE("", profile_path);
+ bool is_supervised;
+ ASSERT_TRUE(profile->GetBoolean("isSupervised", &is_supervised));
+ ASSERT_FALSE(is_supervised);
+}
+
+TEST_F(SigninCreateProfileHandlerTest, CreateProfileWithForceSignin) {
+ g_browser_process->local_state()->SetBoolean(prefs::kForceBrowserSignin,
+ true);
+ ASSERT_TRUE(signin::IsForceSigninEnabled());
+
+ // Expect the call to create the profile.
+ EXPECT_CALL(*handler(), DoCreateProfile(_, _, _, _, _))
+ .WillOnce(Invoke(handler(),
+ &TestSigninCreateProfileHandler::RealDoCreateProfile));
+
+ // Expect no calls to register a supervised user.
+ EXPECT_CALL(*handler(), RegisterSupervisedUser(_, _, _, _)).Times(0);
+
+ // Expect no new browser window for the new profile.
+ EXPECT_CALL(*handler(), OpenNewWindowForProfile(_, _)).Times(0);
+
+ // Expect a signin dialog opened for the new profile.
+ EXPECT_CALL(*handler(), OpenSigninDialogForProfile(_)).Times(1);
+
+ base::ListValue list_args;
+ list_args.AppendString(kTestProfileName);
+ list_args.AppendString(profiles::GetDefaultAvatarIconUrl(0));
+ list_args.AppendBoolean(false); // create_shortcut
+ list_args.AppendBoolean(false); // is_supervised
+ handler()->CreateProfile(&list_args);
+
+ // Expect a JS callbacks with the new profile information.
+ EXPECT_EQ(1U, web_ui()->call_data().size());
+
+ EXPECT_EQ(kTestWebUIResponse, web_ui()->call_data()[0]->function_name());
+
+ std::string callback_name;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg1()->GetAsString(&callback_name));
+ EXPECT_EQ("create-profile-success", callback_name);
+
+ const base::DictionaryValue* profile;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg2()->GetAsDictionary(&profile));
+ std::string profile_name;
+ ASSERT_TRUE(profile->GetString("name", &profile_name));
+ EXPECT_NE("", profile_name);
+ std::string profile_path;
+ ASSERT_TRUE(profile->GetString("filePath", &profile_path));
+ EXPECT_NE("", profile_path);
+ bool is_supervised;
+ ASSERT_TRUE(profile->GetBoolean("isSupervised", &is_supervised));
+ ASSERT_FALSE(is_supervised);
+ bool show_confirmation;
+ ASSERT_TRUE(profile->GetBoolean("showConfirmation", &show_confirmation));
+ ASSERT_FALSE(show_confirmation);
+}
+
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+
+TEST_F(SigninCreateProfileHandlerTest, CreateSupervisedUser) {
+ // Expect the call to create the profile.
+ EXPECT_CALL(*handler(), DoCreateProfile(_, _, _, _, _))
+ .WillOnce(Invoke(handler(),
+ &TestSigninCreateProfileHandler::RealDoCreateProfile));
+
+ // Expect the call to register the supervised user.
+ EXPECT_CALL(*handler(), RegisterSupervisedUser(_, _, _, _))
+ .WillOnce(Invoke(
+ handler(),
+ &TestSigninCreateProfileHandler::RealRegisterSupervisedUser));
+
+ // Expect no new browser window for the new supervised profile to be opened.
+ EXPECT_CALL(*handler(), OpenNewWindowForProfile(_, _)).Times(0);
+
+ // Create a supervised profile.
+ base::ListValue list_args;
+ list_args.Clear();
+ list_args.AppendString(kSupervisedUsername1);
+ list_args.AppendString(profiles::GetDefaultAvatarIconUrl(0));
+ list_args.AppendBoolean(false); // create_shortcut
+ list_args.AppendBoolean(true); // is_supervised
+ list_args.AppendString(""); // supervised_user_id
+ list_args.AppendString(custodian()->GetPath().value());
+ handler()->CreateProfile(&list_args);
+
+ // Expect a JS callbacks with the new profile information.
+ EXPECT_EQ(1U, web_ui()->call_data().size());
+
+ EXPECT_EQ(kTestWebUIResponse, web_ui()->call_data()[0]->function_name());
+
+ std::string callback_name;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg1()->GetAsString(&callback_name));
+ EXPECT_EQ("create-profile-success", callback_name);
+
+ const base::DictionaryValue* profile;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg2()->GetAsDictionary(&profile));
+ std::string profile_name;
+ ASSERT_TRUE(profile->GetString("name", &profile_name));
+ EXPECT_NE("", profile_name);
+ std::string profile_path;
+ ASSERT_TRUE(profile->GetString("filePath", &profile_path));
+ EXPECT_NE("", profile_path);
+ bool is_supervised;
+ ASSERT_TRUE(profile->GetBoolean("isSupervised", &is_supervised));
+ ASSERT_TRUE(is_supervised);
+}
+
+TEST_F(SigninCreateProfileHandlerTest, ImportSupervisedUser) {
+ // Expect the call to create the profile.
+ EXPECT_CALL(*handler(), DoCreateProfile(_, _, _, _, _))
+ .WillOnce(Invoke(handler(),
+ &TestSigninCreateProfileHandler::RealDoCreateProfile));
+
+ // Expect the call to register the supervised user.
+ EXPECT_CALL(*handler(), RegisterSupervisedUser(_, _, _, _))
+ .WillOnce(Invoke(
+ handler(),
+ &TestSigninCreateProfileHandler::RealRegisterSupervisedUser));
+
+ // Expect a new browser window for the new profile to be opened.
+ EXPECT_CALL(*handler(), OpenNewWindowForProfile(_, _));
+
+ // Import a supervised profile.
+ base::ListValue list_args;
+ list_args.Clear();
+ list_args.AppendString(kSupervisedUsername1);
+ list_args.AppendString(profiles::GetDefaultAvatarIconUrl(0));
+ list_args.AppendBoolean(false); // create_shortcut
+ list_args.AppendBoolean(true); // is_supervised
+ list_args.AppendString(
+ kSupervisedUserId1); // supervised_user_id
+ list_args.AppendString(custodian()->GetPath().value());
+ handler()->CreateProfile(&list_args);
+
+ // Expect a JS callbacks with the new profile information.
+ EXPECT_EQ(1U, web_ui()->call_data().size());
+
+ EXPECT_EQ(kTestWebUIResponse, web_ui()->call_data()[0]->function_name());
+
+ std::string callback_name;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg1()->GetAsString(&callback_name));
+ EXPECT_EQ("create-profile-success", callback_name);
+
+ const base::DictionaryValue* profile;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg2()->GetAsDictionary(&profile));
+ std::string profile_name;
+ ASSERT_TRUE(profile->GetString("name", &profile_name));
+ EXPECT_NE("", profile_name);
+ std::string profile_path;
+ ASSERT_TRUE(profile->GetString("filePath", &profile_path));
+ EXPECT_NE("", profile_path);
+ bool is_supervised;
+ ASSERT_TRUE(profile->GetBoolean("isSupervised", &is_supervised));
+ ASSERT_TRUE(is_supervised);
+}
+
+TEST_F(SigninCreateProfileHandlerTest, ImportSupervisedUserAlreadyOnDevice) {
+ // Import a supervised profile whose already on the current device.
+ base::ListValue list_args;
+ list_args.Clear();
+ list_args.AppendString(kSupervisedUsername2);
+ list_args.AppendString(profiles::GetDefaultAvatarIconUrl(0));
+ list_args.AppendBoolean(false);
+ list_args.AppendBoolean(true);
+ list_args.AppendString(kSupervisedUserId2);
+ list_args.AppendString(custodian()->GetPath().value());
+ handler()->CreateProfile(&list_args);
+
+ // Expect a JS callbacks containing an error message.
+ EXPECT_EQ(1U, web_ui()->call_data().size());
+
+ EXPECT_EQ(kTestWebUIResponse, web_ui()->call_data()[0]->function_name());
+
+ std::string callback_name;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg1()->GetAsString(&callback_name));
+ EXPECT_EQ("create-profile-error", callback_name);
+
+ base::string16 expected_error_message = l10n_util::GetStringUTF16(
+ IDS_LEGACY_SUPERVISED_USER_IMPORT_LOCAL_ERROR);
+ base::string16 error_message;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg2()->GetAsString(&error_message));
+ EXPECT_EQ(expected_error_message, error_message);
+}
+
+TEST_F(SigninCreateProfileHandlerTest, CustodianNotAuthenticated) {
+ // Stop Sync before signing out.
+ SupervisedUserSyncService* sync_service_ =
+ SupervisedUserSyncServiceFactory::GetForProfile(custodian());
+ sync_service_->StopSyncing(syncer::SUPERVISED_USERS);
+
+ // Sign out the custodian.
+ signin_manager()->ForceSignOut();
+
+ // Create a supervised profile.
+ base::ListValue list_args;
+ list_args.Clear();
+ list_args.AppendString(kSupervisedUsername1);
+ list_args.AppendString(profiles::GetDefaultAvatarIconUrl(0));
+ list_args.AppendBoolean(false); // create_shortcut
+ list_args.AppendBoolean(true); // is_supervised
+ list_args.AppendString(""); // supervised_user_id
+ list_args.AppendString(custodian()->GetPath().value());
+ handler()->CreateProfile(&list_args);
+
+ // Expect a JS callbacks containing an error message.
+ EXPECT_EQ(1U, web_ui()->call_data().size());
+
+ EXPECT_EQ(kTestWebUIResponse, web_ui()->call_data()[0]->function_name());
+
+ std::string callback_name;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg1()->GetAsString(&callback_name));
+ EXPECT_EQ("create-profile-error", callback_name);
+
+ base::string16 expected_error_message = l10n_util::GetStringFUTF16(
+ IDS_PROFILES_CREATE_CUSTODIAN_ACCOUNT_DETAILS_OUT_OF_DATE_ERROR,
+ base::ASCIIToUTF16(custodian()->GetProfileUserName()));
+ base::string16 error_message;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg2()->GetAsString(&error_message));
+ EXPECT_EQ(expected_error_message, error_message);
+}
+
+TEST_F(SigninCreateProfileHandlerTest, CustodianHasAuthError) {
+ // Set an Auth Error for the custodian.
+ const GoogleServiceAuthError error(
+ GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
+ FakeAuthStatusProvider provider(
+ SigninErrorControllerFactory::GetForProfile(custodian()));
+ provider.SetAuthError(kTestGaiaId1, error);
+
+ // Create a supervised profile.
+ base::ListValue list_args;
+ list_args.Clear();
+ list_args.AppendString(kSupervisedUsername1);
+ list_args.AppendString(profiles::GetDefaultAvatarIconUrl(0));
+ list_args.AppendBoolean(false); // create_shortcut
+ list_args.AppendBoolean(true); // is_supervised
+ list_args.AppendString(""); // supervised_user_id
+ list_args.AppendString(custodian()->GetPath().value());
+ handler()->CreateProfile(&list_args);
+
+ // Expect a JS callbacks containing an error message.
+ EXPECT_EQ(1U, web_ui()->call_data().size());
+
+ EXPECT_EQ(kTestWebUIResponse, web_ui()->call_data()[0]->function_name());
+
+ std::string callback_name;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg1()->GetAsString(&callback_name));
+ EXPECT_EQ("create-profile-error", callback_name);
+
+ base::string16 expected_error_message = l10n_util::GetStringFUTF16(
+ IDS_PROFILES_CREATE_CUSTODIAN_ACCOUNT_DETAILS_OUT_OF_DATE_ERROR,
+ base::ASCIIToUTF16(custodian()->GetProfileUserName()));
+ base::string16 error_message;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg2()->GetAsString(&error_message));
+ EXPECT_EQ(expected_error_message, error_message);
+}
+
+TEST_F(SigninCreateProfileHandlerTest, NotAllowedToCreateSupervisedUser) {
+ // Custodian is not permitted to create supervised users.
+ custodian()->GetPrefs()->SetBoolean(prefs::kSupervisedUserCreationAllowed,
+ false);
+
+ // Create a supervised profile.
+ base::ListValue list_args;
+ list_args.Clear();
+ list_args.AppendString(kSupervisedUsername1);
+ list_args.AppendString(profiles::GetDefaultAvatarIconUrl(0));
+ list_args.AppendBoolean(false); // create_shortcut
+ list_args.AppendBoolean(true); // is_supervised
+ list_args.AppendString(""); // supervised_user_id
+ list_args.AppendString(custodian()->GetPath().value());
+ handler()->CreateProfile(&list_args);
+
+ // Expect a JS callbacks containing an error message.
+ EXPECT_EQ(1U, web_ui()->call_data().size());
+
+ EXPECT_EQ(kTestWebUIResponse, web_ui()->call_data()[0]->function_name());
+
+ std::string callback_name;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg1()->GetAsString(&callback_name));
+ EXPECT_EQ("create-profile-error", callback_name);
+
+ base::string16 expected_error_message = l10n_util::GetStringUTF16(
+ IDS_PROFILES_CREATE_SUPERVISED_NOT_ALLOWED_BY_POLICY);
+ base::string16 error_message;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg2()->GetAsString(&error_message));
+ EXPECT_EQ(expected_error_message, error_message);
+}
+
+#endif
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.cc b/chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.cc
new file mode 100644
index 00000000000..2af9a846d7e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.cc
@@ -0,0 +1,204 @@
+// 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 "chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h"
+
+#include <vector>
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/metrics/user_metrics.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/webui/signin/signin_email_confirmation_ui.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+// Dialog size.
+const int kDialogWidth = 512;
+const int kDialogMinHeight = 200;
+const int kDialogMaxHeight = 700;
+
+// Dialog action key;
+const char kActionKey[] = "action";
+
+// Dialog action values.
+const char kActionCancel[] = "cancel";
+const char kActionCreateNewUser[] = "createNewUser";
+const char kActionStartSync[] = "startSync";
+
+} // namespace
+
+class SigninEmailConfirmationDialog::DialogWebContentsObserver
+ : public content::WebContentsObserver {
+ public:
+ DialogWebContentsObserver(content::WebContents* web_contents,
+ SigninEmailConfirmationDialog* dialog)
+ : content::WebContentsObserver(web_contents),
+ signin_email_confirmation_dialog_(dialog) {}
+ ~DialogWebContentsObserver() override {}
+
+ private:
+ void WebContentsDestroyed() override {
+ // The dialog is already closed. No need to call CloseDialog() again.
+ // NOTE: |this| is deleted after |ResetDialogObserver| returns.
+ signin_email_confirmation_dialog_->ResetDialogObserver();
+ }
+
+ void RenderProcessGone(base::TerminationStatus status) override {
+ signin_email_confirmation_dialog_->CloseDialog();
+ }
+
+ SigninEmailConfirmationDialog* signin_email_confirmation_dialog_;
+};
+
+SigninEmailConfirmationDialog::SigninEmailConfirmationDialog(
+ content::WebContents* contents,
+ Profile* profile,
+ const std::string& last_email,
+ const std::string& new_email,
+ const Callback& callback)
+ : web_contents_(contents),
+ profile_(profile),
+ last_email_(last_email),
+ new_email_(new_email),
+ callback_(callback) {
+ chrome::RecordDialogCreation(
+ chrome::DialogIdentifier::SIGN_IN_EMAIL_CONFIRMATION);
+}
+
+SigninEmailConfirmationDialog::~SigninEmailConfirmationDialog() {}
+
+// static
+void SigninEmailConfirmationDialog::AskForConfirmation(
+ content::WebContents* contents,
+ Profile* profile,
+ const std::string& last_email,
+ const std::string& email,
+ const Callback& callback) {
+ base::RecordAction(base::UserMetricsAction("Signin_Show_ImportDataPrompt"));
+ SigninEmailConfirmationDialog* dialog = new SigninEmailConfirmationDialog(
+ contents, profile, last_email, email, callback);
+ dialog->ShowDialog();
+}
+
+void SigninEmailConfirmationDialog::ShowDialog() {
+ gfx::Size minSize(kDialogWidth, kDialogMinHeight);
+ gfx::Size maxSize(kDialogWidth, kDialogMaxHeight);
+ ConstrainedWebDialogDelegate* dialog_delegate =
+ ShowConstrainedWebDialogWithAutoResize(profile_, this, web_contents_,
+ minSize, maxSize);
+
+ content::WebContents* dialog_web_contents = dialog_delegate->GetWebContents();
+ dialog_observer_.reset(
+ new DialogWebContentsObserver(dialog_web_contents, this));
+}
+
+void SigninEmailConfirmationDialog::CloseDialog() {
+ content::WebContents* dialog_web_contents = GetDialogWebContents();
+ if (dialog_web_contents == nullptr)
+ return;
+ content::WebUI* web_ui = dialog_web_contents->GetWebUI();
+ if (web_ui) {
+ SigninEmailConfirmationUI* signin_email_confirmation_ui =
+ static_cast<SigninEmailConfirmationUI*>(web_ui->GetController());
+ if (signin_email_confirmation_ui)
+ signin_email_confirmation_ui->Close();
+ }
+}
+
+void SigninEmailConfirmationDialog::ResetDialogObserver() {
+ dialog_observer_.reset();
+}
+
+content::WebContents* SigninEmailConfirmationDialog::GetDialogWebContents()
+ const {
+ return dialog_observer_.get() ? dialog_observer_->web_contents() : nullptr;
+}
+
+// ui::WebDialogDelegate implementation
+
+ui::ModalType SigninEmailConfirmationDialog::GetDialogModalType() const {
+ return ui::MODAL_TYPE_WINDOW;
+}
+
+base::string16 SigninEmailConfirmationDialog::GetDialogTitle() const {
+ return base::string16();
+}
+
+GURL SigninEmailConfirmationDialog::GetDialogContentURL() const {
+ return GURL(chrome::kChromeUISigninEmailConfirmationURL);
+}
+
+void SigninEmailConfirmationDialog::GetWebUIMessageHandlers(
+ std::vector<content::WebUIMessageHandler*>* handlers) const {}
+
+void SigninEmailConfirmationDialog::GetDialogSize(gfx::Size* size) const {
+ DCHECK(size);
+
+ // Set the dialog width if it's not set, so that the dialog is center-aligned
+ // horizontally when it appears. Avoid setting a dialog height in here as
+ // this dialog auto-resizes.
+ if (size->IsEmpty())
+ size->set_width(kDialogWidth);
+}
+
+std::string SigninEmailConfirmationDialog::GetDialogArgs() const {
+ std::string data;
+ base::DictionaryValue dialog_args;
+ dialog_args.SetString("lastEmail", last_email_);
+ dialog_args.SetString("newEmail", new_email_);
+ base::JSONWriter::Write(dialog_args, &data);
+ return data;
+}
+
+void SigninEmailConfirmationDialog::OnDialogClosed(
+ const std::string& json_retval) {
+ Action action = CLOSE;
+ std::unique_ptr<base::DictionaryValue> ret_value(
+ base::DictionaryValue::From(base::JSONReader::Read(json_retval)));
+ if (ret_value) {
+ std::string action_string;
+ if (ret_value->GetString(kActionKey, &action_string)) {
+ if (action_string == kActionCancel) {
+ action = CLOSE;
+ } else if (action_string == kActionCreateNewUser) {
+ action = CREATE_NEW_USER;
+ } else if (action_string == kActionStartSync) {
+ action = START_SYNC;
+ } else {
+ NOTREACHED() << "Unexpected action value [" << action_string << "]";
+ }
+ } else {
+ NOTREACHED() << "No action in the dialog close return arguments";
+ }
+ } else {
+ // If the dialog is dismissed without any return value, then simply close
+ // the dialog. (see http://crbug.com/667690)
+ action = CLOSE;
+ }
+
+ if (!callback_.is_null()) {
+ callback_.Run(action);
+ callback_.Reset();
+ }
+}
+
+void SigninEmailConfirmationDialog::OnCloseContents(
+ content::WebContents* source,
+ bool* out_close_dialog) {
+ *out_close_dialog = true;
+}
+
+bool SigninEmailConfirmationDialog::ShouldShowDialogTitle() const {
+ return false;
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h b/chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h
new file mode 100644
index 00000000000..353cd21cbcf
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h
@@ -0,0 +1,99 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_EMAIL_CONFIRMATION_DIALOG_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_EMAIL_CONFIRMATION_DIALOG_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
+#include "ui/web_dialogs/web_dialog_delegate.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+class WebUIMessageHandler;
+class Profile;
+
+namespace content {
+class WebContents;
+}
+
+// A tab-modal dialog to ask the user to confirm his email before signing in.
+class SigninEmailConfirmationDialog : public ui::WebDialogDelegate {
+ public:
+ // Actions that can be taken when the user is asked to confirm their account.
+ enum Action {
+ // The user chose not to sign in to the current profile and wants chrome
+ // to create a new profile instead.
+ CREATE_NEW_USER,
+
+ // The user chose to sign in and enable sync in the current profile.
+ START_SYNC,
+
+ // The user chose abort sign in.
+ CLOSE
+ };
+
+ // Callback indicating action performed by the user.
+ using Callback = base::Callback<void(Action)>;
+
+ // Create and show the dialog, which owns itself.
+ // Ask the user for confirmation before starting to sync.
+ static void AskForConfirmation(content::WebContents* contents,
+ Profile* profile,
+ const std::string& last_email,
+ const std::string& email,
+ const Callback& callback);
+
+ private:
+ class DialogWebContentsObserver;
+
+ SigninEmailConfirmationDialog(content::WebContents* contents,
+ Profile* profile,
+ const std::string& last_email,
+ const std::string& new_email,
+ const Callback& callback);
+ ~SigninEmailConfirmationDialog() override;
+
+ // WebDialogDelegate implementation.
+ ui::ModalType GetDialogModalType() const override;
+ base::string16 GetDialogTitle() const override;
+ GURL GetDialogContentURL() const override;
+ void GetWebUIMessageHandlers(
+ std::vector<content::WebUIMessageHandler*>* handlers) const override;
+ void GetDialogSize(gfx::Size* size) const override;
+ std::string GetDialogArgs() const override;
+ void OnDialogClosed(const std::string& json_retval) override;
+ void OnCloseContents(content::WebContents* source,
+ bool* out_close_dialog) override;
+ bool ShouldShowDialogTitle() const override;
+
+ // Shows the dialog and releases ownership of this object. It will
+ // delete itself when the dialog is closed.
+ void ShowDialog();
+
+ // Closes the dialog.
+ void CloseDialog();
+
+ // Resets the dialog observer.
+ void ResetDialogObserver();
+
+ // Returns the media router dialog WebContents.
+ // Returns nullptr if there is no dialog.
+ content::WebContents* GetDialogWebContents() const;
+
+ // Web contents from which the "Learn more" link should be opened.
+ content::WebContents* const web_contents_;
+ Profile* const profile_;
+
+ std::string last_email_;
+ std::string new_email_;
+ Callback callback_;
+
+ // Observer for lifecycle events of the web contents of the dialog.
+ std::unique_ptr<DialogWebContentsObserver> dialog_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(SigninEmailConfirmationDialog);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_EMAIL_CONFIRMATION_DIALOG_H_
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_ui.cc b/chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_ui.cc
new file mode 100644
index 00000000000..bbf4deee60a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_ui.cc
@@ -0,0 +1,65 @@
+// 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 "chrome/browser/ui/webui/signin/signin_email_confirmation_ui.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "ui/web_dialogs/web_dialog_delegate.h"
+
+SigninEmailConfirmationUI::SigninEmailConfirmationUI(content::WebUI* web_ui)
+ : ConstrainedWebDialogUI(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+
+ content::WebUIDataSource* source = content::WebUIDataSource::Create(
+ chrome::kChromeUISigninEmailConfirmationHost);
+ source->SetJsonPath("strings.js");
+ source->SetDefaultResource(IDR_SIGNIN_EMAIL_CONFIRMATION_HTML);
+ source->AddResourcePath("signin_email_confirmation.js",
+ IDR_SIGNIN_EMAIL_CONFIRMATION_JS);
+ source->AddResourcePath("signin_shared_css.html", IDR_SIGNIN_SHARED_CSS_HTML);
+ source->AddLocalizedString("signinEmailConfirmationTitle",
+ IDS_SIGNIN_EMAIL_CONFIRMATION_TITLE);
+ source->AddLocalizedString(
+ "signinEmailConfirmationCreateProfileButtonTitle",
+ IDS_SIGNIN_EMAIL_CONFIRMATION_CREATE_PROFILE_RADIO_BUTTON_TITLE);
+ source->AddLocalizedString(
+ "signinEmailConfirmationCreateProfileButtonSubtitle",
+ IDS_SIGNIN_EMAIL_CONFIRMATION_CREATE_PROFILE_RADIO_BUTTON_SUBTITLE);
+ source->AddLocalizedString(
+ "signinEmailConfirmationStartSyncButtonTitle",
+ IDS_SIGNIN_EMAIL_CONFIRMATION_START_SYNC_RADIO_BUTTON_TITLE);
+ source->AddLocalizedString(
+ "signinEmailConfirmationStartSyncButtonSubtitle",
+ IDS_SIGNIN_EMAIL_CONFIRMATION_START_SYNC_RADIO_BUTTON_SUBTITLE);
+ source->AddLocalizedString(
+ "signinEmailConfirmationConfirmLabel",
+ IDS_SIGNIN_EMAIL_CONFIRMATION_CONFIRM_BUTTON_LABEL);
+ source->AddLocalizedString("signinEmailConfirmationCloseLabel",
+ IDS_SIGNIN_EMAIL_CONFIRMATION_CLOSE_BUTTON_LABEL);
+ base::DictionaryValue strings;
+ webui::SetLoadTimeDataDefaults(g_browser_process->GetApplicationLocale(),
+ &strings);
+ source->AddLocalizedStrings(strings);
+
+ content::WebUIDataSource::Add(profile, source);
+}
+
+SigninEmailConfirmationUI::~SigninEmailConfirmationUI() {}
+
+void SigninEmailConfirmationUI::Close() {
+ ConstrainedWebDialogDelegate* delegate = GetConstrainedDelegate();
+ if (delegate) {
+ delegate->GetWebDialogDelegate()->OnDialogClosed(std::string());
+ delegate->OnDialogCloseFromWebUI();
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_ui.h b/chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_ui.h
new file mode 100644
index 00000000000..9da3743872c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_email_confirmation_ui.h
@@ -0,0 +1,23 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_EMAIL_CONFIRMATION_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_EMAIL_CONFIRMATION_UI_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
+
+class SigninEmailConfirmationUI : public ConstrainedWebDialogUI {
+ public:
+ explicit SigninEmailConfirmationUI(content::WebUI* web_ui);
+ ~SigninEmailConfirmationUI() override;
+
+ // Closes this sign-in email confirmation webUI.
+ void Close();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SigninEmailConfirmationUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_EMAIL_CONFIRMATION_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_error_handler.cc b/chromium/chrome/browser/ui/webui/signin/signin_error_handler.cc
new file mode 100644
index 00000000000..6824d82f41e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_error_handler.cc
@@ -0,0 +1,116 @@
+// 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 "chrome/browser/ui/webui/signin/signin_error_handler.h"
+
+#include "base/bind.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/signin/signin_ui_util.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/user_manager.h"
+#include "chrome/browser/ui/webui/signin/signin_utils.h"
+#include "content/public/browser/web_ui.h"
+#include "url/gurl.h"
+
+SigninErrorHandler::SigninErrorHandler(Browser* browser, bool is_system_profile)
+ : browser_(browser), is_system_profile_(is_system_profile) {
+ // |browser_| must not be null when this dialog is presented from the
+ // user manager.
+ DCHECK(browser_ || is_system_profile_);
+ BrowserList::AddObserver(this);
+}
+
+SigninErrorHandler::~SigninErrorHandler() {
+ BrowserList::RemoveObserver(this);
+}
+
+void SigninErrorHandler::OnBrowserRemoved(Browser* browser) {
+ if (browser_ == browser)
+ browser_ = nullptr;
+}
+
+void SigninErrorHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "confirm",
+ base::Bind(&SigninErrorHandler::HandleConfirm, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "switchToExistingProfile",
+ base::Bind(&SigninErrorHandler::HandleSwitchToExistingProfile,
+ base::Unretained(this)));
+ if (!is_system_profile_) {
+ web_ui()->RegisterMessageCallback(
+ "learnMore", base::Bind(&SigninErrorHandler::HandleLearnMore,
+ base::Unretained(this)));
+ }
+ web_ui()->RegisterMessageCallback(
+ "initializedWithSize",
+ base::Bind(&SigninErrorHandler::HandleInitializedWithSize,
+ base::Unretained(this)));
+}
+
+void SigninErrorHandler::HandleSwitchToExistingProfile(
+ const base::ListValue* args) {
+ if (duplicate_profile_path_.empty())
+ return;
+
+ // CloseDialog will eventually destroy this object, so nothing should access
+ // its members after this call. However, closing the dialog may steal focus
+ // back to the original window, so make a copy of the path to switch to and
+ // perform the switch after the dialog is closed.
+ base::FilePath path_switching_to = duplicate_profile_path_;
+ CloseDialog();
+
+ // Switch to the existing duplicate profile. Do not create a new window when
+ // any existing ones can be reused.
+ profiles::SwitchToProfile(path_switching_to, false,
+ ProfileManager::CreateCallback(),
+ ProfileMetrics::SWITCH_PROFILE_DUPLICATE);
+}
+
+void SigninErrorHandler::HandleConfirm(const base::ListValue* args) {
+ CloseDialog();
+}
+
+void SigninErrorHandler::HandleLearnMore(const base::ListValue* args) {
+ // "Learn more" only shown when is_system_profile_=false
+ DCHECK(!is_system_profile_);
+ if (!browser_)
+ return;
+ CloseDialog();
+ signin_ui_util::ShowSigninErrorLearnMorePage(browser_->profile());
+}
+
+void SigninErrorHandler::HandleInitializedWithSize(
+ const base::ListValue* args) {
+ AllowJavascript();
+ if (duplicate_profile_path_.empty())
+ CallJavascriptFunction("signin.error.removeSwitchButton");
+
+ signin::SetInitializedModalHeight(browser_, web_ui(), args);
+
+ // After the dialog is shown, some platforms might have an element focused.
+ // To be consistent, clear the focused element on all platforms.
+ // TODO(anthonyvd): Figure out why this is needed on Mac and not other
+ // platforms and if there's a way to start unfocused while avoiding this
+ // workaround.
+ CallJavascriptFunction("signin.error.clearFocus");
+}
+
+void SigninErrorHandler::CloseDialog() {
+ if (is_system_profile_) {
+ CloseUserManagerProfileDialog();
+ } else if (browser_){
+ CloseBrowserModalSigninDialog();
+ }
+}
+
+void SigninErrorHandler::CloseBrowserModalSigninDialog() {
+ browser_->signin_view_controller()->CloseModalSignin();
+}
+
+void SigninErrorHandler::CloseUserManagerProfileDialog() {
+ UserManagerProfileDialog::HideDialog();
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_error_handler.h b/chromium/chrome/browser/ui/webui/signin/signin_error_handler.h
new file mode 100644
index 00000000000..d6e21d7b155
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_error_handler.h
@@ -0,0 +1,93 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_ERROR_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_ERROR_HANDLER_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/browser_list_observer.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace base {
+class ListValue;
+}
+
+class SigninErrorHandler : public content::WebUIMessageHandler,
+ public chrome::BrowserListObserver {
+ public:
+ // Constructor of a message handler that handles messages from the
+ // sign-in error WebUI.
+ // If |is_system_profile| is true, then the sign-in error dialog was
+ // presented from the user manager and |browser| is null. Otherwise, the
+ // sign-in error dialog was presented on a browser window and |browser| must
+ // not be null.
+ SigninErrorHandler(Browser* browser, bool is_system_profile);
+ ~SigninErrorHandler() override;
+
+ // chrome::BrowserListObserver:
+ void OnBrowserRemoved(Browser* browser) override;
+
+ // content::WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ // Sets the existing profile path that has the same username used for signin.
+ // This function is called when the signin error is a duplicate account error.
+ void set_duplicate_profile_path(
+ const base::FilePath& duplicate_profile_path) {
+ duplicate_profile_path_ = duplicate_profile_path;
+ }
+
+ protected:
+ // Handles "switch" message from the page. No arguments.
+ // This message is sent when the user switches to the existing profile of the
+ // same username used for signin.
+ virtual void HandleSwitchToExistingProfile(const base::ListValue* args);
+
+ // Handles "confirm" message from the page. No arguments.
+ // This message is sent when the user acknowledges the signin error.
+ virtual void HandleConfirm(const base::ListValue* args);
+
+ // Handles "learnMore" message from the page. No arguments.
+ // This message is sent when the user clicks on the "Learn more" link in the
+ // signin error dialog, which closes the dialog and takes the user to the
+ // Chrome Help page about fixing sync problems.
+ virtual void HandleLearnMore(const base::ListValue* args);
+
+ // Handles the web ui message sent when the html content is done being laid
+ // out and it's time to resize the native view hosting it to fit. |args| is
+ // a single integer value for the height the native view should resize to.
+ virtual void HandleInitializedWithSize(const base::ListValue* args);
+
+ // CloseDialog will eventually destroy this object, so nothing should access
+ // its members after this call.
+ void CloseDialog();
+
+ // Closes the modal sign-in view dialog.
+ //
+ // Virtual, so that it can be overriden from unit tests.
+ virtual void CloseBrowserModalSigninDialog();
+
+ // Closes the user manager profile dialog.
+ //
+ // Virtual, so that it can be overriden from unit tests.
+ virtual void CloseUserManagerProfileDialog();
+
+ private:
+ // Weak reference to the browser that showed the sign-in error dialog.
+ // This is null when this sign-in error dialog is presented from the user
+ // manager.
+ Browser* browser_;
+
+ // True when this sign-in error dialog is presented from the user manager.
+ bool is_system_profile_;
+
+ base::FilePath duplicate_profile_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(SigninErrorHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_ERROR_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_error_handler_unittest.cc b/chromium/chrome/browser/ui/webui/signin/signin_error_handler_unittest.cc
new file mode 100644
index 00000000000..a6687c9c3b1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_error_handler_unittest.cc
@@ -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.
+
+#include "chrome/browser/ui/webui/signin/signin_error_handler.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/signin/signin_error_ui.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/browser_with_test_window_test.h"
+#include "chrome/test/base/dialog_test_browser_window.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_web_ui.h"
+
+namespace {
+const char kSigninErrorLearnMoreUrl[] =
+ "https://support.google.com/chrome/answer/1181420?";
+
+class TestingSigninErrorHandler : public SigninErrorHandler {
+ public:
+ TestingSigninErrorHandler(Browser* browser,
+ bool is_system_profile,
+ content::WebUI* web_ui)
+ : SigninErrorHandler(browser, is_system_profile),
+ browser_modal_dialog_did_close_(false),
+ user_manager_profile_dialog_did_close_(false) {
+ set_web_ui(web_ui);
+ }
+
+ void CloseBrowserModalSigninDialog() override {
+ browser_modal_dialog_did_close_ = true;
+ SigninErrorHandler::CloseBrowserModalSigninDialog();
+ }
+
+ void CloseUserManagerProfileDialog() override {
+ user_manager_profile_dialog_did_close_ = true;
+ SigninErrorHandler::CloseUserManagerProfileDialog();
+ }
+
+ using SigninErrorHandler::HandleSwitchToExistingProfile;
+ using SigninErrorHandler::HandleConfirm;
+ using SigninErrorHandler::HandleLearnMore;
+ using SigninErrorHandler::HandleInitializedWithSize;
+
+ bool browser_modal_dialog_did_close() {
+ return browser_modal_dialog_did_close_;
+ }
+
+ bool user_manager_profile_dialog_did_close() {
+ return user_manager_profile_dialog_did_close_;
+ }
+
+ private:
+ bool browser_modal_dialog_did_close_;
+ bool user_manager_profile_dialog_did_close_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestingSigninErrorHandler);
+};
+
+class SigninErrorHandlerTest : public BrowserWithTestWindowTest {
+ public:
+ SigninErrorHandlerTest()
+ : web_ui_(new content::TestWebUI), handler_(nullptr) {}
+
+ void SetUp() override {
+ BrowserWithTestWindowTest::SetUp();
+ chrome::NewTab(browser());
+ web_ui()->set_web_contents(
+ browser()->tab_strip_model()->GetActiveWebContents());
+ signin_error_ui_.reset(new SigninErrorUI(web_ui()));
+ }
+
+ void TearDown() override {
+ signin_error_ui_.reset();
+ web_ui_.reset();
+ BrowserWithTestWindowTest::TearDown();
+ }
+
+ void CreateHandlerInBrowser() {
+ DCHECK(!handler_);
+ auto handler = base::MakeUnique<TestingSigninErrorHandler>(
+ browser(), false /* is_system_profile */, web_ui());
+ handler_ = handler.get();
+ signin_error_ui_.reset(new SigninErrorUI(web_ui()));
+ web_ui()->AddMessageHandler(std::move(handler));
+ }
+
+ void CreateHandlerInUserManager() {
+ DCHECK(!handler_);
+ auto handler = base::MakeUnique<TestingSigninErrorHandler>(
+ nullptr /* browser */, true /* is_system_profile */, web_ui());
+ handler_ = handler.get();
+ web_ui()->AddMessageHandler(std::move(handler));
+ }
+
+ TestingSigninErrorHandler* handler() { return handler_; }
+
+ content::TestWebUI* web_ui() { return web_ui_.get(); }
+
+ // BrowserWithTestWindowTest
+ BrowserWindow* CreateBrowserWindow() override {
+ return new DialogTestBrowserWindow;
+ }
+
+ private:
+ std::unique_ptr<content::TestWebUI> web_ui_;
+ std::unique_ptr<SigninErrorUI> signin_error_ui_;
+ TestingSigninErrorHandler* handler_; // Not owned.
+
+ DISALLOW_COPY_AND_ASSIGN(SigninErrorHandlerTest);
+};
+
+TEST_F(SigninErrorHandlerTest, InBrowserHandleLearnMore) {
+ // Before the test, there is only one new tab opened.
+ TabStripModel* tab_strip_model = browser()->tab_strip_model();
+ EXPECT_EQ(1, tab_strip_model->count());
+ EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
+ tab_strip_model->GetActiveWebContents()->GetURL());
+
+ // Open learn more
+ CreateHandlerInBrowser();
+ base::ListValue args;
+ handler()->HandleLearnMore(&args);
+
+ // Dialog should be closed now.
+ EXPECT_TRUE(handler()->browser_modal_dialog_did_close());
+
+ // Verify that the learn more URL was opened.
+ EXPECT_EQ(2, tab_strip_model->count());
+ EXPECT_EQ(GURL(kSigninErrorLearnMoreUrl),
+ tab_strip_model->GetActiveWebContents()->GetURL());
+}
+
+TEST_F(SigninErrorHandlerTest, InBrowserHandleLearnMoreAfterBrowserRemoved) {
+ // Before the test, there is only one new tab opened.
+ TabStripModel* tab_strip_model = browser()->tab_strip_model();
+ EXPECT_EQ(1, tab_strip_model->count());
+ EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
+ tab_strip_model->GetActiveWebContents()->GetURL());
+
+ // Inform the handler that the browser was removed;
+ CreateHandlerInBrowser();
+ handler()->OnBrowserRemoved(browser());
+
+ // Open learn more
+ base::ListValue args;
+ handler()->HandleLearnMore(&args);
+
+ // Dialog is not closed if the browser was removed.
+ EXPECT_FALSE(handler()->browser_modal_dialog_did_close());
+
+ // Verify that the learn more URL was not opened as the browser was removed.
+ EXPECT_EQ(1, tab_strip_model->count());
+ EXPECT_EQ(GURL(chrome::kChromeUINewTabURL),
+ tab_strip_model->GetActiveWebContents()->GetURL());
+}
+
+TEST_F(SigninErrorHandlerTest, InBrowserTestConfirm) {
+ CreateHandlerInBrowser();
+ base::ListValue args;
+ handler()->HandleConfirm(&args);
+
+ // Confirm simply closes the dialog.
+ EXPECT_TRUE(handler()->browser_modal_dialog_did_close());
+}
+
+TEST_F(SigninErrorHandlerTest, InUserManagerTestConfirm) {
+ CreateHandlerInUserManager();
+ base::ListValue args;
+ handler()->HandleConfirm(&args);
+
+ // Confirm simply closes the dialog.
+ EXPECT_TRUE(handler()->user_manager_profile_dialog_did_close());
+}
+
+} // namespace
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_error_ui.cc b/chromium/chrome/browser/ui/webui/signin/signin_error_ui.cc
new file mode 100644
index 00000000000..e13b6a95fdd
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_error_ui.cc
@@ -0,0 +1,134 @@
+// 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 "chrome/browser/ui/webui/signin/signin_error_ui.h"
+
+#include <vector>
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/user_manager.h"
+#include "chrome/browser/ui/webui/signin/login_ui_service.h"
+#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
+#include "chrome/browser/ui/webui/signin/signin_error_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.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"
+#include "ui/gfx/text_elider.h"
+
+SigninErrorUI::SigninErrorUI(content::WebUI* web_ui)
+ : SigninWebDialogUI(web_ui) {
+ Profile* webui_profile = Profile::FromWebUI(web_ui);
+ if (webui_profile->GetOriginalProfile()->IsSystemProfile()) {
+ InitializeMessageHandlerForUserManager();
+ }
+}
+
+void SigninErrorUI::InitializeMessageHandlerWithBrowser(Browser* browser) {
+ DCHECK(browser);
+ Initialize(browser, false /* is_system_profile */);
+}
+
+void SigninErrorUI::InitializeMessageHandlerForUserManager() {
+ Initialize(nullptr, true /* is_system_profile */);
+}
+
+void SigninErrorUI::Initialize(Browser* browser, bool is_system_profile) {
+ Profile* webui_profile = Profile::FromWebUI(web_ui());
+ Profile* signin_profile;
+ std::unique_ptr<SigninErrorHandler> handler =
+ base::MakeUnique<SigninErrorHandler>(browser, is_system_profile);
+
+ if (is_system_profile) {
+ signin_profile = g_browser_process->profile_manager()->GetProfileByPath(
+ UserManager::GetSigninProfilePath());
+ // Sign in is completed before profile creation.
+ if (!signin_profile)
+ signin_profile = webui_profile->GetOriginalProfile();
+ } else {
+ signin_profile = webui_profile;
+ }
+
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUISigninErrorHost);
+ source->SetJsonPath("strings.js");
+ source->SetDefaultResource(IDR_SIGNIN_ERROR_HTML);
+ source->AddResourcePath("signin_error.js", IDR_SIGNIN_ERROR_JS);
+ source->AddResourcePath("signin_shared_css.html", IDR_SIGNIN_SHARED_CSS_HTML);
+ source->AddBoolean("isSystemProfile", is_system_profile);
+
+ // Retrieve the last signin error message and email used.
+ LoginUIService* login_ui_service =
+ LoginUIServiceFactory::GetForProfile(signin_profile);
+ const base::string16 last_login_result(
+ login_ui_service->GetLastLoginResult());
+ const base::string16 email = login_ui_service->GetLastLoginErrorEmail();
+ if (email.empty()) {
+ source->AddLocalizedString("signinErrorTitle", IDS_SIGNIN_ERROR_TITLE);
+ } else {
+ source->AddString(
+ "signinErrorTitle",
+ l10n_util::GetStringFUTF16(IDS_SIGNIN_ERROR_EMAIL_TITLE, email));
+ }
+
+ // Tweak the dialog UI depending on whether the signin error is
+ // username-in-use error and the error UI is shown with a browser window.
+ base::string16 existing_name;
+ if (!is_system_profile &&
+ last_login_result.compare(
+ l10n_util::GetStringUTF16(IDS_SYNC_USER_NAME_IN_USE_ERROR)) == 0) {
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ if (profile_manager) {
+ std::vector<ProfileAttributesEntry*> entries =
+ profile_manager->GetProfileAttributesStorage()
+ .GetAllProfilesAttributes();
+ DCHECK(!email.empty());
+ for (const ProfileAttributesEntry* entry : entries) {
+ if (gaia::AreEmailsSame(base::UTF16ToUTF8(email),
+ base::UTF16ToUTF8(entry->GetUserName()))) {
+ handler->set_duplicate_profile_path(entry->GetPath());
+ existing_name = entry->GetName();
+ }
+ }
+ }
+ DCHECK(!existing_name.empty());
+ source->AddString(
+ "signinErrorMessage",
+ l10n_util::GetStringFUTF16(IDS_SYNC_USER_NAME_IN_USE_BY_ERROR,
+ existing_name));
+ // Elide the existing name for the switch user button label.
+ existing_name =
+ gfx::TruncateString(existing_name, 10, gfx::CHARACTER_BREAK);
+ } else {
+ source->AddString("signinErrorMessage", last_login_result);
+ }
+
+ // Add button label strings.
+ 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("signinErrorCloseLabel",
+ IDS_SIGNIN_ERROR_CLOSE_BUTTON_LABEL);
+ source->AddLocalizedString("signinErrorOkLabel",
+ IDS_SIGNIN_ERROR_OK_BUTTON_LABEL);
+
+ base::DictionaryValue strings;
+ webui::SetLoadTimeDataDefaults(g_browser_process->GetApplicationLocale(),
+ &strings);
+ source->AddLocalizedStrings(strings);
+
+ content::WebUIDataSource::Add(webui_profile, source);
+ web_ui()->AddMessageHandler(std::move(handler));
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_error_ui.h b/chromium/chrome/browser/ui/webui/signin/signin_error_ui.h
new file mode 100644
index 00000000000..ce7985c6b43
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_error_ui.h
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_ERROR_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_ERROR_UI_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/signin/signin_web_dialog_ui.h"
+
+namespace ui {
+class WebUI;
+}
+
+class SigninErrorUI : public SigninWebDialogUI {
+ public:
+ explicit SigninErrorUI(content::WebUI* web_ui);
+ ~SigninErrorUI() override {}
+
+ // SigninWebDialogUI:
+ void InitializeMessageHandlerWithBrowser(Browser* browser) override;
+
+ private:
+ void InitializeMessageHandlerForUserManager();
+ void Initialize(Browser* browser, bool is_system_profile);
+
+ DISALLOW_COPY_AND_ASSIGN(SigninErrorUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_ERROR_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_supervised_user_import_handler.cc b/chromium/chrome/browser/ui/webui/signin/signin_supervised_user_import_handler.cc
new file mode 100644
index 00000000000..f7e115ff656
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_supervised_user_import_handler.cc
@@ -0,0 +1,317 @@
+// 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 "chrome/browser/ui/webui/signin/signin_supervised_user_import_handler.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/value_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_avatar_icon_util.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/signin/signin_error_controller_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/supervised_user/legacy/supervised_user_shared_settings_service.h"
+#include "chrome/browser/supervised_user/legacy/supervised_user_shared_settings_service_factory.h"
+#include "chrome/browser/supervised_user/legacy/supervised_user_sync_service.h"
+#include "chrome/browser/supervised_user/legacy/supervised_user_sync_service_factory.h"
+#include "chrome/browser/supervised_user/supervised_user_constants.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/user_manager.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/signin_error_controller.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/common/referrer.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+
+SigninSupervisedUserImportHandler::SigninSupervisedUserImportHandler()
+ : weak_ptr_factory_(this) {
+}
+
+SigninSupervisedUserImportHandler::~SigninSupervisedUserImportHandler() {
+}
+
+void SigninSupervisedUserImportHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ DCHECK(localized_strings);
+
+ localized_strings->SetString("supervisedUserImportTitle",
+ l10n_util::GetStringUTF16(
+ IDS_IMPORT_EXISTING_LEGACY_SUPERVISED_USER_TITLE));
+ localized_strings->SetString("supervisedUserImportText",
+ l10n_util::GetStringUTF16(
+ IDS_IMPORT_EXISTING_LEGACY_SUPERVISED_USER_TEXT));
+ localized_strings->SetString("noSupervisedUserImportText",
+ l10n_util::GetStringUTF16(IDS_IMPORT_NO_EXISTING_SUPERVISED_USER_TEXT));
+ localized_strings->SetString("supervisedUserImportOk",
+ l10n_util::GetStringUTF16(IDS_IMPORT_EXISTING_LEGACY_SUPERVISED_USER_OK));
+ localized_strings->SetString("supervisedUserAlreadyOnThisDevice",
+ l10n_util::GetStringUTF16(
+ IDS_LEGACY_SUPERVISED_USER_ALREADY_ON_THIS_DEVICE));
+}
+
+void SigninSupervisedUserImportHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("getExistingSupervisedUsers",
+ base::Bind(&SigninSupervisedUserImportHandler::GetExistingSupervisedUsers,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("openUrlInLastActiveProfileBrowser",
+ base::Bind(
+ &SigninSupervisedUserImportHandler::OpenUrlInLastActiveProfileBrowser,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "authenticateCustodian",
+ base::Bind(&SigninSupervisedUserImportHandler::AuthenticateCustodian,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "cancelLoadingSupervisedUsers",
+ base::Bind(
+ &SigninSupervisedUserImportHandler::HandleCancelLoadSupervisedUsers,
+ base::Unretained(this)));
+}
+
+void SigninSupervisedUserImportHandler::AssignWebUICallbackId(
+ const base::ListValue* args) {
+ CHECK_LE(1U, args->GetSize());
+ CHECK(webui_callback_id_.empty());
+ CHECK(args->GetString(0, &webui_callback_id_));
+ AllowJavascript();
+}
+
+void SigninSupervisedUserImportHandler::OpenUrlInLastActiveProfileBrowser(
+ const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+ std::string url;
+ bool success = args->GetString(0, &url);
+ DCHECK(success);
+ content::OpenURLParams params(GURL(url), content::Referrer(),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui::PAGE_TRANSITION_LINK, false);
+ // ProfileManager::GetLastUsedProfile() will attempt to load the default
+ // profile if there is no last used profile. If the default profile is not
+ // fully loaded and initialized, it will attempt to do so synchronously.
+ // Therefore we cannot use that method here. If the last used profile is not
+ // loaded, we do nothing. This is an edge case and should not happen often.
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ base::FilePath last_used_profile_dir =
+ profile_manager->GetLastUsedProfileDir(profile_manager->user_data_dir());
+ Profile* last_used_profile =
+ profile_manager->GetProfileByPath(last_used_profile_dir);
+
+ if (last_used_profile) {
+ // Last used profile may be the Guest Profile.
+ if (ProfileManager::IncognitoModeForced(last_used_profile))
+ last_used_profile = last_used_profile->GetOffTheRecordProfile();
+
+ // Get the browser owned by the last used profile or create a new one if
+ // it doesn't exist.
+ Browser* browser = chrome::FindLastActiveWithProfile(last_used_profile);
+ if (!browser)
+ browser = new Browser(
+ Browser::CreateParams(Browser::TYPE_TABBED, last_used_profile, true));
+ browser->OpenURL(params);
+ }
+}
+
+void SigninSupervisedUserImportHandler::AuthenticateCustodian(
+ const base::ListValue* args) {
+ CHECK_EQ(1U, args->GetSize());
+
+ std::string email;
+ bool success = args->GetString(0, &email);
+ DCHECK(success);
+
+ UserManagerProfileDialog::ShowReauthDialog(
+ web_ui()->GetWebContents()->GetBrowserContext(), email,
+ signin_metrics::Reason::REASON_REAUTHENTICATION);
+}
+
+void SigninSupervisedUserImportHandler::GetExistingSupervisedUsers(
+ const base::ListValue* args) {
+ CHECK_EQ(2U, args->GetSize());
+ AssignWebUICallbackId(args);
+
+ base::FilePath custodian_profile_path;
+ const base::Value* profile_path_value;
+ bool success = args->Get(1, &profile_path_value);
+ DCHECK(success);
+ success = base::GetValueAsFilePath(*profile_path_value,
+ &custodian_profile_path);
+ DCHECK(success);
+
+ // Load custodian profile.
+ g_browser_process->profile_manager()->CreateProfileAsync(
+ custodian_profile_path,
+ base::Bind(
+ &SigninSupervisedUserImportHandler::LoadCustodianProfileCallback,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::string16(), std::string(), std::string());
+}
+
+void SigninSupervisedUserImportHandler::HandleCancelLoadSupervisedUsers(
+ const base::ListValue* args) {
+ webui_callback_id_.clear();
+}
+
+void SigninSupervisedUserImportHandler::LoadCustodianProfileCallback(
+ Profile* profile, Profile::CreateStatus status) {
+
+ // This method gets called once before with Profile::CREATE_STATUS_CREATED.
+ switch (status) {
+ case Profile::CREATE_STATUS_LOCAL_FAIL: {
+ RejectCallback(GetLocalErrorMessage());
+ break;
+ }
+ case Profile::CREATE_STATUS_CREATED: {
+ // Ignore the intermediate status.
+ break;
+ }
+ case Profile::CREATE_STATUS_INITIALIZED: {
+ // We are only interested in Profile::CREATE_STATUS_INITIALIZED when
+ // everything is ready.
+ if (profile->IsSupervised()) {
+ webui_callback_id_.clear();
+ return;
+ }
+
+ if (!IsAccountConnected(profile) || HasAuthError(profile)) {
+ RejectCallback(GetAuthErrorMessage(profile));
+ return;
+ }
+
+ SupervisedUserSyncService* supervised_user_sync_service =
+ SupervisedUserSyncServiceFactory::GetForProfile(profile);
+ if (supervised_user_sync_service) {
+ supervised_user_sync_service->GetSupervisedUsersAsync(
+ base::Bind(
+ &SigninSupervisedUserImportHandler::SendExistingSupervisedUsers,
+ weak_ptr_factory_.GetWeakPtr(), profile));
+ }
+ break;
+ }
+ case Profile::CREATE_STATUS_CANCELED:
+ case Profile::CREATE_STATUS_REMOTE_FAIL:
+ case Profile::MAX_CREATE_STATUS: {
+ NOTREACHED();
+ break;
+ }
+ }
+}
+
+void SigninSupervisedUserImportHandler::RejectCallback(
+ const base::string16& error) {
+ RejectJavascriptCallback(base::Value(webui_callback_id_), base::Value(error));
+ webui_callback_id_.clear();
+}
+
+base::string16 SigninSupervisedUserImportHandler::GetLocalErrorMessage() const {
+ return l10n_util::GetStringUTF16(
+ IDS_LEGACY_SUPERVISED_USER_IMPORT_LOCAL_ERROR);
+}
+
+base::string16 SigninSupervisedUserImportHandler::GetAuthErrorMessage(
+ Profile* profile) const {
+ return l10n_util::GetStringFUTF16(
+ IDS_PROFILES_CREATE_CUSTODIAN_ACCOUNT_DETAILS_OUT_OF_DATE_ERROR,
+ base::ASCIIToUTF16(profile->GetProfileUserName()));
+}
+
+void SigninSupervisedUserImportHandler::SendExistingSupervisedUsers(
+ Profile* profile,
+ const base::DictionaryValue* dict) {
+ DCHECK(dict);
+ std::vector<ProfileAttributesEntry*> entries =
+ g_browser_process->profile_manager()->GetProfileAttributesStorage().
+ GetAllProfilesAttributes();
+
+ // Collect the ids of local supervised user profiles.
+ std::set<std::string> supervised_user_ids;
+ for (auto* entry : entries) {
+ // Filter out omitted profiles. These are currently being imported, and
+ // shouldn't show up as "already on this device" just yet.
+ if (entry->IsLegacySupervised() && !entry->IsOmitted()) {
+ supervised_user_ids.insert(entry->GetSupervisedUserId());
+ }
+ }
+
+ base::ListValue supervised_users;
+ SupervisedUserSharedSettingsService* service =
+ SupervisedUserSharedSettingsServiceFactory::GetForBrowserContext(profile);
+ for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
+ const base::DictionaryValue* value = NULL;
+ bool success = it.value().GetAsDictionary(&value);
+ DCHECK(success);
+ std::string name;
+ value->GetString(SupervisedUserSyncService::kName, &name);
+
+ std::unique_ptr<base::DictionaryValue> supervised_user(
+ new base::DictionaryValue);
+ supervised_user->SetString("id", it.key());
+ supervised_user->SetString("name", name);
+
+ int avatar_index = SupervisedUserSyncService::kNoAvatar;
+ const base::Value* avatar_index_value =
+ service->GetValue(it.key(), supervised_users::kChromeAvatarIndex);
+ if (avatar_index_value) {
+ success = avatar_index_value->GetAsInteger(&avatar_index);
+ } else {
+ // Check if there is a legacy avatar index stored.
+ std::string avatar_str;
+ value->GetString(SupervisedUserSyncService::kChromeAvatar, &avatar_str);
+ success =
+ SupervisedUserSyncService::GetAvatarIndex(avatar_str, &avatar_index);
+ }
+ DCHECK(success);
+
+ std::string avatar_url =
+ avatar_index == SupervisedUserSyncService::kNoAvatar ?
+ profiles::GetDefaultAvatarIconUrl(
+ profiles::GetPlaceholderAvatarIndex()) :
+ profiles::GetDefaultAvatarIconUrl(avatar_index);
+ supervised_user->SetString("iconURL", avatar_url);
+ bool on_current_device =
+ supervised_user_ids.find(it.key()) != supervised_user_ids.end();
+ supervised_user->SetBoolean("onCurrentDevice", on_current_device);
+
+ supervised_users.Append(std::move(supervised_user));
+ }
+
+ // Resolve callback with response.
+ ResolveJavascriptCallback(base::Value(webui_callback_id_), supervised_users);
+ webui_callback_id_.clear();
+}
+
+bool SigninSupervisedUserImportHandler::IsAccountConnected(
+ Profile* profile) const {
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetForProfile(profile);
+ return signin_manager && signin_manager->IsAuthenticated();
+}
+
+bool SigninSupervisedUserImportHandler::HasAuthError(Profile* profile) const {
+ SigninErrorController* error_controller =
+ SigninErrorControllerFactory::GetForProfile(profile);
+ if (!error_controller)
+ return true;
+
+ GoogleServiceAuthError::State state = error_controller->auth_error().state();
+ return state != GoogleServiceAuthError::NONE;
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_supervised_user_import_handler.h b/chromium/chrome/browser/ui/webui/signin/signin_supervised_user_import_handler.h
new file mode 100644
index 00000000000..8eaf06f2660
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_supervised_user_import_handler.h
@@ -0,0 +1,101 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_SUPERVISED_USER_IMPORT_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_SUPERVISED_USER_IMPORT_HANDLER_H_
+
+#include <string>
+
+#include "base/callback_list.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
+#include "base/strings/string16.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/supervised_user/supervised_users.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+// Handler for the 'import existing supervised user' dialog.
+class SigninSupervisedUserImportHandler : public content::WebUIMessageHandler {
+ public:
+ SigninSupervisedUserImportHandler();
+ ~SigninSupervisedUserImportHandler() override;
+
+ void GetLocalizedValues(base::DictionaryValue* localized_strings);
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(SigninSupervisedUserImportHandlerTest,
+ NotAuthenticated);
+ FRIEND_TEST_ALL_PREFIXES(SigninSupervisedUserImportHandlerTest, AuthError);
+ FRIEND_TEST_ALL_PREFIXES(SigninSupervisedUserImportHandlerTest,
+ CustodianIsSupervised);
+ FRIEND_TEST_ALL_PREFIXES(SigninSupervisedUserImportHandlerTest,
+ SendExistingSupervisedUsers);
+ // Assigns a new |webui_callback_id_|. Ensures that previous in-flight request
+ // has been fulfilled.
+ void AssignWebUICallbackId(const base::ListValue* args);
+
+ // Callback for the "openUrlInLastActiveProfileBrowser" message. Opens the
+ // given url in a new background tab in the browser owned by the last active
+ // profile. Hyperlinks don't work in the user manager since the system profile
+ // browser is not tabbed.
+ void OpenUrlInLastActiveProfileBrowser(const base::ListValue* args);
+
+ // Used to cancel loading existing supervised users. Resets WebUI callback ID
+ // of the last in-flight async request.
+ void HandleCancelLoadSupervisedUsers(const base::ListValue* args);
+
+ // Callback for the "getExistingSupervisedUsers" message. Returns a list of
+ // supervised users attached to the given custodian profile.
+ void GetExistingSupervisedUsers(const base::ListValue* args);
+
+ // Callback for the "authenticateCustodian" message. Authenticates the
+ // custodian profile with the given email address.
+ void AuthenticateCustodian(const base::ListValue* args);
+
+ void LoadCustodianProfileCallback(Profile* custodian_profile,
+ Profile::CreateStatus status);
+
+ // Reject the WebUI callback with an error message.
+ void RejectCallback(const base::string16& error);
+
+ base::string16 GetLocalErrorMessage() const;
+
+ base::string16 GetAuthErrorMessage(Profile* profile) const;
+
+ // Sends an array of supervised users to WebUI. Each entry is of the form:
+ // supervisedProfile = {
+ // id: "Supervised User ID",
+ // name: "Supervised User Name",
+ // iconURL: "chrome://path/to/icon/image",
+ // onCurrentDevice: true or false,
+ // }
+ // The array holds all existing supervised users attached to the
+ // custodian's profile which initiated the request.
+ void SendExistingSupervisedUsers(Profile* profile,
+ const base::DictionaryValue* dict);
+
+ bool IsAccountConnected(Profile* profile) const;
+ bool HasAuthError(Profile* profile) const;
+
+ // The WebUI callback ID of the last in-flight async request. There is always
+ // only one in-flight such request.
+ std::string webui_callback_id_;
+
+ Profile* last_used_profile;
+
+ base::WeakPtrFactory<SigninSupervisedUserImportHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SigninSupervisedUserImportHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_SUPERVISED_USER_IMPORT_HANDLER_H_
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
new file mode 100644
index 00000000000..e71153c8110
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_supervised_user_import_handler_unittest.cc
@@ -0,0 +1,262 @@
+// 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 "chrome/browser/ui/webui/signin/signin_supervised_user_import_handler.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "chrome/browser/signin/fake_signin_manager_builder.h"
+#include "chrome/browser/signin/signin_error_controller_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/supervised_user/legacy/supervised_user_sync_service.h"
+#include "chrome/browser/supervised_user/legacy/supervised_user_sync_service_factory.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.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"
+#include "components/sync_preferences/pref_service_syncable.h"
+#include "content/public/test/test_web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+const char kTestGaiaId[] = "test-gaia-id";
+const char kTestEmail[] = "foo@bar.com";
+
+const char kTestWebUIResponse[] = "cr.webUIResponse";
+const char kTestCallbackId[] = "test-callback-id";
+
+const char kSupervisedUserId[] = "test-supervised-id";
+const char kSupervisedUsername[] = "test-supervised-username";
+const char kSupervisedUserAvatarName[] = "chrome-avatar-index:0";
+const char kSupervisedUserAvatarUrl[] = "chrome://theme/IDR_PROFILE_AVATAR_0";
+
+syncer::SyncData CreateSyncData(const std::string& id,
+ const std::string& name,
+ const std::string& chrome_avatar) {
+ sync_pb::EntitySpecifics specifics;
+ specifics.mutable_managed_user()->set_id(id);
+ specifics.mutable_managed_user()->set_name(name);
+ 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());
+}
+
+} // namespace
+
+class TestSigninSupervisedUserImportHandler :
+ public SigninSupervisedUserImportHandler {
+ public:
+ explicit TestSigninSupervisedUserImportHandler(content::WebUI* web_ui) {
+ set_web_ui(web_ui);
+ }
+};
+
+class SigninSupervisedUserImportHandlerTest : public BrowserWithTestWindowTest {
+ public:
+ SigninSupervisedUserImportHandlerTest() : web_ui_(new content::TestWebUI) {}
+
+ void SetUp() override {
+ BrowserWithTestWindowTest::SetUp();
+ handler_.reset(new TestSigninSupervisedUserImportHandler(web_ui()));
+
+ // Build a test profile.
+ profile_manager_.reset(
+ new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
+ ASSERT_TRUE(profile_manager_->SetUp());
+
+ TestingProfile::TestingFactories factories;
+ factories.push_back(std::make_pair(SigninManagerFactory::GetInstance(),
+ BuildFakeSigninManagerBase));
+ profile_ = profile_manager_.get()->CreateTestingProfile(
+ "test-profile",
+ std::unique_ptr<sync_preferences::PrefServiceSyncable>(),
+ base::UTF8ToUTF16("test-profile"), 0, std::string(), factories);
+
+ // Authenticate the test profile.
+ fake_signin_manager_ = static_cast<FakeSigninManagerForTesting*>(
+ SigninManagerFactory::GetForProfile(profile_));
+ fake_signin_manager_->SetAuthenticatedAccountInfo(kTestGaiaId, kTestEmail);
+
+ // Add supervised users to the profile.
+ SupervisedUserSyncService* sync_service_ =
+ SupervisedUserSyncServiceFactory::GetForProfile(profile_);
+ syncer::SyncDataList sync_data;
+ sync_data.push_back(CreateSyncData(kSupervisedUserId,
+ kSupervisedUsername,
+ kSupervisedUserAvatarName));
+ syncer::SyncMergeResult result = sync_service_->MergeDataAndStartSyncing(
+ syncer::SUPERVISED_USERS,
+ sync_data,
+ std::unique_ptr<syncer::SyncChangeProcessor>(
+ new syncer::FakeSyncChangeProcessor()),
+ std::unique_ptr<syncer::SyncErrorFactory>(
+ new syncer::SyncErrorFactoryMock()));
+ EXPECT_FALSE(result.error().IsSet());
+ EXPECT_EQ(1u, sync_service_->GetSupervisedUsers()->size());
+ }
+
+ void TearDown() override {
+ profile_manager_.reset();
+ handler_.reset();
+ web_ui_.reset();
+ BrowserWithTestWindowTest::TearDown();
+ }
+
+ content::TestWebUI* web_ui() {
+ return web_ui_.get();
+ }
+
+ TestSigninSupervisedUserImportHandler* handler() {
+ return handler_.get();
+ }
+
+ TestingProfileManager* profile_manager() {
+ return profile_manager_.get();
+ }
+
+ TestingProfile* profile() {
+ return profile_;
+ }
+
+ FakeSigninManagerForTesting* signin_manager() {
+ return fake_signin_manager_;
+ }
+
+ void VerifyResponse(size_t expected_total_calls,
+ const std::string& expected_callback_id,
+ bool expected_fulfilled) {
+ EXPECT_EQ(expected_total_calls, web_ui()->call_data().size());
+
+ EXPECT_EQ(kTestWebUIResponse, web_ui()->call_data()[0]->function_name());
+
+ std::string callback_id;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg1()->GetAsString(&callback_id));
+ EXPECT_EQ(expected_callback_id, callback_id);
+
+ bool fulfilled;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg2()->GetAsBoolean(&fulfilled));
+ EXPECT_EQ(expected_fulfilled, fulfilled);
+ }
+
+ private:
+ std::unique_ptr<content::TestWebUI> web_ui_;
+ std::unique_ptr<TestSigninSupervisedUserImportHandler> handler_;
+ std::unique_ptr<TestingProfileManager> profile_manager_;
+ TestingProfile* profile_;
+ FakeSigninManagerForTesting* fake_signin_manager_;
+};
+
+TEST_F(SigninSupervisedUserImportHandlerTest, NotAuthenticated) {
+ // Stop Sync before signing out.
+ SupervisedUserSyncService* sync_service_ =
+ SupervisedUserSyncServiceFactory::GetForProfile(profile());
+ sync_service_->StopSyncing(syncer::SUPERVISED_USERS);
+
+ // Sign out the user.
+ signin_manager()->ForceSignOut();
+
+ // Test the JS -> C++ -> JS callback path.
+ base::ListValue list_args;
+ list_args.AppendString(kTestCallbackId);
+ list_args.AppendString(profile()->GetPath().AsUTF16Unsafe());
+ handler()->GetExistingSupervisedUsers(&list_args);
+
+ // Expect an error response.
+ VerifyResponse(1U, kTestCallbackId, false);
+
+ base::string16 expected_error_message = l10n_util::GetStringFUTF16(
+ IDS_PROFILES_CREATE_CUSTODIAN_ACCOUNT_DETAILS_OUT_OF_DATE_ERROR,
+ base::ASCIIToUTF16(profile()->GetProfileUserName()));
+ base::string16 error_message;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg3()->GetAsString(&error_message));
+ EXPECT_EQ(expected_error_message, error_message);
+}
+
+TEST_F(SigninSupervisedUserImportHandlerTest, AuthError) {
+ // Set Auth Error.
+ const GoogleServiceAuthError error(
+ GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
+ FakeAuthStatusProvider provider(
+ SigninErrorControllerFactory::GetForProfile(profile()));
+ provider.SetAuthError(kTestGaiaId, error);
+
+ // Test the JS -> C++ -> JS callback path.
+ base::ListValue list_args;
+ list_args.AppendString(kTestCallbackId);
+ list_args.AppendString(profile()->GetPath().AsUTF16Unsafe());
+ handler()->GetExistingSupervisedUsers(&list_args);
+
+ // Expect an error response.
+ VerifyResponse(1U, kTestCallbackId, false);
+
+ base::string16 expected_error_message = l10n_util::GetStringFUTF16(
+ IDS_PROFILES_CREATE_CUSTODIAN_ACCOUNT_DETAILS_OUT_OF_DATE_ERROR,
+ base::ASCIIToUTF16(profile()->GetProfileUserName()));
+ base::string16 error_message;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg3()->GetAsString(&error_message));
+ EXPECT_EQ(expected_error_message, error_message);
+}
+
+TEST_F(SigninSupervisedUserImportHandlerTest, CustodianIsSupervised) {
+ // Build a supervised test profile.
+ TestingProfile* profile_ = profile_manager()->CreateTestingProfile(
+ "supervised-test-profile",
+ std::unique_ptr<sync_preferences::PrefServiceSyncable>(),
+ base::UTF8ToUTF16("supervised-test-profile"), 0,
+ "12345", // supervised_user_id
+ TestingProfile::TestingFactories());
+
+ // Test the JS -> C++ -> JS callback path.
+ base::ListValue list_args;
+ list_args.AppendString(kTestCallbackId);
+ list_args.AppendString(profile_->GetPath().AsUTF16Unsafe());
+ handler()->GetExistingSupervisedUsers(&list_args);
+
+ // Expect to do nothing.
+ EXPECT_EQ(0U, web_ui()->call_data().size());
+}
+
+TEST_F(SigninSupervisedUserImportHandlerTest, SendExistingSupervisedUsers) {
+ // Test the JS -> C++ -> JS callback path.
+ base::ListValue list_args;
+ list_args.AppendString(kTestCallbackId);
+ list_args.AppendString(profile()->GetPath().AsUTF16Unsafe());
+ handler()->GetExistingSupervisedUsers(&list_args);
+
+ // Expect a success response.
+ VerifyResponse(1U, kTestCallbackId, true);
+
+ const base::ListValue* supervised_users;
+ ASSERT_TRUE(web_ui()->call_data()[0]->arg3()->GetAsList(&supervised_users));
+ EXPECT_EQ(1U, supervised_users->GetSize());
+
+ const base::DictionaryValue* supervised_user;
+ ASSERT_TRUE(supervised_users->GetDictionary(0, &supervised_user));
+ std::string id;
+ ASSERT_TRUE(supervised_user->GetString("id", &id));
+ EXPECT_EQ(kSupervisedUserId, id);
+ std::string name;
+ ASSERT_TRUE(supervised_user->GetString("name", &name));
+ EXPECT_EQ(kSupervisedUsername, name);
+ std::string iconURL;
+ ASSERT_TRUE(supervised_user->GetString("iconURL", &iconURL));
+ EXPECT_EQ(kSupervisedUserAvatarUrl, iconURL);
+ bool onCurrentDevice;
+ ASSERT_TRUE(supervised_user->GetBoolean("onCurrentDevice", &onCurrentDevice));
+ ASSERT_FALSE(onCurrentDevice);
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_utils.cc b/chromium/chrome/browser/ui/webui/signin/signin_utils.cc
new file mode 100644
index 00000000000..cc398328f66
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_utils.cc
@@ -0,0 +1,90 @@
+// 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 "chrome/browser/ui/webui/signin/signin_utils.h"
+
+#include <set>
+
+#include "base/bind.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/common/pref_names.h"
+#include "components/guest_view/browser/guest_view_manager.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/browser/guest_view/web_view/web_view_guest.h"
+
+namespace {
+
+bool AddWebContentsToSet(std::set<content::WebContents*>* frame_set,
+ const std::string& web_view_name,
+ content::WebContents* web_contents) {
+ auto* web_view = extensions::WebViewGuest::FromWebContents(web_contents);
+ if (web_view && web_view->name() == web_view_name)
+ frame_set->insert(web_contents);
+ return false;
+}
+
+} // namespace
+
+namespace signin {
+
+content::RenderFrameHost* GetAuthFrame(content::WebContents* web_contents,
+ const std::string& parent_frame_name) {
+ content::WebContents* auth_web_contents =
+ GetAuthFrameWebContents(web_contents, parent_frame_name);
+ return auth_web_contents ? auth_web_contents->GetMainFrame() : nullptr;
+}
+
+content::WebContents* GetAuthFrameWebContents(
+ content::WebContents* web_contents,
+ const std::string& parent_frame_name) {
+ std::set<content::WebContents*> frame_set;
+ auto* manager = guest_view::GuestViewManager::FromBrowserContext(
+ web_contents->GetBrowserContext());
+ if (manager) {
+ manager->ForEachGuest(
+ web_contents,
+ base::Bind(&AddWebContentsToSet, &frame_set, parent_frame_name));
+ }
+ DCHECK_GE(1U, frame_set.size());
+ if (!frame_set.empty())
+ return *frame_set.begin();
+
+ return nullptr;
+}
+
+Browser* GetDesktopBrowser(content::WebUI* web_ui) {
+ Browser* browser =
+ chrome::FindBrowserWithWebContents(web_ui->GetWebContents());
+ if (!browser)
+ browser = chrome::FindLastActiveWithProfile(Profile::FromWebUI(web_ui));
+ return browser;
+}
+
+void SetInitializedModalHeight(Browser* browser,
+ content::WebUI* web_ui,
+ const base::ListValue* args) {
+#if defined(OS_CHROMEOS)
+ NOTREACHED();
+#else
+ if (!browser)
+ return;
+
+ double height;
+ const bool success = args->GetDouble(0, &height);
+ DCHECK(success);
+ browser->signin_view_controller()->SetModalSigninHeight(
+ static_cast<int>(height));
+#endif
+}
+
+bool IsForceSigninEnabled() {
+ PrefService* prefs = g_browser_process->local_state();
+ return prefs->GetBoolean(prefs::kForceBrowserSignin);
+}
+
+} // namespace signin
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_utils.h b/chromium/chrome/browser/ui/webui/signin/signin_utils.h
new file mode 100644
index 00000000000..6a05e7d5048
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_utils.h
@@ -0,0 +1,46 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_UTILS_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_UTILS_H_
+
+#include <string>
+
+#include "base/values.h"
+
+class Browser;
+
+namespace content {
+class RenderFrameHost;
+class WebContents;
+class WebUI;
+}
+
+namespace signin {
+
+// Gets a webview within an auth page that has the specified parent frame name
+// (i.e. <webview name="foobar"></webview>).
+content::RenderFrameHost* GetAuthFrame(content::WebContents* web_contents,
+ const std::string& parent_frame_name);
+
+content::WebContents* GetAuthFrameWebContents(
+ content::WebContents* web_contents,
+ const std::string& parent_frame_name);
+
+// Gets the browser containing the web UI; if none is found, returns the last
+// active browser for web UI's profile.
+Browser* GetDesktopBrowser(content::WebUI* web_ui);
+
+// Sets the height of the WebUI modal dialog after its initialization. This is
+// needed to better accomodate different locales' text heights.
+void SetInitializedModalHeight(Browser* browser,
+ content::WebUI* web_ui,
+ const base::ListValue* args);
+
+// Return true if force signin is enabled by group policy.
+bool IsForceSigninEnabled();
+
+} // namespace signin
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_UTILS_H_
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_web_dialog_ui.cc b/chromium/chrome/browser/ui/webui/signin/signin_web_dialog_ui.cc
new file mode 100644
index 00000000000..f770aa17502
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_web_dialog_ui.cc
@@ -0,0 +1,8 @@
+// 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_web_dialog_ui.h"
+
+SigninWebDialogUI::SigninWebDialogUI(content::WebUI* web_ui)
+ : WebDialogUI(web_ui) {}
diff --git a/chromium/chrome/browser/ui/webui/signin/signin_web_dialog_ui.h b/chromium/chrome/browser/ui/webui/signin/signin_web_dialog_ui.h
new file mode 100644
index 00000000000..19a875e4579
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/signin_web_dialog_ui.h
@@ -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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_WEB_DIALOG_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_WEB_DIALOG_UI_H_
+
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+class Browser;
+
+// Base class for web UI dialogs used for sign-in, which must be Browser-aware.
+class SigninWebDialogUI : public ui::WebDialogUI {
+ public:
+ // Creates a WebUI message handler with the specified browser and adds it to
+ // the web UI.
+ virtual void InitializeMessageHandlerWithBrowser(Browser* browser) = 0;
+
+ protected:
+ explicit SigninWebDialogUI(content::WebUI* web_ui);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_WEB_DIALOG_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
new file mode 100644
index 00000000000..df79adbc471
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
@@ -0,0 +1,153 @@
+// 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.
+
+#include "chrome/browser/ui/webui/signin/sync_confirmation_handler.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/metrics/user_metrics.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"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/signin_view_controller_delegate.h"
+#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
+#include "chrome/browser/ui/webui/signin/signin_utils.h"
+#include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "url/gurl.h"
+
+const int kProfileImageSize = 128;
+
+SyncConfirmationHandler::SyncConfirmationHandler(Browser* browser)
+ : profile_(browser->profile()),
+ browser_(browser),
+ did_user_explicitly_interact(false) {
+ DCHECK(profile_);
+ DCHECK(browser_);
+ BrowserList::AddObserver(this);
+}
+
+SyncConfirmationHandler::~SyncConfirmationHandler() {
+ BrowserList::RemoveObserver(this);
+ AccountTrackerServiceFactory::GetForProfile(profile_)->RemoveObserver(this);
+
+ // Abort signin and prevent sync from starting if none of the actions on the
+ // sync confirmation dialog are taken by the user.
+ if (!did_user_explicitly_interact) {
+ HandleUndo(nullptr);
+ base::RecordAction(base::UserMetricsAction("Signin_Abort_Signin"));
+ }
+}
+
+void SyncConfirmationHandler::OnBrowserRemoved(Browser* browser) {
+ if (browser_ == browser)
+ browser_ = nullptr;
+}
+
+void SyncConfirmationHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("confirm",
+ base::Bind(&SyncConfirmationHandler::HandleConfirm,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("undo",
+ base::Bind(&SyncConfirmationHandler::HandleUndo, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback("initializedWithSize",
+ base::Bind(&SyncConfirmationHandler::HandleInitializedWithSize,
+ base::Unretained(this)));
+}
+
+void SyncConfirmationHandler::HandleConfirm(const base::ListValue* args) {
+ did_user_explicitly_interact = true;
+ bool configure_sync_first = false;
+ CHECK(args->GetBoolean(0, &configure_sync_first));
+ CloseModalSigninWindow(configure_sync_first
+ ? LoginUIService::CONFIGURE_SYNC_FIRST
+ : LoginUIService::SYNC_WITH_DEFAULT_SETTINGS);
+}
+
+void SyncConfirmationHandler::HandleUndo(const base::ListValue* args) {
+ did_user_explicitly_interact = true;
+ base::RecordAction(base::UserMetricsAction("Signin_Undo_Signin"));
+ LoginUIServiceFactory::GetForProfile(profile_)->SyncConfirmationUIClosed(
+ LoginUIService::ABORT_SIGNIN);
+ SigninManagerFactory::GetForProfile(profile_)->SignOut(
+ signin_metrics::ABORT_SIGNIN,
+ signin_metrics::SignoutDelete::IGNORE_METRIC);
+
+ if (browser_)
+ browser_->signin_view_controller()->CloseModalSignin();
+}
+
+void SyncConfirmationHandler::SetUserImageURL(const std::string& picture_url) {
+ std::string picture_url_to_load;
+ GURL url;
+ if (picture_url != AccountTrackerService::kNoPictureURLFound &&
+ profiles::GetImageURLWithThumbnailSize(GURL(picture_url),
+ kProfileImageSize, &url)) {
+ picture_url_to_load = url.spec();
+ } else {
+ // Use the placeholder avatar icon until the account picture URL is fetched.
+ picture_url_to_load = profiles::GetPlaceholderAvatarIconUrl();
+ }
+ base::Value picture_url_value(picture_url_to_load);
+ web_ui()->CallJavascriptFunctionUnsafe("sync.confirmation.setUserImageURL",
+ picture_url_value);
+}
+
+void SyncConfirmationHandler::OnAccountUpdated(const AccountInfo& info) {
+ if (!info.IsValid())
+ return;
+
+ SigninManager* signin_manager = SigninManagerFactory::GetForProfile(profile_);
+ if (info.account_id != signin_manager->GetAuthenticatedAccountId())
+ return;
+
+ AccountTrackerServiceFactory::GetForProfile(profile_)->RemoveObserver(this);
+ SetUserImageURL(info.picture_url);
+}
+
+void SyncConfirmationHandler::CloseModalSigninWindow(
+ LoginUIService::SyncConfirmationUIClosedResult result) {
+ LoginUIServiceFactory::GetForProfile(profile_)->SyncConfirmationUIClosed(
+ result);
+ if (browser_)
+ browser_->signin_view_controller()->CloseModalSignin();
+}
+
+void SyncConfirmationHandler::HandleInitializedWithSize(
+ const base::ListValue* args) {
+ if (!browser_)
+ return;
+
+ std::string account_id = SigninManagerFactory::GetForProfile(profile_)
+ ->GetAuthenticatedAccountId();
+ if (account_id.empty()) {
+ // No account is signed in, so there is nothing to be displayed in the sync
+ // confirmation dialog.
+ return;
+ }
+ AccountTrackerService* account_tracker =
+ AccountTrackerServiceFactory::GetForProfile(profile_);
+ AccountInfo account_info = account_tracker->GetAccountInfo(account_id);
+
+ if (!account_info.IsValid()) {
+ SetUserImageURL(AccountTrackerService::kNoPictureURLFound);
+ account_tracker->AddObserver(this);
+ } else {
+ SetUserImageURL(account_info.picture_url);
+ }
+
+ signin::SetInitializedModalHeight(browser_, web_ui(), args);
+
+ // After the dialog is shown, some platforms might have an element focused.
+ // To be consistent, clear the focused element on all platforms.
+ // TODO(anthonyvd): Figure out why this is needed on Mac and not other
+ // platforms and if there's a way to start unfocused while avoiding this
+ // workaround.
+ web_ui()->CallJavascriptFunctionUnsafe("sync.confirmation.clearFocus");
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.h b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.h
new file mode 100644
index 00000000000..7de63347d18
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler.h
@@ -0,0 +1,74 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_SYNC_CONFIRMATION_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_SYNC_CONFIRMATION_HANDLER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/browser_list_observer.h"
+#include "chrome/browser/ui/webui/signin/login_ui_service.h"
+#include "components/signin/core/browser/account_tracker_service.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace base {
+class ListValue;
+}
+
+class SyncConfirmationHandler : public content::WebUIMessageHandler,
+ public AccountTrackerService::Observer,
+ public chrome::BrowserListObserver {
+ public:
+ explicit SyncConfirmationHandler(Browser* browser);
+ ~SyncConfirmationHandler() override;
+
+ // content::WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ // AccountTrackerService::Observer:
+ void OnAccountUpdated(const AccountInfo& info) override;
+
+ // chrome::BrowserListObserver:
+ void OnBrowserRemoved(Browser* browser) override;
+
+ protected:
+ // Handles "confirm" message from the page. No arguments.
+ // This message is sent when the user confirms that they want complete sign in
+ // with default sync settings. Passed a single boolean argument: whether to
+ // configure settings before signing in.
+ virtual void HandleConfirm(const base::ListValue* args);
+
+ // Handles "undo" message from the page. No arguments.
+ // This message is sent when the user clicks "undo" on the sync confirmation
+ // dialog, which aborts signin and prevents sync from starting.
+ virtual void HandleUndo(const base::ListValue* args);
+
+ // Handles the web ui message sent when the html content is done being laid
+ // out and it's time to resize the native view hosting it to fit. |args| is
+ // a single integer value for the height the native view should resize to.
+ virtual void HandleInitializedWithSize(const base::ListValue* args);
+
+ // Sets the profile picture shown in the dialog to the image at |url|.
+ virtual void SetUserImageURL(const std::string& url);
+
+ // Closes the modal signin window and calls
+ // LoginUIService::SyncConfirmationUIClosed with |result|. |result| indicates
+ // the option chosen by the user in the confirmation UI.
+ void CloseModalSigninWindow(
+ LoginUIService::SyncConfirmationUIClosedResult result);
+
+ private:
+ Profile* profile_;
+
+ // Weak reference to the browser that showed the sync confirmation dialog.
+ Browser* browser_;
+
+ // Records whether the user clicked on Undo, Ok, or Settings.
+ bool did_user_explicitly_interact;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncConfirmationHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_SYNC_CONFIRMATION_HANDLER_H_
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
new file mode 100644
index 00000000000..e5e5eabcefe
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
@@ -0,0 +1,355 @@
+// 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 "chrome/browser/ui/webui/signin/sync_confirmation_handler.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/test/user_action_tester.h"
+#include "base/values.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"
+#include "chrome/browser/signin/fake_account_fetcher_service_builder.h"
+#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/ui/browser_commands.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/signin/sync_confirmation_ui.h"
+#include "chrome/test/base/browser_with_test_window_test.h"
+#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/signin/core/browser/account_fetcher_service.h"
+#include "components/signin/core/browser/fake_account_fetcher_service.h"
+#include "components/signin/core/browser/fake_signin_manager.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_web_ui.h"
+
+const int kExpectedProfileImageSize = 128;
+
+// The dialog needs to be initialized with a height but the actual value doesn't
+// really matter in unit tests.
+const double kDefaultDialogHeight = 350.0;
+
+class TestingSyncConfirmationHandler : public SyncConfirmationHandler {
+ public:
+ TestingSyncConfirmationHandler(Browser* browser, content::WebUI* web_ui)
+ : SyncConfirmationHandler(browser) {
+ set_web_ui(web_ui);
+ }
+
+ using SyncConfirmationHandler::HandleConfirm;
+ using SyncConfirmationHandler::HandleUndo;
+ using SyncConfirmationHandler::HandleInitializedWithSize;
+ using SyncConfirmationHandler::SetUserImageURL;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestingSyncConfirmationHandler);
+};
+
+class TestingOneClickSigninSyncStarter : public OneClickSigninSyncStarter {
+ public:
+ TestingOneClickSigninSyncStarter(Profile* profile,
+ Browser* browser,
+ const std::string& gaia_id,
+ const std::string& email,
+ const std::string& password,
+ const std::string& refresh_token,
+ ProfileMode profile_mode,
+ StartSyncMode start_mode,
+ content::WebContents* web_contents,
+ ConfirmationRequired display_confirmation,
+ const GURL& current_url,
+ const GURL& continue_url,
+ Callback callback)
+ : OneClickSigninSyncStarter(profile,
+ browser,
+ gaia_id,
+ email,
+ password,
+ refresh_token,
+ profile_mode,
+ start_mode,
+ web_contents,
+ display_confirmation,
+ current_url,
+ continue_url,
+ callback) {}
+
+ protected:
+ void ShowSyncSetupSettingsSubpage() override {
+ // Intentionally don't open a tab to settings.
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestingOneClickSigninSyncStarter);
+};
+
+class SyncConfirmationHandlerTest : public BrowserWithTestWindowTest {
+ public:
+ SyncConfirmationHandlerTest()
+ : did_user_explicitly_interact(false), web_ui_(new content::TestWebUI) {}
+
+ void SetUp() override {
+ BrowserWithTestWindowTest::SetUp();
+ chrome::NewTab(browser());
+ web_ui()->set_web_contents(
+ browser()->tab_strip_model()->GetActiveWebContents());
+
+ auto handler =
+ base::MakeUnique<TestingSyncConfirmationHandler>(browser(), web_ui());
+ handler_ = handler.get();
+ sync_confirmation_ui_.reset(new SyncConfirmationUI(web_ui()));
+ web_ui()->AddMessageHandler(std::move(handler));
+
+ // This dialog assumes the signin flow was completed, which kicks off the
+ // SigninManager.
+ new TestingOneClickSigninSyncStarter(
+ profile(), browser(), "gaia", "foo@example.com", "password",
+ "refresh_token", OneClickSigninSyncStarter::CURRENT_PROFILE,
+ OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS, nullptr,
+ OneClickSigninSyncStarter::NO_CONFIRMATION, GURL(), GURL(),
+ OneClickSigninSyncStarter::Callback());
+ }
+
+ void TearDown() override {
+ sync_confirmation_ui_.reset();
+ web_ui_.reset();
+ BrowserWithTestWindowTest::TearDown();
+
+ if (did_user_explicitly_interact)
+ EXPECT_EQ(0, user_action_tester()->GetActionCount("Signin_Abort_Signin"));
+ else
+ EXPECT_EQ(1, user_action_tester()->GetActionCount("Signin_Abort_Signin"));
+ }
+
+ TestingSyncConfirmationHandler* handler() {
+ return handler_;
+ }
+
+ content::TestWebUI* web_ui() {
+ return web_ui_.get();
+ }
+
+ FakeAccountFetcherService* account_fetcher_service() {
+ return static_cast<FakeAccountFetcherService*>(
+ AccountFetcherServiceFactory::GetForProfile(profile()));
+ }
+
+ FakeSigninManager* signin_manager() {
+ return static_cast<FakeSigninManager*>(
+ SigninManagerFactory::GetForProfile(profile()));
+ }
+
+ browser_sync::ProfileSyncService* sync() {
+ return ProfileSyncServiceFactory::GetForProfile(profile());
+ }
+
+ base::UserActionTester* user_action_tester() {
+ return &user_action_tester_;
+ }
+
+ // BrowserWithTestWindowTest
+ BrowserWindow* CreateBrowserWindow() override {
+ return new DialogTestBrowserWindow;
+ }
+
+ TestingProfile* CreateProfile() override {
+ TestingProfile::Builder builder;
+ builder.AddTestingFactory(AccountFetcherServiceFactory::GetInstance(),
+ FakeAccountFetcherServiceBuilder::BuildForTests);
+ builder.AddTestingFactory(
+ SigninManagerFactory::GetInstance(), BuildFakeSigninManagerBase);
+ return builder.Build().release();
+ }
+
+ protected:
+ bool did_user_explicitly_interact;
+
+ private:
+ std::unique_ptr<content::TestWebUI> web_ui_;
+ std::unique_ptr<SyncConfirmationUI> sync_confirmation_ui_;
+ TestingSyncConfirmationHandler* handler_; // Not owned.
+ base::UserActionTester user_action_tester_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncConfirmationHandlerTest);
+};
+
+TEST_F(SyncConfirmationHandlerTest, TestSetImageIfPrimaryAccountReady) {
+ account_fetcher_service()->FakeUserInfoFetchSuccess(
+ "gaia",
+ "foo@example.com",
+ "gaia",
+ "",
+ "full_name",
+ "given_name",
+ "locale",
+ "http://picture.example.com/picture.jpg");
+
+ base::ListValue args;
+ args.Set(0, base::MakeUnique<base::Value>(kDefaultDialogHeight));
+ handler()->HandleInitializedWithSize(&args);
+ EXPECT_EQ(2U, web_ui()->call_data().size());
+
+ // When the primary account is ready, setUserImageURL happens before
+ // clearFocus since the image URL is known before showing the dialog.
+ EXPECT_EQ("sync.confirmation.setUserImageURL",
+ web_ui()->call_data()[0]->function_name());
+ EXPECT_TRUE(
+ web_ui()->call_data()[0]->arg1()->IsType(base::Value::Type::STRING));
+ std::string passed_picture_url;
+ EXPECT_TRUE(
+ web_ui()->call_data()[0]->arg1()->GetAsString(&passed_picture_url));
+
+ EXPECT_EQ("sync.confirmation.clearFocus",
+ web_ui()->call_data()[1]->function_name());
+
+ std::string original_picture_url =
+ AccountTrackerServiceFactory::GetForProfile(profile())->
+ GetAccountInfo("gaia").picture_url;
+ GURL picture_url_with_size;
+ EXPECT_TRUE(profiles::GetImageURLWithThumbnailSize(GURL(original_picture_url),
+ kExpectedProfileImageSize,
+ &picture_url_with_size));
+ EXPECT_EQ(picture_url_with_size.spec(), passed_picture_url);
+}
+
+TEST_F(SyncConfirmationHandlerTest, TestSetImageIfPrimaryAccountReadyLater) {
+ base::ListValue args;
+ args.Set(0, base::MakeUnique<base::Value>(kDefaultDialogHeight));
+ handler()->HandleInitializedWithSize(&args);
+ EXPECT_EQ(2U, web_ui()->call_data().size());
+
+ account_fetcher_service()->FakeUserInfoFetchSuccess(
+ "gaia",
+ "foo@example.com",
+ "gaia",
+ "",
+ "full_name",
+ "given_name",
+ "locale",
+ "http://picture.example.com/picture.jpg");
+
+ EXPECT_EQ(3U, web_ui()->call_data().size());
+
+ // When the primary account isn't yet ready when the dialog is shown,
+ // setUserImageURL is called with the default placeholder image.
+ EXPECT_EQ("sync.confirmation.setUserImageURL",
+ web_ui()->call_data()[0]->function_name());
+ EXPECT_TRUE(
+ web_ui()->call_data()[0]->arg1()->IsType(base::Value::Type::STRING));
+ std::string passed_picture_url;
+ EXPECT_TRUE(
+ web_ui()->call_data()[0]->arg1()->GetAsString(&passed_picture_url));
+ EXPECT_EQ(profiles::GetPlaceholderAvatarIconUrl(), passed_picture_url);
+
+ // When the primary account isn't yet ready when the dialog is shown,
+ // clearFocus is called before the second call to setUserImageURL.
+ EXPECT_EQ("sync.confirmation.clearFocus",
+ web_ui()->call_data()[1]->function_name());
+
+ EXPECT_EQ("sync.confirmation.setUserImageURL",
+ web_ui()->call_data()[2]->function_name());
+ EXPECT_TRUE(
+ web_ui()->call_data()[2]->arg1()->IsType(base::Value::Type::STRING));
+ EXPECT_TRUE(
+ web_ui()->call_data()[2]->arg1()->GetAsString(&passed_picture_url));
+
+ std::string original_picture_url =
+ AccountTrackerServiceFactory::GetForProfile(profile())->
+ GetAccountInfo("gaia").picture_url;
+ GURL picture_url_with_size;
+ EXPECT_TRUE(profiles::GetImageURLWithThumbnailSize(GURL(original_picture_url),
+ kExpectedProfileImageSize,
+ &picture_url_with_size));
+ EXPECT_EQ(picture_url_with_size.spec(), passed_picture_url);
+}
+
+TEST_F(SyncConfirmationHandlerTest,
+ TestSetImageIgnoredIfSecondaryAccountUpdated) {
+ base::ListValue args;
+ args.Set(0, base::MakeUnique<base::Value>(kDefaultDialogHeight));
+ handler()->HandleInitializedWithSize(&args);
+ EXPECT_EQ(2U, web_ui()->call_data().size());
+
+ AccountTrackerServiceFactory::GetForProfile(profile())->SeedAccountInfo(
+ "bar_gaia", "bar@example.com");
+ account_fetcher_service()->FakeUserInfoFetchSuccess(
+ "bar_gaia", "bar@example.com", "bar_gaia", "", "bar_full_name",
+ "bar_given_name", "bar_locale",
+ "http://picture.example.com/bar_picture.jpg");
+
+ // Updating the account info of a secondary account should not update the
+ // image of the sync confirmation dialog.
+ EXPECT_EQ(2U, web_ui()->call_data().size());
+
+ account_fetcher_service()->FakeUserInfoFetchSuccess(
+ "gaia", "foo@example.com", "gaia", "", "full_name", "given_name",
+ "locale", "http://picture.example.com/picture.jpg");
+
+ // Updating the account info of the primary account should update the
+ // image of the sync confirmation dialog.
+ EXPECT_EQ(3U, web_ui()->call_data().size());
+ EXPECT_EQ("sync.confirmation.setUserImageURL",
+ web_ui()->call_data()[2]->function_name());
+}
+
+TEST_F(SyncConfirmationHandlerTest, TestHandleUndo) {
+ EXPECT_FALSE(sync()->IsFirstSetupComplete());
+ EXPECT_TRUE(sync()->IsFirstSetupInProgress());
+
+ handler()->HandleUndo(nullptr);
+ did_user_explicitly_interact = true;
+
+ EXPECT_FALSE(sync()->IsFirstSetupInProgress());
+ EXPECT_FALSE(sync()->IsFirstSetupComplete());
+ EXPECT_FALSE(
+ SigninManagerFactory::GetForProfile(profile())->IsAuthenticated());
+ EXPECT_EQ(1, user_action_tester()->GetActionCount("Signin_Undo_Signin"));
+ EXPECT_EQ(0, user_action_tester()->GetActionCount(
+ "Signin_Signin_WithDefaultSyncSettings"));
+ EXPECT_EQ(0, user_action_tester()->GetActionCount(
+ "Signin_Signin_WithAdvancedSyncSettings"));
+}
+
+TEST_F(SyncConfirmationHandlerTest, TestHandleConfirm) {
+ EXPECT_FALSE(sync()->IsFirstSetupComplete());
+ EXPECT_TRUE(sync()->IsFirstSetupInProgress());
+
+ base::ListValue args;
+ args.AppendBoolean(false /* show advanced */);
+ handler()->HandleConfirm(&args);
+ did_user_explicitly_interact = true;
+
+ EXPECT_FALSE(sync()->IsFirstSetupInProgress());
+ EXPECT_TRUE(sync()->IsFirstSetupComplete());
+ EXPECT_TRUE(
+ SigninManagerFactory::GetForProfile(profile())->IsAuthenticated());
+ EXPECT_EQ(0, user_action_tester()->GetActionCount("Signin_Undo_Signin"));
+ EXPECT_EQ(1, user_action_tester()->GetActionCount(
+ "Signin_Signin_WithDefaultSyncSettings"));
+ EXPECT_EQ(0, user_action_tester()->GetActionCount(
+ "Signin_Signin_WithAdvancedSyncSettings"));
+}
+
+TEST_F(SyncConfirmationHandlerTest, TestHandleConfirmWithAdvancedSyncSettings) {
+ EXPECT_FALSE(sync()->IsFirstSetupComplete());
+ EXPECT_TRUE(sync()->IsFirstSetupInProgress());
+
+ base::ListValue args;
+ args.AppendBoolean(true /* show advanced */);
+ handler()->HandleConfirm(&args);
+ did_user_explicitly_interact = true;
+
+ EXPECT_FALSE(sync()->IsFirstSetupInProgress());
+ EXPECT_FALSE(sync()->IsFirstSetupComplete());
+ EXPECT_TRUE(
+ SigninManagerFactory::GetForProfile(profile())->IsAuthenticated());
+ EXPECT_EQ(0, user_action_tester()->GetActionCount("Signin_Undo_Signin"));
+ EXPECT_EQ(0, user_action_tester()->GetActionCount(
+ "Signin_Signin_WithDefaultSyncSettings"));
+ EXPECT_EQ(1, user_action_tester()->GetActionCount(
+ "Signin_Signin_WithAdvancedSyncSettings"));
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
new file mode 100644
index 00000000000..701a65ff3a3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
@@ -0,0 +1,70 @@
+// 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.
+
+#include "chrome/browser/ui/webui/signin/sync_confirmation_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.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 "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.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();
+
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUISyncConfirmationHost);
+ source->SetJsonPath("strings.js");
+ source->SetDefaultResource(IDR_SYNC_CONFIRMATION_HTML);
+ source->AddResourcePath("sync_confirmation.css", IDR_SYNC_CONFIRMATION_CSS);
+ source->AddResourcePath("sync_confirmation.js", IDR_SYNC_CONFIRMATION_JS);
+ source->AddResourcePath("signin_shared_css.html", IDR_SIGNIN_SHARED_CSS_HTML);
+ 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("syncConfirmationSyncSettingsLabel",
+ IDS_SYNC_CONFIRMATION_SYNC_SETTINGS_LABEL);
+ source->AddLocalizedString("syncDisabledConfirmationDetails",
+ IDS_SYNC_DISABLED_CONFIRMATION_DETAILS);
+
+ int title_ids = IDS_SYNC_CONFIRMATION_TITLE;
+ int confirm_button_ids = IDS_SYNC_CONFIRMATION_CONFIRM_BUTTON_LABEL;
+ int undo_button_ids = IDS_SYNC_CONFIRMATION_UNDO_BUTTON_LABEL;
+ if (!is_sync_allowed) {
+ title_ids = IDS_SYNC_DISABLED_CONFIRMATION_CHROME_SYNC_TITLE;
+ confirm_button_ids = IDS_SYNC_DISABLED_CONFIRMATION_CONFIRM_BUTTON_LABEL;
+ undo_button_ids = IDS_SYNC_DISABLED_CONFIRMATION_UNDO_BUTTON_LABEL;
+ }
+ source->AddLocalizedString("syncConfirmationTitle", title_ids);
+ source->AddLocalizedString("syncConfirmationConfirmLabel",
+ confirm_button_ids);
+ source->AddLocalizedString("syncConfirmationUndoLabel", undo_button_ids);
+
+ base::DictionaryValue strings;
+ webui::SetLoadTimeDataDefaults(
+ g_browser_process->GetApplicationLocale(), &strings);
+ source->AddLocalizedStrings(strings);
+
+ content::WebUIDataSource::Add(profile, source);
+}
+
+void SyncConfirmationUI::InitializeMessageHandlerWithBrowser(Browser* browser) {
+ web_ui()->AddMessageHandler(
+ base::MakeUnique<SyncConfirmationHandler>(browser));
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.h b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.h
new file mode 100644
index 00000000000..7d5b5431041
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/sync_confirmation_ui.h
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_SYNC_CONFIRMATION_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_SYNC_CONFIRMATION_UI_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/signin/signin_web_dialog_ui.h"
+
+namespace ui {
+class WebUI;
+}
+
+// WebUI controller for the sync confirmation dialog.
+//
+// Note: This controller does not set the WebUI message handler. It is
+// the responsability of the caller to pass the correct message handler.
+class SyncConfirmationUI : public SigninWebDialogUI {
+ public:
+ explicit SyncConfirmationUI(content::WebUI* web_ui);
+ ~SyncConfirmationUI() override {}
+
+ // SigninWebDialogUI:
+ void InitializeMessageHandlerWithBrowser(Browser* browser) override;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncConfirmationUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_SYNC_CONFIRMATION_UI_H_
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
new file mode 100644
index 00000000000..7436f8abffe
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
@@ -0,0 +1,1065 @@
+// 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/signin/user_manager_screen_handler.h"
+
+#include <stddef.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/feature_list.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/profiler/scoped_tracker.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/value_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/api/screenlock_private/screenlock_private_api.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_avatar_icon_util.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_metrics.h"
+#include "chrome/browser/profiles/profile_statistics.h"
+#include "chrome/browser/profiles/profile_statistics_factory.h"
+#include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/profiles/profiles_state.h"
+#include "chrome/browser/signin/local_auth.h"
+#include "chrome/browser/signin/signin_util.h"
+#include "chrome/browser/ui/app_list/app_list_service.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_list_observer.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_features.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#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/common/profile_management_switches.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "google_apis/gaia/gaia_auth_fetcher.h"
+#include "google_apis/gaia/gaia_constants.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/image/image_util.h"
+
+#if defined(USE_ASH)
+#include "ash/shell.h" // nogncheck
+#endif
+
+namespace {
+// User dictionary keys.
+const char kKeyUsername[] = "username";
+const char kKeyDisplayName[]= "displayName";
+const char kKeyEmailAddress[] = "emailAddress";
+const char kKeyProfilePath[] = "profilePath";
+const char kKeyPublicAccount[] = "publicAccount";
+const char kKeyLegacySupervisedUser[] = "legacySupervisedUser";
+const char kKeyChildUser[] = "childUser";
+const char kKeyCanRemove[] = "canRemove";
+const char kKeyIsOwner[] = "isOwner";
+const char kKeyIsDesktop[] = "isDesktopUser";
+const char kKeyAvatarUrl[] = "userImage";
+const char kKeyNeedsSignin[] = "needsSignin";
+const char kKeyHasLocalCreds[] = "hasLocalCreds";
+const char kKeyStatistics[] = "statistics";
+const char kKeyIsProfileLoaded[] = "isProfileLoaded";
+
+// JS API callback names.
+const char kJsApiUserManagerInitialize[] = "userManagerInitialize";
+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[] =
+ "removeUserWarningLoadStats";
+const char kJsApiUserManagerGetRemoveWarningDialogMessage[] =
+ "getRemoveWarningDialogMessage";
+const char kJsApiUserManagerAreAllProfilesLocked[] =
+ "areAllProfilesLocked";
+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;
+
+ // If the avatar is too small (i.e. the old-style low resolution avatar),
+ // it will be pixelated when displayed in the User Manager, so we should
+ // return the placeholder avatar instead.
+ gfx::Image avatar_image = entry->GetAvatarIcon();
+ if (avatar_image.Width() <= profiles::kAvatarIconWidth ||
+ avatar_image.Height() <= profiles::kAvatarIconHeight ) {
+ avatar_image = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+ profiles::GetPlaceholderAvatarIconResourceID());
+ }
+ gfx::Image resized_image = profiles::GetSizedAvatarIcon(
+ avatar_image, is_gaia_picture, kAvatarIconSize, kAvatarIconSize);
+ 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);
+ return service->GetBoolean(prefs::kBrowserGuestModeEnabled);
+}
+
+bool IsAddPersonEnabled() {
+ PrefService* service = g_browser_process->local_state();
+ DCHECK(service);
+ return service->GetBoolean(prefs::kBrowserAddPersonEnabled);
+}
+
+// Executes the action specified by the URL's Hash parameter, if any. Deletes
+// itself after the action would be performed.
+class UrlHashHelper : public chrome::BrowserListObserver {
+ public:
+ UrlHashHelper(Browser* browser, const std::string& hash);
+ ~UrlHashHelper() override;
+
+ void ExecuteUrlHash();
+
+ // chrome::BrowserListObserver overrides:
+ void OnBrowserRemoved(Browser* browser) override;
+
+ private:
+ Browser* browser_;
+ Profile* profile_;
+ std::string hash_;
+
+ DISALLOW_COPY_AND_ASSIGN(UrlHashHelper);
+};
+
+UrlHashHelper::UrlHashHelper(Browser* browser, const std::string& hash)
+ : browser_(browser),
+ profile_(browser->profile()),
+ hash_(hash) {
+ BrowserList::AddObserver(this);
+}
+
+UrlHashHelper::~UrlHashHelper() {
+ BrowserList::RemoveObserver(this);
+}
+
+void UrlHashHelper::OnBrowserRemoved(Browser* browser) {
+ if (browser == browser_)
+ browser_ = nullptr;
+}
+
+void UrlHashHelper::ExecuteUrlHash() {
+ if (hash_ == profiles::kUserManagerSelectProfileAppLauncher) {
+ AppListService* app_list_service = AppListService::Get();
+ app_list_service->ShowForProfile(profile_);
+ return;
+ }
+
+ Browser* target_browser = browser_;
+ if (!target_browser) {
+ target_browser = chrome::FindLastActiveWithProfile(profile_);
+ if (!target_browser)
+ return;
+ }
+
+ if (hash_ == profiles::kUserManagerSelectProfileTaskManager)
+ chrome::OpenTaskManager(target_browser);
+ else if (hash_ == profiles::kUserManagerSelectProfileAboutChrome)
+ chrome::ShowAboutChrome(target_browser);
+ else if (hash_ == profiles::kUserManagerSelectProfileChromeSettings)
+ chrome::ShowSettings(target_browser);
+}
+
+void HandleLogRemoveUserWarningShown(const base::ListValue* args) {
+ ProfileMetrics::LogProfileDeleteUser(
+ ProfileMetrics::DELETE_PROFILE_USER_MANAGER_SHOW_WARNING);
+}
+
+} // namespace
+
+// ProfileUpdateObserver ------------------------------------------------------
+
+class UserManagerScreenHandler::ProfileUpdateObserver
+ : public ProfileAttributesStorage::Observer {
+ public:
+ ProfileUpdateObserver(
+ ProfileManager* profile_manager, UserManagerScreenHandler* handler)
+ : profile_manager_(profile_manager),
+ user_manager_handler_(handler) {
+ DCHECK(profile_manager_);
+ DCHECK(user_manager_handler_);
+ profile_manager_->GetProfileAttributesStorage().AddObserver(this);
+ }
+
+ ~ProfileUpdateObserver() override {
+ DCHECK(profile_manager_);
+ profile_manager_->GetProfileAttributesStorage().RemoveObserver(this);
+ }
+
+ private:
+ // ProfileAttributesStorage::Observer implementation:
+ // If any change has been made to a profile, propagate it to all the
+ // visible user manager screens.
+ void OnProfileAdded(const base::FilePath& profile_path) override {
+ user_manager_handler_->SendUserList();
+ }
+
+ void OnProfileWasRemoved(const base::FilePath& profile_path,
+ const base::string16& profile_name) override {
+ // TODO(noms): Change 'SendUserList' to 'removeUser' JS-call when
+ // UserManager is able to find pod belonging to removed user.
+ user_manager_handler_->SendUserList();
+ }
+
+ void OnProfileNameChanged(const base::FilePath& profile_path,
+ const base::string16& old_profile_name) override {
+ user_manager_handler_->SendUserList();
+ }
+
+ void OnProfileAvatarChanged(const base::FilePath& profile_path) override {
+ user_manager_handler_->SendUserList();
+ }
+
+ void OnProfileHighResAvatarLoaded(
+ const base::FilePath& profile_path) override {
+ // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461175
+ // is fixed.
+ tracked_objects::ScopedTracker tracking_profile(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "461175 UserManagerScreenHandler::OnProfileHighResAvatarLoaded"));
+ user_manager_handler_->SendUserList();
+ }
+
+ void OnProfileSigninRequiredChanged(
+ const base::FilePath& profile_path) override {
+ user_manager_handler_->SendUserList();
+ }
+
+ void OnProfileIsOmittedChanged(
+ const base::FilePath& profile_path) override {
+ user_manager_handler_->SendUserList();
+ }
+
+ ProfileManager* profile_manager_;
+
+ UserManagerScreenHandler* user_manager_handler_; // Weak; owns us.
+
+ DISALLOW_COPY_AND_ASSIGN(ProfileUpdateObserver);
+};
+
+// UserManagerScreenHandler ---------------------------------------------------
+
+UserManagerScreenHandler::UserManagerScreenHandler() : weak_ptr_factory_(this) {
+ profile_attributes_storage_observer_.reset(
+ new UserManagerScreenHandler::ProfileUpdateObserver(
+ g_browser_process->profile_manager(), this));
+
+ // TODO(mahmadi): Remove the following once prefs are cleared for everyone.
+ PrefService* service = g_browser_process->local_state();
+ DCHECK(service);
+
+ const PrefService::Preference* guest_mode_enabled_pref =
+ service->FindPreference(prefs::kBrowserGuestModeEnabled);
+ const PrefService::Preference* add_person_enabled_pref =
+ service->FindPreference(prefs::kBrowserAddPersonEnabled);
+
+ if (base::FeatureList::IsEnabled(features::kMaterialDesignSettings) &&
+ (guest_mode_enabled_pref->HasUserSetting() ||
+ add_person_enabled_pref->HasUserSetting())) {
+ service->ClearPref(guest_mode_enabled_pref->name());
+ service->ClearPref(add_person_enabled_pref->name());
+ base::RecordAction(
+ base::UserMetricsAction("UserManager_Cleared_Legacy_User_Prefs"));
+ }
+}
+
+UserManagerScreenHandler::~UserManagerScreenHandler() {
+ proximity_auth::ScreenlockBridge::Get()->SetLockHandler(NULL);
+}
+
+void UserManagerScreenHandler::ShowBannerMessage(
+ const base::string16& message) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "login.AccountPickerScreen.showBannerMessage", base::Value(message));
+}
+
+void UserManagerScreenHandler::ShowUserPodCustomIcon(
+ const AccountId& account_id,
+ const proximity_auth::ScreenlockBridge::UserPodCustomIconOptions&
+ icon_options) {
+ std::unique_ptr<base::DictionaryValue> icon =
+ icon_options.ToDictionaryValue();
+ if (!icon || icon->empty())
+ return;
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "login.AccountPickerScreen.showUserPodCustomIcon",
+ base::Value(account_id.GetUserEmail()), *icon);
+}
+
+void UserManagerScreenHandler::HideUserPodCustomIcon(
+ const AccountId& account_id) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "login.AccountPickerScreen.hideUserPodCustomIcon",
+ base::Value(account_id.GetUserEmail()));
+}
+
+void UserManagerScreenHandler::EnableInput() {
+ // Nothing here because UI is not disabled when starting to authenticate.
+}
+
+void UserManagerScreenHandler::SetAuthType(
+ const AccountId& account_id,
+ proximity_auth::ScreenlockBridge::LockHandler::AuthType auth_type,
+ const base::string16& auth_value) {
+ if (GetAuthType(account_id) ==
+ proximity_auth::ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD)
+ return;
+
+ user_auth_type_map_[account_id.GetUserEmail()] = auth_type;
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "login.AccountPickerScreen.setAuthType",
+ base::Value(account_id.GetUserEmail()), base::Value(auth_type),
+ base::Value(auth_value));
+}
+
+proximity_auth::ScreenlockBridge::LockHandler::AuthType
+UserManagerScreenHandler::GetAuthType(const AccountId& account_id) const {
+ const auto it = user_auth_type_map_.find(account_id.GetUserEmail());
+ if (it == user_auth_type_map_.end())
+ return proximity_auth::ScreenlockBridge::LockHandler::OFFLINE_PASSWORD;
+ return it->second;
+}
+
+proximity_auth::ScreenlockBridge::LockHandler::ScreenType
+UserManagerScreenHandler::GetScreenType() const {
+ return proximity_auth::ScreenlockBridge::LockHandler::LOCK_SCREEN;
+}
+
+void UserManagerScreenHandler::Unlock(const AccountId& account_id) {
+ const base::FilePath path = profiles::GetPathOfProfileWithEmail(
+ g_browser_process->profile_manager(), account_id.GetUserEmail());
+ if (!path.empty()) {
+ authenticating_profile_path_ = path;
+ ReportAuthenticationResult(true, ProfileMetrics::AUTH_LOCAL);
+ }
+}
+
+void UserManagerScreenHandler::AttemptEasySignin(const AccountId& account_id,
+ const std::string& secret,
+ const std::string& key_label) {
+ NOTREACHED();
+}
+
+void UserManagerScreenHandler::HandleInitialize(const base::ListValue* args) {
+ // If the URL has a hash parameter, store it for later.
+ args->GetString(0, &url_hash_);
+
+ SendUserList();
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "cr.ui.UserManager.showUserManagerScreen",
+ base::Value(IsGuestModeEnabled()), base::Value(IsAddPersonEnabled()));
+
+ proximity_auth::ScreenlockBridge::Get()->SetLockHandler(this);
+}
+
+void UserManagerScreenHandler::HandleAuthenticatedLaunchUser(
+ const base::ListValue* args) {
+ const base::Value* profile_path_value;
+ if (!args->Get(0, &profile_path_value))
+ return;
+
+ base::FilePath profile_path;
+ if (!base::GetValueAsFilePath(*profile_path_value, &profile_path))
+ return;
+
+ ProfileAttributesEntry* entry;
+ if (!g_browser_process->profile_manager()->GetProfileAttributesStorage().
+ GetProfileAttributesWithPath(profile_path, &entry)) {
+ return;
+ }
+
+ base::string16 email_address;
+ if (!args->GetString(1, &email_address))
+ return;
+
+ std::string password;
+ if (!args->GetString(2, &password))
+ return;
+
+ authenticating_profile_path_ = profile_path;
+ email_address_ = base::UTF16ToUTF8(email_address);
+
+ // Only try to validate locally or check the password change detection
+ // if we actually have a local credential saved.
+ if (!entry->GetLocalAuthCredentials().empty()) {
+ if (LocalAuth::ValidateLocalAuthCredentials(entry, password)) {
+ ReportAuthenticationResult(true, ProfileMetrics::AUTH_LOCAL);
+ return;
+ }
+
+ // This could be a mis-typed password or typing a new password while we
+ // still have a hash of the old one. The new way of checking a password
+ // change makes use of a token so we do that... if it's available.
+ if (!oauth_client_) {
+ oauth_client_.reset(new gaia::GaiaOAuthClient(
+ content::BrowserContext::GetDefaultStoragePartition(
+ web_ui()->GetWebContents()->GetBrowserContext())->
+ GetURLRequestContext()));
+ }
+
+ const std::string token = entry->GetPasswordChangeDetectionToken();
+ if (!token.empty()) {
+ oauth_client_->GetTokenHandleInfo(token, kMaxOAuthRetries, this);
+ return;
+ }
+ }
+
+ content::BrowserContext* browser_context =
+ web_ui()->GetWebContents()->GetBrowserContext();
+
+ if (!email_address_.empty()) {
+ // In order to support the upgrade case where we have a local hash but no
+ // password token, the user must perform a full online reauth.
+ UserManagerProfileDialog::ShowReauthDialog(
+ browser_context, email_address_, signin_metrics::Reason::REASON_UNLOCK);
+ } else if (entry->IsSigninRequired() && entry->IsSupervised()) {
+ // Supervised profile will only be locked when force-sign-in is enabled
+ // and it shouldn't be unlocked. Display the error message directly via
+ // the system profile to avoid profile creation.
+ LoginUIServiceFactory::GetForProfile(
+ Profile::FromWebUI(web_ui())->GetOriginalProfile())
+ ->DisplayLoginResult(nullptr,
+ l10n_util::GetStringUTF16(
+ IDS_SUPERVISED_USER_NOT_ALLOWED_BY_POLICY),
+ base::string16());
+ UserManagerProfileDialog::ShowDialogAndDisplayErrorMessage(browser_context);
+ } else {
+ // Fresh sign in via user manager without existing email address.
+ UserManagerProfileDialog::ShowSigninDialog(browser_context, profile_path);
+ }
+}
+
+void UserManagerScreenHandler::HandleRemoveUser(const base::ListValue* args) {
+ DCHECK(args);
+ const base::Value* profile_path_value;
+ if (!args->Get(0, &profile_path_value)) {
+ NOTREACHED();
+ return;
+ }
+
+ base::FilePath profile_path;
+ if (!base::GetValueAsFilePath(*profile_path_value, &profile_path)) {
+ NOTREACHED();
+ return;
+ }
+
+ DCHECK(profiles::IsMultipleProfilesEnabled());
+
+ if (profiles::AreAllNonChildNonSupervisedProfilesLocked()) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "cr.webUIListenerCallback", base::Value("show-error-dialog"),
+ base::Value(l10n_util::GetStringUTF8(
+ IDS_USER_MANAGER_REMOVE_PROFILE_PROFILES_LOCKED_ERROR)));
+ return;
+ }
+
+ // 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);
+}
+
+void UserManagerScreenHandler::HandleLaunchGuest(const base::ListValue* args) {
+ if (IsGuestModeEnabled()) {
+ profiles::SwitchToGuestProfile(
+ base::Bind(&UserManagerScreenHandler::OnSwitchToProfileComplete,
+ weak_ptr_factory_.GetWeakPtr()));
+ } else {
+ // The UI should have prevented the user from allowing the selection of
+ // guest mode.
+ NOTREACHED();
+ }
+}
+
+void UserManagerScreenHandler::HandleAreAllProfilesLocked(
+ const base::ListValue* args) {
+ std::string webui_callback_id;
+ CHECK_EQ(1U, args->GetSize());
+ bool success = args->GetString(0, &webui_callback_id);
+ DCHECK(success);
+
+ AllowJavascript();
+ ResolveJavascriptCallback(
+ base::Value(webui_callback_id),
+ base::Value(profiles::AreAllNonChildNonSupervisedProfilesLocked()));
+}
+
+void UserManagerScreenHandler::HandleLaunchUser(const base::ListValue* args) {
+ const base::Value* profile_path_value = NULL;
+ if (!args->Get(0, &profile_path_value))
+ return;
+
+ base::FilePath profile_path;
+ if (!base::GetValueAsFilePath(*profile_path_value, &profile_path))
+ return;
+
+ ProfileAttributesEntry* entry;
+ if (!g_browser_process->profile_manager()->GetProfileAttributesStorage().
+ GetProfileAttributesWithPath(profile_path, &entry)) {
+ NOTREACHED();
+ return;
+ }
+
+ // It's possible that a user breaks into the user-manager page using the
+ // JavaScript Inspector and causes a "locked" profile to call this
+ // unauthenticated version of "launch" instead of the proper one. Thus,
+ // we have to validate in (secure) C++ code that it really is a profile
+ // not needing authentication. If it is, just ignore the "launch" request.
+ if (entry->IsSigninRequired())
+ return;
+ ProfileMetrics::LogProfileAuthResult(ProfileMetrics::AUTH_UNNECESSARY);
+
+ profiles::SwitchToProfile(
+ profile_path, false, /* reuse any existing windows */
+ base::Bind(&UserManagerScreenHandler::OnSwitchToProfileComplete,
+ weak_ptr_factory_.GetWeakPtr()),
+ 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;
+ CHECK(args->GetString(0, &email));
+ const AccountId account_id = AccountId::FromUserEmail(email);
+ SetAuthType(
+ account_id,
+ proximity_auth::ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD,
+ base::string16());
+ HideUserPodCustomIcon(account_id);
+}
+
+void UserManagerScreenHandler::HandleRemoveUserWarningLoadStats(
+ const base::ListValue* args) {
+ const base::Value* profile_path_value;
+
+ if (!args->Get(0, &profile_path_value))
+ return;
+
+ base::FilePath profile_path;
+
+ if (!base::GetValueAsFilePath(*profile_path_value, &profile_path))
+ return;
+
+ base::Value return_profile_path(profile_path.value());
+ Profile* profile = g_browser_process->profile_manager()->
+ GetProfileByPath(profile_path);
+
+ if (!profile)
+ return;
+
+ if (!chrome::FindAnyBrowser(profile, true)) {
+ // If no windows are open for that profile, the statistics in
+ // ProfileAttributesStorage are up to date. The statistics in
+ // ProfileAttributesStorage are returned because the copy in user_pod_row.js
+ // may be outdated. However, if some statistics are missing in
+ // ProfileAttributesStorage (i.e. |item.success| is false), then the actual
+ // statistics are queried instead.
+ base::DictionaryValue return_value;
+ profiles::ProfileCategoryStats stats =
+ ProfileStatistics::GetProfileStatisticsFromAttributesStorage(
+ profile_path);
+ bool stats_success = true;
+ for (const auto& item : stats) {
+ std::unique_ptr<base::DictionaryValue> stat(new base::DictionaryValue);
+ stat->SetIntegerWithoutPathExpansion("count", item.count);
+ stat->SetBooleanWithoutPathExpansion("success", item.success);
+ return_value.SetWithoutPathExpansion(item.category, std::move(stat));
+ stats_success &= item.success;
+ }
+ if (stats_success) {
+ web_ui()->CallJavascriptFunctionUnsafe("updateRemoveWarningDialog",
+ base::Value(profile_path.value()),
+ return_value);
+ return;
+ }
+ }
+
+ ProfileStatisticsFactory::GetForProfile(profile)->GatherStatistics(
+ base::Bind(
+ &UserManagerScreenHandler::RemoveUserDialogLoadStatsCallback,
+ weak_ptr_factory_.GetWeakPtr(), profile_path));
+}
+
+void UserManagerScreenHandler::RemoveUserDialogLoadStatsCallback(
+ base::FilePath profile_path,
+ profiles::ProfileCategoryStats result) {
+ // Copy result into return_value.
+ base::DictionaryValue return_value;
+ for (const auto& item : result) {
+ std::unique_ptr<base::DictionaryValue> stat(new base::DictionaryValue);
+ stat->SetIntegerWithoutPathExpansion("count", item.count);
+ stat->SetBooleanWithoutPathExpansion("success", item.success);
+ return_value.SetWithoutPathExpansion(item.category, std::move(stat));
+ }
+ web_ui()->CallJavascriptFunctionUnsafe("updateRemoveWarningDialog",
+ base::Value(profile_path.value()),
+ return_value);
+}
+
+void UserManagerScreenHandler::HandleGetRemoveWarningDialogMessage(
+ const base::ListValue* args) {
+ const base::DictionaryValue* arg;
+ if (!args->GetDictionary(0, &arg))
+ return;
+
+ std::string profile_path("");
+ bool is_synced_user = false;
+ bool has_errors = false;
+
+ if (!arg->GetString("profilePath", &profile_path) ||
+ !arg->GetBoolean("isSyncedUser", &is_synced_user) ||
+ !arg->GetBoolean("hasErrors", &has_errors))
+ return;
+
+ int total_count = 0;
+ if (!arg->GetInteger("totalCount", &total_count))
+ return;
+
+ int message_id = is_synced_user ?
+ (has_errors ? IDS_LOGIN_POD_USER_REMOVE_WARNING_SYNC_WITH_ERRORS :
+ IDS_LOGIN_POD_USER_REMOVE_WARNING_SYNC) :
+ (has_errors ? IDS_LOGIN_POD_USER_REMOVE_WARNING_NONSYNC_WITH_ERRORS :
+ IDS_LOGIN_POD_USER_REMOVE_WARNING_NONSYNC);
+
+ base::Value message =
+ base::Value(l10n_util::GetPluralStringFUTF16(message_id, total_count));
+
+ web_ui()->CallJavascriptFunctionUnsafe("updateRemoveWarningDialogSetMessage",
+ base::Value(profile_path), message,
+ base::Value(total_count));
+}
+
+void UserManagerScreenHandler::OnGetTokenInfoResponse(
+ std::unique_ptr<base::DictionaryValue> token_info) {
+ // Password is unchanged so user just mistyped it. Ask again.
+ ReportAuthenticationResult(false, ProfileMetrics::AUTH_FAILED);
+}
+
+void UserManagerScreenHandler::OnOAuthError() {
+ // Password has changed. Go through online signin flow.
+ DCHECK(!email_address_.empty());
+ oauth_client_.reset();
+ UserManagerProfileDialog::ShowReauthDialog(
+ web_ui()->GetWebContents()->GetBrowserContext(), email_address_,
+ signin_metrics::Reason::REASON_UNLOCK);
+}
+
+void UserManagerScreenHandler::OnNetworkError(int response_code) {
+ // Inconclusive but can't do real signin without being online anyway.
+ oauth_client_.reset();
+ ReportAuthenticationResult(false, ProfileMetrics::AUTH_FAILED_OFFLINE);
+}
+
+void UserManagerScreenHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(kJsApiUserManagerInitialize,
+ base::Bind(&UserManagerScreenHandler::HandleInitialize,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kJsApiUserManagerAuthLaunchUser,
+ base::Bind(&UserManagerScreenHandler::HandleAuthenticatedLaunchUser,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kJsApiUserManagerLaunchGuest,
+ base::Bind(&UserManagerScreenHandler::HandleLaunchGuest,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(kJsApiUserManagerLaunchUser,
+ base::Bind(&UserManagerScreenHandler::HandleLaunchUser,
+ base::Unretained(this)));
+ 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,
+ base::Bind(&UserManagerScreenHandler::HandleRemoveUserWarningLoadStats,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kJsApiUserManagerGetRemoveWarningDialogMessage,
+ base::Bind(&UserManagerScreenHandler::HandleGetRemoveWarningDialogMessage,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ kJsApiUserManagerAreAllProfilesLocked,
+ 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("getTouchViewState", kDoNothingCallback);
+ // Unused callbacks from display_manager.js
+ web_ui()->RegisterMessageCallback("showAddUser", kDoNothingCallback);
+ web_ui()->RegisterMessageCallback("loadWallpaper", kDoNothingCallback);
+ web_ui()->RegisterMessageCallback("updateCurrentScreen", kDoNothingCallback);
+ web_ui()->RegisterMessageCallback("loginVisible", kDoNothingCallback);
+ // Unused callbacks from user_pod_row.js
+ web_ui()->RegisterMessageCallback("focusPod", kDoNothingCallback);
+ web_ui()->RegisterMessageCallback("noPodFocused", kDoNothingCallback);
+}
+
+void UserManagerScreenHandler::GetLocalizedValues(
+ base::DictionaryValue* localized_strings) {
+ // For Control Bar.
+ localized_strings->SetString("signedIn",
+ l10n_util::GetStringUTF16(IDS_SCREEN_LOCK_ACTIVE_USER));
+ localized_strings->SetString("addUser",
+ l10n_util::GetStringUTF16(IDS_ADD_USER_BUTTON));
+ localized_strings->SetString("cancel", l10n_util::GetStringUTF16(IDS_CANCEL));
+ localized_strings->SetString(
+ "browseAsGuest", l10n_util::GetStringUTF16(IDS_BROWSE_AS_GUEST_BUTTON));
+ localized_strings->SetString("signOutUser",
+ l10n_util::GetStringUTF16(IDS_SCREEN_LOCK_SIGN_OUT));
+ localized_strings->SetString("addSupervisedUser",
+ l10n_util::GetStringUTF16(IDS_CREATE_LEGACY_SUPERVISED_USER_MENU_LABEL));
+
+ // For AccountPickerScreen.
+ localized_strings->SetString("screenType", "login-add-user");
+ localized_strings->SetString("highlightStrength", "normal");
+ localized_strings->SetString("title",
+ l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
+ localized_strings->SetString("passwordHint",
+ l10n_util::GetStringUTF16(IDS_LOGIN_POD_EMPTY_PASSWORD_TEXT));
+ localized_strings->SetString("signingIn",
+ l10n_util::GetStringUTF16(IDS_LOGIN_POD_SIGNING_IN));
+ localized_strings->SetString("podMenuButtonAccessibleName",
+ l10n_util::GetStringUTF16(IDS_LOGIN_POD_MENU_BUTTON_ACCESSIBLE_NAME));
+ localized_strings->SetString("podMenuRemoveItemAccessibleName",
+ l10n_util::GetStringUTF16(
+ IDS_LOGIN_POD_MENU_REMOVE_ITEM_ACCESSIBLE_NAME));
+ localized_strings->SetString("removeUser",
+ l10n_util::GetStringUTF16(IDS_LOGIN_POD_USER_REMOVE_WARNING_BUTTON));
+ localized_strings->SetString("passwordFieldAccessibleName",
+ l10n_util::GetStringUTF16(IDS_LOGIN_POD_PASSWORD_FIELD_ACCESSIBLE_NAME));
+ localized_strings->SetString("bootIntoWallpaper", "off");
+
+ // For AccountPickerScreen, the remove user warning overlay.
+ localized_strings->SetString("removeUserWarningButtonTitle",
+ l10n_util::GetStringUTF16(IDS_LOGIN_POD_USER_REMOVE_WARNING_BUTTON));
+ localized_strings->SetString("removeUserWarningTextNonSyncNoStats",
+ l10n_util::GetStringUTF16(
+ IDS_LOGIN_POD_USER_REMOVE_WARNING_NONSYNC_NOSTATS));
+ localized_strings->SetString("removeUserWarningTextNonSyncCalculating",
+ l10n_util::GetStringUTF16(
+ IDS_LOGIN_POD_USER_REMOVE_WARNING_NONSYNC_CALCULATING));
+ localized_strings->SetString("removeUserWarningTextHistory",
+ l10n_util::GetStringUTF16(IDS_LOGIN_POD_USER_REMOVE_WARNING_HISTORY));
+ localized_strings->SetString("removeUserWarningTextPasswords",
+ l10n_util::GetStringUTF16(IDS_LOGIN_POD_USER_REMOVE_WARNING_PASSWORDS));
+ localized_strings->SetString("removeUserWarningTextBookmarks",
+ l10n_util::GetStringUTF16(IDS_LOGIN_POD_USER_REMOVE_WARNING_BOOKMARKS));
+ localized_strings->SetString("removeUserWarningTextSettings",
+ l10n_util::GetStringUTF16(IDS_LOGIN_POD_USER_REMOVE_WARNING_SETTINGS));
+ localized_strings->SetString("removeUserWarningTextCalculating",
+ l10n_util::GetStringUTF16(IDS_LOGIN_POD_USER_REMOVE_WARNING_CALCULATING));
+ localized_strings->SetString("removeUserWarningTextSyncNoStats",
+ l10n_util::GetStringUTF16(
+ IDS_LOGIN_POD_USER_REMOVE_WARNING_SYNC_NOSTATS));
+ localized_strings->SetString("removeUserWarningTextSyncCalculating",
+ l10n_util::GetStringUTF16(
+ IDS_LOGIN_POD_USER_REMOVE_WARNING_SYNC_CALCULATING));
+ localized_strings->SetString("removeLegacySupervisedUserWarningText",
+ l10n_util::GetStringFUTF16(
+ IDS_LOGIN_POD_LEGACY_SUPERVISED_USER_REMOVE_WARNING,
+ base::UTF8ToUTF16(
+ chrome::kLegacySupervisedUserManagementDisplayURL)));
+ localized_strings->SetString(
+ "removeNonOwnerUserWarningText",
+ l10n_util::GetStringUTF16(IDS_LOGIN_POD_NON_OWNER_USER_REMOVE_WARNING));
+
+ // Strings needed for the User Manager tutorial slides.
+ localized_strings->SetString("tutorialNext",
+ l10n_util::GetStringUTF16(IDS_USER_MANAGER_TUTORIAL_NEXT));
+ localized_strings->SetString("tutorialDone",
+ l10n_util::GetStringUTF16(IDS_USER_MANAGER_TUTORIAL_DONE));
+ localized_strings->SetString("slideWelcomeTitle",
+ l10n_util::GetStringUTF16(IDS_USER_MANAGER_TUTORIAL_SLIDE_INTRO_TITLE));
+ localized_strings->SetString("slideWelcomeText",
+ l10n_util::GetStringUTF16(IDS_USER_MANAGER_TUTORIAL_SLIDE_INTRO_TEXT));
+ localized_strings->SetString("slideYourChromeTitle",
+ l10n_util::GetStringUTF16(
+ IDS_USER_MANAGER_TUTORIAL_SLIDE_YOUR_CHROME_TITLE));
+ localized_strings->SetString("slideYourChromeText", l10n_util::GetStringUTF16(
+ IDS_USER_MANAGER_TUTORIAL_SLIDE_YOUR_CHROME_TEXT));
+ localized_strings->SetString("slideGuestsTitle",
+ l10n_util::GetStringUTF16(IDS_USER_MANAGER_TUTORIAL_SLIDE_GUEST_TITLE));
+ localized_strings->SetString("slideGuestsText",
+ l10n_util::GetStringUTF16(IDS_USER_MANAGER_TUTORIAL_SLIDE_GUEST_TEXT));
+ localized_strings->SetString("slideFriendsTitle",
+ l10n_util::GetStringUTF16(IDS_USER_MANAGER_TUTORIAL_SLIDE_FRIENDS_TITLE));
+ localized_strings->SetString("slideFriendsText",
+ l10n_util::GetStringUTF16(IDS_USER_MANAGER_TUTORIAL_SLIDE_FRIENDS_TEXT));
+ localized_strings->SetString("slideCompleteTitle",
+ l10n_util::GetStringUTF16(IDS_USER_MANAGER_TUTORIAL_SLIDE_OUTRO_TITLE));
+ localized_strings->SetString("slideCompleteText",
+ l10n_util::GetStringUTF16(IDS_USER_MANAGER_TUTORIAL_SLIDE_OUTRO_TEXT));
+ localized_strings->SetString("slideCompleteUserNotFound",
+ l10n_util::GetStringUTF16(
+ IDS_USER_MANAGER_TUTORIAL_SLIDE_OUTRO_USER_NOT_FOUND));
+ localized_strings->SetString("slideCompleteAddUser",
+ l10n_util::GetStringUTF16(
+ IDS_USER_MANAGER_TUTORIAL_SLIDE_OUTRO_ADD_USER));
+
+ // Strings needed for the user_pod_template public account div, but not ever
+ // actually displayed for desktop users.
+ localized_strings->SetString("publicAccountReminder", base::string16());
+ localized_strings->SetString("publicSessionLanguageAndInput",
+ base::string16());
+ localized_strings->SetString("publicAccountEnter", base::string16());
+ localized_strings->SetString("publicAccountEnterAccessibleName",
+ base::string16());
+ localized_strings->SetString("publicAccountMonitoringWarning",
+ base::string16());
+ localized_strings->SetString("publicAccountLearnMore", base::string16());
+ localized_strings->SetString("publicAccountMonitoringInfo", base::string16());
+ localized_strings->SetString("publicAccountMonitoringInfoItem1",
+ base::string16());
+ localized_strings->SetString("publicAccountMonitoringInfoItem2",
+ base::string16());
+ localized_strings->SetString("publicAccountMonitoringInfoItem3",
+ base::string16());
+ localized_strings->SetString("publicAccountMonitoringInfoItem4",
+ base::string16());
+ localized_strings->SetString("publicSessionSelectLanguage", base::string16());
+ localized_strings->SetString("publicSessionSelectKeyboard", base::string16());
+ localized_strings->SetString("signinBannerText", base::string16());
+ localized_strings->SetString("launchAppButton", base::string16());
+ localized_strings->SetString("multiProfilesRestrictedPolicyTitle",
+ base::string16());
+ localized_strings->SetString("multiProfilesNotAllowedPolicyMsg",
+ base::string16());
+ localized_strings->SetString("multiProfilesPrimaryOnlyPolicyMsg",
+ base::string16());
+ localized_strings->SetString("multiProfilesOwnerPrimaryOnlyMsg",
+ base::string16());
+
+ // Error message when trying to add a profile while all profiles are locked.
+ localized_strings->SetString("addProfileAllProfilesLockedError",
+ l10n_util::GetStringUTF16(
+ IDS_USER_MANAGER_ADD_PROFILE_PROFILES_LOCKED_ERROR));
+ // Error message when trying to browse as guest while all profiles are locked.
+ localized_strings->SetString("browseAsGuestAllProfilesLockedError",
+ l10n_util::GetStringUTF16(
+ IDS_USER_MANAGER_GO_GUEST_PROFILES_LOCKED_ERROR));
+}
+
+void UserManagerScreenHandler::SendUserList() {
+ base::ListValue users_list;
+ std::vector<ProfileAttributesEntry*> entries =
+ g_browser_process->profile_manager()->GetProfileAttributesStorage().
+ GetAllProfilesAttributesSortedByName();
+ user_auth_type_map_.clear();
+
+ // Profile deletion is not allowed in Metro mode.
+ bool can_remove = true;
+#if defined(USE_ASH)
+ can_remove = !ash::Shell::HasInstance();
+#endif
+
+ for (const ProfileAttributesEntry* entry : entries) {
+ // Don't show profiles still in the middle of being set up as new legacy
+ // supervised users.
+ if (entry->IsOmitted())
+ continue;
+
+ std::unique_ptr<base::DictionaryValue> profile_value(
+ new base::DictionaryValue());
+ base::FilePath profile_path = entry->GetPath();
+
+ profile_value->SetString(kKeyUsername, entry->GetUserName());
+ profile_value->SetString(kKeyEmailAddress, entry->GetUserName());
+ profile_value->SetString(kKeyDisplayName,
+ profiles::GetAvatarNameForProfile(profile_path));
+ profile_value->Set(kKeyProfilePath,
+ base::CreateFilePathValue(profile_path));
+ profile_value->SetBoolean(kKeyPublicAccount, false);
+ profile_value->SetBoolean(kKeyLegacySupervisedUser,
+ entry->IsLegacySupervised());
+ profile_value->SetBoolean(kKeyChildUser, entry->IsChild());
+ profile_value->SetBoolean(kKeyNeedsSignin, entry->IsSigninRequired());
+ profile_value->SetBoolean(kKeyHasLocalCreds,
+ !entry->GetLocalAuthCredentials().empty());
+ profile_value->SetBoolean(kKeyIsOwner, false);
+ profile_value->SetBoolean(kKeyCanRemove, can_remove);
+ profile_value->SetBoolean(kKeyIsDesktop, true);
+ profile_value->SetString(kKeyAvatarUrl, GetAvatarImage(entry));
+
+ profiles::ProfileCategoryStats stats =
+ ProfileStatistics::GetProfileStatisticsFromAttributesStorage(
+ profile_path);
+ std::unique_ptr<base::DictionaryValue> stats_dict(
+ new base::DictionaryValue);
+ for (const auto& item : stats) {
+ std::unique_ptr<base::DictionaryValue> stat(new base::DictionaryValue);
+ stat->SetIntegerWithoutPathExpansion("count", item.count);
+ stat->SetBooleanWithoutPathExpansion("success", item.success);
+ stats_dict->SetWithoutPathExpansion(item.category, std::move(stat));
+ }
+ profile_value->SetWithoutPathExpansion(kKeyStatistics,
+ std::move(stats_dict));
+
+ // GetProfileByPath returns a pointer if the profile is fully loaded, NULL
+ // otherwise.
+ Profile* profile =
+ g_browser_process->profile_manager()->GetProfileByPath(profile_path);
+ profile_value->SetBoolean(kKeyIsProfileLoaded, profile != nullptr);
+
+ users_list.Append(std::move(profile_value));
+ }
+
+ web_ui()->CallJavascriptFunctionUnsafe("login.AccountPickerScreen.loadUsers",
+ users_list,
+ base::Value(IsGuestModeEnabled()));
+
+ // This is the latest C++ code we have in the flow to show the UserManager.
+ // This may be invoked more than once per UserManager lifetime; the
+ // UserManager will ensure all relevant logging only happens once.
+ UserManager::OnUserManagerShown();
+}
+
+void UserManagerScreenHandler::ReportAuthenticationResult(
+ bool success,
+ ProfileMetrics::ProfileAuth auth) {
+ ProfileMetrics::LogProfileAuthResult(auth);
+ email_address_.clear();
+
+ if (success) {
+ profiles::SwitchToProfile(
+ authenticating_profile_path_, true,
+ base::Bind(&UserManagerScreenHandler::OnSwitchToProfileComplete,
+ weak_ptr_factory_.GetWeakPtr()),
+ ProfileMetrics::SWITCH_PROFILE_UNLOCK);
+ } else {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "cr.ui.UserManager.showSignInError", base::Value(0),
+ base::Value(l10n_util::GetStringUTF8(
+ auth == ProfileMetrics::AUTH_FAILED_OFFLINE
+ ? IDS_LOGIN_ERROR_AUTHENTICATING_OFFLINE
+ : IDS_LOGIN_ERROR_AUTHENTICATING)),
+ base::Value(""), base::Value(0));
+ }
+}
+
+void UserManagerScreenHandler::OnBrowserWindowReady(Browser* browser) {
+ DCHECK(browser);
+ DCHECK(browser->window());
+
+ // Unlock the profile after browser opens so startup can read the lock bit.
+ // Any necessary authentication must have been successful to reach this point.
+ ProfileAttributesEntry* entry = nullptr;
+ if (!browser->profile()->IsGuestSession()) {
+ bool has_entry = g_browser_process->profile_manager()->
+ GetProfileAttributesStorage().
+ GetProfileAttributesWithPath(browser->profile()->GetPath(), &entry);
+ DCHECK(has_entry);
+ // If force sign in is enabled and profile is not signed in, do not close
+ // UserManager and unlock profile.
+ if (signin_util::IsForceSigninEnabled() && !entry->IsAuthenticated())
+ return;
+ entry->SetIsSigninRequired(false);
+ }
+
+ if (!url_hash_.empty()) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&UrlHashHelper::ExecuteUrlHash,
+ base::Owned(new UrlHashHelper(browser, url_hash_))));
+ }
+
+ // This call is last as it deletes this object.
+ UserManager::Hide();
+}
+
+void UserManagerScreenHandler::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK_EQ(chrome::NOTIFICATION_BROWSER_WINDOW_READY, type);
+
+ // Only respond to one Browser Window Ready event.
+ registrar_.Remove(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+ content::NotificationService::AllSources());
+ OnBrowserWindowReady(content::Source<Browser>(source).ptr());
+}
+
+// This callback is run after switching to a new profile has finished. This
+// means either a new browser has been created (but not the window), or an
+// existing one has been found. The HideUserManager task needs to be posted
+// since closing the User Manager before the window is created can flakily
+// cause Chrome to close.
+void UserManagerScreenHandler::OnSwitchToProfileComplete(
+ Profile* profile, Profile::CreateStatus profile_create_status) {
+ Browser* browser = chrome::FindAnyBrowser(profile, false);
+ if (browser && browser->window()) {
+ OnBrowserWindowReady(browser);
+ } else {
+ registrar_.Add(this,
+ chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+ content::NotificationService::AllSources());
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/signin/user_manager_screen_handler.h b/chromium/chrome/browser/ui/webui/signin/user_manager_screen_handler.h
new file mode 100644
index 00000000000..522737931bf
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/user_manager_screen_handler.h
@@ -0,0 +1,145 @@
+// 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_SIGNIN_USER_MANAGER_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_USER_MANAGER_SCREEN_HANDLER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_metrics.h"
+#include "chrome/browser/profiles/profile_statistics.h"
+#include "components/proximity_auth/screenlock_bridge.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "google_apis/gaia/gaia_oauth_client.h"
+
+class AccountId;
+class Browser;
+
+namespace base {
+class DictionaryValue;
+class FilePath;
+class ListValue;
+}
+
+class UserManagerScreenHandler
+ : public content::WebUIMessageHandler,
+ public proximity_auth::ScreenlockBridge::LockHandler,
+ public gaia::GaiaOAuthClient::Delegate,
+ public content::NotificationObserver {
+ public:
+ UserManagerScreenHandler();
+ ~UserManagerScreenHandler() override;
+
+ void GetLocalizedValues(base::DictionaryValue* localized_strings);
+
+ private:
+ // An observer for any changes to Profiles in the ProfileAttributesStorage so
+ // that all the visible user manager screens can be updated.
+ class ProfileUpdateObserver;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // content::NotificationObserver implementation:
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ // proximity_auth::ScreenlockBridge::LockHandler implementation.
+ void ShowBannerMessage(const base::string16& message) override;
+ void ShowUserPodCustomIcon(
+ const AccountId& account_id,
+ const proximity_auth::ScreenlockBridge::UserPodCustomIconOptions&
+ icon_options) override;
+ void HideUserPodCustomIcon(const AccountId& account_id) override;
+ void EnableInput() override;
+ void SetAuthType(
+ const AccountId& account_id,
+ proximity_auth::ScreenlockBridge::LockHandler::AuthType auth_type,
+ const base::string16& auth_value) override;
+ AuthType GetAuthType(const AccountId& account_id) const override;
+ ScreenType GetScreenType() const override;
+ void Unlock(const AccountId& account_id) override;
+ void AttemptEasySignin(const AccountId& account_id,
+ const std::string& secret,
+ const std::string& key_label) override;
+
+ void HandleInitialize(const base::ListValue* args);
+ void HandleAuthenticatedLaunchUser(const base::ListValue* args);
+ void HandleLaunchGuest(const base::ListValue* args);
+ void HandleLaunchUser(const base::ListValue* args);
+ void HandleRemoveUser(const base::ListValue* args);
+ void HandleAreAllProfilesLocked(const base::ListValue* args);
+ void HandleAttemptUnlock(const base::ListValue* args);
+ void HandleHardlockUserPod(const base::ListValue* args);
+ void HandleRemoveUserWarningLoadStats(const base::ListValue* args);
+ void HandleGetRemoveWarningDialogMessage(const base::ListValue* args);
+
+ // Callback function used by HandleRemoveUserWarningLoadStats
+ void RemoveUserDialogLoadStatsCallback(
+ base::FilePath profile_path,
+ profiles::ProfileCategoryStats result);
+
+ // Handle GAIA auth results.
+ void OnGetTokenInfoResponse(
+ std::unique_ptr<base::DictionaryValue> token_info) override;
+ void OnOAuthError() override;
+ void OnNetworkError(int response_code) override;
+
+ // Handle when Notified of a NOTIFICATION_BROWSER_WINDOW_READY event.
+ void OnBrowserWindowReady(Browser* browser);
+
+ // Sends user list to account chooser.
+ void SendUserList();
+
+ // Pass success/failure information back to the web page.
+ void ReportAuthenticationResult(bool success,
+ ProfileMetrics::ProfileAuth metric);
+
+ // Perform cleanup once the profile and browser are open.
+ void OnSwitchToProfileComplete(Profile* profile,
+ Profile::CreateStatus profile_create_status);
+
+ // Observes the ProfileAttributesStorage and gets notified when a profile has
+ // been modified, so that the displayed user pods can be updated.
+ std::unique_ptr<ProfileUpdateObserver> profile_attributes_storage_observer_;
+
+ // Authenticator used when local-auth fails.
+ std::unique_ptr<gaia::GaiaOAuthClient> oauth_client_;
+
+ // The path of the profile currently being authenticated.
+ base::FilePath authenticating_profile_path_;
+
+ // Login email held during on-line auth for later use.
+ std::string email_address_;
+
+ // URL hash, used to key post-profile actions if present.
+ std::string url_hash_;
+
+ typedef std::map<std::string,
+ proximity_auth::ScreenlockBridge::LockHandler::AuthType>
+ UserAuthTypeMap;
+ UserAuthTypeMap user_auth_type_map_;
+
+ content::NotificationRegistrar registrar_;
+
+ // The CancelableTaskTracker is currently used by GetProfileStatistics
+ base::CancelableTaskTracker tracker_;
+
+ base::WeakPtrFactory<UserManagerScreenHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserManagerScreenHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_USER_MANAGER_SCREEN_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/signin/user_manager_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/signin/user_manager_ui_browsertest.cc
new file mode 100644
index 00000000000..55ec6aaad04
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin/user_manager_ui_browsertest.cc
@@ -0,0 +1,88 @@
+// 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 "base/command_line.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_window.h"
+#include "chrome/browser/profiles/profiles_state.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/signin/core/common/profile_management_switches.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "ui/base/l10n/l10n_util.h"
+
+class UserManagerUIBrowserTest : public InProcessBrowserTest,
+ public testing::WithParamInterface<bool> {
+ public:
+ UserManagerUIBrowserTest() {}
+};
+
+IN_PROC_BROWSER_TEST_F(UserManagerUIBrowserTest, PageLoads) {
+ ui_test_utils::NavigateToURL(
+ browser(), GURL(chrome::kChromeUIMdUserManagerUrl));
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+
+ base::string16 title = web_contents->GetTitle();
+ EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), title);
+
+ // If the page has loaded correctly, then there should be an account picker.
+ int num_account_pickers = -1;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
+ web_contents,
+ "domAutomationController.send("
+ "document.getElementsByClassName('account-picker').length)",
+ &num_account_pickers));
+ EXPECT_EQ(1, num_account_pickers);
+
+ int num_pods = -1;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
+ web_contents,
+ "domAutomationController.send("
+ "parseInt(document.getElementById('pod-row').getAttribute('ncolumns')))",
+ &num_pods));
+
+ // There should be one user pod for each profile.
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ EXPECT_EQ(num_pods, static_cast<int>(profile_manager->GetNumberOfProfiles()));
+}
+
+IN_PROC_BROWSER_TEST_F(UserManagerUIBrowserTest, PageRedirectsToAboutChrome) {
+ std::string user_manager_url = chrome::kChromeUIMdUserManagerUrl;
+ user_manager_url += profiles::kUserManagerSelectProfileAboutChrome;
+
+ ui_test_utils::NavigateToURL(browser(), GURL(user_manager_url));
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+
+ // If this is a Windows style path, escape all the slashes.
+ std::string profile_path;
+ base::ReplaceChars(browser()->profile()->GetPath().MaybeAsASCII(),
+ "\\", "\\\\", &profile_path);
+
+ std::string launch_js =
+ base::StringPrintf("Oobe.launchUser('%s')", profile_path.c_str());
+
+ bool result = content::ExecuteScript(web_contents, launch_js);
+ EXPECT_TRUE(result);
+ base::RunLoop().RunUntilIdle();
+
+ content::WebContents* about_chrome_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ GURL current_URL = about_chrome_contents->GetVisibleURL();
+ EXPECT_EQ(GURL(chrome::kChromeUIHelpURL), current_URL);
+}
+
+// TODO(mlerman): Test that unlocking a locked profile causes the extensions
+// service to become unblocked.
diff --git a/chromium/chrome/browser/ui/webui/signin_internals_ui.cc b/chromium/chrome/browser/ui/webui/signin_internals_ui.cc
new file mode 100644
index 00000000000..94bc931cd86
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin_internals_ui.cc
@@ -0,0 +1,115 @@
+// 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/ui/webui/signin_internals_ui.h"
+
+#include <string>
+#include <vector>
+
+#include "base/hash.h"
+#include "base/profiler/scoped_tracker.h"
+#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/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 "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace {
+
+content::WebUIDataSource* CreateSignInInternalsHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUISignInInternalsHost);
+
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("signin_internals.js", IDR_SIGNIN_INTERNALS_INDEX_JS);
+ source->SetDefaultResource(IDR_SIGNIN_INTERNALS_INDEX_HTML);
+ source->UseGzip(std::unordered_set<std::string>());
+ return source;
+}
+
+} // namespace
+
+SignInInternalsUI::SignInInternalsUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreateSignInInternalsHTMLSource());
+ if (profile) {
+ AboutSigninInternals* about_signin_internals =
+ AboutSigninInternalsFactory::GetForProfile(profile);
+ if (about_signin_internals)
+ about_signin_internals->AddSigninObserver(this);
+ }
+}
+
+SignInInternalsUI::~SignInInternalsUI() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (profile) {
+ AboutSigninInternals* about_signin_internals =
+ AboutSigninInternalsFactory::GetForProfile(profile);
+ if (about_signin_internals) {
+ about_signin_internals->RemoveSigninObserver(this);
+ }
+ }
+}
+
+bool SignInInternalsUI::OverrideHandleWebUIMessage(
+ const GURL& source_url,
+ const std::string& name,
+ const base::ListValue& content) {
+ if (name == "getSigninInfo") {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ if (!profile)
+ return false;
+
+ AboutSigninInternals* about_signin_internals =
+ AboutSigninInternalsFactory::GetForProfile(profile);
+ // TODO(vishwath): The UI would look better if we passed in a dict with some
+ // reasonable defaults, so the about:signin-internals page doesn't look
+ // empty in incognito mode. Alternatively, we could force about:signin to
+ // open in non-incognito mode always (like about:settings for ex.).
+ if (about_signin_internals) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "chrome.signin.getSigninInfo.handleReply",
+ *about_signin_internals->GetSigninStatus());
+
+ std::vector<gaia::ListedAccount> cookie_accounts;
+ std::vector<gaia::ListedAccount> signed_out_accounts;
+ GaiaCookieManagerService* cookie_manager_service =
+ GaiaCookieManagerServiceFactory::GetForProfile(profile);
+ if (cookie_manager_service->ListAccounts(
+ &cookie_accounts, &signed_out_accounts,
+ "ChromiumSignInInternalsUI")) {
+ about_signin_internals->OnGaiaAccountsInCookieUpdated(
+ cookie_accounts,
+ signed_out_accounts,
+ GoogleServiceAuthError(GoogleServiceAuthError::NONE));
+ }
+
+ return true;
+ }
+ }
+ return false;
+}
+
+void SignInInternalsUI::OnSigninStateChanged(
+ const base::DictionaryValue* info) {
+ // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
+ // fixed.
+ tracked_objects::ScopedTracker tracking_profile(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "422460 SignInInternalsUI::OnSigninStateChanged"));
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "chrome.signin.onSigninInfoChanged.fire", *info);
+}
+
+void SignInInternalsUI::OnCookieAccountsFetched(
+ const base::DictionaryValue* info) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "chrome.signin.onCookieAccountsFetched.fire", *info);
+}
diff --git a/chromium/chrome/browser/ui/webui/signin_internals_ui.h b/chromium/chrome/browser/ui/webui/signin_internals_ui.h
new file mode 100644
index 00000000000..217a72291a8
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/signin_internals_ui.h
@@ -0,0 +1,36 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SIGNIN_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SIGNIN_INTERNALS_UI_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/values.h"
+#include "components/signin/core/browser/about_signin_internals.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The implementation for the chrome://signin-internals page.
+class SignInInternalsUI : public content::WebUIController,
+ public AboutSigninInternals::Observer {
+ public:
+ explicit SignInInternalsUI(content::WebUI* web_ui);
+ ~SignInInternalsUI() override;
+
+ // content::WebUIController implementation.
+ bool OverrideHandleWebUIMessage(const GURL& source_url,
+ const std::string& name,
+ const base::ListValue& args) override;
+
+ // AboutSigninInternals::Observer::OnSigninStateChanged implementation.
+ void OnSigninStateChanged(const base::DictionaryValue* info) override;
+
+ // Notification that the cookie accounts are ready to be displayed.
+ void OnCookieAccountsFetched(const base::DictionaryValue* info) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SignInInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SIGNIN_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/site_settings_helper.cc b/chromium/chrome/browser/ui/webui/site_settings_helper.cc
new file mode 100644
index 00000000000..8c28fbeb998
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/site_settings_helper.cc
@@ -0,0 +1,463 @@
+// 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 "chrome/browser/ui/webui/site_settings_helper.h"
+
+#include <functional>
+#include <string>
+
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/permissions/chooser_context_base.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/usb/usb_chooser_context_factory.h"
+#include "chrome/common/pref_names.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/prefs/pref_service.h"
+#include "extensions/browser/extension_registry.h"
+
+namespace site_settings {
+
+const char kAppName[] = "appName";
+const char kAppId[] = "appId";
+const char kSetting[] = "setting";
+const char kOrigin[] = "origin";
+const char kDisplayName[] = "displayName";
+const char kOriginForFavicon[] = "originForFavicon";
+const char kPolicyProviderId[] = "policy";
+const char kSource[] = "source";
+const char kIncognito[] = "incognito";
+const char kEmbeddingOrigin[] = "embeddingOrigin";
+const char kPreferencesSource[] = "preference";
+const char kObject[] = "object";
+const char kObjectName[] = "objectName";
+
+const char kGroupTypeUsb[] = "usb-devices";
+
+ChooserContextBase* GetUsbChooserContext(Profile* profile) {
+ return reinterpret_cast<ChooserContextBase*>(
+ UsbChooserContextFactory::GetForProfile(profile));
+}
+
+namespace {
+
+// Maps from the UI string to the object it represents (for sorting purposes).
+typedef std::multimap<std::string, const base::DictionaryValue*> SortedObjects;
+
+// Maps from a secondary URL to the set of objects it has permission to access.
+typedef std::map<GURL, SortedObjects> OneOriginObjects;
+
+// Maps from a primary URL/source pair to a OneOriginObjects. All the mappings
+// in OneOriginObjects share the given primary URL and source.
+typedef std::map<std::pair<GURL, std::string>, OneOriginObjects>
+ AllOriginObjects;
+
+const ContentSettingsTypeNameEntry kContentSettingsTypeGroupNames[] = {
+ {CONTENT_SETTINGS_TYPE_COOKIES, "cookies"},
+ {CONTENT_SETTINGS_TYPE_IMAGES, "images"},
+ {CONTENT_SETTINGS_TYPE_JAVASCRIPT, "javascript"},
+ {CONTENT_SETTINGS_TYPE_PLUGINS, "plugins"},
+ {CONTENT_SETTINGS_TYPE_POPUPS, "popups"},
+ {CONTENT_SETTINGS_TYPE_GEOLOCATION, "location"},
+ {CONTENT_SETTINGS_TYPE_NOTIFICATIONS, "notifications"},
+ {CONTENT_SETTINGS_TYPE_AUTO_SELECT_CERTIFICATE, "auto-select-certificate"},
+ {CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS, "register-protocol-handler"},
+ {CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, "media-stream-mic"},
+ {CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, "media-stream-camera"},
+ {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_SSL_CERT_DECISIONS, "ssl-cert-decisions"},
+#if defined(OS_CHROMEOS)
+ {CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER, "protectedContent"},
+#endif
+ {CONTENT_SETTINGS_TYPE_BACKGROUND_SYNC, "background-sync"},
+ {CONTENT_SETTINGS_TYPE_SUBRESOURCE_FILTER, "subresource-filter"},
+};
+
+} // namespace
+
+bool HasRegisteredGroupName(ContentSettingsType type) {
+ for (size_t i = 0; i < arraysize(kContentSettingsTypeGroupNames); ++i) {
+ if (type == kContentSettingsTypeGroupNames[i].type)
+ return true;
+ }
+ return false;
+}
+
+ContentSettingsType ContentSettingsTypeFromGroupName(const std::string& name) {
+ for (size_t i = 0; i < arraysize(kContentSettingsTypeGroupNames); ++i) {
+ if (name == kContentSettingsTypeGroupNames[i].name)
+ return kContentSettingsTypeGroupNames[i].type;
+ }
+
+ NOTREACHED() << name << " is not a recognized content settings type.";
+ return CONTENT_SETTINGS_TYPE_DEFAULT;
+}
+
+std::string ContentSettingsTypeToGroupName(ContentSettingsType type) {
+ for (size_t i = 0; i < arraysize(kContentSettingsTypeGroupNames); ++i) {
+ if (type == kContentSettingsTypeGroupNames[i].type)
+ return kContentSettingsTypeGroupNames[i].name;
+ }
+
+ NOTREACHED() << type << " is not a recognized content settings type.";
+ return std::string();
+}
+
+// Add an "Allow"-entry to the list of |exceptions| for a |url_pattern| from
+// the web extent of a hosted |app|.
+void AddExceptionForHostedApp(const std::string& url_pattern,
+ const extensions::Extension& app, base::ListValue* exceptions) {
+ std::unique_ptr<base::DictionaryValue> exception(new base::DictionaryValue());
+
+ std::string setting_string =
+ content_settings::ContentSettingToString(CONTENT_SETTING_ALLOW);
+ DCHECK(!setting_string.empty());
+
+ exception->SetString(site_settings::kSetting, setting_string);
+ exception->SetString(site_settings::kOrigin, url_pattern);
+ exception->SetString(site_settings::kDisplayName, url_pattern);
+ exception->SetString(site_settings::kEmbeddingOrigin, url_pattern);
+ exception->SetString(site_settings::kSource, "HostedApp");
+ exception->SetBoolean(site_settings::kIncognito, false);
+ exception->SetString(kAppName, app.name());
+ exception->SetString(kAppId, app.id());
+ exceptions->Append(std::move(exception));
+}
+
+// Create a DictionaryValue* that will act as a data source for a single row
+// in a HostContentSettingsMap-controlled exceptions table (e.g., cookies).
+std::unique_ptr<base::DictionaryValue> GetExceptionForPage(
+ const ContentSettingsPattern& pattern,
+ const ContentSettingsPattern& secondary_pattern,
+ const std::string& display_name,
+ const ContentSetting& setting,
+ const std::string& provider_name,
+ bool incognito) {
+ base::DictionaryValue* exception = new base::DictionaryValue();
+ exception->SetString(kOrigin, pattern.ToString());
+ exception->SetString(kDisplayName, display_name);
+ exception->SetString(kEmbeddingOrigin,
+ secondary_pattern == ContentSettingsPattern::Wildcard() ?
+ std::string() :
+ secondary_pattern.ToString());
+
+ std::string setting_string =
+ content_settings::ContentSettingToString(setting);
+ DCHECK(!setting_string.empty());
+
+ exception->SetString(kSetting, setting_string);
+ exception->SetString(kSource, provider_name);
+ exception->SetBoolean(kIncognito, incognito);
+ return base::WrapUnique(exception);
+}
+
+std::string GetDisplayName(
+ const ContentSettingsPattern& pattern,
+ const extensions::ExtensionRegistry* extension_registry) {
+ if (extension_registry &&
+ pattern.GetScheme() == ContentSettingsPattern::SCHEME_CHROMEEXTENSION) {
+ GURL url(pattern.ToString());
+ // For the extension scheme, the pattern must be a valid URL.
+ DCHECK(url.is_valid());
+ const extensions::Extension* extension =
+ extension_registry->GetExtensionById(
+ url.host(), extensions::ExtensionRegistry::EVERYTHING);
+ if (extension)
+ return extension->name();
+ }
+ return pattern.ToString();
+}
+
+void GetExceptionsFromHostContentSettingsMap(
+ const HostContentSettingsMap* map,
+ ContentSettingsType type,
+ const extensions::ExtensionRegistry* extension_registry,
+ content::WebUI* web_ui,
+ bool incognito,
+ const std::string* filter,
+ base::ListValue* exceptions) {
+ ContentSettingsForOneType entries;
+ map->GetSettingsForOneType(type, std::string(), &entries);
+ // Group settings by primary_pattern.
+ AllPatternsSettings all_patterns_settings;
+ for (ContentSettingsForOneType::iterator i = entries.begin();
+ i != entries.end(); ++i) {
+ // Don't add default settings.
+ if (i->primary_pattern == ContentSettingsPattern::Wildcard() &&
+ i->secondary_pattern == ContentSettingsPattern::Wildcard() &&
+ i->source != kPreferencesSource) {
+ continue;
+ }
+
+ // Off-the-record HostContentSettingsMap contains incognito content settings
+ // as well as normal content settings. Here, we use the incongnito settings
+ // only.
+ if (map->is_incognito() && !i->incognito)
+ continue;
+
+ if (filter && i->primary_pattern.ToString() != *filter)
+ continue;
+
+ all_patterns_settings[std::make_pair(i->primary_pattern, i->source)]
+ [i->secondary_pattern] = i->setting;
+ }
+
+ // Keep the exceptions sorted by provider so they will be displayed in
+ // precedence order.
+ std::vector<std::unique_ptr<base::DictionaryValue>>
+ all_provider_exceptions[HostContentSettingsMap::NUM_PROVIDER_TYPES];
+
+ // |all_patterns_settings| is sorted from the lowest precedence pattern to
+ // the highest (see operator< in ContentSettingsPattern), so traverse it in
+ // reverse to show the patterns with the highest precedence (the more specific
+ // ones) on the top.
+ for (AllPatternsSettings::reverse_iterator i = all_patterns_settings.rbegin();
+ i != all_patterns_settings.rend();
+ ++i) {
+ const ContentSettingsPattern& primary_pattern = i->first.first;
+ const OnePatternSettings& one_settings = i->second;
+ const std::string display_name =
+ GetDisplayName(primary_pattern, extension_registry);
+
+ // The "parent" entry either has an identical primary and secondary pattern,
+ // or has a wildcard secondary. The two cases are indistinguishable in the
+ // UI.
+ OnePatternSettings::const_iterator parent =
+ one_settings.find(primary_pattern);
+ if (parent == one_settings.end())
+ parent = one_settings.find(ContentSettingsPattern::Wildcard());
+
+ const std::string& source = i->first.second;
+ auto& this_provider_exceptions = all_provider_exceptions
+ [HostContentSettingsMap::GetProviderTypeFromSource(source)];
+
+ // Add the "parent" entry for the non-embedded setting.
+ ContentSetting parent_setting =
+ parent == one_settings.end() ? CONTENT_SETTING_DEFAULT : parent->second;
+ const ContentSettingsPattern& secondary_pattern =
+ parent == one_settings.end() ? primary_pattern : parent->first;
+ this_provider_exceptions.push_back(
+ GetExceptionForPage(primary_pattern, secondary_pattern, display_name,
+ parent_setting, source, incognito));
+
+ // Add the "children" for any embedded settings.
+ for (OnePatternSettings::const_iterator j = one_settings.begin();
+ j != one_settings.end(); ++j) {
+ // Skip the non-embedded setting which we already added above.
+ if (j == parent)
+ continue;
+
+ ContentSetting content_setting = j->second;
+ this_provider_exceptions.push_back(
+ GetExceptionForPage(primary_pattern, j->first, display_name,
+ content_setting, source, incognito));
+ }
+ }
+
+ // For camera and microphone, we do not have policy exceptions, but we do have
+ // the policy-set allowed URLs, which should be displayed in the same manner.
+ if (type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC ||
+ type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA) {
+ auto& policy_exceptions = all_provider_exceptions
+ [HostContentSettingsMap::GetProviderTypeFromSource(kPolicyProviderId)];
+ DCHECK(policy_exceptions.empty());
+ GetPolicyAllowedUrls(type, &policy_exceptions, extension_registry, web_ui,
+ incognito);
+ }
+
+ for (auto& one_provider_exceptions : all_provider_exceptions) {
+ for (auto& exception : one_provider_exceptions)
+ exceptions->Append(std::move(exception));
+ }
+}
+
+void GetContentCategorySetting(
+ const HostContentSettingsMap* map,
+ ContentSettingsType content_type,
+ base::DictionaryValue* object) {
+ std::string provider;
+ std::string setting = content_settings::ContentSettingToString(
+ map->GetDefaultContentSetting(content_type, &provider));
+ DCHECK(!setting.empty());
+
+ object->SetString(site_settings::kSetting, setting);
+ if (provider != "default")
+ object->SetString(site_settings::kSource, provider);
+}
+
+void GetPolicyAllowedUrls(
+ ContentSettingsType type,
+ std::vector<std::unique_ptr<base::DictionaryValue>>* exceptions,
+ const extensions::ExtensionRegistry* extension_registry,
+ content::WebUI* web_ui,
+ bool incognito) {
+ DCHECK(type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC ||
+ type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
+
+ PrefService* prefs = Profile::FromWebUI(web_ui)->GetPrefs();
+ const base::ListValue* policy_urls = prefs->GetList(
+ type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
+ ? prefs::kAudioCaptureAllowedUrls
+ : prefs::kVideoCaptureAllowedUrls);
+
+ // Convert the URLs to |ContentSettingsPattern|s. Ignore any invalid ones.
+ std::vector<ContentSettingsPattern> patterns;
+ for (const auto& entry : *policy_urls) {
+ std::string url;
+ bool valid_string = entry.GetAsString(&url);
+ if (!valid_string)
+ continue;
+
+ ContentSettingsPattern pattern = ContentSettingsPattern::FromString(url);
+ if (!pattern.IsValid())
+ continue;
+
+ patterns.push_back(pattern);
+ }
+
+ // The patterns are shown in the UI in a reverse order defined by
+ // |ContentSettingsPattern::operator<|.
+ std::sort(
+ patterns.begin(), patterns.end(), std::greater<ContentSettingsPattern>());
+
+ for (const ContentSettingsPattern& pattern : patterns) {
+ std::string display_name = GetDisplayName(pattern, extension_registry);
+ exceptions->push_back(GetExceptionForPage(
+ pattern, ContentSettingsPattern(), display_name, CONTENT_SETTING_ALLOW,
+ kPolicyProviderId, incognito));
+ }
+}
+
+const ChooserTypeNameEntry* ChooserTypeFromGroupName(const std::string& name) {
+ for (const auto& chooser_type : kChooserTypeGroupNames) {
+ if (chooser_type.name == name)
+ return &chooser_type;
+ }
+ return nullptr;
+}
+
+// Create a DictionaryValue* that will act as a data source for a single row
+// in a chooser permission exceptions table.
+std::unique_ptr<base::DictionaryValue> GetChooserExceptionForPage(
+ const GURL& requesting_origin,
+ const GURL& embedding_origin,
+ const std::string& provider_name,
+ bool incognito,
+ const std::string& name,
+ const base::DictionaryValue* object) {
+ std::unique_ptr<base::DictionaryValue> exception(new base::DictionaryValue());
+
+ std::string setting_string =
+ content_settings::ContentSettingToString(CONTENT_SETTING_DEFAULT);
+ DCHECK(!setting_string.empty());
+
+ exception->SetString(site_settings::kSetting, setting_string);
+ exception->SetString(site_settings::kOrigin, requesting_origin.spec());
+ exception->SetString(site_settings::kDisplayName, requesting_origin.spec());
+ exception->SetString(
+ site_settings::kEmbeddingOrigin, embedding_origin.spec());
+ exception->SetString(site_settings::kSource, provider_name);
+ exception->SetBoolean(kIncognito, incognito);
+ if (object) {
+ exception->SetString(kObjectName, name);
+ exception->Set(kObject, object->CreateDeepCopy());
+ }
+ return exception;
+}
+
+void GetChooserExceptionsFromProfile(
+ Profile* profile,
+ bool incognito,
+ const ChooserTypeNameEntry& chooser_type,
+ base::ListValue* exceptions) {
+ if (incognito) {
+ if (!profile->HasOffTheRecordProfile())
+ return;
+ profile = profile->GetOffTheRecordProfile();
+ }
+
+ ChooserContextBase* chooser_context = chooser_type.get_context(profile);
+ std::vector<std::unique_ptr<ChooserContextBase::Object>> objects =
+ chooser_context->GetAllGrantedObjects();
+ AllOriginObjects all_origin_objects;
+ for (const auto& object : objects) {
+ std::string name;
+ bool found = object->object.GetString(chooser_type.ui_name_key, &name);
+ DCHECK(found);
+ // It is safe for this structure to hold references into |objects| because
+ // they are both destroyed at the end of this function.
+ all_origin_objects[make_pair(object->requesting_origin,
+ object->source)][object->embedding_origin]
+ .insert(make_pair(name, &object->object));
+ }
+
+ // Keep the exceptions sorted by provider so they will be displayed in
+ // precedence order.
+ std::vector<std::unique_ptr<base::DictionaryValue>>
+ all_provider_exceptions[HostContentSettingsMap::NUM_PROVIDER_TYPES];
+
+ for (const auto& all_origin_objects_entry : all_origin_objects) {
+ const GURL& requesting_origin = all_origin_objects_entry.first.first;
+ const std::string& source = all_origin_objects_entry.first.second;
+ const OneOriginObjects& one_origin_objects =
+ all_origin_objects_entry.second;
+
+ auto& this_provider_exceptions = all_provider_exceptions
+ [HostContentSettingsMap::GetProviderTypeFromSource(source)];
+
+ // Add entries for any non-embedded origins.
+ bool has_embedded_entries = false;
+ for (const auto& one_origin_objects_entry : one_origin_objects) {
+ const GURL& embedding_origin = one_origin_objects_entry.first;
+ const SortedObjects& sorted_objects = one_origin_objects_entry.second;
+
+ // Skip the embedded settings which will be added below.
+ if (requesting_origin != embedding_origin) {
+ has_embedded_entries = true;
+ continue;
+ }
+
+ for (const auto& sorted_objects_entry : sorted_objects) {
+ this_provider_exceptions.push_back(GetChooserExceptionForPage(
+ requesting_origin, embedding_origin, source, incognito,
+ sorted_objects_entry.first,
+ sorted_objects_entry.second));
+ }
+ }
+
+ if (has_embedded_entries) {
+ // Add a "parent" entry that simply acts as a heading for all entries
+ // where |requesting_origin| has been embedded.
+ this_provider_exceptions.push_back(
+ GetChooserExceptionForPage(requesting_origin, requesting_origin,
+ source, incognito, std::string(),
+ nullptr));
+
+ // Add the "children" for any embedded settings.
+ for (const auto& one_origin_objects_entry : one_origin_objects) {
+ const GURL& embedding_origin = one_origin_objects_entry.first;
+ const SortedObjects& sorted_objects = one_origin_objects_entry.second;
+
+ // Skip the non-embedded setting which we already added above.
+ if (requesting_origin == embedding_origin)
+ continue;
+
+ for (const auto& sorted_objects_entry : sorted_objects) {
+ this_provider_exceptions.push_back(GetChooserExceptionForPage(
+ requesting_origin, embedding_origin, source, incognito,
+ sorted_objects_entry.first, sorted_objects_entry.second));
+ }
+ }
+ }
+ }
+
+ for (auto& one_provider_exceptions : all_provider_exceptions) {
+ for (auto& exception : one_provider_exceptions)
+ exceptions->Append(std::move(exception));
+ }
+}
+
+} // namespace site_settings
diff --git a/chromium/chrome/browser/ui/webui/site_settings_helper.h b/chromium/chrome/browser/ui/webui/site_settings_helper.h
new file mode 100644
index 00000000000..bc3b51aaa76
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/site_settings_helper.h
@@ -0,0 +1,141 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SITE_SETTINGS_HELPER_H_
+#define CHROME_BROWSER_UI_WEBUI_SITE_SETTINGS_HELPER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/content_settings_pattern.h"
+#include "components/content_settings/core/common/content_settings_types.h"
+#include "content/public/browser/web_ui.h"
+#include "extensions/common/extension.h"
+
+class ChooserContextBase;
+class HostContentSettingsMap;
+class Profile;
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+namespace extensions {
+class ExtensionRegistry;
+}
+
+namespace site_settings {
+
+// Maps from a secondary pattern to a setting.
+typedef std::map<ContentSettingsPattern, ContentSetting>
+ OnePatternSettings;
+// Maps from a primary pattern/source pair to a OnePatternSettings. All the
+// mappings in OnePatternSettings share the given primary pattern and source.
+typedef std::map<std::pair<ContentSettingsPattern, std::string>,
+ OnePatternSettings>
+ AllPatternsSettings;
+
+extern const char kSetting[];
+extern const char kOrigin[];
+extern const char kDisplayName[];
+extern const char kOriginForFavicon[];
+extern const char kPolicyProviderId[];
+extern const char kSource[];
+extern const char kIncognito[];
+extern const char kEmbeddingOrigin[];
+extern const char kPreferencesSource[];
+
+// Group types.
+extern const char kGroupTypeUsb[];
+
+// Returns whether a group name has been registered for the given type.
+bool HasRegisteredGroupName(ContentSettingsType type);
+
+// Gets a content settings type from the group name identifier.
+ContentSettingsType ContentSettingsTypeFromGroupName(const std::string& name);
+
+// Gets a string identifier for the group name.
+std::string ContentSettingsTypeToGroupName(ContentSettingsType type);
+
+// Helper function to construct a dictonary for an exception.
+std::unique_ptr<base::DictionaryValue> GetExceptionForPage(
+ const ContentSettingsPattern& pattern,
+ const ContentSettingsPattern& secondary_pattern,
+ const std::string& display_name,
+ const ContentSetting& setting,
+ const std::string& provider_name,
+ bool incognito);
+
+// Helper function to construct a dictonary for a hosted app exception.
+void AddExceptionForHostedApp(const std::string& url_pattern,
+ const extensions::Extension& app, base::ListValue* exceptions);
+
+// Fills in |exceptions| with Values for the given |type| from |map|.
+// If |filter| is not null then only exceptions with matching primary patterns
+// will be returned.
+void GetExceptionsFromHostContentSettingsMap(
+ const HostContentSettingsMap* map,
+ ContentSettingsType type,
+ const extensions::ExtensionRegistry* extension_registry,
+ content::WebUI* web_ui,
+ bool incognito,
+ const std::string* filter,
+ base::ListValue* exceptions);
+
+// Fills in object saying what the current settings is for the category (such as
+// enabled or blocked) and the source of that setting (such preference, policy,
+// or extension).
+void GetContentCategorySetting(
+ const HostContentSettingsMap* map,
+ ContentSettingsType content_type,
+ base::DictionaryValue* object);
+
+// Returns exceptions constructed from the policy-set allowed URLs
+// for the content settings |type| mic or camera.
+void GetPolicyAllowedUrls(
+ ContentSettingsType type,
+ std::vector<std::unique_ptr<base::DictionaryValue>>* exceptions,
+ const extensions::ExtensionRegistry* extension_registry,
+ content::WebUI* web_ui,
+ bool incognito);
+
+// This struct facilitates lookup of a chooser context factory function by name
+// for a given content settings type and is declared early so that it can used
+// by functions below.
+struct ChooserTypeNameEntry {
+ ContentSettingsType type;
+ ChooserContextBase* (*get_context)(Profile*);
+ const char* name;
+ const char* ui_name_key;
+};
+
+ChooserContextBase* GetUsbChooserContext(Profile* profile);
+
+struct ContentSettingsTypeNameEntry {
+ ContentSettingsType type;
+ const char* name;
+};
+
+const ChooserTypeNameEntry kChooserTypeGroupNames[] = {
+ {CONTENT_SETTINGS_TYPE_USB_CHOOSER_DATA, &GetUsbChooserContext,
+ kGroupTypeUsb, "name"},
+};
+
+const ChooserTypeNameEntry* ChooserTypeFromGroupName(const std::string& name);
+
+// Fills in |exceptions| with Values for the given |chooser_type| from map.
+void GetChooserExceptionsFromProfile(
+ Profile* profile,
+ bool incognito,
+ const ChooserTypeNameEntry& chooser_type,
+ base::ListValue* exceptions);
+
+} // namespace site_settings
+
+#endif // CHROME_BROWSER_UI_WEBUI_SITE_SETTINGS_HELPER_H_
diff --git a/chromium/chrome/browser/ui/webui/site_settings_helper_unittest.cc b/chromium/chrome/browser/ui/webui/site_settings_helper_unittest.cc
new file mode 100644
index 00000000000..a241f35e3e4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/site_settings_helper_unittest.cc
@@ -0,0 +1,115 @@
+// 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 "base/memory/ptr_util.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/ui/webui/site_settings_helper.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/test/content_settings_mock_provider.h"
+#include "components/content_settings/core/test/content_settings_test_utils.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace site_settings {
+
+namespace {
+const ContentSettingsType kContentType = CONTENT_SETTINGS_TYPE_GEOLOCATION;
+}
+
+class SiteSettingsHelperTest : public testing::Test {
+ public:
+ void VerifySetting(const base::ListValue& exceptions,
+ int index,
+ const std::string& pattern,
+ const std::string& setting) {
+ const base::DictionaryValue* dict;
+ exceptions.GetDictionary(index, &dict);
+ std::string actual_pattern;
+ dict->GetString("origin", &actual_pattern);
+ EXPECT_EQ(pattern, actual_pattern);
+ std::string actual_display_name;
+ dict->GetString("displayName", &actual_display_name);
+ EXPECT_EQ(pattern, actual_display_name);
+ std::string actual_setting;
+ dict->GetString("setting", &actual_setting);
+ EXPECT_EQ(setting, actual_setting);
+ }
+
+ void AddSetting(HostContentSettingsMap* map,
+ const std::string& pattern,
+ ContentSetting setting) {
+ map->SetContentSettingCustomScope(
+ ContentSettingsPattern::FromString(pattern),
+ ContentSettingsPattern::Wildcard(), kContentType, std::string(),
+ setting);
+ }
+
+ private:
+ content::TestBrowserThreadBundle thread_bundle_;
+};
+
+TEST_F(SiteSettingsHelperTest, CheckExceptionOrder) {
+ TestingProfile profile;
+ HostContentSettingsMap* map =
+ HostContentSettingsMapFactory::GetForProfile(&profile);
+
+ base::ListValue exceptions;
+ // Check that the initial state of the map is empty.
+ site_settings::GetExceptionsFromHostContentSettingsMap(
+ map, kContentType, /*extension_registry=*/nullptr, /*web_ui=*/nullptr,
+ /*incognito=*/false, /*filter=*/nullptr, &exceptions);
+ EXPECT_EQ(0u, exceptions.GetSize());
+
+ map->SetDefaultContentSetting(kContentType, CONTENT_SETTING_ALLOW);
+
+ // Add a policy exception.
+ auto policy_provider = base::MakeUnique<content_settings::MockProvider>();
+ policy_provider->SetWebsiteSetting(
+ ContentSettingsPattern::FromString("http://[*.]google.com"),
+ ContentSettingsPattern::Wildcard(), kContentType, "",
+ new base::Value(CONTENT_SETTING_BLOCK));
+ policy_provider->set_read_only(true);
+ content_settings::TestUtils::OverrideProvider(
+ map, std::move(policy_provider), HostContentSettingsMap::POLICY_PROVIDER);
+
+ // Add user preferences.
+ AddSetting(map, "http://*", CONTENT_SETTING_BLOCK);
+ AddSetting(map, "http://maps.google.com", CONTENT_SETTING_BLOCK);
+ AddSetting(map, "http://[*.]google.com", CONTENT_SETTING_ALLOW);
+
+ // Add an extension exception.
+ auto extension_provider = base::MakeUnique<content_settings::MockProvider>();
+ extension_provider->SetWebsiteSetting(
+ ContentSettingsPattern::FromString("http://drive.google.com"),
+ ContentSettingsPattern::Wildcard(), kContentType, "",
+ new base::Value(CONTENT_SETTING_ASK));
+ extension_provider->set_read_only(true);
+ content_settings::TestUtils::OverrideProvider(
+ map, std::move(extension_provider),
+ HostContentSettingsMap::CUSTOM_EXTENSION_PROVIDER);
+
+ exceptions.Clear();
+ site_settings::GetExceptionsFromHostContentSettingsMap(
+ map, kContentType, /*extension_registry=*/nullptr, /*web_ui=*/nullptr,
+ /*incognito=*/false, /*filter=*/nullptr, &exceptions);
+
+ EXPECT_EQ(5u, exceptions.GetSize());
+
+ // The policy exception should be returned first, the extension exception
+ // second and pref exceptions afterwards.
+ // The default content setting should not be returned.
+ int i = 0;
+ // From policy provider:
+ VerifySetting(exceptions, i++, "http://[*.]google.com", "block");
+ // From extension provider:
+ VerifySetting(exceptions, i++, "http://drive.google.com", "ask");
+ // From user preferences:
+ VerifySetting(exceptions, i++, "http://maps.google.com", "block");
+ VerifySetting(exceptions, i++, "http://[*.]google.com", "allow");
+ VerifySetting(exceptions, i++, "http://*", "block");
+}
+
+} // namespace site_settings
diff --git a/chromium/chrome/browser/ui/webui/snippets_internals_message_handler.cc b/chromium/chrome/browser/ui/webui/snippets_internals_message_handler.cc
new file mode 100644
index 00000000000..703d28073dc
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/snippets_internals_message_handler.cc
@@ -0,0 +1,520 @@
+// 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 "chrome/browser/ui/webui/snippets_internals_message_handler.h"
+
+#include <memory>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/feature_list.h"
+#include "base/i18n/time_formatting.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/field_trial.h"
+#include "base/optional.h"
+#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/values.h"
+#include "chrome/browser/android/chrome_feature_list.h"
+#include "chrome/browser/ntp_snippets/content_suggestions_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_features.h"
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/category_info.h"
+#include "components/ntp_snippets/category_rankers/category_ranker.h"
+#include "components/ntp_snippets/features.h"
+#include "components/ntp_snippets/pref_names.h"
+#include "components/ntp_snippets/remote/remote_suggestions_fetcher.h"
+#include "components/ntp_snippets/remote/remote_suggestions_provider.h"
+#include "components/ntp_snippets/switches.h"
+#include "components/offline_pages/core/offline_page_feature.h"
+#include "components/prefs/pref_service.h"
+#include "components/variations/variations_associated_data.h"
+#include "content/public/browser/web_ui.h"
+
+using ntp_snippets::ContentSuggestion;
+using ntp_snippets::Category;
+using ntp_snippets::CategoryInfo;
+using ntp_snippets::CategoryStatus;
+using ntp_snippets::KnownCategories;
+using ntp_snippets::RemoteSuggestionsProvider;
+using ntp_snippets::UserClassifier;
+
+namespace {
+
+std::unique_ptr<base::DictionaryValue> PrepareSuggestion(
+ const ContentSuggestion& suggestion,
+ int index) {
+ auto entry = base::MakeUnique<base::DictionaryValue>();
+ entry->SetString("idWithinCategory", suggestion.id().id_within_category());
+ entry->SetString("url", suggestion.url().spec());
+ entry->SetString("urlWithFavicon", suggestion.url_with_favicon().spec());
+ entry->SetString("title", suggestion.title());
+ entry->SetString("snippetText", suggestion.snippet_text());
+ entry->SetString("publishDate",
+ TimeFormatShortDateAndTime(suggestion.publish_date()));
+ entry->SetString("fetchDate",
+ TimeFormatShortDateAndTime(suggestion.fetch_date()));
+ entry->SetString("publisherName", suggestion.publisher_name());
+ entry->SetString("id", "content-suggestion-" + base::IntToString(index));
+ entry->SetDouble("score", suggestion.score());
+
+ if (suggestion.download_suggestion_extra()) {
+ const auto& extra = *suggestion.download_suggestion_extra();
+ auto value = base::MakeUnique<base::DictionaryValue>();
+ value->SetString("downloadGUID", extra.download_guid);
+ value->SetString("targetFilePath",
+ extra.target_file_path.LossyDisplayName());
+ value->SetString("mimeType", extra.mime_type);
+ value->SetString(
+ "offlinePageID",
+ base::StringPrintf("0x%016llx", static_cast<long long unsigned int>(
+ extra.offline_page_id)));
+ value->SetBoolean("isDownloadAsset", extra.is_download_asset);
+ entry->Set("downloadSuggestionExtra", std::move(value));
+ }
+
+ if (suggestion.recent_tab_suggestion_extra()) {
+ const auto& extra = *suggestion.recent_tab_suggestion_extra();
+ auto value = base::MakeUnique<base::DictionaryValue>();
+ value->SetInteger("tabID", extra.tab_id);
+ value->SetString(
+ "offlinePageID",
+ base::StringPrintf("0x%016llx", static_cast<long long unsigned int>(
+ extra.offline_page_id)));
+ entry->Set("recentTabSuggestionExtra", std::move(value));
+ }
+
+ if (suggestion.notification_extra()) {
+ const auto& extra = *suggestion.notification_extra();
+ auto value = base::MakeUnique<base::DictionaryValue>();
+ value->SetString("deadline", TimeFormatShortDateAndTime(extra.deadline));
+ entry->Set("notificationExtra", std::move(value));
+ }
+
+ return entry;
+}
+
+std::string GetCategoryStatusName(CategoryStatus status) {
+ switch (status) {
+ case CategoryStatus::INITIALIZING:
+ return "INITIALIZING";
+ case CategoryStatus::AVAILABLE:
+ return "AVAILABLE";
+ case CategoryStatus::AVAILABLE_LOADING:
+ return "AVAILABLE_LOADING";
+ case CategoryStatus::NOT_PROVIDED:
+ return "NOT_PROVIDED";
+ case CategoryStatus::ALL_SUGGESTIONS_EXPLICITLY_DISABLED:
+ return "ALL_SUGGESTIONS_EXPLICITLY_DISABLED";
+ case CategoryStatus::CATEGORY_EXPLICITLY_DISABLED:
+ return "CATEGORY_EXPLICITLY_DISABLED";
+ case CategoryStatus::LOADING_ERROR:
+ return "LOADING_ERROR";
+ }
+ return std::string();
+}
+
+std::set<variations::VariationID> SnippetsExperiments() {
+ std::set<variations::VariationID> result;
+ for (const base::Feature* const* feature = ntp_snippets::kAllFeatures;
+ *feature; ++feature) {
+ base::FieldTrial* trial = base::FeatureList::GetFieldTrial(**feature);
+ if (!trial) {
+ continue;
+ }
+ if (trial->GetGroupNameWithoutActivation().empty()) {
+ continue;
+ }
+ for (variations::IDCollectionKey key :
+ {variations::GOOGLE_WEB_PROPERTIES,
+ variations::GOOGLE_WEB_PROPERTIES_SIGNED_IN,
+ variations::GOOGLE_WEB_PROPERTIES_TRIGGER}) {
+ const variations::VariationID id = variations::GetGoogleVariationID(
+ key, trial->trial_name(), trial->group_name());
+ if (id != variations::EMPTY_ID) {
+ result.insert(id);
+ }
+ }
+ }
+ return result;
+}
+
+} // namespace
+
+SnippetsInternalsMessageHandler::SnippetsInternalsMessageHandler(
+ ntp_snippets::ContentSuggestionsService* content_suggestions_service,
+ PrefService* pref_service)
+ : content_suggestions_service_observer_(this),
+ dom_loaded_(false),
+ content_suggestions_service_(content_suggestions_service),
+ remote_suggestions_provider_(
+ content_suggestions_service_
+ ->remote_suggestions_provider_for_debugging()),
+ pref_service_(pref_service),
+ weak_ptr_factory_(this) {}
+
+SnippetsInternalsMessageHandler::~SnippetsInternalsMessageHandler() {}
+
+void SnippetsInternalsMessageHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "clearCachedSuggestions",
+ base::Bind(&SnippetsInternalsMessageHandler::HandleClearCachedSuggestions,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "clearClassification",
+ base::Bind(&SnippetsInternalsMessageHandler::ClearClassification,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "clearDismissedSuggestions",
+ base::Bind(
+ &SnippetsInternalsMessageHandler::HandleClearDismissedSuggestions,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "download", base::Bind(&SnippetsInternalsMessageHandler::HandleDownload,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "fetchRemoteSuggestionsInTheBackground",
+ base::Bind(&SnippetsInternalsMessageHandler::
+ FetchRemoteSuggestionsInTheBackground,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "refreshContent",
+ base::Bind(&SnippetsInternalsMessageHandler::HandleRefreshContent,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ "toggleDismissedSuggestions",
+ base::Bind(
+ &SnippetsInternalsMessageHandler::HandleToggleDismissedSuggestions,
+ base::Unretained(this)));
+
+ content_suggestions_service_observer_.Add(content_suggestions_service_);
+}
+
+void SnippetsInternalsMessageHandler::OnNewSuggestions(Category category) {
+ if (!dom_loaded_) {
+ return;
+ }
+ SendContentSuggestions();
+}
+
+void SnippetsInternalsMessageHandler::OnCategoryStatusChanged(
+ Category category,
+ CategoryStatus new_status) {
+ if (!dom_loaded_) {
+ return;
+ }
+ SendContentSuggestions();
+}
+
+void SnippetsInternalsMessageHandler::OnSuggestionInvalidated(
+ const ntp_snippets::ContentSuggestion::ID& suggestion_id) {
+ if (!dom_loaded_) {
+ return;
+ }
+ SendContentSuggestions();
+}
+
+void SnippetsInternalsMessageHandler::OnFullRefreshRequired() {
+ if (!dom_loaded_) {
+ return;
+ }
+ SendContentSuggestions();
+}
+
+void SnippetsInternalsMessageHandler::ContentSuggestionsServiceShutdown() {}
+
+void SnippetsInternalsMessageHandler::HandleRefreshContent(
+ const base::ListValue* args) {
+ DCHECK_EQ(0u, args->GetSize());
+
+ dom_loaded_ = true;
+
+ for (std::pair<const Category, DismissedState>& category_state_pair :
+ dismissed_state_) {
+ if (category_state_pair.second == DismissedState::VISIBLE) {
+ category_state_pair.second = DismissedState::LOADING;
+ content_suggestions_service_->GetDismissedSuggestionsForDebugging(
+ category_state_pair.first,
+ base::Bind(
+ &SnippetsInternalsMessageHandler::OnDismissedSuggestionsLoaded,
+ weak_ptr_factory_.GetWeakPtr(), category_state_pair.first));
+ }
+ }
+
+ SendAllContent();
+}
+
+void SnippetsInternalsMessageHandler::HandleDownload(
+ const base::ListValue* args) {
+ DCHECK_EQ(0u, args->GetSize());
+
+ SendString("remote-status", std::string());
+
+ if (!remote_suggestions_provider_) {
+ return;
+ }
+
+ remote_suggestions_provider_->ReloadSuggestions();
+}
+
+void SnippetsInternalsMessageHandler::HandleClearCachedSuggestions(
+ const base::ListValue* args) {
+ DCHECK_EQ(1u, args->GetSize());
+
+ int category_id;
+ if (!args->GetInteger(0, &category_id)) {
+ return;
+ }
+
+ content_suggestions_service_->ClearCachedSuggestions(
+ Category::FromIDValue(category_id));
+ SendContentSuggestions();
+}
+
+void SnippetsInternalsMessageHandler::HandleClearDismissedSuggestions(
+ const base::ListValue* args) {
+ DCHECK_EQ(1u, args->GetSize());
+
+ int category_id;
+ if (!args->GetInteger(0, &category_id)) {
+ return;
+ }
+
+ Category category = Category::FromIDValue(category_id);
+ content_suggestions_service_->ClearDismissedSuggestionsForDebugging(category);
+ SendContentSuggestions();
+ dismissed_state_[category] = DismissedState::LOADING;
+ content_suggestions_service_->GetDismissedSuggestionsForDebugging(
+ category,
+ base::Bind(&SnippetsInternalsMessageHandler::OnDismissedSuggestionsLoaded,
+ weak_ptr_factory_.GetWeakPtr(), category));
+}
+
+void SnippetsInternalsMessageHandler::HandleToggleDismissedSuggestions(
+ const base::ListValue* args) {
+ DCHECK_EQ(2u, args->GetSize());
+
+ int category_id;
+ if (!args->GetInteger(0, &category_id)) {
+ return;
+ }
+ bool dismissed_visible;
+ if (!args->GetBoolean(1, &dismissed_visible)) {
+ return;
+ }
+
+ Category category = Category::FromIDValue(category_id);
+ if (dismissed_visible) {
+ dismissed_state_[category] = DismissedState::LOADING;
+ content_suggestions_service_->GetDismissedSuggestionsForDebugging(
+ category,
+ base::Bind(
+ &SnippetsInternalsMessageHandler::OnDismissedSuggestionsLoaded,
+ weak_ptr_factory_.GetWeakPtr(), category));
+ } else {
+ dismissed_state_[category] = DismissedState::HIDDEN;
+ dismissed_suggestions_[category].clear();
+ }
+}
+
+void SnippetsInternalsMessageHandler::ClearClassification(
+ const base::ListValue* args) {
+ DCHECK_EQ(0u, args->GetSize());
+ content_suggestions_service_->user_classifier()
+ ->ClearClassificationForDebugging();
+ SendClassification();
+}
+
+void SnippetsInternalsMessageHandler::FetchRemoteSuggestionsInTheBackground(
+ const base::ListValue* args) {
+ DCHECK_EQ(0u, args->GetSize());
+ remote_suggestions_provider_->RefetchInTheBackground(
+ RemoteSuggestionsProvider::FetchStatusCallback());
+}
+
+void SnippetsInternalsMessageHandler::SendAllContent() {
+ SendBoolean(
+ "flag-article-suggestions",
+ base::FeatureList::IsEnabled(ntp_snippets::kArticleSuggestionsFeature));
+ SendBoolean("flag-recent-offline-tab-suggestions",
+ base::FeatureList::IsEnabled(
+ ntp_snippets::kRecentOfflineTabSuggestionsFeature));
+ SendBoolean("flag-offlining-recent-pages-feature",
+ base::FeatureList::IsEnabled(
+ offline_pages::kOffliningRecentPagesFeature));
+ SendBoolean(
+ "flag-asset-download-suggestions",
+ base::FeatureList::IsEnabled(features::kAssetDownloadSuggestionsFeature));
+ SendBoolean("flag-offline-page-download-suggestions",
+ base::FeatureList::IsEnabled(
+ features::kOfflinePageDownloadSuggestionsFeature));
+ SendBoolean(
+ "flag-bookmark-suggestions",
+ base::FeatureList::IsEnabled(ntp_snippets::kBookmarkSuggestionsFeature));
+
+ SendBoolean("flag-physical-web-page-suggestions",
+ base::FeatureList::IsEnabled(
+ ntp_snippets::kPhysicalWebPageSuggestionsFeature));
+
+ SendBoolean("flag-physical-web", base::FeatureList::IsEnabled(
+ chrome::android::kPhysicalWebFeature));
+
+ SendClassification();
+ SendRankerDebugData();
+ SendLastRemoteSuggestionsBackgroundFetchTime();
+
+ if (remote_suggestions_provider_) {
+ const ntp_snippets::RemoteSuggestionsFetcher* fetcher =
+ remote_suggestions_provider_->suggestions_fetcher_for_debugging();
+ SendString("switch-fetch-url", fetcher->fetch_url().spec());
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "chrome.SnippetsInternals.receiveJson",
+ base::Value(fetcher->last_json()));
+ }
+
+ std::set<variations::VariationID> ids = SnippetsExperiments();
+ std::vector<std::string> string_ids;
+ for (auto id : ids) {
+ string_ids.push_back(base::IntToString(id));
+ }
+ SendString("experiment-ids", base::JoinString(string_ids, ", "));
+
+ SendContentSuggestions();
+}
+
+void SnippetsInternalsMessageHandler::SendClassification() {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "chrome.SnippetsInternals.receiveClassification",
+ base::Value(content_suggestions_service_->user_classifier()
+ ->GetUserClassDescriptionForDebugging()),
+ base::Value(
+ content_suggestions_service_->user_classifier()->GetEstimatedAvgTime(
+ UserClassifier::Metric::NTP_OPENED)),
+ base::Value(
+ content_suggestions_service_->user_classifier()->GetEstimatedAvgTime(
+ UserClassifier::Metric::SUGGESTIONS_SHOWN)),
+ base::Value(
+ content_suggestions_service_->user_classifier()->GetEstimatedAvgTime(
+ UserClassifier::Metric::SUGGESTIONS_USED)));
+}
+
+void SnippetsInternalsMessageHandler::SendRankerDebugData() {
+ std::vector<ntp_snippets::CategoryRanker::DebugDataItem> data =
+ content_suggestions_service_->category_ranker()->GetDebugData();
+
+ std::unique_ptr<base::ListValue> items_list(new base::ListValue);
+ for (const auto& item : data) {
+ auto entry = base::MakeUnique<base::DictionaryValue>();
+ entry->SetString("label", item.label);
+ entry->SetString("content", item.content);
+ items_list->Append(std::move(entry));
+ }
+
+ base::DictionaryValue result;
+ result.Set("list", std::move(items_list));
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "chrome.SnippetsInternals.receiveRankerDebugData", result);
+}
+
+void SnippetsInternalsMessageHandler::
+ SendLastRemoteSuggestionsBackgroundFetchTime() {
+ base::Time time = base::Time::FromInternalValue(pref_service_->GetInt64(
+ ntp_snippets::prefs::kLastSuccessfulBackgroundFetchTime));
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "chrome.SnippetsInternals."
+ "receiveLastRemoteSuggestionsBackgroundFetchTime",
+ base::Value(base::TimeFormatShortDateAndTime(time)));
+}
+
+void SnippetsInternalsMessageHandler::SendContentSuggestions() {
+ std::unique_ptr<base::ListValue> categories_list(new base::ListValue);
+
+ int index = 0;
+ for (Category category : content_suggestions_service_->GetCategories()) {
+ CategoryStatus status =
+ content_suggestions_service_->GetCategoryStatus(category);
+ base::Optional<CategoryInfo> info =
+ content_suggestions_service_->GetCategoryInfo(category);
+ DCHECK(info);
+ const std::vector<ContentSuggestion>& suggestions =
+ content_suggestions_service_->GetSuggestionsForCategory(category);
+
+ std::unique_ptr<base::ListValue> suggestions_list(new base::ListValue);
+ for (const ContentSuggestion& suggestion : suggestions) {
+ suggestions_list->Append(PrepareSuggestion(suggestion, index++));
+ }
+
+ std::unique_ptr<base::ListValue> dismissed_list(new base::ListValue);
+ for (const ContentSuggestion& suggestion :
+ dismissed_suggestions_[category]) {
+ dismissed_list->Append(PrepareSuggestion(suggestion, index++));
+ }
+
+ std::unique_ptr<base::DictionaryValue> category_entry(
+ new base::DictionaryValue);
+ category_entry->SetInteger("categoryId", category.id());
+ category_entry->SetString(
+ "dismissedContainerId",
+ "dismissed-suggestions-" + base::IntToString(category.id()));
+ category_entry->SetString("title", info->title());
+ category_entry->SetString("status", GetCategoryStatusName(status));
+ category_entry->Set("suggestions", std::move(suggestions_list));
+ category_entry->Set("dismissedSuggestions", std::move(dismissed_list));
+ categories_list->Append(std::move(category_entry));
+ }
+
+ if (remote_suggestions_provider_) {
+ const std::string& status =
+ remote_suggestions_provider_->suggestions_fetcher_for_debugging()
+ ->last_status();
+ if (!status.empty()) {
+ SendString("remote-status", "Finished: " + status);
+ }
+ }
+
+ base::DictionaryValue result;
+ result.Set("list", std::move(categories_list));
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "chrome.SnippetsInternals.receiveContentSuggestions", result);
+}
+
+void SnippetsInternalsMessageHandler::SendBoolean(const std::string& name,
+ bool value) {
+ SendString(name, value ? "True" : "False");
+}
+
+void SnippetsInternalsMessageHandler::SendString(const std::string& name,
+ const std::string& value) {
+ base::Value string_name(name);
+ base::Value string_value(value);
+
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "chrome.SnippetsInternals.receiveProperty", string_name, string_value);
+}
+
+void SnippetsInternalsMessageHandler::OnDismissedSuggestionsLoaded(
+ Category category,
+ std::vector<ContentSuggestion> dismissed_suggestions) {
+ if (dismissed_state_[category] == DismissedState::HIDDEN) {
+ return;
+ }
+ dismissed_suggestions_[category] = std::move(dismissed_suggestions);
+ dismissed_state_[category] = DismissedState::VISIBLE;
+ SendContentSuggestions();
+}
diff --git a/chromium/chrome/browser/ui/webui/snippets_internals_message_handler.h b/chromium/chrome/browser/ui/webui/snippets_internals_message_handler.h
new file mode 100644
index 00000000000..965e2d42f90
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/snippets_internals_message_handler.h
@@ -0,0 +1,102 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SNIPPETS_INTERNALS_MESSAGE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SNIPPETS_INTERNALS_MESSAGE_HANDLER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
+#include "components/ntp_snippets/category.h"
+#include "components/ntp_snippets/category_status.h"
+#include "components/ntp_snippets/content_suggestions_service.h"
+#include "components/ntp_snippets/remote/remote_suggestions_provider.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace base {
+class ListValue;
+} // namespace base
+
+namespace ntp_snippets {
+class ContentSuggestionsService;
+} // namespace ntp_snippets
+
+class PrefService;
+
+// The implementation for the chrome://snippets-internals page.
+class SnippetsInternalsMessageHandler
+ : public content::WebUIMessageHandler,
+ public ntp_snippets::ContentSuggestionsService::Observer {
+ public:
+ SnippetsInternalsMessageHandler(
+ ntp_snippets::ContentSuggestionsService* content_suggestions_service,
+ PrefService* pref_service);
+ ~SnippetsInternalsMessageHandler() override;
+
+ private:
+ enum class DismissedState { HIDDEN, LOADING, VISIBLE };
+
+ // content::WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ // ntp_snippets::ContentSuggestionsService::Observer:
+ void OnNewSuggestions(ntp_snippets::Category category) override;
+ void OnCategoryStatusChanged(
+ ntp_snippets::Category category,
+ ntp_snippets::CategoryStatus new_status) override;
+ void OnSuggestionInvalidated(
+ const ntp_snippets::ContentSuggestion::ID& suggestion_id) override;
+ void OnFullRefreshRequired() override;
+ void ContentSuggestionsServiceShutdown() override;
+
+ void HandleRefreshContent(const base::ListValue* args);
+ void HandleDownload(const base::ListValue* args);
+ void HandleClearCachedSuggestions(const base::ListValue* args);
+ void HandleClearDismissedSuggestions(const base::ListValue* args);
+ void HandleToggleDismissedSuggestions(const base::ListValue* args);
+ void ClearClassification(const base::ListValue* args);
+ void FetchRemoteSuggestionsInTheBackground(const base::ListValue* args);
+
+ void SendAllContent();
+ void SendClassification();
+ void SendRankerDebugData();
+ void SendLastRemoteSuggestionsBackgroundFetchTime();
+ void SendContentSuggestions();
+ void SendBoolean(const std::string& name, bool value);
+ void SendString(const std::string& name, const std::string& value);
+
+ void OnDismissedSuggestionsLoaded(
+ ntp_snippets::Category category,
+ std::vector<ntp_snippets::ContentSuggestion> dismissed_suggestions);
+
+ ScopedObserver<ntp_snippets::ContentSuggestionsService,
+ ntp_snippets::ContentSuggestionsService::Observer>
+ content_suggestions_service_observer_;
+
+ // Tracks whether we can already send messages to the page.
+ bool dom_loaded_;
+
+ ntp_snippets::ContentSuggestionsService* content_suggestions_service_;
+ ntp_snippets::RemoteSuggestionsProvider* remote_suggestions_provider_;
+ PrefService* pref_service_;
+
+ std::map<ntp_snippets::Category,
+ DismissedState,
+ ntp_snippets::Category::CompareByID>
+ dismissed_state_;
+ std::map<ntp_snippets::Category,
+ std::vector<ntp_snippets::ContentSuggestion>,
+ ntp_snippets::Category::CompareByID>
+ dismissed_suggestions_;
+
+ base::WeakPtrFactory<SnippetsInternalsMessageHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SnippetsInternalsMessageHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SNIPPETS_INTERNALS_MESSAGE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/snippets_internals_ui.cc b/chromium/chrome/browser/ui/webui/snippets_internals_ui.cc
new file mode 100644
index 00000000000..a8f475d61ff
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/snippets_internals_ui.cc
@@ -0,0 +1,41 @@
+// 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 "chrome/browser/ui/webui/snippets_internals_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/ntp_snippets/content_suggestions_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/snippets_internals_message_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace {
+
+content::WebUIDataSource* CreateSnippetsInternalsHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUISnippetsInternalsHost);
+
+ source->AddResourcePath("snippets_internals.js", IDR_SNIPPETS_INTERNALS_JS);
+ source->AddResourcePath("snippets_internals.css", IDR_SNIPPETS_INTERNALS_CSS);
+ source->SetDefaultResource(IDR_SNIPPETS_INTERNALS_HTML);
+ source->UseGzip(std::unordered_set<std::string>());
+ return source;
+}
+
+} // namespace
+
+SnippetsInternalsUI::SnippetsInternalsUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreateSnippetsInternalsHTMLSource());
+
+ web_ui->AddMessageHandler(base::MakeUnique<SnippetsInternalsMessageHandler>(
+ ContentSuggestionsServiceFactory::GetInstance()->GetForProfile(profile),
+ profile->GetPrefs()));
+}
+
+SnippetsInternalsUI::~SnippetsInternalsUI() {}
diff --git a/chromium/chrome/browser/ui/webui/snippets_internals_ui.h b/chromium/chrome/browser/ui/webui/snippets_internals_ui.h
new file mode 100644
index 00000000000..c6046996d71
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/snippets_internals_ui.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SNIPPETS_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SNIPPETS_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The implementation for the chrome://snippets-internals page.
+class SnippetsInternalsUI : public content::WebUIController {
+ public:
+ explicit SnippetsInternalsUI(content::WebUI* web_ui);
+ ~SnippetsInternalsUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SnippetsInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SNIPPETS_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/supervised_user_internals_message_handler.cc b/chromium/chrome/browser/ui/webui/supervised_user_internals_message_handler.cc
new file mode 100644
index 00000000000..f9ca895383e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/supervised_user_internals_message_handler.cc
@@ -0,0 +1,288 @@
+// 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.
+
+#include "chrome/browser/ui/webui/supervised_user_internals_message_handler.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/account_tracker_service_factory.h"
+#include "chrome/browser/supervised_user/child_accounts/child_account_service.h"
+#include "chrome/browser/supervised_user/supervised_user_service.h"
+#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
+#include "chrome/browser/supervised_user/supervised_user_settings_service.h"
+#include "chrome/browser/supervised_user/supervised_user_settings_service_factory.h"
+#include "chrome/browser/supervised_user/supervised_user_url_filter.h"
+#include "chrome/common/channel_info.h"
+#include "components/signin/core/browser/account_tracker_service.h"
+#include "components/supervised_user_error_page/supervised_user_error_page.h"
+#include "components/url_formatter/url_fixer.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_ui.h"
+
+using content::BrowserThread;
+
+namespace {
+
+// Creates a 'section' for display on about:supervised-user-internals,
+// consisting of a title and a list of fields. Returns a pointer to the new
+// section's contents, for use with |AddSectionEntry| below. Note that
+// |parent_list|, not the caller, owns the newly added section.
+base::ListValue* AddSection(base::ListValue* parent_list,
+ const std::string& title) {
+ std::unique_ptr<base::DictionaryValue> section(new base::DictionaryValue);
+ std::unique_ptr<base::ListValue> section_contents(new base::ListValue);
+ section->SetString("title", title);
+ // Grab a raw pointer to the result before |Pass()|ing it on.
+ base::ListValue* result = section_contents.get();
+ section->Set("data", std::move(section_contents));
+ parent_list->Append(std::move(section));
+ return result;
+}
+
+// Adds a bool entry to a section (created with |AddSection| above).
+void AddSectionEntry(base::ListValue* section_list,
+ const std::string& name,
+ bool value) {
+ std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue);
+ entry->SetString("stat_name", name);
+ entry->SetBoolean("stat_value", value);
+ entry->SetBoolean("is_valid", true);
+ section_list->Append(std::move(entry));
+}
+
+// Adds a string entry to a section (created with |AddSection| above).
+void AddSectionEntry(base::ListValue* section_list,
+ const std::string& name,
+ const std::string& value) {
+ std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue);
+ entry->SetString("stat_name", name);
+ entry->SetString("stat_value", value);
+ entry->SetBoolean("is_valid", true);
+ section_list->Append(std::move(entry));
+}
+
+std::string FilteringBehaviorToString(
+ SupervisedUserURLFilter::FilteringBehavior behavior) {
+ switch (behavior) {
+ case SupervisedUserURLFilter::ALLOW:
+ return "Allow";
+ case SupervisedUserURLFilter::WARN:
+ return "Warn";
+ case SupervisedUserURLFilter::BLOCK:
+ return "Block";
+ case SupervisedUserURLFilter::INVALID:
+ return "Invalid";
+ }
+ return "Unknown";
+}
+
+std::string FilteringBehaviorToString(
+ SupervisedUserURLFilter::FilteringBehavior behavior, bool uncertain) {
+ std::string result = FilteringBehaviorToString(behavior);
+ if (uncertain)
+ result += " (Uncertain)";
+ return result;
+}
+
+std::string FilteringBehaviorReasonToString(
+ supervised_user_error_page::FilteringBehaviorReason reason) {
+ switch (reason) {
+ case supervised_user_error_page::DEFAULT:
+ return "Default";
+ case supervised_user_error_page::ASYNC_CHECKER:
+ return "AsyncChecker";
+ case supervised_user_error_page::BLACKLIST:
+ return "Blacklist";
+ case supervised_user_error_page::MANUAL:
+ return "Manual";
+ case supervised_user_error_page::WHITELIST:
+ return "Whitelist";
+ case supervised_user_error_page::NOT_SIGNED_IN:
+ // Should never happen, only used for requests from WebView
+ NOTREACHED();
+ }
+ return "Unknown/invalid";
+}
+
+} // namespace
+
+SupervisedUserInternalsMessageHandler::SupervisedUserInternalsMessageHandler()
+ : scoped_observer_(this), weak_factory_(this) {}
+
+SupervisedUserInternalsMessageHandler::
+ ~SupervisedUserInternalsMessageHandler() {
+}
+
+void SupervisedUserInternalsMessageHandler::RegisterMessages() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ web_ui()->RegisterMessageCallback("registerForEvents",
+ base::Bind(&SupervisedUserInternalsMessageHandler::
+ HandleRegisterForEvents,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback("getBasicInfo",
+ base::Bind(&SupervisedUserInternalsMessageHandler::HandleGetBasicInfo,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback("tryURL",
+ base::Bind(&SupervisedUserInternalsMessageHandler::HandleTryURL,
+ base::Unretained(this)));
+}
+
+void SupervisedUserInternalsMessageHandler::OnURLFilterChanged() {
+ SendBasicInfo();
+}
+
+SupervisedUserService*
+SupervisedUserInternalsMessageHandler::GetSupervisedUserService() {
+ Profile* profile = Profile::FromWebUI(web_ui());
+ return SupervisedUserServiceFactory::GetForProfile(
+ profile->GetOriginalProfile());
+}
+
+void SupervisedUserInternalsMessageHandler::HandleRegisterForEvents(
+ const base::ListValue* args) {
+ DCHECK(args->empty());
+ if (scoped_observer_.IsObservingSources())
+ return;
+
+ scoped_observer_.Add(GetSupervisedUserService()->GetURLFilter());
+}
+
+void SupervisedUserInternalsMessageHandler::HandleGetBasicInfo(
+ const base::ListValue* args) {
+ SendBasicInfo();
+}
+
+void SupervisedUserInternalsMessageHandler::HandleTryURL(
+ const base::ListValue* args) {
+ DCHECK_EQ(1u, args->GetSize());
+ std::string url_str;
+ if (!args->GetString(0, &url_str))
+ return;
+
+ GURL url = url_formatter::FixupURL(url_str, std::string());
+ if (!url.is_valid())
+ return;
+
+ SupervisedUserURLFilter* filter = GetSupervisedUserService()->GetURLFilter();
+ std::map<std::string, base::string16> whitelists =
+ filter->GetMatchingWhitelistTitles(url);
+ filter->GetFilteringBehaviorForURLWithAsyncChecks(
+ url, base::Bind(&SupervisedUserInternalsMessageHandler::OnTryURLResult,
+ weak_factory_.GetWeakPtr(), whitelists));
+}
+
+void SupervisedUserInternalsMessageHandler::SendBasicInfo() {
+ std::unique_ptr<base::ListValue> section_list(new base::ListValue);
+
+ base::ListValue* section_general = AddSection(section_list.get(), "General");
+ AddSectionEntry(section_general, "Chrome version",
+ chrome::GetVersionString());
+ AddSectionEntry(section_general, "Child detection enabled",
+ ChildAccountService::IsChildAccountDetectionEnabled());
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+
+ base::ListValue* section_profile = AddSection(section_list.get(), "Profile");
+ AddSectionEntry(section_profile, "Account", profile->GetProfileUserName());
+ AddSectionEntry(section_profile, "Legacy Supervised",
+ profile->IsLegacySupervised());
+ AddSectionEntry(section_profile, "Child", profile->IsChild());
+
+ SupervisedUserURLFilter* filter = GetSupervisedUserService()->GetURLFilter();
+
+ base::ListValue* section_filter = AddSection(section_list.get(), "Filter");
+ AddSectionEntry(section_filter, "Blacklist active", filter->HasBlacklist());
+ AddSectionEntry(section_filter, "Online checks active",
+ filter->HasAsyncURLChecker());
+ AddSectionEntry(section_filter, "Default behavior",
+ FilteringBehaviorToString(
+ filter->GetDefaultFilteringBehavior()));
+
+ AccountTrackerService* account_tracker =
+ AccountTrackerServiceFactory::GetForProfile(profile);
+ // |account_tracker| is null in incognito and guest profiles.
+ if (account_tracker) {
+ for (const auto& account: account_tracker->GetAccounts()) {
+ base::ListValue* section_user = AddSection(section_list.get(),
+ "User Information for " + account.full_name);
+ AddSectionEntry(section_user, "Account id", account.account_id);
+ AddSectionEntry(section_user, "Gaia", account.gaia);
+ AddSectionEntry(section_user, "Email", account.email);
+ AddSectionEntry(section_user, "Given name", account.given_name);
+ AddSectionEntry(section_user, "Hosted domain", account.hosted_domain);
+ AddSectionEntry(section_user, "Locale", account.locale);
+ AddSectionEntry(section_user, "Is child", account.is_child_account);
+ AddSectionEntry(section_user, "Is valid", account.IsValid());
+ }
+ }
+
+ base::DictionaryValue result;
+ result.Set("sections", std::move(section_list));
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "chrome.supervised_user_internals.receiveBasicInfo", result);
+
+ // Trigger retrieval of the user settings
+ SupervisedUserSettingsService* settings_service =
+ SupervisedUserSettingsServiceFactory::GetForProfile(profile);
+ user_settings_subscription_ = settings_service->Subscribe(base::Bind(
+ &SupervisedUserInternalsMessageHandler::SendSupervisedUserSettings,
+ weak_factory_.GetWeakPtr()));
+}
+
+void SupervisedUserInternalsMessageHandler::SendSupervisedUserSettings(
+ const base::DictionaryValue* settings) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "chrome.supervised_user_internals.receiveUserSettings",
+ *(settings ? settings : base::MakeUnique<base::Value>().get()));
+}
+
+void SupervisedUserInternalsMessageHandler::OnTryURLResult(
+ const std::map<std::string, base::string16>& whitelists,
+ SupervisedUserURLFilter::FilteringBehavior behavior,
+ supervised_user_error_page::FilteringBehaviorReason reason,
+ bool uncertain) {
+ std::vector<std::string> whitelists_list;
+ for (const auto& whitelist : whitelists) {
+ whitelists_list.push_back(
+ base::StringPrintf("%s: %s", whitelist.first.c_str(),
+ base::UTF16ToUTF8(whitelist.second).c_str()));
+ }
+ std::string whitelists_str = base::JoinString(whitelists_list, "; ");
+ base::DictionaryValue result;
+ result.SetString("allowResult",
+ FilteringBehaviorToString(behavior, uncertain));
+ result.SetBoolean("manual", reason == supervised_user_error_page::MANUAL &&
+ behavior == SupervisedUserURLFilter::ALLOW);
+ result.SetString("whitelists", whitelists_str);
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "chrome.supervised_user_internals.receiveTryURLResult", result);
+}
+
+void SupervisedUserInternalsMessageHandler::OnSiteListUpdated() {}
+
+void SupervisedUserInternalsMessageHandler::OnURLChecked(
+ const GURL& url,
+ SupervisedUserURLFilter::FilteringBehavior behavior,
+ supervised_user_error_page::FilteringBehaviorReason reason,
+ bool uncertain) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ base::DictionaryValue result;
+ result.SetString("url", url.possibly_invalid_spec());
+ result.SetString("result", FilteringBehaviorToString(behavior, uncertain));
+ result.SetString("reason", FilteringBehaviorReasonToString(reason));
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "chrome.supervised_user_internals.receiveFilteringResult", result);
+}
diff --git a/chromium/chrome/browser/ui/webui/supervised_user_internals_message_handler.h b/chromium/chrome/browser/ui/webui/supervised_user_internals_message_handler.h
new file mode 100644
index 00000000000..aa6c3d9b680
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/supervised_user_internals_message_handler.h
@@ -0,0 +1,73 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SUPERVISED_USER_INTERNALS_MESSAGE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SUPERVISED_USER_INTERNALS_MESSAGE_HANDLER_H_
+
+#include "base/callback_list.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/supervised_user/supervised_user_service_observer.h"
+#include "chrome/browser/supervised_user/supervised_user_url_filter.h"
+#include "components/supervised_user_error_page/supervised_user_error_page.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace base {
+class ListValue;
+} // namespace base
+
+class SupervisedUserService;
+
+// The implementation for the chrome://supervised-user-internals page.
+class SupervisedUserInternalsMessageHandler
+ : public content::WebUIMessageHandler,
+ public SupervisedUserServiceObserver,
+ public SupervisedUserURLFilter::Observer {
+ public:
+ SupervisedUserInternalsMessageHandler();
+ ~SupervisedUserInternalsMessageHandler() override;
+
+ private:
+ // content::WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ // SupervisedUserServiceObserver:
+ void OnURLFilterChanged() override;
+
+ SupervisedUserService* GetSupervisedUserService();
+
+ void HandleRegisterForEvents(const base::ListValue* args);
+ void HandleGetBasicInfo(const base::ListValue* args);
+ void HandleTryURL(const base::ListValue* args);
+
+ void SendBasicInfo();
+ void SendSupervisedUserSettings(const base::DictionaryValue* settings);
+
+ void OnTryURLResult(
+ const std::map<std::string, base::string16>& whitelists,
+ SupervisedUserURLFilter::FilteringBehavior behavior,
+ supervised_user_error_page::FilteringBehaviorReason reason,
+ bool uncertain);
+
+ // SupervisedUserURLFilter::Observer:
+ void OnSiteListUpdated() override;
+ void OnURLChecked(const GURL& url,
+ SupervisedUserURLFilter::FilteringBehavior behavior,
+ supervised_user_error_page::FilteringBehaviorReason reason,
+ bool uncertain) override;
+
+ std::unique_ptr<
+ base::CallbackList<void(const base::DictionaryValue*)>::Subscription>
+ user_settings_subscription_;
+
+ ScopedObserver<SupervisedUserURLFilter, SupervisedUserURLFilter::Observer>
+ scoped_observer_;
+
+ base::WeakPtrFactory<SupervisedUserInternalsMessageHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SupervisedUserInternalsMessageHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SUPERVISED_USER_INTERNALS_MESSAGE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/supervised_user_internals_ui.cc b/chromium/chrome/browser/ui/webui/supervised_user_internals_ui.cc
new file mode 100644
index 00000000000..d64411636be
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/supervised_user_internals_ui.cc
@@ -0,0 +1,42 @@
+// 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.
+
+#include "chrome/browser/ui/webui/supervised_user_internals_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/supervised_user_internals_message_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace {
+
+content::WebUIDataSource* CreateSupervisedUserInternalsHTMLSource() {
+ content::WebUIDataSource* source = content::WebUIDataSource::Create(
+ chrome::kChromeUISupervisedUserInternalsHost);
+
+ source->AddResourcePath("supervised_user_internals.js",
+ IDR_SUPERVISED_USER_INTERNALS_JS);
+ source->AddResourcePath("supervised_user_internals.css",
+ IDR_SUPERVISED_USER_INTERNALS_CSS);
+ source->SetDefaultResource(IDR_SUPERVISED_USER_INTERNALS_HTML);
+ source->UseGzip(std::unordered_set<std::string>());
+ return source;
+}
+
+} // namespace
+
+SupervisedUserInternalsUI::SupervisedUserInternalsUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile,
+ CreateSupervisedUserInternalsHTMLSource());
+
+ web_ui->AddMessageHandler(
+ base::MakeUnique<SupervisedUserInternalsMessageHandler>());
+}
+
+SupervisedUserInternalsUI::~SupervisedUserInternalsUI() {}
diff --git a/chromium/chrome/browser/ui/webui/supervised_user_internals_ui.h b/chromium/chrome/browser/ui/webui/supervised_user_internals_ui.h
new file mode 100644
index 00000000000..ff12e938ac0
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/supervised_user_internals_ui.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SUPERVISED_USER_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SUPERVISED_USER_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The implementation for the chrome://supervised-user-internals page.
+class SupervisedUserInternalsUI : public content::WebUIController {
+ public:
+ explicit SupervisedUserInternalsUI(content::WebUI* web_ui);
+ ~SupervisedUserInternalsUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SupervisedUserInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SUPERVISED_USER_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/sync_file_system_internals/DEPS b/chromium/chrome/browser/ui/webui/sync_file_system_internals/DEPS
new file mode 100644
index 00000000000..cdad3c26c29
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_file_system_internals/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+components/drive",
+]
diff --git a/chromium/chrome/browser/ui/webui/sync_file_system_internals/OWNERS b/chromium/chrome/browser/ui/webui/sync_file_system_internals/OWNERS
new file mode 100644
index 00000000000..6bf7d0a8f63
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_file_system_internals/OWNERS
@@ -0,0 +1,7 @@
+calvinlo@chromium.org
+kinuko@chromium.org
+nhiroki@chromium.org
+tzik@chromium.org
+
+# TEAM: storage-dev@chromium.org
+# COMPONENT: Blink>Storage>FileSystem
diff --git a/chromium/chrome/browser/ui/webui/sync_file_system_internals/dump_database_handler.cc b/chromium/chrome/browser/ui/webui/sync_file_system_internals/dump_database_handler.cc
new file mode 100644
index 00000000000..5d2d2dbda6e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_file_system_internals/dump_database_handler.cc
@@ -0,0 +1,44 @@
+// 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/sync_file_system_internals/dump_database_handler.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync_file_system/sync_file_system_service.h"
+#include "chrome/browser/sync_file_system/sync_file_system_service_factory.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+using sync_file_system::SyncFileSystemServiceFactory;
+
+namespace syncfs_internals {
+
+DumpDatabaseHandler::DumpDatabaseHandler(Profile* profile)
+ : profile_(profile) {}
+DumpDatabaseHandler::~DumpDatabaseHandler() {}
+
+void DumpDatabaseHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "getDatabaseDump",
+ base::Bind(&DumpDatabaseHandler::GetDatabaseDump,
+ base::Unretained(this)));
+}
+
+void DumpDatabaseHandler::GetDatabaseDump(const base::ListValue*) {
+ std::unique_ptr<base::ListValue> list;
+ sync_file_system::SyncFileSystemService* sync_service =
+ SyncFileSystemServiceFactory::GetForProfile(profile_);
+ if (sync_service) {
+ sync_service->DumpDatabase(
+ base::Bind(&DumpDatabaseHandler::DidGetDatabaseDump,
+ base::Unretained(this)));
+ }
+}
+
+void DumpDatabaseHandler::DidGetDatabaseDump(const base::ListValue& list) {
+ web_ui()->CallJavascriptFunctionUnsafe("DumpDatabase.onGetDatabaseDump",
+ list);
+}
+
+} // namespace syncfs_internals
diff --git a/chromium/chrome/browser/ui/webui/sync_file_system_internals/dump_database_handler.h b/chromium/chrome/browser/ui/webui/sync_file_system_internals/dump_database_handler.h
new file mode 100644
index 00000000000..d59091314dc
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_file_system_internals/dump_database_handler.h
@@ -0,0 +1,35 @@
+// 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_SYNC_FILE_SYSTEM_INTERNALS_DUMP_DATABASE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SYNC_FILE_SYSTEM_INTERNALS_DUMP_DATABASE_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+class Profile;
+
+namespace syncfs_internals {
+
+class DumpDatabaseHandler : public content::WebUIMessageHandler {
+ public:
+ explicit DumpDatabaseHandler(Profile* profile);
+ ~DumpDatabaseHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ void GetDatabaseDump(const base::ListValue* args);
+ void DidGetDatabaseDump(const base::ListValue& list);
+
+ Profile* profile_;
+
+ DISALLOW_COPY_AND_ASSIGN(DumpDatabaseHandler);
+};
+
+} // namespace syncfs_internals
+
+#endif // CHROME_BROWSER_UI_WEBUI_SYNC_FILE_SYSTEM_INTERNALS_DUMP_DATABASE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.cc b/chromium/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.cc
new file mode 100644
index 00000000000..a9e8ec0244d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.cc
@@ -0,0 +1,117 @@
+// 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/sync_file_system_internals/extension_statuses_handler.h"
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync_file_system/sync_file_system_service.h"
+#include "chrome/browser/sync_file_system/sync_file_system_service_factory.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"
+
+using sync_file_system::SyncFileSystemServiceFactory;
+using sync_file_system::SyncServiceState;
+
+namespace syncfs_internals {
+
+namespace {
+
+void ConvertExtensionStatusToDictionary(
+ const base::WeakPtr<ExtensionService>& extension_service,
+ const base::Callback<void(const base::ListValue&)>& callback,
+ const std::map<GURL, std::string>& status_map) {
+ if (!extension_service) {
+ callback.Run(base::ListValue());
+ return;
+ }
+
+ base::ListValue list;
+ for (std::map<GURL, std::string>::const_iterator itr = status_map.begin();
+ itr != status_map.end();
+ ++itr) {
+ std::string extension_id = itr->first.HostNoBrackets();
+
+ // Join with human readable extension name.
+ const extensions::Extension* extension =
+ extension_service->GetExtensionById(extension_id, true);
+ if (!extension)
+ continue;
+
+ std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+ dict->SetString("extensionID", extension_id);
+ dict->SetString("extensionName", extension->name());
+ dict->SetString("status", itr->second);
+ list.Append(std::move(dict));
+ }
+
+ callback.Run(list);
+}
+
+} // namespace
+
+ExtensionStatusesHandler::ExtensionStatusesHandler(Profile* profile)
+ : profile_(profile),
+ weak_ptr_factory_(this) {}
+
+ExtensionStatusesHandler::~ExtensionStatusesHandler() {}
+
+void ExtensionStatusesHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "getExtensionStatuses",
+ base::Bind(&ExtensionStatusesHandler::GetExtensionStatuses,
+ base::Unretained(this)));
+}
+
+// static
+void ExtensionStatusesHandler::GetExtensionStatusesAsDictionary(
+ Profile* profile,
+ const base::Callback<void(const base::ListValue&)>& callback) {
+ DCHECK(profile);
+
+ sync_file_system::SyncFileSystemService* sync_service =
+ SyncFileSystemServiceFactory::GetForProfile(profile);
+ if (!sync_service) {
+ callback.Run(base::ListValue());
+ return;
+ }
+
+ ExtensionService* extension_service =
+ extensions::ExtensionSystem::Get(profile)->extension_service();
+ if (!extension_service) {
+ callback.Run(base::ListValue());
+ return;
+ }
+
+ sync_service->GetExtensionStatusMap(base::Bind(
+ &ConvertExtensionStatusToDictionary,
+ extension_service->AsWeakPtr(), callback));
+}
+
+void ExtensionStatusesHandler::GetExtensionStatuses(
+ const base::ListValue* args) {
+ DCHECK(args);
+ GetExtensionStatusesAsDictionary(
+ profile_,
+ base::Bind(&ExtensionStatusesHandler::DidGetExtensionStatuses,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ExtensionStatusesHandler::DidGetExtensionStatuses(
+ const base::ListValue& list) {
+ web_ui()->CallJavascriptFunctionUnsafe(
+ "ExtensionStatuses.onGetExtensionStatuses", list);
+}
+
+} // namespace syncfs_internals
diff --git a/chromium/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.h b/chromium/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.h
new file mode 100644
index 00000000000..26fef71c775
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.h
@@ -0,0 +1,47 @@
+// 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_SYNC_FILE_SYSTEM_INTERNALS_EXTENSION_STATUSES_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SYNC_FILE_SYSTEM_INTERNALS_EXTENSION_STATUSES_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+class Profile;
+
+namespace syncfs_internals {
+
+// This class handles message from WebUI page of chrome://syncfs-internals/
+// for the Extension Statuses tab. It corresponds to browser/resources/
+// sync_file_system_internals/extension_statuses.html. All methods in this class
+// should be called on UI thread.
+class ExtensionStatusesHandler : public content::WebUIMessageHandler {
+ public:
+ explicit ExtensionStatusesHandler(Profile* profile);
+ ~ExtensionStatusesHandler() override;
+
+ // Shared by Extension Statuses Tab and also File Metadata Tab to generate the
+ // extension drop down.
+ static void GetExtensionStatusesAsDictionary(
+ Profile* profile,
+ const base::Callback<void(const base::ListValue&)>& callback);
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ void GetExtensionStatuses(const base::ListValue* args);
+ void DidGetExtensionStatuses(const base::ListValue& list);
+
+ Profile* profile_;
+ base::WeakPtrFactory<ExtensionStatusesHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionStatusesHandler);
+};
+
+} // namespace syncfs_internals
+
+#endif // CHROME_BROWSER_UI_WEBUI_SYNC_FILE_SYSTEM_INTERNALS_EXTENSION_STATUSES_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/sync_file_system_internals/file_metadata_handler.cc b/chromium/chrome/browser/ui/webui/sync_file_system_internals/file_metadata_handler.cc
new file mode 100644
index 00000000000..19108f4f13f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_file_system_internals/file_metadata_handler.cc
@@ -0,0 +1,84 @@
+// 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/sync_file_system_internals/file_metadata_handler.h"
+
+#include <map>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/api/sync_file_system/sync_file_system_api_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync_file_system/sync_file_system_service.h"
+#include "chrome/browser/sync_file_system/sync_file_system_service_factory.h"
+#include "chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "extensions/common/extension.h"
+
+using sync_file_system::RemoteFileSyncService;
+using sync_file_system::SyncFileSystemServiceFactory;
+using sync_file_system::SyncServiceState;
+
+namespace syncfs_internals {
+
+FileMetadataHandler::FileMetadataHandler(Profile* profile)
+ : profile_(profile),
+ weak_factory_(this) {}
+
+FileMetadataHandler::~FileMetadataHandler() {}
+
+void FileMetadataHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "getExtensions",
+ base::Bind(&FileMetadataHandler::GetExtensions,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getFileMetadata",
+ base::Bind(&FileMetadataHandler::GetFileMetadata,
+ base::Unretained(this)));
+}
+
+void FileMetadataHandler::GetFileMetadata(
+ const base::ListValue* args) {
+ std::string extension_id;
+ if (!args->GetString(0, &extension_id) || extension_id.empty()) {
+ LOG(WARNING) << "GetFileMetadata() Extension ID wasn't given";
+ return;
+ }
+
+ // Extension ID from JS is just the host. Need to reformat it to chrome
+ // extension type GURL.
+ const GURL origin = extensions::Extension::GetBaseURLFromExtensionId(
+ extension_id);
+
+ // Get all metadata for the one specific origin.
+ sync_file_system::SyncFileSystemService* sync_service =
+ SyncFileSystemServiceFactory::GetForProfile(profile_);
+ if (!sync_service)
+ return;
+ sync_service->DumpFiles(origin,
+ base::Bind(&FileMetadataHandler::DidGetFileMetadata,
+ weak_factory_.GetWeakPtr()));
+}
+
+void FileMetadataHandler::GetExtensions(const base::ListValue* args) {
+ DCHECK(args);
+ ExtensionStatusesHandler::GetExtensionStatusesAsDictionary(
+ profile_,
+ base::Bind(&FileMetadataHandler::DidGetExtensions,
+ weak_factory_.GetWeakPtr()));
+}
+
+void FileMetadataHandler::DidGetExtensions(const base::ListValue& list) {
+ web_ui()->CallJavascriptFunctionUnsafe("FileMetadata.onGetExtensions", list);
+}
+
+void FileMetadataHandler::DidGetFileMetadata(const base::ListValue& files) {
+ web_ui()->CallJavascriptFunctionUnsafe("FileMetadata.onGetFileMetadata",
+ files);
+}
+
+} // namespace syncfs_internals
diff --git a/chromium/chrome/browser/ui/webui/sync_file_system_internals/file_metadata_handler.h b/chromium/chrome/browser/ui/webui/sync_file_system_internals/file_metadata_handler.h
new file mode 100644
index 00000000000..eac5ccc6239
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_file_system_internals/file_metadata_handler.h
@@ -0,0 +1,47 @@
+// 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_SYNC_FILE_SYSTEM_INTERNALS_FILE_METADATA_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SYNC_FILE_SYSTEM_INTERNALS_FILE_METADATA_HANDLER_H_
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/sync_file_system/remote_file_sync_service.h"
+#include "chrome/browser/sync_file_system/sync_status_code.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+class Profile;
+
+namespace syncfs_internals {
+
+// This class handles messages from WebUI page of chrome://syncfs-internals/
+// for the File Metadata tab. It corresponds to browser/resources/
+// sync_file_system_internals/file_metadata.html. All methods in this class
+// should be called on UI thread.
+class FileMetadataHandler : public content::WebUIMessageHandler {
+ public:
+ explicit FileMetadataHandler(Profile* profile);
+ ~FileMetadataHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ void GetExtensions(const base::ListValue* args);
+ void DidGetExtensions(const base::ListValue& list);
+
+ void GetFileMetadata(const base::ListValue* args);
+ void DidGetFileMetadata(const base::ListValue& files);
+
+ Profile* profile_;
+ base::WeakPtrFactory<FileMetadataHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileMetadataHandler);
+};
+} // namespace syncfs_internals
+
+#endif // CHROME_BROWSER_UI_WEBUI_SYNC_FILE_SYSTEM_INTERNALS_FILE_METADATA_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_handler.cc b/chromium/chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_handler.cc
new file mode 100644
index 00000000000..a17ed60d4ef
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_handler.cc
@@ -0,0 +1,194 @@
+// 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/sync_file_system_internals/sync_file_system_internals_handler.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/values.h"
+#include "chrome/browser/drive/drive_notification_manager_factory.h"
+#include "chrome/browser/extensions/api/sync_file_system/sync_file_system_api_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync_file_system/logger.h"
+#include "chrome/browser/sync_file_system/sync_file_system_service.h"
+#include "chrome/browser/sync_file_system/sync_file_system_service_factory.h"
+#include "chrome/browser/sync_file_system/sync_service_state.h"
+#include "chrome/common/extensions/api/sync_file_system.h"
+#include "components/drive/drive_notification_manager.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_ui.h"
+#include "google_apis/drive/time_util.h"
+
+using drive::EventLogger;
+using sync_file_system::SyncFileSystemServiceFactory;
+using sync_file_system::SyncServiceState;
+
+namespace syncfs_internals {
+
+SyncFileSystemInternalsHandler::SyncFileSystemInternalsHandler(Profile* profile)
+ : profile_(profile),
+ observing_task_log_(false) {
+ sync_file_system::SyncFileSystemService* sync_service =
+ SyncFileSystemServiceFactory::GetForProfile(profile);
+ if (sync_service)
+ sync_service->AddSyncEventObserver(this);
+}
+
+SyncFileSystemInternalsHandler::~SyncFileSystemInternalsHandler() {
+ sync_file_system::SyncFileSystemService* sync_service =
+ SyncFileSystemServiceFactory::GetForProfile(profile_);
+ if (!sync_service)
+ return;
+ sync_service->RemoveSyncEventObserver(this);
+ if (observing_task_log_)
+ sync_service->task_logger()->RemoveObserver(this);
+}
+
+void SyncFileSystemInternalsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "getServiceStatus",
+ base::Bind(&SyncFileSystemInternalsHandler::GetServiceStatus,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getLog",
+ base::Bind(&SyncFileSystemInternalsHandler::GetLog,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "clearLogs",
+ base::Bind(&SyncFileSystemInternalsHandler::ClearLogs,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getNotificationSource",
+ base::Bind(&SyncFileSystemInternalsHandler::GetNotificationSource,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "observeTaskLog",
+ base::Bind(&SyncFileSystemInternalsHandler::ObserveTaskLog,
+ base::Unretained(this)));
+}
+
+void SyncFileSystemInternalsHandler::OnSyncStateUpdated(
+ const GURL& app_origin,
+ sync_file_system::SyncServiceState state,
+ const std::string& description) {
+ std::string state_string = extensions::api::sync_file_system::ToString(
+ extensions::SyncServiceStateToExtensionEnum(state));
+ if (!description.empty())
+ state_string += " (" + description + ")";
+
+ // TODO(calvinlo): OnSyncStateUpdated should be updated to also provide the
+ // notification mechanism (XMPP or Polling).
+ web_ui()->CallJavascriptFunctionUnsafe("SyncService.onGetServiceStatus",
+ base::Value(state_string));
+}
+
+void SyncFileSystemInternalsHandler::OnFileSynced(
+ const storage::FileSystemURL& url,
+ sync_file_system::SyncFileType file_type,
+ sync_file_system::SyncFileStatus status,
+ sync_file_system::SyncAction action,
+ sync_file_system::SyncDirection direction) {
+}
+
+void SyncFileSystemInternalsHandler::OnLogRecorded(
+ const sync_file_system::TaskLogger::TaskLog& task_log) {
+ base::DictionaryValue dict;
+ int64_t duration = (task_log.end_time - task_log.start_time).InMilliseconds();
+ dict.SetInteger("duration", duration);
+ dict.SetString("task_description", task_log.task_description);
+ dict.SetString("result_description", task_log.result_description);
+
+ std::unique_ptr<base::ListValue> details(new base::ListValue);
+ details->AppendStrings(task_log.details);
+ dict.Set("details", std::move(details));
+ web_ui()->CallJavascriptFunctionUnsafe("TaskLog.onTaskLogRecorded", dict);
+}
+
+void SyncFileSystemInternalsHandler::GetServiceStatus(
+ const base::ListValue* args) {
+ SyncServiceState state_enum = sync_file_system::SYNC_SERVICE_DISABLED;
+ sync_file_system::SyncFileSystemService* sync_service =
+ SyncFileSystemServiceFactory::GetForProfile(profile_);
+ if (sync_service)
+ state_enum = sync_service->GetSyncServiceState();
+ const std::string state_string = extensions::api::sync_file_system::ToString(
+ extensions::SyncServiceStateToExtensionEnum(state_enum));
+ web_ui()->CallJavascriptFunctionUnsafe("SyncService.onGetServiceStatus",
+ base::Value(state_string));
+}
+
+void SyncFileSystemInternalsHandler::GetNotificationSource(
+ const base::ListValue* args) {
+ drive::DriveNotificationManager* drive_notification_manager =
+ drive::DriveNotificationManagerFactory::FindForBrowserContext(profile_);
+ if (!drive_notification_manager)
+ return;
+ bool xmpp_enabled = drive_notification_manager->push_notification_enabled();
+ std::string notification_source = xmpp_enabled ? "XMPP" : "Polling";
+ web_ui()->CallJavascriptFunctionUnsafe("SyncService.onGetNotificationSource",
+ base::Value(notification_source));
+}
+
+void SyncFileSystemInternalsHandler::GetLog(
+ const base::ListValue* args) {
+ const std::vector<EventLogger::Event> log =
+ sync_file_system::util::GetLogHistory();
+
+ int last_log_id_sent;
+ if (!args->GetInteger(0, &last_log_id_sent))
+ last_log_id_sent = -1;
+
+ // Collate events which haven't been sent to WebUI yet.
+ base::ListValue list;
+ for (std::vector<EventLogger::Event>::const_iterator log_entry = log.begin();
+ log_entry != log.end();
+ ++log_entry) {
+ if (log_entry->id <= last_log_id_sent)
+ continue;
+
+ std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+ dict->SetInteger("id", log_entry->id);
+ dict->SetString("time",
+ google_apis::util::FormatTimeAsStringLocaltime(log_entry->when));
+ dict->SetString("logEvent", log_entry->what);
+ list.Append(std::move(dict));
+ last_log_id_sent = log_entry->id;
+ }
+ if (list.empty())
+ return;
+
+ web_ui()->CallJavascriptFunctionUnsafe("SyncService.onGetLog", list);
+}
+
+void SyncFileSystemInternalsHandler::ClearLogs(const base::ListValue* args) {
+ sync_file_system::util::ClearLog();
+}
+
+void SyncFileSystemInternalsHandler::ObserveTaskLog(
+ const base::ListValue* args) {
+ sync_file_system::SyncFileSystemService* sync_service =
+ SyncFileSystemServiceFactory::GetForProfile(profile_);
+ if (!sync_service)
+ return;
+ if (!observing_task_log_) {
+ observing_task_log_ = true;
+ sync_service->task_logger()->AddObserver(this);
+ }
+
+ DCHECK(sync_service->task_logger());
+ const sync_file_system::TaskLogger::LogList& log =
+ sync_service->task_logger()->GetLog();
+
+ for (sync_file_system::TaskLogger::LogList::const_iterator itr = log.begin();
+ itr != log.end(); ++itr)
+ OnLogRecorded(**itr);
+}
+
+} // namespace syncfs_internals
diff --git a/chromium/chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_handler.h b/chromium/chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_handler.h
new file mode 100644
index 00000000000..bbfe6106ab4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_handler.h
@@ -0,0 +1,64 @@
+// 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_SYNC_FILE_SYSTEM_INTERNALS_SYNC_FILE_SYSTEM_INTERNALS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SYNC_FILE_SYSTEM_INTERNALS_SYNC_FILE_SYSTEM_INTERNALS_HANDLER_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/sync_file_system/sync_event_observer.h"
+#include "chrome/browser/sync_file_system/task_logger.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+class Profile;
+
+namespace syncfs_internals {
+
+// This class handles message from WebUI page of chrome://syncfs-internals/
+// for the Sync Service tab. It corresponds to browser/resources/
+// sync_file_system_internals/sync_service.html. All methods in this class
+// should be called on UI thread.
+class SyncFileSystemInternalsHandler
+ : public content::WebUIMessageHandler,
+ public sync_file_system::SyncEventObserver,
+ public sync_file_system::TaskLogger::Observer {
+ public:
+ explicit SyncFileSystemInternalsHandler(Profile* profile);
+ ~SyncFileSystemInternalsHandler() override;
+
+ // content::WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // sync_file_system::SyncEventObserver interface implementation.
+ void OnSyncStateUpdated(const GURL& app_origin,
+ sync_file_system::SyncServiceState state,
+ const std::string& description) override;
+ void OnFileSynced(const storage::FileSystemURL& url,
+ sync_file_system::SyncFileType file_type,
+ sync_file_system::SyncFileStatus status,
+ sync_file_system::SyncAction action,
+ sync_file_system::SyncDirection direction) override;
+
+ // sync_file_system::TaskLogger::Observer implementation.
+ void OnLogRecorded(
+ const sync_file_system::TaskLogger::TaskLog& task_log) override;
+
+ private:
+ void GetServiceStatus(const base::ListValue* args);
+ void GetNotificationSource(const base::ListValue* args);
+ void GetLog(const base::ListValue* args);
+ void ClearLogs(const base::ListValue* args);
+ void ObserveTaskLog(const base::ListValue* args);
+
+ Profile* profile_;
+ bool observing_task_log_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncFileSystemInternalsHandler);
+};
+
+} // namespace syncfs_internals
+
+#endif // CHROME_BROWSER_UI_WEBUI_SYNC_FILE_SYSTEM_INTERNALS_SYNC_FILE_SYSTEM_INTERNALS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_ui.cc b/chromium/chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_ui.cc
new file mode 100644
index 00000000000..dcdfcb7f880
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_ui.cc
@@ -0,0 +1,61 @@
+// 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/sync_file_system_internals/sync_file_system_internals_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/sync_file_system_internals/dump_database_handler.h"
+#include "chrome/browser/ui/webui/sync_file_system_internals/extension_statuses_handler.h"
+#include "chrome/browser/ui/webui/sync_file_system_internals/file_metadata_handler.h"
+#include "chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/sync_file_system_internals_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace {
+
+content::WebUIDataSource* CreateSyncFileSystemInternalsHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(
+ chrome::kChromeUISyncFileSystemInternalsHost);
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath(
+ "utils.js",
+ IDR_SYNC_FILE_SYSTEM_INTERNALS_UTILS_JS);
+ source->AddResourcePath(
+ "extension_statuses.js",
+ IDR_SYNC_FILE_SYSTEM_INTERNALS_EXTENSION_STATUSES_JS);
+ source->AddResourcePath(
+ "file_metadata.js", IDR_SYNC_FILE_SYSTEM_INTERNALS_FILE_METADATA_JS);
+ source->AddResourcePath(
+ "sync_service.js", IDR_SYNC_FILE_SYSTEM_INTERNALS_SYNC_SERVICE_JS);
+ source->AddResourcePath(
+ "task_log.js", IDR_SYNC_FILE_SYSTEM_INTERNALS_TASK_LOG_JS);
+ source->AddResourcePath(
+ "dump_database.js", IDR_SYNC_FILE_SYSTEM_INTERNALS_DUMP_DATABASE_JS);
+ source->SetDefaultResource(IDR_SYNC_FILE_SYSTEM_INTERNALS_MAIN_HTML);
+ return source;
+}
+
+} // namespace
+
+SyncFileSystemInternalsUI::SyncFileSystemInternalsUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ web_ui->AddMessageHandler(
+ base::MakeUnique<syncfs_internals::SyncFileSystemInternalsHandler>(
+ profile));
+ web_ui->AddMessageHandler(
+ base::MakeUnique<syncfs_internals::ExtensionStatusesHandler>(profile));
+ web_ui->AddMessageHandler(
+ base::MakeUnique<syncfs_internals::FileMetadataHandler>(profile));
+ web_ui->AddMessageHandler(
+ base::MakeUnique<syncfs_internals::DumpDatabaseHandler>(profile));
+ content::WebUIDataSource::Add(profile,
+ CreateSyncFileSystemInternalsHTMLSource());
+}
+
+SyncFileSystemInternalsUI::~SyncFileSystemInternalsUI() {}
diff --git a/chromium/chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_ui.h b/chromium/chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_ui.h
new file mode 100644
index 00000000000..f8d8b452b79
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_ui.h
@@ -0,0 +1,20 @@
+// 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_SYNC_FILE_SYSTEM_INTERNALS_SYNC_FILE_SYSTEM_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SYNC_FILE_SYSTEM_INTERNALS_SYNC_FILE_SYSTEM_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+class SyncFileSystemInternalsUI : public content::WebUIController {
+ public:
+ explicit SyncFileSystemInternalsUI(content::WebUI* web_ui);
+ ~SyncFileSystemInternalsUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SyncFileSystemInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SYNC_FILE_SYSTEM_INTERNALS_SYNC_FILE_SYSTEM_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/sync_internals_browsertest.js b/chromium/chrome/browser/ui/webui/sync_internals_browsertest.js
new file mode 100644
index 00000000000..ad8967328ea
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_internals_browsertest.js
@@ -0,0 +1,407 @@
+// 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.
+
+/**
+ * Test fixture for sync internals WebUI testing.
+ * @constructor
+ * @extends {testing.Test}
+ */
+function SyncInternalsWebUITest() {}
+
+SyncInternalsWebUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /**
+ * Browse to the sync internals page.
+ * @override
+ */
+ browsePreload: 'chrome://sync-internals',
+
+ /**
+ * Disable accessibility testing for this page.
+ * @override
+ */
+ runAccessibilityChecks: false,
+
+ /** @override */
+ preLoad: function() {
+ this.makeAndRegisterMockHandler([
+ 'getAllNodes',
+ ]);
+ },
+
+ /**
+ * Checks aboutInfo's details section for the specified field.
+ * @param {boolean} isValid Whether the field is valid.
+ * @param {string} key The name of the key to search for in details.
+ * @param {string} value The expected value if |key| is found.
+ * @return {boolean} whether the field was found in the details.
+ * @protected
+ */
+ hasInDetails: function(isValid, key, value) {
+ var details = chrome.sync.aboutInfo.details;
+ if (!details)
+ return false;
+ for (var i = 0; i < details.length; ++i) {
+ if (!details[i].data)
+ continue;
+ for (var j = 0; j < details[i].data.length; ++j) {
+ var obj = details[i].data[j];
+ if (obj.stat_name == key)
+ return obj.is_valid == isValid && obj.stat_value == value;
+ }
+ }
+ return false;
+ }
+};
+
+/**
+ * Constant hard-coded value to return from mock getAllNodes.
+ * @const
+ */
+var HARD_CODED_ALL_NODES = [{
+ 'nodes': [{
+ 'ATTACHMENT_METADATA': '',
+ 'BASE_SERVER_SPECIFICS': {},
+ 'BASE_VERSION': '1396470970810000',
+ 'CTIME': 'Wednesday, December 31, 1969 4:00:00 PM',
+ 'ID': 'sZ:ADqtAZwzF4GOIyvkI2enSI62AU5p/7MNmvuSSyf7yXJ1SkJwpp1YL' +
+ '6bbMkF8inzqW+EO6n2aPJ/uXccW9GHxorBlnKoZAWHVzg==',
+ 'IS_DEL': false,
+ 'IS_DIR': true,
+ 'IS_UNAPPLIED_UPDATE': false,
+ 'IS_UNSYNCED': false,
+ 'LOCAL_EXTERNAL_ID': '0',
+ 'META_HANDLE': '387',
+ 'MTIME': 'Wednesday, December 31, 1969 4:00:00 PM',
+ 'NON_UNIQUE_NAME': 'Autofill',
+ 'PARENT_ID': 'r',
+ 'SERVER_CTIME': 'Wednesday, December 31, 1969 4:00:00 PM',
+ 'SERVER_IS_DEL': false,
+ 'SERVER_IS_DIR': true,
+ 'SERVER_MTIME': 'Wednesday, December 31, 1969 4:00:00 PM',
+ 'SERVER_NON_UNIQUE_NAME': 'Autofill',
+ 'SERVER_PARENT_ID': 'r',
+ 'SERVER_SPECIFICS': {
+ 'autofill': {
+ 'usage_timestamp': []
+ }
+ },
+ 'SERVER_UNIQUE_POSITION': 'INVALID[]',
+ 'SERVER_VERSION': '1396470970810000',
+ 'SPECIFICS': {
+ 'autofill': {
+ 'usage_timestamp': []
+ }
+ },
+ 'SYNCING': false,
+ 'TRANSACTION_VERSION': '1',
+ 'UNIQUE_BOOKMARK_TAG': '',
+ 'UNIQUE_CLIENT_TAG': '',
+ 'UNIQUE_POSITION': 'INVALID[]',
+ 'UNIQUE_SERVER_TAG': 'google_chrome_autofill',
+ 'isDirty': false,
+ 'modelType': 'Autofill'
+ }, {
+ 'ATTACHMENT_METADATA': '',
+ 'BASE_SERVER_SPECIFICS': {},
+ 'BASE_VERSION': '1394241139528639',
+ 'CTIME': 'Friday, March 7, 2014 5:12:19 PM',
+ 'ID': 'sZ:ADqtAZwzc/ol1iaz+yNLjjWak9PBE0o/hATzpqJsyq/HX2xzV2f88' +
+ 'FaOrT7HDE4tyn7zx2LWgkAFvZfCA5mOy4p0XFgiY0L+mw==',
+ 'IS_DEL': false,
+ 'IS_DIR': false,
+ 'IS_UNAPPLIED_UPDATE': false,
+ 'IS_UNSYNCED': false,
+ 'LOCAL_EXTERNAL_ID': '0',
+ 'META_HANDLE': '2989',
+ 'MTIME': 'Friday, March 7, 2014 5:12:19 PM',
+ 'NON_UNIQUE_NAME': 'autofill_entry|Email|rlsynctet2',
+ 'PARENT_ID': 'sZ:ADqtAZwzF4GOIyvkI2enSI62AU5p/7MNmvuSSyf7yXJ1Sk' +
+ 'Jwpp1YL6bbMkF8inzqW+EO6n2aPJ/uXccW9GHxorBlnKoZAWHVzg==',
+ 'SERVER_CTIME': 'Friday, March 7, 2014 5:12:19 PM',
+ 'SERVER_IS_DEL': false,
+ 'SERVER_IS_DIR': false,
+ 'SERVER_MTIME': 'Friday, March 7, 2014 5:12:19 PM',
+ 'SERVER_NON_UNIQUE_NAME': 'autofill_entry|Email|rlsynctet2',
+ 'SERVER_PARENT_ID': 'sZ:ADqtAZwzF4GOIyvkI2enSI62AU5p/7MNmvuSSyf' +
+ '7yXJ1SkJwpp1YL6bbMkF8inzqW+EO6n2aPJ/uXccW9GHxorBlnKoZAWHVzg==',
+ 'SERVER_SPECIFICS': {
+ 'autofill': {
+ 'name': 'Email',
+ 'usage_timestamp': ['13038713887000000', '13038713890000000'],
+ 'value': 'rlsynctet2'
+ }
+ },
+ 'SERVER_UNIQUE_POSITION': 'INVALID[]',
+ 'SERVER_VERSION': '1394241139528639',
+ 'SPECIFICS': {
+ 'autofill': {
+ 'name': 'Email',
+ 'usage_timestamp': ['13038713887000000', '13038713890000000'],
+ 'value': 'rlsynctet2'
+ }
+ },
+ 'SYNCING': false,
+ 'TRANSACTION_VERSION': '1',
+ 'UNIQUE_BOOKMARK_TAG': '',
+ 'UNIQUE_CLIENT_TAG': 'EvliorKUf1rLjT+BGkNZp586Tsk=',
+ 'UNIQUE_POSITION': 'INVALID[]',
+ 'UNIQUE_SERVER_TAG': '',
+ 'isDirty': false,
+ 'modelType': 'Autofill'
+ }],
+ 'type': 'Autofill'
+}];
+
+/**
+ * A value to return in mock onReceivedUpdatedAboutInfo event.
+ * @const
+ */
+HARD_CODED_ABOUT_INFO = {
+ 'actionable_error': [
+ {
+ 'is_valid': false,
+ 'stat_name': 'Error Type',
+ 'stat_value': 'Uninitialized'
+ },
+ {
+ 'is_valid': false,
+ 'stat_name': 'Action',
+ 'stat_value': 'Uninitialized'
+ },
+ {
+ 'is_valid': false,
+ 'stat_name': 'URL',
+ 'stat_value': 'Uninitialized'
+ },
+ {
+ 'is_valid': false,
+ 'stat_name': 'Error Description',
+ 'stat_value': 'Uninitialized'
+ }
+ ],
+ 'actionable_error_detected': false,
+ 'details': [
+ {
+ 'data': [
+ {
+ 'is_valid': true,
+ 'stat_name': 'Summary',
+ 'stat_value': 'Sync service initialized'
+ }
+ ],
+ 'is_sensitive': false,
+ 'title': 'Summary'
+ },
+ ],
+ 'type_status': [
+ {
+ 'status': 'header',
+ 'name': 'Model Type',
+ 'num_entries': 'Total Entries',
+ 'num_live': 'Live Entries',
+ 'message': 'Message',
+ 'state': 'State',
+ 'group_type': 'Group Type',
+ },
+ {
+ 'status': 'ok',
+ 'name': 'Bookmarks',
+ 'num_entries': 2793,
+ 'num_live': 2793,
+ 'message': '',
+ 'state': 'Running',
+ 'group_type': 'Group UI',
+ },
+ ],
+ 'unrecoverable_error_detected': false
+};
+
+NETWORK_EVENT_DETAILS_1 = {
+ 'details': 'Notified types: Bookmarks, Autofill',
+ 'proto': {},
+ 'time': 1395874542192.407,
+ 'type': 'Normal GetUpdate request',
+};
+
+NETWORK_EVENT_DETAILS_2 = {
+ 'details': 'Received error: SYNC_AUTH_ERROR',
+ 'proto': {},
+ 'time': 1395874542192.837,
+ 'type': 'GetUpdates Response',
+};
+
+TEST_F('SyncInternalsWebUITest', 'Uninitialized', function() {
+ assertNotEquals(null, chrome.sync.aboutInfo);
+ expectTrue(this.hasInDetails(false, 'Summary', 'Uninitialized'));
+});
+
+// Test that username is set correctly when the user is signed in or not.
+// On chromeos, browser tests are signed in by default. On other platforms,
+// browser tests are signed out.
+GEN('#if defined(OS_CHROMEOS)');
+TEST_F('SyncInternalsWebUITest', 'SignedIn', function() {
+ assertNotEquals(null, chrome.sync.aboutInfo);
+ expectTrue(this.hasInDetails(true, 'Username', 'stub-user@example.com'));
+});
+GEN('#else');
+TEST_F('SyncInternalsWebUITest', 'SignedOut', function() {
+ assertNotEquals(null, chrome.sync.aboutInfo);
+ expectTrue(this.hasInDetails(true, 'Username', ''));
+});
+GEN('#endif // defined(OS_CHROMEOS)');
+
+TEST_F('SyncInternalsWebUITest', 'LoadPastedAboutInfo', function() {
+ // Expose the text field.
+ $('import-status').click();
+
+ // Fill it with fake data.
+ $('status-text').value = JSON.stringify(HARD_CODED_ABOUT_INFO);
+
+ // Trigger the import.
+ $('import-status').click();
+
+ expectTrue(this.hasInDetails(true, 'Summary', 'Sync service initialized'));
+});
+
+TEST_F('SyncInternalsWebUITest', 'NetworkEventsTest', function() {
+ networkEvent1 = new Event('onProtocolEvent');
+ networkEvent1.details = NETWORK_EVENT_DETAILS_1;
+ networkEvent2 = new Event('onProtocolEvent');
+ networkEvent2.details = NETWORK_EVENT_DETAILS_2;
+
+ chrome.sync.events.dispatchEvent(networkEvent1);
+ chrome.sync.events.dispatchEvent(networkEvent2);
+
+ expectEquals(2, $('traffic-event-container').children.length);
+
+ // Test that repeated events are not re-displayed.
+ chrome.sync.events.dispatchEvent(networkEvent1);
+ expectEquals(2, $('traffic-event-container').children.length);
+});
+
+TEST_F('SyncInternalsWebUITest', 'SearchTabDoesntChangeOnItemSelect',
+ function() {
+ // Select the search tab.
+ $('sync-search-tab').selected = true;
+ expectTrue($('sync-search-tab').selected);
+
+ // Build the data model and attach to result list.
+ cr.ui.List.decorate($('sync-results-list'));
+ $('sync-results-list').dataModel = new cr.ui.ArrayDataModel([
+ {
+ value: 'value 0',
+ toString: function() { return 'node 0'; },
+ },
+ {
+ value: 'value 1',
+ toString: function() { return 'node 1'; },
+ }
+ ]);
+
+ // Select the first list item and verify the search tab remains selected.
+ $('sync-results-list').getListItemByIndex(0).selected = true;
+ expectTrue($('sync-search-tab').selected);
+});
+
+TEST_F('SyncInternalsWebUITest', 'NodeBrowserTest', function() {
+ var getAllNodesSavedArgs = new SaveMockArguments();
+ this.mockHandler.expects(once()).
+ getAllNodes(getAllNodesSavedArgs.match(ANYTHING)).
+ will(callFunctionWithSavedArgs(getAllNodesSavedArgs,
+ chrome.sync.getAllNodesCallback,
+ HARD_CODED_ALL_NODES));
+
+ // Hit the refresh button.
+ $('node-browser-refresh-button').click();
+
+ // Check that the refresh time was updated.
+ expectNotEquals($('node-browser-refresh-time').textContent, 'Never');
+
+ // Verify some hard-coded assumptions. These depend on the vaue of the
+ // hard-coded nodes, specified elsewhere in this file.
+
+ // Start with the tree itself.
+ var tree = $('sync-node-tree');
+ assertEquals(1, tree.items.length);
+
+ // Check the type root and expand it.
+ var typeRoot = tree.items[0];
+ expectFalse(typeRoot.expanded);
+ typeRoot.expanded = true;
+ assertEquals(1, typeRoot.items.length);
+
+ // An actual sync node. The child of the type root.
+ var leaf = typeRoot.items[0];
+
+ // Verify that selecting it affects the details view.
+ expectTrue($('node-details').hasAttribute('hidden'));
+ leaf.selected = true;
+ expectFalse($('node-details').hasAttribute('hidden'));
+});
+
+TEST_F('SyncInternalsWebUITest', 'NodeBrowserRefreshOnTabSelect', function() {
+ var getAllNodesSavedArgs = new SaveMockArguments();
+ this.mockHandler.expects(once()).
+ getAllNodes(getAllNodesSavedArgs.match(ANYTHING)).
+ will(callFunctionWithSavedArgs(getAllNodesSavedArgs,
+ chrome.sync.getAllNodesCallback,
+ HARD_CODED_ALL_NODES));
+
+ // Should start with non-refreshed node browser.
+ expectEquals($('node-browser-refresh-time').textContent, 'Never');
+
+ // Selecting the tab will refresh it.
+ $('sync-browser-tab').selected = true;
+ expectNotEquals($('node-browser-refresh-time').textContent, 'Never');
+
+ // Re-selecting the tab shouldn't re-refresh.
+ $('node-browser-refresh-time').textContent = 'TestCanary';
+ $('sync-browser-tab').selected = false;
+ $('sync-browser-tab').selected = true;
+ expectEquals($('node-browser-refresh-time').textContent, 'TestCanary');
+});
+
+// Tests that the events log page correctly receives and displays an event.
+TEST_F('SyncInternalsWebUITest', 'EventLogTest', function() {
+ // Dispatch an event.
+ var connectionEvent = new Event('onConnectionStatusChange');
+ connectionEvent.details = {'status': 'CONNECTION_OK'};
+ chrome.sync.events.dispatchEvent(connectionEvent);
+
+ // Verify that it is displayed in the events log.
+ var syncEventsTable = $('sync-events');
+ var firstRow = syncEventsTable.children[0];
+
+ // Makes some assumptions about column ordering. We'll need re-think this if
+ // it turns out to be a maintenance burden.
+ assertEquals(4, firstRow.children.length);
+ var detailsText = firstRow.children[0].textContent;
+ var submoduleName = firstRow.children[1].textContent;
+ var eventName = firstRow.children[2].textContent;
+
+ expectGE(submoduleName.indexOf('manager'), 0,
+ 'submoduleName=' + submoduleName);
+ expectGE(eventName.indexOf('onConnectionStatusChange'), 0,
+ 'eventName=' + eventName);
+ expectGE(detailsText.indexOf('CONNECTION_OK'), 0,
+ 'detailsText=' + detailsText);
+});
+
+TEST_F('SyncInternalsWebUITest', 'DumpSyncEventsToText', function() {
+ // Dispatch an event.
+ var connectionEvent = new Event('onConnectionStatusChange');
+ connectionEvent.details = {'status': 'CONNECTION_OK'};
+ chrome.sync.events.dispatchEvent(connectionEvent);
+
+ // Click the dump-to-text button.
+ $('dump-to-text').click();
+
+ // Verify our event is among the results.
+ var eventDumpText = $('data-dump').textContent;
+
+ expectGE(eventDumpText.indexOf('onConnectionStatusChange'), 0);
+ expectGE(eventDumpText.indexOf('CONNECTION_OK'), 0);
+});
diff --git a/chromium/chrome/browser/ui/webui/sync_internals_message_handler.cc b/chromium/chrome/browser/ui/webui/sync_internals_message_handler.cc
new file mode 100644
index 00000000000..5979034d19c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_internals_message_handler.cc
@@ -0,0 +1,323 @@
+// 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/ui/webui/sync_internals_message_handler.h"
+
+#include <stdint.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/feature_list.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/sync/user_event_service_factory.h"
+#include "chrome/common/channel_info.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/sync/base/weak_handle.h"
+#include "components/sync/driver/about_sync_util.h"
+#include "components/sync/driver/sync_driver_switches.h"
+#include "components/sync/driver/sync_service.h"
+#include "components/sync/engine/cycle/commit_counters.h"
+#include "components/sync/engine/cycle/status_counters.h"
+#include "components/sync/engine/cycle/update_counters.h"
+#include "components/sync/engine/events/protocol_event.h"
+#include "components/sync/js/js_event_details.h"
+#include "components/sync/protocol/sync.pb.h"
+#include "components/sync/user_events/user_event_service.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_ui.h"
+
+using base::DictionaryValue;
+using base::ListValue;
+using base::Value;
+using browser_sync::ProfileSyncService;
+using syncer::JsEventDetails;
+using syncer::ModelTypeSet;
+using syncer::SyncService;
+using syncer::WeakHandle;
+
+namespace {
+
+// Converts the string at |index| in |list| to an int, defaulting to 0 on error.
+int64_t StringAtIndexToInt64(const base::ListValue* list, int index) {
+ std::string str;
+ if (list->GetString(index, &str)) {
+ int64_t integer = 0;
+ if (base::StringToInt64(str, &integer))
+ return integer;
+ }
+ return 0;
+}
+
+} // namespace
+
+SyncInternalsMessageHandler::SyncInternalsMessageHandler()
+ : SyncInternalsMessageHandler(
+ base::BindRepeating(
+ &SyncInternalsMessageHandler::BindForSyncServiceProvider,
+ base::Unretained(this)),
+ base::BindRepeating(
+ &syncer::sync_ui_util::ConstructAboutInformation)) {}
+
+SyncInternalsMessageHandler::SyncInternalsMessageHandler(
+ SyncServiceProvider sync_service_provider,
+ AboutSyncDataDelegate about_sync_data_delegate)
+ : sync_service_provider_(std::move(sync_service_provider)),
+ about_sync_data_delegate_(std::move(about_sync_data_delegate)),
+ weak_ptr_factory_(this) {}
+
+SyncInternalsMessageHandler::~SyncInternalsMessageHandler() {
+ UnregisterModelNotifications();
+}
+
+void SyncInternalsMessageHandler::OnJavascriptDisallowed() {
+ // Invaliding weak ptrs works well here because the only weak ptr we vend is
+ // to the sync side to give us information that should be used to populate the
+ // javascript side. If javascript is disallowed, we don't care about updating
+ // the UI with data, so dropping those callbacks is fine.
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ UnregisterModelNotifications();
+}
+
+void SyncInternalsMessageHandler::RegisterMessages() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ web_ui()->RegisterMessageCallback(
+ syncer::sync_ui_util::kRegisterForEvents,
+ base::Bind(&SyncInternalsMessageHandler::HandleRegisterForEvents,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ syncer::sync_ui_util::kRegisterForPerTypeCounters,
+ base::Bind(&SyncInternalsMessageHandler::HandleRegisterForPerTypeCounters,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ syncer::sync_ui_util::kRequestUpdatedAboutInfo,
+ base::Bind(&SyncInternalsMessageHandler::HandleRequestUpdatedAboutInfo,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ syncer::sync_ui_util::kRequestListOfTypes,
+ base::Bind(&SyncInternalsMessageHandler::HandleRequestListOfTypes,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ syncer::sync_ui_util::kRequestUserEventsVisibility,
+ base::Bind(
+ &SyncInternalsMessageHandler::HandleRequestUserEventsVisibility,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ syncer::sync_ui_util::kWriteUserEvent,
+ base::Bind(&SyncInternalsMessageHandler::HandleWriteUserEvent,
+ base::Unretained(this)));
+
+ web_ui()->RegisterMessageCallback(
+ syncer::sync_ui_util::kGetAllNodes,
+ base::Bind(&SyncInternalsMessageHandler::HandleGetAllNodes,
+ base::Unretained(this)));
+}
+
+void SyncInternalsMessageHandler::HandleRegisterForEvents(
+ const ListValue* args) {
+ DCHECK(args->empty());
+ AllowJavascript();
+
+ // is_registered_ flag protects us from double-registering. This could
+ // happen on a page refresh, where the JavaScript gets re-run but the
+ // message handler remains unchanged.
+ SyncService* service = sync_service_provider_.Run();
+ if (service && !is_registered_) {
+ service->AddObserver(this);
+ service->AddProtocolEventObserver(this);
+ js_controller_ = service->GetJsController();
+ js_controller_->AddJsEventHandler(this);
+ is_registered_ = true;
+ }
+}
+
+void SyncInternalsMessageHandler::HandleRegisterForPerTypeCounters(
+ const ListValue* args) {
+ DCHECK(args->empty());
+ AllowJavascript();
+
+ SyncService* service = sync_service_provider_.Run();
+ if (!service)
+ return;
+
+ if (!is_registered_for_counters_) {
+ service->AddTypeDebugInfoObserver(this);
+ is_registered_for_counters_ = true;
+ } else {
+ // Re-register to ensure counters get re-emitted.
+ service->RemoveTypeDebugInfoObserver(this);
+ service->AddTypeDebugInfoObserver(this);
+ }
+}
+
+void SyncInternalsMessageHandler::HandleRequestUpdatedAboutInfo(
+ const ListValue* args) {
+ DCHECK(args->empty());
+ AllowJavascript();
+ SendAboutInfo();
+}
+
+void SyncInternalsMessageHandler::HandleRequestListOfTypes(
+ const ListValue* args) {
+ DCHECK(args->empty());
+ AllowJavascript();
+
+ DictionaryValue event_details;
+ auto type_list = base::MakeUnique<ListValue>();
+ ModelTypeSet protocol_types = syncer::ProtocolTypes();
+ for (ModelTypeSet::Iterator it = protocol_types.First(); it.Good();
+ it.Inc()) {
+ type_list->AppendString(ModelTypeToString(it.Get()));
+ }
+ event_details.Set(syncer::sync_ui_util::kTypes, std::move(type_list));
+ DispatchEvent(syncer::sync_ui_util::kOnReceivedListOfTypes, event_details);
+}
+
+void SyncInternalsMessageHandler::HandleGetAllNodes(const ListValue* args) {
+ DCHECK_EQ(1U, args->GetSize());
+ AllowJavascript();
+
+ int request_id = 0;
+ bool success = ExtractIntegerValue(args, &request_id);
+ DCHECK(success);
+
+ SyncService* service = sync_service_provider_.Run();
+ if (service) {
+ // This opens up the possibility of non-javascript code calling us
+ // asynchronously, and potentially at times we're not allowed to call into
+ // the javascript side. We guard against this by invalidating this weak ptr
+ // should javascript become disallowed.
+ service->GetAllNodes(
+ base::Bind(&SyncInternalsMessageHandler::OnReceivedAllNodes,
+ weak_ptr_factory_.GetWeakPtr(), request_id));
+ }
+}
+
+void SyncInternalsMessageHandler::HandleRequestUserEventsVisibility(
+ const base::ListValue* args) {
+ DCHECK(args->empty());
+ AllowJavascript();
+ CallJavascriptFunction(
+ syncer::sync_ui_util::kUserEventsVisibilityCallback,
+ Value(base::FeatureList::IsEnabled(switches::kSyncUserEvents)));
+}
+
+void SyncInternalsMessageHandler::HandleWriteUserEvent(
+ const base::ListValue* args) {
+ DCHECK_EQ(2U, args->GetSize());
+ AllowJavascript();
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ syncer::UserEventService* user_event_service =
+ browser_sync::UserEventServiceFactory::GetForProfile(
+ profile->GetOriginalProfile());
+
+ sync_pb::UserEventSpecifics event_specifics;
+ event_specifics.set_event_time_usec(StringAtIndexToInt64(args, 0));
+ event_specifics.set_navigation_id(StringAtIndexToInt64(args, 1));
+ user_event_service->RecordUserEvent(event_specifics);
+}
+
+void SyncInternalsMessageHandler::OnReceivedAllNodes(
+ int request_id,
+ std::unique_ptr<ListValue> nodes) {
+ CallJavascriptFunction(syncer::sync_ui_util::kGetAllNodesCallback,
+ Value(request_id), *nodes);
+}
+
+void SyncInternalsMessageHandler::OnStateChanged(SyncService* sync) {
+ SendAboutInfo();
+}
+
+void SyncInternalsMessageHandler::OnProtocolEvent(
+ const syncer::ProtocolEvent& event) {
+ std::unique_ptr<DictionaryValue> value(syncer::ProtocolEvent::ToValue(event));
+ DispatchEvent(syncer::sync_ui_util::kOnProtocolEvent, *value);
+}
+
+void SyncInternalsMessageHandler::OnCommitCountersUpdated(
+ syncer::ModelType type,
+ const syncer::CommitCounters& counters) {
+ EmitCounterUpdate(type, syncer::sync_ui_util::kCommit, counters.ToValue());
+}
+
+void SyncInternalsMessageHandler::OnUpdateCountersUpdated(
+ syncer::ModelType type,
+ const syncer::UpdateCounters& counters) {
+ EmitCounterUpdate(type, syncer::sync_ui_util::kUpdate, counters.ToValue());
+}
+
+void SyncInternalsMessageHandler::OnStatusCountersUpdated(
+ syncer::ModelType type,
+ const syncer::StatusCounters& counters) {
+ EmitCounterUpdate(type, syncer::sync_ui_util::kStatus, counters.ToValue());
+}
+
+void SyncInternalsMessageHandler::EmitCounterUpdate(
+ syncer::ModelType type,
+ const std::string& counter_type,
+ std::unique_ptr<DictionaryValue> value) {
+ auto details = base::MakeUnique<DictionaryValue>();
+ details->SetString(syncer::sync_ui_util::kModelType, ModelTypeToString(type));
+ details->SetString(syncer::sync_ui_util::kCounterType, counter_type);
+ details->Set(syncer::sync_ui_util::kCounters, std::move(value));
+ DispatchEvent(syncer::sync_ui_util::kOnCountersUpdated, *details);
+}
+
+void SyncInternalsMessageHandler::HandleJsEvent(
+ const std::string& name,
+ const JsEventDetails& details) {
+ DVLOG(1) << "Handling event: " << name
+ << " with details " << details.ToString();
+ DispatchEvent(name, details.Get());
+}
+
+void SyncInternalsMessageHandler::SendAboutInfo() {
+ std::unique_ptr<DictionaryValue> value = about_sync_data_delegate_.Run(
+ sync_service_provider_.Run(), chrome::GetChannel());
+ DispatchEvent(syncer::sync_ui_util::kOnAboutInfoUpdated, *value);
+}
+
+SyncService* SyncInternalsMessageHandler::BindForSyncServiceProvider() {
+ return ProfileSyncServiceFactory::GetForProfile(
+ Profile::FromWebUI(web_ui())->GetOriginalProfile());
+}
+
+void SyncInternalsMessageHandler::DispatchEvent(const std::string& name,
+ const Value& details_value) {
+ CallJavascriptFunction(syncer::sync_ui_util::kDispatchEvent, Value(name),
+ details_value);
+}
+
+void SyncInternalsMessageHandler::UnregisterModelNotifications() {
+ SyncService* service = sync_service_provider_.Run();
+ if (!service)
+ return;
+
+ // Cannot use ScopedObserver to do all the tracking because most don't follow
+ // AddObserver/RemoveObserver method naming style.
+ if (is_registered_) {
+ DCHECK(js_controller_);
+ service->RemoveObserver(this);
+ service->RemoveProtocolEventObserver(this);
+ js_controller_->RemoveJsEventHandler(this);
+ js_controller_ = nullptr;
+ is_registered_ = false;
+ }
+
+ if (is_registered_for_counters_) {
+ service->RemoveTypeDebugInfoObserver(this);
+ is_registered_for_counters_ = false;
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/sync_internals_message_handler.h b/chromium/chrome/browser/ui/webui/sync_internals_message_handler.h
new file mode 100644
index 00000000000..2ef1840b073
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_internals_message_handler.h
@@ -0,0 +1,147 @@
+// 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_UI_WEBUI_SYNC_INTERNALS_MESSAGE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SYNC_INTERNALS_MESSAGE_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
+#include "base/values.h"
+#include "components/sync/driver/sync_service_observer.h"
+#include "components/sync/engine/cycle/type_debug_info_observer.h"
+#include "components/sync/engine/events/protocol_event_observer.h"
+#include "components/sync/js/js_controller.h"
+#include "components/sync/js/js_event_handler.h"
+#include "components/version_info/channel.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace browser_sync {
+class ProfileSyncService;
+} // namespace browser_sync
+
+namespace syncer {
+class SyncService;
+} // namespace syncer
+
+// The implementation for the chrome://sync-internals page.
+class SyncInternalsMessageHandler : public content::WebUIMessageHandler,
+ public syncer::JsEventHandler,
+ public syncer::SyncServiceObserver,
+ public syncer::ProtocolEventObserver,
+ public syncer::TypeDebugInfoObserver {
+ public:
+ SyncInternalsMessageHandler();
+ ~SyncInternalsMessageHandler() override;
+
+ // content::WebUIMessageHandler implementation.
+ void OnJavascriptDisallowed() override;
+ void RegisterMessages() override;
+
+ // Sets up observers to receive events and forward them to the UI.
+ void HandleRegisterForEvents(const base::ListValue* args);
+
+ // Sets up observers to receive per-type counters and forward them to the UI.
+ void HandleRegisterForPerTypeCounters(const base::ListValue* args);
+
+ // Fires an event to send updated info back to the page.
+ void HandleRequestUpdatedAboutInfo(const base::ListValue* args);
+
+ // Fires and event to send the list of types back to the page.
+ void HandleRequestListOfTypes(const base::ListValue* args);
+
+ // Handler for getAllNodes message. Needs a |request_id| argument.
+ void HandleGetAllNodes(const base::ListValue* args);
+
+ // Handler for requests to get UserEvents tab visibility.
+ void HandleRequestUserEventsVisibility(const base::ListValue* args);
+
+ // Handler for writeUserEvent message.
+ void HandleWriteUserEvent(const base::ListValue* args);
+
+ // syncer::JsEventHandler implementation.
+ void HandleJsEvent(const std::string& name,
+ const syncer::JsEventDetails& details) override;
+
+ // Callback used in GetAllNodes.
+ void OnReceivedAllNodes(int request_id,
+ std::unique_ptr<base::ListValue> nodes);
+
+ // syncer::SyncServiceObserver implementation.
+ void OnStateChanged(syncer::SyncService* sync) override;
+
+ // syncer::ProtocolEventObserver implementation.
+ void OnProtocolEvent(const syncer::ProtocolEvent& e) override;
+
+ // syncer::TypeDebugInfoObserver implementation.
+ void OnCommitCountersUpdated(syncer::ModelType type,
+ const syncer::CommitCounters& counters) override;
+ void OnUpdateCountersUpdated(syncer::ModelType type,
+ const syncer::UpdateCounters& counters) override;
+ void OnStatusCountersUpdated(syncer::ModelType type,
+ const syncer::StatusCounters& counters) override;
+
+ // Helper to emit counter updates.
+ //
+ // Used in implementation of On*CounterUpdated methods. Emits the given
+ // dictionary value with additional data to specify the model type and
+ // counter type.
+ void EmitCounterUpdate(syncer::ModelType type,
+ const std::string& counter_type,
+ std::unique_ptr<base::DictionaryValue> value);
+
+ protected:
+ using SyncServiceProvider = base::RepeatingCallback<syncer::SyncService*()>;
+
+ using AboutSyncDataDelegate =
+ base::RepeatingCallback<std::unique_ptr<base::DictionaryValue>(
+ syncer::SyncService* service,
+ version_info::Channel channel)>;
+
+ // Constructor used for unit testing to override dependencies.
+ SyncInternalsMessageHandler(SyncServiceProvider sync_service_provider,
+ AboutSyncDataDelegate about_sync_data_delegate);
+
+ private:
+ // Fetches updated aboutInfo and sends it to the page in the form of an
+ // onAboutInfoUpdated event.
+ void SendAboutInfo();
+
+ // Gets the ProfileSyncService of the underlying original profile. May return
+ // nullptr (e.g., if sync is disabled on the command line). Shouldn't be
+ // called directly, but instead through |sync_service_provider_|.
+ syncer::SyncService* BindForSyncServiceProvider();
+
+ // Sends a dispatch event to the UI. Javascript must be enabled.
+ void DispatchEvent(const std::string& name, const base::Value& details_value);
+
+ // Unregisters for notifications from all notifications coming from the sync
+ // machinery. Leaves notifications hooked into the UI alone.
+ void UnregisterModelNotifications();
+
+ base::WeakPtr<syncer::JsController> js_controller_;
+
+ // A flag used to prevent double-registration with ProfileSyncService.
+ bool is_registered_ = false;
+
+ // A flag used to prevent double-registration as TypeDebugInfoObserver with
+ // ProfileSyncService.
+ bool is_registered_for_counters_ = false;
+
+ // An abstraction of who provides the SyncService.
+ SyncServiceProvider sync_service_provider_;
+
+ // An abstraction of who creates the about sync info value map.
+ AboutSyncDataDelegate about_sync_data_delegate_;
+
+ base::WeakPtrFactory<SyncInternalsMessageHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncInternalsMessageHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SYNC_INTERNALS_MESSAGE_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/sync_internals_message_handler_unittest.cc b/chromium/chrome/browser/ui/webui/sync_internals_message_handler_unittest.cc
new file mode 100644
index 00000000000..b1611198114
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_internals_message_handler_unittest.cc
@@ -0,0 +1,313 @@
+// 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.
+
+#include "chrome/browser/ui/webui/sync_internals_message_handler.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/memory/ref_counted.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/browser_sync/browser_sync_switches.h"
+#include "components/sync/driver/about_sync_util.h"
+#include "components/sync/driver/fake_sync_service.h"
+#include "components/sync/driver/sync_service.h"
+#include "components/sync/js/js_test_util.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_web_ui.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::DictionaryValue;
+using base::ListValue;
+using base::Value;
+using syncer::SyncService;
+using syncer::SyncServiceObserver;
+using syncer::TypeDebugInfoObserver;
+
+namespace {
+
+class TestableSyncInternalsMessageHandler : public SyncInternalsMessageHandler {
+ public:
+ TestableSyncInternalsMessageHandler(
+ content::WebUI* web_ui,
+ SyncServiceProvider sync_service_provider,
+ AboutSyncDataDelegate about_sync_data_delegate)
+ : SyncInternalsMessageHandler(std::move(sync_service_provider),
+ std::move(about_sync_data_delegate)) {
+ set_web_ui(web_ui);
+ }
+};
+
+class TestSyncService : public syncer::FakeSyncService {
+ public:
+ void AddObserver(SyncServiceObserver* observer) override {
+ ++add_observer_count_;
+ }
+
+ void RemoveObserver(SyncServiceObserver* observer) override {
+ ++remove_observer_count_;
+ }
+
+ void AddTypeDebugInfoObserver(TypeDebugInfoObserver* observer) override {
+ ++add_type_debug_info_observer_count_;
+ }
+
+ void RemoveTypeDebugInfoObserver(TypeDebugInfoObserver* observer) override {
+ ++remove_type_debug_info_observer_count_;
+ }
+
+ base::WeakPtr<syncer::JsController> GetJsController() override {
+ return js_controller_.AsWeakPtr();
+ }
+
+ void GetAllNodes(const base::Callback<void(std::unique_ptr<base::ListValue>)>&
+ callback) override {
+ get_all_nodes_callback_ = std::move(callback);
+ }
+
+ int add_observer_count() const { return add_observer_count_; }
+ int remove_observer_count() const { return remove_observer_count_; }
+ int add_type_debug_info_observer_count() const {
+ return add_type_debug_info_observer_count_;
+ }
+ int remove_type_debug_info_observer_count() const {
+ return remove_type_debug_info_observer_count_;
+ }
+ base::Callback<void(std::unique_ptr<base::ListValue>)>
+ get_all_nodes_callback() {
+ return std::move(get_all_nodes_callback_);
+ }
+
+ private:
+ int add_observer_count_ = 0;
+ int remove_observer_count_ = 0;
+ int add_type_debug_info_observer_count_ = 0;
+ int remove_type_debug_info_observer_count_ = 0;
+ syncer::MockJsController js_controller_;
+ base::Callback<void(std::unique_ptr<base::ListValue>)>
+ get_all_nodes_callback_;
+};
+
+class SyncInternalsMessageHandlerTest : public ::testing::Test {
+ protected:
+ SyncInternalsMessageHandlerTest() {
+ site_instance_ = content::SiteInstance::Create(&profile_);
+ web_contents_.reset(content::WebContents::Create(
+ content::WebContents::CreateParams(&profile_, site_instance_.get())));
+ web_ui_.set_web_contents(web_contents_.get());
+ test_sync_service_ = base::MakeUnique<TestSyncService>();
+ handler_.reset(new TestableSyncInternalsMessageHandler(
+ &web_ui_,
+ base::BindRepeating(&SyncInternalsMessageHandlerTest::sync_service,
+ base::Unretained(this)),
+ base::BindRepeating(
+ &SyncInternalsMessageHandlerTest::ConstructAboutInformation,
+ base::Unretained(this))));
+ }
+
+ std::unique_ptr<DictionaryValue> ConstructAboutInformation(
+ SyncService* service,
+ version_info::Channel channel) {
+ ++about_sync_data_delegate_call_count_;
+ last_delegate_sync_service_ = service;
+ auto dictionary = base::MakeUnique<DictionaryValue>();
+ dictionary->SetString("fake_key", "fake_value");
+ return dictionary;
+ }
+
+ void ValidateAboutInfoCall() {
+ const auto& data_vector = web_ui_.call_data();
+ ASSERT_FALSE(data_vector.empty());
+ EXPECT_EQ(1u, data_vector.size());
+
+ const content::TestWebUI::CallData& call_data = *data_vector[0];
+
+ EXPECT_EQ(syncer::sync_ui_util::kDispatchEvent, call_data.function_name());
+
+ const Value* arg1 = call_data.arg1();
+ ASSERT_TRUE(arg1);
+ std::string event_type;
+ EXPECT_TRUE(arg1->GetAsString(&event_type));
+ EXPECT_EQ(syncer::sync_ui_util::kOnAboutInfoUpdated, event_type);
+
+ const Value* arg2 = call_data.arg2();
+ ASSERT_TRUE(arg2);
+
+ const DictionaryValue* root_dictionary = nullptr;
+ ASSERT_TRUE(arg2->GetAsDictionary(&root_dictionary));
+
+ std::string fake_value;
+ EXPECT_TRUE(root_dictionary->GetString("fake_key", &fake_value));
+ EXPECT_EQ("fake_value", fake_value);
+ }
+
+ void ValidateEmptyAboutInfoCall() {
+ EXPECT_TRUE(web_ui_.call_data().empty());
+ }
+
+ TestSyncService* test_sync_service() { return test_sync_service_.get(); }
+
+ TestableSyncInternalsMessageHandler* handler() { return handler_.get(); }
+
+ int CallCountWithName(const std::string& function_name) {
+ int count = 0;
+ for (const auto& call_data : web_ui_.call_data()) {
+ if (call_data->function_name() == function_name) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ int about_sync_data_delegate_call_count() const {
+ return about_sync_data_delegate_call_count_;
+ }
+
+ const SyncService* last_delegate_sync_service() const {
+ return last_delegate_sync_service_;
+ }
+
+ void ResetSyncService() { test_sync_service_.reset(); }
+
+ void ResetHandler() { handler_.reset(); }
+
+ private:
+ SyncService* sync_service() { return test_sync_service_.get(); }
+
+ content::TestBrowserThreadBundle thread_bundle_;
+ TestingProfile profile_;
+ content::TestWebUI web_ui_;
+ scoped_refptr<content::SiteInstance> site_instance_;
+ std::unique_ptr<content::WebContents> web_contents_;
+ std::unique_ptr<TestSyncService> test_sync_service_;
+ std::unique_ptr<TestableSyncInternalsMessageHandler> handler_;
+ int about_sync_data_delegate_call_count_ = 0;
+ SyncService* last_delegate_sync_service_ = nullptr;
+};
+
+TEST_F(SyncInternalsMessageHandlerTest, AddRemoveObservers) {
+ ListValue empty_list;
+
+ EXPECT_EQ(0, test_sync_service()->add_observer_count());
+ handler()->HandleRegisterForEvents(&empty_list);
+ EXPECT_EQ(1, test_sync_service()->add_observer_count());
+
+ EXPECT_EQ(0, test_sync_service()->add_type_debug_info_observer_count());
+ handler()->HandleRegisterForPerTypeCounters(&empty_list);
+ EXPECT_EQ(1, test_sync_service()->add_type_debug_info_observer_count());
+
+ EXPECT_EQ(0, test_sync_service()->remove_observer_count());
+ EXPECT_EQ(0, test_sync_service()->remove_type_debug_info_observer_count());
+ ResetHandler();
+ EXPECT_EQ(1, test_sync_service()->remove_observer_count());
+ EXPECT_EQ(1, test_sync_service()->remove_type_debug_info_observer_count());
+
+ // Add calls should never have increased since the initial subscription.
+ EXPECT_EQ(1, test_sync_service()->add_observer_count());
+ EXPECT_EQ(1, test_sync_service()->add_type_debug_info_observer_count());
+}
+
+TEST_F(SyncInternalsMessageHandlerTest, AddRemoveObserversDisallowJavascript) {
+ ListValue empty_list;
+
+ EXPECT_EQ(0, test_sync_service()->add_observer_count());
+ handler()->HandleRegisterForEvents(&empty_list);
+ EXPECT_EQ(1, test_sync_service()->add_observer_count());
+
+ EXPECT_EQ(0, test_sync_service()->add_type_debug_info_observer_count());
+ handler()->HandleRegisterForPerTypeCounters(&empty_list);
+ EXPECT_EQ(1, test_sync_service()->add_type_debug_info_observer_count());
+
+ EXPECT_EQ(0, test_sync_service()->remove_observer_count());
+ EXPECT_EQ(0, test_sync_service()->remove_type_debug_info_observer_count());
+ handler()->DisallowJavascript();
+ EXPECT_EQ(1, test_sync_service()->remove_observer_count());
+ EXPECT_EQ(1, test_sync_service()->remove_type_debug_info_observer_count());
+
+ // Deregistration should not repeat, no counts should increase.
+ ResetHandler();
+ EXPECT_EQ(1, test_sync_service()->add_observer_count());
+ EXPECT_EQ(1, test_sync_service()->add_type_debug_info_observer_count());
+ EXPECT_EQ(1, test_sync_service()->remove_observer_count());
+ EXPECT_EQ(1, test_sync_service()->remove_type_debug_info_observer_count());
+}
+
+TEST_F(SyncInternalsMessageHandlerTest, AddRemoveObserversSyncDisabled) {
+ // Simulate completely disabling sync by flag or other mechanism.
+ ResetSyncService();
+
+ ListValue empty_list;
+ handler()->HandleRegisterForEvents(&empty_list);
+ handler()->HandleRegisterForPerTypeCounters(&empty_list);
+ handler()->DisallowJavascript();
+ // Cannot verify observer methods on sync services were not called, because
+ // there is no sync service. Rather, we're just making sure the handler hasn't
+ // performed any invalid operations when the sync service is missing.
+}
+
+TEST_F(SyncInternalsMessageHandlerTest,
+ RepeatedHandleRegisterForPerTypeCounters) {
+ ListValue empty_list;
+ handler()->HandleRegisterForPerTypeCounters(&empty_list);
+ EXPECT_EQ(1, test_sync_service()->add_type_debug_info_observer_count());
+ EXPECT_EQ(0, test_sync_service()->remove_type_debug_info_observer_count());
+
+ handler()->HandleRegisterForPerTypeCounters(&empty_list);
+ EXPECT_EQ(2, test_sync_service()->add_type_debug_info_observer_count());
+ EXPECT_EQ(1, test_sync_service()->remove_type_debug_info_observer_count());
+
+ handler()->HandleRegisterForPerTypeCounters(&empty_list);
+ EXPECT_EQ(3, test_sync_service()->add_type_debug_info_observer_count());
+ EXPECT_EQ(2, test_sync_service()->remove_type_debug_info_observer_count());
+
+ ResetHandler();
+ EXPECT_EQ(3, test_sync_service()->add_type_debug_info_observer_count());
+ EXPECT_EQ(3, test_sync_service()->remove_type_debug_info_observer_count());
+}
+
+TEST_F(SyncInternalsMessageHandlerTest, HandleGetAllNodes) {
+ ListValue args;
+ args.AppendInteger(0);
+ handler()->HandleGetAllNodes(&args);
+ test_sync_service()->get_all_nodes_callback().Run(
+ base::MakeUnique<ListValue>());
+ EXPECT_EQ(1, CallCountWithName(syncer::sync_ui_util::kGetAllNodesCallback));
+
+ handler()->HandleGetAllNodes(&args);
+ // This breaks the weak ref the callback is hanging onto. Which results in
+ // the call count not incrementing.
+ handler()->DisallowJavascript();
+ test_sync_service()->get_all_nodes_callback().Run(
+ base::MakeUnique<ListValue>());
+ EXPECT_EQ(1, CallCountWithName(syncer::sync_ui_util::kGetAllNodesCallback));
+
+ handler()->HandleGetAllNodes(&args);
+ test_sync_service()->get_all_nodes_callback().Run(
+ base::MakeUnique<ListValue>());
+ EXPECT_EQ(2, CallCountWithName(syncer::sync_ui_util::kGetAllNodesCallback));
+}
+
+TEST_F(SyncInternalsMessageHandlerTest, SendAboutInfo) {
+ handler()->AllowJavascriptForTesting();
+ handler()->OnStateChanged(nullptr);
+ EXPECT_EQ(1, about_sync_data_delegate_call_count());
+ EXPECT_NE(nullptr, last_delegate_sync_service());
+ ValidateAboutInfoCall();
+}
+
+TEST_F(SyncInternalsMessageHandlerTest, SendAboutInfoSyncDisabled) {
+ // Simulate completely disabling sync by flag or other mechanism.
+ ResetSyncService();
+
+ handler()->AllowJavascriptForTesting();
+ handler()->OnStateChanged(nullptr);
+ EXPECT_EQ(1, about_sync_data_delegate_call_count());
+ EXPECT_EQ(nullptr, last_delegate_sync_service());
+ ValidateAboutInfoCall();
+}
+
+} // namespace
diff --git a/chromium/chrome/browser/ui/webui/sync_internals_ui.cc b/chromium/chrome/browser/ui/webui/sync_internals_ui.cc
new file mode 100644
index 00000000000..97763c58b0e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_internals_ui.cc
@@ -0,0 +1,60 @@
+// 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/ui/webui/sync_internals_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/sync_internals_message_handler.h"
+#include "chrome/common/url_constants.h"
+#include "components/grit/components_resources.h"
+#include "components/sync/driver/about_sync_util.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace {
+
+content::WebUIDataSource* CreateSyncInternalsHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUISyncInternalsHost);
+
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath(syncer::sync_ui_util::kSyncIndexJS,
+ IDR_SYNC_DRIVER_SYNC_INTERNALS_INDEX_JS);
+ source->AddResourcePath(syncer::sync_ui_util::kChromeSyncJS,
+ IDR_SYNC_DRIVER_SYNC_INTERNALS_CHROME_SYNC_JS);
+ source->AddResourcePath(syncer::sync_ui_util::kTypesJS,
+ IDR_SYNC_DRIVER_SYNC_INTERNALS_TYPES_JS);
+ source->AddResourcePath(syncer::sync_ui_util::kSyncLogJS,
+ IDR_SYNC_DRIVER_SYNC_INTERNALS_SYNC_LOG_JS);
+ source->AddResourcePath(syncer::sync_ui_util::kSyncNodeBrowserJS,
+ IDR_SYNC_DRIVER_SYNC_INTERNALS_SYNC_NODE_BROWSER_JS);
+ source->AddResourcePath(syncer::sync_ui_util::kSyncSearchJS,
+ IDR_SYNC_DRIVER_SYNC_INTERNALS_SYNC_SEARCH_JS);
+ source->AddResourcePath(syncer::sync_ui_util::kAboutJS,
+ IDR_SYNC_DRIVER_SYNC_INTERNALS_ABOUT_JS);
+ source->AddResourcePath(syncer::sync_ui_util::kDataJS,
+ IDR_SYNC_DRIVER_SYNC_INTERNALS_DATA_JS);
+ source->AddResourcePath(syncer::sync_ui_util::kEventsJS,
+ IDR_SYNC_DRIVER_SYNC_INTERNALS_EVENTS_JS);
+ source->AddResourcePath(syncer::sync_ui_util::kSearchJS,
+ IDR_SYNC_DRIVER_SYNC_INTERNALS_SEARCH_JS);
+ source->AddResourcePath(syncer::sync_ui_util::kUserEventsJS,
+ IDR_SYNC_DRIVER_SYNC_INTERNALS_USER_EVENTS_JS);
+ source->SetDefaultResource(IDR_SYNC_DRIVER_SYNC_INTERNALS_INDEX_HTML);
+ return source;
+}
+
+} // namespace
+
+SyncInternalsUI::SyncInternalsUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreateSyncInternalsHTMLSource());
+
+ web_ui->AddMessageHandler(base::MakeUnique<SyncInternalsMessageHandler>());
+}
+
+SyncInternalsUI::~SyncInternalsUI() {}
+
diff --git a/chromium/chrome/browser/ui/webui/sync_internals_ui.h b/chromium/chrome/browser/ui/webui/sync_internals_ui.h
new file mode 100644
index 00000000000..e1a773d53e7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_internals_ui.h
@@ -0,0 +1,22 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SYNC_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SYNC_INTERNALS_UI_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The implementation for the chrome://sync-internals page.
+class SyncInternalsUI : public content::WebUIController {
+ public:
+ explicit SyncInternalsUI(content::WebUI* web_ui);
+ ~SyncInternalsUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SyncInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SYNC_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/sync_internals_ui_unittest.cc b/chromium/chrome/browser/ui/webui/sync_internals_ui_unittest.cc
new file mode 100644
index 00000000000..96933922e21
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_internals_ui_unittest.cc
@@ -0,0 +1,222 @@
+// 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/ui/webui/sync_internals_ui.h"
+
+#include <cstddef>
+#include <string>
+
+#include "base/message_loop/message_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/browser_sync/profile_sync_service_mock.h"
+#include "components/sync/js/js_arg_list.h"
+#include "components/sync/js/js_event_details.h"
+#include "components/sync/js/js_test_util.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::ASCIIToUTF16;
+
+// Rewrite to use WebUI testing infrastructure. Current code below is mostly
+// testing how WebUI concrete class serializes function parameters, and that
+// SyncInternalsUI::HandleJSEvent/HandleJsReply prefix the given function with
+// "chrome.sync." and postfix it with ".fire" or ".handleReply".
+// http://crbug.com/110517
+// Also, instead of using ProfileMock, call SetTestingFactoryAndUse on the
+// PasswordStoreFactory to set up a particular store during testing.
+/*
+
+namespace {
+
+using browser_sync::HasArgsAsList;
+using syncer::JsArgList;
+using syncer::JsEventDetails;
+using content::BrowserThread;
+using content::WebContents;
+using testing::_;
+using testing::Mock;
+using testing::NiceMock;
+using testing::Return;
+using testing::StrictMock;
+
+// Subclass of WebUI to mock out ExecuteJavascript.
+class TestSyncWebUI: public WebUI {
+ public:
+ explicit TestSyncWebUI(WebContents* web_contents)
+ : WebUI(web_contents) {}
+ virtual ~TestSyncWebUI() {}
+
+ MOCK_METHOD1(ExecuteJavascript, void(const base::string16&));
+};
+
+// Tests with non-NULL ProfileSyncService.
+class SyncInternalsUITestWithService : public ChromeRenderViewHostTestHarness {
+ protected:
+ SyncInternalsUITestWithService() : sync_internals_ui_(NULL) {}
+
+ virtual ~SyncInternalsUITestWithService() {}
+
+ virtual void SetUp() {
+ NiceMock<ProfileMock>* profile_mock = new NiceMock<ProfileMock>();
+ StrictMock<browser_sync::ProfileSyncServiceMock> profile_sync_service_mock;
+ EXPECT_CALL(*profile_mock, GetProfileSyncService())
+ .WillOnce(Return(&profile_sync_service_mock));
+ browser_context_.reset(profile_mock);
+
+ ChromeRenderViewHostTestHarness::SetUp();
+
+ EXPECT_CALL(profile_sync_service_mock, GetJsController())
+ .WillOnce(Return(mock_js_controller_.AsWeakPtr()));
+
+ EXPECT_CALL(mock_js_controller_, AddJsEventHandler(_));
+
+ {
+ // Needed by |sync_internals_ui_|'s constructor. The
+ // message loop is provided by ChromeRenderViewHostTestHarness.
+ content::TestBrowserThread ui_thread_(BrowserThread::UI,
+ base::MessageLoopForUI::current());
+ // |sync_internals_ui_|'s constructor triggers all the
+ // expectations above.
+ web_ui_.reset(new TestSyncWebUI(web_contents()));
+ sync_internals_ui_ = new SyncInternalsUI(web_ui_.get());
+ web_ui_->SetController(sync_internals_ui_);
+ }
+
+ Mock::VerifyAndClearExpectations(profile_mock);
+ Mock::VerifyAndClearExpectations(&mock_js_controller_);
+ }
+
+ virtual void TearDown() {
+ Mock::VerifyAndClearExpectations(&mock_js_controller_);
+
+ // Called by |sync_internals_ui_|'s destructor.
+ EXPECT_CALL(mock_js_controller_,
+ RemoveJsEventHandler(sync_internals_ui_));
+ sync_internals_ui_ = NULL;
+ web_ui_.reset();
+
+ ChromeRenderViewHostTestHarness::TearDown();
+ }
+
+ StrictMock<browser_sync::MockJsController> mock_js_controller_;
+ std::unique_ptr<TestSyncWebUI> web_ui_;
+ SyncInternalsUI* sync_internals_ui_;
+};
+
+TEST_F(SyncInternalsUITestWithService, HandleJsEvent) {
+ EXPECT_CALL(*web_ui_,
+ ExecuteJavascript(
+ ASCIIToUTF16("chrome.sync.testMessage.fire({});")));
+
+ sync_internals_ui_->HandleJsEvent("testMessage", JsEventDetails());
+}
+
+TEST_F(SyncInternalsUITestWithService, HandleJsReply) {
+ EXPECT_CALL(
+ *web_ui_,
+ ExecuteJavascript(
+ ASCIIToUTF16("chrome.sync.testMessage.handleReply(5,true);")));
+
+ base::ListValue args;
+ args.Append(new base::Value(5));
+ args.Append(new base::Value(true));
+ sync_internals_ui_->HandleJsReply("testMessage", JsArgList(&args));
+}
+
+TEST_F(SyncInternalsUITestWithService, OnWebUISendBasic) {
+ const std::string& name = "testName";
+ base::ListValue args;
+ args.Append(new base::Value(10));
+
+ EXPECT_CALL(mock_js_controller_,
+ ProcessJsMessage(name, HasArgsAsList(args), _));
+
+ sync_internals_ui_->OverrideHandleWebUIMessage(GURL(), name, args);
+}
+
+// Tests with NULL ProfileSyncService.
+class SyncInternalsUITestWithoutService
+ : public ChromeRenderViewHostTestHarness {
+ protected:
+ SyncInternalsUITestWithoutService() : sync_internals_ui_(NULL) {}
+
+ virtual ~SyncInternalsUITestWithoutService() {}
+
+ virtual void SetUp() {
+ NiceMock<ProfileMock>* profile_mock = new NiceMock<ProfileMock>();
+ EXPECT_CALL(*profile_mock, GetProfileSyncService())
+ .WillOnce(Return(static_cast<browser_sync::ProfileSyncService*>(NULL)));
+ browser_context_.reset(profile_mock);
+
+ ChromeRenderViewHostTestHarness::SetUp();
+
+ {
+ // Needed by |sync_internals_ui_|'s constructor. The
+ // message loop is provided by ChromeRenderViewHostTestHarness.
+ content::TestBrowserThread ui_thread_(BrowserThread::UI,
+ base::MessageLoopForUI::current());
+ // |sync_internals_ui_|'s constructor triggers all the
+ // expectations above.
+ web_ui_.reset(new TestSyncWebUI(web_contents()));
+ sync_internals_ui_ = new SyncInternalsUI(web_ui_.get());
+ web_ui_->SetController(sync_internals_ui_);
+ }
+
+ Mock::VerifyAndClearExpectations(profile_mock);
+ }
+
+ std::unique_ptr<TestSyncWebUI> web_ui_;
+ SyncInternalsUI* sync_internals_ui_;
+};
+
+TEST_F(SyncInternalsUITestWithoutService, HandleJsEvent) {
+ EXPECT_CALL(*web_ui_,
+ ExecuteJavascript(
+ ASCIIToUTF16("chrome.sync.testMessage.fire({});")));
+
+ sync_internals_ui_->HandleJsEvent("testMessage", JsEventDetails());
+}
+
+TEST_F(SyncInternalsUITestWithoutService, HandleJsReply) {
+ EXPECT_CALL(
+ *web_ui_,
+ ExecuteJavascript(
+ ASCIIToUTF16("chrome.sync.testMessage.handleReply(5,true);")));
+
+ base::ListValue args;
+ args.Append(new base::Value(5));
+ args.Append(new base::Value(true));
+ sync_internals_ui_->HandleJsReply(
+ "testMessage", JsArgList(&args));
+}
+
+TEST_F(SyncInternalsUITestWithoutService, OnWebUISendBasic) {
+ const std::string& name = "testName";
+ base::ListValue args;
+ args.Append(new base::Value(5));
+
+ // Should drop the message.
+ sync_internals_ui_->OverrideHandleWebUIMessage(GURL(), name, args);
+}
+
+// TODO(lipalani) - add a test case to test about:sync with a non null
+// service.
+TEST_F(SyncInternalsUITestWithoutService, OnWebUISendGetAboutInfo) {
+ const char kAboutInfoCall[] =
+ "chrome.sync.getAboutInfo.handleReply({\"summary\":\"SYNC DISABLED\"});";
+ EXPECT_CALL(*web_ui_,
+ ExecuteJavascript(ASCIIToUTF16(kAboutInfoCall)));
+
+ base::ListValue args;
+ sync_internals_ui_->OverrideHandleWebUIMessage(
+ GURL(), "getAboutInfo", args);
+}
+
+} // namespace
+
+*/
diff --git a/chromium/chrome/browser/ui/webui/sync_setup_browsertest.js b/chromium/chrome/browser/ui/webui/sync_setup_browsertest.js
new file mode 100644
index 00000000000..578dfd0b417
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/sync_setup_browsertest.js
@@ -0,0 +1,139 @@
+// 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.
+
+GEN('#if !defined(OS_CHROMEOS)');
+
+/**
+ * Test fixture for sync setup WebUI testing.
+ * @constructor
+ * @extends {testing.Test}
+ */
+function SyncSetupWebUITest() {}
+
+SyncSetupWebUITest.prototype = {
+ __proto__: testing.Test.prototype,
+
+ /**
+ * Browse to the settings sub-frame.
+ */
+ browsePreload: 'chrome://settings-frame',
+
+ /** @override */
+ preLoad: function() {
+ this.makeAndRegisterMockHandler(['SyncSetupConfigure',
+ 'SyncSetupShowSetupUI',
+ 'SyncSetupStartSignIn',
+ ]);
+ },
+
+ /**
+ * Verifies starting point is not synced.
+ */
+ verifyUnsynced: function() {
+ assertFalse(BrowserOptions.getInstance().signedIn_);
+ },
+
+ /**
+ * Clicks the "Sign in to Chrome" button.
+ */
+ startSyncing: function() {
+ var startStopSyncButton = BrowserOptions.getStartStopSyncButton();
+ assertNotEquals(null, startStopSyncButton);
+ startStopSyncButton.click();
+ },
+};
+
+/**
+ * Async version of SyncSetupWebUITest.
+ * @extends {SyncSetupWebUITest}
+ * @constructor
+ */
+function SyncSetupWebUITestAsync() {}
+
+SyncSetupWebUITestAsync.prototype = {
+ __proto__: SyncSetupWebUITest.prototype,
+
+ /** @override */
+ isAsync: true,
+
+ /**
+ * Verifies that initial state is unsynced and simulates signing in.
+ * @override
+ */
+ setUp: function() {
+ SyncSetupWebUITest.prototype.setUp.call(this);
+
+ // For testing, don't wait to execute timeouts.
+ var oldSetTimeout = setTimeout;
+ setTimeout = function(fn, timeout) {
+ oldSetTimeout(fn, 0);
+ };
+
+ // Make sure the user is not starting off in the signed in or syncing state.
+ this.verifyUnsynced();
+
+ // Handle SyncSetupShowSetupUI by navigating to chrome://settings/syncSetup.
+ this.mockHandler.expects(once()).SyncSetupShowSetupUI().will(callFunction(
+ function() {
+ PageManager.showPageByName('syncSetup');
+ }));
+ },
+};
+
+// This test is flaky on Linux bot (crbug.com/579666) and Windows & Mac bots
+// (crbug.com/608975).
+TEST_F('SyncSetupWebUITestAsync', 'DISABLED_VerifySignIn', function() {
+ // Handle SyncSetupStartSignIn by displaying the sync setup dialog, verifying
+ // that a confirmation dialog appears, and clicking OK to dismiss the dialog.
+ // Note that this test doesn't actually do a gaia sign in.
+ this.mockHandler.expects(once()).SyncSetupStartSignIn(
+ [false] /* createSupervisedUser */).will(callFunction(function() {
+ SyncSetupOverlay.showSyncSetupPage('configure');
+ var okButton = $('confirm-everything-ok');
+ assertNotEquals(null, okButton);
+ okButton.click();
+ }));
+
+ // The test completes after the sync config is sent out.
+ this.mockHandler.expects(once()).SyncSetupConfigure(ANYTHING).
+ will(callFunction(testDone));
+
+ // Click the "Sign in to Chrome..." button.
+ this.startSyncing();
+});
+
+// This test is flaky on Linux, Windows and Mac bots. See crbug.com/579666,
+// crbug.com/638884 and crbug.com/718947.
+GEN('#if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MACOSX)');
+GEN('#define MAYBE_RestoreSyncDataTypes DISABLED_RestoreSyncDataTypes');
+GEN('#else');
+GEN('#define MAYBE_RestoreSyncDataTypes RestoreSyncDataTypes');
+GEN('#endif // defined(OS_LINUX) || defined(OS_WIN)');
+// Test that switching to and from "Sync everything" saves and restores types.
+TEST_F('SyncSetupWebUITestAsync', 'MAYBE_RestoreSyncDataTypes', function() {
+ this.mockHandler.expects(once()).SyncSetupStartSignIn(
+ [false] /* createSupervisedUser */).will(callFunction(function() {
+ SyncSetupOverlay.showSyncSetupPage('configure', {});
+
+ $('sync-select-datatypes').selectedIndex = 1;
+ cr.dispatchSimpleEvent($('sync-select-datatypes'), 'change', true);
+
+ $('bookmarks-checkbox').checked = false;
+ cr.dispatchSimpleEvent($('bookmarks-checkbox'), 'change', true);
+
+ $('sync-select-datatypes').selectedIndex = 0;
+ cr.dispatchSimpleEvent($('sync-select-datatypes'), 'change', true);
+ assertTrue($('bookmarks-checkbox').checked);
+
+ $('sync-select-datatypes').selectedIndex = 1;
+ cr.dispatchSimpleEvent($('sync-select-datatypes'), 'change', true);
+ assertFalse($('bookmarks-checkbox').checked);
+
+ testDone();
+ }));
+
+ this.startSyncing();
+});
+
+GEN('#endif // OS_CHROMEOS');
diff --git a/chromium/chrome/browser/ui/webui/system_info_ui.cc b/chromium/chrome/browser/ui/webui/system_info_ui.cc
new file mode 100644
index 00000000000..ebf7121cc53
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/system_info_ui.cc
@@ -0,0 +1,201 @@
+// 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/system_info_ui.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/feedback/system_logs/about_system_logs_fetcher.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "net/base/directory_lister.h"
+#include "net/base/escape.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/jstemplate_builder.h"
+#include "ui/base/webui/web_ui_util.h"
+
+using content::WebContents;
+using content::WebUIMessageHandler;
+using system_logs::SystemLogsResponse;
+using system_logs::AboutSystemLogsFetcher;
+
+class SystemInfoUIHTMLSource : public content::URLDataSource{
+ public:
+ SystemInfoUIHTMLSource();
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ std::string GetMimeType(const std::string&) const override {
+ return "text/html";
+ }
+ std::string GetContentSecurityPolicyScriptSrc() const override {
+ // 'unsafe-inline' is added to script-src.
+ return "script-src 'self' chrome://resources 'unsafe-eval' "
+ "'unsafe-inline';";
+ }
+
+ std::string GetContentSecurityPolicyStyleSrc() const override {
+ return "style-src 'self' chrome://resources 'unsafe-inline';";
+ }
+
+ private:
+ ~SystemInfoUIHTMLSource() override {}
+
+ void SysInfoComplete(std::unique_ptr<SystemLogsResponse> response);
+ void RequestComplete();
+ void WaitForData();
+
+ // Stored data from StartDataRequest()
+ std::string path_;
+ content::URLDataSource::GotDataCallback callback_;
+
+ std::unique_ptr<SystemLogsResponse> response_;
+ base::WeakPtrFactory<SystemInfoUIHTMLSource> weak_ptr_factory_;
+ DISALLOW_COPY_AND_ASSIGN(SystemInfoUIHTMLSource);
+};
+
+// The handler for Javascript messages related to the "system" view.
+class SystemInfoHandler : public WebUIMessageHandler,
+ public base::SupportsWeakPtr<SystemInfoHandler> {
+ public:
+ SystemInfoHandler();
+ ~SystemInfoHandler() override;
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SystemInfoHandler);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// SystemInfoUIHTMLSource
+//
+////////////////////////////////////////////////////////////////////////////////
+
+SystemInfoUIHTMLSource::SystemInfoUIHTMLSource() : weak_ptr_factory_(this) {}
+
+std::string SystemInfoUIHTMLSource::GetSource() const {
+ return chrome::kChromeUISystemInfoHost;
+}
+
+void SystemInfoUIHTMLSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ path_ = path;
+ callback_ = callback;
+
+ AboutSystemLogsFetcher* fetcher = new AboutSystemLogsFetcher();
+ fetcher->Fetch(base::Bind(&SystemInfoUIHTMLSource::SysInfoComplete,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void SystemInfoUIHTMLSource::SysInfoComplete(
+ std::unique_ptr<SystemLogsResponse> sys_info) {
+ response_ = std::move(sys_info);
+ RequestComplete();
+}
+
+void SystemInfoUIHTMLSource::RequestComplete() {
+ base::DictionaryValue strings;
+ strings.SetString("title", l10n_util::GetStringUTF16(IDS_ABOUT_SYS_TITLE));
+ strings.SetString("description",
+ l10n_util::GetStringUTF16(IDS_ABOUT_SYS_DESC));
+ strings.SetString("tableTitle",
+ l10n_util::GetStringUTF16(IDS_ABOUT_SYS_TABLE_TITLE));
+ strings.SetString(
+ "logFileTableTitle",
+ l10n_util::GetStringUTF16(IDS_ABOUT_SYS_LOG_FILE_TABLE_TITLE));
+ strings.SetString("expandAllBtn",
+ l10n_util::GetStringUTF16(IDS_ABOUT_SYS_EXPAND_ALL));
+ strings.SetString("collapseAllBtn",
+ l10n_util::GetStringUTF16(IDS_ABOUT_SYS_COLLAPSE_ALL));
+ strings.SetString("expandBtn",
+ l10n_util::GetStringUTF16(IDS_ABOUT_SYS_EXPAND));
+ strings.SetString("collapseBtn",
+ l10n_util::GetStringUTF16(IDS_ABOUT_SYS_COLLAPSE));
+ strings.SetString("parseError",
+ l10n_util::GetStringUTF16(IDS_ABOUT_SYS_PARSE_ERROR));
+
+ const std::string& app_locale = g_browser_process->GetApplicationLocale();
+ webui::SetLoadTimeDataDefaults(app_locale, &strings);
+
+ if (response_.get()) {
+ auto details = base::MakeUnique<base::ListValue>();
+ for (SystemLogsResponse::const_iterator it = response_->begin();
+ it != response_->end();
+ ++it) {
+ std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue);
+ val->SetString("statName", it->first);
+ val->SetString("statValue", it->second);
+ details->Append(std::move(val));
+ }
+ strings.Set("details", std::move(details));
+ }
+ static const base::StringPiece systeminfo_html(
+ ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_ABOUT_SYS_HTML));
+ std::string full_html = webui::GetI18nTemplateHtml(systeminfo_html, &strings);
+ callback_.Run(base::RefCountedString::TakeString(&full_html));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// SystemInfoHandler
+//
+////////////////////////////////////////////////////////////////////////////////
+SystemInfoHandler::SystemInfoHandler() {
+}
+
+SystemInfoHandler::~SystemInfoHandler() {
+}
+
+void SystemInfoHandler::RegisterMessages() {
+ // TODO(stevenjb): add message registration, callbacks...
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// SystemInfoUI
+//
+////////////////////////////////////////////////////////////////////////////////
+
+SystemInfoUI::SystemInfoUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<SystemInfoHandler>());
+ SystemInfoUIHTMLSource* html_source = new SystemInfoUIHTMLSource();
+
+ // Set up the chrome://system/ source.
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::URLDataSource::Add(profile, html_source);
+}
diff --git a/chromium/chrome/browser/ui/webui/system_info_ui.h b/chromium/chrome/browser/ui/webui/system_info_ui.h
new file mode 100644
index 00000000000..5671021a1d1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/system_info_ui.h
@@ -0,0 +1,19 @@
+// 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_SYSTEM_INFO_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_SYSTEM_INFO_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+class SystemInfoUI : public content::WebUIController {
+ public:
+ explicit SystemInfoUI(content::WebUI* web_ui);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SystemInfoUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_SYSTEM_INFO_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/task_scheduler_internals/OWNERS b/chromium/chrome/browser/ui/webui/task_scheduler_internals/OWNERS
new file mode 100644
index 00000000000..ffc802c14e6
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/task_scheduler_internals/OWNERS
@@ -0,0 +1 @@
+file://base/task_scheduler/OWNERS
diff --git a/chromium/chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui.cc b/chromium/chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui.cc
new file mode 100644
index 00000000000..7568aae2420
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui.cc
@@ -0,0 +1,117 @@
+// 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 "chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/task_scheduler/task_scheduler.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/task_scheduler_internals_resources.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace {
+
+std::unique_ptr<base::Value> SnapshotHistogramToValue(
+ const base::HistogramBase* histogram) {
+ std::unique_ptr<base::ListValue> values =
+ base::MakeUnique<base::ListValue>();
+
+ std::unique_ptr<base::HistogramSamples> samples =
+ histogram->SnapshotSamples();
+ std::unique_ptr<base::SampleCountIterator> iterator = samples->Iterator();
+ while (!iterator->Done()) {
+ base::HistogramBase::Sample min;
+ int64_t max;
+ base::HistogramBase::Count count;
+ iterator->Get(&min, &max, &count);
+
+ std::unique_ptr<base::DictionaryValue> bucket =
+ base::MakeUnique<base::DictionaryValue>();
+ bucket->SetInteger("min", min);
+ // Note: DictionaryValue does not support 64-bit integer values. The checked
+ // cast below is OK in this case because none of the histograms passed to
+ // this function should be logging MaxInt32 as a sparse histogram bucket,
+ // which is the only case max will exceed 32-bit range.
+ bucket->SetInteger("max", base::checked_cast<int>(max));
+ bucket->SetInteger("count", count);
+
+ values->Append(std::move(bucket));
+ iterator->Next();
+ }
+ return std::move(values);
+}
+
+class TaskSchedulerDataHandler : public content::WebUIMessageHandler {
+ public:
+ TaskSchedulerDataHandler() = default;
+
+ // content::WebUIMessageHandler:
+ void RegisterMessages() override {
+ web_ui()->RegisterMessageCallback(
+ "getTaskSchedulerData",
+ base::Bind(&TaskSchedulerDataHandler::GetTaskSchedulerData,
+ base::Unretained(this)));
+ }
+
+ private:
+ void GetTaskSchedulerData(const base::ListValue*) {
+ base::DictionaryValue data;
+
+ base::TaskScheduler* task_scheduler = base::TaskScheduler::GetInstance();
+ data.SetBoolean("instantiated", !!task_scheduler);
+
+ if (task_scheduler) {
+ std::unique_ptr<base::ListValue> histogram_value =
+ base::MakeUnique<base::ListValue>();
+ std::vector<const base::HistogramBase*> histograms =
+ task_scheduler->GetHistograms();
+
+ for (const base::HistogramBase* const histogram : histograms) {
+ std::unique_ptr<base::DictionaryValue> buckets =
+ base::MakeUnique<base::DictionaryValue>();
+ buckets->SetString("name", histogram->histogram_name());
+ buckets->Set("buckets", SnapshotHistogramToValue(histogram));
+ histogram_value->Append(std::move(buckets));
+ }
+
+ data.Set("histograms", std::move(histogram_value));
+ }
+
+ AllowJavascript();
+ CallJavascriptFunction(
+ "TaskSchedulerInternals.onGetTaskSchedulerData", data);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(TaskSchedulerDataHandler);
+};
+
+} // namespace
+
+TaskSchedulerInternalsUI::TaskSchedulerInternalsUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<TaskSchedulerDataHandler>());
+
+ content::WebUIDataSource* html_source =
+ content::WebUIDataSource::Create(
+ chrome::kChromeUITaskSchedulerInternalsHost);
+ html_source->AddResourcePath(
+ "index.css", IDR_TASK_SCHEDULER_INTERNALS_RESOURCES_INDEX_CSS);
+ html_source->AddResourcePath(
+ "index.js", IDR_TASK_SCHEDULER_INTERNALS_RESOURCES_INDEX_JS);
+ html_source->SetDefaultResource(
+ IDR_TASK_SCHEDULER_INTERNALS_RESOURCES_INDEX_HTML);
+
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), html_source);
+}
+
+TaskSchedulerInternalsUI::~TaskSchedulerInternalsUI() = default;
diff --git a/chromium/chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui.h b/chromium/chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui.h
new file mode 100644
index 00000000000..3af79735096
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_TASK_SCHEDULER_INTERNALS_TASK_SCHEDULER_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_TASK_SCHEDULER_INTERNALS_TASK_SCHEDULER_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The WebUI for chrome://taskscheduler-internals.
+class TaskSchedulerInternalsUI : public content::WebUIController {
+ public:
+ explicit TaskSchedulerInternalsUI(content::WebUI* web_ui);
+ ~TaskSchedulerInternalsUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TaskSchedulerInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_TASK_SCHEDULER_INTERNALS_TASK_SCHEDULER_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui_browsertest.cc
new file mode 100644
index 00000000000..84f8b5bda2d
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/task_scheduler_internals/task_scheduler_internals_ui_browsertest.cc
@@ -0,0 +1,41 @@
+// 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 "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "chrome/test/base/web_ui_browser_test.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/url_constants.h"
+#include "ui/base/window_open_disposition.h"
+
+class TaskSchedulerInternalsWebUIBrowserTest : public WebUIBrowserTest {
+ public:
+ TaskSchedulerInternalsWebUIBrowserTest() = default;
+ ~TaskSchedulerInternalsWebUIBrowserTest() override = default;
+
+ void SetUpOnMainThread() override {
+ WebUIBrowserTest::SetUpOnMainThread();
+ std::string url_string(content::kChromeUIScheme);
+ url_string += "://";
+ url_string += chrome::kChromeUITaskSchedulerInternalsHost;
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(),
+ GURL(url_string),
+ WindowOpenDisposition::CURRENT_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TaskSchedulerInternalsWebUIBrowserTest);
+};
+
+// Test that navigation to the internals page works.
+IN_PROC_BROWSER_TEST_F(TaskSchedulerInternalsWebUIBrowserTest, Navigate) {
+ EXPECT_EQ(base::ASCIIToUTF16("Task Scheduler Internals"),
+ browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
+}
diff --git a/chromium/chrome/browser/ui/webui/test_files_request_filter.cc b/chromium/chrome/browser/ui/webui/test_files_request_filter.cc
new file mode 100644
index 00000000000..2d5291221d4
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/test_files_request_filter.cc
@@ -0,0 +1,48 @@
+// 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 "chrome/browser/ui/webui/test_files_request_filter.h"
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/path_service.h"
+#include "base/strings/string_split.h"
+#include "base/threading/thread_restrictions.h"
+#include "chrome/common/chrome_paths.h"
+
+namespace {
+
+bool HandleTestFileRequestCallback(
+ const std::string& path,
+ const content::WebUIDataSource::GotDataCallback& callback) {
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+ std::vector<std::string> url_substr =
+ base::SplitString(path, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+ if (url_substr.size() != 2 || url_substr[0] != "test")
+ return false;
+
+ std::string contents;
+ base::FilePath test_data_dir;
+ PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
+ if (!base::ReadFileToString(
+ test_data_dir.AppendASCII("webui").AppendASCII(url_substr[1]),
+ &contents))
+ return false;
+
+ base::RefCountedString* ref_contents = new base::RefCountedString();
+ ref_contents->data() = contents;
+ callback.Run(ref_contents);
+ return true;
+}
+
+} // namespace
+
+namespace test {
+
+content::WebUIDataSource::HandleRequestCallback GetTestFilesRequestFilter() {
+ return base::Bind(&HandleTestFileRequestCallback);
+}
+
+} // namespace test
diff --git a/chromium/chrome/browser/ui/webui/test_files_request_filter.h b/chromium/chrome/browser/ui/webui/test_files_request_filter.h
new file mode 100644
index 00000000000..e6e49bd7e6c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/test_files_request_filter.h
@@ -0,0 +1,19 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_TEST_FILES_REQUEST_FILTER_H_
+#define CHROME_BROWSER_UI_WEBUI_TEST_FILES_REQUEST_FILTER_H_
+
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace test {
+
+// Returns a callback to be used as a filter in WebUIDataSource.
+// The filter responds with a content of "%DIR_TEST_DATA%/webui/<filename>" if
+// request path has "/test/<filename>" format.
+content::WebUIDataSource::HandleRequestCallback GetTestFilesRequestFilter();
+
+} // namespace test
+
+#endif // CHROME_BROWSER_UI_WEBUI_TEST_FILES_REQUEST_FILTER_H_
diff --git a/chromium/chrome/browser/ui/webui/theme_handler.cc b/chromium/chrome/browser/ui/webui/theme_handler.cc
new file mode 100644
index 00000000000..b1aaf4cdfbd
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/theme_handler.cc
@@ -0,0 +1,58 @@
+// 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/ui/webui/theme_handler.h"
+
+#include "base/values.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/themes/theme_service_factory.h"
+#include "chrome/browser/ui/webui/theme_source.h"
+#include "chrome/grit/theme_resources.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_ui.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// ThemeHandler
+
+ThemeHandler::ThemeHandler() {
+}
+
+ThemeHandler::~ThemeHandler() {
+}
+
+void ThemeHandler::RegisterMessages() {
+ // These are not actual message registrations, but can't be done in the
+ // constructor since they need the web_ui value to be set, which is done
+ // post-construction, but before registering messages.
+ InitializeCSSCaches();
+ // Listen for theme installation.
+ registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
+ content::Source<ThemeService>(
+ ThemeServiceFactory::GetForProfile(GetProfile())));
+}
+
+void ThemeHandler::Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK_EQ(chrome::NOTIFICATION_BROWSER_THEME_CHANGED, type);
+ InitializeCSSCaches();
+ bool has_custom_bg = ThemeService::GetThemeProviderForProfile(GetProfile())
+ .HasCustomImage(IDR_THEME_NTP_BACKGROUND);
+ // TODO(dbeam): why does this need to be a dictionary?
+ base::DictionaryValue dictionary;
+ dictionary.SetBoolean("hasCustomBackground", has_custom_bg);
+ web_ui()->CallJavascriptFunctionUnsafe("ntp.themeChanged", dictionary);
+}
+
+void ThemeHandler::InitializeCSSCaches() {
+ Profile* profile = GetProfile();
+ ThemeSource* theme = new ThemeSource(profile);
+ content::URLDataSource::Add(profile, theme);
+}
+
+Profile* ThemeHandler::GetProfile() const {
+ return Profile::FromWebUI(web_ui());
+}
diff --git a/chromium/chrome/browser/ui/webui/theme_handler.h b/chromium/chrome/browser/ui/webui/theme_handler.h
new file mode 100644
index 00000000000..7c2b88b2514
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/theme_handler.h
@@ -0,0 +1,41 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_THEME_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_THEME_HANDLER_H_
+
+#include "base/macros.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+class Profile;
+
+// A class to keep the ThemeSource up to date when theme changes.
+class ThemeHandler : public content::WebUIMessageHandler,
+ public content::NotificationObserver {
+ public:
+ explicit ThemeHandler();
+ ~ThemeHandler() override;
+
+ private:
+ // content::WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // Re/set the CSS caches.
+ void InitializeCSSCaches();
+
+ // content::NotificationObserver implementation.
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ Profile* GetProfile() const;
+
+ content::NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThemeHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_THEME_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/theme_source.cc b/chromium/chrome/browser/ui/webui/theme_source.cc
new file mode 100644
index 00000000000..a48745c145e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/theme_source.cc
@@ -0,0 +1,229 @@
+// 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/ui/webui/theme_source.h"
+
+#include "base/memory/ref_counted_memory.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/resources_util.h"
+#include "chrome/browser/search/instant_io_context.h"
+#include "chrome/browser/themes/browser_theme_pack.h"
+#include "chrome/browser/themes/theme_properties.h"
+#include "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/themes/theme_service_factory.h"
+#include "chrome/browser/ui/webui/ntp/ntp_resource_cache.h"
+#include "chrome/browser/ui/webui/ntp/ntp_resource_cache_factory.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/theme_resources.h"
+#include "components/version_info/version_info.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/url_request/url_request.h"
+#include "ui/base/layout.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/image/image_skia_rep.h"
+#include "url/gurl.h"
+
+namespace {
+
+GURL GetThemeUrl(const std::string& path) {
+ return GURL(std::string(content::kChromeUIScheme) + "://" +
+ std::string(chrome::kChromeUIThemeHost) + "/" + path);
+}
+
+bool IsNewTabCssPath(const std::string& path) {
+ static const char kNewTabCSSPath[] = "css/new_tab_theme.css";
+ static const char kIncognitoNewTabCSSPath[] =
+ "css/incognito_new_tab_theme.css";
+ return (path == kNewTabCSSPath) || (path == kIncognitoNewTabCSSPath);
+}
+
+void ProcessImageOnUiThread(const gfx::ImageSkia& image,
+ float scale,
+ scoped_refptr<base::RefCountedBytes> data) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ const gfx::ImageSkiaRep& rep = image.GetRepresentation(scale);
+ gfx::PNGCodec::EncodeBGRASkBitmap(
+ rep.sk_bitmap(), false /* discard transparency */, &data->data());
+}
+
+void ProcessResourceOnUiThread(int resource_id,
+ float scale,
+ scoped_refptr<base::RefCountedBytes> data) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ ProcessImageOnUiThread(
+ *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id),
+ scale, data);
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// ThemeSource, public:
+
+ThemeSource::ThemeSource(Profile* profile) : profile_(profile) {}
+
+ThemeSource::~ThemeSource() = default;
+
+std::string ThemeSource::GetSource() const {
+ return chrome::kChromeUIThemeHost;
+}
+
+void ThemeSource::StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) {
+ // Default scale factor if not specified.
+ float scale = 1.0f;
+ std::string parsed_path;
+ webui::ParsePathAndScale(GetThemeUrl(path), &parsed_path, &scale);
+
+ if (IsNewTabCssPath(parsed_path)) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ NTPResourceCache::WindowType type =
+ NTPResourceCache::GetWindowType(profile_, /*render_host=*/nullptr);
+ NTPResourceCache* cache = NTPResourceCacheFactory::GetForProfile(profile_);
+ callback.Run(cache->GetNewTabCSS(type));
+ return;
+ }
+
+ int resource_id = -1;
+ if (parsed_path == "current-channel-logo") {
+ switch (chrome::GetChannel()) {
+#if defined(GOOGLE_CHROME_BUILD)
+ case version_info::Channel::CANARY:
+ resource_id = IDR_PRODUCT_LOGO_32_CANARY;
+ break;
+ case version_info::Channel::DEV:
+ resource_id = IDR_PRODUCT_LOGO_32_DEV;
+ break;
+ case version_info::Channel::BETA:
+ resource_id = IDR_PRODUCT_LOGO_32_BETA;
+ break;
+ case version_info::Channel::STABLE:
+ resource_id = IDR_PRODUCT_LOGO_32;
+ break;
+#else
+ case version_info::Channel::CANARY:
+ case version_info::Channel::DEV:
+ case version_info::Channel::BETA:
+ case version_info::Channel::STABLE:
+ NOTREACHED();
+#endif
+ case version_info::Channel::UNKNOWN:
+ resource_id = IDR_PRODUCT_LOGO_32;
+ break;
+ }
+ } else {
+ resource_id = ResourcesUtil::GetThemeResourceId(parsed_path);
+ }
+
+ // Limit the maximum scale we'll respond to. Very large scale factors can
+ // take significant time to serve or, at worst, crash the browser due to OOM.
+ // We don't want to clamp to the max scale factor, though, for devices that
+ // use 2x scale without 2x data packs, as well as omnibox requests for larger
+ // (but still reasonable) scales (see below).
+ const float max_scale = ui::GetScaleForScaleFactor(
+ ResourceBundle::GetSharedInstance().GetMaxScaleFactor());
+ const float unreasonable_scale = max_scale * 32;
+ if ((resource_id == -1) || (scale >= unreasonable_scale)) {
+ // Either we have no data to send back, or the requested scale is
+ // unreasonably large. This shouldn't happen normally, as chrome://theme/
+ // URLs are only used by WebUI pages and component extensions. However, the
+ // user can also enter these into the omnibox, so we need to fail
+ // gracefully.
+ callback.Run(nullptr);
+ } else if ((GetMimeType(path) == "image/png") && (scale > max_scale)) {
+ SendThemeImage(callback, resource_id, scale);
+ } else {
+ SendThemeBitmap(callback, resource_id, scale);
+ }
+}
+
+std::string ThemeSource::GetMimeType(const std::string& path) const {
+ std::string parsed_path;
+ webui::ParsePathAndScale(GetThemeUrl(path), &parsed_path, nullptr);
+ return IsNewTabCssPath(parsed_path) ? "text/css" : "image/png";
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+ThemeSource::TaskRunnerForRequestPath(const std::string& path) const {
+ std::string parsed_path;
+ webui::ParsePathAndScale(GetThemeUrl(path), &parsed_path, nullptr);
+
+ if (IsNewTabCssPath(parsed_path)) {
+ // We'll get this data from the NTPResourceCache, which must be accessed on
+ // the UI thread.
+ return content::URLDataSource::TaskRunnerForRequestPath(path);
+ }
+
+ // If it's not a themeable image, we don't need to go to the UI thread.
+ int resource_id = ResourcesUtil::GetThemeResourceId(parsed_path);
+ return BrowserThemePack::IsPersistentImageID(resource_id)
+ ? content::URLDataSource::TaskRunnerForRequestPath(path)
+ : nullptr;
+}
+
+bool ThemeSource::AllowCaching() const {
+ return false;
+}
+
+bool ThemeSource::ShouldServiceRequest(
+ const GURL& url,
+ content::ResourceContext* resource_context,
+ int render_process_id) const {
+ return url.SchemeIs(chrome::kChromeSearchScheme)
+ ? InstantIOContext::ShouldServiceRequest(url, resource_context,
+ render_process_id)
+ : URLDataSource::ShouldServiceRequest(url, resource_context,
+ render_process_id);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ThemeSource, private:
+
+void ThemeSource::SendThemeBitmap(
+ const content::URLDataSource::GotDataCallback& callback,
+ int resource_id,
+ float scale) {
+ ui::ScaleFactor scale_factor = ui::GetSupportedScaleFactor(scale);
+ if (BrowserThemePack::IsPersistentImageID(resource_id)) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ scoped_refptr<base::RefCountedMemory> image_data(
+ ThemeService::GetThemeProviderForProfile(profile_->GetOriginalProfile())
+ .GetRawData(resource_id, scale_factor));
+ callback.Run(image_data.get());
+ } else {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+ const ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ callback.Run(rb.LoadDataResourceBytesForScale(resource_id, scale_factor));
+ }
+}
+
+void ThemeSource::SendThemeImage(
+ const content::URLDataSource::GotDataCallback& callback,
+ int resource_id,
+ float scale) {
+ scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes());
+ if (BrowserThemePack::IsPersistentImageID(resource_id)) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ const ui::ThemeProvider& tp = ThemeService::GetThemeProviderForProfile(
+ profile_->GetOriginalProfile());
+ ProcessImageOnUiThread(*tp.GetImageSkiaNamed(resource_id), scale, data);
+ callback.Run(data.get());
+ } else {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+ // Fetching image data in ResourceBundle should happen on the UI thread. See
+ // crbug.com/449277
+ content::BrowserThread::PostTaskAndReply(
+ content::BrowserThread::UI, FROM_HERE,
+ base::BindOnce(&ProcessResourceOnUiThread, resource_id, scale, data),
+ base::BindOnce(callback, data));
+ }
+}
diff --git a/chromium/chrome/browser/ui/webui/theme_source.h b/chromium/chrome/browser/ui/webui/theme_source.h
new file mode 100644
index 00000000000..724e01fa27c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/theme_source.h
@@ -0,0 +1,56 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_THEME_SOURCE_H_
+#define CHROME_BROWSER_UI_WEBUI_THEME_SOURCE_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "content/public/browser/url_data_source.h"
+
+class Profile;
+
+class ThemeSource : public content::URLDataSource {
+ public:
+ explicit ThemeSource(Profile* profile);
+ ~ThemeSource() override;
+
+ // content::URLDataSource implementation.
+ std::string GetSource() const override;
+ void StartDataRequest(
+ const std::string& path,
+ const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
+ const content::URLDataSource::GotDataCallback& callback) override;
+ std::string GetMimeType(const std::string& path) const override;
+ scoped_refptr<base::SingleThreadTaskRunner> TaskRunnerForRequestPath(
+ const std::string& path) const override;
+ bool AllowCaching() const override;
+ bool ShouldServiceRequest(const GURL& url,
+ content::ResourceContext* resource_context,
+ int render_process_id) const override;
+
+ private:
+ // Fetches and sends the theme bitmap.
+ void SendThemeBitmap(const content::URLDataSource::GotDataCallback& callback,
+ int resource_id,
+ float scale);
+
+ // Used in place of SendThemeBitmap when the desired scale is larger than
+ // what the resource bundle supports. This can rescale the provided bitmap up
+ // to the desired size.
+ void SendThemeImage(const content::URLDataSource::GotDataCallback& callback,
+ int resource_id,
+ float scale);
+
+ // The profile this object was initialized with.
+ Profile* profile_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThemeSource);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_THEME_SOURCE_H_
diff --git a/chromium/chrome/browser/ui/webui/theme_source_unittest.cc b/chromium/chrome/browser/ui/webui/theme_source_unittest.cc
new file mode 100644
index 00000000000..1fe1c490c21
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/theme_source_unittest.cc
@@ -0,0 +1,99 @@
+// 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 <stddef.h>
+
+#include "base/bind.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/theme_source.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/theme_resources.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class WebUISourcesTest : public testing::Test {
+ public:
+ WebUISourcesTest() : result_data_size_(0) {}
+
+ TestingProfile* profile() const { return profile_.get(); }
+ ThemeSource* theme_source() const { return theme_source_.get(); }
+ size_t result_data_size() const { return result_data_size_; }
+
+ void StartDataRequest(const std::string& source) {
+ theme_source()->StartDataRequest(
+ source,
+ content::ResourceRequestInfo::WebContentsGetter(),
+ callback_);
+ }
+
+ size_t result_data_size_;
+
+ private:
+ void SetUp() override {
+ profile_.reset(new TestingProfile());
+ theme_source_.reset(new ThemeSource(profile_.get()));
+ callback_ = base::Bind(&WebUISourcesTest::SendResponse,
+ base::Unretained(this));
+ }
+
+ void TearDown() override {
+ theme_source_.reset();
+ profile_.reset();
+ }
+
+ void SendResponse(scoped_refptr<base::RefCountedMemory> data) {
+ result_data_size_ = data ? data->size() : 0;
+ }
+
+ content::URLDataSource::GotDataCallback callback_;
+
+ content::TestBrowserThreadBundle test_browser_thread_bundle_;
+
+ std::unique_ptr<TestingProfile> profile_;
+ std::unique_ptr<ThemeSource> theme_source_;
+};
+
+TEST_F(WebUISourcesTest, ThemeSourceMimeTypes) {
+ EXPECT_EQ(theme_source()->GetMimeType("css/new_tab_theme.css"), "text/css");
+ EXPECT_EQ(theme_source()->GetMimeType("css/new_tab_theme.css?foo"),
+ "text/css");
+ EXPECT_EQ(theme_source()->GetMimeType("WRONGURL"), "image/png");
+}
+
+TEST_F(WebUISourcesTest, ThemeSourceImages) {
+ // We used to PNGEncode the images ourselves, but encoder differences
+ // invalidated that. We now just check that the image exists.
+ StartDataRequest("IDR_THEME_FRAME_INCOGNITO");
+ size_t min = 0;
+ EXPECT_GT(result_data_size_, min);
+
+ StartDataRequest("IDR_THEME_TOOLBAR");
+ EXPECT_GT(result_data_size_, min);
+}
+
+TEST_F(WebUISourcesTest, ThemeSourceCSS) {
+ // Generating the test data for the NTP CSS would just involve copying the
+ // method, or being super brittle and hard-coding the result (requiring
+ // an update to the unittest every time the CSS template changes), so we
+ // just check for a successful request and data that is non-null.
+ size_t empty_size = 0;
+
+ StartDataRequest("css/new_tab_theme.css");
+ base::RunLoop().RunUntilIdle();
+ EXPECT_NE(result_data_size_, empty_size);
+
+ StartDataRequest("css/new_tab_theme.css?pie");
+ base::RunLoop().RunUntilIdle();
+ EXPECT_NE(result_data_size_, empty_size);
+
+#if !DCHECK_IS_ON()
+ // Check that we send NULL back when we can't find what we're looking for.
+ StartDataRequest("css/WRONGURL");
+ EXPECT_EQ(result_data_size_, empty_size);
+#endif
+}
diff --git a/chromium/chrome/browser/ui/webui/translate_internals/OWNERS b/chromium/chrome/browser/ui/webui/translate_internals/OWNERS
new file mode 100644
index 00000000000..bb5c3afcabc
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/translate_internals/OWNERS
@@ -0,0 +1 @@
+file://components/translate/OWNERS
diff --git a/chromium/chrome/browser/ui/webui/translate_internals/translate_internals_handler.cc b/chromium/chrome/browser/ui/webui/translate_internals/translate_internals_handler.cc
new file mode 100644
index 00000000000..d2f80d1f756
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/translate_internals/translate_internals_handler.cc
@@ -0,0 +1,252 @@
+// 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/translate_internals/translate_internals_handler.h"
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/translate/chrome_translate_client.h"
+#include "chrome/browser/translate/translate_service.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "components/translate/core/browser/translate_download_manager.h"
+#include "components/translate/core/browser/translate_error_details.h"
+#include "components/translate/core/browser/translate_event_details.h"
+#include "components/translate/core/browser/translate_pref_names.h"
+#include "components/translate/core/browser/translate_prefs.h"
+#include "components/translate/core/common/language_detection_details.h"
+#include "components/variations/service/variations_service.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+
+TranslateInternalsHandler::TranslateInternalsHandler() {
+ notification_registrar_.Add(this,
+ chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED,
+ content::NotificationService::AllSources());
+
+ error_subscription_ =
+ translate::TranslateManager::RegisterTranslateErrorCallback(
+ base::Bind(&TranslateInternalsHandler::OnTranslateError,
+ base::Unretained(this)));
+
+ translate::TranslateLanguageList* language_list =
+ translate::TranslateDownloadManager::GetInstance()->language_list();
+ if (!language_list) {
+ NOTREACHED();
+ return;
+ }
+
+ event_subscription_ = language_list->RegisterEventCallback(base::Bind(
+ &TranslateInternalsHandler::OnTranslateEvent, base::Unretained(this)));
+}
+
+TranslateInternalsHandler::~TranslateInternalsHandler() {
+ // |event_subscription_| and |error_subscription_| are deleted automatically
+ // and un-register the callbacks automatically.
+}
+
+void TranslateInternalsHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "removePrefItem", base::Bind(&TranslateInternalsHandler::OnRemovePrefItem,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "requestInfo", base::Bind(&TranslateInternalsHandler::OnRequestInfo,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "overrideCountry",
+ base::Bind(&TranslateInternalsHandler::OnOverrideCountry,
+ base::Unretained(this)));
+}
+
+void TranslateInternalsHandler::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK_EQ(chrome::NOTIFICATION_TAB_LANGUAGE_DETERMINED, type);
+ const translate::LanguageDetectionDetails* language_detection_details =
+ content::Details<const translate::LanguageDetectionDetails>(details)
+ .ptr();
+ content::WebContents* web_contents =
+ content::Source<content::WebContents>(source).ptr();
+
+ if (web_contents->GetBrowserContext()->IsOffTheRecord() ||
+ !TranslateService::IsTranslatableURL(language_detection_details->url)) {
+ return;
+ }
+
+ base::DictionaryValue dict;
+ dict.SetDouble("time", language_detection_details->time.ToJsTime());
+ dict.SetString("url", language_detection_details->url.spec());
+ dict.SetString("content_language",
+ language_detection_details->content_language);
+ dict.SetString("cld_language", language_detection_details->cld_language);
+ dict.SetBoolean("is_cld_reliable",
+ language_detection_details->is_cld_reliable);
+ dict.SetBoolean("has_notranslate",
+ language_detection_details->has_notranslate);
+ dict.SetString("html_root_language",
+ language_detection_details->html_root_language);
+ dict.SetString("adopted_language",
+ language_detection_details->adopted_language);
+ dict.SetString("content", language_detection_details->contents);
+ SendMessageToJs("languageDetectionInfoAdded", dict);
+}
+
+void TranslateInternalsHandler::OnTranslateError(
+ const translate::TranslateErrorDetails& details) {
+ base::DictionaryValue dict;
+ dict.SetDouble("time", details.time.ToJsTime());
+ dict.SetString("url", details.url.spec());
+ dict.SetInteger("error", details.error);
+ SendMessageToJs("translateErrorDetailsAdded", dict);
+}
+
+void TranslateInternalsHandler::OnTranslateEvent(
+ const translate::TranslateEventDetails& details) {
+ base::DictionaryValue dict;
+ dict.SetDouble("time", details.time.ToJsTime());
+ dict.SetString("filename", details.filename);
+ dict.SetInteger("line", details.line);
+ dict.SetString("message", details.message);
+ SendMessageToJs("translateEventDetailsAdded", dict);
+}
+
+void TranslateInternalsHandler::OnRemovePrefItem(const base::ListValue* args) {
+ content::WebContents* web_contents = web_ui()->GetWebContents();
+ Profile* profile =
+ Profile::FromBrowserContext(web_contents->GetBrowserContext());
+ PrefService* prefs = profile->GetOriginalProfile()->GetPrefs();
+ std::unique_ptr<translate::TranslatePrefs> translate_prefs(
+ ChromeTranslateClient::CreateTranslatePrefs(prefs));
+
+ std::string pref_name;
+ if (!args->GetString(0, &pref_name))
+ return;
+
+ if (pref_name == "blocked_languages") {
+ std::string language;
+ if (!args->GetString(1, &language))
+ return;
+ translate_prefs->UnblockLanguage(language);
+ } else if (pref_name == "site_blacklist") {
+ std::string site;
+ if (!args->GetString(1, &site))
+ return;
+ translate_prefs->RemoveSiteFromBlacklist(site);
+ } else if (pref_name == "whitelists") {
+ std::string from, to;
+ if (!args->GetString(1, &from))
+ return;
+ if (!args->GetString(2, &to))
+ return;
+ translate_prefs->RemoveLanguagePairFromWhitelist(from, to);
+ } else if (pref_name == "too_often_denied") {
+ translate_prefs->ResetDenialState();
+ } else {
+ return;
+ }
+
+ SendPrefsToJs();
+}
+
+void TranslateInternalsHandler::OnOverrideCountry(const base::ListValue* args) {
+ std::string country;
+ if (args->GetString(0, &country)) {
+ variations::VariationsService* variations_service =
+ g_browser_process->variations_service();
+ if (variations_service) {
+ SendCountryToJs(
+ variations_service->OverrideStoredPermanentCountry(country));
+ }
+ }
+}
+
+void TranslateInternalsHandler::OnRequestInfo(const base::ListValue* /*args*/) {
+ SendPrefsToJs();
+ SendSupportedLanguagesToJs();
+ SendCountryToJs(false);
+}
+
+void TranslateInternalsHandler::SendMessageToJs(const std::string& message,
+ const base::Value& value) {
+ const char func[] = "cr.translateInternals.messageHandler";
+ base::Value message_data(message);
+ web_ui()->CallJavascriptFunctionUnsafe(func, message_data, value);
+}
+
+void TranslateInternalsHandler::SendPrefsToJs() {
+ content::WebContents* web_contents = web_ui()->GetWebContents();
+ Profile* profile =
+ Profile::FromBrowserContext(web_contents->GetBrowserContext());
+ PrefService* prefs = profile->GetOriginalProfile()->GetPrefs();
+
+ base::DictionaryValue dict;
+
+ static const char* keys[] = {
+ prefs::kEnableTranslate,
+ translate::TranslatePrefs::kPrefTranslateBlockedLanguages,
+ translate::TranslatePrefs::kPrefTranslateSiteBlacklist,
+ translate::TranslatePrefs::kPrefTranslateWhitelists,
+ translate::TranslatePrefs::kPrefTranslateDeniedCount,
+ translate::TranslatePrefs::kPrefTranslateIgnoredCount,
+ translate::TranslatePrefs::kPrefTranslateAcceptedCount,
+ translate::TranslatePrefs::kPrefTranslateLastDeniedTimeForLanguage,
+ translate::TranslatePrefs::kPrefTranslateTooOftenDeniedForLanguage,
+ };
+ for (const char* key : keys) {
+ const PrefService::Preference* pref = prefs->FindPreference(key);
+ if (pref)
+ dict.Set(key, base::MakeUnique<base::Value>(*pref->GetValue()));
+ }
+
+ SendMessageToJs("prefsUpdated", dict);
+}
+
+void TranslateInternalsHandler::SendSupportedLanguagesToJs() {
+ base::DictionaryValue dict;
+
+ std::vector<std::string> languages;
+ translate::TranslateDownloadManager::GetSupportedLanguages(&languages);
+ base::Time last_updated =
+ translate::TranslateDownloadManager::GetSupportedLanguagesLastUpdated();
+
+ auto languages_list = base::MakeUnique<base::ListValue>();
+ for (std::vector<std::string>::iterator it = languages.begin();
+ it != languages.end(); ++it) {
+ const std::string& lang = *it;
+ languages_list->AppendString(lang);
+ }
+
+ dict.Set("languages", std::move(languages_list));
+ dict.SetDouble("last_updated", last_updated.ToJsTime());
+ SendMessageToJs("supportedLanguagesUpdated", dict);
+}
+
+void TranslateInternalsHandler::SendCountryToJs(bool was_updated) {
+ std::string country;
+ variations::VariationsService* variations_service =
+ g_browser_process->variations_service();
+ if (variations_service)
+ country = variations_service->GetStoredPermanentCountry();
+
+ base::DictionaryValue dict;
+ if (!country.empty()) {
+ dict.SetString("country", country);
+ dict.SetBoolean("update", was_updated);
+ }
+ SendMessageToJs("countryUpdated", dict);
+}
diff --git a/chromium/chrome/browser/ui/webui/translate_internals/translate_internals_handler.h b/chromium/chrome/browser/ui/webui/translate_internals/translate_internals_handler.h
new file mode 100644
index 00000000000..d33e75aa058
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/translate_internals/translate_internals_handler.h
@@ -0,0 +1,100 @@
+// 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_TRANSLATE_INTERNALS_TRANSLATE_INTERNALS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_TRANSLATE_INTERNALS_TRANSLATE_INTERNALS_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback_list.h"
+#include "base/macros.h"
+#include "components/translate/core/browser/translate_language_list.h"
+#include "components/translate/core/browser/translate_manager.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "content/public/common/webplugininfo.h"
+
+namespace translate {
+struct TranslateErrorDetails;
+struct TranslateEventDetails;
+}
+
+namespace base {
+class ListValue;
+class Value;
+}
+
+namespace content {
+class NotificationDetails;
+class NotificationSource;
+}
+
+// The handler class for TranslateInternals page operations.
+class TranslateInternalsHandler : public content::WebUIMessageHandler,
+ public content::NotificationObserver {
+ public:
+ TranslateInternalsHandler();
+ ~TranslateInternalsHandler() override;
+
+ // content::WebUIMessageHandler methods:
+ void RegisterMessages() override;
+
+ private:
+ // content::NotificationObserver implementation:
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ // Callback for translate errors.
+ void OnTranslateError(const translate::TranslateErrorDetails& details);
+
+ // Callback for translate events.
+ virtual void OnTranslateEvent(
+ const translate::TranslateEventDetails& details);
+
+ // Handles the Javascript message 'removePrefItem'. This message is sent
+ // when UI requests to remove an item in the preference.
+ void OnRemovePrefItem(const base::ListValue* args);
+
+ // Handles the Javascript message 'overrideCountry'. This message is sent
+ // when UI requests to override the stored country.
+ void OnOverrideCountry(const base::ListValue* country);
+
+ // Handles the Javascript message 'requestInfo'. This message is sent
+ // when UI needs to show information concerned with the translation.
+ // For now, this returns only prefs to Javascript.
+ // |args| is not used.
+ void OnRequestInfo(const base::ListValue* args);
+
+ // Sends a messsage to Javascript.
+ void SendMessageToJs(const std::string& message, const base::Value& value);
+
+ // Sends the current preference to Javascript.
+ void SendPrefsToJs();
+
+ // Sends the languages currently supported by the server to JavaScript.
+ void SendSupportedLanguagesToJs();
+
+ // Sends the stored permanent country to Javascript.
+ // |was_updated| tells Javascript if the country has been updated or not.
+ void SendCountryToJs(bool was_updated);
+
+ // Subscription for translate events coming from the translate language list.
+ std::unique_ptr<
+ translate::TranslateLanguageList::EventCallbackList::Subscription>
+ event_subscription_;
+
+ // Subscription for translate errors coming from the translate manager.
+ std::unique_ptr<
+ translate::TranslateManager::TranslateErrorCallbackList::Subscription>
+ error_subscription_;
+
+ content::NotificationRegistrar notification_registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(TranslateInternalsHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_TRANSLATE_INTERNALS_TRANSLATE_INTERNALS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/translate_internals/translate_internals_ui.cc b/chromium/chrome/browser/ui/webui/translate_internals/translate_internals_ui.cc
new file mode 100644
index 00000000000..1c4bf06b636
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/translate_internals/translate_internals_ui.cc
@@ -0,0 +1,88 @@
+// 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/translate_internals/translate_internals_ui.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/translate_internals/translate_internals_handler.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/translate_internals_resources.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "third_party/cld/cld_version.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+// Sets the languages to |dict|. Each key is a language code and each value is
+// a language name in the locale.
+void GetLanguages(base::DictionaryValue* dict) {
+ DCHECK(dict);
+
+ const std::string app_locale = g_browser_process->GetApplicationLocale();
+ std::vector<std::string> language_codes;
+ l10n_util::GetAcceptLanguagesForLocale(app_locale, &language_codes);
+
+ for (std::vector<std::string>::iterator it = language_codes.begin();
+ it != language_codes.end(); ++it) {
+ const std::string& lang_code = *it;
+ base::string16 lang_name =
+ l10n_util::GetDisplayNameForLocale(lang_code, app_locale, false);
+ dict->SetString(lang_code, lang_name);
+ }
+}
+
+content::WebUIDataSource* CreateTranslateInternalsHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUITranslateInternalsHost);
+
+ source->SetDefaultResource(IDR_TRANSLATE_INTERNALS_TRANSLATE_INTERNALS_HTML);
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("translate_internals.js",
+ IDR_TRANSLATE_INTERNALS_TRANSLATE_INTERNALS_JS);
+ source->UseGzip(std::unordered_set<std::string>());
+
+ base::DictionaryValue langs;
+ GetLanguages(&langs);
+ for (base::DictionaryValue::Iterator it(langs); !it.IsAtEnd(); it.Advance()) {
+ std::string key = "language-" + it.key();
+ std::string value;
+ it.value().GetAsString(&value);
+ source->AddString(key, value);
+ }
+
+ std::string cld_version = "";
+ // The version string is hardcoded here to avoid linking with the CLD
+ // library, see http://crbug.com/297777.
+#if BUILDFLAG(CLD_VERSION) == 2
+ cld_version = "2";
+#elif BUILDFLAG(CLD_VERSION) == 3
+ cld_version = "3";
+#else
+# error "CLD_VERSION must be 2 or 3"
+#endif
+ source->AddString("cld-version", cld_version);
+
+ return source;
+}
+
+} // namespace
+
+TranslateInternalsUI::TranslateInternalsUI(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ web_ui->AddMessageHandler(base::MakeUnique<TranslateInternalsHandler>());
+
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, CreateTranslateInternalsHTMLSource());
+}
diff --git a/chromium/chrome/browser/ui/webui/translate_internals/translate_internals_ui.h b/chromium/chrome/browser/ui/webui/translate_internals/translate_internals_ui.h
new file mode 100644
index 00000000000..b35cf1ce543
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/translate_internals/translate_internals_ui.h
@@ -0,0 +1,21 @@
+// 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_TRANSLATE_INTERNALS_TRANSLATE_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_TRANSLATE_INTERNALS_TRANSLATE_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The implementation for the chrome://translate-internals page.
+class TranslateInternalsUI : public content::WebUIController {
+ public:
+ explicit TranslateInternalsUI(content::WebUI* web_ui);
+ ~TranslateInternalsUI() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TranslateInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_TRANSLATE_INTERNALS_TRANSLATE_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/uber/uber_ui.cc b/chromium/chrome/browser/ui/webui/uber/uber_ui.cc
new file mode 100644
index 00000000000..5a8dfc15281
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/uber/uber_ui.cc
@@ -0,0 +1,187 @@
+// 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/ui/webui/uber/uber_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
+#include "build/build_config.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
+#include "chrome/browser/ui/webui/extensions/extensions_ui.h"
+#include "chrome/browser/ui/webui/log_web_ui_url.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/chrome_manifest_url_handlers.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/common/browser_side_navigation_policy.h"
+
+using content::NavigationController;
+using content::NavigationEntry;
+using content::RenderFrameHost;
+using content::WebContents;
+
+namespace {
+
+content::WebUIDataSource* CreateUberHTMLSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIUberHost);
+
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("uber.js", IDR_UBER_JS);
+ source->AddResourcePath("uber_utils.js", IDR_UBER_UTILS_JS);
+ source->SetDefaultResource(IDR_UBER_HTML);
+ source->OverrideContentSecurityPolicyChildSrc("child-src chrome:;");
+
+ // Hack alert: continue showing "Loading..." until a real title is set.
+ source->AddLocalizedString("pageTitle", IDS_TAB_LOADING_TITLE);
+
+ source->AddString("extensionsFrameURL", chrome::kChromeUIExtensionsFrameURL);
+ source->AddString("extensionsHost", chrome::kChromeUIExtensionsHost);
+ source->AddString("helpFrameURL", chrome::kChromeUIHelpFrameURL);
+ source->AddString("helpHost", chrome::kChromeUIHelpHost);
+ source->AddString("settingsFrameURL", chrome::kChromeUISettingsFrameURL);
+ source->AddString("settingsHost", chrome::kChromeUISettingsHost);
+
+ return source;
+}
+
+content::WebUIDataSource* CreateUberFrameHTMLSource(
+ content::BrowserContext* browser_context) {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIUberFrameHost);
+ Profile* profile = Profile::FromBrowserContext(browser_context);
+
+ source->SetJsonPath("strings.js");
+ source->AddResourcePath("uber_frame.js", IDR_UBER_FRAME_JS);
+ source->SetDefaultResource(IDR_UBER_FRAME_HTML);
+
+ // TODO(jhawkins): Attempt to get rid of IDS_SHORT_PRODUCT_OS_NAME.
+#if defined(OS_CHROMEOS)
+ source->AddLocalizedString("shortProductName", IDS_SHORT_PRODUCT_OS_NAME);
+#else
+ source->AddLocalizedString("shortProductName", IDS_SHORT_PRODUCT_NAME);
+#endif // defined(OS_CHROMEOS)
+
+ source->AddBoolean("hideExtensions",
+ base::FeatureList::IsEnabled(features::kMaterialDesignExtensions));
+ source->AddBoolean("hideSettingsAndHelp",
+ ::switches::SettingsWindowEnabled() ||
+ base::FeatureList::IsEnabled(features::kMaterialDesignSettings));
+ source->AddString("extensionsHost", chrome::kChromeUIExtensionsHost);
+ source->AddLocalizedString("extensionsDisplayName",
+ IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE);
+ source->AddString("helpHost", chrome::kChromeUIHelpHost);
+ source->AddLocalizedString("helpDisplayName", IDS_ABOUT_TITLE);
+ source->AddString("settingsHost", chrome::kChromeUISettingsHost);
+ source->AddLocalizedString("settingsDisplayName", IDS_SETTINGS_TITLE);
+
+ source->DisableDenyXFrameOptions();
+ source->OverrideContentSecurityPolicyChildSrc("child-src chrome:;");
+
+ source->AddBoolean("profileIsGuest", profile->IsGuestSession());
+
+ return source;
+}
+
+} // namespace
+
+SubframeLogger::SubframeLogger(content::WebContents* contents)
+ : WebContentsObserver(contents) {}
+
+SubframeLogger::~SubframeLogger() {}
+
+void SubframeLogger::DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) {
+ if (!navigation_handle->HasCommitted())
+ return;
+
+ const GURL& url = navigation_handle->GetURL();
+ if (url == chrome::kChromeUIExtensionsFrameURL ||
+ url == chrome::kChromeUIHelpFrameURL ||
+ url == chrome::kChromeUISettingsFrameURL ||
+ url == chrome::kChromeUIUberFrameURL) {
+ webui::LogWebUIUrl(url);
+ }
+}
+
+UberUI::UberUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ subframe_logger_ = base::MakeUnique<SubframeLogger>(web_ui->GetWebContents());
+ content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
+ CreateUberHTMLSource());
+
+ RegisterSubpage(chrome::kChromeUIExtensionsFrameURL,
+ chrome::kChromeUIExtensionsHost);
+ RegisterSubpage(chrome::kChromeUIHelpFrameURL,
+ chrome::kChromeUIHelpHost);
+ RegisterSubpage(chrome::kChromeUISettingsFrameURL,
+ chrome::kChromeUISettingsHost);
+ RegisterSubpage(chrome::kChromeUIUberFrameURL,
+ chrome::kChromeUIUberHost);
+}
+
+UberUI::~UberUI() {
+}
+
+void UberUI::RegisterSubpage(const std::string& page_url,
+ const std::string& page_host) {
+ sub_uis_[page_url] = web_ui()->GetWebContents()->CreateSubframeWebUI(
+ GURL(page_url), page_host);
+}
+
+content::WebUI* UberUI::GetSubpage(const std::string& page_url) {
+ if (!base::ContainsKey(sub_uis_, page_url))
+ return nullptr;
+ return sub_uis_[page_url].get();
+}
+
+void UberUI::RenderFrameCreated(RenderFrameHost* render_frame_host) {
+ for (auto iter = sub_uis_.begin(); iter != sub_uis_.end(); ++iter) {
+ iter->second->GetController()->RenderFrameCreated(render_frame_host);
+ }
+}
+
+bool UberUI::OverrideHandleWebUIMessage(const GURL& source_url,
+ const std::string& message,
+ const base::ListValue& args) {
+ // Find the appropriate subpage and forward the message.
+ auto subpage = sub_uis_.find(source_url.GetOrigin().spec());
+ if (subpage == sub_uis_.end()) {
+ // The message was sent from the uber page itself.
+ DCHECK_EQ(std::string(chrome::kChromeUIUberHost), source_url.host());
+ return false;
+ }
+
+ // The message was sent from a subpage.
+ // TODO(jam) fix this to use interface
+ // return subpage->second->GetController()->OverrideHandleWebUIMessage(
+ // source_url, message, args);
+ subpage->second->ProcessWebUIMessage(source_url, message, args);
+ return true;
+}
+
+// UberFrameUI
+
+UberFrameUI::UberFrameUI(content::WebUI* web_ui) : WebUIController(web_ui) {
+ content::BrowserContext* browser_context =
+ web_ui->GetWebContents()->GetBrowserContext();
+ content::WebUIDataSource::Add(browser_context,
+ CreateUberFrameHTMLSource(browser_context));
+}
+
+UberFrameUI::~UberFrameUI() {
+}
diff --git a/chromium/chrome/browser/ui/webui/uber/uber_ui.h b/chromium/chrome/browser/ui/webui/uber/uber_ui.h
new file mode 100644
index 00000000000..8be770897ad
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/uber/uber_ui.h
@@ -0,0 +1,66 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_UBER_UBER_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_UBER_UBER_UI_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/values.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// Logs visits to subframe URLs (e.g. chrome://settings-frame).
+class SubframeLogger : public content::WebContentsObserver {
+ public:
+ explicit SubframeLogger(content::WebContents* contents);
+ ~SubframeLogger() override;
+
+ // content::WebContentsObserver implementation.
+ void DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SubframeLogger);
+};
+
+// The WebUI class for the uber page (chrome://chrome). It manages the UI for
+// the uber page (navigation bar and so forth) as well as WebUI objects for
+// pages that appear in the uber page.
+class UberUI : public content::WebUIController {
+ public:
+ explicit UberUI(content::WebUI* web_ui);
+ ~UberUI() override;
+
+ content::WebUI* GetSubpage(const std::string& page_url);
+
+ // WebUIController implementation.
+ bool OverrideHandleWebUIMessage(const GURL& source_url,
+ const std::string& message,
+ const base::ListValue& args) override;
+
+ // We forward these to |sub_uis_|.
+ void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
+
+ private:
+ // Creates and stores a WebUI for the given URL.
+ void RegisterSubpage(const std::string& page_url,
+ const std::string& page_host);
+
+ std::unique_ptr<SubframeLogger> subframe_logger_;
+
+ // Map from URL origin to WebUI instance.
+ std::map<std::string, std::unique_ptr<content::WebUI>> sub_uis_;
+
+ DISALLOW_COPY_AND_ASSIGN(UberUI);
+};
+
+class UberFrameUI : public content::WebUIController {
+ public:
+ explicit UberFrameUI(content::WebUI* web_ui);
+ ~UberFrameUI() override;
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_UBER_UBER_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/uber/uber_ui_browsertest.cc b/chromium/chrome/browser/ui/webui/uber/uber_ui_browsertest.cc
new file mode 100644
index 00000000000..01eca7bb383
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/uber/uber_ui_browsertest.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 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.
+
+#include <string>
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/test_extension_system.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "chrome/test/base/web_ui_browser_test.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_builder.h"
+
+class UberUIBrowserTest : public WebUIBrowserTest {
+ public:
+ UberUIBrowserTest() {}
+ ~UberUIBrowserTest() override {}
+
+ bool GetJsBool(const char* js) {
+ bool result = false;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+ GetWebContents(),
+ std::string("domAutomationController.send(") + js + ");",
+ &result));
+ return result;
+ }
+
+ void SelectTab(const std::string& name) {
+ ASSERT_TRUE(content::ExecuteScript(
+ GetWebContents(),
+ std::string("var data = {pageId: '") + name + "'};" +
+ "uber.invokeMethodOnWindow(this, 'changeSelection', data);"));
+ }
+
+ private:
+ content::WebContents* GetWebContents() {
+ return browser()->tab_strip_model()->GetActiveWebContents();
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(UberUIBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(UberUIBrowserTest, EnableMdExtensionsHidesExtensions) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures({features::kMaterialDesignExtensions},
+ {features::kMaterialDesignSettings});
+
+ ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIUberFrameURL));
+ SelectTab("settings");
+ EXPECT_TRUE(GetJsBool("$('extensions').hidden"));
+}
+
+IN_PROC_BROWSER_TEST_F(UberUIBrowserTest, EnableMdSettingsHidesSettings) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatures({features::kMaterialDesignSettings},
+ {features::kMaterialDesignExtensions});
+
+ ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIUberFrameURL));
+ SelectTab("extensions");
+ EXPECT_TRUE(GetJsBool("$('settings').hidden && $('help').hidden"));
+}
+
+IN_PROC_BROWSER_TEST_F(UberUIBrowserTest,
+ EnableSettingsWindowHidesSettingsAndHelp) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(features::kMaterialDesignSettings);
+
+ base::CommandLine::ForCurrentProcess()->AppendSwitch(
+ ::switches::kEnableSettingsWindow);
+ ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIUberFrameURL));
+ SelectTab("extensions");
+ EXPECT_TRUE(GetJsBool("$('settings').hidden && $('help').hidden"));
+}
diff --git a/chromium/chrome/browser/ui/webui/usb_internals/OWNERS b/chromium/chrome/browser/ui/webui/usb_internals/OWNERS
new file mode 100644
index 00000000000..08850f42120
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/usb_internals/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromium/chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.cc b/chromium/chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.cc
new file mode 100644
index 00000000000..4b711599245
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.cc
@@ -0,0 +1,111 @@
+// 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 "chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.h"
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "device/base/device_client.h"
+#include "device/usb/usb_device.h"
+#include "device/usb/usb_device_handle.h"
+#include "device/usb/usb_service.h"
+#include "device/usb/webusb_descriptors.h"
+#include "url/gurl.h"
+
+namespace {
+
+class TestUsbDevice : public device::UsbDevice {
+ public:
+ TestUsbDevice(const std::string& name,
+ const std::string& serial_number,
+ const GURL& landing_page);
+
+ // device::UsbDevice overrides:
+ void Open(const OpenCallback& callback) override;
+
+ private:
+ ~TestUsbDevice() override;
+
+ DISALLOW_COPY_AND_ASSIGN(TestUsbDevice);
+};
+
+TestUsbDevice::TestUsbDevice(const std::string& name,
+ const std::string& serial_number,
+ const GURL& landing_page)
+ : UsbDevice(0x0210,
+ 0xff,
+ 0xff,
+ 0xff,
+ 0x0000,
+ 0x000,
+ 0x0100,
+ base::string16(),
+ base::UTF8ToUTF16(name),
+ base::UTF8ToUTF16(serial_number)) {
+ webusb_landing_page_ = landing_page;
+}
+
+void TestUsbDevice::Open(const OpenCallback& callback) {
+ callback.Run(nullptr);
+}
+
+TestUsbDevice::~TestUsbDevice() {}
+
+} // namespace
+
+UsbInternalsPageHandler::UsbInternalsPageHandler(
+ mojom::UsbInternalsPageHandlerRequest request)
+ : binding_(this, std::move(request)) {}
+
+UsbInternalsPageHandler::~UsbInternalsPageHandler() {}
+
+void UsbInternalsPageHandler::AddDeviceForTesting(
+ const std::string& name,
+ const std::string& serial_number,
+ const std::string& landing_page,
+ const AddDeviceForTestingCallback& callback) {
+ device::UsbService* service = device::DeviceClient::Get()->GetUsbService();
+ if (service) {
+ GURL landing_page_url(landing_page);
+ if (!landing_page_url.is_valid()) {
+ callback.Run(false, "Landing page URL is invalid.");
+ return;
+ }
+
+ service->AddDeviceForTesting(
+ new TestUsbDevice(name, serial_number, landing_page_url));
+ callback.Run(true, "Added.");
+ } else {
+ callback.Run(false, "USB service unavailable.");
+ }
+}
+
+void UsbInternalsPageHandler::RemoveDeviceForTesting(
+ const std::string& guid,
+ const RemoveDeviceForTestingCallback& callback) {
+ device::UsbService* service = device::DeviceClient::Get()->GetUsbService();
+ if (service)
+ service->RemoveDeviceForTesting(guid);
+ callback.Run();
+}
+
+void UsbInternalsPageHandler::GetTestDevices(
+ const GetTestDevicesCallback& callback) {
+ std::vector<scoped_refptr<device::UsbDevice>> devices;
+ device::UsbService* service = device::DeviceClient::Get()->GetUsbService();
+ if (service)
+ service->GetTestDevices(&devices);
+ std::vector<mojom::TestDeviceInfoPtr> result;
+ result.reserve(devices.size());
+ for (const auto& device : devices) {
+ auto device_info = mojom::TestDeviceInfo::New();
+ device_info->guid = device->guid();
+ device_info->name = base::UTF16ToUTF8(device->product_string());
+ device_info->serial_number = base::UTF16ToUTF8(device->serial_number());
+ device_info->landing_page = device->webusb_landing_page();
+ result.push_back(std::move(device_info));
+ }
+ callback.Run(std::move(result));
+}
diff --git a/chromium/chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.h b/chromium/chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.h
new file mode 100644
index 00000000000..6ae1c8aaf16
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.h
@@ -0,0 +1,37 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_USB_INTERNALS_USB_INTERNALS_PAGE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_USB_INTERNALS_USB_INTERNALS_PAGE_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/mojo_web_ui_handler.h"
+#include "chrome/browser/ui/webui/usb_internals/usb_internals.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+class UsbInternalsPageHandler : public mojom::UsbInternalsPageHandler,
+ public MojoWebUIHandler {
+ public:
+ explicit UsbInternalsPageHandler(
+ mojom::UsbInternalsPageHandlerRequest request);
+ ~UsbInternalsPageHandler() override;
+
+ // mojom::UsbInternalsPageHandler overrides:
+ void AddDeviceForTesting(
+ const std::string& name,
+ const std::string& serial_number,
+ const std::string& landing_page,
+ const AddDeviceForTestingCallback& callback) override;
+ void RemoveDeviceForTesting(
+ const std::string& guid,
+ const RemoveDeviceForTestingCallback& callback) override;
+ void GetTestDevices(const GetTestDevicesCallback& callback) override;
+
+ private:
+ mojo::Binding<mojom::UsbInternalsPageHandler> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(UsbInternalsPageHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_USB_INTERNALS_USB_INTERNALS_PAGE_HANDLER_H_
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
new file mode 100644
index 00000000000..aa60c9c9a5f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/usb_internals/usb_internals_ui.cc
@@ -0,0 +1,37 @@
+// 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 "chrome/browser/ui/webui/usb_internals/usb_internals_ui.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+UsbInternalsUI::UsbInternalsUI(content::WebUI* web_ui)
+ : MojoWebUIController(web_ui) {
+ // Set up the chrome://usb-internals source.
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(chrome::kChromeUIUsbInternalsHost);
+ source->AddResourcePath("usb_internals.css", IDR_USB_INTERNALS_CSS);
+ source->AddResourcePath("usb_internals.js", IDR_USB_INTERNALS_JS);
+ source->AddResourcePath(
+ "chrome/browser/ui/webui/usb_internals/usb_internals.mojom",
+ IDR_USB_INTERNALS_MOJO_JS);
+ source->AddResourcePath("url/mojo/origin.mojom", IDR_ORIGIN_MOJO_JS);
+ source->AddResourcePath("url/mojo/url.mojom", IDR_URL_MOJO_JS);
+ source->SetDefaultResource(IDR_USB_INTERNALS_HTML);
+ source->UseGzip(std::unordered_set<std::string>());
+
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), source);
+}
+
+UsbInternalsUI::~UsbInternalsUI() {}
+
+void UsbInternalsUI::BindUIHandler(
+ const service_manager::BindSourceInfo& source_info,
+ mojom::UsbInternalsPageHandlerRequest request) {
+ page_handler_.reset(new UsbInternalsPageHandler(std::move(request)));
+}
diff --git a/chromium/chrome/browser/ui/webui/usb_internals/usb_internals_ui.h b/chromium/chrome/browser/ui/webui/usb_internals/usb_internals_ui.h
new file mode 100644
index 00000000000..72985ebbf7f
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/usb_internals/usb_internals_ui.h
@@ -0,0 +1,31 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_USB_INTERNALS_USB_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_USB_INTERNALS_USB_INTERNALS_UI_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/mojo_web_ui_controller.h"
+#include "chrome/browser/ui/webui/usb_internals/usb_internals.mojom.h"
+
+class UsbInternalsPageHandler;
+
+// The WebUI for chrome://usb-internals.
+class UsbInternalsUI
+ : public MojoWebUIController<mojom::UsbInternalsPageHandler> {
+ public:
+ explicit UsbInternalsUI(content::WebUI* web_ui);
+ ~UsbInternalsUI() override;
+
+ private:
+ // MojoWebUIController overrides:
+ void BindUIHandler(const service_manager::BindSourceInfo& source_info,
+ mojom::UsbInternalsPageHandlerRequest request) override;
+
+ std::unique_ptr<UsbInternalsPageHandler> page_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(UsbInternalsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_USB_INTERNALS_USB_INTERNALS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/user_actions/user_actions_ui.cc b/chromium/chrome/browser/ui/webui/user_actions/user_actions_ui.cc
new file mode 100644
index 00000000000..d12ecffc156
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/user_actions/user_actions_ui.cc
@@ -0,0 +1,31 @@
+// Copyright 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/ui/webui/user_actions/user_actions_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/user_actions/user_actions_ui_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+UserActionsUI::UserActionsUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ // Set up the chrome://user-actions/ source.
+ content::WebUIDataSource* html_source =
+ content::WebUIDataSource::Create(chrome::kChromeUIUserActionsHost);
+ html_source->SetDefaultResource(IDR_USER_ACTIONS_HTML);
+ html_source->AddResourcePath("user_actions.css", IDR_USER_ACTIONS_CSS);
+ html_source->AddResourcePath("user_actions.js", IDR_USER_ACTIONS_JS);
+
+ Profile* profile = Profile::FromWebUI(web_ui);
+ content::WebUIDataSource::Add(profile, html_source);
+
+ web_ui->AddMessageHandler(base::MakeUnique<UserActionsUIHandler>());
+}
+
+UserActionsUI::~UserActionsUI() {}
diff --git a/chromium/chrome/browser/ui/webui/user_actions/user_actions_ui.h b/chromium/chrome/browser/ui/webui/user_actions/user_actions_ui.h
new file mode 100644
index 00000000000..cc25fefb7d2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/user_actions/user_actions_ui.h
@@ -0,0 +1,21 @@
+// Copyright 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_USER_ACTIONS_USER_ACTIONS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_USER_ACTIONS_USER_ACTIONS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The UI for chrome://user-actions/
+class UserActionsUI : public content::WebUIController {
+ public:
+ explicit UserActionsUI(content::WebUI* contents);
+ ~UserActionsUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(UserActionsUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_USER_ACTIONS_USER_ACTIONS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/user_actions/user_actions_ui_handler.cc b/chromium/chrome/browser/ui/webui/user_actions/user_actions_ui_handler.cc
new file mode 100644
index 00000000000..ef6ea8f008e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/user_actions/user_actions_ui_handler.cc
@@ -0,0 +1,29 @@
+// Copyright 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/ui/webui/user_actions/user_actions_ui_handler.h"
+
+#include "base/bind.h"
+#include "base/metrics/user_metrics.h"
+#include "base/values.h"
+#include "content/public/browser/web_ui.h"
+
+UserActionsUIHandler::UserActionsUIHandler()
+ : action_callback_(base::Bind(&UserActionsUIHandler::OnUserAction,
+ base::Unretained(this))) {
+ base::AddActionCallback(action_callback_);
+}
+
+UserActionsUIHandler::~UserActionsUIHandler() {
+ base::RemoveActionCallback(action_callback_);
+}
+
+void UserActionsUIHandler::RegisterMessages() {}
+
+void UserActionsUIHandler::OnUserAction(const std::string& action) {
+ base::Value user_action_name(action);
+ web_ui()->CallJavascriptFunctionUnsafe("userActions.observeUserAction",
+ user_action_name);
+}
+
diff --git a/chromium/chrome/browser/ui/webui/user_actions/user_actions_ui_handler.h b/chromium/chrome/browser/ui/webui/user_actions/user_actions_ui_handler.h
new file mode 100644
index 00000000000..9423ff27910
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/user_actions/user_actions_ui_handler.h
@@ -0,0 +1,32 @@
+// Copyright 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_USER_ACTIONS_USER_ACTIONS_UI_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_USER_ACTIONS_USER_ACTIONS_UI_HANDLER_H_
+
+#include "base/macros.h"
+#include "base/metrics/user_metrics.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+// UI Handler for chrome://user-actions/
+// It listens to user action notifications and passes those notifications
+// into the Javascript to update the page.
+class UserActionsUIHandler : public content::WebUIMessageHandler {
+ public:
+ UserActionsUIHandler();
+ ~UserActionsUIHandler() override;
+
+ // WebUIMessageHandler implementation:
+ // Does nothing for now.
+ void RegisterMessages() override;
+
+ private:
+ void OnUserAction(const std::string& action);
+
+ base::ActionCallback action_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserActionsUIHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_USER_ACTIONS_USER_ACTIONS_UI_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/version_handler.cc b/chromium/chrome/browser/ui/webui/version_handler.cc
new file mode 100644
index 00000000000..d95e8dff4ea
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/version_handler.cc
@@ -0,0 +1,131 @@
+// 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/ui/webui/version_handler.h"
+
+#include <stddef.h>
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/metrics/field_trial.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/plugins/plugin_prefs.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/variations/active_field_trials.h"
+#include "components/version_ui/version_handler_helper.h"
+#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_ui.h"
+#include "content/public/common/content_constants.h"
+#include "ppapi/features/features.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+namespace {
+
+// Retrieves the executable and profile paths on the FILE thread.
+void GetFilePaths(const base::FilePath& profile_path,
+ base::string16* exec_path_out,
+ base::string16* profile_path_out) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
+
+ base::FilePath executable_path = base::MakeAbsoluteFilePath(
+ base::CommandLine::ForCurrentProcess()->GetProgram());
+ if (!executable_path.empty())
+ *exec_path_out = executable_path.LossyDisplayName();
+ else
+ *exec_path_out = l10n_util::GetStringUTF16(IDS_VERSION_UI_PATH_NOTFOUND);
+
+ base::FilePath profile_path_copy(base::MakeAbsoluteFilePath(profile_path));
+ if (!profile_path.empty() && !profile_path_copy.empty())
+ *profile_path_out = profile_path.LossyDisplayName();
+ else
+ *profile_path_out = l10n_util::GetStringUTF16(IDS_VERSION_UI_PATH_NOTFOUND);
+}
+
+} // namespace
+
+VersionHandler::VersionHandler()
+ : weak_ptr_factory_(this) {
+}
+
+VersionHandler::~VersionHandler() {
+}
+
+void VersionHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ version_ui::kRequestVersionInfo,
+ base::Bind(&VersionHandler::HandleRequestVersionInfo,
+ base::Unretained(this)));
+}
+
+void VersionHandler::HandleRequestVersionInfo(const base::ListValue* args) {
+#if BUILDFLAG(ENABLE_PLUGINS)
+ // The Flash version information is needed in the response, so make sure
+ // the plugins are loaded.
+ content::PluginService::GetInstance()->GetPlugins(
+ base::Bind(&VersionHandler::OnGotPlugins,
+ weak_ptr_factory_.GetWeakPtr()));
+#endif
+
+ // Grab the executable path on the FILE thread. It is returned in
+ // OnGotFilePaths.
+ base::string16* exec_path_buffer = new base::string16;
+ base::string16* profile_path_buffer = new base::string16;
+ content::BrowserThread::PostTaskAndReply(
+ content::BrowserThread::FILE, FROM_HERE,
+ base::BindOnce(&GetFilePaths, Profile::FromWebUI(web_ui())->GetPath(),
+ base::Unretained(exec_path_buffer),
+ base::Unretained(profile_path_buffer)),
+ base::BindOnce(
+ &VersionHandler::OnGotFilePaths, weak_ptr_factory_.GetWeakPtr(),
+ 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());
+}
+
+void VersionHandler::OnGotFilePaths(base::string16* executable_path_data,
+ base::string16* profile_path_data) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ base::Value exec_path(*executable_path_data);
+ base::Value profile_path(*profile_path_data);
+ web_ui()->CallJavascriptFunctionUnsafe(version_ui::kReturnFilePaths,
+ exec_path, profile_path);
+}
+
+#if BUILDFLAG(ENABLE_PLUGINS)
+void VersionHandler::OnGotPlugins(
+ const std::vector<content::WebPluginInfo>& plugins) {
+ // Obtain the version of the first enabled Flash plugin.
+ std::vector<content::WebPluginInfo> info_array;
+ content::PluginService::GetInstance()->GetPluginInfoArray(
+ GURL(), content::kFlashPluginSwfMimeType, false, &info_array, NULL);
+ std::string flash_version_and_path =
+ l10n_util::GetStringUTF8(IDS_PLUGINS_DISABLED_PLUGIN);
+ PluginPrefs* plugin_prefs =
+ PluginPrefs::GetForProfile(Profile::FromWebUI(web_ui())).get();
+ if (plugin_prefs) {
+ for (size_t i = 0; i < info_array.size(); ++i) {
+ if (plugin_prefs->IsPluginEnabled(info_array[i])) {
+ flash_version_and_path = base::StringPrintf(
+ "%s %s", base::UTF16ToUTF8(info_array[i].version).c_str(),
+ base::UTF16ToUTF8(info_array[i].path.LossyDisplayName()).c_str());
+ break;
+ }
+ }
+ }
+
+ base::Value arg(flash_version_and_path);
+
+ web_ui()->CallJavascriptFunctionUnsafe(version_ui::kReturnFlashVersion, arg);
+}
+#endif // BUILDFLAG(ENABLE_PLUGINS)
diff --git a/chromium/chrome/browser/ui/webui/version_handler.h b/chromium/chrome/browser/ui/webui/version_handler.h
new file mode 100644
index 00000000000..d121739f25c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/version_handler.h
@@ -0,0 +1,46 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_VERSION_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_VERSION_HANDLER_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "content/public/common/webplugininfo.h"
+
+// Handler class for Version page operations.
+class VersionHandler : public content::WebUIMessageHandler {
+ public:
+ VersionHandler();
+ ~VersionHandler() override;
+
+ // content::WebUIMessageHandler implementation.
+ void RegisterMessages() override;
+
+ // Callback for the "requestVersionInfo" message. This asynchronously requests
+ // the flash version and eventually returns it to the front end along with the
+ // list of variations using OnGotPlugins.
+ virtual void HandleRequestVersionInfo(const base::ListValue* args);
+
+ private:
+ // Callback which handles returning the executable and profile paths to the
+ // front end.
+ void OnGotFilePaths(base::string16* executable_path_data,
+ base::string16* profile_path_data);
+
+ // Callback for GetPlugins which responds to the page with the Flash version.
+ // This also initiates the OS Version load on ChromeOS.
+ void OnGotPlugins(const std::vector<content::WebPluginInfo>& plugins);
+
+ // Factory for the creating refs in callbacks.
+ base::WeakPtrFactory<VersionHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(VersionHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_VERSION_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/version_handler_chromeos.cc b/chromium/chrome/browser/ui/webui/version_handler_chromeos.cc
new file mode 100644
index 00000000000..95bdee7b4b3
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/version_handler_chromeos.cc
@@ -0,0 +1,52 @@
+// 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/ui/webui/version_handler_chromeos.h"
+
+#include "base/bind.h"
+#include "base/task_scheduler/post_task.h"
+#include "content/public/browser/web_ui.h"
+
+VersionHandlerChromeOS::VersionHandlerChromeOS() : weak_factory_(this) {}
+
+VersionHandlerChromeOS::~VersionHandlerChromeOS() {}
+
+void VersionHandlerChromeOS::HandleRequestVersionInfo(
+ const base::ListValue* args) {
+ // Start the asynchronous load of the versions.
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&chromeos::version_loader::GetVersion,
+ chromeos::version_loader::VERSION_FULL),
+ base::Bind(&VersionHandlerChromeOS::OnVersion,
+ weak_factory_.GetWeakPtr()));
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&chromeos::version_loader::GetFirmware),
+ base::Bind(&VersionHandlerChromeOS::OnOSFirmware,
+ weak_factory_.GetWeakPtr()));
+ base::PostTaskWithTraitsAndReplyWithResult(
+ FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+ base::Bind(&chromeos::version_loader::GetARCVersion),
+ base::Bind(&VersionHandlerChromeOS::OnARCVersion,
+ weak_factory_.GetWeakPtr()));
+
+ // Parent class takes care of the rest.
+ VersionHandler::HandleRequestVersionInfo(args);
+}
+
+void VersionHandlerChromeOS::OnVersion(const std::string& version) {
+ base::Value arg(version);
+ web_ui()->CallJavascriptFunctionUnsafe("returnOsVersion", arg);
+}
+
+void VersionHandlerChromeOS::OnOSFirmware(const std::string& version) {
+ base::Value arg(version);
+ web_ui()->CallJavascriptFunctionUnsafe("returnOsFirmwareVersion", arg);
+}
+
+void VersionHandlerChromeOS::OnARCVersion(const std::string& version) {
+ base::Value arg(version);
+ web_ui()->CallJavascriptFunctionUnsafe("returnARCVersion", arg);
+}
diff --git a/chromium/chrome/browser/ui/webui/version_handler_chromeos.h b/chromium/chrome/browser/ui/webui/version_handler_chromeos.h
new file mode 100644
index 00000000000..b924106d79e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/version_handler_chromeos.h
@@ -0,0 +1,36 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_VERSION_HANDLER_CHROMEOS_H_
+#define CHROME_BROWSER_UI_WEBUI_VERSION_HANDLER_CHROMEOS_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/webui/version_handler.h"
+#include "chromeos/system/version_loader.h"
+
+// VersionHandlerChromeOS is responsible for loading the Chrome OS
+// version.
+class VersionHandlerChromeOS : public VersionHandler {
+ public:
+ VersionHandlerChromeOS();
+ ~VersionHandlerChromeOS() override;
+
+ // VersionHandler overrides:
+ void HandleRequestVersionInfo(const base::ListValue* args) override;
+
+ // Callbacks from chromeos::VersionLoader.
+ void OnVersion(const std::string& version);
+ void OnOSFirmware(const std::string& version);
+ void OnARCVersion(const std::string& version);
+
+ private:
+ base::WeakPtrFactory<VersionHandlerChromeOS> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(VersionHandlerChromeOS);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_VERSION_HANDLER_CHROMEOS_H_
diff --git a/chromium/chrome/browser/ui/webui/version_ui.cc b/chromium/chrome/browser/ui/webui/version_ui.cc
new file mode 100644
index 00000000000..632d0ea50c1
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/version_ui.cc
@@ -0,0 +1,200 @@
+// 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/ui/webui/version_ui.h"
+
+#include "base/command_line.h"
+#include "base/i18n/message_formatter.h"
+#include "base/memory/ptr_util.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/version_handler.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/chrome_content_client.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/install_static/install_details.h"
+#include "components/grit/components_resources.h"
+#include "components/strings/grit/components_chromium_strings.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/version_info/version_info.h"
+#include "components/version_ui/version_ui_constants.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/common/user_agent.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "v8/include/v8-version-string.h"
+
+#if defined(OS_ANDROID)
+#include "chrome/browser/ui/android/android_about_app_info.h"
+#else // !defined(OS_ANDROID)
+#include "chrome/browser/ui/webui/theme_source.h"
+#endif
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/ui/webui/version_handler_chromeos.h"
+#endif
+
+using content::WebUIDataSource;
+
+namespace {
+
+WebUIDataSource* CreateVersionUIDataSource() {
+ WebUIDataSource* html_source =
+ WebUIDataSource::Create(chrome::kChromeUIVersionHost);
+
+ // Localized and data strings.
+ html_source->AddLocalizedString(version_ui::kTitle, IDS_VERSION_UI_TITLE);
+ html_source->AddLocalizedString(version_ui::kApplicationLabel,
+ IDS_PRODUCT_NAME);
+ html_source->AddString(version_ui::kVersion,
+ version_info::GetVersionNumber());
+ html_source->AddString(version_ui::kVersionModifier,
+ chrome::GetChannelString());
+ html_source->AddString(version_ui::kJSEngine, "V8");
+ html_source->AddString(version_ui::kJSVersion, V8_VERSION_STRING);
+ html_source->AddLocalizedString(version_ui::kCompany,
+ IDS_ABOUT_VERSION_COMPANY_NAME);
+ html_source->AddString(
+ version_ui::kCopyright,
+ base::i18n::MessageFormatter::FormatWithNumberedArgs(
+ l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_COPYRIGHT),
+ base::Time::Now()));
+ html_source->AddLocalizedString(version_ui::kRevision,
+ IDS_VERSION_UI_REVISION);
+ html_source->AddString(version_ui::kCL, version_info::GetLastChange());
+ html_source->AddLocalizedString(version_ui::kOfficial,
+ version_info::IsOfficialBuild()
+ ? IDS_VERSION_UI_OFFICIAL
+ : IDS_VERSION_UI_UNOFFICIAL);
+ html_source->AddLocalizedString(version_ui::kUserAgentName,
+ IDS_VERSION_UI_USER_AGENT);
+ html_source->AddString(version_ui::kUserAgent, GetUserAgent());
+ html_source->AddLocalizedString(version_ui::kCommandLineName,
+ IDS_VERSION_UI_COMMAND_LINE);
+ // Note that the executable path and profile path are retrieved asynchronously
+ // and returned in VersionHandler::OnGotFilePaths. The area is initially
+ // blank.
+ html_source->AddLocalizedString(version_ui::kExecutablePathName,
+ IDS_VERSION_UI_EXECUTABLE_PATH);
+ html_source->AddString(version_ui::kExecutablePath, std::string());
+ html_source->AddLocalizedString(version_ui::kProfilePathName,
+ IDS_VERSION_UI_PROFILE_PATH);
+ html_source->AddString(version_ui::kProfilePath, std::string());
+ html_source->AddLocalizedString(version_ui::kVariationsName,
+ IDS_VERSION_UI_VARIATIONS);
+
+#if defined(OS_CHROMEOS)
+ html_source->AddLocalizedString(version_ui::kARC, IDS_ARC_LABEL);
+ html_source->AddLocalizedString(version_ui::kPlatform, IDS_PLATFORM_LABEL);
+ html_source->AddLocalizedString(version_ui::kCustomizationId,
+ IDS_VERSION_UI_CUSTOMIZATION_ID);
+ html_source->AddLocalizedString(version_ui::kFirmwareVersion,
+ IDS_VERSION_UI_FIRMWARE_VERSION);
+#else
+ html_source->AddLocalizedString(version_ui::kOSName, IDS_VERSION_UI_OS);
+ html_source->AddString(version_ui::kOSType, version_info::GetOSType());
+#endif // OS_CHROMEOS
+
+#if defined(OS_ANDROID)
+ html_source->AddString(version_ui::kOSVersion,
+ AndroidAboutAppInfo::GetOsInfo());
+ html_source->AddLocalizedString(version_ui::kGmsName, IDS_VERSION_UI_GMS);
+ html_source->AddString(version_ui::kGmsVersion,
+ AndroidAboutAppInfo::GetGmsInfo());
+#else
+ html_source->AddString(version_ui::kFlashPlugin, "Flash");
+ // Note that the Flash version is retrieve asynchronously and returned in
+ // VersionHandler::OnGotPlugins. The area is initially blank.
+ html_source->AddString(version_ui::kFlashVersion, std::string());
+#endif // OS_ANDROID
+
+#if defined(ARCH_CPU_64_BITS)
+ html_source->AddLocalizedString(version_ui::kVersionBitSize,
+ IDS_VERSION_UI_64BIT);
+#else
+ html_source->AddLocalizedString(version_ui::kVersionBitSize,
+ IDS_VERSION_UI_32BIT);
+#endif
+
+#if defined(OS_WIN)
+ html_source->AddString(
+ version_ui::kCommandLine,
+ base::CommandLine::ForCurrentProcess()->GetCommandLineString());
+#elif defined(OS_POSIX)
+ std::string command_line;
+ typedef std::vector<std::string> ArgvList;
+ const ArgvList& argv = base::CommandLine::ForCurrentProcess()->argv();
+ for (ArgvList::const_iterator iter = argv.begin(); iter != argv.end(); iter++)
+ command_line += " " + *iter;
+ // TODO(viettrungluu): |command_line| could really have any encoding, whereas
+ // below we assumes it's UTF-8.
+ html_source->AddString(version_ui::kCommandLine, command_line);
+#endif
+
+#if defined(OS_WIN)
+#if defined(__clang__)
+ html_source->AddString(version_ui::kCompiler, "clang");
+#elif defined(_MSC_VER) && _MSC_VER == 1900
+#if BUILDFLAG(PGO_BUILD)
+ html_source->AddString(version_ui::kCompiler, "MSVC 2015 (PGO)");
+#else
+ html_source->AddString(version_ui::kCompiler, "MSVC 2015");
+#endif
+#elif defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1911
+#if BUILDFLAG(PGO_BUILD)
+ html_source->AddString(version_ui::kCompiler, "MSVC 2017 (PGO)");
+#else
+ html_source->AddString(version_ui::kCompiler, "MSVC 2017");
+#endif
+#elif defined(_MSC_VER)
+#error "Unsupported version of MSVC."
+#else
+ html_source->AddString(version_ui::kCompiler, "Unknown");
+#endif
+
+ base::string16 update_cohort_name =
+ install_static::InstallDetails::Get().update_cohort_name();
+ if (!update_cohort_name.empty()) {
+ html_source->AddString(version_ui::kUpdateCohortName,
+ l10n_util::GetStringFUTF16(
+ IDS_VERSION_UI_COHORT_NAME, update_cohort_name));
+ }
+#endif // defined(OS_WIN)
+
+ html_source->SetJsonPath("strings.js");
+ html_source->AddResourcePath(version_ui::kVersionJS, IDR_VERSION_UI_JS);
+ html_source->AddResourcePath(version_ui::kAboutVersionCSS,
+ IDR_VERSION_UI_CSS);
+ html_source->SetDefaultResource(IDR_VERSION_UI_HTML);
+ return html_source;
+}
+
+} // namespace
+
+VersionUI::VersionUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+
+#if defined(OS_CHROMEOS)
+ web_ui->AddMessageHandler(base::MakeUnique<VersionHandlerChromeOS>());
+#else
+ web_ui->AddMessageHandler(base::MakeUnique<VersionHandler>());
+#endif
+
+#if !defined(OS_ANDROID)
+ // Set up the chrome://theme/ source.
+ ThemeSource* theme = new ThemeSource(profile);
+ content::URLDataSource::Add(profile, theme);
+#endif
+
+ WebUIDataSource::Add(profile, CreateVersionUIDataSource());
+}
+
+VersionUI::~VersionUI() {
+}
diff --git a/chromium/chrome/browser/ui/webui/version_ui.h b/chromium/chrome/browser/ui/webui/version_ui.h
new file mode 100644
index 00000000000..9de2301fd8e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/version_ui.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_VERSION_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_VERSION_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The WebUI handler for chrome://version.
+class VersionUI : public content::WebUIController {
+ public:
+ explicit VersionUI(content::WebUI* web_ui);
+ ~VersionUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(VersionUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_VERSION_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/voice_search_ui.cc b/chromium/chrome/browser/ui/webui/voice_search_ui.cc
new file mode 100644
index 00000000000..6e3882b6949
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/voice_search_ui.cc
@@ -0,0 +1,446 @@
+// 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/ui/webui/voice_search_ui.h"
+
+#include <string>
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/files/file_enumerator.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/metrics/field_trial.h"
+#include "base/path_service.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/plugins/plugin_prefs.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search/hotword_service.h"
+#include "chrome/browser/search/hotword_service_factory.h"
+#include "chrome/browser/ui/app_list/start_page_service.h"
+#include "chrome/browser/ui/webui/version_handler.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/chrome_content_client.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/features.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/version_info/version_info.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/plugin_service.h"
+#include "content/public/browser/url_data_source.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "content/public/common/user_agent.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension.h"
+#include "extensions/features/features.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
+using base::ASCIIToUTF16;
+using content::WebUIMessageHandler;
+
+namespace {
+
+content::WebUIDataSource* CreateVoiceSearchUiHtmlSource() {
+ content::WebUIDataSource* html_source =
+ content::WebUIDataSource::Create(chrome::kChromeUIVoiceSearchHost);
+
+ html_source->AddLocalizedString("loadingMessage",
+ IDS_VOICESEARCH_LOADING_MESSAGE);
+ html_source->AddLocalizedString("voiceSearchLongTitle",
+ IDS_VOICESEARCH_TITLE_MESSAGE);
+
+ html_source->SetJsonPath("strings.js");
+ html_source->AddResourcePath("about_voicesearch.js",
+ IDR_ABOUT_VOICESEARCH_JS);
+ html_source->SetDefaultResource(IDR_ABOUT_VOICESEARCH_HTML);
+ return html_source;
+}
+
+// Helper functions for collecting a list of key-value pairs that will
+// be displayed.
+void AddPair16(base::ListValue* list,
+ const base::string16& key,
+ const base::string16& value) {
+ std::unique_ptr<base::DictionaryValue> results(new base::DictionaryValue());
+ results->SetString("key", key);
+ results->SetString("value", value);
+ list->Append(std::move(results));
+}
+
+void AddPair(base::ListValue* list,
+ const base::StringPiece& key,
+ const base::StringPiece& value) {
+ AddPair16(list, UTF8ToUTF16(key), UTF8ToUTF16(value));
+}
+
+void AddPairBool(base::ListValue* list,
+ const base::StringPiece& key,
+ bool value) {
+ AddPair(list, key, value ? "Yes" : "No");
+}
+
+// Generate an empty data-pair which acts as a line break.
+void AddLineBreak(base::ListValue* list) {
+ AddPair(list, "", "");
+}
+
+void AddSharedModulePlatformsOnFileThread(base::ListValue* list,
+ const base::FilePath& path,
+ base::Closure callback) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
+
+ if (!path.empty()) {
+ // Display available platforms for shared module.
+ base::FilePath platforms_path = path.AppendASCII("_platform_specific");
+ base::FileEnumerator enumerator(
+ platforms_path, false, base::FileEnumerator::DIRECTORIES);
+ base::string16 files;
+ for (base::FilePath name = enumerator.Next();
+ !name.empty();
+ name = enumerator.Next()) {
+ files += name.BaseName().LossyDisplayName() + ASCIIToUTF16(" ");
+ }
+ AddPair16(list,
+ ASCIIToUTF16("Shared Module Platforms"),
+ files.empty() ? ASCIIToUTF16("undefined") : files);
+ AddLineBreak(list);
+ }
+
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ callback);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// VoiceSearchDomHandler
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// The handler for Javascript messages for the about:flags page.
+class VoiceSearchDomHandler : public WebUIMessageHandler {
+ public:
+ explicit VoiceSearchDomHandler(Profile* profile)
+ : profile_(profile),
+ weak_factory_(this) {}
+
+ ~VoiceSearchDomHandler() override {}
+
+ // WebUIMessageHandler implementation.
+ void RegisterMessages() override {
+ web_ui()->RegisterMessageCallback(
+ "requestVoiceSearchInfo",
+ base::Bind(&VoiceSearchDomHandler::HandleRequestVoiceSearchInfo,
+ base::Unretained(this)));
+ }
+
+ private:
+ // Callback for the "requestVoiceSearchInfo" message. No arguments.
+ void HandleRequestVoiceSearchInfo(const base::ListValue* args) {
+ PopulatePageInformation();
+ }
+
+ void ReturnVoiceSearchInfo(std::unique_ptr<base::ListValue> info) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(info);
+ base::DictionaryValue voiceSearchInfo;
+ voiceSearchInfo.Set("voiceSearchInfo", std::move(info));
+ web_ui()->CallJavascriptFunctionUnsafe("returnVoiceSearchInfo",
+ voiceSearchInfo);
+ }
+
+ // Fill in the data to be displayed on the page.
+ void PopulatePageInformation() {
+ // Store Key-Value pairs of about-information.
+ std::unique_ptr<base::ListValue> list(new base::ListValue());
+
+ // Populate information.
+ AddOperatingSystemInfo(list.get());
+ AddAudioInfo(list.get());
+ AddLanguageInfo(list.get());
+ AddHotwordInfo(list.get());
+ AddAppListInfo(list.get());
+
+ AddExtensionInfo(extension_misc::kHotwordNewExtensionId,
+ "Extension",
+ list.get());
+
+ AddExtensionInfo(extension_misc::kHotwordSharedModuleId,
+ "Shared Module",
+ list.get());
+
+ base::FilePath path;
+ extensions::ExtensionSystem* extension_system =
+ extensions::ExtensionSystem::Get(profile_);
+ if (extension_system) {
+ ExtensionService* extension_service =
+ extension_system->extension_service();
+ const extensions::Extension* extension =
+ extension_service->GetExtensionById(
+ extension_misc::kHotwordSharedModuleId, true);
+ if (extension)
+ path = extension->path();
+ }
+ base::ListValue* raw_list = list.get();
+ content::BrowserThread::PostTask(
+ content::BrowserThread::FILE, FROM_HERE,
+ base::Bind(&AddSharedModulePlatformsOnFileThread, raw_list, path,
+ base::Bind(&VoiceSearchDomHandler::ReturnVoiceSearchInfo,
+ weak_factory_.GetWeakPtr(),
+ base::Passed(std::move(list)))));
+ }
+
+ // Adds information regarding the system and chrome version info to list.
+ void AddOperatingSystemInfo(base::ListValue* list) {
+ // Obtain the Chrome version info.
+ AddPair(list,
+ l10n_util::GetStringUTF8(IDS_PRODUCT_NAME),
+ version_info::GetVersionNumber() + " (" +
+ chrome::GetChannelString() + ")");
+
+ // OS version information.
+ std::string os_label = version_info::GetOSType();
+#if defined(OS_WIN)
+ base::win::OSInfo* os = base::win::OSInfo::GetInstance();
+ switch (os->version()) {
+ case base::win::VERSION_XP:
+ os_label += " XP";
+ break;
+ case base::win::VERSION_SERVER_2003:
+ os_label += " Server 2003 or XP Pro 64 bit";
+ break;
+ case base::win::VERSION_VISTA:
+ os_label += " Vista or Server 2008";
+ break;
+ case base::win::VERSION_WIN7:
+ os_label += " 7 or Server 2008 R2";
+ break;
+ case base::win::VERSION_WIN8:
+ os_label += " 8 or Server 2012";
+ break;
+ default:
+ os_label += " UNKNOWN";
+ break;
+ }
+ os_label += " SP" + base::IntToString(os->service_pack().major);
+
+ if (os->service_pack().minor > 0)
+ os_label += "." + base::IntToString(os->service_pack().minor);
+
+ if (os->architecture() == base::win::OSInfo::X64_ARCHITECTURE)
+ os_label += " 64 bit";
+#endif
+ AddPair(list, l10n_util::GetStringUTF8(IDS_VERSION_UI_OS), os_label);
+
+ AddLineBreak(list);
+ }
+
+ // Adds information regarding audio to the list.
+ void AddAudioInfo(base::ListValue* list) {
+ // NaCl and its associated functions are not available on most mobile
+ // platforms. ENABLE_EXTENSIONS covers those platforms and hey would not
+ // allow Hotwording anyways since it is an extension.
+ std::string nacl_enabled = "not available";
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+ nacl_enabled = "No";
+ // Determine if NaCl is available.
+ base::FilePath path;
+ if (PathService::Get(chrome::FILE_NACL_PLUGIN, &path)) {
+ content::WebPluginInfo info;
+ PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile_).get();
+ if (content::PluginService::GetInstance()->GetPluginInfoByPath(path,
+ &info) &&
+ plugin_prefs->IsPluginEnabled(info)) {
+ nacl_enabled = "Yes";
+ }
+ }
+#endif
+
+ AddPair(list, "NaCl Enabled", nacl_enabled);
+
+ HotwordService* hotword_service =
+ HotwordServiceFactory::GetForProfile(profile_);
+ AddPairBool(list, "Microphone Present",
+ hotword_service && hotword_service->microphone_available());
+
+ AddPairBool(list, "Audio Capture Allowed",
+ profile_->GetPrefs()->GetBoolean(prefs::kAudioCaptureAllowed));
+
+ AddLineBreak(list);
+ }
+
+ // Adds information regarding languages to the list.
+ void AddLanguageInfo(base::ListValue* list) {
+ std::string locale =
+#if defined(OS_CHROMEOS)
+ // On ChromeOS locale is per-profile.
+ profile_->GetPrefs()->GetString(prefs::kApplicationLocale);
+#else
+ g_browser_process->GetApplicationLocale();
+#endif
+ AddPair(list, "Current Language", locale);
+
+ AddPair(list,
+ "Hotword Previous Language",
+ profile_->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage));
+
+ AddLineBreak(list);
+ }
+
+ // Adds information specific to the hotword configuration to the list.
+ void AddHotwordInfo(base::ListValue* list) {
+ HotwordService* hotword_service =
+ HotwordServiceFactory::GetForProfile(profile_);
+ AddPairBool(list, "Hotword Module Installable",
+ hotword_service && hotword_service->IsHotwordAllowed());
+
+ AddPairBool(list, "Hotword Search Enabled",
+ profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled));
+
+ AddPairBool(
+ list, "Always-on Hotword Search Enabled",
+ profile_->GetPrefs()->GetBoolean(prefs::kHotwordAlwaysOnSearchEnabled));
+
+ AddPairBool(list, "Hotword Audio Logging Enabled",
+ hotword_service && hotword_service->IsOptedIntoAudioLogging());
+
+ AddLineBreak(list);
+ }
+
+ // Adds information specific to an extension to the list.
+ void AddExtensionInfo(const std::string& extension_id,
+ const std::string& name_prefix,
+ base::ListValue* list) {
+ DCHECK(!name_prefix.empty());
+ std::string version("undefined");
+ std::string id("undefined");
+ base::FilePath path;
+
+ extensions::ExtensionSystem* extension_system =
+ extensions::ExtensionSystem::Get(profile_);
+ if (extension_system) {
+ ExtensionService* extension_service =
+ extension_system->extension_service();
+ const extensions::Extension* extension =
+ extension_service->GetExtensionById(extension_id, true);
+ if (extension) {
+ id = extension->id();
+ version = extension->VersionString();
+ path = extension->path();
+ }
+ }
+ AddPair(list, name_prefix + " Id", id);
+ AddPair(list, name_prefix + " Version", version);
+ AddPair16(list,
+ ASCIIToUTF16(name_prefix + " Path"),
+ path.empty() ?
+ ASCIIToUTF16("undefined") : path.LossyDisplayName());
+
+ extensions::ExtensionPrefs* extension_prefs =
+ extensions::ExtensionPrefs::Get(profile_);
+ int pref_state = -1;
+ extension_prefs->ReadPrefAsInteger(extension_id, "state", &pref_state);
+ std::string state;
+ switch (pref_state) {
+ case extensions::Extension::DISABLED:
+ state = "DISABLED";
+ break;
+ case extensions::Extension::ENABLED:
+ state = "ENABLED";
+ break;
+ case extensions::Extension::EXTERNAL_EXTENSION_UNINSTALLED:
+ state = "EXTERNAL_EXTENSION_UNINSTALLED";
+ break;
+ default:
+ state = "undefined";
+ }
+
+ AddPair(list, name_prefix + " State", state);
+
+ AddLineBreak(list);
+ }
+
+ // Adds information specific to voice search in the app launcher to the list.
+ void AddAppListInfo(base::ListValue* list) {
+#if BUILDFLAG(ENABLE_APP_LIST)
+ std::string state = "No Start Page Service";
+ app_list::StartPageService* start_page_service =
+ app_list::StartPageService::Get(profile_);
+ if (start_page_service) {
+ app_list::SpeechRecognitionState speech_state =
+ start_page_service->state();
+ switch (speech_state) {
+ case app_list::SPEECH_RECOGNITION_OFF:
+ state = "SPEECH_RECOGNITION_OFF";
+ break;
+ case app_list::SPEECH_RECOGNITION_READY:
+ state = "SPEECH_RECOGNITION_READY";
+ break;
+ case app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING:
+ state = "SPEECH_RECOGNITION_HOTWORD_LISTENING";
+ break;
+ case app_list::SPEECH_RECOGNITION_RECOGNIZING:
+ state = "SPEECH_RECOGNITION_RECOGNIZING";
+ break;
+ case app_list::SPEECH_RECOGNITION_IN_SPEECH:
+ state = "SPEECH_RECOGNITION_IN_SPEECH";
+ break;
+ case app_list::SPEECH_RECOGNITION_STOPPING:
+ state = "SPEECH_RECOGNITION_STOPPING";
+ break;
+ case app_list::SPEECH_RECOGNITION_NETWORK_ERROR:
+ state = "SPEECH_RECOGNITION_NETWORK_ERROR";
+ break;
+ default:
+ state = "undefined";
+ }
+ }
+ AddPair(list, "Start Page State", state);
+ AddLineBreak(list);
+#endif
+ }
+
+ Profile* profile_;
+ base::WeakPtrFactory<VoiceSearchDomHandler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(VoiceSearchDomHandler);
+};
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// VoiceSearchUI
+//
+///////////////////////////////////////////////////////////////////////////////
+
+VoiceSearchUI::VoiceSearchUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ web_ui->AddMessageHandler(base::MakeUnique<VoiceSearchDomHandler>(profile));
+
+ // Set up the about:voicesearch source.
+ content::WebUIDataSource::Add(profile, CreateVoiceSearchUiHtmlSource());
+}
+
+VoiceSearchUI::~VoiceSearchUI() {}
diff --git a/chromium/chrome/browser/ui/webui/voice_search_ui.h b/chromium/chrome/browser/ui/webui/voice_search_ui.h
new file mode 100644
index 00000000000..a4d25680ae2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/voice_search_ui.h
@@ -0,0 +1,21 @@
+// 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_UI_WEBUI_VOICE_SEARCH_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_VOICE_SEARCH_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The WebUI handler for chrome://voicesearch.
+class VoiceSearchUI : public content::WebUIController {
+ public:
+ explicit VoiceSearchUI(content::WebUI* web_ui);
+ ~VoiceSearchUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(VoiceSearchUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_VOICE_SEARCH_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/web_dialog_web_contents_delegate_unittest.cc b/chromium/chrome/browser/ui/webui/web_dialog_web_contents_delegate_unittest.cc
new file mode 100644
index 00000000000..df6506a8507
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/web_dialog_web_contents_delegate_unittest.cc
@@ -0,0 +1,118 @@
+// 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 "ui/web_dialogs/web_dialog_web_contents_delegate.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/chrome_web_contents_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/browser_with_test_window_test.h"
+#include "chrome/test/base/test_browser_window.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/history/core/browser/history_types.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/web_contents_tester.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+#include "url/gurl.h"
+
+using content::OpenURLParams;
+using content::Referrer;
+using content::BrowserContext;
+using content::WebContents;
+using content::WebContentsTester;
+using ui::WebDialogWebContentsDelegate;
+
+namespace {
+
+class TestWebContentsDelegate : public WebDialogWebContentsDelegate {
+ public:
+ explicit TestWebContentsDelegate(content::BrowserContext* context)
+ : WebDialogWebContentsDelegate(context, new ChromeWebContentsHandler) {
+ }
+ ~TestWebContentsDelegate() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestWebContentsDelegate);
+};
+
+class WebDialogWebContentsDelegateTest : public BrowserWithTestWindowTest {
+ public:
+ void SetUp() override {
+ BrowserWithTestWindowTest::SetUp();
+ test_web_contents_delegate_.reset(new TestWebContentsDelegate(profile()));
+ }
+
+ void TearDown() override {
+ test_web_contents_delegate_.reset(NULL);
+ BrowserWithTestWindowTest::TearDown();
+ }
+
+ protected:
+ std::unique_ptr<TestWebContentsDelegate> test_web_contents_delegate_;
+};
+
+TEST_F(WebDialogWebContentsDelegateTest, DoNothingMethodsTest) {
+ // None of the following calls should do anything.
+ EXPECT_TRUE(test_web_contents_delegate_->IsPopupOrPanel(NULL));
+ history::HistoryAddPageArgs should_add_args(
+ GURL(), base::Time::Now(), 0, 0, GURL(), history::RedirectList(),
+ ui::PAGE_TRANSITION_TYPED, history::SOURCE_SYNCED, false, true);
+ test_web_contents_delegate_->NavigationStateChanged(
+ NULL, content::InvalidateTypes(0));
+ test_web_contents_delegate_->ActivateContents(NULL);
+ test_web_contents_delegate_->LoadingStateChanged(NULL, true);
+ test_web_contents_delegate_->CloseContents(NULL);
+ test_web_contents_delegate_->UpdateTargetURL(NULL, GURL());
+ test_web_contents_delegate_->MoveContents(NULL, gfx::Rect());
+ EXPECT_EQ(0, browser()->tab_strip_model()->count());
+ EXPECT_EQ(1U, chrome::GetTotalBrowserCount());
+}
+
+TEST_F(WebDialogWebContentsDelegateTest, OpenURLFromTabTest) {
+ test_web_contents_delegate_->OpenURLFromTab(
+ NULL, OpenURLParams(GURL(url::kAboutBlankURL), Referrer(),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui::PAGE_TRANSITION_LINK, false));
+ // This should create a new foreground tab in the existing browser.
+ EXPECT_EQ(1, browser()->tab_strip_model()->count());
+ EXPECT_EQ(1U, chrome::GetTotalBrowserCount());
+}
+
+TEST_F(WebDialogWebContentsDelegateTest, AddNewContentsForegroundTabTest) {
+ WebContents* contents =
+ WebContentsTester::CreateTestWebContents(profile(), NULL);
+ test_web_contents_delegate_->AddNewContents(
+ NULL, contents, WindowOpenDisposition::NEW_FOREGROUND_TAB, gfx::Rect(),
+ false, NULL);
+ // This should create a new foreground tab in the existing browser.
+ EXPECT_EQ(1, browser()->tab_strip_model()->count());
+ EXPECT_EQ(1U, chrome::GetTotalBrowserCount());
+}
+
+TEST_F(WebDialogWebContentsDelegateTest, DetachTest) {
+ EXPECT_EQ(profile(), test_web_contents_delegate_->browser_context());
+ test_web_contents_delegate_->Detach();
+ EXPECT_EQ(NULL, test_web_contents_delegate_->browser_context());
+ // Now, none of the following calls should do anything.
+ test_web_contents_delegate_->OpenURLFromTab(
+ NULL, OpenURLParams(GURL(url::kAboutBlankURL), Referrer(),
+ WindowOpenDisposition::NEW_FOREGROUND_TAB,
+ ui::PAGE_TRANSITION_LINK, false));
+ test_web_contents_delegate_->AddNewContents(
+ NULL, NULL, WindowOpenDisposition::NEW_FOREGROUND_TAB, gfx::Rect(), false,
+ NULL);
+ EXPECT_EQ(0, browser()->tab_strip_model()->count());
+ EXPECT_EQ(1U, chrome::GetTotalBrowserCount());
+}
+
+} // namespace
diff --git a/chromium/chrome/browser/ui/webui/web_ui_test_handler.cc b/chromium/chrome/browser/ui/webui/web_ui_test_handler.cc
new file mode 100644
index 00000000000..0c700a9afab
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/web_ui_test_handler.cc
@@ -0,0 +1,117 @@
+// 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/ui/webui/web_ui_test_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/common/render_messages.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::RenderViewHost;
+
+WebUITestHandler::WebUITestHandler()
+ : test_done_(false),
+ test_succeeded_(false),
+ run_test_done_(false),
+ run_test_succeeded_(false),
+ is_waiting_(false) {
+}
+
+void WebUITestHandler::PreloadJavaScript(const base::string16& js_text,
+ RenderViewHost* preload_host) {
+ DCHECK(preload_host);
+ preload_host->Send(new ChromeViewMsg_WebUIJavaScript(
+ preload_host->GetRoutingID(), js_text));
+}
+
+void WebUITestHandler::RunJavaScript(const base::string16& js_text) {
+ web_ui()->GetWebContents()->GetMainFrame()->ExecuteJavaScriptForTests(
+ js_text);
+}
+
+bool WebUITestHandler::RunJavaScriptTestWithResult(
+ const base::string16& js_text) {
+ test_succeeded_ = false;
+ run_test_succeeded_ = false;
+ content::RenderFrameHost* frame = web_ui()->GetWebContents()->GetMainFrame();
+ frame->ExecuteJavaScriptForTests(
+ js_text, base::Bind(&WebUITestHandler::JavaScriptComplete,
+ base::Unretained(this)));
+ return WaitForResult();
+}
+
+void WebUITestHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("testResult",
+ base::Bind(&WebUITestHandler::HandleTestResult, base::Unretained(this)));
+}
+
+void WebUITestHandler::HandleTestResult(const base::ListValue* test_result) {
+ // Quit the message loop if |is_waiting_| so waiting process can get result or
+ // error. To ensure this gets done, do this before ASSERT* calls.
+ if (is_waiting_)
+ base::MessageLoopForUI::current()->QuitWhenIdle();
+
+ SCOPED_TRACE("WebUITestHandler::HandleTestResult");
+
+ EXPECT_FALSE(test_done_);
+ test_done_ = true;
+ test_succeeded_ = false;
+
+ ASSERT_TRUE(test_result->GetBoolean(0, &test_succeeded_));
+ if (!test_succeeded_) {
+ std::string message;
+ ASSERT_TRUE(test_result->GetString(1, &message));
+ LOG(ERROR) << message;
+ }
+}
+
+void WebUITestHandler::JavaScriptComplete(const base::Value* result) {
+ // Quit the message loop if |is_waiting_| so waiting process can get result or
+ // error. To ensure this gets done, do this before ASSERT* calls.
+ if (is_waiting_)
+ base::MessageLoopForUI::current()->QuitWhenIdle();
+
+ SCOPED_TRACE("WebUITestHandler::JavaScriptComplete");
+
+ EXPECT_FALSE(run_test_done_);
+ run_test_done_ = true;
+ run_test_succeeded_ = false;
+
+ ASSERT_TRUE(result->GetAsBoolean(&run_test_succeeded_));
+}
+
+bool WebUITestHandler::WaitForResult() {
+ SCOPED_TRACE("WebUITestHandler::WaitForResult");
+ test_done_ = false;
+ run_test_done_ = false;
+ is_waiting_ = true;
+
+ // Either sync test completion or the testDone() will cause message loop
+ // to quit.
+ content::RunMessageLoop();
+
+ // Run a second message loop when not |run_test_done_| so that the sync test
+ // completes, or |run_test_succeeded_| but not |test_done_| so async tests
+ // complete.
+ if (!run_test_done_ || (run_test_succeeded_ && !test_done_)) {
+ content::RunMessageLoop();
+ }
+
+ is_waiting_ = false;
+
+ // To succeed the test must execute as well as pass the test.
+ return run_test_succeeded_ && test_succeeded_;
+}
diff --git a/chromium/chrome/browser/ui/webui/web_ui_test_handler.h b/chromium/chrome/browser/ui/webui/web_ui_test_handler.h
new file mode 100644
index 00000000000..cb95c2f0b9a
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/web_ui_test_handler.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2011 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_WEB_UI_TEST_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_WEB_UI_TEST_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace base {
+class ListValue;
+class Value;
+}
+
+namespace content {
+class RenderViewHost;
+}
+
+// This class registers test framework specific handlers on WebUI objects.
+class WebUITestHandler : public content::WebUIMessageHandler {
+ public:
+ WebUITestHandler();
+
+ // Sends a message through |preload_host| with the |js_text| to preload at the
+ // appropriate time before the onload call is made.
+ void PreloadJavaScript(const base::string16& js_text,
+ content::RenderViewHost* preload_host);
+
+ // Runs |js_text| in this object's WebUI frame. Does not wait for any result.
+ void RunJavaScript(const base::string16& js_text);
+
+ // Runs |js_text| in this object's WebUI frame. Waits for result, logging an
+ // error message on failure. Returns test pass/fail.
+ bool RunJavaScriptTestWithResult(const base::string16& js_text);
+
+ // WebUIMessageHandler overrides.
+ // Add test handlers to the current WebUI object.
+ void RegisterMessages() override;
+
+ private:
+ // Receives testResult messages.
+ void HandleTestResult(const base::ListValue* test_result);
+
+ // Gets the callback that Javascript execution is complete.
+ void JavaScriptComplete(const base::Value* result);
+
+ // Runs a message loop until test finishes. Returns the result of the
+ // test.
+ bool WaitForResult();
+
+ // Received test pass/fail;
+ bool test_done_;
+
+ // Pass fail result of current test.
+ bool test_succeeded_;
+
+ // Test code finished trying to execute. This will be set to true when the
+ // selected tab is done with this execution request whether it was able to
+ // parse/execute the javascript or not.
+ bool run_test_done_;
+
+ // Test code was able to execute successfully. This is *NOT* the test
+ // pass/fail.
+ bool run_test_succeeded_;
+
+ // Waiting for a test to finish.
+ bool is_waiting_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebUITestHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_WEB_UI_TEST_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/webapks_handler.cc b/chromium/chrome/browser/ui/webui/webapks_handler.cc
new file mode 100644
index 00000000000..264fe0e41be
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/webapks_handler.cc
@@ -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.
+
+#include "chrome/browser/ui/webui/webapks_handler.h"
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "chrome/browser/android/shortcut_helper.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/common/manifest_util.h"
+#include "ui/gfx/color_utils.h"
+
+namespace {
+// Converts a color from the format documented in content::Manifest to a
+// rgba() CSS string.
+std::string ColorToString(int64_t color) {
+ if (color == content::Manifest::kInvalidOrMissingColor)
+ return std::string();
+ return color_utils::SkColorToRgbaString(reinterpret_cast<uint32_t&>(color));
+}
+} // namespace
+
+WebApksHandler::WebApksHandler() : weak_ptr_factory_(this) {}
+
+WebApksHandler::~WebApksHandler() {}
+
+void WebApksHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "requestWebApksInfo",
+ base::Bind(&WebApksHandler::HandleRequestWebApksInfo,
+ base::Unretained(this)));
+}
+
+void WebApksHandler::HandleRequestWebApksInfo(const base::ListValue* args) {
+ AllowJavascript();
+ ShortcutHelper::RetrieveWebApks(base::Bind(
+ &WebApksHandler::OnWebApkInfoRetrieved, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void WebApksHandler::OnWebApkInfoRetrieved(
+ const std::vector<WebApkInfo>& webapks_list) {
+ if (!IsJavascriptAllowed())
+ return;
+ base::ListValue list;
+ for (const auto& webapk_info : webapks_list) {
+ std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
+ result->SetString("name", webapk_info.name);
+ result->SetString("shortName", webapk_info.short_name);
+ result->SetString("packageName", webapk_info.package_name);
+ result->SetInteger("shellApkVersion", webapk_info.shell_apk_version);
+ result->SetInteger("versionCode", webapk_info.version_code);
+ result->SetString("uri", webapk_info.uri);
+ result->SetString("scope", webapk_info.scope);
+ result->SetString("manifestUrl", webapk_info.manifest_url);
+ result->SetString("manifestStartUrl", webapk_info.manifest_start_url);
+ result->SetString("displayMode",
+ content::WebDisplayModeToString(webapk_info.display));
+ result->SetString(
+ "orientation",
+ content::WebScreenOrientationLockTypeToString(webapk_info.orientation));
+ result->SetString("themeColor", ColorToString(webapk_info.theme_color));
+ result->SetString("backgroundColor",
+ ColorToString(webapk_info.background_color));
+ list.Append(std::move(result));
+ }
+
+ CallJavascriptFunction("returnWebApksInfo", list);
+}
diff --git a/chromium/chrome/browser/ui/webui/webapks_handler.h b/chromium/chrome/browser/ui/webui/webapks_handler.h
new file mode 100644
index 00000000000..7d051ee8a1e
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/webapks_handler.h
@@ -0,0 +1,43 @@
+// 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_WEBAPKS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_WEBAPKS_HANDLER_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/android/webapk/webapk_info.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace base {
+class ListValue;
+} // namespace base
+
+// Handles JavaScript messages from the chrome://webapks page.
+class WebApksHandler : public content::WebUIMessageHandler {
+ public:
+ WebApksHandler();
+ ~WebApksHandler() override;
+
+ // content::WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ // Handler for the "requestWebApksInfo" message. This requests
+ // information for the installed WebAPKs and returns it to JS using
+ // OnWebApkInfoReceived().
+ virtual void HandleRequestWebApksInfo(const base::ListValue* args);
+
+ private:
+ // Sends information for the installed WebAPKs to JS.
+ void OnWebApkInfoRetrieved(const std::vector<WebApkInfo>& webapks_list);
+
+ // Factory for the creating refs in callbacks.
+ base::WeakPtrFactory<WebApksHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebApksHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_WEBAPKS_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/webapks_ui.cc b/chromium/chrome/browser/ui/webui/webapks_ui.cc
new file mode 100644
index 00000000000..d713293024c
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/webapks_ui.cc
@@ -0,0 +1,43 @@
+// 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/webapks_ui.h"
+
+#include <string>
+#include <unordered_set>
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/webapks_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/webapks_ui_resources.h"
+#include "components/grit/components_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+using content::WebUIDataSource;
+
+namespace {
+
+WebUIDataSource* CreateWebApksUIDataSource() {
+ WebUIDataSource* html_source =
+ WebUIDataSource::Create(chrome::kChromeUIWebApksHost);
+ html_source->SetJsonPath("strings.js");
+ html_source->AddResourcePath("webapks.js", IDR_WEBAPKS_UI_JS);
+ html_source->AddResourcePath("about_webapks.css", IDR_WEBAPKS_UI_CSS);
+ html_source->SetDefaultResource(IDR_WEBAPKS_UI_HTML);
+ html_source->UseGzip(std::unordered_set<std::string>());
+
+ return html_source;
+}
+
+} // anonymous namespace
+
+WebApksUI::WebApksUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+ web_ui->AddMessageHandler(base::MakeUnique<WebApksHandler>());
+ WebUIDataSource::Add(profile, CreateWebApksUIDataSource());
+}
+
+WebApksUI::~WebApksUI() {}
diff --git a/chromium/chrome/browser/ui/webui/webapks_ui.h b/chromium/chrome/browser/ui/webui/webapks_ui.h
new file mode 100644
index 00000000000..c2a1f0c95f9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/webapks_ui.h
@@ -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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_WEBAPKS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_WEBAPKS_UI_H_
+
+#include "base/macros.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The WebUI handler for chrome://webapks.
+class WebApksUI : public content::WebUIController {
+ public:
+ explicit WebApksUI(content::WebUI* web_ui);
+ ~WebApksUI() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WebApksUI);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_WEBAPKS_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/webui_browsertest.cc b/chromium/chrome/browser/ui/webui/webui_browsertest.cc
new file mode 100644
index 00000000000..dbbee1d0ecd
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/webui_browsertest.cc
@@ -0,0 +1,94 @@
+// 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.
+
+#include "base/command_line.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_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 "content/public/browser/child_process_security_policy.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+
+namespace {
+
+class TestWebUIMessageHandler : public content::WebUIMessageHandler {
+ public:
+ void RegisterMessages() override {}
+};
+
+} // namespace
+
+using WebUIImplBrowserTest = InProcessBrowserTest;
+
+// Tests that navigating between WebUIs of different types results in
+// SiteInstance swap when running in process-per-tab process model.
+IN_PROC_BROWSER_TEST_F(WebUIImplBrowserTest, ForceSwapOnDifferenteWebUITypes) {
+ base::CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kProcessPerTab);
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+
+ const GURL web_ui_url(std::string(content::kChromeUIScheme) + "://" +
+ std::string(chrome::kChromeUIFlagsHost));
+ EXPECT_TRUE(ChromeWebUIControllerFactory::GetInstance()->UseWebUIForURL(
+ web_contents->GetBrowserContext(), web_ui_url));
+ ui_test_utils::NavigateToURL(browser(), web_ui_url);
+ EXPECT_TRUE(
+ content::ChildProcessSecurityPolicy::GetInstance()->HasWebUIBindings(
+ web_contents->GetRenderProcessHost()->GetID()));
+
+ // Capture the SiteInstance before navigating for later comparison.
+ scoped_refptr<content::SiteInstance> orig_site_instance(
+ web_contents->GetSiteInstance());
+
+ // Navigate to a different WebUI type and ensure that the SiteInstance
+ // has changed and the new process also has WebUI bindings.
+ const GURL web_ui_url2(std::string(content::kChromeUIScheme) + "://" +
+ std::string(chrome::kChromeUIVersionHost));
+ EXPECT_TRUE(ChromeWebUIControllerFactory::GetInstance()->UseWebUIForURL(
+ web_contents->GetBrowserContext(), web_ui_url2));
+ ui_test_utils::NavigateToURL(browser(), web_ui_url2);
+ EXPECT_NE(orig_site_instance, web_contents->GetSiteInstance());
+ EXPECT_TRUE(
+ content::ChildProcessSecurityPolicy::GetInstance()->HasWebUIBindings(
+ web_contents->GetRenderProcessHost()->GetID()));
+}
+
+IN_PROC_BROWSER_TEST_F(WebUIImplBrowserTest, InPageNavigationsAndReload) {
+ ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUITermsURL));
+
+ content::WebUIMessageHandler* test_handler = new TestWebUIMessageHandler;
+ browser()
+ ->tab_strip_model()
+ ->GetActiveWebContents()
+ ->GetWebUI()
+ ->AddMessageHandler(base::WrapUnique(test_handler));
+ test_handler->AllowJavascriptForTesting();
+
+ // Push onto window.history. Back should now be an in-page navigation.
+ ASSERT_TRUE(content::ExecuteScript(
+ browser()->tab_strip_model()->GetActiveWebContents(),
+ "window.history.pushState({}, '', 'foo.html')"));
+ chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB);
+ content::WaitForLoadStop(
+ browser()->tab_strip_model()->GetActiveWebContents());
+
+ // Test handler should still have JavaScript allowed after in-page navigation.
+ EXPECT_TRUE(test_handler->IsJavascriptAllowed());
+
+ chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
+ content::WaitForLoadStop(
+ browser()->tab_strip_model()->GetActiveWebContents());
+
+ // Verify that after a reload, the test handler has been disallowed.
+ EXPECT_FALSE(test_handler->IsJavascriptAllowed());
+}
diff --git a/chromium/chrome/browser/ui/webui/webui_webview_browsertest.cc b/chromium/chrome/browser/ui/webui/webui_webview_browsertest.cc
new file mode 100644
index 00000000000..636e059a741
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/webui_webview_browsertest.cc
@@ -0,0 +1,371 @@
+// 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.
+
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
+#include "chrome/browser/signin/signin_promo.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "chrome/test/base/web_ui_browser_test.h"
+#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/context_menu_params.h"
+#include "content/public/common/drop_data.h"
+#include "content/public/test/browser_test_utils.h"
+#include "extensions/test/extension_test_message_listener.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+
+// Turn these tests off on Mac while we collect data on windows server crashes
+// on mac chromium builders.
+// http://crbug.com/653353
+#if !defined(OS_MACOSX)
+
+#if !defined(OS_CHROMEOS) && defined(USE_AURA)
+#include "ui/aura/window.h"
+
+namespace {
+
+class WebUIMessageListener : public base::SupportsWeakPtr<WebUIMessageListener>{
+ public:
+ WebUIMessageListener(content::WebUI* web_ui, const std::string& message)
+ : message_loop_(new content::MessageLoopRunner) {
+ web_ui->RegisterMessageCallback(
+ message, base::Bind(&WebUIMessageListener::HandleMessage,
+ AsWeakPtr()));
+ }
+ bool Wait() {
+ message_loop_->Run();
+ return true;
+ }
+
+ private:
+ void HandleMessage(const base::ListValue* test_result) {
+ message_loop_->Quit();
+ }
+
+ scoped_refptr<content::MessageLoopRunner> message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebUIMessageListener);
+};
+
+class DNDToInputNavigationObserver : public content::WebContentsObserver {
+ public:
+ explicit DNDToInputNavigationObserver(content::WebContents* web_contents) {
+ Observe(web_contents);
+ }
+ void DidFinishNavigation(
+ content::NavigationHandle* navigation_handle) override {
+ navigated = true;
+ }
+ bool Navigated() const { return navigated; }
+
+ private:
+ bool navigated = false;
+
+ DISALLOW_COPY_AND_ASSIGN(DNDToInputNavigationObserver);
+};
+
+int ExecuteHostScriptAndExtractInt(content::WebContents* web_contents,
+ const std::string& script) {
+ int result;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractInt(
+ web_contents, "window.domAutomationController.send(" + script + ");",
+ &result));
+ return result;
+}
+
+int ExecuteGuestScriptAndExtractInt(content::WebContents* web_contents,
+ const std::string& web_view_id,
+ const std::string& script) {
+ int result;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractInt(
+ web_contents,
+ "document.getElementById('" + web_view_id + "').executeScript({ "
+ "code: '" + script + "' }, function (results) {"
+ " window.domAutomationController.send(results[0]);});",
+ &result));
+ return result;
+}
+} // namespace
+#endif
+
+class WebUIWebViewBrowserTest : public WebUIBrowserTest {
+ public:
+ WebUIWebViewBrowserTest() {}
+
+ void SetUpOnMainThread() override {
+ WebUIBrowserTest::SetUpOnMainThread();
+ AddLibrary(
+ base::FilePath(FILE_PATH_LITERAL("webview_content_script_test.js")));
+ AddLibrary(
+ base::FilePath(FILE_PATH_LITERAL("webview_basic.js")));
+
+ base::FilePath test_data_dir;
+ PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
+ embedded_test_server()->ServeFilesFromDirectory(test_data_dir);
+ ASSERT_TRUE(embedded_test_server()->Start());
+ }
+
+ GURL GetTestUrl(const std::string& path) const {
+ return embedded_test_server()->base_url().Resolve(path);
+ }
+
+ GURL GetWebViewEnabledWebUIURL() const {
+#if defined(OS_CHROMEOS)
+ return GURL(chrome::kChromeUIOobeURL).Resolve("/login");
+#else
+ return GURL(signin::GetPromoURL(
+ signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE,
+ signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT, false));
+#endif
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WebUIWebViewBrowserTest);
+};
+
+// Checks that hiding and showing the WebUI host page doesn't break guests in
+// it.
+// Regression test for http://crbug.com/515268
+IN_PROC_BROWSER_TEST_F(WebUIWebViewBrowserTest, DisplayNone) {
+ ui_test_utils::NavigateToURL(browser(), GetWebViewEnabledWebUIURL());
+
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
+ "testDisplayNone", base::Value(GetTestUrl("empty.html").spec())));
+}
+
+IN_PROC_BROWSER_TEST_F(WebUIWebViewBrowserTest, ExecuteScriptCode) {
+ ui_test_utils::NavigateToURL(browser(), GetWebViewEnabledWebUIURL());
+
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
+ "testExecuteScriptCode", base::Value(GetTestUrl("empty.html").spec())));
+}
+
+IN_PROC_BROWSER_TEST_F(WebUIWebViewBrowserTest, ExecuteScriptCodeFromFile) {
+ ui_test_utils::NavigateToURL(browser(), GetWebViewEnabledWebUIURL());
+
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
+ "testExecuteScriptCodeFromFile",
+ base::Value(GetTestUrl("empty.html").spec())));
+}
+
+IN_PROC_BROWSER_TEST_F(WebUIWebViewBrowserTest, AddContentScript) {
+ ui_test_utils::NavigateToURL(browser(), GetWebViewEnabledWebUIURL());
+
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
+ "testAddContentScript", base::Value(GetTestUrl("empty.html").spec())));
+}
+
+IN_PROC_BROWSER_TEST_F(WebUIWebViewBrowserTest, AddMultiContentScripts) {
+ ui_test_utils::NavigateToURL(browser(), GetWebViewEnabledWebUIURL());
+
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
+ "testAddMultiContentScripts",
+ base::Value(GetTestUrl("empty.html").spec())));
+}
+
+IN_PROC_BROWSER_TEST_F(
+ WebUIWebViewBrowserTest,
+ AddContentScriptWithSameNameShouldOverwriteTheExistingOne) {
+ ui_test_utils::NavigateToURL(browser(), GetWebViewEnabledWebUIURL());
+
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
+ "testAddContentScriptWithSameNameShouldOverwriteTheExistingOne",
+ base::Value(GetTestUrl("empty.html").spec())));
+}
+
+IN_PROC_BROWSER_TEST_F(
+ WebUIWebViewBrowserTest,
+ AddContentScriptToOneWebViewShouldNotInjectToTheOtherWebView) {
+ ui_test_utils::NavigateToURL(browser(), GetWebViewEnabledWebUIURL());
+
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
+ "testAddContentScriptToOneWebViewShouldNotInjectToTheOtherWebView",
+ base::Value(GetTestUrl("empty.html").spec())));
+}
+
+IN_PROC_BROWSER_TEST_F(WebUIWebViewBrowserTest, AddAndRemoveContentScripts) {
+ ui_test_utils::NavigateToURL(browser(), GetWebViewEnabledWebUIURL());
+
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
+ "testAddAndRemoveContentScripts",
+ base::Value(GetTestUrl("empty.html").spec())));
+}
+
+IN_PROC_BROWSER_TEST_F(WebUIWebViewBrowserTest,
+ AddContentScriptsWithNewWindowAPI) {
+ ui_test_utils::NavigateToURL(browser(), GetWebViewEnabledWebUIURL());
+
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
+ "testAddContentScriptsWithNewWindowAPI",
+ base::Value(GetTestUrl("guest_from_opener.html").spec())));
+}
+
+// https://crbug.com/665512.
+IN_PROC_BROWSER_TEST_F(
+ WebUIWebViewBrowserTest,
+ DISABLED_ContentScriptIsInjectedAfterTerminateAndReloadWebView) {
+ ui_test_utils::NavigateToURL(browser(), GetWebViewEnabledWebUIURL());
+
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
+ "testContentScriptIsInjectedAfterTerminateAndReloadWebView",
+ base::Value(GetTestUrl("empty.html").spec())));
+}
+
+// TODO(crbug.com/662673) Flaky on CrOS trybots.
+#if defined(OS_CHROMEOS)
+#define MAYBE_ContentScriptExistsAsLongAsWebViewTagExists \
+ DISABLED_ContentScriptExistsAsLongAsWebViewTagExists
+#else
+#define MAYBE_ContentScriptExistsAsLongAsWebViewTagExists \
+ ContentScriptExistsAsLongAsWebViewTagExists
+#endif
+IN_PROC_BROWSER_TEST_F(WebUIWebViewBrowserTest,
+ MAYBE_ContentScriptExistsAsLongAsWebViewTagExists) {
+ ui_test_utils::NavigateToURL(browser(), GetWebViewEnabledWebUIURL());
+
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
+ "testContentScriptExistsAsLongAsWebViewTagExists",
+ base::Value(GetTestUrl("empty.html").spec())));
+}
+
+IN_PROC_BROWSER_TEST_F(WebUIWebViewBrowserTest, AddContentScriptWithCode) {
+ ui_test_utils::NavigateToURL(browser(), GetWebViewEnabledWebUIURL());
+
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
+ "testAddContentScriptWithCode",
+ base::Value(GetTestUrl("empty.html").spec())));
+}
+
+#if defined(OS_CHROMEOS)
+// Right now we only have incognito WebUI on CrOS, but this should
+// theoretically work for all platforms.
+IN_PROC_BROWSER_TEST_F(WebUIWebViewBrowserTest, AddContentScriptIncognito) {
+ Browser* incognito_browser =
+ OpenURLOffTheRecord(browser()->profile(), GetWebViewEnabledWebUIURL());
+
+ SetWebUIInstance(
+ incognito_browser->tab_strip_model()->GetActiveWebContents()->GetWebUI());
+
+ ASSERT_TRUE(WebUIBrowserTest::RunJavascriptAsyncTest(
+ "testAddContentScript", base::Value(GetTestUrl("empty.html").spec())));
+}
+#endif
+
+IN_PROC_BROWSER_TEST_F(WebUIWebViewBrowserTest, ContextMenuInspectElement) {
+ ui_test_utils::NavigateToURL(browser(), GetWebViewEnabledWebUIURL());
+ content::ContextMenuParams params;
+ TestRenderViewContextMenu menu(
+ browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(),
+ params);
+ EXPECT_FALSE(menu.IsItemPresent(IDC_CONTENT_CONTEXT_INSPECTELEMENT));
+}
+
+#if !defined(OS_CHROMEOS) && defined(USE_AURA)
+IN_PROC_BROWSER_TEST_F(WebUIWebViewBrowserTest, DISABLED_DragAndDropToInput) {
+ ui_test_utils::NavigateToURL(browser(), GetWebViewEnabledWebUIURL());
+ ASSERT_TRUE(
+ WebUIBrowserTest::RunJavascriptAsyncTest("testDragAndDropToInput"));
+
+ content::WebContents* const embedder_web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+
+ // Flush any pending events to make sure we start with a clean slate.
+ content::RunAllPendingInMessageLoop();
+ content::RenderViewHost* const render_view_host =
+ embedder_web_contents->GetRenderViewHost();
+
+ gfx::NativeView view = embedder_web_contents->GetNativeView();
+ view->SetBounds(gfx::Rect(0, 0, 400, 400));
+
+ const gfx::Rect webview_rect(
+ ExecuteHostScriptAndExtractInt(embedder_web_contents,
+ "webview.offsetLeft"),
+ ExecuteHostScriptAndExtractInt(embedder_web_contents,
+ "webview.offsetTop"),
+ ExecuteHostScriptAndExtractInt(embedder_web_contents,
+ "webview.offsetWidth"),
+ ExecuteHostScriptAndExtractInt(embedder_web_contents,
+ "webview.offsetHeight"));
+
+ const gfx::Rect guest_dest_rect(
+ ExecuteGuestScriptAndExtractInt(embedder_web_contents, "webview",
+ "destNode.offsetLeft"),
+ ExecuteGuestScriptAndExtractInt(embedder_web_contents, "webview",
+ "destNode.offsetTop"),
+ ExecuteGuestScriptAndExtractInt(embedder_web_contents, "webview",
+ "destNode.offsetWidth"),
+ ExecuteGuestScriptAndExtractInt(embedder_web_contents, "webview",
+ "destNode.offsetHeight"));
+ const gfx::Point client_pt(
+ guest_dest_rect.x() + guest_dest_rect.width() / 2 + webview_rect.x(),
+ guest_dest_rect.y() + guest_dest_rect.height() / 2 + webview_rect.y());
+ gfx::Rect container_bounds = embedder_web_contents->GetContainerBounds();
+ const gfx::Point screen_pt(container_bounds.x(), container_bounds.y());
+ const blink::WebDragOperationsMask drag_operation_mask =
+ static_cast<blink::WebDragOperationsMask>(blink::kWebDragOperationCopy |
+ blink::kWebDragOperationLink |
+ blink::kWebDragOperationMove);
+ content::DropData dropdata;
+ dropdata.did_originate_from_renderer = true;
+ dropdata.url = GURL(url::kAboutBlankURL);
+ dropdata.url_title = base::string16(base::ASCIIToUTF16("Drop me"));
+
+ // Drag url into input in webview.
+
+ // TODO(paulmeyer): The following drag-and-drop calls on
+ // render_view_host->GetWidget() will need to be targeted to specific
+ // RenderWidgetHosts in order to work with OOPIFs. See crbug.com/647249.
+
+ {
+ EXPECT_TRUE(content::ExecuteScript(embedder_web_contents,
+ "console.log('step1: Drag Enter')"));
+
+ WebUIMessageListener listener(embedder_web_contents->GetWebUI(),
+ "Step1: destNode gets dragenter");
+ render_view_host->GetWidget()->FilterDropData(&dropdata);
+ render_view_host->GetWidget()->DragTargetDragEnter(
+ dropdata, client_pt, screen_pt, drag_operation_mask,
+ blink::WebInputEvent::kLeftButtonDown);
+ ASSERT_TRUE(listener.Wait());
+ }
+
+ {
+ EXPECT_TRUE(content::ExecuteScript(embedder_web_contents,
+ "console.log('step2: Drag Over')"));
+
+ WebUIMessageListener listener(embedder_web_contents->GetWebUI(),
+ "Step2: destNode gets dragover");
+ render_view_host->GetWidget()->DragTargetDragOver(
+ client_pt, screen_pt, drag_operation_mask,
+ blink::WebInputEvent::kLeftButtonDown);
+ ASSERT_TRUE(listener.Wait());
+ }
+
+ {
+ EXPECT_TRUE(content::ExecuteScript(embedder_web_contents,
+ "console.log('step3: Drop')"));
+
+ DNDToInputNavigationObserver observer(embedder_web_contents);
+ WebUIMessageListener listener(embedder_web_contents->GetWebUI(),
+ "Step3: destNode gets drop");
+ render_view_host->GetWidget()->DragTargetDrop(
+ dropdata, client_pt, screen_pt, 0);
+ ASSERT_TRUE(listener.Wait());
+ // Confirm no navigation
+ EXPECT_FALSE(observer.Navigated());
+ EXPECT_EQ(GetWebViewEnabledWebUIURL(), embedder_web_contents->GetURL());
+ }
+}
+#endif
+
+#endif // !defined(OS_MACOSX)
diff --git a/chromium/chrome/browser/ui/webui/welcome_handler.cc b/chromium/chrome/browser/ui/webui/welcome_handler.cc
new file mode 100644
index 00000000000..82b10df1e90
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/welcome_handler.cc
@@ -0,0 +1,97 @@
+// 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 "chrome/browser/ui/webui/welcome_handler.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "chrome/browser/profiles/profile.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_navigator.h"
+#include "chrome/browser/ui/profile_chooser_constants.h"
+#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
+#include "chrome/common/url_constants.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/signin/core/browser/signin_metrics.h"
+#include "ui/base/page_transition_types.h"
+
+WelcomeHandler::WelcomeHandler(content::WebUI* web_ui)
+ : profile_(Profile::FromWebUI(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);
+ UMA_HISTOGRAM_ENUMERATION("Welcome.SignInPromptResult", result_,
+ WelcomeResult::WELCOME_RESULT_MAX);
+}
+
+// Override from LoginUIService::Observer.
+void WelcomeHandler::OnSyncConfirmationUIClosed(
+ LoginUIService::SyncConfirmationUIClosedResult result) {
+ if (result != LoginUIService::ABORT_SIGNIN) {
+ result_ = WelcomeResult::SIGNED_IN;
+ GoToNewTabPage();
+ }
+}
+
+// Handles backend events necessary when user clicks "Sign in."
+void WelcomeHandler::HandleActivateSignIn(const base::ListValue* args) {
+ result_ = WelcomeResult::ATTEMPTED;
+ base::RecordAction(base::UserMetricsAction("WelcomePage_SignInClicked"));
+
+ if (SigninManagerFactory::GetForProfile(profile_)->IsAuthenticated()) {
+ // In general, this page isn't shown to signed-in users; however, if one
+ // should arrive here, then opening the sign-in dialog will likely lead
+ // to a crash. Thus, we just act like sign-in was "successful" and whisk
+ // them away to the NTP instead.
+ GoToNewTabPage();
+ } else {
+ Browser* browser = GetBrowser();
+ browser->signin_view_controller()->ShowModalSignin(
+ profiles::BubbleViewMode::BUBBLE_VIEW_MODE_GAIA_SIGNIN, browser,
+ signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE);
+ }
+}
+
+// Handles backend events necessary when user clicks "No thanks."
+void WelcomeHandler::HandleUserDecline(const base::ListValue* args) {
+ // Set the appropriate decline result, based on whether or not the user
+ // attempted to sign in.
+ result_ = (result_ == WelcomeResult::ATTEMPTED)
+ ? WelcomeResult::ATTEMPTED_DECLINED
+ : result_ = WelcomeResult::DECLINED;
+ GoToNewTabPage();
+}
+
+// Override from WebUIMessageHandler.
+void WelcomeHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "handleActivateSignIn", base::Bind(&WelcomeHandler::HandleActivateSignIn,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "handleUserDecline",
+ base::Bind(&WelcomeHandler::HandleUserDecline, base::Unretained(this)));
+}
+
+void WelcomeHandler::GoToNewTabPage() {
+ chrome::NavigateParams params(GetBrowser(), GURL(chrome::kChromeUINewTabURL),
+ ui::PageTransition::PAGE_TRANSITION_LINK);
+ chrome::Navigate(&params);
+}
+
+Browser* WelcomeHandler::GetBrowser() {
+ DCHECK(web_ui());
+ content::WebContents* contents = web_ui()->GetWebContents();
+ DCHECK(contents);
+ Browser* browser = chrome::FindBrowserWithWebContents(contents);
+ DCHECK(browser);
+ return browser;
+}
diff --git a/chromium/chrome/browser/ui/webui/welcome_handler.h b/chromium/chrome/browser/ui/webui/welcome_handler.h
new file mode 100644
index 00000000000..b685467cbec
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/welcome_handler.h
@@ -0,0 +1,60 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_WELCOME_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_WELCOME_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/signin/login_ui_service.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+class Browser;
+class Profile;
+
+// Handles actions on Welcome page.
+class WelcomeHandler : public content::WebUIMessageHandler,
+ public LoginUIService::Observer {
+ public:
+ explicit WelcomeHandler(content::WebUI* web_ui);
+ ~WelcomeHandler() override;
+
+ // LoginUIService::Observer:
+ void OnSyncConfirmationUIClosed(
+ LoginUIService::SyncConfirmationUIClosedResult result) override;
+
+ // content::WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ private:
+ enum WelcomeResult {
+ // User navigated away from page.
+ DEFAULT = 0,
+ // User clicked the "No Thanks" button.
+ DECLINED = 1,
+ // User completed sign-in flow.
+ SIGNED_IN = 2,
+ // User attempted sign-in flow, then navigated away.
+ ATTEMPTED = 3,
+ // User attempted sign-in flow, then clicked "No Thanks."
+ ATTEMPTED_DECLINED = 4,
+
+ // New results must be added before this line, and should correspond to
+ // values in tools/metrics/histograms/histograms.xml.
+ WELCOME_RESULT_MAX
+ };
+
+ void HandleActivateSignIn(const base::ListValue* args);
+ void HandleUserDecline(const base::ListValue* args);
+ void GoToNewTabPage();
+
+ Browser* GetBrowser();
+
+ Profile* profile_;
+ LoginUIService* login_ui_service_;
+ WelcomeResult result_;
+
+ DISALLOW_COPY_AND_ASSIGN(WelcomeHandler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_WELCOME_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/welcome_ui.cc b/chromium/chrome/browser/ui/webui/welcome_ui.cc
new file mode 100644
index 00000000000..7afb9d2adb7
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/welcome_ui.cc
@@ -0,0 +1,73 @@
+// 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 "chrome/browser/ui/webui/welcome_ui.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/welcome_handler.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chrome_unscaled_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "net/base/url_util.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/resources/grit/webui_resources.h"
+
+WelcomeUI::WelcomeUI(content::WebUI* web_ui, const GURL& url)
+ : content::WebUIController(web_ui) {
+ Profile* profile = Profile::FromWebUI(web_ui);
+
+ // This page is not shown to incognito or guest profiles. If one should end up
+ // here, we return, causing a 404-like page.
+ if (!profile ||
+ profile->GetProfileType() != Profile::ProfileType::REGULAR_PROFILE) {
+ return;
+ }
+
+ // Store that this profile has been shown the Welcome page.
+ profile->GetPrefs()->SetBoolean(prefs::kHasSeenWelcomePage, true);
+
+ web_ui->AddMessageHandler(base::MakeUnique<WelcomeHandler>(web_ui));
+
+ content::WebUIDataSource* html_source =
+ content::WebUIDataSource::Create(url.host());
+
+ // Check URL for variations.
+ std::string value;
+ bool is_everywhere_variant =
+ (net::GetValueForKeyInQuery(url, "variant", &value) &&
+ value == "everywhere");
+
+ int header_id = is_everywhere_variant ? IDS_WELCOME_HEADER_AFTER_FIRST_RUN
+ : IDS_WELCOME_HEADER;
+ html_source->AddString("headerText", l10n_util::GetStringUTF16(header_id));
+
+// The subheader describes the browser as being "by Google", so we only show
+// it in the "Welcome to Chrome" variant, and only on branded builds.
+#if defined(GOOGLE_CHROME_BUILD)
+ base::string16 subheader =
+ is_everywhere_variant ? base::string16()
+ : l10n_util::GetStringUTF16(IDS_WELCOME_SUBHEADER);
+ html_source->AddString("subheaderText", subheader);
+#endif
+
+ html_source->AddLocalizedString("descriptionText", IDS_WELCOME_DESCRIPTION);
+ html_source->AddLocalizedString("acceptText", IDS_WELCOME_ACCEPT_BUTTON);
+ html_source->AddLocalizedString("declineText", IDS_WELCOME_DECLINE_BUTTON);
+
+ html_source->AddResourcePath("welcome.js", IDR_WELCOME_JS);
+ html_source->AddResourcePath("welcome.css", IDR_WELCOME_CSS);
+ html_source->AddResourcePath("logo.png", IDR_PRODUCT_LOGO_128);
+ html_source->AddResourcePath("logo2x.png", IDR_PRODUCT_LOGO_256);
+ html_source->AddResourcePath("watermark.svg", IDR_WEBUI_IMAGES_GOOGLE_LOGO);
+ html_source->SetDefaultResource(IDR_WELCOME_HTML);
+
+ content::WebUIDataSource::Add(profile, html_source);
+}
+
+WelcomeUI::~WelcomeUI() {}
diff --git a/chromium/chrome/browser/ui/webui/welcome_ui.h b/chromium/chrome/browser/ui/webui/welcome_ui.h
new file mode 100644
index 00000000000..4548d4d1697
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/welcome_ui.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_WELCOME_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_WELCOME_UI_H_
+
+#include "content/public/browser/web_ui_controller.h"
+#include "url/gurl.h"
+
+// The WebUI for chrome://welcome, the page which greets new Desktop users and
+// promotes sign-in. By default, this page uses the "Welcome to Chrome" language
+// and layout; the "Take Chrome Everywhere" variant may be accessed by appending
+// the query string "?variant=everywhere".
+class WelcomeUI : public content::WebUIController {
+ public:
+ WelcomeUI(content::WebUI* web_ui, const GURL& url);
+ ~WelcomeUI() override;
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_WELCOME_UI_H_
diff --git a/chromium/chrome/browser/ui/webui/welcome_win10_handler.cc b/chromium/chrome/browser/ui/webui/welcome_win10_handler.cc
new file mode 100644
index 00000000000..eab42d6f7f9
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/welcome_win10_handler.cc
@@ -0,0 +1,176 @@
+// 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 "chrome/browser/ui/webui/welcome_win10_handler.h"
+
+#include "base/bind.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "chrome/browser/shell_integration.h"
+#include "chrome/browser/shell_integration_win.h"
+#include "chrome/common/url_constants.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "url/gurl.h"
+
+namespace {
+
+void RecordDefaultBrowserResult(
+ const std::string& histogram_suffix,
+ shell_integration::DefaultWebClientState default_browser_state) {
+ base::UmaHistogramBoolean(
+ base::StringPrintf("Welcome.Win10.DefaultPromptResult_%s",
+ histogram_suffix.c_str()),
+ default_browser_state == shell_integration::IS_DEFAULT);
+}
+
+void RecordPinnedResult(const std::string& histogram_suffix,
+ bool succeeded,
+ bool is_pinned) {
+ if (!succeeded)
+ return;
+
+ base::UmaHistogramBoolean(
+ base::StringPrintf("Welcome.Win10.PinnedPromptResult_%s",
+ histogram_suffix.c_str()),
+ is_pinned);
+}
+
+} // namespace
+
+WelcomeWin10Handler::WelcomeWin10Handler(bool inline_style_variant)
+ : inline_style_variant_(inline_style_variant), weak_ptr_factory_(this) {
+ // The check is started as early as possible because waiting for the page to
+ // be fully loaded is unnecessarily wasting time.
+ StartIsPinnedToTaskbarCheck();
+}
+
+WelcomeWin10Handler::~WelcomeWin10Handler() {
+ // The instructions for pinning Chrome to the taskbar were only displayed if
+ // Chrome wasn't pinned to the taskbar at page construction time.
+ bool pin_instructions_shown =
+ pinned_state_result_.has_value() && !pinned_state_result_.value();
+
+ std::string histogram_suffix;
+ histogram_suffix += inline_style_variant_ ? "Inline" : "Sectioned";
+ histogram_suffix += pin_instructions_shown ? "Combined" : "Default";
+
+ // Closing the page. Record whether the instructions were useful.
+ (new shell_integration::DefaultBrowserWorker(
+ base::Bind(&RecordDefaultBrowserResult, histogram_suffix)))
+ ->StartCheckIsDefault();
+
+ if (pin_instructions_shown) {
+ // On error, call RecordPinnedResult() with |succeeded| == false.
+ base::Closure error_callback =
+ base::Bind(&RecordPinnedResult, histogram_suffix, false, false);
+ shell_integration::win::GetIsPinnedToTaskbarState(
+ error_callback, base::Bind(&RecordPinnedResult, histogram_suffix));
+ }
+}
+
+void WelcomeWin10Handler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "handleSetDefaultBrowser",
+ base::Bind(&WelcomeWin10Handler::HandleSetDefaultBrowser,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "handleContinue",
+ base::Bind(&WelcomeWin10Handler::HandleContinue, base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "getPinnedToTaskbarState",
+ base::Bind(&WelcomeWin10Handler::HandleGetPinnedToTaskbarState,
+ base::Unretained(this)));
+}
+
+void WelcomeWin10Handler::HandleGetPinnedToTaskbarState(
+ const base::ListValue* args) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ AllowJavascript();
+
+ // Save the callback id so that the result can be sent back when it is
+ // available.
+ bool callback_id_found = args->GetString(0, &pinned_state_callback_id_);
+ DCHECK(callback_id_found);
+
+ // Send back the result if it is already available.
+ if (pinned_state_result_.has_value()) {
+ SendPinnedToTaskbarStateResult();
+ return;
+ }
+
+ // Only wait for a small amount of time for the result. If the timer fires,
+ // it will be assumed that Chrome isn't pinned to the taskbar. This is to make
+ // sure the instructions are displayed in case it was impossible to determine
+ // the pinned state.
+ constexpr base::TimeDelta kPinnedToTaskbarTimeout =
+ base::TimeDelta::FromMilliseconds(200);
+ timer_.Start(FROM_HERE, kPinnedToTaskbarTimeout,
+ base::Bind(&WelcomeWin10Handler::OnIsPinnedToTaskbarDetermined,
+ base::Unretained(this), true, false));
+}
+
+void WelcomeWin10Handler::HandleSetDefaultBrowser(const base::ListValue* args) {
+ base::RecordAction(
+ base::UserMetricsAction("Win10WelcomePage_SetAsDefaultBrowser"));
+ // The worker owns itself.
+ (new shell_integration::DefaultBrowserWorker(
+ shell_integration::DefaultWebClientWorkerCallback()))
+ ->StartSetAsDefault();
+}
+
+void WelcomeWin10Handler::HandleContinue(const base::ListValue* args) {
+ web_ui()->GetWebContents()->GetController().LoadURL(
+ GURL(chrome::kChromeUINewTabURL), content::Referrer(),
+ ui::PageTransition::PAGE_TRANSITION_LINK, std::string());
+}
+
+void WelcomeWin10Handler::StartIsPinnedToTaskbarCheck() {
+ // Assume that Chrome is pinned to the taskbar if an error occurs.
+ base::Closure error_callback =
+ base::Bind(&WelcomeWin10Handler::OnIsPinnedToTaskbarDetermined,
+ weak_ptr_factory_.GetWeakPtr(), false, true);
+
+ shell_integration::win::GetIsPinnedToTaskbarState(
+ error_callback,
+ base::Bind(&WelcomeWin10Handler::OnIsPinnedToTaskbarResult,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void WelcomeWin10Handler::OnIsPinnedToTaskbarResult(bool succeeded,
+ bool is_pinned_to_taskbar) {
+ // Assume that Chrome is pinned to the taskbar if an error occured.
+ OnIsPinnedToTaskbarDetermined(false, !succeeded || is_pinned_to_taskbar);
+}
+
+void WelcomeWin10Handler::OnIsPinnedToTaskbarDetermined(
+ bool timed_out,
+ bool is_pinned_to_taskbar) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ // Early exit if the pinned_state was already determined.
+ if (pinned_state_result_.has_value())
+ return;
+
+ UMA_HISTOGRAM_BOOLEAN("Welcome.Win10.PinCheckTimedOut", timed_out);
+
+ // Stop the timer if it's still running.
+ timer_.Stop();
+
+ // Cache the value.
+ pinned_state_result_.emplace(is_pinned_to_taskbar);
+
+ // If the page already called getPinnedToTaskbarState(), the result can be
+ // sent back.
+ if (!pinned_state_callback_id_.empty())
+ SendPinnedToTaskbarStateResult();
+}
+
+void WelcomeWin10Handler::SendPinnedToTaskbarStateResult() {
+ ResolveJavascriptCallback(base::Value(pinned_state_callback_id_),
+ base::Value(pinned_state_result_.value()));
+}
diff --git a/chromium/chrome/browser/ui/webui/welcome_win10_handler.h b/chromium/chrome/browser/ui/webui/welcome_win10_handler.h
new file mode 100644
index 00000000000..6a0db2772cb
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/welcome_win10_handler.h
@@ -0,0 +1,69 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_WELCOME_WIN10_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_WELCOME_WIN10_HANDLER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/timer/timer.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace base {
+class ListValue;
+}
+
+// Handles actions on the Windows 10 specific Welcome page.
+class WelcomeWin10Handler : public content::WebUIMessageHandler {
+ public:
+ explicit WelcomeWin10Handler(bool inline_style_variant);
+ ~WelcomeWin10Handler() override;
+
+ // content::WebUIMessageHandler:
+ void RegisterMessages() override;
+
+ private:
+ // Handlers for javascript calls.
+ void HandleGetPinnedToTaskbarState(const base::ListValue* args);
+ void HandleSetDefaultBrowser(const base::ListValue* args);
+ void HandleContinue(const base::ListValue* args);
+
+ void StartIsPinnedToTaskbarCheck();
+
+ // Callback for shell_integration::win::GetIsPinnedToTaskbarState().
+ void OnIsPinnedToTaskbarResult(bool succeeded, bool is_pinned_to_taskbar);
+
+ // Sets the internal result and optionally call
+ // SendPinnedToTaskbarStateResult() in the case that
+ // |pinned_state_callback_id_| is not empty.
+ void OnIsPinnedToTaskbarDetermined(bool timed_out, bool is_pinned_to_taskbar);
+
+ // Returns the result to the getPinnedToTaskbarState() javascript call via the
+ // promise.
+ void SendPinnedToTaskbarStateResult();
+
+ base::OneShotTimer timer_;
+
+ // Acts as a cache to hold the taskbar pinned state of Chrome. It has no value
+ // until this state is determined.
+ base::Optional<bool> pinned_state_result_;
+
+ // The callback id used to return the result to the getPinnedToTaskbarState()
+ // javascript call. This id is empty until we receive the call; thus this
+ // variable is used to determine if the result should be sent to the caller
+ // when it is received, or wait for the call to happen.
+ std::string pinned_state_callback_id_;
+
+ // Indicates if the inline style variant is displayed.
+ bool inline_style_variant_;
+
+ base::WeakPtrFactory<WelcomeWin10Handler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(WelcomeWin10Handler);
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_WELCOME_WIN10_HANDLER_H_
diff --git a/chromium/chrome/browser/ui/webui/welcome_win10_ui.cc b/chromium/chrome/browser/ui/webui/welcome_win10_ui.cc
new file mode 100644
index 00000000000..8628e5b9dde
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/welcome_win10_ui.cc
@@ -0,0 +1,138 @@
+// 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 "chrome/browser/ui/webui/welcome_win10_ui.h"
+
+#include <string>
+
+#include "base/feature_list.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/startup/startup_features.h"
+#include "chrome/browser/ui/webui/welcome_win10_handler.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "chrome/grit/chrome_unscaled_resources.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/theme_resources.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "net/base/url_util.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+namespace {
+
+// Helper function to check the presence of a key/value inside the query in the
+// |url|.
+bool UrlContainsKeyValueInQuery(const GURL& url,
+ const std::string& key,
+ const std::string& expected_value) {
+ std::string value;
+ return net::GetValueForKeyInQuery(url, key, &value) &&
+ value == expected_value;
+}
+
+bool ShouldShowInlineStyleVariant(const GURL& url) {
+ // Can be overridden via a query.
+ // TODO(pmonette): Remove these checks when they are no longer needed
+ // (crbug.com/658918).
+ if (UrlContainsKeyValueInQuery(url, "style", "inline"))
+ return true;
+ if (UrlContainsKeyValueInQuery(url, "style", "sectioned"))
+ return false;
+
+ return base::FeatureList::IsEnabled(features::kWelcomeWin10InlineStyle);
+}
+
+// Adds all the needed localized strings to |html_source|, depending on
+// the value of |is_first_run|.
+void AddLocalizedStrings(content::WebUIDataSource* html_source,
+ bool is_first_run) {
+ // Only show the "Welcome to Chrome" text on first run.
+ int welcome_header_id = is_first_run
+ ? IDS_WIN10_WELCOME_HEADER
+ : IDS_WIN10_WELCOME_HEADER_AFTER_FIRST_RUN;
+ html_source->AddLocalizedString("headerText", welcome_header_id);
+
+ html_source->AddLocalizedString("continueText", IDS_WIN10_WELCOME_CONTINUE);
+
+ // Default browser strings.
+ html_source->AddLocalizedString("defaultBrowserSubheaderText",
+ IDS_WIN10_WELCOME_MAKE_DEFAULT_SUBHEADING);
+ html_source->AddLocalizedString("openSettingsText",
+ IDS_WIN10_WELCOME_OPEN_SETTINGS);
+ html_source->AddLocalizedString("clickEdgeText",
+ IDS_WIN10_WELCOME_CLICK_EDGE);
+ html_source->AddLocalizedString("clickSelectChrome",
+ IDS_WIN10_WELCOME_SELECT);
+ html_source->AddLocalizedString("webBrowserLabel",
+ IDS_WIN10_WELCOME_BROWSER_LABEL);
+ html_source->AddLocalizedString("microsoftEdgeLabel",
+ IDS_WIN10_WELCOME_EDGE_LABEL);
+
+ // Taskbar pin strings.
+ html_source->AddLocalizedString("pinSubheaderText",
+ IDS_WIN10_WELCOME_PIN_SUBHEADING);
+ html_source->AddLocalizedString("rightClickText",
+ IDS_WIN10_WELCOME_RIGHT_CLICK_TASKBAR);
+ html_source->AddLocalizedString("pinInstructionText",
+ IDS_WIN10_WELCOME_PIN_INSTRUCTION);
+ html_source->AddLocalizedString("pinToTaskbarLabel",
+ IDS_WIN10_WELCOME_PIN_LABEL);
+}
+
+} // namespace
+
+WelcomeWin10UI::WelcomeWin10UI(content::WebUI* web_ui, const GURL& url)
+ : content::WebUIController(web_ui) {
+ static const char kCssFilePath[] = "welcome.css";
+ static const char kJsFilePath[] = "welcome.js";
+ static const char kDefaultFilePath[] = "default.webp";
+ static const char kPinFilePath[] = "pin.webp";
+
+ // Remember that the Win10 promo page has been shown.
+ g_browser_process->local_state()->SetBoolean(prefs::kHasSeenWin10PromoPage,
+ true);
+
+ // Determine which variation to show.
+ bool is_first_run = !UrlContainsKeyValueInQuery(url, "text", "faster");
+ bool is_inline_style = ShouldShowInlineStyleVariant(url);
+
+ web_ui->AddMessageHandler(
+ base::MakeUnique<WelcomeWin10Handler>(is_inline_style));
+
+ content::WebUIDataSource* html_source =
+ content::WebUIDataSource::Create(url.host());
+
+ AddLocalizedStrings(html_source, is_first_run);
+
+ if (is_inline_style) {
+ html_source->AddResourcePath(kCssFilePath, IDR_WELCOME_WIN10_INLINE_CSS);
+ html_source->AddResourcePath(kJsFilePath, IDR_WELCOME_WIN10_INLINE_JS);
+ html_source->AddResourcePath(kDefaultFilePath,
+ IDR_WELCOME_WIN10_DEFAULT_SMALL_WEBP);
+ html_source->AddResourcePath(kPinFilePath,
+ IDR_WELCOME_WIN10_PIN_SMALL_WEBP);
+ html_source->SetDefaultResource(IDR_WELCOME_WIN10_INLINE_HTML);
+ } else {
+ html_source->AddResourcePath(kCssFilePath, IDR_WELCOME_WIN10_SECTIONED_CSS);
+ html_source->AddResourcePath(kJsFilePath, IDR_WELCOME_WIN10_SECTIONED_JS);
+ html_source->AddResourcePath(kDefaultFilePath,
+ IDR_WELCOME_WIN10_DEFAULT_LARGE_WEBP);
+ html_source->AddResourcePath(kPinFilePath,
+ IDR_WELCOME_WIN10_PIN_LARGE_WEBP);
+ html_source->SetDefaultResource(IDR_WELCOME_WIN10_SECTIONED_HTML);
+ }
+
+ html_source->AddResourcePath("logo-small.png", IDR_PRODUCT_LOGO_64);
+ html_source->AddResourcePath("logo-large.png", IDR_PRODUCT_LOGO_128);
+
+ content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), html_source);
+}
+
+WelcomeWin10UI::~WelcomeWin10UI() = default;
diff --git a/chromium/chrome/browser/ui/webui/welcome_win10_ui.h b/chromium/chrome/browser/ui/webui/welcome_win10_ui.h
new file mode 100644
index 00000000000..21c926d9ef2
--- /dev/null
+++ b/chromium/chrome/browser/ui/webui/welcome_win10_ui.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_WELCOME_WIN10_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_WELCOME_WIN10_UI_H_
+
+#include "content/public/browser/web_ui_controller.h"
+
+class GURL;
+
+// The WebUI for chrome://welcome-win10, the page which greets new Windows 10
+// users and educates them about setting the default browser and pinning the
+// browser to their taskbar.
+class WelcomeWin10UI : public content::WebUIController {
+ public:
+ WelcomeWin10UI(content::WebUI* web_ui, const GURL& url);
+ ~WelcomeWin10UI() override;
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_WELCOME_WIN10_UI_H_